diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index 1234d59..9ed8f8b 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -1,3 +1,4 @@ +--- name: Docker Image CI on: @@ -12,6 +13,8 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + with: + fetch-depth: 0 - name: Build the Docker image run: docker compose build --no-cache @@ -23,14 +26,15 @@ jobs: - name: Run Django tests with coverage for netbox_slm run: | chmod go+w ci/reports - docker compose run netbox sh -c " \ - /opt/netbox/venv/bin/coverage run --source='netbox_slm' manage.py test netbox_slm && \ - /opt/netbox/venv/bin/coverage report --fail-under=0 && \ + docker compose run netbox sh -c "\ + /opt/netbox/venv/bin/coverage run --source='netbox_slm' manage.py test netbox_slm &&\ + /opt/netbox/venv/bin/coverage report --fail-under=0 &&\ /opt/netbox/venv/bin/coverage xml -o /ci/reports/coverage.xml" + timeout-minutes: 6 - - name: Sonarcloud scan + - name: Sonar scan if: env.SONAR_TOKEN != null - uses: sonarsource/sonarcloud-github-action@master + uses: SonarSource/sonarqube-scan-action@v4 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} diff --git a/.github/workflows/markdown-lint.yml b/.github/workflows/markdown-lint.yml new file mode 100644 index 0000000..d5c114e --- /dev/null +++ b/.github/workflows/markdown-lint.yml @@ -0,0 +1,21 @@ +--- +name: Markdown lint + +on: + push: + branches: + - master + pull_request: + types: [opened, synchronize, reopened] + +jobs: + vale: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: errata-ai/vale-action@v2.1.1 + with: + fail_on_error: true + filter_mode: nofilter + vale_flags: "--glob=*.md --config=ci/.vale.ini" diff --git a/.github/workflows/python.yml b/.github/workflows/python.yml index 38f0bbe..0ff443d 100644 --- a/.github/workflows/python.yml +++ b/.github/workflows/python.yml @@ -1,3 +1,4 @@ +--- name: Python on: @@ -10,16 +11,22 @@ on: jobs: build: runs-on: ubuntu-latest + strategy: + matrix: + include: + - python-version: 3.11 + - python-version: 3.12 + - python-version: 3.13 steps: - uses: actions/checkout@v4 - name: Set up Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: - python-version: "3.11" + python-version: ${{ matrix.python-version }} - - name: Install tools - run: pip install .[tools] + - name: Install ci tools + run: pip install .[ci] - name: Run ruff lint run: ruff check netbox_slm diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..20503c9 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,87 @@ +# Changelog + +## [Unreleased] + +### Added + +* Toggle to show plugin as top level menu item (#42) +* Bulk import except `LaxURLFields` (#4) +* Changelog with backdated changes (#51) + +### Changed + +* Support NetBox v4.2.3 (#51) + +## [1.7.0](https://github.com/ICTU/netbox_slm/releases/tag/1.7.0) - 2024-07-09 + +### Changed + +* Support NetBox v4.0.6 (#38) + +### Fixed + +* Hidden plugin data when not logged in (#37) + +### Removed + +* Broken bulk import and bulk edit (#4) + +## [1.6.0](https://github.com/ICTU/netbox_slm/releases/tag/1.6.0) - 2024-02-13 + +### Added + +* Additional `SoftwareProductVersion` fields (#10) +* Additional `SoftwareLicense` fields (#26) + +### Changed + +* Support NetBox v3.7.2 (#35) + +## [1.5](https://github.com/ICTU/netbox_slm/releases/tag/1.5) - 2023-10-04 + +### Added + +* Hyperlink option for `SoftwareLicense.stored_location` (#27) + +### Changed + +* Update docs and imports for newer NetBox versions (#23) +* Support NetBox v3.6.1 (#21) +* Allow linking `SoftwareProductInstallation` to `Cluster` (#17) + +### Fixed + +* Date picker for start and expiration dates (#22) + +## [1.4](https://github.com/ICTU/netbox_slm/releases/tag/1.4) - 2023-06-30 + +### Added + +* License information for software installations (#8) + +### Changed + +* Replace `NetBoxModelCSVForm` with `NetBoxModelImportForm` (#18) + +## [1.3](https://github.com/ICTU/netbox_slm/releases/tag/1.3) - 2023-04-19 + +### Added + +* Set up continuous integration (#3) + +### Changed + +* Support NetBox v3.4.7 (#14) + +### Fixed + +* Unify API serialization (#12) + +## [1.2](https://github.com/ICTU/netbox_slm/releases/tag/1.2) - 2022-05-19 + +* Fixed filtering of relevant versions when adding a new installation +* Manufacturer is now required for the add software product form + +## [1.1](https://github.com/ICTU/netbox_slm/releases/tag/1.1) - 2022-05-03 + +* Fixed search filter diff --git a/README.md b/README.md index 6ad5e7d..fd4e57a 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ ## Installation Guide -When using the Docker version of NetBox, first follow the netbox-docker [quickstart](https://github.com/netbox-community/netbox-docker#quickstart) instructions to clone the netbox-docker repo and set up the ``docker-compose.override.yml``. +When using the Docker version of NetBox, first follow the `netbox-docker` [quickstart](https://github.com/netbox-community/netbox-docker#quickstart) instructions to clone the `netbox-docker` repository and set up the ``docker-compose.override.yml``. Next, follow these instructions (based on the NetBox docker variant [instructions](https://github.com/netbox-community/netbox-docker/wiki/Configuration#custom-configuration-files)) @@ -73,54 +73,52 @@ directory run $ twine upload dist/* ``` -On Github.com create a similar tag and version. These steps could be -automated with a github workflow. +On GitHub create a similar tag and version. ## Developer Guide (local installation) -*Follow the steps below on your local system to run netbox and the -netbox_slm plugin in developer mode* +*Follow the steps below on your local system to run NetBox and the +`netbox_slm` plugin in developer mode* ### Setup The goal below is to run all NetBox components in Docker and run a local NetBox Django copy with auto-reload to develop the plugin pointing to -the Dockerized postgres and redis instances, basically ignoring the -netbox docker runtime server. +the PostgreSQL and Redis container instances, basically ignoring the +NetBox docker runtime server. ### Steps - from your projects directory clone the netbox repository +from your projects directory clone the NetBox repository ``` $ git clone https://github.com/netbox-community/netbox $ cd netbox ``` - install the virtual environment +install the virtual environment ``` $ pipenv shell $ pipenv install ``` - create and edit netbox/configuration.py (based on the template file) add these lines at the end of the file; +create and edit `netbox/configuration.py` (based on the template file) add these lines at the end of the file; ``` DEBUG = True SECRET_KEY = 'dummy' - DEVELOPER = True PLUGINS = [ 'netbox_slm', ] ``` The NetBox installation above will be used to run Django management -commands like runserver, makemigrations and migrate, which will be +commands like `runserver`, `makemigrations` and `migrate`, which will be explained in the next steps below; - from your projects directory clone the netbox-slm repository +from your projects directory clone the `netbox_slm` repository ``` $ git clone https://github.com/ICTU/netbox_slm @@ -128,37 +126,37 @@ explained in the next steps below; $ ./start-netbox.sh ``` -This will start NetBox locally (requires Docker) and forward the redis -and postgres ports to the localhost (make sure there’s no processes -using these ports or change the dockerfiles accordingly) +This will start NetBox locally (requires Docker) and forward the Redis +and PostgreSQL ports to the localhost (make sure there’s no processes +using these ports or change the Dockerfile(s) accordingly) -Note, you can also start and stop netbox by hand: +Note, you can also start and stop NetBox by hand: ``` $ cd netbox-docker - $ docker-compose up -d + $ docker compose up -d ``` - or stop the stack with +or stop the stack with ``` - $ docker-compose down + $ docker compose down ``` - # to start fresh: +# to start fresh: ``` - $ docker-compose down + $ docker compose down $ docker volume rm netbox-docker_netbox-postgres-data # et cetera - $ docker-compose up -d --force-recreate + $ docker compose up -d --force-recreate ``` - this will require you to re-run the migrate commando's for netbox-slm, see further down below +this will require you to re-run the migrate commando's for `netbox_slm`, see further down below -Go back to the netbox configuration.py file and update the postgres and -redis connection strings (username, password) to the ones the netbox -docker backend is using, for example (using default user and passwords -from the netbox docker example): +Go back to the NetBox `configuration.py` file and update the PostgreSQL and +Redis connection strings (username, password) to the ones the NetBox +Docker backend is using, for example (using default user and passwords +from the NetBox Docker example): ``` <> @@ -209,7 +207,7 @@ from the netbox docker example): <> ``` -Now you can run commands from the netbox repository like this; +Now you can run commands from the NetBox repository like this; ``` $ cd netbox/netbox @@ -218,8 +216,8 @@ Now you can run commands from the netbox repository like this; $ python3 manage.py runserver 8001 ``` -Visit http://127.0.0.1:8001 in the browser to see the auto reloading -version of the netbox UI. Port 8000 is taken by the docker ran variant. +Visit http://127.0.0.1:8001 in the browser to see the auto reloading version of the NetBox UI. +Port 8000 is taken by the Docker ran variant. ## Get in touch diff --git a/ci/.vale.ini b/ci/.vale.ini new file mode 100644 index 0000000..b8a4715 --- /dev/null +++ b/ci/.vale.ini @@ -0,0 +1,9 @@ +StylesPath = styles + +MinAlertLevel = suggestion +Vocab = Base + +Packages = proselint + +[*.md] +BasedOnStyles = Vale, proselint diff --git a/ci/Dockerfile b/ci/Dockerfile index bc4ef09..ff5cbcd 100644 --- a/ci/Dockerfile +++ b/ci/Dockerfile @@ -1,17 +1,18 @@ -ARG PYTHON_VERSION=3.11 +ARG PYTHON_VERSION=3.12 FROM python:${PYTHON_VERSION}-alpine AS compile-image +# Python version build arg is not actually supported by NetBox container image, only used for maintenance convenience WORKDIR /build -RUN python -m venv /opt/netbox/venv -ENV PATH="/opt/netbox/venv/bin:$PATH" +RUN python3 -m venv /opt/netbox/venv &&\ + source /opt/netbox/venv/bin/activate COPY ../ /build -RUN pip install -U .[build] -RUN python -m build -RUN pip install --no-index /build +RUN pip install -U .[build] &&\ + python -m build &&\ + pip install --no-index /build -FROM netboxcommunity/netbox:v4.0.6 +FROM netboxcommunity/netbox:v4.2.3 +ARG PYTHON_VERSION=3.12 -ARG PYTHON_VERSION=3.11 COPY --from=compile-image /opt/netbox/venv/lib/python${PYTHON_VERSION}/site-packages/netbox_slm /opt/netbox/venv/lib/python${PYTHON_VERSION}/site-packages/netbox_slm diff --git a/ci/Dockerfile-CI b/ci/Dockerfile-CI index c21d0b3..6ff4195 100644 --- a/ci/Dockerfile-CI +++ b/ci/Dockerfile-CI @@ -1,7 +1,5 @@ -FROM netboxcommunity/netbox:v4.0.6 +FROM netboxcommunity/netbox:v4.2.3 +ARG PYTHON_VERSION=3.12 -RUN mkdir /ci && chmod go+w /ci -COPY ../ci/requirements_ci.txt /ci/ -RUN pip install -r /ci/requirements_ci.txt - -COPY ../netbox_slm /opt/netbox/netbox/netbox_slm +COPY ../pyproject.toml /tmp/ +RUN uv pip install -r /tmp/pyproject.toml --extra ci diff --git a/ci/config/configuration.py b/ci/config/configuration.py new file mode 100644 index 0000000..e9d5aa7 --- /dev/null +++ b/ci/config/configuration.py @@ -0,0 +1,45 @@ +""" +Minimal NetBox config for NetBox SLM plugin testing, docs: https://netbox.readthedocs.io/en/stable/configuration/ +Based on https://github.com/netbox-community/netbox/blob/v4.2.3/netbox/netbox/configuration_testing.py +""" +from os import environ + +ALLOWED_HOSTS = ['*'] + +DATABASE = { + 'NAME': environ.get('DB_NAME', 'netbox'), + 'USER': environ.get('DB_USER', ''), + 'PASSWORD': environ.get('DB_PASSWORD', ''), + 'HOST': environ.get('DB_HOST', 'localhost'), + 'PORT': environ.get('DB_PORT', ''), + 'CONN_MAX_AGE': int(environ.get('DB_CONN_MAX_AGE', '300')), +} + +DEBUG = environ.get('DEBUG', 'False').lower() == 'true' +DEVELOPER = environ.get('DEVELOPER', 'False').lower() == 'true' + +PLUGINS = ["netbox_slm"] +PLUGINS_CONFIG = { + "netbox_slm": { + "top_level_menu": environ.get('SLM_TOP_LEVEL_MENU', 'True').lower() == 'true', + }, +} + +REDIS = { + 'tasks': { + 'HOST': environ.get('REDIS_HOST', 'localhost'), + 'PORT': int(environ.get('REDIS_PORT', 6379)), + 'PASSWORD': environ.get('REDIS_PASSWORD', ''), + 'DATABASE': int(environ.get('REDIS_DATABASE', 0)), + 'SSL': environ.get('REDIS_SSL', 'False').lower() == 'true', + }, + 'caching': { + 'HOST': environ.get('REDIS_CACHE_HOST', environ.get('REDIS_HOST', 'localhost')), + 'PORT': int(environ.get('REDIS_CACHE_PORT', environ.get('REDIS_PORT', 6379))), + 'PASSWORD': environ.get('REDIS_CACHE_PASSWORD', environ.get('REDIS_PASSWORD', '')), + 'DATABASE': int(environ.get('REDIS_CACHE_DATABASE', 1)), + 'SSL': environ.get('REDIS_CACHE_SSL', environ.get('REDIS_SSL', 'False')).lower() == 'true', + }, +} + +SECRET_KEY = 'dummydummydummydummydummydummydummydummydummydummy' diff --git a/ci/configuration/configuration.py b/ci/configuration/configuration.py deleted file mode 100644 index 3fc314d..0000000 --- a/ci/configuration/configuration.py +++ /dev/null @@ -1,260 +0,0 @@ -#### -## We recommend to not edit this file. -## Create separate files to overwrite the settings. -## See `extra.py` as an example. -#### - -import re -from os import environ -from os.path import abspath, dirname, join - -# For reference see https://netbox.readthedocs.io/en/stable/configuration/ -# Based on https://github.com/netbox-community/netbox/blob/master/netbox/netbox/configuration.example.py - -# Read secret from file -def _read_secret(secret_name, default = None): - try: - f = open('/run/secrets/' + secret_name, 'r', encoding='utf-8') - except EnvironmentError: - return default - else: - with f: - return f.readline().strip() - -_BASE_DIR = dirname(dirname(abspath(__file__))) - -######################### -# # -# Required settings # -# # -######################### - -# This is a list of valid fully-qualified domain names (FQDNs) for the NetBox server. NetBox will not permit write -# access to the server via any other hostnames. The first FQDN in the list will be treated as the preferred name. -# -# Example: ALLOWED_HOSTS = ['netbox.example.com', 'netbox.internal.local'] -ALLOWED_HOSTS = environ.get('ALLOWED_HOSTS', '*').split(' ') - -# PostgreSQL database configuration. See the Django documentation for a complete list of available parameters: -# https://docs.djangoproject.com/en/stable/ref/settings/#databases -DATABASE = { - 'NAME': environ.get('DB_NAME', 'netbox'), # Database name - 'USER': environ.get('DB_USER', ''), # PostgreSQL username - 'PASSWORD': _read_secret('db_password', environ.get('DB_PASSWORD', '')), - # PostgreSQL password - 'HOST': environ.get('DB_HOST', 'localhost'), # Database server - 'PORT': environ.get('DB_PORT', ''), # Database port (leave blank for default) - 'OPTIONS': {'sslmode': environ.get('DB_SSLMODE', 'prefer')}, - # Database connection SSLMODE - 'CONN_MAX_AGE': int(environ.get('DB_CONN_MAX_AGE', '300')), - # Max database connection age - 'DISABLE_SERVER_SIDE_CURSORS': environ.get('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 -# configuration exists for each. Full connection details are required in both sections, and it is strongly recommended -# to use two separate database IDs. -REDIS = { - 'tasks': { - 'HOST': environ.get('REDIS_HOST', 'localhost'), - 'PORT': int(environ.get('REDIS_PORT', 6379)), - 'PASSWORD': _read_secret('redis_password', environ.get('REDIS_PASSWORD', '')), - 'DATABASE': int(environ.get('REDIS_DATABASE', 0)), - 'SSL': environ.get('REDIS_SSL', 'False').lower() == 'true', - 'INSECURE_SKIP_TLS_VERIFY': environ.get('REDIS_INSECURE_SKIP_TLS_VERIFY', 'False').lower() == 'true', - }, - 'caching': { - 'HOST': environ.get('REDIS_CACHE_HOST', environ.get('REDIS_HOST', 'localhost')), - 'PORT': int(environ.get('REDIS_CACHE_PORT', environ.get('REDIS_PORT', 6379))), - 'PASSWORD': _read_secret('redis_cache_password', environ.get('REDIS_CACHE_PASSWORD', environ.get('REDIS_PASSWORD', ''))), - 'DATABASE': int(environ.get('REDIS_CACHE_DATABASE', 1)), - 'SSL': environ.get('REDIS_CACHE_SSL', environ.get('REDIS_SSL', 'False')).lower() == 'true', - 'INSECURE_SKIP_TLS_VERIFY': environ.get('REDIS_CACHE_INSECURE_SKIP_TLS_VERIFY', environ.get('REDIS_INSECURE_SKIP_TLS_VERIFY', 'False')).lower() == 'true', - }, -} - -# This key is used for secure generation of random numbers and strings. It must never be exposed outside of this file. -# For optimal security, SECRET_KEY should be at least 50 characters in length and contain a mix of letters, numbers, and -# symbols. NetBox will not run without this defined. For more information, see -# https://docs.djangoproject.com/en/stable/ref/settings/#std:setting-SECRET_KEY -SECRET_KEY = _read_secret('secret_key', environ.get('SECRET_KEY', '')) - - -######################### -# # -# Optional settings # -# # -######################### - -# Specify one or more name and email address tuples representing NetBox administrators. These people will be notified of -# application errors (assuming correct email settings are provided). -ADMINS = [ - # ['John Doe', 'jdoe@example.com'], -] - -# URL schemes that are allowed within links in NetBox -ALLOWED_URL_SCHEMES = ( - 'file', 'ftp', 'ftps', 'http', 'https', 'irc', 'mailto', 'sftp', 'ssh', 'tel', 'telnet', 'tftp', 'vnc', 'xmpp', -) - -# Optionally display a persistent banner at the top and/or bottom of every page. HTML is allowed. To display the same -# content in both banners, define BANNER_TOP and set BANNER_BOTTOM = BANNER_TOP. -BANNER_TOP = environ.get('BANNER_TOP', '') -BANNER_BOTTOM = environ.get('BANNER_BOTTOM', '') - -# Text to include on the login page above the login form. HTML is allowed. -BANNER_LOGIN = environ.get('BANNER_LOGIN', '') - -# Base URL path if accessing NetBox within a directory. For example, if installed at http://example.com/netbox/, set: -# BASE_PATH = 'netbox/' -BASE_PATH = environ.get('BASE_PATH', '') - -# Maximum number of days to retain logged changes. Set to 0 to retain changes indefinitely. (Default: 90) -CHANGELOG_RETENTION = int(environ.get('CHANGELOG_RETENTION', 90)) - -# API Cross-Origin Resource Sharing (CORS) settings. If CORS_ORIGIN_ALLOW_ALL is set to True, all origins will be -# allowed. Otherwise, define a list of allowed origins using either CORS_ORIGIN_WHITELIST or -# CORS_ORIGIN_REGEX_WHITELIST. For more information, see https://github.com/ottoyiu/django-cors-headers -CORS_ORIGIN_ALLOW_ALL = environ.get('CORS_ORIGIN_ALLOW_ALL', 'False').lower() == 'true' -CORS_ORIGIN_WHITELIST = list(filter(None, environ.get('CORS_ORIGIN_WHITELIST', 'https://localhost').split(' '))) -CORS_ORIGIN_REGEX_WHITELIST = [re.compile(r) for r in list(filter(None, environ.get('CORS_ORIGIN_REGEX_WHITELIST', '').split(' ')))] - -# Cross-Site-Request-Forgery-Attack settings. If Netbox is sitting behind a reverse proxy, you might need to set the CSRF_TRUSTED_ORIGINS flag. -# Django 4.0 requires to specify the URL Scheme in this setting. An example environment variable could be specified like: -# CSRF_TRUSTED_ORIGINS=https://demo.netbox.dev http://demo.netbox.dev -CSRF_TRUSTED_ORIGINS = list(filter(None, environ.get('CSRF_TRUSTED_ORIGINS', '').split(' '))) - -# Set to True to enable server debugging. WARNING: Debugging introduces a substantial performance penalty and may reveal -# sensitive information about your installation. Only enable debugging while performing testing. Never enable debugging -# on a production system. -DEBUG = environ.get('DEBUG', 'False').lower() == 'true' -DEVELOPER = environ.get('DEVELOPER', 'False').lower() == 'true' - -# Email settings -EMAIL = { - 'SERVER': environ.get('EMAIL_SERVER', 'localhost'), - 'PORT': int(environ.get('EMAIL_PORT', 25)), - 'USERNAME': environ.get('EMAIL_USERNAME', ''), - 'PASSWORD': _read_secret('email_password', environ.get('EMAIL_PASSWORD', '')), - 'USE_SSL': environ.get('EMAIL_USE_SSL', 'False').lower() == 'true', - 'USE_TLS': environ.get('EMAIL_USE_TLS', 'False').lower() == 'true', - 'SSL_CERTFILE': environ.get('EMAIL_SSL_CERTFILE', ''), - 'SSL_KEYFILE': environ.get('EMAIL_SSL_KEYFILE', ''), - 'TIMEOUT': int(environ.get('EMAIL_TIMEOUT', 10)), # seconds - 'FROM_EMAIL': environ.get('EMAIL_FROM', ''), -} - -# Enforcement of unique IP space can be toggled on a per-VRF basis. To enforce unique IP space within the global table -# (all prefixes and IP addresses not assigned to a VRF), set ENFORCE_GLOBAL_UNIQUE to True. -ENFORCE_GLOBAL_UNIQUE = environ.get('ENFORCE_GLOBAL_UNIQUE', 'False').lower() == 'true' - -# Exempt certain models from the enforcement of view permissions. Models listed here will be viewable by all users and -# by anonymous users. List models in the form `.`. Add '*' to this list to exempt all models. -EXEMPT_VIEW_PERMISSIONS = list(filter(None, environ.get('EXEMPT_VIEW_PERMISSIONS', '').split(' '))) - -# Enable GraphQL API. -GRAPHQL_ENABLED = environ.get('GRAPHQL_ENABLED', 'True').lower() == 'true' - -# Enable custom logging. Please see the Django documentation for detailed guidance on configuring custom logs: -# https://docs.djangoproject.com/en/stable/topics/logging/ -LOGGING = {} - -# Setting this to True will permit only authenticated users to access any part of NetBox. By default, anonymous users -# are permitted to access most data in NetBox (excluding secrets) but not make any changes. -LOGIN_REQUIRED = environ.get('LOGIN_REQUIRED', 'False').lower() == 'true' - -# The length of time (in seconds) for which a user will remain logged into the web UI before being prompted to -# re-authenticate. (Default: 1209600 [14 days]) -LOGIN_TIMEOUT = int(environ.get('LOGIN_TIMEOUT', 1209600)) - -# 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' - -# Maps provider -MAPS_URL = environ.get('MAPS_URL', None) - -# An API consumer can request an arbitrary number of objects =by appending the "limit" parameter to the URL (e.g. -# "?limit=1000"). This setting defines the maximum limit. Setting it to 0 or None will allow an API consumer to request -# all objects by specifying "?limit=0". -MAX_PAGE_SIZE = int(environ.get('MAX_PAGE_SIZE', 1000)) - -# The file path where uploaded media such as image attachments are stored. A trailing slash is not needed. Note that -# the default value of this setting is derived from the installed location. -MEDIA_ROOT = environ.get('MEDIA_ROOT', join(_BASE_DIR, 'media')) - -# Expose Prometheus monitoring metrics at the HTTP endpoint '/metrics' -METRICS_ENABLED = environ.get('METRICS_ENABLED', 'False').lower() == 'true' - -# Credentials that NetBox will uses to authenticate to devices when connecting via NAPALM. -NAPALM_USERNAME = environ.get('NAPALM_USERNAME', '') -NAPALM_PASSWORD = _read_secret('napalm_password', environ.get('NAPALM_PASSWORD', '')) - -# NAPALM timeout (in seconds). (Default: 30) -NAPALM_TIMEOUT = int(environ.get('NAPALM_TIMEOUT', 30)) - -# NAPALM optional arguments (see http://napalm.readthedocs.io/en/latest/support/#optional-arguments). Arguments must -# be provided as a dictionary. -NAPALM_ARGS = {} - -# Determine how many objects to display per page within a list. (Default: 50) -PAGINATE_COUNT = int(environ.get('PAGINATE_COUNT', 50)) - -# Enable installed plugins. Add the name of each plugin to the list. -PLUGINS = ['netbox_slm'] - -# Plugins configuration settings. These settings are used by various plugins that the user may have installed. -# Each key in the dictionary is the name of an installed plugin and its value is a dictionary of settings. -PLUGINS_CONFIG = { - "netbox_slm": { - "top_level_menu": True - }, -} - -# When determining the primary IP address for a device, IPv6 is preferred over IPv4 by default. Set this to True to -# prefer IPv4 instead. -PREFER_IPV4 = environ.get('PREFER_IPV4', 'False').lower() == 'true' - -# Rack elevation size defaults, in pixels. For best results, the ratio of width to height should be roughly 10:1. -RACK_ELEVATION_DEFAULT_UNIT_HEIGHT = int(environ.get('RACK_ELEVATION_DEFAULT_UNIT_HEIGHT', 22)) -RACK_ELEVATION_DEFAULT_UNIT_WIDTH = int(environ.get('RACK_ELEVATION_DEFAULT_UNIT_WIDTH', 220)) - -# Remote authentication support -REMOTE_AUTH_ENABLED = environ.get('REMOTE_AUTH_ENABLED', 'False').lower() == 'true' -REMOTE_AUTH_BACKEND = environ.get('REMOTE_AUTH_BACKEND', 'netbox.authentication.RemoteUserBackend') -REMOTE_AUTH_HEADER = environ.get('REMOTE_AUTH_HEADER', 'HTTP_REMOTE_USER') -REMOTE_AUTH_AUTO_CREATE_USER = environ.get('REMOTE_AUTH_AUTO_CREATE_USER', 'True').lower() == 'true' -REMOTE_AUTH_DEFAULT_GROUPS = list(filter(None, environ.get('REMOTE_AUTH_DEFAULT_GROUPS', '').split(' '))) - -# This repository is used to check whether there is a new release of NetBox available. Set to None to disable the -# version check or use the URL below to check for release in the official NetBox repository. -# https://api.github.com/repos/netbox-community/netbox/releases -RELEASE_CHECK_URL = environ.get('RELEASE_CHECK_URL', None) - -# The file path where custom reports will be stored. A trailing slash is not needed. Note that the default value of -# this setting is derived from the installed location. -REPORTS_ROOT = environ.get('REPORTS_ROOT', '/etc/netbox/reports') - -# Maximum execution time for background tasks, in seconds. -RQ_DEFAULT_TIMEOUT = int(environ.get('RQ_DEFAULT_TIMEOUT', 300)) - -# The file path where custom scripts will be stored. A trailing slash is not needed. Note that the default value of -# this setting is derived from the installed location. -SCRIPTS_ROOT = environ.get('SCRIPTS_ROOT', '/etc/netbox/scripts') - -# By default, NetBox will store session data in the database. Alternatively, a file path can be specified here to use -# local file storage instead. (This can be useful for enabling authentication on a standby instance with read-only -# database access.) Note that the user as which NetBox runs must have read and write permissions to this path. -SESSION_FILE_PATH = environ.get('SESSIONS_ROOT', None) - -# Time zone (default: UTC) -TIME_ZONE = environ.get('TIME_ZONE', 'UTC') - -# Date/time formatting. See the following link for supported formats: -# https://docs.djangoproject.com/en/stable/ref/templates/builtins/#date -DATE_FORMAT = environ.get('DATE_FORMAT', 'N j, Y') -SHORT_DATE_FORMAT = environ.get('SHORT_DATE_FORMAT', 'Y-m-d') -TIME_FORMAT = environ.get('TIME_FORMAT', 'g:i a') -SHORT_TIME_FORMAT = environ.get('SHORT_TIME_FORMAT', 'H:i:s') -DATETIME_FORMAT = environ.get('DATETIME_FORMAT', 'N j, Y g:i a') -SHORT_DATETIME_FORMAT = environ.get('SHORT_DATETIME_FORMAT', 'Y-m-d H:i') diff --git a/ci/configuration/extra.py b/ci/configuration/extra.py deleted file mode 100644 index 25ec2ee..0000000 --- a/ci/configuration/extra.py +++ /dev/null @@ -1,11 +0,0 @@ -PLUGINS = ['netbox_slm'] -DEBUG = True -SECRET_KEY = 'dummydummydummydummydummydummydummydummydummydummy' -DEVELOPER = True -PLUGINS_CONFIG = { - 'netbox_slm': dict( - TEST_RUNNER='xmlrunner.extra.djangotestrunner.XMLTestRunner', - TEST_OUTPUT_DIR='/ci/reports/', - TEST_OUTPUT_FILE_NAME='junit.xml', - ) -} diff --git a/ci/docker-compose.ci.yml b/ci/docker-compose.ci.yml index 30b49e3..0032d0c 100644 --- a/ci/docker-compose.ci.yml +++ b/ci/docker-compose.ci.yml @@ -8,13 +8,25 @@ services: dockerfile: ci/Dockerfile-CI image: netbox:slm environment: - - COVERAGE_FILE=/ci/.coverage + - COVERAGE_FILE=/tmp/.coverage + healthcheck: + disable: true volumes: - - ./reports:/ci/reports + - ./config:/etc/netbox/config:ro + - ./reports:/ci/reports:rw + - ../netbox_slm:/opt/netbox/netbox/netbox_slm:ro netbox-worker: image: netbox:slm + depends_on: + - netbox + healthcheck: + disable: true netbox-housekeeping: image: netbox:slm + depends_on: + - netbox + healthcheck: + disable: true postgres: ports: - "5432:5432" diff --git a/ci/docker-compose.yml b/ci/docker-compose.yml index f7965b9..8ae9410 100644 --- a/ci/docker-compose.yml +++ b/ci/docker-compose.yml @@ -1,63 +1,101 @@ --- +# based on https://github.com/netbox-community/netbox-docker/blob/3.2.0/docker-compose.yml services: netbox: &netbox - image: netboxcommunity/netbox:${VERSION-v4.0.6} + image: netboxcommunity/netbox:${VERSION-v4.2.3} depends_on: - - postgres - - redis - - redis-cache - - netbox-worker + - postgres + - redis + - redis-cache env_file: env/netbox.env - user: 'unit:root' + user: "unit:root" + healthcheck: + test: curl -f http://localhost:8080/login/ || exit 1 + start_period: 90s + timeout: 3s + interval: 15s volumes: - - ./configuration:/etc/netbox/config:z,ro - - netbox-media-files:/opt/netbox/netbox/media:z + - ./config:/etc/netbox/config:ro + - netbox-media-files:/opt/netbox/netbox/media:rw + - netbox-reports-files:/opt/netbox/netbox/reports:rw + - netbox-scripts-files:/opt/netbox/netbox/scripts:rw netbox-worker: <<: *netbox depends_on: - - redis - - postgres + netbox: + condition: service_healthy command: - - /opt/netbox/venv/bin/python - - /opt/netbox/netbox/manage.py - - rqworker + - /opt/netbox/venv/bin/python + - /opt/netbox/netbox/manage.py + - rqworker + healthcheck: + test: ps -aux | grep -v grep | grep -q rqworker || exit 1 + start_period: 20s + timeout: 3s + interval: 15s netbox-housekeeping: <<: *netbox depends_on: - - redis - - postgres + netbox: + condition: service_healthy command: - - /opt/netbox/housekeeping.sh + - /opt/netbox/housekeeping.sh + healthcheck: + test: ps -aux | grep -v grep | grep -q housekeeping || exit 1 + start_period: 20s + timeout: 3s + interval: 15s # postgres postgres: - image: postgres:14-alpine + image: postgres:17-alpine + healthcheck: + test: pg_isready -q -t 2 -d $$POSTGRES_DB -U $$POSTGRES_USER + start_period: 20s + timeout: 30s + interval: 10s + retries: 5 env_file: env/postgres.env volumes: - - netbox-postgres-data:/var/lib/postgresql/data + - netbox-postgres-data:/var/lib/postgresql/data # redis redis: - image: redis:7-alpine + image: valkey/valkey:8.0-alpine command: - - sh - - -c # this is to evaluate the $REDIS_PASSWORD from the env - - redis-server --appendonly yes --requirepass $$REDIS_PASSWORD ## $$ because of docker-compose + - sh + - -c + - valkey-server --appendonly yes --requirepass $$REDIS_PASSWORD + healthcheck: &redis-healthcheck + test: '[ $$(valkey-cli --pass "$${REDIS_PASSWORD}" ping) = ''PONG'' ]' + start_period: 5s + timeout: 3s + interval: 1s + retries: 5 env_file: env/redis.env volumes: - - netbox-redis-data:/data + - netbox-redis-data:/data redis-cache: - image: redis:7-alpine + image: valkey/valkey:8.0-alpine command: - - sh - - -c # this is to evaluate the $REDIS_PASSWORD from the env - - redis-server --requirepass $$REDIS_PASSWORD ## $$ because of docker-compose + - sh + - -c + - valkey-server --requirepass $$REDIS_PASSWORD + healthcheck: *redis-healthcheck env_file: env/redis-cache.env + volumes: + - netbox-redis-cache-data:/data volumes: netbox-media-files: driver: local netbox-postgres-data: driver: local + netbox-redis-cache-data: + driver: local netbox-redis-data: driver: local + netbox-reports-files: + driver: local + netbox-scripts-files: + driver: local diff --git a/ci/requirements_ci.txt b/ci/requirements_ci.txt deleted file mode 100644 index f98f879..0000000 --- a/ci/requirements_ci.txt +++ /dev/null @@ -1,2 +0,0 @@ -coverage==7.5.4 -unittest-xml-reporting==3.2.0 diff --git a/ci/styles/config/vocabularies/Base/accept.txt b/ci/styles/config/vocabularies/Base/accept.txt new file mode 100644 index 0000000..9619de2 --- /dev/null +++ b/ci/styles/config/vocabularies/Base/accept.txt @@ -0,0 +1,68 @@ +Anchore +APIs +Caddy +Checkmarx +Cobertura +Dependabot +Docker-composition +Dockerfile +DTDs +ESLint +Gravatar +Jira +JMeter +JUnit +NCover +Nginx +OJAudit +OpenShift +PDFs +Pydantic +Robocop +Snyk +Trello +Trivy +UUIDs +Wekan +[Hh]ostname +[Uu]nmerged +asyncio +autoformatting +breakpoint +clearable +cloc +discoverability +donut +errored +favicon +fixme +hostnames? +hotspots? +lookback +misconfigured +mypy +namespace +npm +parameterizable +phpldapadmin +[Pp]erformancetest +severities +sparkline +subfolders +submenus +suppressions +todo +tooltips? +tracebacks? +unencrypted +unicode +unmerged +upvotes +url +xml +NetBox +NetBox SLM +Mart +Visser +Suchow +Redistributions diff --git a/ci/styles/config/vocabularies/Base/reject.txt b/ci/styles/config/vocabularies/Base/reject.txt new file mode 100644 index 0000000..70dde23 --- /dev/null +++ b/ci/styles/config/vocabularies/Base/reject.txt @@ -0,0 +1,3 @@ +docker composition +Docker composition +docker-composition diff --git a/pyproject.toml b/pyproject.toml index 39b2504..0f3eab0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -30,12 +30,13 @@ dependencies = [] [project.optional-dependencies] build = [ - "build == 1.2.1", - "setuptools == 70.3.0", - "twine == 5.1.1", + "build == 1.2.2.post1", + "setuptools == 75.8.0", + "twine == 6.1.0", ] -tools = [ - "ruff == 0.5.1", +ci = [ + "coverage == 7.6.11", + "ruff == 0.9.6", ] [tool.setuptools.dynamic] @@ -46,7 +47,7 @@ include = ["netbox_slm"] [tool.ruff] line-length = 120 -target-version = "py311" +target-version = "py312" src = ["netbox_slm"] [tool.ruff.lint.per-file-ignores] diff --git a/sonar-project.properties b/sonar-project.properties index ed333fd..f7742b4 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -2,12 +2,11 @@ sonar.organization=ictu sonar.projectKey=ICTU_netbox_slm sonar.projectName=netbox-slm -sonar.projectVersion=1.4 +sonar.projectVersion=1.7.0 # Path is relative to the sonar-project.properties file sonar.sources=netbox_slm -sonar.python.version=3.9, 3.10, 3.11 +sonar.python.version=3.11, 3.12, 3.13 -# Unit tests -sonar.python.xunit.reportPath=ci/reports/junit.xml -sonar.python.coverage.reportPaths=ci/reports/coverage.xml +# Python test reports +sonar.python.coverage.reportPaths=/ci/reports/coverage.xml