lint updates

This commit is contained in:
ryanmerolle 2022-07-29 16:48:41 +00:00
parent 41a3f29217
commit 75db50b737
16 changed files with 280 additions and 202 deletions

View File

@ -12,15 +12,16 @@ from os.path import abspath, dirname, join
# Based on https://github.com/netbox-community/netbox/blob/master/netbox/netbox/configuration.example.py # Based on https://github.com/netbox-community/netbox/blob/master/netbox/netbox/configuration.example.py
# Read secret from file # Read secret from file
def _read_secret(secret_name, default = None): def _read_secret(secret_name, default=None):
try: try:
f = open('/run/secrets/' + secret_name, 'r', encoding='utf-8') f = open("/run/secrets/" + secret_name, encoding="utf-8")
except EnvironmentError: except OSError:
return default return default
else: else:
with f: with f:
return f.readline().strip() return f.readline().strip()
_BASE_DIR = dirname(dirname(abspath(__file__))) _BASE_DIR = dirname(dirname(abspath(__file__)))
######################### #########################
@ -33,44 +34,60 @@ _BASE_DIR = dirname(dirname(abspath(__file__)))
# access to the server via any other hostnames. The first FQDN in the list will be treated as the preferred name. # 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'] # Example: ALLOWED_HOSTS = ['netbox.example.com', 'netbox.internal.local']
ALLOWED_HOSTS = environ.get('ALLOWED_HOSTS', '*').split(' ') ALLOWED_HOSTS = environ.get("ALLOWED_HOSTS", "*").split(" ")
# PostgreSQL database configuration. See the Django documentation for a complete list of available parameters: # PostgreSQL database configuration. See the Django documentation for a complete list of available parameters:
# https://docs.djangoproject.com/en/stable/ref/settings/#databases # https://docs.djangoproject.com/en/stable/ref/settings/#databases
DATABASE = { DATABASE = {
'NAME': environ.get('DB_NAME', 'netbox'), # Database name "NAME": environ.get("DB_NAME", "netbox"), # Database name
'USER': environ.get('DB_USER', ''), # PostgreSQL username "USER": environ.get("DB_USER", ""), # PostgreSQL username
'PASSWORD': _read_secret('db_password', environ.get('DB_PASSWORD', '')), "PASSWORD": _read_secret("db_password", environ.get("DB_PASSWORD", "")),
# PostgreSQL password # PostgreSQL password
'HOST': environ.get('DB_HOST', 'localhost'), # Database server "HOST": environ.get("DB_HOST", "localhost"), # Database server
'PORT': environ.get('DB_PORT', ''), # Database port (leave blank for default) "PORT": environ.get("DB_PORT", ""), # Database port (leave blank for default)
'OPTIONS': {'sslmode': environ.get('DB_SSLMODE', 'prefer')}, "OPTIONS": {"sslmode": environ.get("DB_SSLMODE", "prefer")},
# Database connection SSLMODE # Database connection SSLMODE
'CONN_MAX_AGE': int(environ.get('DB_CONN_MAX_AGE', '300')), "CONN_MAX_AGE": int(environ.get("DB_CONN_MAX_AGE", "300")),
# Max database connection age # Max database connection age
'DISABLE_SERVER_SIDE_CURSORS': environ.get('DB_DISABLE_SERVER_SIDE_CURSORS', 'False').lower() == 'true', "DISABLE_SERVER_SIDE_CURSORS": environ.get(
# Disable the use of server-side cursors transaction pooling "DB_DISABLE_SERVER_SIDE_CURSORS",
"False",
).lower()
== "true",
# Disable the use of server-side cursors transaction pooling
} }
# Redis database settings. Redis is used for caching and for queuing background tasks such as webhook events. A separate # 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 # configuration exists for each. Full connection details are required in both sections, and it is strongly recommended
# to use two separate database IDs. # to use two separate database IDs.
REDIS = { REDIS = {
'tasks': { "tasks": {
'HOST': environ.get('REDIS_HOST', 'localhost'), "HOST": environ.get("REDIS_HOST", "localhost"),
'PORT': int(environ.get('REDIS_PORT', 6379)), "PORT": int(environ.get("REDIS_PORT", 6379)),
'PASSWORD': _read_secret('redis_password', environ.get('REDIS_PASSWORD', '')), "PASSWORD": _read_secret("redis_password", environ.get("REDIS_PASSWORD", "")),
'DATABASE': int(environ.get('REDIS_DATABASE', 0)), "DATABASE": int(environ.get("REDIS_DATABASE", 0)),
'SSL': environ.get('REDIS_SSL', 'False').lower() == 'true', "SSL": environ.get("REDIS_SSL", "False").lower() == "true",
'INSECURE_SKIP_TLS_VERIFY': environ.get('REDIS_INSECURE_SKIP_TLS_VERIFY', 'False').lower() == 'true', "INSECURE_SKIP_TLS_VERIFY": environ.get(
"REDIS_INSECURE_SKIP_TLS_VERIFY",
"False",
).lower()
== "true",
}, },
'caching': { "caching": {
'HOST': environ.get('REDIS_CACHE_HOST', environ.get('REDIS_HOST', 'localhost')), "HOST": environ.get("REDIS_CACHE_HOST", environ.get("REDIS_HOST", "localhost")),
'PORT': int(environ.get('REDIS_CACHE_PORT', environ.get('REDIS_PORT', 6379))), "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', ''))), "PASSWORD": _read_secret(
'DATABASE': int(environ.get('REDIS_CACHE_DATABASE', 1)), "redis_cache_password",
'SSL': environ.get('REDIS_CACHE_SSL', environ.get('REDIS_SSL', 'False')).lower() == 'true', environ.get("REDIS_CACHE_PASSWORD", environ.get("REDIS_PASSWORD", "")),
'INSECURE_SKIP_TLS_VERIFY': environ.get('REDIS_CACHE_INSECURE_SKIP_TLS_VERIFY', environ.get('REDIS_INSECURE_SKIP_TLS_VERIFY', 'False')).lower() == 'true', ),
"DATABASE": int(environ.get("REDIS_CACHE_DATABASE", 1)),
"SSL": environ.get("REDIS_CACHE_SSL", environ.get("REDIS_SSL", "False")).lower()
== "true",
"INSECURE_SKIP_TLS_VERIFY": environ.get(
"REDIS_CACHE_INSECURE_SKIP_TLS_VERIFY",
environ.get("REDIS_INSECURE_SKIP_TLS_VERIFY", "False"),
).lower()
== "true",
}, },
} }
@ -78,7 +95,7 @@ REDIS = {
# For optimal security, SECRET_KEY should be at least 50 characters in length and contain a mix of letters, numbers, and # 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 # symbols. NetBox will not run without this defined. For more information, see
# https://docs.djangoproject.com/en/stable/ref/settings/#std:setting-SECRET_KEY # https://docs.djangoproject.com/en/stable/ref/settings/#std:setting-SECRET_KEY
SECRET_KEY = _read_secret('secret_key', environ.get('SECRET_KEY', '')) SECRET_KEY = _read_secret("secret_key", environ.get("SECRET_KEY", ""))
######################### #########################
@ -95,60 +112,82 @@ ADMINS = [
# URL schemes that are allowed within links in NetBox # URL schemes that are allowed within links in NetBox
ALLOWED_URL_SCHEMES = ( ALLOWED_URL_SCHEMES = (
'file', 'ftp', 'ftps', 'http', 'https', 'irc', 'mailto', 'sftp', 'ssh', 'tel', 'telnet', 'tftp', 'vnc', 'xmpp', "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 # 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. # content in both banners, define BANNER_TOP and set BANNER_BOTTOM = BANNER_TOP.
BANNER_TOP = environ.get('BANNER_TOP', '') BANNER_TOP = environ.get("BANNER_TOP", "")
BANNER_BOTTOM = environ.get('BANNER_BOTTOM', '') BANNER_BOTTOM = environ.get("BANNER_BOTTOM", "")
# Text to include on the login page above the login form. HTML is allowed. # Text to include on the login page above the login form. HTML is allowed.
BANNER_LOGIN = environ.get('BANNER_LOGIN', '') 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 URL path if accessing NetBox within a directory. For example, if installed at http://example.com/netbox/, set:
# BASE_PATH = 'netbox/' # BASE_PATH = 'netbox/'
BASE_PATH = environ.get('BASE_PATH', '') BASE_PATH = environ.get("BASE_PATH", "")
# Maximum number of days to retain logged changes. Set to 0 to retain changes indefinitely. (Default: 90) # 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)) 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 # 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 # 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_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_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_WHITELIST = list(
CORS_ORIGIN_REGEX_WHITELIST = [re.compile(r) for r in list(filter(None, environ.get('CORS_ORIGIN_REGEX_WHITELIST', '').split(' ')))] 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 # 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 # sensitive information about your installation. Only enable debugging while performing testing. Never enable debugging
# on a production system. # on a production system.
DEBUG = environ.get('DEBUG', 'False').lower() == 'true' DEBUG = environ.get("DEBUG", "False").lower() == "true"
# Email settings # Email settings
EMAIL = { EMAIL = {
'SERVER': environ.get('EMAIL_SERVER', 'localhost'), "SERVER": environ.get("EMAIL_SERVER", "localhost"),
'PORT': int(environ.get('EMAIL_PORT', 25)), "PORT": int(environ.get("EMAIL_PORT", 25)),
'USERNAME': environ.get('EMAIL_USERNAME', ''), "USERNAME": environ.get("EMAIL_USERNAME", ""),
'PASSWORD': _read_secret('email_password', environ.get('EMAIL_PASSWORD', '')), "PASSWORD": _read_secret("email_password", environ.get("EMAIL_PASSWORD", "")),
'USE_SSL': environ.get('EMAIL_USE_SSL', 'False').lower() == 'true', "USE_SSL": environ.get("EMAIL_USE_SSL", "False").lower() == "true",
'USE_TLS': environ.get('EMAIL_USE_TLS', 'False').lower() == 'true', "USE_TLS": environ.get("EMAIL_USE_TLS", "False").lower() == "true",
'SSL_CERTFILE': environ.get('EMAIL_SSL_CERTFILE', ''), "SSL_CERTFILE": environ.get("EMAIL_SSL_CERTFILE", ""),
'SSL_KEYFILE': environ.get('EMAIL_SSL_KEYFILE', ''), "SSL_KEYFILE": environ.get("EMAIL_SSL_KEYFILE", ""),
'TIMEOUT': int(environ.get('EMAIL_TIMEOUT', 10)), # seconds "TIMEOUT": int(environ.get("EMAIL_TIMEOUT", 10)), # seconds
'FROM_EMAIL': environ.get('EMAIL_FROM', ''), "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 # 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. # (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' 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 # 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. # 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(' '))) EXEMPT_VIEW_PERMISSIONS = list(
filter(None, environ.get("EXEMPT_VIEW_PERMISSIONS", "").split(" ")),
)
# Enable GraphQL API. # Enable GraphQL API.
GRAPHQL_ENABLED = environ.get('GRAPHQL_ENABLED', 'True').lower() == 'true' GRAPHQL_ENABLED = environ.get("GRAPHQL_ENABLED", "True").lower() == "true"
# Enable custom logging. Please see the Django documentation for detailed guidance on configuring custom logs: # Enable custom logging. Please see the Django documentation for detailed guidance on configuring custom logs:
# https://docs.djangoproject.com/en/stable/topics/logging/ # https://docs.djangoproject.com/en/stable/topics/logging/
@ -156,93 +195,103 @@ LOGGING = {}
# Setting this to True will permit only authenticated users to access any part of NetBox. By default, anonymous users # 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. # are permitted to access most data in NetBox (excluding secrets) but not make any changes.
LOGIN_REQUIRED = environ.get('LOGIN_REQUIRED', 'False').lower() == 'true' 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 # 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]) # re-authenticate. (Default: 1209600 [14 days])
LOGIN_TIMEOUT = int(environ.get('LOGIN_TIMEOUT', 1209600)) LOGIN_TIMEOUT = int(environ.get("LOGIN_TIMEOUT", 1209600))
# Setting this to True will display a "maintenance mode" banner at the top of every page. # 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' 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. # 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 # "?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". # all objects by specifying "?limit=0".
MAX_PAGE_SIZE = int(environ.get('MAX_PAGE_SIZE', 1000)) 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 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. # the default value of this setting is derived from the installed location.
MEDIA_ROOT = environ.get('MEDIA_ROOT', join(_BASE_DIR, 'media')) MEDIA_ROOT = environ.get("MEDIA_ROOT", join(_BASE_DIR, "media"))
# Expose Prometheus monitoring metrics at the HTTP endpoint '/metrics' # Expose Prometheus monitoring metrics at the HTTP endpoint '/metrics'
METRICS_ENABLED = environ.get('METRICS_ENABLED', 'False').lower() == 'true' METRICS_ENABLED = environ.get("METRICS_ENABLED", "False").lower() == "true"
# Credentials that NetBox will uses to authenticate to devices when connecting via NAPALM. # Credentials that NetBox will uses to authenticate to devices when connecting via NAPALM.
NAPALM_USERNAME = environ.get('NAPALM_USERNAME', '') NAPALM_USERNAME = environ.get("NAPALM_USERNAME", "")
NAPALM_PASSWORD = _read_secret('napalm_password', environ.get('NAPALM_PASSWORD', '')) NAPALM_PASSWORD = _read_secret("napalm_password", environ.get("NAPALM_PASSWORD", ""))
# NAPALM timeout (in seconds). (Default: 30) # NAPALM timeout (in seconds). (Default: 30)
NAPALM_TIMEOUT = int(environ.get('NAPALM_TIMEOUT', 30)) NAPALM_TIMEOUT = int(environ.get("NAPALM_TIMEOUT", 30))
# NAPALM optional arguments (see http://napalm.readthedocs.io/en/latest/support/#optional-arguments). Arguments must # NAPALM optional arguments (see http://napalm.readthedocs.io/en/latest/support/#optional-arguments). Arguments must
# be provided as a dictionary. # be provided as a dictionary.
NAPALM_ARGS = {} NAPALM_ARGS = {}
# Determine how many objects to display per page within a list. (Default: 50) # Determine how many objects to display per page within a list. (Default: 50)
PAGINATE_COUNT = int(environ.get('PAGINATE_COUNT', 50)) PAGINATE_COUNT = int(environ.get("PAGINATE_COUNT", 50))
# Enable installed plugins. Add the name of each plugin to the list. # Enable installed plugins. Add the name of each plugin to the list.
PLUGINS = [] PLUGINS = []
# Plugins configuration settings. These settings are used by various plugins that the user may have installed. # 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. # Each key in the dictionary is the name of an installed plugin and its value is a dictionary of settings.
PLUGINS_CONFIG = { PLUGINS_CONFIG = {}
}
# When determining the primary IP address for a device, IPv6 is preferred over IPv4 by default. Set this to True to # 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 instead.
PREFER_IPV4 = environ.get('PREFER_IPV4', 'False').lower() == 'true' 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 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_HEIGHT = int(
RACK_ELEVATION_DEFAULT_UNIT_WIDTH = int(environ.get('RACK_ELEVATION_DEFAULT_UNIT_WIDTH', 220)) 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 authentication support
REMOTE_AUTH_ENABLED = environ.get('REMOTE_AUTH_ENABLED', 'False').lower() == 'true' REMOTE_AUTH_ENABLED = environ.get("REMOTE_AUTH_ENABLED", "False").lower() == "true"
REMOTE_AUTH_BACKEND = environ.get('REMOTE_AUTH_BACKEND', 'netbox.authentication.RemoteUserBackend') REMOTE_AUTH_BACKEND = environ.get(
REMOTE_AUTH_HEADER = environ.get('REMOTE_AUTH_HEADER', 'HTTP_REMOTE_USER') "REMOTE_AUTH_BACKEND",
REMOTE_AUTH_AUTO_CREATE_USER = environ.get('REMOTE_AUTH_AUTO_CREATE_USER', 'True').lower() == 'true' "netbox.authentication.RemoteUserBackend",
REMOTE_AUTH_DEFAULT_GROUPS = list(filter(None, environ.get('REMOTE_AUTH_DEFAULT_GROUPS', '').split(' '))) )
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 repository is used to check whether there is a new release of NetBox available. Set to None to disable the # 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. # 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 # https://api.github.com/repos/netbox-community/netbox/releases
RELEASE_CHECK_URL = environ.get('RELEASE_CHECK_URL', None) 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 # 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. # this setting is derived from the installed location.
REPORTS_ROOT = environ.get('REPORTS_ROOT', '/etc/netbox/reports') REPORTS_ROOT = environ.get("REPORTS_ROOT", "/etc/netbox/reports")
# Maximum execution time for background tasks, in seconds. # Maximum execution time for background tasks, in seconds.
RQ_DEFAULT_TIMEOUT = int(environ.get('RQ_DEFAULT_TIMEOUT', 300)) 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 # 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. # this setting is derived from the installed location.
SCRIPTS_ROOT = environ.get('SCRIPTS_ROOT', '/etc/netbox/scripts') 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 # 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 # 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. # database access.) Note that the user as which NetBox runs must have read and write permissions to this path.
SESSION_FILE_PATH = environ.get('SESSIONS_ROOT', None) SESSION_FILE_PATH = environ.get("SESSIONS_ROOT", None)
# Time zone (default: UTC) # Time zone (default: UTC)
TIME_ZONE = environ.get('TIME_ZONE', 'UTC') TIME_ZONE = environ.get("TIME_ZONE", "UTC")
# 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/stable/ref/templates/builtins/#date # https://docs.djangoproject.com/en/stable/ref/templates/builtins/#date
DATE_FORMAT = environ.get('DATE_FORMAT', 'N j, Y') DATE_FORMAT = environ.get("DATE_FORMAT", "N j, Y")
SHORT_DATE_FORMAT = environ.get('SHORT_DATE_FORMAT', 'Y-m-d') SHORT_DATE_FORMAT = environ.get("SHORT_DATE_FORMAT", "Y-m-d")
TIME_FORMAT = environ.get('TIME_FORMAT', 'g:i a') TIME_FORMAT = environ.get("TIME_FORMAT", "g:i a")
SHORT_TIME_FORMAT = environ.get('SHORT_TIME_FORMAT', 'H:i:s') SHORT_TIME_FORMAT = environ.get("SHORT_TIME_FORMAT", "H:i:s")
DATETIME_FORMAT = environ.get('DATETIME_FORMAT', 'N j, Y g:i a') 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') SHORT_DATETIME_FORMAT = environ.get("SHORT_DATETIME_FORMAT", "Y-m-d H:i")

View File

@ -3,53 +3,52 @@
from os import environ from os import environ
# Set LOGLEVEL in netbox.env or docker-compose.overide.yml to override a logging level of INFO. # Set LOGLEVEL in netbox.env or docker-compose.overide.yml to override a logging level of INFO.
LOGLEVEL = environ.get('LOGLEVEL', 'INFO') LOGLEVEL = environ.get("LOGLEVEL", "INFO")
LOGGING = { LOGGING = {
"version": 1,
'version': 1, "disable_existing_loggers": False,
'disable_existing_loggers': False, "formatters": {
'formatters': { "verbose": {
'verbose': { "format": "{levelname} {asctime} {module} {process:d} {thread:d} {message}",
'format': '{levelname} {asctime} {module} {process:d} {thread:d} {message}', "style": "{",
'style': '{', },
}, "simple": {
'simple': { "format": "{levelname} {message}",
'format': '{levelname} {message}', "style": "{",
'style': '{', },
}, },
}, "filters": {
'filters': { "require_debug_false": {
'require_debug_false': { "()": "django.utils.log.RequireDebugFalse",
'()': 'django.utils.log.RequireDebugFalse', },
}, },
}, "handlers": {
'handlers': { "console": {
'console': { "level": LOGLEVEL,
'level': LOGLEVEL, "filters": ["require_debug_false"],
'filters': ['require_debug_false'], "class": "logging.StreamHandler",
'class': 'logging.StreamHandler', "formatter": "simple",
'formatter': 'simple' },
}, "mail_admins": {
'mail_admins': { "level": "ERROR",
'level': 'ERROR', "class": "django.utils.log.AdminEmailHandler",
'class': 'django.utils.log.AdminEmailHandler', "filters": ["require_debug_false"],
'filters': ['require_debug_false'] },
} },
}, "loggers": {
'loggers': { "django": {
'django': { "handlers": ["console"],
'handlers': ['console'], "propagate": True,
'propagate': True, },
}, "django.request": {
'django.request': { "handlers": ["mail_admins"],
'handlers': ['mail_admins'], "level": "ERROR",
'level': 'ERROR', "propagate": False,
'propagate': False, },
}, "django_auth_ldap": {
'django_auth_ldap': { "handlers": ["console"],
'handlers': ['console',], "level": LOGLEVEL,
'level': LOGLEVEL, },
} },
}
} }

View File

@ -24,19 +24,15 @@
// "source.organizeImports": true // "source.organizeImports": true
// } // }
//}, //},
//"python.sortImports.args": [ "python.sortImports.args": [
// "--profile=black" "--profile=black"
//], ],
"python.sortImports.path": "/opt/netbox/venv/bin/isort", "python.sortImports.path": "/opt/netbox/venv/bin/isort",
"python.analysis.typeCheckingMode": "strict", "python.analysis.typeCheckingMode": "strict",
"python.analysis.extraPaths": [ "python.analysis.extraPaths": [
// "/opt/netbox/",
"/opt/netbox/netbox" "/opt/netbox/netbox"
], ],
"python.autoComplete.extraPaths": [ "python.autoComplete.extraPaths": [
// "/opt/netbox/netbox/",
// "/opt/netbox/netbox/**",
// "/opt/netbox/netbox/**/**",
"/opt/netbox/netbox" "/opt/netbox/netbox"
], ],
"python.defaultInterpreterPath": "/opt/netbox/venv/bin/python3", "python.defaultInterpreterPath": "/opt/netbox/venv/bin/python3",

View File

@ -77,5 +77,6 @@ into the release notes.
Please put an x into the brackets (like `[x]`) if you've completed that task. Please put an x into the brackets (like `[x]`) if you've completed that task.
--> -->
* [ ] I have explained my PR according to the information in the comments or in a linked issue. * [ ] I have explained my PR according to the information in the comments
or in a linked issue.
* [ ] My PR targets the `dev` branch. * [ ] My PR targets the `dev` branch.

3
.htmlhintrc Normal file
View File

@ -0,0 +1,3 @@
{
"doctype-first": false
}

View File

@ -1,5 +1,5 @@
repos: repos:
- repo: 'https://github.com/pre-commit/pre-commit-hooks' - repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.3.0 rev: v4.3.0
hooks: hooks:
- id: trailing-whitespace - id: trailing-whitespace
@ -10,38 +10,45 @@ repos:
- id: name-tests-test - id: name-tests-test
- id: requirements-txt-fixer - id: requirements-txt-fixer
- id: check-docstring-first - id: check-docstring-first
- repo: 'https://github.com/asottile/add-trailing-comma' - repo: https://github.com/asottile/add-trailing-comma
rev: v2.2.3 rev: v2.2.3
hooks: hooks:
- id: add-trailing-comma - id: add-trailing-comma
args: args:
- '--py36-plus' - "--py36-plus"
- repo: 'https://github.com/asottile/pyupgrade' - repo: https://github.com/asottile/pyupgrade
rev: v2.37.3 rev: v2.37.3
hooks: hooks:
- id: pyupgrade - id: pyupgrade
args: args:
- '--py37-plus' - "--py37-plus"
- repo: 'https://github.com/PyCQA/isort' - repo: https://github.com/PyCQA/isort
rev: 5.10.1 rev: 5.10.1
hooks: hooks:
- id: isort - id: isort
args: args:
- '--filter-files' - "--filter-files"
- repo: 'https://github.com/psf/black' - "--profile=black"
- repo: https://github.com/psf/black
rev: 22.6.0 rev: 22.6.0
hooks: hooks:
- id: black - id: black
language_version: python3 language_version: python3
#- repo: 'https://github.com/adrienverge/yamllint' #- repo: https://github.com/adrienverge/yamllint
# rev: v1.26.3 # rev: v1.26.3
# hooks: # hooks:
# - id: yamllint # - id: yamllint
- repo: 'https://github.com/psf/black' - repo: https://github.com/psf/black
rev: 22.6.0 rev: 22.6.0
hooks: hooks:
- id: black - id: black
- repo: 'https://github.com/igorshubovych/markdownlint-cli' - repo: https://github.com/Lucas-C/pre-commit-hooks-nodejs
rev: v1.1.2
hooks:
- id: htmlhint
# optional custom config:
args: [--config, .htmlhintrc]
- repo: https://github.com/igorshubovych/markdownlint-cli
rev: v0.32.1 rev: v0.32.1
hooks: hooks:
- id: markdownlint - id: markdownlint

View File

@ -3,8 +3,10 @@
## Reporting Bugs ## Reporting Bugs
* First, ensure that you're running the [latest stable version](https://github.com/netbox-community/netbox/releases) * First, ensure that you're running the [latest stable version](https://github.com/netbox-community/netbox/releases)
of NetBox or this plugin [latest stable version](https://github.com/ryanmerolle/netbox-access-lists/releases). If you're running an older version, it's possible that the bug has of NetBox or this plugin [latest stable version](https://github.com/ryanmerolle/netbox-access-lists/releases).
already been fixed or you are running a version of the plugin not tested with the NetBox version you are running [Compatibility Matrix](./README.md#compatibility). If you're running an older version, it's possible that the bug has already been fixed
or you are running a version of the plugin not tested with the NetBox version
you are running [Compatibility Matrix](./README.md#compatibility).
* Next, check the GitHub [issues list](https://github.com/ryanmerolle/netbox-access-lists/issues) * Next, check the GitHub [issues list](https://github.com/ryanmerolle/netbox-access-lists/issues)
to see if the bug you've found has already been reported. If you think you may to see if the bug you've found has already been reported. If you think you may

View File

@ -175,4 +175,3 @@
of your accepting any such warranty or additional liability. of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS END OF TERMS AND CONDITIONS

View File

@ -24,7 +24,8 @@ This plugin was first developed using 3.2.5, and tested with all of 3.2.0.
## Installing ## Installing
For adding to a NetBox Docker setup see [the general instructions for using netbox-docker with plugins](https://github.com/netbox-community/netbox-docker/wiki/Using-Netbox-Plugins). For adding to a NetBox Docker setup see
[the general instructions for using netbox-docker with plugins](https://github.com/netbox-community/netbox-docker/wiki/Using-Netbox-Plugins).
While this is still in development and not yet on pypi you can install with pip: While this is still in development and not yet on pypi you can install with pip:
@ -38,7 +39,8 @@ or by adding to your `local_requirements.txt` or `plugin_requirements.txt` (netb
git+https://github.com/ryanmerolle/netbox-access-lists.git@dev git+https://github.com/ryanmerolle/netbox-access-lists.git@dev
``` ```
Enable the plugin in `/opt/netbox/netbox/netbox/configuration.py` or if you use netbox-docker, your `/configuration/plugins.py` file : Enable the plugin in `/opt/netbox/netbox/netbox/configuration.py`,
or if you use netbox-docker, your `/configuration/plugins.py` file :
```python ```python
PLUGINS = [ PLUGINS = [

View File

@ -103,7 +103,7 @@ class AccessListSerializer(NetBoxModelSerializer):
if "assigned_object_type" in data and "assigned_object_id" in data: if "assigned_object_type" in data and "assigned_object_id" in data:
try: try:
assigned_object = data["assigned_object_type"].get_object_for_this_type( assigned_object = data["assigned_object_type"].get_object_for_this_type(
id=data["assigned_object_id"] id=data["assigned_object_id"],
) )
except ObjectDoesNotExist: except ObjectDoesNotExist:
# Sets a standard error message for invalid GFK # Sets a standard error message for invalid GFK
@ -118,7 +118,7 @@ class AccessListSerializer(NetBoxModelSerializer):
and self.instance.rule_count > 0 and self.instance.rule_count > 0
): ):
error_message["type"] = [ error_message["type"] = [
"This ACL has ACL rules associated, CANNOT change ACL type." "This ACL has ACL rules associated, CANNOT change ACL type.",
] ]
if error_message: if error_message:
@ -180,7 +180,7 @@ class ACLInterfaceAssignmentSerializer(NetBoxModelSerializer):
if "assigned_object_type" in data and "assigned_object_id" in data: if "assigned_object_type" in data and "assigned_object_id" in data:
try: try:
assigned_object = data["assigned_object_type"].get_object_for_this_type( assigned_object = data["assigned_object_type"].get_object_for_this_type(
id=data["assigned_object_id"] id=data["assigned_object_id"],
) )
except ObjectDoesNotExist: except ObjectDoesNotExist:
# Sets a standard error message for invalid GFK # Sets a standard error message for invalid GFK
@ -265,7 +265,7 @@ class ACLStandardRuleSerializer(NetBoxModelSerializer):
# Check if action set to remark, but source_prefix set. # Check if action set to remark, but source_prefix set.
if data.get("source_prefix"): if data.get("source_prefix"):
error_message["source_prefix"] = [ error_message["source_prefix"] = [
error_message_action_remark_source_prefix_set error_message_action_remark_source_prefix_set,
] ]
if error_message: if error_message:
@ -339,27 +339,27 @@ class ACLExtendedRuleSerializer(NetBoxModelSerializer):
# Check if action set to remark, but source_prefix set. # Check if action set to remark, but source_prefix set.
if data.get("source_prefix"): if data.get("source_prefix"):
error_message["source_prefix"] = [ error_message["source_prefix"] = [
error_message_action_remark_source_prefix_set error_message_action_remark_source_prefix_set,
] ]
# Check if action set to remark, but source_ports set. # Check if action set to remark, but source_ports set.
if data.get("source_ports"): if data.get("source_ports"):
error_message["source_ports"] = [ error_message["source_ports"] = [
"Action is set to remark, Source Ports CANNOT be set." "Action is set to remark, Source Ports CANNOT be set.",
] ]
# Check if action set to remark, but destination_prefix set. # Check if action set to remark, but destination_prefix set.
if data.get("destination_prefix"): if data.get("destination_prefix"):
error_message["destination_prefix"] = [ error_message["destination_prefix"] = [
"Action is set to remark, Destination Prefix CANNOT be set." "Action is set to remark, Destination Prefix CANNOT be set.",
] ]
# Check if action set to remark, but destination_ports set. # Check if action set to remark, but destination_ports set.
if data.get("destination_ports"): if data.get("destination_ports"):
error_message["destination_ports"] = [ error_message["destination_ports"] = [
"Action is set to remark, Destination Ports CANNOT be set." "Action is set to remark, Destination Ports CANNOT be set.",
] ]
# Check if action set to remark, but protocol set. # Check if action set to remark, but protocol set.
if data.get("protocol"): if data.get("protocol"):
error_message["protocol"] = [ error_message["protocol"] = [
"Action is set to remark, Protocol CANNOT be set." "Action is set to remark, Protocol CANNOT be set.",
] ]
if error_message: if error_message:

View File

@ -40,7 +40,8 @@ class ACLInterfaceAssignmentViewSet(NetBoxModelViewSet):
""" """
queryset = models.ACLInterfaceAssignment.objects.prefetch_related( queryset = models.ACLInterfaceAssignment.objects.prefetch_related(
"access_list", "tags" "access_list",
"tags",
) )
serializer_class = ACLInterfaceAssignmentSerializer serializer_class = ACLInterfaceAssignmentSerializer
filterset_class = filtersets.ACLInterfaceAssignmentFilterSet filterset_class = filtersets.ACLInterfaceAssignmentFilterSet

View File

@ -32,7 +32,7 @@ __all__ = (
# Sets a standard mark_safe help_text value to be used by the various classes # Sets a standard mark_safe help_text value to be used by the various classes
help_text_acl_rule_logic = mark_safe( help_text_acl_rule_logic = mark_safe(
"<b>*Note:</b> CANNOT be set if action is set to remark." "<b>*Note:</b> CANNOT be set if action is set to remark.",
) )
# Sets a standard help_text value to be used by the various classes for acl action # Sets a standard help_text value to be used by the various classes for acl action
help_text_acl_action = "Action the rule will take (remark, deny, or allow)." help_text_acl_action = "Action the rule will take (remark, deny, or allow)."
@ -116,7 +116,7 @@ class AccessListForm(NetBoxModelForm):
"default_action": "The default behavior of the ACL.", "default_action": "The default behavior of the ACL.",
"name": "The name uniqueness per device is case insensitive.", "name": "The name uniqueness per device is case insensitive.",
"type": mark_safe( "type": mark_safe(
"<b>*Note:</b> CANNOT be changed if ACL Rules are assoicated to this Access List." "<b>*Note:</b> CANNOT be changed if ACL Rules are assoicated to this Access List.",
), ),
} }
@ -161,12 +161,12 @@ class AccessListForm(NetBoxModelForm):
or (virtual_chassis and virtual_machine) or (virtual_chassis and virtual_machine)
): ):
raise forms.ValidationError( raise forms.ValidationError(
"Access Lists must be assigned to one host (either a device, virtual chassis or virtual machine) at a time." "Access Lists must be assigned to one host (either a device, virtual chassis or virtual machine) at a time.",
) )
# Check if no hosts selected. # Check if no hosts selected.
if not device and not virtual_chassis and not virtual_machine: if not device and not virtual_chassis and not virtual_machine:
raise forms.ValidationError( raise forms.ValidationError(
"Access Lists must be assigned to a device, virtual chassis or virtual machine." "Access Lists must be assigned to a device, virtual chassis or virtual machine.",
) )
if device: if device:
@ -175,12 +175,14 @@ class AccessListForm(NetBoxModelForm):
elif virtual_chassis: elif virtual_chassis:
host_type = "virtual_chassis" host_type = "virtual_chassis"
existing_acls = AccessList.objects.filter( existing_acls = AccessList.objects.filter(
name=name, virtual_chassis=virtual_chassis name=name,
virtual_chassis=virtual_chassis,
).exists() ).exists()
elif virtual_machine: elif virtual_machine:
host_type = "virtual_machine" host_type = "virtual_machine"
existing_acls = AccessList.objects.filter( existing_acls = AccessList.objects.filter(
name=name, virtual_machine=virtual_machine name=name,
virtual_machine=virtual_machine,
).exists() ).exists()
host = cleaned_data.get(host_type) host = cleaned_data.get(host_type)
@ -200,7 +202,7 @@ class AccessListForm(NetBoxModelForm):
acl_type == "standard" and self.instance.aclextendedrules.exists() acl_type == "standard" and self.instance.aclextendedrules.exists()
): ):
error_message["type"] = [ error_message["type"] = [
"This ACL has ACL rules associated, CANNOT change ACL type." "This ACL has ACL rules associated, CANNOT change ACL type.",
] ]
if error_message: if error_message:
@ -265,7 +267,7 @@ class ACLInterfaceAssignmentForm(NetBoxModelForm):
# }, # },
label="Access List", label="Access List",
help_text=mark_safe( help_text=mark_safe(
"<b>*Note:</b> Access List must be present on the device already." "<b>*Note:</b> Access List must be present on the device already.",
), ),
) )
comments = CommentField() comments = CommentField()
@ -304,7 +306,7 @@ class ACLInterfaceAssignmentForm(NetBoxModelForm):
) )
help_texts = { help_texts = {
"direction": mark_safe( "direction": mark_safe(
"<b>*Note:</b> CANNOT assign 2 ACLs to the same interface & direction." "<b>*Note:</b> CANNOT assign 2 ACLs to the same interface & direction.",
), ),
} }
@ -338,7 +340,7 @@ class ACLInterfaceAssignmentForm(NetBoxModelForm):
if interface or vminterface: if interface or vminterface:
assigned_object_id = VMInterface.objects.get(pk=assigned_object.pk).pk assigned_object_id = VMInterface.objects.get(pk=assigned_object.pk).pk
assigned_object_type_id = ContentType.objects.get_for_model( assigned_object_type_id = ContentType.objects.get_for_model(
assigned_object assigned_object,
).pk ).pk
access_list_host = AccessList.objects.get(pk=access_list.pk).assigned_object access_list_host = AccessList.objects.get(pk=access_list.pk).assigned_object
@ -403,7 +405,7 @@ class ACLInterfaceAssignmentForm(NetBoxModelForm):
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
# Set assigned object # Set assigned object
self.instance.assigned_object = self.cleaned_data.get( self.instance.assigned_object = self.cleaned_data.get(
"interface" "interface",
) or self.cleaned_data.get("vminterface") ) or self.cleaned_data.get("vminterface")
return super().save(*args, **kwargs) return super().save(*args, **kwargs)
@ -422,7 +424,7 @@ class ACLStandardRuleForm(NetBoxModelForm):
"type": "standard", "type": "standard",
}, },
help_text=mark_safe( help_text=mark_safe(
"<b>*Note:</b> This field will only display Standard ACLs." "<b>*Note:</b> This field will only display Standard ACLs.",
), ),
label="Access List", label="Access List",
) )
@ -457,7 +459,7 @@ class ACLStandardRuleForm(NetBoxModelForm):
"index": help_text_acl_rule_index, "index": help_text_acl_rule_index,
"action": help_text_acl_action, "action": help_text_acl_action,
"remark": mark_safe( "remark": mark_safe(
"<b>*Note:</b> CANNOT be set if source prefix OR action is set." "<b>*Note:</b> CANNOT be set if source prefix OR action is set.",
), ),
} }
@ -480,7 +482,7 @@ class ACLStandardRuleForm(NetBoxModelForm):
# Check if action set to remark, but source_prefix set. # Check if action set to remark, but source_prefix set.
if cleaned_data.get("source_prefix"): if cleaned_data.get("source_prefix"):
error_message["source_prefix"] = [ error_message["source_prefix"] = [
error_message_action_remark_source_prefix_set error_message_action_remark_source_prefix_set,
] ]
# Check remark set, but action not set to remark. # Check remark set, but action not set to remark.
elif cleaned_data.get("remark"): elif cleaned_data.get("remark"):
@ -504,7 +506,7 @@ class ACLExtendedRuleForm(NetBoxModelForm):
"type": "extended", "type": "extended",
}, },
help_text=mark_safe( help_text=mark_safe(
"<b>*Note:</b> This field will only display Extended ACLs." "<b>*Note:</b> This field will only display Extended ACLs.",
), ),
label="Access List", label="Access List",
) )
@ -562,7 +564,7 @@ class ACLExtendedRuleForm(NetBoxModelForm):
"index": help_text_acl_rule_index, "index": help_text_acl_rule_index,
"protocol": help_text_acl_rule_logic, "protocol": help_text_acl_rule_logic,
"remark": mark_safe( "remark": mark_safe(
"<b>*Note:</b> CANNOT be set if action is not set to remark." "<b>*Note:</b> CANNOT be set if action is not set to remark.",
), ),
"source_ports": help_text_acl_rule_logic, "source_ports": help_text_acl_rule_logic,
} }
@ -591,27 +593,27 @@ class ACLExtendedRuleForm(NetBoxModelForm):
# Check if action set to remark, but source_prefix set. # Check if action set to remark, but source_prefix set.
if cleaned_data.get("source_prefix"): if cleaned_data.get("source_prefix"):
error_message["source_prefix"] = [ error_message["source_prefix"] = [
error_message_action_remark_source_prefix_set error_message_action_remark_source_prefix_set,
] ]
# Check if action set to remark, but source_ports set. # Check if action set to remark, but source_ports set.
if cleaned_data.get("source_ports"): if cleaned_data.get("source_ports"):
error_message["source_ports"] = [ error_message["source_ports"] = [
"Action is set to remark, Source Ports CANNOT be set." "Action is set to remark, Source Ports CANNOT be set.",
] ]
# Check if action set to remark, but destination_prefix set. # Check if action set to remark, but destination_prefix set.
if cleaned_data.get("destination_prefix"): if cleaned_data.get("destination_prefix"):
error_message["destination_prefix"] = [ error_message["destination_prefix"] = [
"Action is set to remark, Destination Prefix CANNOT be set." "Action is set to remark, Destination Prefix CANNOT be set.",
] ]
# Check if action set to remark, but destination_ports set. # Check if action set to remark, but destination_ports set.
if cleaned_data.get("destination_ports"): if cleaned_data.get("destination_ports"):
error_message["destination_ports"] = [ error_message["destination_ports"] = [
"Action is set to remark, Destination Ports CANNOT be set." "Action is set to remark, Destination Ports CANNOT be set.",
] ]
# Check if action set to remark, but protocol set. # Check if action set to remark, but protocol set.
if cleaned_data.get("protocol"): if cleaned_data.get("protocol"):
error_message["protocol"] = [ error_message["protocol"] = [
"Action is set to remark, Protocol CANNOT be set." "Action is set to remark, Protocol CANNOT be set.",
] ]
# Check if action not set to remark, but remark set. # Check if action not set to remark, but remark set.
elif cleaned_data.get("remark"): elif cleaned_data.get("remark"):

