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)
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/')
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")