Merge pull request #209 from netbox-community/2.7-prep

Prepare for Netbox 2.7
This commit is contained in:
Christian Mäder 2020-01-20 10:26:50 +01:00 committed by GitHub
commit 8caf755914
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 162 additions and 115 deletions

View File

@ -26,7 +26,9 @@ RUN pip install --prefix="/install" --no-warn-script-location \
# ruamel is used in startup_scripts # ruamel is used in startup_scripts
'ruamel.yaml>=0.15,<0.16' \ 'ruamel.yaml>=0.15,<0.16' \
# django_auth_ldap is required for ldap # django_auth_ldap is required for ldap
django_auth_ldap django_auth_ldap \
# django-storages was introduced in 2.7 and is optional
django-storages
ARG NETBOX_PATH ARG NETBOX_PATH
COPY ${NETBOX_PATH}/requirements.txt / COPY ${NETBOX_PATH}/requirements.txt /

View File

@ -37,9 +37,9 @@ DATABASE = {
# PostgreSQL password # PostgreSQL password
'HOST': os.environ.get('DB_HOST', 'localhost'), # Database server 'HOST': os.environ.get('DB_HOST', 'localhost'), # Database server
'PORT': os.environ.get('DB_PORT', ''), # Database port (leave blank for default) 'PORT': os.environ.get('DB_PORT', ''), # Database port (leave blank for default)
'OPTIONS': {'sslmode': os.environ.get('DB_SSLMODE', 'prefer')}, 'OPTIONS': {'sslmode': os.environ.get('DB_SSLMODE', 'prefer')},
# Database connection SSLMODE # Database connection SSLMODE
'CONN_MAX_AGE': int(os.environ.get('DB_CONN_MAX_AGE', '300')), 'CONN_MAX_AGE': int(os.environ.get('DB_CONN_MAX_AGE', '300')),
# Database connection persistence # Database connection persistence
} }
@ -51,13 +51,22 @@ SECRET_KEY = os.environ.get('SECRET_KEY', read_secret('secret_key'))
# Redis database settings. The Redis database is used for caching and background processing such as webhooks # Redis database settings. The Redis database is used for caching and background processing such as webhooks
REDIS = { REDIS = {
'HOST': os.environ.get('REDIS_HOST', 'localhost'), 'webhooks': {
'PORT': int(os.environ.get('REDIS_PORT', 6379)), 'HOST': os.environ.get('REDIS_HOST', 'localhost'),
'PASSWORD': os.environ.get('REDIS_PASSWORD', read_secret('redis_password')), 'PORT': int(os.environ.get('REDIS_PORT', 6379)),
'DATABASE': os.environ.get('REDIS_DATABASE', '0'), 'PASSWORD': os.environ.get('REDIS_PASSWORD', read_secret('redis_password')),
'CACHE_DATABASE': os.environ.get('REDIS_CACHE_DATABASE', '1'), 'DATABASE': int(os.environ.get('REDIS_DATABASE', 0)),
'DEFAULT_TIMEOUT': os.environ.get('REDIS_TIMEOUT', '300'), 'DEFAULT_TIMEOUT': int(os.environ.get('REDIS_TIMEOUT', 300)),
'SSL': os.environ.get('REDIS_SSL', 'False').lower() == 'true', 'SSL': os.environ.get('REDIS_SSL', 'False').lower() == 'true',
},
'caching': {
'HOST': os.environ.get('REDIS_CACHE_HOST', os.environ.get('REDIS_HOST', 'localhost')),
'PORT': int(os.environ.get('REDIS_CACHE_PORT', os.environ.get('REDIS_PORT', 6379))),
'PASSWORD': os.environ.get('REDIS_CACHE_PASSWORD', os.environ.get('REDIS_PASSWORD', read_secret('redis_cache_password'))),
'DATABASE': int(os.environ.get('REDIS_CACHE_DATABASE', 1)),
'DEFAULT_TIMEOUT': int(os.environ.get('REDIS_CACHE_TIMEOUT', os.environ.get('REDIS_TIMEOUT', 300))),
'SSL': os.environ.get('REDIS_CACHE_SSL', os.environ.get('REDIS_SSL', 'False')).lower() == 'true',
},
} }
######################### #########################
@ -172,10 +181,6 @@ SCRIPTS_ROOT = os.environ.get('SCRIPTS_ROOT', '/etc/netbox/scripts')
# Time zone (default: UTC) # Time zone (default: UTC)
TIME_ZONE = os.environ.get('TIME_ZONE', 'UTC') TIME_ZONE = os.environ.get('TIME_ZONE', 'UTC')
# The Webhook event backend is disabled by default. Set this to True to enable it. Note that this requires a Redis
# database be configured and accessible by NetBox (see `REDIS` below).
WEBHOOKS_ENABLED = os.environ.get('WEBHOOKS_ENABLED', 'False').lower() == 'true'
# Date/time formatting. See the following link for supported formats: # Date/time formatting. See the following link for supported formats:
# https://docs.djangoproject.com/en/dev/ref/templates/builtins/#date # https://docs.djangoproject.com/en/dev/ref/templates/builtins/#date
DATE_FORMAT = os.environ.get('DATE_FORMAT', 'N j, Y') DATE_FORMAT = os.environ.get('DATE_FORMAT', 'N j, Y')

