Skip to content

Commit 8038f40

Browse files
committed
Add handling of device secrets
1 parent 04dd361 commit 8038f40

File tree

5 files changed

+39
-18
lines changed

5 files changed

+39
-18
lines changed

netbox_onboarding/api/serializers.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ class OnboardingTaskSerializer(serializers.ModelSerializer):
3232

3333
password = serializers.CharField(required=False, write_only=True, help_text="Device password",)
3434

35+
secret = serializers.CharField(required=False, write_only=True, help_text="Device secret password",)
36+
3537
site = serializers.SlugRelatedField(
3638
many=False,
3739
read_only=False,
@@ -63,7 +65,7 @@ class OnboardingTaskSerializer(serializers.ModelSerializer):
6365

6466
status = serializers.CharField(required=False, help_text="Onboarding Status")
6567

66-
failed_raison = serializers.CharField(required=False, help_text="Failure reason")
68+
failed_reason = serializers.CharField(required=False, help_text="Failure reason")
6769

6870
message = serializers.CharField(required=False, help_text="Status message")
6971

@@ -79,12 +81,13 @@ class Meta: # noqa: D106 "Missing docstring in public nested class"
7981
"ip_address",
8082
"username",
8183
"password",
84+
"secret",
8285
"site",
8386
"role",
8487
"device_type",
8588
"platform",
8689
"status",
87-
"failed_raison",
90+
"failed_reason",
8891
"message",
8992
"port",
9093
"timeout",
@@ -95,8 +98,9 @@ def create(self, validated_data):
9598
# Fields are string-type so default to empty (instead of None)
9699
username = validated_data.pop("username", "")
97100
password = validated_data.pop("password", "")
101+
secret = validated_data.pop("secret", "")
98102

99-
credentials = Credentials(username=username, password=password)
103+
credentials = Credentials(username=username, password=password, secret=secret,)
100104

101105
ot = OnboardingTask.objects.create(**validated_data)
102106

netbox_onboarding/forms.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ class OnboardingTaskForm(BootstrapMixin, forms.ModelForm):
3737
password = forms.CharField(
3838
required=False, widget=forms.PasswordInput, help_text="Device password (will not be stored in database)"
3939
)
40+
secret = forms.CharField(
41+
required=False, widget=forms.PasswordInput, help_text="Device secret (will not be stored in database)"
42+
)
4043

4144
platform = forms.ModelChoiceField(
4245
queryset=Platform.objects.all(),
@@ -66,6 +69,7 @@ class Meta: # noqa: D106 "Missing docstring in public nested class"
6669
"timeout",
6770
"username",
6871
"password",
72+
"secret",
6973
"platform",
7074
"role",
7175
"device_type",
@@ -75,7 +79,7 @@ def save(self, commit=True, **kwargs):
7579
"""Save the model, and add it and the associated credentials to the onboarding worker queue."""
7680
model = super().save(commit=commit, **kwargs)
7781
if commit:
78-
credentials = Credentials(self.data.get("username"), self.data.get("password"))
82+
credentials = Credentials(self.data.get("username"), self.data.get("password"), self.data.get("secret"))
7983
get_queue("default").enqueue("netbox_onboarding.worker.onboard_device", model.pk, credentials)
8084
return model
8185

@@ -113,6 +117,7 @@ class OnboardingTaskFeedCSVForm(CustomFieldModelCSVForm):
113117
ip_address = forms.CharField(required=True, help_text="IP Address of the onboarded device")
114118
username = forms.CharField(required=False, help_text="Username, will not be stored in database")
115119
password = forms.CharField(required=False, help_text="Password, will not be stored in database")
120+
secret = forms.CharField(required=False, help_text="Secret password, will not be stored in database")
116121
platform = forms.ModelChoiceField(
117122
queryset=Platform.objects.all(),
118123
required=False,
@@ -140,6 +145,6 @@ def save(self, commit=True, **kwargs):
140145
"""Save the model, and add it and the associated credentials to the onboarding worker queue."""
141146
model = super().save(commit=commit, **kwargs)
142147
if commit:
143-
credentials = Credentials(self.data.get("username"), self.data.get("password"))
148+
credentials = Credentials(self.data.get("username"), self.data.get("password"), self.data.get("secret"))
144149
get_queue("default").enqueue("netbox_onboarding.worker.onboard_device", model.pk, credentials)
145150
return model

netbox_onboarding/models.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,3 +58,7 @@ class OnboardingTask(models.Model):
5858

5959
class Meta: # noqa: D106 "missing docstring in public nested class"
6060
ordering = ["created_on"]
61+
62+
def __str__(self):
63+
"""String representation of an OnboardingTask."""
64+
return f"{self.site} : {self.ip_address}"

netbox_onboarding/onboard.py

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
"""
1414

1515
import logging
16-
import os
1716
import re
1817
import socket
1918

@@ -70,13 +69,14 @@ def __str__(self):
7069
class NetdevKeeper:
7170
"""Used to maintain information about the network device during the onboarding process."""
7271

73-
def __init__(self, onboarding_task, username=None, password=None):
72+
def __init__(self, onboarding_task, username=None, password=None, secret=None):
7473
"""Initialize the network device keeper instance and ensure the required configuration parameters are provided.
7574
7675
Args:
7776
onboarding_task (OnboardingTask): Task being processed
78-
username (str): Device username (if unspecified, NAPALM_USERNAME environment variable will be used)
79-
password (str): Device password (if unspecified, NAPALM_PASSWORD environment variable will be used)
77+
username (str): Device username (if unspecified, NAPALM_USERNAME settings variable will be used)
78+
password (str): Device password (if unspecified, NAPALM_PASSWORD settings variable will be used)
79+
secret (str): Device secret password (if unspecified, NAPALM_ARGS["secret"] settings variable will be used)
8080
8181
Raises:
8282
OnboardException('fail-config'):
@@ -92,8 +92,9 @@ def __init__(self, onboarding_task, username=None, password=None):
9292
self.serial_number = None
9393
self.mgmt_ifname = None
9494
self.mgmt_pflen = None
95-
self.username = username or os.environ.get("NAPALM_USERNAME", None)
96-
self.password = password or os.environ.get("NAPALM_PASSWORD", None)
95+
self.username = username or settings.NAPALM_USERNAME
96+
self.password = password or settings.NAPALM_PASSWORD
97+
self.secret = secret or settings.NAPALM_ARGS.get("secret", None)
9798

9899
def check_reachability(self):
99100
"""Ensure that the device at the mgmt-ipaddr provided is reachable.
@@ -129,6 +130,7 @@ def guess_netmiko_device_type(**kwargs):
129130
"host": kwargs.get("host"),
130131
"username": kwargs.get("username"),
131132
"password": kwargs.get("password"),
133+
"secret": kwargs.get("secret"),
132134
}
133135

134136
try:
@@ -159,7 +161,7 @@ def get_platform_slug(self):
159161
platform_slug = self.ot.platform.slug
160162
else:
161163
platform_slug = self.guess_netmiko_device_type(
162-
host=self.ot.ip_address, username=self.username, password=self.password
164+
host=self.ot.ip_address, username=self.username, password=self.password, secret=self.secret,
163165
)
164166

165167
logging.info("PLATFORM NAME is %s", platform_slug)
@@ -233,7 +235,15 @@ def get_required_info(
233235
)
234236

235237
driver = get_network_driver(driver_name)
236-
dev = driver(hostname=mgmt_ipaddr, username=self.username, password=self.password, timeout=self.ot.timeout)
238+
optional_args = settings.NAPALM_ARGS.copy()
239+
optional_args["secret"] = self.secret
240+
dev = driver(
241+
hostname=mgmt_ipaddr,
242+
username=self.username,
243+
password=self.password,
244+
timeout=self.ot.timeout,
245+
optional_args=optional_args,
246+
)
237247

238248
dev.open()
239249
logging.info("COLLECT: device facts")
@@ -265,16 +275,13 @@ def get_mgmt_info():
265275
return (if_name, if_addr_data["prefix_length"])
266276
return (default_mgmt_if, default_mgmt_pfxlen)
267277

268-
mgmt_ifname, mgmt_pflen = get_mgmt_info()
269-
270278
# retain the attributes that will be later used by NetBox processing.
271279

272280
self.hostname = facts["hostname"]
273281
self.vendor = facts["vendor"].title()
274282
self.model = facts["model"].lower()
275283
self.serial_number = facts["serial_number"]
276-
self.mgmt_ifname = mgmt_ifname
277-
self.mgmt_pflen = mgmt_pflen
284+
self.mgmt_ifname, self.mgmt_pflen = get_mgmt_info()
278285

279286

280287
# -----------------------------------------------------------------------------

netbox_onboarding/utils/credentials.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,11 @@
1616
class Credentials:
1717
"""Class used to hide user's credentials in RQ worker and Django."""
1818

19-
def __init__(self, username=None, password=None):
19+
def __init__(self, username=None, password=None, secret=None):
2020
"""Create a Credentials instance."""
2121
self.username = username
2222
self.password = password
23+
self.secret = secret
2324

2425
def __repr__(self):
2526
"""Return string representation of a Credentials object."""

0 commit comments

Comments
 (0)