25
29
            print "No Password or Passfile in client config file"
 
26
30
            # raise RuntimeError XXX
 
27
31
            self.password = "gazonk"
 
 
32
        self.fqdn = fqdn                # string
 
 
33
        self.created = datetime.datetime.now()
 
 
34
        self.last_seen = None           # datetime.datetime()
 
31
35
        if timeout is None:
 
32
 
            timeout = self.server.options.timeout
 
33
 
        self.timeout = timeout
 
 
36
            timeout = options.timeout
 
 
37
        self.timeout = timeout          # datetime.timedelta()
 
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
 
 
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):
 
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)
 
 
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)
 
69
130
class udp_handler(SocketServer.DatagramRequestHandler, object):
 
 
111
170
class IPv6_TCPServer(SocketServer.ForkingTCPServer, object):
 
112
 
    __init__ = init_with_options
 
113
 
    address_family = socket.AF_INET6
 
114
 
    allow_reuse_address = True
 
 
171
    __metaclass__ = server_metaclass
 
115
172
    request_queue_size = 1024
 
116
 
    server_bind = server_bind
 
119
175
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):
 
124
212
    parser = OptionParser()
 
 
144
232
    parser.add_option("-t", "--timeout", type="string", # Parsed later
 
146
234
                      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")
 
147
240
    (options, args) = parser.parse_args()
 
149
 
    # Parse the time argument
 
 
247
    # Parse the time arguments
 
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):
 
 
249
        options.timeout = string_to_delta(options.timeout)
 
167
251
        parser.error("option --timeout: Unparseable time")
 
 
254
        options.interval = string_to_delta(options.interval)
 
 
256
        parser.error("option --interval: Unparseable time")
 
169
258
    cert = gnutls.crypto.X509Certificate(open(options.cert).read())
 
170
259
    key = gnutls.crypto.X509PrivateKey(open(options.key).read())
 
171
260
    ca = gnutls.crypto.X509Certificate(open(options.ca).read())