View File

@ -5,6 +5,7 @@ services:
depends_on: depends_on:
- postgres - postgres
- redis - redis
- redis-cache
- netbox-worker - netbox-worker
env_file: env/netbox.env env_file: env/netbox.env
user: '101' user: '101'
@ -50,6 +51,13 @@ services:
env_file: env/redis.env env_file: env/redis.env
volumes: volumes:
- netbox-redis-data:/data - netbox-redis-data:/data
redis-cache:
image: redis:5-alpine
command:
- sh
- -c # this is to evaluate the $REDIS_PASSWORD from the env
- redis-server --requirepass $$REDIS_PASSWORD ## $$ because of docker-compose
env_file: env/redis.env
volumes: volumes:
netbox-static-files: netbox-static-files:
driver: local driver: local

5
env/netbox.env vendored
View File

@ -17,8 +17,11 @@ MAX_PAGE_SIZE=1000
REDIS_HOST=redis REDIS_HOST=redis
REDIS_PASSWORD=H733Kdjndks81 REDIS_PASSWORD=H733Kdjndks81
REDIS_DATABASE=0 REDIS_DATABASE=0
REDIS_CACHE_DATABASE=1
REDIS_SSL=false REDIS_SSL=false
REDIS_CACHE_HOST=redis-cache
REDIS_CACHE_PASSWORD=t4Ph722qJ5QHeQ1qfu36
REDIS_CACHE_DATABASE=0
REDIS_CACHE_SSL=false
SECRET_KEY=r8OwDznj!!dci#P9ghmRfdu1Ysxm0AiPeDCQhKE+N_rClfWNj SECRET_KEY=r8OwDznj!!dci#P9ghmRfdu1Ysxm0AiPeDCQhKE+N_rClfWNj
SKIP_STARTUP_SCRIPTS=false SKIP_STARTUP_SCRIPTS=false
SKIP_SUPERUSER=false SKIP_SUPERUSER=false

1
env/redis-cache.env vendored Normal file
View File

@ -0,0 +1 @@
REDIS_PASSWORD=t4Ph722qJ5QHeQ1qfu36

View File

