def _make_error(error_id, exception): """Make a JSON error response.""" error = { 'http_code': exception.status_code, 'error_id': error_id, 'description': errors.codes[error_id], 'timestamp': utils.rfc3339now(), 'context': str(exception) } return json(error, status=exception.status_code)
async def get_plugins(): """The handler for the Synse Server "plugins" API command. Returns: PluginsResponse: The "plugins" response scheme model. """ logger.debug(_('Plugins Command')) # Register plugins. If no plugins exist, this will attempt to register # new ones. If plugins already exist, this will just ensure that all of # the tracked plugins are up to date. plugin.register_plugins() # We need to collect information from a few sources for each plugin: # - config (network/address) .. this should be encoded in the plugin model # - metadata (grpc metadata) .. we need to make a call for this # - health (grpc health) .. we need to make a call for this plugins = [] # FIXME (etd): as of pylint 2.1.1, this gets marked with 'not-an-iterable' # It still appears to work just fine, so need to figure out why it is getting # marked as such and what should be done to fix it. async for p in plugin.get_plugins(): # pylint: disable=not-an-iterable _plugin = p[1] # Get the plugin config and add it to the plugin data plugin_data = { 'tag': _plugin.tag, 'name': _plugin.name, 'description': _plugin.description, 'maintainer': _plugin.maintainer, 'vcs': _plugin.vcs, 'version': { 'plugin_version': _plugin.version.pluginVersion, 'sdk_version': _plugin.version.sdkVersion, 'build_date': _plugin.version.buildDate, 'git_commit': _plugin.version.gitCommit, 'git_tag': _plugin.version.gitTag, 'arch': _plugin.version.arch, 'os': _plugin.version.os, }, 'network': { 'protocol': _plugin.protocol, 'address': _plugin.address } } # Get the plugin health data try: health = _plugin.client.health() except grpc.RpcError as ex: plugin_data['health'] = { 'timestamp': utils.rfc3339now(), 'status': 'error', 'message': str(ex), 'checks': [] } else: plugin_data['health'] = { 'timestamp': health.timestamp, 'status': util.plugin_health_status_name(health.status), 'message': '', 'checks': [{ 'name': check.name, 'status': util.plugin_health_status_name(check.status), 'message': check.message, 'timestamp': check.timestamp, 'type': check.type, } for check in health.checks] } plugins.append(plugin_data) return PluginsResponse(data=plugins)
async def read(rack, board, device): """The handler for the Synse Server "read" API command. Args: rack (str): The rack which the device resides on. board (str): The board which the device resides on. device (str): The device to read. Returns: ReadResponse: The "read" response scheme model. """ logger.debug( _('Read Command (args: {}, {}, {})').format(rack, board, device)) # Lookup the known info for the specified device. plugin_name, dev = await cache.get_device_info(rack, board, device) logger.debug( _('Device {} is managed by plugin {}').format(device, plugin_name)) # Get the plugin context for the device's specified protocol. _plugin = plugin.get_plugin(plugin_name) logger.debug(_('Got plugin: {}').format(_plugin)) if not _plugin: raise errors.PluginNotFoundError( _('Unable to find plugin named "{}" to read').format(plugin_name)) try: # Perform a gRPC read on the device's managing plugin read_data = _plugin.client.read(rack, board, device) except grpc.RpcError as ex: # FIXME (etd) - this isn't the nicest way of doing this check. # this string is returned from the SDK, and its not likely to change # anytime soon, so this is "safe" for now, but we should see if there # is a better way to check this other than comparing strings.. if hasattr(ex, 'code') and hasattr(ex, 'details'): if grpc.StatusCode.NOT_FOUND == ex.code( ) and 'no readings found' in ex.details().lower(): # FIXME (etd) - with SDK v1.0, is the below correct? We should not longer # have to pass the "null" string. I think an empty string should also not # indicate no readings.. it should be the NOT_FOUND error (or, at least # some kind of error). # Currently, in the SDK, there are three different behaviors for # devices that do not have readings. Either (a). "null" is returned, # (b). an empty string ("") is returned, or (c). a gRPC error is # returned with the NOT_FOUND status code. Cases (a) and (b) are # handled in the ReadResponse initialization (below). This block # handles case (c). # # The reason for the difference between (a) and (b) is just one # of implementation. The empty string is the default value for the # gRPC read response, but sometimes it is useful to have an explict # value set to make things easier to read. # # The difference between those and (c) is more distinct. (c) should # only happen when a configured device is not being read from at all. # Essentially, (c) is the fallback for when device-specific handlers # fail to read a configured device. # # To summarize: # (a), (b) # A device is configured and the plugin's device handlers # can operate on the device. This indicates that the plugin # is working, but the device could be failing or disconnected. # # (c) # A device is configured, but the plugin's device handler # can not (or is not) able to operate on the device. This # could indicate either a plugin configuration error or # an error with the plugin logic itself. # Create empty readings for each of the device's readings. logger.warning( _('Read for {}/{}/{} returned gRPC "no readings found". Will ' 'apply None as reading value. Note that this response might ' 'indicate plugin error/misconfiguration.').format( rack, board, device)) read_data = [] for output in dev.output: read_data.append( api.Reading( timestamp=utils.rfc3339now(), type=output.type, )) else: raise errors.FailedReadCommandError(str(ex)) from ex else: raise errors.FailedReadCommandError(str(ex)) from ex return ReadResponse(device=dev, readings=read_data)
def __init__(self): self.data['timestamp'] = utils.rfc3339now()