def store(self): return SNMPPluginStore({ SectionName("section0"): SNMPPluginStoreItem( [ BackendSNMPTree(base=".1.2.3", oids=[ BackendOIDSpec("4.5", "string", False), BackendOIDSpec("9.7", "string", False) ]), BackendSNMPTree(base=".8.9.0", oids=[ BackendOIDSpec("1.2", "string", False), BackendOIDSpec("3.4", "string", False) ]), ], SNMPDetectSpec([[ ("oid0", "regex0", True), ("oid1", "regex1", True), ("oid2", "regex2", False), ]]), ), SectionName("section1"): SNMPPluginStoreItem( [ BackendSNMPTree(base=".1.2.3", oids=[ BackendOIDSpec("4.5", "string", False), BackendOIDSpec("6.7.8", "string", False) ]) ], SNMPDetectSpec([[ ("oid3", "regex3", True), ("oid4", "regex4", False), ]]), ), })
def fetcher_fixture(self, file_cache): SNMPFetcher.snmp_plugin_store = SNMPPluginStore({ SectionName("pim"): SNMPPluginStoreItem( trees=[ BackendSNMPTree(base=".1.1.1", oids=[ BackendOIDSpec("1.2", "string", False), BackendOIDSpec("3.4", "string", False) ]) ], detect_spec=SNMPDetectSpec([[("1.2.3.4", "pim device", True)]]), ), SectionName("pam"): SNMPPluginStoreItem( trees=[ BackendSNMPTree( base=".1.2.3", oids=[ BackendOIDSpec("4.5", "string", False), BackendOIDSpec("6.7", "string", False), BackendOIDSpec("8.9", "string", False) ], ), ], detect_spec=SNMPDetectSpec([[("1.2.3.4", "pam device", True)]]), ), SectionName("pum"): SNMPPluginStoreItem( trees=[ BackendSNMPTree(base=".2.2.2", oids=[BackendOIDSpec("2.2", "string", False)]), BackendSNMPTree(base=".3.3.3", oids=[BackendOIDSpec("2.2", "string", False)]), ], detect_spec=SNMPDetectSpec([[]]), ), }) return SNMPFetcher( file_cache, disabled_sections=set(), configured_snmp_sections=set(), inventory_snmp_sections=set(), on_error="raise", missing_sys_description=False, use_snmpwalk_cache=False, do_status_data_inventory=False, snmp_config=SNMPHostConfig( is_ipv6_primary=False, hostname="bob", ipaddress="1.2.3.4", credentials="public", port=42, is_bulkwalk_host=False, is_snmpv2or3_without_bulkwalk_host=False, bulk_walk_size_of=0, timing={}, oid_range_limits=[], snmpv3_contexts=[], character_encoding=None, is_usewalk_host=False, snmp_backend=SNMPBackend.classic, ), )
def test_all_of(): spec1 = SNMPDetectSpec([[(".1", "1?", True)]]) spec2 = SNMPDetectSpec([[(".2", "2?", True)]]) spec3 = SNMPDetectSpec([[(".3", "3?", True)]]) assert utils.all_of(spec1, spec2, spec3) == SNMPDetectSpec([[ (".1", "1?", True), (".2", "2?", True), (".3", "3?", True), ]]) spec12 = utils.all_of(spec1, spec2) assert utils.all_of(spec1, spec2, spec3) == utils.all_of(spec12, spec3)
def test_all_of_any_of(): spec1 = SNMPDetectSpec([[(".1", "1?", True)]]) spec2 = SNMPDetectSpec([[(".2", "2?", True)]]) spec3 = SNMPDetectSpec([[(".3", "3?", True)]]) spec4 = SNMPDetectSpec([[(".4", "4?", True)]]) spec12 = utils.any_of(spec1, spec2) spec34 = utils.any_of(spec3, spec4) assert utils.all_of(spec12, spec34) == SNMPDetectSpec([ [(".1", "1?", True), (".3", "3?", True)], [(".1", "1?", True), (".4", "4?", True)], [(".2", "2?", True), (".3", "3?", True)], [(".2", "2?", True), (".4", "4?", True)], ])
def test_rule_indipendent( self, monkeypatch, hostname, ipaddress, ): plugin = section_plugins.create_snmp_section_plugin( name="norris", parse_function=lambda string_table: None, trees=[ SNMPTree( base='.1.2.3', oids=['2.3'], ), ], detect_spec=SNMPDetectSpec([[('.1.2.3.4.5', 'Foo.*', True)]]), ) source = self.do_monkeypatch_and_make_source( monkeypatch, plugin, hostname, ipaddress, ) assert source._make_snmp_section_detects() == [(plugin.name, plugin.detect_spec)]
def test_create_snmp_section_plugin(): trees: List[SNMPTree] = [ SNMPTree( base='.1.2.3', oids=[OIDEnd(), '2.3'], ), ] detect = SNMPDetectSpec([ [('.1.2.3.4.5', 'Foo.*', True)], ]) plugin = section_plugins.create_snmp_section_plugin( name="norris", parsed_section_name="chuck", parse_function=_parse_dummy, fetch=trees, detect_spec=detect, supersedes=["foo", "bar"], ) assert isinstance(plugin, SNMPSectionPlugin) assert len(plugin) == 8 assert plugin.name == SectionName("norris") assert plugin.parsed_section_name == ParsedSectionName("chuck") assert plugin.parse_function is _parse_dummy assert plugin.host_label_function is section_plugins._noop_host_label_function assert plugin.detect_spec == detect assert plugin.trees == trees assert plugin.supersedes == {SectionName("bar"), SectionName("foo")}
def from_json(cls, serialized: Dict[str, Any]) -> 'SNMPFetcher': # The SNMPv3 configuration is represented by a tuple of different lengths (see # SNMPCredentials). Since we just deserialized from JSON, we have to convert the # list used by JSON back to a tuple. # SNMPv1/v2 communities are represented by a string: Leave it untouched. if isinstance(serialized["snmp_config"]["credentials"], list): serialized["snmp_config"]["credentials"] = tuple( serialized["snmp_config"]["credentials"]) return cls( file_cache=SNMPFileCache.from_json(serialized.pop("file_cache")), snmp_section_trees={ SectionName(name): [SNMPTree.from_json(tree) for tree in trees ] for name, trees in serialized["snmp_section_trees"].items() }, snmp_section_detects=[ ( SectionName(name), # The cast is necessary as mypy does not infer types in a list comprehension. # See https://github.com/python/mypy/issues/5068 SNMPDetectSpec([[cast(SNMPDetectAtom, tuple(inner)) for inner in outer] for outer in specs]), ) for name, specs in serialized["snmp_section_detects"] ], configured_snmp_sections={ SectionName(name) for name in serialized["configured_snmp_sections"] }, on_error=serialized["on_error"], missing_sys_description=serialized["missing_sys_description"], use_snmpwalk_cache=serialized["use_snmpwalk_cache"], snmp_config=SNMPHostConfig(**serialized["snmp_config"]), )
def deserialize(cls, serialized: Mapping[str, Any]) -> "SNMPPluginStoreItem": return cls( [BackendSNMPTree.from_json(tree) for tree in serialized["trees"]], SNMPDetectSpec.from_json(serialized["detect_spec"]), serialized["inventory"], )
def _make_snmp_plugin_store() -> SNMPPluginStore: return SNMPPluginStore({ s.name: SNMPPluginStoreItem( [BackendSNMPTree.from_frontend(base=t.base, oids=t.oids) for t in s.trees], SNMPDetectSpec(s.detect_spec)) for s in agent_based_register.iter_all_snmp_sections() })
def snmp_plugin_fixture(self) -> None: SNMPFetcher.plugin_store = SNMPPluginStore({ SectionName("pim"): SNMPPluginStoreItem( trees=[ BackendSNMPTree( base=".1.1.1", oids=[ BackendOIDSpec("1.2", "string", False), BackendOIDSpec("3.4", "string", False), ], ) ], detect_spec=SNMPDetectSpec([[("1.2.3.4", "pim device", True)] ]), inventory=False, ), SectionName("pam"): SNMPPluginStoreItem( trees=[ BackendSNMPTree( base=".1.2.3", oids=[ BackendOIDSpec("4.5", "string", False), BackendOIDSpec("6.7", "string", False), BackendOIDSpec("8.9", "string", False), ], ), ], detect_spec=SNMPDetectSpec([[("1.2.3.4", "pam device", True)] ]), inventory=False, ), SectionName("pum"): SNMPPluginStoreItem( trees=[ BackendSNMPTree( base=".2.2.2", oids=[BackendOIDSpec("2.2", "string", False)]), BackendSNMPTree( base=".3.3.3", oids=[BackendOIDSpec("2.2", "string", False)]), ], detect_spec=SNMPDetectSpec([[]]), inventory=False, ), })
def deserialize(cls, serialized: Dict[str, Any]) -> "SNMPPluginStoreItem": try: return cls( [SNMPTree.from_json(tree) for tree in serialized["trees"]], SNMPDetectSpec.from_json(serialized["detect_spec"]), ) except (LookupError, TypeError, ValueError) as exc: raise ValueError(serialized) from exc
def test_any_of(): spec1 = SNMPDetectSpec([[(".1", "1?", True)]]) spec2 = SNMPDetectSpec([[(".2", "2?", True)]]) spec3 = SNMPDetectSpec([[(".3", "3?", True)]]) spec123 = utils.any_of(spec1, spec2, spec3) _validate_detect_spec(spec123) assert spec123 == [ [(".1", "1?", True)], [(".2", "2?", True)], [(".3", "3?", True)], ] spec12 = utils.any_of(spec1, spec2) assert spec123 == utils.any_of(spec12, spec3)
def _ast_convert_unary(unop_ast: ast.UnaryOp) -> SNMPDetectSpec: if isinstance(unop_ast.op, ast.Not): operand = _ast_convert_dispatcher(unop_ast.operand) _validate_detect_spec(operand) # We can only negate atomic specs, for now if len(operand) == 1 and len(operand[0]) == 1: oidstr, pattern, result = operand[0][0] return SNMPDetectSpec([[(oidstr, pattern, not result)]]) raise NotImplementedError("cannot negate operand") raise ValueError(ast.dump(unop_ast))
def specs(self): return SNMPDetectSpec( [ [ ("oid0", "regex0", True), ("oid1", "regex1", True), ("oid2", "regex2", False), ] ] )
def test_rule_dependent( self, monkeypatch, discovery_rulesets, hostname, ipaddress, ): detect_spec_1 = SNMPDetectSpec([[('.1.2.3.4.5', 'Bar.*', False)]]) detect_spec_2 = SNMPDetectSpec([[('.7.8.9', 'huh.*', True)]]) def evaluator(discovery_ruleset): if len(discovery_ruleset) > 0 and discovery_ruleset[0]: return detect_spec_1 return detect_spec_2 plugin = section_plugins.create_snmp_section_plugin( name="norris", parse_function=lambda string_table: None, trees=[ SNMPTree( base='.1.2.3', oids=['2.3'], ), ], detect_spec=SNMPDetectSpec([[('.1.2.3.4.5', 'Foo.*', True)]]), rule_dependent_detect_spec=SNMPRuleDependentDetectSpec( [RuleSetName('discovery_ruleset')], evaluator, ), ) snmp_configurator = self.do_monkeypatch_and_make_configurator( monkeypatch, plugin, hostname, ipaddress, ) assert snmp_configurator._make_snmp_scan_sections() == [ SNMPScanSection( plugin.name, detect_spec_1, ) ]
def fetcher_fixture(self, file_cache): return SNMPFetcher( file_cache, snmp_section_trees={ SectionName("pim"): [SNMPTree(base=".1.1.1", oids=["1.2", "3.4"])], SectionName("pam"): [SNMPTree(base=".1.2.3", oids=["4.5", "6.7", "8.9"])], SectionName("pum"): [ SNMPTree(base=".2.2.2", oids=["2.2"]), SNMPTree(base=".3.3.3", oids=["2.2"]), ], }, snmp_section_detects={ SectionName("pim"): SNMPDetectSpec([[("1.2.3.4", "pim device", True)]]), SectionName("pam"): SNMPDetectSpec([[("1.2.3.4", "pam device", True)]]), }, disabled_sections=set(), configured_snmp_sections=set(), structured_data_snmp_sections=set(), on_error="raise", missing_sys_description=False, use_snmpwalk_cache=False, snmp_config=SNMPHostConfig( is_ipv6_primary=False, hostname="bob", ipaddress="1.2.3.4", credentials="public", port=42, is_bulkwalk_host=False, is_snmpv2or3_without_bulkwalk_host=False, bulk_walk_size_of=0, timing={}, oid_range_limits=[], snmpv3_contexts=[], character_encoding=None, is_usewalk_host=False, snmp_backend=SNMPBackend.classic, ), )
def create_detect_spec( name: str, snmp_scan_function: Callable, fallback_files: List[str], ) -> SNMPDetectSpec: migrated = _lookup_migrated(snmp_scan_function) if migrated is not None: return migrated key = _lookup_key_from_code(snmp_scan_function.__code__) preconverted = PRECONVERTED_DETECT_SPECS.get(key) if preconverted is not None: return SNMPDetectSpec(preconverted) return SNMPDetectSpec( PRECONVERTED_DETECT_SPECS.setdefault( key, _compute_detect_spec( section_name=name, scan_function=snmp_scan_function, fallback_files=fallback_files, )))
def _compute_detect_spec( *, section_name: str, scan_function: Callable, fallback_files: List[str], ) -> SNMPDetectSpec: scan_func_ast = _get_scan_function_ast(section_name, scan_function, fallback_files) expression_ast = _get_expression_from_function(section_name, scan_func_ast) if _is_false(expression_ast): return SNMPDetectSpec() return _ast_convert_dispatcher(expression_ast)
def any_of(*specs: SNMPDetectSpec) -> SNMPDetectSpec: """Detect the device if any of the passed specifications are met Args: spec: A valid specification for SNMP device detection Returns: A valid specification for SNMP device detection Example: >>> DETECT = any_of(exists("1.2.3.4"), exists("1.2.3.5")) """ return SNMPDetectSpec(sum(specs, []))
def exists(oidstr: str) -> SNMPDetectSpec: """Detect the device if the OID exists at all Args: oidstr: The OID that is required to exist Returns: A valid specification for SNMP device detection Example: >>> DETECT = exists("1.2.3") """ return SNMPDetectSpec([[(oidstr, '.*', True)]])
def test_create_snmp_section_plugin_single_tree(): single_tree = SNMPTree(base='.1.2.3', oids=[OIDEnd(), '2.3']) plugin = section_plugins.create_snmp_section_plugin( name="norris", parse_function=lambda string_table: string_table, # just one, no list: fetch=single_tree, detect_spec=SNMPDetectSpec([[('.1.2.3.4.5', 'Foo.*', True)]]), ) assert plugin.trees == [single_tree] # the plugin only specified a single tree (not a list), # so a wrapper should unpack the argument: assert plugin.parse_function([[['A', 'B']]]) == [['A', 'B']]
def endswith(oidstr: str, value: str) -> SNMPDetectSpec: """Detect the device if the value of the OID ends with the given string Args: oidstr: The OID to match the value against value: The expected end of the OIDs value Returns: A valid specification for SNMP device detection Example: >>> DETECT = endswith("1.2.3", "nix") """ return SNMPDetectSpec([[(oidstr, '.*%s' % re.escape(value), True)]])
def contains(oidstr: str, value: str) -> SNMPDetectSpec: """Detect the device if the value of the OID contains the given string Args: oidstr: The OID to match the value against value: The substring expected to be in the OIDs value Returns: A valid specification for SNMP device detection Example: >>> DETECT = contains("1.2.3", "isco") """ return SNMPDetectSpec([[(oidstr, '.*%s.*' % re.escape(value), True)]])
def matches(oidstr: str, value: str) -> SNMPDetectSpec: """Detect the device if the value of the OID matches the expression Args: oidstr: The OID to match the value against value: The regular expression that the value of the OID should match Returns: A valid specification for SNMP device detection Example: >>> DETECT = match("1.2.3.4", ".* Server") """ return SNMPDetectSpec([[(oidstr, value, True)]])
def equals(oidstr: str, value: str) -> SNMPDetectSpec: """Detect the device if the value of the OID equals the given string Args: oidstr: The OID to match the value against value: The expected value of the OID Returns: A valid specification for SNMP device detection Example: >>> DETECT = equals("1.2.3", "MySwitch") """ return SNMPDetectSpec([[(oidstr, '%s' % re.escape(value), True)]])
def test_make_snmp_section_detects_for_inventory(monkeypatch, hostname, ipaddress, check_plugin): plugin = section_plugins.create_snmp_section_plugin( name="norris", parse_function=lambda string_table: None, fetch=[ SNMPTree( base='.1.2.3', oids=['2.3'], ), ], detect_spec=SNMPDetectSpec([[('.1.2.3.4.5', 'Foo.*', True)]]), ) monkeypatch.setattr(_config, 'registered_snmp_sections', {plugin.name: plugin}) monkeypatch.setattr(_config, 'registered_inventory_plugins', {check_plugin.name: check_plugin}) Scenario().add_host(hostname).apply(monkeypatch) source = SNMPSource.snmp(hostname, ipaddress, mode=Mode.INVENTORY) assert source._make_snmp_section_detects() == {plugin.name: plugin.detect_spec}
def test_make_snmp_section_detects(monkeypatch, hostname, ipaddress): plugin = section_plugins.create_snmp_section_plugin( name="norris", parse_function=lambda string_table: None, fetch=[ SNMPTree( base='.1.2.3', oids=['2.3'], ), ], detect_spec=SNMPDetectSpec([[('.1.2.3.4.5', 'Foo.*', True)]]), ) monkeypatch.setattr( register, 'iter_all_snmp_sections', lambda: [plugin], ) Scenario().add_host(hostname).apply(monkeypatch) source = SNMPSource.snmp(hostname, ipaddress, mode=Mode.DISCOVERY) assert source._make_snmp_section_detects() == {plugin.name: plugin.detect_spec}
def all_of(spec_0: SNMPDetectSpec, spec_1: SNMPDetectSpec, *specs: SNMPDetectSpec) -> SNMPDetectSpec: """Detect the device if all passed specifications are met Args: spec_0: A valid specification for SNMP device detection spec_1: A valid specification for SNMP device detection Returns: A valid specification for SNMP device detection Example: >>> DETECT = all_of(exists("1.2.3.4"), contains("1.2.3.5", "foo")) """ reduced = SNMPDetectSpec(l0 + l1 for l0, l1 in itertools.product(spec_0, spec_1)) if not specs: return reduced return all_of(reduced, *specs)
def create_detect_spec(name: str, snmp_scan_function: Callable, fallback_files: List[str]) -> SNMPDetectSpec: if snmp_scan_function.__name__ in MIGRATED_SCAN_FUNCTIONS: try: _ = snmp_scan_function(lambda x, default=None: "") except NotImplementedError as exc: if str(exc) == "already migrated": return MIGRATED_SCAN_FUNCTIONS[snmp_scan_function.__name__] raise NotImplementedError("please remove migrated code entirely") scan_func_ast = _get_scan_function_ast(name, snmp_scan_function, fallback_files) expression_ast = _get_expression_from_function(name, scan_func_ast) if _is_false(expression_ast): spec = SNMPDetectSpec() else: spec = _ast_convert_dispatcher(expression_ast) return spec
def test_serialization(self, specs): assert SNMPDetectSpec.from_json(specs.to_json()) == specs