@ -1,3 +1,18 @@
## Possible Choices:
## type:
## - text
## - integer
## - boolean
## - date
## - url
## - select
## filter_logic:
## - disabled
## - loose
## - exact
##
## Examples:
# text_field: # text_field:
# type: text # type: text
# label: Custom Text # label: Custom Text
@ -22,8 +37,8 @@
# weight: 10 # weight: 10
# on_objects: # on_objects:
# - tenancy.models.Tenant # - tenancy.models.Tenant
# selection_field: # select_field:
# type: selection # type: select
# label: Choose between items # label: Choose between items
# required: false # required: false
# filter_logic: exact # filter_logic: exact
@ -41,8 +56,8 @@
# weight: 50 # weight: 50
# - value: Fourth Item # - value: Fourth Item
# weight: 40 # weight: 40
# selection_field_auto_weight: # select_field_auto_weight:
# type: selection # type: select
# label: Choose between items # label: Choose between items
# required: false # required: false
# filter_logic: loose # filter_logic: loose

View File

@ -1,8 +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 # - device: server01
# enabled: true # enabled: true
# type: Virtual # type: virtual
# name: to-server02 # name: to-server02
# - device: server02 # - device: server02
# enabled: true # enabled: true
# type: Virtual # type: virtual
# name: to-server01 # name: to-server01

View File

@ -1,9 +1,24 @@
## Possible Choices:
## face:
## - front
## - rear
## status:
## - offline
## - active
## - planned
## - staged
## - failed
## - inventory
## - decommissioning
##
## Examples:
# - name: server01 # - name: server01
# device_role: server # device_role: server
# device_type: Other # device_type: Other
# site: AMS 1 # site: AMS 1
# rack: rack-01 # rack: rack-01
# face: Front # face: front
# position: 1 # position: 1
# custom_fields: # custom_fields:
# text_field: Description # text_field: Description
@ -12,7 +27,7 @@
# device_type: Other # device_type: Other
# site: AMS 2 # site: AMS 2
# rack: rack-02 # rack: rack-02
# face: Front # face: front
# position: 2 # position: 2
# custom_fields: # custom_fields:
# text_field: Description # text_field: Description
@ -21,7 +36,7 @@
# device_type: Other # device_type: Other
# site: SING 1 # site: SING 1
# rack: rack-03 # rack: rack-03
# face: Front # face: front
# position: 3 # position: 3
# custom_fields: # custom_fields:
# text_field: Description # text_field: Description

View File

@ -1,26 +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 # - address: 10.1.1.1/24
# device: server01 # device: server01
# interface: to-server02 # interface: to-server02
# status: Active # status: active
# vrf: vrf1 # vrf: vrf1
# - address: 2001:db8:a000:1::1/64 # - address: 2001:db8:a000:1::1/64
# device: server01 # device: server01
# interface: to-server02 # interface: to-server02
# status: Active # status: active
# vrf: vrf1 # vrf: vrf1
# - address: 10.1.1.2/24 # - address: 10.1.1.2/24
# device: server02 # device: server02
# interface: to-server01 # interface: to-server01
# status: Active # status: active
# - address: 2001:db8:a000:1::2/64 # - address: 2001:db8:a000:1::2/64
# device: server02 # device: server02
# interface: to-server01 # interface: to-server01
# status: Active # status: active
# - address: 10.1.1.10/24 # - address: 10.1.1.10/24
# description: reserved IP # description: reserved IP
# status: Reserved # status: reserved
# tenant: tenant1 # tenant: tenant1
# - address: 2001:db8:a000:1::10/64 # - address: 2001:db8:a000:1::10/64
# description: reserved IP # description: reserved IP
# status: Reserved # status: reserved
# tenant: tenant1 # tenant: tenant1

View File

