Skip to content
Snippets Groups Projects
Select Git revision
  • 0f2283b9a2f863971f74f3c40912d3e1d7ca748d
  • develop default
  • master protected
  • feature/frontend-tests
  • 0.106
  • 0.105
  • 0.104
  • 0.103
  • 0.102
  • 0.101
  • 0.100
  • 0.99
  • 0.98
  • 0.97
  • 0.96
  • 0.95
  • 0.94
  • 0.93
  • 0.92
  • 0.91
  • 0.90
  • 0.89
  • 0.88
  • 0.87
24 results

WithLegend.tsx

Blame
  • cli.py 7.15 KiB
    """
    This script queries Inventory Provider for changes
    and configures Sensu with the snmp polling checks
    required by BRIAN.
    
    .. code-block:: console
    
        % brian-polling-manager --help
        Usage: brian-polling-manager [OPTIONS]
    
          Update BRIAN snmp checks based on Inventory Provider data.
    
        Options:
          --config TEXT         configuration filename
          --force / --no-force  update even if inventory hasn't been updated
          --help                Show this message and exit.
    
    The required configuration file must be
    formatted according to the following schema:
    
    .. asjson::
        brian_polling_manager.cli.CONFIG_SCHEMA
    
    """
    import json
    import logging
    import os
    from typing import Union
    
    import click
    import jsonschema
    
    from brian_polling_manager import inventory, interfaces, environment
    
    logger = logging.getLogger(__name__)
    
    _DEFAULT_CONFIG = {
        'inventory': [
            'http://test-inventory-provider01.geant.org:8080',
            'http://test-inventory-provider02.geant.org:8080'
        ],
        'sensu': {
            'api-base': [
                'https://test-poller-sensu-agent01.geant.org:8080',
                'https://test-poller-sensu-agent02.geant.org:8080',
                'https://test-poller-sensu-agent03.geant.org:8080'
            ],
            'token': '696a815c-607e-4090-81de-58988c83033e',
            'interface-check': {
                'script': '/var/lib/sensu/bin/counter2influx.sh',
                'measurement': 'counters',
                'interval': 300,
                'subscriptions': ['interfacecounters'],
                'output_metric_handlers': ['influx-db-handler'],
                'namespace': 'default',
                'round_robin': True,
                'command': ('{script} {measurement} '
                            '{community} {hostname} '
                            '{interface} {ifIndex}'),
            }
        },
        'statedir': '/tmp/',
        'logging': environment.DEFAULT_LOGGING_FILENAME
    }
    
    CONFIG_SCHEMA = {
        '$schema': 'http://json-schema.org/draft-07/schema#',
        'definitions': {
            'influx-check': {
                'type': 'object',
                'properties': {
                    'script': {'type': 'string'},
                    'measurement': {'type': 'string'},
                    'interval': {'type': 'integer'},
                    'subscriptions': {
                        'type': 'array',
                        'items': {'type': 'string'}
                    },
                    'output_metric_handlers': {
                        'type': 'array',
                        'items': {'type': 'string'}
                    },
                    'namespace': {'type': 'string'},
                    'round_robin': {'type': 'boolean'},
                    'command': {'type': 'string'},
                },
                'required': ['script', 'measurement', 'interval',
                             'subscriptions', 'output_metric_handlers',
                             'namespace', 'round_robin', 'command'],
                'additionalProperties': False
            },
            'sensu': {
                'type': 'object',
                'properties': {
                    'api-base': {
                        'type': 'array',
                        'items': {'type': 'string'},
                        'minItems': 1
                    },
                    'token': {'type': 'string'},
                    'interface-check': {'$ref': '#/definitions/influx-check'}
                },
                'required': ['api-base', 'token', 'interface-check'],
                'additionalProperties': False
            }
        },
        'type': 'object',
        'properties': {
            'inventory': {
                'type': 'array',
                'items': {'type': 'string'},
                'minItems': 1
            },
            'sensu': {'$ref': '#/definitions/sensu'},
            'statedir': {'type': 'string'},
            'logging': {'type': 'string'}
        },
        'required': ['inventory', 'sensu', 'statedir'],
        'additionalProperties': False
    }
    
    
    class State(object):
    
        INTERFACES = 'interfaces.json'
        STATE = 'state.json'
    
        STATE_SCHEMA = {
            '$schema': 'http://json-schema.org/draft-07/schema#',
            'type': 'object',
            'properties': {
                'last': {'type': 'number'}
            },
            'required': ['last'],
            'additionalProperties': False
        }
    
        def __init__(self, state_dir: str):
            assert os.path.isdir(state_dir)
            self.filenames = {
                'state': os.path.join(state_dir, State.STATE),
                'cache': os.path.join(state_dir, State.INTERFACES)
            }
    
        @staticmethod
        def _load_json(filename, schema):
            try:
                with open(filename) as f:
                    state = json.loads(f.read())
                    jsonschema.validate(state, schema)
                    return state
            except (json.JSONDecodeError, jsonschema.ValidationError, OSError):
                logger.exception(
                    f'unable to open state file {filename}')
                return None
    
        @property
        def last(self) -> int:
            state = State._load_json(self.filenames['state'], State.STATE_SCHEMA)
            return state['last'] if state else -1
    
        @last.setter
        def last(self, new_last: Union[float, None]):
            if not new_last or new_last < 0:
                os.unlink(self.filenames['state'])
            else:
                state = {'last': new_last}
                with open(self.filenames['state'], 'w') as f:
                    f.write(json.dumps(state))
    
        @property
        def interfaces(self) -> list:
            return State._load_json(
                self.filenames['cache'],
                inventory.INVENTORY_INTERFACES_SCHEMA)
    
        @interfaces.setter
        def interfaces(self, new_interfaces):
            try:
                jsonschema.validate(
                    new_interfaces,
                    inventory.INVENTORY_INTERFACES_SCHEMA)
            except jsonschema.ValidationError:
                logger.exception('invalid interface state data')
                return
    
            with open(self.filenames['cache'], 'w') as f:
                f.write(json.dumps(new_interfaces))
    
    
    def _validate_config(ctx, param, file):
        """
        loads, validates and returns configuration parameters
    
        :param ctx:
        :param param:
        :param value: filename (string)
        :return: a dict containing configuration parameters
        """
        if file is None:
            config = _DEFAULT_CONFIG
        else:
            try:
                config = json.loads(file.read())
            except (json.JSONDecodeError, jsonschema.ValidationError) as e:
                raise click.BadParameter(str(e))
    
        try:
            jsonschema.validate(config, CONFIG_SCHEMA)
        except jsonschema.ValidationError as e:
            raise click.BadParameter(str(e))
    
        environment.setup_logging(config.get('logging', None))
    
        return config
    
    
    @click.command()
    @click.option(
        '--config',
        default=None,
        type=click.File('r'),
        callback=_validate_config,
        help='configuration filename')
    @click.option(
        '--force/--no-force',
        default=False,
        help="refresh inventory data even if it hasn't been updated")
    def main(config, force):
        """
        Update BRIAN snmp checks based on Inventory Provider data.
        """
    
        state = State(config['statedir'])
        last = inventory.last_update_timestamp(config['inventory'])
        if force or not last or last != state.last:
            state.last = last
            state.interfaces = inventory.load_interfaces(config['inventory'])
    
        interfaces.refresh(config['sensu'], state)
    
    
    if __name__ == '__main__':
        main()