Example #1
0
def initialize_variation_modules(variation_modules, mode):
    log.info('Initializing variation modules...')

    for name, modules_contexts in variation_modules.items():

        body = modules_contexts[0]

        for handler in ('init', 'variate', 'shutdown'):
            if handler not in body:
                log.error('missing "%s" handler at variation module '
                          '"%s"' % (handler, name))
                return 1

        try:
            body['init'](options=body['args'], mode=mode)

        except Exception as exc:
            log.error(
                'Variation module "%s" from "%s" load FAILED: '
                '%s' % (body['alias'], body['path'], exc))

        else:
            log.info(
                'Variation module "%s" from "%s" '
                'loaded OK' % (body['alias'], body['path']))
Example #2
0
def _cbFun(sendRequestHandle, errorIndication, errorStatus, errorIndex,
           varBinds, cbCtx):
    oid, value = cbCtx

    if errorIndication or errorStatus:
        log.info('notification: for %s=%r failed with errorIndication %s, '
                 'errorStatus %s' % (oid, value, errorIndication, errorStatus))
Example #3
0
    def get_handles(self):
        if self.is_open():
            if self._text_file_time != os.stat(self._text_file)[8]:
                log.info('Text file %s modified, closing' % self._text_file)
                self.close()

        if not self.is_open():
            self.create()
            self.open()

        return self._text, self._db
Example #4
0
    def configure(cls, fmt, *args):
        try:
            reporter = cls.REPORTERS[fmt]

        except KeyError:
            raise error.SnmpsimError('Unsupported reporting format: %s' % fmt)

        cls._reporter = reporter(*args)

        log.info('Using "%s" activity reporting method with '
                 'params %s' % (cls._reporter, ', '.join(args)))
Example #5
0
    def get_handles(self):
        if not self._record_index.is_open():
            if len(DataFile.opened_queue) > self.max_queue_entries:
                log.info('Closing %s' % self)
                DataFile.opened_queue[0].close()
                del DataFile.opened_queue[0]

            DataFile.opened_queue.append(self)

            log.info('Opening %s' % self)

        return self._record_index.get_handles()
Example #6
0
def init(**context):
    options = {}

    if context['options']:
        options.update(
            dict([utils.split(x, ':')
                  for x in utils.split(context['options'], ',')]))

    connectOpts = 'host', 'port', 'password', 'db', 'unix_socket'

    connectParams = dict(
        [(k, options[k]) for k in options if k in connectOpts])

    for k in 'port', 'db':
        if k in connectParams:
            connectParams[k] = int(connectParams[k])

    if not connectParams:
        raise error.SnmpsimError('Redis connect parameters not specified')

    if not redis:
        raise error.SnmpsimError('redis-py Python package must be installed!')

    moduleContext['dbConn'] = redis.StrictRedis(**connectParams)

    if context['mode'] == 'recording':
        if 'key-spaces-id' in options:
            moduleContext['key-spaces-id'] = int(options['key-spaces-id'])

        else:
            moduleContext['key-spaces-id'] = random.randrange(0, 0xffffffff)

        log.info('redis: using key-spaces-id %s' % moduleContext['key-spaces-id'])

        if 'iterations' in options:
            moduleContext['iterations'] = max(0, int(options['iterations']) - 1)

        if 'period' in options:
            moduleContext['period'] = float(options['period'])

        else:
            moduleContext['period'] = 60.0

        redisScript = options.get('evalsha')
        if redisScript:
            log.info('redis: using server-side script %s' % redisScript)

    elif context['mode'] == 'variating':
        moduleContext['booted'] = time.time()

    moduleContext['ready'] = True
Example #7
0
    def _get_call_context(self, ac_info, next_flag=False, set_flag=False):
        if ac_info is None:
            return {'nextFlag': next_flag, 'setFlag': set_flag}

        ac_fun, snmp_engine = ac_info  # we injected snmpEngine object earlier

        # this API is first introduced in pysnmp 4.2.6
        execCtx = snmp_engine.observer.getExecutionContext(
            'rfc3412.receiveMessage:request')

        (transport_domain, transport_address, security_model, security_name,
         security_level, context_engine_id, context_name,
         pdu_type) = (execCtx['transportDomain'], execCtx['transportAddress'],
                      execCtx['securityModel'], execCtx['securityName'],
                      execCtx['securityLevel'], execCtx['contextEngineId'],
                      execCtx['contextName'],
                      execCtx['pdu'].__class__.__name__)

        if isinstance(transport_address, udp.UdpTransportAddress):
            transport_protocol = 'udpv4'

        elif isinstance(transport_address, udp6.Udp6TransportAddress):
            transport_protocol = 'udpv6'

        else:
            transport_protocol = 'unknown'

        log.info('SNMP EngineID %s, transportDomain %s, transportAddress %s, '
                 'securityModel %s, securityName %s, securityLevel '
                 '%s' % (hasattr(snmp_engine, 'snmpEngineID')
                         and snmp_engine.snmpEngineID.prettyPrint()
                         or '<unknown>', transport_domain, transport_address,
                         security_model, security_name, security_level))

        return {
            'snmpEngine': snmp_engine,
            'transportDomain': rfc1902.ObjectIdentifier(transport_domain),
            'transportAddress': transport_address,
            'transportEndpoint': transport_address.getLocalAddress(),
            'transportProtocol': transport_protocol,
            'securityModel': security_model,
            'securityName': security_name,
            'securityLevel': security_level,
            'contextEngineId': context_engine_id,
            'contextName': context_name,
            'pduType': pdu_type,
            'nextFlag': next_flag,
            'setFlag': set_flag
        }
Example #8
0
def init(**context):

    if context['options']:
        for x in split(context['options'], ','):
            k, v = split(x, ':')
            if k == 'addon':
                if k in moduleContext:
                    moduleContext[k].append(v)

                else:
                    moduleContext[k] = [v]

            else:
                moduleContext[k] = v

    if context['mode'] == 'variating':
        moduleContext['booted'] = time.time()

    elif context['mode'] == 'recording':
        if 'dir' not in moduleContext:
            raise error.SnmpsimError('SNMP snapshots directory not specified')

        if not os.path.exists(moduleContext['dir']):
            log.info('multiplex: creating ' '%s...' % moduleContext['dir'])

            os.makedirs(moduleContext['dir'])

        if 'iterations' in moduleContext:
            moduleContext['iterations'] = max(
                0,
                int(moduleContext['iterations']) - 1)

        if 'period' in moduleContext:
            moduleContext['period'] = float(moduleContext['period'])

        else:
            moduleContext['period'] = 10.0

    moduleContext['ready'] = True
Example #9
0
    def create(self, force_index_build=False, validate_data=False):
        text_file_time = os.stat(self._text_file)[8]

        # gdbm on OS X seems to voluntarily append .db, trying to catch that

        index_needed = force_index_build

        for db_file in self._db_files:

            if os.path.exists(db_file):
                if text_file_time < os.stat(db_file)[8]:
                    if index_needed:
                        log.info('Forced index rebuild %s' % db_file)

                    elif not whichdb.whichdb(self._db_file):
                        index_needed = True
                        log.info('Unsupported index format, rebuilding '
                                 'index %s' % db_file)

                else:
                    index_needed = True
                    log.info('Index %s out of date' % db_file)

                break

        else:
            index_needed = True
            log.info('Index %s does not exist for data file '
                     '%s' % (self._db_file, self._text_file))

        if index_needed:
            # these might speed-up indexing
            open_flags = 'nfu'

            errors = []

            while open_flags:
                try:
                    db = dbm.open(self._db_file, open_flags)

                except Exception as exc:
                    log.debug('DBM open with flags "%s" failed on file '
                              '%s: %s' % (open_flags, self._db_file, exc))
                    errors.append(str(exc))
                    open_flags = open_flags[:-1]
                    continue

                else:
                    break
            else:
                raise error.SnmpsimError(
                    'Failed to create %s for data file '
                    '%s: %s' %
                    (self._db_file, self._text_file, '; '.join(errors)))

            try:
                text = self._text_parser.open(self._text_file)

            except Exception as exc:
                raise error.SnmpsimError('Failed to open data file %s: %s' %
                                         (self._db_file, exc))

            log.info('Building index %s for data file %s (open flags '
                     '"%s")...' % (self._db_file, self._text_file, open_flags))

            sys.stdout.flush()

            line_no = 0
            offset = 0
            prev_offset = -1

            while True:
                line, line_no, offset = get_record(text, line_no, offset)

                if not line:
                    # reference to last OID in data file
                    db['last'] = '%d,%d,%d' % (offset, 0, prev_offset)
                    break

                try:
                    oid, tag, val = self._text_parser.grammar.parse(line)

                except Exception as exc:
                    db.close()

                    for db_file in self._db_files:
                        try:
                            os.remove(db_file)

                        except OSError:
                            pass

                    raise error.SnmpsimError('Data error at %s:%d:'
                                             ' %s' %
                                             (self._text_file, line_no, exc))

                if validate_data:
                    try:
                        self._text_parser.evaluate_oid(oid)

                    except Exception as exc:
                        db.close()

                        for db_file in self._db_files:
                            try:
                                os.remove(db_file)

                            except OSError:
                                pass

                        raise error.SnmpsimError(
                            'OID error at %s:%d: %s' %
                            (self._text_file, line_no, exc))

                    try:
                        self._text_parser.evaluate_value(oid,
                                                         tag,
                                                         val,
                                                         dataValidation=True)

                    except Exception as exc:
                        log.info('ERROR at line %s, value %r: '
                                 '%s' % (line_no, val, exc))

                # for lines serving subtrees, type is empty in tag field
                db[oid] = '%d,%d,%d' % (offset, tag[0] == ':', prev_offset)

                if tag[0] == ':':
                    prev_offset = offset

                else:
                    prev_offset = -1  # not a subtree - no back reference

                offset += len(line)

            text.close()
            db.close()

            log.info('...%d entries indexed' % line_no)

        self._text_file_time = os.stat(self._text_file)[8]

        self._db_type = whichdb.whichdb(self._db_file)

        return self
