Merge branch 'ScanPlusGmbH-openshift-config'

This commit is contained in:
Christian Mäder 2018-02-22 18:05:07 +01:00
commit a0f4fc677e
No known key found for this signature in database
GPG Key ID: 92FFD0A711F196BB
16 changed files with 428 additions and 172 deletions

4
.dockerignore Normal file
View File

@ -0,0 +1,4 @@
.github
.travis.yml
build*
*.env

View File

@ -16,7 +16,11 @@ RUN apk add --no-cache \
postgresql-dev \ postgresql-dev \
wget wget
RUN pip install gunicorn RUN pip install \
# gunicorn is used for launching netbox
gunicorn \
# ruamel is used in startup_scripts
ruamel.yaml
WORKDIR /opt WORKDIR /opt
@ -31,11 +35,14 @@ RUN pip install -r requirements.txt
COPY docker/configuration.docker.py /opt/netbox/netbox/netbox/configuration.py COPY docker/configuration.docker.py /opt/netbox/netbox/netbox/configuration.py
COPY docker/gunicorn_config.py /opt/netbox/ COPY docker/gunicorn_config.py /opt/netbox/
COPY docker/nginx.conf /etc/netbox-nginx/nginx.conf COPY docker/nginx.conf /etc/netbox-nginx/nginx.conf
COPY docker/docker-entrypoint.sh docker-entrypoint.sh
COPY startup_scripts/ /opt/netbox/startup_scripts/
COPY initializers/ /opt/netbox/initializers/
COPY configuration/configuration.py /etc/netbox/configuration.py
WORKDIR /opt/netbox/netbox WORKDIR /opt/netbox/netbox
COPY docker/docker-entrypoint.sh /docker-entrypoint.sh ENTRYPOINT [ "/opt/netbox/docker-entrypoint.sh" ]
ENTRYPOINT [ "/docker-entrypoint.sh" ]
VOLUME ["/etc/netbox-nginx/"] VOLUME ["/etc/netbox-nginx/"]

View File

