示例#1
0
 def start(self):
     """Start the DHCP Server
     """
     logger.info("Start the server")
     self.lease_mgr = LeaseManager(self.options)
     #~ self.uuid = self.options.get_option(self.section, 'uuid')
     #~ if self.uuid == None:
         #~ self.uuid = muuid.uuid1()
         #~ self.options.set_option(self.section, 'uuid', '%s'%self.uuid)
     self.loop_sleep = 0.25
     loop_sleep = self.options.get_option('system','loop_sleep')
     if loop_sleep is not None:
         try:
             self.loop_sleep = int(loop_sleep)
         except Exception:
             logger.exception("[%s] - Exception when retrieving value of loop_sleep. Use default value instead", self.__class__.__name__)
     self.network = DhcpNetwork(self._stopevent, self.options, is_primary=True, is_secondary=False, do_heartbeat_dispatch=True)
     JNTDBServer.start(self)
     JNTControllerManager.start_controller(self, self.section, self.options, cmd_classes=[COMMAND_DHCPD], hadd=None, name="DHCP Server",
         product_name="DHCP Server", product_type="DHCP Server")
     self.mqtt_resolv = MQTTClient(options=self.options.data, loop_sleep=self.loop_sleep)
     self.mqtt_resolv.connect()
     self.mqtt_resolv.start()
     #~ print "self.network.resolv_timeout", self.network.resolv_timeout
     self.resolv_timer = threading.Timer(self.network.resolv_timeout, self.resolv_heartbeat)
     self.resolv_timer.start()
     self.network.boot({0:self.get_controller_hadd()}, loop_sleep=self.loop_sleep)
     self.mqtt_client = MQTTClient(options=self.options.data, loop_sleep=self.loop_sleep)
     self.mqtt_client.add_topic(topic='/dhcp/lease/new', callback=self.mqtt_on_lease_new)
     self.mqtt_client.add_topic(topic='/dhcp/lease/repair', callback=self.mqtt_on_lease_repair)
     self.mqtt_client.add_topic(topic='/dhcp/lease/lock', callback=self.mqtt_on_lease_lock)
     self.mqtt_client.add_topic(topic='/dhcp/lease/remove', callback=self.mqtt_on_lease_remove)
     self.mqtt_client.add_topic(topic='/dhcp/lease/release', callback=self.mqtt_on_lease_release)
     self.mqtt_client.add_topic(topic='/dhcp/heartbeat#', callback=self.mqtt_on_heartbeat)
     self.mqtt_client.add_topic(topic='/dhcp/resolv/hadd', callback=self.mqtt_on_resolv_hadd)
     self.mqtt_client.add_topic(topic='/dhcp/resolv/name', callback=self.mqtt_on_resolv_name)
     self.mqtt_client.add_topic(topic='/dhcp/resolv/cmd_classes', callback=self.mqtt_on_resolv_cmd_classes)
     self.mqtt_client.connect()
     self.mqtt_client.subscribe(topic='/dhcp/#', callback=self.mqtt_on_message)
     self.mqtt_client.start()
     self.heartbeat_timer = threading.Timer(self.lease_mgr.heartbeat_timeout, self.check_heartbeat)
     self.heartbeat_timer.start()
     #ProgrammingError: (pysqlite2.dbapi2.ProgrammingError) SQLite objects created in a thread can only be used in that same thread.
     #The object was created in thread id 139632282289984 and this is thread id 139632153548544
     #[SQL: u'SELECT dhcpd_lease.add_ctrl AS dhcpd_lease_add_ctrl, dhcpd_lease.add_node AS dhcpd_lease_add_node, dhcpd_lease.name AS dhcpd_lease_name, dhcpd_lease.location AS dhcpd_lease_location, dhcpd_lease.cmd_classes AS dhcpd_lease_cmd_classes, dhcpd_lease.state AS dhcpd_lease_state, dhcpd_lease.last_seen AS dhcpd_lease_last_seen \nFROM dhcpd_lease'] [parameters: [immutabledict({})]]
     #self.lease_mgr.start(self.dbsession)
     #Use a new session for the lease
     self.lease_mgr.start(self.create_session(), self.network.heartbeat_cache)
     JNTControllerManager.start_controller_timer(self)