Example #10
0
def main():

    parser = argparse.ArgumentParser(add_help=False)

    parser.add_argument(
        '-v', '--version', action='version',
        version=utils.TITLE)

    parser.add_argument(
        '-h', action='store_true', dest='usage',
        help='Brief usage message')

    parser.add_argument(
        '--help', action='store_true',
        help='Detailed help message')

    parser.add_argument(
        '--quiet', action='store_true',
        help='Do not print out informational messages')

    parser.add_argument(
        '--debug', choices=pysnmp_debug.flagMap,
        action='append', type=str, default=[],
        help='Enable one or more categories of SNMP debugging.')

    parser.add_argument(
        '--debug-asn1', choices=pyasn1_debug.FLAG_MAP,
        action='append', type=str, default=[],
        help='Enable one or more categories of ASN.1 debugging.')

    parser.add_argument(
        '--logging-method', type=lambda x: x.split(':'),
        metavar='=<%s[:args]>]' % '|'.join(log.METHODS_MAP),
        default='stderr', help='Logging method.')

    parser.add_argument(
        '--log-level', choices=log.LEVELS_MAP,
        type=str, default='info', help='Logging level.')

    parser.add_argument(
        '--reporting-method', type=lambda x: x.split(':'),
        metavar='=<%s[:args]>]' % '|'.join(ReportingManager.REPORTERS),
        default='null', help='Activity metrics reporting method.')

    parser.add_argument(
        '--daemonize', action='store_true',
        help='Disengage from controlling terminal and become a daemon')

    parser.add_argument(
        '--process-user', type=str,
        help='If run as root, switch simulator daemon to this user right '
             'upon binding privileged ports')

    parser.add_argument(
        '--process-group', type=str,
        help='If run as root, switch simulator daemon to this group right '
             'upon binding privileged ports')

    parser.add_argument(
        '--pid-file', metavar='<FILE>', type=str,
        default='/var/run/%s/%s.pid' % (__name__, os.getpid()),
        help='SNMP simulation data file to write records to')

    parser.add_argument(
        '--cache-dir', metavar='<DIR>', type=str,
        help='Location for SNMP simulation data file indices to create')

    parser.add_argument(
        '--force-index-rebuild', action='store_true',
        help='Rebuild simulation data files indices even if they seem '
             'up to date')

    parser.add_argument(
        '--validate-data', action='store_true',
        help='Validate simulation data files on daemon start-up')

    parser.add_argument(
        '--variation-modules-dir', metavar='<DIR>', type=str,
        action='append', default=[],
        help='Variation modules search path(s)')

    parser.add_argument(
        '--variation-module-options', metavar='<module[=alias][:args]>',
        type=str, action='append', default=[],
        help='Options for a specific variation module')

    parser.add_argument(
        '--v3-only', action='store_true',
        help='Trip legacy SNMP v1/v2c support to gain a little lesser memory '
             'footprint')

    parser.add_argument(
        '--transport-id-offset', type=int, default=0,
        help='Start numbering the last sub-OID of transport endpoint OIDs '
             'starting from this ID')

    parser.add_argument(
        '--max-var-binds', type=int, default=64,
        help='Maximum number of variable bindings to include in a single '
             'response')

    parser.add_argument(
        '--args-from-file', metavar='<FILE>', type=str,
        help='Read SNMP engine(s) command-line configuration from this '
             'file. Can be useful when command-line is too long')

    # We do not parse SNMP params with argparse, but we want its -h/--help
    snmp_helper = argparse.ArgumentParser(
        description=DESCRIPTION, add_help=False, parents=[parser])

    v3_usage = """\
Configure one or more independent SNMP engines. Each SNMP engine has a
distinct engine ID, its own set of SNMP USM users, one or more network
transport endpoints to listen on and its own simulation data directory.

Each SNMP engine configuration starts with `--v3-engine-id <arg>` parameter
followed by other configuration options up to the next `--v3-engine-id`
option or end of command line

Example
-------

$ snmp-command-responder \\
    --v3-engine-id auto \\
        --data-dir ./data --agent-udpv4-endpoint=127.0.0.1:1024 \\
    --v3-engine-id auto \\
        --data-dir ./data --agent-udpv4-endpoint=127.0.0.1:1025 \\ 
        --data-dir ./data --agent-udpv4-endpoint=127.0.0.1:1026

Besides network endpoints, simulated agents can be addressed by SNMPv1/v2c
community name or SNMPv3 context engine ID/name. These parameters are
configured automatically based on simulation data file paths relative to
`--data-dir`.
"""
    v3_group = snmp_helper.add_argument_group(v3_usage)

    v3_group.add_argument(
        '--v3-engine-id', type=str, metavar='<HEX|auto>', default='auto',
        help='SNMPv3 engine ID')

    v3_group.add_argument(
        '--v3-user', metavar='<STRING>',
        type=functools.partial(_parse_sized_string, min_length=1),
        help='SNMPv3 USM user (security) name')

    v3_group.add_argument(
        '--v3-auth-key', type=_parse_sized_string,
        help='SNMPv3 USM authentication key (must be > 8 chars)')

    v3_group.add_argument(
        '--v3-auth-proto', choices=AUTH_PROTOCOLS,
        type=lambda x: x.upper(), default='NONE',
        help='SNMPv3 USM authentication protocol')

    v3_group.add_argument(
        '--v3-priv-key', type=_parse_sized_string,
        help='SNMPv3 USM privacy (encryption) key (must be > 8 chars)')

    v3_group.add_argument(
        '--v3-priv-proto', choices=PRIV_PROTOCOLS,
        type=lambda x: x.upper(), default='NONE',
        help='SNMPv3 USM privacy (encryption) protocol')

    v3_group.add_argument(
        '--v3-context-engine-id',
        type=lambda x: univ.OctetString(hexValue=x[2:]),
        help='SNMPv3 context engine ID')

    v3_group.add_argument(
        '--v3-context-name', type=str, default='',
        help='SNMPv3 context engine ID')

    v3_group.add_argument(
        '--agent-udpv4-endpoint', type=endpoints.parse_endpoint,
        metavar='<[X.X.X.X]:NNNNN>',
        help='SNMP agent UDP/IPv4 address to listen on (name:port)')

    v3_group.add_argument(
        '--agent-udpv6-endpoint',
        type=functools.partial(endpoints.parse_endpoint, ipv6=True),
        metavar='<[X:X:..X]:NNNNN>',
        help='SNMP agent UDP/IPv6 address to listen on ([name]:port)')

    v3_group.add_argument(
        '--data-dir',
        type=str, metavar='<DIR>',
        help='SNMP simulation data recordings directory.')

    args, unparsed_args = parser.parse_known_args()

    if args.usage:
        snmp_helper.print_usage(sys.stderr)
        return 1

    if args.help:
        snmp_helper.print_help(sys.stderr)
        return 1

    _, unknown_args = snmp_helper.parse_known_args(unparsed_args)
    if unknown_args:
        sys.stderr.write(
            'ERROR: Unknown command-line parameter(s) '
            '%s\r\n' % ' '.join(unknown_args))
        snmp_helper.print_usage(sys.stderr)
        return 1

    # Reformat unparsed args into a list of (option, value) tuples
    snmp_args = []
    name = None

    for opt in unparsed_args:
        if '=' in opt:
            snmp_args.append(opt.split('='))

        elif name:
            snmp_args.append((name, opt))
            name = None

        else:
            name = opt

    if name:
        sys.stderr.write(
            'ERROR: Non-paired command-line key-value parameter '
            '%s\r\n' % name)
        snmp_helper.print_usage(sys.stderr)
        return 1

    if args.cache_dir:
        confdir.cache = args.cache_dir

    if args.variation_modules_dir:
        confdir.variation = args.variation_modules_dir

    variation_modules_options = variation.parse_modules_options(
        args.variation_module_options)

    if args.args_from_file:
        try:
            with open(args.args_from_file) as fl:
                snmp_args.extend([handler.split('=', 1) for handler in fl.read().split()])

        except Exception as exc:
            sys.stderr.write(
                'ERROR: file %s opening failure: '
                '%s\r\n' % (args.args_from_file, exc))
            snmp_helper.print_usage(sys.stderr)
            return 1

    with daemon.PrivilegesOf(args.process_user, args.process_group):

        proc_name = os.path.basename(sys.argv[0])

        try:
            log.set_logger(proc_name, *args.logging_method, force=True)

            if args.log_level:
                log.set_level(args.log_level)

        except SnmpsimError as exc:
            sys.stderr.write('%s\r\n' % exc)
            snmp_helper.print_usage(sys.stderr)
            return 1

        try:
            ReportingManager.configure(*args.reporting_method)

        except SnmpsimError as exc:
            sys.stderr.write('%s\r\n' % exc)
            snmp_helper.print_usage(sys.stderr)
            return 1

    if args.daemonize:
        try:
            daemon.daemonize(args.pid_file)

        except Exception as exc:
            sys.stderr.write(
                'ERROR: cant daemonize process: %s\r\n' % exc)
            snmp_helper.print_usage(sys.stderr)
            return 1

    if not os.path.exists(confdir.cache):
        try:
            with daemon.PrivilegesOf(args.process_user, args.process_group):
                os.makedirs(confdir.cache)

        except OSError as exc:
            log.error('failed to create cache directory "%s": '
                      '%s' % (confdir.cache, exc))
            return 1

        else:
            log.info('Cache directory "%s" created' % confdir.cache)

    variation_modules = variation.load_variation_modules(
        confdir.variation, variation_modules_options)

    with daemon.PrivilegesOf(args.process_user, args.process_group):
        variation.initialize_variation_modules(
            variation_modules, mode='variating')

    def configure_managed_objects(
            data_dirs, data_index_instrum_controller, snmp_engine=None,
            snmp_context=None):
        """Build pysnmp Managed Objects base from data files information"""

        _mib_instrums = {}
        _data_files = {}

        for dataDir in data_dirs:

            log.info(
                'Scanning "%s" directory for %s data '
                'files...' % (dataDir, ','.join(
                    [' *%s%s' % (os.path.extsep, x.ext)
                     for x in variation.RECORD_TYPES.values()])))

            if not os.path.exists(dataDir):
                log.info('Directory "%s" does not exist' % dataDir)
                continue

            log.msg.inc_ident()

            for (full_path,
                 text_parser,
                 community_name) in datafile.get_data_files(dataDir):
                if community_name in _data_files:
                    log.error(
                        'ignoring duplicate Community/ContextName "%s" for data '
                        'file %s (%s already loaded)' % (community_name, full_path,
                                                         _data_files[community_name]))
                    continue

                elif full_path in _mib_instrums:
                    mib_instrum = _mib_instrums[full_path]
                    log.info('Configuring *shared* %s' % (mib_instrum,))

                else:
                    data_file = datafile.DataFile(
                        full_path, text_parser, variation_modules)
                    data_file.index_text(args.force_index_rebuild, args.validate_data)

                    MibController = controller.MIB_CONTROLLERS[data_file.layout]
                    mib_instrum = MibController(data_file)

                    _mib_instrums[full_path] = mib_instrum
                    _data_files[community_name] = full_path

                    log.info('Configuring %s' % (mib_instrum,))

                log.info('SNMPv1/2c community name: %s' % (community_name,))

                agent_name = md5(
                    univ.OctetString(community_name).asOctets()).hexdigest()

                context_name = agent_name

                if not args.v3_only:
                    # snmpCommunityTable::snmpCommunityIndex can't be > 32
                    config.addV1System(
                        snmp_engine, agent_name, community_name,
                        contextName=context_name)

                snmp_context.registerContextName(context_name, mib_instrum)

                if len(community_name) <= 32:
                    snmp_context.registerContextName(community_name, mib_instrum)

                data_index_instrum_controller.add_data_file(
                    full_path, community_name, context_name)

                log.info(
                    'SNMPv3 Context Name: %s'
                    '%s' % (context_name, len(community_name) <= 32 and
                            ' or %s' % community_name or ''))

            log.msg.dec_ident()

        del _mib_instrums
        del _data_files

    # Bind transport endpoints
    for idx, opt in enumerate(snmp_args):
        if opt[0] == '--agent-udpv4-endpoint':
            snmp_args[idx] = (
                opt[0], endpoints.IPv4TransportEndpoints().add(opt[1]))

        elif opt[0] == '--agent-udpv6-endpoint':
            snmp_args[idx] = (
                opt[0], endpoints.IPv6TransportEndpoints().add(opt[1]))

    # Start configuring SNMP engine(s)

    transport_dispatcher = AsyncoreDispatcher()

    transport_dispatcher.registerRoutingCbFun(lambda td, t, d: td)

    if not snmp_args or snmp_args[0][0] != '--v3-engine-id':
        snmp_args.insert(0, ('--v3-engine-id', 'auto'))

    if snmp_args and snmp_args[-1][0] != 'end-of-options':
        snmp_args.append(('end-of-options', ''))

    snmp_engine = None

    transport_index = {
        'udpv4': args.transport_id_offset,
        'udpv6': args.transport_id_offset,
    }

    for opt in snmp_args:

        if opt[0] in ('--v3-engine-id', 'end-of-options'):

            if snmp_engine:

                log.info('--- SNMP Engine configuration')

                log.info(
                    'SNMPv3 EngineID: '
                    '%s' % (hasattr(snmp_engine, 'snmpEngineID')
                            and snmp_engine.snmpEngineID.prettyPrint() or '<unknown>',))

                if not v3_context_engine_ids:
                    v3_context_engine_ids.append((None, []))

                log.msg.inc_ident()

                log.info('--- Simulation data recordings configuration')

                for v3_context_engine_id, ctx_data_dirs in v3_context_engine_ids:
                    snmp_context = context.SnmpContext(snmp_engine, v3_context_engine_id)
                    # unregister default context
                    snmp_context.unregisterContextName(null)

                    log.info(
                        'SNMPv3 Context Engine ID: '
                        '%s' % snmp_context.contextEngineId.prettyPrint())

                    data_index_instrum_controller = controller.DataIndexInstrumController()

                    with daemon.PrivilegesOf(args.process_user, args.process_group):
                        configure_managed_objects(
                            ctx_data_dirs or data_dirs or confdir.data,
                            data_index_instrum_controller,
                            snmp_engine,
                            snmp_context
                        )

                # Configure access to data index

                config.addV1System(snmp_engine, 'index',
                                   'index', contextName='index')

                log.info('--- SNMPv3 USM configuration')

                if not v3_users:
                    v3_users = ['simulator']
                    v3_auth_keys[v3_users[0]] = 'auctoritas'
                    v3_auth_protos[v3_users[0]] = 'MD5'
                    v3_priv_keys[v3_users[0]] = 'privatus'
                    v3_priv_protos[v3_users[0]] = 'DES'

                for v3User in v3_users:
                    if v3User in v3_auth_keys:
                        if v3User not in v3_auth_protos:
                            v3_auth_protos[v3User] = 'MD5'

                    elif v3User in v3_auth_protos:
                        log.error(
                            'auth protocol configured without key for user '
                            '%s' % v3User)
                        return 1

                    else:
                        v3_auth_keys[v3User] = None
                        v3_auth_protos[v3User] = 'NONE'

                    if v3User in v3_priv_keys:
                        if v3User not in v3_priv_protos:
                            v3_priv_protos[v3User] = 'DES'

                    elif v3User in v3_priv_protos:
                        log.error(
                            'privacy protocol configured without key for user '
                            '%s' % v3User)
                        return 1

                    else:
                        v3_priv_keys[v3User] = None
                        v3_priv_protos[v3User] = 'NONE'

                    if (AUTH_PROTOCOLS[v3_auth_protos[v3User]] == config.usmNoAuthProtocol and
                            PRIV_PROTOCOLS[v3_priv_protos[v3User]] != config.usmNoPrivProtocol):
                        log.error(
                            'privacy impossible without authentication for USM user '
                            '%s' % v3User)
                        return 1

                    try:
                        config.addV3User(
                            snmp_engine,
                            v3User,
                            AUTH_PROTOCOLS[v3_auth_protos[v3User]],
                            v3_auth_keys[v3User],
                            PRIV_PROTOCOLS[v3_priv_protos[v3User]],
                            v3_priv_keys[v3User])

                    except error.PySnmpError as exc:
                        log.error(
                            'bad USM values for user %s: '
                            '%s' % (v3User, exc))
                        return 1

                    log.info('SNMPv3 USM SecurityName: %s' % v3User)

                    if AUTH_PROTOCOLS[v3_auth_protos[v3User]] != config.usmNoAuthProtocol:
                        log.info(
                            'SNMPv3 USM authentication key: %s, '
                            'authentication protocol: '
                            '%s' % (v3_auth_keys[v3User], v3_auth_protos[v3User]))

                    if PRIV_PROTOCOLS[v3_priv_protos[v3User]] != config.usmNoPrivProtocol:
                        log.info(
                            'SNMPv3 USM encryption (privacy) key: %s, '
                            'encryption protocol: '
                            '%s' % (v3_priv_keys[v3User], v3_priv_protos[v3User]))

                snmp_context.registerContextName('index', data_index_instrum_controller)

                log.info(
                    'Maximum number of variable bindings in SNMP response: '
                    '%s' % local_max_var_binds)

                log.info('--- Transport configuration')

                if not agent_udpv4_endpoints and not agent_udpv6_endpoints:
                    log.error(
                        'agent endpoint address(es) not specified for SNMP '
                        'engine ID %s' % v3_engine_id)
                    return 1

                for agent_udpv4_endpoint in agent_udpv4_endpoints:
                    transport_domain = udp.domainName + (transport_index['udpv4'],)
                    transport_index['udpv4'] += 1

                    snmp_engine.registerTransportDispatcher(
                        transport_dispatcher, transport_domain)

                    config.addSocketTransport(
                        snmp_engine, transport_domain, agent_udpv4_endpoint[0])

                    log.info(
                        'Listening at UDP/IPv4 endpoint %s, transport ID '
                        '%s' % (agent_udpv4_endpoint[1],
                                '.'.join([str(handler) for handler in transport_domain])))

                for agent_udpv6_endpoint in agent_udpv6_endpoints:
                    transport_domain = udp6.domainName + (transport_index['udpv6'],)
                    transport_index['udpv6'] += 1

                    snmp_engine.registerTransportDispatcher(
                        transport_dispatcher, transport_domain)

                    config.addSocketTransport(
                        snmp_engine,
                        transport_domain, agent_udpv6_endpoint[0])

                    log.info(
                        'Listening at UDP/IPv6 endpoint %s, transport ID '
                        '%s' % (agent_udpv6_endpoint[1],
                                '.'.join([str(handler) for handler in transport_domain])))

                # SNMP applications
                GetCommandResponder(snmp_engine, snmp_context)
                SetCommandResponder(snmp_engine, snmp_context)
                NextCommandResponder(snmp_engine, snmp_context)
                BulkCommandResponder(
                    snmp_engine, snmp_context).maxVarBinds = local_max_var_binds

                log.msg.dec_ident()

                if opt[0] == 'end-of-options':
                    # Load up the rest of MIBs while running privileged
                    (snmp_engine
                     .msgAndPduDsp
                     .mibInstrumController
                     .mibBuilder.loadModules())
                    break

            # Prepare for next engine ID configuration

            v3_context_engine_ids = []
            data_dirs = []
            local_max_var_binds = args.max_var_binds
            v3_users = []
            v3_auth_keys = {}
            v3_auth_protos = {}
            v3_priv_keys = {}
            v3_priv_protos = {}
            agent_udpv4_endpoints = []
            agent_udpv6_endpoints = []

            try:
                v3_engine_id = opt[1]
                if not v3_engine_id or v3_engine_id.lower() == 'auto':
                    snmp_engine = engine.SnmpEngine()

                else:
                    snmp_engine = engine.SnmpEngine(
                        snmpEngineID=univ.OctetString(hexValue=v3_engine_id))

            except Exception as exc:
                log.error(
                    'SNMPv3 Engine initialization failed, EngineID "%s": '
                    '%s' % (v3_engine_id, exc))
                return 1

            config.addContext(snmp_engine, '')

        elif opt[0] == '--v3-context-engine-id':
            v3_context_engine_ids.append((univ.OctetString(hexValue=opt[1]), []))

        elif opt[0] == '--data-dir':
            if v3_context_engine_ids:
                v3_context_engine_ids[-1][1].append(opt[1])

            else:
                data_dirs.append(opt[1])

        elif opt[0] == '--max-varbinds':
            local_max_var_binds = opt[1]

        elif opt[0] == '--v3-user':
            v3_users.append(opt[1])

        elif opt[0] == '--v3-auth-key':
            if not v3_users:
                log.error('--v3-user should precede %s' % opt[0])
                return 1

            if v3_users[-1] in v3_auth_keys:
                log.error(
                    'repetitive %s option for user %s' % (opt[0], v3_users[-1]))
                return 1

            v3_auth_keys[v3_users[-1]] = opt[1]

        elif opt[0] == '--v3-auth-proto':
            if opt[1].upper() not in AUTH_PROTOCOLS:
                log.error('bad v3 auth protocol %s' % opt[1])
                return 1

            else:
                if not v3_users:
                    log.error('--v3-user should precede %s' % opt[0])
                    return 1

                if v3_users[-1] in v3_auth_protos:
                    log.error(
                        'repetitive %s option for user %s' % (opt[0], v3_users[-1]))
                    return 1

                v3_auth_protos[v3_users[-1]] = opt[1].upper()

        elif opt[0] == '--v3-priv-key':
            if not v3_users:
                log.error('--v3-user should precede %s' % opt[0])
                return 1

            if v3_users[-1] in v3_priv_keys:
                log.error(
                    'repetitive %s option for user %s' % (opt[0], v3_users[-1]))
                return 1

            v3_priv_keys[v3_users[-1]] = opt[1]

        elif opt[0] == '--v3-priv-proto':
            if opt[1].upper() not in PRIV_PROTOCOLS:
                log.error('bad v3 privacy protocol %s' % opt[1])
                return 1

            else:
                if not v3_users:
                    log.error('--v3-user should precede %s' % opt[0])
                    return 1

                if v3_users[-1] in v3_priv_protos:
                    log.error(
                        'repetitive %s option for user %s' % (opt[0], v3_users[-1]))
                    return 1

                v3_priv_protos[v3_users[-1]] = opt[1].upper()

        elif opt[0] == '--agent-udpv4-endpoint':
            agent_udpv4_endpoints.append(opt[1])

        elif opt[0] == '--agent-udpv6-endpoint':
            agent_udpv6_endpoints.append(opt[1])

    transport_dispatcher.jobStarted(1)  # server job would never finish

    with daemon.PrivilegesOf(args.process_user, args.process_group, final=True):

        try:
            transport_dispatcher.runDispatcher()

        except KeyboardInterrupt:
            log.info('Shutting down process...')

        finally:
            if variation_modules:
                log.info('Shutting down variation modules:')

                for name, contexts in variation_modules.items():
                    body = contexts[0]
                    try:
                        body['shutdown'](options=body['args'], mode='variation')

                    except Exception as exc:
                        log.error(
                            'Variation module "%s" shutdown FAILED: '
                            '%s' % (name, exc))

                    else:
                        log.info('Variation module "%s" shutdown OK' % name)

            transport_dispatcher.closeDispatcher()

            log.info('Process terminated')

    return 0
