Skip to content

Commit d83ed1f

Browse files
committed
Gracefully handle case where mgmt address is not found on the device, plus other fixes
1 parent e9c9256 commit d83ed1f

File tree

6 files changed

+46
-32
lines changed

6 files changed

+46
-32
lines changed

netbox_onboarding/onboard.py

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
import re
1818
import socket
1919

20-
from first import first
2120
from napalm import get_network_driver
2221
from napalm.base.exceptions import ConnectionException, CommandErrorException
2322

@@ -217,6 +216,9 @@ def get_required_info(self):
217216
try:
218217
platform_slug = self.get_platform_slug()
219218
platform_object = self.get_platform_object_from_netbox(platform_slug=platform_slug)
219+
if self.ot.platform != platform_object:
220+
self.ot.platform = platform_object
221+
self.ot.save()
220222

221223
driver_name = platform_object.napalm_driver
222224

@@ -248,17 +250,18 @@ def get_required_info(self):
248250
# locate the interface assigned with the mgmt_ipaddr value and retain
249251
# the interface name and IP prefix-length so that we can use it later
250252
# when creating the IPAM IP-Address instance.
253+
# Note that in some cases (e.g., NAT) the mgmt_ipaddr may differ than
254+
# the interface addresses present on the device. We need to handle this.
251255

252-
try:
253-
mgmt_ifname, mgmt_pflen = first(
254-
(if_name, if_addr_data["prefix_length"])
255-
for if_name, if_data in ip_ifs.items()
256-
for if_addr, if_addr_data in if_data["ipv4"].items()
257-
if if_addr == mgmt_ipaddr
258-
)
256+
def get_mgmt_info():
257+
"""Get the interface name and prefix length for the management interface."""
258+
for if_name, if_data in ip_ifs.items():
259+
for if_addr, if_addr_data in if_data["ipv4"].items():
260+
if if_addr == mgmt_ipaddr:
261+
return (if_name, if_addr_data["prefix_length"])
262+
return ("PLACEHOLDER", 0)
259263

260-
except Exception as exc:
261-
raise OnboardException(reason="fail-general", message=str(exc))
264+
mgmt_ifname, mgmt_pflen = get_mgmt_info()
262265

263266
# retain the attributes that will be later used by NetBox processing.
264267

