29
25
print "No Password or Passfile in client config file"
30
26
# raise RuntimeError XXX
31
27
self.password = "gazonk"
32
self.fqdn = fqdn # string
33
self.created = datetime.datetime.now()
34
self.last_seen = None # datetime.datetime()
35
31
if timeout is None:
36
timeout = options.timeout
37
self.timeout = timeout # datetime.timedelta()
32
timeout = self.server.options.timeout
33
self.timeout = timeout
39
interval = options.interval
40
self.interval = interval # datetime.timedelta()
41
self.next_check = datetime.datetime.now() # datetime.datetime()
42
self.checker = None # or a subprocess.Popen()
43
def check_action(self, now=None):
44
"""The checker said something and might have completed.
45
Check if is has, and take appropriate actions."""
46
if self.checker.poll() is None:
47
# False alarm, no result yet
51
now = datetime.datetime.now()
52
if self.checker.returncode == 0:
54
while self.next_check <= now:
55
self.next_check += self.interval
56
handle_request = check_action
57
def start_checker(self):
35
interval = self.server.options.interval
36
self.interval = interval
38
def server_bind(self):
39
if self.options.interface:
40
if not hasattr(socket, "SO_BINDTODEVICE"):
41
# From /usr/include/asm-i486/socket.h
42
socket.SO_BINDTODEVICE = 25
60
self.checker = subprocess.Popen("sleep 1; fping -q -- %s"
61
% re.escape(self.fqdn),
62
stdout=subprocess.PIPE,
65
except subprocess.OSError, e:
66
print "Failed to start subprocess:", e
67
def stop_checker(self):
68
if self.checker is None:
70
os.kill(self.checker.pid, signal.SIGTERM)
71
if self.checker.poll() is None:
72
os.kill(self.checker.pid, signal.SIGKILL)
74
__del__ = stop_checker
76
if self.checker is None:
78
return self.checker.stdout.fileno()
80
"""The time when something must be done about this client"""
81
return min(self.last_seen + self.timeout, self.next_check)
82
def still_valid(self, now=None):
83
"""Has this client's timeout not passed?"""
85
now = datetime.datetime.now()
86
return now < (self.last_seen + timeout)
87
def it_is_time_to_check(self, now=None):
89
now = datetime.datetime.now()
90
return self.next_check <= now
93
class server_metaclass(type):
94
"Common behavior for the UDP and TCP server classes"
95
def __new__(cls, name, bases, attrs):
96
attrs["address_family"] = socket.AF_INET6
97
attrs["allow_reuse_address"] = True
98
def server_bind(self):
99
if self.options.interface:
100
if not hasattr(socket, "SO_BINDTODEVICE"):
101
# From /usr/include/asm-i486/socket.h
102
socket.SO_BINDTODEVICE = 25
104
self.socket.setsockopt(socket.SOL_SOCKET,
105
socket.SO_BINDTODEVICE,
106
self.options.interface)
107
except socket.error, error:
108
if error[0] == errno.EPERM:
109
print "Warning: No permission to bind to interface", \
110
self.options.interface
113
return super(type(self), self).server_bind()
114
attrs["server_bind"] = server_bind
115
def init(self, *args, **kwargs):
116
if "options" in kwargs:
117
self.options = kwargs["options"]
118
del kwargs["options"]
119
if "clients" in kwargs:
120
self.clients = kwargs["clients"]
121
del kwargs["clients"]
122
if "credentials" in kwargs:
123
self.credentials = kwargs["credentials"]
124
del kwargs["credentials"]
125
return super(type(self), self).__init__(*args, **kwargs)
126
attrs["__init__"] = init
127
return type.__new__(cls, name, bases, attrs)
44
self.socket.setsockopt(socket.SOL_SOCKET,
45
socket.SO_BINDTODEVICE,
46
self.options.interface)
47
except socket.error, error:
48
if error[0] == errno.EPERM:
49
print "Warning: Denied permission to bind to interface", \
50
self.options.interface
53
return super(type(self), self).server_bind()
56
def init_with_options(self, *args, **kwargs):
57
if "options" in kwargs:
58
self.options = kwargs["options"]
60
if "clients" in kwargs:
61
self.clients = kwargs["clients"]
63
if "credentials" in kwargs:
64
self.credentials = kwargs["credentials"]
65
del kwargs["credentials"]
66
return super(type(self), self).__init__(*args, **kwargs)
130
69
class udp_handler(SocketServer.DatagramRequestHandler, object):
170
111
class IPv6_TCPServer(SocketServer.ForkingTCPServer, object):
171
__metaclass__ = server_metaclass
112
__init__ = init_with_options
113
address_family = socket.AF_INET6
114
allow_reuse_address = True
172
115
request_queue_size = 1024
116
server_bind = server_bind
175
119
in6addr_any = "::"
177
def string_to_delta(interval):
178
"""Parse a string and return a datetime.timedelta
180
>>> string_to_delta('7d')
181
datetime.timedelta(7)
182
>>> string_to_delta('60s')
183
datetime.timedelta(0, 60)
184
>>> string_to_delta('60m')
185
datetime.timedelta(0, 3600)
186
>>> string_to_delta('24h')
187
datetime.timedelta(1)
188
>>> string_to_delta(u'1w')
189
datetime.timedelta(7)
192
suffix=unicode(interval[-1])
193
value=int(interval[:-1])
195
delta = datetime.timedelta(value)
197
delta = datetime.timedelta(0, value)
199
delta = datetime.timedelta(0, 0, 0, 0, value)
201
delta = datetime.timedelta(0, 0, 0, 0, 0, value)
203
delta = datetime.timedelta(0, 0, 0, 0, 0, 0, value)
206
except (ValueError, IndexError):
212
124
parser = OptionParser()
232
144
parser.add_option("-t", "--timeout", type="string", # Parsed later
234
146
help="Amount of downtime allowed for clients")
235
parser.add_option("--interval", type="string", # Parsed later
237
help="How often to check that a client is up")
238
parser.add_option("--check", action="store_true", default=False,
239
help="Run self-test")
240
147
(options, args) = parser.parse_args()
247
# Parse the time arguments
149
# Parse the time argument
249
options.timeout = string_to_delta(options.timeout)
151
suffix=options.timeout[-1]
152
value=int(options.timeout[:-1])
154
options.timeout = datetime.timedelta(value)
156
options.timeout = datetime.timedelta(0, value)
158
options.timeout = datetime.timedelta(0, 0, 0, 0, value)
160
options.timeout = datetime.timedelta(0, 0, 0, 0, 0, value)
162
options.timeout = datetime.timedelta(0, 0, 0, 0, 0, 0,
166
except (ValueError, IndexError):
251
167
parser.error("option --timeout: Unparseable time")
254
options.interval = string_to_delta(options.interval)
256
parser.error("option --interval: Unparseable time")
258
169
cert = gnutls.crypto.X509Certificate(open(options.cert).read())
259
170
key = gnutls.crypto.X509PrivateKey(open(options.key).read())
260
171
ca = gnutls.crypto.X509Certificate(open(options.ca).read())