Skip to content

Commit ae84fb9

Browse files
carbonarokmzbroch
authored andcommitted
Changes for multiple criteria searching
1 parent 0606ca7 commit ae84fb9

File tree

4 files changed

+123
-5
lines changed

4 files changed

+123
-5
lines changed

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,10 @@ The plugin behavior can be controlled with the following list of settings
8080
<Napalm Driver Name>: <Loadable Python Module>
8181
}
8282
```
83+
- `object_match_strategy` (string), defines the method for searching models. There are
84+
currently two strategies, strict and loose. Strict has to be a direct match, normally
85+
using a slug. Loose allows a range of search criteria to match a single object. If multiple
86+
objects are returned an error is raised.
8387

8488
## Usage
8589

netbox_onboarding/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ class OnboardingConfig(PluginConfig):
4343
"skip_manufacturer_on_update": False,
4444
"platform_map": {},
4545
"onboarding_extensions_map": {"ios": "netbox_onboarding.onboarding_extensions.ios",},
46+
"object_match_strategy": "loose",
4647
}
4748
caching_config = {}
4849

netbox_onboarding/netbox_keeper.py

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,43 @@
3030
PLUGIN_SETTINGS = settings.PLUGINS_CONFIG["netbox_onboarding"]
3131

3232

33+
def object_match(obj, search_array):
34+
"""Used to search models for multiple criteria.
35+
36+
Inputs:
37+
obj: The model used for searching.
38+
search_array: Nested dictionaries used to search models. First criteria will be used
39+
for strict searching. Loose searching will loop through the search_array
40+
until it finds a match. Example below.
41+
[
42+
{"slug__iexact": 'switch1'},
43+
{"model__iexact": 'Cisco'}
44+
]
45+
"""
46+
try:
47+
result = obj.objects.get(**search_array[0])
48+
return result
49+
except obj.DoesNotExist:
50+
if PLUGIN_SETTINGS["object_match_strategy"] == "loose":
51+
for search_array_element in search_array[1:]:
52+
try:
53+
result = obj.objects.get(**search_array_element)
54+
return result
55+
except obj.DoesNotExist:
56+
pass
57+
except obj.MultipleObjectsReturned:
58+
raise OnboardException(
59+
reason="fail-general",
60+
message=f"ERROR multiple objects found in {str(obj)} searching on {str(search_array_element)})",
61+
)
62+
raise
63+
except obj.MultipleObjectsReturned:
64+
raise OnboardException(
65+
reason="fail-general",
66+
message=f"ERROR multiple objects found in {str(obj)} searching on {str(search_array_element)})",
67+
)
68+
69+
3370
class NetboxKeeper:
3471
"""Used to manage the information relating to the network device within the NetBox server."""
3572

@@ -147,7 +184,8 @@ def ensure_device_manufacturer(
147184
nb_manufacturer_slug = slugify(self.netdev_vendor)
148185

149186
try:
150-
self.nb_manufacturer = Manufacturer.objects.get(slug=nb_manufacturer_slug)
187+
search_array = [{"slug__iexact": nb_manufacturer_slug}]
188+
self.nb_manufacturer = object_match(Manufacturer, search_array)
151189
except Manufacturer.DoesNotExist:
152190
if create_manufacturer:
153191
self.nb_manufacturer = Manufacturer.objects.create(name=self.netdev_vendor, slug=nb_manufacturer_slug)
@@ -201,7 +239,13 @@ def ensure_device_type(
201239
nb_device_type_slug = slugify(nb_device_type_text)
202240

203241
try:
204-
self.nb_device_type = DeviceType.objects.get(slug=nb_device_type_slug)
242+
search_array = [
243+
{"slug__iexact": nb_device_type_slug},
244+
{"model__iexact": self.netdev_model},
245+
{"part_number__iexact": self.netdev_model},
246+
]
247+
248+
self.nb_device_type = object_match(DeviceType, search_array)
205249

206250
if self.nb_device_type.manufacturer.id != self.nb_manufacturer.id:
207251
raise OnboardException(

netbox_onboarding/tests/test_netbox_keeper.py

Lines changed: 72 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,9 @@ def setUp(self):
3131
"""Create a superuser and token for API calls."""
3232
self.site1 = Site.objects.create(name="USWEST", slug="uswest")
3333

34-
def test_ensure_device_manufacturer_missing(self):
34+
def test_ensure_device_manufacturer_strict_missing(self):
3535
"""Verify ensure_device_manufacturer function when Manufacturer object is not present."""
36+
PLUGIN_SETTINGS["object_match_strategy"] = "strict"
3637
onboarding_kwargs = {
3738
"netdev_hostname": "device1",
3839
"netdev_nb_role_slug": PLUGIN_SETTINGS["default_device_role"],
@@ -52,8 +53,54 @@ def test_ensure_device_manufacturer_missing(self):
5253
self.assertIsInstance(nbk.nb_manufacturer, Manufacturer)
5354
self.assertEqual(nbk.nb_manufacturer.slug, slugify(onboarding_kwargs["netdev_vendor"]))
5455

55-
def test_ensure_device_type_missing(self):
56+
def test_ensure_device_manufacturer_loose_missing(self):
57+
"""Verify ensure_device_manufacturer function when Manufacturer object is not present."""
58+
PLUGIN_SETTINGS["object_match_strategy"] = "loose"
59+
onboarding_kwargs = {
60+
"netdev_hostname": "device1",
61+
"netdev_nb_role_slug": PLUGIN_SETTINGS["default_device_role"],
62+
"netdev_vendor": "Cisco",
63+
"netdev_model": "CSR1000v",
64+
"netdev_nb_site_slug": self.site1.slug,
65+
}
66+
67+
nbk = NetboxKeeper(**onboarding_kwargs)
68+
69+
with self.assertRaises(OnboardException) as exc_info:
70+
nbk.ensure_device_manufacturer(create_manufacturer=False)
71+
self.assertEqual(exc_info.exception.message, "ERROR manufacturer not found: Cisco")
72+
self.assertEqual(exc_info.exception.reason, "fail-config")
73+
74+
nbk.ensure_device_manufacturer(create_manufacturer=True)
75+
self.assertIsInstance(nbk.nb_manufacturer, Manufacturer)
76+
self.assertEqual(nbk.nb_manufacturer.slug, slugify(onboarding_kwargs["netdev_vendor"]))
77+
78+
def test_ensure_device_type_strict_missing(self):
79+
"""Verify ensure_device_type function when DeviceType object is not present."""
80+
PLUGIN_SETTINGS["object_match_strategy"] = "strict"
81+
onboarding_kwargs = {
82+
"netdev_hostname": "device1",
83+
"netdev_nb_role_slug": PLUGIN_SETTINGS["default_device_role"],
84+
"netdev_vendor": "Cisco",
85+
"netdev_model": "CSR1000v",
86+
"netdev_nb_site_slug": self.site1.slug,
87+
}
88+
89+
nbk = NetboxKeeper(**onboarding_kwargs)
90+
nbk.nb_manufacturer = Manufacturer.objects.create(name="Cisco", slug="cisco")
91+
92+
with self.assertRaises(OnboardException) as exc_info:
93+
nbk.ensure_device_type(create_device_type=False)
94+
self.assertEqual(exc_info.exception.message, "ERROR device type not found: CSR1000v")
95+
self.assertEqual(exc_info.exception.reason, "fail-config")
96+
97+
nbk.ensure_device_type(create_device_type=True)
98+
self.assertIsInstance(nbk.nb_device_type, DeviceType)
99+
self.assertEqual(nbk.nb_device_type.slug, slugify(onboarding_kwargs["netdev_model"]))
100+
101+
def test_ensure_device_type_loose_missing(self):
56102
"""Verify ensure_device_type function when DeviceType object is not present."""
103+
PLUGIN_SETTINGS["object_match_strategy"] = "loose"
57104
onboarding_kwargs = {
58105
"netdev_hostname": "device1",
59106
"netdev_nb_role_slug": PLUGIN_SETTINGS["default_device_role"],
@@ -74,8 +121,30 @@ def test_ensure_device_type_missing(self):
74121
self.assertIsInstance(nbk.nb_device_type, DeviceType)
75122
self.assertEqual(nbk.nb_device_type.slug, slugify(onboarding_kwargs["netdev_model"]))
76123

77-
def test_ensure_device_type_present(self):
124+
def test_ensure_device_type_strict_present(self):
125+
"""Verify ensure_device_type function when DeviceType object is already present."""
126+
PLUGIN_SETTINGS["object_match_strategy"] = "strict"
127+
manufacturer = Manufacturer.objects.create(name="Juniper", slug="juniper")
128+
129+
device_type = DeviceType.objects.create(slug="srx3600", model="SRX3600", manufacturer=manufacturer)
130+
131+
onboarding_kwargs = {
132+
"netdev_hostname": "device2",
133+
"netdev_nb_role_slug": PLUGIN_SETTINGS["default_device_role"],
134+
"netdev_vendor": "Juniper",
135+
"netdev_nb_device_type_slug": device_type.slug,
136+
"netdev_nb_site_slug": self.site1.slug,
137+
}
138+
139+
nbk = NetboxKeeper(**onboarding_kwargs)
140+
nbk.nb_manufacturer = manufacturer
141+
142+
nbk.ensure_device_type(create_device_type=False)
143+
self.assertEqual(nbk.nb_device_type, device_type)
144+
145+
def test_ensure_device_type_loose_present(self):
78146
"""Verify ensure_device_type function when DeviceType object is already present."""
147+
PLUGIN_SETTINGS["object_match_strategy"] = "loose"
79148
manufacturer = Manufacturer.objects.create(name="Juniper", slug="juniper")
80149

81150
device_type = DeviceType.objects.create(slug="srx3600", model="SRX3600", manufacturer=manufacturer)

0 commit comments

Comments
 (0)