@@ -322,14 +325,14 @@ def ensure_device_type(
322325
# instance.
323326

324327
try:
325-
self.manufacturer = Manufacturer.objects.get(slug=self.netdev.vendor)
328+
self.manufacturer = Manufacturer.objects.get(slug=self.netdev.vendor.lower())
326329
except Manufacturer.DoesNotExist:
327330
if not create_manufacturer:
328331
raise OnboardException(
329332
reason="fail-config", message=f"ERROR manufacturer not found: {self.netdev.vendor}"
330333
)
331334

332-
self.manufacturer = Manufacturer.objects.create(name=self.netdev.vendor, slug=self.netdev.vendor)
335+
self.manufacturer = Manufacturer.objects.create(name=self.netdev.vendor, slug=self.netdev.vendor.lower())
333336
self.manufacturer.save()
334337

335338
# Now see if the device type (slug) already exists,
@@ -343,7 +346,9 @@ def ensure_device_type(
343346
logging.warning("device model is now: %s", self.netdev.model)
344347

345348
try:
346-
self.device_type = DeviceType.objects.get(slug=self.netdev.model)
349+
self.device_type = DeviceType.objects.get(slug=self.netdev.model.lower())
350+
self.netdev.ot.device_type = self.device_type.slug
351+
self.netdev.ot.save()
347352
except DeviceType.DoesNotExist:
348353
if not create_device_type:
349354
raise OnboardException(
@@ -352,9 +357,11 @@ def ensure_device_type(
352357

353358
logging.info("CREATE: device-type: %s", self.netdev.model)
354359
self.device_type = DeviceType.objects.create(
355-
slug=self.netdev.model, model=self.netdev.model.upper(), manufacturer=self.manufacturer
360+
slug=self.netdev.model.lower(), model=self.netdev.model.upper(), manufacturer=self.manufacturer
356361
)
357362
self.device_type.save()
363+
self.netdev.ot.device_type = self.device_type.slug
364+
self.netdev.ot.save()
358365
return
359366

360367
if self.device_type.manufacturer.id != self.manufacturer.id:
@@ -398,6 +405,7 @@ def ensure_device_role(
398405

399406
def ensure_device_instance(self):
400407
"""Ensure that the device instance exists in NetBox and is assigned the provided device role or DEFAULT_ROLE."""
408+
# TODO: this can create duplicate entries in NetBox...
401409
device, _ = Device.objects.get_or_create(
402410
name=self.netdev.hostname,
403411
device_type=self.device_type,
@@ -409,7 +417,7 @@ def ensure_device_instance(self):
409417
device.serial = self.netdev.serial_number
410418
device.save()
411419

412-
self.netdev.ot.device = device
420+
self.netdev.ot.created_device = device
413421
self.netdev.ot.save()
414422

415423
self.device = device

netbox_onboarding/tables.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,22 @@ class OnboardingTaskTable(BaseTable):
2020
"""Table for displaying OnboardingTask instances."""
2121

2222
site = tables.LinkColumn()
23+
platform = tables.LinkColumn()
24+
created_device = tables.LinkColumn()
2325

2426
class Meta(BaseTable.Meta): # noqa: D106 "Missing docstring in public nested class"
2527
model = OnboardingTask
26-
fields = ("pk", "created_on", "ip_address", "site", "platform", "device", "status", "failed_reason", "message")
28+
fields = (
29+
"pk",
30+
"created_on",
31+
"ip_address",
32+
"site",
33+
"platform",
34+
"created_device",
35+
"status",
36+
"failed_reason",
37+
"message",
38+
)
2739

2840

2941
class OnboardingTaskFeedBulkTable(BaseTable):

netbox_onboarding/tests/test_onboard.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ def test_ensure_device_instance_not_exist(self):
117117

118118
nbk.ensure_device_instance()
119119
self.assertIsInstance(nbk.device, Device)
120-
self.assertEqual(nbk.device, nbk.netdev.ot.device)
120+
self.assertEqual(nbk.device, nbk.netdev.ot.created_device)
121121
self.assertEqual(nbk.device.serial, "123456")
122122

123123
def test_ensure_interface_not_exist(self):

netbox_onboarding/worker.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
limitations under the License.
1313
"""
1414
import logging
15+
import time
1516

1617
from django_rq import job
1718

@@ -29,7 +30,12 @@ def onboard_device(task_id, credentials):
2930
username = credentials.username
3031
password = credentials.password
3132

32-
ot = OnboardingTask.objects.get(id=task_id)
33+
try:
34+
ot = OnboardingTask.objects.get(id=task_id)
35+
except OnboardingTask.DoesNotExist:
36+
# TODO: maybe we started before the DB was done writing it, or maybe it was deleted out from under us?
37+
time.sleep(1)
38+
ot = OnboardingTask.objects.get(id=task_id)
3339

3440
logging.info("START: onboard device")
3541

@@ -54,6 +60,7 @@ def onboard_device(task_id, credentials):
5460
except Exception as exc:
5561
ot.status = OnboardingStatusChoices.STATUS_FAILED
5662
ot.failed_reason = OnboardingFailChoices.FAIL_GENERAL
63+
ot.message = str(exc)
5764
ot.save()
5865
raise
5966

poetry.lock

Lines changed: 1 addition & 13 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ packages = [
1010
[tool.poetry.dependencies]
1111
python = "^3.6 || ^3.7 || ^3.8"
1212
invoke = "^1.4.1"
13-
first = "^2.0.2"
1413
napalm = "^2.5.0"
1514

1615
[tool.poetry.dev-dependencies]

0 commit comments

Comments
 (0)