Example #11
0
def variate(oid, tag, value, **context):
    if 'dbConn' in moduleContext:
        dbConn = moduleContext['dbConn']

    else:
        raise error.SnmpsimError('variation module not initialized')

    if 'settings' not in recordContext:
        settings = recordContext['settings'] = dict(
            [utils.split(x, '=') for x in utils.split(value, ',')])

        if 'key-spaces-id' not in settings:
            log.info('redis:mandatory key-spaces-id option is missing')
            return context['origOid'], tag, context['errorStatus']

        settings['period'] = float(settings.get('period', 60))

        if 'evalsha' in settings:
            if not dbConn.script_exists(settings['evalsha']):
                log.info('redis: lua script %s does not exist '
                        'at Redis' % settings['evalsha'])
                return context['origOid'], tag, context['errorStatus']

        recordContext['ready'] = True

    if 'ready' not in recordContext:
        return context['origOid'], tag, context['errorStatus']

    redisScript = recordContext['settings'].get('evalsha')

    keySpacesId = recordContext['settings']['key-spaces-id']

    if recordContext['settings']['period'] and dbConn.llen(keySpacesId):
        booted = time.time() - moduleContext['booted']
        keySpaceIdx = int(booted) % dbConn.llen(keySpacesId)

    else:
        keySpaceIdx = 0

    keySpace = lindex(dbConn, keySpacesId, keySpaceIdx)

    if ('current-keyspace' not in recordContext or
            recordContext['current-keyspace'] != keySpace):
        log.info('redis: now using keyspace %s (cycling period'
                ' %s)' % (
            keySpace, recordContext['settings']['period'] or '<disabled>'))

        recordContext['current-keyspace'] = keySpace

    if keySpace is None:
        return origOid, tag, context['errorStatus']

    origOid = context['origOid']
    dbOid = '.'.join(['%10s' % x for x in str(origOid).split('.')])

    if context['setFlag']:
        if 'hexvalue' in context:
            textTag = context['hextag']
            textValue = context['hexvalue']

        else:
            textTag = SnmprecGrammar().get_tag_by_type(context['origValue'])
            textValue = str(context['origValue'])

        if redisScript:
            prevTagAndValue = evalsha(dbConn, redisScript, 1, keySpace + '-' + dbOid)

        else:
            prevTagAndValue = get(dbConn, keySpace + '-' + dbOid)

        if prevTagAndValue:
            prevTag, prevValue = prevTagAndValue.split('|')

            if unpackTag(prevTag)[0] != unpackTag(textTag)[0]:
                idx = max(0, context['varsTotal'] - context['varsRemaining'] - 1)
                raise WrongValueError(name=origOid, idx=idx)

        else:
            dbConn.linsert(
                keySpace + '-oids_ordering', 'after',
                getNextOid(dbConn, keySpace, dbOid), dbOid)

        if redisScript:
            evalsha(dbConn, redisScript, 1, keySpace + '-' + dbOid,
                           textTag + '|' + textValue)

        else:
            dbConn.set(keySpace + '-' + dbOid, textTag + '|' + textValue)

        return origOid, textTag, context['origValue']

    else:
        if context['nextFlag']:
            textOid = lindex(
                dbConn, keySpace + '-oids_ordering',
                getNextOid(dbConn, keySpace, dbOid, index=True))

        else:
            textOid = keySpace + '-' + dbOid

        if redisScript:
            tagAndValue = evalsha(dbConn, redisScript, 1, textOid)

        else:
            tagAndValue = get(dbConn, textOid)

        if not tagAndValue:
            return origOid, tag, context['errorStatus']

        textOid = '.'.join(
            [x.strip() for x in textOid.split('-', 1)[1].split('.')])
        textTag, textValue = tagAndValue.split('|', 1)

        return textOid, textTag, textValue
Example #12
0
else:
    v3ContextEngineId = None
    v3ContextName = ''

try:
    log.setLogger(PROGRAM_NAME, *loggingMethod, force=True)

    if loggingLevel:
        log.setLevel(loggingLevel)

except error.SnmpsimError:
    sys.stderr.write('%s\r\n%s\r\n' % (sys.exc_info()[1], helpMessage))
    sys.exit(1)

if getBulkFlag and not snmpVersion:
    log.info('will be using GETNEXT with SNMPv1!')
    getBulkFlag = False

# Attempt to reopen std output stream in binary mode
if outputFile is sys.stderr:
    if sys.version_info[0] > 2:
        outputFile = outputFile.buffer
    elif sys.platform == "win32":
        import msvcrt

        msvcrt.setmode(outputFile.fileno(), os.O_BINARY)

# Load variation module

if variationModuleName:
    for variationModulesDir in confdir.variation:
Example #13
0
def main():

    parser = argparse.ArgumentParser(description=DESCRIPTION)

    parser.add_argument('-v',
                        '--version',
                        action='version',
                        version=utils.TITLE)

    parser.add_argument('--quiet',
                        action='store_true',
                        help='Do not print out informational messages')

    parser.add_argument(
        '--debug',
        choices=pysnmp_debug.flagMap,
        action='append',
        type=str,
        default=[],
        help='Enable one or more categories of SNMP debugging.')

    parser.add_argument(
        '--debug-asn1',
        choices=pyasn1_debug.FLAG_MAP,
        action='append',
        type=str,
        default=[],
        help='Enable one or more categories of ASN.1 debugging.')

    parser.add_argument('--logging-method',
                        type=lambda x: x.split(':'),
                        metavar='=<%s[:args]>]' % '|'.join(log.METHODS_MAP),
                        default='stderr',
                        help='Logging method.')

    parser.add_argument('--log-level',
                        choices=log.LEVELS_MAP,
                        type=str,
                        default='info',
                        help='Logging level.')

    parser.add_argument('--reporting-method',
                        type=lambda x: x.split(':'),
                        metavar='=<%s[:args]>]' %
                        '|'.join(ReportingManager.REPORTERS),
                        default='null',
                        help='Activity metrics reporting method.')

    parser.add_argument(
        '--daemonize',
        action='store_true',
        help='Disengage from controlling terminal and become a daemon')

    parser.add_argument(
        '--process-user',
        type=str,
        help='If run as root, switch simulator daemon to this user right '
        'upon binding privileged ports')

    parser.add_argument(
        '--process-group',
        type=str,
        help='If run as root, switch simulator daemon to this group right '
        'upon binding privileged ports')

    parser.add_argument('--pid-file',
                        metavar='<FILE>',
                        type=str,
                        default='/var/run/%s/%s.pid' % (__name__, os.getpid()),
                        help='SNMP simulation data file to write records to')

    parser.add_argument(
        '--cache-dir',
        metavar='<DIR>',
        type=str,
        help='Location for SNMP simulation data file indices to create')

    parser.add_argument(
        '--force-index-rebuild',
        action='store_true',
        help='Rebuild simulation data files indices even if they seem '
        'up to date')

    parser.add_argument(
        '--validate-data',
        action='store_true',
        help='Validate simulation data files on daemon start-up')

    parser.add_argument('--variation-modules-dir',
                        metavar='<DIR>',
                        type=str,
                        action='append',
                        default=[],
                        help='Variation modules search path(s)')

    parser.add_argument('--variation-module-options',
                        metavar='<module[=alias][:args]>',
                        type=str,
                        action='append',
                        default=[],
                        help='Options for a specific variation module')

    parser.add_argument(
        '--v2c-arch',
        action='store_true',
        help='Use lightweight, legacy SNMP architecture capable to support '
        'v1/v2c versions of SNMP')

    parser.add_argument(
        '--v3-only',
        action='store_true',
        help='Trip legacy SNMP v1/v2c support to gain a little lesser memory '
        'footprint')

    parser.add_argument(
        '--transport-id-offset',
        type=int,
        default=0,
        help='Start numbering the last sub-OID of transport endpoint OIDs '
        'starting from this ID')

    parser.add_argument(
        '--max-var-binds',
        type=int,
        default=64,
        help='Maximum number of variable bindings to include in a single '
        'response')

    parser.add_argument('--data-dir',
                        type=str,
                        action='append',
                        metavar='<DIR>',
                        dest='data_dirs',
                        help='SNMP simulation data recordings directory.')

    endpoint_group = parser.add_mutually_exclusive_group(required=True)

    endpoint_group.add_argument(
        '--agent-udpv4-endpoint',
        type=str,
        action='append',
        metavar='<[X.X.X.X]:NNNNN>',
        dest='agent_udpv4_endpoints',
        default=[],
        help='SNMP agent UDP/IPv4 address to listen on (name:port)')

    endpoint_group.add_argument(
        '--agent-udpv6-endpoint',
        type=str,
        action='append',
        metavar='<[X:X:..X]:NNNNN>',
        dest='agent_udpv6_endpoints',
        default=[],
        help='SNMP agent UDP/IPv6 address to listen on ([name]:port)')

    args = parser.parse_args()

    if args.cache_dir:
        confdir.cache = args.cache_dir

    if args.variation_modules_dir:
        confdir.variation = args.variation_modules_dir

    variation_modules_options = variation.parse_modules_options(
        args.variation_module_options)

    with daemon.PrivilegesOf(args.process_user, args.process_group):

        proc_name = os.path.basename(sys.argv[0])

        try:
            log.set_logger(proc_name, *args.logging_method, force=True)

            if args.log_level:
                log.set_level(args.log_level)

        except SnmpsimError as exc:
            sys.stderr.write('%s\r\n' % exc)
            parser.print_usage(sys.stderr)
            return 1

        try:
            ReportingManager.configure(*args.reporting_method)

        except SnmpsimError as exc:
            sys.stderr.write('%s\r\n' % exc)
            parser.print_usage(sys.stderr)
            return 1

    if args.daemonize:
        try:
            daemon.daemonize(args.pid_file)

        except Exception as exc:
            sys.stderr.write('ERROR: cant daemonize process: %s\r\n' % exc)
            parser.print_usage(sys.stderr)
            return 1

    if not os.path.exists(confdir.cache):
        try:
            with daemon.PrivilegesOf(args.process_user, args.process_group):
                os.makedirs(confdir.cache)

        except OSError as exc:
            log.error('failed to create cache directory "%s": '
                      '%s' % (confdir.cache, exc))
            return 1

        else:
            log.info('Cache directory "%s" created' % confdir.cache)

    variation_modules = variation.load_variation_modules(
        confdir.variation, variation_modules_options)

    with daemon.PrivilegesOf(args.process_user, args.process_group):
        variation.initialize_variation_modules(variation_modules,
                                               mode='variating')

    def configure_managed_objects(data_dirs,
                                  data_index_instrum_controller,
                                  snmp_engine=None,
                                  snmp_context=None):
        """Build pysnmp Managed Objects base from data files information"""

        _mib_instrums = {}
        _data_files = {}

        for dataDir in data_dirs:

            log.info('Scanning "%s" directory for %s data '
                     'files...' % (dataDir, ','.join([
                         ' *%s%s' % (os.path.extsep, x.ext)
                         for x in variation.RECORD_TYPES.values()
                     ])))

            if not os.path.exists(dataDir):
                log.info('Directory "%s" does not exist' % dataDir)
                continue

            log.msg.inc_ident()

            for (full_path, text_parser,
                 community_name) in datafile.get_data_files(dataDir):
                if community_name in _data_files:
                    log.error(
                        'ignoring duplicate Community/ContextName "%s" for data '
                        'file %s (%s already loaded)' %
                        (community_name, full_path,
                         _data_files[community_name]))
                    continue

                elif full_path in _mib_instrums:
                    mib_instrum = _mib_instrums[full_path]
                    log.info('Configuring *shared* %s' % (mib_instrum, ))

                else:
                    data_file = datafile.DataFile(full_path, text_parser,
                                                  variation_modules)
                    data_file.index_text(args.force_index_rebuild,
                                         args.validate_data)

                    MibController = controller.MIB_CONTROLLERS[
                        data_file.layout]
                    mib_instrum = MibController(data_file)

                    _mib_instrums[full_path] = mib_instrum
                    _data_files[community_name] = full_path

                    log.info('Configuring %s' % (mib_instrum, ))

                log.info('SNMPv1/2c community name: %s' % (community_name, ))

                contexts[univ.OctetString(community_name)] = mib_instrum

                data_index_instrum_controller.add_data_file(
                    full_path, community_name)

            log.msg.dec_ident()

        del _mib_instrums
        del _data_files

    def get_bulk_handler(req_var_binds, non_repeaters, max_repetitions,
                         read_next_vars):
        """Only v2c arch GETBULK handler"""
        N = min(int(non_repeaters), len(req_var_binds))
        M = int(max_repetitions)
        R = max(len(req_var_binds) - N, 0)

        if R:
            M = min(M, int(args.max_var_binds / R))

        if N:
            rsp_var_binds = read_next_vars(req_var_binds[:N])

        else:
            rsp_var_binds = []

        var_binds = req_var_binds[-R:]

        while M and R:
            rsp_var_binds.extend(read_next_vars(var_binds))
            var_binds = rsp_var_binds[-R:]
            M -= 1

        return rsp_var_binds

    def commandResponderCbFun(transport_dispatcher, transport_domain,
                              transport_address, whole_msg):
        """v2c arch command responder request handling callback"""
        while whole_msg:
            msg_ver = api.decodeMessageVersion(whole_msg)

            if msg_ver in api.protoModules:
                p_mod = api.protoModules[msg_ver]

            else:
                log.error('Unsupported SNMP version %s' % (msg_ver, ))
                return

            req_msg, whole_msg = decoder.decode(whole_msg,
                                                asn1Spec=p_mod.Message())

            community_name = req_msg.getComponentByPosition(1)

            for candidate in datafile.probe_context(
                    transport_domain,
                    transport_address,
                    context_engine_id=datafile.SELF_LABEL,
                    context_name=community_name):
                if candidate in contexts:
                    log.info(
                        'Using %s selected by candidate %s; transport ID %s, '
                        'source address %s, context engine ID <empty>, '
                        'community name '
                        '"%s"' % (contexts[candidate], candidate,
                                  univ.ObjectIdentifier(transport_domain),
                                  transport_address[0], community_name))
                    community_name = candidate
                    break

            else:
                log.error('No data file selected for transport ID %s, source '
                          'address %s, community name '
                          '"%s"' % (univ.ObjectIdentifier(transport_domain),
                                    transport_address[0], community_name))
                return whole_msg

            rsp_msg = p_mod.apiMessage.getResponse(req_msg)
            rsp_pdu = p_mod.apiMessage.getPDU(rsp_msg)
            req_pdu = p_mod.apiMessage.getPDU(req_msg)

            if req_pdu.isSameTypeWith(p_mod.GetRequestPDU()):
                backend_fun = contexts[community_name].readVars

            elif req_pdu.isSameTypeWith(p_mod.SetRequestPDU()):
                backend_fun = contexts[community_name].writeVars

            elif req_pdu.isSameTypeWith(p_mod.GetNextRequestPDU()):
                backend_fun = contexts[community_name].readNextVars

            elif (hasattr(p_mod, 'GetBulkRequestPDU')
                  and req_pdu.isSameTypeWith(p_mod.GetBulkRequestPDU())):

                if not msg_ver:
                    log.info('GETBULK over SNMPv1 from %s:%s' %
                             (transport_domain, transport_address))
                    return whole_msg

                def backend_fun(var_binds):
                    return get_bulk_handler(
                        var_binds, p_mod.apiBulkPDU.getNonRepeaters(req_pdu),
                        p_mod.apiBulkPDU.getMaxRepetitions(req_pdu),
                        contexts[community_name].readNextVars)

            else:
                log.error('Unsupported PDU type %s from '
                          '%s:%s' % (req_pdu.__class__.__name__,
                                     transport_domain, transport_address))
                return whole_msg

            try:
                var_binds = backend_fun(p_mod.apiPDU.getVarBinds(req_pdu))

            except NoDataNotification:
                return whole_msg

            except Exception as exc:
                log.error('Ignoring SNMP engine failure: %s' % exc)
                return whole_msg

            if not msg_ver:

                for idx in range(len(var_binds)):

                    oid, val = var_binds[idx]

                    if val.tagSet in SNMP_2TO1_ERROR_MAP:
                        var_binds = p_mod.apiPDU.getVarBinds(req_pdu)

                        p_mod.apiPDU.setErrorStatus(
                            rsp_pdu, SNMP_2TO1_ERROR_MAP[val.tagSet])
                        p_mod.apiPDU.setErrorIndex(rsp_pdu, idx + 1)

                        break

            p_mod.apiPDU.setVarBinds(rsp_pdu, var_binds)

            transport_dispatcher.sendMessage(encoder.encode(rsp_msg),
                                             transport_domain,
                                             transport_address)

        return whole_msg

    # Configure access to data index

    log.info('Maximum number of variable bindings in SNMP '
             'response: %s' % args.max_var_binds)

    data_index_instrum_controller = controller.DataIndexInstrumController()

    contexts = {univ.OctetString('index'): data_index_instrum_controller}

    with daemon.PrivilegesOf(args.process_user, args.process_group):
        configure_managed_objects(args.data_dirs or confdir.data,
                                  data_index_instrum_controller)

    contexts['index'] = data_index_instrum_controller

    # Configure socket server
    transport_dispatcher = AsyncoreDispatcher()

    transport_index = args.transport_id_offset
    for agent_udpv4_endpoint in args.agent_udpv4_endpoints:
        transport_domain = udp.domainName + (transport_index, )
        transport_index += 1

        agent_udpv4_endpoint = (
            endpoints.IPv4TransportEndpoints().add(agent_udpv4_endpoint))

        transport_dispatcher.registerTransport(transport_domain,
                                               agent_udpv4_endpoint[0])

        log.info('Listening at UDP/IPv4 endpoint %s, transport ID '
                 '%s' % (agent_udpv4_endpoint[1], '.'.join(
                     [str(handler) for handler in transport_domain])))

    transport_index = args.transport_id_offset

    for agent_udpv6_endpoint in args.agent_udpv6_endpoints:
        transport_domain = udp6.domainName + (transport_index, )
        transport_index += 1

        agent_udpv6_endpoint = (
            endpoints.IPv4TransportEndpoints().add(agent_udpv6_endpoint))

        transport_dispatcher.registerTransport(transport_domain,
                                               agent_udpv6_endpoint[0])

        log.info('Listening at UDP/IPv6 endpoint %s, transport ID '
                 '%s' % (agent_udpv6_endpoint[1], '.'.join(
                     [str(handler) for handler in transport_domain])))

    transport_dispatcher.registerRecvCbFun(commandResponderCbFun)

    transport_dispatcher.jobStarted(1)  # server job would never finish

    with daemon.PrivilegesOf(args.process_user, args.process_group,
                             final=True):

        try:
            transport_dispatcher.runDispatcher()

        except KeyboardInterrupt:
            log.info('Shutting down process...')

        finally:
            if variation_modules:
                log.info('Shutting down variation modules:')

                for name, contexts in variation_modules.items():
                    body = contexts[0]
                    try:
                        body['shutdown'](options=body['args'],
                                         mode='variation')

                    except Exception as exc:
                        log.error('Variation module "%s" shutdown FAILED: '
                                  '%s' % (name, exc))

                    else:
                        log.info('Variation module "%s" shutdown OK' % name)

            transport_dispatcher.closeDispatcher()

            log.info('Process terminated')

    return 0
