72 changed files with 2372 additions and 1 deletions
-
26.drone.yml
-
1.gitignore
-
177LICENSE
-
10README.md
-
74docker-compose.yml
-
2env/.gitignore
-
248etc/netbox/etc/netbox/config/configuration.py
-
55etc/netbox/etc/netbox/config/extra.py
-
84etc/netbox/etc/netbox/config/ldap/ldap_config.py
-
46etc/netbox/etc/netbox/reports/devices.py.example
-
0etc/netbox/etc/netbox/scripts/__init__.py
-
6etc/netbox/opt/netbox/initializers/aggregates.yml
-
2etc/netbox/opt/netbox/initializers/cluster_types.yml
-
5etc/netbox/opt/netbox/initializers/clusters.yml
-
97etc/netbox/opt/netbox/initializers/custom_fields.yml
-
18etc/netbox/opt/netbox/initializers/dcim_interfaces.yml
-
15etc/netbox/opt/netbox/initializers/device_roles.yml
-
23etc/netbox/opt/netbox/initializers/device_types.yml
-
44etc/netbox/opt/netbox/initializers/devices.yml
-
35etc/netbox/opt/netbox/initializers/groups.yml
-
44etc/netbox/opt/netbox/initializers/ip_addresses.yml
-
6etc/netbox/opt/netbox/initializers/manufacturers.yml
-
15etc/netbox/opt/netbox/initializers/platforms.yml
-
2etc/netbox/opt/netbox/initializers/prefix_vlan_roles.yml
-
29etc/netbox/opt/netbox/initializers/prefixes.yml
-
3etc/netbox/opt/netbox/initializers/rack_groups.yml
-
12etc/netbox/opt/netbox/initializers/rack_roles.yml
-
41etc/netbox/opt/netbox/initializers/racks.yml
-
2etc/netbox/opt/netbox/initializers/regions.yml
-
9etc/netbox/opt/netbox/initializers/rirs.yml
-
32etc/netbox/opt/netbox/initializers/sites.yml
-
4etc/netbox/opt/netbox/initializers/tenant_groups.yml
-
5etc/netbox/opt/netbox/initializers/tenants.yml
-
23etc/netbox/opt/netbox/initializers/users.yml
-
28etc/netbox/opt/netbox/initializers/virtual_machines.yml
-
12etc/netbox/opt/netbox/initializers/virtualization_interfaces.yml
-
6etc/netbox/opt/netbox/initializers/vlan_groups.yml
-
19etc/netbox/opt/netbox/initializers/vlans.yml
-
8etc/netbox/opt/netbox/initializers/vrfs.yml
-
23etc/netbox/opt/netbox/startup_scripts/000_users.py
-
23etc/netbox/opt/netbox/startup_scripts/010_groups.py
-
54etc/netbox/opt/netbox/startup_scripts/020_custom_fields.py
-
26etc/netbox/opt/netbox/startup_scripts/030_regions.py
-
41etc/netbox/opt/netbox/startup_scripts/040_sites.py
-
14etc/netbox/opt/netbox/startup_scripts/050_manufacturers.py
-
51etc/netbox/opt/netbox/startup_scripts/060_device_types.py
-
23etc/netbox/opt/netbox/startup_scripts/070_rack_roles.py
-
25etc/netbox/opt/netbox/startup_scripts/075_rack_groups.py
-
52etc/netbox/opt/netbox/startup_scripts/080_racks.py
-
24etc/netbox/opt/netbox/startup_scripts/090_device_roles.py
-
26etc/netbox/opt/netbox/startup_scripts/100_platforms.py
-
14etc/netbox/opt/netbox/startup_scripts/110_tenant_groups.py
-
39etc/netbox/opt/netbox/startup_scripts/120_tenants.py
-
59etc/netbox/opt/netbox/startup_scripts/130_devices.py
-
14etc/netbox/opt/netbox/startup_scripts/140_cluster_types.py
-
14etc/netbox/opt/netbox/startup_scripts/150_rirs.py
-
42etc/netbox/opt/netbox/startup_scripts/160_aggregates.py
-
51etc/netbox/opt/netbox/startup_scripts/170_clusters.py
-
42etc/netbox/opt/netbox/startup_scripts/180_vrfs.py
-
14etc/netbox/opt/netbox/startup_scripts/190_prefix_vlan_roles.py
-
40etc/netbox/opt/netbox/startup_scripts/200_vlan_groups.py
-
45etc/netbox/opt/netbox/startup_scripts/210_vlans.py
-
46etc/netbox/opt/netbox/startup_scripts/220_prefixes.py
-
56etc/netbox/opt/netbox/startup_scripts/230_virtual_machines.py
-
38etc/netbox/opt/netbox/startup_scripts/240_virtualization_interfaces.py
-
38etc/netbox/opt/netbox/startup_scripts/250_dcim_interfaces.py
-
69etc/netbox/opt/netbox/startup_scripts/260_ip_addresses.py
-
43etc/netbox/opt/netbox/startup_scripts/270_primary_ips.py
-
29etc/netbox/opt/netbox/startup_scripts/__main__.py
-
2etc/netbox/opt/netbox/startup_scripts/startup_script_utils/__init__.py
-
10etc/netbox/opt/netbox/startup_scripts/startup_script_utils/load_yaml.py
-
18etc/netbox/opt/netbox/startup_scripts/startup_script_utils/permissions.py
@ -0,0 +1,26 @@ |
|||||
|
kind: pipeline |
||||
|
type: ssh |
||||
|
name: default |
||||
|
|
||||
|
clone: |
||||
|
disable: true |
||||
|
|
||||
|
server: |
||||
|
host: |
||||
|
from_secret: host |
||||
|
user: |
||||
|
from_secret: user |
||||
|
password: |
||||
|
from_secret: password |
||||
|
|
||||
|
steps: |
||||
|
- name: release |
||||
|
commands: |
||||
|
- cd /mnt/md0/experimental/netbox |
||||
|
- git pull |
||||
|
|
||||
|
trigger: |
||||
|
branch: |
||||
|
- master |
||||
|
event: |
||||
|
- push |
@ -0,0 +1 @@ |
|||||
|
/etc/postgres |
@ -0,0 +1,177 @@ |
|||||
|
|
||||
|
Apache License |
||||
|
Version 2.0, January 2004 |
||||
|
http://www.apache.org/licenses/ |
||||
|
|
||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION |
||||
|
|
||||
|
1. Definitions. |
||||
|
|
||||
|
"License" shall mean the terms and conditions for use, reproduction, |
||||
|
and distribution as defined by Sections 1 through 9 of this document. |
||||
|
|
||||
|
"Licensor" shall mean the copyright owner or entity authorized by |
||||
|
the copyright owner that is granting the License. |
||||
|
|
||||
|
"Legal Entity" shall mean the union of the acting entity and all |
||||
|
other entities that control, are controlled by, or are under common |
||||
|
control with that entity. For the purposes of this definition, |
||||
|
"control" means (i) the power, direct or indirect, to cause the |
||||
|
direction or management of such entity, whether by contract or |
||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the |
||||
|
outstanding shares, or (iii) beneficial ownership of such entity. |
||||
|
|
||||
|
"You" (or "Your") shall mean an individual or Legal Entity |
||||
|
exercising permissions granted by this License. |
||||
|
|
||||
|
"Source" form shall mean the preferred form for making modifications, |
||||
|
including but not limited to software source code, documentation |
||||
|
source, and configuration files. |
||||
|
|
||||
|
"Object" form shall mean any form resulting from mechanical |
||||
|
transformation or translation of a Source form, including but |
||||
|
not limited to compiled object code, generated documentation, |
||||
|
and conversions to other media types. |
||||
|
|
||||
|
"Work" shall mean the work of authorship, whether in Source or |
||||
|
Object form, made available under the License, as indicated by a |
||||
|
copyright notice that is included in or attached to the work |
||||
|
(an example is provided in the Appendix below). |
||||
|
|
||||
|
"Derivative Works" shall mean any work, whether in Source or Object |
||||
|
form, that is based on (or derived from) the Work and for which the |
||||
|
editorial revisions, annotations, elaborations, or other modifications |
||||
|
represent, as a whole, an original work of authorship. For the purposes |
||||
|
of this License, Derivative Works shall not include works that remain |
||||
|
separable from, or merely link (or bind by name) to the interfaces of, |
||||
|
the Work and Derivative Works thereof. |
||||
|
|
||||
|
"Contribution" shall mean any work of authorship, including |
||||
|
the original version of the Work and any modifications or additions |
||||
|
to that Work or Derivative Works thereof, that is intentionally |
||||
|
submitted to Licensor for inclusion in the Work by the copyright owner |
||||
|
or by an individual or Legal Entity authorized to submit on behalf of |
||||
|
the copyright owner. For the purposes of this definition, "submitted" |
||||
|
means any form of electronic, verbal, or written communication sent |
||||
|
to the Licensor or its representatives, including but not limited to |
||||
|
communication on electronic mailing lists, source code control systems, |
||||
|
and issue tracking systems that are managed by, or on behalf of, the |
||||
|
Licensor for the purpose of discussing and improving the Work, but |
||||
|
excluding communication that is conspicuously marked or otherwise |
||||
|
designated in writing by the copyright owner as "Not a Contribution." |
||||
|
|
||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity |
||||
|
on behalf of whom a Contribution has been received by Licensor and |
||||
|
subsequently incorporated within the Work. |
||||
|
|
||||
|
2. Grant of Copyright License. Subject to the terms and conditions of |
||||
|
this License, each Contributor hereby grants to You a perpetual, |
||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable |
||||
|
copyright license to reproduce, prepare Derivative Works of, |
||||
|
publicly display, publicly perform, sublicense, and distribute the |
||||
|
Work and such Derivative Works in Source or Object form. |
||||
|
|
||||
|
3. Grant of Patent License. Subject to the terms and conditions of |
||||
|
this License, each Contributor hereby grants to You a perpetual, |
||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable |
||||
|
(except as stated in this section) patent license to make, have made, |
||||
|
use, offer to sell, sell, import, and otherwise transfer the Work, |
||||
|
where such license applies only to those patent claims licensable |
||||
|
by such Contributor that are necessarily infringed by their |
||||
|
Contribution(s) alone or by combination of their Contribution(s) |
||||
|
with the Work to which such Contribution(s) was submitted. If You |
||||
|
institute patent litigation against any entity (including a |
||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work |
||||
|
or a Contribution incorporated within the Work constitutes direct |
||||
|
or contributory patent infringement, then any patent licenses |
||||
|
granted to You under this License for that Work shall terminate |
||||
|
as of the date such litigation is filed. |
||||
|
|
||||
|
4. Redistribution. You may reproduce and distribute copies of the |
||||
|
Work or Derivative Works thereof in any medium, with or without |
||||
|
modifications, and in Source or Object form, provided that You |
||||
|
meet the following conditions: |
||||
|
|
||||
|
(a) You must give any other recipients of the Work or |
||||
|
Derivative Works a copy of this License; and |
||||
|
|
||||
|
(b) You must cause any modified files to carry prominent notices |
||||
|
stating that You changed the files; and |
||||
|
|
||||
|
(c) You must retain, in the Source form of any Derivative Works |
||||
|
that You distribute, all copyright, patent, trademark, and |
||||
|
attribution notices from the Source form of the Work, |
||||
|
excluding those notices that do not pertain to any part of |
||||
|
the Derivative Works; and |
||||
|
|
||||
|
(d) If the Work includes a "NOTICE" text file as part of its |
||||
|
distribution, then any Derivative Works that You distribute must |
||||
|
include a readable copy of the attribution notices contained |
||||
|
within such NOTICE file, excluding those notices that do not |
||||
|
pertain to any part of the Derivative Works, in at least one |
||||
|
of the following places: within a NOTICE text file distributed |
||||
|
as part of the Derivative Works; within the Source form or |
||||
|
documentation, if provided along with the Derivative Works; or, |
||||
|
within a display generated by the Derivative Works, if and |
||||
|
wherever such third-party notices normally appear. The contents |
||||
|
of the NOTICE file are for informational purposes only and |
||||
|
do not modify the License. You may add Your own attribution |
||||
|
notices within Derivative Works that You distribute, alongside |
||||
|
or as an addendum to the NOTICE text from the Work, provided |
||||
|
that such additional attribution notices cannot be construed |
||||
|
as modifying the License. |
||||
|
|
||||
|
You may add Your own copyright statement to Your modifications and |
||||
|
may provide additional or different license terms and conditions |
||||
|
for use, reproduction, or distribution of Your modifications, or |
||||
|
for any such Derivative Works as a whole, provided Your use, |
||||
|
reproduction, and distribution of the Work otherwise complies with |
||||
|
the conditions stated in this License. |
||||
|
|
||||
|
5. Submission of Contributions. Unless You explicitly state otherwise, |
||||
|
any Contribution intentionally submitted for inclusion in the Work |
||||
|
by You to the Licensor shall be under the terms and conditions of |
||||
|
this License, without any additional terms or conditions. |
||||
|
Notwithstanding the above, nothing herein shall supersede or modify |
||||
|
the terms of any separate license agreement you may have executed |
||||
|
with Licensor regarding such Contributions. |
||||
|
|
||||
|
6. Trademarks. This License does not grant permission to use the trade |
||||
|
names, trademarks, service marks, or product names of the Licensor, |
||||
|
except as required for reasonable and customary use in describing the |
||||
|
origin of the Work and reproducing the content of the NOTICE file. |
||||
|
|
||||
|
7. Disclaimer of Warranty. Unless required by applicable law or |
||||
|
agreed to in writing, Licensor provides the Work (and each |
||||
|
Contributor provides its Contributions) on an "AS IS" BASIS, |
||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or |
||||
|
implied, including, without limitation, any warranties or conditions |
||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A |
||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the |
||||
|
appropriateness of using or redistributing the Work and assume any |
||||
|
risks associated with Your exercise of permissions under this License. |
||||
|
|
||||
|
8. Limitation of Liability. In no event and under no legal theory, |
||||
|
whether in tort (including negligence), contract, or otherwise, |
||||
|
unless required by applicable law (such as deliberate and grossly |
||||
|
negligent acts) or agreed to in writing, shall any Contributor be |
||||
|
liable to You for damages, including any direct, indirect, special, |
||||
|
incidental, or consequential damages of any character arising as a |
||||
|
result of this License or out of the use or inability to use the |
||||
|
Work (including but not limited to damages for loss of goodwill, |
||||
|
work stoppage, computer failure or malfunction, or any and all |
||||
|
other commercial damages or losses), even if such Contributor |
||||
|
has been advised of the possibility of such damages. |
||||
|
|
||||
|
9. Accepting Warranty or Additional Liability. While redistributing |
||||
|
the Work or Derivative Works thereof, You may choose to offer, |
||||
|
and charge a fee for, acceptance of support, warranty, indemnity, |
||||
|
or other liability obligations and/or rights consistent with this |
||||
|
License. However, in accepting such obligations, You may act only |
||||
|
on Your own behalf and on Your sole responsibility, not on behalf |
||||
|
of any other Contributor, and only if You agree to indemnify, |
||||
|
defend, and hold each Contributor harmless for any liability |
||||
|
incurred by, or claims asserted against, such Contributor by reason |
||||
|
of your accepting any such warranty or additional liability. |
||||
|
|
||||
|
END OF TERMS AND CONDITIONS |
@ -1,2 +1,10 @@ |
|||||
# netbox |
|
||||
|
### netbox |
||||
|
|
||||
|
- [docs](https://netbox.readthedocs.io/en/stable/) |
||||
|
- [git](https://github.com/netbox-community/netbox-docker) |
||||
|
|
||||
|
### services |
||||
|
- [netbox](https://hub.docker.com/r/netboxcommunity/netbox) |
||||
|
- [nginx](https://hub.docker.com/_/nginx) |
||||
|
- [postgres](https://hub.docker.com/_/postgres) |
||||
|
- [redis](https://hub.docker.com/_/redis) |
@ -0,0 +1,74 @@ |
|||||
|
version: "3" |
||||
|
|
||||
|
volumes: |
||||
|
netbox-static-files: |
||||
|
driver: local |
||||
|
netbox-nginx-config: |
||||
|
driver: local |
||||
|
netbox-media-files: |
||||
|
driver: local |
||||
|
netbox-redis-data: |
||||
|
driver: local |
||||
|
|
||||
|
services: |
||||
|
netbox: &netbox |
||||
|
image: netboxcommunity/netbox:v2.9.9 |
||||
|
depends_on: |
||||
|
- postgres |
||||
|
- redis |
||||
|
- redis-cache |
||||
|
- netbox-worker |
||||
|
env_file: env/netbox.env |
||||
|
user: '101' |
||||
|
volumes: |
||||
|
- ./etc/netbox/etc/netbox/config:/etc/netbox/config:z,ro |
||||
|
- ./etc/netbox/etc/netbox/reports:/etc/netbox/reports:z,ro |
||||
|
- ./etc/netbox/etc/netbox/scripts:/etc/netbox/scripts:z,ro |
||||
|
- ./etc/netbox/opt/netbox/initializers:/opt/netbox/initializers:z,ro |
||||
|
- ./etc/netbox/opt/netbox/startup_scripts:/opt/netbox/startup_scripts:z,ro |
||||
|
- netbox-nginx-config:/etc/netbox-nginx:z |
||||
|
- netbox-static-files:/opt/netbox/netbox/static:z |
||||
|
- netbox-media-files:/opt/netbox/netbox/media:z |
||||
|
netbox-worker: |
||||
|
<<: *netbox |
||||
|
depends_on: |
||||
|
- redis |
||||
|
entrypoint: |
||||
|
- python3 |
||||
|
- /opt/netbox/netbox/manage.py |
||||
|
command: |
||||
|
- rqworker |
||||
|
|
||||
|
nginx: |
||||
|
command: nginx -c /etc/netbox-nginx/nginx.conf |
||||
|
image: nginx:1.19-alpine |
||||
|
depends_on: |
||||
|
- netbox |
||||
|
ports: |
||||
|
- "3009:8080" |
||||
|
volumes: |
||||
|
- netbox-nginx-config:/etc/netbox-nginx/:ro |
||||
|
- netbox-static-files:/opt/netbox/netbox/static:ro |
||||
|
|
||||
|
postgres: |
||||
|
image: postgres:13.1-alpine |
||||
|
env_file: env/postgres.env |
||||
|
volumes: |
||||
|
- ./etc/postgres/var/lib/postgresql/data:/var/lib/postgresql/data |
||||
|
|
||||
|
redis: |
||||
|
image: redis:6.0.9-alpine |
||||
|
command: |
||||
|
- sh |
||||
|
- -c # this is to evaluate the $REDIS_PASSWORD from the env |
||||
|
- redis-server --appendonly yes --requirepass $$REDIS_PASSWORD ## $$ because of docker-compose |
||||
|
env_file: env/redis.env |
||||
|
volumes: |
||||
|
- netbox-redis-data:/data |
||||
|
redis-cache: |
||||
|
image: redis:6.0.9-alpine |
||||
|
command: |
||||
|
- sh |
||||
|
- -c # this is to evaluate the $REDIS_PASSWORD from the env |
||||
|
- redis-server --requirepass $$REDIS_PASSWORD ## $$ because of docker-compose |
||||
|
env_file: env/redis-cache.env |
@ -0,0 +1,2 @@ |
|||||
|
* |
||||
|
!.gitignore |
@ -0,0 +1,248 @@ |
|||||
|
#### |
||||
|
## We recommend to not edit this file. |
||||
|
## Create separate files to overwrite the settings. |
||||
|
## See `extra.py` as an example. |
||||
|
#### |
||||
|
|
||||
|
import re |
||||
|
|
||||
|
from os.path import dirname, abspath, join |
||||
|
from os import environ |
||||
|
|
||||
|
# For reference see https://netbox.readthedocs.io/en/stable/configuration/ |
||||
|
# Based on https://github.com/netbox-community/netbox/blob/master/netbox/netbox/configuration.example.py |
||||
|
|
||||
|
# Read secret from file |
||||
|
def _read_secret(secret_name, default = None): |
||||
|
try: |
||||
|
f = open('/run/secrets/' + secret_name, 'r', encoding='utf-8') |
||||
|
except EnvironmentError: |
||||
|
return default |
||||
|
else: |
||||
|
with f: |
||||
|
return f.readline().strip() |
||||
|
|
||||
|
_BASE_DIR = dirname(dirname(abspath(__file__))) |
||||
|
|
||||
|
######################### |
||||
|
# # |
||||
|
# Required settings # |
||||
|
# # |
||||
|
######################### |
||||
|
|
||||
|
# This is a list of valid fully-qualified domain names (FQDNs) for the NetBox server. NetBox will not permit write |
||||
|
# access to the server via any other hostnames. The first FQDN in the list will be treated as the preferred name. |
||||
|
# |
||||
|
# Example: ALLOWED_HOSTS = ['netbox.example.com', 'netbox.internal.local'] |
||||
|
ALLOWED_HOSTS = environ.get('ALLOWED_HOSTS', '*').split(' ') |
||||
|
|
||||
|
# PostgreSQL database configuration. See the Django documentation for a complete list of available parameters: |
||||
|
# https://docs.djangoproject.com/en/stable/ref/settings/#databases |
||||
|
DATABASE = { |
||||
|
'NAME': environ.get('DB_NAME', 'netbox'), # Database name |
||||
|
'USER': environ.get('DB_USER', ''), # PostgreSQL username |
||||
|
'PASSWORD': _read_secret('db_password', environ.get('DB_PASSWORD', '')), |
||||
|
# PostgreSQL password |
||||
|
'HOST': environ.get('DB_HOST', 'localhost'), # Database server |
||||
|
'PORT': environ.get('DB_PORT', ''), # Database port (leave blank for default) |
||||
|
'OPTIONS': {'sslmode': environ.get('DB_SSLMODE', 'prefer')}, |
||||
|
# Database connection SSLMODE |
||||
|
'CONN_MAX_AGE': int(environ.get('DB_CONN_MAX_AGE', '300')), |
||||
|
# Max database connection age |
||||
|
} |
||||
|
|
||||
|
# Redis database settings. Redis is used for caching and for queuing background tasks such as webhook events. A separate |
||||
|
# configuration exists for each. Full connection details are required in both sections, and it is strongly recommended |
||||
|
# to use two separate database IDs. |
||||
|
REDIS = { |
||||
|
'tasks': { |
||||
|
'HOST': environ.get('REDIS_HOST', 'localhost'), |
||||
|
'PORT': int(environ.get('REDIS_PORT', 6379)), |
||||
|
'PASSWORD': _read_secret('redis_password', environ.get('REDIS_PASSWORD', '')), |
||||
|
'DATABASE': int(environ.get('REDIS_DATABASE', 0)), |
||||
|
'SSL': environ.get('REDIS_SSL', 'False').lower() == 'true', |
||||
|
}, |
||||
|
'caching': { |
||||
|
'HOST': environ.get('REDIS_CACHE_HOST', environ.get('REDIS_HOST', 'localhost')), |
||||
|
'PORT': int(environ.get('REDIS_CACHE_PORT', environ.get('REDIS_PORT', 6379))), |
||||
|
'PASSWORD': _read_secret('redis_cache_password', environ.get('REDIS_CACHE_PASSWORD', environ.get('REDIS_PASSWORD', ''))), |
||||
|
'DATABASE': int(environ.get('REDIS_CACHE_DATABASE', 1)), |
||||
|
'SSL': environ.get('REDIS_CACHE_SSL', environ.get('REDIS_SSL', 'False')).lower() == 'true', |
||||
|
}, |
||||
|
} |
||||
|
|
||||
|
# This key is used for secure generation of random numbers and strings. It must never be exposed outside of this file. |
||||
|
# For optimal security, SECRET_KEY should be at least 50 characters in length and contain a mix of letters, numbers, and |
||||
|
# symbols. NetBox will not run without this defined. For more information, see |
||||
|
# https://docs.djangoproject.com/en/stable/ref/settings/#std:setting-SECRET_KEY |
||||
|
SECRET_KEY = _read_secret('secret_key', environ.get('SECRET_KEY', '')) |
||||
|
|
||||
|
|
||||
|
######################### |
||||
|
# # |
||||
|
# Optional settings # |
||||
|
# # |
||||
|
######################### |
||||
|
|
||||
|
# Specify one or more name and email address tuples representing NetBox administrators. These people will be notified of |
||||
|
# application errors (assuming correct email settings are provided). |
||||
|
ADMINS = [ |
||||
|
# ['John Doe', 'jdoe@example.com'], |
||||
|
] |
||||
|
|
||||
|
# URL schemes that are allowed within links in NetBox |
||||
|
ALLOWED_URL_SCHEMES = ( |
||||
|
'file', 'ftp', 'ftps', 'http', 'https', 'irc', 'mailto', 'sftp', 'ssh', 'tel', 'telnet', 'tftp', 'vnc', 'xmpp', |
||||
|
) |
||||
|
|
||||
|
# Optionally display a persistent banner at the top and/or bottom of every page. HTML is allowed. To display the same |
||||
|
# content in both banners, define BANNER_TOP and set BANNER_BOTTOM = BANNER_TOP. |
||||
|
BANNER_TOP = environ.get('BANNER_TOP', '') |
||||
|
BANNER_BOTTOM = environ.get('BANNER_BOTTOM', '') |
||||
|
|
||||
|
# Text to include on the login page above the login form. HTML is allowed. |
||||
|
BANNER_LOGIN = environ.get('BANNER_LOGIN', '') |
||||
|
|
||||
|
# Base URL path if accessing NetBox within a directory. For example, if installed at http://example.com/netbox/, set: |
||||
|
# BASE_PATH = 'netbox/' |
||||
|
BASE_PATH = environ.get('BASE_PATH', '') |
||||
|
|
||||
|
# Cache timeout in seconds. Set to 0 to dissable caching. Defaults to 900 (15 minutes) |
||||
|
CACHE_TIMEOUT = int(environ.get('CACHE_TIMEOUT', 900)) |
||||
|
|
||||
|
# Maximum number of days to retain logged changes. Set to 0 to retain changes indefinitely. (Default: 90) |
||||
|
CHANGELOG_RETENTION = int(environ.get('CHANGELOG_RETENTION', 90)) |
||||
|
|
||||
|
# API Cross-Origin Resource Sharing (CORS) settings. If CORS_ORIGIN_ALLOW_ALL is set to True, all origins will be |
||||
|
# allowed. Otherwise, define a list of allowed origins using either CORS_ORIGIN_WHITELIST or |
||||
|
# CORS_ORIGIN_REGEX_WHITELIST. For more information, see https://github.com/ottoyiu/django-cors-headers |
||||
|
CORS_ORIGIN_ALLOW_ALL = environ.get('CORS_ORIGIN_ALLOW_ALL', 'False').lower() == 'true' |
||||
|
CORS_ORIGIN_WHITELIST = list(filter(None, environ.get('CORS_ORIGIN_WHITELIST', 'https://localhost').split(' '))) |
||||
|
CORS_ORIGIN_REGEX_WHITELIST = [re.compile(r) for r in list(filter(None, environ.get('CORS_ORIGIN_REGEX_WHITELIST', '').split(' ')))] |
||||
|
|
||||
|
# Set to True to enable server debugging. WARNING: Debugging introduces a substantial performance penalty and may reveal |
||||
|
# sensitive information about your installation. Only enable debugging while performing testing. Never enable debugging |
||||
|
# on a production system. |
||||
|
DEBUG = environ.get('DEBUG', 'False').lower() == 'true' |
||||
|
|
||||
|
# Email settings |
||||
|
EMAIL = { |
||||
|
'SERVER': environ.get('EMAIL_SERVER', 'localhost'), |
||||
|
'PORT': int(environ.get('EMAIL_PORT', 25)), |
||||
|
'USERNAME': environ.get('EMAIL_USERNAME', ''), |
||||
|
'PASSWORD': _read_secret('email_password', environ.get('EMAIL_PASSWORD', '')), |
||||
|
'USE_SSL': environ.get('EMAIL_USE_SSL', 'False').lower() == 'true', |
||||
|
'USE_TLS': environ.get('EMAIL_USE_TLS', 'False').lower() == 'true', |
||||
|
'SSL_CERTFILE': environ.get('EMAIL_SSL_CERTFILE', ''), |
||||
|
'SSL_KEYFILE': environ.get('EMAIL_SSL_KEYFILE', ''), |
||||
|
'TIMEOUT': int(environ.get('EMAIL_TIMEOUT', 10)), # seconds |
||||
|
'FROM_EMAIL': environ.get('EMAIL_FROM', ''), |
||||
|
} |
||||
|
|
||||
|
# Enforcement of unique IP space can be toggled on a per-VRF basis. To enforce unique IP space within the global table |
||||
|
# (all prefixes and IP addresses not assigned to a VRF), set ENFORCE_GLOBAL_UNIQUE to True. |
||||
|
ENFORCE_GLOBAL_UNIQUE = environ.get('ENFORCE_GLOBAL_UNIQUE', 'False').lower() == 'true' |
||||
|
|
||||
|
# Exempt certain models from the enforcement of view permissions. Models listed here will be viewable by all users and |
||||
|
# by anonymous users. List models in the form `<app>.<model>`. Add '*' to this list to exempt all models. |
||||
|
EXEMPT_VIEW_PERMISSIONS = list(filter(None, environ.get('EXEMPT_VIEW_PERMISSIONS', '').split(' '))) |
||||
|
|
||||
|
# Enable custom logging. Please see the Django documentation for detailed guidance on configuring custom logs: |
||||
|
# https://docs.djangoproject.com/en/stable/topics/logging/ |
||||
|
LOGGING = {} |
||||
|
|
||||
|
# Setting this to True will permit only authenticated users to access any part of NetBox. By default, anonymous users |
||||
|
# are permitted to access most data in NetBox (excluding secrets) but not make any changes. |
||||
|
LOGIN_REQUIRED = environ.get('LOGIN_REQUIRED', 'False').lower() == 'true' |
||||
|
|
||||
|
# The length of time (in seconds) for which a user will remain logged into the web UI before being prompted to |
||||
|
# re-authenticate. (Default: 1209600 [14 days]) |
||||
|
LOGIN_TIMEOUT = environ.get('LOGIN_TIMEOUT', None) |
||||
|
|
||||
|
# Setting this to True will display a "maintenance mode" banner at the top of every page. |
||||
|
MAINTENANCE_MODE = environ.get('MAINTENANCE_MODE', 'False').lower() == 'true' |
||||
|
|
||||
|
# An API consumer can request an arbitrary number of objects =by appending the "limit" parameter to the URL (e.g. |
||||
|
# "?limit=1000"). This setting defines the maximum limit. Setting it to 0 or None will allow an API consumer to request |
||||
|
# all objects by specifying "?limit=0". |
||||
|
MAX_PAGE_SIZE = int(environ.get('MAX_PAGE_SIZE', 1000)) |
||||
|
|
||||
|
# The file path where uploaded media such as image attachments are stored. A trailing slash is not needed. Note that |
||||
|
# the default value of this setting is derived from the installed location. |
||||
|
MEDIA_ROOT = environ.get('MEDIA_ROOT', join(_BASE_DIR, 'media')) |
||||
|
|
||||
|
# Expose Prometheus monitoring metrics at the HTTP endpoint '/metrics' |
||||
|
METRICS_ENABLED = environ.get('METRICS_ENABLED', 'False').lower() == 'true' |
||||
|
|
||||
|
# Credentials that NetBox will uses to authenticate to devices when connecting via NAPALM. |
||||
|
NAPALM_USERNAME = environ.get('NAPALM_USERNAME', '') |
||||
|
NAPALM_PASSWORD = _read_secret('napalm_password', environ.get('NAPALM_PASSWORD', '')) |
||||
|
|
||||
|
# NAPALM timeout (in seconds). (Default: 30) |
||||
|
NAPALM_TIMEOUT = int(environ.get('NAPALM_TIMEOUT', 30)) |
||||
|
|
||||
|
# NAPALM optional arguments (see http://napalm.readthedocs.io/en/latest/support/#optional-arguments). Arguments must |
||||
|
# be provided as a dictionary. |
||||
|
NAPALM_ARGS = {} |
||||
|
|
||||
|
# Determine how many objects to display per page within a list. (Default: 50) |
||||
|
PAGINATE_COUNT = int(environ.get('PAGINATE_COUNT', 50)) |
||||
|
|
||||
|
# Enable installed plugins. Add the name of each plugin to the list. |
||||
|
PLUGINS = [] |
||||
|
|
||||
|
# Plugins configuration settings. These settings are used by various plugins that the user may have installed. |
||||
|
# Each key in the dictionary is the name of an installed plugin and its value is a dictionary of settings. |
||||
|
PLUGINS_CONFIG = { |
||||
|
} |
||||
|
|
||||
|
# When determining the primary IP address for a device, IPv6 is preferred over IPv4 by default. Set this to True to |
||||
|
# prefer IPv4 instead. |
||||
|
PREFER_IPV4 = environ.get('PREFER_IPV4', 'False').lower() == 'true' |
||||
|
|
||||
|
# Rack elevation size defaults, in pixels. For best results, the ratio of width to height should be roughly 10:1. |
||||
|
RACK_ELEVATION_DEFAULT_UNIT_HEIGHT = int(environ.get('RACK_ELEVATION_DEFAULT_UNIT_HEIGHT', 22)) |
||||
|
RACK_ELEVATION_DEFAULT_UNIT_WIDTH = int(environ.get('RACK_ELEVATION_DEFAULT_UNIT_WIDTH', 220)) |
||||
|
|
||||
|
# Remote authentication support |
||||
|
REMOTE_AUTH_ENABLED = environ.get('REMOTE_AUTH_ENABLED', 'False').lower() == 'true' |
||||
|
REMOTE_AUTH_BACKEND = environ.get('REMOTE_AUTH_BACKEND', 'netbox.authentication.RemoteUserBackend') |
||||
|
REMOTE_AUTH_HEADER = environ.get('REMOTE_AUTH_HEADER', 'HTTP_REMOTE_USER') |
||||
|
REMOTE_AUTH_AUTO_CREATE_USER = environ.get('REMOTE_AUTH_AUTO_CREATE_USER', 'True').lower() == 'true' |
||||
|
REMOTE_AUTH_DEFAULT_GROUPS = list(filter(None, environ.get('REMOTE_AUTH_DEFAULT_GROUPS', '').split(' '))) |
||||
|
|
||||
|
# This determines how often the GitHub API is called to check the latest release of NetBox. Must be at least 1 hour. |
||||
|
RELEASE_CHECK_TIMEOUT = int(environ.get('RELEASE_CHECK_TIMEOUT', 24 * 3600)) |
||||
|
|
||||
|
# This repository is used to check whether there is a new release of NetBox available. Set to None to disable the |
||||
|
# version check or use the URL below to check for release in the official NetBox repository. |
||||
|
# https://api.github.com/repos/netbox-community/netbox/releases |
||||
|
RELEASE_CHECK_URL = environ.get('RELEASE_CHECK_URL', None) |
||||
|
|
||||
|
# The file path where custom reports will be stored. A trailing slash is not needed. Note that the default value of |
||||
|
# this setting is derived from the installed location. |
||||
|
REPORTS_ROOT = environ.get('REPORTS_ROOT', '/etc/netbox/reports') |
||||
|
|
||||
|
# Maximum execution time for background tasks, in seconds. |
||||
|
RQ_DEFAULT_TIMEOUT = int(environ.get('RQ_DEFAULT_TIMEOUT', 300)) |
||||
|
|
||||
|
# The file path where custom scripts will be stored. A trailing slash is not needed. Note that the default value of |
||||
|
# this setting is derived from the installed location. |
||||
|
SCRIPTS_ROOT = environ.get('SCRIPTS_ROOT', '/etc/netbox/scripts') |
||||
|
|
||||
|
# By default, NetBox will store session data in the database. Alternatively, a file path can be specified here to use |
||||
|
# local file storage instead. (This can be useful for enabling authentication on a standby instance with read-only |
||||
|
# database access.) Note that the user as which NetBox runs must have read and write permissions to this path. |
||||
|
SESSION_FILE_PATH = environ.get('REPORTS_ROOT', None) |
||||
|
|
||||
|
# Time zone (default: UTC) |
||||
|
TIME_ZONE = environ.get('TIME_ZONE', 'UTC') |
||||
|
|
||||
|
# Date/time formatting. See the following link for supported formats: |
||||
|
# https://docs.djangoproject.com/en/stable/ref/templates/builtins/#date |
||||
|
DATE_FORMAT = environ.get('DATE_FORMAT', 'N j, Y') |
||||
|
SHORT_DATE_FORMAT = environ.get('SHORT_DATE_FORMAT', 'Y-m-d') |
||||
|
TIME_FORMAT = environ.get('TIME_FORMAT', 'g:i a') |
||||
|
SHORT_TIME_FORMAT = environ.get('SHORT_TIME_FORMAT', 'H:i:s') |
||||
|
DATETIME_FORMAT = environ.get('DATETIME_FORMAT', 'N j, Y g:i a') |
||||
|
SHORT_DATETIME_FORMAT = environ.get('SHORT_DATETIME_FORMAT', 'Y-m-d H:i') |
@ -0,0 +1,55 @@ |
|||||
|
#### |
||||
|
## This file contains extra configuration options that can't be configured |
||||
|
## directly through environment variables. |
||||
|
#### |
||||
|
|
||||
|
## Specify one or more name and email address tuples representing NetBox administrators. These people will be notified of |
||||
|
## application errors (assuming correct email settings are provided). |
||||
|
# ADMINS = [ |
||||
|
# # ['John Doe', 'jdoe@example.com'], |
||||
|
# ] |
||||
|
|
||||
|
|
||||
|
## URL schemes that are allowed within links in NetBox |
||||
|
# ALLOWED_URL_SCHEMES = ( |
||||
|
# 'file', 'ftp', 'ftps', 'http', 'https', 'irc', 'mailto', 'sftp', 'ssh', 'tel', 'telnet', 'tftp', 'vnc', 'xmpp', |
||||
|
# ) |
||||
|
|
||||
|
|
||||
|
## NAPALM optional arguments (see http://napalm.readthedocs.io/en/latest/support/#optional-arguments). Arguments must |
||||
|
## be provided as a dictionary. |
||||
|
# NAPALM_ARGS = {} |
||||
|
|
||||
|
|
||||
|
## Enable installed plugins. Add the name of each plugin to the list. |
||||
|
# from netbox.configuration.configuration import PLUGINS |
||||
|
# PLUGINS.append('my_plugin') |
||||
|
|
||||
|
## Plugins configuration settings. These settings are used by various plugins that the user may have installed. |
||||
|
## Each key in the dictionary is the name of an installed plugin and its value is a dictionary of settings. |
||||
|
# from netbox.configuration.configuration import PLUGINS_CONFIG |
||||
|
# PLUGINS_CONFIG['my_plugin'] = { |
||||
|
# 'foo': 'bar', |
||||
|
# 'buzz': 'bazz' |
||||
|
# } |
||||
|
|
||||
|
|
||||
|
## Remote authentication support |
||||
|
# REMOTE_AUTH_DEFAULT_PERMISSIONS = {} |
||||
|
|
||||
|
|
||||
|
## By default uploaded media is stored on the local filesystem. Using Django-storages is also supported. Provide the |
||||
|
## class path of the storage driver in STORAGE_BACKEND and any configuration options in STORAGE_CONFIG. For example: |
||||
|
# STORAGE_BACKEND = 'storages.backends.s3boto3.S3Boto3Storage' |
||||
|
# STORAGE_CONFIG = { |
||||
|
# 'AWS_ACCESS_KEY_ID': 'Key ID', |
||||
|
# 'AWS_SECRET_ACCESS_KEY': 'Secret', |
||||
|
# 'AWS_STORAGE_BUCKET_NAME': 'netbox', |
||||
|
# 'AWS_S3_REGION_NAME': 'eu-west-1', |
||||
|
# } |
||||
|
|
||||
|
|
||||
|
## This file can contain arbitrary Python code, e.g.: |
||||
|
# from datetime import datetime |
||||
|
# now = datetime.now().strftime("%d/%m/%Y %H:%M:%S") |
||||
|
# BANNER_TOP = f'<marquee width="200px">This instance started on {now}.</marquee>' |
@ -0,0 +1,84 @@ |
|||||
|
import ldap |
||||
|
|
||||
|
from django_auth_ldap.config import LDAPSearch |
||||
|
from importlib import import_module |
||||
|
from os import environ |
||||
|
|
||||
|
# Read secret from file |
||||
|
def _read_secret(secret_name, default=None): |
||||
|
try: |
||||
|
f = open('/run/secrets/' + secret_name, 'r', encoding='utf-8') |
||||
|
except EnvironmentError: |
||||
|
return default |
||||
|
else: |
||||
|
with f: |
||||
|
return f.readline().strip() |
||||
|
|
||||
|
# Import and return the group type based on string name |
||||
|
def _import_group_type(group_type_name): |
||||
|
mod = import_module('django_auth_ldap.config') |
||||
|
try: |
||||
|
return getattr(mod, group_type_name)() |
||||
|
except: |
||||
|
return None |
||||
|
|
||||
|
# Server URI |
||||
|
AUTH_LDAP_SERVER_URI = environ.get('AUTH_LDAP_SERVER_URI', '') |
||||
|
|
||||
|
# The following may be needed if you are binding to Active Directory. |
||||
|
AUTH_LDAP_CONNECTION_OPTIONS = { |
||||
|
ldap.OPT_REFERRALS: 0 |
||||
|
} |
||||
|
|
||||
|
# Set the DN and password for the NetBox service account. |
||||
|
AUTH_LDAP_BIND_DN = environ.get('AUTH_LDAP_BIND_DN', '') |
||||
|
AUTH_LDAP_BIND_PASSWORD = _read_secret('auth_ldap_bind_password', environ.get('AUTH_LDAP_BIND_PASSWORD', '')) |
||||
|
|
||||
|
# Set a string template that describes any user’s distinguished name based on the username. |
||||
|
AUTH_LDAP_USER_DN_TEMPLATE = environ.get('AUTH_LDAP_USER_DN_TEMPLATE', None) |
||||
|
|
||||
|
# Enable STARTTLS for ldap authentication. |
||||
|
AUTH_LDAP_START_TLS = environ.get('AUTH_LDAP_START_TLS', 'False').lower() == 'true' |
||||
|
|
||||
|
# Include this setting if you want to ignore certificate errors. This might be needed to accept a self-signed cert. |
||||
|
# Note that this is a NetBox-specific setting which sets: |
||||
|
# ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_NEVER) |
||||
|
LDAP_IGNORE_CERT_ERRORS = environ.get('LDAP_IGNORE_CERT_ERRORS', 'False').lower() == 'true' |
||||
|
|
||||
|
AUTH_LDAP_USER_SEARCH_BASEDN = environ.get('AUTH_LDAP_USER_SEARCH_BASEDN', '') |
||||
|
AUTH_LDAP_USER_SEARCH_ATTR = environ.get('AUTH_LDAP_USER_SEARCH_ATTR', 'sAMAccountName') |
||||
|
AUTH_LDAP_USER_SEARCH = LDAPSearch(AUTH_LDAP_USER_SEARCH_BASEDN, |
||||
|
ldap.SCOPE_SUBTREE, |
||||
|
"(" + AUTH_LDAP_USER_SEARCH_ATTR + "=%(user)s)") |
||||
|
|
||||
|
# This search ought to return all groups to which the user belongs. django_auth_ldap uses this to determine group |
||||
|
# heirarchy. |
||||
|
AUTH_LDAP_GROUP_SEARCH_BASEDN = environ.get('AUTH_LDAP_GROUP_SEARCH_BASEDN', '') |
||||
|
AUTH_LDAP_GROUP_SEARCH_CLASS = environ.get('AUTH_LDAP_GROUP_SEARCH_CLASS', 'group') |
||||
|
AUTH_LDAP_GROUP_SEARCH = LDAPSearch(AUTH_LDAP_GROUP_SEARCH_BASEDN, ldap.SCOPE_SUBTREE, |
||||
|
"(objectClass=" + AUTH_LDAP_GROUP_SEARCH_CLASS + ")") |
||||
|
AUTH_LDAP_GROUP_TYPE = _import_group_type(environ.get('AUTH_LDAP_GROUP_TYPE', 'GroupOfNamesType')) |
||||
|
|
||||
|
# Define a group required to login. |
||||
|
AUTH_LDAP_REQUIRE_GROUP = environ.get('AUTH_LDAP_REQUIRE_GROUP_DN', '') |
||||
|
|
||||
|
# Define special user types using groups. Exercise great caution when assigning superuser status. |
||||
|
AUTH_LDAP_USER_FLAGS_BY_GROUP = { |
||||
|
"is_active": environ.get('AUTH_LDAP_REQUIRE_GROUP_DN', ''), |
||||
|
"is_staff": environ.get('AUTH_LDAP_IS_ADMIN_DN', ''), |
||||
|
"is_superuser": environ.get('AUTH_LDAP_IS_SUPERUSER_DN', '') |
||||
|
} |
||||
|
|
||||
|
# For more granular permissions, we can map LDAP groups to Django groups. |
||||
|
AUTH_LDAP_FIND_GROUP_PERMS = environ.get('AUTH_LDAP_FIND_GROUP_PERMS', 'True').lower() == 'true' |
||||
|
AUTH_LDAP_MIRROR_GROUPS = environ.get('AUTH_LDAP_MIRROR_GROUPS', '').lower() == 'true' |
||||
|
|
||||
|
# Cache groups for one hour to reduce LDAP traffic |
||||
|
AUTH_LDAP_CACHE_TIMEOUT = int(environ.get('AUTH_LDAP_CACHE_TIMEOUT', 3600)) |
||||
|
|
||||
|
# Populate the Django user from the LDAP directory. |
||||
|
AUTH_LDAP_USER_ATTR_MAP = { |
||||
|
"first_name": environ.get('AUTH_LDAP_ATTR_FIRSTNAME', 'givenName'), |
||||
|
"last_name": environ.get('AUTH_LDAP_ATTR_LASTNAME', 'sn'), |
||||
|
"email": environ.get('AUTH_LDAP_ATTR_MAIL', 'mail') |
||||
|
} |
@ -0,0 +1,46 @@ |
|||||
|
from dcim.choices import DeviceStatusChoices |
||||
|
from dcim.models import ConsolePort, Device, PowerPort |
||||
|
from extras.reports import Report |
||||
|
|
||||
|
|
||||
|
class DeviceConnectionsReport(Report): |
||||
|
description = "Validate the minimum physical connections for each device" |
||||
|
|
||||
|
def test_console_connection(self): |
||||
|
|
||||
|
# Check that every console port for every active device has a connection defined. |
||||
|
active = DeviceStatusChoices.STATUS_ACTIVE |
||||
|
for console_port in ConsolePort.objects.prefetch_related('device').filter(device__status=active): |
||||
|
if console_port.connected_endpoint is None: |
||||
|
self.log_failure( |
||||
|
console_port.device, |
||||
|
"No console connection defined for {}".format(console_port.name) |
||||
|
) |
||||
|
elif not console_port.connection_status: |
||||
|
self.log_warning( |
||||
|
console_port.device, |
||||
|
"Console connection for {} marked as planned".format(console_port.name) |
||||
|
) |
||||
|
else: |
||||
|
self.log_success(console_port.device) |
||||
|
|
||||
|
def test_power_connections(self): |
||||
|
|
||||
|
# Check that every active device has at least two connected power supplies. |
||||
|
for device in Device.objects.filter(status=DeviceStatusChoices.STATUS_ACTIVE): |
||||
|
connected_ports = 0 |
||||
|
for power_port in PowerPort.objects.filter(device=device): |
||||
|
if power_port.connected_endpoint is not None: |
||||
|
connected_ports += 1 |
||||
|
if not power_port.connection_status: |
||||
|
self.log_warning( |
||||
|
device, |
||||
|
"Power connection for {} marked as planned".format(power_port.name) |
||||
|
) |
||||
|
if connected_ports < 2: |
||||
|
self.log_failure( |
||||
|
device, |
||||
|
"{} connected power supplies found (2 needed)".format(connected_ports) |
||||
|
) |
||||
|
else: |
||||
|
self.log_success(device) |
@ -0,0 +1,6 @@ |
|||||
|
# - prefix: 10.0.0.0/16 |
||||
|
# rir: RFC1918 |
||||
|
# - prefix: fd00:ccdd::/32 |
||||
|
# rir: RFC4193 ULA |
||||
|
# - prefix: 2001:db8::/32 |
||||
|
# rir: RFC3849 |
@ -0,0 +1,2 @@ |
|||||
|
# - name: Hyper-V |
||||
|
# slug: hyper-v |
@ -0,0 +1,5 @@ |
|||||
|
# - name: cluster1 |
||||
|
# type: Hyper-V |
||||
|
# - name: cluster2 |
||||
|
# type: Hyper-V |
||||
|
# site: SING 1 |
@ -0,0 +1,97 @@ |
|||||
|
## 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 |
||||
|
# on_objects: |
||||
|
# - dcim.models.Device |
||||
|
# choices: |
||||
|
# - value: First Item |
||||
|
# weight: 10 |
||||
|
# - value: Second Item |
||||
|
# weight: 20 |
||||
|
# - value: Third Item |
||||
|
# weight: 30 |
||||
|
# - value: Fifth Item |
||||
|
# weight: 50 |
||||
|
# - value: Fourth Item |
||||
|
# weight: 40 |
||||
|
# select_field_auto_weight: |
||||
|
# type: select |
||||
|
# label: Choose between items |
||||
|
# required: false |
||||
|
# filter_logic: loose |
||||
|
# weight: 30 |
||||
|
# on_objects: |
||||
|
# - dcim.models.Device |
||||
|
# choices: |
||||
|
# - value: A |
||||
|
# - value: B |
||||
|
# - value: C |
||||
|
# - value: "D like deprecated" |
||||
|
# weight: 999 |
||||
|
# - value: E |
||||
|
# boolean_field: |
||||
|
# type: boolean |
||||
|
# label: Yes Or No? |
||||
|
# required: true |
||||
|
# filter_logic: loose |
||||
|
# default: "false" # important: but "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 |
@ -0,0 +1,18 @@ |
|||||
|
## 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 |
||||
|
# enabled: true |
||||
|
# type: virtual |
||||
|
# name: to-server02 |
||||
|
# - device: server02 |
||||
|
# enabled: true |
||||
|
# type: virtual |
||||
|
# name: to-server01 |
@ -0,0 +1,15 @@ |
|||||
|
# - name: switch |
||||
|
# slug: switch |
||||
|
# color: Grey |
||||
|
# - name: router |
||||
|
# slug: router |
||||
|
# color: Cyan |
||||
|
# - name: load-balancer |
||||
|
# slug: load-balancer |
||||
|
# color: Red |
||||
|
# - name: server |
||||
|
# slug: server |
||||
|
# color: Blue |
||||
|
# - name: patchpanel |
||||
|
# slug: patchpanel |
||||
|
# color: Black |
@ -0,0 +1,23 @@ |
|||||
|
# - model: Model 1 |
||||
|
# manufacturer: Manufacturer 1 |
||||
|
# slug: model-1 |
||||
|
# u_height: 2 |
||||
|
# custom_fields: |
||||
|
# text_field: Description |
||||
|
# - model: Model 2 |
||||
|
# manufacturer: Manufacturer 1 |
||||
|
# slug: model-2 |
||||
|
# custom_fields: |
||||
|
# text_field: Description |
||||
|
# - model: Model 3 |
||||
|
# manufacturer: Manufacturer 1 |
||||
|
# slug: model-3 |
||||
|
# is_full_depth: false |
||||
|
# u_height: 0 |
||||
|
# custom_fields: |
||||
|
# text_field: Description |
||||
|
# - model: Other |
||||
|
# manufacturer: No Name |
||||
|
# slug: other |
||||
|
# custom_fields: |
||||
|
# text_field: Description |
@ -0,0 +1,44 @@ |
|||||
|
## 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_fields: |
||||
|
# 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_fields: |
||||
|
# text_field: Description |
||||
|
# - name: server03 |
||||
|
# device_role: server |
||||
|
# device_type: Other |
||||
|
# site: SING 1 |
||||
|
# rack: rack-03 |
||||
|
# face: front |
||||
|
# position: 3 |
||||
|
# custom_fields: |
||||
|
# text_field: Description |
@ -0,0 +1,35 @@ |
|||||
|
## To list all permissions, run: |
||||
|
## |
||||
|
## docker-compose run --rm --entrypoint /bin/bash netbox |
||||
|
## $ ./manage.py migrate |
||||
|
## $ ./manage.py shell |
||||
|
## > from django.contrib.auth.models import Permission |
||||
|
## > print('\n'.join([p.codename for p in Permission.objects.all()])) |
||||
|
## |
||||
|
## Permission lists support wildcards. See the examples below. |
||||
|
## |
||||
|
## Examples: |
||||
|
|
||||
|
# applications: |
||||
|
# users: |
||||
|
# - technical_user |
||||
|
# readers: |
||||
|
# users: |
||||
|
# - reader |
||||
|
# writers: |
||||
|
# users: |
||||
|
# - writer |
||||
|
# permissions: |
||||
|
# - delete_device |
||||
|
# - delete_virtualmachine |
||||
|
# - add_* |
||||
|
# - change_* |
||||
|
# vm_managers: |
||||
|
# permissions: |
||||
|
# - '*_virtualmachine' |
||||
|
# device_managers: |
||||
|
# permissions: |
||||
|
# - '*device*' |
||||
|
# creators: |
||||
|
# permissions: |
||||
|
# - add_* |
@ -0,0 +1,44 @@ |
|||||
|
## 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 |
@ -0,0 +1,6 @@ |
|||||
|
# - name: Manufacturer 1 |
||||
|
# slug: manufacturer-1 |
||||
|
# - name: Manufacturer 2 |
||||
|
# slug: manufacturer-2 |
||||
|
# - name: No Name |
||||
|
# slug: no-name |
@ -0,0 +1,15 @@ |
|||||
|
# - 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'}" |
@ -0,0 +1,2 @@ |
|||||
|
# - name: Main Management |
||||
|
# slug: main-management |
@ -0,0 +1,29 @@ |
|||||
|
## 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 |
@ -0,0 +1,3 @@ |
|||||
|
# - name: cage 101 |
||||
|
# slug: cage-101 |
||||
|
# site: SING 1 |
@ -0,0 +1,12 @@ |
|||||
|
# - name: Role 1 |
||||
|
# slug: role-1 |
||||
|
# color: Pink |
||||
|
# - name: Role 2 |
||||
|
# slug: role-2 |
||||
|
# color: Cyan |
||||
|
# - name: Role 3 |
||||
|
# slug: role-3 |
||||
|
# color: Grey |
||||
|
# - name: Role 4 |
||||
|
# slug: role-4 |
||||
|
# color: Teal |
@ -0,0 +1,41 @@ |
|||||
|
## 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_fields: |
||||
|
# text_field: Description |
||||
|
# - site: AMS 2 |
||||
|
# name: rack-02 |
||||
|
# role: Role 2 |
||||
|
# type: 4-post-cabinet |
||||
|
# width: 19 |
||||
|
# u_height: 47 |
||||
|
# custom_fields: |
||||
|
# text_field: Description |
||||
|
# - site: SING 1 |
||||
|
# name: rack-03 |
||||
|
# group: cage 101 |
||||
|
# role: Role 3 |
||||
|
# type: 4-post-cabinet |
||||
|
# width: 19 |
||||
|
# u_height: 47 |
||||
|
# custom_fields: |
||||
|
# text_field: Description |
@ -0,0 +1,2 @@ |
|||||
|
- name: Saint-Petersburg |
||||
|
slug: spb |
@ -0,0 +1,9 @@ |
|||||
|
# - is_private: true |
||||
|
# name: RFC1918 |
||||
|
# slug: rfc1918 |
||||
|
# - is_private: true |
||||
|
# name: RFC4193 ULA |
||||
|
# slug: rfc4193-ula |
||||
|
# - is_private: true |
||||
|
# name: RFC3849 |
||||
|
# slug: rfc3849 |
@ -0,0 +1,32 @@ |
|||||
|
# - name: AMS 1 |
||||
|
# slug: ams1 |
||||
|
# region: Downtown |
||||
|
# status: active |
||||
|
# facility: Amsterdam 1 |
||||
|
# asn: 12345 |
||||
|
# custom_fields: |
||||
|
# text_field: Description |
||||
|
# - name: AMS 2 |
||||
|
# slug: ams2 |
||||
|
# region: Downtown |
||||
|
# status: active |
||||
|
# facility: Amsterdam 2 |
||||
|
# asn: 54321 |
||||
|
# custom_fields: |
||||
|
# text_field: Description |
||||
|
# - name: AMS 3 |
||||
|
# slug: ams3 |
||||
|
# region: Suburbs |
||||
|
# status: active |
||||
|
# facility: Amsterdam 3 |
||||
|
# asn: 67890 |
||||
|
# custom_fields: |
||||
|
# text_field: Description |
||||
|
# - name: SING 1 |
||||
|
# slug: sing1 |
||||
|
# region: Singapore |
||||
|
# status: active |
||||
|
# facility: Singapore 1 |
||||
|
# asn: 09876 |
||||
|
# custom_fields: |
||||
|
# text_field: Description |
@ -0,0 +1,4 @@ |
|||||
|
# - name: Tenant Group 1 |
||||
|
# slug: tenant-group-1 |
||||
|
# - name: Tenant Group 2 |
||||
|
# slug: tenant-group-2 |
@ -0,0 +1,5 @@ |
|||||
|
# - name: tenant1 |
||||
|
# slug: tenant1 |
||||
|
# - name: tenant2 |
||||
|
# slug: tenant2 |
||||
|
# group: Tenant Group 2 |
@ -0,0 +1,23 @@ |
|||||
|
## To list all permissions, run: |
||||
|
## |
||||
|
## docker-compose run --rm --entrypoint /bin/bash netbox |
||||
|
## $ ./manage.py migrate |
||||
|
## $ ./manage.py shell |
||||
|
## > from django.contrib.auth.models import Permission |
||||
|
## > print('\n'.join([p.codename for p in Permission.objects.all()])) |
||||
|
## |
||||
|
## Permission lists support wildcards. See the examples below. |
||||
|
## |
||||
|
## Examples: |
||||
|
|
||||
|
# technical_user: |
||||
|
# api_token: 0123456789technicaluser789abcdef01234567 # must be looooong! |
||||
|
# reader: |
||||
|
# password: reader |
||||
|
# writer: |
||||
|
# password: writer |
||||
|
# permissions: |
||||
|
# - delete_device |
||||
|
# - delete_virtualmachine |
||||
|
# - add_* |
||||
|
# - change_* |
@ -0,0 +1,28 @@ |
|||||
|
## 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 |
@ -0,0 +1,12 @@ |
|||||
|
# - 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 |
@ -0,0 +1,6 @@ |
|||||
|
# - name: VLAN group 1 |
||||
|
# site: AMS 1 |
||||
|
# slug: vlan-group-1 |
||||
|
# - name: VLAN group 2 |
||||
|
# site: AMS 1 |
||||
|
# slug: vlan-group-2 |
@ -0,0 +1,19 @@ |
|||||
|
## 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 |
@ -0,0 +1,8 @@ |
|||||
|
# - enforce_unique: true |
||||
|
# name: vrf1 |
||||
|
# tenant: tenant1 |
||||
|
# description: main VRF |
||||
|
# - enforce_unique: true |
||||
|
# name: vrf2 |
||||
|
# rd: "6500:6500" |
||||
|
# tenant: tenant2 |
@ -0,0 +1,23 @@ |
|||||
|
import sys |
||||
|
|
||||
|
from django.contrib.auth.models import Group, User |
||||
|
from startup_script_utils import load_yaml, set_permissions |
||||
|
from users.models import Token |
||||
|
|
||||
|
users = load_yaml('/opt/netbox/initializers/users.yml') |
||||
|
if users is None: |
||||
|
sys.exit() |
||||
|
|
||||
|
for username, user_details in users.items(): |
||||
|
if not User.objects.filter(username=username): |
||||
|
user = User.objects.create_user( |
||||
|
username = username, |
||||
|
password = user_details.get('password', 0) or User.objects.make_random_password()) |
||||
|
|
||||
|
print("👤 Created user",username) |
||||
|
|
||||
|
if user_details.get('api_token', 0): |
||||
|
Token.objects.create(user=user, key=user_details['api_token']) |
||||
|
|
||||
|
yaml_permissions = user_details.get('permissions', []) |
||||
|
set_permissions(user.user_permissions, yaml_permissions) |
@ -0,0 +1,23 @@ |
|||||
|
import sys |
||||
|
|
||||
|
from django.contrib.auth.models import Group, User |
||||
|
from startup_script_utils import load_yaml, set_permissions |
||||
|
|
||||
|
groups = load_yaml('/opt/netbox/initializers/groups.yml') |
||||
|
if groups is None: |
||||
|
sys.exit() |
||||
|
|
||||
|
for groupname, group_details in groups.items(): |
||||
|
group, created = Group.objects.get_or_create(name=groupname) |
||||
|
|
||||
|
if created: |
||||
|
print("👥 Created group", groupname) |
||||
|
|
||||
|
for username in group_details.get('users', []): |
||||
|
user = User.objects.get(username=username) |
||||
|
|
||||
|
if user: |
||||
|
user.groups.add(group) |
||||
|
|
||||
|
yaml_permissions = group_details.get('permissions', []) |
||||
|
set_permissions(group.permissions, yaml_permissions) |
@ -0,0 +1,54 @@ |
|||||
|
from extras.models import CustomField, CustomFieldChoice |
||||
|
|
||||
|
from startup_script_utils import load_yaml |
||||
|
import sys |
||||
|
|
||||
|
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', 0): |
||||
|
custom_field.default = cf_details['default'] |
||||
|
|
||||
|
if cf_details.get('description', 0): |
||||
|
custom_field.description = cf_details['description'] |
||||
|
|
||||
|
if cf_details.get('label', 0): |
||||
|
custom_field.label = cf_details['label'] |
||||
|
|
||||
|
for object_type in cf_details.get('on_objects', []): |
||||
|
custom_field.obj_type.add(get_class_for_class_path(object_type)) |
||||
|
|
||||
|
if cf_details.get('required', 0): |
||||
|
custom_field.required = cf_details['required'] |
||||
|
|
||||
|
if cf_details.get('type', 0): |
||||
|
custom_field.type = cf_details['type'] |
||||
|
|
||||
|
if cf_details.get('weight', 0): |
||||
|
custom_field.weight = cf_details['weight'] |
||||
|
|
||||
|
custom_field.save() |
||||
|
|
||||
|
for idx, choice_details in enumerate(cf_details.get('choices', [])): |
||||
|
choice, _ = CustomFieldChoice.objects.get_or_create( |
||||
|
field=custom_field, |
||||
|
value=choice_details['value'], |
||||
|
defaults={'weight': idx * 10} |
||||
|
) |
||||
|
|
||||
|
print("🔧 Created custom field", cf_name) |
@ -0,0 +1,26 @@ |
|||||
|
from dcim.models import Region |
||||
|
from startup_script_utils import load_yaml |
||||
|
import sys |
||||
|
|
||||
|
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) |
||||
|
|
||||
|
region, created = Region.objects.get_or_create(**params) |
||||
|
|
||||
|
if created: |
||||
|
print("🌐 Created region", region.name) |
@ -0,0 +1,41 @@ |
|||||
|
from dcim.models import Region, Site |
||||
|
from extras.models import CustomField, CustomFieldValue |
||||
|
from tenancy.models import Tenant |
||||
|
from startup_script_utils import load_yaml |
||||
|
import sys |
||||
|
|
||||
|
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_fields = params.pop('custom_fields', None) |
||||
|
|
||||
|
for assoc, details in optional_assocs.items(): |
||||
|
if assoc in params: |
||||
|
model, field = details |
||||
|
query = { field: params.pop(assoc) } |
||||
|
|
||||
|
params[assoc] = model.objects.get(**query) |
||||
|
|
||||
|
site, created = Site.objects.get_or_create(**params) |
||||
|
|
||||
|
if created: |
||||
|
if custom_fields is not None: |
||||
|
for cf_name, cf_value in custom_fields.items(): |
||||
|
custom_field = CustomField.objects.get(name=cf_name) |
||||
|
custom_field_value = CustomFieldValue.objects.create( |
||||
|
field=custom_field, |
||||
|
obj=site, |
||||
|
value=cf_value |
||||
|
) |
||||
|
|
||||
|
site.custom_field_values.add(custom_field_value) |
||||
|
|
||||
|
print("📍 Created site", site.name) |
@ -0,0 +1,14 @@ |
|||||
|
from dcim.models import Manufacturer |
||||
|
from startup_script_utils import load_yaml |
||||
|
import sys |
||||
|
|
||||
|
manufacturers = load_yaml('/opt/netbox/initializers/manufacturers.yml') |
||||
|
|
||||
|
if manufacturers is None: |
||||
|
sys.exit() |
||||
|
|
||||
|
for params in manufacturers: |
||||
|
manufacturer, created = Manufacturer.objects.get_or_create(**params) |
||||
|
|
||||
|
if created: |
||||
|
print("🏭 Created Manufacturer", manufacturer.name) |
@ -0,0 +1,51 @@ |
|||||
|
from dcim.models import DeviceType, Manufacturer, Region |
||||
|
from tenancy.models import Tenant |
||||
|
from extras.models import CustomField, CustomFieldValue |
||||
|
from startup_script_utils import load_yaml |
||||
|
import sys |
||||
|
|
||||
|
device_types = load_yaml('/opt/netbox/initializers/device_types.yml') |
||||
|
|
||||
|
if device_types is None: |
||||
|
sys.exit() |
||||
|
|
||||
|
required_assocs = { |
||||
|
'manufacturer': (Manufacturer, 'name') |
||||
|
} |
||||
|
|
||||
|
optional_assocs = { |
||||
|
'region': (Region, 'name'), |
||||
|
'tenant': (Tenant, 'name') |
||||
|
} |
||||
|
|
||||
|
for params in device_types: |
||||
|
custom_fields = params.pop('custom_fields', None) |
||||
|
|
||||
|
for assoc, details in required_assocs.items(): |
||||
|
model, field = details |
||||
|
query = { field: params.pop(assoc) } |
||||
|
|
||||
|
params[assoc] = model.objects.get(**query) |
||||
|
|
||||
|
for assoc, details in optional_assocs.items(): |
||||
|
if assoc in params: |
||||
|
model, field = details |
||||
|
query = { field: params.pop(assoc) } |
||||
|
|
||||
|
params[assoc] = model.objects.get(**query) |
||||
|
|
||||
|
device_type, created = DeviceType.objects.get_or_create(**params) |
||||
|
|
||||
|
if created: |
||||
|
if custom_fields is not None: |
||||
|
for cf_name, cf_value in custom_fields.items(): |
||||
|
custom_field = CustomField.objects.get(name=cf_name) |
||||
|
custom_field_value = CustomFieldValue.objects.create( |
||||
|
field=custom_field, |
||||
|
obj=device_type, |
||||
|
value=cf_value |
||||
|
) |
||||
|
|
||||
|
device_type.custom_field_values.add(custom_field_value) |
||||
|
|
||||
|
print("🔡 Created device type", device_type.manufacturer, device_type.model) |
@ -0,0 +1,23 @@ |
|||||
|
from dcim.models import RackRole |
||||
|
from utilities.choices import ColorChoices |
||||
|
|
||||
|
from startup_script_utils import load_yaml |
||||
|
import sys |
||||
|
|
||||
|
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] |
||||
|
|
||||
|
rack_role, created = RackRole.objects.get_or_create(**params) |
||||
|
|
||||
|
if created: |
||||
|
print("🎨 Created rack role", rack_role.name) |
@ -0,0 +1,25 @@ |
|||||
|
from dcim.models import Site,RackGroup |
||||
|
from startup_script_utils import load_yaml |
||||
|
import sys |
||||
|
|
||||
|
rack_groups = load_yaml('/opt/netbox/initializers/rack_groups.yml') |
||||
|
|
||||
|
if rack_groups is None: |
||||
|
sys.exit() |
||||
|
|
||||
|
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) |
||||
|
|
||||
|
rack_group, created = RackGroup.objects.get_or_create(**params) |
||||
|
|
||||
|
if created: |
||||
|
print("🎨 Created rack group", rack_group.name) |
||||
|
|
@ -0,0 +1,52 @@ |
|||||
|
from dcim.models import Site, RackRole, Rack, RackGroup |
||||
|
from tenancy.models import Tenant |
||||
|
from extras.models import CustomField, CustomFieldValue |
||||
|
from startup_script_utils import load_yaml |
||||
|
import sys |
||||
|
|
||||
|
racks = load_yaml('/opt/netbox/initializers/racks.yml') |
||||
|
|
||||
|
if racks is None: |
||||
|
sys.exit() |
||||
|
|
||||
|
required_assocs = { |
||||
|
'site': (Site, 'name') |
||||
|
} |
||||
|
|
||||
|
optional_assocs = { |
||||
|
'role': (RackRole, 'name'), |
||||
|
'tenant': (Tenant, 'name'), |
||||
|
'group': (RackGroup, 'name') |
||||
|
} |
||||
|
|
||||
|
for params in racks: |
||||
|
custom_fields = params.pop('custom_fields', None) |
||||
|
|
||||
|
for assoc, details in required_assocs.items(): |
||||
|
model, field = details |
||||
|
query = { field: params.pop(assoc) } |
||||
|
|
||||
|
params[assoc] = model.objects.get(**query) |
||||
|
|
||||
|
for assoc, details in optional_assocs.items(): |
||||
|
if assoc in params: |
||||
|
model, field = details |
||||
|
query = { field: params.pop(assoc) } |
||||
|
|
||||
|
params[assoc] = model.objects.get(**query) |
||||
|
|
||||
|
rack, created = Rack.objects.get_or_create(**params) |
||||
|
|
||||
|
if created: |
||||
|
if custom_fields is not None: |
||||
|
for cf_name, cf_value in custom_fields.items(): |
||||
|
custom_field = CustomField.objects.get(name=cf_name) |
||||
|
custom_field_value = CustomFieldValue.objects.create( |
||||
|
field=custom_field, |
||||
|
obj=rack, |
||||
|
value=cf_value |
||||
|
) |
||||
|
|
||||
|
rack.custom_field_values.add(custom_field_value) |
||||
|
|
||||
|
print("🔳 Created rack", rack.site, rack.name) |
@ -0,0 +1,24 @@ |
|||||
|
from dcim.models import DeviceRole |
||||
|
from utilities.choices import ColorChoices |
||||
|
|
||||
|
from startup_script_utils import load_yaml |
||||
|
import sys |
||||
|
|
||||
|
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] |
||||
|
|
||||
|
device_role, created = DeviceRole.objects.get_or_create(**params) |
||||
|
|
||||
|
if created: |
||||
|
print("🎨 Created device role", device_role.name) |
@ -0,0 +1,26 @@ |
|||||
|
from dcim.models import Manufacturer, Platform |
||||
|
from startup_script_utils import load_yaml |
||||
|
import sys |
||||
|
|
||||
|
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) |
||||
|
|
||||
|
platform, created = Platform.objects.get_or_create(**params) |
||||
|
|
||||
|
if created: |
||||
|
print("💾 Created platform", platform.name) |
@ -0,0 +1,14 @@ |
|||||
|
from tenancy.models import TenantGroup |
||||
|
from startup_script_utils import load_yaml |
||||
|
import sys |
||||
|
|
||||
|
tenant_groups = load_yaml('/opt/netbox/initializers/tenant_groups.yml') |
||||
|
|
||||
|
if tenant_groups is None: |
||||
|
sys.exit() |
||||
|
|
||||
|
for params in tenant_groups: |
||||
|
tenant_group, created = TenantGroup.objects.get_or_create(**params) |
||||
|
|
||||
|
if created: |
||||
|
print("🔳 Created Tenant Group", tenant_group.name) |
@ -0,0 +1,39 @@ |
|||||
|
from tenancy.models import Tenant, TenantGroup |
||||
|
from extras.models import CustomField, CustomFieldValue |
||||
|
from startup_script_utils import load_yaml |
||||
|
import sys |
||||
|
|
||||
|
tenants = load_yaml('/opt/netbox/initializers/tenants.yml') |
||||
|
|
||||
|
if tenants is None: |
||||
|
sys.exit() |
||||
|
|
||||
|
optional_assocs = { |
||||
|
'group': (TenantGroup, 'name') |
||||
|
} |
||||
|
|
||||
|
for params in tenants: |
||||
|
custom_fields = params.pop('custom_fields', None) |
||||
|
|
||||
|
for assoc, details in optional_assocs.items(): |
||||
|
if assoc in params: |
||||
|
model, field = details |
||||
|
query = { field: params.pop(assoc) } |
||||
|
|
||||
|
params[assoc] = model.objects.get(**query) |
||||
|
|
||||
|
tenant, created = Tenant.objects.get_or_create(**params) |
||||
|
|
||||
|
if created: |
||||
|
if custom_fields is not None: |
||||
|
for cf_name, cf_value in custom_fields.items(): |
||||
|
custom_field = CustomField.objects.get(name=cf_name) |
||||
|
custom_field_value = CustomFieldValue.objects.create( |
||||
|
field=custom_field, |
||||
|
obj=tenant, |
||||
|
value=cf_value |
||||
|
) |
||||
|
|
||||
|
tenant.custom_field_values.add(custom_field_value) |
||||
|
|
||||
|
print("👩💻 Created Tenant", tenant.name) |
@ -0,0 +1,59 @@ |
|||||
|
from dcim.models import Site, Rack, DeviceRole, DeviceType, Device, Platform |
||||
|
from virtualization.models import Cluster |
||||
|
from tenancy.models import Tenant |
||||
|
from extras.models import CustomField, CustomFieldValue |
||||
|
from startup_script_utils import load_yaml |
||||
|
import sys |
||||
|
|
||||
|
devices = load_yaml('/opt/netbox/initializers/devices.yml') |
||||
|
|
||||
|
if devices is None: |
||||
|
sys.exit() |
||||
|
|
||||
|
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') |
||||
|
} |
||||
|
|
||||
|
for params in devices: |
||||
|
custom_fields = params.pop('custom_fields', None) |
||||
|
# 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) |
||||
|
|
||||
|
device, created = Device.objects.get_or_create(**params) |
||||
|
|
||||
|
if created: |
||||
|
if custom_fields is not None: |
||||
|
for cf_name, cf_value in custom_fields.items(): |
||||
|
custom_field = CustomField.objects.get(name=cf_name) |
||||
|
custom_field_value = CustomFieldValue.objects.create( |
||||
|
field=custom_field, |
||||
|
obj=device, |
||||
|
value=cf_value |
||||
|
) |
||||
|
|
||||
|
device.custom_field_values.add(custom_field_value) |
||||
|
|
||||
|
print("🖥️ Created device", device.name) |
@ -0,0 +1,14 @@ |
|||||
|
from virtualization.models import ClusterType |
||||
|
from startup_script_utils import load_yaml |
||||
|
import sys |
||||
|
|
||||
|
cluster_types = load_yaml('/opt/netbox/initializers/cluster_types.yml') |
||||
|
|
||||
|
if cluster_types is None: |
||||
|
sys.exit() |
||||
|
|
||||
|
for params in cluster_types: |
||||
|
cluster_type, created = ClusterType.objects.get_or_create(**params) |
||||
|
|
||||
|
if created: |
||||
|
print("🧰 Created Cluster Type", cluster_type.name) |
@ -0,0 +1,14 @@ |
|||||
|
from ipam.models import RIR |
||||
|
from startup_script_utils import load_yaml |
||||
|
import sys |
||||
|
|
||||
|
rirs = load_yaml('/opt/netbox/initializers/rirs.yml') |
||||
|
|
||||
|
if rirs is None: |
||||
|
sys.exit() |
||||
|
|
||||
|
for params in rirs: |
||||
|
rir, created = RIR.objects.get_or_create(**params) |
||||
|
|
||||
|
if created: |
||||
|
print("🗺️ Created RIR", rir.name) |
@ -0,0 +1,42 @@ |
|||||
|
from ipam.models import Aggregate, RIR |
||||
|
|
||||
|
from extras.models import CustomField, CustomFieldValue |
||||
|
|
||||
|
from netaddr import IPNetwork |
||||
|
from startup_script_utils import load_yaml |
||||
|
import sys |
||||
|
|
||||
|
aggregates = load_yaml('/opt/netbox/initializers/aggregates.yml') |
||||
|
|
||||
|
if aggregates is None: |
||||
|
sys.exit() |
||||
|
|
||||
|
required_assocs = { |
||||
|
'rir': (RIR, 'name') |
||||
|
} |
||||
|
|
||||
|
for params in aggregates: |
||||
|
custom_fields = params.pop('custom_fields', None) |
||||
|
params['prefix'] = IPNetwork(params['prefix']) |
||||
|
|
||||
|
for assoc, details in required_assocs.items(): |
||||
|
model, field = details |
||||
|
query = { field: params.pop(assoc) } |
||||
|
|
||||
|
params[assoc] = model.objects.get(**query) |
||||
|
|
||||
|
aggregate, created = Aggregate.objects.get_or_create(**params) |
||||
|
|
||||
|
if created: |
||||
|
if custom_fields is not None: |
||||
|
for cf_name, cf_value in custom_fields.items(): |
||||
|
custom_field = CustomField.objects.get(name=cf_name) |
||||
|
custom_field_value = CustomFieldValue.objects.create( |
||||
|
field=custom_field, |
||||
|
obj=aggregate, |
||||
|
value=cf_value |
||||
|
) |
||||
|
|
||||
|
aggregate.custom_field_values.add(custom_field_value) |
||||
|
|
||||
|
print("🗞️ Created Aggregate", aggregate.prefix) |
@ -0,0 +1,51 @@ |
|||||
|
from dcim.models import Site |
||||
|
from virtualization.models import Cluster, ClusterType, ClusterGroup |
||||
|
from extras.models import CustomField, CustomFieldValue |
||||
|
from startup_script_utils import load_yaml |
||||
|
import sys |
||||
|
|
||||
|
clusters = load_yaml('/opt/netbox/initializers/clusters.yml') |
||||
|
|
||||
|
if clusters is None: |
||||
|
sys.exit() |
||||
|
|
||||
|
required_assocs = { |
||||
|
'type': (ClusterType, 'name') |
||||
|
} |
||||
|
|
||||
|
optional_assocs = { |
||||
|
'site': (Site, 'name'), |
||||
|
'group': (ClusterGroup, 'name') |
||||
|
} |
||||
|
|
||||
|
for params in clusters: |
||||
|
custom_fields = params.pop('custom_fields', None) |
||||
|
|
||||
|
for assoc, details in required_assocs.items(): |
||||
|
model, field = details |
||||
|
query = { field: params.pop(assoc) } |
||||
|
|
||||
|
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) |
||||
|
|
||||
|
cluster, created = Cluster.objects.get_or_create(**params) |
||||
|
|
||||
|
if created: |
||||
|
if custom_fields is not None: |
||||
|
for cf_name, cf_value in custom_fields.items(): |
||||
|
custom_field = CustomField.objects.get(name=cf_name) |
||||
|
custom_field_value = CustomFieldValue.objects.create( |
||||
|
field=custom_field, |
||||
|
obj=cluster, |
||||
|
value=cf_value |
||||
|
) |
||||
|
|
||||
|
cluster.custom_field_values.add(custom_field_value) |
||||
|
|
||||
|
print("🗄️ Created cluster", cluster.name) |
@ -0,0 +1,42 @@ |
|||||
|
from ipam.models import VRF |
||||
|
from tenancy.models import Tenant |
||||
|
|
||||
|
from extras.models import CustomField, CustomFieldValue |
||||
|
|
||||
|
from startup_script_utils import load_yaml |
||||
|
import sys |
||||
|
|
||||
|
vrfs = load_yaml('/opt/netbox/initializers/vrfs.yml') |
||||
|
|
||||
|
if vrfs is None: |
||||
|
sys.exit() |
||||
|
|
||||
|
optional_assocs = { |
||||
|
'tenant': (Tenant, 'name') |
||||
|
} |
||||
|
|
||||
|
for params in vrfs: |
||||
|
custom_fields = params.pop('custom_fields', None) |
||||
|
|
||||
|
for assoc, details in optional_assocs.items(): |
||||
|
if assoc in params: |
||||
|
model, field = details |
||||
|
query = { field: params.pop(assoc) } |
||||
|
|
||||
|
params[assoc] = model.objects.get(**query) |
||||
|
|
||||
|
vrf, created = VRF.objects.get_or_create(**params) |
||||
|
|
||||
|
if created: |
||||
|
if custom_fields is not None: |
||||
|
for cf_name, cf_value in custom_fields.items(): |
||||
|
custom_field = CustomField.objects.get(name=cf_name) |
||||
|
custom_field_value = CustomFieldValue.objects.create( |
||||
|
field=custom_field, |
||||
|
obj=vrf, |
||||
|
value=cf_value |
||||
|
) |
||||
|
|
||||
|
vrf.custom_field_values.add(custom_field_value) |
||||
|
|
||||
|
print("📦 Created VRF", vrf.name) |
@ -0,0 +1,14 @@ |
|||||
|
from ipam.models import Role |
||||
|
from startup_script_utils import load_yaml |
||||
|
import sys |
||||
|
|
||||
|
roles = load_yaml('/opt/netbox/initializers/prefix_vlan_roles.yml') |
||||
|
|
||||
|
if roles is None: |
||||
|
sys.exit() |
||||
|
|
||||
|
for params in roles: |
||||
|
role, created = Role.objects.get_or_create(**params) |
||||
|
|
||||
|
if created: |
||||
|
print("⛹️ Created Prefix/VLAN Role", role.name) |
@ -0,0 +1,40 @@ |
|||||
|
from dcim.models import Site |
||||
|
from ipam.models import VLANGroup |
||||
|
from extras.models import CustomField, CustomFieldValue |
||||
|
from startup_script_utils import load_yaml |
||||
|
import sys |
||||
|
|
||||
|
vlan_groups = load_yaml('/opt/netbox/initializers/vlan_groups.yml') |
||||
|
|
||||
|
if vlan_groups is None: |
||||
|
sys.exit() |
||||
|
|
||||
|
optional_assocs = { |
||||
|
'site': (Site, 'name') |
||||
|
} |
||||
|
|
||||
|
for params in vlan_groups: |
||||
|
custom_fields = params.pop('custom_fields', None) |
||||
|
|
||||
|
for assoc, details in optional_assocs.items(): |
||||
|
if assoc in params: |
||||
|
model, field = details |
||||
|
query = { field: params.pop(assoc) } |
||||
|
|
||||
|
params[assoc] = model.objects.get(**query) |
||||
|
|
||||
|
vlan_group, created = VLANGroup.objects.get_or_create(**params) |
||||
|
|
||||
|
if created: |
||||
|
if custom_fields is not None: |
||||
|
for cf_name, cf_value in custom_fields.items(): |
||||
|
custom_field = CustomField.objects.get(name=cf_name) |
||||
|
custom_field_value = CustomFieldValue.objects.create( |
||||
|
field=custom_field, |
||||
|
obj=vlan_group, |
||||
|
value=cf_value |
||||
|
) |
||||
|
|
||||
|
vlan_group.custom_field_values.add(custom_field_value) |
||||
|
|
||||
|
print("🏘️ Created VLAN Group", vlan_group.name) |
@ -0,0 +1,45 @@ |
|||||
|
from dcim.models import Site |
||||
|
from ipam.models import VLAN, VLANGroup, Role |
||||
|
from tenancy.models import Tenant, TenantGroup |
||||
|
from extras.models import CustomField, CustomFieldValue |
||||
|
from startup_script_utils import load_yaml |
||||
|
import sys |
||||
|
|
||||
|
vlans = load_yaml('/opt/netbox/initializers/vlans.yml') |
||||
|
|
||||
|
if vlans is None: |
||||
|
sys.exit() |
||||
|
|
||||
|
optional_assocs = { |
||||
|
'site': (Site, 'name'), |
||||
|
'tenant': (Tenant, 'name'), |
||||
|
'tenant_group': (TenantGroup, 'name'), |
||||
|
'group': (VLANGroup, 'name'), |
||||
|
'role': (Role, 'name') |
||||
|
} |
||||
|
|
||||
|
for params in vlans: |
||||
|
custom_fields = params.pop('custom_fields', None) |
||||
|
|
||||
|
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) |
||||
|
|
||||
|
vlan, created = VLAN.objects.get_or_create(**params) |
||||
|
|
||||
|
if created: |
||||
|
if custom_fields is not None: |
||||
|
for cf_name, cf_value in custom_fields.items(): |
||||
|
custom_field = CustomField.objects.get(name=cf_name) |
||||
|
custom_field_value = CustomFieldValue.objects.create( |
||||
|
field=custom_field, |
||||
|
obj=vlan, |
||||
|
value=cf_value |
||||
|
) |
||||
|
|
||||
|
vlan.custom_field_values.add(custom_field_value) |
||||
|
|
||||
|
print("🏠 Created VLAN", vlan.name) |
@ -0,0 +1,46 @@ |
|||||
|
from dcim.models import Site |
||||
|
from ipam.models import Prefix, VLAN, Role, VRF |
||||
|
from tenancy.models import Tenant, TenantGroup |
||||
|
from extras.models import CustomField, CustomFieldValue |
||||
|
from netaddr import IPNetwork |
||||
|
from startup_script_utils import load_yaml |
||||
|
import sys |
||||
|
|
||||
|
prefixes = load_yaml('/opt/netbox/initializers/prefixes.yml') |
||||
|
|
||||
|
if prefixes is None: |
||||
|
sys.exit() |
||||
|
|
||||
|
optional_assocs = { |
||||
|
'site': (Site, 'name'), |
||||
|
'tenant': (Tenant, 'name'), |
||||
|
'tenant_group': (TenantGroup, 'name'), |
||||
|
'vlan': (VLAN, 'name'), |
||||
|
'role': (Role, 'name'), |
||||
|
'vrf': (VRF, 'name') |
||||
|
} |
||||
|
|
||||
|
for params in prefixes: |
||||
|
custom_fields = params.pop('custom_fields', None) |
||||
|
params['prefix'] = IPNetwork(params['prefix']) |
||||
|
|
||||
|
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) |
||||
|
|
||||
|
prefix, created = Prefix.objects.get_or_create(**params) |
||||
|
|
||||
|
if created: |
||||
|
if custom_fields is not None: |
||||
|
for cf_name, cf_value in custom_fields.items(): |
||||
|
custom_field = CustomField.objects.get(name=cf_name) |
||||
|
custom_field_value = CustomFieldValue.objects.create( |
||||
|
field=custom_field, |
||||
|
obj=prefix, |
||||
|
value=cf_value |
||||
|
) |
||||
|
prefix.custom_field_values.add(custom_field_value) |
||||
|
|
||||
|
print("📌 Created Prefix", prefix.prefix) |
@ -0,0 +1,56 @@ |
|||||
|
from dcim.models import Site, Platform, DeviceRole |
||||
|
from virtualization.models import Cluster, VirtualMachine |
||||
|
from tenancy.models import Tenant |
||||
|
from extras.models import CustomField, CustomFieldValue |
||||
|
from startup_script_utils import load_yaml |
||||
|
import sys |
||||
|
|
||||
|
virtual_machines = load_yaml('/opt/netbox/initializers/virtual_machines.yml') |
||||
|
|
||||
|
if virtual_machines is None: |
||||
|
sys.exit() |
||||
|
|
||||
|
required_assocs = { |
||||
|
'cluster': (Cluster, 'name') |
||||
|
} |
||||
|
|
||||
|
optional_assocs = { |
||||
|
'tenant': (Tenant, 'name'), |
||||
|
'platform': (Platform, 'name'), |
||||
|
'role': (DeviceRole, 'name') |
||||
|
} |
||||
|
|
||||
|
for params in virtual_machines: |
||||
|
custom_fields = params.pop('custom_fields', None) |
||||
|
# 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) |
||||
|
|
||||
|
virtual_machine, created = VirtualMachine.objects.get_or_create(**params) |
||||
|
|
||||
|
if created: |
||||
|
if custom_fields is not None: |
||||
|
for cf_name, cf_value in custom_fields.items(): |
||||
|
custom_field = CustomField.objects.get(name=cf_name) |
||||
|
custom_field_value = CustomFieldValue.objects.create( |
||||
|
field=custom_field, |
||||
|
obj=virtual_machine, |
||||
|
value=cf_value |
||||
|
) |
||||
|
|
||||
|
virtual_machine.custom_field_values.add(custom_field_value) |
||||
|
|
||||
|
print("🖥️ Created virtual machine", virtual_machine.name) |
@ -0,0 +1,38 @@ |
|||||
|
from virtualization.models import VirtualMachine, VMInterface |
||||
|
from extras.models import CustomField, CustomFieldValue |
||||
|
from startup_script_utils import load_yaml |
||||
|
import sys |
||||
|
|
||||
|
interfaces = load_yaml('/opt/netbox/initializers/virtualization_interfaces.yml') |
||||
|
|
||||
|
if interfaces is None: |
||||
|
sys.exit() |
||||
|
|
||||
|
required_assocs = { |
||||
|
'virtual_machine': (VirtualMachine, 'name') |
||||
|
} |
||||
|
|
||||
|
for params in interfaces: |
||||
|
custom_fields = params.pop('custom_fields', None) |
||||
|
|
||||
|
for assoc, details in required_assocs.items(): |
||||
|
model, field = details |
||||
|
query = { field: params.pop(assoc) } |
||||
|
|
||||
|
params[assoc] = model.objects.get(**query) |
||||
|
|
||||
|
interface, created = VMInterface.objects.get_or_create(**params) |
||||
|
|
||||
|
if created: |
||||
|
if custom_fields is not None: |
||||
|
for cf_name, cf_value in custom_fields.items(): |
||||
|
custom_field = CustomField.objects.get(name=cf_name) |
||||
|
custom_field_value = CustomFieldValue.objects.create( |
||||
|
field=custom_field, |
||||
|
obj=interface, |
||||
|
value=cf_value |
||||
|
) |
||||
|
|
||||
|
interface.custom_field_values.add(custom_field_value) |
||||
|
|
||||
|
print("🧷 Created interface", interface.name, interface.virtual_machine.name) |
@ -0,0 +1,38 @@ |
|||||
|
from dcim.models import Interface, Device |
||||
|
from extras.models import CustomField, CustomFieldValue |
||||
|
from startup_script_utils import load_yaml |
||||
|
import sys |
||||
|
|
||||
|
interfaces= load_yaml('/opt/netbox/initializers/dcim_interfaces.yml') |
||||
|
|
||||
|
if interfaces is None: |
||||
|
sys.exit() |
||||
|
|
||||
|
required_assocs = { |
||||
|
'device': (Device, 'name') |
||||
|
} |
||||
|
|
||||
|
for params in interfaces: |
||||
|
custom_fields = params.pop('custom_fields', None) |
||||
|
|
||||
|
for assoc, details in required_assocs.items(): |
||||
|
model, field = details |
||||
|
query = { field: params.pop(assoc) } |
||||
|
|
||||
|
params[assoc] = model.objects.get(**query) |
||||
|
|
||||
|
interface, created = Interface.objects.get_or_create(**params) |
||||
|
|
||||
|
if created: |
||||
|
if custom_fields is not None: |
||||
|
for cf_name, cf_value in custom_fields.items(): |
||||
|
custom_field = CustomField.objects.get(name=cf_name) |
||||
|
custom_field_value = CustomFieldValue.objects.create( |
||||
|
field=custom_field, |
||||
|
obj=interface, |
||||
|
value=cf_value |
||||
|
) |
||||
|
|
||||
|
interface.custom_field_values.add(custom_field_value) |
||||
|
|
||||
|
print("🧷 Created interface", interface.name, interface.device.name) |
@ -0,0 +1,69 @@ |
|||||
|
import sys |
||||
|
|
||||
|
from dcim.models import Device, Interface |
||||
|
from django.contrib.contenttypes.models import ContentType |
||||
|
from django.db.models import Q |
||||
|
from extras.models import CustomField, CustomFieldValue |
||||
|
from ipam.models import VRF, IPAddress |
||||
|
from netaddr import IPNetwork |
||||
|
from startup_script_utils import load_yaml |
||||
|
from 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() |
||||
|
|
||||
|
optional_assocs = { |
||||
|
'tenant': (Tenant, 'name'), |
||||
|
'vrf': (VRF, 'name'), |
||||
|
'interface': (None, None) |
||||
|
} |
||||
|
|
||||
|
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: |
||||
|
vm = params.pop('virtual_machine', None) |
||||
|
device = params.pop('device', None) |
||||
|
custom_fields = params.pop('custom_fields', None) |
||||
|
params['address'] = IPNetwork(params['address']) |
||||
|
|
||||
|
if vm and device: |
||||
|
print("IP Address can only specify one of the following: virtual_machine or device.") |
||||
|
sys.exit() |
||||
|
|
||||
|
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) |
||||
|
|
||||
|
ip_address, created = IPAddress.objects.get_or_create(**params) |
||||
|
|
||||
|
if created: |
||||
|
if custom_fields is not None: |
||||
|
for cf_name, cf_value in custom_fields.items(): |
||||
|
custom_field = CustomField.objects.get(name=cf_name) |
||||
|
custom_field_value = CustomFieldValue.objects.create( |
||||
|
field=custom_field, |
||||
|
obj=ip_address, |
||||
|
value=cf_value |
||||
|
) |
||||
|
|
||||
|
ip_address.custom_field_values.add(custom_field_value) |
||||
|
|
||||
|
print("🧬 Created IP Address", ip_address.address) |
@ -0,0 +1,43 @@ |
|||||
|
from dcim.models import Device |
||||
|
from ipam.models import IPAddress |
||||
|
from virtualization.models import VirtualMachine |
||||
|
from startup_script_utils import load_yaml |
||||
|
import sys |
||||
|
|
||||
|
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') |
||||
|
|
||||
|
if devices is None and virtual_machines is None: |
||||
|
sys.exit() |
||||
|
|
||||
|
optional_assocs = { |
||||
|
'primary_ip4': (IPAddress, 'address'), |
||||
|
'primary_ip6': (IPAddress, 'address') |
||||
|
} |
||||
|
|
||||
|
link_primary_ip(devices, Device) |
||||
|
link_primary_ip(virtual_machines, VirtualMachine) |
@ -0,0 +1,29 @@ |
|||||
|
#!/usr/bin/env python3 |
||||
|
|
||||
|
import runpy |
||||
|
from os import scandir |
||||
|
from os.path import dirname, abspath |
||||
|
|
||||
|
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 |
@ -0,0 +1,2 @@ |
|||||
|
from .load_yaml import load_yaml |
||||
|
from .permissions import set_permissions |
@ -0,0 +1,10 @@ |
|||||
|
from ruamel.yaml import YAML |
||||
|
from pathlib import Path |
||||
|
|
||||
|
def load_yaml(yaml_file: str): |
||||
|
yf = Path(yaml_file) |
||||
|
if not yf.is_file(): |
||||
|
return None |
||||
|
with yf.open("r") as stream: |
||||
|
yaml = YAML(typ="safe") |
||||
|
return yaml.load(stream) |
@ -0,0 +1,18 @@ |
|||||
|
from django.contrib.auth.models import Permission |
||||
|
|
||||
|
|
||||
|
def set_permissions(subject, permission_filters): |
||||
|
if subject is None or permission_filters is None: |
||||
|
return |
||||
|
subject.clear() |
||||
|
for permission_filter in permission_filters: |
||||
|
if "*" in permission_filter: |
||||
|
permission_filter_regex = "^" + permission_filter.replace("*", ".*") + "$" |
||||
|
permissions = Permission.objects.filter(codename__iregex=permission_filter_regex) |
||||
|
print(" ⚿ Granting", permissions.count(), "permissions matching '" + permission_filter + "'") |
||||
|
else: |
||||
|
permissions = Permission.objects.filter(codename=permission_filter) |
||||
|
print(" ⚿ Granting permission", permission_filter) |
||||
|
|
||||
|
for permission in permissions: |
||||
|
subject.add(permission) |
Reference in new issue