View File

@ -30,7 +30,9 @@ class Migration(migrations.Migration):
( (
"id", "id",
models.BigAutoField( models.BigAutoField(
auto_created=True, primary_key=True, serialize=False auto_created=True,
primary_key=True,
serialize=False,
), ),
), ),
("created", models.DateTimeField(auto_now_add=True, null=True)), ("created", models.DateTimeField(auto_now_add=True, null=True)),
@ -58,14 +60,15 @@ class Migration(migrations.Migration):
( (
"tags", "tags",
taggit.managers.TaggableManager( taggit.managers.TaggableManager(
through="extras.TaggedItem", to="extras.Tag" through="extras.TaggedItem",
to="extras.Tag",
), ),
), ),
], ],
options={ options={
"ordering": ("name", "device"), "ordering": ("name", "device"),
"unique_together": { "unique_together": {
("assigned_object_type", "assigned_object_id", "name") ("assigned_object_type", "assigned_object_id", "name"),
}, },
"verbose_name": "Access List", "verbose_name": "Access List",
}, },
@ -76,7 +79,9 @@ class Migration(migrations.Migration):
( (
"id", "id",
models.BigAutoField( models.BigAutoField(
auto_created=True, primary_key=True, serialize=False auto_created=True,
primary_key=True,
serialize=False,
), ),
), ),
("created", models.DateTimeField(auto_now_add=True, null=True)), ("created", models.DateTimeField(auto_now_add=True, null=True)),
@ -110,7 +115,8 @@ class Migration(migrations.Migration):
( (
"tags", "tags",
taggit.managers.TaggableManager( taggit.managers.TaggableManager(
through="extras.TaggedItem", to="extras.Tag" through="extras.TaggedItem",
to="extras.Tag",
), ),
), ),
], ],
@ -127,7 +133,7 @@ class Migration(migrations.Migration):
"assigned_object_id", "assigned_object_id",
"access_list", "access_list",
"direction", "direction",
) ),
}, },
"verbose_name": "ACL Interface Assignment", "verbose_name": "ACL Interface Assignment",
}, },
@ -138,7 +144,9 @@ class Migration(migrations.Migration):
( (
"id", "id",
models.BigAutoField( models.BigAutoField(
auto_created=True, primary_key=True, serialize=False auto_created=True,
primary_key=True,
serialize=False,
), ),
), ),
("created", models.DateTimeField(auto_now_add=True, null=True)), ("created", models.DateTimeField(auto_now_add=True, null=True)),
@ -154,7 +162,8 @@ class Migration(migrations.Migration):
( (
"tags", "tags",
taggit.managers.TaggableManager( taggit.managers.TaggableManager(
through="extras.TaggedItem", to="extras.Tag" through="extras.TaggedItem",
to="extras.Tag",
), ),
), ),
( (
@ -192,7 +201,9 @@ class Migration(migrations.Migration):
( (
"id", "id",
models.BigAutoField( models.BigAutoField(
auto_created=True, primary_key=True, serialize=False auto_created=True,
primary_key=True,
serialize=False,
), ),
), ),
("created", models.DateTimeField(auto_now_add=True, null=True)), ("created", models.DateTimeField(auto_now_add=True, null=True)),
@ -208,7 +219,8 @@ class Migration(migrations.Migration):
( (
"tags", "tags",
taggit.managers.TaggableManager( taggit.managers.TaggableManager(
through="extras.TaggedItem", to="extras.Tag" through="extras.TaggedItem",
to="extras.Tag",
), ),
), ),
( (

View File

@ -135,7 +135,8 @@ class ACLInterfaceAssignment(NetBoxModel):
it conveniently returns the absolute URL for any particular object. it conveniently returns the absolute URL for any particular object.
""" """
return reverse( return reverse(
"plugins:netbox_access_lists:aclinterfaceassignment", args=[self.pk] "plugins:netbox_access_lists:aclinterfaceassignment",
args=[self.pk],
) )
def get_direction_color(self): def get_direction_color(self):

View File

@ -22,7 +22,8 @@ class ACLInterfaceAssignments(PluginTemplateExtension):
ctype = ContentType.objects.get_for_model(obj) ctype = ContentType.objects.get_for_model(obj)
if ctype.model in ["interface", "vminterface"]: if ctype.model in ["interface", "vminterface"]:
acl_interface_assignments = ACLInterfaceAssignment.objects.filter( acl_interface_assignments = ACLInterfaceAssignment.objects.filter(
assigned_object_id=obj.pk, assigned_object_type=ctype assigned_object_id=obj.pk,
assigned_object_type=ctype,
) )
return self.render( return self.render(
@ -44,7 +45,8 @@ class AccessLists(PluginTemplateExtension):
ctype = ContentType.objects.get_for_model(obj) ctype = ContentType.objects.get_for_model(obj)
if ctype.model in ["device", "virtualchassis", "virtualmachine"]: if ctype.model in ["device", "virtualchassis", "virtualmachine"]:
access_lists = AccessList.objects.filter( access_lists = AccessList.objects.filter(
assigned_object_id=obj.pk, assigned_object_type=ctype assigned_object_id=obj.pk,
assigned_object_type=ctype,
) )
return self.render( return self.render(

View File

@ -11,7 +11,9 @@ urlpatterns = (
# Access Lists # Access Lists
path("access-lists/", views.AccessListListView.as_view(), name="accesslist_list"), path("access-lists/", views.AccessListListView.as_view(), name="accesslist_list"),
path( path(
"access-lists/add/", views.AccessListEditView.as_view(), name="accesslist_add" "access-lists/add/",
views.AccessListEditView.as_view(),
name="accesslist_add",
), ),
# path('access-lists/edit/', views.AccessListBulkEditView.as_view(), name='accesslist_bulk_edit'), # path('access-lists/edit/', views.AccessListBulkEditView.as_view(), name='accesslist_bulk_edit'),
path( path(