Example #14
0
def variate(oid, tag, value, **context):
    # in --v2c-arch some of the items are not defined
    transport_domain = transport_address = security_model = '<undefined>'
    security_name = security_level = context_name = transport_domain

    if 'transportDomain' in context:
        transport_domain = rfc1902.ObjectName(
            context['transportDomain']).prettyPrint()

    if 'transportAddress' in context:
        transport_address = ':'.join(
            [str(x) for x in context['transportAddress']])

    if 'securityModel' in context:
        security_model = str(context['securityModel'])

    if 'securityName' in context:
        security_name = str(context['securityName'])

    if 'securityLevel' in context:
        security_level = str(context['securityLevel'])

    if 'contextName' in context:
        context_name = str(context['contextName'])

    args = [(x.replace('@TRANSPORTDOMAIN@', transport_domain).replace(
        '@TRANSPORTADDRESS@', transport_address).replace(
            '@SECURITYMODEL@',
            security_model).replace('@SECURITYNAME@', security_name).replace(
                '@SECURITYLEVEL@',
                security_level).replace('@CONTEXTNAME@', context_name).replace(
                    '@DATAFILE@',
                    context['dataFile']).replace('@OID@', str(oid)).replace(
                        '@TAG@', tag).replace('@ORIGOID@',
                                              str(context['origOid'])).
             replace('@ORIGTAG@',
                     str(sum(
                         [x
                          for x in context['origValue'].tagSet[0]]))).replace(
                              '@ORIGVALUE@',
                              str(context['origValue'])).replace(
                                  '@SETFLAG@',
                                  str(int(context['setFlag']))).replace(
                                      '@NEXTFLAG@',
                                      str(int(context['nextFlag']))).replace(
                                          '@SUBTREEFLAG@',
                                          str(int(context['subtreeFlag']))))
            for x in split(value, ' ')]

    log.info('subprocess: executing external process "%s"' % ' '.join(args))

    try:
        handler = subprocess.check_output

    except AttributeError:
        log.info('subprocess: old Python, expect no output!')

        try:
            handler = subprocess.check_call

        except AttributeError:
            handler = subprocess.call

    try:
        return oid, tag, handler(args,
                                 shell=moduleContext['settings']['shell'])

    except getattr(subprocess, 'CalledProcessError', Exception):
        log.info('subprocess: external program execution failed')
        return context['origOid'], tag, context['errorStatus']
Example #15
0
def variate(oid, tag, value, **context):
    if not context['nextFlag'] and not context['exactMatch']:
        return context['origOid'], tag, context['errorStatus']

    if 'settings' not in recordContext:
        recordContext['settings'] = dict([split(x, '=') for x in split(value, ',')])

        if 'vlist' in recordContext['settings']:

            vlist = {}

            recordContext['settings']['vlist'] = split(
                recordContext['settings']['vlist'], ':')

            while recordContext['settings']['vlist']:
                o, v, e = recordContext['settings']['vlist'][:3]

                vl = recordContext['settings']['vlist'][3:]
                recordContext['settings']['vlist'] = vl

                type_tag, _ = SnmprecRecord.unpack_tag(tag)

                v = SnmprecGrammar.TAG_MAP[type_tag](v)

                if o not in vlist:
                    vlist[o] = {}

                if o == 'eq':
                    vlist[o][v] = e

                elif o in ('lt', 'gt'):
                    vlist[o] = v, e

                else:
                    log.info('writecache: bad vlist syntax: '
                            '%s' % recordContext['settings']['vlist'])

            recordContext['settings']['vlist'] = vlist

        if 'status' in recordContext['settings']:
            st = recordContext['settings']['status'].lower()
            recordContext['settings']['status'] = st

    if oid not in moduleContext:
        moduleContext[oid] = {}

        type_tag, _ = SnmprecRecord.unpack_tag(tag)

        moduleContext[oid]['type'] = SnmprecGrammar.TAG_MAP[type_tag]()

    text_oid = str(oid)

    if context['setFlag']:
        if 'vlist' in recordContext['settings']:
            if ('eq' in recordContext['settings']['vlist'] and
                    context['origValue'] in recordContext['settings']['vlist']['eq']):
                e = recordContext['settings']['vlist']['eq'][context['origValue']]

            elif ('lt' in recordContext['settings']['vlist'] and
                    context['origValue'] < recordContext['settings']['vlist']['lt'][0]):
                e = recordContext['settings']['vlist']['lt'][1]

            elif ('gt' in recordContext['settings']['vlist'] and
                    context['origValue'] > recordContext['settings']['vlist']['gt'][0]):
                e = recordContext['settings']['vlist']['gt'][1]

            else:
                e = None

            if e in ERROR_TYPES:
                idx = max(0, context['varsTotal'] - context['varsRemaining'] - 1)
                raise ERROR_TYPES[e](name=oid, idx=idx)

        if moduleContext[oid]['type'].isSameTypeWith(context['origValue']):
            moduleContext['cache'][text_oid] = context['origValue']

        else:
            return context['origOid'], tag, context['errorStatus']

    if 'status' in recordContext['settings']:

        if ('op' not in recordContext['settings'] or
                recordContext['settings']['op'] == 'any' or
                recordContext['settings']['op'] == 'set' and context['setFlag'] or
                recordContext['settings']['op'] == 'get' and not context['setFlag']):

            e = recordContext['settings']['status']

            if e in ERROR_TYPES:
                idx = max(0, context['varsTotal'] - context['varsRemaining'] - 1)
                raise ERROR_TYPES[e](name=oid, idx=idx)

    if text_oid in moduleContext['cache']:
        return oid, tag, moduleContext['cache'][text_oid]

    elif 'hexvalue' in recordContext['settings']:
        return oid, tag, moduleContext[oid]['type'].clone(
            hexValue=recordContext['settings']['hexvalue'])

    elif 'value' in recordContext['settings']:
        return oid, tag, moduleContext[oid]['type'].clone(
            recordContext['settings']['value'])

    else:
        return oid, tag, context['errorStatus']
Example #16
0
        if isinstance(startOID, rfc1902.ObjectIdentity):
            startOID.resolveWithMib(mibViewController)

        if isinstance(stopOID, rfc1902.ObjectIdentity):
            stopOID.resolveWithMib(mibViewController)

    except PySnmpError:
        sys.stderr.write('ERROR: %s\r\n' % sys.exc_info()[1])
        sys.exit(-1)

# Load variation module

if variationModuleName:

    for variationModulesDir in confdir.variation:
        log.info('Scanning "%s" directory for variation '
                 'modules...' % variationModulesDir)

        if not os.path.exists(variationModulesDir):
            log.info('Directory "%s" does not exist' % variationModulesDir)
            continue

        mod = os.path.join(variationModulesDir, variationModuleName + '.py')
        if not os.path.exists(mod):
            log.info('Variation module "%s" not found' % mod)
            continue

        ctx = {'path': mod, 'moduleContext': {}}

        try:
            if sys.version_info[0] > 2:
                exec(compile(open(mod).read(), mod, 'exec'), ctx)
Example #17
0
def cbFun(snmpEngine, sendRequestHandle, errorIndication,
          errorStatus, errorIndex, varBindTable, cbCtx):

    if errorIndication and not cbCtx['retries']:
        cbCtx['errors'] += 1
        log.error('SNMP Engine error: %s' % errorIndication)
        return

    # SNMPv1 response may contain noSuchName error *and* SNMPv2c exception,
    # so we ignore noSuchName error here
    if errorStatus and errorStatus != 2 or errorIndication:
        log.error(
            'Remote SNMP error %s' % (
                    errorIndication or errorStatus.prettyPrint()))

        if cbCtx['retries']:
            try:
                nextOID = varBindTable[-1][0][0]

            except IndexError:
                nextOID = cbCtx['lastOID']

            else:
                log.error('Failed OID: %s' % nextOID)

            # fuzzy logic of walking a broken OID
            if len(nextOID) < 4:
                pass

            elif (continueOnErrors - cbCtx['retries']) * 10 / continueOnErrors > 5:
                nextOID = nextOID[:-2] + (nextOID[-2] + 1,)

            elif nextOID[-1]:
                nextOID = nextOID[:-1] + (nextOID[-1] + 1,)

            else:
                nextOID = nextOID[:-2] + (nextOID[-2] + 1, 0)

            cbCtx['retries'] -= 1
            cbCtx['lastOID'] = nextOID

            log.info(
                'Retrying with OID %s (%s retries left)'
                '...' % (nextOID, cbCtx['retries']))

            # initiate another SNMP walk iteration
            if getBulkFlag:
                cmdGen.sendVarBinds(
                    snmpEngine,
                    'tgt',
                    v3ContextEngineId, v3Context,
                    0, getBulkRepetitions,
                    [(nextOID, None)],
                    cbFun, cbCtx)

            else:
                cmdGen.sendVarBinds(
                    snmpEngine,
                    'tgt',
                    v3ContextEngineId, v3Context,
                    [(nextOID, None)],
                    cbFun, cbCtx)

        cbCtx['errors'] += 1

        return

    if continueOnErrors != cbCtx['retries']:
        cbCtx['retries'] += 1

    if varBindTable and varBindTable[-1] and varBindTable[-1][0]:
        cbCtx['lastOID'] = varBindTable[-1][0][0]

    stopFlag = False

    # Walk var-binds
    for varBindRow in varBindTable:
        for oid, value in varBindRow:

            # EOM
            if stopOID and oid >= stopOID:
                stopFlag = True  # stop on out of range condition

            elif (value is None or
                      value.tagSet in (rfc1905.NoSuchObject.tagSet,
                                       rfc1905.NoSuchInstance.tagSet,
                                       rfc1905.EndOfMibView.tagSet)):
                stopFlag = True

            # remove value enumeration
            if value.tagSet == rfc1902.Integer32.tagSet:
                value = rfc1902.Integer32(value)

            if value.tagSet == rfc1902.Unsigned32.tagSet:
                value = rfc1902.Unsigned32(value)

            if value.tagSet == rfc1902.Bits.tagSet:
                value = rfc1902.OctetString(value)

            # Build .snmprec record

            context = {
                'origOid': oid,
                'origValue': value,
                'count': cbCtx['count'],
                'total': cbCtx['total'],
                'iteration': cbCtx['iteration'],
                'reqTime': cbCtx['reqTime'],
                'startOID': startOID,
                'stopOID': stopOID,
                'stopFlag': stopFlag,
                'variationModule': variationModule
            }

            try:
                line = dataFileHandler.format(oid, value, **context)

            except error.MoreDataNotification:
                cbCtx['count'] = 0
                cbCtx['iteration'] += 1

                moreDataNotification = sys.exc_info()[1]

                if 'period' in moreDataNotification:
                    log.info(
                        '%s OIDs dumped, waiting %.2f sec(s)'
                        '...' % (cbCtx['total'],
                                 moreDataNotification['period']))

                    time.sleep(moreDataNotification['period'])

                # initiate another SNMP walk iteration
                if getBulkFlag:
                    cmdGen.sendVarBinds(
                        snmpEngine,
                        'tgt',
                        v3ContextEngineId, v3Context,
                        0, getBulkRepetitions,
                        [(startOID, None)],
                        cbFun, cbCtx)

                else:
                    cmdGen.sendVarBinds(
                        snmpEngine,
                        'tgt',
                        v3ContextEngineId, v3Context,
                        [(startOID, None)],
                        cbFun, cbCtx)

                stopFlag = True  # stop current iteration

            except error.NoDataNotification:
                pass

            except error.SnmpsimError:
                log.error((sys.exc_info()[1],))
                continue

            else:
                outputFile.write(line)

                cbCtx['count'] += 1
                cbCtx['total'] += 1

                if cbCtx['count'] % 100 == 0:
                    log.info('OIDs dumped: %s/%s' % (
                        cbCtx['iteration'], cbCtx['count']))

    # Next request time
    cbCtx['reqTime'] = time.time()

    # Continue walking
    return not stopFlag
Example #18
0
    v3ContextEngineId = None
    v3ContextName = ''

try:
    log.setLogger(PROGRAM_NAME, *loggingMethod, force=True)

    if loggingLevel:
        log.setLevel(loggingLevel)

except error.SnmpsimError:
    sys.stderr.write(
        '%s\r\n%s\r\n' % (sys.exc_info()[1], helpMessage))
    sys.exit(1)

if getBulkFlag and not snmpVersion:
    log.info('will be using GETNEXT with SNMPv1!')
    getBulkFlag = False

# Attempt to reopen std output stream in binary mode
if outputFile is sys.stderr:
    if sys.version_info[0] > 2:
        outputFile = outputFile.buffer

    elif sys.platform == "win32":
        import msvcrt

        msvcrt.setmode(outputFile.fileno(), os.O_BINARY)

# Load variation module

