From 50ade7bce199fce08fdd785d04cbb07572b52e8b Mon Sep 17 00:00:00 2001 From: Tobias Genannt Date: Wed, 5 Feb 2020 15:31:01 +0100 Subject: [PATCH 01/10] Add function to load YAML files This commit starts to remove some code redundancy from the startup scripts for easier maintenance. --- startup_scripts/000_users.py | 57 ++++++------ startup_scripts/010_groups.py | 56 ++++++------ startup_scripts/020_custom_fields.py | 63 ++++++------- startup_scripts/030_regions.py | 37 ++++---- startup_scripts/040_sites.py | 59 ++++++------ startup_scripts/050_manufacturers.py | 21 ++--- startup_scripts/060_device_types.py | 73 +++++++-------- startup_scripts/070_rack_roles.py | 31 +++---- startup_scripts/075_rack_groups.py | 36 ++++---- startup_scripts/080_racks.py | 75 ++++++++-------- startup_scripts/090_device_roles.py | 32 +++---- startup_scripts/100_platforms.py | 38 ++++---- startup_scripts/110_tenant_groups.py | 21 ++--- startup_scripts/120_tenants.py | 58 ++++++------ startup_scripts/130_devices.py | 86 +++++++++--------- startup_scripts/140_cluster_types.py | 21 ++--- startup_scripts/150_rirs.py | 21 ++--- startup_scripts/160_aggregates.py | 58 ++++++------ startup_scripts/170_clusters.py | 74 +++++++-------- startup_scripts/180_vrfs.py | 58 ++++++------ startup_scripts/190_prefix_vlan_roles.py | 21 ++--- startup_scripts/200_vlan_groups.py | 58 ++++++------ startup_scripts/210_vlans.py | 66 +++++++------- startup_scripts/220_prefixes.py | 72 +++++++-------- startup_scripts/230_virtual_machines.py | 76 ++++++++-------- .../240_virtualization_interfaces.py | 56 ++++++------ startup_scripts/250_dcim_interfaces.py | 56 ++++++------ startup_scripts/260_ip_addresses.py | 89 +++++++++---------- .../startup_script_utils/__init__.py | 1 + .../startup_script_utils/load_yaml.py | 10 +++ 30 files changed, 669 insertions(+), 811 deletions(-) create mode 100644 startup_scripts/startup_script_utils/__init__.py create mode 100644 startup_scripts/startup_script_utils/load_yaml.py diff --git a/startup_scripts/000_users.py b/startup_scripts/000_users.py index 6962df0..534a793 100644 --- a/startup_scripts/000_users.py +++ b/startup_scripts/000_users.py @@ -1,42 +1,37 @@ from django.contrib.auth.models import Permission, Group, User from users.models import Token -from ruamel.yaml import YAML -from pathlib import Path +from startup_script_utils import load_yaml import sys -file = Path('/opt/netbox/initializers/users.yml') -if not file.is_file(): +users = load_yaml('/opt/netbox/initializers/users.yml') + +if users is None: sys.exit() -with file.open('r') as stream: - yaml=YAML(typ='safe') - users = yaml.load(stream) +for username, user_details in users.items(): + if not User.objects.filter(username=username): + user = User.objects.create_user( + username = username, + password = user_details.get('password', 0) or User.objects.make_random_password) - if users is not None: - for username, user_details in users.items(): - if not User.objects.filter(username=username): - user = User.objects.create_user( - username = username, - password = user_details.get('password', 0) or User.objects.make_random_password) + print("πŸ‘€ Created user",username) - print("πŸ‘€ Created user",username) + if user_details.get('api_token', 0): + Token.objects.create(user=user, key=user_details['api_token']) - if user_details.get('api_token', 0): - Token.objects.create(user=user, key=user_details['api_token']) + yaml_permissions = user_details.get('permissions', []) + if yaml_permissions: + subject = user.user_permissions + subject.clear() + for yaml_permission in yaml_permissions: + if '*' in yaml_permission: + permission_filter = '^' + yaml_permission.replace('*','.*') + '$' + permissions = Permission.objects.filter(codename__iregex=permission_filter) + print(" ⚿ Granting", permissions.count(), "permissions matching '" + yaml_permission + "'") + else: + permissions = Permission.objects.filter(codename=yaml_permission) + print(" ⚿ Granting permission", yaml_permission) - yaml_permissions = user_details.get('permissions', []) - if yaml_permissions: - subject = user.user_permissions - subject.clear() - for yaml_permission in yaml_permissions: - if '*' in yaml_permission: - permission_filter = '^' + yaml_permission.replace('*','.*') + '$' - permissions = Permission.objects.filter(codename__iregex=permission_filter) - print(" ⚿ Granting", permissions.count(), "permissions matching '" + yaml_permission + "'") - else: - permissions = Permission.objects.filter(codename=yaml_permission) - print(" ⚿ Granting permission", yaml_permission) - - for permission in permissions: - subject.add(permission) + for permission in permissions: + subject.add(permission) diff --git a/startup_scripts/010_groups.py b/startup_scripts/010_groups.py index 08fb4bf..bad2a10 100644 --- a/startup_scripts/010_groups.py +++ b/startup_scripts/010_groups.py @@ -1,41 +1,35 @@ from django.contrib.auth.models import Permission, Group, User -from ruamel.yaml import YAML -from pathlib import Path +from startup_script_utils import load_yaml import sys -file = Path('/opt/netbox/initializers/groups.yml') -if not file.is_file(): +groups = load_yaml('/opt/netbox/initializers/groups.yml') +if groups is None: sys.exit() -with file.open('r') as stream: - yaml=YAML(typ='safe') - groups = yaml.load(stream) +for groupname, group_details in groups.items(): + group, created = Group.objects.get_or_create(name=groupname) - if groups is not None: - for groupname, group_details in groups.items(): - group, created = Group.objects.get_or_create(name=groupname) + if created: + print("πŸ‘₯ Created group", groupname) - if created: - print("πŸ‘₯ Created group", groupname) + for username in group_details.get('users', []): + user = User.objects.get(username=username) - for username in group_details.get('users', []): - user = User.objects.get(username=username) + if user: + user.groups.add(group) - if user: - user.groups.add(group) + yaml_permissions = group_details.get('permissions', []) + if yaml_permissions: + subject = group.permissions + subject.clear() + for yaml_permission in yaml_permissions: + if '*' in yaml_permission: + permission_filter = '^' + yaml_permission.replace('*','.*') + '$' + permissions = Permission.objects.filter(codename__iregex=permission_filter) + print(" ⚿ Granting", permissions.count(), "permissions matching '" + yaml_permission + "'") + else: + permissions = Permission.objects.filter(codename=yaml_permission) + print(" ⚿ Granting permission", yaml_permission) - yaml_permissions = group_details.get('permissions', []) - if yaml_permissions: - subject = group.permissions - subject.clear() - for yaml_permission in yaml_permissions: - if '*' in yaml_permission: - permission_filter = '^' + yaml_permission.replace('*','.*') + '$' - permissions = Permission.objects.filter(codename__iregex=permission_filter) - print(" ⚿ Granting", permissions.count(), "permissions matching '" + yaml_permission + "'") - else: - permissions = Permission.objects.filter(codename=yaml_permission) - print(" ⚿ Granting permission", yaml_permission) - - for permission in permissions: - subject.add(permission) + for permission in permissions: + subject.add(permission) diff --git a/startup_scripts/020_custom_fields.py b/startup_scripts/020_custom_fields.py index 2f6ba72..2cb48a0 100644 --- a/startup_scripts/020_custom_fields.py +++ b/startup_scripts/020_custom_fields.py @@ -1,7 +1,6 @@ from extras.models import CustomField, CustomFieldChoice -from ruamel.yaml import YAML -from pathlib import Path +from startup_script_utils import load_yaml import sys def get_class_for_class_path(class_path): @@ -13,47 +12,43 @@ def get_class_for_class_path(class_path): clazz = getattr(module, class_name) return ContentType.objects.get_for_model(clazz) -file = Path('/opt/netbox/initializers/custom_fields.yml') -if not file.is_file(): +customfields = load_yaml('/opt/netbox/initializers/custom_fields.yml') + +if customfields is None: sys.exit() -with file.open('r') as stream: - yaml = YAML(typ='safe') - customfields = yaml.load(stream) +for cf_name, cf_details in customfields.items(): + custom_field, created = CustomField.objects.get_or_create(name = cf_name) - if customfields is not None: - 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): + custom_field.default = cf_details['default'] - if created: - if cf_details.get('default', 0): - custom_field.default = cf_details['default'] + if cf_details.get('description', 0): + custom_field.description = cf_details['description'] - if cf_details.get('description', 0): - custom_field.description = cf_details['description'] + if cf_details.get('label', 0): + custom_field.label = cf_details['label'] - if cf_details.get('label', 0): - 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)) - for object_type in cf_details.get('on_objects', []): - custom_field.obj_type.add(get_class_for_class_path(object_type)) + if cf_details.get('required', 0): + custom_field.required = cf_details['required'] - if cf_details.get('required', 0): - custom_field.required = cf_details['required'] + if cf_details.get('type', 0): + custom_field.type = cf_details['type'] - if cf_details.get('type', 0): - custom_field.type = cf_details['type'] + if cf_details.get('weight', 0): + custom_field.weight = cf_details['weight'] - if cf_details.get('weight', 0): - custom_field.weight = cf_details['weight'] + custom_field.save() - 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} + ) - 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) + print("πŸ”§ Created custom field", cf_name) diff --git a/startup_scripts/030_regions.py b/startup_scripts/030_regions.py index d9e7bdf..0b61d89 100644 --- a/startup_scripts/030_regions.py +++ b/startup_scripts/030_regions.py @@ -1,31 +1,26 @@ from dcim.models import Region -from ruamel.yaml import YAML -from pathlib import Path +from startup_script_utils import load_yaml import sys -file = Path('/opt/netbox/initializers/regions.yml') -if not file.is_file(): +regions = load_yaml('/opt/netbox/initializers/regions.yml') + +if regions is None: sys.exit() -with file.open('r') as stream: - yaml=YAML(typ='safe') - regions = yaml.load(stream) +optional_assocs = { + 'parent': (Region, 'name') +} - optional_assocs = { - 'parent': (Region, 'name') - } +for params in regions: - if regions is not None: - for params in regions: + for assoc, details in optional_assocs.items(): + if assoc in params: + model, field = details + query = { field: params.pop(assoc) } - for assoc, details in optional_assocs.items(): - if assoc in params: - model, field = details - query = { field: params.pop(assoc) } + params[assoc] = model.objects.get(**query) - params[assoc] = model.objects.get(**query) + region, created = Region.objects.get_or_create(**params) - region, created = Region.objects.get_or_create(**params) - - if created: - print("🌐 Created region", region.name) + if created: + print("🌐 Created region", region.name) diff --git a/startup_scripts/040_sites.py b/startup_scripts/040_sites.py index 27be053..828a86b 100644 --- a/startup_scripts/040_sites.py +++ b/startup_scripts/040_sites.py @@ -1,46 +1,41 @@ from dcim.models import Region, Site from extras.models import CustomField, CustomFieldValue from tenancy.models import Tenant -from ruamel.yaml import YAML -from pathlib import Path +from startup_script_utils import load_yaml import sys -file = Path('/opt/netbox/initializers/sites.yml') -if not file.is_file(): +sites = load_yaml('/opt/netbox/initializers/sites.yml') + +if sites is None: sys.exit() -with file.open('r') as stream: - yaml = YAML(typ='safe') - sites = yaml.load(stream) +optional_assocs = { + 'region': (Region, 'name'), + 'tenant': (Tenant, 'name') +} - optional_assocs = { - 'region': (Region, 'name'), - 'tenant': (Tenant, 'name') - } +for params in sites: + custom_fields = params.pop('custom_fields', None) - if sites is not None: - for params in sites: - custom_fields = params.pop('custom_fields', None) + for assoc, details in optional_assocs.items(): + if assoc in params: + model, field = details + query = { field: params.pop(assoc) } - for assoc, details in optional_assocs.items(): - if assoc in params: - model, field = details - query = { field: params.pop(assoc) } + params[assoc] = model.objects.get(**query) - params[assoc] = model.objects.get(**query) + site, created = Site.objects.get_or_create(**params) - 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 + ) - 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) - site.custom_field_values.add(custom_field_value) - - print("πŸ“ Created site", site.name) + print("πŸ“ Created site", site.name) diff --git a/startup_scripts/050_manufacturers.py b/startup_scripts/050_manufacturers.py index 2f8d850..b9ebb32 100644 --- a/startup_scripts/050_manufacturers.py +++ b/startup_scripts/050_manufacturers.py @@ -1,19 +1,14 @@ from dcim.models import Manufacturer -from ruamel.yaml import YAML -from pathlib import Path +from startup_script_utils import load_yaml import sys -file = Path('/opt/netbox/initializers/manufacturers.yml') -if not file.is_file(): +manufacturers = load_yaml('/opt/netbox/initializers/manufacturers.yml') + +if manufacturers is None: sys.exit() -with file.open('r') as stream: - yaml = YAML(typ='safe') - manufacturers = yaml.load(stream) +for params in manufacturers: + manufacturer, created = Manufacturer.objects.get_or_create(**params) - if manufacturers is not None: - for params in manufacturers: - manufacturer, created = Manufacturer.objects.get_or_create(**params) - - if created: - print("🏭 Created Manufacturer", manufacturer.name) + if created: + print("🏭 Created Manufacturer", manufacturer.name) diff --git a/startup_scripts/060_device_types.py b/startup_scripts/060_device_types.py index b806cfc..e6cea93 100644 --- a/startup_scripts/060_device_types.py +++ b/startup_scripts/060_device_types.py @@ -1,56 +1,51 @@ from dcim.models import DeviceType, Manufacturer, Region from tenancy.models import Tenant from extras.models import CustomField, CustomFieldValue -from ruamel.yaml import YAML -from pathlib import Path +from startup_script_utils import load_yaml import sys -file = Path('/opt/netbox/initializers/device_types.yml') -if not file.is_file(): +device_types = load_yaml('/opt/netbox/initializers/device_types.yml') + +if device_types is None: sys.exit() -with file.open('r') as stream: - yaml = YAML(typ='safe') - device_types = yaml.load(stream) +required_assocs = { + 'manufacturer': (Manufacturer, 'name') +} - required_assocs = { - 'manufacturer': (Manufacturer, 'name') - } +optional_assocs = { + 'region': (Region, 'name'), + 'tenant': (Tenant, 'name') +} - optional_assocs = { - 'region': (Region, 'name'), - 'tenant': (Tenant, 'name') - } +for params in device_types: + custom_fields = params.pop('custom_fields', None) - if device_types is not None: - for params in device_types: - custom_fields = params.pop('custom_fields', None) + for assoc, details in required_assocs.items(): + model, field = details + query = { field: params.pop(assoc) } - for assoc, details in required_assocs.items(): - model, field = details - query = { field: params.pop(assoc) } + params[assoc] = model.objects.get(**query) - params[assoc] = model.objects.get(**query) + for assoc, details in optional_assocs.items(): + if assoc in params: + model, field = details + query = { field: params.pop(assoc) } - for assoc, details in optional_assocs.items(): - if assoc in params: - model, field = details - query = { field: params.pop(assoc) } + params[assoc] = model.objects.get(**query) - params[assoc] = model.objects.get(**query) + device_type, created = DeviceType.objects.get_or_create(**params) - 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 + ) - 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) - device_type.custom_field_values.add(custom_field_value) - - print("πŸ”‘ Created device type", device_type.manufacturer, device_type.model) + print("πŸ”‘ Created device type", device_type.manufacturer, device_type.model) diff --git a/startup_scripts/070_rack_roles.py b/startup_scripts/070_rack_roles.py index 0f43b7f..98a5be4 100644 --- a/startup_scripts/070_rack_roles.py +++ b/startup_scripts/070_rack_roles.py @@ -1,28 +1,23 @@ from dcim.models import RackRole -from ruamel.yaml import YAML from utilities.forms import COLOR_CHOICES -from pathlib import Path +from startup_script_utils import load_yaml import sys -file = Path('/opt/netbox/initializers/rack_roles.yml') -if not file.is_file(): +rack_roles = load_yaml('/opt/netbox/initializers/rack_roles.yml') + +if rack_roles is None: sys.exit() -with file.open('r') as stream: - yaml=YAML(typ='safe') - rack_roles = yaml.load(stream) +for params in rack_roles: + if 'color' in params: + color = params.pop('color') - if rack_roles is not None: - for params in rack_roles: - if 'color' in params: - color = params.pop('color') + for color_tpl in COLOR_CHOICES: + if color in color_tpl: + params['color'] = color_tpl[0] - for color_tpl in COLOR_CHOICES: - if color in color_tpl: - params['color'] = color_tpl[0] + rack_role, created = RackRole.objects.get_or_create(**params) - rack_role, created = RackRole.objects.get_or_create(**params) - - if created: - print("🎨 Created rack role", rack_role.name) + if created: + print("🎨 Created rack role", rack_role.name) diff --git a/startup_scripts/075_rack_groups.py b/startup_scripts/075_rack_groups.py index 7deaa11..dc5ec20 100644 --- a/startup_scripts/075_rack_groups.py +++ b/startup_scripts/075_rack_groups.py @@ -1,31 +1,25 @@ from dcim.models import Site,RackGroup -from ruamel.yaml import YAML - -from pathlib import Path +from startup_script_utils import load_yaml import sys -file = Path('/opt/netbox/initializers/rack_groups.yml') -if not file.is_file(): +rack_groups = load_yaml('/opt/netbox/initializers/rack_groups.yml') + +if rack_groups is None: sys.exit() -with file.open('r') as stream: - yaml=YAML(typ='safe') - rack_groups= yaml.load(stream) +required_assocs = { + 'site': (Site, 'name') +} - required_assocs = { - 'site': (Site, 'name') - } +for params in rack_groups: - if rack_groups is not None: - for params in rack_groups: + for assoc, details in required_assocs.items(): + model, field = details + query = { field: params.pop(assoc) } + params[assoc] = model.objects.get(**query) - for assoc, details in required_assocs.items(): - model, field = details - query = { field: params.pop(assoc) } - params[assoc] = model.objects.get(**query) + rack_group, created = RackGroup.objects.get_or_create(**params) - rack_group, created = RackGroup.objects.get_or_create(**params) - - if created: - print("🎨 Created rack group", rack_group.name) + if created: + print("🎨 Created rack group", rack_group.name) diff --git a/startup_scripts/080_racks.py b/startup_scripts/080_racks.py index ed7713d..279cb2c 100644 --- a/startup_scripts/080_racks.py +++ b/startup_scripts/080_racks.py @@ -1,57 +1,52 @@ from dcim.models import Site, RackRole, Rack, RackGroup from tenancy.models import Tenant from extras.models import CustomField, CustomFieldValue -from ruamel.yaml import YAML -from pathlib import Path +from startup_script_utils import load_yaml import sys -file = Path('/opt/netbox/initializers/racks.yml') -if not file.is_file(): +racks = load_yaml('/opt/netbox/initializers/racks.yml') + +if racks is None: sys.exit() -with file.open('r') as stream: - yaml = YAML(typ='safe') - racks = yaml.load(stream) +required_assocs = { + 'site': (Site, 'name') +} - required_assocs = { - 'site': (Site, 'name') - } +optional_assocs = { + 'role': (RackRole, 'name'), + 'tenant': (Tenant, 'name'), + 'group': (RackGroup, 'name') +} - optional_assocs = { - 'role': (RackRole, 'name'), - 'tenant': (Tenant, 'name'), - 'group': (RackGroup, 'name') - } +for params in racks: + custom_fields = params.pop('custom_fields', None) - if racks is not None: - for params in racks: - custom_fields = params.pop('custom_fields', None) + for assoc, details in required_assocs.items(): + model, field = details + query = { field: params.pop(assoc) } - for assoc, details in required_assocs.items(): - model, field = details - query = { field: params.pop(assoc) } + params[assoc] = model.objects.get(**query) - params[assoc] = model.objects.get(**query) + for assoc, details in optional_assocs.items(): + if assoc in params: + model, field = details + query = { field: params.pop(assoc) } - for assoc, details in optional_assocs.items(): - if assoc in params: - model, field = details - query = { field: params.pop(assoc) } + params[assoc] = model.objects.get(**query) - params[assoc] = model.objects.get(**query) + rack, created = Rack.objects.get_or_create(**params) - 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 + ) - 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) - rack.custom_field_values.add(custom_field_value) - - print("πŸ”³ Created rack", rack.site, rack.name) + print("πŸ”³ Created rack", rack.site, rack.name) diff --git a/startup_scripts/090_device_roles.py b/startup_scripts/090_device_roles.py index 4584336..c75253d 100644 --- a/startup_scripts/090_device_roles.py +++ b/startup_scripts/090_device_roles.py @@ -1,29 +1,23 @@ from dcim.models import DeviceRole -from ruamel.yaml import YAML from utilities.forms import COLOR_CHOICES - -from pathlib import Path +from startup_script_utils import load_yaml import sys -file = Path('/opt/netbox/initializers/device_roles.yml') -if not file.is_file(): +device_roles = load_yaml('/opt/netbox/initializers/device_roles.yml') + +if device_roles is None: sys.exit() -with file.open('r') as stream: - yaml=YAML(typ='safe') - device_roles = yaml.load(stream) +for params in device_roles: - if device_roles is not None: - for params in device_roles: + if 'color' in params: + color = params.pop('color') - if 'color' in params: - color = params.pop('color') + for color_tpl in COLOR_CHOICES: + if color in color_tpl: + params['color'] = color_tpl[0] - for color_tpl in COLOR_CHOICES: - if color in color_tpl: - params['color'] = color_tpl[0] + device_role, created = DeviceRole.objects.get_or_create(**params) - device_role, created = DeviceRole.objects.get_or_create(**params) - - if created: - print("🎨 Created device role", device_role.name) + if created: + print("🎨 Created device role", device_role.name) diff --git a/startup_scripts/100_platforms.py b/startup_scripts/100_platforms.py index cb47054..3673230 100644 --- a/startup_scripts/100_platforms.py +++ b/startup_scripts/100_platforms.py @@ -1,32 +1,26 @@ from dcim.models import Manufacturer, Platform -from ruamel.yaml import YAML - -from pathlib import Path +from startup_script_utils import load_yaml import sys -file = Path('/opt/netbox/initializers/platforms.yml') -if not file.is_file(): +platforms = load_yaml('/opt/netbox/initializers/platforms.yml') + +if platforms is None: sys.exit() -with file.open('r') as stream: - yaml = YAML(typ='safe') - platforms = yaml.load(stream) +optional_assocs = { + 'manufacturer': (Manufacturer, 'name'), +} - optional_assocs = { - 'manufacturer': (Manufacturer, 'name'), - } +for params in platforms: - if platforms is not None: - for params in platforms: + for assoc, details in optional_assocs.items(): + if assoc in params: + model, field = details + query = { field: params.pop(assoc) } - for assoc, details in optional_assocs.items(): - if assoc in params: - model, field = details - query = { field: params.pop(assoc) } + params[assoc] = model.objects.get(**query) - params[assoc] = model.objects.get(**query) + platform, created = Platform.objects.get_or_create(**params) - platform, created = Platform.objects.get_or_create(**params) - - if created: - print("πŸ’Ύ Created platform", platform.name) + if created: + print("πŸ’Ύ Created platform", platform.name) diff --git a/startup_scripts/110_tenant_groups.py b/startup_scripts/110_tenant_groups.py index eaf75dc..c106d67 100644 --- a/startup_scripts/110_tenant_groups.py +++ b/startup_scripts/110_tenant_groups.py @@ -1,19 +1,14 @@ from tenancy.models import TenantGroup -from ruamel.yaml import YAML -from pathlib import Path +from startup_script_utils import load_yaml import sys -file = Path('/opt/netbox/initializers/tenant_groups.yml') -if not file.is_file(): +tenant_groups = load_yaml('/opt/netbox/initializers/tenant_groups.yml') + +if tenant_groups is None: sys.exit() -with file.open('r') as stream: - yaml = YAML(typ='safe') - tenant_groups = yaml.load(stream) +for params in tenant_groups: + tenant_group, created = TenantGroup.objects.get_or_create(**params) - if tenant_groups is not None: - for params in tenant_groups: - tenant_group, created = TenantGroup.objects.get_or_create(**params) - - if created: - print("πŸ”³ Created Tenant Group", tenant_group.name) + if created: + print("πŸ”³ Created Tenant Group", tenant_group.name) diff --git a/startup_scripts/120_tenants.py b/startup_scripts/120_tenants.py index 8252dc7..121c83a 100644 --- a/startup_scripts/120_tenants.py +++ b/startup_scripts/120_tenants.py @@ -1,45 +1,39 @@ from tenancy.models import Tenant, TenantGroup from extras.models import CustomField, CustomFieldValue -from ruamel.yaml import YAML - -from pathlib import Path +from startup_script_utils import load_yaml import sys -file = Path('/opt/netbox/initializers/tenants.yml') -if not file.is_file(): +tenants = load_yaml('/opt/netbox/initializers/tenants.yml') + +if tenants is None: sys.exit() -with file.open('r') as stream: - yaml = YAML(typ='safe') - tenants = yaml.load(stream) +optional_assocs = { + 'group': (TenantGroup, 'name') +} - optional_assocs = { - 'group': (TenantGroup, 'name') - } +for params in tenants: + custom_fields = params.pop('custom_fields', None) - if tenants is not None: - for params in tenants: - custom_fields = params.pop('custom_fields', None) + for assoc, details in optional_assocs.items(): + if assoc in params: + model, field = details + query = { field: params.pop(assoc) } - for assoc, details in optional_assocs.items(): - if assoc in params: - model, field = details - query = { field: params.pop(assoc) } + params[assoc] = model.objects.get(**query) - params[assoc] = model.objects.get(**query) + tenant, created = Tenant.objects.get_or_create(**params) - 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 + ) - 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) - tenant.custom_field_values.add(custom_field_value) - - print("πŸ‘©β€πŸ’» Created Tenant", tenant.name) + print("πŸ‘©β€πŸ’» Created Tenant", tenant.name) diff --git a/startup_scripts/130_devices.py b/startup_scripts/130_devices.py index 4217549..1e0d894 100644 --- a/startup_scripts/130_devices.py +++ b/startup_scripts/130_devices.py @@ -3,63 +3,57 @@ from ipam.models import IPAddress from virtualization.models import Cluster from tenancy.models import Tenant from extras.models import CustomField, CustomFieldValue -from ruamel.yaml import YAML - -from pathlib import Path +from startup_script_utils import load_yaml import sys -file = Path('/opt/netbox/initializers/devices.yml') -if not file.is_file(): +devices = load_yaml('/opt/netbox/initializers/devices.yml') + +if devices is None: sys.exit() -with file.open('r') as stream: - yaml = YAML(typ='safe') - devices = yaml.load(stream) +required_assocs = { + 'device_role': (DeviceRole, 'name'), + 'device_type': (DeviceType, 'model'), + 'site': (Site, 'name') +} - required_assocs = { - 'device_role': (DeviceRole, 'name'), - 'device_type': (DeviceType, 'model'), - 'site': (Site, 'name') - } +optional_assocs = { + 'tenant': (Tenant, 'name'), + 'platform': (Platform, 'name'), + 'rack': (Rack, 'name'), + 'cluster': (Cluster, 'name'), + 'primary_ip4': (IPAddress, 'address'), + 'primary_ip6': (IPAddress, 'address') +} - optional_assocs = { - 'tenant': (Tenant, 'name'), - 'platform': (Platform, 'name'), - 'rack': (Rack, 'name'), - 'cluster': (Cluster, 'name'), - 'primary_ip4': (IPAddress, 'address'), - 'primary_ip6': (IPAddress, 'address') - } +for params in devices: + custom_fields = params.pop('custom_fields', None) - if devices is not None: - for params in devices: - custom_fields = params.pop('custom_fields', None) + for assoc, details in required_assocs.items(): + model, field = details + query = { field: params.pop(assoc) } - for assoc, details in required_assocs.items(): - model, field = details - query = { field: params.pop(assoc) } + params[assoc] = model.objects.get(**query) - params[assoc] = model.objects.get(**query) + for assoc, details in optional_assocs.items(): + if assoc in params: + model, field = details + query = { field: params.pop(assoc) } - for assoc, details in optional_assocs.items(): - if assoc in params: - model, field = details - query = { field: params.pop(assoc) } + params[assoc] = model.objects.get(**query) - params[assoc] = model.objects.get(**query) + device, created = Device.objects.get_or_create(**params) - 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 + ) - 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) - device.custom_field_values.add(custom_field_value) - - print("πŸ–₯️ Created device", device.name) + print("πŸ–₯️ Created device", device.name) diff --git a/startup_scripts/140_cluster_types.py b/startup_scripts/140_cluster_types.py index 9bd4be1..d39933f 100644 --- a/startup_scripts/140_cluster_types.py +++ b/startup_scripts/140_cluster_types.py @@ -1,19 +1,14 @@ from virtualization.models import ClusterType -from ruamel.yaml import YAML -from pathlib import Path +from startup_script_utils import load_yaml import sys -file = Path('/opt/netbox/initializers/cluster_types.yml') -if not file.is_file(): +cluster_types = load_yaml('/opt/netbox/initializers/cluster_types.yml') + +if cluster_types is None: sys.exit() -with file.open('r') as stream: - yaml = YAML(typ='safe') - cluster_types = yaml.load(stream) +for params in cluster_types: + cluster_type, created = ClusterType.objects.get_or_create(**params) - if cluster_types is not None: - for params in cluster_types: - cluster_type, created = ClusterType.objects.get_or_create(**params) - - if created: - print("🧰 Created Cluster Type", cluster_type.name) + if created: + print("🧰 Created Cluster Type", cluster_type.name) diff --git a/startup_scripts/150_rirs.py b/startup_scripts/150_rirs.py index 9cee56f..8bcf51f 100644 --- a/startup_scripts/150_rirs.py +++ b/startup_scripts/150_rirs.py @@ -1,19 +1,14 @@ from ipam.models import RIR -from ruamel.yaml import YAML -from pathlib import Path +from startup_script_utils import load_yaml import sys -file = Path('/opt/netbox/initializers/rirs.yml') -if not file.is_file(): +rirs = load_yaml('/opt/netbox/initializers/rirs.yml') + +if rirs is None: sys.exit() -with file.open('r') as stream: - yaml = YAML(typ='safe') - rirs = yaml.load(stream) +for params in rirs: + rir, created = RIR.objects.get_or_create(**params) - if rirs is not None: - for params in rirs: - rir, created = RIR.objects.get_or_create(**params) - - if created: - print("πŸ—ΊοΈ Created RIR", rir.name) + if created: + print("πŸ—ΊοΈ Created RIR", rir.name) diff --git a/startup_scripts/160_aggregates.py b/startup_scripts/160_aggregates.py index f932709..0ffe9b0 100644 --- a/startup_scripts/160_aggregates.py +++ b/startup_scripts/160_aggregates.py @@ -1,46 +1,42 @@ from ipam.models import Aggregate, RIR -from ruamel.yaml import YAML + from extras.models import CustomField, CustomFieldValue from netaddr import IPNetwork -from pathlib import Path +from startup_script_utils import load_yaml import sys -file = Path('/opt/netbox/initializers/aggregates.yml') -if not file.is_file(): +aggregates = load_yaml('/opt/netbox/initializers/aggregates.yml') + +if aggregates is None: sys.exit() -with file.open('r') as stream: - yaml = YAML(typ='safe') - aggregates = yaml.load(stream) +required_assocs = { + 'rir': (RIR, 'name') +} - required_assocs = { - 'rir': (RIR, 'name') - } +for params in aggregates: + custom_fields = params.pop('custom_fields', None) + params['prefix'] = IPNetwork(params['prefix']) - if aggregates is not None: - for params in aggregates: - custom_fields = params.pop('custom_fields', None) - params['prefix'] = IPNetwork(params['prefix']) + for assoc, details in required_assocs.items(): + model, field = details + query = { field: params.pop(assoc) } - for assoc, details in required_assocs.items(): - model, field = details - query = { field: params.pop(assoc) } + params[assoc] = model.objects.get(**query) - params[assoc] = model.objects.get(**query) + aggregate, created = Aggregate.objects.get_or_create(**params) - 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 + ) - 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) - aggregate.custom_field_values.add(custom_field_value) - - print("πŸ—žοΈ Created Aggregate", aggregate.prefix) + print("πŸ—žοΈ Created Aggregate", aggregate.prefix) diff --git a/startup_scripts/170_clusters.py b/startup_scripts/170_clusters.py index 597f73f..a7e2065 100644 --- a/startup_scripts/170_clusters.py +++ b/startup_scripts/170_clusters.py @@ -1,57 +1,51 @@ from dcim.models import Site from virtualization.models import Cluster, ClusterType, ClusterGroup from extras.models import CustomField, CustomFieldValue -from ruamel.yaml import YAML - -from pathlib import Path +from startup_script_utils import load_yaml import sys -file = Path('/opt/netbox/initializers/clusters.yml') -if not file.is_file(): +clusters = load_yaml('/opt/netbox/initializers/clusters.yml') + +if clusters is None: sys.exit() -with file.open('r') as stream: - yaml = YAML(typ='safe') - clusters = yaml.load(stream) +required_assocs = { + 'type': (ClusterType, 'name') +} - required_assocs = { - 'type': (ClusterType, 'name') - } +optional_assocs = { + 'site': (Site, 'name'), + 'group': (ClusterGroup, 'name') +} - optional_assocs = { - 'site': (Site, 'name'), - 'group': (ClusterGroup, 'name') - } +for params in clusters: + custom_fields = params.pop('custom_fields', None) - if clusters is not None: - for params in clusters: - custom_fields = params.pop('custom_fields', None) + for assoc, details in required_assocs.items(): + model, field = details + query = { field: params.pop(assoc) } - for assoc, details in required_assocs.items(): - model, field = details - query = { field: params.pop(assoc) } + params[assoc] = model.objects.get(**query) - params[assoc] = model.objects.get(**query) + for assoc, details in optional_assocs.items(): + if assoc in params: + model, field = details + query = { field: params.pop(assoc) } - for assoc, details in optional_assocs.items(): - if assoc in params: - model, field = details - query = { field: params.pop(assoc) } + params[assoc] = model.objects.get(**query) - params[assoc] = model.objects.get(**query) + cluster, created = Cluster.objects.get_or_create(**params) - 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 + ) - 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) - cluster.custom_field_values.add(custom_field_value) - - print("πŸ—„οΈ Created cluster", cluster.name) + print("πŸ—„οΈ Created cluster", cluster.name) diff --git a/startup_scripts/180_vrfs.py b/startup_scripts/180_vrfs.py index d611c04..496710d 100644 --- a/startup_scripts/180_vrfs.py +++ b/startup_scripts/180_vrfs.py @@ -1,46 +1,42 @@ from ipam.models import VRF from tenancy.models import Tenant -from ruamel.yaml import YAML + from extras.models import CustomField, CustomFieldValue -from pathlib import Path +from startup_script_utils import load_yaml import sys -file = Path('/opt/netbox/initializers/vrfs.yml') -if not file.is_file(): +vrfs = load_yaml('/opt/netbox/initializers/vrfs.yml') + +if vrfs is None: sys.exit() -with file.open('r') as stream: - yaml = YAML(typ='safe') - vrfs = yaml.load(stream) +optional_assocs = { + 'tenant': (Tenant, 'name') +} - optional_assocs = { - 'tenant': (Tenant, 'name') - } +for params in vrfs: + custom_fields = params.pop('custom_fields', None) - if vrfs is not None: - for params in vrfs: - custom_fields = params.pop('custom_fields', None) + for assoc, details in optional_assocs.items(): + if assoc in params: + model, field = details + query = { field: params.pop(assoc) } - for assoc, details in optional_assocs.items(): - if assoc in params: - model, field = details - query = { field: params.pop(assoc) } + params[assoc] = model.objects.get(**query) - params[assoc] = model.objects.get(**query) + vrf, created = VRF.objects.get_or_create(**params) - 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 + ) - 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) - vrf.custom_field_values.add(custom_field_value) - - print("πŸ“¦ Created VRF", vrf.name) + print("πŸ“¦ Created VRF", vrf.name) diff --git a/startup_scripts/190_prefix_vlan_roles.py b/startup_scripts/190_prefix_vlan_roles.py index 61fa606..72c8eee 100644 --- a/startup_scripts/190_prefix_vlan_roles.py +++ b/startup_scripts/190_prefix_vlan_roles.py @@ -1,19 +1,14 @@ from ipam.models import Role -from ruamel.yaml import YAML -from pathlib import Path +from startup_script_utils import load_yaml import sys -file = Path('/opt/netbox/initializers/prefix_vlan_roles.yml') -if not file.is_file(): +roles = load_yaml('/opt/netbox/initializers/prefix_vlan_roles.yml') + +if roles is None: sys.exit() -with file.open('r') as stream: - yaml = YAML(typ='safe') - roles = yaml.load(stream) +for params in roles: + role, created = Role.objects.get_or_create(**params) - if roles is not None: - for params in roles: - role, created = Role.objects.get_or_create(**params) - - if created: - print("⛹️‍ Created Prefix/VLAN Role", role.name) + if created: + print("⛹️‍ Created Prefix/VLAN Role", role.name) diff --git a/startup_scripts/200_vlan_groups.py b/startup_scripts/200_vlan_groups.py index 54250fb..f8dc55d 100644 --- a/startup_scripts/200_vlan_groups.py +++ b/startup_scripts/200_vlan_groups.py @@ -1,46 +1,40 @@ from dcim.models import Site from ipam.models import VLANGroup from extras.models import CustomField, CustomFieldValue -from ruamel.yaml import YAML - -from pathlib import Path +from startup_script_utils import load_yaml import sys -file = Path('/opt/netbox/initializers/vlan_groups.yml') -if not file.is_file(): +vlan_groups = load_yaml('/opt/netbox/initializers/vlan_groups.yml') + +if vlan_groups is None: sys.exit() -with file.open('r') as stream: - yaml = YAML(typ='safe') - vlan_groups = yaml.load(stream) +optional_assocs = { + 'site': (Site, 'name') +} - optional_assocs = { - 'site': (Site, 'name') - } +for params in vlan_groups: + custom_fields = params.pop('custom_fields', None) - if vlan_groups is not None: - for params in vlan_groups: - custom_fields = params.pop('custom_fields', None) + for assoc, details in optional_assocs.items(): + if assoc in params: + model, field = details + query = { field: params.pop(assoc) } - for assoc, details in optional_assocs.items(): - if assoc in params: - model, field = details - query = { field: params.pop(assoc) } + params[assoc] = model.objects.get(**query) - params[assoc] = model.objects.get(**query) + vlan_group, created = VLANGroup.objects.get_or_create(**params) - 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 + ) - 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) - vlan_group.custom_field_values.add(custom_field_value) - - print("🏘️ Created VLAN Group", vlan_group.name) + print("🏘️ Created VLAN Group", vlan_group.name) diff --git a/startup_scripts/210_vlans.py b/startup_scripts/210_vlans.py index ab6bd2b..ceab196 100644 --- a/startup_scripts/210_vlans.py +++ b/startup_scripts/210_vlans.py @@ -2,50 +2,44 @@ from dcim.models import Site from ipam.models import VLAN, VLANGroup, Role from tenancy.models import Tenant, TenantGroup from extras.models import CustomField, CustomFieldValue -from ruamel.yaml import YAML - -from pathlib import Path +from startup_script_utils import load_yaml import sys -file = Path('/opt/netbox/initializers/vlans.yml') -if not file.is_file(): +vlans = load_yaml('/opt/netbox/initializers/vlans.yml') + +if vlans is None: sys.exit() -with file.open('r') as stream: - yaml = YAML(typ='safe') - vlans = yaml.load(stream) +optional_assocs = { + 'site': (Site, 'name'), + 'tenant': (Tenant, 'name'), + 'tenant_group': (TenantGroup, 'name'), + 'group': (VLANGroup, 'name'), + 'role': (Role, 'name') +} - optional_assocs = { - 'site': (Site, 'name'), - 'tenant': (Tenant, 'name'), - 'tenant_group': (TenantGroup, 'name'), - 'group': (VLANGroup, 'name'), - 'role': (Role, 'name') - } +for params in vlans: + custom_fields = params.pop('custom_fields', None) - if vlans is not None: - for params in vlans: - custom_fields = params.pop('custom_fields', None) + for assoc, details in optional_assocs.items(): + if assoc in params: + model, field = details + query = { field: params.pop(assoc) } - for assoc, details in optional_assocs.items(): - if assoc in params: - model, field = details - query = { field: params.pop(assoc) } + params[assoc] = model.objects.get(**query) - params[assoc] = model.objects.get(**query) + vlan, created = VLAN.objects.get_or_create(**params) - 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 + ) - 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) - vlan.custom_field_values.add(custom_field_value) - - print("🏠 Created VLAN", vlan.name) + print("🏠 Created VLAN", vlan.name) diff --git a/startup_scripts/220_prefixes.py b/startup_scripts/220_prefixes.py index d13578a..b047c8c 100644 --- a/startup_scripts/220_prefixes.py +++ b/startup_scripts/220_prefixes.py @@ -2,53 +2,45 @@ 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 ruamel.yaml import YAML - from netaddr import IPNetwork -from pathlib import Path +from startup_script_utils import load_yaml import sys -file = Path('/opt/netbox/initializers/prefixes.yml') -if not file.is_file(): +prefixes = load_yaml('/opt/netbox/initializers/prefixes.yml') + +if prefixes is None: sys.exit() -with file.open('r') as stream: - yaml = YAML(typ='safe') - prefixes = yaml.load(stream) +optional_assocs = { + 'site': (Site, 'name'), + 'tenant': (Tenant, 'name'), + 'tenant_group': (TenantGroup, 'name'), + 'vlan': (VLAN, 'name'), + 'role': (Role, 'name'), + 'vrf': (VRF, 'name') +} - optional_assocs = { - 'site': (Site, 'name'), - 'tenant': (Tenant, 'name'), - 'tenant_group': (TenantGroup, 'name'), - 'vlan': (VLAN, 'name'), - 'role': (Role, 'name'), - 'vrf': (VRF, 'name') - } +for params in prefixes: + custom_fields = params.pop('custom_fields', None) + params['prefix'] = IPNetwork(params['prefix']) - if prefixes is not None: - for params in prefixes: - custom_fields = params.pop('custom_fields', None) - params['prefix'] = IPNetwork(params['prefix']) + for assoc, details in optional_assocs.items(): + if assoc in params: + model, field = details + query = { field: params.pop(assoc) } + params[assoc] = model.objects.get(**query) - for assoc, details in optional_assocs.items(): - if assoc in params: - model, field = details - query = { field: params.pop(assoc) } + prefix, created = Prefix.objects.get_or_create(**params) - params[assoc] = model.objects.get(**query) + 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) - 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) - - print("πŸ“Œ Created Prefix", prefix.prefix) + print("πŸ“Œ Created Prefix", prefix.prefix) diff --git a/startup_scripts/230_virtual_machines.py b/startup_scripts/230_virtual_machines.py index 449df8a..6776c4d 100644 --- a/startup_scripts/230_virtual_machines.py +++ b/startup_scripts/230_virtual_machines.py @@ -2,58 +2,52 @@ 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 ruamel.yaml import YAML - -from pathlib import Path +from startup_script_utils import load_yaml import sys -file = Path('/opt/netbox/initializers/virtual_machines.yml') -if not file.is_file(): +virtual_machines = load_yaml('/opt/netbox/initializers/virtual_machines.yml') + +if virtual_machines is None: sys.exit() -with file.open('r') as stream: - yaml = YAML(typ='safe') - virtual_machines = yaml.load(stream) +required_assocs = { + 'cluster': (Cluster, 'name') +} - required_assocs = { - 'cluster': (Cluster, 'name') - } +optional_assocs = { + 'tenant': (Tenant, 'name'), + 'platform': (Platform, 'name'), + 'role': (DeviceRole, 'name') +} - optional_assocs = { - 'tenant': (Tenant, 'name'), - 'platform': (Platform, 'name'), - 'role': (DeviceRole, 'name') - } +for params in virtual_machines: + custom_fields = params.pop('custom_fields', None) - if virtual_machines is not None: - for params in virtual_machines: - custom_fields = params.pop('custom_fields', None) + for assoc, details in required_assocs.items(): + model, field = details + query = { field: params.pop(assoc) } - for assoc, details in required_assocs.items(): - model, field = details - query = { field: params.pop(assoc) } + params[assoc] = model.objects.get(**query) - params[assoc] = model.objects.get(**query) + for assoc, details in optional_assocs.items(): + if assoc in params: + model, field = details + query = { field: params.pop(assoc) } - for assoc, details in optional_assocs.items(): - if assoc in params: - model, field = details - query = { field: params.pop(assoc) } + params[assoc] = model.objects.get(**query) - params[assoc] = model.objects.get(**query) + virtual_machine, created = VirtualMachine.objects.get_or_create(**params) - 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 + ) - 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) - virtual_machine.custom_field_values.add(custom_field_value) - - print("πŸ–₯️ Created virtual machine", virtual_machine.name) + print("πŸ–₯️ Created virtual machine", virtual_machine.name) diff --git a/startup_scripts/240_virtualization_interfaces.py b/startup_scripts/240_virtualization_interfaces.py index 8d2ca96..e5d75f7 100644 --- a/startup_scripts/240_virtualization_interfaces.py +++ b/startup_scripts/240_virtualization_interfaces.py @@ -1,45 +1,39 @@ from dcim.models import Interface from virtualization.models import VirtualMachine from extras.models import CustomField, CustomFieldValue -from ruamel.yaml import YAML - -from pathlib import Path +from startup_script_utils import load_yaml import sys -file = Path('/opt/netbox/initializers/virtualization_interfaces.yml') -if not file.is_file(): +interfaces = load_yaml('/opt/netbox/initializers/virtualization_interfaces.yml') + +if interfaces is None: sys.exit() -with file.open('r') as stream: - yaml = YAML(typ='safe') - interfaces = yaml.load(stream) +required_assocs = { + 'virtual_machine': (VirtualMachine, 'name') +} - required_assocs = { - 'virtual_machine': (VirtualMachine, 'name') - } +for params in interfaces: + custom_fields = params.pop('custom_fields', None) - if interfaces is not None: - for params in interfaces: - custom_fields = params.pop('custom_fields', None) + for assoc, details in required_assocs.items(): + model, field = details + query = { field: params.pop(assoc) } - for assoc, details in required_assocs.items(): - model, field = details - query = { field: params.pop(assoc) } + params[assoc] = model.objects.get(**query) - params[assoc] = model.objects.get(**query) + interface, created = Interface.objects.get_or_create(**params) - 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 + ) - 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) - interface.custom_field_values.add(custom_field_value) - - print("🧷 Created interface", interface.name, interface.virtual_machine.name) + 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 ec30b5c..51f885b 100644 --- a/startup_scripts/250_dcim_interfaces.py +++ b/startup_scripts/250_dcim_interfaces.py @@ -1,44 +1,38 @@ from dcim.models import Interface, Device from extras.models import CustomField, CustomFieldValue -from ruamel.yaml import YAML - -from pathlib import Path +from startup_script_utils import load_yaml import sys -file = Path('/opt/netbox/initializers/dcim_interfaces.yml') -if not file.is_file(): +interfaces= load_yaml('/opt/netbox/initializers/dcim_interfaces.yml') + +if interfaces is None: sys.exit() -with file.open('r') as stream: - yaml = YAML(typ='safe') - interfaces = yaml.load(stream) +required_assocs = { + 'device': (Device, 'name') +} - required_assocs = { - 'device': (Device, 'name') - } +for params in interfaces: + custom_fields = params.pop('custom_fields', None) - if interfaces is not None: - for params in interfaces: - custom_fields = params.pop('custom_fields', None) + for assoc, details in required_assocs.items(): + model, field = details + query = { field: params.pop(assoc) } - for assoc, details in required_assocs.items(): - model, field = details - query = { field: params.pop(assoc) } + params[assoc] = model.objects.get(**query) - params[assoc] = model.objects.get(**query) + interface, created = Interface.objects.get_or_create(**params) - 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 + ) - 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) - interface.custom_field_values.add(custom_field_value) - - print("🧷 Created interface", interface.name, interface.device.name) + 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 d109a36..96ec4b0 100644 --- a/startup_scripts/260_ip_addresses.py +++ b/startup_scripts/260_ip_addresses.py @@ -3,63 +3,58 @@ from dcim.models import Device, Interface from virtualization.models import VirtualMachine from tenancy.models import Tenant from extras.models import CustomField, CustomFieldValue -from ruamel.yaml import YAML from netaddr import IPNetwork -from pathlib import Path +from startup_script_utils import load_yaml import sys -file = Path('/opt/netbox/initializers/ip_addresses.yml') -if not file.is_file(): +ip_addresses = load_yaml('/opt/netbox/initializers/ip_addresses.yml') + +if ip_addresses is None: sys.exit() -with file.open('r') as stream: - yaml = YAML(typ='safe') - ip_addresses = yaml.load(stream) +optional_assocs = { + 'tenant': (Tenant, 'name'), + 'vrf': (VRF, 'name'), + 'interface': (Interface, 'name') +} - optional_assocs = { - 'tenant': (Tenant, 'name'), - 'vrf': (VRF, 'name'), - 'interface': (Interface, 'name') - } +for params in ip_addresses: + vm = params.pop('virtual_machine', None) + device = params.pop('device', None) + custom_fields = params.pop('custom_fields', None) + params['address'] = IPNetwork(params['address']) - if ip_addresses is not None: - for params in ip_addresses: - 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: + print("IP Address can only specify one of the following: virtual_machine or device.") + sys.exit() - if vm and device: - print("IP Address can only specify one of the following: virtual_machine or device.") - sys.exit() + for assoc, details in optional_assocs.items(): + if assoc in params: + model, field = details + if assoc == 'interface': + if vm: + vm_id = VirtualMachine.objects.get(name=vm).id + query = { field: params.pop(assoc), "virtual_machine_id": vm_id } + elif device: + dev_id = Device.objects.get(name=device).id + query = { field: params.pop(assoc), "device_id": dev_id } + else: + query = { field: params.pop(assoc) } + params[assoc] = model.objects.get(**query) - for assoc, details in optional_assocs.items(): - if assoc in params: - model, field = details - if assoc == 'interface': - if vm: - vm_id = VirtualMachine.objects.get(name=vm).id - query = { field: params.pop(assoc), "virtual_machine_id": vm_id } - elif device: - dev_id = Device.objects.get(name=device).id - query = { field: params.pop(assoc), "device_id": dev_id } - else: - query = { field: params.pop(assoc) } - params[assoc] = model.objects.get(**query) + ip_address, created = IPAddress.objects.get_or_create(**params) - 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 + ) - 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) - ip_address.custom_field_values.add(custom_field_value) - - print("🧬 Created IP Address", ip_address.address) + 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 new file mode 100644 index 0000000..7a37f57 --- /dev/null +++ b/startup_scripts/startup_script_utils/__init__.py @@ -0,0 +1 @@ +from .load_yaml import load_yaml diff --git a/startup_scripts/startup_script_utils/load_yaml.py b/startup_scripts/startup_script_utils/load_yaml.py new file mode 100644 index 0000000..4c16816 --- /dev/null +++ b/startup_scripts/startup_script_utils/load_yaml.py @@ -0,0 +1,10 @@ +from ruamel.yaml import YAML +from pathlib import Path + +def load_yaml(yaml_file: str): + yf = Path(yaml_file) + if not yf.is_file(): + return None + with yf.open("r") as stream: + yaml = YAML(typ="safe") + return yaml.load(stream) From 8d3bd48c7a2bcafab679238b69680aff4cd376d5 Mon Sep 17 00:00:00 2001 From: Tobias Genannt Date: Wed, 12 Feb 2020 08:34:01 +0100 Subject: [PATCH 02/10] Permission handling in external function This move the setting of user and group permissions in the startup script to its own function. --- startup_scripts/000_users.py | 24 ++++--------------- startup_scripts/010_groups.py | 20 ++++------------ .../startup_script_utils/__init__.py | 1 + .../startup_script_utils/permissions.py | 18 ++++++++++++++ 4 files changed, 28 insertions(+), 35 deletions(-) create mode 100644 startup_scripts/startup_script_utils/permissions.py diff --git a/startup_scripts/000_users.py b/startup_scripts/000_users.py index 534a793..6605424 100644 --- a/startup_scripts/000_users.py +++ b/startup_scripts/000_users.py @@ -1,11 +1,10 @@ -from django.contrib.auth.models import Permission, Group, User -from users.models import Token - -from startup_script_utils import load_yaml import sys -users = load_yaml('/opt/netbox/initializers/users.yml') +from django.contrib.auth.models import Group, User +from startup_script_utils import load_yaml, set_permissions +from users.models import Token +users = load_yaml('/opt/netbox/initializers/users.yml') if users is None: sys.exit() @@ -21,17 +20,4 @@ for username, user_details in users.items(): Token.objects.create(user=user, key=user_details['api_token']) yaml_permissions = user_details.get('permissions', []) - if yaml_permissions: - subject = user.user_permissions - subject.clear() - for yaml_permission in yaml_permissions: - if '*' in yaml_permission: - permission_filter = '^' + yaml_permission.replace('*','.*') + '$' - permissions = Permission.objects.filter(codename__iregex=permission_filter) - print(" ⚿ Granting", permissions.count(), "permissions matching '" + yaml_permission + "'") - else: - permissions = Permission.objects.filter(codename=yaml_permission) - print(" ⚿ Granting permission", yaml_permission) - - for permission in permissions: - subject.add(permission) + set_permissions(user.user_permissions, yaml_permissions) diff --git a/startup_scripts/010_groups.py b/startup_scripts/010_groups.py index bad2a10..951ca96 100644 --- a/startup_scripts/010_groups.py +++ b/startup_scripts/010_groups.py @@ -1,7 +1,8 @@ -from django.contrib.auth.models import Permission, Group, User -from startup_script_utils import load_yaml import sys +from django.contrib.auth.models import Group, User +from startup_script_utils import load_yaml, set_permissions + groups = load_yaml('/opt/netbox/initializers/groups.yml') if groups is None: sys.exit() @@ -19,17 +20,4 @@ for groupname, group_details in groups.items(): user.groups.add(group) yaml_permissions = group_details.get('permissions', []) - if yaml_permissions: - subject = group.permissions - subject.clear() - for yaml_permission in yaml_permissions: - if '*' in yaml_permission: - permission_filter = '^' + yaml_permission.replace('*','.*') + '$' - permissions = Permission.objects.filter(codename__iregex=permission_filter) - print(" ⚿ Granting", permissions.count(), "permissions matching '" + yaml_permission + "'") - else: - permissions = Permission.objects.filter(codename=yaml_permission) - print(" ⚿ Granting permission", yaml_permission) - - for permission in permissions: - subject.add(permission) + set_permissions(group.permissions, yaml_permissions) diff --git a/startup_scripts/startup_script_utils/__init__.py b/startup_scripts/startup_script_utils/__init__.py index 7a37f57..c3cf28f 100644 --- a/startup_scripts/startup_script_utils/__init__.py +++ b/startup_scripts/startup_script_utils/__init__.py @@ -1 +1,2 @@ from .load_yaml import load_yaml +from .permissions import set_permissions diff --git a/startup_scripts/startup_script_utils/permissions.py b/startup_scripts/startup_script_utils/permissions.py new file mode 100644 index 0000000..add83ee --- /dev/null +++ b/startup_scripts/startup_script_utils/permissions.py @@ -0,0 +1,18 @@ +from django.contrib.auth.models import Permission + + +def set_permissions(subject, permission_filters): + if subject is None or permission_filters is None: + return + subject.clear() + for permission_filter in permission_filters: + if "*" in permission_filter: + permission_filter_regex = "^" + permission_filter.replace("*", ".*") + "$" + permissions = Permission.objects.filter(codename__iregex=permission_filter_regex) + print(" ⚿ Granting", permissions.count(), "permissions matching '" + permission_filter + "'") + else: + permissions = Permission.objects.filter(codename=permission_filter) + print(" ⚿ Granting permission", permission_filter) + + for permission in permissions: + subject.add(permission) From 355ebadd108f51708b692b4d232dd25f010e9829 Mon Sep 17 00:00:00 2001 From: Tobias Genannt Date: Fri, 14 Feb 2020 12:31:41 +0100 Subject: [PATCH 03/10] Fixed configuration inconsistency for Redis cache In the configuration.py we use database 1 as the default but in the .env file we used 0. This sets both values to 1 as the default. --- env/netbox.env | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/env/netbox.env b/env/netbox.env index aaa7482..008196e 100644 --- a/env/netbox.env +++ b/env/netbox.env @@ -20,7 +20,7 @@ REDIS_DATABASE=0 REDIS_SSL=false REDIS_CACHE_HOST=redis-cache REDIS_CACHE_PASSWORD=t4Ph722qJ5QHeQ1qfu36 -REDIS_CACHE_DATABASE=0 +REDIS_CACHE_DATABASE=1 REDIS_CACHE_SSL=false SECRET_KEY=r8OwDznj!!dci#P9ghmRfdu1Ysxm0AiPeDCQhKE+N_rClfWNj SKIP_STARTUP_SCRIPTS=false From b9c44b85ccaac94a8522034634357385c8b9751d Mon Sep 17 00:00:00 2001 From: Tobias Genannt Date: Fri, 14 Feb 2020 12:33:28 +0100 Subject: [PATCH 04/10] The Redis cache container was using the wrong env Our docker-compose.yml pointed the env file for the Redis cache to the wrong file. Now the Redis cache password between the netbox.env and redis-cache.env match. --- docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index 3806480..9272b91 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -57,7 +57,7 @@ services: - sh - -c # this is to evaluate the $REDIS_PASSWORD from the env - redis-server --requirepass $$REDIS_PASSWORD ## $$ because of docker-compose - env_file: env/redis.env + env_file: env/redis-cache.env volumes: netbox-static-files: driver: local From 20c7461c7b3ac84fd4a09b8cd74de7baa2d0df9f Mon Sep 17 00:00:00 2001 From: Tobias Genannt Date: Fri, 14 Feb 2020 12:35:47 +0100 Subject: [PATCH 05/10] Fixed test failures because of missing cache MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When running tests with Β΄test.shΒ΄ some errors where logged because now redis-cache instance was running. --- docker-compose.test.yml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/docker-compose.test.yml b/docker-compose.test.yml index 9420617..5ccd92a 100644 --- a/docker-compose.test.yml +++ b/docker-compose.test.yml @@ -5,6 +5,7 @@ services: depends_on: - postgres - redis + - redis-cache env_file: env/netbox.env user: '101' volumes: @@ -34,8 +35,15 @@ services: command: - sh - -c # this is to evaluate the $REDIS_PASSWORD from the env - - redis-server --requirepass $$REDIS_PASSWORD ## $$ because of docker-compose + - redis-server --appendonly yes --requirepass $$REDIS_PASSWORD ## $$ because of docker-compose env_file: env/redis.env + redis-cache: + image: redis:5-alpine + command: + - sh + - -c # this is to evaluate the $REDIS_PASSWORD from the env + - redis-server --requirepass $$REDIS_PASSWORD ## $$ because of docker-compose + env_file: env/redis-cache.env volumes: netbox-static-files: driver: local From 5f90ad8c56a546eccd4713aaf7c09ba37ae0c7c1 Mon Sep 17 00:00:00 2001 From: Alexander GITTINGS Date: Wed, 11 Mar 2020 10:40:10 +0000 Subject: [PATCH 06/10] inlude docs in container --- .dockerignore | 1 - 1 file changed, 1 deletion(-) diff --git a/.dockerignore b/.dockerignore index bdb18c4..00fd8b8 100644 --- a/.dockerignore +++ b/.dockerignore @@ -7,5 +7,4 @@ build* docker-compose.override.yml .netbox/.git* .netbox/.travis.yml -.netbox/docs .netbox/scripts From 7f489aa11d717c8cc584d5d34e3d3c6790b5fd0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20M=C3=A4der?= Date: Tue, 17 Mar 2020 16:07:15 +0100 Subject: [PATCH 07/10] Improved the README - adjust the _Quick Start_ section to match the instructions in the _Getting Started_ guide on our wiki - Adds a new _Getting Help_ section - Improvements in explaining the tags of the published Docker image --- README.md | 80 +++++++++++++++++++++++++++---------------------------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/README.md b/README.md index 5f12bfc..1919dfa 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,8 @@ [The Github repository](netbox-docker-github) houses the components needed to build Netbox as a Docker container. Images are built using this code and are released to [Docker Hub][netbox-dockerhub] once a day. -Do you have any questions? Before opening an issue on Github, please join the [Network To Code][ntc-slack] Slack and ask for help in our [`#netbox-docker`][netbox-docker-slack] channel. +Do you have any questions? +Before opening an issue on Github, please join the [Network To Code][ntc-slack] Slack and ask for help in our [`#netbox-docker`][netbox-docker-slack] channel. [github-stargazers]: https://github.com/netbox-community/netbox-docker/stargazers [github-release]: https://github.com/netbox-community/netbox-docker/releases @@ -25,16 +26,18 @@ Do you have any questions? Before opening an issue on Github, please join the [N ## Docker Tags -* `vX.Y.Z`: Release builds, built from [releases of Netbox][netbox-releases]. -* `latest`: Release builds, built from [`master` branch of Netbox][netbox-master]. -* `snapshot`: Pre-release builds, built from the [`develop` branch of Netbox][netbox-develop]. -* `develop-X.Y`: Pre-release builds, built from the corresponding [branch of Netbox][netbox-branches]. +* `vX.Y.Z`: These are release builds, automatically built from [the corresponding releases of Netbox][netbox-releases]. +* `latest`: These are release builds, automatically built from [the `master` branch of Netbox][netbox-master]. +* `snapshot`: These are pre-release builds, automatically built from the [`develop` branch of Netbox][netbox-develop]. +* `develop-X.Y`: These are pre-release builds, automatically built from the corresponding [branch of Netbox][netbox-branches]. -Then there is currently one extra tags for each of the above labels: +Then there is currently one extra tags for each of the above tags: * `-ldap`: Contains additional dependencies and configurations for connecting Netbox to an LDAP directroy. [Learn more about that in our wiki][netbox-docker-ldap]. +New images are built and published automatically every ~24h. + [netbox-releases]: https://github.com/netbox-community/netbox/releases [netbox-master]: https://github.com/netbox-community/netbox/tree/master [netbox-develop]: https://github.com/netbox-community/netbox/tree/develop @@ -43,42 +46,50 @@ Then there is currently one extra tags for each of the above labels: ## Quickstart -To get Netbox up and running in Docker: +To get Netbox Docker up and running run the following commands. +There is a more complete [_Getting Started_ guide on our wiki][wiki-getting-started] which explains every step. ```bash git clone -b release https://github.com/netbox-community/netbox-docker.git cd netbox-docker +tee netbox-docker.override.yml </dev/null & -``` - -Alternatively, use something like [Reception][docker-reception] to connect to _docker-compose_ projects. - +The whole application will be available after a few minutes. +Open the URL `http://0.0.0.0:8000/` in a web-browser. +You should see the Netbox homepage. +In the top-right corner you can login. The default credentials are: * Username: **admin** * Password: **admin** * API Token: **0123456789abcdef0123456789abcdef01234567** -There is a more complete [Getting Started guide on our Wiki][wiki-getting-started]. - [wiki-getting-started]: https://github.com/netbox-community/netbox-docker/wiki/Getting-Started [docker-reception]: https://github.com/nxt-engineering/reception +## Documentation + +Please refer [to our wiki on Github][netbox-docker-wiki] for further information on how to use this Netbox Docker image properly. +It covers advanced topics such as using secret files, deployment to Kubernetes as well as NAPALM and LDAP configuration. + +[netbox-docker-wiki]: https://github.com/netbox-community/netbox-docker/wiki/ + +## Getting Help + +Please join [our Slack channel `#netbox-docker`][netbox-docker-slack] on the [Network To Code Slack][ntc-slack]. +It's free to use and there are almost always people online that can help. + +If you need help with using Netbox or developing for it or against it's API you may find the `#netbox` channel on the same Slack instance very helpful. + ## Dependencies This project relies only on *Docker* and *docker-compose* meeting these requirements: @@ -88,17 +99,10 @@ This project relies only on *Docker* and *docker-compose* meeting these requirem To check the version installed on your system run `docker --version` and `docker-compose --version`. -## Documentation +## Use a Specific Netbox Version -Please refer [to our wiki on Github][netbox-docker-wiki] for further information on how to use this Netbox Docker image properly. -It covers advanced topics such as using secret files, deployment to Kubernetes as well as NAPALM and LDAP configuration. - -[netbox-docker-wiki]: https://github.com/netbox-community/netbox-docker/wiki/ - -## Netbox Version - -The `docker-compose.yml` file is prepared to run a specific version of Netbox. -To use this feature, set the environment-variable `VERSION` before launching `docker-compose`, as shown below. +The `docker-compose.yml` file is prepared to run a specific version of Netbox, instead of `latest`. +To use this feature, set and export the environment-variable `VERSION` before launching `docker-compose`, as shown below. `VERSION` may be set to the name of [any tag of the `netboxcommunity/netbox` Docker image on Docker Hub][netbox-dockerhub]. @@ -141,10 +145,6 @@ For more details on custom builds [consult our wiki][netbox-docker-wiki-build]. [netbox-docker-wiki-build]: https://github.com/netbox-community/netbox-docker/wiki/Build -### Pre-made Docker Images - -New Docker images are built and published every 24h. - ## Tests We have a test script. From 8bc77c1bc08fed47a238869a60f7631d8f4ffcfa Mon Sep 17 00:00:00 2001 From: erin Date: Fri, 27 Mar 2020 21:30:26 -0700 Subject: [PATCH 08/10] Add RELEASE_CHECK_URL to configuration and default env. --- configuration/configuration.py | 9 +++++++++ env/netbox.env | 1 + 2 files changed, 10 insertions(+) diff --git a/configuration/configuration.py b/configuration/configuration.py index af121d9..94534ae 100644 --- a/configuration/configuration.py +++ b/configuration/configuration.py @@ -170,6 +170,15 @@ PAGINATE_COUNT = int(os.environ.get('PAGINATE_COUNT', 50)) # When determining the primary IP address for a device, IPv6 is preferred over IPv4 by default. Set this to True to # prefer IPv4 instead. PREFER_IPV4 = os.environ.get('PREFER_IPV4', 'False').lower() == 'true' + +# This determines how often the GitHub API is called to check the latest release of NetBox. Must be at least 1 hour. +RELEASE_CHECK_TIMEOUT = os.environ.get('RELEASE_CHECK_TIMEOUT', 24 * 3600) + +# This repository is used to check whether there is a new release of NetBox available. Set to None to disable the +# version check or use the URL below to check for release in the official NetBox repository. +# https://api.github.com/repos/netbox-community/netbox/releases +RELEASE_CHECK_URL = os.environ.get('RELEASE_CHECK_URL', None) + # The file path where custom reports will be stored. A trailing slash is not needed. Note that the default value of # this setting is derived from the installed location. REPORTS_ROOT = os.environ.get('REPORTS_ROOT', '/etc/netbox/reports') diff --git a/env/netbox.env b/env/netbox.env index aaa7482..7f5e849 100644 --- a/env/netbox.env +++ b/env/netbox.env @@ -30,3 +30,4 @@ SUPERUSER_EMAIL=admin@example.com SUPERUSER_PASSWORD=admin SUPERUSER_API_TOKEN=0123456789abcdef0123456789abcdef01234567 WEBHOOKS_ENABLED=true +RELEASE_CHECK_URL=https://api.github.com/repos/netbox-community/netbox/releases From f87ffe7c3359638283169437dd1d8b4f35a6b548 Mon Sep 17 00:00:00 2001 From: nepeat Date: Sun, 29 Mar 2020 01:40:08 -0700 Subject: [PATCH 09/10] Update configuration/configuration.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Christian MΓ€der --- configuration/configuration.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configuration/configuration.py b/configuration/configuration.py index 94534ae..af5d3ba 100644 --- a/configuration/configuration.py +++ b/configuration/configuration.py @@ -171,7 +171,7 @@ PAGINATE_COUNT = int(os.environ.get('PAGINATE_COUNT', 50)) # prefer IPv4 instead. PREFER_IPV4 = os.environ.get('PREFER_IPV4', 'False').lower() == 'true' -# This determines how often the GitHub API is called to check the latest release of NetBox. Must be at least 1 hour. +# This determines how often the GitHub API is called to check the latest release of NetBox in seconds. Must be at least 1 hour. RELEASE_CHECK_TIMEOUT = os.environ.get('RELEASE_CHECK_TIMEOUT', 24 * 3600) # This repository is used to check whether there is a new release of NetBox available. Set to None to disable the From 23e2da52f885bbce4848830a2e8f7d120a3aba44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20M=C3=A4der?= Date: Mon, 30 Mar 2020 15:15:30 +0200 Subject: [PATCH 10/10] Preparation for 0.23.0 --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 2157409..ca222b7 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.22.0 +0.23.0