@ -1,13 +1,22 @@
## Possible Choices:
## status:
## - container
## - active
## - reserved
## - deprecated
##
## Examples:
# - description: prefix1 # - description: prefix1
# prefix: 10.1.1.0/24 # prefix: 10.1.1.0/24
# site: AMS 1 # site: AMS 1
# status: Active # status: active
# tenant: tenant1 # tenant: tenant1
# vlan: vlan1 # vlan: vlan1
# - description: prefix2 # - description: prefix2
# prefix: 10.1.2.0/24 # prefix: 10.1.2.0/24
# site: AMS 2 # site: AMS 2
# status: Active # status: active
# tenant: tenant2 # tenant: tenant2
# vlan: vlan2 # vlan: vlan2
# is_pool: true # is_pool: true
@ -15,6 +24,6 @@
# - description: ipv6 prefix1 # - description: ipv6 prefix1
# prefix: 2001:db8:a000:1::/64 # prefix: 2001:db8:a000:1::/64
# site: AMS 2 # site: AMS 2
# status: Active # status: active
# tenant: tenant2 # tenant: tenant2
# vlan: vlan2 # vlan: vlan2

View File

@ -1,16 +1,32 @@
## 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 # - site: AMS 1
# name: rack-01 # name: rack-01
# role: Role 1 # role: Role 1
# type: 4-post cabinet # type: 4-post-cabinet
# width: 19 inches # width: 19
# u_height: 47 # u_height: 47
# custom_fields: # custom_fields:
# text_field: Description # text_field: Description
# - site: AMS 2 # - site: AMS 2
# name: rack-02 # name: rack-02
# role: Role 2 # role: Role 2
# type: 4-post cabinet # type: 4-post-cabinet
# width: 19 inches # width: 19
# u_height: 47 # u_height: 47
# custom_fields: # custom_fields:
# text_field: Description # text_field: Description
@ -18,8 +34,8 @@
# name: rack-03 # name: rack-03
# group: cage 101 # group: cage 101
# role: Role 3 # role: Role 3
# type: 4-post cabinet # type: 4-post-cabinet
# width: 19 inches # width: 19
# u_height: 47 # u_height: 47
# custom_fields: # custom_fields:
# text_field: Description # text_field: Description

View File

@ -1,10 +1,18 @@
## Possible Choices:
## status:
## - active
## - offline
## - staged
##
## Examples:
# - cluster: cluster1 # - cluster: cluster1
# comments: VM1 # comments: VM1
# disk: 200 # disk: 200
# memory: 4096 # memory: 4096
# name: virtual machine 1 # name: virtual machine 1
# platform: Platform 2 # platform: Platform 2
# status: Active # status: active
# tenant: tenant1 # tenant: tenant1
# vcpus: 8 # vcpus: 8
# - cluster: cluster1 # - cluster: cluster1
@ -13,6 +21,6 @@
# memory: 2048 # memory: 2048
# name: virtual machine 2 # name: virtual machine 2
# platform: Platform 2 # platform: Platform 2
# status: Active # status: active
# tenant: tenant1 # tenant: tenant1
# vcpus: 8 # vcpus: 8

View File

@ -1,11 +1,19 @@
## Possible Choices:
## status:
## - active
## - reserved
## - deprecated
##
## Examples:
# - name: vlan1 # - name: vlan1
# site: AMS 1 # site: AMS 1
# status: Active # status: active
# vid: 5 # vid: 5
# role: Main Management # role: Main Management
# description: VLAN 5 for MGMT # description: VLAN 5 for MGMT
# - group: VLAN group 2 # - group: VLAN group 2
# name: vlan2 # name: vlan2
# site: AMS 1 # site: AMS 1
# status: Active # status: active
# vid: 1300 # vid: 1300

View File

