From 036f94a450d8a76eed02980f8a5e43dbd29e481f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20M=C3=A4der?= Date: Sat, 17 Oct 2020 19:24:35 +0200 Subject: [PATCH 1/6] Simplify test script for manual tests --- test.sh | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/test.sh b/test.sh index 26de625..dc892b1 100755 --- a/test.sh +++ b/test.sh @@ -1,12 +1,29 @@ #!/bin/bash +# Runs the original Netbox unit tests and tests whether all initializers work. +# Usage: +# ./test.sh latest +# ./test.sh v2.9.7 +# ./test.sh develop-2.10 +# IMAGE='netboxcommunity/netbox:latest' ./test.sh +# IMAGE='netboxcommunity/netbox:v2.9.7' ./test.sh +# IMAGE='netboxcommunity/netbox:develop-2.10' ./test.sh +# export IMAGE='netboxcommunity/netbox:latest'; ./test.sh +# export IMAGE='netboxcommunity/netbox:v2.9.7'; ./test.sh +# export IMAGE='netboxcommunity/netbox:develop-2.10'; ./test.sh # exit when a command exits with an exit code != 0 set -e -# version is used by `docker-compose.yml` do determine the tag +# IMAGE is used by `docker-compose.yml` do determine the tag # of the Docker Image that is to be used -export IMAGE="${IMAGE-netboxcommunity/netbox:latest}" +if [ "${1}x" != "x" ]; then + # Use the command line argument + export IMAGE="netboxcommunity/netbox:${1}" +else + export IMAGE="${IMAGE-netboxcommunity/netbox:latest}" +fi +# Ensure that an IMAGE is defined if [ -z "${IMAGE}" ]; then echo "โš ๏ธ No image defined" @@ -63,7 +80,7 @@ echo "๐Ÿณ๐Ÿณ๐Ÿณ Start testing '${IMAGE}'" trap test_cleanup EXIT ERR test_setup -test_netbox_unit_tests +#test_netbox_unit_tests test_initializers echo "๐Ÿณ๐Ÿณ๐Ÿณ Done testing '${IMAGE}'" From 5c9bea8b50157f3235f0bfdf57fa331d86b72ac3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20M=C3=A4der?= Date: Sat, 17 Oct 2020 21:51:38 +0200 Subject: [PATCH 2/6] Update Custom Field logic for Netbox v2.10.x --- initializers/custom_fields.yml | 30 +++++++++++---- initializers/device_types.yml | 8 ++-- initializers/devices.yml | 6 +-- initializers/racks.yml | 6 +-- initializers/sites.yml | 16 ++++---- startup_scripts/020_custom_fields.py | 37 ++++++++++--------- startup_scripts/040_sites.py | 21 +++-------- startup_scripts/060_device_types.py | 21 +++-------- startup_scripts/080_racks.py | 21 +++-------- startup_scripts/120_tenants.py | 19 +++------- startup_scripts/130_devices.py | 24 ++++-------- startup_scripts/160_aggregates.py | 24 ++++-------- startup_scripts/170_clusters.py | 21 +++-------- startup_scripts/180_vrfs.py | 23 +++--------- startup_scripts/200_vlan_groups.py | 19 +++------- startup_scripts/210_vlans.py | 19 +++------- startup_scripts/220_prefixes.py | 21 ++++------- startup_scripts/230_virtual_machines.py | 24 ++++-------- .../240_virtualization_interfaces.py | 19 +++------- startup_scripts/250_dcim_interfaces.py | 21 +++-------- startup_scripts/260_ip_addresses.py | 17 ++------- .../startup_script_utils/__init__.py | 1 + .../startup_script_utils/custom_fields.py | 15 ++++++++ .../startup_script_utils/load_yaml.py | 3 +- 24 files changed, 168 insertions(+), 268 deletions(-) create mode 100644 startup_scripts/startup_script_utils/custom_fields.py diff --git a/initializers/custom_fields.yml b/initializers/custom_fields.yml index 4085ab0..fae780d 100644 --- a/initializers/custom_fields.yml +++ b/initializers/custom_fields.yml @@ -37,7 +37,7 @@ # weight: 10 # on_objects: # - tenancy.models.Tenant -# select_field: +# legacy_select_field: # type: select # label: Choose between items # required: false @@ -56,6 +56,21 @@ # weight: 50 # - value: Fourth Item # weight: 40 +# select_field: +# type: select +# label: Choose between items +# required: false +# filter_logic: exact +# weight: 30 +# default: First Item +# on_objects: +# - dcim.models.Device +# choices: +# - First Item +# - Second Item +# - Third Item +# - Fifth Item +# - Fourth Item # select_field_auto_weight: # type: select # label: Choose between items @@ -65,18 +80,17 @@ # on_objects: # - dcim.models.Device # choices: -# - value: A -# - value: B -# - value: C -# - value: "D like deprecated" -# weight: 999 -# - value: E +# - A +# - B +# - C +# - E +# - D like deprecated # boolean_field: # type: boolean # label: Yes Or No? # required: true # filter_logic: loose -# default: "false" # important: but "false" in quotes! +# default: "false" # important: put "false" in quotes! # weight: 90 # on_objects: # - dcim.models.Device diff --git a/initializers/device_types.yml b/initializers/device_types.yml index 9b27da3..88798b5 100644 --- a/initializers/device_types.yml +++ b/initializers/device_types.yml @@ -2,22 +2,22 @@ # manufacturer: Manufacturer 1 # slug: model-1 # u_height: 2 -# custom_fields: +# custom_field_data: # text_field: Description # - model: Model 2 # manufacturer: Manufacturer 1 # slug: model-2 -# custom_fields: +# custom_field_data: # text_field: Description # - model: Model 3 # manufacturer: Manufacturer 1 # slug: model-3 # is_full_depth: false # u_height: 0 -# custom_fields: +# custom_field_data: # text_field: Description # - model: Other # manufacturer: No Name # slug: other -# custom_fields: +# custom_field_data: # text_field: Description diff --git a/initializers/devices.yml b/initializers/devices.yml index e968503..0de0504 100644 --- a/initializers/devices.yml +++ b/initializers/devices.yml @@ -20,7 +20,7 @@ # rack: rack-01 # face: front # position: 1 -# custom_fields: +# custom_field_data: # text_field: Description # - name: server02 # device_role: server @@ -31,7 +31,7 @@ # position: 2 # primary_ip4: 10.1.1.2/24 # primary_ip6: 2001:db8:a000:1::2/64 -# custom_fields: +# custom_field_data: # text_field: Description # - name: server03 # device_role: server @@ -40,5 +40,5 @@ # rack: rack-03 # face: front # position: 3 -# custom_fields: +# custom_field_data: # text_field: Description diff --git a/initializers/racks.yml b/initializers/racks.yml index 51502de..379553d 100644 --- a/initializers/racks.yml +++ b/initializers/racks.yml @@ -20,7 +20,7 @@ # type: 4-post-cabinet # width: 19 # u_height: 47 -# custom_fields: +# custom_field_data: # text_field: Description # - site: AMS 2 # name: rack-02 @@ -28,7 +28,7 @@ # type: 4-post-cabinet # width: 19 # u_height: 47 -# custom_fields: +# custom_field_data: # text_field: Description # - site: SING 1 # name: rack-03 @@ -37,5 +37,5 @@ # type: 4-post-cabinet # width: 19 # u_height: 47 -# custom_fields: +# custom_field_data: # text_field: Description diff --git a/initializers/sites.yml b/initializers/sites.yml index f3e05ba..0015f4e 100644 --- a/initializers/sites.yml +++ b/initializers/sites.yml @@ -4,29 +4,29 @@ # status: active # facility: Amsterdam 1 # asn: 12345 -# custom_fields: -# text_field: Description +# custom_field_data: +# text_field: Description for AMS1 # - name: AMS 2 # slug: ams2 # region: Downtown # status: active # facility: Amsterdam 2 # asn: 54321 -# custom_fields: -# text_field: Description +# custom_field_data: +# text_field: Description for AMS2 # - name: AMS 3 # slug: ams3 # region: Suburbs # status: active # facility: Amsterdam 3 # asn: 67890 -# custom_fields: -# text_field: Description +# custom_field_data: +# text_field: Description for AMS3 # - name: SING 1 # slug: sing1 # region: Singapore # status: active # facility: Singapore 1 # asn: 09876 -# custom_fields: -# text_field: Description +# custom_field_data: +# text_field: Description for SING1 diff --git a/startup_scripts/020_custom_fields.py b/startup_scripts/020_custom_fields.py index 2cb48a0..3b2f1c1 100644 --- a/startup_scripts/020_custom_fields.py +++ b/startup_scripts/020_custom_fields.py @@ -1,8 +1,8 @@ -from extras.models import CustomField, CustomFieldChoice - -from startup_script_utils import load_yaml import sys +from extras.models import CustomField +from startup_script_utils import load_yaml + def get_class_for_class_path(class_path): import importlib from django.contrib.contenttypes.models import ContentType @@ -21,34 +21,37 @@ for cf_name, cf_details in customfields.items(): custom_field, created = CustomField.objects.get_or_create(name = cf_name) if created: - if cf_details.get('default', 0): + if cf_details.get('default', False): custom_field.default = cf_details['default'] - if cf_details.get('description', 0): + if cf_details.get('description', False): custom_field.description = cf_details['description'] - if cf_details.get('label', 0): + if cf_details.get('label', False): custom_field.label = cf_details['label'] for object_type in cf_details.get('on_objects', []): - custom_field.obj_type.add(get_class_for_class_path(object_type)) + custom_field.content_types.add(get_class_for_class_path(object_type)) - if cf_details.get('required', 0): + if cf_details.get('required', False): custom_field.required = cf_details['required'] - if cf_details.get('type', 0): + if cf_details.get('type', False): custom_field.type = cf_details['type'] - if cf_details.get('weight', 0): + if cf_details.get('weight', -1) >= 0: custom_field.weight = cf_details['weight'] + if cf_details.get('choices', False): + custom_field.choices = [] + + for _, choice_detail in enumerate(cf_details.get('choices', [])): + if isinstance(choice_detail, str): + custom_field.choices.append(choice_detail) + else: # legacy mode + print(f"โš ๏ธ Please migrate the 'choices' of '{cf_name}' to the new format, as 'weight' is no longer supported!") + custom_field.choices.append(choice_detail['value']) + custom_field.save() - for idx, choice_details in enumerate(cf_details.get('choices', [])): - choice, _ = CustomFieldChoice.objects.get_or_create( - field=custom_field, - value=choice_details['value'], - defaults={'weight': idx * 10} - ) - print("๐Ÿ”ง Created custom field", cf_name) diff --git a/startup_scripts/040_sites.py b/startup_scripts/040_sites.py index 828a86b..a4f7e71 100644 --- a/startup_scripts/040_sites.py +++ b/startup_scripts/040_sites.py @@ -1,9 +1,9 @@ -from dcim.models import Region, Site -from extras.models import CustomField, CustomFieldValue -from tenancy.models import Tenant -from startup_script_utils import load_yaml import sys +from dcim.models import Region, Site +from startup_script_utils import * +from tenancy.models import Tenant + sites = load_yaml('/opt/netbox/initializers/sites.yml') if sites is None: @@ -15,7 +15,7 @@ optional_assocs = { } for params in sites: - custom_fields = params.pop('custom_fields', None) + custom_field_data = pop_custom_fields(params) for assoc, details in optional_assocs.items(): if assoc in params: @@ -27,15 +27,6 @@ for params in sites: site, created = Site.objects.get_or_create(**params) if created: - if custom_fields is not None: - for cf_name, cf_value in custom_fields.items(): - custom_field = CustomField.objects.get(name=cf_name) - custom_field_value = CustomFieldValue.objects.create( - field=custom_field, - obj=site, - value=cf_value - ) - - site.custom_field_values.add(custom_field_value) + set_custom_fields_values(site, custom_field_data) print("๐Ÿ“ Created site", site.name) diff --git a/startup_scripts/060_device_types.py b/startup_scripts/060_device_types.py index e6cea93..e0695b1 100644 --- a/startup_scripts/060_device_types.py +++ b/startup_scripts/060_device_types.py @@ -1,9 +1,9 @@ -from dcim.models import DeviceType, Manufacturer, Region -from tenancy.models import Tenant -from extras.models import CustomField, CustomFieldValue -from startup_script_utils import load_yaml import sys +from dcim.models import DeviceType, Manufacturer, Region +from startup_script_utils import * +from tenancy.models import Tenant + device_types = load_yaml('/opt/netbox/initializers/device_types.yml') if device_types is None: @@ -19,7 +19,7 @@ optional_assocs = { } for params in device_types: - custom_fields = params.pop('custom_fields', None) + custom_field_data = pop_custom_fields(params) for assoc, details in required_assocs.items(): model, field = details @@ -37,15 +37,6 @@ for params in device_types: device_type, created = DeviceType.objects.get_or_create(**params) if created: - if custom_fields is not None: - for cf_name, cf_value in custom_fields.items(): - custom_field = CustomField.objects.get(name=cf_name) - custom_field_value = CustomFieldValue.objects.create( - field=custom_field, - obj=device_type, - value=cf_value - ) - - device_type.custom_field_values.add(custom_field_value) + set_custom_fields_values(device_type, custom_field_data) print("๐Ÿ”ก Created device type", device_type.manufacturer, device_type.model) diff --git a/startup_scripts/080_racks.py b/startup_scripts/080_racks.py index 279cb2c..4504624 100644 --- a/startup_scripts/080_racks.py +++ b/startup_scripts/080_racks.py @@ -1,9 +1,9 @@ -from dcim.models import Site, RackRole, Rack, RackGroup -from tenancy.models import Tenant -from extras.models import CustomField, CustomFieldValue -from startup_script_utils import load_yaml import sys +from dcim.models import Site, RackRole, Rack, RackGroup +from startup_script_utils import * +from tenancy.models import Tenant + racks = load_yaml('/opt/netbox/initializers/racks.yml') if racks is None: @@ -20,7 +20,7 @@ optional_assocs = { } for params in racks: - custom_fields = params.pop('custom_fields', None) + custom_field_data = pop_custom_fields(params) for assoc, details in required_assocs.items(): model, field = details @@ -38,15 +38,6 @@ for params in racks: rack, created = Rack.objects.get_or_create(**params) if created: - if custom_fields is not None: - for cf_name, cf_value in custom_fields.items(): - custom_field = CustomField.objects.get(name=cf_name) - custom_field_value = CustomFieldValue.objects.create( - field=custom_field, - obj=rack, - value=cf_value - ) - - rack.custom_field_values.add(custom_field_value) + set_custom_fields_values(rack, custom_field_data) print("๐Ÿ”ณ Created rack", rack.site, rack.name) diff --git a/startup_scripts/120_tenants.py b/startup_scripts/120_tenants.py index 121c83a..3b3b221 100644 --- a/startup_scripts/120_tenants.py +++ b/startup_scripts/120_tenants.py @@ -1,8 +1,8 @@ -from tenancy.models import Tenant, TenantGroup -from extras.models import CustomField, CustomFieldValue -from startup_script_utils import load_yaml import sys +from startup_script_utils import * +from tenancy.models import Tenant, TenantGroup + tenants = load_yaml('/opt/netbox/initializers/tenants.yml') if tenants is None: @@ -13,7 +13,7 @@ optional_assocs = { } for params in tenants: - custom_fields = params.pop('custom_fields', None) + custom_field_data = pop_custom_fields(params) for assoc, details in optional_assocs.items(): if assoc in params: @@ -25,15 +25,6 @@ for params in tenants: tenant, created = Tenant.objects.get_or_create(**params) if created: - if custom_fields is not None: - for cf_name, cf_value in custom_fields.items(): - custom_field = CustomField.objects.get(name=cf_name) - custom_field_value = CustomFieldValue.objects.create( - field=custom_field, - obj=tenant, - value=cf_value - ) - - tenant.custom_field_values.add(custom_field_value) + set_custom_fields_values(tenant, custom_field_data) print("๐Ÿ‘ฉโ€๐Ÿ’ป Created Tenant", tenant.name) diff --git a/startup_scripts/130_devices.py b/startup_scripts/130_devices.py index 7233dd0..d73fd07 100644 --- a/startup_scripts/130_devices.py +++ b/startup_scripts/130_devices.py @@ -1,10 +1,10 @@ -from dcim.models import Site, Rack, DeviceRole, DeviceType, Device, Platform -from virtualization.models import Cluster -from tenancy.models import Tenant -from extras.models import CustomField, CustomFieldValue -from startup_script_utils import load_yaml import sys +from dcim.models import Site, Rack, DeviceRole, DeviceType, Device, Platform +from startup_script_utils import * +from tenancy.models import Tenant +from virtualization.models import Cluster + devices = load_yaml('/opt/netbox/initializers/devices.yml') if devices is None: @@ -24,7 +24,8 @@ optional_assocs = { } for params in devices: - custom_fields = params.pop('custom_fields', None) + custom_field_data = pop_custom_fields(params) + # primary ips are handled later in `270_primary_ips.py` params.pop('primary_ip4', None) params.pop('primary_ip6', None) @@ -45,15 +46,6 @@ for params in devices: device, created = Device.objects.get_or_create(**params) if created: - if custom_fields is not None: - for cf_name, cf_value in custom_fields.items(): - custom_field = CustomField.objects.get(name=cf_name) - custom_field_value = CustomFieldValue.objects.create( - field=custom_field, - obj=device, - value=cf_value - ) - - device.custom_field_values.add(custom_field_value) + set_custom_fields_values(device, custom_field_data) print("๐Ÿ–ฅ๏ธ Created device", device.name) diff --git a/startup_scripts/160_aggregates.py b/startup_scripts/160_aggregates.py index 0ffe9b0..cc1d220 100644 --- a/startup_scripts/160_aggregates.py +++ b/startup_scripts/160_aggregates.py @@ -1,11 +1,9 @@ -from ipam.models import Aggregate, RIR - -from extras.models import CustomField, CustomFieldValue - -from netaddr import IPNetwork -from startup_script_utils import load_yaml import sys +from ipam.models import Aggregate, RIR +from netaddr import IPNetwork +from startup_script_utils import * + aggregates = load_yaml('/opt/netbox/initializers/aggregates.yml') if aggregates is None: @@ -16,7 +14,8 @@ required_assocs = { } for params in aggregates: - custom_fields = params.pop('custom_fields', None) + custom_field_data = pop_custom_fields(params) + params['prefix'] = IPNetwork(params['prefix']) for assoc, details in required_assocs.items(): @@ -28,15 +27,6 @@ for params in aggregates: aggregate, created = Aggregate.objects.get_or_create(**params) if created: - if custom_fields is not None: - for cf_name, cf_value in custom_fields.items(): - custom_field = CustomField.objects.get(name=cf_name) - custom_field_value = CustomFieldValue.objects.create( - field=custom_field, - obj=aggregate, - value=cf_value - ) - - aggregate.custom_field_values.add(custom_field_value) + set_custom_fields_values(aggregate, custom_field_data) print("๐Ÿ—ž๏ธ Created Aggregate", aggregate.prefix) diff --git a/startup_scripts/170_clusters.py b/startup_scripts/170_clusters.py index a7e2065..ffd965e 100644 --- a/startup_scripts/170_clusters.py +++ b/startup_scripts/170_clusters.py @@ -1,9 +1,9 @@ -from dcim.models import Site -from virtualization.models import Cluster, ClusterType, ClusterGroup -from extras.models import CustomField, CustomFieldValue -from startup_script_utils import load_yaml import sys +from dcim.models import Site +from startup_script_utils import * +from virtualization.models import Cluster, ClusterType, ClusterGroup + clusters = load_yaml('/opt/netbox/initializers/clusters.yml') if clusters is None: @@ -19,7 +19,7 @@ optional_assocs = { } for params in clusters: - custom_fields = params.pop('custom_fields', None) + custom_field_data = pop_custom_fields(params) for assoc, details in required_assocs.items(): model, field = details @@ -37,15 +37,6 @@ for params in clusters: cluster, created = Cluster.objects.get_or_create(**params) if created: - if custom_fields is not None: - for cf_name, cf_value in custom_fields.items(): - custom_field = CustomField.objects.get(name=cf_name) - custom_field_value = CustomFieldValue.objects.create( - field=custom_field, - obj=cluster, - value=cf_value - ) - - cluster.custom_field_values.add(custom_field_value) + set_custom_fields_values(cluster, custom_field_data) print("๐Ÿ—„๏ธ Created cluster", cluster.name) diff --git a/startup_scripts/180_vrfs.py b/startup_scripts/180_vrfs.py index 496710d..2f22316 100644 --- a/startup_scripts/180_vrfs.py +++ b/startup_scripts/180_vrfs.py @@ -1,11 +1,9 @@ -from ipam.models import VRF -from tenancy.models import Tenant - -from extras.models import CustomField, CustomFieldValue - -from startup_script_utils import load_yaml import sys +from ipam.models import VRF +from startup_script_utils import * +from tenancy.models import Tenant + vrfs = load_yaml('/opt/netbox/initializers/vrfs.yml') if vrfs is None: @@ -16,7 +14,7 @@ optional_assocs = { } for params in vrfs: - custom_fields = params.pop('custom_fields', None) + custom_field_data = pop_custom_fields(params) for assoc, details in optional_assocs.items(): if assoc in params: @@ -28,15 +26,6 @@ for params in vrfs: vrf, created = VRF.objects.get_or_create(**params) if created: - if custom_fields is not None: - for cf_name, cf_value in custom_fields.items(): - custom_field = CustomField.objects.get(name=cf_name) - custom_field_value = CustomFieldValue.objects.create( - field=custom_field, - obj=vrf, - value=cf_value - ) - - vrf.custom_field_values.add(custom_field_value) + set_custom_fields_values(vrf, custom_field_data) print("๐Ÿ“ฆ Created VRF", vrf.name) diff --git a/startup_scripts/200_vlan_groups.py b/startup_scripts/200_vlan_groups.py index f8dc55d..35c3616 100644 --- a/startup_scripts/200_vlan_groups.py +++ b/startup_scripts/200_vlan_groups.py @@ -1,8 +1,8 @@ +import sys + from dcim.models import Site from ipam.models import VLANGroup -from extras.models import CustomField, CustomFieldValue -from startup_script_utils import load_yaml -import sys +from startup_script_utils import * vlan_groups = load_yaml('/opt/netbox/initializers/vlan_groups.yml') @@ -14,7 +14,7 @@ optional_assocs = { } for params in vlan_groups: - custom_fields = params.pop('custom_fields', None) + custom_field_data = pop_custom_fields(params) for assoc, details in optional_assocs.items(): if assoc in params: @@ -26,15 +26,6 @@ for params in vlan_groups: vlan_group, created = VLANGroup.objects.get_or_create(**params) if created: - if custom_fields is not None: - for cf_name, cf_value in custom_fields.items(): - custom_field = CustomField.objects.get(name=cf_name) - custom_field_value = CustomFieldValue.objects.create( - field=custom_field, - obj=vlan_group, - value=cf_value - ) - - vlan_group.custom_field_values.add(custom_field_value) + set_custom_fields_values(vlan_group, custom_field_data) print("๐Ÿ˜๏ธ Created VLAN Group", vlan_group.name) diff --git a/startup_scripts/210_vlans.py b/startup_scripts/210_vlans.py index ceab196..7848fc1 100644 --- a/startup_scripts/210_vlans.py +++ b/startup_scripts/210_vlans.py @@ -1,9 +1,9 @@ +import sys + from dcim.models import Site from ipam.models import VLAN, VLANGroup, Role +from startup_script_utils import * from tenancy.models import Tenant, TenantGroup -from extras.models import CustomField, CustomFieldValue -from startup_script_utils import load_yaml -import sys vlans = load_yaml('/opt/netbox/initializers/vlans.yml') @@ -19,7 +19,7 @@ optional_assocs = { } for params in vlans: - custom_fields = params.pop('custom_fields', None) + custom_field_data = pop_custom_fields(params) for assoc, details in optional_assocs.items(): if assoc in params: @@ -31,15 +31,6 @@ for params in vlans: vlan, created = VLAN.objects.get_or_create(**params) if created: - if custom_fields is not None: - for cf_name, cf_value in custom_fields.items(): - custom_field = CustomField.objects.get(name=cf_name) - custom_field_value = CustomFieldValue.objects.create( - field=custom_field, - obj=vlan, - value=cf_value - ) - - vlan.custom_field_values.add(custom_field_value) + set_custom_fields_values(vlan, custom_field_data) print("๐Ÿ  Created VLAN", vlan.name) diff --git a/startup_scripts/220_prefixes.py b/startup_scripts/220_prefixes.py index b047c8c..9175521 100644 --- a/startup_scripts/220_prefixes.py +++ b/startup_scripts/220_prefixes.py @@ -1,10 +1,10 @@ +import sys + from dcim.models import Site from ipam.models import Prefix, VLAN, Role, VRF -from tenancy.models import Tenant, TenantGroup -from extras.models import CustomField, CustomFieldValue from netaddr import IPNetwork -from startup_script_utils import load_yaml -import sys +from startup_script_utils import * +from tenancy.models import Tenant, TenantGroup prefixes = load_yaml('/opt/netbox/initializers/prefixes.yml') @@ -21,7 +21,8 @@ optional_assocs = { } for params in prefixes: - custom_fields = params.pop('custom_fields', None) + custom_field_data = pop_custom_fields(params) + params['prefix'] = IPNetwork(params['prefix']) for assoc, details in optional_assocs.items(): @@ -33,14 +34,6 @@ for params in prefixes: prefix, created = Prefix.objects.get_or_create(**params) if created: - if custom_fields is not None: - for cf_name, cf_value in custom_fields.items(): - custom_field = CustomField.objects.get(name=cf_name) - custom_field_value = CustomFieldValue.objects.create( - field=custom_field, - obj=prefix, - value=cf_value - ) - prefix.custom_field_values.add(custom_field_value) + set_custom_fields_values(prefix, custom_field_data) print("๐Ÿ“Œ Created Prefix", prefix.prefix) diff --git a/startup_scripts/230_virtual_machines.py b/startup_scripts/230_virtual_machines.py index f138886..328d3ea 100644 --- a/startup_scripts/230_virtual_machines.py +++ b/startup_scripts/230_virtual_machines.py @@ -1,10 +1,10 @@ -from dcim.models import Site, Platform, DeviceRole -from virtualization.models import Cluster, VirtualMachine -from tenancy.models import Tenant -from extras.models import CustomField, CustomFieldValue -from startup_script_utils import load_yaml import sys +from dcim.models import Platform, DeviceRole +from startup_script_utils import * +from tenancy.models import Tenant +from virtualization.models import Cluster, VirtualMachine + virtual_machines = load_yaml('/opt/netbox/initializers/virtual_machines.yml') if virtual_machines is None: @@ -21,7 +21,8 @@ optional_assocs = { } for params in virtual_machines: - custom_fields = params.pop('custom_fields', None) + custom_field_data = pop_custom_fields(params) + # primary ips are handled later in `270_primary_ips.py` params.pop('primary_ip4', None) params.pop('primary_ip6', None) @@ -42,15 +43,6 @@ for params in virtual_machines: virtual_machine, created = VirtualMachine.objects.get_or_create(**params) if created: - if custom_fields is not None: - for cf_name, cf_value in custom_fields.items(): - custom_field = CustomField.objects.get(name=cf_name) - custom_field_value = CustomFieldValue.objects.create( - field=custom_field, - obj=virtual_machine, - value=cf_value - ) - - virtual_machine.custom_field_values.add(custom_field_value) + set_custom_fields_values(virtual_machine, custom_field_data) print("๐Ÿ–ฅ๏ธ Created virtual machine", virtual_machine.name) diff --git a/startup_scripts/240_virtualization_interfaces.py b/startup_scripts/240_virtualization_interfaces.py index f04f30b..c6f8e50 100644 --- a/startup_scripts/240_virtualization_interfaces.py +++ b/startup_scripts/240_virtualization_interfaces.py @@ -1,8 +1,8 @@ -from virtualization.models import VirtualMachine, VMInterface -from extras.models import CustomField, CustomFieldValue -from startup_script_utils import load_yaml import sys +from startup_script_utils import * +from virtualization.models import VirtualMachine, VMInterface + interfaces = load_yaml('/opt/netbox/initializers/virtualization_interfaces.yml') if interfaces is None: @@ -13,7 +13,7 @@ required_assocs = { } for params in interfaces: - custom_fields = params.pop('custom_fields', None) + custom_field_data = pop_custom_fields(params) for assoc, details in required_assocs.items(): model, field = details @@ -24,15 +24,6 @@ for params in interfaces: interface, created = VMInterface.objects.get_or_create(**params) if created: - if custom_fields is not None: - for cf_name, cf_value in custom_fields.items(): - custom_field = CustomField.objects.get(name=cf_name) - custom_field_value = CustomFieldValue.objects.create( - field=custom_field, - obj=interface, - value=cf_value - ) - - interface.custom_field_values.add(custom_field_value) + set_custom_fields_values(interface, custom_field_data) print("๐Ÿงท Created interface", interface.name, interface.virtual_machine.name) diff --git a/startup_scripts/250_dcim_interfaces.py b/startup_scripts/250_dcim_interfaces.py index 51f885b..3b7067d 100644 --- a/startup_scripts/250_dcim_interfaces.py +++ b/startup_scripts/250_dcim_interfaces.py @@ -1,9 +1,9 @@ -from dcim.models import Interface, Device -from extras.models import CustomField, CustomFieldValue -from startup_script_utils import load_yaml import sys -interfaces= load_yaml('/opt/netbox/initializers/dcim_interfaces.yml') +from dcim.models import Interface, Device +from startup_script_utils import * + +interfaces = load_yaml('/opt/netbox/initializers/dcim_interfaces.yml') if interfaces is None: sys.exit() @@ -13,7 +13,7 @@ required_assocs = { } for params in interfaces: - custom_fields = params.pop('custom_fields', None) + custom_field_data = pop_custom_fields(params) for assoc, details in required_assocs.items(): model, field = details @@ -24,15 +24,6 @@ for params in interfaces: interface, created = Interface.objects.get_or_create(**params) if created: - if custom_fields is not None: - for cf_name, cf_value in custom_fields.items(): - custom_field = CustomField.objects.get(name=cf_name) - custom_field_value = CustomFieldValue.objects.create( - field=custom_field, - obj=interface, - value=cf_value - ) - - interface.custom_field_values.add(custom_field_value) + set_custom_fields_values(interface, custom_field_data) print("๐Ÿงท Created interface", interface.name, interface.device.name) diff --git a/startup_scripts/260_ip_addresses.py b/startup_scripts/260_ip_addresses.py index 7d164fd..a0582a2 100644 --- a/startup_scripts/260_ip_addresses.py +++ b/startup_scripts/260_ip_addresses.py @@ -3,10 +3,9 @@ import sys from dcim.models import Device, Interface from django.contrib.contenttypes.models import ContentType from django.db.models import Q -from extras.models import CustomField, CustomFieldValue from ipam.models import VRF, IPAddress from netaddr import IPNetwork -from startup_script_utils import load_yaml +from startup_script_utils import * from tenancy.models import Tenant from virtualization.models import VirtualMachine, VMInterface @@ -25,9 +24,10 @@ vm_interface_ct = ContentType.objects.filter(Q(app_label='virtualization', model interface_ct = ContentType.objects.filter(Q(app_label='dcim', model='interface')).first() for params in ip_addresses: + custom_field_data = pop_custom_fields(params) + vm = params.pop('virtual_machine', None) device = params.pop('device', None) - custom_fields = params.pop('custom_fields', None) params['address'] = IPNetwork(params['address']) if vm and device: @@ -55,15 +55,6 @@ for params in ip_addresses: ip_address, created = IPAddress.objects.get_or_create(**params) if created: - if custom_fields is not None: - for cf_name, cf_value in custom_fields.items(): - custom_field = CustomField.objects.get(name=cf_name) - custom_field_value = CustomFieldValue.objects.create( - field=custom_field, - obj=ip_address, - value=cf_value - ) - - ip_address.custom_field_values.add(custom_field_value) + set_custom_fields_values(ip_address, custom_field_data) print("๐Ÿงฌ Created IP Address", ip_address.address) diff --git a/startup_scripts/startup_script_utils/__init__.py b/startup_scripts/startup_script_utils/__init__.py index c3cf28f..1e9f042 100644 --- a/startup_scripts/startup_script_utils/__init__.py +++ b/startup_scripts/startup_script_utils/__init__.py @@ -1,2 +1,3 @@ from .load_yaml import load_yaml from .permissions import set_permissions +from .custom_fields import set_custom_fields_values, pop_custom_fields diff --git a/startup_scripts/startup_script_utils/custom_fields.py b/startup_scripts/startup_script_utils/custom_fields.py new file mode 100644 index 0000000..1be7a94 --- /dev/null +++ b/startup_scripts/startup_script_utils/custom_fields.py @@ -0,0 +1,15 @@ +def set_custom_fields_values(entity, custom_field_data): + if not custom_field_data: + return + + entity.custom_field_data = custom_field_data + return entity.save() + +def pop_custom_fields(params): + if 'custom_field_data' in params: + return params.pop('custom_field_data') + elif 'custom_fields' in params: + print("โš ๏ธ Please rename 'custom_fields' to 'custom_field_data'!") + return params.pop('custom_fields') + + return None diff --git a/startup_scripts/startup_script_utils/load_yaml.py b/startup_scripts/startup_script_utils/load_yaml.py index 4c16816..ecae7af 100644 --- a/startup_scripts/startup_script_utils/load_yaml.py +++ b/startup_scripts/startup_script_utils/load_yaml.py @@ -1,5 +1,6 @@ -from ruamel.yaml import YAML from pathlib import Path +from ruamel.yaml import YAML + def load_yaml(yaml_file: str): yf = Path(yaml_file) From 349e269356866f651311b35ae95bf400461ac17c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20M=C3=A4der?= Date: Sat, 17 Oct 2020 22:00:22 +0200 Subject: [PATCH 3/6] Remove the legacy select_field from the samples This was only used for testing. --- initializers/custom_fields.yml | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/initializers/custom_fields.yml b/initializers/custom_fields.yml index fae780d..98111fc 100644 --- a/initializers/custom_fields.yml +++ b/initializers/custom_fields.yml @@ -37,25 +37,6 @@ # weight: 10 # on_objects: # - tenancy.models.Tenant -# legacy_select_field: -# type: select -# label: Choose between items -# required: false -# filter_logic: exact -# weight: 30 -# on_objects: -# - dcim.models.Device -# choices: -# - value: First Item -# weight: 10 -# - value: Second Item -# weight: 20 -# - value: Third Item -# weight: 30 -# - value: Fifth Item -# weight: 50 -# - value: Fourth Item -# weight: 40 # select_field: # type: select # label: Choose between items From 77d3dcded020ddd9ea33a9059f1edc1a18062515 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20M=C3=A4der?= Date: Sat, 17 Oct 2020 22:10:33 +0200 Subject: [PATCH 4/6] Fix leftover from testing in test.sh --- test.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test.sh b/test.sh index dc892b1..fa6fa38 100755 --- a/test.sh +++ b/test.sh @@ -80,7 +80,7 @@ echo "๐Ÿณ๐Ÿณ๐Ÿณ Start testing '${IMAGE}'" trap test_cleanup EXIT ERR test_setup -#test_netbox_unit_tests +test_netbox_unit_tests test_initializers echo "๐Ÿณ๐Ÿณ๐Ÿณ Done testing '${IMAGE}'" From 234baa40a5191e0865d52c7ca2a11aeeed0c2ac4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20M=C3=A4der?= Date: Sun, 18 Oct 2020 03:01:57 +0200 Subject: [PATCH 5/6] Remove redundant assignment in for loop --- startup_scripts/020_custom_fields.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/startup_scripts/020_custom_fields.py b/startup_scripts/020_custom_fields.py index 3b2f1c1..da93a92 100644 --- a/startup_scripts/020_custom_fields.py +++ b/startup_scripts/020_custom_fields.py @@ -45,7 +45,7 @@ for cf_name, cf_details in customfields.items(): if cf_details.get('choices', False): custom_field.choices = [] - for _, choice_detail in enumerate(cf_details.get('choices', [])): + for choice_detail in enumerate(cf_details.get('choices', [])): if isinstance(choice_detail, str): custom_field.choices.append(choice_detail) else: # legacy mode From e383fd42bdf9826979e3d3a17887ef252cd9f7d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Ma=CC=88der?= Date: Mon, 14 Dec 2020 22:09:08 +0100 Subject: [PATCH 6/6] Fix custom fields initializer --- initializers/custom_fields.yml | 13 +++++++------ startup_scripts/000_users.py | 2 +- startup_scripts/020_custom_fields.py | 11 ++++++----- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/initializers/custom_fields.yml b/initializers/custom_fields.yml index 98111fc..654fb69 100644 --- a/initializers/custom_fields.yml +++ b/initializers/custom_fields.yml @@ -52,7 +52,7 @@ # - Third Item # - Fifth Item # - Fourth Item -# select_field_auto_weight: +# select_field_legacy_format: # type: select # label: Choose between items # required: false @@ -61,11 +61,12 @@ # on_objects: # - dcim.models.Device # choices: -# - A -# - B -# - C -# - E -# - D like deprecated +# - value: A # this is the deprecated format. +# - value: B # we only use it for the tests. +# - value: C # please see above for the new format. +# - value: "D like deprecated" +# weight: 999 +# - value: E # boolean_field: # type: boolean # label: Yes Or No? diff --git a/startup_scripts/000_users.py b/startup_scripts/000_users.py index a801d85..ffd4bec 100644 --- a/startup_scripts/000_users.py +++ b/startup_scripts/000_users.py @@ -1,6 +1,6 @@ import sys -from django.contrib.auth.models import Group, User +from django.contrib.auth.models import User from startup_script_utils import load_yaml, set_permissions from users.models import Token diff --git a/startup_scripts/020_custom_fields.py b/startup_scripts/020_custom_fields.py index da93a92..7479a5e 100644 --- a/startup_scripts/020_custom_fields.py +++ b/startup_scripts/020_custom_fields.py @@ -45,12 +45,13 @@ for cf_name, cf_details in customfields.items(): if cf_details.get('choices', False): custom_field.choices = [] - for choice_detail in enumerate(cf_details.get('choices', [])): - if isinstance(choice_detail, str): - custom_field.choices.append(choice_detail) - else: # legacy mode - print(f"โš ๏ธ Please migrate the 'choices' of '{cf_name}' to the new format, as 'weight' is no longer supported!") + for choice_detail in cf_details.get('choices', []): + if isinstance(choice_detail, dict) and 'value' in choice_detail: + # legacy mode + print(f"โš ๏ธ Please migrate the choice '{choice_detail['value']}' of '{cf_name}' to the new format, as 'weight' is no longer supported!") custom_field.choices.append(choice_detail['value']) + else: + custom_field.choices.append(choice_detail) custom_field.save()