Skip to content

Commit

Permalink
Merge pull request #140 from nautobot/shooks-assign-secret-group
Browse files Browse the repository at this point in the history
Add options to:
  - assign the secret group used in the job to the resulting device object
  - add the 'management only' flag to the created interface
  • Loading branch information
scetron authored Dec 15, 2023
2 parents 409ddd3 + 4f30587 commit ed4c0ce
Show file tree
Hide file tree
Showing 5 changed files with 56 additions and 2 deletions.
2 changes: 2 additions & 0 deletions docs/admin/install.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@ Although the plugin can run without providing any settings, the plugin behavior
- `default_management_prefix_length` integer ( default 0), length of the prefix that will be used for the management IP address, if the IP can't be found.
- `skip_device_type_on_update` boolean (default False), If True, an existing Nautobot device will not get its device type updated. If False, device type will be updated with one discovered on a device.
- `skip_manufacturer_on_update` boolean (default False), If True, an existing Nautobot device will not get its manufacturer updated. If False, manufacturer will be updated with one discovered on a device.
- `assign_secrets_group` boolean (default False), If True, the credentials used to connect to the device will be assigned as the secrets group for the device upon creation. If False, no secrets group will be assigned.
- `set_management_only_interface` boolean (default False), If True, the interface that is created or updated will be set to management only. If False, the interface will be set to not be management only.
- `platform_map` (dictionary), mapping of an **auto-detected** Netmiko platform to the **Nautobot slug** name of your Platform. The dictionary should be in the format:
```python
{
Expand Down
2 changes: 2 additions & 0 deletions nautobot_device_onboarding/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ class NautobotDeviceOnboardingConfig(NautobotAppConfig):
"skip_device_type_on_update": False,
"skip_manufacturer_on_update": False,
"platform_map": {},
"assign_secrets_group": False,
"set_management_only_interface": False,
"onboarding_extensions_map": {
"ios": "nautobot_device_onboarding.onboarding_extensions.ios",
},
Expand Down
3 changes: 3 additions & 0 deletions nautobot_device_onboarding/jobs.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ def __init__(self, *args, **kwargs):
self.location = None
self.device_type = None
self.role = None
self.credentials = None
super().__init__(*args, **kwargs)

def run(self, *args, **data):
Expand All @@ -83,6 +84,7 @@ def run(self, *args, **data):
self.location = data["location"]
self.device_type = data["device_type"]
self.role = data["role"]
self.credentials = data["credentials"]

self.logger.info("START: onboarding devices")
# allows for itteration without having to spawn multiple jobs
Expand Down Expand Up @@ -126,6 +128,7 @@ def _onboard(self, address):
"netdev_nb_role_name": self.role.name if self.role else PLUGIN_SETTINGS["default_device_role"],
"netdev_nb_role_color": PLUGIN_SETTINGS["default_device_role_color"],
"netdev_nb_platform_name": self.platform.name if self.platform else None,
"netdev_nb_credentials": self.credentials if PLUGIN_SETTINGS["assign_secrets_group"] else None,
# Kwargs discovered on the Onboarded Device:
"netdev_hostname": netdev_dict["netdev_hostname"],
"netdev_vendor": netdev_dict["netdev_vendor"],
Expand Down
23 changes: 22 additions & 1 deletion nautobot_device_onboarding/nautobot_keeper.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ def __init__( # pylint: disable=R0913,R0914
netdev_netmiko_device_type=None,
onboarding_class=None,
driver_addon_result=None,
netdev_nb_credentials=None,
):
"""Create an instance and initialize the managed attributes that are used throughout the onboard processing.
Expand All @@ -112,13 +113,15 @@ def __init__( # pylint: disable=R0913,R0914
netdev_netmiko_device_type (str): Device's Netmiko device type
onboarding_class (Object): Onboarding Class (future use)
driver_addon_result (Any): Attached extended result (future use)
netdev_nb_credentials (Object): Device's secrets group object
"""
self.netdev_mgmt_ip_address = netdev_mgmt_ip_address
self.netdev_nb_location_name = netdev_nb_location_name
self.netdev_nb_device_type_name = netdev_nb_device_type_name
self.netdev_nb_role_name = netdev_nb_role_name
self.netdev_nb_role_color = netdev_nb_role_color
self.netdev_nb_platform_name = netdev_nb_platform_name
self.netdev_nb_credentials = netdev_nb_credentials

self.netdev_hostname = netdev_hostname
self.netdev_vendor = netdev_vendor
Expand Down Expand Up @@ -412,12 +415,21 @@ def ensure_device_instance(self, default_status=PLUGIN_SETTINGS["default_device_
def ensure_interface(self):
"""Ensures that the interface associated with the mgmt_ipaddr exists and is assigned to the device."""
if self.netdev_mgmt_ifname:
mgmt_only_setting = PLUGIN_SETTINGS["set_management_only_interface"]

# TODO: Add option for default interface status
self.nb_mgmt_ifname, _ = Interface.objects.get_or_create(
name=self.netdev_mgmt_ifname,
device=self.device,
defaults={"type": InterfaceTypeChoices.TYPE_OTHER, "status": Status.objects.get(name="Active")},
defaults={
"type": InterfaceTypeChoices.TYPE_OTHER,
"status": Status.objects.get(name="Active"),
"mgmt_only": mgmt_only_setting,
},
)
if mgmt_only_setting:
self.nb_mgmt_ifname.mgmt_only = mgmt_only_setting
self.nb_mgmt_ifname.validated_save()

ensure_default_cf(obj=self.nb_mgmt_ifname, model=Interface)

Expand Down Expand Up @@ -468,6 +480,12 @@ def ensure_primary_ip(self):
self.device.full_clean()
self.device.save()

def ensure_secret_group(self):
"""Optionally assign secret group from onboarding to created/updated device."""
if PLUGIN_SETTINGS["assign_secrets_group"]:
self.device.secrets_group = self.netdev_nb_credentials
self.device.validated_save()

def ensure_device(self):
"""Ensure that the device represented by the DevNetKeeper exists in the Nautobot system."""
self.ensure_onboarded_device()
Expand All @@ -481,3 +499,6 @@ def ensure_device(self):
if PLUGIN_SETTINGS["create_management_interface_if_missing"]:
self.ensure_interface()
self.ensure_primary_ip()

if PLUGIN_SETTINGS["assign_secrets_group"]:
self.ensure_secret_group()
28 changes: 27 additions & 1 deletion nautobot_device_onboarding/tests/test_nautobot_keeper.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from nautobot.extras.models import Role, Status, CustomField
from nautobot.ipam.models import IPAddress
from nautobot.extras.choices import CustomFieldTypeChoices

from nautobot.extras.models.secrets import SecretsGroup
from nautobot_device_onboarding.exceptions import OnboardException
from nautobot_device_onboarding.nautobot_keeper import NautobotKeeper

Expand Down Expand Up @@ -563,3 +563,29 @@ def test_ensure_custom_fields(self):
self.assertEqual(device.device_type.manufacturer.cf["cf_manufacturer"], "Foobar!")
self.assertEqual(device.interfaces.get(name="Management0").cf["cf_interface"], "2016-06-23")
self.assertEqual(device.primary_ip.cf["cf_ipaddress"], "http://example.com/")

def test_ensure_secret_group_exist(self):
"Verify secret group assignment to device when specified in plugin config."

PLUGIN_SETTINGS["assign_secrets_group"] = True
test_secret_group = SecretsGroup.objects.create(name="test_secret_group")

onboarding_kwargs = {
"netdev_hostname": "device1",
"netdev_nb_role_name": "switch",
"netdev_vendor": "Cisco",
"netdev_model": "c2960",
"netdev_nb_location_name": self.site1,
"netdev_netmiko_device_type": "cisco_ios",
"netdev_serial_number": "123456",
"netdev_mgmt_ip_address": "192.0.2.10",
"netdev_mgmt_ifname": "GigaEthernet0",
"netdev_nb_credentials": test_secret_group,
}

nbk = NautobotKeeper(**onboarding_kwargs)

nbk.ensure_device()

nbk.ensure_secret_group()
self.assertEqual(nbk.netdev_nb_credentials.name, test_secret_group.name)

0 comments on commit ed4c0ce

Please sign in to comment.