diff --git a/Dockerfile b/Dockerfile index 6928723..2bc5dff 100644 --- a/Dockerfile +++ b/Dockerfile @@ -41,6 +41,7 @@ RUN export DEBIAN_FRONTEND=noninteractive \ --yes -qq --no-install-recommends \ && apt-get install \ --yes -qq --no-install-recommends \ + bzip2 \ ca-certificates \ curl \ libldap-common \ @@ -70,8 +71,6 @@ COPY docker/ldap_config.docker.py /opt/netbox/netbox/netbox/ldap_config.py COPY docker/docker-entrypoint.sh /opt/netbox/docker-entrypoint.sh COPY docker/housekeeping.sh /opt/netbox/housekeeping.sh COPY docker/launch-netbox.sh /opt/netbox/launch-netbox.sh -COPY startup_scripts/ /opt/netbox/startup_scripts/ -COPY initializers/ /opt/netbox/initializers/ COPY configuration/ /etc/netbox/config/ COPY docker/nginx-unit.json /etc/unit/ diff --git a/README.md b/README.md index b76f9cf..18496a8 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ ![Docker Pulls](https://img.shields.io/docker/pulls/netboxcommunity/netbox) [![GitHub license](https://img.shields.io/github/license/netbox-community/netbox-docker)][netbox-docker-license] -[The GitHub repository](netbox-docker-github) houses the components needed to build NetBox as a container. +[The GitHub repository][netbox-docker-github] houses the components needed to build NetBox as a container. Images are built regularly using the code in that repository and are pushed to [Docker Hub][netbox-dockerhub], [Quay.io][netbox-quayio] and [GitHub Container Registry][netbox-ghcr]. Do you have any questions? @@ -16,7 +16,6 @@ please join [our Slack][netbox-docker-slack] and ask for help in the [`#netbox-d [github-stargazers]: https://github.com/netbox-community/netbox-docker/stargazers [github-release]: https://github.com/netbox-community/netbox-docker/releases -[netbox-docker-microbadger]: https://microbadger.com/images/netboxcommunity/netbox [netbox-dockerhub]: https://hub.docker.com/r/netboxcommunity/netbox/ [netbox-quayio]: https://quay.io/repository/netboxcommunity/netbox [netbox-ghcr]: https://github.com/netbox-community/netbox-docker/pkgs/container/netbox @@ -56,7 +55,6 @@ The default credentials are: * API Token: **0123456789abcdef0123456789abcdef01234567** [wiki-getting-started]: https://github.com/netbox-community/netbox-docker/wiki/Getting-Started -[docker-reception]: https://github.com/nxt-engineering/reception ## Container Image Tags @@ -92,17 +90,9 @@ For each of the above tag, there is an extra tag: This is the same version as `snapshot-a.b.c`. It always points to the latest version of _NetBox Docker_. -Then there is currently one extra tags for each of the above tags: - -* `-ldap`: - These container images contain additional dependencies and configuration files for connecting NetBox to an LDAP directory. - [Learn more about that in our wiki][netbox-docker-ldap]. - [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 -[netbox-branches]: https://github.com/netbox-community/netbox/branches -[netbox-docker-ldap]: https://github.com/netbox-community/netbox-docker/wiki/LDAP ## Documentation @@ -127,10 +117,11 @@ you may find [the `#netbox` channel][netbox-slack-channel] on the same Slack ins ## Dependencies -This project relies only on *Docker* and *docker-compose* meeting these requirements: +This project relies only on _Docker_ and _docker-compose_ meeting these requirements: -* The *Docker version* must be at least `19.03`. -* The *docker-compose version* must be at least `1.28.0`. +* The _Docker version_ must be at least `20.10.10`. +* The _containerd version_ must be at least `1.5.6`. +* The _docker-compose version_ must be at least `1.28.0`. To check the version installed on your system run `docker --version` and `docker-compose --version`. diff --git a/VERSION b/VERSION index 7ec1d6d..ccbccc3 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.1.0 +2.2.0 diff --git a/docker-compose.test.yml b/docker-compose.test.yml index 04b5939..3e4686e 100644 --- a/docker-compose.test.yml +++ b/docker-compose.test.yml @@ -11,9 +11,8 @@ services: SKIP_STARTUP_SCRIPTS: ${SKIP_STARTUP_SCRIPTS-false} user: 'unit:root' volumes: - - ./startup_scripts:/opt/netbox/startup_scripts:z,ro - - ./${INITIALIZERS_DIR-initializers}:/opt/netbox/initializers:z,ro - ./configuration:/etc/netbox/config:z,ro + - ./test-configuration/logging.py:/etc/netbox/config/logging.py:z,ro - ./reports:/etc/netbox/reports:z,ro - ./scripts:/etc/netbox/scripts:z,ro - netbox-media-files:/opt/netbox/netbox/media:z diff --git a/docker-compose.yml b/docker-compose.yml index 20a1036..029d87e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,7 +1,7 @@ version: '3.4' services: netbox: &netbox - image: netboxcommunity/netbox:${VERSION-v3.2-2.1.0} + image: netboxcommunity/netbox:${VERSION-v3.3-2.2.0} depends_on: - postgres - redis @@ -10,8 +10,6 @@ services: env_file: env/netbox.env user: 'unit:root' volumes: - - ./startup_scripts:/opt/netbox/startup_scripts:z,ro - - ./initializers:/opt/netbox/initializers:z,ro - ./configuration:/etc/netbox/config:z,ro - ./reports:/etc/netbox/reports:z,ro - ./scripts:/etc/netbox/scripts:z,ro diff --git a/docker/docker-entrypoint.sh b/docker/docker-entrypoint.sh index ed5605f..6afa1ea 100755 --- a/docker/docker-entrypoint.sh +++ b/docker/docker-entrypoint.sh @@ -80,11 +80,13 @@ END echo "πŸ’‘ Superuser Username: ${SUPERUSER_NAME}, E-Mail: ${SUPERUSER_EMAIL}" fi -# Run the startup scripts (and initializers) +# Print warning if startup scripts (and initializers) would've been run # Remove for next release if [ "$SKIP_STARTUP_SCRIPTS" == "true" ]; then - echo "↩️ Skipping startup scripts" + # Nothing to do + echo "" # Empty block not allowed else - echo "import runpy; runpy.run_path('../startup_scripts')" | ./manage.py shell --interface python + echo "⚠️⚠️⚠️ WARNING: The initializers have been moved to a plugin. See release notes." + echo "⚠️⚠️⚠️ Set environment variable 'SKIP_STARTUP_SCRIPTS' to 'true' to remove this warning." fi echo "βœ… Initialisation is done." diff --git a/env/netbox.env b/env/netbox.env index 456cac9..b2f647e 100644 --- a/env/netbox.env +++ b/env/netbox.env @@ -34,7 +34,6 @@ REDIS_PASSWORD=H733Kdjndks81 REDIS_SSL=false RELEASE_CHECK_URL=https://api.github.com/repos/netbox-community/netbox/releases SECRET_KEY=r8OwDznj!!dci#P9ghmRfdu1Ysxm0AiPeDCQhKE+N_rClfWNj -SKIP_STARTUP_SCRIPTS=false SKIP_SUPERUSER=false SUPERUSER_API_TOKEN=0123456789abcdef0123456789abcdef01234567 SUPERUSER_EMAIL=admin@example.com diff --git a/initializers/aggregates.yml b/initializers/aggregates.yml deleted file mode 100644 index b04eb7e..0000000 --- a/initializers/aggregates.yml +++ /dev/null @@ -1,7 +0,0 @@ -# - prefix: 10.0.0.0/16 -# rir: RFC1918 -# tenant: tenant1 -# - prefix: fd00:ccdd::/32 -# rir: RFC4193 ULA -# - prefix: 2001:db8::/32 -# rir: RFC3849 diff --git a/initializers/asns.yml b/initializers/asns.yml deleted file mode 100644 index f75408b..0000000 --- a/initializers/asns.yml +++ /dev/null @@ -1,7 +0,0 @@ -# - asn: 1 -# rir: RFC1918 -# tenant: tenant1 -# - asn: 2 -# rir: RFC4193 ULA -# - asn: 3 -# rir: RFC3849 diff --git a/initializers/cables.yml b/initializers/cables.yml deleted file mode 100644 index 3257643..0000000 --- a/initializers/cables.yml +++ /dev/null @@ -1,71 +0,0 @@ -# # Required parameters for termination X ('a' or 'b'): -# # -# # ``` -# # termination_x_name -> name of interface -# # termination_x_device -> name of the device interface belongs to -# # termination_x_class -> required if different than 'Interface' which is the default -# # ``` -# # -# # Supported termination classes: Interface, ConsolePort, ConsoleServerPort, FrontPort, RearPort, PowerPort, PowerOutlet -# # -# # -# # If a termination is a circuit then the required parameter is termination_x_circuit. -# # Required parameters for a circuit termination: -# # -# # ``` -# # termination_x_circuit: -# # term_side -> termination side of a circuit. Must be A or B -# # cid -> circuit ID value -# # site OR provider_network -> name of Site or ProviderNetwork respectively. If both provided, Site takes precedence -# # ``` -# # -# # If a termination is a power feed then the required parameter is termination_x_feed. -# # -# # ``` -# # termination_x_feed: -# # name -> name of the PowerFeed object -# # power_panel: -# # name -> name of the PowerPanel the PowerFeed is attached to -# # site -> name of the Site in which the PowerPanel is present -# # ``` -# # -# # Any other Cable parameters supported by Netbox are supported as the top level keys, e.g. 'type', 'status', etc. -# # -# # - termination_a_name: console -# # termination_a_device: spine -# # termination_a_class: ConsolePort -# # termination_b_name: tty9 -# # termination_b_device: console-server -# # termination_b_class: ConsoleServerPort -# # type: cat6 -# # -# - termination_a_name: to-server02 -# termination_a_device: server01 -# termination_b_name: to-server01 -# termination_b_device: server02 -# status: planned -# type: mmf - -# - termination_a_name: eth0 -# termination_a_device: server02 -# termination_b_circuit: -# term_side: A -# cid: Circuit_ID-1 -# site: AMS 1 -# type: cat6 - -# - termination_a_name: psu0 -# termination_a_device: server04 -# termination_a_class: PowerPort -# termination_b_feed: -# name: power feed 1 -# power_panel: -# name: power panel AMS 1 -# site: AMS 1 - -# - termination_a_name: outlet1 -# termination_a_device: server04 -# termination_a_class: PowerOutlet -# termination_b_name: psu1 -# termination_b_device: server04 -# termination_b_class: PowerPort diff --git a/initializers/circuit_types.yml b/initializers/circuit_types.yml deleted file mode 100644 index 0e9c356..0000000 --- a/initializers/circuit_types.yml +++ /dev/null @@ -1,6 +0,0 @@ -# - name: VPLS -# slug: vpls -# - name: MPLS -# slug: mpls -# - name: Internet -# slug: internet diff --git a/initializers/circuits.yml b/initializers/circuits.yml deleted file mode 100644 index d47b06f..0000000 --- a/initializers/circuits.yml +++ /dev/null @@ -1,7 +0,0 @@ -# - cid: Circuit_ID-1 -# provider: Provider1 -# type: Internet -# tenant: tenant1 -# - cid: Circuit_ID-2 -# provider: Provider2 -# type: MPLS diff --git a/initializers/cluster_groups.yml b/initializers/cluster_groups.yml deleted file mode 100644 index a611afa..0000000 --- a/initializers/cluster_groups.yml +++ /dev/null @@ -1,4 +0,0 @@ -# - name: Group 1 -# slug: group-1 -# - name: Group 2 -# slug: group-2 diff --git a/initializers/cluster_types.yml b/initializers/cluster_types.yml deleted file mode 100644 index 136dd1d..0000000 --- a/initializers/cluster_types.yml +++ /dev/null @@ -1,2 +0,0 @@ -# - name: Hyper-V -# slug: hyper-v diff --git a/initializers/clusters.yml b/initializers/clusters.yml deleted file mode 100644 index 4e15477..0000000 --- a/initializers/clusters.yml +++ /dev/null @@ -1,7 +0,0 @@ -# - name: cluster1 -# type: Hyper-V -# group: Group 1 -# tenant: tenant1 -# - name: cluster2 -# type: Hyper-V -# site: SING 1 diff --git a/initializers/contact_groups.yml b/initializers/contact_groups.yml deleted file mode 100644 index a3ead09..0000000 --- a/initializers/contact_groups.yml +++ /dev/null @@ -1,7 +0,0 @@ -# - name: Network-Team -# slug: network-team -# description: This is a new contact group for the Network-Team -# - name: New Contact Group -# slug: new-contact-group -# description: This is a new contact group sub under of Network-Team -# parent: Network-Team diff --git a/initializers/contact_roles.yml b/initializers/contact_roles.yml deleted file mode 100644 index 1a72e43..0000000 --- a/initializers/contact_roles.yml +++ /dev/null @@ -1,3 +0,0 @@ -# - name: New Contact Role -# slug: new-contact-role -# description: This is a new contact role description diff --git a/initializers/contacts.yml b/initializers/contacts.yml deleted file mode 100644 index 3ab41a2..0000000 --- a/initializers/contacts.yml +++ /dev/null @@ -1,20 +0,0 @@ -# - name: Lee Widget -# title: CEO of Widget Corp -# phone: 221-555-1212 -# email: widgetCEO@widgetcorp.com -# address: 1200 Nowhere Blvd, Scranton NJ, 555111 -# comments: This is a very important contact -# - name: Ali Gator -# group: Network-Team -# title: Consultant for Widget Corp -# phone: 221-555-1213 -# email: Consultant@widgetcorp.com -# address: 1200 Nowhere Blvd, Scranton NJ, 555111 -# comments: This is a very important contact -# - name: Karlchen Maier -# group: New Contact Group -# title: COO of Widget Corp -# phone: 221-555-1214 -# email: Karlchen@widgetcorp.com -# address: 1200 Nowhere Blvd, Scranton NJ, 555111 -# comments: This is a very important contact diff --git a/initializers/custom_fields.yml b/initializers/custom_fields.yml deleted file mode 100644 index beed68b..0000000 --- a/initializers/custom_fields.yml +++ /dev/null @@ -1,93 +0,0 @@ -## Possible Choices: -## type: -## - text -## - integer -## - boolean -## - date -## - url -## - select -## filter_logic: -## - disabled -## - loose -## - exact -## -## Examples: - -# text_field: -# type: text -# label: Custom Text -# description: Enter text in a text field. -# required: false -# weight: 0 -# on_objects: -# - dcim.models.Device -# - dcim.models.Rack -# - dcim.models.Site -# - dcim.models.DeviceType -# - ipam.models.IPAddress -# - ipam.models.Prefix -# - tenancy.models.Tenant -# - virtualization.models.VirtualMachine -# integer_field: -# type: integer -# label: Custom Number -# description: Enter numbers into an integer field. -# required: true -# filter_logic: loose -# weight: 10 -# on_objects: -# - tenancy.models.Tenant -# select_field: -# type: select -# label: Choose between items -# required: false -# filter_logic: exact -# weight: 30 -# default: First Item -# on_objects: -# - dcim.models.Device -# choices: -# - First Item -# - Second Item -# - Third Item -# - Fifth Item -# - Fourth Item -# select_field_legacy_format: -# type: select -# label: Choose between items -# required: false -# filter_logic: loose -# weight: 30 -# on_objects: -# - dcim.models.Device -# choices: -# - value: A # this is the deprecated format. -# - value: B # we only use it for the tests. -# - value: C # please see above for the new format. -# - value: "D like deprecated" -# weight: 999 -# - value: E -# boolean_field: -# type: boolean -# label: Yes Or No? -# required: true -# filter_logic: loose -# default: "false" # important: put "false" in quotes! -# weight: 90 -# on_objects: -# - dcim.models.Device -# url_field: -# type: url -# label: Hyperlink -# description: Link to something nice. -# required: true -# filter_logic: disabled -# on_objects: -# - tenancy.models.Tenant -# date_field: -# type: date -# label: Important Date -# required: false -# filter_logic: disabled -# on_objects: -# - dcim.models.Device diff --git a/initializers/custom_links.yml b/initializers/custom_links.yml deleted file mode 100644 index eb733ca..0000000 --- a/initializers/custom_links.yml +++ /dev/null @@ -1,21 +0,0 @@ -## Possible Choices: -## new_window: -## - True -## - False -## content_type: -## - device -## - site -## - any-other-content-type -## -## Examples: - -# - name: link_to_repo -# link_text: 'Link to Netbox Docker' -# link_url: 'https://github.com/netbox-community/netbox-docker' -# new_window: False -# content_type: device -# - name: link_to_localhost -# link_text: 'Link to localhost' -# link_url: 'http://localhost' -# new_window: True -# content_type: device diff --git a/initializers/dcim_interfaces.yml b/initializers/dcim_interfaces.yml deleted file mode 100644 index a889d46..0000000 --- a/initializers/dcim_interfaces.yml +++ /dev/null @@ -1,35 +0,0 @@ -## Possible Choices: -## type: -## - virtual -## - lag -## - 1000base-t -## - ... and many more. See for yourself: -## https://github.com/netbox-community/netbox/blob/295d4f0394b431351c0cb2c3ecc791df68c6c2fb/netbox/dcim/choices.py#L510 -## -## Examples: - -# - device: server01 -# name: ath0 -# type: 1000base-t -# lag: ae0 -# bridge: br0 -# - device: server01 -# name: ath1 -# type: 1000base-t -# parent: ath0 -# - device: server01 -# enabled: true -# type: 1000base-x-sfp -# name: to-server02 -# - device: server02 -# enabled: true -# type: 1000base-x-sfp -# name: to-server01 -# - device: server02 -# enabled: true -# type: 1000base-t -# name: eth0 -# - device: server02 -# enabled: true -# type: virtual -# name: loopback diff --git a/initializers/device_roles.yml b/initializers/device_roles.yml deleted file mode 100644 index ee4234f..0000000 --- a/initializers/device_roles.yml +++ /dev/null @@ -1,15 +0,0 @@ -# - 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 deleted file mode 100644 index b1ee130..0000000 --- a/initializers/device_types.yml +++ /dev/null @@ -1,57 +0,0 @@ -# - model: Model 1 -# manufacturer: Manufacturer 1 -# slug: model-1 -# u_height: 2 -# custom_field_data: -# text_field: Description -# - model: Model 2 -# manufacturer: Manufacturer 1 -# slug: model-2 -# custom_field_data: -# text_field: Description -# - model: Model 3 -# manufacturer: Manufacturer 1 -# slug: model-3 -# is_full_depth: false -# u_height: 0 -# custom_field_data: -# text_field: Description -# - model: Other -# manufacturer: No Name -# slug: other -# custom_field_data: -# text_field: Description -# interfaces: -# - name: eth0 -# type: 1000base-t -# mgmt_only: True -# - name: eth1 -# type: 1000base-t -# console_server_ports: -# - name_template: ttyS[1-48] -# type: rj-45 -# power_ports: -# - name_template: psu[0,1] -# type: iec-60320-c14 -# maximum_draw: 35 -# allocated_draw: 35 -# front_ports: -# - name_template: front[1,2] -# type: 8p8c -# rear_port_template: rear[0,1] -# rear_port_position_template: "[1,2]" -# rear_ports: -# - name_template: rear[0,1] -# type: 8p8c -# positions_template: "[3,2]" -# device_bays: -# - name: bay0 # both non-template and template field specified; non-template field takes precedence -# name_template: bay[0-9] -# label: test0 -# label_template: test[0-5,9,6-8] -# description: Test description -# power_outlets: -# - name_template: outlet[0,1] -# type: iec-60320-c5 -# power_port: psu0 -# feed_leg: B diff --git a/initializers/devices.yml b/initializers/devices.yml deleted file mode 100644 index 8324c36..0000000 --- a/initializers/devices.yml +++ /dev/null @@ -1,53 +0,0 @@ -## Possible Choices: -## face: -## - front -## - rear -## status: -## - offline -## - active -## - planned -## - staged -## - failed -## - inventory -## - decommissioning -## -## Examples: - -# - name: server01 -# device_role: server -# device_type: Other -# site: AMS 1 -# rack: rack-01 -# face: front -# position: 1 -# custom_field_data: -# text_field: Description -# - name: server02 -# device_role: server -# device_type: Other -# site: AMS 2 -# rack: rack-02 -# face: front -# position: 2 -# primary_ip4: 10.1.1.2/24 -# primary_ip6: 2001:db8:a000:1::2/64 -# custom_field_data: -# text_field: Description -# - name: server03 -# device_role: server -# device_type: Other -# site: SING 1 -# rack: rack-03 -# face: front -# position: 3 -# custom_field_data: -# text_field: Description -# - name: server04 -# device_role: server -# device_type: Other -# site: SING 1 -# location: cage 101 -# face: front -# position: 3 -# custom_field_data: -# text_field: Description diff --git a/initializers/groups.yml b/initializers/groups.yml deleted file mode 100644 index 15213a6..0000000 --- a/initializers/groups.yml +++ /dev/null @@ -1,9 +0,0 @@ -# applications: -# users: -# - technical_user -# readers: -# users: -# - reader -# writers: -# users: -# - writer diff --git a/initializers/ip_addresses.yml b/initializers/ip_addresses.yml deleted file mode 100644 index 6ac38e9..0000000 --- a/initializers/ip_addresses.yml +++ /dev/null @@ -1,44 +0,0 @@ -## Possible Choices: -## status: -## - active -## - reserved -## - deprecated -## - dhcp -## role: -## - loopback -## - secondary -## - anycast -## - vip -## - vrrp -## - hsrp -## - glbp -## - carp -## -## Examples: - -# - address: 10.1.1.1/24 -# device: server01 -# interface: to-server02 -# status: active -# vrf: vrf1 -# - address: 2001:db8:a000:1::1/64 -# device: server01 -# interface: to-server02 -# status: active -# vrf: vrf1 -# - address: 10.1.1.2/24 -# device: server02 -# interface: to-server01 -# status: active -# - address: 2001:db8:a000:1::2/64 -# device: server02 -# interface: to-server01 -# status: active -# - address: 10.1.1.10/24 -# description: reserved IP -# status: reserved -# tenant: tenant1 -# - address: 2001:db8:a000:1::10/64 -# description: reserved IP -# status: reserved -# tenant: tenant1 diff --git a/initializers/locations.yml b/initializers/locations.yml deleted file mode 100644 index 244fc00..0000000 --- a/initializers/locations.yml +++ /dev/null @@ -1,3 +0,0 @@ -# - name: cage 101 -# slug: cage-101 -# site: SING 1 diff --git a/initializers/manufacturers.yml b/initializers/manufacturers.yml deleted file mode 100644 index 023e8e1..0000000 --- a/initializers/manufacturers.yml +++ /dev/null @@ -1,6 +0,0 @@ -# - name: Manufacturer 1 -# slug: manufacturer-1 -# - name: Manufacturer 2 -# slug: manufacturer-2 -# - name: No Name -# slug: no-name diff --git a/initializers/object_permissions.yml b/initializers/object_permissions.yml deleted file mode 100644 index 332011f..0000000 --- a/initializers/object_permissions.yml +++ /dev/null @@ -1,48 +0,0 @@ -# all.ro: -# actions: -# - view -# description: 'Read Only for All Objects' -# enabled: true -# groups: -# - applications -# - readers -# object_types: all -# users: -# - jdoe -# all.rw: -# actions: -# - add -# - change -# - delete -# - view -# description: 'Read/Write for All Objects' -# enabled: true -# groups: -# - writers -# object_types: all -# network_team.rw: -# actions: -# - add -# - change -# - delete -# - view -# description: "Network Team Permissions" -# enabled: true -# object_types: -# circuits: -# - circuit -# - circuittermination -# - circuittype -# - provider -# dcim: all -# ipam: -# - aggregate -# - ipaddress -# - prefix -# - rir -# - role -# - routetarget -# - service -# - vlan -# - vlangroup -# - vrf diff --git a/initializers/platforms.yml b/initializers/platforms.yml deleted file mode 100644 index b0b7ba3..0000000 --- a/initializers/platforms.yml +++ /dev/null @@ -1,15 +0,0 @@ -# - name: Platform 1 -# slug: platform-1 -# manufacturer: Manufacturer 1 -# napalm_driver: driver1 -# napalm_args: "{'arg1': 'value1', 'arg2': 'value2'}" -# - name: Platform 2 -# slug: platform-2 -# manufacturer: Manufacturer 2 -# napalm_driver: driver2 -# napalm_args: "{'arg1': 'value1', 'arg2': 'value2'}" -# - name: Platform 3 -# slug: platform-3 -# manufacturer: No Name -# napalm_driver: driver3 -# napalm_args: "{'arg1': 'value1', 'arg2': 'value2'}" diff --git a/initializers/power_feeds.yml b/initializers/power_feeds.yml deleted file mode 100644 index 7ef5891..0000000 --- a/initializers/power_feeds.yml +++ /dev/null @@ -1,14 +0,0 @@ -# - name: power feed 1 -# power_panel: power panel AMS 1 -# voltage: 208 -# amperage: 50 -# max_utilization: 80 -# phase: Single phase -# rack: rack-01 -# - name: power feed 2 -# power_panel: power panel SING 1 -# voltage: 208 -# amperage: 50 -# max_utilization: 80 -# phase: Three-phase -# rack: rack-03 diff --git a/initializers/power_panels.yml b/initializers/power_panels.yml deleted file mode 100644 index a670ba7..0000000 --- a/initializers/power_panels.yml +++ /dev/null @@ -1,5 +0,0 @@ -# - name: power panel AMS 1 -# site: AMS 1 -# - name: power panel SING 1 -# site: SING 1 -# location: cage 101 diff --git a/initializers/prefix_vlan_roles.yml b/initializers/prefix_vlan_roles.yml deleted file mode 100644 index 0ea7720..0000000 --- a/initializers/prefix_vlan_roles.yml +++ /dev/null @@ -1,2 +0,0 @@ -# - name: Main Management -# slug: main-management diff --git a/initializers/prefixes.yml b/initializers/prefixes.yml deleted file mode 100644 index fbf3eee..0000000 --- a/initializers/prefixes.yml +++ /dev/null @@ -1,29 +0,0 @@ -## Possible Choices: -## status: -## - container -## - active -## - reserved -## - deprecated -## -## Examples: - -# - description: prefix1 -# prefix: 10.1.1.0/24 -# site: AMS 1 -# status: active -# tenant: tenant1 -# vlan: vlan1 -# - description: prefix2 -# prefix: 10.1.2.0/24 -# site: AMS 2 -# status: active -# tenant: tenant2 -# vlan: vlan2 -# is_pool: true -# vrf: vrf2 -# - description: ipv6 prefix1 -# prefix: 2001:db8:a000:1::/64 -# site: AMS 2 -# status: active -# tenant: tenant2 -# vlan: vlan2 diff --git a/initializers/providers.yml b/initializers/providers.yml deleted file mode 100644 index ca2ca1a..0000000 --- a/initializers/providers.yml +++ /dev/null @@ -1,6 +0,0 @@ -# - name: Provider1 -# slug: provider1 -# asn: 121 -# - name: Provider2 -# slug: provider2 -# asn: 122 diff --git a/initializers/rack_roles.yml b/initializers/rack_roles.yml deleted file mode 100644 index e8d1e3e..0000000 --- a/initializers/rack_roles.yml +++ /dev/null @@ -1,12 +0,0 @@ -# - 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 deleted file mode 100644 index 9071e19..0000000 --- a/initializers/racks.yml +++ /dev/null @@ -1,41 +0,0 @@ -## Possible Choices: -## width: -## - 19 -## - 23 -## types: -## - 2-post-frame -## - 4-post-frame -## - 4-post-cabinet -## - wall-frame -## - wall-cabinet -## outer_unit: -## - mm -## - in -## -## Examples: - -# - site: AMS 1 -# name: rack-01 -# role: Role 1 -# type: 4-post-cabinet -# width: 19 -# u_height: 47 -# custom_field_data: -# text_field: Description -# - site: AMS 2 -# name: rack-02 -# role: Role 2 -# type: 4-post-cabinet -# width: 19 -# u_height: 47 -# custom_field_data: -# text_field: Description -# - site: SING 1 -# name: rack-03 -# location: cage 101 -# role: Role 3 -# type: 4-post-cabinet -# width: 19 -# u_height: 47 -# custom_field_data: -# text_field: Description diff --git a/initializers/regions.yml b/initializers/regions.yml deleted file mode 100644 index 1353e05..0000000 --- a/initializers/regions.yml +++ /dev/null @@ -1,10 +0,0 @@ -# - name: Singapore -# slug: singapore -# - name: Amsterdam -# slug: amsterdam -# - name: Downtown -# slug: downtown -# parent: Amsterdam -# - name: Suburbs -# slug: suburbs -# parent: Amsterdam diff --git a/initializers/rirs.yml b/initializers/rirs.yml deleted file mode 100644 index 0aacd59..0000000 --- a/initializers/rirs.yml +++ /dev/null @@ -1,9 +0,0 @@ -# - is_private: true -# name: RFC1918 -# slug: rfc1918 -# - is_private: true -# name: RFC4193 ULA -# slug: rfc4193-ula -# - is_private: true -# name: RFC3849 -# slug: rfc3849 diff --git a/initializers/route_targets.yml b/initializers/route_targets.yml deleted file mode 100644 index 2ef1406..0000000 --- a/initializers/route_targets.yml +++ /dev/null @@ -1,3 +0,0 @@ -# - name: 65000:1001 -# tenant: tenant1 -# - name: 65000:1002 diff --git a/initializers/services.yml b/initializers/services.yml deleted file mode 100644 index 8d4441c..0000000 --- a/initializers/services.yml +++ /dev/null @@ -1,15 +0,0 @@ -# - name: DNS -# protocol: TCP -# ports: -# - 53 -# virtual_machine: virtual machine 1 -# - name: DNS -# protocol: UDP -# ports: -# - 53 -# virtual_machine: virtual machine 1 -# - name: MISC -# protocol: UDP -# ports: -# - 4000 -# device: server01 diff --git a/initializers/sites.yml b/initializers/sites.yml deleted file mode 100644 index 700c853..0000000 --- a/initializers/sites.yml +++ /dev/null @@ -1,30 +0,0 @@ -# - name: AMS 1 -# slug: ams1 -# region: Downtown -# status: active -# facility: Amsterdam 1 -# custom_field_data: -# text_field: Description for AMS1 -# - name: AMS 2 -# slug: ams2 -# region: Downtown -# status: active -# facility: Amsterdam 2 -# custom_field_data: -# text_field: Description for AMS2 -# - name: AMS 3 -# slug: ams3 -# region: Suburbs -# status: active -# facility: Amsterdam 3 -# tenant: tenant1 -# custom_field_data: -# text_field: Description for AMS3 -# - name: SING 1 -# slug: sing1 -# region: Singapore -# status: active -# facility: Singapore 1 -# tenant: tenant2 -# custom_field_data: -# text_field: Description for SING1 diff --git a/initializers/tags.yml b/initializers/tags.yml deleted file mode 100644 index 1cbe3be..0000000 --- a/initializers/tags.yml +++ /dev/null @@ -1,12 +0,0 @@ -# - name: Tag 1 -# slug: tag-1 -# color: Pink -# - name: Tag 2 -# slug: tag-2 -# color: Cyan -# - name: Tag 3 -# slug: tag-3 -# color: Grey -# - name: Tag 4 -# slug: tag-4 -# color: Teal diff --git a/initializers/tenant_groups.yml b/initializers/tenant_groups.yml deleted file mode 100644 index 87ecd4d..0000000 --- a/initializers/tenant_groups.yml +++ /dev/null @@ -1,4 +0,0 @@ -# - name: Tenant Group 1 -# slug: tenant-group-1 -# - name: Tenant Group 2 -# slug: tenant-group-2 diff --git a/initializers/tenants.yml b/initializers/tenants.yml deleted file mode 100644 index 1dbfbe5..0000000 --- a/initializers/tenants.yml +++ /dev/null @@ -1,5 +0,0 @@ -# - name: tenant1 -# slug: tenant1 -# - name: tenant2 -# slug: tenant2 -# group: Tenant Group 2 diff --git a/initializers/users.yml b/initializers/users.yml deleted file mode 100644 index d6484c2..0000000 --- a/initializers/users.yml +++ /dev/null @@ -1,15 +0,0 @@ -# technical_user: -# api_token: 0123456789technicaluser789abcdef01234567 # must be looooong! -# reader: -# password: reader -# writer: -# password: writer -# api_token: "" # a token is generated automatically unless the value is explicity set to empty -# jdoe: -# first_name: John -# last_name: Doe -# api_token: 0123456789jdoe789abcdef01234567jdoe -# is_active: True -# is_superuser: False -# is_staff: False -# email: john.doe@example.com diff --git a/initializers/virtual_machines.yml b/initializers/virtual_machines.yml deleted file mode 100644 index 918df93..0000000 --- a/initializers/virtual_machines.yml +++ /dev/null @@ -1,28 +0,0 @@ -## Possible Choices: -## status: -## - active -## - offline -## - staged -## -## Examples: - -# - cluster: cluster1 -# comments: VM1 -# disk: 200 -# memory: 4096 -# name: virtual machine 1 -# platform: Platform 2 -# status: active -# tenant: tenant1 -# vcpus: 8 -# - cluster: cluster1 -# comments: VM2 -# disk: 100 -# memory: 2048 -# name: virtual machine 2 -# platform: Platform 2 -# primary_ip4: 10.1.1.10/24 -# primary_ip6: 2001:db8:a000:1::10/64 -# status: active -# tenant: tenant1 -# vcpus: 8 diff --git a/initializers/virtualization_interfaces.yml b/initializers/virtualization_interfaces.yml deleted file mode 100644 index aeedd58..0000000 --- a/initializers/virtualization_interfaces.yml +++ /dev/null @@ -1,12 +0,0 @@ -# - description: Network Interface 1 -# enabled: true -# mac_address: 00:77:77:77:77:77 -# mtu: 1500 -# name: Network Interface 1 -# virtual_machine: virtual machine 1 -# - description: Network Interface 2 -# enabled: true -# mac_address: 00:55:55:55:55:55 -# mtu: 1500 -# name: Network Interface 2 -# virtual_machine: virtual machine 1 diff --git a/initializers/vlan_groups.yml b/initializers/vlan_groups.yml deleted file mode 100644 index facd34f..0000000 --- a/initializers/vlan_groups.yml +++ /dev/null @@ -1,24 +0,0 @@ -# - name: VLAN group 1 -# scope_type: dcim.region -# scope: Amsterdam -# slug: vlan-group-1 -# - name: VLAN group 2 -# scope_type: dcim.site -# scope: AMS 1 -# slug: vlan-group-2 -# - name: VLAN group 3 -# scope_type: dcim.location -# scope: cage 101 -# slug: vlan-group-3 -# - name: VLAN group 4 -# scope_type: dcim.rack -# scope: rack-01 -# slug: vlan-group-4 -# - name: VLAN group 5 -# scope_type: virtualization.cluster -# scope: cluster1 -# slug: vlan-group-5 -# - name: VLAN group 6 -# scope_type: virtualization.clustergroup -# scope: Group 1 -# slug: vlan-group-6 diff --git a/initializers/vlans.yml b/initializers/vlans.yml deleted file mode 100644 index a8cd521..0000000 --- a/initializers/vlans.yml +++ /dev/null @@ -1,19 +0,0 @@ -## Possible Choices: -## status: -## - active -## - reserved -## - deprecated -## -## Examples: - -# - name: vlan1 -# site: AMS 1 -# status: active -# vid: 5 -# role: Main Management -# description: VLAN 5 for MGMT -# - group: VLAN group 2 -# name: vlan2 -# site: AMS 1 -# status: active -# vid: 1300 diff --git a/initializers/vrfs.yml b/initializers/vrfs.yml deleted file mode 100644 index 40b9031..0000000 --- a/initializers/vrfs.yml +++ /dev/null @@ -1,8 +0,0 @@ -# - enforce_unique: true -# name: vrf1 -# tenant: tenant1 -# description: main VRF -# - enforce_unique: true -# name: vrf2 -# rd: "6500:6500" -# tenant: tenant2 diff --git a/initializers/webhooks.yml b/initializers/webhooks.yml deleted file mode 100644 index deb1b39..0000000 --- a/initializers/webhooks.yml +++ /dev/null @@ -1,27 +0,0 @@ -## Possible Choices: -## object_types: -## - device -## - site -## - any-other-content-type -## types: -## - type_create -## - type_update -## - type_delete -## Examples: - -# - name: device_creation -# payload_url: 'http://localhost:8080' -# object_types: -# - device -# - cable -# type_create: True -# - name: device_update -# payload_url: 'http://localhost:8080' -# object_types: -# - device -# type_update: True -# - name: device_delete -# payload_url: 'http://localhost:8080' -# object_types: -# - device -# type_delete: True diff --git a/requirements-container.txt b/requirements-container.txt index 37403b0..7aec758 100644 --- a/requirements-container.txt +++ b/requirements-container.txt @@ -1,6 +1,5 @@ django-auth-ldap==4.1.0 -django-storages[azure,boto3,dropbox,google,libcloud,sftp]==1.12.3 +django-storages[azure,boto3,dropbox,google,libcloud,sftp]==1.13.1 napalm==4.0.0 psycopg2==2.9.3 social-auth-core[openidconnect]==4.3.0 -ruamel.yaml==0.17.21 diff --git a/startup_scripts/000_users.py b/startup_scripts/000_users.py deleted file mode 100644 index 7ca6305..0000000 --- a/startup_scripts/000_users.py +++ /dev/null @@ -1,25 +0,0 @@ -import sys - -from django.contrib.auth.models import User -from startup_script_utils import load_yaml -from users.models import Token - -users = load_yaml("/opt/netbox/initializers/users.yml") -if users is None: - sys.exit() - -for username, user_details in users.items(): - - api_token = user_details.pop("api_token", Token.generate_key()) - password = user_details.pop("password", User.objects.make_random_password()) - - user, created = User.objects.get_or_create(username=username, defaults=user_details) - - if created: - user.set_password(password) - user.save() - - if api_token: - Token.objects.get_or_create(user=user, key=api_token) - - print("πŸ‘€ Created user", username) diff --git a/startup_scripts/010_groups.py b/startup_scripts/010_groups.py deleted file mode 100644 index a7a2429..0000000 --- a/startup_scripts/010_groups.py +++ /dev/null @@ -1,23 +0,0 @@ -import sys - -from startup_script_utils import load_yaml -from users.models import AdminGroup, AdminUser - -groups = load_yaml("/opt/netbox/initializers/groups.yml") -if groups is None: - sys.exit() - -for groupname, group_details in groups.items(): - group, created = AdminGroup.objects.get_or_create(name=groupname) - - if created: - print("πŸ‘₯ Created group", groupname) - - for username in group_details.get("users", []): - user = AdminUser.objects.get(username=username) - - if user: - group.user_set.add(user) - print(" πŸ‘€ Assigned user %s to group %s" % (username, group.name)) - - group.save() diff --git a/startup_scripts/020_object_permissions.py b/startup_scripts/020_object_permissions.py deleted file mode 100644 index 562a09c..0000000 --- a/startup_scripts/020_object_permissions.py +++ /dev/null @@ -1,68 +0,0 @@ -import sys - -from django.contrib.contenttypes.models import ContentType -from startup_script_utils import load_yaml -from users.models import AdminGroup, AdminUser, ObjectPermission - -object_permissions = load_yaml("/opt/netbox/initializers/object_permissions.yml") - -if object_permissions is None: - sys.exit() - - -for permission_name, permission_details in object_permissions.items(): - - object_permission, created = ObjectPermission.objects.get_or_create( - name=permission_name, - defaults={ - "description": permission_details["description"], - "enabled": permission_details["enabled"], - "actions": permission_details["actions"], - }, - ) - - if permission_details.get("object_types", 0): - object_types = permission_details["object_types"] - - if object_types == "all": - object_permission.object_types.set(ContentType.objects.all()) - - else: - for app_label, models in object_types.items(): - if models == "all": - app_models = ContentType.objects.filter(app_label=app_label) - - for app_model in app_models: - object_permission.object_types.add(app_model.id) - else: - # There is - for model in models: - object_permission.object_types.add( - ContentType.objects.get(app_label=app_label, model=model) - ) - - print("πŸ”“ Created object permission", object_permission.name) - - if permission_details.get("groups", 0): - for groupname in permission_details["groups"]: - group = AdminGroup.objects.filter(name=groupname).first() - - if group: - object_permission.groups.add(group) - print( - " πŸ‘₯ Assigned group %s object permission of %s" - % (groupname, object_permission.name) - ) - - if permission_details.get("users", 0): - for username in permission_details["users"]: - user = AdminUser.objects.filter(username=username).first() - - if user: - object_permission.users.add(user) - print( - " πŸ‘€ Assigned user %s object permission of %s" - % (username, object_permission.name) - ) - - object_permission.save() diff --git a/startup_scripts/030_custom_fields.py b/startup_scripts/030_custom_fields.py deleted file mode 100644 index a40883c..0000000 --- a/startup_scripts/030_custom_fields.py +++ /dev/null @@ -1,67 +0,0 @@ -import sys - -from extras.models import CustomField -from startup_script_utils import load_yaml - - -def get_class_for_class_path(class_path): - import importlib - - from django.contrib.contenttypes.models import ContentType - - module_name, class_name = class_path.rsplit(".", 1) - module = importlib.import_module(module_name) - clazz = getattr(module, class_name) - return ContentType.objects.get_for_model(clazz) - - -customfields = load_yaml("/opt/netbox/initializers/custom_fields.yml") - -if customfields is None: - sys.exit() - -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", False): - custom_field.default = cf_details["default"] - - if cf_details.get("description", False): - custom_field.description = cf_details["description"] - - if cf_details.get("label", False): - custom_field.label = cf_details["label"] - - for object_type in cf_details.get("on_objects", []): - custom_field.content_types.add(get_class_for_class_path(object_type)) - - if cf_details.get("required", False): - custom_field.required = cf_details["required"] - - if cf_details.get("type", False): - custom_field.type = cf_details["type"] - - if cf_details.get("filter_logic", False): - custom_field.filter_logic = cf_details["filter_logic"] - - if cf_details.get("weight", -1) >= 0: - custom_field.weight = cf_details["weight"] - - if cf_details.get("choices", False): - custom_field.choices = [] - - for choice_detail in cf_details.get("choices", []): - if isinstance(choice_detail, dict) and "value" in choice_detail: - # legacy mode - print( - f"⚠️ Please migrate the choice '{choice_detail['value']}' of '{cf_name}'" - + " to the new format, as 'weight' is no longer supported!" - ) - custom_field.choices.append(choice_detail["value"]) - else: - custom_field.choices.append(choice_detail) - - custom_field.save() - - print("πŸ”§ Created custom field", cf_name) diff --git a/startup_scripts/040_custom_links.py b/startup_scripts/040_custom_links.py deleted file mode 100644 index d8c0bba..0000000 --- a/startup_scripts/040_custom_links.py +++ /dev/null @@ -1,35 +0,0 @@ -import sys - -from django.contrib.contenttypes.models import ContentType -from extras.models import CustomLink -from startup_script_utils import load_yaml, split_params - -custom_links = load_yaml("/opt/netbox/initializers/custom_links.yml") - -if custom_links is None: - sys.exit() - - -def get_content_type_id(content_type): - try: - return ContentType.objects.get(model=content_type).id - except ContentType.DoesNotExist: - pass - - -for link in custom_links: - content_type = link.pop("content_type") - link["content_type_id"] = get_content_type_id(content_type) - if link["content_type_id"] is None: - print( - "⚠️ Unable to create Custom Link '{0}': The content_type '{1}' is unknown".format( - link.get("name"), content_type - ) - ) - continue - - matching_params, defaults = split_params(link) - custom_link, created = CustomLink.objects.get_or_create(**matching_params, defaults=defaults) - - if created: - print("πŸ”— Created Custom Link '{0}'".format(custom_link.name)) diff --git a/startup_scripts/050_tags.py b/startup_scripts/050_tags.py deleted file mode 100644 index 06a18cb..0000000 --- a/startup_scripts/050_tags.py +++ /dev/null @@ -1,24 +0,0 @@ -import sys - -from extras.models import Tag -from startup_script_utils import load_yaml, split_params -from utilities.choices import ColorChoices - -tags = load_yaml("/opt/netbox/initializers/tags.yml") - -if tags is None: - sys.exit() - -for params in tags: - if "color" in params: - color = params.pop("color") - - for color_tpl in ColorChoices: - if color in color_tpl: - params["color"] = color_tpl[0] - - matching_params, defaults = split_params(params) - tag, created = Tag.objects.get_or_create(**matching_params, defaults=defaults) - - if created: - print("🎨 Created Tag", tag.name) diff --git a/startup_scripts/060_webhooks.py b/startup_scripts/060_webhooks.py deleted file mode 100644 index 9de6e7e..0000000 --- a/startup_scripts/060_webhooks.py +++ /dev/null @@ -1,36 +0,0 @@ -import sys - -from django.contrib.contenttypes.models import ContentType -from extras.models import Webhook -from startup_script_utils import load_yaml, split_params - -webhooks = load_yaml("/opt/netbox/initializers/webhooks.yml") - -if webhooks is None: - sys.exit() - - -def get_content_type_id(hook_name, content_type): - try: - return ContentType.objects.get(model=content_type).id - except ContentType.DoesNotExist as ex: - print("⚠️ Webhook '{0}': The object_type '{1}' is unknown.".format(hook_name, content_type)) - raise ex - - -for hook in webhooks: - obj_types = hook.pop("object_types") - - try: - obj_type_ids = [get_content_type_id(hook["name"], obj) for obj in obj_types] - except ContentType.DoesNotExist: - continue - - matching_params, defaults = split_params(hook) - webhook, created = Webhook.objects.get_or_create(**matching_params, defaults=defaults) - - if created: - webhook.content_types.set(obj_type_ids) - webhook.save() - - print("πŸͺ Created Webhook {0}".format(webhook.name)) diff --git a/startup_scripts/070_tenant_groups.py b/startup_scripts/070_tenant_groups.py deleted file mode 100644 index 0bfebad..0000000 --- a/startup_scripts/070_tenant_groups.py +++ /dev/null @@ -1,16 +0,0 @@ -import sys - -from startup_script_utils import load_yaml, split_params -from tenancy.models import TenantGroup - -tenant_groups = load_yaml("/opt/netbox/initializers/tenant_groups.yml") - -if tenant_groups is None: - sys.exit() - -for params in tenant_groups: - matching_params, defaults = split_params(params) - tenant_group, created = TenantGroup.objects.get_or_create(**matching_params, defaults=defaults) - - if created: - print("πŸ”³ Created Tenant Group", tenant_group.name) diff --git a/startup_scripts/080_tenants.py b/startup_scripts/080_tenants.py deleted file mode 100644 index 88d68b7..0000000 --- a/startup_scripts/080_tenants.py +++ /dev/null @@ -1,34 +0,0 @@ -import sys - -from startup_script_utils import ( - load_yaml, - pop_custom_fields, - set_custom_fields_values, - split_params, -) -from tenancy.models import Tenant, TenantGroup - -tenants = load_yaml("/opt/netbox/initializers/tenants.yml") - -if tenants is None: - sys.exit() - -optional_assocs = {"group": (TenantGroup, "name")} - -for params in tenants: - custom_field_data = pop_custom_fields(params) - - 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) - - matching_params, defaults = split_params(params) - tenant, created = Tenant.objects.get_or_create(**matching_params, defaults=defaults) - - if created: - print("πŸ‘©β€πŸ’» Created Tenant", tenant.name) - - set_custom_fields_values(tenant, custom_field_data) diff --git a/startup_scripts/090_regions.py b/startup_scripts/090_regions.py deleted file mode 100644 index 592c4c5..0000000 --- a/startup_scripts/090_regions.py +++ /dev/null @@ -1,26 +0,0 @@ -import sys - -from dcim.models import Region -from startup_script_utils import load_yaml, split_params - -regions = load_yaml("/opt/netbox/initializers/regions.yml") - -if regions is None: - sys.exit() - -optional_assocs = {"parent": (Region, "name")} - -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) - - matching_params, defaults = split_params(params) - region, created = Region.objects.get_or_create(**matching_params, defaults=defaults) - - if created: - print("🌐 Created region", region.name) diff --git a/startup_scripts/110_sites.py b/startup_scripts/110_sites.py deleted file mode 100644 index c310e14..0000000 --- a/startup_scripts/110_sites.py +++ /dev/null @@ -1,35 +0,0 @@ -import sys - -from dcim.models import Region, Site -from startup_script_utils import ( - load_yaml, - pop_custom_fields, - set_custom_fields_values, - split_params, -) -from tenancy.models import Tenant - -sites = load_yaml("/opt/netbox/initializers/sites.yml") - -if sites is None: - sys.exit() - -optional_assocs = {"region": (Region, "name"), "tenant": (Tenant, "name")} - -for params in sites: - custom_field_data = pop_custom_fields(params) - - 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) - - matching_params, defaults = split_params(params) - site, created = Site.objects.get_or_create(**matching_params, defaults=defaults) - - if created: - print("πŸ“ Created site", site.name) - - set_custom_fields_values(site, custom_field_data) diff --git a/startup_scripts/120_locations.py b/startup_scripts/120_locations.py deleted file mode 100644 index 6a269b2..0000000 --- a/startup_scripts/120_locations.py +++ /dev/null @@ -1,25 +0,0 @@ -import sys - -from dcim.models import Location, Site -from startup_script_utils import load_yaml, split_params - -rack_groups = load_yaml("/opt/netbox/initializers/locations.yml") - -if rack_groups is None: - sys.exit() - -match_params = ["name", "slug", "site"] -required_assocs = {"site": (Site, "name")} - -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) - - matching_params, defaults = split_params(params, match_params) - location, created = Location.objects.get_or_create(**matching_params, defaults=defaults) - - if created: - print("🎨 Created location", location.name) diff --git a/startup_scripts/130_rack_roles.py b/startup_scripts/130_rack_roles.py deleted file mode 100644 index 8d43237..0000000 --- a/startup_scripts/130_rack_roles.py +++ /dev/null @@ -1,24 +0,0 @@ -import sys - -from dcim.models import RackRole -from startup_script_utils import load_yaml, split_params -from utilities.choices import ColorChoices - -rack_roles = load_yaml("/opt/netbox/initializers/rack_roles.yml") - -if rack_roles is None: - sys.exit() - -for params in rack_roles: - if "color" in params: - color = params.pop("color") - - for color_tpl in ColorChoices: - if color in color_tpl: - params["color"] = color_tpl[0] - - matching_params, defaults = split_params(params) - rack_role, created = RackRole.objects.get_or_create(**matching_params, defaults=defaults) - - if created: - print("🎨 Created rack role", rack_role.name) diff --git a/startup_scripts/140_racks.py b/startup_scripts/140_racks.py deleted file mode 100644 index f3dc145..0000000 --- a/startup_scripts/140_racks.py +++ /dev/null @@ -1,47 +0,0 @@ -import sys - -from dcim.models import Location, Rack, RackRole, Site -from startup_script_utils import ( - load_yaml, - pop_custom_fields, - set_custom_fields_values, - split_params, -) -from tenancy.models import Tenant - -racks = load_yaml("/opt/netbox/initializers/racks.yml") - -if racks is None: - sys.exit() - -match_params = ["name", "site"] -required_assocs = {"site": (Site, "name")} -optional_assocs = { - "role": (RackRole, "name"), - "tenant": (Tenant, "name"), - "location": (Location, "name"), -} - -for params in racks: - custom_field_data = pop_custom_fields(params) - - 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) - - matching_params, defaults = split_params(params, match_params) - rack, created = Rack.objects.get_or_create(**matching_params, defaults=defaults) - - if created: - print("πŸ”³ Created rack", rack.site, rack.name) - - set_custom_fields_values(rack, custom_field_data) diff --git a/startup_scripts/150_power_panels.py b/startup_scripts/150_power_panels.py deleted file mode 100644 index 1397a93..0000000 --- a/startup_scripts/150_power_panels.py +++ /dev/null @@ -1,42 +0,0 @@ -import sys - -from dcim.models import Location, PowerPanel, Site -from startup_script_utils import ( - load_yaml, - pop_custom_fields, - set_custom_fields_values, - split_params, -) - -power_panels = load_yaml("/opt/netbox/initializers/power_panels.yml") - -if power_panels is None: - sys.exit() - -match_params = ["name", "site"] -required_assocs = {"site": (Site, "name")} -optional_assocs = {"location": (Location, "name")} - -for params in power_panels: - custom_field_data = pop_custom_fields(params) - - 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) - - matching_params, defaults = split_params(params, match_params) - power_panel, created = PowerPanel.objects.get_or_create(**matching_params, defaults=defaults) - - if created: - print("⚑ Created Power Panel", power_panel.site, power_panel.name) - - set_custom_fields_values(power_panel, custom_field_data) diff --git a/startup_scripts/160_power_feeds.py b/startup_scripts/160_power_feeds.py deleted file mode 100644 index 7b3e6ac..0000000 --- a/startup_scripts/160_power_feeds.py +++ /dev/null @@ -1,42 +0,0 @@ -import sys - -from dcim.models import PowerFeed, PowerPanel, Rack -from startup_script_utils import ( - load_yaml, - pop_custom_fields, - set_custom_fields_values, - split_params, -) - -power_feeds = load_yaml("/opt/netbox/initializers/power_feeds.yml") - -if power_feeds is None: - sys.exit() - -match_params = ["name", "power_panel"] -required_assocs = {"power_panel": (PowerPanel, "name")} -optional_assocs = {"rack": (Rack, "name")} - -for params in power_feeds: - custom_field_data = pop_custom_fields(params) - - 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) - - matching_params, defaults = split_params(params, match_params) - power_feed, created = PowerFeed.objects.get_or_create(**matching_params, defaults=defaults) - - if created: - print("⚑ Created Power Feed", power_feed.name) - - set_custom_fields_values(power_feed, custom_field_data) diff --git a/startup_scripts/170_manufacturers.py b/startup_scripts/170_manufacturers.py deleted file mode 100644 index c105a46..0000000 --- a/startup_scripts/170_manufacturers.py +++ /dev/null @@ -1,16 +0,0 @@ -import sys - -from dcim.models import Manufacturer -from startup_script_utils import load_yaml, split_params - -manufacturers = load_yaml("/opt/netbox/initializers/manufacturers.yml") - -if manufacturers is None: - sys.exit() - -for params in manufacturers: - matching_params, defaults = split_params(params) - manufacturer, created = Manufacturer.objects.get_or_create(**matching_params, defaults=defaults) - - if created: - print("🏭 Created Manufacturer", manufacturer.name) diff --git a/startup_scripts/180_device_roles.py b/startup_scripts/180_device_roles.py deleted file mode 100644 index 3cea0f1..0000000 --- a/startup_scripts/180_device_roles.py +++ /dev/null @@ -1,25 +0,0 @@ -import sys - -from dcim.models import DeviceRole -from startup_script_utils import load_yaml, split_params -from utilities.choices import ColorChoices - -device_roles = load_yaml("/opt/netbox/initializers/device_roles.yml") - -if device_roles is None: - sys.exit() - -for params in device_roles: - - if "color" in params: - color = params.pop("color") - - for color_tpl in ColorChoices: - if color in color_tpl: - params["color"] = color_tpl[0] - - matching_params, defaults = split_params(params) - device_role, created = DeviceRole.objects.get_or_create(**matching_params, defaults=defaults) - - if created: - print("🎨 Created device role", device_role.name) diff --git a/startup_scripts/190_device_types.py b/startup_scripts/190_device_types.py deleted file mode 100644 index d20c9e6..0000000 --- a/startup_scripts/190_device_types.py +++ /dev/null @@ -1,139 +0,0 @@ -import sys -from typing import List - -from dcim.models import DeviceType, Manufacturer, Region -from dcim.models.device_component_templates import ( - ConsolePortTemplate, - ConsoleServerPortTemplate, - DeviceBayTemplate, - FrontPortTemplate, - InterfaceTemplate, - PowerOutletTemplate, - PowerPortTemplate, - RearPortTemplate, -) -from startup_script_utils import ( - load_yaml, - pop_custom_fields, - set_custom_fields_values, - split_params, -) -from tenancy.models import Tenant -from utilities.forms.utils import expand_alphanumeric_pattern - - -def expand_templates(params: List[dict], device_type: DeviceType) -> List[dict]: - templateable_fields = ["name", "label", "positions", "rear_port", "rear_port_position"] - - expanded = [] - for param in params: - param["device_type"] = device_type - expanded_fields = {} - has_plain_fields = False - - for field in templateable_fields: - template_value = param.pop(f"{field}_template", None) - - if field in param: - has_plain_fields = True - elif template_value: - expanded_fields[field] = list(expand_alphanumeric_pattern(template_value)) - - if expanded_fields and has_plain_fields: - raise ValueError(f"Mix of plain and template keys provided for {templateable_fields}") - elif not expanded_fields: - expanded.append(param) - continue - - elements = list(expanded_fields.values()) - master_len = len(elements[0]) - if not all([len(elem) == master_len for elem in elements]): - raise ValueError( - f"Number of elements in template fields " - f"{list(expanded_fields.keys())} must be equal" - ) - - for idx in range(master_len): - tmp = param.copy() - for field, value in expanded_fields.items(): - if field in nested_assocs: - model, match_key = nested_assocs[field] - query = {match_key: value[idx], "device_type": device_type} - tmp[field] = model.objects.get(**query) - else: - tmp[field] = value[idx] - expanded.append(tmp) - return expanded - - -device_types = load_yaml("/opt/netbox/initializers/device_types.yml") - -if device_types is None: - sys.exit() - -match_params = ["manufacturer", "model", "slug"] -required_assocs = {"manufacturer": (Manufacturer, "name")} -optional_assocs = {"region": (Region, "name"), "tenant": (Tenant, "name")} -nested_assocs = {"rear_port": (RearPortTemplate, "name"), "power_port": (PowerPortTemplate, "name")} - -supported_components = { - "interfaces": (InterfaceTemplate, ["name"]), - "console_ports": (ConsolePortTemplate, ["name"]), - "console_server_ports": (ConsoleServerPortTemplate, ["name"]), - "power_ports": (PowerPortTemplate, ["name"]), - "power_outlets": (PowerOutletTemplate, ["name"]), - "rear_ports": (RearPortTemplate, ["name"]), - "front_ports": (FrontPortTemplate, ["name"]), - "device_bays": (DeviceBayTemplate, ["name"]), -} - -for params in device_types: - custom_field_data = pop_custom_fields(params) - components = [(v[0], v[1], params.pop(k, [])) for k, v in supported_components.items()] - - 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) - - matching_params, defaults = split_params(params, match_params) - device_type, created = DeviceType.objects.get_or_create(**matching_params, defaults=defaults) - - if created: - print("πŸ”‘ Created device type", device_type.manufacturer, device_type.model) - - set_custom_fields_values(device_type, custom_field_data) - - for component in components: - c_model, c_match_params, c_params = component - c_match_params.append("device_type") - - if not c_params: - continue - - expanded_c_params = expand_templates(c_params, device_type) - - for n_assoc, n_details in nested_assocs.items(): - n_model, n_field = n_details - for c_param in expanded_c_params: - if n_assoc in c_param: - n_query = {n_field: c_param[n_assoc], "device_type": device_type} - c_param[n_assoc] = n_model.objects.get(**n_query) - - for new_param in expanded_c_params: - new_matching_params, new_defaults = split_params(new_param, c_match_params) - new_obj, new_obj_created = c_model.objects.get_or_create( - **new_matching_params, defaults=new_defaults - ) - if new_obj_created: - print( - f"🧷 Created {c_model._meta} {new_obj} component for device type {device_type}" - ) diff --git a/startup_scripts/200_devices.py b/startup_scripts/200_devices.py deleted file mode 100644 index 189471c..0000000 --- a/startup_scripts/200_devices.py +++ /dev/null @@ -1,58 +0,0 @@ -import sys - -from dcim.models import Device, DeviceRole, DeviceType, Location, Platform, Rack, Site -from startup_script_utils import ( - load_yaml, - pop_custom_fields, - set_custom_fields_values, - split_params, -) -from tenancy.models import Tenant -from virtualization.models import Cluster - -devices = load_yaml("/opt/netbox/initializers/devices.yml") - -if devices is None: - sys.exit() - -match_params = ["device_type", "name", "site"] -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"), - "location": (Location, "name"), -} - -for params in devices: - custom_field_data = pop_custom_fields(params) - - # primary ips are handled later in `380_primary_ips.py` - params.pop("primary_ip4", None) - params.pop("primary_ip6", 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) - - matching_params, defaults = split_params(params, match_params) - device, created = Device.objects.get_or_create(**matching_params, defaults=defaults) - - if created: - print("πŸ–₯️ Created device", device.name) - - set_custom_fields_values(device, custom_field_data) diff --git a/startup_scripts/210_dcim_interfaces.py b/startup_scripts/210_dcim_interfaces.py deleted file mode 100644 index 6e57e73..0000000 --- a/startup_scripts/210_dcim_interfaces.py +++ /dev/null @@ -1,70 +0,0 @@ -import sys - -from dcim.models import Device, Interface -from startup_script_utils import ( - load_yaml, - pop_custom_fields, - set_custom_fields_values, - split_params, -) - -interfaces = load_yaml("/opt/netbox/initializers/dcim_interfaces.yml") - -if interfaces is None: - sys.exit() - -match_params = ["device", "name"] -required_assocs = {"device": (Device, "name")} -related_assocs = { - "bridge": (Interface, "name"), - "lag": (Interface, "name"), - "parent": (Interface, "name"), -} - -for params in interfaces: - custom_field_data = pop_custom_fields(params) - - related_interfaces = {k: params.pop(k, None) for k in related_assocs} - - for assoc, details in required_assocs.items(): - model, field = details - query = {field: params.pop(assoc)} - - params[assoc] = model.objects.get(**query) - - matching_params, defaults = split_params(params, match_params) - interface, created = Interface.objects.get_or_create(**matching_params, defaults=defaults) - - if created: - print(f"🧷 Created interface {interface} on {interface.device}") - - set_custom_fields_values(interface, custom_field_data) - - for related_field, related_value in related_interfaces.items(): - if not related_value: - continue - - r_model, r_field = related_assocs[related_field] - - if related_field == "parent" and not interface.parent_id: - query = {r_field: related_value, "device": interface.device} - try: - related_obj = r_model.objects.get(**query) - except Interface.DoesNotExist: - print(f"⚠️ Could not find parent interface with: {query} for interface {interface}") - raise - - interface.parent_id = related_obj.id - interface.save() - print( - f"🧷 Attached interface {interface} on {interface.device} " - f"to parent {related_obj}" - ) - else: - query = {r_field: related_value, "device": interface.device, "type": related_field} - related_obj, rel_obj_created = r_model.objects.get_or_create(**query) - - if rel_obj_created: - setattr(interface, f"{related_field}_id", related_obj.id) - interface.save() - print(f"🧷 Created {related_field} interface {interface} on {interface.device}") diff --git a/startup_scripts/220_platforms.py b/startup_scripts/220_platforms.py deleted file mode 100644 index 73905d4..0000000 --- a/startup_scripts/220_platforms.py +++ /dev/null @@ -1,28 +0,0 @@ -import sys - -from dcim.models import Manufacturer, Platform -from startup_script_utils import load_yaml, split_params - -platforms = load_yaml("/opt/netbox/initializers/platforms.yml") - -if platforms is None: - sys.exit() - -optional_assocs = { - "manufacturer": (Manufacturer, "name"), -} - -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) - - matching_params, defaults = split_params(params) - platform, created = Platform.objects.get_or_create(**matching_params, defaults=defaults) - - if created: - print("πŸ’Ύ Created platform", platform.name) diff --git a/startup_scripts/230_route_targets.py b/startup_scripts/230_route_targets.py deleted file mode 100644 index 12ca9a5..0000000 --- a/startup_scripts/230_route_targets.py +++ /dev/null @@ -1,35 +0,0 @@ -import sys - -from ipam.models import RouteTarget -from startup_script_utils import ( - load_yaml, - pop_custom_fields, - set_custom_fields_values, - split_params, -) -from tenancy.models import Tenant - -route_targets = load_yaml("/opt/netbox/initializers/route_targets.yml") - -if route_targets is None: - sys.exit() - -optional_assocs = {"tenant": (Tenant, "name")} - -for params in route_targets: - custom_field_data = pop_custom_fields(params) - - 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) - - matching_params, defaults = split_params(params) - route_target, created = RouteTarget.objects.get_or_create(**matching_params, defaults=defaults) - - if created: - print("🎯 Created Route Target", route_target.name) - - set_custom_fields_values(route_target, custom_field_data) diff --git a/startup_scripts/240_vrfs.py b/startup_scripts/240_vrfs.py deleted file mode 100644 index 1b4cd1a..0000000 --- a/startup_scripts/240_vrfs.py +++ /dev/null @@ -1,36 +0,0 @@ -import sys - -from ipam.models import VRF -from startup_script_utils import ( - load_yaml, - pop_custom_fields, - set_custom_fields_values, - split_params, -) -from tenancy.models import Tenant - -vrfs = load_yaml("/opt/netbox/initializers/vrfs.yml") - -if vrfs is None: - sys.exit() - -match_params = ["name", "rd"] -optional_assocs = {"tenant": (Tenant, "name")} - -for params in vrfs: - custom_field_data = pop_custom_fields(params) - - 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) - - matching_params, defaults = split_params(params) - vrf, created = VRF.objects.get_or_create(**matching_params, defaults=defaults) - - if created: - print("πŸ“¦ Created VRF", vrf.name) - - set_custom_fields_values(vrf, custom_field_data) diff --git a/startup_scripts/250_rirs.py b/startup_scripts/250_rirs.py deleted file mode 100644 index 08d1703..0000000 --- a/startup_scripts/250_rirs.py +++ /dev/null @@ -1,16 +0,0 @@ -import sys - -from ipam.models import RIR -from startup_script_utils import load_yaml, split_params - -rirs = load_yaml("/opt/netbox/initializers/rirs.yml") - -if rirs is None: - sys.exit() - -for params in rirs: - matching_params, defaults = split_params(params) - rir, created = RIR.objects.get_or_create(**matching_params, defaults=defaults) - - if created: - print("πŸ—ΊοΈ Created RIR", rir.name) diff --git a/startup_scripts/260_asns.py b/startup_scripts/260_asns.py deleted file mode 100644 index 93776d2..0000000 --- a/startup_scripts/260_asns.py +++ /dev/null @@ -1,34 +0,0 @@ -import sys - -from ipam.models import ASN, RIR -from startup_script_utils import load_yaml, split_params -from tenancy.models import Tenant - -asns = load_yaml("/opt/netbox/initializers/asns.yml") - -if asns is None: - sys.exit() - -match_params = ["asn", "rir"] -required_assocs = {"rir": (RIR, "name")} -optional_assocs = {"tenant": (Tenant, "name")} - -for params in asns: - 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) - - matching_params, defaults = split_params(params, match_params) - asn, created = ASN.objects.get_or_create(**matching_params, defaults=defaults) - - if created: - print(f"πŸ”‘ Created ASN {asn.asn}") diff --git a/startup_scripts/270_aggregates.py b/startup_scripts/270_aggregates.py deleted file mode 100644 index d37c4cc..0000000 --- a/startup_scripts/270_aggregates.py +++ /dev/null @@ -1,48 +0,0 @@ -import sys - -from ipam.models import RIR, Aggregate -from netaddr import IPNetwork -from startup_script_utils import ( - load_yaml, - pop_custom_fields, - set_custom_fields_values, - split_params, -) -from tenancy.models import Tenant - -aggregates = load_yaml("/opt/netbox/initializers/aggregates.yml") - -if aggregates is None: - sys.exit() - -match_params = ["prefix", "rir"] -required_assocs = {"rir": (RIR, "name")} -optional_assocs = { - "tenant": (Tenant, "name"), -} - -for params in aggregates: - custom_field_data = pop_custom_fields(params) - - params["prefix"] = IPNetwork(params["prefix"]) - - 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) - - matching_params, defaults = split_params(params, match_params) - aggregate, created = Aggregate.objects.get_or_create(**matching_params, defaults=defaults) - - if created: - print("πŸ—žοΈ Created Aggregate", aggregate.prefix) - - set_custom_fields_values(aggregate, custom_field_data) diff --git a/startup_scripts/280_prefix_vlan_roles.py b/startup_scripts/280_prefix_vlan_roles.py deleted file mode 100644 index c17feb8..0000000 --- a/startup_scripts/280_prefix_vlan_roles.py +++ /dev/null @@ -1,16 +0,0 @@ -import sys - -from ipam.models import Role -from startup_script_utils import load_yaml, split_params - -roles = load_yaml("/opt/netbox/initializers/prefix_vlan_roles.yml") - -if roles is None: - sys.exit() - -for params in roles: - matching_params, defaults = split_params(params) - role, created = Role.objects.get_or_create(**matching_params, defaults=defaults) - - if created: - print("⛹️‍ Created Prefix/VLAN Role", role.name) diff --git a/startup_scripts/290_cluster_types.py b/startup_scripts/290_cluster_types.py deleted file mode 100644 index 642bc7a..0000000 --- a/startup_scripts/290_cluster_types.py +++ /dev/null @@ -1,16 +0,0 @@ -import sys - -from startup_script_utils import load_yaml, split_params -from virtualization.models import ClusterType - -cluster_types = load_yaml("/opt/netbox/initializers/cluster_types.yml") - -if cluster_types is None: - sys.exit() - -for params in cluster_types: - matching_params, defaults = split_params(params) - cluster_type, created = ClusterType.objects.get_or_create(**matching_params, defaults=defaults) - - if created: - print("🧰 Created Cluster Type", cluster_type.name) diff --git a/startup_scripts/300_cluster_groups.py b/startup_scripts/300_cluster_groups.py deleted file mode 100644 index a8e3573..0000000 --- a/startup_scripts/300_cluster_groups.py +++ /dev/null @@ -1,18 +0,0 @@ -import sys - -from startup_script_utils import load_yaml, split_params -from virtualization.models import ClusterGroup - -cluster_groups = load_yaml("/opt/netbox/initializers/cluster_groups.yml") - -if cluster_groups is None: - sys.exit() - -for params in cluster_groups: - matching_params, defaults = split_params(params) - cluster_group, created = ClusterGroup.objects.get_or_create( - **matching_params, defaults=defaults - ) - - if created: - print("πŸ—„οΈ Created Cluster Group", cluster_group.name) diff --git a/startup_scripts/310_clusters.py b/startup_scripts/310_clusters.py deleted file mode 100644 index e36a5f9..0000000 --- a/startup_scripts/310_clusters.py +++ /dev/null @@ -1,48 +0,0 @@ -import sys - -from dcim.models import Site -from startup_script_utils import ( - load_yaml, - pop_custom_fields, - set_custom_fields_values, - split_params, -) -from tenancy.models import Tenant -from virtualization.models import Cluster, ClusterGroup, ClusterType - -clusters = load_yaml("/opt/netbox/initializers/clusters.yml") - -if clusters is None: - sys.exit() - -match_params = ["name", "type"] -required_assocs = {"type": (ClusterType, "name")} -optional_assocs = { - "site": (Site, "name"), - "group": (ClusterGroup, "name"), - "tenant": (Tenant, "name"), -} - -for params in clusters: - custom_field_data = pop_custom_fields(params) - - 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) - - matching_params, defaults = split_params(params, match_params) - cluster, created = Cluster.objects.get_or_create(**matching_params, defaults=defaults) - - if created: - print("πŸ—„οΈ Created cluster", cluster.name) - - set_custom_fields_values(cluster, custom_field_data) diff --git a/startup_scripts/320_vlan_groups.py b/startup_scripts/320_vlan_groups.py deleted file mode 100644 index a81e695..0000000 --- a/startup_scripts/320_vlan_groups.py +++ /dev/null @@ -1,47 +0,0 @@ -import sys - -from django.contrib.contenttypes.models import ContentType -from ipam.models import VLANGroup -from startup_script_utils import ( - load_yaml, - pop_custom_fields, - set_custom_fields_values, - split_params, -) - -vlan_groups = load_yaml("/opt/netbox/initializers/vlan_groups.yml") - -if vlan_groups is None: - sys.exit() - -optional_assocs = {"scope": (None, "name")} - -for params in vlan_groups: - custom_field_data = pop_custom_fields(params) - - for assoc, details in optional_assocs.items(): - if assoc in params: - model, field = details - query = {field: params.pop(assoc)} - # Get model from Contenttype - scope_type = params.pop("scope_type", None) - if not scope_type: - print(f"VLAN Group '{params['name']}': scope_type is missing from VLAN Group") - continue - app_label, model = str(scope_type).split(".") - ct = ContentType.objects.filter(app_label=app_label, model=model).first() - if not ct: - print( - f"VLAN Group '{params['name']}': ContentType for " - + f"app_label = '{app_label}' and model = '{model}' not found" - ) - continue - params["scope_id"] = ct.model_class().objects.get(**query).id - - matching_params, defaults = split_params(params) - vlan_group, created = VLANGroup.objects.get_or_create(**matching_params, defaults=defaults) - - if created: - print("🏘️ Created VLAN Group", vlan_group.name) - - set_custom_fields_values(vlan_group, custom_field_data) diff --git a/startup_scripts/330_vlans.py b/startup_scripts/330_vlans.py deleted file mode 100644 index e378881..0000000 --- a/startup_scripts/330_vlans.py +++ /dev/null @@ -1,43 +0,0 @@ -import sys - -from dcim.models import Site -from ipam.models import VLAN, Role, VLANGroup -from startup_script_utils import ( - load_yaml, - pop_custom_fields, - set_custom_fields_values, - split_params, -) -from tenancy.models import Tenant, TenantGroup - -vlans = load_yaml("/opt/netbox/initializers/vlans.yml") - -if vlans is None: - sys.exit() - -match_params = ["name", "vid"] -optional_assocs = { - "site": (Site, "name"), - "tenant": (Tenant, "name"), - "tenant_group": (TenantGroup, "name"), - "group": (VLANGroup, "name"), - "role": (Role, "name"), -} - -for params in vlans: - custom_field_data = pop_custom_fields(params) - - 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) - - matching_params, defaults = split_params(params, match_params) - vlan, created = VLAN.objects.get_or_create(**matching_params, defaults=defaults) - - if created: - print("🏠 Created VLAN", vlan.name) - - set_custom_fields_values(vlan, custom_field_data) diff --git a/startup_scripts/340_virtual_machines.py b/startup_scripts/340_virtual_machines.py deleted file mode 100644 index 51d3bc6..0000000 --- a/startup_scripts/340_virtual_machines.py +++ /dev/null @@ -1,54 +0,0 @@ -import sys - -from dcim.models import DeviceRole, Platform -from startup_script_utils import ( - load_yaml, - pop_custom_fields, - set_custom_fields_values, - split_params, -) -from tenancy.models import Tenant -from virtualization.models import Cluster, VirtualMachine - -virtual_machines = load_yaml("/opt/netbox/initializers/virtual_machines.yml") - -if virtual_machines is None: - sys.exit() - -match_params = ["cluster", "name"] -required_assocs = {"cluster": (Cluster, "name")} -optional_assocs = { - "tenant": (Tenant, "name"), - "platform": (Platform, "name"), - "role": (DeviceRole, "name"), -} - -for params in virtual_machines: - custom_field_data = pop_custom_fields(params) - - # primary ips are handled later in `270_primary_ips.py` - params.pop("primary_ip4", None) - params.pop("primary_ip6", None) - - 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) - - matching_params, defaults = split_params(params, match_params) - virtual_machine, created = VirtualMachine.objects.get_or_create( - **matching_params, defaults=defaults - ) - - if created: - print("πŸ–₯️ Created virtual machine", virtual_machine.name) - - set_custom_fields_values(virtual_machine, custom_field_data) diff --git a/startup_scripts/350_virtualization_interfaces.py b/startup_scripts/350_virtualization_interfaces.py deleted file mode 100644 index c6bbfa0..0000000 --- a/startup_scripts/350_virtualization_interfaces.py +++ /dev/null @@ -1,34 +0,0 @@ -import sys - -from startup_script_utils import ( - load_yaml, - pop_custom_fields, - set_custom_fields_values, - split_params, -) -from virtualization.models import VirtualMachine, VMInterface - -interfaces = load_yaml("/opt/netbox/initializers/virtualization_interfaces.yml") - -if interfaces is None: - sys.exit() - -match_params = ["name", "virtual_machine"] -required_assocs = {"virtual_machine": (VirtualMachine, "name")} - -for params in interfaces: - custom_field_data = pop_custom_fields(params) - - for assoc, details in required_assocs.items(): - model, field = details - query = {field: params.pop(assoc)} - - params[assoc] = model.objects.get(**query) - - matching_params, defaults = split_params(params, match_params) - interface, created = VMInterface.objects.get_or_create(**matching_params, defaults=defaults) - - if created: - print("🧷 Created interface", interface.name, interface.virtual_machine.name) - - set_custom_fields_values(interface, custom_field_data) diff --git a/startup_scripts/360_prefixes.py b/startup_scripts/360_prefixes.py deleted file mode 100644 index ffb774b..0000000 --- a/startup_scripts/360_prefixes.py +++ /dev/null @@ -1,46 +0,0 @@ -import sys - -from dcim.models import Site -from ipam.models import VLAN, VRF, Prefix, Role -from netaddr import IPNetwork -from startup_script_utils import ( - load_yaml, - pop_custom_fields, - set_custom_fields_values, - split_params, -) -from tenancy.models import Tenant, TenantGroup - -prefixes = load_yaml("/opt/netbox/initializers/prefixes.yml") - -if prefixes is None: - sys.exit() - -match_params = ["prefix", "site", "vrf", "vlan"] -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_field_data = pop_custom_fields(params) - - 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) - - matching_params, defaults = split_params(params, match_params) - prefix, created = Prefix.objects.get_or_create(**matching_params, defaults=defaults) - - if created: - print("πŸ“Œ Created Prefix", prefix.prefix) - - set_custom_fields_values(prefix, custom_field_data) diff --git a/startup_scripts/370_ip_addresses.py b/startup_scripts/370_ip_addresses.py deleted file mode 100644 index b35cb49..0000000 --- a/startup_scripts/370_ip_addresses.py +++ /dev/null @@ -1,70 +0,0 @@ -import sys - -from dcim.models import Device, Interface -from django.contrib.contenttypes.models import ContentType -from django.db.models import Q -from ipam.models import VRF, IPAddress -from netaddr import IPNetwork -from startup_script_utils import ( - load_yaml, - pop_custom_fields, - set_custom_fields_values, - split_params, -) -from tenancy.models import Tenant -from virtualization.models import VirtualMachine, VMInterface - -ip_addresses = load_yaml("/opt/netbox/initializers/ip_addresses.yml") - -if ip_addresses is None: - sys.exit() - -match_params = ["address", "vrf"] -optional_assocs = { - "tenant": (Tenant, "name"), - "vrf": (VRF, "name"), - "interface": (Interface, "name"), -} - -vm_interface_ct = ContentType.objects.filter( - Q(app_label="virtualization", model="vminterface") -).first() -interface_ct = ContentType.objects.filter(Q(app_label="dcim", model="interface")).first() - -for params in ip_addresses: - custom_field_data = pop_custom_fields(params) - - vm = params.pop("virtual_machine", None) - device = params.pop("device", None) - 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() - - 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 = {"name": params.pop(assoc), "virtual_machine_id": vm_id} - params["assigned_object_type"] = vm_interface_ct - params["assigned_object_id"] = VMInterface.objects.get(**query).id - elif device: - dev_id = Device.objects.get(name=device).id - query = {"name": params.pop(assoc), "device_id": dev_id} - params["assigned_object_type"] = interface_ct - params["assigned_object_id"] = Interface.objects.get(**query).id - else: - query = {field: params.pop(assoc)} - - params[assoc] = model.objects.get(**query) - - matching_params, defaults = split_params(params, match_params) - ip_address, created = IPAddress.objects.get_or_create(**matching_params, defaults=defaults) - - if created: - print("🧬 Created IP Address", ip_address.address) - - set_custom_fields_values(ip_address, custom_field_data) diff --git a/startup_scripts/380_primary_ips.py b/startup_scripts/380_primary_ips.py deleted file mode 100644 index 11c3179..0000000 --- a/startup_scripts/380_primary_ips.py +++ /dev/null @@ -1,47 +0,0 @@ -import sys - -from dcim.models import Device -from ipam.models import IPAddress -from startup_script_utils import load_yaml -from virtualization.models import VirtualMachine - - -def link_primary_ip(assets, asset_model): - for params in assets: - primary_ip_fields = set(params) & {"primary_ip4", "primary_ip6"} - if not primary_ip_fields: - continue - - for assoc, details in optional_assocs.items(): - if assoc in params: - model, field = details - query = {field: params.pop(assoc)} - - try: - params[assoc] = model.objects.get(**query) - except model.DoesNotExist: - primary_ip_fields -= {assoc} - print(f"⚠️ IP Address '{query[field]}' not found") - - asset = asset_model.objects.get(name=params["name"]) - for field in primary_ip_fields: - if getattr(asset, field) != params[field]: - setattr(asset, field, params[field]) - print(f"πŸ”— Define primary IP '{params[field].address}' on '{asset.name}'") - asset.save() - - -devices = load_yaml("/opt/netbox/initializers/devices.yml") -virtual_machines = load_yaml("/opt/netbox/initializers/virtual_machines.yml") - -optional_assocs = { - "primary_ip4": (IPAddress, "address"), - "primary_ip6": (IPAddress, "address"), -} - -if devices is None and virtual_machines is None: - sys.exit() -if devices is not None: - link_primary_ip(devices, Device) -if virtual_machines is not None: - link_primary_ip(virtual_machines, VirtualMachine) diff --git a/startup_scripts/400_services.py b/startup_scripts/400_services.py deleted file mode 100644 index a92d82b..0000000 --- a/startup_scripts/400_services.py +++ /dev/null @@ -1,32 +0,0 @@ -import sys - -from dcim.models import Device -from ipam.models import Service -from startup_script_utils import load_yaml, split_params -from virtualization.models import VirtualMachine - -services = load_yaml("/opt/netbox/initializers/services.yml") - -if services is None: - sys.exit() - -match_params = ["name", "device", "virtual_machine"] -optional_assocs = { - "device": (Device, "name"), - "virtual_machine": (VirtualMachine, "name"), -} - -for params in services: - - 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) - - matching_params, defaults = split_params(params, match_params) - service, created = Service.objects.get_or_create(**params) - - if created: - print("🧰 Created Service", service.name) diff --git a/startup_scripts/420_providers.py b/startup_scripts/420_providers.py deleted file mode 100644 index e68e1f3..0000000 --- a/startup_scripts/420_providers.py +++ /dev/null @@ -1,25 +0,0 @@ -import sys - -from circuits.models import Provider -from startup_script_utils import ( - load_yaml, - pop_custom_fields, - set_custom_fields_values, - split_params, -) - -providers = load_yaml("/opt/netbox/initializers/providers.yml") - -if providers is None: - sys.exit() - -for params in providers: - custom_field_data = pop_custom_fields(params) - - matching_params, defaults = split_params(params) - provider, created = Provider.objects.get_or_create(**matching_params, defaults=defaults) - - if created: - print("πŸ“‘ Created provider", provider.name) - - set_custom_fields_values(provider, custom_field_data) diff --git a/startup_scripts/440_circuit_types.py b/startup_scripts/440_circuit_types.py deleted file mode 100644 index 6d3f7fa..0000000 --- a/startup_scripts/440_circuit_types.py +++ /dev/null @@ -1,25 +0,0 @@ -import sys - -from circuits.models import CircuitType -from startup_script_utils import ( - load_yaml, - pop_custom_fields, - set_custom_fields_values, - split_params, -) - -circuit_types = load_yaml("/opt/netbox/initializers/circuit_types.yml") - -if circuit_types is None: - sys.exit() - -for params in circuit_types: - custom_field_data = pop_custom_fields(params) - - matching_params, defaults = split_params(params) - circuit_type, created = CircuitType.objects.get_or_create(**matching_params, defaults=defaults) - - if created: - print("⚑ Created Circuit Type", circuit_type.name) - - set_custom_fields_values(circuit_type, custom_field_data) diff --git a/startup_scripts/450_circuits.py b/startup_scripts/450_circuits.py deleted file mode 100644 index 68f159b..0000000 --- a/startup_scripts/450_circuits.py +++ /dev/null @@ -1,43 +0,0 @@ -import sys - -from circuits.models import Circuit, CircuitType, Provider -from startup_script_utils import ( - load_yaml, - pop_custom_fields, - set_custom_fields_values, - split_params, -) -from tenancy.models import Tenant - -circuits = load_yaml("/opt/netbox/initializers/circuits.yml") - -if circuits is None: - sys.exit() - -match_params = ["cid", "provider", "type"] -required_assocs = {"provider": (Provider, "name"), "type": (CircuitType, "name")} -optional_assocs = {"tenant": (Tenant, "name")} - -for params in circuits: - custom_field_data = pop_custom_fields(params) - - 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) - - matching_params, defaults = split_params(params, match_params) - circuit, created = Circuit.objects.get_or_create(**matching_params, defaults=defaults) - - if created: - print("⚑ Created Circuit", circuit.cid) - - set_custom_fields_values(circuit, custom_field_data) diff --git a/startup_scripts/460_cables.py b/startup_scripts/460_cables.py deleted file mode 100644 index 6e4cec2..0000000 --- a/startup_scripts/460_cables.py +++ /dev/null @@ -1,227 +0,0 @@ -import sys -from typing import Tuple - -from circuits.models import Circuit, CircuitTermination, ProviderNetwork -from dcim.models import ( - Cable, - ConsolePort, - ConsoleServerPort, - FrontPort, - Interface, - PowerFeed, - PowerOutlet, - PowerPanel, - PowerPort, - RearPort, - Site, -) -from django.contrib.contenttypes.models import ContentType -from django.db.models import Q -from startup_script_utils import load_yaml - -CONSOLE_PORT_TERMINATION = ContentType.objects.get_for_model(ConsolePort) -CONSOLE_SERVER_PORT_TERMINATION = ContentType.objects.get_for_model(ConsoleServerPort) -FRONT_PORT_TERMINATION = ContentType.objects.get_for_model(FrontPort) -REAR_PORT_TERMINATION = ContentType.objects.get_for_model(RearPort) -FRONT_AND_REAR = [FRONT_PORT_TERMINATION, REAR_PORT_TERMINATION] -POWER_PORT_TERMINATION = ContentType.objects.get_for_model(PowerPort) -POWER_OUTLET_TERMINATION = ContentType.objects.get_for_model(PowerOutlet) -POWER_FEED_TERMINATION = ContentType.objects.get_for_model(PowerFeed) -POWER_TERMINATIONS = [POWER_PORT_TERMINATION, POWER_OUTLET_TERMINATION, POWER_FEED_TERMINATION] - -VIRTUAL_INTERFACES = ["bridge", "lag", "virtual"] - - -def get_termination_object(params: dict, side: str): - klass = params.pop(f"termination_{side}_class") - name = params.pop(f"termination_{side}_name", None) - device = params.pop(f"termination_{side}_device", None) - feed_params = params.pop(f"termination_{side}_feed", None) - circuit_params = params.pop(f"termination_{side}_circuit", {}) - - if name and device: - termination = klass.objects.get(name=name, device__name=device) - return termination - elif feed_params: - q = { - "name": feed_params["power_panel"]["name"], - "site__name": feed_params["power_panel"]["site"], - } - power_panel = PowerPanel.objects.get(**q) - termination = PowerFeed.objects.get(name=feed_params["name"], power_panel=power_panel) - return termination - elif circuit_params: - circuit = Circuit.objects.get(cid=circuit_params.pop("cid")) - term_side = circuit_params.pop("term_side").upper() - - site_name = circuit_params.pop("site", None) - provider_network = circuit_params.pop("provider_network", None) - - if site_name: - circuit_params["site"] = Site.objects.get(name=site_name) - elif provider_network: - circuit_params["provider_network"] = ProviderNetwork.objects.get(name=provider_network) - else: - raise ValueError( - f"⚠️ Missing one of required parameters: 'site' or 'provider_network' " - f"for side {term_side} of circuit {circuit}" - ) - - termination, created = CircuitTermination.objects.get_or_create( - circuit=circuit, term_side=term_side, defaults=circuit_params - ) - if created: - print(f"⚑ Created new CircuitTermination {termination}") - - return termination - - raise ValueError( - f"⚠️ Missing parameters for termination_{side}. " - "Need termination_{side}_name AND termination_{side}_device OR termination_{side}_circuit" - ) - - -def get_termination_class_by_name(port_class: str): - if not port_class: - return Interface - - return globals()[port_class] - - -def cable_in_cables(term_a: tuple, term_b: tuple) -> bool: - """Check if cable exist for given terminations. - Each tuple should consist termination object and termination type - """ - - cable = Cable.objects.filter( - Q( - termination_a_id=term_a[0].id, - termination_a_type=term_a[1], - termination_b_id=term_b[0].id, - termination_b_type=term_b[1], - ) - | Q( - termination_a_id=term_b[0].id, - termination_a_type=term_b[1], - termination_b_id=term_a[0].id, - termination_b_type=term_a[1], - ) - ) - return cable.exists() - - -def check_termination_types(type_a, type_b) -> Tuple[bool, str]: - if type_a in POWER_TERMINATIONS and type_b in POWER_TERMINATIONS: - if type_a == type_b: - return False, "Can't connect the same power terminations together" - elif ( - type_a == POWER_OUTLET_TERMINATION - and type_b == POWER_FEED_TERMINATION - or type_a == POWER_FEED_TERMINATION - and type_b == POWER_OUTLET_TERMINATION - ): - return False, "PowerOutlet can't be connected with PowerFeed" - elif type_a in POWER_TERMINATIONS or type_b in POWER_TERMINATIONS: - return False, "Can't mix power terminations with port terminations" - elif type_a in FRONT_AND_REAR or type_b in FRONT_AND_REAR: - return True, "" - elif ( - type_a == CONSOLE_PORT_TERMINATION - and type_b != CONSOLE_SERVER_PORT_TERMINATION - or type_b == CONSOLE_PORT_TERMINATION - and type_a != CONSOLE_SERVER_PORT_TERMINATION - ): - return False, "ConsolePorts can only be connected to ConsoleServerPorts or Front/Rear ports" - return True, "" - - -def get_cable_name(termination_a: tuple, termination_b: tuple) -> str: - """Returns name of a cable in format: - device_a interface_a <---> interface_b device_b - or for circuits: - circuit_a termination_a <---> termination_b circuit_b - """ - cable_name = [] - - for is_side_b, termination in enumerate([termination_a, termination_b]): - try: - power_panel_id = getattr(termination[0], "power_panel_id", None) - if power_panel_id: - power_feed = PowerPanel.objects.get(id=power_panel_id) - segment = [f"{power_feed}", f"{termination[0]}"] - else: - segment = [f"{termination[0].device}", f"{termination[0]}"] - except AttributeError: - segment = [f"{termination[0].circuit.cid}", f"{termination[0]}"] - - if is_side_b: - segment.reverse() - - cable_name.append(" ".join(segment)) - - return " <---> ".join(cable_name) - - -def check_interface_types(*args): - for termination in args: - try: - if termination.type in VIRTUAL_INTERFACES: - raise Exception( - f"⚠️ Virtual interfaces are not supported for cabling. " - f"Termination {termination.device} {termination} {termination.type}" - ) - except AttributeError: - # CircuitTermination doesn't have a type field - pass - - -def check_terminations_are_free(*args): - any_failed = False - for termination in args: - if termination.cable_id: - any_failed = True - print( - f"⚠️ Termination {termination} is already occupied " - f"with cable #{termination.cable_id}" - ) - if any_failed: - raise Exception("⚠️ At least one end of the cable is already occupied.") - - -cables = load_yaml("/opt/netbox/initializers/cables.yml") - -if cables is None: - sys.exit() - -for params in cables: - params["termination_a_class"] = get_termination_class_by_name(params.get("termination_a_class")) - params["termination_b_class"] = get_termination_class_by_name(params.get("termination_b_class")) - - term_a = get_termination_object(params, side="a") - term_b = get_termination_object(params, side="b") - - check_interface_types(term_a, term_b) - - term_a_ct = ContentType.objects.get_for_model(term_a) - term_b_ct = ContentType.objects.get_for_model(term_b) - - types_ok, msg = check_termination_types(term_a_ct, term_b_ct) - cable_name = get_cable_name((term_a, term_a_ct), (term_b, term_b_ct)) - - if not types_ok: - print(f"⚠️ Invalid termination types for {cable_name}. {msg}") - continue - - if cable_in_cables((term_a, term_a_ct), (term_b, term_b_ct)): - continue - - check_terminations_are_free(term_a, term_b) - - params["termination_a_id"] = term_a.id - params["termination_b_id"] = term_b.id - params["termination_a_type"] = term_a_ct - params["termination_b_type"] = term_b_ct - - cable = Cable.objects.create(**params) - - print(f"🧷 Created cable {cable} {cable_name}") diff --git a/startup_scripts/470_contact_groups.py b/startup_scripts/470_contact_groups.py deleted file mode 100644 index f5fc4fd..0000000 --- a/startup_scripts/470_contact_groups.py +++ /dev/null @@ -1,36 +0,0 @@ -import sys - -from startup_script_utils import ( - load_yaml, - pop_custom_fields, - set_custom_fields_values, - split_params, -) -from tenancy.models import ContactGroup - -contact_groups = load_yaml("/opt/netbox/initializers/contact_groups.yml") - -if contact_groups is None: - sys.exit() - -optional_assocs = {"parent": (ContactGroup, "name")} - -for params in contact_groups: - custom_field_data = pop_custom_fields(params) - - 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) - - matching_params, defaults = split_params(params) - contact_group, created = ContactGroup.objects.get_or_create( - **matching_params, defaults=defaults - ) - - if created: - print("πŸ”³ Created Contact Group", contact_group.name) - - set_custom_fields_values(contact_group, custom_field_data) diff --git a/startup_scripts/480_contact_roles.py b/startup_scripts/480_contact_roles.py deleted file mode 100644 index 1b3d8bc..0000000 --- a/startup_scripts/480_contact_roles.py +++ /dev/null @@ -1,26 +0,0 @@ -import sys - -from startup_script_utils import ( - load_yaml, - pop_custom_fields, - set_custom_fields_values, - split_params, -) -from tenancy.models import ContactRole - -contact_roles = load_yaml("/opt/netbox/initializers/contact_roles.yml") - -if contact_roles is None: - sys.exit() - - -for params in contact_roles: - custom_field_data = pop_custom_fields(params) - - matching_params, defaults = split_params(params) - contact_role, created = ContactRole.objects.get_or_create(**matching_params, defaults=defaults) - - if created: - print("πŸ”³ Created Contact Role", contact_role.name) - - set_custom_fields_values(contact_role, custom_field_data) diff --git a/startup_scripts/490_contacts.py b/startup_scripts/490_contacts.py deleted file mode 100644 index 80d75d6..0000000 --- a/startup_scripts/490_contacts.py +++ /dev/null @@ -1,34 +0,0 @@ -import sys - -from startup_script_utils import ( - load_yaml, - pop_custom_fields, - set_custom_fields_values, - split_params, -) -from tenancy.models import Contact, ContactGroup - -contacts = load_yaml("/opt/netbox/initializers/contacts.yml") - -if contacts is None: - sys.exit() - -optional_assocs = {"group": (ContactGroup, "name")} - -for params in contacts: - custom_field_data = pop_custom_fields(params) - - 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) - - matching_params, defaults = split_params(params) - contact, created = Contact.objects.get_or_create(**matching_params, defaults=defaults) - - if created: - print("πŸ‘©β€πŸ’» Created Contact", contact.name) - - set_custom_fields_values(contact, custom_field_data) diff --git a/startup_scripts/__main__.py b/startup_scripts/__main__.py deleted file mode 100644 index 3a3a3bd..0000000 --- a/startup_scripts/__main__.py +++ /dev/null @@ -1,31 +0,0 @@ -#!/usr/bin/env python3 - -import runpy -from os import scandir -from os.path import abspath, dirname - -this_dir = dirname(abspath(__file__)) - - -def filename(f): - return f.name - - -with scandir(this_dir) as it: - for f in sorted(it, key=filename): - if not f.is_file(): - continue - - if f.name.startswith("__"): - continue - - if not f.name.endswith(".py"): - continue - - print(f"▢️ Running the startup script {f.path}") - try: - runpy.run_path(f.path) - except SystemExit as e: - if e.code is not None and e.code != 0: - print(f"‼️ The startup script {f.path} returned with code {e.code}, exiting.") - raise diff --git a/startup_scripts/startup_script_utils/__init__.py b/startup_scripts/startup_script_utils/__init__.py deleted file mode 100644 index 4c25c6b..0000000 --- a/startup_scripts/startup_script_utils/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from .custom_fields import pop_custom_fields, set_custom_fields_values -from .load_yaml import load_yaml -from .utils import split_params diff --git a/startup_scripts/startup_script_utils/custom_fields.py b/startup_scripts/startup_script_utils/custom_fields.py deleted file mode 100644 index 74e8a25..0000000 --- a/startup_scripts/startup_script_utils/custom_fields.py +++ /dev/null @@ -1,45 +0,0 @@ -from django.contrib.contenttypes.models import ContentType -from django.core.exceptions import ObjectDoesNotExist -from extras.models import CustomField - - -def set_custom_fields_values(entity, custom_field_data): - if not custom_field_data: - return - - missing_cfs = [] - save = False - for key, value in custom_field_data.items(): - try: - cf = CustomField.objects.get(name=key) - except ObjectDoesNotExist: - missing_cfs.append(key) - else: - ct = ContentType.objects.get_for_model(entity) - if ct not in cf.content_types.all(): - print( - f"⚠️ Custom field {key} is not enabled for {entity}'s model!" - "Please check the 'on_objects' for that custom field in custom_fields.yml" - ) - elif key not in entity.custom_field_data: - entity.custom_field_data[key] = value - save = True - - if missing_cfs: - raise Exception( - f"⚠️ Custom field(s) '{missing_cfs}' requested for {entity} but not found in Netbox!" - "Please chceck the custom_fields.yml" - ) - - if save: - entity.save() - - -def pop_custom_fields(params): - if "custom_field_data" in params: - return params.pop("custom_field_data") - elif "custom_fields" in params: - print("⚠️ Please rename 'custom_fields' to 'custom_field_data'!") - return params.pop("custom_fields") - - return None diff --git a/startup_scripts/startup_script_utils/load_yaml.py b/startup_scripts/startup_script_utils/load_yaml.py deleted file mode 100644 index 048ac5c..0000000 --- a/startup_scripts/startup_script_utils/load_yaml.py +++ /dev/null @@ -1,12 +0,0 @@ -from pathlib import Path - -from ruamel.yaml import YAML - - -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) diff --git a/startup_scripts/startup_script_utils/utils.py b/startup_scripts/startup_script_utils/utils.py deleted file mode 100644 index e31f796..0000000 --- a/startup_scripts/startup_script_utils/utils.py +++ /dev/null @@ -1,15 +0,0 @@ -from typing import Tuple - - -def split_params(params: dict, unique_params: list = None) -> Tuple[dict, dict]: - """Split params dict into dict with matching params and a dict with default values""" - - if unique_params is None: - unique_params = ["name", "slug"] - - matching_params = {} - for unique_param in unique_params: - param = params.pop(unique_param, None) - if param: - matching_params[unique_param] = param - return matching_params, params diff --git a/test-configuration/logging.py b/test-configuration/logging.py new file mode 100644 index 0000000..ab15e2a --- /dev/null +++ b/test-configuration/logging.py @@ -0,0 +1,4 @@ +LOGGING = { + 'version': 1, + 'disable_existing_loggers': True +} diff --git a/test.sh b/test.sh index d2091e4..ca36a8f 100755 --- a/test.sh +++ b/test.sh @@ -37,21 +37,8 @@ fi # The docker compose command to use doco="docker-compose --file docker-compose.test.yml --project-name netbox_docker_test_${1}" -INITIALIZERS_DIR=".initializers" - test_setup() { echo "πŸ— Setup up test environment" - if [ -d "${INITIALIZERS_DIR}" ]; then - rm -rf "${INITIALIZERS_DIR}" - fi - - mkdir "${INITIALIZERS_DIR}" - ( - cd initializers - for script in *.yml; do - sed -E 's/^# //' "${script}" >"../${INITIALIZERS_DIR}/${script}" - done - ) } test_netbox_unit_tests() { @@ -59,19 +46,9 @@ test_netbox_unit_tests() { $doco run --rm netbox /opt/netbox/venv/bin/python /opt/netbox/netbox/manage.py test } -test_initializers() { - echo "🏭 Testing Initializers" - export INITIALIZERS_DIR - $doco run --rm netbox /opt/netbox/docker-entrypoint.sh ./manage.py check -} - test_cleanup() { echo "πŸ’£ Cleaning Up" $doco down -v - - if [ -d "${INITIALIZERS_DIR}" ]; then - rm -rf "${INITIALIZERS_DIR}" - fi } echo "🐳🐳🐳 Start testing '${IMAGE}'" @@ -81,6 +58,5 @@ trap test_cleanup EXIT ERR test_setup test_netbox_unit_tests -test_initializers echo "🐳🐳🐳 Done testing '${IMAGE}'"