Skip to content

Commit 5815558

Browse files
committed
Adding mac initializer. Fixing cables, clusters, prefixes and virtualizzation_interfaces
1 parent 5779448 commit 5815558

File tree

13 files changed

+139
-23
lines changed

13 files changed

+139
-23
lines changed

src/netbox_initializers/initializers/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
from .interfaces import InterfaceInitializer
2323
from .ip_addresses import IPAddressInitializer
2424
from .locations import LocationInitializer
25+
from .macs import MACAddressInitializer
2526
from .manufacturers import ManufacturerInitializer
2627
from .object_permissions import ObjectPermissionInitializer
2728
from .platforms import PlatformInitializer

src/netbox_initializers/initializers/base.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from typing import Tuple
33

44
from core.models import ObjectType
5+
from dcim.models import MACAddress
56
from django.core.exceptions import ObjectDoesNotExist
67
from extras.models import CustomField, Tag
78
from ruamel.yaml import YAML
@@ -90,6 +91,22 @@ def set_tags(self, entity, tags):
9091
if save:
9192
entity.save()
9293

94+
def set_mac_addresses(self, entity, mac_addresses):
95+
if not mac_addresses:
96+
return
97+
98+
if not hasattr(entity, "mac_addresses"):
99+
raise Exception(f"⚠️ MAC Address cannot be applied to {entity}'s model")
100+
101+
save = False
102+
for mac_address in MACAddress.objects.filter(mac_address__in=mac_addresses):
103+
104+
entity.mac_addresses.add(mac_address)
105+
save = True
106+
107+
if save:
108+
entity.save()
109+
93110
def split_params(self, params: dict, unique_params: list = None) -> Tuple[dict, dict]:
94111
"""Split params dict into dict with matching params and a dict with default values"""
95112

@@ -140,6 +157,7 @@ class InitializationError(Exception):
140157
"prefix_vlan_roles",
141158
"vlan_groups",
142159
"vlans",
160+
"macs",
143161
"devices",
144162
"interfaces",
145163
"route_targets",

src/netbox_initializers/initializers/cables.py

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from typing import Tuple
2-
3-
from circuits.models import Circuit, CircuitTermination, ProviderNetwork
2+
from circuits.constants import CIRCUIT_TERMINATION_TERMINATION_TYPES
3+
from circuits.models import Circuit, CircuitTermination
44
from dcim.models import (
55
Cable,
66
CableTermination,
@@ -13,12 +13,12 @@
1313
PowerPanel,
1414
PowerPort,
1515
RearPort,
16-
Site,
1716
)
1817
from django.contrib.contenttypes.models import ContentType
1918
from django.db.models import Q
2019

2120
from netbox_initializers.initializers.base import BaseInitializer, register_initializer
21+
from netbox_initializers.initializers.utils import get_scope_details
2222

2323
CONSOLE_PORT_TERMINATION = ContentType.objects.get_for_model(ConsolePort)
2424
CONSOLE_SERVER_PORT_TERMINATION = ContentType.objects.get_for_model(ConsoleServerPort)
@@ -55,16 +55,11 @@ def get_termination_object(params: dict, side: str):
5555
circuit = Circuit.objects.get(cid=circuit_params.pop("cid"))
5656
term_side = circuit_params.pop("term_side").upper()
5757

58-
site_name = circuit_params.pop("site", None)
59-
provider_network = circuit_params.pop("provider_network", None)
60-
61-
if site_name:
62-
circuit_params["site"] = Site.objects.get(name=site_name)
63-
elif provider_network:
64-
circuit_params["provider_network"] = ProviderNetwork.objects.get(name=provider_network)
58+
if scope := circuit_params.pop("scope", None):
59+
circuit_params["termination_type"], circuit_params["termination_id"] = get_scope_details(scope, CIRCUIT_TERMINATION_TERMINATION_TYPES)
6560
else:
6661
raise ValueError(
67-
f"⚠️ Missing one of required parameters: 'site' or 'provider_network' "
62+
f"⚠️ Missing required parameter: 'scope'"
6863
f"for side {term_side} of circuit {circuit}"
6964
)
7065

