def __init__(self, port=soscoap.COAP_PORT): '''Pass in port for non-standard CoAP port. ''' self._server = CoapServer(port=port) self._server.registerForResourceGet(self._getResource) self._server.registerForResourcePut(self._putResource) self._server.registerForResourcePost(self._postResource) self._delay = 0 self._verIgnores = 0
def __init__(self, hostAddr, hostPort, sourcePort): '''Initializes on destination host and source port. Also uses sourcePort + 1 for the server to receive commands. ''' self._hostTuple = (hostAddr, hostPort) self._client = CoapClient(sourcePort=sourcePort, dest=self._hostTuple) self._client.registerForResponse(self._responseClient) self._server = CoapServer(port=sourcePort + 1) self._server.registerForResourcePost(self._postServerResource) self._registeredPaths = {} self._notificationAction = None
def __init__(self, hostAddr, hostPort, sourcePort, query): '''Initializes on destination host and source port.''' self._hostTuple = (hostAddr, hostPort) self._client = CoapClient(sourcePort=sourcePort, dest=self._hostTuple) self._client.registerForResponse(self._responseClient) self._server = CoapServer(port=5681) self._server.registerForResourcePost(self._postServerResource) self._queryName = query
def __init__(self, uripath, filename): self.uripath = uripath self.filename = filename # Must be defined for use by close(). self._chanfile = None self._server = CoapServer() self._server.registerForResourceGet(self._getResource) self._server.registerForResourcePut(self._putResource) self._server.registerForResourcePost(self._postResource)
class ConIgnoreServer(object): def __init__(self, ignores): """Pass in count of confirmable messages to ignore.""" self._server = CoapServer(port=5683) self._server.registerForResourceGet(self._getResource) self._ignores = ignores def _getResource(self, resource): """Sets the value for the provided resource, for a GET request.""" if resource.path == '/time': if self._ignores > 0: self._ignores = self._ignores - 1 raise IgnoreRequestException return else: resource.type = 'string' now = datetime.datetime.now() resource.value = now.strftime("%Y-%m-%d %H:%M").encode('ascii') else: raise NotImplementedError('Unknown path') return def start(self): self._server.start()
class GcoapObserver(object): '''Reads statistics from a RIOT gcoap URL. Attributes: :_hostuple: tuple IPv6 address tuple for message destination :_client: CoapClient Provides CoAP client for server queries :_registeredPaths: string:bytearray, where the key is the short name for the path, and the value is the token used to register for Observe notifications for the path :_server: CoapServer Provides CoAP server for remote client commands :_notificationAction: If None, sends a normal 'ACK' response for a confirmable notification. If 'reset', sends a 'RST' response, which directs the server to deregister the client from further notifications. If 'ignore', does not send a response, which also directs the server to deregister the client for a confirmable notification. Note: 'reset_non' is NOT supported. Usage: #. sr = StatsReader(hostAddr, hostPort, sourcePort, query) -- Create instance #. sr.start() -- Starts asyncore networking loop #. sr.close() -- Cleanup ''' def __init__(self, hostAddr, hostPort, sourcePort): '''Initializes on destination host and source port. Also uses sourcePort + 1 for the server to receive commands. ''' self._hostTuple = (hostAddr, hostPort) self._client = CoapClient(sourcePort=sourcePort, dest=self._hostTuple) self._client.registerForResponse(self._responseClient) self._server = CoapServer(port=sourcePort + 1) self._server.registerForResourcePost(self._postServerResource) self._registeredPaths = {} self._notificationAction = None def _responseClient(self, message): '''Reads a response to a request ''' log.debug('Running client response handler') prefix = '0' if message.codeDetail < 10 else '' obsList = message.findOption(OptionType.Observe) obsValue = '<none>' if len(obsList) == 0 else obsList[0].value obsText = 'len: {0}; val: {1}'.format(len(obsList), obsValue) print('Response code: {0}.{1}{2}; Observe {3}'.format( message.codeClass, prefix, message.codeDetail, obsText)) if message.token in self._registeredPaths.values(): if message.messageType == MessageType.CON: if self._notificationAction == 'reset': self._sendNotifResponse(message, 'reset') elif self._notificationAction == None: self._sendNotifResponse(message, 'ack') else: # no response when _notificationAction is 'ignore' pass elif message.messageType == MessageType.NON: if self._notificationAction == 'reset_non': self._sendNotifResponse(message, 'reset') def _postServerResource(self, resource): '''Reads a command ''' log.debug('Resource path is {0}'.format(resource.path)) observeAction = None observePath = None if resource.path == '/reg/stats': observeAction = 'reg' observePath = 'stats' elif resource.path == '/reg/core': observeAction = 'reg' observePath = 'core' elif resource.path == '/reg/stats2': observeAction = 'reg' observePath = 'stats2' elif resource.path == '/dereg/stats': observeAction = 'dereg' observePath = 'stats' elif resource.path == '/dereg/core': observeAction = 'dereg' observePath = 'core' elif resource.path == '/dereg/stats2': observeAction = 'dereg' observePath = 'stats2' elif resource.path == '/notif/con_ignore': self._notificationAction = 'ignore' elif resource.path == '/notif/con_reset': self._notificationAction = 'reset' elif resource.path == '/notif/non_reset': self._notificationAction = 'reset_non' elif resource.path == '/ping': print('Got ping post') if observePath: if observeAction == 'reg' and resource.pathQuery: self._query(observeAction, observePath, tokenText=resource.pathQuery) else: self._query(observeAction, observePath) def _query(self, observeAction, observePath, tokenText=None): '''Runs the reader's query. Uses a randomly generated two byte token, or the provided string encoded bytes. :param observeAction: string -- reg (register), dereg (deregister); triggers inclusion of Observe option :param observePath: string Path for register/deregister :param tokenText: string String encoding of token bytes; must by an even-numbered length of characters like '05' or '05a6' ''' # create message msg = CoapMessage(self._hostTuple) msg.messageType = MessageType.NON msg.codeClass = CodeClass.Request msg.codeDetail = RequestCode.GET msg.messageId = random.randint(0, 65535) if observePath == 'core': msg.addOption(CoapOption(OptionType.UriPath, '.well-known')) msg.addOption(CoapOption(OptionType.UriPath, 'core')) elif observePath == 'stats': msg.addOption(CoapOption(OptionType.UriPath, 'cli')) msg.addOption(CoapOption(OptionType.UriPath, 'stats')) elif observePath == 'stats2': msg.addOption(CoapOption(OptionType.UriPath, 'cli')) msg.addOption(CoapOption(OptionType.UriPath, 'stats2')) if observeAction == 'reg': # register msg.addOption(CoapOption(OptionType.Observe, 0)) if tokenText: msg.tokenLength = len(tokenText) / 2 msg.token = bytearray(msg.tokenLength) for i in range(0, msg.tokenLength): msg.token[i] = int(tokenText[2 * i:2 * (i + 1)], base=16) else: msg.tokenLength = 2 msg.token = bytearray(2) msg.token[0] = random.randint(0, 255) msg.token[1] = random.randint(0, 255) self._registeredPaths[observePath] = msg.token elif observeAction == 'dereg': # deregister msg.addOption(CoapOption(OptionType.Observe, 1)) msg.tokenLength = 2 msg.token = self._registeredPaths[observePath] # assume deregistration will succeed del self._registeredPaths[observePath] # send message log.debug('Sending query') self._client.send(msg) def _sendNotifResponse(self, notif, responseType): '''Sends an empty ACK or RST response to a notification :param notif: CoapMessage Observe notification from server ''' msg = CoapMessage(notif.address) msg.codeClass = CodeClass.Empty msg.codeDetail = ClientResponseCode.Empty msg.messageId = notif.messageId msg.tokenLength = 0 msg.token = None if responseType == 'reset': msg.messageType = MessageType.RST else: msg.messageType = MessageType.ACK log.debug('Sending {0} for notification response'.format(responseType)) self._client.send(msg) def start(self): '''Starts networking; returns when networking is stopped. Only need to start client, which automatically starts server, too. ''' self._client.start() def close(self): '''Releases resources''' self._client.close()
:return: The created host :rtype: Host ''' host = Host() host.interface_id = resource.value host.address = resource.sourceAddress[0] host.name = getInvariantName(host) # requires ipAddress host.coords = "100,100" # arbitrary values, so shows on map return host if __name__ == "__main__": logging.basicConfig(filename='nethead.log', level=logging.DEBUG, format='%(asctime)s %(module)s %(message)s') log.info('Initializing Nethead server') formattedPath = '\n\t'.join(str(p) for p in sys.path) log.info('Running server with sys.path:\n\t{0}'.format(formattedPath)) server = None try: coapServer = CoapServer() if coapServer: server = HostManager( coapServer ) coapServer.start() except KeyboardInterrupt: pass except: log.exception('Catch-all handler for Nethead server')
def __init__(self, ignores): """Pass in count of confirmable messages to ignore.""" self._server = CoapServer(port=5683) self._server.registerForResourceGet(self._getResource) self._ignores = ignores
class StatsReader(object): '''Reads statistics from a RIOT gcoap URL. NB: As shown in the Usage section below, StatsReader starts the asyncore loop before sending the query, like a server. This approach means the query must be called via another thread. Another approach would be to use asyncore's capabilities to create a single-use CoapClient and send the query at startup, in the same thread. Attributes: :_addrTuple: tuple IPv6 address tuple for message destination :_client: CoapClient Provides CoAP message protocol :_queryName: string GET query to send to CoAP server Usage: #. sr = StatsReader(hostAddr, hostPort, sourcePort, query) -- Create instance #. sr.start() -- Starts asyncore networking loop #. sr.close() -- Cleanup ''' def __init__(self, hostAddr, hostPort, sourcePort, query): '''Initializes on destination host and source port.''' self._hostTuple = (hostAddr, hostPort) self._client = CoapClient(sourcePort=sourcePort, dest=self._hostTuple) self._client.registerForResponse(self._responseClient) self._server = CoapServer(port=5681) self._server.registerForResourcePost(self._postServerResource) self._queryName = query def _responseClient(self, message): '''Reads a response to a request ''' prefix = '0' if message.codeDetail < 10 else '' obsList = message.findOption(OptionType.Observe) obsValue = '<none>' if len(obsList) == 0 else obsList[0].value obsText = 'len: {0}; val: {1}'.format(len(obsList), obsValue) print('Response code: {0}.{1}{2}; Observe {3}'.format(message.codeClass, prefix, message.codeDetail, obsText)) def _postServerResource(self, resource): '''Reads a command ''' log.debug('Resource path is {0}'.format(resource.path)) observeAction = None if resource.path == '/reg': observeAction = 'reg' elif resource.path == '/dereg': observeAction = 'dereg' self._query(observeAction) def _query(self, observeAction): '''Runs the reader's query :param observeAction: reg (register), dereg (deregister), or None; triggers inclusion of Observe option ''' # create message msg = CoapMessage(self._hostTuple) msg.messageType = MessageType.NON msg.codeClass = CodeClass.Request msg.codeDetail = RequestCode.GET msg.messageId = random.randint(0, 65535) msg.tokenLength = 2 msg.token = (0x35, 0x61) if self._queryName == 'core': msg.addOption( CoapOption(OptionType.UriPath, '.well-known') ) msg.addOption( CoapOption(OptionType.UriPath, 'core') ) elif self._queryName == 'stats': msg.addOption( CoapOption(OptionType.UriPath, 'cli') ) msg.addOption( CoapOption(OptionType.UriPath, 'stats') ) if observeAction == 'reg': # register msg.addOption( CoapOption(OptionType.Observe, 0) ) elif observeAction == 'dereg': # deregister msg.addOption( CoapOption(OptionType.Observe, 1) ) # send message log.debug('Sending query') self._client.send(msg) def start(self): '''Starts networking; returns when networking is stopped. Only need to start client, which automatically starts server, too. ''' self._client.start() def close(self): '''Releases resources''' self._client.close()
class ValueRecorder(object): '''Records the values posted to a provided URI. Records the values to a file. Attributes: :uripath: str URI for resource :filename: str Name of target file for recording :_chanfile: File Recording target :_server: CoapServer Provides CoAP message protocol Usage: #. cr = ValueRecorder(uripath, filename) -- Create instance #. cr.start() -- Starts to listen and record messages #. cr.close() -- Releases sytem resources URIs: | /ver -- GET program version | /<uripath-attribute> -- PUT/POST to <filename-attribute> file, where the attribute names are provided to the class constructor ''' def __init__(self, uripath, filename): self.uripath = uripath self.filename = filename # Must be defined for use by close(). self._chanfile = None self._server = CoapServer() self._server.registerForResourceGet(self._getResource) self._server.registerForResourcePut(self._putResource) self._server.registerForResourcePost(self._postResource) def close(self): '''Releases system resources. ''' if self._chanfile: self._chanfile.close() def _getResource(self, resource): '''Sets the value for the provided resource, for a GET request. ''' log.debug('Resource path is {0}'.format(resource.path)) if resource.path == '/ver': resource.type = 'string' resource.value = VERSION log.debug('Got resource value') else: log.debug('Unknown path') def _postResource(self, resource): '''Records the value for the provided resource, for a POST request. :param resource.value: str ASCII in CSV format, with two fields: 1. int Time 2. int Value ''' log.debug('Resource path is {0}'.format(resource.path)) if resource.path == self.uripath: self._chanfile.writelines((resource.value, '\n')) self._chanfile.flush() log.debug('POST resource value done') else: raise NotImplementedError('Unknown path') def _putResource(self, resource): '''Records the value for the provided resource, for a PUT request. :param resource.value: str ASCII in CSV format, with two fields: 1. int Time 2. int Value ''' log.debug('Resource path is {0}'.format(resource.path)) if resource.path == self.uripath: self._chanfile.writelines((resource.value, '\n')) self._chanfile.flush() log.debug('PUT resource value done') else: raise NotImplementedError('Unknown path') def start(self): '''Creates the server, and opens the file for this recorder. :raises IOError: If cannot open file ''' self._chanfile = open(self.filename, 'w') self._server.start()
class GcoapTester(object): '''Provides a server for testing gcoap client commands. Attributes: :_server: CoapServer Provides CoAP message protocol :_delay: Time in seconds to delay a response; useful for testing Usage: #. cr = GcoapTester() -- Create instance #. cr.start() -- Starts to listen #. cr.close() -- Releases sytem resources URIs: | /ver -- GET program version | /toobig -- GET large text payload. CoAP PDU exceeds 128-byte buffer used by gcoap. | /ignore -- GET that does not respond. | Configuration | /cf/delay -- POST integer seconds to delay future responses | /ver/ignores -- PUT count of /ver requests to ignore before responding; tests client retry mechanism ''' def __init__(self, port=soscoap.COAP_PORT): '''Pass in port for non-standard CoAP port. ''' self._server = CoapServer(port=port) self._server.registerForResourceGet(self._getResource) self._server.registerForResourcePut(self._putResource) self._server.registerForResourcePost(self._postResource) self._delay = 0 self._verIgnores = 0 def close(self): '''Releases system resources. ''' pass def _getResource(self, resource): '''Sets the value for the provided resource, for a GET request. ''' log.debug('Resource path is {0}'.format(resource.path)) if resource.path == '/ver': if self._verIgnores > 0: self._verIgnores = self._verIgnores - 1 raise IgnoreRequestException return else: resource.type = 'string' resource.value = VERSION elif resource.path == '/toobig': resource.type = 'string' resource.value = '1234567890' * 13 elif resource.path == '/ignore': time.sleep(self._delay) raise IgnoreRequestException return else: time.sleep(self._delay) raise NotImplementedError('Unknown path') return time.sleep(self._delay) def _postResource(self, resource): '''Accepts the value for the provided resource, for a POST request. ''' log.debug('Resource path is {0}'.format(resource.path)) if resource.path == '/cf/delay': self._delay = int(resource.value) log.debug('Post delay value: {0}'.format(self._delay)) else: time.sleep(self._delay) raise NotImplementedError('Unknown path: {0}'.format( resource.path)) def _putResource(self, resource): '''Accepts the value for the provided resource, for a PUT request. ''' if resource.path == '/ver/ignores': self._verIgnores = int(resource.value) log.debug('Ignores for /ver: {0}'.format(self._verIgnores)) else: raise NotImplementedError('Unknown path: {0}'.format( resource.path)) def start(self): '''Creates the server, and opens the file for this recorder. :raises IOError: If cannot open file ''' self._server.start()