diff --git a/README.md b/README.md index 36faed4..01dc87f 100644 --- a/README.md +++ b/README.md @@ -161,7 +161,7 @@ if created: #### Initializers -Initializers are built-in startup scripts for defining NetBox custom fields, groups and users. +Initializers are built-in startup scripts for defining NetBox custom fields, groups, users and many other resources. All you need to do is to mount you own `initializers` folder ([see `docker-compose.yml`][netbox-docker-compose]). Look at the [`initializers` folder][netbox-docker-initializers] to learn how the files must look like. @@ -325,10 +325,10 @@ Then make sure that the `redis` container and at least one `netbox-worker` are r # check the container status $ docker-compose ps -Name Command State Ports +Name Command State Ports -------------------------------------------------------------------------------------------------------- -netbox-docker_netbox-worker_1 /opt/netbox/docker-entrypo ... Up -netbox-docker_netbox_1 /opt/netbox/docker-entrypo ... Up +netbox-docker_netbox-worker_1 /opt/netbox/docker-entrypo ... Up +netbox-docker_netbox_1 /opt/netbox/docker-entrypo ... Up netbox-docker_nginx_1 nginx -c /etc/netbox-nginx ... Up 80/tcp, 0.0.0.0:32776->8080/tcp netbox-docker_postgres_1 docker-entrypoint.sh postgres Up 5432/tcp netbox-docker_redis_1 docker-entrypoint.sh redis ... Up 6379/tcp diff --git a/initializers/custom_fields.yml b/initializers/custom_fields.yml index e70d6e5..0b6472a 100644 --- a/initializers/custom_fields.yml +++ b/initializers/custom_fields.yml @@ -7,6 +7,8 @@ # on_objects: # - dcim.models.Device # - dcim.models.Rack +# - dcim.models.Site +# - dcim.models.DeviceType # - ipam.models.IPAddress # - ipam.models.Prefix # - tenancy.models.Tenant diff --git a/initializers/device_roles.yml b/initializers/device_roles.yml new file mode 100644 index 0000000..ee4234f --- /dev/null +++ b/initializers/device_roles.yml @@ -0,0 +1,15 @@ +# - name: switch +# slug: switch +# color: Grey +# - name: router +# slug: router +# color: Cyan +# - name: load-balancer +# slug: load-balancer +# color: Red +# - name: server +# slug: server +# color: Blue +# - name: patchpanel +# slug: patchpanel +# color: Black diff --git a/initializers/device_types.yml b/initializers/device_types.yml new file mode 100644 index 0000000..d0e4c36 --- /dev/null +++ b/initializers/device_types.yml @@ -0,0 +1,23 @@ +# - model: Model 1 +# manufacturer: Manufacturer 1 +# slug: model-1 +# u_height: 2 +# custom_fields: +# text_field: Description +# - model: Model 2 +# manufacturer: Manufacturer 1 +# slug: model-2 +# custom_fields: +# text_field: Description +# - model: Model 3 +# manufacturer: Manufacturer 1 +# slug: model-3 +# is_full_depth: false +# u_height: 0 +# custom_fields: +# text_field: Description +# - model: Other +# manufacturer: NoName +# slug: other +# custom_fields: +# text_field: Description diff --git a/initializers/devices.yml b/initializers/devices.yml new file mode 100644 index 0000000..0beb6f2 --- /dev/null +++ b/initializers/devices.yml @@ -0,0 +1,27 @@ +# - name: server01 +# device_role: server +# device_type: Other +# site: AMS 1 +# rack: rack-01 +# face: Front +# position: 1 +# custom_fields: +# text_field: Description +# - name: server02 +# device_role: server +# device_type: Other +# site: AMS 2 +# rack: rack-02 +# face: Front +# position: 2 +# custom_fields: +# text_field: Description +# - name: server03 +# device_role: server +# device_type: Other +# site: SING 1 +# rack: rack-03 +# face: Front +# position: 3 +# custom_fields: +# text_field: Description diff --git a/initializers/manufacturers.yml b/initializers/manufacturers.yml new file mode 100644 index 0000000..d737a5f --- /dev/null +++ b/initializers/manufacturers.yml @@ -0,0 +1,6 @@ +# - name: Manufacturer 1 +# slug: manufacturer-1 +# - name: Manufacturer 2 +# slug: manufacturer-2 +# - name: NoName +# slug: noname diff --git a/initializers/platforms.yml b/initializers/platforms.yml new file mode 100644 index 0000000..bc9926a --- /dev/null +++ b/initializers/platforms.yml @@ -0,0 +1,19 @@ +# # Allowed rpc clients are: juniper-junos, cisco-ios, opengear +# - name: Platform 1 +# slug: platform-1 +# manufacturer: Manufacturer 1 +# napalm_driver: driver1 +# napalm_args: "{'arg1': 'value1', 'arg2': 'value2'}" +# rpc_client: juniper-junos +# - name: Platform 2 +# slug: platform-2 +# manufacturer: Manufacturer 2 +# napalm_driver: driver2 +# napalm_args: "{'arg1': 'value1', 'arg2': 'value2'}" +# rpc_client: opengear +# - name: Platform 3 +# slug: platform-3 +# manufacturer: NoName +# napalm_driver: driver3 +# napalm_args: "{'arg1': 'value1', 'arg2': 'value2'}" +# rpc_client: juniper-junos diff --git a/initializers/rack_roles.yml b/initializers/rack_roles.yml new file mode 100644 index 0000000..e8d1e3e --- /dev/null +++ b/initializers/rack_roles.yml @@ -0,0 +1,12 @@ +# - name: Role 1 +# slug: role-1 +# color: Pink +# - name: Role 2 +# slug: role-2 +# color: Cyan +# - name: Role 3 +# slug: role-3 +# color: Grey +# - name: Role 4 +# slug: role-4 +# color: Teal diff --git a/initializers/racks.yml b/initializers/racks.yml new file mode 100644 index 0000000..9a71743 --- /dev/null +++ b/initializers/racks.yml @@ -0,0 +1,24 @@ +# - site: AMS 1 +# name: rack-01 +# role: Role 1 +# type: 4-post cabinet +# width: 19 inches +# u_height: 47 +# custom_fields: +# text_field: Description +# - site: AMS 2 +# name: rack-02 +# role: Role 2 +# type: 4-post cabinet +# width: 19 inches +# u_height: 47 +# custom_fields: +# text_field: Description +# - site: SING 1 +# name: rack-03 +# role: Role 3 +# type: 4-post cabinet +# width: 19 inches +# u_height: 47 +# custom_fields: +# text_field: Description diff --git a/initializers/regions.yml b/initializers/regions.yml new file mode 100644 index 0000000..1353e05 --- /dev/null +++ b/initializers/regions.yml @@ -0,0 +1,10 @@ +# - name: Singapore +# slug: singapore +# - name: Amsterdam +# slug: amsterdam +# - name: Downtown +# slug: downtown +# parent: Amsterdam +# - name: Suburbs +# slug: suburbs +# parent: Amsterdam diff --git a/initializers/sites.yml b/initializers/sites.yml new file mode 100644 index 0000000..2381f99 --- /dev/null +++ b/initializers/sites.yml @@ -0,0 +1,32 @@ +# - name: AMS 1 +# slug: ams1 +# region: Downtown +# status: 1 +# facility: Amsterdam 1 +# asn: 12345 +# custom_fields: +# text_field: Description +# - name: AMS 2 +# slug: ams2 +# region: Downtown +# status: 1 +# facility: Amsterdam 2 +# asn: 54321 +# custom_fields: +# text_field: Description +# - name: AMS 3 +# slug: ams3 +# region: Suburbs +# status: 1 +# facility: Amsterdam 3 +# asn: 67890 +# custom_fields: +# text_field: Description +# - name: SING 1 +# slug: sing1 +# region: Singapore +# status: 1 +# facility: Singapore 1 +# asn: 09876 +# custom_fields: +# text_field: Description diff --git a/startup_scripts/00_users.py b/startup_scripts/000_users.py similarity index 100% rename from startup_scripts/00_users.py rename to startup_scripts/000_users.py diff --git a/startup_scripts/10_groups.py b/startup_scripts/010_groups.py similarity index 100% rename from startup_scripts/10_groups.py rename to startup_scripts/010_groups.py diff --git a/startup_scripts/20_custom_fields.py b/startup_scripts/020_custom_fields.py similarity index 100% rename from startup_scripts/20_custom_fields.py rename to startup_scripts/020_custom_fields.py diff --git a/startup_scripts/030_regions.py b/startup_scripts/030_regions.py new file mode 100644 index 0000000..eed0cac --- /dev/null +++ b/startup_scripts/030_regions.py @@ -0,0 +1,25 @@ +from dcim.models import Region +from ruamel.yaml import YAML + +with open('/opt/netbox/initializers/regions.yml', 'r') as stream: + yaml=YAML(typ='safe') + regions = yaml.load(stream) + + optional_assocs = { + 'parent': (Region, 'name') + } + + 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) } + + params[assoc] = model.objects.get(**query) + + region, created = Region.objects.get_or_create(**params) + + if created: + print("🌐 Created region", region.name) diff --git a/startup_scripts/040_sites.py b/startup_scripts/040_sites.py new file mode 100644 index 0000000..b974a82 --- /dev/null +++ b/startup_scripts/040_sites.py @@ -0,0 +1,40 @@ +from dcim.models import Region, Site +from extras.models import CustomField, CustomFieldValue +from tenancy.models import Tenant +from ruamel.yaml import YAML + +with open('/opt/netbox/initializers/sites.yml', 'r') as stream: + yaml = YAML(typ='safe') + sites = yaml.load(stream) + + optional_assocs = { + 'region': (Region, 'name'), + 'tenant': (Tenant, 'name') + } + + 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) } + + params[assoc] = model.objects.get(**query) + + 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) + + print("📍 Created site", site.name) diff --git a/startup_scripts/050_manufacturers.py b/startup_scripts/050_manufacturers.py new file mode 100644 index 0000000..6f43fd1 --- /dev/null +++ b/startup_scripts/050_manufacturers.py @@ -0,0 +1,13 @@ +from dcim.models import Manufacturer +from ruamel.yaml import YAML + +with open('/opt/netbox/initializers/manufacturers.yml', 'r') as stream: + yaml = YAML(typ='safe') + manufacturers = yaml.load(stream) + + if manufacturers is not None: + for params in manufacturers: + manufacturer, created = Manufacturer.objects.get_or_create(**params) + + if created: + print("🏭 Created Manufacturer", manufacturer.name) diff --git a/startup_scripts/060_device_types.py b/startup_scripts/060_device_types.py new file mode 100644 index 0000000..e663172 --- /dev/null +++ b/startup_scripts/060_device_types.py @@ -0,0 +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 + +with open('/opt/netbox/initializers/device_types.yml', 'r') as stream: + yaml = YAML(typ='safe') + device_types = yaml.load(stream) + + required_assocs = { + 'manufacturer': (Manufacturer, 'name') + } + + optional_assocs = { + 'region': (Region, 'name'), + 'tenant': (Tenant, 'name') + } + + 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) } + + 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) } + + params[assoc] = model.objects.get(**query) + + 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) + + 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 new file mode 100644 index 0000000..e4023e3 --- /dev/null +++ b/startup_scripts/070_rack_roles.py @@ -0,0 +1,21 @@ +from dcim.models import RackRole +from ruamel.yaml import YAML +from utilities.forms import COLOR_CHOICES + +with open('/opt/netbox/initializers/rack_roles.yml', 'r') as stream: + yaml=YAML(typ='safe') + rack_roles = yaml.load(stream) + + 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] + + rack_role, created = RackRole.objects.get_or_create(**params) + + if created: + print("🎨 Created rack role", rack_role.name) diff --git a/startup_scripts/080_racks.py b/startup_scripts/080_racks.py new file mode 100644 index 0000000..c90e20a --- /dev/null +++ b/startup_scripts/080_racks.py @@ -0,0 +1,60 @@ +from dcim.models import Site, RackRole, Rack, RackGroup +from tenancy.models import Tenant +from extras.models import CustomField, CustomFieldValue +from dcim.constants import RACK_TYPE_CHOICES, RACK_WIDTH_CHOICES +from ruamel.yaml import YAML + +with open('/opt/netbox/initializers/racks.yml', 'r') as stream: + yaml = YAML(typ='safe') + racks = yaml.load(stream) + + required_assocs = { + 'site': (Site, 'name') + } + + optional_assocs = { + 'role': (RackRole, 'name'), + 'tenant': (Tenant, 'name'), + 'group': (RackGroup, 'name') + } + + 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) } + + 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) } + + params[assoc] = model.objects.get(**query) + + for rack_type in RACK_TYPE_CHOICES: + if params['type'] in rack_type: + params['type'] = rack_type[0] + + for rack_width in RACK_WIDTH_CHOICES: + if params['width'] in rack_width: + params['width'] = rack_width[0] + + 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) + + print("🔳 Created rack", rack.site, rack.name) diff --git a/startup_scripts/090_device_roles.py b/startup_scripts/090_device_roles.py new file mode 100644 index 0000000..6a8a16b --- /dev/null +++ b/startup_scripts/090_device_roles.py @@ -0,0 +1,22 @@ +from dcim.models import DeviceRole +from ruamel.yaml import YAML +from utilities.forms import COLOR_CHOICES + +with open('/opt/netbox/initializers/device_roles.yml', 'r') as stream: + yaml=YAML(typ='safe') + device_roles = yaml.load(stream) + + if device_roles is not None: + for params in device_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] + + device_role, created = DeviceRole.objects.get_or_create(**params) + + if created: + print("🎨 Created device role", device_role.name) diff --git a/startup_scripts/100_platforms.py b/startup_scripts/100_platforms.py new file mode 100644 index 0000000..c7916b9 --- /dev/null +++ b/startup_scripts/100_platforms.py @@ -0,0 +1,25 @@ +from dcim.models import Manufacturer, Platform +from ruamel.yaml import YAML + +with open('/opt/netbox/initializers/platforms.yml', 'r') as stream: + yaml = YAML(typ='safe') + platforms = yaml.load(stream) + + optional_assocs = { + 'manufacturer': (Manufacturer, 'name'), + } + + 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) } + + params[assoc] = model.objects.get(**query) + + platform, created = Platform.objects.get_or_create(**params) + + if created: + print("💾 Created platform", platform.name) diff --git a/startup_scripts/110_devices.py b/startup_scripts/110_devices.py new file mode 100644 index 0000000..1818431 --- /dev/null +++ b/startup_scripts/110_devices.py @@ -0,0 +1,64 @@ +from dcim.models import Site, Rack, DeviceRole, DeviceType, Device, Platform +from dcim.constants import RACK_FACE_CHOICES +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 + +with open('/opt/netbox/initializers/devices.yml', 'r') as stream: + yaml = YAML(typ='safe') + devices = yaml.load(stream) + + 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') + } + + 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) } + + 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) } + + params[assoc] = model.objects.get(**query) + + if 'face' in params: + for rack_face in RACK_FACE_CHOICES: + if params['face'] in rack_face: + params['face'] = rack_face[0] + + 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) + + print("🖥️ Created device", device.name)