src/netbox_initializers/initializers/clusters.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
MATCH_PARAMS = ["name", "type"]
88
REQUIRED_ASSOCS = {"type": (ClusterType, "name")}
99
OPTIONAL_ASSOCS = {
10-
"site": (Site, "name"),
10+
"scope": (Site, "name"),
1111
"group": (ClusterGroup, "name"),
1212
"tenant": (Tenant, "name"),
1313
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
from dcim.models import MACAddress
2+
3+
from netbox_initializers.initializers.base import BaseInitializer, register_initializer
4+
5+
6+
class MACAddressInitializer(BaseInitializer):
7+
data_file_name = "macs.yml"
8+
9+
def load_data(self):
10+
macs = self.load_yaml()
11+
if macs is None:
12+
return
13+
14+
for mac in macs:
15+
tags = mac.pop("tags", None)
16+
macaddress, created = MACAddress.objects.get_or_create(**mac)
17+
18+
if created:
19+
print("🗺️ Created MAC Address", macaddress.mac_address)
20+
21+
self.set_tags(macaddress, tags)
22+
23+
24+
register_initializer("macs", MACAddressInitializer)

src/netbox_initializers/initializers/prefixes.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
1-
from dcim.models import Site
21
from ipam.models import VLAN, VRF, Prefix, Role
32
from netaddr import IPNetwork
43
from tenancy.models import Tenant, TenantGroup
4+
from dcim.constants import LOCATION_SCOPE_TYPES
5+
from django.contrib.contenttypes.models import ContentType
56

67
from netbox_initializers.initializers.base import BaseInitializer, register_initializer
8+
from netbox_initializers.initializers.utils import get_scope_details
79

8-
MATCH_PARAMS = ["prefix", "site", "vrf", "vlan"]
10+
MATCH_PARAMS = ["prefix", "scope", "vrf", "vlan"]
911
OPTIONAL_ASSOCS = {
10-
"site": (Site, "name"),
1112
"tenant": (Tenant, "name"),
1213
"tenant_group": (TenantGroup, "name"),
1314
"vlan": (VLAN, "name"),
@@ -29,6 +30,9 @@ def load_data(self):
2930

3031
params["prefix"] = IPNetwork(params["prefix"])
3132

33+
if scope := params.pop("scope"):
34+
params["scope_type"], params["scope_id"] = get_scope_details(scope, LOCATION_SCOPE_TYPES)
35+
3236
for assoc, details in OPTIONAL_ASSOCS.items():
3337
if assoc in params:
3438
model, field = details
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
from django.contrib.contenttypes.models import ContentType
2+
3+
4+
def get_scope_details(scope: dict, allowed_termination_types: list):
5+
try:
6+
scope_type = ContentType.objects.get(app_label__in=["dcim", "circuits"], model=scope["type"])
7+
if scope["type"] not in allowed_termination_types:
8+
raise ValueError(f"{scope['type']} scope type is not permitted on {scope_type.app_label}")
9+
except ContentType.DoesNotExist:
10+
raise ValueError(f"⚠️ Invalid scope type: {scope['type']}")
11+
12+
scope_id = scope_type.model_class().objects.get(name=scope["name"]).id
13+
return scope_type, scope_id

src/netbox_initializers/initializers/virtualization_interfaces.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1+
from dcim.models import MACAddress
12
from virtualization.models import VirtualMachine, VMInterface
23

34
from netbox_initializers.initializers.base import BaseInitializer, register_initializer
45

56
MATCH_PARAMS = ["name", "virtual_machine"]
67
REQUIRED_ASSOCS = {"virtual_machine": (VirtualMachine, "name")}
8+
OPTIONAL_ASSOCS = {"primary_mac_address": (MACAddress, "mac_address")}
79

810

911
class VMInterfaceInitializer(BaseInitializer):
@@ -16,13 +18,21 @@ def load_data(self):
1618
for params in interfaces:
1719
custom_field_data = self.pop_custom_fields(params)
1820
tags = params.pop("tags", None)
21+
mac_addresses = params.pop("mac_addresses", None)
1922

2023
for assoc, details in REQUIRED_ASSOCS.items():
2124
model, field = details
2225
query = {field: params.pop(assoc)}
2326

2427
params[assoc] = model.objects.get(**query)
2528

29+
for assoc, details in OPTIONAL_ASSOCS.items():
30+
if assoc in params:
31+
model, field = details
32+
query = {field: params.pop(assoc)}
33+
34+
params[assoc] = model.objects.get(**query)
35+
2636
matching_params, defaults = self.split_params(params, MATCH_PARAMS)
2737
interface, created = VMInterface.objects.get_or_create(
2838
**matching_params, defaults=defaults
@@ -33,6 +43,7 @@ def load_data(self):
3343

3444
self.set_custom_fields_values(interface, custom_field_data)
3545
self.set_tags(interface, tags)
46+
self.set_mac_addresses(interface, mac_addresses)
3647

3748

3849
register_initializer("virtualization_interfaces", VMInterfaceInitializer)

src/netbox_initializers/initializers/yaml/cables.yml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@
1616
# # termination_x_circuit:
1717
# # term_side -> termination side of a circuit. Must be A or B
1818
# # cid -> circuit ID value
19-
# # site OR provider_network -> name of Site or ProviderNetwork respectively. If both provided, Site takes precedence
19+
# # scope:
20+
# # type -> select one of the following: region, site, sitegroup, location
21+
# # name -> name of the object in the respective scope type
2022
# # ```
2123
# #
2224
# # If a termination is a power feed then the required parameter is termination_x_feed.
@@ -51,7 +53,9 @@
5153
# termination_b_circuit:
5254
# term_side: A
5355
# cid: Circuit_ID-1
54-
# site: AMS 1
56+
# scope:
57+
# type: site
58+
# name: AMS 1
5559
# type: cat6
5660

5761
# - termination_a_name: psu0

src/netbox_initializers/initializers/yaml/clusters.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,4 @@
44
# tenant: tenant1
55
# - name: cluster2
66
# type: Hyper-V
7-
# site: SING 1
7+
# scope: SING 1

0 commit comments

Comments
 (0)