Ejemplo n.º 1
0
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)
Ejemplo n.º 2
0
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)
Ejemplo n.º 3
0
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)
Ejemplo n.º 4
0
 def __init__(self):
     self.data['timestamp'] = utils.rfc3339now()