@ -66,11 +66,11 @@ For example defining `ALLOWED_HOSTS=localhost ::1 127.0.0.1` would allows access
[compose-env]: https://docs.docker.com/compose/environment-variables/ [compose-env]: https://docs.docker.com/compose/environment-variables/
### Custom Initialisation Code (e.g. Automatically Setting Up Custom Fields) ### Custom Initialization Code (e.g. Automatically Setting Up Custom Fields)
When using `docker-compose`, all the python scripts present in `docker/startup_scripts` will automatically be executed after the application boots in the context of `./manage.py`. When using `docker-compose`, all the python scripts present in `/opt/netbox/startup_scripts` will automatically be executed after the application boots in the context of `./manage.py`.
That mechanism can be used for many things, and in particular to load Netbox custom fields: That mechanism can be used for many things, e.g. to create Netbox custom fields:
```python ```python
# docker/startup_scripts/load_custom_fields.py # docker/startup_scripts/load_custom_fields.py
@ -94,12 +94,54 @@ if created:
my_custom_field.obj_type.add(device_type) my_custom_field.obj_type.add(device_type)
``` ```
#### Initializers
Initializers are built-in startup scripts for defining Netbox custom fields, groups and users.
All you need to do is to mount you own `initializers` folder ([see `docker-compose.yml`][netbox-docker-compose]).
Look at the [`initializers` folder][netbox-docker-initializers] to learn how the files must look like.
Here's an example for defining a custom field:
```yaml
# initializers/custom_fields.yml
text_field:
type: text
label: Custom Text
description: Enter text in a text field.
required: false
filterable: true
weight: 0
on_objects:
- dcim.models.Device
- dcim.models.Rack
- ipam.models.IPAddress
- ipam.models.Prefix
- tenancy.models.Tenant
- virtualization.models.VirtualMachine
```
[netbox-docker-initializers]: https://github.com/ninech/netbox-docker/tree/master/initializers
[netbox-docker-compose]: https://github.com/ninech/netbox-docker/blob/master/docker-compose.yml
#### Custom Docker Image
You can also build your own Netbox Docker image containing your own startup scripts, custom fields, users and groups
like this:
```
ARG VERSION=latest
FROM ninech/netbox:$VERSION
COPY startup_scripts/ /opt/netbox/startup_scripts/
COPY initializers/ /opt/netbox/initializers/
```
### Production ### Production
The default settings are optimized for (local) development environments. The default settings are optimized for (local) development environments.
You should therefore adjust the configuration for production setups, at least the following variables: You should therefore adjust the configuration for production setups, at least the following variables:
* `ALLOWED_HOSTS`: Add all URLs that lead to your netbox instance. * `ALLOWED_HOSTS`: Add all URLs that lead to your Netbox instance.
* `DB_*`: Use a persistent database. * `DB_*`: Use a persistent database.
* `EMAIL_*`: Use your own mailserver. * `EMAIL_*`: Use your own mailserver.
* `MAX_PAGE_SIZE`: Use the recommended default of 1000. * `MAX_PAGE_SIZE`: Use the recommended default of 1000.
@ -109,7 +151,7 @@ You should therefore adjust the configuration for production setups, at least th
You may run this image in a cluster such as Docker Swarm, Kubernetes or OpenShift, but this is advanced level. You may run this image in a cluster such as Docker Swarm, Kubernetes or OpenShift, but this is advanced level.
In this case, we encourage you to statically configure Netbox by starting from [Netbox's example config file][default-config], and mounting it into your container using the mechanism provided by your container platform (i.e. [Docker Swarm configs][swarm-config], [Kubernetes ConfigMap][k8s-config], [OpenShift ConfigMaps][openshift-config]). In this case, we encourage you to statically configure Netbox by starting from [Netbox's example config file][default-config], and mounting it into your container in the directory `/etc/netbox/` using the mechanism provided by your container platform (i.e. [Docker Swarm configs][swarm-config], [Kubernetes ConfigMap][k8s-config], [OpenShift ConfigMaps][openshift-config]).
But if you rather continue to configure your application through environment variables, you may continue to use [the built-in configuration file][docker-config]. But if you rather continue to configure your application through environment variables, you may continue to use [the built-in configuration file][docker-config].
We discourage storing secrets in environment variables, as environment variable are passed on to all sub-processes and may leak easily into other systems, e.g. error collecting tools that often collect all environment variables whenever an error occurs. We discourage storing secrets in environment variables, as environment variable are passed on to all sub-processes and may leak easily into other systems, e.g. error collecting tools that often collect all environment variables whenever an error occurs.

View File