if variationModuleName:
Example #19
0
def probe_hash_context(responder, snmp_engine):
    """v3arch SNMP context name searcher"""
    execCtx = snmp_engine.observer.getExecutionContext(
        'rfc3412.receiveMessage:request')

    (transport_domain,
     transport_address,
     context_engine_id,
     context_name) = (
        execCtx['transportDomain'],
        execCtx['transportAddress'],
        execCtx['contextEngineId'],
        execCtx['contextName'].prettyPrint()
    )

    if context_engine_id == snmp_engine.snmpEngineID:
        context_engine_id = datafile.SELF_LABEL

    else:
        context_engine_id = context_engine_id.prettyPrint()

    for candidate in datafile.probe_context(
            transport_domain, transport_address,
            context_engine_id, context_name):

        if len(candidate) > 32:
            probed_context_name = md5(candidate).hexdigest()

        else:
            probed_context_name = candidate

        try:
            mib_instrum = responder.snmpContext.getMibInstrum(
                probed_context_name)

        except error.PySnmpError:
            pass

        else:
            log.info(
                'Using %s selected by candidate %s; transport ID %s, '
                'source address %s, context engine ID %s, '
                'community name '
                '"%s"' % (mib_instrum, candidate,
                          univ.ObjectIdentifier(transport_domain),
                          transport_address[0], context_engine_id,
                          probed_context_name))
            context_name = probed_context_name
            break
    else:
        mib_instrum = responder.snmpContext.getMibInstrum(context_name)
        log.info(
            'Using %s selected by contextName "%s", transport ID %s, '
            'source address %s' % (mib_instrum, context_name,
                                   univ.ObjectIdentifier(transport_domain),
                                   transport_address[0]))

    if not isinstance(mib_instrum, (
            controller.MibInstrumController,
            controller.DataIndexInstrumController)):
        log.error(
            'LCD access denied (contextName does not match any data file)')
        raise NoDataNotification()

    return context_name
Example #20
0
def load_variation_modules(search_path, modules_options):

    variation_modules = {}
    modules_options = modules_options.copy()

    for variation_modules_dir in search_path:
        log.info(
            'Scanning "%s" directory for variation '
            'modules...' % variation_modules_dir)

        if not os.path.exists(variation_modules_dir):
            log.info('Directory "%s" does not exist' % variation_modules_dir)
            continue

        for d_file in os.listdir(variation_modules_dir):
            if d_file[-3:] != '.py':
                continue

            _to_load = []

            mod_name = os.path.splitext(os.path.basename(d_file))[0]

            if mod_name in modules_options:
                while modules_options[mod_name]:
                    alias, params = modules_options[mod_name].pop()
                    _to_load.append((alias, params))

                del modules_options[mod_name]

            else:
                _to_load.append((mod_name, ''))

            mod = os.path.abspath(os.path.join(variation_modules_dir, d_file))

            for alias, params in _to_load:
                if alias in variation_modules:
                    log.error(
                        'ignoring duplicate variation module "%s" at '
                        '"%s"' % (alias, mod))
                    continue

                ctx = {
                    'path': mod,
                    'alias': alias,
                    'args': params,
                    'moduleContext': {}
                }

                try:
                    with open(mod) as fl:
                        exec(compile(fl.read(), mod, 'exec'), ctx)

                except Exception as exc:
                    log.error(
                        'Variation module "%s" execution failure: '
                        '%s' % (mod, exc))
                    return 1

                # moduleContext, agentContexts, recordContexts
                variation_modules[alias] = ctx, {}, {}

        log.info('A total of %s modules found in '
                 '%s' % (len(variation_modules), variation_modules_dir))

    if modules_options:
        log.info('WARNING: unused options for variation modules: '
                '%s' % ', '.join(modules_options))

    return variation_modules
Example #21
0
    def configure_managed_objects(data_dirs,
                                  data_index_instrum_controller,
                                  snmp_engine=None,
                                  snmp_context=None):
        """Build pysnmp Managed Objects base from data files information"""

        _mib_instrums = {}
        _data_files = {}

        for dataDir in data_dirs:

            log.info('Scanning "%s" directory for %s data '
                     'files...' % (dataDir, ','.join([
                         ' *%s%s' % (os.path.extsep, x.ext)
                         for x in variation.RECORD_TYPES.values()
                     ])))

            if not os.path.exists(dataDir):
                log.info('Directory "%s" does not exist' % dataDir)
                continue

            log.msg.inc_ident()

            for (full_path, text_parser,
                 community_name) in datafile.get_data_files(dataDir):
                if community_name in _data_files:
                    log.error(
                        'ignoring duplicate Community/ContextName "%s" for data '
                        'file %s (%s already loaded)' %
                        (community_name, full_path,
                         _data_files[community_name]))
                    continue

                elif full_path in _mib_instrums:
                    mib_instrum = _mib_instrums[full_path]
                    log.info('Configuring *shared* %s' % (mib_instrum, ))

                else:
                    data_file = datafile.DataFile(full_path, text_parser,
                                                  variation_modules)
                    data_file.index_text(args.force_index_rebuild,
                                         args.validate_data)

                    MibController = controller.MIB_CONTROLLERS[
                        data_file.layout]
                    mib_instrum = MibController(data_file)

                    _mib_instrums[full_path] = mib_instrum
                    _data_files[community_name] = full_path

                    log.info('Configuring %s' % (mib_instrum, ))

                log.info('SNMPv1/2c community name: %s' % (community_name, ))

                contexts[univ.OctetString(community_name)] = mib_instrum

                data_index_instrum_controller.add_data_file(
                    full_path, community_name)

            log.msg.dec_ident()

        del _mib_instrums
        del _data_files
Example #22
0
    def commandResponderCbFun(transport_dispatcher, transport_domain,
                              transport_address, whole_msg):
        """v2c arch command responder request handling callback"""
        while whole_msg:
            msg_ver = api.decodeMessageVersion(whole_msg)

            if msg_ver in api.protoModules:
                p_mod = api.protoModules[msg_ver]

            else:
                log.error('Unsupported SNMP version %s' % (msg_ver, ))
                return

            req_msg, whole_msg = decoder.decode(whole_msg,
                                                asn1Spec=p_mod.Message())

            community_name = req_msg.getComponentByPosition(1)

            for candidate in datafile.probe_context(
                    transport_domain,
                    transport_address,
                    context_engine_id=datafile.SELF_LABEL,
                    context_name=community_name):
                if candidate in contexts:
                    log.info(
                        'Using %s selected by candidate %s; transport ID %s, '
                        'source address %s, context engine ID <empty>, '
                        'community name '
                        '"%s"' % (contexts[candidate], candidate,
                                  univ.ObjectIdentifier(transport_domain),
                                  transport_address[0], community_name))
                    community_name = candidate
                    break

            else:
                log.error('No data file selected for transport ID %s, source '
                          'address %s, community name '
                          '"%s"' % (univ.ObjectIdentifier(transport_domain),
                                    transport_address[0], community_name))
                return whole_msg

            rsp_msg = p_mod.apiMessage.getResponse(req_msg)
            rsp_pdu = p_mod.apiMessage.getPDU(rsp_msg)
            req_pdu = p_mod.apiMessage.getPDU(req_msg)

            if req_pdu.isSameTypeWith(p_mod.GetRequestPDU()):
                backend_fun = contexts[community_name].readVars

            elif req_pdu.isSameTypeWith(p_mod.SetRequestPDU()):
                backend_fun = contexts[community_name].writeVars

            elif req_pdu.isSameTypeWith(p_mod.GetNextRequestPDU()):
                backend_fun = contexts[community_name].readNextVars

            elif (hasattr(p_mod, 'GetBulkRequestPDU')
                  and req_pdu.isSameTypeWith(p_mod.GetBulkRequestPDU())):

                if not msg_ver:
                    log.info('GETBULK over SNMPv1 from %s:%s' %
                             (transport_domain, transport_address))
                    return whole_msg

                def backend_fun(var_binds):
                    return get_bulk_handler(
                        var_binds, p_mod.apiBulkPDU.getNonRepeaters(req_pdu),
                        p_mod.apiBulkPDU.getMaxRepetitions(req_pdu),
                        contexts[community_name].readNextVars)

            else:
                log.error('Unsupported PDU type %s from '
                          '%s:%s' % (req_pdu.__class__.__name__,
                                     transport_domain, transport_address))
                return whole_msg

            try:
                var_binds = backend_fun(p_mod.apiPDU.getVarBinds(req_pdu))

            except NoDataNotification:
                return whole_msg

            except Exception as exc:
                log.error('Ignoring SNMP engine failure: %s' % exc)
                return whole_msg

            if not msg_ver:

                for idx in range(len(var_binds)):

                    oid, val = var_binds[idx]

                    if val.tagSet in SNMP_2TO1_ERROR_MAP:
                        var_binds = p_mod.apiPDU.getVarBinds(req_pdu)

                        p_mod.apiPDU.setErrorStatus(
                            rsp_pdu, SNMP_2TO1_ERROR_MAP[val.tagSet])
                        p_mod.apiPDU.setErrorIndex(rsp_pdu, idx + 1)

                        break

            p_mod.apiPDU.setVarBinds(rsp_pdu, var_binds)

            transport_dispatcher.sendMessage(encoder.encode(rsp_msg),
                                             transport_domain,
                                             transport_address)

        return whole_msg
Example #23
0
def record(oid, tag, value, **context):
    if 'ready' not in moduleContext:
        raise error.SnmpsimError('module not initialized')

    if 'started' not in moduleContext:
        moduleContext['started'] = time.time()

    if context['stopFlag']:
        if 'file' in moduleContext:
            moduleContext['file'].close()
            del moduleContext['file']

        else:
            moduleContext['filenum'] = 0

        if 'iterations' in moduleContext and moduleContext['iterations']:
            log.info('multiplex: %s iterations '
                     'remaining' % moduleContext['iterations'])

            moduleContext['started'] = time.time()
            moduleContext['iterations'] -= 1
            moduleContext['filenum'] += 1

            wait = max(
                0, moduleContext['period'] -
                (time.time() - moduleContext['started']))

            raise error.MoreDataNotification(period=wait)

        else:
            raise error.NoDataNotification()

    if 'file' not in moduleContext:
        if 'filenum' not in moduleContext:
            moduleContext['filenum'] = 0

        dstRecordType = moduleContext.get('recordtype', 'snmprec')

        ext = os.path.extsep + RECORD_SET[dstRecordType].ext

        snmprecFile = '%.5d%s%s' % (moduleContext['filenum'], os.path.extsep,
                                    ext)

        snmprecfile = os.path.join(moduleContext['dir'], snmprecFile)

        moduleContext['parser'] = RECORD_SET[dstRecordType]
        moduleContext['file'] = moduleContext['parser'].open(snmprecfile, 'wb')

        log.info('multiplex: writing into %s file...' % snmprecfile)

    record = moduleContext['parser'].format(context['origOid'],
                                            context['origValue'])

    moduleContext['file'].write(record)

    if not context['total']:
        settings = {'dir': moduleContext['dir'].replace(os.path.sep, '/')}

        if 'period' in moduleContext:
            settings['period'] = '%.2f' % float(moduleContext['period'])

        if 'addon' in moduleContext:
            settings.update(
                dict([split(x, '=') for x in moduleContext['addon']]))

        value = ','.join(['%s=%s' % (k, v) for k, v in settings.items()])

        return str(context['startOID']), ':multiplex', value

    else:
        raise error.NoDataNotification()
Example #24
0
def variate(oid, tag, value, **context):
    if not context['nextFlag'] and not context['exactMatch']:
        return context['origOid'], tag, context['errorStatus']

    if 'settings' not in recordContext:
        recordContext['settings'] = dict(
            [split(x, '=') for x in split(value, ',')])

        if 'hexvalue' in recordContext['settings']:
            recordContext['settings']['value'] = [
                int(recordContext['settings']['hexvalue'][x:x + 2], 16)
                for x in range(0, len(recordContext['settings']['hexvalue']), 2)]

        if 'status' in recordContext['settings']:
            recordContext['settings']['status'] = recordContext['settings']['status'].lower()

        if 'op' not in recordContext['settings']:
            recordContext['settings']['op'] = 'any'

        if 'vlist' in recordContext['settings']:
            vlist = {}

            recordContext['settings']['vlist'] = split(recordContext['settings']['vlist'], ':')

            while recordContext['settings']['vlist']:
                o, v, e = recordContext['settings']['vlist'][:3]

                recordContext['settings']['vlist'] = recordContext['settings']['vlist'][3:]

                typeTag, _ = SnmprecRecord.unpack_tag(tag)

                v = SnmprecGrammar.TAG_MAP[typeTag](v)

                if o not in vlist:
                    vlist[o] = {}

                if o == 'eq':
                    vlist[o][v] = e

                elif o in ('lt', 'gt'):
                    vlist[o] = v, e

                else:
                    log.info('error: bad vlist syntax: %s' % recordContext['settings']['vlist'])

            recordContext['settings']['vlist'] = vlist

    e = None

    if context['setFlag']:
        if 'vlist' in recordContext['settings']:
            if ('eq' in recordContext['settings']['vlist'] and
                    context['origValue'] in recordContext['settings']['vlist']['eq']):
                e = recordContext['settings']['vlist']['eq'][context['origValue']]

            elif ('lt' in recordContext['settings']['vlist'] and
                    context['origValue'] < recordContext['settings']['vlist']['lt'][0]):
                e = recordContext['settings']['vlist']['lt'][1]

            elif ('gt' in recordContext['settings']['vlist'] and
                    context['origValue'] > recordContext['settings']['vlist']['gt'][0]):
                e = recordContext['settings']['vlist']['gt'][1]

        elif recordContext['settings']['op'] in ('set', 'any'):
            if 'status' in recordContext['settings']:
                e = recordContext['settings']['status']

    else:
        if recordContext['settings']['op'] in ('get', 'any'):
            if 'status' in recordContext['settings']:
                e = recordContext['settings']['status']

    if e and e in ERROR_TYPES:
        log.info('error: reporting %s for %s' % (e, oid))
        raise ERROR_TYPES[e](
            name=oid, idx=max(0, context['varsTotal'] - context['varsRemaining'] - 1))

    if context['setFlag']:
        recordContext['settings']['value'] = context['origValue']

    return oid, tag, recordContext['settings'].get('value', context['errorStatus'])
