Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New features and bug fixes on Netbox 4 #102

Open
wants to merge 13 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/netbox_initializers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ class NetBoxInitializersConfig(PluginConfig):
version = "4.0.0"
base_url = "initializers"
min_version = "4.0-beta1"
max_version = "4.0.99"
max_version = "4.1.99"


config = NetBoxInitializersConfig
14 changes: 13 additions & 1 deletion src/netbox_initializers/initializers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
"vrfs",
"aggregates",
"virtual_machines",
"virtualization_disks",
"virtualization_interfaces",
"prefixes",
"ip_addresses",
Expand Down Expand Up @@ -112,13 +113,23 @@ def set_custom_fields_values(self, entity, custom_field_data):
"Please check the 'on_objects' for that custom field in custom_fields.yml"
)
elif key not in entity.custom_field_data:
# If the type is a object, we need to get its related type
if cf.type == "object":
model = cf.related_object_type.model_class()
value = model.objects.get(**value).id

if cf.type == "multiobject":
yaml_value = value
model = cf.related_object_type.model_class()
value = [model.objects.get(**x).id for x in yaml_value]

entity.custom_field_data[key] = value
save = True

if missing_cfs:
raise Exception(
f"⚠️ Custom field(s) '{missing_cfs}' requested for {entity} but not found in Netbox!"
"Please chceck the custom_fields.yml"
"Please check the custom_fields.yml"
)

if save:
Expand Down Expand Up @@ -213,6 +224,7 @@ def register_initializer(name: str, initializer):
from .tenants import TenantInitializer
from .users import UserInitializer
from .virtual_machines import VirtualMachineInitializer
from .virtualization_disks import VMDiskInitializer
from .virtualization_interfaces import VMInterfaceInitializer
from .vlan_groups import VLANGroupInitializer
from .vlans import VLANInitializer
Expand Down
19 changes: 2 additions & 17 deletions src/netbox_initializers/initializers/cables.py
Original file line number Diff line number Diff line change
Expand Up @@ -231,23 +231,8 @@ def load_data(self):

check_terminations_are_free(term_a, term_b)

cable = Cable.objects.create(**params)

params_a_term = {
"termination_id": term_a.id,
"termination_type": term_a_ct,
"cable": cable,
"cable_end": "A",
}
CableTermination.objects.create(**params_a_term)

params_b_term = {
"termination_id": term_b.id,
"termination_type": term_b_ct,
"cable": cable,
"cable_end": "B",
}
CableTermination.objects.create(**params_b_term)
cable = Cable(a_terminations=[term_a], b_terminations=[term_b], **params)
cable.save()

print(f"🧷 Created cable {cable} {cable_name}")
self.set_tags(cable, tags)
Expand Down
10 changes: 5 additions & 5 deletions src/netbox_initializers/initializers/custom_fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ def load_data(self):
custom_field, created = CustomField.objects.get_or_create(name=cf_name)

if created:
for object_type in cf_details.get("on_objects", []):
custom_field.object_types.add(get_class_for_class_path(object_type))

if cf_details.get("default", False):
custom_field.default = cf_details["default"]

Expand All @@ -34,9 +37,6 @@ def load_data(self):
if cf_details.get("label", False):
custom_field.label = cf_details["label"]

for object_type in cf_details.get("on_objects", []):
custom_field.object_types.add(get_class_for_class_path(object_type))

if cf_details.get("required", False):
custom_field.required = cf_details["required"]

Expand All @@ -52,8 +52,8 @@ def load_data(self):
if cf_details.get("group_name", False):
custom_field.group_name = cf_details["group_name"]

if cf_details.get("ui_visibility", False):
custom_field.ui_visibility = cf_details["ui_visibility"]
if cf_details.get("ui_visible", False):
custom_field.ui_visible = cf_details["ui_visible"]

if cf_details.get("search_weight", -1) >= 0:
custom_field.search_weight = cf_details["search_weight"]
Expand Down
16 changes: 7 additions & 9 deletions src/netbox_initializers/initializers/custom_links.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ def get_content_type(content_type):
try:
return ObjectType.objects.get(model=content_type)
except ObjectType.DoesNotExist:
pass
print(f"⚠️ The content_type '{content_type}' is unknown")
return None