@ -0,0 +1,156 @@
import os
import socket
# For reference see http://netbox.readthedocs.io/en/latest/configuration/mandatory-settings/
# Based on https://github.com/digitalocean/netbox/blob/develop/netbox/netbox/configuration.example.py
# Read secret from file
def read_secret(secret_name):
try:
f = open('/run/secrets/' + secret_name, 'r', encoding='utf-8')
except EnvironmentError:
return ''
else:
with f:
return f.readline().strip()
BASE_DIR = os.path.dirname(os.path.dirname(os.path.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 = os.environ.get('ALLOWED_HOSTS', socket.gethostname()).split(' ')
# PostgreSQL database configuration.
DATABASE = {
'NAME': os.environ.get('DB_NAME', 'netbox'), # Database name
'USER': os.environ.get('DB_USER', ''), # PostgreSQL username
'PASSWORD': os.environ.get('DB_PASSWORD', read_secret('db_password')),
# PostgreSQL password
'HOST': os.environ.get('DB_HOST', 'localhost'), # Database server
'PORT': os.environ.get('DB_PORT', ''), # Database port (leave blank for default)
}
# 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/dev/ref/settings/#std:setting-SECRET_KEY
SECRET_KEY = os.environ.get('SECRET_KEY', read_secret('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'],
]
# 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 = os.environ.get('BANNER_TOP', '')
BANNER_BOTTOM = os.environ.get('BANNER_BOTTOM', '')
# Text to include on the login page above the login form. HTML is allowed.
BANNER_LOGIN = os.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 = os.environ.get('BASE_PATH', '')
# 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 = os.environ.get('CORS_ORIGIN_ALLOW_ALL', False)
CORS_ORIGIN_WHITELIST = os.environ.get('CORS_ORIGIN_WHITELIST', '').split(' ')
CORS_ORIGIN_REGEX_WHITELIST = [
# r'^(https?://)?(\w+\.)?example\.com$',
]
# 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 = os.environ.get('DEBUG', False)
# Email settings
EMAIL = {
'SERVER': os.environ.get('EMAIL_SERVER', 'localhost'),
'PORT': int(os.environ.get('EMAIL_PORT', 25)),
'USERNAME': os.environ.get('EMAIL_USERNAME', ''),
'PASSWORD': os.environ.get('EMAIL_PASSWORD', read_secret('email_password')),
'TIMEOUT': int(os.environ.get('EMAIL_TIMEOUT', 10)), # seconds
'FROM_EMAIL': os.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 = os.environ.get('ENFORCE_GLOBAL_UNIQUE', False)
# Enable custom logging. Please see the Django documentation for detailed guidance on configuring custom logs:
# https://docs.djangoproject.com/en/1.11/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 = os.environ.get('LOGIN_REQUIRED', False)
# Base URL path if accessing NetBox within a directory. For example, if installed at http://example.com/netbox/, set:
# BASE_PATH = 'netbox/'
BASE_PATH = os.environ.get('BASE_PATH', '')
# Setting this to True will display a "maintenance mode" banner at the top of every page.
MAINTENANCE_MODE = os.environ.get('MAINTENANCE_MODE', False)
# 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(os.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 = os.environ.get('MEDIA_ROOT', os.path.join(BASE_DIR, 'media'))
# Credentials that NetBox will use to access live devices.
NAPALM_USERNAME = os.environ.get('NAPALM_USERNAME', '')
NAPALM_PASSWORD = os.environ.get('NAPALM_PASSWORD', read_secret('napalm_password'))
# NAPALM timeout (in seconds). (Default: 30)
NAPALM_TIMEOUT = int(os.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(os.environ.get('PAGINATE_COUNT', 50))
# When determining the primary IP address for a device, IPv6 is preferred over IPv4 by default. Set this to True to
# prefer IPv4 instead.
PREFER_IPV4 = os.environ.get('PREFER_IPV4', False)
# The file path where custom reports will be stored. A trailing slash is not needed. Note that the default value of
# this setting is derived from the installed location.
REPORTS_ROOT = os.environ.get('REPORTS_ROOT', os.path.join(BASE_DIR, 'reports'))
# Time zone (default: UTC)
TIME_ZONE = os.environ.get('TIME_ZONE', 'UTC')
# Date/time formatting. See the following link for supported formats:
# https://docs.djangoproject.com/en/dev/ref/templates/builtins/#date
DATE_FORMAT = os.environ.get('DATE_FORMAT', 'N j, Y')
SHORT_DATE_FORMAT = os.environ.get('SHORT_DATE_FORMAT', 'Y-m-d')
TIME_FORMAT = os.environ.get('TIME_FORMAT', 'g:i a')
SHORT_TIME_FORMAT = os.environ.get('SHORT_TIME_FORMAT', 'H:i:s')
DATETIME_FORMAT = os.environ.get('DATETIME_FORMAT', 'N j, Y g:i a')
SHORT_DATETIME_FORMAT = os.environ.get('SHORT_DATETIME_FORMAT', 'Y-m-d H:i')

View File

@ -9,11 +9,13 @@ services:
depends_on: depends_on:
- postgres - postgres
env_file: netbox.env env_file: netbox.env
volumes:
- ./configuration:/etc/netbox:ro
command: command:
- ./manage.py - ./manage.py
- test - test
postgres: postgres:
image: postgres:9.6-alpine image: postgres:10.2-alpine
env_file: postgres.env env_file: postgres.env
volumes: volumes:
netbox-static-files: netbox-static-files:

View File

@ -10,23 +10,25 @@ services:
- postgres - postgres
env_file: netbox.env env_file: netbox.env
volumes: volumes:
- ./docker/startup_scripts:/opt/netbox/netbox/startup_scripts - ./startup_scripts:/opt/netbox/startup_scripts:ro
- ./initializers:/opt/netbox/initializers:ro
- ./configuration:/etc/netbox:ro
- netbox-nginx-config:/etc/netbox-nginx/ - netbox-nginx-config:/etc/netbox-nginx/
- netbox-static-files:/opt/netbox/netbox/static - netbox-static-files:/opt/netbox/netbox/static
- netbox-media-files:/opt/netbox/netbox/media - netbox-media-files:/opt/netbox/netbox/media
- netbox-report-files:/opt/netbox/netbox/reports - netbox-report-files:/opt/netbox/netbox/reports
nginx: nginx:
image: nginx:1.11-alpine image: nginx:1.13-alpine
command: nginx -g 'daemon off;' -c /etc/netbox-nginx/nginx.conf command: nginx -g 'daemon off;' -c /etc/netbox-nginx/nginx.conf
depends_on: depends_on:
- netbox - netbox
ports: ports:
- 8080 - 8080
volumes: volumes:
- netbox-static-files:/opt/netbox/netbox/static - netbox-static-files:/opt/netbox/netbox/static:ro
- netbox-nginx-config:/etc/netbox-nginx/ - netbox-nginx-config:/etc/netbox-nginx/:ro
postgres: postgres:
image: postgres:9.6-alpine image: postgres:10.2-alpine
env_file: postgres.env env_file: postgres.env
volumes: volumes:
- netbox-postgres-data:/var/lib/postgresql/data - netbox-postgres-data:/var/lib/postgresql/data

View File

@ -1,156 +1,10 @@
import os import importlib.util
import socket import sys
# For reference see http://netbox.readthedocs.io/en/latest/configuration/mandatory-settings/
# Based on https://github.com/digitalocean/netbox/blob/develop/netbox/netbox/configuration.example.py
# Read secret from file
def read_secret(secret_name):
try: try:
f = open('/run/secrets/' + secret_name, 'r', encoding='utf-8') spec = importlib.util.spec_from_file_location('configuration', '/etc/netbox/configuration.py')
except EnvironmentError: module = importlib.util.module_from_spec(spec)
return '' spec.loader.exec_module(module)
else: sys.modules['netbox.configuration'] = module
with f: except:
return f.readline().strip() raise ImportError('')
BASE_DIR = os.path.dirname(os.path.dirname(os.path.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 = os.environ.get('ALLOWED_HOSTS', socket.gethostname()).split(' ')
# PostgreSQL database configuration.
DATABASE = {
'NAME': os.environ.get('DB_NAME', 'netbox'), # Database name
'USER': os.environ.get('DB_USER', ''), # PostgreSQL username
'PASSWORD': os.environ.get('DB_PASSWORD', read_secret('db_password')),
# PostgreSQL password
'HOST': os.environ.get('DB_HOST', 'localhost'), # Database server
'PORT': os.environ.get('DB_PORT', ''), # Database port (leave blank for default)
}
# 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/dev/ref/settings/#std:setting-SECRET_KEY
SECRET_KEY = os.environ.get('SECRET_KEY', read_secret('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'],
]
# 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 = os.environ.get('BANNER_TOP', '')
BANNER_BOTTOM = os.environ.get('BANNER_BOTTOM', '')
# Text to include on the login page above the login form. HTML is allowed.
BANNER_LOGIN = os.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 = os.environ.get('BASE_PATH', '')
# 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 = os.environ.get('CORS_ORIGIN_ALLOW_ALL', False)
CORS_ORIGIN_WHITELIST = os.environ.get('CORS_ORIGIN_WHITELIST', '').split(' ')
CORS_ORIGIN_REGEX_WHITELIST = [
# r'^(https?://)?(\w+\.)?example\.com$',
]
# 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 = os.environ.get('DEBUG', False)
# Email settings
EMAIL = {
'SERVER': os.environ.get('EMAIL_SERVER', 'localhost'),
'PORT': int(os.environ.get('EMAIL_PORT', 25)),
'USERNAME': os.environ.get('EMAIL_USERNAME', ''),
'PASSWORD': os.environ.get('EMAIL_PASSWORD', read_secret('email_password')),
'TIMEOUT': int(os.environ.get('EMAIL_TIMEOUT', 10)), # seconds
'FROM_EMAIL': os.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 = os.environ.get('ENFORCE_GLOBAL_UNIQUE', False)
# Enable custom logging. Please see the Django documentation for detailed guidance on configuring custom logs:
# https://docs.djangoproject.com/en/1.11/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 = os.environ.get('LOGIN_REQUIRED', False)
# Base URL path if accessing NetBox within a directory. For example, if installed at http://example.com/netbox/, set:
# BASE_PATH = 'netbox/'
BASE_PATH = os.environ.get('BASE_PATH', '')
# Setting this to True will display a "maintenance mode" banner at the top of every page.
MAINTENANCE_MODE = os.environ.get('MAINTENANCE_MODE', False)
# 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(os.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 = os.environ.get('MEDIA_ROOT', os.path.join(BASE_DIR, 'media'))
# Credentials that NetBox will use to access live devices.
NAPALM_USERNAME = os.environ.get('NAPALM_USERNAME', '')
NAPALM_PASSWORD = os.environ.get('NAPALM_PASSWORD', read_secret('napalm_password'))
# NAPALM timeout (in seconds). (Default: 30)
NAPALM_TIMEOUT = int(os.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(os.environ.get('PAGINATE_COUNT', 50))
# When determining the primary IP address for a device, IPv6 is preferred over IPv4 by default. Set this to True to
# prefer IPv4 instead.
PREFER_IPV4 = os.environ.get('PREFER_IPV4', False)
# The file path where custom reports will be stored. A trailing slash is not needed. Note that the default value of
# this setting is derived from the installed location.
REPORTS_ROOT = os.environ.get('REPORTS_ROOT', os.path.join(BASE_DIR, 'reports'))
# Time zone (default: UTC)
TIME_ZONE = os.environ.get('TIME_ZONE', 'UTC')
# Date/time formatting. See the following link for supported formats:
# https://docs.djangoproject.com/en/dev/ref/templates/builtins/#date
DATE_FORMAT = os.environ.get('DATE_FORMAT', 'N j, Y')
SHORT_DATE_FORMAT = os.environ.get('SHORT_DATE_FORMAT', 'Y-m-d')
TIME_FORMAT = os.environ.get('TIME_FORMAT', 'g:i a')
SHORT_TIME_FORMAT = os.environ.get('SHORT_TIME_FORMAT', 'H:i:s')
DATETIME_FORMAT = os.environ.get('DATETIME_FORMAT', 'N j, Y g:i a')
SHORT_DATETIME_FORMAT = os.environ.get('SHORT_DATETIME_FORMAT', 'Y-m-d H:i')

View File

@ -39,7 +39,8 @@ if not User.objects.filter(username='${SUPERUSER_NAME}'):
Token.objects.create(user=u, key='${SUPERUSER_API_TOKEN}') Token.objects.create(user=u, key='${SUPERUSER_API_TOKEN}')
END END
for script in $(ls startup_scripts/*.py 2> /dev/null); do for script in /opt/netbox/startup_scripts/*.py; do
echo "⚙️ Executing '$script'"
./manage.py shell --plain < "${script}" ./manage.py shell --plain < "${script}"
done done
@ -48,5 +49,6 @@ done
echo "✅ Initialisation is done." echo "✅ Initialisation is done."
# launch whatever is passed by docker via RUN # launch whatever is passed by docker
# (i.e. the RUN instruction in the Dockerfile)
exec ${@} exec ${@}

View File

@ -2,7 +2,6 @@ command = '/usr/bin/gunicorn'
pythonpath = '/opt/netbox/netbox' pythonpath = '/opt/netbox/netbox'
bind = '0.0.0.0:8001' bind = '0.0.0.0:8001'
workers = 3 workers = 3
user = 'root'
errorlog = '-' errorlog = '-'
accesslog = '-' accesslog = '-'
capture_output = False capture_output = False

View File

@ -0,0 +1,66 @@
# text_field:
# type: text
# label: Custom Text
# description: Enter text in a text field.
# required: false
# filterable: true
# weight: 0
# on_objects:
# - dcim.models.Device
# - dcim.models.Rack
# - 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
# filterable: true
# weight: 10
# on_objects:
# - tenancy.models.Tenant
# selection_field:
# type: selection
# label: Choose between items
# required: false
# filterable: true
# 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
# boolean_field:
# type: boolean
# label: Yes Or No?
# required: true
# filterable: true
# 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
# filterable: false
# on_objects:
# - tenancy.models.Tenant
# date_field:
# type: date
# label: Important Date
# required: false
# filterable: false
# on_objects:
# - dcim.models.Device

9
initializers/groups.yml Normal file
View File

@ -0,0 +1,9 @@
# applications:
# users:
# - technical_user
# readers:
# users:
# - reader
# writers:
# users:
# - writer

6
initializers/users.yml Normal file
View File

@ -0,0 +1,6 @@
# technical_user:
# api_token: 0123456789technicaluser789abcdef01234567 # must be looooong!
# reader:
# password: reader
# writer:
# password: writer

View File

@ -0,0 +1,20 @@
from django.contrib.auth.models import Group, User
from users.models import Token
from ruamel.yaml import YAML
with open('/opt/netbox/initializers/users.yml', 'r') as stream:
yaml=YAML(typ='safe')
users = yaml.load(stream)
if users is not None:
for username, user_details in users.items():
if not User.objects.filter(username=username):
user = User.objects.create_user(
username = username,
password = user_details.get('password', 0) or User.objects.make_random_password)
print("👤 Created user ",username)
if user_details.get('api_token', 0):
Token.objects.create(user=user, key=user_details['api_token'])

View File

@ -0,0 +1,19 @@
from django.contrib.auth.models import Group, User
from ruamel.yaml import YAML
with open('/opt/netbox/initializers/groups.yml', 'r') as stream:
yaml=YAML(typ='safe')
groups = yaml.load(stream)
if groups is not None:
for groupname, group_details in groups.items():
group, created = Group.objects.get_or_create(name=groupname)
if created:
print("👥 Created group", groupname)
for username in group_details['users']:
user = User.objects.get(username=username)
if user:
user.groups.add(group)

View File

@ -0,0 +1,68 @@
from extras.constants import CF_TYPE_TEXT, CF_TYPE_INTEGER, CF_TYPE_BOOLEAN, CF_TYPE_DATE, CF_TYPE_URL, CF_TYPE_SELECT
from extras.models import CustomField, CustomFieldChoice
from ruamel.yaml import YAML
text_to_fields = {
'boolean': CF_TYPE_BOOLEAN,
'date': CF_TYPE_DATE,
'integer': CF_TYPE_INTEGER,
'selection': CF_TYPE_SELECT,
'text': CF_TYPE_TEXT,
'url': CF_TYPE_URL,
}
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)
with open('/opt/netbox/initializers/custom_fields.yml', 'r') as stream:
yaml = YAML(typ='safe')
customfields = yaml.load(stream)
if customfields is not None:
for cf_name, cf_details in customfields.items():
custom_field, created = CustomField.objects.get_or_create(name = cf_name)
if created:
if cf_details.get('default', 0):
custom_field.default = cf_details['default']
if cf_details.get('description', 0):
custom_field.description = cf_details['description']
if cf_details.get('filterable', 0):
custom_field.is_filterables = cf_details['filterable']
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 = text_to_fields[cf_details['type']]
if cf_details.get('weight', 0):
custom_field.weight = cf_details['weight']
custom_field.save()
for choice_details in cf_details.get('choices', []):
choice = CustomFieldChoice.objects.create(
field=custom_field,
value=choice_details['value'])
if choice_details.get('weight', 0):
choice.weight = choice_details['weight']
choice.save()
print("🔧 Created custom field", cf_name)