Example #25
0
def variate(oid, tag, value, **context):

    if 'snmpEngine' in context and context['snmpEngine']:

        snmpEngine = context['snmpEngine']

        if snmpEngine not in moduleContext:
            moduleContext[snmpEngine] = {}

        if context['transportDomain'] not in moduleContext[snmpEngine]:
            # register this SNMP Engine to handle our transports'
            # receiver IDs (which we build by outbound and simulator
            # transportDomains concatenation)
            snmpEngine.registerTransportDispatcher(
                snmpEngine.transportDispatcher,
                UdpTransportTarget.transportDomain +
                context['transportDomain'])

            snmpEngine.registerTransportDispatcher(
                snmpEngine.transportDispatcher,
                Udp6TransportTarget.transportDomain +
                context['transportDomain'])

            moduleContext[snmpEngine][context['transportDomain']] = 1

    else:
        raise error.SnmpsimError(
            'Variation module is not given snmpEngine. '
            'Make sure you are not running in --v2c-arch mode')

    if not context['nextFlag'] and not context['exactMatch']:
        return context['origOid'], tag, context['errorStatus']

    if 'settings' not in recordContext:

        recordContext['settings'] = dict(
            [split(x, '=') for x in split(value, ',')])

        for k, v in MODULE_OPTIONS:
            recordContext['settings'].setdefault(k, v)

        if 'hexvalue' in recordContext['settings']:
            recordContext['settings']['value'] = [
                int(recordContext['settings']['hexvalue'][x:x + 2],
                    16) for x in range(
                        0, len(recordContext['settings']['hexvalue']), 2)
            ]

        if 'vlist' in recordContext['settings']:
            vlist = {}

            recordContext['settings']['vlist'] = split(
                recordContext['settings']['vlist'], ':')

            while recordContext['settings']['vlist']:

                o, v = recordContext['settings']['vlist'][:2]

                vlist = recordContext['settings']['vlist'][2:]
                recordContext['settings']['vlist'] = vlist

                typeTag, _ = SnmprecRecord.unpack_tag(tag)

                v = SnmprecGrammar.TAG_MAP[typeTag](v)

                if o not in vlist:
                    vlist[o] = set()

                if o == 'eq':
                    vlist[o].add(v)

                elif o in ('lt', 'gt'):
                    vlist[o] = v

                else:
                    log.info('notification: bad vlist syntax: '
                             '%s' % recordContext['settings']['vlist'])

            recordContext['settings']['vlist'] = vlist

    args = recordContext['settings']

    if context['setFlag'] and 'vlist' in args:
        if ('eq' in args['vlist']
                and context['origValue'] in args['vlist']['eq']):
            pass

        elif ('lt' in args['vlist']
              and context['origValue'] < args['vlist']['lt']):
            pass

        elif ('gt' in args['vlist']
              and context['origValue'] > args['vlist']['gt']):
            pass

        else:
            return oid, tag, context['origValue']

    if args['op'] not in ('get', 'set', 'any', '*'):
        log.info('notification: unknown SNMP request type configured: '
                 '%s' % args['op'])
        return context['origOid'], tag, context['errorStatus']

    if (args['op'] == 'get' and not context['setFlag']
            or args['op'] == 'set' and context['setFlag']
            or args['op'] in ('any', '*')):

        if args['version'] in ('1', '2c'):
            authData = CommunityData(args['community'],
                                     mpModel=args['version'] == '2c' and 1
                                     or 0)

        elif args['version'] == '3':
            if args['authproto'] == 'md5':
                authProtocol = usmHMACMD5AuthProtocol

            elif args['authproto'] == 'sha':
                authProtocol = usmHMACSHAAuthProtocol

            elif args['authproto'] == 'none':
                authProtocol = usmNoAuthProtocol

            else:
                log.info('notification: unknown auth proto '
                         '%s' % args['authproto'])
                return context['origOid'], tag, context['errorStatus']

            if args['privproto'] == 'des':
                privProtocol = usmDESPrivProtocol

            elif args['privproto'] == 'aes':
                privProtocol = usmAesCfb128Protocol

            elif args['privproto'] == 'none':
                privProtocol = usmNoPrivProtocol

            else:
                log.info('notification: unknown privacy proto '
                         '%s' % args['privproto'])
                return context['origOid'], tag, context['errorStatus']

            authData = UsmUserData(args['user'],
                                   args['authkey'],
                                   args['privkey'],
                                   authProtocol=authProtocol,
                                   privProtocol=privProtocol)

        else:
            log.info('notification: unknown SNMP version %s' % args['version'])
            return context['origOid'], tag, context['errorStatus']

        if 'host' not in args:
            log.info('notification: target hostname not configured for '
                     'OID %s' % (oid, ))
            return context['origOid'], tag, context['errorStatus']

        if args['proto'] == 'udp':
            target = UdpTransportTarget((args['host'], int(args['port'])))

        elif args['proto'] == 'udp6':
            target = Udp6TransportTarget((args['host'], int(args['port'])))

        else:
            log.info('notification: unknown transport %s' % args['proto'])
            return context['origOid'], tag, context['errorStatus']

        localAddress = None

        if 'bindaddr' in args:
            localAddress = args['bindaddr']

        else:
            transportDomain = context['transportDomain'][:len(target.
                                                              transportDomain)]
            if transportDomain == target.transportDomain:
                localAddress = snmpEngine.transportDispatcher.getTransport(
                    context['transportDomain']).getLocalAddress()[0]

            else:
                log.info(
                    'notification: incompatible network transport types used by '
                    'CommandResponder vs NotificationOriginator')

                if 'bindaddr' in args:
                    localAddress = args['bindaddr']

        if localAddress:
            log.info('notification: binding to local address %s' %
                     localAddress)
            target.setLocalAddress((localAddress, 0))

        # this will make target objects different based on their bind address
        target.transportDomain = target.transportDomain + context[
            'transportDomain']

        varBinds = []

        if 'uptime' in args:
            varBinds.append((ObjectIdentifier('1.3.6.1.2.1.1.3.0'),
                             TimeTicks(args['uptime'])))

        if args['version'] == '1':
            if 'agentaddress' in args:
                varBinds.append((ObjectIdentifier('1.3.6.1.6.3.18.1.3.0'),
                                 IpAddress(args['agentaddress'])))

            if 'enterprise' in args:
                varBinds.append((ObjectIdentifier('1.3.6.1.6.3.1.1.4.3.0'),
                                 ObjectIdentifier(args['enterprise'])))

        if 'varbinds' in args:
            vbs = split(args['varbinds'], ':')
            while vbs:
                varBinds.append(
                    (ObjectIdentifier(vbs[0]), TYPE_MAP[vbs[1]](vbs[2])))
                vbs = vbs[3:]

        notificationType = NotificationType(ObjectIdentity(
            args['trapoid'])).addVarBinds(*varBinds)

        sendNotification(snmpEngine,
                         authData,
                         target,
                         ContextData(),
                         args['ntftype'],
                         notificationType,
                         cbFun=_cbFun,
                         cbCtx=(oid, value))

        log.info('notification: sending Notification to %s with credentials '
                 '%s' % (authData, target))

    if context['setFlag'] or 'value' not in args:
        return oid, tag, context['origValue']

    else:
        return oid, tag, args['value']
Example #26
0
def cbFun(snmpEngine, sendRequestHandle, errorIndication, errorStatus,
          errorIndex, varBindTable, cbCtx):

    if errorIndication and not cbCtx['retries']:
        cbCtx['errors'] += 1
        log.error('SNMP Engine error: %s' % errorIndication)
        return

    # SNMPv1 response may contain noSuchName error *and* SNMPv2c exception,
    # so we ignore noSuchName error here
    if errorStatus and errorStatus != 2 or errorIndication:
        log.error('Remote SNMP error %s' %
                  (errorIndication or errorStatus.prettyPrint()))

        if cbCtx['retries']:
            try:
                nextOID = varBindTable[-1][0][0]

            except IndexError:
                nextOID = cbCtx['lastOID']

            else:
                log.error('Failed OID: %s' % nextOID)

            # fuzzy logic of walking a broken OID
            if len(nextOID) < 4:
                pass

            elif (continueOnErrors -
                  cbCtx['retries']) * 10 / continueOnErrors > 5:
                nextOID = nextOID[:-2] + (nextOID[-2] + 1, )

            elif nextOID[-1]:
                nextOID = nextOID[:-1] + (nextOID[-1] + 1, )

            else:
                nextOID = nextOID[:-2] + (nextOID[-2] + 1, 0)

            cbCtx['retries'] -= 1
            cbCtx['lastOID'] = nextOID

            log.info('Retrying with OID %s (%s retries left)...' %
                     (nextOID, cbCtx['retries']))

            # initiate another SNMP walk iteration
            if getBulkFlag:
                cmdGen.sendVarBinds(snmpEngine, 'tgt', v3ContextEngineId,
                                    v3Context, 0, getBulkRepetitions,
                                    [(nextOID, None)], cbFun, cbCtx)
            else:
                cmdGen.sendVarBinds(snmpEngine, 'tgt', v3ContextEngineId,
                                    v3Context, [(nextOID, None)], cbFun, cbCtx)

        cbCtx['errors'] += 1

        return

    if continueOnErrors != cbCtx['retries']:
        cbCtx['retries'] += 1

    if varBindTable and varBindTable[-1] and varBindTable[-1][0]:
        cbCtx['lastOID'] = varBindTable[-1][0][0]

    stopFlag = False

    # Walk var-binds
    for varBindRow in varBindTable:
        for oid, value in varBindRow:
            # EOM
            if stopOID and oid >= stopOID:
                stopFlag = True  # stop on out of range condition
            elif (value is None
                  or value.tagSet in (rfc1905.NoSuchObject.tagSet,
                                      rfc1905.NoSuchInstance.tagSet,
                                      rfc1905.EndOfMibView.tagSet)):
                stopFlag = True

            # remove value enumeration
            if value.tagSet == rfc1902.Integer32.tagSet:
                value = rfc1902.Integer32(value)

            if value.tagSet == rfc1902.Unsigned32.tagSet:
                value = rfc1902.Unsigned32(value)

            if value.tagSet == rfc1902.Bits.tagSet:
                value = rfc1902.OctetString(value)

            # Build .snmprec record

            context = {
                'origOid': oid,
                'origValue': value,
                'count': cbCtx['count'],
                'total': cbCtx['total'],
                'iteration': cbCtx['iteration'],
                'reqTime': cbCtx['reqTime'],
                'startOID': startOID,
                'stopOID': stopOID,
                'stopFlag': stopFlag,
                'variationModule': variationModule
            }

            try:
                line = dataFileHandler.format(oid, value, **context)
            except error.MoreDataNotification:
                cbCtx['count'] = 0
                cbCtx['iteration'] += 1

                moreDataNotification = sys.exc_info()[1]
                if 'period' in moreDataNotification:
                    log.info('%s OIDs dumped, waiting %.2f sec(s)...' %
                             (cbCtx['total'], moreDataNotification['period']))
                    time.sleep(moreDataNotification['period'])

                # initiate another SNMP walk iteration
                if getBulkFlag:
                    cmdGen.sendVarBinds(snmpEngine, 'tgt', v3ContextEngineId,
                                        v3Context, 0, getBulkRepetitions,
                                        [(startOID, None)], cbFun, cbCtx)
                else:
                    cmdGen.sendVarBinds(snmpEngine, 'tgt', v3ContextEngineId,
                                        v3Context, [(startOID, None)], cbFun,
                                        cbCtx)

                stopFlag = True  # stop current iteration

            except error.NoDataNotification:
                pass

            except error.SnmpsimError:
                log.error((sys.exc_info()[1], ))
                continue

            else:
                outputFile.write(line)

                cbCtx['count'] += 1
                cbCtx['total'] += 1

                if cbCtx['count'] % 100 == 0:
                    log.error('OIDs dumped: %s/%s' %
                              (cbCtx['iteration'], cbCtx['count']))

    # Next request time
    cbCtx['reqTime'] = time.time()

    # Continue walking
    return not stopFlag