示例#2
0
class DHCPServer(JNTDBServer, JNTControllerManager):
    """The Dynamic Home Configuration Protocol Server

    """
    def __init__(self, options):
        """
        """
        self.network = None
        self.lease_mgr = None
        self.mqtt_resolv = None
        self.mqtt_client = None
        self.resolv_timer = None
        self.heartbeat_timer = None
        self.section = "dhcp"
        JNTDBServer.__init__(self, options)
        JNTControllerManager.__init__(self)

    def __del__(self):
        """
        """
        try:
            self.stop()
        except Exception:
            logger.debug("[%s] - Catched exception", self.__class__.__name__)

    def start(self):
        """Start the DHCP Server
        """
        logger.info("Start the server")
        self.lease_mgr = LeaseManager(self.options)
        #~ self.uuid = self.options.get_option(self.section, 'uuid')
        #~ if self.uuid == None:
            #~ self.uuid = muuid.uuid1()
            #~ self.options.set_option(self.section, 'uuid', '%s'%self.uuid)
        self.loop_sleep = 0.25
        loop_sleep = self.options.get_option('system','loop_sleep')
        if loop_sleep is not None:
            try:
                self.loop_sleep = int(loop_sleep)
            except Exception:
                logger.exception("[%s] - Exception when retrieving value of loop_sleep. Use default value instead", self.__class__.__name__)
        self.network = DhcpNetwork(self._stopevent, self.options, is_primary=True, is_secondary=False, do_heartbeat_dispatch=True)
        JNTDBServer.start(self)
        JNTControllerManager.start_controller(self, self.section, self.options, cmd_classes=[COMMAND_DHCPD], hadd=None, name="DHCP Server",
            product_name="DHCP Server", product_type="DHCP Server")
        self.mqtt_resolv = MQTTClient(options=self.options.data, loop_sleep=self.loop_sleep)
        self.mqtt_resolv.connect()
        self.mqtt_resolv.start()
        #~ print "self.network.resolv_timeout", self.network.resolv_timeout
        self.resolv_timer = threading.Timer(self.network.resolv_timeout, self.resolv_heartbeat)
        self.resolv_timer.start()
        self.network.boot({0:self.get_controller_hadd()}, loop_sleep=self.loop_sleep)
        self.mqtt_client = MQTTClient(options=self.options.data, loop_sleep=self.loop_sleep)
        self.mqtt_client.add_topic(topic='/dhcp/lease/new', callback=self.mqtt_on_lease_new)
        self.mqtt_client.add_topic(topic='/dhcp/lease/repair', callback=self.mqtt_on_lease_repair)
        self.mqtt_client.add_topic(topic='/dhcp/lease/lock', callback=self.mqtt_on_lease_lock)
        self.mqtt_client.add_topic(topic='/dhcp/lease/remove', callback=self.mqtt_on_lease_remove)
        self.mqtt_client.add_topic(topic='/dhcp/lease/release', callback=self.mqtt_on_lease_release)
        self.mqtt_client.add_topic(topic='/dhcp/heartbeat#', callback=self.mqtt_on_heartbeat)
        self.mqtt_client.add_topic(topic='/dhcp/resolv/hadd', callback=self.mqtt_on_resolv_hadd)
        self.mqtt_client.add_topic(topic='/dhcp/resolv/name', callback=self.mqtt_on_resolv_name)
        self.mqtt_client.add_topic(topic='/dhcp/resolv/cmd_classes', callback=self.mqtt_on_resolv_cmd_classes)
        self.mqtt_client.connect()
        self.mqtt_client.subscribe(topic='/dhcp/#', callback=self.mqtt_on_message)
        self.mqtt_client.start()
        self.heartbeat_timer = threading.Timer(self.lease_mgr.heartbeat_timeout, self.check_heartbeat)
        self.heartbeat_timer.start()
        #ProgrammingError: (pysqlite2.dbapi2.ProgrammingError) SQLite objects created in a thread can only be used in that same thread.
        #The object was created in thread id 139632282289984 and this is thread id 139632153548544
        #[SQL: u'SELECT dhcpd_lease.add_ctrl AS dhcpd_lease_add_ctrl, dhcpd_lease.add_node AS dhcpd_lease_add_node, dhcpd_lease.name AS dhcpd_lease_name, dhcpd_lease.location AS dhcpd_lease_location, dhcpd_lease.cmd_classes AS dhcpd_lease_cmd_classes, dhcpd_lease.state AS dhcpd_lease_state, dhcpd_lease.last_seen AS dhcpd_lease_last_seen \nFROM dhcpd_lease'] [parameters: [immutabledict({})]]
        #self.lease_mgr.start(self.dbsession)
        #Use a new session for the lease
        self.lease_mgr.start(self.create_session(), self.network.heartbeat_cache)
        JNTControllerManager.start_controller_timer(self)

    def resolv_heartbeat(self):
        """
        """
        logger.debug("[%s] - Send heartbeat on resolv", self.__class__.__name__)
        if self.resolv_timer is not None:
            #The manager is started
            self.resolv_timer.cancel()
            self.resolv_timer = None
        self.resolv_timer = threading.Timer(self.network.resolv_timeout, self.resolv_heartbeat)
        self.resolv_timer.start()
        if self.get_controller_hadd() is not None:
            #~ print self.nodes[node].hadd
            add_ctrl, add_node = self.get_controller().split_hadd()
            msg = {'add_ctrl':add_ctrl, 'add_node':add_node, 'state':'ONLINE'}
            self.mqtt_resolv.publish(topic="/dhcp/resolv/heartbeat", payload=json_dumps(msg))

    def reload(self):
        """Reload the server
        """
        logger.info("[%s] - Reload the server", self.__class__.__name__)
        self.stop()
        time.sleep(1.0)
        self.start()

    def start_threads(self):
        """Start the threads associated to this server.
        """
        pass

    def run(self):
        i = 0
        while not self._stopevent.isSet():
            i += 1
            self._stopevent.wait(self.loop_sleep)

    def stop(self):
        """Stop the DHCP Server
        """
        logger.info("Stop the server")
        if self.heartbeat_timer is not None:
            #The manager is started
            self.heartbeat_timer.cancel()
            self.heartbeat_timer = None
        if self.resolv_timer is not None:
            #The manager is started
            self.resolv_timer.cancel()
            self.resolv_timer = None
        JNTControllerManager.stop_controller_timer(self)
        if self.network is not None:
            self.network.stop()
        if self.lease_mgr is not None:
            self.lease_mgr.stop()
        if self.mqtt_resolv is not None:
            self.mqtt_resolv.stop()
            self.mqtt_resolv = None
        JNTControllerManager.stop_controller(self)
        maxi = 1
        while maxi<10 and not self.network.is_stopped:
            self._stopevent.wait(self.loop_sleep*10)
            maxi += self.loop_sleep*10
        if self.mqtt_client is not None:
            self.mqtt_client.unsubscribe(topic='/dhcp/#')
            self.mqtt_client.remove_topic(topic='/dhcp/lease/new')
            self.mqtt_client.remove_topic(topic='/dhcp/lease/repair')
            self.mqtt_client.remove_topic(topic='/dhcp/lease/lock')
            self.mqtt_client.remove_topic(topic='/dhcp/lease/remove')
            self.mqtt_client.remove_topic(topic='/dhcp/lease/release')
            self.mqtt_client.remove_topic(topic='/dhcp/heartbeat#')
            self.mqtt_client.remove_topic(topic='/dhcp/resolv/hadd')
            self.mqtt_client.remove_topic(topic='/dhcp/resolv/name')
            self.mqtt_client.remove_topic(topic='/dhcp/resolv/cmd_classes')
            self.mqtt_client.stop()
            self.mqtt_client = None
        JNTDBServer.stop(self)
        self.network = None
        self.lease_mgr = None
        logger.info("Server stopped")

    def start_threads(self):
        """Start the threads associated to this server.
        """
        pass

    def check_heartbeat(self):
        """Check the states of the machines. Must be called in a timer
        Called in a separate thread. Must use a scoped_session.

        """
        if self.heartbeat_timer is not None:
            #The manager is started
            self.heartbeat_timer.cancel()
            self.heartbeat_timer = None
            self.heartbeat_timer = threading.Timer(self.lease_mgr.heartbeat_timeout, self.check_heartbeat)
            self.heartbeat_timer.start()
        self.lease_mgr.check_heartbeat(session=self.create_session())

    def mqtt_on_message(self, client, userdata, message):
        """On generic message

        :param client: the Client instance that is calling the callback.
        :type client: paho.mqtt.client.Client
        :param userdata: user data of any type and can be set when creating a new client instance or with user_data_set(userdata).
        :type userdata: all
        :param message: The message variable is a MQTTMessage that describes all of the message parameters.
        :type message: paho.mqtt.client.MQTTMessage
        """
        #~ print "mqtt_on_message Ok"
        pass

    def mqtt_on_lease_new(self, client, userdata, message):
        """On generic message

        :param client: the Client instance that is calling the callback.
        :type client: paho.mqtt.client.Client
        :param userdata: user data of any type and can be set when creating a new client instance or with user_data_set(userdata).
        :type userdata: all
        :param message: The message variable is a MQTTMessage that describes all of the message parameters.
        :type message: paho.mqtt.client.MQTTMessage
        """
        logger.debug("mqtt_on_lease_new receive %s", message.payload)
        res = {}
        res['msg_status'] = 200
        data = json_loads(message.payload)
        if 'rep_uuid' not in data:
            logger.debug("mqtt_on_lease_new receive a request with no rep_uuid")
            return
        for ffield in ['add_ctrl', 'add_node', 'options']:
            if ffield not in data:
                res['msg_status'] = 400
                res['msg_error'] = "Missing field %s in request" % ffield
        res['rep_uuid'] = data['rep_uuid']
        if res['msg_status'] == 200:
            for ffield in ['name', 'location']:
                if ffield not in data['options']:
                    res['msg_status'] = 400
                    res['msg_error'] = "Missing option %s in request" % ffield
        if res['msg_status'] == 200:
            lease = self.lease_mgr.new_lease(data['add_ctrl'], data['add_node'], data['options'])
            res.update(lease)
        #print res
        self.publish_reply(uuid=data['rep_uuid'], payload=json_dumps(res))

    def mqtt_on_lease_repair(self, client, userdata, message):
        """On lease repair message

        :param client: the Client instance that is calling the callback.
        :type client: paho.mqtt.client.Client
        :param userdata: user data of any type and can be set when creating a new client instance or with user_data_set(userdata).
        :type userdata: all
        :param message: The message variable is a MQTTMessage that describes all of the message parameters.
        :type message: paho.mqtt.client.MQTTMessage
        """
        logger.debug("mqtt_on_lease_repair receive %s", message.payload)
        res = {}
        res['msg_status'] = 200
        data = json_loads(message.payload)
        if 'rep_uuid' not in data:
            logger.debug("mqtt_on_lease_repair receive a request with no rep_uuid")
            return
        for ffield in ['add_ctrl', 'add_node', 'options']:
            if ffield not in data:
                res['msg_status'] = 400
                res['msg_error'] = "Missing field %s in request" % ffield
        res['rep_uuid'] = data['rep_uuid']
        if res['msg_status'] == 200:
            for ffield in ['name', 'location']:
                if ffield not in data['options']:
                    res['msg_status'] = 400
                    res['msg_error'] = "Missing option %s in request" % ffield
        if res['msg_status'] == 200:
            lease = self.lease_mgr.repair_lease(data['add_ctrl'], data['add_node'], data['options'])
            res.update(lease)
        #print res
        self.publish_reply(uuid=data['rep_uuid'], payload=json_dumps(res))

    def mqtt_on_lease_lock(self, client, userdata, message):
        """On lease lock message

        :param client: the Client instance that is calling the callback.
        :type client: paho.mqtt.client.Client
        :param userdata: user data of any type and can be set when creating a new client instance or with user_data_set(userdata).
        :type userdata: all
        :param message: The message variable is a MQTTMessage that describes all of the message parameters.
        :type message: paho.mqtt.client.MQTTMessage
        """
        logger.debug("mqtt_on_lease_lock receive %s", message.payload)
        res = {}
        res['msg_status'] = 200
        data = json_loads(message.payload)
        if 'rep_uuid' not in data:
            logger.debug("mqtt_on_lease_lock receive a request with no rep_uuid")
            return
        for ffield in ['add_ctrl', 'add_node']:
            if ffield not in data:
                res['msg_status'] = 400
                res['msg_error'] = "Missing field %s in request" % ffield
        res['rep_uuid'] = data['rep_uuid']
        if res['msg_status'] == 200:
            lease = self.lease_mgr.lock_lease(data['add_ctrl'], data['add_node'])
            if lease is None:
                res['msg_status'] = 400
                res['msg_error'] = "Can't find a lease for %s:%s" % (data['add_ctrl'], data['add_node'])
                res['add_ctrl'] = data['add_ctrl']
                res['add_node'] = data['add_node']
            else:
                res.update(lease)
        #print res
        self.publish_reply(uuid=data['rep_uuid'], payload=json_dumps(res))

    def mqtt_on_lease_release(self, client, userdata, message):
        """On lease release message

        :param client: the Client instance that is calling the callback.
        :type client: paho.mqtt.client.Client
        :param userdata: user data of any type and can be set when creating a new client instance or with user_data_set(userdata).
        :type userdata: all
        :param message: The message variable is a MQTTMessage that describes all of the message parameters.
        :type message: paho.mqtt.client.MQTTMessage
        """
        logger.debug("mqtt_on_lease_release receive %s", message.payload)
        res = {}
        res['msg_status'] = 200
        data = json_loads(message.payload)
        if 'rep_uuid' not in data:
            logger.debug("mqtt_on_lease_release receive a request with no rep_uuid")
            return
        for ffield in ['add_ctrl', 'add_node']:
            if ffield not in data:
                res['msg_status'] = 400
                res['msg_error'] = "Missing field %s in request" % ffield
        res['rep_uuid'] = data['rep_uuid']
        if res['msg_status'] == 200:
            lease = self.lease_mgr.release_lease(data['add_ctrl'], data['add_node'])
            res.update(lease)
        #print res
        self.publish_reply(uuid=data['rep_uuid'], payload=json_dumps(res))

    def mqtt_on_lease_remove(self, client, userdata, message):
        """On generic message

        :param client: the Client instance that is calling the callback.
        :type client: paho.mqtt.client.Client
        :param userdata: user data of any type and can be set when creating a new client instance or with user_data_set(userdata).
        :type userdata: all
        :param message: The message variable is a MQTTMessage that describes all of the message parameters.
        :type message: paho.mqtt.client.MQTTMessage
        """
        logger.debug("mqtt_on_lease_remove receive %s", message.payload)
        res = {}
        res['msg_status'] = 200
        data = json_loads(message.payload)
        if 'rep_uuid' not in data:
            logger.debug("mqtt_on_lease_remove receive a request with no rep_uuid")
            return
        for ffield in ['add_ctrl', 'add_node']:
            if ffield not in data:
                res['msg_status'] = 400
                res['msg_error'] = "Missing field %s in request" % ffield
        res['rep_uuid'] = data['rep_uuid']
        if res['msg_status'] == 200:
            self.lease_mgr.remove_lease(data['add_ctrl'], data['add_node'])
            res['add_ctrl'] = data['add_ctrl']
            res['add_node'] = data['add_node']
        #print res
        self.publish_reply(uuid=data['rep_uuid'], payload=json_dumps(res))

    def mqtt_on_heartbeat(self, client, userdata, message):
        """On heartbeat message

        :param client: the Client instance that is calling the callback.
        :type client: paho.mqtt.client.Client
        :param userdata: user data of any type and can be set when creating a new client instance or with user_data_set(userdata).
        :type userdata: all
        :param message: The message variable is a MQTTMessage that describes all of the message parameters.
        :type message: paho.mqtt.client.MQTTMessage
        """
        hb = HeartbeatMessage(message)
        add_ctrl, add_node, state = hb.get_heartbeat()
        if add_ctrl is not None:
            self.lease_mgr.heartbeat_hadd(add_ctrl, add_node, state)

    def mqtt_on_resolv_cmd_classes(self, client, userdata, message):
        """On generic message

        :param client: the Client instance that is calling the callback.
        :type client: paho.mqtt.client.Client
        :param userdata: user data of any type and can be set when creating a new client instance or with user_data_set(userdata).
        :type userdata: all
        :param message: The message variable is a MQTTMessage that describes all of the message parameters.
        :type message: paho.mqtt.client.MQTTMessage
        """
        #~ print "mqtt_on_resolv_cmd_classes Ok"
        pass

    def mqtt_on_resolv_name(self, client, userdata, message):
        """On generic message

        :param client: the Client instance that is calling the callback.
        :type client: paho.mqtt.client.Client
        :param userdata: user data of any type and can be set when creating a new client instance or with user_data_set(userdata).
        :type userdata: all
        :param message: The message variable is a MQTTMessage that describes all of the message parameters.
        :type message: paho.mqtt.client.MQTTMessage
        """
        #~ print "mqtt_on_resolv_name Ok"
        pass

    def mqtt_on_resolv_hadd(self, client, userdata, message):
        """On resolv hadd message

        :param client: the Client instance that is calling the callback.
        :type client: paho.mqtt.client.Client
        :param userdata: user data of any type and can be set when creating a new client instance or with user_data_set(userdata).
        :type userdata: all
        :param message: The message variable is a MQTTMessage that describes all of the message parameters.
        :type message: paho.mqtt.client.MQTTMessage
        """
        #~ print "mqtt_on_resolv_hadd receive %s"
        logger.debug("mqtt_on_resolv_hadd receive %s", message.payload)
        res = {}
        res['msg_status'] = 200
        data = json_loads(message.payload)
        if 'rep_uuid' not in data:
            logger.debug("mqtt_on_resolv_hadd receive a request with no rep_uuid")
            return
        for ffield in ['add_ctrl', 'add_node']:
            if ffield not in data:
                res['msg_status'] = 400
                res['msg_error'] = "Missing field %s in request" % ffield
        res['rep_uuid'] = data['rep_uuid']
        if res['msg_status'] == 200:
            lease = self.lease_mgr.resolv_hadd(data['add_ctrl'], data['add_node'])
            #print lease
            res.update(lease)
        #print res
        self.publish_reply(uuid=data['rep_uuid'], payload=json_dumps(res))

    def publish_reply(self, uuid, payload=None, qos=0, retain=False):
        """Publish an uid reply to clients.

        This causes a message to be sent to the broker and subsequently from
        the broker to any clients subscribing to matching topics.

        :param uuid: The uuid sent in the request.
        :type uuid: str
        :param payload: The actual message to send. If not given, or set to None a
                        zero length message will be used. Passing an int or float will result
                        in the payload being converted to a string representing that number. If
                        you wish to send a true int/float, use struct.pack() to create the
                        payload you require.
        :type payload: message
        :param qos: The quality of service level to use.
        :type qos: int
        :param retain: If set to true, the message will be set as the "last known good"/retained message for the topic.
        :type retain: bool
        """
        self.mqtt_client.publish_reply(uuid=uuid, payload=payload, qos=qos, retain=retain)

    def publish_stats(self, stat, value=None, qos=0, retain=False):
        """Publish a message on a topic.

        This causes a message to be sent to the broker and subsequently from
        the broker to any clients subscribing to matching topics.

        :param stat: The stat to send.
        :type stat: str
        :param value: the value of the stat
        :type value: message
        :param qos: The quality of service level to use.
        :type qos: int
        :param retain: If set to true, the message will be set as the "last known good"/retained message for the topic.
        :type retain: bool
        """
        self.mqtt_client.publish_stats(stat=stat, value=value, qos=qos, retain=retain)
        self.publish('$SYS/dhcp/')