Esempio n. 1
0
class JNTControllerManager(object):
    """A node dedicated for a special thread/server like the the DHCP server or the listener thread in the webapp
    """
    def __init__(self):
        self.mqtt_controller = None
        self._controller = None
        self.heartbeat_controller_timer = None
        self._requests = {'request_info_nodes' : self.request_info_nodes, 'request_info_users' : self.request_info_users, 'request_info_configs' : self.request_info_configs,
                          'request_info_systems' : self.request_info_systems, 'request_info_basics' : self.request_info_basics, 'request_info_commands' : self.request_info_commands }
        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)


    def stop_controller_timer(self):
        """Stop the controller timer
        """
        if self.heartbeat_controller_timer is not None:
            self.heartbeat_controller_timer.cancel()
            self.heartbeat_controller_timer = None

    def start_controller_timer(self):
        """Start the controller tier
        """
        self.stop_controller_timer()
        self.heartbeat_controller_timer = threading.Timer(self._controller.heartbeat+5, self.heartbeat_controller)
        self.heartbeat_controller_timer.start()

    def stop_controller(self):
        """Stop the controller
        """
        logger.info("Stop the controller")
        if self.mqtt_controller is not None:
            self.mqtt_controller.unsubscribe(topic=TOPIC_NODES_REQUEST%(self._controller.hadd))
            self.mqtt_controller.stop()
            if self.mqtt_controller.is_alive():
                try:
                    self.mqtt_controller.join()
                except:
                    logger.exception("Catched exception")
            self.mqtt_controller = None

    def start_controller(self, section, options, **kwargs):
        """Start the controller
        """
        logger.info("Start the controller")
        cmd_classes = kwargs.pop('cmd_classes', [])
        if not COMMAND_CONTROLLER in cmd_classes:
            cmd_classes.append(COMMAND_CONTROLLER)
        self._controller = JNTNode( uuid=section, options=options, cmd_classes=cmd_classes, **kwargs)
        self._controller.add_internal_system_values()
        self._controller.add_internal_config_values()
        self._controller.hadd_get(section, None)
        self._controller.load_system_from_local()
        self.mqtt_controller = MQTTClient(options=options.data)
        self.mqtt_controller.connect()
        logger.debug("[%s] - Subscribe to topic %s", self.__class__.__name__, TOPIC_NODES_REQUEST%(self._controller.hadd))
        self.mqtt_controller.subscribe(topic=TOPIC_NODES_REQUEST%(self._controller.hadd), callback=self.on_controller_request)
        self.mqtt_controller.start()

    def heartbeat_controller(self):
        """Send a add_ctrl:-1 heartbeat message. It will ping all devices managed by this controller.
        """
        logger.debug("[%s] - Send heartbeat for controller", self.__class__.__name__)
        if self.heartbeat_controller_timer is not None:
            #The manager is started
            self.heartbeat_controller_timer.cancel()
            self.heartbeat_controller_timer = None
        self.heartbeat_controller_timer = threading.Timer(self._controller.heartbeat, self.heartbeat_controller)
        self.heartbeat_controller_timer.start()
        if self._controller.hadd is not None:
            #~ print self.nodes[node].hadd
            add_ctrl, add_node = self._controller.split_hadd()
            msg = {'add_ctrl':add_ctrl, 'add_node':add_node, 'state':'ONLINE'}
            self.mqtt_controller.publish_heartbeat_msg(msg)

    def get_controller_hadd(self):
        """Return the controller hadd"""
        if self._controller is None:
            return None
        return self._controller.hadd

    def get_controller(self):
        """Return the controller"""
        return self._controller

    def on_controller_request(self, client, userdata, message):
        """On request

        :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("on_request receive message %s", message.payload)
        try:
            data = json_loads(message.payload)
            #~ print data['uuid']
            #We should check what value is requested
            #{'hadd', 'cmd_class', 'type'='list', 'genre'='0x04', 'data'='node|value|config', 'uuid'='request_info'}
            if data['cmd_class'] == COMMAND_DISCOVERY:
                if data['genre'] == 0x04:
                    if data['uuid'] in self._requests:
                        resp = {}
                        resp.update(data)
                        try:
                            if message.topic.find('broadcast') != -1:
                                topic = "/broadcast/reply/%s" % data['data']
                                self._requests[data['uuid']](topic, resp)
                            else:
                                topic = "/nodes/%s/reply" % data['data']
                                self._requests[data['uuid']](topic, resp)
                            return
                        except:
                            logger.exception("Exception when running on_request method")
                            return
            logger.warning("Unknown request value %s", data)
        except:
            logger.exception("Exception in on_request")


    def request_info_nodes(self, reply_topic, resp):
        """
        """
        resp['data'] = self._controller.to_dict()
        msg = json_dumps(resp)
        self.publish_request(reply_topic, msg)

    def request_info_users(self, reply_topic, resp):
        """
        """
        resp['data'] = {}
        for kvalue in self._controller.values.keys():
            value = self._controller.values[kvalue]
            if value.genre == 0x02:
                if value.hadd not in resp['data']:
                    resp['data'][value.hadd] = {}
                resp['data'][value.hadd][value.uuid] = value.to_dict()
        msg = json_dumps(resp)
        self.publish_request(reply_topic, msg)

    def request_info_configs(self, reply_topic, resp):
        """
        """
        resp['data'] = {}
        for kvalue in self._controller.values.keys():
            value = self._controller.values[kvalue]
            if value.genre == 0x03:
                if value.hadd not in resp['data']:
                    resp['data'][value.hadd] = {}
                resp['data'][value.hadd][value.uuid] = value.to_dict()
        msg = json_dumps(resp)
        self.publish_request(reply_topic, msg)

    def request_info_basics(self, reply_topic, resp):
        """
        """
        resp['data'] = {}
        for kvalue in self._controller.values.keys():
            value = self._controller.values[kvalue]
            if value.genre == 0x01:
                if value.hadd not in resp['data']:
                    resp['data'][value.hadd] = {}
                resp['data'][value.hadd][value.uuid] = value.to_dict()
        msg = json_dumps(resp)
        self.publish_request(reply_topic, msg)

    def request_info_systems(self, reply_topic, resp):
        """
        """
        resp['data'] = {}
        for kvalue in self._controller.values.keys():
            value = self._controller.values[kvalue]
            if value.genre == 0x04:
                if value.hadd not in resp['data']:
                    resp['data'][value.hadd] = {}
                resp['data'][value.hadd][value.uuid] = value.to_dict()
        msg = json_dumps(resp)
        self.publish_request(reply_topic, msg)

    def request_info_commands(self, reply_topic, resp):
        """
        """
        resp['data'] = {}
        for kvalue in self._controller.values.keys():
            value = self._controller.values[kvalue]
            if value.genre == 0x04:
                if value.hadd not in resp['data']:
                    resp['data'][value.hadd] = {}
                resp['data'][value.hadd][value.uuid] = value.to_dict()
        msg = json_dumps(resp)
        self.publish_request(reply_topic, msg)

    def publish_request(self, reply_topic, msg):
        """
        """
        self.mqtt_controller.publish(topic=reply_topic, payload=msg)
Esempio n. 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/')
Esempio n. 3
0
class JNTTServer(JNTTBase):
    """Server base test
    """

    server_class = None
    server_conf = ""
    server_section = None
    hadd_ctrl = None
    hadds = None

    def setUp(self):
        JNTTBase.setUp(self)
        self.mqttc = None
        self.message = None
        self.message_received = False
        self.hearbeat_mqttc = None
        self.heartbeat_message = None
        self.heartbeat_waiting = None
        self.heartbeat_waitings = None
        self.heartbeat_received = False
        self.server = None
        self.running_server = None
        if self.hadd_ctrl is None and self.hadds is not None and len(self.hadds) > 0:
            self.hadd_ctrl = self.hadds[0]

    def tearDown(self):
        self.stopClient()
        self.mqttc = None
        self.mqtthearbeat = None
        self.stopClient()
        self.stopServer()
        self.mqttc = None
        self.message = None
        self.message_received = False
        self.hearbeat_mqttc = None
        self.heartbeat_message = None
        self.heartbeat_waiting = None
        self.heartbeat_waitings = None
        self.heartbeat_received = False
        self.server = None
        self.running_server = None
        JNTTBase.tearDown(self)

    def startClient(self, conf=None):
        if conf is None:
            conf = self.server.options.data
        if self.mqttc is None:
            self.mqttc = MQTTClient(options=conf)
            self.mqttc.connect()
            self.mqttc.start()
        if self.hearbeat_mqttc is None:
            self.hearbeat_mqttc = MQTTClient(options=conf)
            self.hearbeat_mqttc.connect()
            self.hearbeat_mqttc.start()
            self.hearbeat_mqttc.subscribe(topic=TOPIC_HEARTBEAT, callback=self.mqtt_on_heartbeat_message)

    def stopClient(self):
        if self.mqttc != None:
            self.mqttc.stop()
        if self.hearbeat_mqttc != None:
            self.hearbeat_mqttc.unsubscribe(topic=TOPIC_HEARTBEAT)
            self.hearbeat_mqttc.stop()
        if self.mqttc != None:
            if self.mqttc.is_alive():
                self.mqttc.join()
            self.mqttc = None
        if self.hearbeat_mqttc != None:
            if self.hearbeat_mqttc.is_alive():
                self.hearbeat_mqttc.join()
            self.hearbeat_mqttc = None

    def startServer(self):
        if self.server is None:
            with mock.patch(
                "sys.argv", ["%s" % self.server_class, "start", "--conf_file=%s" % self.getDataFile(self.server_conf)]
            ):
                options = vars(jnt_parse_args())
                self.server = self.server_class(options)
            self.server.start()
            self.running_server = threading.Timer(0.01, self.server.run)
            self.running_server.start()
            time.sleep(1.5)

    def stopServer(self):
        if self.server is not None:
            self.server.stop()
            time.sleep(5)
            self.server = None
        if self.running_server is not None:
            self.running_server.cancel()
            time.sleep(5)
            self.running_server = None
        self.message = None

    def start(self):
        self.startServer()
        self.startClient()

    def stop(self):
        self.stopClient()
        self.stopServer()

    def mqtt_on_heartbeat_message(self, client, userdata, message):
        """On generic message
        """
        self.heartbeat_message = message
        hb = HeartbeatMessage(self.heartbeat_message)
        hbadd_ctrl, hbadd_node, state = hb.get_heartbeat()
        if hbadd_ctrl is not None and hbadd_node is not None:
            if self.heartbeat_waiting is None:
                if self.heartbeat_waitings is None:
                    self.heartbeat_received = True
                elif HADD % (hbadd_ctrl, hbadd_node) in self.heartbeat_waitings:
                    self.heartbeat_waitings.remove(HADD % (hbadd_ctrl, hbadd_node))
                    if len(self.heartbeat_waitings) == 0:
                        self.heartbeat_received = True
            elif self.heartbeat_waiting == HADD % (hbadd_ctrl, hbadd_node):
                self.heartbeat_received = True
        print "HADD : %s/%s = %s" % (hbadd_ctrl, hbadd_node, state)

    def assertInLogfile(self, expr="^ERROR "):
        """Assert an expression is in logifle
        Must be called at the end of process, when the server has closed the logfile.
        """
        self.assertTrue(self.server_conf is not None)
        options = JNTOptions(options={"conf_file": self.getDataFile(self.server_conf)})
        log_file_from_config = options.get_option("handler_file", "args", None)
        self.assertTrue(log_file_from_config is not None)
        # I know, it's bad
        log_args = eval(log_file_from_config)
        log_file_from_config = log_args[0]
        self.assertFile(log_file_from_config)
        found = False
        with open(log_file_from_config, "r") as hand:
            for line in hand:
                print line
                if re.search(expr, line):
                    found = True
        self.assertTrue(found)

    def assertNotInLogfile(self, expr="^ERROR "):
        """Assert an expression is not in logifle.
        Must be called at the end of process, when the server has closed the logfile.
        """
        self.assertTrue(self.server_conf is not None)
        options = JNTOptions(options={"conf_file": self.getDataFile(self.server_conf)})
        log_file_from_config = options.get_option("handler_file", "args", None)
        self.assertTrue(log_file_from_config is not None)
        # I know, it's bad
        log_args = eval(log_file_from_config)
        log_file_from_config = log_args[0]
        self.assertFile(log_file_from_config)
        found = False
        with open(log_file_from_config, "r") as hand:
            for line in hand:
                print line
                if re.search(expr, line):
                    found = True
        self.assertFalse(found)

    def assertHeartbeatNode(self, hadd=None, timeout=60):
        print "Waiting for %s" % (hadd)
        self.heartbeat_waiting = hadd
        self.heartbeat_waitings = None
        self.heartbeat_message = None
        self.heartbeat_received = False
        i = 0
        while i < timeout * 10000 and not self.heartbeat_received:
            time.sleep(0.0001)
            i += 1
        self.assertTrue(self.heartbeat_received)
        time.sleep(0.5)

    def assertHeartbeatNodes(self, hadds=None, timeout=60):
        if hadds is None:
            hadds = self.hadds
        print "Waiting for %s" % (hadds)
        self.heartbeat_waiting = None
        self.heartbeat_waitings = list(hadds)
        self.heartbeat_message = None
        self.heartbeat_received = False
        i = 0
        while i < timeout * 10000 and not self.heartbeat_received:
            time.sleep(0.0001)
            i += 1
        print "Unreceived heartbeats %s" % self.heartbeat_waitings
        self.assertTrue(self.heartbeat_received)
        time.sleep(0.5)

    def waitHeartbeatNodes(self, hadds=None, timeout=60):
        if hadds is None:
            hadds = self.hadds
        print "Waiting for %s" % (hadds)
        self.heartbeat_waiting = None
        self.heartbeat_waitings = list(hadds)
        self.heartbeat_message = None
        self.heartbeat_received = False
        i = 0
        while i < timeout * 10000 and not self.heartbeat_received:
            time.sleep(0.0001)
            i += 1
        print "Unreceived heartbeats %s" % self.heartbeat_waitings
        time.sleep(0.5)

    def assertNodeRequest(
        self,
        cmd_class=0,
        genre=0x04,
        uuid="request_info_nodes",
        node_hadd=None,
        client_hadd=None,
        data=None,
        is_writeonly=False,
        is_readonly=False,
        timeout=5,
    ):
        self.message_received = False
        print "Waiting for %s : %s" % (node_hadd, uuid)

        def mqtt_on_message(client, userdata, message):
            """On generic message
            """
            self.message = message
            self.message_received = True

        self.mqttc.subscribe(topic=TOPIC_NODES_REPLY % client_hadd, callback=mqtt_on_message)
        time.sleep(0.5)
        msg = {
            "cmd_class": cmd_class,
            "genre": genre,
            "uuid": uuid,
            "reply_hadd": client_hadd,
            "data": data,
            "hadd": node_hadd,
            "is_writeonly": is_writeonly,
            "is_readonly": is_readonly,
        }
        self.mqttc.publish("/nodes/%s/request" % (node_hadd), json_dumps(msg))
        i = 0
        while i < timeout * 10000 and not self.message_received:
            time.sleep(0.0001)
            i += 1
        self.assertTrue(self.message_received)
        self.mqttc.unsubscribe(topic=TOPIC_NODES_REPLY % client_hadd)
        time.sleep(0.5)

    def assertBroadcastRequest(
        self,
        cmd_class=0,
        genre=0x04,
        uuid="request_info_nodes",
        node_hadd=None,
        client_hadd=None,
        data=None,
        is_writeonly=False,
        is_readonly=False,
        timeout=5,
    ):
        self.message_received = False

        def mqtt_on_message(client, userdata, message):
            """On generic message
            """
            self.message = message
            self.message_received = True

        self.mqttc.subscribe(topic=TOPIC_BROADCAST_REPLY % client_hadd, callback=mqtt_on_message)
        time.sleep(0.5)
        msg = {
            "cmd_class": cmd_class,
            "genre": genre,
            "uuid": uuid,
            "reply_hadd": client_hadd,
            "data": data,
            "is_writeonly": is_writeonly,
            "is_readonly": is_readonly,
        }
        self.mqttc.publish(TOPIC_BROADCAST_REQUEST, json_dumps(msg))
        i = 0
        while i < timeout * 10000 and not self.message_received:
            time.sleep(0.0001)
            i += 1
        self.assertTrue(self.message_received)
        self.mqttc.unsubscribe(topic=TOPIC_BROADCAST_REPLY % client_hadd)
        time.sleep(0.5)

    def assertUpdateValue(
        self,
        type="user",
        data=None,
        cmd_class=0,
        genre=0x04,
        uuid="request_info_nodes",
        node_hadd=None,
        client_hadd=None,
        is_writeonly=False,
        is_readonly=False,
        timeout=5,
    ):
        self.message_received = False
        self.message = None
        print "Waiting for %s : %s" % (node_hadd, uuid)

        def mqtt_on_message(client, userdata, message):
            """On generic message
            """
            msg = json_loads(message.payload)
            print "Received message %s" % msg
            if msg["uuid"] == uuid and msg["hadd"] == node_hadd:
                self.message = message
                self.message_received = True

        self.mqttc.subscribe(topic="/values/%s/%s/#" % (type, node_hadd), callback=mqtt_on_message)
        print "Subscribe to /values/%s/%s/#" % (type, node_hadd)
        time.sleep(0.5)
        msg = {
            "cmd_class": cmd_class,
            "genre": genre,
            "uuid": uuid,
            "reply_hadd": client_hadd,
            "data": data,
            "hadd": node_hadd,
            "is_writeonly": is_writeonly,
            "is_readonly": is_readonly,
        }
        self.mqttc.publish("/nodes/%s/request" % (node_hadd), json_dumps(msg))
        i = 0
        while i < timeout * 10000 and not self.message_received:
            time.sleep(0.0001)
            i += 1
        self.assertTrue(self.message_received)
        self.assertTrue(self.message is not None)
        self.assertTrue(self.message.payload is not None)
        if data is not None:
            msg = json_loads(self.message.payload)
            self.assertEqual(msg["data"], data)
        self.mqttc.unsubscribe(topic="/values/%s/%s/#" % (type, node_hadd))
        time.sleep(0.5)

    def assertNotUpdateValue(
        self,
        type="user",
        data=None,
        cmd_class=0,
        genre=0x04,
        uuid="request_info_nodes",
        node_hadd=None,
        client_hadd=None,
        is_writeonly=False,
        is_readonly=False,
        timeout=5,
    ):
        self.message_received = False
        self.message = None
        print "Waiting for %s : %s" % (node_hadd, uuid)

        def mqtt_on_message(client, userdata, message):
            """On generic message
            """
            msg = json_loads(message.payload)
            print "Received message %s" % msg
            if msg["uuid"] == uuid and msg["hadd"] == node_hadd:
                self.message = message
                self.message_received = True

        self.mqttc.subscribe(topic="/values/%s/%s/#" % (type, node_hadd), callback=mqtt_on_message)
        print "Subscribe to /values/%s/%s/#" % (type, node_hadd)
        time.sleep(0.5)
        msg = {
            "cmd_class": cmd_class,
            "genre": genre,
            "uuid": uuid,
            "reply_hadd": client_hadd,
            "data": data,
            "hadd": node_hadd,
            "is_writeonly": is_writeonly,
            "is_readonly": is_readonly,
        }
        self.mqttc.publish("/nodes/%s/request" % (node_hadd), json_dumps(msg))
        i = 0
        while i < timeout * 10000 and not self.message_received:
            time.sleep(0.0001)
            i += 1
        self.assertTrue(self.message is None)
        self.assertFalse(self.message_received)
        self.mqttc.unsubscribe(topic="/values/%s/%s/#" % (type, node_hadd))
        time.sleep(0.5)

    def assertWaitValue(
        self,
        type="user",
        data=None,
        cmd_class=0,
        genre=0x04,
        uuid="request_info_nodes",
        node_hadd=None,
        client_hadd=None,
        is_writeonly=False,
        is_readonly=False,
        timeout=5,
    ):
        self.message_received = False
        self.message = None
        print "Waiting for %s : %s" % (node_hadd, uuid)

        def mqtt_on_message(client, userdata, message):
            """On generic message
            """
            msg = json_loads(message.payload)
            print "Received message %s" % msg
            if msg["uuid"] == uuid and msg["hadd"] == node_hadd:
                self.message = message
                self.message_received = True

        self.mqttc.subscribe(topic="/values/%s/%s/#" % (type, node_hadd), callback=mqtt_on_message)
        print "Subscribe to /values/%s/%s/#" % (type, node_hadd)
        time.sleep(0.5)
        i = 0
        while i < timeout * 10000 and not self.message_received:
            time.sleep(0.0001)
            i += 1
        self.assertTrue(self.message_received)
        self.assertTrue(self.message is not None)
        self.assertTrue(self.message.payload is not None)
        if data is not None:
            msg = json_loads(self.message.payload)
            self.assertEqual(msg["data"], data)
        self.mqttc.unsubscribe(topic="/values/%s/%s/#" % (type, node_hadd))
        time.sleep(0.5)

    def assertNotWaitValue(
        self,
        type="user",
        data=None,
        cmd_class=0,
        genre=0x04,
        uuid="request_info_nodes",
        node_hadd=None,
        client_hadd=None,
        is_writeonly=False,
        is_readonly=False,
        timeout=5,
    ):
        self.message_received = False
        self.message = None
        print "Waiting for %s : %s" % (node_hadd, uuid)

        def mqtt_on_message(client, userdata, message):
            """On generic message
            """
            msg = json_loads(message.payload)
            print "Received message %s" % msg
            if msg["uuid"] == uuid and msg["hadd"] == node_hadd:
                self.message = message
                self.message_received = True

        self.mqttc.subscribe(topic="/values/%s/%s/#" % (type, node_hadd), callback=mqtt_on_message)
        print "Subscribe to /values/%s/%s/#" % (type, node_hadd)
        time.sleep(0.5)
        i = 0
        while i < timeout * 10000 and not self.message_received:
            time.sleep(0.0001)
            i += 1
        self.assertTrue(self.message is None)
        self.assertFalse(self.message_received)
        self.mqttc.unsubscribe(topic="/values/%s/%s/#" % (type, node_hadd))
        time.sleep(0.5)

    def assertFsmBoot(self, state="booting"):
        """Assert Finish State Machine can boot
        """
        thread = self.server.find_thread(self.server_section)
        self.assertNotEqual(thread, None)
        JNTTBase.assertFsmBoot(self, bus=thread.bus, state="booting")