diff --git a/changes/2.added b/changes/2.added new file mode 100755 index 0000000..d96ed1e --- /dev/null +++ b/changes/2.added @@ -0,0 +1 @@ +Add initial set of unittest. diff --git a/nautobot_plugin_nornir/tests/fixtures.py b/nautobot_plugin_nornir/tests/fixtures.py new file mode 100755 index 0000000..d69fda0 --- /dev/null +++ b/nautobot_plugin_nornir/tests/fixtures.py @@ -0,0 +1,60 @@ +"""Fixtures for reusable test code.""" + +from nautobot.dcim.models import Device, DeviceType, Location, LocationType, Manufacturer, Platform +from nautobot.extras.models.roles import ContentType, Role +from nautobot.extras.models.statuses import Status + + +def create_pre_req_data(): + """Create the data that all tests need.""" + device_content_type = ContentType.objects.get(model="device") + location_type_region = LocationType.objects.create(name="Region") + location_type = LocationType.objects.create(name="Site") + location_type.content_types.set([device_content_type]) + active = Status.objects.get(name="Active") + location_us = Location.objects.create( + name="US", + location_type=location_type_region, + status_id=active.id, + ) + Location.objects.create( + name="USWEST", + parent=location_us, + location_type_id=location_type.id, + status_id=active.id, + ) + manufacturer1 = Manufacturer.objects.create(name="Juniper") + Platform.objects.create(name="Juniper Junos", network_driver="juniper_junos", napalm_driver="junos") + + DeviceType.objects.create(model="SRX3600", manufacturer=manufacturer1) + device_role1 = Role.objects.create(name="Firewall") + device_role1.content_types.set([device_content_type]) + device_role2 = Role.objects.create(name="Switch") + device_role2.content_types.set([device_content_type]) + + +def create_test_data(): + """Create test data.""" + create_pre_req_data() + location = Location.objects.get(name="USWEST") + device_type1 = DeviceType.objects.get(model="SRX3600") + platform = Platform.objects.get(name="Juniper Junos") + device_role1 = Role.objects.get(name="Firewall") + device_role2 = Role.objects.get(name="Switch") + active = Status.objects.get(name="Active") + Device.objects.create( + name="device1", + location=location, + device_type=device_type1, + platform=platform, + role=device_role1, + status_id=active.id, + ) + Device.objects.create( + name="device2", + location=location, + device_type=device_type1, + platform=platform, + role=device_role2, + status_id=active.id, + ) diff --git a/nautobot_plugin_nornir/tests/test_credentials_env_vars.py b/nautobot_plugin_nornir/tests/test_credentials_env_vars.py new file mode 100755 index 0000000..642c72a --- /dev/null +++ b/nautobot_plugin_nornir/tests/test_credentials_env_vars.py @@ -0,0 +1,81 @@ +"""Unit Tests for NautobotORM Inventory with Environment Vars.""" + +import os +from unittest import mock + +from django.test import TestCase +from nautobot.dcim.models import Device +from nornir import InitNornir +from nornir.core.plugins.inventory import InventoryPluginRegister + +from nautobot_plugin_nornir.plugins.credentials.settings_vars import PLUGIN_CFG +from nautobot_plugin_nornir.plugins.inventory.nautobot_orm import NautobotORMInventory +from nautobot_plugin_nornir.tests.fixtures import create_test_data + +InventoryPluginRegister.register("nautobot-inventory", NautobotORMInventory) + + +@mock.patch.dict( + PLUGIN_CFG, + { + "nornir_settings": { + "credentials": "nautobot_plugin_nornir.plugins.credentials.env_vars.CredentialsEnvVars", + }, + }, +) +@mock.patch.dict(os.environ, {"NAPALM_USERNAME": "credsenv-user"}) +@mock.patch.dict(os.environ, {"NAPALM_PASSWORD": "credsenv-password123"}) +@mock.patch.dict(os.environ, {"DEVICE_SECRET": "credsenv-secret123"}) +class EnvironmentVarCredentialTests(TestCase): + """Test cases for ensuring the NautobotORM Inventory is working properly with env vars.""" + + def setUp(self): + """Create a superuser and token for API calls.""" + create_test_data() + + def test_hosts_credentials(self): + """Ensure credentials is assigned to hosts.""" + # pylint: disable=duplicate-code + qs = Device.objects.all() + nr_obj = InitNornir( + inventory={ + "plugin": "nautobot-inventory", + "options": { + "credentials_class": PLUGIN_CFG["nornir_settings"]["credentials"], + "params": PLUGIN_CFG["nornir_settings"].get("inventory_params"), + "queryset": qs, + }, + }, + ) + self.assertEqual(nr_obj.inventory.hosts["device1"].username, "credsenv-user") + self.assertEqual(nr_obj.inventory.hosts["device1"].password, "credsenv-password123") + self.assertEqual( + nr_obj.inventory.hosts["device1"]["connection_options"]["netmiko"]["extras"]["secret"], "credsenv-secret123" + ) + self.assertEqual( + nr_obj.inventory.hosts["device1"]["connection_options"]["napalm"]["extras"]["optional_args"]["secret"], + "credsenv-secret123", + ) + # self.assertEqual( + # nr_obj.inventory.hosts["device1"]["connection_options"]["pyntc"]["extras"]["secret"], "credsenv-secret123" + # ) + # self.assertEqual( + # nr_obj.inventory.hosts["device1"]["connection_options"]["scrapli"]["extras"]["auth_secondary"], "credsenv-secret123" + # ) + self.assertEqual(nr_obj.inventory.hosts["device1"]["connection_options"]["napalm"]["platform"], "junos") + self.assertEqual(nr_obj.inventory.hosts["device2"].username, "credsenv-user") + self.assertEqual(nr_obj.inventory.hosts["device2"].password, "credsenv-password123") + self.assertEqual( + nr_obj.inventory.hosts["device2"]["connection_options"]["netmiko"]["extras"]["secret"], "credsenv-secret123" + ) + self.assertEqual( + nr_obj.inventory.hosts["device2"]["connection_options"]["napalm"]["extras"]["optional_args"]["secret"], + "credsenv-secret123", + ) + # self.assertEqual( + # nr_obj.inventory.hosts["device2"]["connection_options"]["pyntc"]["extras"]["secret"], "credsenv-secret123" + # ) + # self.assertEqual( + # nr_obj.inventory.hosts["device2"]["connection_options"]["scrapli"]["extras"]["auth_secondary"], "credsenv-secret123" + # ) + self.assertEqual(nr_obj.inventory.hosts["device2"]["connection_options"]["napalm"]["platform"], "junos") diff --git a/nautobot_plugin_nornir/tests/test_credentials_nautobot_secrets.py b/nautobot_plugin_nornir/tests/test_credentials_nautobot_secrets.py new file mode 100755 index 0000000..9fa2bc6 --- /dev/null +++ b/nautobot_plugin_nornir/tests/test_credentials_nautobot_secrets.py @@ -0,0 +1,136 @@ +"""Unit Tests for NautobotORM Inventory with Nautobot Secrets Feature.""" + +import os +from unittest import mock + +from django.test import TestCase +from nautobot.dcim.models import Device +from nautobot.extras.models.secrets import ( + Secret, + SecretsGroup, + SecretsGroupAccessTypeChoices, + SecretsGroupAssociation, + SecretsGroupSecretTypeChoices, +) +from nornir import InitNornir +from nornir.core.plugins.inventory import InventoryPluginRegister + +from nautobot_plugin_nornir.plugins.credentials.settings_vars import PLUGIN_CFG +from nautobot_plugin_nornir.plugins.inventory.nautobot_orm import NautobotORMInventory +from nautobot_plugin_nornir.tests.fixtures import create_test_data + +InventoryPluginRegister.register("nautobot-inventory", NautobotORMInventory) + + +@mock.patch.dict( + PLUGIN_CFG, + { + "nornir_settings": { + "credentials": "nautobot_plugin_nornir.plugins.credentials.nautobot_secrets.CredentialsNautobotSecrets", + }, + }, +) +@mock.patch.dict(os.environ, {"NET_FIREWALL_USERNAME": "fw-user"}) +@mock.patch.dict(os.environ, {"NET_FIREWALL_PASSWORD": "fw-password123"}) +@mock.patch.dict(os.environ, {"NET_FIREWALL_SECRET": "fw-secret123"}) +@mock.patch.dict(os.environ, {"NET_SWITCH_USERNAME": "sw-user"}) +@mock.patch.dict(os.environ, {"NET_SWITCH_PASSWORD": "sw-password123"}) +@mock.patch.dict(os.environ, {"NET_SWITCH_SECRET": "sw-secret123"}) +class SecretsGroupCredentialTests(TestCase): + """Test cases for ensuring the NautobotORM Inventory is working properly with Secrets Feature.""" + + def setUp(self): + """Create a superuser and token for API calls.""" + create_test_data() + + user_user = Secret.objects.create( + name="Environment Vars User", + parameters={"variable": "NET_{{ obj.role.name | upper }}_USERNAME"}, + provider="environment-variable", + ) + password = Secret.objects.create( + name="Environment Vars Password", + parameters={"variable": "NET_{{ obj.role.name | upper }}_PASSWORD"}, + provider="environment-variable", + ) + secret = Secret.objects.create( + name="Environment Vars Secret", + parameters={"variable": "NET_{{ obj.role.name | upper }}_SECRET"}, + provider="environment-variable", + ) + + sec_group = SecretsGroup.objects.create(name="Environment Vars SG") + SecretsGroup.objects.create(name="Net Creds") + SecretsGroupAssociation.objects.create( + secret=user_user, + secrets_group=sec_group, + access_type=SecretsGroupAccessTypeChoices.TYPE_GENERIC, + secret_type=SecretsGroupSecretTypeChoices.TYPE_USERNAME, + ) + SecretsGroupAssociation.objects.create( + secret=password, + secrets_group=sec_group, + access_type=SecretsGroupAccessTypeChoices.TYPE_GENERIC, + secret_type=SecretsGroupSecretTypeChoices.TYPE_PASSWORD, + ) + SecretsGroupAssociation.objects.create( + secret=secret, + secrets_group=sec_group, + access_type=SecretsGroupAccessTypeChoices.TYPE_GENERIC, + secret_type=SecretsGroupSecretTypeChoices.TYPE_SECRET, + ) + dev1 = Device.objects.get(name="device1") + dev1.secrets_group = sec_group + dev1.save() + + dev2 = Device.objects.get(name="device2") + dev2.secrets_group = sec_group + dev2.save() + + + def test_hosts_credentials(self): + """Ensure credentials is assigned to hosts.""" + # pylint: disable=duplicate-code + qs = Device.objects.all() + nr_obj = InitNornir( + inventory={ + "plugin": "nautobot-inventory", + "options": { + "credentials_class": PLUGIN_CFG["nornir_settings"]["credentials"], + "params": PLUGIN_CFG["nornir_settings"].get("inventory_params"), + "queryset": qs, + }, + }, + ) + self.assertEqual(nr_obj.inventory.hosts["device1"].username, "fw-user") + self.assertEqual(nr_obj.inventory.hosts["device1"].password, "fw-password123") + self.assertEqual( + nr_obj.inventory.hosts["device1"]["connection_options"]["netmiko"]["extras"]["secret"], "fw-secret123" + ) + self.assertEqual( + nr_obj.inventory.hosts["device1"]["connection_options"]["napalm"]["extras"]["optional_args"]["secret"], + "fw-secret123", + ) + # self.assertEqual( + # nr_obj.inventory.hosts["device1"]["connection_options"]["pyntc"]["extras"]["secret"], "credsenv-secret123" + # ) + # self.assertEqual( + # nr_obj.inventory.hosts["device1"]["connection_options"]["scrapli"]["extras"]["auth_secondary"], "credsenv-secret123" + # ) + self.assertEqual(nr_obj.inventory.hosts["device1"]["connection_options"]["napalm"]["platform"], "junos") + self.assertEqual(nr_obj.inventory.hosts["device2"].username, "sw-user") + self.assertEqual(nr_obj.inventory.hosts["device2"].password, "sw-password123") + self.assertEqual( + nr_obj.inventory.hosts["device2"]["connection_options"]["netmiko"]["extras"]["secret"], "sw-secret123" + ) + self.assertEqual( + nr_obj.inventory.hosts["device2"]["connection_options"]["napalm"]["extras"]["optional_args"]["secret"], + "sw-secret123", + ) + # self.assertEqual( + # nr_obj.inventory.hosts["device2"]["connection_options"]["pyntc"]["extras"]["secret"], "credsenv-secret123" + # ) + # self.assertEqual( + # nr_obj.inventory.hosts["device2"]["connection_options"]["scrapli"]["extras"]["auth_secondary"], "credsenv-secret123" + # ) + self.assertEqual(nr_obj.inventory.hosts["device2"]["connection_options"]["napalm"]["platform"], "junos") diff --git a/nautobot_plugin_nornir/tests/test_credentials_settings_vars.py b/nautobot_plugin_nornir/tests/test_credentials_settings_vars.py new file mode 100755 index 0000000..cbf6895 --- /dev/null +++ b/nautobot_plugin_nornir/tests/test_credentials_settings_vars.py @@ -0,0 +1,80 @@ +"""Unit Tests for NautobotORM Inventory with Settings Vars.""" + +from unittest import mock + +from django.test import TestCase +from nautobot.dcim.models import Device +from nornir import InitNornir +from nornir.core.plugins.inventory import InventoryPluginRegister + +from nautobot_plugin_nornir.plugins.credentials.settings_vars import PLUGIN_CFG +from nautobot_plugin_nornir.plugins.inventory.nautobot_orm import NautobotORMInventory +from nautobot_plugin_nornir.tests.fixtures import create_test_data + +InventoryPluginRegister.register("nautobot-inventory", NautobotORMInventory) + + +@mock.patch.dict( + PLUGIN_CFG, + { + "nornir_settings": { + "credentials": "nautobot_plugin_nornir.plugins.credentials.settings_vars.CredentialsSettingsVars", + }, + "username": "settings-ntc", + "password": "settings-password123", + "secret": "settings-secret123", + }, +) +class SettingsVarCredentialTests(TestCase): + """Test cases for ensuring the NautobotORM Inventory is working properly with settings vars.""" + + def setUp(self): + """Create a superuser and token for API calls.""" + create_test_data() + + def test_hosts_credentials(self): + """Ensure credentials is assigned to hosts.""" + # pylint: disable=duplicate-code + qs = Device.objects.all() + nr_obj = InitNornir( + inventory={ + "plugin": "nautobot-inventory", + "options": { + "credentials_class": PLUGIN_CFG["nornir_settings"]["credentials"], + "params": PLUGIN_CFG["nornir_settings"].get("inventory_params"), + "queryset": qs, + }, + }, + ) + self.assertEqual(nr_obj.inventory.hosts["device1"].username, "settings-ntc") + self.assertEqual(nr_obj.inventory.hosts["device1"].password, "settings-password123") + self.assertEqual( + nr_obj.inventory.hosts["device1"]["connection_options"]["netmiko"]["extras"]["secret"], "settings-secret123" + ) + self.assertEqual( + nr_obj.inventory.hosts["device1"]["connection_options"]["napalm"]["extras"]["optional_args"]["secret"], + "settings-secret123", + ) + # self.assertEqual( + # nr_obj.inventory.hosts["device1"]["connection_options"]["pyntc"]["extras"]["secret"], "settings-secret123" + # ) + # self.assertEqual( + # nr_obj.inventory.hosts["device1"]["connection_options"]["scrapli"]["extras"]["auth_secondary"], "settings-secret123" + # ) + self.assertEqual(nr_obj.inventory.hosts["device1"]["connection_options"]["napalm"]["platform"], "junos") + self.assertEqual(nr_obj.inventory.hosts["device2"].username, "settings-ntc") + self.assertEqual(nr_obj.inventory.hosts["device2"].password, "settings-password123") + self.assertEqual( + nr_obj.inventory.hosts["device2"]["connection_options"]["netmiko"]["extras"]["secret"], "settings-secret123" + ) + self.assertEqual( + nr_obj.inventory.hosts["device2"]["connection_options"]["napalm"]["extras"]["optional_args"]["secret"], + "settings-secret123", + ) + # self.assertEqual( + # nr_obj.inventory.hosts["device2"]["connection_options"]["pyntc"]["extras"]["secret"], "settings-secret123" + # ) + # self.assertEqual( + # nr_obj.inventory.hosts["device2"]["connection_options"]["scrapli"]["extras"]["auth_secondary"], "settings-secret123" + # ) + self.assertEqual(nr_obj.inventory.hosts["device2"]["connection_options"]["napalm"]["platform"], "junos") diff --git a/nautobot_plugin_nornir/tests/test_nautobot_orm.py b/nautobot_plugin_nornir/tests/test_inventory_nautobot_orm.py similarity index 54% rename from nautobot_plugin_nornir/tests/test_nautobot_orm.py rename to nautobot_plugin_nornir/tests/test_inventory_nautobot_orm.py index e60acdf..75e5a26 100644 --- a/nautobot_plugin_nornir/tests/test_nautobot_orm.py +++ b/nautobot_plugin_nornir/tests/test_inventory_nautobot_orm.py @@ -3,11 +3,11 @@ from unittest.mock import patch from django.test import TestCase -from nautobot.dcim.models import Device, DeviceType, Location, LocationType, Manufacturer, Platform -from nautobot.extras.models.roles import ContentType, Role -from nautobot.extras.models.statuses import Status +from nautobot.dcim.models import Device +from nautobot_plugin_nornir.constants import CONNECTION_SECRETS_PATHS, DRIVERS, PLUGIN_CFG from nautobot_plugin_nornir.plugins.inventory.nautobot_orm import NautobotORMInventory +from nautobot_plugin_nornir.tests.fixtures import create_test_data class NautobotORMInventoryTests(TestCase): @@ -15,49 +15,7 @@ class NautobotORMInventoryTests(TestCase): def setUp(self): """Create a superuser and token for API calls.""" - device_content_type = ContentType.objects.get(model="device") - location_type_region = LocationType.objects.create(name="Region") - self.location_type = LocationType.objects.create(name="Site") - self.location_type.content_types.set([device_content_type]) - active = Status.objects.get(name="Active") - location_us = Location.objects.create( - name="US", - location_type=location_type_region, - status_id=active.id, - ) - self.location = Location.objects.create( - name="USWEST", - parent=location_us, - location_type_id=self.location_type.id, - status_id=active.id, - ) - self.manufacturer1 = Manufacturer.objects.create(name="Juniper") - self.platform = Platform.objects.create( - name="Juniper Junos", network_driver="juniper_junos", napalm_driver="junos" - ) - self.device_type1 = DeviceType.objects.create(model="SRX3600", manufacturer=self.manufacturer1) - self.device_role1 = Role.objects.create(name="Firewall") - self.device_role1.content_types.set([device_content_type]) - self.device_role2 = Role.objects.create(name="Switch") - self.device_role2.content_types.set([device_content_type]) - - Device.objects.create( - name="device1", - location=self.location, - device_type=self.device_type1, - platform=self.platform, - role=self.device_role1, - status_id=active.id, - ) - - Device.objects.create( - name="device2", - location=self.location, - device_type=self.device_type1, - platform=self.platform, - role=self.device_role2, - status_id=active.id, - ) + create_test_data() def test_init_default(self): """Ensure inventory is working properly with default settings.""" @@ -78,8 +36,8 @@ def test_init_filters_device(self): def test_hosts_platform(self): """Ensure platform is assigned to hosts.""" inv = NautobotORMInventory().load() - self.assertEqual(inv.hosts["device1"]["connection_options"]["napalm"]["platform"], self.platform.napalm_driver) - self.assertEqual(inv.hosts["device2"]["connection_options"]["napalm"]["platform"], self.platform.napalm_driver) + self.assertEqual(inv.hosts["device1"]["connection_options"]["napalm"]["platform"], "junos") + self.assertEqual(inv.hosts["device2"]["connection_options"]["napalm"]["platform"], "junos") def test_get_all_devices_to_parent_mapping(self): """Ensure the mapping of devices to parents is correct.""" @@ -112,3 +70,57 @@ def test_get_all_devices_to_parent_mapping_denied(self): "device2": ["location__US"], }, ) + + def test_credentials_path(self): + """Ensure credentials path is getting built.""" + self.assertEqual( + CONNECTION_SECRETS_PATHS, + { + "netmiko": "netmiko.extras.secret", + "napalm": "napalm.extras.optional_args.secret", + "pyntc": "pyntc.extras.secret", + "scrapli": "scrapli.extras.auth_secondary", + }, + ) + + @patch.dict( + PLUGIN_CFG, + { + "nornir_settings": { + "credentials": "nautobot_plugin_nornir.plugins.credentials.settings_vars.CredentialsSettingsVars", + }, + "connection_options": None, + }, + ) + def test_credentials_path_drivers_defaults(self): + """Ensure credentials path is getting built.""" + self.assertEqual(sorted(DRIVERS), sorted(["napalm", "netmiko", "scrapli", "pyntc"])) + + # global_options = PLUGIN_CFG.get("connection_options", {item: {} for item in DRIVERS}) + # drivers = list(set(DRIVERS + list(PLUGIN_CFG.get("connection_options", {}).keys()))) + # self.assertEqual(list(global_options.keys()), drivers) + + @patch.dict( + PLUGIN_CFG, + { + "nornir_settings": { + "credentials": "nautobot_plugin_nornir.plugins.credentials.settings_vars.CredentialsSettingsVars", + }, + "connection_options": { + "napalm": { + "extras": { + "optional_args": {"global_delay_factor": 1}, + }, + }, + "netmiko": { + "extras": { + "global_delay_factor": 1, + }, + }, + }, + }, + ) + def test_credentials_path_drivers_global_override(self): + """Ensure credentials path is getting built.""" + global_options = PLUGIN_CFG.get("connection_options", {item: {} for item in DRIVERS}) + self.assertEqual(sorted(global_options), sorted(["napalm", "netmiko"]))