Expand All @@ -20,13 +20,11 @@ def load_data(self):
if custom_links is None:
return
for link in custom_links:
content_type_name = link.pop("content_type")
content_type = get_content_type(content_type_name)
if content_type is None:
content_types = [get_content_type(x) for x in link.pop("content_type")]

if None in content_types:
print(
"⚠️ Unable to create Custom Link '{0}': The content_type '{1}' is unknown".format(
link.get("name"), content_type
)
f"⚠️ Unable to create Custom Link '{ link.get('name') }' due to unknown content_type"
)
continue

Expand All @@ -36,9 +34,9 @@ def load_data(self):
)

if created:
custom_link.object_types.add(content_type)
custom_link.object_types.set(content_types)
custom_link.save()
print("🔗 Created Custom Link '{0}'".format(custom_link.name))
print(f"🔗 Created Custom Link '{custom_link.name}'")


register_initializer("custom_links", CustomLinkInitializer)
8 changes: 6 additions & 2 deletions src/netbox_initializers/initializers/device_types.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from typing import List

from dcim.models import DeviceType, Manufacturer, Region
from dcim.models import DeviceType, Manufacturer, Platform, Region
from dcim.models.device_component_templates import (
ConsolePortTemplate,
ConsoleServerPortTemplate,
Expand All @@ -18,7 +18,11 @@

MATCH_PARAMS = ["manufacturer", "model", "slug"]
REQUIRED_ASSOCS = {"manufacturer": (Manufacturer, "name")}
OPTIONAL_ASSOCS = {"region": (Region, "name"), "tenant": (Tenant, "name")}
OPTIONAL_ASSOCS = {
"region": (Region, "name"),
"tenant": (Tenant, "name"),
"default_platform": (Platform, "name")
}
NESTED_ASSOCS = {"rear_port": (RearPortTemplate, "name"), "power_port": (PowerPortTemplate, "name")}
SUPPORTED_COMPONENTS = {
"interfaces": (InterfaceTemplate, ["name"]),
Expand Down
20 changes: 20 additions & 0 deletions src/netbox_initializers/initializers/devices.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
from dcim.models import Device, DeviceRole, DeviceType, Location, Platform, Rack, Site
from django.apps import apps
from extras.models import ConfigTemplate
from tenancy.models import Tenant
from utilities.fields import CounterCacheField
from virtualization.models import Cluster

from . import BaseInitializer, register_initializer
Expand All @@ -24,6 +26,19 @@
class DeviceInitializer(BaseInitializer):
data_file_name = "devices.yml"

def recalculate_counters(self, device: Device):
counter_fields = [
field for field in device._meta.get_fields() if type(field) is CounterCacheField
]

updated_values = {}
for field in counter_fields:
model = apps.get_model(field.to_model_name)
count = model.objects.filter(**{field.to_field_name: device}).count()
updated_values[field.name] = count

Device.objects.filter(pk=device.pk).update(**updated_values)

def load_data(self):
devices = self.load_yaml()
if devices is None:
Expand All @@ -35,8 +50,10 @@ def load_data(self):
# primary ips are handled later in `380_primary_ips.py`
params.pop("primary_ip4", None)
params.pop("primary_ip6", None)
params.pop("oob_ip", None)
params.pop("primary_ip4_vrf", None)
params.pop("primary_ip6_vrf", None)
params.pop("oob_ip_vrf", None)

for assoc, details in REQUIRED_ASSOCS.items():
model, field = details
Expand All @@ -60,5 +77,8 @@ def load_data(self):
self.set_custom_fields_values(device, custom_field_data)
self.set_tags(device, tags)

# If we set any tags or custom fields, we need to recalculate all `CounterCacheField`
self.recalculate_counters(device)


register_initializer("devices", DeviceInitializer)
3 changes: 2 additions & 1 deletion src/netbox_initializers/initializers/locations.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
from dcim.models import Location, Site
from tenancy.models import Tenant

from . import BaseInitializer, register_initializer

OPTIONAL_ASSOCS = {"site": (Site, "name"), "parent": (Location, "name")}
OPTIONAL_ASSOCS = {"site": (Site, "name"), "parent": (Location, "name"), "tenant": (Tenant, "name")}


class LocationInitializer(BaseInitializer):
Expand Down
3 changes: 2 additions & 1 deletion src/netbox_initializers/initializers/primary_ips.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
OPTIONAL_ASSOCS = {
"primary_ip4": (IPAddress, "address"),
"primary_ip6": (IPAddress, "address"),
"oob_ip": (IPAddress, "address"),
}


Expand All @@ -26,7 +27,7 @@ def get_vrf_id(vrf_name):

def link_primary_ip(assets, asset_model):
for params in assets:
primary_ip_fields = set(params) & {"primary_ip4", "primary_ip6"}
primary_ip_fields = set(params) & {"primary_ip4", "primary_ip6", "oob_ip"}
if not primary_ip_fields:
continue

Expand Down
3 changes: 3 additions & 0 deletions src/netbox_initializers/initializers/sites.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,10 @@ def load_data(self):
self.set_custom_fields_values(site, custom_field_data)
self.set_tags(site, tags)

site_asns = site.asns.all()
for asn in asnFounds:
if asn in site_asns:
continue
site.asns.add(asn)
print(" 👤 Assigned asn %s to site %s" % (asn, site.name))

Expand Down
36 changes: 36 additions & 0 deletions src/netbox_initializers/initializers/virtualization_disks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
from virtualization.models import VirtualDisk, VirtualMachine

from . import BaseInitializer, register_initializer

MATCH_PARAMS = ["name", "virtual_machine"]
REQUIRED_ASSOCS = {"virtual_machine": (VirtualMachine, "name")}


class VMDiskInitializer(BaseInitializer):
data_file_name = "virtualization_disks.yml"

def load_data(self):
disks = self.load_yaml()
if disks is None:
return
for params in disks:
custom_field_data = self.pop_custom_fields(params)
tags = params.pop("tags", None)

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

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

matching_params, defaults = self.split_params(params, MATCH_PARAMS)
disk, created = VirtualDisk.objects.get_or_create(**matching_params, defaults=defaults)

if created:
print("💽 Created Disk", disk.name, disk.virtual_machine.name)

self.set_custom_fields_values(disk, custom_field_data)
self.set_tags(disk, tags)


register_initializer("virtualization_disks", VMDiskInitializer)
8 changes: 4 additions & 4 deletions src/netbox_initializers/initializers/yaml/custom_fields.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@
## - disabled
## - loose
## - exact
## ui_visibility:
## - read-write
## - read-only
## ui_visible:
## - aways
## - if-set
## - hidden
##
## Examples:
Expand All @@ -27,7 +27,7 @@
# required: false
# weight: 0
# group_name: group1
# ui_visibility: read-only
# ui_visible: if-set
# search_weight: 100
# on_objects:
# - dcim.models.Device
Expand Down
5 changes: 4 additions & 1 deletion src/netbox_initializers/initializers/yaml/custom_links.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,11 @@
# link_url: 'https://github.com/netbox-community/netbox-docker'
# new_window: False
# content_type: device

# - name: link_to_localhost
# link_text: 'Link to localhost'
# link_url: 'http://localhost'
# new_window: True
# content_type: device
# content_type:
# - device
# - site
2 changes: 2 additions & 0 deletions src/netbox_initializers/initializers/yaml/devices.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@
# primary_ip4_vrf: vrf1
# primary_ip6: 2001:db8:a000:1::2/64
# primary_ip6_vrf: vrf1
# oob_ip: 192.168.100.2/24
# oob_ip_vrf: vrf2
# custom_field_data:
# text_field: Description
# - name: server03
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# - name: Virtual Disk 1
# description: Virtual Disk 1
# size: 256
# virtual_machine: virtual machine 1

# - name: Virtual Disk 2
# description: Virtual Disk 2
# size: 1024
# virtual_machine: virtual machine 1
Loading