Example #27
0
def main():
    variation_module = None

    parser = argparse.ArgumentParser(description=DESCRIPTION)

    parser.add_argument(
        '-v', '--version', action='version',
        version=utils.TITLE)

    parser.add_argument(
        '--quiet', action='store_true',
        help='Do not print out informational messages')

    parser.add_argument(
        '--debug', choices=pysnmp_debug.flagMap,
        action='append', type=str, default=[],
        help='Enable one or more categories of SNMP debugging.')

    parser.add_argument(
        '--debug-asn1', choices=pyasn1_debug.FLAG_MAP,
        action='append', type=str, default=[],
        help='Enable one or more categories of ASN.1 debugging.')

    parser.add_argument(
        '--logging-method', type=lambda x: x.split(':'),
        metavar='=<%s[:args]>]' % '|'.join(log.METHODS_MAP),
        default='stderr', help='Logging method.')

    parser.add_argument(
        '--log-level', choices=log.LEVELS_MAP,
        type=str, default='info', help='Logging level.')

    v1arch_group = parser.add_argument_group('SNMPv1/v2c parameters')

    v1arch_group.add_argument(
        '--protocol-version', choices=['1', '2c'],
        default='2c', help='SNMPv1/v2c protocol version')

    v1arch_group.add_argument(
        '--community', type=str, default='public',
        help='SNMP community name')

    v3arch_group = parser.add_argument_group('SNMPv3 parameters')

    v3arch_group.add_argument(
        '--v3-user', metavar='<STRING>',
        type=functools.partial(_parse_sized_string, min_length=1),
        help='SNMPv3 USM user (security) name')

    v3arch_group.add_argument(
        '--v3-auth-key', type=_parse_sized_string,
        help='SNMPv3 USM authentication key (must be > 8 chars)')

    v3arch_group.add_argument(
        '--v3-auth-proto', choices=AUTH_PROTOCOLS,
        type=lambda x: x.upper(), default='NONE',
        help='SNMPv3 USM authentication protocol')

    v3arch_group.add_argument(
        '--v3-priv-key', type=_parse_sized_string,
        help='SNMPv3 USM privacy (encryption) key (must be > 8 chars)')

    v3arch_group.add_argument(
        '--v3-priv-proto', choices=PRIV_PROTOCOLS,
        type=lambda x: x.upper(), default='NONE',
        help='SNMPv3 USM privacy (encryption) protocol')

    v3arch_group.add_argument(
        '--v3-context-engine-id',
        type=lambda x: univ.OctetString(hexValue=x[2:]),
        help='SNMPv3 context engine ID')

    v3arch_group.add_argument(
        '--v3-context-name', type=str, default='',
        help='SNMPv3 context engine ID')

    parser.add_argument(
        '--use-getbulk', action='store_true',
        help='Use SNMP GETBULK PDU for mass SNMP managed objects retrieval')

    parser.add_argument(
        '--getbulk-repetitions', type=int, default=25,
        help='Use SNMP GETBULK PDU for mass SNMP managed objects retrieval')

    endpoint_group = parser.add_mutually_exclusive_group(required=True)

    endpoint_group.add_argument(
        '--agent-udpv4-endpoint', type=endpoints.parse_endpoint,
        metavar='<[X.X.X.X]:NNNNN>',
        help='SNMP agent UDP/IPv4 address to pull simulation data '
             'from (name:port)')

    endpoint_group.add_argument(
        '--agent-udpv6-endpoint',
        type=functools.partial(endpoints.parse_endpoint, ipv6=True),
        metavar='<[X:X:..X]:NNNNN>',
        help='SNMP agent UDP/IPv6 address to pull simulation data '
             'from ([name]:port)')

    parser.add_argument(
        '--timeout', type=int, default=3,
        help='SNMP command response timeout (in seconds)')

    parser.add_argument(
        '--retries', type=int, default=3,
        help='SNMP command retries')

    parser.add_argument(
        '--start-object', metavar='<MIB::Object|OID>', type=_parse_mib_object,
        default=univ.ObjectIdentifier('1.3.6'),
        help='Drop all simulation data records prior to this OID specified '
             'as MIB object (MIB::Object) or OID (1.3.6.)')

    parser.add_argument(
        '--stop-object', metavar='<MIB::Object|OID>',
        type=functools.partial(_parse_mib_object, last=True),
        help='Drop all simulation data records after this OID specified '
             'as MIB object (MIB::Object) or OID (1.3.6.)')

    parser.add_argument(
        '--mib-source', dest='mib_sources', metavar='<URI|PATH>',
        action='append', type=str,
        default=['http://mibs.snmplabs.com/asn1/@mib@'],
        help='One or more URIs pointing to a collection of ASN.1 MIB files.'
             'Optional "@mib@" token gets replaced with desired MIB module '
             'name during MIB search.')

    parser.add_argument(
        '--destination-record-type', choices=variation.RECORD_TYPES,
        default='snmprec',
        help='Produce simulation data with record of this type')

    parser.add_argument(
        '--output-file', metavar='<FILE>', type=str,
        help='SNMP simulation data file to write records to')

    parser.add_argument(
        '--continue-on-errors', metavar='<tolerance-level>',
        type=int, default=0,
        help='Keep on pulling SNMP data even if intermittent errors occur')

    variation_group = parser.add_argument_group(
        'Simulation data variation options')

    parser.add_argument(
        '--variation-modules-dir', action='append', type=str,
        help='Search variation module by this path')

    variation_group.add_argument(
        '--variation-module', type=str,
        help='Pass gathered simulation data through this variation module')

    variation_group.add_argument(
        '--variation-module-options', type=str, default='',
        help='Variation module options')

    args = parser.parse_args()

    if args.debug:
        pysnmp_debug.setLogger(pysnmp_debug.Debug(*args.debug))

    if args.debug_asn1:
        pyasn1_debug.setLogger(pyasn1_debug.Debug(*args.debug_asn1))

    if args.output_file:
        ext = os.path.extsep
        ext += variation.RECORD_TYPES[args.destination_record_type].ext

        if not args.output_file.endswith(ext):
            args.output_file += ext

        record = variation.RECORD_TYPES[args.destination_record_type]
        args.output_file = record.open(args.output_file, 'wb')

    else:
        args.output_file = sys.stdout

        if sys.version_info >= (3, 0, 0):
            # binary mode write
            args.output_file = sys.stdout.buffer

        elif sys.platform == "win32":
            import msvcrt

            msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)

    # Catch missing params

    if args.protocol_version == '3':
        if not args.v3_user:
            sys.stderr.write('ERROR: --v3-user is missing\r\n')
            parser.print_usage(sys.stderr)
            return 1

        if args.v3_priv_key and not args.v3_auth_key:
            sys.stderr.write('ERROR: --v3-auth-key is missing\r\n')
            parser.print_usage(sys.stderr)
            return 1

        if AUTH_PROTOCOLS[args.v3_auth_proto] == config.usmNoAuthProtocol:
            if args.v3_auth_key:
                args.v3_auth_proto = 'MD5'

        else:
            if not args.v3_auth_key:
                sys.stderr.write('ERROR: --v3-auth-key is missing\r\n')
                parser.print_usage(sys.stderr)
                return 1

        if PRIV_PROTOCOLS[args.v3_priv_proto] == config.usmNoPrivProtocol:
            if args.v3_priv_key:
                args.v3_priv_proto = 'DES'

        else:
            if not args.v3_priv_key:
                sys.stderr.write('ERROR: --v3-priv-key is missing\r\n')
                parser.print_usage(sys.stderr)
                return 1

    proc_name = os.path.basename(sys.argv[0])

    try:
        log.set_logger(proc_name, *args.logging_method, force=True)

        if args.log_level:
            log.set_level(args.log_level)

    except error.SnmpsimError as exc:
        sys.stderr.write('%s\r\n' % exc)
        parser.print_usage(sys.stderr)
        return 1

    if args.use_getbulk and args.protocol_version == '1':
        log.info('will be using GETNEXT with SNMPv1!')
        args.use_getbulk = False

    # Load variation module

    if args.variation_module:

        for variation_modules_dir in (
                args.variation_modules_dir or confdir.variation):
            log.info(
                'Scanning "%s" directory for variation '
                'modules...' % variation_modules_dir)

            if not os.path.exists(variation_modules_dir):
                log.info('Directory "%s" does not exist' % variation_modules_dir)
                continue

            mod = os.path.join(variation_modules_dir, args.variation_module + '.py')
            if not os.path.exists(mod):
                log.info('Variation module "%s" not found' % mod)
                continue

            ctx = {'path': mod, 'moduleContext': {}}

            try:
                with open(mod) as fl:
                    exec (compile(fl.read(), mod, 'exec'), ctx)

            except Exception as exc:
                log.error('Variation module "%s" execution failure: '
                          '%s' % (mod, exc))
                return 1

            variation_module = ctx
            log.info('Variation module "%s" loaded' % args.variation_module)
            break

        else:
            log.error('variation module "%s" not found' % args.variation_module)
            return 1

    # SNMP configuration

    snmp_engine = engine.SnmpEngine()

    if args.protocol_version == '3':

        if args.v3_priv_key is None and args.v3_auth_key is None:
            secLevel = 'noAuthNoPriv'

        elif args.v3_priv_key is None:
            secLevel = 'authNoPriv'

        else:
            secLevel = 'authPriv'

        config.addV3User(
            snmp_engine, args.v3_user,
            AUTH_PROTOCOLS[args.v3_auth_proto], args.v3_auth_key,
            PRIV_PROTOCOLS[args.v3_priv_proto], args.v3_priv_key)

        log.info(
            'SNMP version 3, Context EngineID: %s Context name: %s, SecurityName: %s, '
            'SecurityLevel: %s, Authentication key/protocol: %s/%s, Encryption '
            '(privacy) key/protocol: '
            '%s/%s' % (
                args.v3_context_engine_id and args.v3_context_engine_id.prettyPrint() or '<default>',
                args.v3_context_name and args.v3_context_name.prettyPrint() or '<default>', args.v3_user,
                secLevel, args.v3_auth_key is None and '<NONE>' or args.v3_auth_key,
                args.v3_auth_proto,
                args.v3_priv_key is None and '<NONE>' or args.v3_priv_key, args.v3_priv_proto))

    else:

        args.v3_user = '******'
        secLevel = 'noAuthNoPriv'

        config.addV1System(snmp_engine, args.v3_user, args.community)

        log.info(
            'SNMP version %s, Community name: '
            '%s' % (args.protocol_version, args.community))

    config.addTargetParams(
        snmp_engine, 'pms', args.v3_user, secLevel, VERSION_MAP[args.protocol_version])

    if args.agent_udpv6_endpoint:
        config.addSocketTransport(
            snmp_engine, udp6.domainName,
            udp6.Udp6SocketTransport().openClientMode())

        config.addTargetAddr(
            snmp_engine, 'tgt', udp6.domainName, args.agent_udpv6_endpoint, 'pms',
            args.timeout * 100, args.retries)

        log.info('Querying UDP/IPv6 agent at [%s]:%s' % args.agent_udpv6_endpoint)

    elif args.agent_udpv4_endpoint:
        config.addSocketTransport(
            snmp_engine, udp.domainName,
            udp.UdpSocketTransport().openClientMode())

        config.addTargetAddr(
            snmp_engine, 'tgt', udp.domainName, args.agent_udpv4_endpoint, 'pms',
            args.timeout * 100, args.retries)

        log.info('Querying UDP/IPv4 agent at %s:%s' % args.agent_udpv4_endpoint)

    log.info('Agent response timeout: %d secs, retries: '
             '%s' % (args.timeout, args.retries))

    if (isinstance(args.start_object, ObjectIdentity) or
            isinstance(args.stop_object, ObjectIdentity)):

        compiler.addMibCompiler(
            snmp_engine.getMibBuilder(), sources=args.mib_sources)

        mib_view_controller = view.MibViewController(
            snmp_engine.getMibBuilder())

        try:
            if isinstance(args.start_object, ObjectIdentity):
                args.start_object.resolveWithMib(mib_view_controller)

            if isinstance(args.stop_object, ObjectIdentity):
                args.stop_object.resolveWithMib(mib_view_controller)

        except PySnmpError as exc:
            sys.stderr.write('ERROR: %s\r\n' % exc)
            return 1

    # Variation module initialization

    if variation_module:
        log.info('Initializing variation module...')

        for x in ('init', 'record', 'shutdown'):
            if x not in variation_module:
                log.error('missing "%s" handler at variation module '
                          '"%s"' % (x, args.variation_module))
                return 1

        try:
            handler = variation_module['init']

            handler(snmpEngine=snmp_engine, options=args.variation_module_options,
                    mode='recording', startOID=args.start_object,
                    stopOID=args.stop_object)

        except Exception as exc:
            log.error(
                'Variation module "%s" initialization FAILED: '
                '%s' % (args.variation_module, exc))

        else:
            log.info(
                'Variation module "%s" initialization OK' % args.variation_module)

    data_file_handler = variation.RECORD_TYPES[args.destination_record_type]


    # SNMP worker

    def cbFun(snmp_engine, send_request_handle, error_indication,
              error_status, error_index, var_bind_table, cb_ctx):

        if error_indication and not cb_ctx['retries']:
            cb_ctx['errors'] += 1
            log.error('SNMP Engine error: %s' % error_indication)
            return

        # SNMPv1 response may contain noSuchName error *and* SNMPv2c exception,
        # so we ignore noSuchName error here
        if error_status and error_status != 2 or error_indication:
            log.error(
                'Remote SNMP error %s' % (
                        error_indication or error_status.prettyPrint()))

            if cb_ctx['retries']:
                try:
                    next_oid = var_bind_table[-1][0][0]

                except IndexError:
                    next_oid = cb_ctx['lastOID']

                else:
                    log.error('Failed OID: %s' % next_oid)

                # fuzzy logic of walking a broken OID
                if len(next_oid) < 4:
                    pass

                elif (args.continue_on_errors - cb_ctx['retries']) * 10 / args.continue_on_errors > 5:
                    next_oid = next_oid[:-2] + (next_oid[-2] + 1,)

                elif next_oid[-1]:
                    next_oid = next_oid[:-1] + (next_oid[-1] + 1,)

                else:
                    next_oid = next_oid[:-2] + (next_oid[-2] + 1, 0)

                cb_ctx['retries'] -= 1
                cb_ctx['lastOID'] = next_oid

                log.info(
                    'Retrying with OID %s (%s retries left)'
                    '...' % (next_oid, cb_ctx['retries']))

                # initiate another SNMP walk iteration
                if args.use_getbulk:
                    cmd_gen.sendVarBinds(
                        snmp_engine,
                        'tgt',
                        args.v3_context_engine_id, args.v3_context_name,
                        0, args.getbulk_repetitions,
                        [(next_oid, None)],
                        cbFun, cb_ctx)

                else:
                    cmd_gen.sendVarBinds(
                        snmp_engine,
                        'tgt',
                        args.v3_context_engine_id, args.v3_context_name,
                        [(next_oid, None)],
                        cbFun, cb_ctx)

            cb_ctx['errors'] += 1

            return

        if args.continue_on_errors != cb_ctx['retries']:
            cb_ctx['retries'] += 1

        if var_bind_table and var_bind_table[-1] and var_bind_table[-1][0]:
            cb_ctx['lastOID'] = var_bind_table[-1][0][0]

        stop_flag = False

        # Walk var-binds
        for var_bind_row in var_bind_table:
            for oid, value in var_bind_row:

                # EOM
                if args.stop_object and oid >= args.stop_object:
                    stop_flag = True  # stop on out of range condition

                elif (value is None or
                          value.tagSet in (rfc1905.NoSuchObject.tagSet,
                                           rfc1905.NoSuchInstance.tagSet,
                                           rfc1905.EndOfMibView.tagSet)):
                    stop_flag = True

                # remove value enumeration
                if value.tagSet == rfc1902.Integer32.tagSet:
                    value = rfc1902.Integer32(value)

                if value.tagSet == rfc1902.Unsigned32.tagSet:
                    value = rfc1902.Unsigned32(value)

                if value.tagSet == rfc1902.Bits.tagSet:
                    value = rfc1902.OctetString(value)

                # Build .snmprec record

                context = {
                    'origOid': oid,
                    'origValue': value,
                    'count': cb_ctx['count'],
                    'total': cb_ctx['total'],
                    'iteration': cb_ctx['iteration'],
                    'reqTime': cb_ctx['reqTime'],
                    'args.start_object': args.start_object,
                    'stopOID': args.stop_object,
                    'stopFlag': stop_flag,
                    'variationModule': variation_module
                }

                try:
                    line = data_file_handler.format(oid, value, **context)

                except error.MoreDataNotification as exc:
                    cb_ctx['count'] = 0
                    cb_ctx['iteration'] += 1

                    more_data_notification = exc

                    if 'period' in more_data_notification:
                        log.info(
                            '%s OIDs dumped, waiting %.2f sec(s)'
                            '...' % (cb_ctx['total'],
                                     more_data_notification['period']))

                        time.sleep(more_data_notification['period'])

                    # initiate another SNMP walk iteration
                    if args.use_getbulk:
                        cmd_gen.sendVarBinds(
                            snmp_engine,
                            'tgt',
                            args.v3_context_engine_id, args.v3_context_name,
                            0, args.getbulk_repetitions,
                            [(args.start_object, None)],
                            cbFun, cb_ctx)

                    else:
                        cmd_gen.sendVarBinds(
                            snmp_engine,
                            'tgt',
                            args.v3_context_engine_id, args.v3_context_name,
                            [(args.start_object, None)],
                            cbFun, cb_ctx)

                    stop_flag = True  # stop current iteration

                except error.NoDataNotification:
                    pass

                except error.SnmpsimError as exc:
                    log.error(exc)
                    continue

                else:
                    args.output_file.write(line)

                    cb_ctx['count'] += 1
                    cb_ctx['total'] += 1

                    if cb_ctx['count'] % 100 == 0:
                        log.info('OIDs dumped: %s/%s' % (
                            cb_ctx['iteration'], cb_ctx['count']))

        # Next request time
        cb_ctx['reqTime'] = time.time()

        # Continue walking
        return not stop_flag

    cb_ctx = {
        'total': 0,
        'count': 0,
        'errors': 0,
        'iteration': 0,
        'reqTime': time.time(),
        'retries': args.continue_on_errors,
        'lastOID': args.start_object
    }

    if args.use_getbulk:
        cmd_gen = cmdgen.BulkCommandGenerator()

        cmd_gen.sendVarBinds(
            snmp_engine,
            'tgt',
            args.v3_context_engine_id, args.v3_context_name,
            0, args.getbulk_repetitions,
            [(args.start_object, rfc1902.Null(''))],
            cbFun, cb_ctx)

    else:
        cmd_gen = cmdgen.NextCommandGenerator()

        cmd_gen.sendVarBinds(
            snmp_engine,
            'tgt',
            args.v3_context_engine_id, args.v3_context_name,
            [(args.start_object, rfc1902.Null(''))],
            cbFun, cb_ctx)

    log.info(
        'Sending initial %s request for %s (stop at %s)'
        '....' % (args.use_getbulk and 'GETBULK' or 'GETNEXT',
                  args.start_object, args.stop_object or '<end-of-mib>'))

    started = time.time()

    try:
        snmp_engine.transportDispatcher.runDispatcher()

    except KeyboardInterrupt:
        log.info('Shutting down process...')

    finally:
        if variation_module:
            log.info('Shutting down variation module '
                     '%s...' % args.variation_module)

            try:
                handler = variation_module['shutdown']

                handler(snmpEngine=snmp_engine,
                        options=args.variation_module_options,
                        mode='recording')

            except Exception as exc:
                log.error(
                    'Variation module %s shutdown FAILED: '
                    '%s' % (args.variation_module, exc))

            else:
                log.info(
                    'Variation module %s shutdown OK' % args.variation_module)

        snmp_engine.transportDispatcher.closeDispatcher()

        started = time.time() - started

        cb_ctx['total'] += cb_ctx['count']

        log.info(
            'OIDs dumped: %s, elapsed: %.2f sec, rate: %.2f OIDs/sec, errors: '
            '%d' % (cb_ctx['total'], started,
                    started and cb_ctx['count'] // started or 0,
                    cb_ctx['errors']))

        args.output_file.flush()
        args.output_file.close()

        return cb_ctx.get('errors', 0) and 1 or 0
Example #28
0
def record(oid, tag, value, **context):
    if 'ready' not in moduleContext:
        raise error.SnmpsimError('module not initialized')

    if 'dbConn' in moduleContext:
        dbConn = moduleContext['dbConn']

    else:
        raise error.SnmpsimError('variation module not initialized')

    if 'started' not in moduleContext:
        moduleContext['started'] = time.time()

    redisScript = moduleContext.get('evalsha')

    keySpace = '%.10d' % (
            moduleContext['key-spaces-id'] + moduleContext.get('iterations', 0))

    if context['stopFlag']:
        dbConn.sort(
            keySpace + '-' + 'temp_oids_ordering',
            store=keySpace + '-' + 'oids_ordering', alpha=True)

        dbConn.delete(keySpace + '-' + 'temp_oids_ordering')
        dbConn.rpush(moduleContext['key-spaces-id'], keySpace)

        log.info('redis: done with key-space %s' % keySpace)

        if 'iterations' in moduleContext and moduleContext['iterations']:
            log.info('redis: %s iterations remaining' % moduleContext['iterations'])

            moduleContext['started'] = time.time()
            moduleContext['iterations'] -= 1

            runtime = time.time() - moduleContext['started']
            wait = max(0, moduleContext['period'] - runtime)

            raise error.MoreDataNotification(period=wait)

        else:
            raise error.NoDataNotification()

    dbOid = '.'.join(['%10s' % x for x in oid.split('.')])

    if 'hexvalue' in context:
        textTag = context['hextag']
        textValue = context['hexvalue']

    else:
        textTag = SnmprecGrammar().get_tag_by_type(context['origValue'])
        textValue = str(context['origValue'])

    dbConn.lpush(keySpace + '-temp_oids_ordering', keySpace + '-' + dbOid)

    if redisScript:
        evalsha(dbConn, 
            redisScript, 1, keySpace + '-' + dbOid, textTag + '|' + textValue)

    else:
        dbConn.set(keySpace + '-' + dbOid, textTag + '|' + textValue)

    if not context['count']:
        settings = {
            'key-spaces-id': moduleContext['key-spaces-id']
        }

        if 'period' in moduleContext:
            settings['period'] = '%.2f' % float(moduleContext['period'])

        if 'addon' in moduleContext:
            settings.update(dict([utils.split(x, '=')
                                  for x in moduleContext['addon']]))

        value = ','.join(['%s=%s' % (k, v) for k, v in settings.items()])

        return str(context['startOID']), ':redis', value

    else:
        raise error.NoDataNotification()
