def test_get_security_engine_id_not_present(self, m_fetch, m_get_cmd): ir2 = InventoryRecord.from_dict( { "address": "192.168.0.1", "port": "34", "version": "2c", "community": "public", "secret": "secret", "securityEngine": "ENGINE", "walk_interval": 1850, "profiles": "", "SmartProfiles": True, "delete": False, } ) snmpEngine = Mock() logger = Mock() m_get_cmd.return_value = iter( [(None, 0, 0, "Oid1"), (None, 0, 0, "Oid2"), (None, 0, 0, "Oid3")] ) m_fetch.side_effect = Exception("boom") with self.assertRaises(Exception) as e: get_security_engine_id(logger, ir2, snmpEngine) self.assertEqual("boom", e.exception.args[0]) calls = snmpEngine.observer.registerObserver.call_args_list self.assertEqual("rfc3412.prepareDataElements:internal", calls[0].args[1]) m_get_cmd.assert_called()
def test_assignment_of_static_profiles(self): profiles = { "profile1": { "frequency": 20 }, "profile2": { "frequency": 30 }, "profile3": {}, } ir = InventoryRecord.from_dict({ "address": "192.168.0.1", "port": "34", "version": "2c", "community": "public", "secret": "secret", "securityEngine": "ENGINE", "walk_interval": 1850, "profiles": "profile1;profile2;profile3;profile4", "SmartProfiles": False, "delete": False, }) result = assign_profiles(ir, profiles, {}) self.assertEqual({20: ["profile1"], 30: ["profile2"]}, result)
def test_address_not_commented(self): ir_dict = {"address": "#asd"} with self.assertRaises(ValueError) as e: InventoryRecord(**ir_dict) self.assertEqual("field address cannot be commented", e.exception.args[0][0].exc.args[0])
def test_address_not_none(self): ir_dict = {"address": None} with self.assertRaises(ValueError) as e: InventoryRecord(**ir_dict) self.assertEqual("field address cannot be null", e.exception.args[0][0].exc.args[0])
def get_inventory(mongo_inventory, address): host, port = return_address_and_port(address) ir_doc = mongo_inventory.find_one({"address": host, "port": port}) if ir_doc is None: raise ValueError( f"Inventory Doc deleted unable to complete task for {address}") logger.debug(f"{ir_doc}") ir_doc.pop("_id", None) return InventoryRecord(**ir_doc)
def test_address_not_resolvable(self): ir_dict = {"address": "12313sdfsf"} with self.assertRaises(ValueError) as e: InventoryRecord(**ir_dict) self.assertEqual( "field address must be an IP or a resolvable hostname 12313sdfsf", e.exception.args[0][0].exc.args[0], )
def test_to_json(self): ir_dict = { "address": "192.168.0.1", "port": "34", "version": "3", "community": "public", "secret": "secret", "securityEngine": "ENGINE", "walk_interval": 1850, "profiles": "", "SmartProfiles": True, "delete": "", } ir = InventoryRecord(**ir_dict) self.assertEqual( '{"address": "192.168.0.1", "port": 34, "version": "3", "community": ' '"public", "secret": "secret", "securityEngine": "ENGINE", "walk_interval": ' '1850, "profiles": [], "SmartProfiles": true, "delete": false}', ir.to_json(), )
def test_port_too_high(self): ir_dict = { "address": "192.168.0.1", "port": 65537, "version": "2c", "walk_interval": 1850, "SmartProfiles": True, "delete": "", } with self.assertRaises(ValueError) as e: InventoryRecord(**ir_dict) self.assertEqual("Port out of range 65537", e.exception.args[0][0].exc.args[0])
def test_empty_community(self): ir_dict = { "address": "192.168.0.1", "port": "34", "version": "3", "community": "", "secret": "secret", "securityEngine": "ENGINE", "walk_interval": 1850, "profiles": "", "SmartProfiles": True, "delete": False, } ir = InventoryRecord(**ir_dict) self.assertIsNone(ir.community)
def test_too_high_walk_interval(self): ir_dict = { "address": "192.168.0.1", "port": "34", "version": "3", "community": "public", "secret": "secret", "securityEngine": "ENGINE", "walk_interval": 50000, "profiles": "", "SmartProfiles": True, "delete": False, } ir = InventoryRecord(**ir_dict) self.assertEqual(42000, ir.walk_interval)
def test_from_json(self): ir = InventoryRecord.from_json( '{"address": "192.168.0.1", "port": "34", "version": "3", "community": ' '"public", "secret": "secret", "securityEngine": "ENGINE", "walk_interval": ' '1850, "profiles": "", "SmartProfiles": true, "delete": ""}') self.assertEqual(ir.address, "192.168.0.1") self.assertEqual(ir.port, 34) self.assertEqual(ir.version, "3") self.assertEqual(ir.community, "public") self.assertEqual(ir.secret, "secret") self.assertEqual(ir.securityEngine, "ENGINE") self.assertEqual(ir.walk_interval, 1850) self.assertEqual(ir.profiles, []) self.assertEqual(ir.SmartProfiles, True) self.assertEqual(ir.delete, False)
def test_profiles_not_string(self): ir_dict = { "address": "192.168.0.1", "port": "34", "version": "3", "community": "public", "secret": "secret", "securityEngine": "ENGINE", "walk_interval": 1850, "profiles": [], "SmartProfiles": True, "delete": False, } ir = InventoryRecord(**ir_dict) self.assertEqual([], ir.profiles)
def test_getAuthV3_security_engine_not_str( self, m_get_security_engine_id, m_get_secret_value, m_exists ): m_exists.return_value = True m_get_secret_value.side_effect = [ "secret1", "secret2", "secret3", "SHA224", "AES192BLMT", "1", "2", ] m_get_security_engine_id.return_value = "ENGINE123" logger = Mock() snmpEngine = Mock() ir2 = InventoryRecord.from_dict( { "address": "192.168.0.1", "port": "34", "version": "2c", "community": "public", "secret": "secret_ir", "securityEngine": 123, "walk_interval": 1850, "profiles": "", "SmartProfiles": True, "delete": False, } ) result = getAuthV3(logger, ir2, snmpEngine) m_get_security_engine_id.assert_called() self.assertEqual("secret1", result.userName) self.assertEqual("secret2", result.authKey) self.assertEqual("secret3", result.privKey) self.assertEqual(usmHMAC128SHA224AuthProtocol, result.authProtocol) self.assertEqual(usmAesBlumenthalCfb192Protocol, result.privProtocol) self.assertEqual("ENGINE123", result.securityEngineId) self.assertEqual("secret1", result.securityName) self.assertEqual(1, result.authKeyType) self.assertEqual(2, result.privKeyType)
def run_walk(): poller = Poller(no_mongo=True) with open("inventory.csv", encoding="utf-8") as csv_file: # Dict reader will trust the header of the csv ir_reader = DictReader(csv_file) for source_record in ir_reader: address = source_record["address"] if address.startswith("#"): continue try: ir = InventoryRecord(**source_record) retry = True while retry: retry, result = poller.do_work(ir, walk=True) logger.debug(result) except Exception as e: logger.exception(e)
def test_walk_task(self): inventory_record_json = { "address": "192.68.0.1", "port": 456, "version": "3", "community": "public", "secret": "some_secret", "securityEngine": "test_123", "walk_interval": 3456, "profiles": "profile1;profile2;profile3", "SmartProfiles": True, "delete": False, } inventory_record = InventoryRecord.from_dict(inventory_record_json) result = gen_walk_task(inventory_record) self.assertEqual("sc4snmp;192.68.0.1:456;walk", result["name"]) self.assertEqual("splunk_connect_for_snmp.snmp.tasks.walk", result["task"]) self.assertEqual("192.68.0.1:456", result["target"]) self.assertEqual([], result["args"]) self.assertEqual( {"address": "192.68.0.1:456", "profile": None}, result["kwargs"] ) self.assertEqual("_chain", type(result["options"]["link"]).__name__) self.assertEqual( "splunk_connect_for_snmp.enrich.tasks.enrich", result["options"]["link"].tasks[0].name, ) self.assertEqual( "splunk_connect_for_snmp.inventory.tasks.inventory_setup_poller", result["options"]["link"].tasks[1].tasks[0].name, ) self.assertEqual( "splunk_connect_for_snmp.splunk.tasks.prepare", result["options"]["link"].tasks[1].tasks[1].tasks[0].name, ) self.assertEqual( "splunk_connect_for_snmp.splunk.tasks.send", result["options"]["link"].tasks[1].tasks[1].tasks[1].name, ) self.assertEqual({"every": 3456, "period": "seconds"}, result["interval"]) self.assertTrue(result["enabled"]) self.assertTrue(result["run_immediately"])
def test_version_out_of_range(self): ir_dict = { "address": "192.168.0.1", "port": "34", "version": "5a", "community": "public", "secret": "secret", "securityEngine": "ENGINE", "walk_interval": 1850, "profiles": "", "SmartProfiles": True, "delete": False, } with self.assertRaises(ValueError) as e: InventoryRecord(**ir_dict) self.assertEqual( "version out of range 5a accepted is 1 or 2c or 3", e.exception.args[0][0].exc.args[0], )
def test_inventory_setup_poller( self, m_get_inventory, m_assign_profiles, m_find_one, m_task_manager, m_load_profiles, ): periodic_obj_mock = Mock() m_task_manager.return_value = periodic_obj_mock m_get_inventory.return_value = InventoryRecord.from_dict({ "address": "192.168.0.1", "port": "34", "version": "2c", "community": "public", "secret": "secret", "securityEngine": "ENGINE", "walk_interval": 1850, "profiles": "", "SmartProfiles": True, "delete": False, }) m_find_one.return_value = { "state": { "SNMPv2-MIB|sysDescr": { "value": "MIKROTIK" }, "SNMPv2-MIB|sysName": { "value": "Linux Debian 2.0.1" }, "SNMPv2-MIB|sysContact": { "value": "non-existing-name@splunk" }, } } work = {"address": "192.168.0.1"} m_assign_profiles.return_value = { 60: ["BaseUpTime"], 30: ["profile5", "profile2"], 20: ["profile1"], } # when inventory_setup_poller(work) m_load_profiles.assert_not_called() calls = periodic_obj_mock.manage_task.call_args_list self.assertEqual( { "address": "192.168.0.1", "profiles": {"BaseUpTime"}, "frequency": 60 }, calls[0][1]["kwargs"], ) self.assertEqual( { "address": "192.168.0.1", "profiles": {"profile2", "profile5"}, "frequency": 30, }, calls[1][1]["kwargs"], ) self.assertEqual( { "address": "192.168.0.1", "profiles": {"profile1"}, "frequency": 20 }, calls[2][1]["kwargs"], ) periodic_obj_mock.delete_unused_poll_tasks.assert_called_with( "192.168.0.1", [ "sc4snmp;192.168.0.1;60;poll", "sc4snmp;192.168.0.1;30;poll", "sc4snmp;192.168.0.1;20;poll", ], ) periodic_obj_mock.delete_disabled_poll_tasks.assert_called()
from unittest import TestCase from unittest.mock import MagicMock, patch from splunk_connect_for_snmp.common.inventory_record import InventoryRecord from splunk_connect_for_snmp.snmp.exceptions import SnmpActionError from splunk_connect_for_snmp.snmp.manager import Poller inventory_record = InventoryRecord.from_dict({ "address": "192.168.0.1", "port": "34", "version": "2c", "community": "public", "secret": "secret", "securityEngine": "ENGINE", "walk_interval": 1850, "profiles": "", "SmartProfiles": True, "delete": False, }) class TestDoWork(TestCase): @patch("pymongo.MongoClient", MagicMock()) @patch("mongolock.MongoLock.__init__", MagicMock()) @patch("mongolock.MongoLock.lock", MagicMock()) @patch("mongolock.MongoLock.release", MagicMock()) @patch("splunk_connect_for_snmp.snmp.auth.GetAuth", None) @patch("splunk_connect_for_snmp.snmp.manager.get_context_data", MagicMock()) @patch("splunk_connect_for_snmp.snmp.manager.UdpTransportTarget", MagicMock())
def load(): path = INVENTORY_PATH inventory_errors = False mongo_client = pymongo.MongoClient(MONGO_URI) targets_collection = mongo_client.sc4snmp.targets attributes_collection = mongo_client.sc4snmp.attributes mongo_db = mongo_client[MONGO_DB] inventory_records = mongo_db.inventory periodic_obj = customtaskmanager.CustomPeriodicTaskManager() migrate_database(mongo_client, periodic_obj) config_profiles = load_profiles() logger.info(f"Loading inventory from {path}") with open(path, encoding="utf-8") as csv_file: # Dict reader will trust the header of the csv ir_reader = DictReader(csv_file) for source_record in ir_reader: address = source_record["address"] if address.startswith("#"): logger.warning( f"Record: {address} is commented out. Skipping...") continue try: ir = InventoryRecord(**source_record) target = transform_address_to_key(ir.address, ir.port) if ir.delete: periodic_obj.disable_tasks(target) inventory_records.delete_one({ "address": ir.address, "port": ir.port }) targets_collection.remove({"address": target}) attributes_collection.remove({"address": target}) logger.info(f"Deleting record: {target}") else: status = inventory_records.update_one( { "address": ir.address, "port": ir.port }, {"$set": ir.asdict()}, upsert=True, ) profiles = source_record["profiles"].split(";") profile = None if profiles: profiles = [ p for p in profiles if config_profiles.get(p, {}).get( "condition", {}).get("type") == "walk" ] if profiles: profile = profiles[-1] ir.walk_interval = source_record["walk_interval"] if status.matched_count == 0: logger.info(f"New Record {ir} {status.upserted_id}") elif status.modified_count == 1 and status.upserted_id is None: logger.info(f"Modified Record {ir}") else: logger.debug(f"Unchanged Record {ir}") continue task_config = gen_walk_task(ir, profile) periodic_obj.manage_task(**task_config) except Exception as e: inventory_errors = True logger.exception(f"Exception raised for {target}: {e}") return inventory_errors