@ -1,19 +1,9 @@
from extras.constants import CF_TYPE_TEXT, CF_TYPE_INTEGER, CF_TYPE_BOOLEAN, CF_TYPE_DATE, CF_TYPE_URL, CF_TYPE_SELECT, CF_FILTER_CHOICES
from extras.models import CustomField, CustomFieldChoice from extras.models import CustomField, CustomFieldChoice
from ruamel.yaml import YAML from ruamel.yaml import YAML
from pathlib import Path from pathlib import Path
import sys import sys
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): def get_class_for_class_path(class_path):
import importlib import importlib
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
@ -42,12 +32,6 @@ with file.open('r') as stream:
if cf_details.get('description', 0): if cf_details.get('description', 0):
custom_field.description = cf_details['description'] custom_field.description = cf_details['description']
# If no filter_logic is specified then it will default to 'Loose'
if cf_details.get('filter_logic', 0):
for choice_id, choice_text in CF_FILTER_CHOICES:
if choice_text.lower() == cf_details['filter_logic']:
custom_field.filter_logic = choice_id
if cf_details.get('label', 0): if cf_details.get('label', 0):
custom_field.label = cf_details['label'] custom_field.label = cf_details['label']
@ -58,7 +42,7 @@ with file.open('r') as stream:
custom_field.required = cf_details['required'] custom_field.required = cf_details['required']
if cf_details.get('type', 0): if cf_details.get('type', 0):
custom_field.type = text_to_fields[cf_details['type']] custom_field.type = cf_details['type']
if cf_details.get('weight', 0): if cf_details.get('weight', 0):
custom_field.weight = cf_details['weight'] custom_field.weight = cf_details['weight']

View File

@ -1,7 +1,6 @@
from dcim.models import Site, RackRole, Rack, RackGroup from dcim.models import Site, RackRole, Rack, RackGroup
from tenancy.models import Tenant from tenancy.models import Tenant
from extras.models import CustomField, CustomFieldValue from extras.models import CustomField, CustomFieldValue
from dcim.constants import RACK_TYPE_CHOICES, RACK_WIDTH_CHOICES
from ruamel.yaml import YAML from ruamel.yaml import YAML
from pathlib import Path from pathlib import Path
import sys import sys
@ -41,14 +40,6 @@ with file.open('r') as stream:
params[assoc] = model.objects.get(**query) params[assoc] = model.objects.get(**query)
for rack_type in RACK_TYPE_CHOICES:
if params['type'] in rack_type:
params['type'] = rack_type[0]
for rack_width in RACK_WIDTH_CHOICES:
if params['width'] in rack_width:
params['width'] = rack_width[0]
rack, created = Rack.objects.get_or_create(**params) rack, created = Rack.objects.get_or_create(**params)
if created: if created:

View File

@ -1,5 +1,4 @@
from dcim.models import Site, Rack, DeviceRole, DeviceType, Device, Platform from dcim.models import Site, Rack, DeviceRole, DeviceType, Device, Platform
from dcim.constants import RACK_FACE_CHOICES
from ipam.models import IPAddress from ipam.models import IPAddress
from virtualization.models import Cluster from virtualization.models import Cluster
from tenancy.models import Tenant from tenancy.models import Tenant
@ -49,12 +48,6 @@ with file.open('r') as stream:
params[assoc] = model.objects.get(**query) params[assoc] = model.objects.get(**query)
if 'face' in params:
for rack_face in RACK_FACE_CHOICES:
if params['face'] in rack_face:
params['face'] = rack_face[0]
break
device, created = Device.objects.get_or_create(**params) device, created = Device.objects.get_or_create(**params)
if created: if created:

View File

@ -1,6 +1,5 @@
from dcim.models import Site from dcim.models import Site
from ipam.models import VLAN, VLANGroup, Role from ipam.models import VLAN, VLANGroup, Role
from ipam.constants import VLAN_STATUS_CHOICES
from tenancy.models import Tenant, TenantGroup from tenancy.models import Tenant, TenantGroup
from extras.models import CustomField, CustomFieldValue from extras.models import CustomField, CustomFieldValue
from ruamel.yaml import YAML from ruamel.yaml import YAML
@ -35,12 +34,6 @@ with file.open('r') as stream:
params[assoc] = model.objects.get(**query) params[assoc] = model.objects.get(**query)
if 'status' in params:
for vlan_status in VLAN_STATUS_CHOICES:
if params['status'] in vlan_status:
params['status'] = vlan_status[0]
break
vlan, created = VLAN.objects.get_or_create(**params) vlan, created = VLAN.objects.get_or_create(**params)
if created: if created:

