class UDPTransport(object): """ Node communication using the UDP protocol. """ def __init__(self, host, port, protocol=None): self.protocol = protocol self.server = DatagramServer((host, port), handle=self.receive) self.server.start() self.host = self.server.server_host self.port = self.server.server_port def receive(self, data, host_port): # pylint: disable=unused-argument self.protocol.receive(data) # enable debugging using the DummyNetwork callbacks DummyTransport.track_recv(self.protocol.raiden, host_port, data) def send(self, sender, host_port, bytes_): """ Send `bytes_` to `host_port`. Args: sender (address): The address of the running node. host_port (Tuple[(str, int)]): Tuple with the host name and port number. bytes_ (bytes): The bytes that are going to be sent throught the wire. """ self.server.sendto(bytes_, host_port) # enable debugging using the DummyNetwork callbacks DummyTransport.network.track_send(sender, host_port, bytes_) def register(self, proto, host, port): # pylint: disable=unused-argument assert isinstance(proto, RaidenProtocol) self.protocol = proto def stop(self): self.server.stop()
class UDPTransport(object): """ Node communication using the UDP protocol. """ def __init__(self, host, port, protocol=None): self.protocol = protocol self.server = DatagramServer((host, port), handle=self.receive) self.server.start() self.host = self.server.server_host self.port = self.server.server_port def receive(self, data, host_port): # pylint: disable=unused-argument self.protocol.receive(data) # enable debugging using the DummyNetwork callbacks DummyTransport.track_recv(self.protocol.raiden, host_port, data) def send(self, sender, host_port, bytes_): """ Send `bytes_` to `host_port`. Args: sender (address): The address of the running node. host_port (Tuple[(str, int)]): Tuple with the host name and port number. bytes_ (bytes): The bytes that are going to be sent through the wire. """ self.server.sendto(bytes_, host_port) # enable debugging using the DummyNetwork callbacks DummyTransport.network.track_send(sender, host_port, bytes_) def register(self, proto, host, port): # pylint: disable=unused-argument assert isinstance(proto, RaidenProtocol) self.protocol = proto def stop(self): self.server.stop()
class UDPTransport(object): """ Node communication using the UDP protocol. """ def __init__(self, host, port, socket=None, protocol=None, throttle_policy=DummyPolicy()): self.protocol = protocol if socket is not None: self.server = DatagramServer(socket, handle=self.receive) else: self.server = DatagramServer((host, port), handle=self.receive) self.host = self.server.server_host self.port = self.server.server_port self.throttle_policy = throttle_policy def receive(self, data, host_port): # pylint: disable=unused-argument self.protocol.receive(data) # enable debugging using the DummyNetwork callbacks DummyTransport.track_recv(self.protocol.raiden, host_port, data) def send(self, sender, host_port, bytes_): """ Send `bytes_` to `host_port`. Args: sender (address): The address of the running node. host_port (Tuple[(str, int)]): Tuple with the host name and port number. bytes_ (bytes): The bytes that are going to be sent through the wire. """ sleep_timeout = self.throttle_policy.consume(1) # Don't sleep if timeout is zero, otherwise a context-switch is done # and the message is delayed, increasing it's latency if sleep_timeout: gevent.sleep(sleep_timeout) if not hasattr(self.server, 'socket'): raise RuntimeError('trying to send a message on a closed server') self.server.sendto(bytes_, host_port) # enable debugging using the DummyNetwork callbacks DummyTransport.network.track_send(sender, host_port, bytes_) def stop(self): self.server.stop() def stop_accepting(self): self.server.stop_accepting() def start(self): assert not self.server.started # server.stop() clears the handle, since this may be a restart the # handle must always be set self.server.set_handle(self.receive) self.server.start()
class UdpServer(TcpRpcServer): def __init__(self, port): self.port = port self.server = DatagramServer(('', self.port), handle=self.handle) self.protocol = PT_UDP def handle(self, data, address): self.server.sendto(data, address)
class UDPTransport(object): def __init__(self, host, port, protocol=None): self.protocol = protocol self.server = DatagramServer(('', 0), handle=self.receive) self.server.start() self.host = self.server.server_host self.port = self.server.server_port def receive(self, data, host_port): self.protocol.receive(data) def send(self, sender, host_port, data): log.info('TRANSPORT SENDS') self.server.sendto(data, host_port) DummyTransport.network.track_send(sender, host_port, data) # debuging def register(self, proto, host, port): assert isinstance(proto, RaidenProtocol) self.protocol = proto
class UdpServerActor(Actor): def __init__(self, address="0.0.0.0", port=5011): Actor.__init__(self) self.address, self.port = address, port self.server = DatagramServer((self.address, self.port), self.socket_read) # creates a new server gevent.spawn(self.server.serve_forever) def socket_read(self, data, address): print "server actor received", data, address print "server makes echo: " self.socket_write(data, address) def socket_write(self, data, target=None): if target is None: print "target is empty!" else: try: self.server.sendto(data, target) except Exception as e: print "didn't write to socket..", e pass
class UDPTransport(object): def __init__(self, host, port, protocol=None): self.protocol = protocol self.server = DatagramServer(('', 0), handle=self.receive) self.server.start() self.host = self.server.server_host self.port = self.server.server_port def receive(self, data, host_port): self.protocol.receive(data) def send(self, sender, host_port, data): # print "TRANSPORT SENDS", datas.decode(data) try: self.server.sendto(data, host_port) except gevent.socket.error as e: raise e DummyTransport.network.track_send(sender, host_port, data) # debuging def register(self, proto, host, port): assert isinstance(proto, RaidenProtocol) self.protocol = proto
class DMServerCore(OperationRequest): def __init__(self, server_ip, server_port, client_ip, client_port): super(DMServerCore, self).__init__() self.lwm2m_dm_server_ip = server_ip self.lwm2m_dm_server_port = server_port self.local_client_ip_ = client_ip self.local_client_port_ = client_port self.sem = Semaphore() self.sem_counter = 0 self.lwm2m_resources = LWM2MResourceTree() self.registration = Registration(self.lwm2m_resources) self.execution = Execution(self.lwm2m_resources) self.discover = Discovery(lwm2m_resources=self.lwm2m_resources) self.observation = ObservationNotificationEngine(self.lwm2m_resources) self.read = Read(self.lwm2m_resources) self.write = Write(self.lwm2m_resources) self.create_object_instance = Create(self.lwm2m_resources) self.write_attributes = WriteAttributes(self.lwm2m_resources) def create_server(self, ): """ Creates and starts a LWM2M DM Server using Gevent DatagramServer. The server listens at the ip and port specified below. A handler is used to entertain the requests coming at that port """ self.dm_server = DatagramServer((self.lwm2m_dm_server_ip, \ self.lwm2m_dm_server_port), self.handle_request) self.dm_server.start() def stop_server(self, ): """ Stops the LWM2M DM Server """ self.dm_server.stop() def handle_request(self, message, remote): """ Handles the requests coming at the specified ip and port """ rx_record = connection.ReceptionRecord(None, message, remote) msg = rx_record.message uri_query = msg.findOption(options.UriQuery) self.process(rx_record, remote, uri_query) def handle_lwm2m_put(self, msg, uri_query, remote, rx_record): """ It consists of Normal Update, Write Operation, Write Attribute Operation. Write Operation is used to update the resource(s) as per the request. Write Attributes operation is used to update the attributes of the object, object instance or resource. """ method = None try: method = uri_query[0].value.split("=")[1] except: pass if method == "write": path = msg.findOption(URI_PATH_VALUE) content_type_number = msg.findOption(options.ContentType) if content_type_number is None: content_type = "text/plain" else: content_type = constants.media_types[content_type_number.value] self.write.write_resource(msg.payload, path, content_type) payload_forward = msg.payload msg = connection.Message(connection.Message.ACK, code=constants.CHANGED, payload="Resource Updated") self.dm_server.sendto(msg._pack(rx_record.transaction_id), remote) client_port = self.generate_client_port() self.write.forward_write_request(path, payload_forward, \ content_type, remote, client_port) elif method == "write_attributes": path = msg.findOption(URI_PATH_VALUE) content_type_number = msg.findOption(options.ContentType) if content_type_number is None: content_type = "text/plain" else: content_type = constants.media_types[content_type_number.value] payload = loads(msg.payload) self.write_attributes.set_attributes(path, remote, payload) msg = connection.Message(connection.Message.ACK, code=constants.CHANGED, payload="Resource Attributes Updated") self.dm_server.sendto(msg._pack(rx_record.transaction_id), remote) client_port = self.generate_client_port() self.write_attributes.forward_request(path, remote, payload, content_type, client_port) else: endpoint_location = msg.findOption(URI_PATH_VALUE)[0].value if msg.payload == "": self.logger.info("Updating the Registration Params") endpoint_object = self.lwm2m_resources.return_endpoint_object( endpoint_location=endpoint_location) endpoint_object.listener_ip = uri_query[6].value.split("=")[1] endpoint_object.local_ip = uri_query[6].value.split("=")[1] msg = connection.Message(connection.Message.ACK, code=constants.CHANGED, payload="Resource Updated") self.dm_server.sendto(msg._pack(rx_record.transaction_id), remote) else: self.logger.info("Adding/Updating the Resources") payload = self.update_resource( loads(msg.payload), endpoint_location=endpoint_location) msg = connection.Message(connection.Message.ACK, code=constants.CHANGED, payload="Resource Updated") self.dm_server.sendto(msg._pack(rx_record.transaction_id), remote) self.logger.info("Forwarding the Notification") request = lwm2m_api() client_port = self.generate_client_port() response = request.send_notification(self.general_observation.listener_ip, self.general_observation.listener_port, \ self.general_observation.token_id, payload, content_type="application/json", client_port=client_port) def update_resource(self, res_payload, endpoint_location=None, endpoint_name=None): total_res_dict = {} total_object_info = {} payload = res_payload endpoint_object = self.registration.handle_put_resource_updates( res_payload, endpoint_location=endpoint_location, endpoint_name=endpoint_name) for item, value in payload.iteritems(): resources_dict = endpoint_object.objects_dict[item][ "object"].resources_id_dict res_dict = {} for item1, value1 in resources_dict.iteritems(): res_dict.update({item1: value1["object"].res_value}) total_res_dict.update({item: {"resources": res_dict}}) total_object_info = {endpoint_object.endpoint_name: total_res_dict} return total_object_info def process(self, rx_record, remote, uri_query): """ Processes various requests like CON (POST, PUT, GET) or NON. POST requests : Generally used for Registration and Execution PUT requests : Generally used for Updating the resources GET requests : Generally used for Discovery, Observation, Cancel Observation """ msg = rx_record.message self.uri_query = uri_query if msg.transaction_type == connection.Message.CON: if constants.POST == msg.code: method = None try: method = uri_query[0].value.split("=")[1] except: pass if method == "create": path = msg.findOption(URI_PATH_VALUE) content_type_number = msg.findOption(options.ContentType) if content_type_number is None: content_type = "text/plain" else: content_type = constants.media_types[ content_type_number.value] self.create_object_instance.create_instance( path, remote, content_type, loads(msg.payload)) resources = loads(msg.payload) msg = connection.Message(connection.Message.ACK, code=constants.CREATED, payload="Resource Created") self.dm_server.sendto(msg._pack(rx_record.transaction_id), remote) client_port = self.generate_client_port() self.create_object_instance.forward_request( path, remote, resources, content_type, client_port) elif method == "execute": path = msg.findOption(URI_PATH_VALUE) content_type_number = msg.findOption(options.ContentType) if content_type_number is None: content_type = "text/plain" else: content_type = constants.media_types[ content_type_number.value] self.execution.execute_resource(path, remote, msg.payload) execute_payload = msg.payload msg = connection.Message(connection.Message.ACK, code=constants.CHANGED, payload="Resource Executed") self.dm_server.sendto(msg._pack(rx_record.transaction_id), remote) client_port = self.generate_client_port() self.execution.forward_request(path, remote, execute_payload, client_port) elif method == "notify": self.logger.info("Notification Received") client_port = self.generate_client_port() for item1, item2 in loads(msg.payload).iteritems(): if item1 == "observer_ip": observer_ip = item2 elif item1 == "observer_port": observer_port = item2 elif item1 != "observer_ip" and item1 != "observer_port": endpoint_name = item1 for item3, item4 in item2.iteritems(): for item5, item6 in item4[ "resources"].iteritems(): pass res = { item3: { "resources": { item5.split("_")[0]: { "res_value": item6, "res_inst_id": item5.split("_")[1] } } } } payload = {} payload = self.update_resource(res, endpoint_name=endpoint_name) payload["observer_ip"] = observer_ip payload["observer_port"] = observer_port token_id = msg.token observe_value = msg.findOption(options.Observe).value self.logger.info("Forwarding Notification") content_type = "application/json" request = lwm2m_api() response = request.send_notification(self.general_observation.listener_ip, self.general_observation.listener_port, token_id, payload, \ content_type=content_type, time_elapse=observe_value, client_port=client_port) msg = connection.Message(connection.Message.ACK, code=constants.CREATED, payload="Notification Received") self.dm_server.sendto(msg._pack(rx_record.transaction_id), remote) else: """ Handles the Client Registration Request """ self.logger.info( "Registering Client Endpoint in the LWM2M DM Server") endpoint = self.registration.process_registration( msg, uri_query) response = self.registration.register_client(endpoint) registered_client_location = response if registered_client_location is not None: self.logger.info( "Client Endpoint Registration Successful for Endpoint : %s", endpoint.endpoint_name) self.logger.info("The registered location is %s", registered_client_location) payload = self.set_general_observation_params() else: self.logger.info("Client Endpoint Registration Failed") msg = connection.Message( connection.Message.ACK, code=constants.CREATED, location=registered_client_location) self.dm_server.sendto(msg._pack(rx_record.transaction_id), remote) #Send the General Observation to the Registered Client #self.send_general_observation(registered_client_location) elif constants.PUT == msg.code: """ It consists of Normal Update, Write Operation, Write Attribute Operation. Write Operation is used to update the resource(s) as per the request. Write Attributes operation is used to update the attributes of the object, object instance or resource. """ self.handle_lwm2m_put(msg, uri_query, remote, rx_record) elif constants.GET == msg.code: """ Handles Requests like Discovery, Observation """ try: observe_value = msg.findOption(options.Observe).value except: observe_value = "" if observe_value == OBSERVE_OPTION_VALUE_OBSERVATION: """ Sets the Observation. Two types of observations. General Observation and Specific Observation. General Observation is used for anything that is not observed and updates are sent as general notifications using a general token. Specific observation is implicitly defined by the observer(as request) and handled as specific notification with a specific token """ path = msg.findOption(URI_PATH_VALUE) if len(path) == 1: self.set_m2m_server_adapter_params(rx_record, remote) else: self.logger.info( "Specific Observation Request Received") content_type_number = msg.findOption( options.ContentType) if content_type_number is None: content_type = "text/plain" else: content_type = constants.media_types[ content_type_number.value] token_id = msg.token app_ip = loads(msg.payload)["app_ip"] app_port = loads(msg.payload)["app_port"] client_port = self.generate_client_port() response = self.observation.forward_request(path, remote, observe_value, \ self.lwm2m_dm_server_ip, self.lwm2m_dm_server_port, \ app_ip, app_port, token_id, client_port) msg = connection.Message(connection.Message.ACK, code=constants.CONTENT, \ payload="test") #todo: payload to be replaced self.dm_server.sendto( msg._pack(rx_record.transaction_id, token_id), remote) elif observe_value == OBSERVE_OPTION_VALUE_CANCEL_OBSERVATION: """ Removes the observation from the List """ self.logger.info("Cancel Observation Request Received") path = msg.findOption(URI_PATH_VALUE) token_id = msg.token app_ip = loads(msg.payload)["app_ip"] app_port = loads(msg.payload)["app_port"] client_port = self.generate_client_port() response = self.observation.forward_request(path, remote, observe_value, \ self.lwm2m_dm_server_ip, self.lwm2m_dm_server_port, \ app_ip, app_port, token_id, client_port) def _handle_response(response): msg = connection.Message(connection.Message.ACK, code=constants.CONTENT, payload=response) self.dm_server.sendto( msg._pack(rx_record.transaction_id), remote) response.then(_handle_response) else: method = None try: method = uri_query[0].value.split("=")[1] except: pass if method == "read": path = msg.findOption(URI_PATH_VALUE) self.read.read_resource(path, remote) msg = connection.Message(connection.Message.ACK, code=constants.CONTENT, \ payload="info read", content_type="text/plain") self.dm_server.sendto( msg._pack(rx_record.transaction_id), remote) elif method == "discover": if msg.payload == "/.well-known/core": payload = dumps(self.discover.get_all_resources()) else: path = msg.findOption(URI_PATH_VALUE) client_port = self.generate_client_port() payload = self.discover.forward_request( path, remote, client_port) def _handle_response(payload): msg = connection.Message(connection.Message.ACK, code=constants.CONTENT, \ payload=payload, content_type="application/json") self.dm_server.sendto( msg._pack(rx_record.transaction_id), remote) if payload is Promise: payload.then(_handle_response) else: _handle_response(payload) elif msg.transaction_type == connection.Message.NON: print "reached msg non" payload = msg.payload print payload def set_general_observation_params(self, ): return { "listener_ip": self.lwm2m_dm_server_ip, "listener_port": self.lwm2m_dm_server_port } def send_general_observation(self, registered_client_location): if registered_client_location is not None: payload = dumps(self.set_general_observation_params()) endpoint_object = self.lwm2m_resources.return_endpoint_object( endpoint_location=registered_client_location) client_listener_ip = endpoint_object.listener_ip client_listener_port = endpoint_object.listener_port request = lwm2m_api() response = request.observe_resource(client_listener_ip, client_listener_port, \ payload=payload, client_port=self.generate_client_port()) def set_m2m_server_adapter_params(self, rx_record, remote): msg = rx_record.message #content_type is application/json listener_ip = loads(msg.payload)["listener_ip"] listener_port = loads(msg.payload)["listener_port"] token_id = msg.token self.general_observation = GeneralObservationInformation( listener_ip, listener_port, token_id) response = "Observation Started on the LWM2M Server" msg = connection.Message(connection.Message.ACK, code=constants.CONTENT, payload=response) self.dm_server.sendto(msg._pack(rx_record.transaction_id, token_id), remote) def generate_client_port(self, ): if self.sem_counter >= 1000: self.sem_counter = 0 self.sem.acquire() self.sem_counter += 1 sem_counter = self.sem_counter self.sem.release() client_port = self.local_client_port_ + sem_counter return client_port
class DMClient_CoAP_Server(DeviceClass): """ Handles and process the CoAP requests for Registration, Discovery, Observation/Notifications and etc """ def __init__(self, api, dm_server_ip, dm_server_port, local_server_ip, local_server_port, local_client_ip, local_client_port): super(DMClient_CoAP_Server, self).__init__() self.subscription_list = [] self.sender_info = [] self.lwm2m_dm_server_ip = dm_server_ip self.lwm2m_dm_server_port = dm_server_port self.local_server_ip = local_server_ip self.local_server_port = local_server_port self.local_client_ip = local_client_ip self.local_client_port = local_client_port #TODO: change datagram server to wrapper of coap server self.local_server = DatagramServer((self.local_server_ip, self.local_server_port), self.handle_request) self.api = api def handle_request(self, message, remote): rx_record = connection.ReceptionRecord(None, message, remote) msg = rx_record.message uriQuery = msg.findOption(options.UriQuery) self.process(rx_record, remote, uriQuery) def start_server(self, ): print "Local Server Started" self.local_server.start() def stop_server(self, ): print "Local Server Stopped" self.local_server.stop() def process(self, rx_record, remote, uriQ): """ Processes the POST, PUT and GET requests """ msg=rx_record.message self.uriQuery1 = uriQ self.payload = msg.payload global resourceString if constants.POST == msg.code: #Registration Client global server_ip_port, location_address q = rx_record.message.findOption(URI_QUERY_VALUE)[0].value #TODO: check if coap says somthing about execute messages if str(q).find("execute") != -1: print "entered in client:: execute field" self.executeResource(rx_record) msg = connection.Message(connection.Message.ACK, code=constants.CHANGED, payload="execution") self.local_server.sendto(msg._pack(rx_record.transaction_id), remote) else: server_ip = self.lwm2m_dm_server_ip server_port = self.lwm2m_dm_server_port server_ip_port = server_ip + ":" + str(server_port) payload = msg.payload path = "rd?" query = rx_record.message.findOption(URI_QUERY_VALUE)[0].value.strip() #Creates objects in the local database local_lwm2m_client.create_mgmt_objects(query, payload) msg = connection.Message(connection.Message.ACK, code=constants.CREATED, payload="Client Registered") self.local_server.sendto(msg._pack(rx_record.transaction_id), remote) #Registration in the Server res_register = client_request.clientRegistration(lwm2m_server_ip_port, path, query, payload, client_port = 5683) location_address = res_register.findOption(LOCATION_VALUE)[0].value self.storeDiscoverPaths.append({ "path" : "rd", "location" : location_address, "objectID" : None, "objectInstID" : None, "resID" : None}) elif constants.PUT == msg.code: check_pmax = 0 #pmin and pmax imposed from the dm server, upath related to an object for val1 in uriQ: if str(val1).find("pmax") != -1: check_pmax = 1 if check_pmax == 1: path = [] for v in rx_record.message.findOption(URI_PATH_VALUE): path.append(v.value) upath = "/".join(path) pmax = str(rx_record.message.findOption(URI_QUERY_VALUE)[0].value).split("=")[1] pmin = str(rx_record.message.findOption(URI_QUERY_VALUE)[1].value).split("=")[1] check_pmax = 0 filtered_resources = self.write_attributes(upath, pmax, pmin) msg = connection.Message(connection.Message.ACK, code=constants.CHANGED, payload=filtered_resources) self.local_server.sendto(msg._pack(rx_record.transaction_id), remote) else: #TODO: Write: standard exists for writing resources? self.resource_update(rx_record, self.uriQuery1, remote) elif constants.GET == msg.code: try: observe_value = rx_record.message.findOption(options.Observe).value except ValueError: observe_value = None #-1 if observe_value == OBSERVE_OPTION_VALUE_OBSERVATION: filtered_resources = self.observe_resource(rx_record, self.uriQuery1) elif observe_value == OBSERVE_OPTION_VALUE_CANCEL_OBSERVATION: filtered_resources = self.cancel_observe_resource(rx_record) elif str(rx_record.message.findOption(URI_PATH_VALUE)[0].value).find("rd") != -1: filtered_resources = self.handle_discover_request(rx_record) msg = connection.Message(connection.Message.ACK, code=constants.CONTENT, payload=json.dumps(filtered_resources)) self.local_server.sendto(msg._pack(rx_record.transaction_id), remote) def resource_update(self, rx_record, uriQuery1, remote): """ Updates the resources in the local database and LWM2M server """ store_query = [] for val in self.uriQuery1: splitQuery = str(str(val).split(":")[1]).strip() store_query.append({"id" : -1, "code" : str(splitQuery).split("=")[0], "value" : str(splitQuery).split("=")[1]}) split_payload = str(rx_record.message.payload).split("/") local_lwm2m_client.update_mgmt_object(split_payload[0], split_payload[1], store_query) payloadForServer = rx_record.message.payload msg = connection.Message(connection.Message.ACK, code=constants.CHANGED, payload="Resource Updated") self.local_server.sendto(msg._pack(rx_record.transaction_id), remote) # Updates the LWM2M Server path = "rd/" + location_address +"?" query = str(splitQuery).split("=")[0] + "=" + str(splitQuery).split("=")[1] payload = payloadForServer client_request.clientUpdate(lwm2m_server_ip_port, query, payload, path=path, client_port=self.local_client_port) def myfunc(self,): self.p1 = subprocess.Popen(['sh', 'webcamstart.sh', '127.0.0.1', '34000']) #, stdout = subprocess.PIPE) self.p1.communicate() def executeResource(self, rx_record): for path_element in rx_record.message.findOption(URI_PATH_VALUE): print "client: elements %s" %path_element objectID = rx_record.message.findOption(URI_PATH_VALUE)[2].value objectInstID = rx_record.message.findOption(URI_PATH_VALUE)[3].value resID = rx_record.message.findOption(URI_PATH_VALUE)[4].value print objectID, objectInstID, resID print rx_record.message.payload storeQuery = [] storeQuery.append({"id" : int(resID), "code" : "Null", "value" : int(rx_record.message.payload)}) local_lwm2m_client.update_mgmt_object(objectID, objectInstID, storeQuery) #self.p = subprocess.Popen(['sh', 'webcamstart.sh', '127.0.0.1', '34000'], stdout = subprocess.PIPE) #self.api.run_task(self.p.communicate) #self.api.run_task(self.myfunc) #self.myfunc() #print "reached down:: client" self.api.run_task(call, ["cheese"]) #call(["cheese"]) def handle_discover_request(self, rx_record): path = [] for v in rx_record.message.findOption(URI_PATH_VALUE): path.append(v.value) pload = str(rx_record.message.payload).split("&") app_ip = str(pload[0]).split("=")[1] app_port = str(pload[1]).split("=")[1] if len(path) == 2: filtered_resources = self.discover_resource(app_ip, app_port, endPoint = path[1], objectID = None, objectInstID = None, resID = None) elif len(path) == 3: filtered_resources = self.discover_resource(app_ip, app_port, endPoint = path[1], objectID = path[2], objectInstID = None, resID = None) elif len(path) == 4: filtered_resources = self.discover_resource(app_ip, app_port, endPoint = path[1], objectID = path[2], objectInstID = path[3], resID = None) elif len(path) == 5: filtered_resources = self.discover_resource(app_ip, app_port, endPoint = path[1], objectID = path[2], objectInstID = path[3], resID = path[4]) return filtered_resources def discover_resource(self, app_ip, app_port, endPoint, objectID=None, objectInstID=None, resID=None): total_result = [] if objectID is None: answer = local_lwm2m_client.return_maintain_objects() for ans in answer: total_result.append({"app_ip" : app_ip, "app_port" : app_port, "endPointName" : endPoint, "objectID" : ans["objectID"], "objectInstID" : ans["objectInstID"], "pmax" : ans["pmax"], "pmin" : ans["pmin"]}) elif objectID != None and objectInstID != None and resID == None: answer = local_lwm2m_client.return_resources(objectID, objectInstID) for ans in answer: total_result.append({"app_ip" : app_ip, "app_port" : app_port, "endPointName" : endPoint, "objectID" : objectID, "objectInstID" : objectInstID, "pmax" : ans["pmax"], "pmin" : ans["pmin"], "resID" : ans["resID"], "resValue" : ans["resValue"]}) return total_result def observe_resource(self, rx_record, query): first_notification = None uri_port = rx_record.message.findOption(URI_PORT_VALUE).value uri_host = rx_record.message.findOption(URI_HOST_VALUE).value s_list = {} sender_details = {} path = [] for v in rx_record.message.findOption(URI_PATH_VALUE): path.append(v.value) pload = str(rx_record.message.payload).split("&") app_ip = str(pload[0]).split("=")[1] app_port = str(pload[1]).split("=")[1] #check if the path includes the object instance id or just the object id if len(path) == 3: #observation to object id path.append("None") path.append("None") elif len(path) == 4: #observation to resource id path.append("None") objectID = path[2] objectInstID = path[3] resID = path[4] s_list = {"app_ip" : app_ip, "app_port" : app_port, "objectID" : objectID, "objectInstID" : objectInstID, "resID" : resID} sender_details = {"transaction_id" : rx_record.transaction_id, "uri_host" : uri_host, "uri_port" : uri_port} #store the new observation if len(self.subscription_list) == 0: self.subscription_list.append(s_list) self.sender_info.append(sender_details) first_notification = local_lwm2m_client.observe_res(self.subscription_list, s_list, self.sender_info) else: for test in self.subscription_list: if test != s_list: self.subscription_list.append(s_list) self.sender_info.append(sender_details) first_notification = local_lwm2m_client.observe_res(self.subscription_list, s_list, self.sender_info) return first_notification def cancel_observe_resource(self, rx_record): counter = 0 path = [] for v in rx_record.message.findOption(URI_PATH_VALUE): path.append(v.value) pload = str(rx_record.message.payload).split("&") app_ip = str(pload[0]).split("=")[1] app_port = str(pload[1]).split("=")[1] if len(path) == 3: path.append("None") path.append("None") elif len(path) == 4: path.append("None") objectID = path[2] objectInstID = path[3] resID = path[4] try: for val in self.subscription_list: if val["app_ip"] == app_ip and val["app_port"] == app_port and val["objectID"] == objectID: self.subscription_list.remove({"app_ip" : app_ip, "app_port" : app_port, "objectID" : objectID, "objectInstID" : objectInstID, "resID" : resID}) self.sender_info.pop(counter) counter += 1 local_lwm2m_client.cancel_obs_res(objectID, self.subscription_list) except: print "The Object ID %s is not present" %objectID return "Unsubscribed Successful" def write_attributes(self, upath, pmax, pmin): splitPath = str(upath).split("/") create_str = [] create_str.append({"attCode" : "pmax", "attValue" : pmax}) create_str.append({"attCode" : "pmin", "attValue" : pmin}) return local_lwm2m_client.write_attr(create_str, splitPath[2])
except ValueError: continue if (headers.get('x-user-agent', None) == 'redsonic'): location = headers.get('location', None) if location is not None and location not in CLIENTS: print("Found WeMo at {0}".format(location)) CLIENTS[location] = headers #gevent.spawn(discovered.send, self, address=address, # headers=headers) server = DatagramServer(get_ip_address() + ':54321', _response_received) request = '\r\n'.join( ("M-SEARCH * HTTP/1.1", "HOST:{}:{}", "ST:upnp:rootdevice", "MX:2", 'MAN:"ssdp:discover"', "", "")).format(MCAST_IP, MCAST_PORT) with gevent.Timeout(2, KeyboardInterrupt): while True: try: server.set_spawn(1) server.init_socket() server.socket.setsockopt(_socket.IPPROTO_IP, _socket.IP_MULTICAST_TTL, MCAST_TTL) server.start() server.sendto(request.encode(), (MCAST_IP, MCAST_PORT)) gevent.sleep(2) except KeyboardInterrupt: print('Scan Complete') break
class UDPTransport: """ Node communication using the UDP protocol. """ def __init__( self, host, port, socket=None, protocol=None, throttle_policy=DummyPolicy()): self.protocol = protocol if socket is not None: self.server = DatagramServer(socket, handle=self.receive) else: self.server = DatagramServer((host, port), handle=self.receive) self.host = self.server.server_host self.port = self.server.server_port self.throttle_policy = throttle_policy def receive(self, data, host_port): # pylint: disable=unused-argument try: self.protocol.receive(data) except InvalidProtocolMessage as e: log.warning("Can't decode: {} (data={}, len={})".format(str(e), data, len(data))) return except RaidenShuttingDown: # For a clean shutdown return # enable debugging using the DummyNetwork callbacks DummyTransport.track_recv(self.protocol.raiden, host_port, data) def send(self, sender, host_port, bytes_): """ Send `bytes_` to `host_port`. Args: sender (address): The address of the running node. host_port (Tuple[(str, int)]): Tuple with the host name and port number. bytes_ (bytes): The bytes that are going to be sent through the wire. """ sleep_timeout = self.throttle_policy.consume(1) # Don't sleep if timeout is zero, otherwise a context-switch is done # and the message is delayed, increasing it's latency if sleep_timeout: gevent.sleep(sleep_timeout) if not hasattr(self.server, 'socket'): raise RuntimeError('trying to send a message on a closed server') self.server.sendto(bytes_, host_port) # enable debugging using the DummyNetwork callbacks DummyTransport.network.track_send(sender, host_port, bytes_) def stop(self): self.server.stop() # Calling `.close()` on a gevent socket doesn't actually close the underlying os socket # so we do that ourselves here. # See: https://github.com/gevent/gevent/blob/master/src/gevent/_socket2.py#L208 # and: https://groups.google.com/forum/#!msg/gevent/Ro8lRra3nH0/ZENgEXrr6M0J try: self.server._socket.close() except socket.error: pass def stop_accepting(self): self.server.stop_accepting() def start(self): assert not self.server.started # server.stop() clears the handle, since this may be a restart the # handle must always be set self.server.set_handle(self.receive) self.server.start()
class BigBrother(control.Control): def __init__(self): self.Comps = {} self.Robots = {} self.Nodes = {} self.host = self._etc["ip"] self.port = self._etc["port"] self.broadcast = self._etc["broadcast_port"] def __Run__(self): self.L_info("starting BigBrother") self.bcast = DatagramServer(('', self.broadcast), handle=self.receive) self.bcast.start() self.start_worker(self.cleaner, ) def __Close__(self): self.worker_run = False def cleaner(self): while self.worker_run: delete = [] for comp in self.Comps: ser = self.Control_Service(comp) control = Proxy(ser) if not control(): delete.append(comp) for c in delete: del (self.Comps[c]) self.L_info("{} disconnected".format(c)) Robots = list(self.Robots) for R in Robots: if len(self.Components(R)) == 0: self.Unregister_Robot(R) self.L_info("{} Unregistered ".format(R)) Nodes = list(self.Nodes) delete = [] for N in Nodes: ser = self.Nodes[N][1][1] control = Proxy(ser) if not control(): delete.append(N) for c in delete: del (self.Nodes[N]) self.L_info("{} Node disconnected".format(c)) time.sleep(2) def receive(self, data, address): if data.decode() == "hi BigBrother": send = "{}:{}".format(self.host, self.port) self.bcast.sendto(send.encode(), address) else: self.bcast.sendto("0.0.0.0:0".encode(), address) def Register_Node(self, node, uri): if node not in self.Nodes: self.Nodes[node] = uri self.L_info("Node {} Registered at {}".format(node, uri)) return True self.L_info("Node {} is on line at {}".format(node, uri)) return False def Unregister_Node(self, node): if node in self.Nodes: del (self.Nodes[node]) return True return False def Get_Node(self, node): if node in self.Nodes: return self.Nodes[node][0][1] else: return "0.0.0.0:0" def Unregister_Robot(self, robot): if robot in self.Robots: del (self.Robots[robot]) def Register_Robot(self, robot, default): if robot not in self.Robots: self.Robots[robot] = {} self.Robots[robot]["default"] = default self.L_info("Robot {} Registered".format(robot)) return True else: return False def Get_Robots(self): return self.Robots def Get_Robot(self, robot): default = {} if robot in self.Robots: default = self.Robots[robot] return default def Register_Comp(self, name, comp): general = comp["GENERAL"] del (comp["GENERAL"]) robot = general["robot"] if robot not in self.Robots: self.Register_Robot(robot, general) if name not in self.Comps: self.Comps[name] = comp self.L_info("Component {} Registered".format(name)) return True else: return False def Get_Comp(self, name): try: return self.Comps[name] except: return {} def Components(self, robot): return [x for x in self.Comps if x.find(robot + "/") == 0] def Services(self, robot): comps = self.Components(robot) ser = [ x + "/" + k[0] for x in comps for k in self.Comps[x]["INTERFACES"] if k[0] != "Control_Interface" ] return ser def Get_Interface_Uri(self, interface): robot, comp, interface = interface.split("/") comps = self.Components(robot) ser = [ k[1] for x in comps for k in self.Comps[x]["INTERFACES"] if k[0] == interface ] if len(ser) == 1: return ser[0] else: return "0.0.0.0:0" def Control_Service(self, comp): if comp in self.Comps: interfaces = list(self.Comps[comp]["INTERFACES"]) ser = [k[1] for k in interfaces if k[0] == "Control_Interface"] if len(ser) == 1: return ser[0] else: return None def Topics(self, robot): comps = self.Components(robot) top = [ "{}/{}".format(x, k) for x in comps for k in self.Comps[x]["PUB"] ] return top def Events(self, robot): comps = self.Components(robot) events = [ "{}/{}".format(x, k) for x in comps for k in self.Comps[x]["EVENTS"] ] #events=[k for x in comps for k in self.Comps[x]["EVENTS"]] return events def Sub_Topics(self, robot): comps = self.Components(robot) top = [k for x in comps for k in self.Comps[x]["SUB"]] return top def Broker(self, robot): if robot in self.Robots: return self.Robots[robot]["default"]["MQTT_uri"] else: return "0.0.0.0:0"
class UDPTransport: UDP_MAX_MESSAGE_SIZE = 1200 def __init__(self, discovery, udpsocket, throttle_policy, config): # these values are initialized by the start method self.queueids_to_queues: typing.Dict self.raiden: RaidenService self.discovery = discovery self.config = config self.retry_interval = config['retry_interval'] self.retries_before_backoff = config['retries_before_backoff'] self.nat_keepalive_retries = config['nat_keepalive_retries'] self.nat_keepalive_timeout = config['nat_keepalive_timeout'] self.nat_invitation_timeout = config['nat_invitation_timeout'] self.event_stop = Event() self.greenlets = list() self.addresses_events = dict() self.messageids_to_asyncresults = dict() # Maps the addresses to a dict with the latest nonce (using a dict # because python integers are immutable) self.nodeaddresses_to_nonces = dict() cache = cachetools.TTLCache( maxsize=50, ttl=CACHE_TTL, ) cache_wrapper = cachetools.cached(cache=cache) self.get_host_port = cache_wrapper(discovery.get) self.throttle_policy = throttle_policy self.server = DatagramServer(udpsocket, handle=self._receive) def start( self, raiden: RaidenService, queueids_to_queues: typing.List[SendMessageEvent], ): self.raiden = raiden self.queueids_to_queues = dict() # server.stop() clears the handle. Since this may be a restart the # handle must always be set self.server.set_handle(self._receive) for (recipient, queue_name), queue in queueids_to_queues.items(): encoded_queue = list() for sendevent in queue: message = message_from_sendevent(sendevent, raiden.address) raiden.sign(message) encoded = message.encode() encoded_queue.append((encoded, sendevent.message_identifier)) self.init_queue_for(recipient, queue_name, encoded_queue) self.server.start() def stop_and_wait(self): # Stop handling incoming packets, but don't close the socket. The # socket can only be safely closed after all outgoing tasks are stopped self.server.stop_accepting() # Stop processing the outgoing queues self.event_stop.set() gevent.wait(self.greenlets) # All outgoing tasks are stopped. Now it's safe to close the socket. At # this point there might be some incoming message being processed, # keeping the socket open is not useful for these. self.server.stop() # Calling `.close()` on a gevent socket doesn't actually close the underlying os socket # so we do that ourselves here. # See: https://github.com/gevent/gevent/blob/master/src/gevent/_socket2.py#L208 # and: https://groups.google.com/forum/#!msg/gevent/Ro8lRra3nH0/ZENgEXrr6M0J try: self.server._socket.close() # pylint: disable=protected-access except socket.error: pass # Set all the pending results to False for async_result in self.messageids_to_asyncresults.values(): async_result.set(False) def get_health_events(self, recipient): """ Starts a healthcheck task for `recipient` and returns a HealthEvents with locks to react on its current state. """ if recipient not in self.addresses_events: self.start_health_check(recipient) return self.addresses_events[recipient] def start_health_check(self, recipient): """ Starts a task for healthchecking `recipient` if there is not one yet. """ if recipient not in self.addresses_events: ping_nonce = self.nodeaddresses_to_nonces.setdefault( recipient, {'nonce': 0}, # HACK: Allows the task to mutate the object ) events = healthcheck.HealthEvents( event_healthy=Event(), event_unhealthy=Event(), ) self.addresses_events[recipient] = events greenlet_healthcheck = gevent.spawn( healthcheck.healthcheck, self, recipient, self.event_stop, events.event_healthy, events.event_unhealthy, self.nat_keepalive_retries, self.nat_keepalive_timeout, self.nat_invitation_timeout, ping_nonce, ) greenlet_healthcheck.name = f'Healthcheck for {pex(recipient)}' self.greenlets.append(greenlet_healthcheck) def init_queue_for( self, recipient: typing.Address, queue_name: bytes, items: typing.List[QueueItem_T], ) -> Queue_T: """ Create the queue identified by the pair `(recipient, queue_name)` and initialize it with `items`. """ queueid = (recipient, queue_name) queue = self.queueids_to_queues.get(queueid) assert queue is None queue = NotifyingQueue(items=items) self.queueids_to_queues[queueid] = queue events = self.get_health_events(recipient) greenlet_queue = gevent.spawn( single_queue_send, self, recipient, queue, self.event_stop, events.event_healthy, events.event_unhealthy, self.retries_before_backoff, self.retry_interval, self.retry_interval * 10, ) if queue_name == b'global': greenlet_queue.name = f'Queue for {pex(recipient)} - global' else: greenlet_queue.name = f'Queue for {pex(recipient)} - {pex(queue_name)}' self.greenlets.append(greenlet_queue) log.debug( 'new queue created for', node=pex(self.raiden.address), token=pex(queue_name), to=pex(recipient), ) return queue def get_queue_for( self, recipient: typing.Address, queue_name: bytes, ) -> Queue_T: """ Return the queue identified by the pair `(recipient, queue_name)`. If the queue doesn't exist it will be instantiated. """ queueid = (recipient, queue_name) queue = self.queueids_to_queues.get(queueid) if queue is None: items = () queue = self.init_queue_for(recipient, queue_name, items) return queue def send_async( self, recipient: typing.Address, queue_name: bytes, message: 'Message', ): """ Send a new ordered message to recipient. Messages that use the same `queue_name` are ordered. """ if not is_binary_address(recipient): raise ValueError('Invalid address {}'.format(pex(recipient))) # These are not protocol messages, but transport specific messages if isinstance(message, (Delivered, Ping, Pong)): raise ValueError('Do not use send for {} messages'.format(message.__class__.__name__)) messagedata = message.encode() if len(messagedata) > self.UDP_MAX_MESSAGE_SIZE: raise ValueError( 'message size exceeds the maximum {}'.format(self.UDP_MAX_MESSAGE_SIZE), ) # message identifiers must be unique message_id = message.message_identifier # ignore duplicates if message_id not in self.messageids_to_asyncresults: self.messageids_to_asyncresults[message_id] = AsyncResult() queue = self.get_queue_for(recipient, queue_name) queue.put((messagedata, message_id)) log.debug( 'MESSAGE QUEUED', node=pex(self.raiden.address), queue_name=queue_name, to=pex(recipient), message=message, ) def maybe_send(self, recipient: typing.Address, message: Message): """ Send message to recipient if the transport is running. """ if not is_binary_address(recipient): raise InvalidAddress('Invalid address {}'.format(pex(recipient))) messagedata = message.encode() host_port = self.get_host_port(recipient) self.maybe_sendraw(host_port, messagedata) def maybe_sendraw_with_result( self, recipient: typing.Address, messagedata: bytes, message_id: typing.MessageID, ) -> AsyncResult: """ Send message to recipient if the transport is running. Returns: An AsyncResult that will be set once the message is delivered. As long as the message has not been acknowledged with a Delivered message the function will return the same AsyncResult. """ async_result = self.messageids_to_asyncresults.get(message_id) if async_result is None: async_result = AsyncResult() self.messageids_to_asyncresults[message_id] = async_result host_port = self.get_host_port(recipient) self.maybe_sendraw(host_port, messagedata) return async_result def maybe_sendraw(self, host_port: typing.Tuple[int, int], messagedata: bytes): """ Send message to recipient if the transport is running. """ # Don't sleep if timeout is zero, otherwise a context-switch is done # and the message is delayed, increasing it's latency sleep_timeout = self.throttle_policy.consume(1) if sleep_timeout: gevent.sleep(sleep_timeout) # Check the udp socket is still available before trying to send the # message. There must be *no context-switches after this test*. if hasattr(self.server, 'socket'): self.server.sendto( messagedata, host_port, ) def _receive(self, data, host_port): # pylint: disable=unused-argument try: self.receive(data) except RaidenShuttingDown: # For a clean shutdown return def receive(self, messagedata: bytes): """ Handle an UDP packet. """ # pylint: disable=unidiomatic-typecheck if len(messagedata) > self.UDP_MAX_MESSAGE_SIZE: log.error( 'INVALID MESSAGE: Packet larger than maximum size', node=pex(self.raiden.address), message=hexlify(messagedata), length=len(messagedata), ) return message = decode(messagedata) if type(message) == Pong: self.receive_pong(message) elif type(message) == Ping: self.receive_ping(message) elif type(message) == Delivered: self.receive_delivered(message) elif message is not None: self.receive_message(message) else: log.error( 'INVALID MESSAGE: Unknown cmdid', node=pex(self.raiden.address), message=hexlify(messagedata), ) def receive_message(self, message: Message): """ Handle a Raiden protocol message. The protocol requires durability of the messages. The UDP transport relies on the node's WAL for durability. The message will be converted to a state change, saved to the WAL, and *processed* before the durability is confirmed, which is a stronger property than what is required of any transport. """ # pylint: disable=unidiomatic-typecheck if on_message(self.raiden, message): # Sending Delivered after the message is decoded and *processed* # gives a stronger guarantee than what is required from a # transport. # # Alternatives are, from weakest to strongest options: # - Just save it on disk and asynchronously process the messages # - Decode it, save to the WAL, and asynchronously process the # state change # - Decode it, save to the WAL, and process it (the current # implementation) delivered_message = Delivered(message.message_identifier) self.raiden.sign(delivered_message) self.maybe_send( message.sender, delivered_message, ) def receive_delivered(self, delivered: Delivered): """ Handle a Delivered message. The Delivered message is how the UDP transport guarantees persistence by the partner node. The message itself is not part of the raiden protocol, but it's required by this transport to provide the required properties. """ processed = ReceiveDelivered(delivered.delivered_message_identifier) self.raiden.handle_state_change(processed) message_id = delivered.delivered_message_identifier async_result = self.raiden.transport.messageids_to_asyncresults.get(message_id) # clear the async result, otherwise we have a memory leak if async_result is not None: del self.messageids_to_asyncresults[message_id] async_result.set() # Pings and Pongs are used to check the health status of another node. They # are /not/ part of the raiden protocol, only part of the UDP transport, # therefore these messages are not forwarded to the message handler. def receive_ping(self, ping: Ping): """ Handle a Ping message by answering with a Pong. """ log.debug( 'PING RECEIVED', node=pex(self.raiden.address), message_id=ping.nonce, message=ping, sender=pex(ping.sender), ) pong = Pong(ping.nonce) self.raiden.sign(pong) try: self.maybe_send(ping.sender, pong) except (InvalidAddress, UnknownAddress) as e: log.debug("Couldn't send the `Delivered` message", e=e) def receive_pong(self, pong: Pong): """ Handles a Pong message. """ message_id = ('ping', pong.nonce, pong.sender) async_result = self.messageids_to_asyncresults.get(message_id) if async_result is not None: log.debug( 'PONG RECEIVED', node=pex(self.raiden.address), sender=pex(pong.sender), message_id=pong.nonce, ) async_result.set(True) def get_ping(self, nonce: int) -> Ping: """ Returns a signed Ping message. Note: Ping messages don't have an enforced ordering, so a Ping message with a higher nonce may be acknowledged first. """ message = Ping(nonce) self.raiden.sign(message) message_data = message.encode() return message_data def set_node_network_state(self, node_address: typing.Address, node_state): state_change = ActionChangeNodeNetworkState(node_address, node_state) self.raiden.handle_state_change(state_change)
class CoapServer(LoggerMixin): block_size_exponent = 10 """Server handling CoAP requests and generates RequestIndications.""" DEFAULT_CONTENT_TYPE = "application/json" __cached_addresses = {} def __init__(self, request_handler, connector, address="0.0.0.0", server_port=5682, ssl_args=None, *args, **kw): """Initializes the CoAP server. :param request_handler: Handler called with generated RequestIndications :param connector: Base uri used to reach this server (coap://IP:PORT or coaps://IP:PORT) :param address: IP listening to :param server_port: Port listening to :param ssl_args: contains two keywords: "keyfile" and "certfile" containing paths to respective files, None for regular CoAP """ self.server_port = server_port self.connector = connector self.request_handler = request_handler self.subs = {} self.address = address if address == "::": self.__addresses = self._get_addresses(AF_INET6) | \ self._get_addresses(AF_INET) self._resolve_host = self._resolve_host_ipv6 elif address in ("", "0.0.0.0"): self.__addresses = self._get_addresses(AF_INET) else: self.__addresses = get_iterable(address) do_patch() self.logger.debug("Starting Coap Server on %s:%s" % (self.address, self.server_port)) if ssl_args: self.server = DtlsServer((address, self.server_port), self.handle_request, **ssl_args) else: self.server = DatagramServer((address, self.server_port), self.handle_request) self.block1_pending_payload = {} self.block2_pending_payload = {} self.actual_requests = [] self.req_queue = Queue.Queue() start_new_thread(self.request_runner, ()) def _add_headers(self, msg): """Reads the set of UriQuery options and extract additional headers in a dict. Used mainly for Store and Forward functionnalities, recognized headers are formated as: X-etsi-<headername> :param msg: CoAP message containing UriQuery options :return: Dictionnary containing headers """ params = MultiDict() opts = msg.findOption(options.UriQuery) self.logger.debug("Message: %s", msg) self.logger.debug("List of UriQuery options: %s", opts) if not opts: return params for opt in opts: try: opt_split = opt.value.split("=") opt_name = opt_split[0] opt_value = opt_split[1] try: opt_type = opt_name.split('-')[2] except: opt_type = opt_name params.add(opt_type, opt_value) except: self.logger.debug("Unknown header : %s" % opt.value) return params # TODO: map to new scheme def _handle_observe(self, path, msg, rx_record, obs): """Handles the presence of the Observe Option. Creates or deletes a subscription to a resource depending on the Observe value. :param path: Resource path :param msg: CoAP message :param obs: Observe option Object """ params = self._add_headers(msg) username = None accept = self._get_accept(msg) if obs.value == 0: sub_path = path + "/subscriptions" sub_payload = ('{"subscription": {"contact": "coap://%s:%s"}}' % (self.remote, )) request = GenericRequest(RequestMethod.create, sub_path, sub_payload, self.DEFAULT_CONTENT_TYPE, id=rx_record.message.token, username=username, params=params, accept=accept, connector=self.connector) p = self.request_handler(request) def __handle_result(result): if not (self.remote in self.subs): self.subs[self.remote] = {} self.subs[self.remote][path] = result.resourceURI p.then(__handle_result) else: request = GenericRequest(RequestMethod.delete, self.subs[self.remote][path], id=rx_record.message.token, username=username, params=params, accept=accept, connector=self.connector) self.request_handler(request) del self.subs[self.remote][path] if self.subs[self.remote] == {}: del self.subs[self.remote] def _generic_map_put(self, path, msg, rx_record): """Creates an generic update Request Object from a path and a CoAP message. :param path: Resource path :param msg: CoAP message :return: Request Object """ params = self._add_headers(msg) username = None accept = self._get_accept(msg) return GenericRequest(RequestMethod.update, path, msg.payload, msg.content_type, id=rx_record.message.token, username=username, params=params, accept=accept, connector=self.connector) def _generic_map_post(self, path, msg, rx_record): """Creates an generic create Request Object from a path and a CoAP message. :param path: Resource path :param msg: CoAP message :return: Request Object """ method = RequestMethod.create # TODO: does this need to be done more generically? if msg.payload.startswith("m2m:operation="): _, _, rest = msg.payload.partition("=") if rest: method_value = rest[0] msg.payload = rest[1:] if int(method_value) == 5: method = RequestMethod.notify params = self._add_headers(msg) username = None accept = self._get_accept(msg) return GenericRequest(method, path, msg.payload, msg.content_type, id=rx_record.message.token, username=username, params=params, accept=accept, connector=self.connector) def _generic_map_get(self, path, msg, rx_record): """Creates an generic retrieve Request Object from a path and a CoAP message Checks for the Observe option and handles it if needed. :param path: Resource path :param msg: CoAP message :return: Request Object """ obs = msg.findOption(options.Observe) if obs: return self._handle_observe(path, msg, rx_record, obs) params = self._add_headers(msg) username = None accept = self._get_accept(msg) return GenericRequest(RequestMethod.retrieve, path, id=rx_record.message.token, username=username, params=params, accept=accept, connector=self.connector) def _generic_map_delete(self, path, msg, rx_record): """Creates an generic delete Request Object from a path and a CoAP message :param path: Resource path :param msg: CoAP message :return: Request Object """ params = self._add_headers(msg) username = None accept = self._get_accept(msg) return GenericRequest(RequestMethod.delete, path, id=rx_record.message.token, username=username, params=params, accept=accept, connector=self.connector) def _get_addresses(self, family): try: return self.__cached_addresses[family] except KeyError: from netifaces import interfaces, ifaddresses addresses = self.__cached_addresses[family] = set() for interface in interfaces(): try: ifdata = ifaddresses(interface)[family] ifaddrs = map(lambda x: x.split("%")[0], pluck("addr", ifdata)) addresses.update(ifaddrs) except KeyError: pass return addresses def _getaddrinfo(self, host, family): self.logger.debug("Resolving %s", host) try: info = getaddrinfo(host, 0, family, SOCK_DGRAM) return set(map(itemgetter(0), map(itemgetter(-1), info))) except gaierror as e: self.logger.error("Failed to resolve %s: %s", host, e) return set() def _resolve_host(self, host): if is_ipv4(host): return {host} return self._getaddrinfo(host, AF_INET) def _resolve_host_ipv6(self, host): self.logger.debug("Resolving: %s", host) if is_ipv6(host): return {host} # TODO: kca: optimize return self._getaddrinfo(host, AF_INET) | \ self._getaddrinfo(host, AF_INET6) def _get_real_path(self, msg): """Returns the URI needed for further processing. This is either a relative path if the request is to be processed internally or a full URI if the request is retargeted :param msg: CoAP message :return: Uri string """ path = msg.uri parsed = urlparse(path) # prefer the HOST header try: uri_host = msg.findOption(options.UriHost).value uri_port = msg.findOption(options.UriPort).value if ':' in uri_host: netloc = '[' + uri_host + ']:' + str(uri_port) else: netloc = uri_host + ':' + str(uri_port) except AttributeError: netloc = parsed.netloc if not netloc: # no absolute URI and no host header -> not retargeted return path if netloc[0] == "[": target_host, _, target_port = netloc[1:].partition(']') if target_port: target_port = target_port[1:] else: target_host, _, target_port = netloc.partition(":") if not target_port: target_port = "5684" if target_port == self.server_port and self._resolve_host( target_host) & self.__addresses: # port and host match -> not retargeted return urlunparse(ParseResult("", "", *parsed[2:])) # request is retargeted # # construct full URI if needed if not parsed.netloc: path = netloc + path if not parsed.scheme: path = "coap://" + path elif not parsed.scheme: path = "coap://" + path return path def handle_block1(self, block1, rx_record, remote, msg): """Handles the presence of the Block1 Option. Block1 informs the server that the payload of a client request is fragmented. This function aggregates the various fragments, and queries the client for the next fragment. :param block1: Block1 option Object :param rx_record: CoAP message transfer information :param remote: tuple(Client IP, Client PORT) :param msg: CoAP message :return: Completed payload or None if the payload is not complete """ try: self.block1_pending_payload[remote] += msg.payload except (KeyError, TypeError): if block1.block_number != 0: # We discard fragments with block number > 0 if we haven't # received the first one yet return None self.block1_pending_payload[remote] = msg.payload if block1.more: msg = connection.Message(connection.Message.ACK, code=constants.CONTINUE) # block1.block_number += 1 msg.addOption(block1) self.server.sendto( msg._pack(rx_record.transaction_id, rx_record.message.token), remote) return None payload = self.block1_pending_payload[remote] self.block1_pending_payload[remote] = None return payload def handle_block2(self, block2, remote, mesg): """Handles the presence of the Block2 Option. Block2 informs the server that the payload of a server response has to be fragmented. This function returns a specific fragment of a message, as requested by a client. :param block2: Block2 option Object :param remote: tuple(Client IP, Client PORT) :param mesg: CoAP message :return: CoAP message containing a the requested fragment """ msg = copy(mesg) length = 2**block2.size_exponent start = length * block2.block_number end = start + length if end <= len(msg.payload): more = True else: more = False end = len(msg.payload) # This means the last block has to be requested before a new request # with block is created self.block2_pending_payload[remote] = None msg.payload = msg.payload[start:end] new_block2 = options.Block2(block_number=block2.block_number, more=more, size_exponent=block2.size_exponent) msg.replaceOption(new_block2) return msg def _get_accept(self, msg): """Retrives the Accept option value from a CoAP message. :param msg: CoAP message :return: String representation of Accept option """ opt = msg.findOption(options.Accept) if opt: accept = opt.value_as_string else: accept = msg.content_type or None return accept def _generate_error(self, e): """Generates a CoAP RST message from the exception e. :param e: Exception :return: CoAP Message """ code = constants.INTERNAL_SERVER_ERROR data = str(e) return connection.Message(connection.Message.RST, code=code, payload=data) def process(self, rx_record, remote): """Processes a CoAP message using its transfer information. Triggers the different options handlers if needed. Performs a mapping between RESTful methods and ETSI RequestIndications. :param rx_record: CoAP message transfer information :param remote: tuple(Client IP, Client PORT) """ self.logger.debug("process: %s (%s)", rx_record, remote) msg = rx_record.message block1 = rx_record.message.findOption(options.Block1) if block1: block_payload = self.handle_block1(block1, rx_record, remote, msg) if not msg or not block_payload: return msg.payload = block_payload block2 = rx_record.message.findOption(options.Block2) try: self.block2_pending_payload[remote] except KeyError: self.block2_pending_payload[remote] = None finally: block2_pending_payload = self.block2_pending_payload[remote] if block2 and block2_pending_payload: msg = self.handle_block2(block2, remote, block2_pending_payload) return self.server.sendto( msg._pack(rx_record.transaction_id, rx_record.message.token), remote) generic_mappers = { constants.PUT: self._generic_map_put, constants.POST: self._generic_map_post, constants.GET: self._generic_map_get, constants.DELETE: self._generic_map_delete } path = self._get_real_path(msg) mapping_function = generic_mappers[msg.code] generic_req = mapping_function(path, msg, rx_record) def handle_generic_result(result): """Handles a successful response from the Request handler. Generates CoAP Response from a response after converting it to a generic Response Object. Triggers Block2 fragmentation if needed. :param result: response Object """ # result = res self.logger.debug("handle_generic_result(%s)", vars(result)) if result.statusCode == 202: response_msg = connection.Message( connection.Message.ACK, code=http2coap_codes[result.statusCode], payload="Request Accepted") else: pt = rx_record.message.code if pt == constants.GET: accept = self._get_accept(rx_record.message) content_type, data = serialize_generic_result( result, accept=accept) if data is None: response_msg = connection.Message( connection.Message.ACK, code=http2coap_codes[result.statusCode]) else: response_msg = connection.Message( connection.Message.ACK, code=http2coap_codes[result.statusCode], content_type=content_type, payload=data) elif pt == constants.POST: accept = self._get_accept(rx_record.message) content_type, data = serialize_generic_result( result, accept=accept) if data is None: response_msg = connection.Message( connection.Message.ACK, code=http2coap_codes[result.statusCode]) else: response_msg = connection.Message( connection.Message.ACK, code=http2coap_codes[result.statusCode], # Set location option # FIXME: needs to be handled somewhere else # location=(generic_req.path + '/' + # generic_req.params.get('nm', "")), location=result.location, content_type=content_type, payload=data) elif pt == constants.PUT: response_msg = connection.Message( connection.Message.ACK, code=http2coap_codes[result.statusCode]) elif pt == constants.DELETE: response_msg = connection.Message( connection.Message.ACK, code=http2coap_codes[result.statusCode]) else: raise NotImplementedError("Coap result mapping for %s", pt) self.logger.debug("Sending CoAP reply: %s", response_msg) block2 = rx_record.message.findOption(options.Block2) if block2: self.block2_pending_payload[remote] = response_msg response_msg = self.handle_block2(block2, remote, response_msg) elif (response_msg.payload and len(response_msg.payload) > 2**self.block_size_exponent): nr_blocks, r = divmod(len(response_msg.payload), 2**self.block_size_exponent) if r > 0: nr_blocks += 1 self.logger.debug( "Message is too long, sending first " "fragment (of %s)", nr_blocks) block2 = options.Block2(block_number=0, more=True, size_exponent=self.block_size_exponent) self.block2_pending_payload[remote] = response_msg response_msg = self.handle_block2(block2, remote, response_msg) if block1: response_msg.addOption(block1) self.server.sendto( response_msg._pack(rx_record.transaction_id, rx_record.message.token), remote) self.actual_requests.remove( (rx_record.transaction_id, rx_record.message.token)) def handle_error(x): """Handles a failed response from the RequestIndication handler. Generates CoAP Response from ETSI ResponseConfirmation Object. :param x: ETSI ResponseConfirmation Object """ self.logger.debug("handle_error(%s)", vars(result)) try: code = constants.http2coap_codes[x.statusCode] except AttributeError: code = constants.INTERNAL_SERVER_ERROR except KeyError: self.logger.warning( "Failed to map result code to coap: " "%s (%s)", x.statusCode, x) code = constants.INTERNAL_SERVER_ERROR accept = self._get_accept(rx_record.message) content_type, data = encode_error(x, accept=accept) msg = connection.Message(connection.Message.RST, code=code, payload=data, content_type=content_type) self.logger.debug("Sending CoAP error reply: %s", msg) self.server.sendto( msg._pack(rx_record.transaction_id, rx_record.message.token), remote) self.actual_requests.remove( (rx_record.transaction_id, rx_record.message.token)) try: result_promise = self.request_handler(generic_req) result = result_promise.get() if isinstance(result, ErrorResponse): return handle_error(result) except ErrorResponse as result: return handle_error(result) except Exception as exc: self.logger.exception("Caught exception while handling request") return handle_error(exc) return handle_generic_result(result) @staticmethod def get_id_and_token(packed): # rst: copy of the begin of the Connection.decode function before # the potential UnrecognizedOptionError vtoc = ord(packed[0]) if 1 != 0x03 & (vtoc >> 6): raise Exception() # transaction_type = 0x03 & (vtoc >> 4) token_len = (vtoc & 0x0F) (_, transaction_id) = struct.unpack('!BH', packed[1:4]) token = packed[4:4 + token_len] return transaction_id, token def handle_request(self, message, remote): req = self.get_id_and_token(message) if req in self.actual_requests: return self.actual_requests.append(req) self.req_queue.put((message, remote)) def _handle_request(self, message, remote): """Entry points of requests, converts a buffer into CoAP message transfer information :param message: Buffer containing a CoAP message :param remote: tuple(Client IP, Client Port) """ if message and remote: self.remote = remote # TODO(rst): put this later into constants, when coap lib is updated # TODO: newer versions CONSTANTS_EMPTY = 0 CONSTANTS_NOT_IMPLEMENTED = 161 # 5.01 try: rx_record = connection.ReceptionRecord(None, message, remote) if (rx_record.message.code == CONSTANTS_EMPTY and rx_record.message.transaction_type == connection.Message.RST): return except UnrecognizedOptionError: code = CONSTANTS_NOT_IMPLEMENTED msg = connection.Message(connection.Message.RST, code=code) self.server.sendto(msg._pack(*self.get_id_and_token(message)), remote) raise UnrecognizedOptionError else: return self.process(rx_record, remote) # start_new_thread(self.process, (rx_record, remote)) # self.req_queue.put((rx_record, remote)) def request_runner(self): while True: message, remote = self.req_queue.get() self._handle_request(message, remote) # start_new_thread(self._handle_request, (message, remote)) def start(self): """Starts the Server""" self.logger.debug("Coap Server launched") self.server.start() def stop(self): """Stops the Server gracefully""" self.server.stop()
class Bjdns2: def __init__(self, listen, bjdns2_url): self.cache = Cache() ip, port_str = listen.split(':') self.server = DatagramServer((ip, int(port_str)), self.handle) self.session = requests.Session() url = urlparse(bjdns2_url).netloc self.bjdns2_host = url[:url.rfind(':')] # self.bjdns2_ip = bjdns2_ip def query_by_https(self, host, cli_ip): url_template = bjdns2_url + '/?dn={}&ip={}' # if host == self.bjdns2_host: # return self.bjdns2_ip, 3600 # else: if is_private_ip(cli_ip): url = url_template.format(host, '') else: url = url_template.format(host, cli_ip) r = self.session.get(url) result = json.loads(r.text) if result['Status'] == 0: self.cache.write(cli_ip, result['Question'][0], result, None) ip, ttl = resp_from_json(result) return ip, ttl else: return '', 0 def query(self, data, host, q_type, cli_addr) -> bytes: src_ip, _ = cli_addr question = dict(name=host, type=q_type) cache_resp = self.cache.select(src_ip, question) if cache_resp: if host == self.bjdns2_host or q_type != 1: resp = data[:2] + cache_resp log(cli_addr, '[cache]', '[Type:{}]'.format(q_type), host) else: ip, ttl = resp_from_json(cache_resp) log(cli_addr, '[cache]', host, ip, '(ttl:{})'.format(ttl)) resp = make_data(data, ip, ttl) else: if host == self.bjdns2_host or q_type != 1: resp = query_by_udp(data) self.cache.write(src_ip, question, None, resp[2:]) log(cli_addr, '[Type:{}]'.format(q_type), host) else: ip, ttl = self.query_by_https(host, src_ip) log(cli_addr, host, ip, '(ttl:{})'.format(ttl)) resp = make_data(data, ip, ttl) # if ip: # log(data, resp) return resp # else: # return b'' def handle(self, data, cli_addr): host, q_type = parse_query(data) # if host == self.bjdns2_host or type != 1: # log(host, type) # resp = query_by_udp(data) # log(cli_addr, '[Type:{}]'.format(type), host) # elif type == 1: try: resp = self.query(data, host, q_type, cli_addr) except Exception as e: log(e) resp = b'' self.server.sendto(resp, cli_addr) def start(self): self.server.serve_forever()
class UDPTransport: def __init__(self, discovery, udpsocket, throttle_policy, config): # these values are initialized by the start method self.queueids_to_queues: typing.Dict self.raiden: 'RaidenService' self.discovery = discovery self.config = config self.retry_interval = config['retry_interval'] self.retries_before_backoff = config['retries_before_backoff'] self.nat_keepalive_retries = config['nat_keepalive_retries'] self.nat_keepalive_timeout = config['nat_keepalive_timeout'] self.nat_invitation_timeout = config['nat_invitation_timeout'] self.event_stop = Event() self.greenlets = list() self.addresses_events = dict() # Maps the message_id to a SentMessageState self.messageids_to_asyncresults = dict() # Maps the addresses to a dict with the latest nonce (using a dict # because python integers are immutable) self.nodeaddresses_to_nonces = dict() cache = cachetools.TTLCache( maxsize=50, ttl=CACHE_TTL, ) cache_wrapper = cachetools.cached(cache=cache) self.get_host_port = cache_wrapper(discovery.get) self.throttle_policy = throttle_policy self.server = DatagramServer(udpsocket, handle=self._receive) def start(self, raiden, queueids_to_queues): self.raiden = raiden self.queueids_to_queues = dict() # server.stop() clears the handle. Since this may be a restart the # handle must always be set self.server.set_handle(self._receive) for (recipient, queue_name), queue in queueids_to_queues.items(): queue_copy = list(queue) self.init_queue_for(recipient, queue_name, queue_copy) self.server.start() def stop_and_wait(self): # Stop handling incoming packets, but don't close the socket. The # socket can only be safely closed after all outgoing tasks are stopped self.server.stop_accepting() # Stop processing the outgoing queues self.event_stop.set() gevent.wait(self.greenlets) # All outgoing tasks are stopped. Now it's safe to close the socket. At # this point there might be some incoming message being processed, # keeping the socket open is not useful for these. self.server.stop() # Calling `.close()` on a gevent socket doesn't actually close the underlying os socket # so we do that ourselves here. # See: https://github.com/gevent/gevent/blob/master/src/gevent/_socket2.py#L208 # and: https://groups.google.com/forum/#!msg/gevent/Ro8lRra3nH0/ZENgEXrr6M0J try: self.server._socket.close() # pylint: disable=protected-access except socket.error: pass # Set all the pending results to False for async_result in self.messageids_to_asyncresults.values(): async_result.set(False) def get_health_events(self, recipient): """ Starts a healthcheck taks for `recipient` and returns a HealthEvents with locks to react on its current state. """ if recipient not in self.addresses_events: self.start_health_check(recipient) return self.addresses_events[recipient] def start_health_check(self, recipient): """ Starts a task for healthchecking `recipient` if there is not one yet. """ if recipient not in self.addresses_events: ping_nonce = self.nodeaddresses_to_nonces.setdefault( recipient, {'nonce': 0}, # HACK: Allows the task to mutate the object ) events = HealthEvents( event_healthy=Event(), event_unhealthy=Event(), ) self.addresses_events[recipient] = events self.greenlets.append( gevent.spawn( healthcheck, self, recipient, self.event_stop, events.event_healthy, events.event_unhealthy, self.nat_keepalive_retries, self.nat_keepalive_timeout, self.nat_invitation_timeout, ping_nonce, )) def init_queue_for(self, recipient, queue_name, items): """ Create the queue identified by the pair `(recipient, queue_name)` and initialize it with `items`. """ queueid = (recipient, queue_name) queue = self.queueids_to_queues.get(queueid) assert queue is None queue = NotifyingQueue(items=items) self.queueids_to_queues[queueid] = queue events = self.get_health_events(recipient) self.greenlets.append( gevent.spawn( single_queue_send, self, recipient, queue, self.event_stop, events.event_healthy, events.event_unhealthy, self.retries_before_backoff, self.retry_interval, self.retry_interval * 10, )) if log.isEnabledFor(logging.DEBUG): log.debug( 'new queue created for', node=pex(self.raiden.address), token=pex(queue_name), to=pex(recipient), ) return queue def get_queue_for(self, recipient, queue_name): """ Return the queue identified by the pair `(recipient, queue_name)`. If the queue doesn't exist it will be instantiated. """ queueid = (recipient, queue_name) queue = self.queueids_to_queues.get(queueid) if queue is None: items = () queue = self.init_queue_for(recipient, queue_name, items) return queue def send_async(self, queue_name, recipient, message): """ Send a new ordered message to recipient. Messages that use the same `queue_name` are ordered. """ if not isaddress(recipient): raise ValueError('Invalid address {}'.format(pex(recipient))) # These are not protocol messages, but transport specific messages if isinstance(message, (Delivered, Ping, Pong)): raise ValueError('Do not use send for {} messages'.format( message.__class__.__name__)) messagedata = message.encode() if len(messagedata) > UDP_MAX_MESSAGE_SIZE: raise ValueError('message size exceeds the maximum {}'.format( UDP_MAX_MESSAGE_SIZE)) # message identifiers must be unique message_id = message.message_identifier # ignore duplicates if message_id not in self.messageids_to_asyncresults: self.messageids_to_asyncresults[message_id] = AsyncResult() queue = self.get_queue_for(recipient, queue_name) queue.put((messagedata, message_id)) if log.isEnabledFor(logging.DEBUG): log.debug( 'MESSAGE QUEUED', node=pex(self.raiden.address), queue_name=queue_name, to=pex(recipient), message=message, ) def maybe_send(self, recipient, message): """ Send message to recipient if the transport is running. """ if not isaddress(recipient): raise InvalidAddress('Invalid address {}'.format(pex(recipient))) messagedata = message.encode() host_port = self.get_host_port(recipient) self.maybe_sendraw(host_port, messagedata) def maybe_sendraw_with_result(self, recipient, messagedata, message_id): """ Send message to recipient if the transport is running. Returns: An AsyncResult that will be set once the message is delivered. As long as the message has not been acknowledged with a Delivered message the function will return the same AsyncResult. """ async_result = self.messageids_to_asyncresults.get(message_id) if async_result is None: async_result = AsyncResult() self.messageids_to_asyncresults[message_id] = async_result host_port = self.get_host_port(recipient) self.maybe_sendraw(host_port, messagedata) return async_result def maybe_sendraw(self, host_port, messagedata): """ Send message to recipient if the transport is running. """ # Don't sleep if timeout is zero, otherwise a context-switch is done # and the message is delayed, increasing it's latency sleep_timeout = self.throttle_policy.consume(1) if sleep_timeout: gevent.sleep(sleep_timeout) # Check the udp socket is still available before trying to send the # message. There must be *no context-switches after this test*. if hasattr(self.server, 'socket'): self.server.sendto( messagedata, host_port, ) def _receive(self, data, host_port): # pylint: disable=unused-argument try: self.receive(data) except RaidenShuttingDown: # For a clean shutdown return def receive(self, messagedata): """ Handle an UDP packet. """ # pylint: disable=unidiomatic-typecheck if len(messagedata) > UDP_MAX_MESSAGE_SIZE: log.error( 'INVALID MESSAGE: Packet larger than maximum size', node=pex(self.raiden.address), message=hexlify(messagedata), length=len(messagedata), ) return message = decode(messagedata) if type(message) == Pong: self.receive_pong(message) elif type(message) == Ping: self.receive_ping(message) elif type(message) == Delivered: self.receive_delivered(message) elif message is not None: self.receive_message(message) elif log.isEnabledFor(logging.ERROR): log.error( 'INVALID MESSAGE: Unknown cmdid', node=pex(self.raiden.address), message=hexlify(messagedata), ) def receive_message(self, message): """ Handle a Raiden protocol message. The protocol requires durability of the messages. The UDP transport relies on the node's WAL for durability. The message will be converted to a state change, saved to the WAL, and *processed* before the durability is confirmed, which is a stronger property than what is required of any transport. """ # pylint: disable=unidiomatic-typecheck if on_udp_message(self.raiden, message): # Sending Delivered after the message is decoded and *processed* # gives a stronger guarantee than what is required from a # transport. # # Alternatives are, from weakest to strongest options: # - Just save it on disk and asynchronously process the messages # - Decode it, save to the WAL, and asynchronously process the # state change # - Decode it, save to the WAL, and process it (the current # implementation) delivered_message = Delivered(message.message_identifier) self.raiden.sign(delivered_message) self.maybe_send( message.sender, delivered_message, ) def receive_delivered(self, delivered: Delivered): """ Handle a Delivered message. The Delivered message is how the UDP transport guarantees persistence by the partner node. The message itself is not part of the raiden protocol, but it's required by this transport to provide the required properties. """ processed = ReceiveDelivered(delivered.delivered_message_identifier) self.raiden.handle_state_change(processed) message_id = delivered.delivered_message_identifier async_result = self.raiden.protocol.messageids_to_asyncresults.get( message_id) # clear the async result, otherwise we have a memory leak if async_result is not None: del self.messageids_to_asyncresults[message_id] async_result.set() # Pings and Pongs are used to check the health status of another node. They # are /not/ part of the raiden protocol, only part of the UDP transport, # therefore these messages are not forwarded to the message handler. def receive_ping(self, ping): """ Handle a Ping message by answering with a Pong. """ if ping_log.isEnabledFor(logging.DEBUG): ping_log.debug( 'PING RECEIVED', node=pex(self.raiden.address), message_id=ping.nonce, message=ping, sender=pex(ping.sender), ) pong = Pong(ping.nonce) self.raiden.sign(pong) try: self.maybe_send(ping.sender, pong) except (InvalidAddress, UnknownAddress) as e: log.debug("Couldn't send the `Delivered` message", e=e) def receive_pong(self, pong): """ Handles a Pong message. """ message_id = ('ping', pong.nonce, pong.sender) async_result = self.messageids_to_asyncresults.get(message_id) if async_result is not None: if log.isEnabledFor(logging.DEBUG): log.debug( 'PONG RECEIVED', node=pex(self.raiden.address), message_id=pong.nonce, ) async_result.set(True) def get_ping(self, nonce): """ Returns a signed Ping message. Note: Ping messages don't have an enforced ordering, so a Ping message with a higher nonce may be acknowledged first. """ message = Ping(nonce) self.raiden.sign(message) message_data = message.encode() return message_data def set_node_network_state(self, node_address, node_state): state_change = ActionChangeNodeNetworkState(node_address, node_state) self.raiden.handle_state_change(state_change)
class nscl_dm_adapter(Plugin): def _init(self, ): self._initialized() def _start(self, ): self.sem = Semaphore() self.sem_counter = 0 self.set_configurations() self.api.run_task(self.create_server) self.subscribe_nscl() self.api.run_task(self.subscribe_dm_server) if self.config["enable_test"]: pass # self.api.run_task(self.send_execute_command) # Uncomment to check these operations # self.api.run_task(self.send_specific_observation) # self.api.run_task(self.send_specific_observation1) # self.api.run_task(self.send_cancel_observation) #self.api.run_task(self.send_discover_resources) #self.api.run_task(self.send_write_attributes) #self.api.run_task(self.send_create) self._started() def _stop(self, ): self.local_server.stop() self._stopped() def set_configurations(self, ): self.lwm2m_server_ip = self.config["lwm2m_dm_server_ip"] self.lwm2m_server_port = self.config["lwm2m_dm_server_port"] self.nscl_dm_adapter_listener_ip = self.config[ "nscl_dm_adapter_listener_ip"] self.nscl_dm_adapter_listener_port = self.config[ "nscl_dm_adapter_listener_port"] self.nscl_dm_adapter_client_ip = self.config[ "nscl_dm_adapter_client_ip"] self.nscl_dm_adapter_client_port = self.config[ "nscl_dm_adapter_client_port"] def create_server(self, ): self.local_server = DatagramServer( (self.nscl_dm_adapter_listener_ip, self.nscl_dm_adapter_listener_port), self.handle_request) self.local_server.start() def handle_request(self, message, remote): rx_record = connection.ReceptionRecord(None, message, remote) msg = rx_record.message uriQuery = msg.findOption(options.UriQuery) self.process(rx_record, remote, uriQuery) def process(self, rx_record, remote, uri_query): if rx_record.message.transaction_type == connection.Message.CON: if constants.POST == rx_record.message.code: if self.general_notification_token == rx_record.message.token: self.logger.info("General Notification received") msg = connection.Message(connection.Message.ACK, code=constants.CREATED) self.local_server.sendto( msg._pack(rx_record.transaction_id), remote) self.process_resources( json.loads(rx_record.message.payload)) else: self.logger.info("Specific Notification received") msg = connection.Message(connection.Message.ACK, code=constants.CREATED) self.local_server.sendto( msg._pack(rx_record.transaction_id), remote) payload = json.loads(rx_record.message.payload) observer_ip = payload["observer_ip"] observer_port = payload["observer_port"] del payload["observer_ip"] del payload["observer_port"] self.process_resources(payload, observer_ip=observer_ip, observer_port=observer_port) elif rx_record.message.transaction_type == connection.Message.NON: if self.general_notification_token == rx_record.message.token: self.logger.info("General Notification received") self.process_resources(json.loads(rx_record.message.payload)) else: self.logger.info("Specific Notification received") payload = json.loads(rx_record.message.payload) observer_ip = payload["observer_ip"] observer_port = payload["observer_port"] del payload["observer_ip"] del payload["observer_port"] self.process_resources(payload, observer_ip=observer_ip, observer_port=observer_port) def process_resources(self, payload, observer_ip=None, observer_port=None): total_resources = payload if observer_ip != None and observer_port != None: self.logger.info("The notification should be sent to %s:%s", observer_ip, observer_port) for ep_name, object_resources in total_resources.iteritems(): endpoint_name = ep_name for object_ids, resources in object_resources.iteritems(): object_id = object_ids.split("_")[0] object_inst_id = object_ids.split("_")[1] resources_dict = {} for res_ids, res_value in resources["resources"].iteritems(): res_id = res_ids.split("_")[0] res_inst_id = res_ids.split("_")[1] res_value = res_value resource_name = lwm2m_dict_objects[str( object_id)]["resource_list"][str(res_id)]["resName"] is_multi_inst = lwm2m_dict_objects[str( object_id)]["resource_list"][str(res_id)]["multiInst"] if not is_multi_inst: resources_dict.update({resource_name: res_value}) else: resources_dict.update({ resource_name + "_" + str(res_inst_id): res_value }) self.handle_m2m_server(endpoint_name, object_id, object_inst_id, res_id, res_inst_id, resource_name, res_value, resources_dict) def handle_m2m_server(self, endpoint_name, object_id, object_inst_id, res_id, res_inst_id, res_name, res_value, resources_dict): preferred_scl = endpoint_name.split("/")[0] if endpoint_name.find("attachedDevices") == -1: bool_attachedDevices = False else: attached_device_name = endpoint_name.split("/")[-1] bool_attachedDevices = True object_name = lwm2m_dict_objects[str(object_id)]["object_name"] resource_name = lwm2m_dict_objects[str(object_id)]["resource_list"][ str(res_id)]["resName"] moID_value = lwm2m_dict_objects[str(object_id)]["urn"] res_name_res_inst_id = resource_name + "_" + str(res_inst_id) def add_parameters(response): path = response.resource.path resource = ('{"mgmtObjs" : ' + json.dumps(resources_dict) + '}') request = UpdateRequestIndication(path, resource, content_type="application/json") response = self.api.handle_request_indication(request) def handle_mgmtobjs(response): mgmtobj_exists = False for mgmtobj in response.resource.mgmtObjCollection: if mgmtobj.name == object_name + "_" + str(object_inst_id): mgmtobj_exists = True path = mgmtobj.path request = RetrieveRequestIndication(path) response = self.api.handle_request_indication(request) try: if res_name_res_inst_id in response.value.resource.flex_values: if response.value.resource.flex_values[ res_name_res_inst_id] == str(res_value): continue elif res_name in response.value.resource.flex_values: if response.value.resource.flex_values[ res_name] == str(res_value): continue except: pass resource = ('{"mgmtObjs" : ' + json.dumps(resources_dict) + '}') request = UpdateRequestIndication( path, resource, content_type="application/json") response = self.api.handle_request_indication(request) break if not mgmtobj_exists: mgmtobj_ = MgmtObj(id=str(object_name) + "_" + str(object_inst_id), moID=moID_value) path = response.resource.path request = CreateRequestIndication(path, mgmtobj_) response = self.api.handle_request_indication(request) response.then(add_parameters) def retrieve_mgmtobjs(response): path = response.resource.path + "/mgmtObjs" request = RetrieveRequestIndication(path) response = self.api.handle_request_indication(request) response.then(handle_mgmtobjs) def handle_attached_devices(response): attached_device_exists = False for attached_device in response.resource.attachedDeviceCollection: if attached_device.name == attached_device_name: attached_device_exists = True path = attached_device.path + "/mgmtObjs" request = RetrieveRequestIndication(path) response = self.api.handle_request_indication(request) response.then(handle_mgmtobjs) break if not attached_device_exists: attached_device_object = AttachedDevice( id=attached_device_name) path = response.resource.path request = CreateRequestIndication( path=path, resource=attached_device_object) response = self.api.handle_request_indication(request) response.then(retrieve_mgmtobjs) def retrieve_attached_devices(response): path = response.resource.path + "/attachedDevices" request = RetrieveRequestIndication(path) response = self.api.handle_request_indication(request) response.then(handle_attached_devices) def handle_scl(response): scl_exists = False for _scl in response.resource.sclCollection: if _scl.name == preferred_scl: scl_exists = True if bool_attachedDevices: path = _scl.path + "/attachedDevices" else: path = _scl.path + "/mgmtObjs" request = RetrieveRequestIndication(path) response = self.api.handle_request_indication(request) if bool_attachedDevices: response.then(handle_attached_devices) else: response.then(handle_mgmtobjs) break if not scl_exists: scl_object = Scl(sclId=preferred_scl, link="127.0.0.1", sclType="GSCL", mgmtProtocolType="LWM2M") request = CreateRequestIndication(path="/m2m/scls", resource=scl_object) response = self.api.handle_request_indication(request) if bool_attachedDevices: response.then(retrieve_attached_devices) else: response.then(retrieve_mgmtobjs) path = "/m2m/scls" request = RetrieveRequestIndication(path) response = self.api.handle_request_indication(request) response.then(handle_scl) def _handle_mgmtcmd_created(self, instance, request_indication): pass def _handle_mgmtcmd_updated(self, instance, request_indication): pass def _handle_mgmtobj_created(self, instance, request_indication): pass def _handle_mgmtobj_updated(self, instance, request_indication): filter_keyword = "TransportMgmtPolicy" filter_keyword1 = "DeviceCapability" mgmtobj_name = instance.path.split("/")[-1] if mgmtobj_name.startswith(filter_keyword): self.handle_transport_mgmt_policy(instance, mgmtobj_name) elif mgmtobj_name.startswith(filter_keyword1): self.handle_device_capability(instance, mgmtobj_name, request_indication) def handle_device_capability(self, instance, mgmtobj_name, request_indication): generate_endpoint = instance.path.split("/")[3:-2] endpoint_name = "/".join(generate_endpoint) object_name = mgmtobj_name.split("_")[0] object_id = lwm2m_reverse_dict_objects[object_name]["object_id"] object_inst_id = mgmtobj_name.split("_")[1] if "opEnable" in request_indication.resource and "opDisable" in request_indication.resource: return elif "opEnable" in request_indication.resource: res_id = 5 res_inst_id = 0 elif "opDisable" in request_indication.resource: res_id = 6 res_inst_id = 0 else: return self.send_execute_resource(endpoint_name, object_id, object_inst_id, res_id, res_inst_id) def handle_transport_mgmt_policy(self, instance, mgmtobj_name): res_value_exists = False resources_dict = {} total_dict = {} endpoint_dict = {} generate_endpoint = instance.path.split("/")[3:-2] endpoint_name = "/".join(generate_endpoint) object_name = mgmtobj_name.split("_")[0] object_id = lwm2m_reverse_dict_objects[object_name]["object_id"] object_inst_id = mgmtobj_name.split("_")[1] for key, value in instance.flex_values.iteritems(): res_name = key.split("_")[0] try: res_inst_id = key.split("_")[1] except: res_inst_id = 0 res_value = value res_id = lwm2m_reverse_dict_objects[object_name]["resource_list"][ res_name]["resId"] resources_dict.update( {res_id: { "res_inst_id": res_inst_id, "res_value": res_value }}) if res_value != "" and not res_value_exists: res_value_exists = True if res_value_exists: self.logger.info("Sending the Resource Updates to LWM2M Server") payload = json.dumps(resources_dict) content_type = "application/json" request = lwm2m_api() self.sem.acquire() client_port = self.generate_client_port() response = request.write_resource(self.lwm2m_server_ip, self.lwm2m_server_port, endpoint_name, object_id, payload, content_type, object_inst_id=object_inst_id, client_port=client_port) self.sem.release() def generate_client_port(self, ): if self.sem_counter >= 1000: self.sem_counter = 0 self.sem_counter += 1 sem_counter = self.sem_counter client_port = self.nscl_dm_adapter_client_port + sem_counter return client_port def subscribe_dm_server(self, ): self.logger.info( "Trying to subscribe to LWM2M DM Server for General Subscription") payload = json.dumps({"listener_ip": self.nscl_dm_adapter_listener_ip, "listener_port": \ self.nscl_dm_adapter_listener_port}) content_type = "application/json" request = lwm2m_api() response = request.observe_resource( self.lwm2m_server_ip, self.lwm2m_server_port, payload=payload, content_type=content_type, client_port=self.generate_client_port()) def _handle_response(response): self.logger.info( "Successfully subscribed to LWM2M DM Server for General Subscription" ) self.general_notification_token = response.token def _handle_error(*args): self.subscribe_dm_server() response.then(_handle_response, _handle_error) def subscribe_nscl(self, ): self.events.resource_created.register_handler( self._handle_mgmtobj_created, MgmtObj) self.events.resource_updated.register_handler( self._handle_mgmtobj_updated, MgmtObj) self.events.resource_created.register_handler( self._handle_mgmtcmd_created, MgmtCmd) self.events.resource_updated.register_handler( self._handle_mgmtcmd_updated, MgmtCmd) def send_discover_resources(self, ): sleep(20) self.logger.info("Sending discover request to Dm server") server_ip = self.lwm2m_server_ip server_port = self.lwm2m_server_port payload = "/.well-known/core" request = lwm2m_api() response = request.discover_resources( server_ip, server_port, payload=payload, client_port=self.generate_client_port()) discover = Discovery() payload = json.loads(response.payload) discover.display_all_resources(payload) def send_write_attributes(self, ): sleep(10) self.logger.info("Sending attributes info to DM server") server_ip = self.lwm2m_server_ip server_port = self.lwm2m_server_port endpoint_name = "emulated_device_nb_0" object_id = 3 object_inst_id = 0 res_id = 1 res_inst_id = 0 pmax = 50 pmin = 10 gt = None lt = None st = None cancel = None content_type = "application/json" payload = json.dumps({ "pmax": pmax, "pmin": pmin, "gt": gt, "lt": lt, "st": st, "cancel": cancel }) request = lwm2m_api() response = request.write_attributes( server_ip, server_port, endpoint_name, object_id, payload, content_type, object_inst_id=object_inst_id, res_id=res_id, res_inst_id=res_inst_id, client_port=self.generate_client_port()) def send_create(self, ): sleep(10) self.logger.info("Sending create info to DM server") server_ip = self.lwm2m_server_ip server_port = self.lwm2m_server_port endpoint_name = "emulated_device_nb_0" object_id = 3 object_inst_id = 4 res_id = 0 res_inst_id = 0 res_value = "fokus" res_id_res_inst_id = str(res_id) + "_" + str(res_inst_id) payload = {} res_id_res_inst_id = str(res_id) + "_" + str(res_inst_id) payload[res_id_res_inst_id] = { "res_id": res_id, "res_inst_id": res_inst_id, "res_value": res_value } content_type = "application/json" request = lwm2m_api() response = request.create_object_instance( server_ip, server_port, endpoint_name, object_id, json.dumps(payload), content_type, object_inst_id=object_inst_id, client_port=self.generate_client_port()) def send_specific_observation(self, ): sleep(15) self.logger.info("Sending specific observation to DM server") app_ip = "localhost" app_port = "1111" server_ip = self.lwm2m_server_ip server_port = self.lwm2m_server_port endpoint_name = "gscl/attachedDevices/PulseOximeter" object_id = 4200 object_inst_id = 0 res_id = 1 res_inst_id = 0 request = lwm2m_api() response = request.observe_resource( server_ip, server_port, app_ip=app_ip, app_port=app_port, endpoint_name=endpoint_name, object_id=object_id, object_inst_id=object_inst_id, res_id=res_id, res_inst_id=res_inst_id, client_port=self.generate_client_port()) def _handle_response(response): self.logger.info("response token: %s", response.token) response.then(_handle_response) def send_specific_observation1(self, ): sleep(20) self.logger.info("Sending specific observation to DM server") app_ip = "localhost" app_port = "1115" server_ip = self.lwm2m_server_ip server_port = self.lwm2m_server_port endpoint_name = "gscl_PulseOximeter" object_id = 4200 object_inst_id = 0 res_id = 0 res_inst_id = 0 request = lwm2m_api() response = request.observe_resource( server_ip, server_port, app_ip=app_ip, app_port=app_port, endpoint_name=endpoint_name, object_id=object_id, object_inst_id=object_inst_id, res_id=res_id, res_inst_id=res_inst_id, client_port=self.generate_client_port()) def _handle_response(response): self.logger.info("response token: %s", response.token) response.then(_handle_response) def send_cancel_observation(self, ): sleep(22) self.logger.info("Sending Cancel Observation to DM server") app_ip = "localhost" app_port = "1111" server_ip = self.lwm2m_server_ip server_port = self.lwm2m_server_port endpoint_name = "gscl/attachedDevices/PulseOximeter" object_id = 4200 object_inst_id = 0 res_id = 1 res_inst_id = 0 request = lwm2m_api() response = request.cancel_observe_resource( server_ip, server_port, app_ip, app_port, endpoint_name, object_id, object_inst_id=object_inst_id, res_id=res_id, res_inst_id=res_inst_id, client_port=self.generate_client_port()) def _handle_response(response): self.logger.info("response token: %s", response.token) self.logger.info("response %s", response.payload) response.then(_handle_response) def send_execute_resource(self, endpoint_name, object_id, object_inst_id, res_id, res_inst_id, payload=None): self.logger.info("Sending execution to DM server") server_ip = self.lwm2m_server_ip server_port = self.lwm2m_server_port payload = None request = lwm2m_api() response = request.execute_resource( server_ip, server_port, endpoint_name, object_id, object_inst_id, res_id, res_inst_id=res_inst_id, payload=payload, client_port=self.generate_client_port()) self.logger.info("Updating M2M Resource Tree") resources_dict = {} object_id_res_id = str(object_id) + "/" + str(res_id) if object_id_res_id in action_mapping: res_id = action_mapping[object_id_res_id]["target_res_id"] res_value = action_mapping[object_id_res_id]["target_action"] res_name = lwm2m_dict_objects[str(object_id)]["resource_list"][str( res_id)]["resName"] is_multi_inst = lwm2m_dict_objects[str( object_id)]["resource_list"][str(res_id)]["multiInst"] if not is_multi_inst: resources_dict.update({res_name: res_value}) else: resources_dict.update( {res_name + "_" + str(res_inst_id): res_value}) self.handle_m2m_server(endpoint_name, object_id, object_inst_id, res_id, res_inst_id, res_name, res_value, resources_dict)
class UDPNode(NodeABC): def __init__(self, bind_address: Tuple[str, int] = None, handler=None): bind_address = bind_address or self.DEFAULT_ADDRESS handler = handler or (lambda data, address: None) def handle(data: bytes, address: Tuple[str, int]): deserialized = self.deserialize(data) handler(deserialized, address) super().__init__() self._bind_address = bind_address self._server = None # type: DatagramServer self._stop = False def run(): self._tls.gevent = True self._server = DatagramServer(self.bind_address, handle) self._server.start() while self.is_running() and not self._stop: gevent.sleep(0.5) self._server.stop() self._thread = threading.Thread(target=run, daemon=True) self._tls = threading.local() self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) def is_running(self): if self._server: return self._server.started return False @property def bind_address(self) -> Tuple[str, int]: if self._server: return self._server.address return self._bind_address def start(self): self._thread.start() n = 1000 for _ in range(n): if self.is_running(): return self time.sleep(1.0 / n) self.stop() raise Exception('start timeouted 1.0sec') def stop(self, timeout: float = None): self._stop = True self.join(timeout=timeout) return self def join(self, timeout: float = None): self._thread.join(timeout=timeout) def sendto(self, data: Any, address: Tuple[str, int]): serialized = self.serialize(data) assert len(serialized) <= 4096, 'len={} {}'.format( len(serialized), data) try: _ = self._tls.gevent self._server.sendto(serialized, address) except AttributeError: self._socket.sendto(serialized, address) def serialize(self, obj: Any): return pickle.dumps(obj) def deserialize(self, obj: Any): return pickle.loads(obj)
class UDPTransport(Runnable): UDP_MAX_MESSAGE_SIZE = 1200 def __init__(self, discovery, udpsocket, throttle_policy, config): super().__init__() # these values are initialized by the start method self.queueids_to_queues: typing.Dict self.raiden: RaidenService self.discovery = discovery self.config = config self.retry_interval = config['retry_interval'] self.retries_before_backoff = config['retries_before_backoff'] self.nat_keepalive_retries = config['nat_keepalive_retries'] self.nat_keepalive_timeout = config['nat_keepalive_timeout'] self.nat_invitation_timeout = config['nat_invitation_timeout'] self.event_stop = Event() self.event_stop.set() self.greenlets = list() self.addresses_events = dict() self.messageids_to_asyncresults = dict() # Maps the addresses to a dict with the latest nonce (using a dict # because python integers are immutable) self.nodeaddresses_to_nonces = dict() cache = cachetools.TTLCache( maxsize=50, ttl=CACHE_TTL, ) cache_wrapper = cachetools.cached(cache=cache) self.get_host_port = cache_wrapper(discovery.get) self.throttle_policy = throttle_policy self.server = DatagramServer(udpsocket, handle=self.receive) def start( self, raiden: RaidenService, message_handler: MessageHandler, ): if not self.event_stop.ready(): raise RuntimeError('UDPTransport started while running') self.event_stop.clear() self.raiden = raiden self.message_handler = message_handler self.queueids_to_queues = dict() # server.stop() clears the handle. Since this may be a restart the # handle must always be set self.server.set_handle(self.receive) self.server.start() super().start() def _run(self): """ Runnable main method, perform wait on long-running subtasks """ try: self.event_stop.wait() except gevent.GreenletExit: # killed without exception self.event_stop.set() gevent.killall(self.greenlets) # kill children raise # re-raise to keep killed status except Exception: self.stop() # ensure cleanup and wait on subtasks raise def stop(self): if self.event_stop.ready(): return # double call, happens on normal stop, ignore self.event_stop.set() # Stop handling incoming packets, but don't close the socket. The # socket can only be safely closed after all outgoing tasks are stopped self.server.stop_accepting() # Stop processing the outgoing queues gevent.wait(self.greenlets) # All outgoing tasks are stopped. Now it's safe to close the socket. At # this point there might be some incoming message being processed, # keeping the socket open is not useful for these. self.server.stop() # Calling `.close()` on a gevent socket doesn't actually close the underlying os socket # so we do that ourselves here. # See: https://github.com/gevent/gevent/blob/master/src/gevent/_socket2.py#L208 # and: https://groups.google.com/forum/#!msg/gevent/Ro8lRra3nH0/ZENgEXrr6M0J try: self.server._socket.close() # pylint: disable=protected-access except socket.error: pass # Set all the pending results to False for async_result in self.messageids_to_asyncresults.values(): async_result.set(False) def get_health_events(self, recipient): """ Starts a healthcheck task for `recipient` and returns a HealthEvents with locks to react on its current state. """ if recipient not in self.addresses_events: self.start_health_check(recipient) return self.addresses_events[recipient] def start_health_check(self, recipient): """ Starts a task for healthchecking `recipient` if there is not one yet. """ if recipient not in self.addresses_events: ping_nonce = self.nodeaddresses_to_nonces.setdefault( recipient, {'nonce': 0}, # HACK: Allows the task to mutate the object ) events = healthcheck.HealthEvents( event_healthy=Event(), event_unhealthy=Event(), ) self.addresses_events[recipient] = events greenlet_healthcheck = gevent.spawn( healthcheck.healthcheck, self, recipient, self.event_stop, events.event_healthy, events.event_unhealthy, self.nat_keepalive_retries, self.nat_keepalive_timeout, self.nat_invitation_timeout, ping_nonce, ) greenlet_healthcheck.name = f'Healthcheck for {pex(recipient)}' greenlet_healthcheck.link_exception(self.on_error) self.greenlets.append(greenlet_healthcheck) def init_queue_for( self, queue_identifier: QueueIdentifier, items: typing.List[QueueItem_T], ) -> Queue_T: """ Create the queue identified by the queue_identifier and initialize it with `items`. """ recipient = queue_identifier.recipient queue = self.queueids_to_queues.get(queue_identifier) assert queue is None queue = NotifyingQueue(items=items) self.queueids_to_queues[queue_identifier] = queue events = self.get_health_events(recipient) greenlet_queue = gevent.spawn( single_queue_send, self, recipient, queue, queue_identifier, self.event_stop, events.event_healthy, events.event_unhealthy, self.retries_before_backoff, self.retry_interval, self.retry_interval * 10, ) if queue_identifier.channel_identifier == CHANNEL_IDENTIFIER_GLOBAL_QUEUE: greenlet_queue.name = f'Queue for {pex(recipient)} - global' else: greenlet_queue.name = ( f'Queue for {pex(recipient)} - {queue_identifier.channel_identifier}' ) greenlet_queue.link_exception(self.on_error) self.greenlets.append(greenlet_queue) log.debug( 'new queue created for', node=pex(self.raiden.address), queue_identifier=queue_identifier, items_qty=len(items), ) return queue def get_queue_for( self, queue_identifier: QueueIdentifier, ) -> Queue_T: """ Return the queue identified by the given queue identifier. If the queue doesn't exist it will be instantiated. """ queue = self.queueids_to_queues.get(queue_identifier) if queue is None: items = () queue = self.init_queue_for(queue_identifier, items) return queue def send_async( self, queue_identifier: QueueIdentifier, message: 'Message', ): """ Send a new ordered message to recipient. Messages that use the same `queue_identifier` are ordered. """ recipient = queue_identifier.recipient if not is_binary_address(recipient): raise ValueError('Invalid address {}'.format(pex(recipient))) # These are not protocol messages, but transport specific messages if isinstance(message, (Delivered, Ping, Pong)): raise ValueError('Do not use send for {} messages'.format( message.__class__.__name__)) messagedata = message.encode() if len(messagedata) > self.UDP_MAX_MESSAGE_SIZE: raise ValueError( 'message size exceeds the maximum {}'.format( self.UDP_MAX_MESSAGE_SIZE), ) # message identifiers must be unique message_id = message.message_identifier # ignore duplicates if message_id not in self.messageids_to_asyncresults: self.messageids_to_asyncresults[message_id] = AsyncResult() queue = self.get_queue_for(queue_identifier) queue.put((messagedata, message_id)) assert queue.is_set() log.debug( 'Message queued', node=pex(self.raiden.address), queue_identifier=queue_identifier, queue_size=len(queue), message=message, ) def maybe_send(self, recipient: typing.Address, message: Message): """ Send message to recipient if the transport is running. """ if not is_binary_address(recipient): raise InvalidAddress('Invalid address {}'.format(pex(recipient))) messagedata = message.encode() host_port = self.get_host_port(recipient) self.maybe_sendraw(host_port, messagedata) def maybe_sendraw_with_result( self, recipient: typing.Address, messagedata: bytes, message_id: typing.MessageID, ) -> AsyncResult: """ Send message to recipient if the transport is running. Returns: An AsyncResult that will be set once the message is delivered. As long as the message has not been acknowledged with a Delivered message the function will return the same AsyncResult. """ async_result = self.messageids_to_asyncresults.get(message_id) if async_result is None: async_result = AsyncResult() self.messageids_to_asyncresults[message_id] = async_result host_port = self.get_host_port(recipient) self.maybe_sendraw(host_port, messagedata) return async_result def maybe_sendraw(self, host_port: typing.Tuple[int, int], messagedata: bytes): """ Send message to recipient if the transport is running. """ # Don't sleep if timeout is zero, otherwise a context-switch is done # and the message is delayed, increasing it's latency sleep_timeout = self.throttle_policy.consume(1) if sleep_timeout: gevent.sleep(sleep_timeout) # Check the udp socket is still available before trying to send the # message. There must be *no context-switches after this test*. if hasattr(self.server, 'socket'): self.server.sendto( messagedata, host_port, ) def receive( self, messagedata: bytes, host_port: typing.Tuple[str, int], # pylint: disable=unused-argument ) -> bool: """ Handle an UDP packet. """ # pylint: disable=unidiomatic-typecheck if len(messagedata) > self.UDP_MAX_MESSAGE_SIZE: log.warning( 'Invalid message: Packet larger than maximum size', node=pex(self.raiden.address), message=hexlify(messagedata), length=len(messagedata), ) return False try: message = decode(messagedata) except InvalidProtocolMessage as e: log.warning( 'Invalid protocol message', error=str(e), node=pex(self.raiden.address), message=hexlify(messagedata), ) return False if type(message) == Pong: self.receive_pong(message) elif type(message) == Ping: self.receive_ping(message) elif type(message) == Delivered: self.receive_delivered(message) elif message is not None: self.receive_message(message) else: log.warning( 'Invalid message: Unknown cmdid', node=pex(self.raiden.address), message=hexlify(messagedata), ) return False return True def receive_message(self, message: Message): """ Handle a Raiden protocol message. The protocol requires durability of the messages. The UDP transport relies on the node's WAL for durability. The message will be converted to a state change, saved to the WAL, and *processed* before the durability is confirmed, which is a stronger property than what is required of any transport. """ self.message_handler.on_message(self.raiden, message) # Sending Delivered after the message is decoded and *processed* # gives a stronger guarantee than what is required from a # transport. # # Alternatives are, from weakest to strongest options: # - Just save it on disk and asynchronously process the messages # - Decode it, save to the WAL, and asynchronously process the # state change # - Decode it, save to the WAL, and process it (the current # implementation) delivered_message = Delivered(message.message_identifier) self.raiden.sign(delivered_message) self.maybe_send( message.sender, delivered_message, ) def receive_delivered(self, delivered: Delivered): """ Handle a Delivered message. The Delivered message is how the UDP transport guarantees persistence by the partner node. The message itself is not part of the raiden protocol, but it's required by this transport to provide the required properties. """ self.message_handler.on_message(self.raiden, delivered) message_id = delivered.delivered_message_identifier async_result = self.raiden.transport.messageids_to_asyncresults.get( message_id) # clear the async result, otherwise we have a memory leak if async_result is not None: del self.messageids_to_asyncresults[message_id] async_result.set() else: log.warn( 'Unknown delivered message received', message_id=message_id, ) # Pings and Pongs are used to check the health status of another node. They # are /not/ part of the raiden protocol, only part of the UDP transport, # therefore these messages are not forwarded to the message handler. def receive_ping(self, ping: Ping): """ Handle a Ping message by answering with a Pong. """ log_healthcheck.debug( 'Ping received', node=pex(self.raiden.address), message_id=ping.nonce, message=ping, sender=pex(ping.sender), ) pong = Pong(ping.nonce) self.raiden.sign(pong) try: self.maybe_send(ping.sender, pong) except (InvalidAddress, UnknownAddress) as e: log.debug("Couldn't send the `Delivered` message", e=e) def receive_pong(self, pong: Pong): """ Handles a Pong message. """ message_id = ('ping', pong.nonce, pong.sender) async_result = self.messageids_to_asyncresults.get(message_id) if async_result is not None: log_healthcheck.debug( 'Pong received', node=pex(self.raiden.address), sender=pex(pong.sender), message_id=pong.nonce, ) async_result.set(True) else: log_healthcheck.warn( 'Unknown pong received', message_id=message_id, ) def get_ping(self, nonce: int) -> Ping: """ Returns a signed Ping message. Note: Ping messages don't have an enforced ordering, so a Ping message with a higher nonce may be acknowledged first. """ message = Ping( nonce=nonce, current_protocol_version=constants.PROTOCOL_VERSION, ) self.raiden.sign(message) message_data = message.encode() return message_data def set_node_network_state(self, node_address: typing.Address, node_state): state_change = ActionChangeNodeNetworkState(node_address, node_state) self.raiden.handle_state_change(state_change)
class dmServer(Plugin): count_client = 0 discover_client_paths = [] def _init(self): self._initialized() def _start(self): self.start_observation_nscl = False self.total_clients = {} self.setting_address() self.server = DatagramServer( (self.lwm2m_dm_server_ip, self.lwm2m_dm_server_port), self.handle_request) self.start_server() self._started() def _stop(self): self.stop_server() self._stopped() def setting_address(self, ): self.lwm2m_dm_server_ip = self.config["lwm2m_dm_server_ip"] self.lwm2m_dm_server_port = self.config["lwm2m_dm_server_port"] self.client_ip = self.config["client_ip"] self.client_port = self.config["client_port"] self.nscl_dm_adapter_listener_ip = self.config[ "nscl_dm_adapter_listener_ip"] self.nscl_dm_adapter_listener_port = self.config[ "nscl_dm_adapter_listener_port"] self.nscl_dm_adapter_client_ip = self.config[ "nscl_dm_adapter_client_ip"] self.nscl_dm_adapter_client_port = self.config[ "nscl_dm_adapter_client_port"] def handle_request(self, message, remote): rx_record = connection.ReceptionRecord(None, message, remote) msg = rx_record.message uriQuery = msg.findOption(options.UriQuery) self.process(rx_record, remote, uriQuery) def start_server(self, ): print "LWM2M Server Started" self.server.start() def stop_server(self, ): print "LWM2M Server Stopped" self.server.stop() def process(self, rx_record, remote, uriQ): position_client = 0 msg = rx_record.message self.uriQuery1 = uriQ payload_type = False if msg.transaction_type == connection.Message.CON: if constants.POST == msg.code: check_for_execute = 0 for val1 in uriQ: if str(val1).find("execute") != -1: check_for_execute = 1 if check_for_execute == 1: check_for_execute = 0 msg = connection.Message(connection.Message.ACK, code=constants.CREATED) self.server.sendto(msg._pack(rx_record.transaction_id), remote) self.execute_resource(rx_record) else: notify_list = self.client_registration( rx_record, msg, payload_type, remote) self.send_notifications_nscl_adapter(notify_list) elif constants.PUT == msg.code: check_pmax = 0 try: for val1 in uriQ: if str(val1).find("pmax") != -1: check_pmax = 1 except: pass if check_pmax == 1: self.write_attributes(rx_record) check_pmax = 0 else: pars.parse_uri_query(self.uriQuery1) pars.parse_payload(str(msg.payload), payload_type) location_address = str(str( msg.options[1]).split(":")[1]).strip() for val1 in maintain_clients: if (val1["client_ip"] == rx_record.remote[0] and val1["client_port"] == rx_record.remote[1] ) or val1["location"] == location_address: position_client = val1["client_id"] endpoint_name = val1["endPointName"] notify_list = self.total_clients[ position_client].update_mgmt_object( pars.return_parse_uri_query(), pars.return_parse_payload(), endpoint_name, self.start_observation_nscl ) #mayn't be about creating objects:: so can be calling another function if self.start_observation_nscl: self.send_notifications_nscl_adapter( notify_list) break msg = connection.Message(connection.Message.ACK, code=constants.CHANGED, payload="Changed") self.server.sendto(msg._pack(rx_record.transaction_id), remote) elif constants.DELETE == msg.code: self.__storage = "" print 'Deleting value: %s' % (self.__storage, ) msg = connection.Message(connection.Message.ACK, code=constants.DELETED, payload='Deleting value: %s' % (self.__storage, )) #sendto line msising elif constants.GET == msg.code: try: observe_value = rx_record.message.findOption( options.Observe).value except ValueError: observe_value = None #-1 if observe_value == OBSERVE_OPTION_VALUE_OBSERVATION: if rx_record.remote[ 0] == self.nscl_dm_adapter_client_ip and rx_record.remote[ 1] == self.nscl_dm_adapter_client_port: for v in rx_record.message.findOption(URI_PATH_VALUE): self.start_observation_nscl = True self.msg_transaction_id = rx_record.transaction_id self.msg_uri_port = rx_record.message.findOption( URI_PORT_VALUE).value self.msg_uri_host = rx_record.message.findOption( URI_HOST_VALUE).value else: self.resource_observation(rx_record) elif observe_value == OBSERVE_OPTION_VALUE_CANCEL_OBSERVATION: self.cancel_observation(rx_record) elif str( rx_record.message.findOption(URI_PATH_VALUE) [0].value).find(".well-known") != -1: print "Discovered Clients .." for val4 in dmServer.discover_client_paths: print ''.join( ["/", val4["path"], "/", val4["endPointName"]]) elif str( rx_record.message.findOption(URI_PATH_VALUE) [0].value).find("rd") != -1: self.resource_discovery(rx_record) msg = connection.Message(connection.Message.ACK, code=constants.CONTENT, payload="Request Received") self.server.sendto(msg._pack(rx_record.transaction_id), remote) elif msg.transaction_type == connection.Message.ACK: self.notifications_display(msg.payload) def myfunc(self, ): self.p = subprocess.Popen(['sh', 'recv_mp4v.sh', '34000']) #, stdout=subprocess.PIPE) self.p.communicate() def execute_resource(self, rx_record): path = [] upath = "" i = 0 for v in rx_record.message.findOption(11): print "inside execute : server:: %s" % v path.append( str(rx_record.message.findOption(URI_PATH_VALUE)[i].value)) upath += path[i] + "/" i += 1 upath = upath[:len(upath) - 1] for val2 in maintain_clients: if val2["location"] == path[1]: c_server_ip = val2["serverIPInClient"] c_server_port = val2["serverPortInClient"] break c_server_ip_port = str(c_server_ip) + ":" + str(c_server_port) #self.p.communicate() print "reached down :: after runtask" client_request.executeResource(c_server_ip_port, rx_record.message.payload, path=upath, client_port=self.client_port) print "after executeresource:: server" #self.p = subprocess.Popen(['sh', 'recv_mp4v.sh', '34000'], stdout=subprocess.PIPE) #self.api.run_task(self.p.communicate) #self.api.run_task(self.myfunc) ### #self.myfunc() print "reached last ..." def client_registration(self, rx_record, msg, payload_type, remote): temp = [] endpoint_name = rx_record.message.findOption( URI_QUERY_VALUE)[0].value.split("=")[1] server_ip_in_client = rx_record.message.findOption( URI_QUERY_VALUE)[1].value.split("=")[1] #option 15 is for UriQuery server_port_in_client = rx_record.message.findOption( URI_QUERY_VALUE)[2].value.split("=")[1] temp.append(self.uriQuery1[0]) self.uriQuery1 = temp pars.parse_uri_query(self.uriQuery1) pars.parse_payload(str(msg.payload), payload_type) pars.return_parse_payload() locationAddr = self.locID_generator(10) info_dict = { "client_ip": rx_record.remote[0], "client_port": rx_record.remote[1], "serverIPInClient": server_ip_in_client, "serverPortInClient": server_port_in_client, "client_id": dmServer.count_client, "location": locationAddr, "endPointName": endpoint_name } dmServer.discover_client_paths.append({ "path": "rd", "location": locationAddr, "endPointName": endpoint_name, "objectID": None, "objectInstID": None, "resID": None }) maintain_clients.append(info_dict) position_client = dmServer.count_client self.total_clients[position_client] = ClientCollection() dmServer.count_client += 1 msg = connection.Message(connection.Message.ACK, code=constants.CREATED, location=locationAddr) self.server.sendto(msg._pack(rx_record.transaction_id), remote) return self.total_clients[position_client].create_mgmt_objects( pars.return_parse_uri_query(), pars.return_parse_payload(), endpoint_name, self.start_observation_nscl) def send_notifications_nscl_adapter(self, notify_list): if self.start_observation_nscl: sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) msg_notify = connection.Message(connection.Message.ACK, code=constants.CONTENT, payload=json.dumps(notify_list)) sock.sendto(msg_notify._pack(self.msg_transaction_id), (self.msg_uri_host, int(self.msg_uri_port))) sock.close() def notifications_display(self, notifications): print "NOTIFICATIONS ..." storeNotification = json.loads(notifications) for val2 in storeNotification: print "App. IP : %s, App. Port: %s, ObjectID : %s, Instance ID : %s, Resource Name : %s, Resource Value : %s" % ( val2["app_ip"], val2["app_port"], val2["objectID"], val2["objectInstID"], val2["resName"], val2["resValue"]) # Send to the app's ip (and port) def resource_discovery(self, rx_record): app_ip = rx_record.remote[0] app_port = rx_record.remote[1] path = [] upath = "" for v in rx_record.message.findOption(URI_PATH_VALUE): path.append(v.value) upath = "/".join(path) for val2 in maintain_clients: if val2["endPointName"] == path[1] or val2["location"] == path[1]: c_server_ip = val2["serverIPInClient"] c_server_port = val2["serverPortInClient"] count_val = val2["client_id"] break c_server_ip_port = str(c_server_ip) + ":" + str(c_server_port) payload = ''.join( ["app_ip=", str(app_ip), "&app_port=", str(app_port)]) discoveredResources = client_request.discoverResource( c_server_ip_port, path=upath, payload=payload, client_port=self.client_port) print "Discovered Resources .." for val3 in json.loads( discoveredResources.payload ): #it contains requesting application ip and port :: val3["app_ip"] and val3["app_port"] if val3.has_key("resID"): print ''.join([ "/rd/", val3["endPointName"], "/", val3["objectID"], "/", val3["objectInstID"], "/", str(val3["resID"]), " ; pmax : ", str(val3["pmax"]), " , pmin : ", str(val3["pmin"]), " , value : ", str(val3["resValue"]) ]) else: print ''.join([ "/rd/", val3["endPointName"], "/", val3["objectID"], "/", val3["objectInstID"], " ; pmax :", str(val3["pmax"]), " , pmin : ", str(val3["pmin"]) ]) def resource_observation(self, rx_record): print "OBSERVATION STARTED .." self.observeCollection = [] tempObserve = {} app_ip = rx_record.remote[0] app_port = rx_record.remote[1] #move below two lines in the init path = [] for v in rx_record.message.findOption(URI_PATH_VALUE): path.append(v.value) upath = "/".join(path) if len(path) == 3: path.append("None") path.append("None") elif len(path) == 4: path.append("None") #here client means app ip and port self.observeCollection.append({ "clientIP": app_ip, "clientPort": app_port, "endPointName": path[1], "objectID": path[2], "objectInstID": path[3], "resID": path[4] }) tempPayload = "clientIP=" + str(app_ip) + "&clientPort=" + str( app_port) for val2 in maintain_clients: if val2["endPointName"] == path[1] or val2["location"] == path[1]: c_server_ip = val2["serverIPInClient"] c_server_port = val2["serverPortInClient"] break c_server_ip_port = str(c_server_ip) + ":" + str(c_server_port) first_notification = client_request.observeResource( c_server_ip_port, path=upath, payload=tempPayload, uri_host=self.lwm2m_dm_server_ip, uri_port=self.lwm2m_dm_server_port, client_port=self.client_port) if first_notification.payload != "null": self.notifications_display(first_notification.payload) else: print "Already Exists" def cancel_observation(self, rx_record): print "CANCEL OBSERVATION .." app_ip = rx_record.remote[0] app_port = rx_record.remote[1] path = [] for v in rx_record.message.findOption(URI_PATH_VALUE): path.append( str(rx_record.message.findOption(URI_PATH_VALUE)[i].value)) upath = "/".join(path) tempObserve = {} if len(path) == 3: path.append("None") path.append("None") elif len(path) == 4: path.append("None") #here client means app ip and port tempCancelObs = "clientIP=" + str(app_ip) + "&clientPort=" + str( app_port) for val2 in maintain_clients: if val2["endPointName"] == path[1] or val2["location"] == path[1]: c_server_ip = val2["serverIPInClient"] c_server_port = val2["serverPortInClient"] break c_server_ip_port = str(c_server_ip) + ":" + str(c_server_port) client_request.cancelSubscription(c_server_ip_port, path=upath, payload=tempCancelObs, client_port=self.client_port) def write_attributes(self, rx_record): path = [] for v in rx_record.message.findOption(URI_PATH_VALUE): path.append(v.value) upath = "/".join(path) pmax = rx_record.message.findOption(URI_QUERY_VALUE)[0].value.split( "=")[1] pmin = rx_record.message.findOption(URI_QUERY_VALUE)[1].value.split( "=")[1] for val2 in maintain_clients: if val2["endPointName"] == path[1] or val2["location"] == path[1]: c_server_ip = val2["serverIPInClient"] c_server_port = val2["serverPortInClient"] break c_server_ip_port = str(c_server_ip) + ":" + str(c_server_port) query = ''.join(["pmax=", str(pmax), "&pmin=", str(pmin)]) reply = client_request.writeAttributes(c_server_ip_port, query, path=upath, client_port=self.client_port) print reply #update pmax and pmin in server too def locID_generator(self, str_size, chars=string.ascii_uppercase + string.digits): return ''.join([random.choice(chars) for _ in range(str_size)])
class UDPTransport: """ Node communication using the UDP protocol. """ def __init__( self, host, port, socket=None, protocol=None, throttle_policy=DummyPolicy()): self.protocol = protocol if socket is not None: self.server = DatagramServer(socket, handle=self.receive) else: self.server = DatagramServer((host, port), handle=self.receive) self.host = self.server.server_host self.port = self.server.server_port self.throttle_policy = throttle_policy def receive(self, data, host_port): # pylint: disable=unused-argument try: self.protocol.receive(data) except InvalidProtocolMessage as e: if log.isEnabledFor(logging.WARNING): log.warning("Can't decode: {} (data={}, len={})".format(str(e), data, len(data))) return except RaidenShuttingDown: # For a clean shutdown return # enable debugging using the DummyNetwork callbacks DummyTransport.track_recv(self.protocol.raiden, host_port, data) def send(self, sender, host_port, bytes_): """ Send `bytes_` to `host_port`. Args: sender (address): The address of the running node. host_port (Tuple[(str, int)]): Tuple with the host name and port number. bytes_ (bytes): The bytes that are going to be sent through the wire. """ sleep_timeout = self.throttle_policy.consume(1) # Don't sleep if timeout is zero, otherwise a context-switch is done # and the message is delayed, increasing it's latency if sleep_timeout: gevent.sleep(sleep_timeout) if not hasattr(self.server, 'socket'): raise RuntimeError('trying to send a message on a closed server') self.server.sendto(bytes_, host_port) # enable debugging using the DummyNetwork callbacks DummyTransport.network.track_send(sender, host_port, bytes_) def stop(self): self.server.stop() # Calling `.close()` on a gevent socket doesn't actually close the underlying os socket # so we do that ourselves here. # See: https://github.com/gevent/gevent/blob/master/src/gevent/_socket2.py#L208 # and: https://groups.google.com/forum/#!msg/gevent/Ro8lRra3nH0/ZENgEXrr6M0J try: self.server._socket.close() except socket.error: pass def stop_accepting(self): self.server.stop_accepting() def start(self): assert not self.server.started # server.stop() clears the handle, since this may be a restart the # handle must always be set self.server.set_handle(self.receive) self.server.start()
class LocalClientCore(LoggerMixin): def __init__(self, local_listener_ip, local_listener_port, lwm2m_server_ip, lwm2m_server_port, local_client_ip, local_client_port): self.ep_location_mapping = {} self.total_resources = {} self.res_dict = {} self.lwm2m_dm_server_ip = lwm2m_server_ip self.lwm2m_dm_server_port = lwm2m_server_port self.sem = Semaphore() self.local_listener_ip = local_listener_ip self.local_listener_port = local_listener_port self.local_client_ip_ = local_client_ip self.local_client_port = local_client_port #local_client_port #self.local_client_port_end = local_client_port_end #local_client_port self.dispatcher = EventDispatcher() self.lwm2m_resources = LWM2MResourceTree(self.dispatcher) self.registration = Registration(self.lwm2m_resources) self.read = Read(self.lwm2m_resources) self.write = Write(self.lwm2m_resources) self.write_attributes = WriteAttributes(self.lwm2m_resources) self.create_object_instance = Create(self.lwm2m_resources) self.observation = ObservationNotificationEngine( self.lwm2m_resources, self.dispatcher) self.execution = Execution(self.lwm2m_resources) self.discover = Discovery(lwm2m_resources=self.lwm2m_resources) self.observation_started = False def load_dm_adapter(self, dm_adapter): self.dm_adapter = dm_adapter def create_server(self, local_listener_ip=None): """ Creates and starts a local server using Gevent DatagramServer. The server listens at the ip and port specified below. A handler is used to entertain the requests coming at that port """ if local_listener_ip is not None: self.local_listener_ip = local_listener_ip self.logger.info("Local Server Created") self.logger.info("local_listener_ip %s", self.local_listener_ip) self.logger.info("local_listener_port %s", self.local_listener_port) self.local_server = DatagramServer( (self.local_listener_ip, self.local_listener_port), self.handle_lwm2m_request) self.local_server.start() def stop_server(self, ): """ Stops the local server """ self.local_server.stop() def handle_lwm2m_request(self, message, remote): """ Handles the requests coming at the specified ip and port """ rx_record = connection.ReceptionRecord(None, message, remote) msg = rx_record.message uri_query = msg.findOption(options.UriQuery) self.process(rx_record, remote, uri_query) """ Used for Create Object Instance, Execution Operation Request """ def handle_lwm2m_post(self, msg, uri_query, remote, rx_record): method = None try: method = uri_query[0].value.split("=")[1] except: pass if method == "create": path = msg.findOption(URI_PATH_VALUE) content_type_number = msg.findOption(options.ContentType) if content_type_number is None: content_type = "text/plain" else: content_type = constants.media_types[content_type_number.value] self.create_object_instance.create_instance( path, remote, content_type, loads(msg.payload)) msg = connection.Message(connection.Message.ACK, code=constants.CREATED, payload="Resource Created") self.local_server.sendto(msg._pack(rx_record.transaction_id), remote) elif method == "execute": path = msg.findOption(URI_PATH_VALUE) content_type_number = msg.findOption(options.ContentType) if content_type_number is None: content_type = "text/plain" else: content_type = constants.media_types[content_type_number.value] endpoint_name, object_id, object_inst_id, res_id, res_value = \ self.execution.execute_resource(path, remote, msg.payload) msg = connection.Message(connection.Message.ACK, code=constants.CHANGED, payload="Resource Executed") self.local_server.sendto(msg._pack(rx_record.transaction_id), remote) resource = {} resource[res_id] = {"res_value": res_value} content_type = "application/json" self.dm_adapter.update_resources(endpoint_name, object_id, object_inst_id, dumps(resource), content_type=content_type) """ It consists of Normal Update, Write Operation, Write Attribute Operation. Write Operation is used to update the resource(s) as per the request. Write Attributes operation is used to update the attributes of the object, object instance or resource. """ def handle_lwm2m_put(self, msg, remote, rx_record): uri_query = msg.findOption(options.UriQuery) method = None try: method = uri_query[0].value.split("=")[1] except: pass if method == "write": self.logger.info("Updating the Resources in the Client") path = msg.findOption(URI_PATH_VALUE) content_type_number = msg.findOption(options.ContentType) if content_type_number is None: content_type = "text/plain" else: content_type = constants.media_types[content_type_number.value] self.write.write_resource(msg.payload, path, content_type) payload_forward = msg.payload msg = connection.Message(connection.Message.ACK, code=constants.CHANGED, payload="CHANGED") self.local_server.sendto(msg._pack(rx_record.transaction_id), remote) endpoint_name, object_id, object_inst_id, res_id, res_inst_id, _, _ = OperationRequest( ).find_elements(path, remote) self.dm_adapter.update_resources(endpoint_name, object_id, object_inst_id, \ payload_forward, content_type=content_type) elif method == "write_attributes": path = msg.findOption(URI_PATH_VALUE) content_type_number = msg.findOption(options.ContentType) if content_type_number is None: content_type = "text/plain" else: content_type = constants.media_types[content_type_number.value] payload = loads(msg.payload) self.write_attributes.set_attributes(path, remote, payload) msg = connection.Message(connection.Message.ACK, code=constants.CHANGED, payload="Resource Attributes Changed") self.local_server.sendto(msg._pack(rx_record.transaction_id), remote) """ Sets the Observation. Two types of observations. General Observation and Specific Observation. General Observation is used for anything that is not observed and updates are sent as general notifications using a general token. Specific observation is implicitly defined by the observer(as request) and handled as specific notification with a specific token """ def handle_lwm2m_observe(self, msg, remote, rx_record): path = msg.findOption(URI_PATH_VALUE) if len(path) == 1: token_id = self.set_generation_observation_params(msg) payload = "General Observation Started at the Client" content_type = "text/plain" else: self.logger.info("Specific Observation Received") endpoint_name, object_id, object_inst_id, res_id, res_inst_id, _, _ = OperationRequest( ).find_elements(path, remote) token_id = msg.token payload = msg.payload self.observation.set_observation(endpoint_name, object_id, object_inst_id, res_id, token_id, payload, self.lwm2m_dm_server_ip, self.lwm2m_dm_server_port, res_inst_id=res_inst_id) msg = connection.Message(connection.Message.ACK, code=constants.CONTENT) self.local_server.sendto(msg._pack(rx_record.transaction_id, token_id), remote) """ Removes the observation from the List """ def handle_lwm2m_cancel_observe(self, msg, remote, rx_record): self.logger.info("Cancel Observation Request Received") path = msg.findOption(URI_PATH_VALUE) endpoint_name, object_id, object_inst_id, res_id, res_inst_id, _, _ = OperationRequest( ).find_elements(path, remote) token_id = msg.token payload = msg.payload message = self.observation.cancel_observation( endpoint_name, object_id, object_inst_id, res_id, token_id, payload, self.lwm2m_dm_server_ip, self.lwm2m_dm_server_port, res_inst_id=res_inst_id) msg = connection.Message(connection.Message.ACK, code=constants.CONTENT, payload=message) self.local_server.sendto(msg._pack(rx_record.transaction_id), remote) def process(self, rx_record, remote, uri_query): """ Processes various requests like CON (POST, PUT, GET) or NON. POST requests : Generally used for Registration and Execution PUT requests : Generally used for updating the resources GET requests : Generally used for Discovery, Observation, Cancel Observation """ msg = rx_record.message self.uri_query = uri_query if msg.transaction_type == connection.Message.CON: if constants.POST == msg.code: """ Used for Registration requests, Execution Operation Request """ self.handle_lwm2m_post(msg, uri_query, remote, rx_record) elif constants.PUT == msg.code: """ It consists of Normal Update, Write Operation, Write Attribute Operation. Write Operation is used to update the resource(s) as per the request. Write Attributes operation is used to update the attributes of the object, object instance or resource. """ self.handle_lwm2m_put(msg, remote, rx_record) elif constants.GET == msg.code: """ Handles Requests like Discovery, Observation """ try: observe_value = msg.findOption(options.Observe).value except: observe_value = "" if observe_value == OBSERVE_OPTION_VALUE_OBSERVATION: """ Sets the Observation. Two types of observations. General Observation and Specific Observation. General Observation is used for anything that is not observed and updates are sent as general notifications using a general token. Specific observation is implicitly defined by the observer(as request) and handled as specific notification with a specific token """ self.handle_lwm2m_observe(msg, remote, rx_record) elif observe_value == OBSERVE_OPTION_VALUE_CANCEL_OBSERVATION: """ Removes the observation from the List """ self.handle_lwm2m_cancel_observe(msg, remote, rx_record) else: uri_query = msg.findOption(options.UriQuery) method = None try: method = uri_query[0].value.split("=")[1] except: pass if method == "discover": path = msg.findOption(URI_PATH_VALUE) payload = self.discover.get_resource(path, remote) msg = connection.Message(connection.Message.ACK, code=constants.CONTENT, payload=dumps(payload)) self.local_server.sendto( msg._pack(rx_record.transaction_id), remote) def set_generation_observation_params(self, msg): listener_address = json.loads(msg.payload) listener_ip = listener_address["listener_ip"] listener_port = listener_address["listener_port"] token_id = msg.token self.general_observation = GeneralObservationInformation( listener_ip, listener_port, token_id) return token_id def send_client_registration(self, endpoint, local_client_port): """ Client registration request to the LWM2M server """ self.logger.info( "Preparing Client Registration parameters for LWM2M DM Server") registration_params = { "lt": self.lifetime, "lwm2m": self.version, "sms": self.sms_number, "b": self.binding_mode } client_object, response = self.registration.send_client_registration(endpoint, registration_params, self.lwm2m_dm_server_ip, \ self.lwm2m_dm_server_port, self.local_listener_ip, self.local_listener_port, \ local_client_port) self.client = client_object def _handle_response(response): location_address = response.findOption(LOCATION_VALUE)[0].value self.logger.debug( "The registered location address of Client in DM Server is %s", location_address) self.ep_location_mapping[endpoint.endpoint_name] = location_address temp_total_resources = deepcopy(self.total_resources) for ep_name, resdict in temp_total_resources.iteritems(): for mgmt_obj_id, resources in resdict.iteritems(): if self.ep_location_mapping.has_key(ep_name): self.logger.info( "Endpoint Location now available. Forwarding saved resources" ) self.send_add_resources(resources, ep_name, mgmt_obj_id) del self.total_resources[ep_name][mgmt_obj_id] if not any(self.total_resources[ep_name]): del self.total_resources[ep_name] return location_address return response.then(_handle_response) def load_registration_params(self, lifetime=None, version=None, sms_number=None, binding_mode=None): self.lifetime = lifetime self.version = version self.sms_number = sms_number self.binding_mode = binding_mode def local_registration(self, endpoint_name, local_client_port): """ Local registration of the resources in the local server """ self.logger.info("Local Registration Started for Endpoint: %s", endpoint_name) self_object = Endpoint(endpoint_name, objects=None, lifetime=self.lifetime, version=self.version, \ sms_number=self.sms_number, binding_mode=self.binding_mode, \ local_ip=self.local_client_ip_, local_port=local_client_port, \ listener_ip=self.local_listener_ip, listener_port=self.local_listener_port) endpoint = self_object.endpoint response = self.registration.register_client(endpoint) """ Sending Client Registration to the DM Server """ registration_location = self.send_client_registration( endpoint, local_client_port) return endpoint, registration_location def add_resource(self, emulated_device_name, lwm2m_mgmt_obj_id, lwm2m_resource_id, param_value, \ lwm2m_mgmt_obj_inst_id=None, lwm2m_resource_inst_id=None): self.logger.info("Adding Resources in the Resource Model") endpoint = self.lwm2m_resources.return_endpoint_object( emulated_device_name) resource_change_flag = self.lwm2m_resources.add_object_instance_resource_instance(endpoint, lwm2m_mgmt_obj_id, \ lwm2m_resource_id, param_value, object_inst_id=lwm2m_mgmt_obj_inst_id, \ res_inst_id=lwm2m_resource_inst_id) return resource_change_flag def send_add_resources(self, object_and_resources, endpoint_name, mgmt_obj_id_inst_id): if self.ep_location_mapping.has_key(endpoint_name): location_address = self.ep_location_mapping[endpoint_name] else: location_address = None if location_address == None: self.logger.warning( "Location couldn't be fetched !! Saving the Resources") self.res_dict[mgmt_obj_id_inst_id] = object_and_resources self.total_resources[endpoint_name] = self.res_dict else: self.logger.info("Sending Updates on the Resources..") path = location_address query_params = "" payload = json.dumps(object_and_resources) request = lwm2m_api() response = request.client_registration_update(self.lwm2m_dm_server_ip, self.lwm2m_dm_server_port, \ path, query_params, payload, \ client=self.client) def send_total_resources(self, ): #Not used currently self.logger.info("Sending Updates on the Resources") path = self.location_address query_params = "" payload = json.dumps(self.total_resources) request = lwm2m_api() response = request.client_registration_update(self.lwm2m_dm_server_ip, self.lwm2m_dm_server_port, \ path, query_params, payload, \ client_port=self.local_client_port) self.total_resources = {}