Example #29
0
def record(oid, tag, value, **context):
    if 'started' not in moduleContext:
        moduleContext['started'] = time.time()

    if 'iterations' not in moduleContext:
        moduleContext['iterations'] = min(
            1, moduleContext['settings'].get('iterations', 0))

    # single-run recording

    iterations = moduleContext['settings'].get('iterations')
    if not iterations:
        if context['origValue'].tagSet not in INTEGER_TYPES:
            if 'hextag' in context:
                tag = context['hextag']

            if 'hexvalue' in context:
                value = context['hexvalue']

            return oid, tag, value

        if ('taglist' not in moduleContext['settings']
                or tag not in moduleContext['settings']['taglist']):
            return oid, tag, value

        value = 'initial=%s' % value

        if context['origValue'].tagSet == rfc1902.TimeTicks.tagSet:
            value += ',rate=100'

        elif context['origValue'].tagSet == rfc1902.Integer.tagSet:
            value += ',rate=0'

        return oid, tag + ':numeric', value

    # multiple-iteration recording

    if oid not in moduleContext:
        settings = {'initial': value}

        if context['origValue'].tagSet == rfc1902.TimeTicks.tagSet:
            settings['rate'] = 100

        elif context['origValue'].tagSet == rfc1902.Integer.tagSet:
            settings['rate'] = 0  # may be constants

        if 'addon' in moduleContext['settings']:
            settings.update(
                dict([
                    split(x, '=') for x in moduleContext['settings']['addon']
                ]))

        moduleContext[oid] = {}

        moduleContext[oid]['settings'] = settings

    if moduleContext['iterations']:
        if context['stopFlag']:  # switching to final iteration
            log.info('numeric: %s iterations '
                     'remaining' % moduleContext['iterations'])

            moduleContext['iterations'] -= 1
            moduleContext['started'] = time.time()

            running = time.time() - moduleContext['started']
            wait = max(0, float(moduleContext['settings']['period']) - running)

            raise error.MoreDataNotification(period=wait)

        else:  # storing values on first iteration
            moduleContext[oid]['time'] = time.time()
            moduleContext[oid]['value'] = context['origValue']

            if 'hexvalue' in moduleContext[oid]:
                moduleContext[oid]['hexvalue'] = context['hexvalue']

            if 'hextag' in moduleContext[oid]:
                moduleContext[oid]['hextag'] = context['hextag']

            raise error.NoDataNotification()
    else:
        if context['stopFlag']:
            raise error.NoDataNotification()

        if 'value' in moduleContext[oid]:
            if context['origValue'].tagSet not in INTEGER_TYPES:
                if 'hextag' in moduleContext[oid]:
                    tag = moduleContext[oid]['hextag']

                if 'hexvalue' in moduleContext[oid]:
                    value = moduleContext[oid]['hexvalue']

                return oid, tag, value

            if tag not in moduleContext['settings']['taglist']:
                return oid, tag, moduleContext[oid]['value']

            diff = int(context['origValue']) - int(moduleContext[oid]['value'])
            runtime = time.time() - moduleContext[oid]['time']
            moduleContext[oid]['settings']['rate'] = diff / runtime

            tag += ':numeric'
            value = ','.join([
                '%s=%s' % (k, v)
                for k, v in moduleContext[oid]['settings'].items()
            ])

            return oid, tag, value

        else:
            raise error.NoDataNotification()
Example #30
0
def variate(oid, tag, value, **context):
    if not context['nextFlag'] and not context['exactMatch']:
        return context['origOid'], tag, context['errorStatus']

    if 'settings' not in recordContext:
        recordContext['settings'] = dict(
            [split(x, '=') for x in split(value, ',')])

        if 'hexvalue' in recordContext['settings']:
            recordContext['settings']['value'] = [
                int(recordContext['settings']['hexvalue'][x:x + 2], 16)
                for x in range(0, len(recordContext['settings']['hexvalue']), 2)]

        if 'wait' in recordContext['settings']:
            recordContext['settings']['wait'] = float(
                recordContext['settings']['wait'])

        else:
            recordContext['settings']['wait'] = 500.0

        if 'deviation' in recordContext['settings']:
            recordContext['settings']['deviation'] = float(
                recordContext['settings']['deviation'])

        else:
            recordContext['settings']['deviation'] = 0.0

        if 'vlist' in recordContext['settings']:

            vlist = {}

            recordContext['settings']['vlist'] = split(
                recordContext['settings']['vlist'], ':')

            while recordContext['settings']['vlist']:
                o, v, d = recordContext['settings']['vlist'][:3]

                recordContext['settings']['vlist'] = recordContext['settings']['vlist'][3:]

                d = int(d)

                type_tag, _ = SnmprecRecord.unpack_tag(tag)

                v = SnmprecGrammar.TAG_MAP[type_tag](v)

                if o not in vlist:
                    vlist[o] = {}

                if o == 'eq':
                    vlist[o][v] = d

                elif o in ('lt', 'gt'):
                    vlist[o] = v, d

                else:
                    log.info('delay: bad vlist syntax: '
                            '%s' % recordContext['settings']['vlist'])

            recordContext['settings']['vlist'] = vlist

        if 'tlist' in recordContext['settings']:
            tlist = {}

            recordContext['settings']['tlist'] = split(
                recordContext['settings']['tlist'], ':')

            while recordContext['settings']['tlist']:
                o, v, d = recordContext['settings']['tlist'][:3]

                recordContext['settings']['tlist'] = recordContext['settings']['tlist'][3:]

                v = int(v)
                d = int(d)

                if o not in tlist:
                    tlist[o] = {}

                if o == 'eq':
                    tlist[o][v] = d

                elif o in ('lt', 'gt'):
                    tlist[o] = v, d

                else:
                    log.info('delay: bad tlist syntax: '
                            '%s' % recordContext['settings']['tlist'])

            recordContext['settings']['tlist'] = tlist

    if context['setFlag'] and 'vlist' in recordContext['settings']:
        if ('eq' in recordContext['settings']['vlist'] and
                    context['origValue'] in recordContext['settings']['vlist']['eq']):
            delay = recordContext['settings']['vlist']['eq'][context['origValue']]

        elif ('lt' in recordContext['settings']['vlist'] and
                context['origValue'] < recordContext['settings']['vlist']['lt'][0]):
            delay = recordContext['settings']['vlist']['lt'][1]

        elif ('gt' in recordContext['settings']['vlist'] and
                context['origValue'] > recordContext['settings']['vlist']['gt'][0]):
            delay = recordContext['settings']['vlist']['gt'][1]

        else:
            delay = recordContext['settings']['wait']

    elif 'tlist' in recordContext['settings']:
        now = int(time.time())
        if ('eq' in recordContext['settings']['tlist'] and
                now == recordContext['settings']['tlist']['eq']):
            delay = recordContext['settings']['tlist']['eq'][now]

        elif ('lt' in recordContext['settings']['tlist'] and
                now < recordContext['settings']['tlist']['lt'][0]):
            delay = recordContext['settings']['tlist']['lt'][1]

        elif ('gt' in recordContext['settings']['tlist'] and
                now > recordContext['settings']['tlist']['gt'][0]):
            delay = recordContext['settings']['tlist']['gt'][1]

        else:
            delay = recordContext['settings']['wait']

    else:
        delay = recordContext['settings']['wait']

    if recordContext['settings']['deviation']:
        delay += random.randrange(
            -recordContext['settings']['deviation'],
            recordContext['settings']['deviation'])

    if delay < 0:
        delay = 0

    elif delay > 99999:
        log.info('delay: dropping response for %s' % oid)
        raise error.NoDataNotification()

    log.info('delay: waiting %d milliseconds for %s' % (delay, oid))

    time.sleep(delay / 1000)  # ms

    if context['setFlag'] or 'value' not in recordContext['settings']:
        return oid, tag, context['origValue']

    else:
        return oid, tag, recordContext['settings']['value']
Example #31
0
def variate(oid, tag, value, **context):
    if 'dbConn' in moduleContext:
        db_conn = moduleContext['dbConn']

    else:
        raise error.SnmpsimError('variation module not initialized')

    cursor = db_conn.cursor()

    try:
        cursor.execute('set session transaction isolation level '
                       '%s' %
                       ISOLATION_LEVELS[moduleContext['isolationLevel']])
        cursor.fetchall()

    except Exception:  # non-MySQL/Postgres
        pass

    if value:
        db_table = value.split(',').pop(0)

    elif 'dbTable' in moduleContext:
        db_table = moduleContext['dbTable']

    else:
        log.info('SQL table not specified for OID '
                 '%s' % (context['origOid'], ))
        return context['origOid'], tag, context['errorStatus']

    orig_oid = context['origOid']
    sql_oid = '.'.join(['%10s' % x for x in str(orig_oid).split('.')])

    if context['setFlag']:
        if 'hexvalue' in context:
            text_tag = context['hextag']
            text_value = context['hexvalue']

        else:
            text_tag = SnmprecGrammar().get_tag_by_type(context['origValue'])
            text_value = str(context['origValue'])

        cursor.execute('select maxaccess,tag from %s where oid=\'%s\''
                       ' limit 1' % (db_table, sql_oid))

        resultset = cursor.fetchone()

        if resultset:
            maxaccess = resultset[0]
            if maxaccess != 'read-write':
                return orig_oid, tag, context['errorStatus']

            cursor.execute('update %s set tag=\'%s\',value=\'%s\' where '
                           'oid=\'%s\'' %
                           (db_table, text_tag, text_value, sql_oid))

        else:
            cursor.execute('insert into %s values (\'%s\', \'%s\', \'%s\', '
                           '\'read-write\')' %
                           (db_table, sql_oid, text_tag, text_value))

        if context['varsRemaining'] == 0:  # last OID in PDU
            db_conn.commit()

        cursor.close()

        return orig_oid, text_tag, context['origValue']

    else:
        if context['nextFlag']:
            cursor.execute('select oid from %s where oid>\'%s\' order by oid '
                           'limit 1' % (db_table, sql_oid))

            resultset = cursor.fetchone()

            if resultset:
                orig_oid = orig_oid.clone('.'.join(
                    [x.strip() for x in str(resultset[0]).split('.')]))

                sql_oid = '.'.join(
                    ['%10s' % x for x in str(orig_oid).split('.')])

            else:
                cursor.close()
                return orig_oid, tag, context['errorStatus']

        cursor.execute('select tag, value from %s where oid=\'%s\' '
                       'limit 1' % (db_table, sql_oid))

        resultset = cursor.fetchone()

        cursor.close()

        if resultset:
            return orig_oid, str(resultset[0]), str(resultset[1])

        else:
            return orig_oid, tag, context['errorStatus']
Example #32
0
    mibBuilder = builder.MibBuilder()

    mibViewController = view.MibViewController(mibBuilder)

    compiler.addMibCompiler(mibBuilder,
                            sources=mibSources or defaultMibSources)
    if isinstance(startOID, rfc1902.ObjectIdentity):
        startOID.resolveWithMib(mibViewController)
    if isinstance(stopOID, rfc1902.ObjectIdentity):
        stopOID.resolveWithMib(mibViewController)

# Load variation module

if variationModuleName:
    for variationModulesDir in confdir.variation:
        log.info('Scanning "%s" directory for variation modules...' %
                 variationModulesDir)
        if not os.path.exists(variationModulesDir):
            log.info('Directory "%s" does not exist' % variationModulesDir)
            continue

        mod = os.path.join(variationModulesDir, variationModuleName + '.py')
        if not os.path.exists(mod):
            log.info('Variation module "%s" not found' % mod)
            continue

        ctx = {'path': mod, 'moduleContext': {}}

        try:
            if sys.version_info[0] > 2:
                exec(compile(open(mod).read(), mod, 'exec'), ctx)
            else:
Example #33
0
def variate(oid, tag, value, **context):

    if 'settings' not in recordContext:
        recordContext['settings'] = dict(
            [split(x, '=') for x in split(value, ',')])

        if 'dir' not in recordContext['settings']:
            log.info('multiplex: snapshot directory not specified')
            return context['origOid'], tag, context['errorStatus']

        recordContext['settings']['dir'] = recordContext['settings'][
            'dir'].replace('/', os.path.sep)

        if recordContext['settings']['dir'][0] != os.path.sep:
            for x in confdir.data:
                d = os.path.join(x, recordContext['settings']['dir'])
                if os.path.exists(d):
                    break

            else:
                log.info('multiplex: directory %s not '
                         'found' % recordContext['settings']['dir'])
                return context['origOid'], tag, context['errorStatus']

        else:
            d = recordContext['settings']['dir']

        recordContext['dirmap'] = {}
        recordContext['parsermap'] = {}

        for fl in os.listdir(d):
            for ext in RECORD_SET:
                if not fl.endswith(ext):
                    continue
                ident = int(os.path.basename(fl)[:-len(ext) - 1])
                datafile = os.path.join(d, fl)
                recordContext['dirmap'][ident] = datafile
                recordContext['parsermap'][datafile] = RECORD_SET[ext]

        recordContext['keys'] = list(recordContext['dirmap'])

        recordContext['bounds'] = (min(recordContext['keys']),
                                   max(recordContext['keys']))

        if 'period' in recordContext['settings']:
            recordContext['settings']['period'] = float(
                recordContext['settings']['period'])

        else:
            recordContext['settings']['period'] = 60.0

        if 'wrap' in recordContext['settings']:
            recordContext['settings']['wrap'] = bool(
                recordContext['settings']['wrap'])

        else:
            recordContext['settings']['wrap'] = False

        if 'control' in recordContext['settings']:
            recordContext['settings']['control'] = rfc1902.ObjectName(
                recordContext['settings']['control'])

            log.info('multiplex: using control OID %s for subtree %s, '
                     'time-based multiplexing '
                     'disabled' % (recordContext['settings']['control'], oid))

        recordContext['ready'] = True

    if 'ready' not in recordContext:
        return context['origOid'], tag, context['errorStatus']

    if oid not in moduleContext:
        moduleContext[oid] = {}

    if context['setFlag']:
        if 'control' in (recordContext['settings']
                         and recordContext['settings']['control']
                         == context['origOid']):

            fileno = int(context['origValue'])
            if fileno >= len(recordContext['keys']):
                log.info('multiplex: .snmprec file number %s over limit of'
                         ' %s' % (fileno, len(recordContext['keys'])))

                return context['origOid'], tag, context['errorStatus']

            moduleContext[oid]['fileno'] = fileno

            log.info('multiplex: switched to file #%s '
                     '(%s)' %
                     (recordContext['keys'][fileno],
                      recordContext['dirmap'][recordContext['keys'][fileno]]))

            return context['origOid'], tag, context['origValue']

        else:
            return context['origOid'], tag, context['errorStatus']

    if 'control' in recordContext['settings']:
        if 'fileno' not in moduleContext[oid]:
            moduleContext[oid]['fileno'] = 0

        if (not context['nextFlag'] and recordContext['settings']['control']
                == context['origOid']):

            val = rfc1902.Integer32(moduleContext[oid]['fileno'])

            return context['origOid'], tag, val

    else:
        period = recordContext['settings']['period']

        uptime = time.time() - moduleContext['booted']
        timeslot = uptime % (period * len(recordContext['dirmap']))

        fileslot = int(timeslot / period) + recordContext['bounds'][0]

        fileno = bisect.bisect(recordContext['keys'], fileslot) - 1

        if ('fileno' not in moduleContext[oid]
                or moduleContext[oid]['fileno'] < fileno
                or recordContext['settings']['wrap']):
            moduleContext[oid]['fileno'] = fileno

    datafile = recordContext['dirmap'][recordContext['keys'][moduleContext[oid]
                                                             ['fileno']]]

    parser = recordContext['parsermap'][datafile]

    if ('datafile' not in moduleContext[oid]
            or moduleContext[oid]['datafile'] != datafile):

        if 'datafileobj' in moduleContext[oid]:
            moduleContext[oid]['datafileobj'].close()

        recordIndex = RecordIndex(datafile, parser).create()

        moduleContext[oid]['datafileobj'] = recordIndex

        moduleContext[oid]['datafile'] = datafile

        log.info('multiplex: switching to data file %s for '
                 '%s' % (datafile, context['origOid']))

    text, db = moduleContext[oid]['datafileobj'].get_handles()

    textOid = str(
        rfc1902.OctetString('.'.join(['%s' % x for x in context['origOid']])))

    try:
        line = moduleContext[oid]['datafileobj'].lookup(textOid)

    except KeyError:
        offset = search_record_by_oid(context['origOid'], text, parser)
        exactMatch = False

    else:
        offset, subtreeFlag, prevOffset = line.split(str2octs(','))
        exactMatch = True

    text.seek(int(offset))

    line, _, _ = get_record(text)  # matched line

    if context['nextFlag']:
        if exactMatch:
            line, _, _ = get_record(text)

    else:
        if not exactMatch:
            return context['origOid'], tag, context['errorStatus']

    if not line:
        return context['origOid'], tag, context['errorStatus']

    try:
        oid, value = parser.evaluate(line)

    except error.SnmpsimError:
        oid, value = context['origOid'], context['errorStatus']

    return oid, tag, value