View File

@ -1,6 +1,5 @@
from dcim.models import Site from dcim.models import Site
from ipam.models import Prefix, VLAN, Role, VRF from ipam.models import Prefix, VLAN, Role, VRF
from ipam.constants import PREFIX_STATUS_CHOICES
from tenancy.models import Tenant, TenantGroup from tenancy.models import Tenant, TenantGroup
from extras.models import CustomField, CustomFieldValue from extras.models import CustomField, CustomFieldValue
from ruamel.yaml import YAML from ruamel.yaml import YAML
@ -38,12 +37,6 @@ with file.open('r') as stream:
params[assoc] = model.objects.get(**query) params[assoc] = model.objects.get(**query)
if 'status' in params:
for prefix_status in PREFIX_STATUS_CHOICES:
if params['status'] in prefix_status:
params['status'] = prefix_status[0]
break
prefix, created = Prefix.objects.get_or_create(**params) prefix, created = Prefix.objects.get_or_create(**params)
if created: if created:

View File

@ -1,6 +1,5 @@
from dcim.models import Site, Platform, DeviceRole from dcim.models import Site, Platform, DeviceRole
from virtualization.models import Cluster, VirtualMachine from virtualization.models import Cluster, VirtualMachine
from virtualization.constants import VM_STATUS_CHOICES
from tenancy.models import Tenant from tenancy.models import Tenant
from extras.models import CustomField, CustomFieldValue from extras.models import CustomField, CustomFieldValue
from ruamel.yaml import YAML from ruamel.yaml import YAML
@ -43,12 +42,6 @@ with file.open('r') as stream:
params[assoc] = model.objects.get(**query) params[assoc] = model.objects.get(**query)
if 'status' in params:
for vm_status in VM_STATUS_CHOICES:
if params['status'] in vm_status:
params['status'] = vm_status[0]
break
virtual_machine, created = VirtualMachine.objects.get_or_create(**params) virtual_machine, created = VirtualMachine.objects.get_or_create(**params)
if created: if created:

View File

@ -1,5 +1,4 @@
from dcim.models import Interface, Device from dcim.models import Interface, Device
from dcim.constants import IFACE_TYPE_CHOICES
from extras.models import CustomField, CustomFieldValue from extras.models import CustomField, CustomFieldValue
from ruamel.yaml import YAML from ruamel.yaml import YAML
@ -28,16 +27,6 @@ with file.open('r') as stream:
params[assoc] = model.objects.get(**query) params[assoc] = model.objects.get(**query)
if 'type' in params:
for outer_list in IFACE_TYPE_CHOICES:
for type_choices in outer_list[1]:
if params['type'] in type_choices:
params['type'] = type_choices[0]
break
else:
continue
break
interface, created = Interface.objects.get_or_create(**params) interface, created = Interface.objects.get_or_create(**params)
if created: if created:

View File

@ -1,5 +1,4 @@
from ipam.models import IPAddress, VRF from ipam.models import IPAddress, VRF
from ipam.constants import IPADDRESS_STATUS_CHOICES
from dcim.models import Device, Interface from dcim.models import Device, Interface
from virtualization.models import VirtualMachine from virtualization.models import VirtualMachine
from tenancy.models import Tenant from tenancy.models import Tenant
@ -49,12 +48,6 @@ with file.open('r') as stream:
query = { field: params.pop(assoc) } query = { field: params.pop(assoc) }
params[assoc] = model.objects.get(**query) params[assoc] = model.objects.get(**query)
if 'status' in params:
for ip_status in IPADDRESS_STATUS_CHOICES:
if params['status'] in ip_status:
params['status'] = ip_status[0]
break
ip_address, created = IPAddress.objects.get_or_create(**params) ip_address, created = IPAddress.objects.get_or_create(**params)
if created: if created: