Merge pull request #432 from netbox-community/develop

Version 1.0.2
This commit is contained in:
Christian Mäder 2021-02-10 13:15:49 +01:00 committed by GitHub
commit c80fb19507
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
83 changed files with 1248 additions and 1047 deletions

23
.ecrc Normal file
View File

@ -0,0 +1,23 @@
{
"Verbose": false,
"Debug": false,
"IgnoreDefaults": false,
"SpacesAftertabs": false,
"NoColor": false,
"Exclude": [
"LICENSE",
"\\.initializers",
"\\.vscode"
],
"AllowedContentTypes": [],
"PassedFiles": [],
"Disable": {
// set these options to true to disable specific checks
"EndOfLine": false,
"Indentation": false,
"InsertFinalNewline": false,
"TrimTrailingWhitespace": false,
"IndentSize": true,
"MaxLineLength": false
}
}

11
.editorconfig Normal file
View File

@ -0,0 +1,11 @@
root = true
[*]
end_of_line = lf
insert_final_newline = true
charset = utf-8
indent_style = space
indent_size = 2
[*.py]
indent_size = 4

7
.flake8 Normal file
View File

@ -0,0 +1,7 @@
[flake8]
max-line-length = 100
extend-ignore = E203, W503
per-file-ignores =
configuration/*:E131,E251,E266,E302,E305,E501,E722
startup_scripts/startup_script_utils/__init__.py:F401
docker/*:E266,E722

View File

@ -16,14 +16,14 @@ Before raising an issue here, answer the following questions for yourself, pleas
* Have you updated to the latest version and tried again? (i.e. `git pull` and `docker-compose pull`)
* Have you reset the project and tried again? (i.e. `docker-compose down -v`)
* Are you confident that your problem is related to the Docker image or Docker Compose file this project provides?
(Otherwise ask on the Netbox mailing list, please: https://groups.google.com/d/forum/netbox-discuss)
(Otherwise ask on the NetBox mailing list, please: https://groups.google.com/d/forum/netbox-discuss)
* Have you looked through the issues already resolved?
Please try this means to get help before opening an issue here:
* On the networktocode Slack in the #netbox-docker channel: http://slack.networktocode.com/
* On the networktocode Slack in the #netbox channel: http://slack.networktocode.com/
* On the Netbox mailing list: https://groups.google.com/d/forum/netbox-discuss
* On the NetBox mailing list: https://groups.google.com/d/forum/netbox-discuss
Please don't open an issue when you have a PR ready. Just submit the PR, that's good enough.

View File

@ -1,13 +1,15 @@
blank_issues_enabled: false
contact_links:
- name: The \#netbox-docker Slack channel
url: http://slack.networktocode.com/
about: It's usually the quickest way to seek help when you're in trouble with regards to Netbox Docker.
- name: Github Discussions
- name: Question
url: https://github.com/netbox-community/netbox-docker/discussions
about: This is the right place to ask questions about how to use or do certain things with Netbox Docker.
about: The Github Discussions are the right place to ask questions about how to use or do certain things with NetBox Docker.
- name: Have you had a look at our Wiki?
- name: Chat
url: http://slack.networktocode.com/
about: 'Usually the quickest way to seek help with small issues is to join our #netbox-docker Slack channel.'
- name: Community Wiki
url: https://github.com/netbox-community/netbox-docker/wiki
about: Our wiki contains information for common problems and tips for operating Netbox Docker in production.
about: |
Our wiki contains information for common problems and tips for operating NetBox Docker in production.
It's maintained by our excellent community.

View File

@ -15,14 +15,14 @@ Before raising an issue here, answer the following questions for yourself, pleas
* Have you had a look at the rest of the wiki? (https://github.com/netbox-community/netbox-docker/wiki)
* Have you read the release notes recently (https://github.com/netbox-community/netbox-docker/releases)
* Are you confident that your feature/change request is related to the Docker image or Docker Compose file this project provides?
(Otherwise ask on the Netbox mailing list, please: https://groups.google.com/d/forum/netbox-discuss)
(Otherwise ask on the NetBox mailing list, please: https://groups.google.com/d/forum/netbox-discuss)
* Have you looked through the issues already resolved?
Please try this means to get help before opening an issue here:
* On the networktocode Slack in the #netbox-docker channel: http://slack.networktocode.com/
* On the networktocode Slack in the #netbox channel: http://slack.networktocode.com/
* On the Netbox mailing list: https://groups.google.com/d/forum/netbox-discuss
* On the NetBox mailing list: https://groups.google.com/d/forum/netbox-discuss
Please don't open an issue when you have a PR ready. Just submit the PR, that's good enough.

View File

@ -9,6 +9,29 @@ on:
- release
jobs:
lint:
runs-on: ubuntu-latest
name: Checks syntax of our code
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
- name: Lint Code Base
uses: github/super-linter@v3
env:
DEFAULT_BRANCH: develop
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SUPPRESS_POSSUM: true
LINTER_RULES_PATH: /
VALIDATE_ALL_CODEBASE: false
VALIDATE_DOCKERFILE: false
FILTER_REGEX_EXCLUDE: (.*/)?(LICENSE|configuration/.*)
EDITORCONFIG_FILE_NAME: .ecrc
DOCKERFILE_HADOLINT_FILE_NAME: .hadolint.yaml
MARKDOWN_CONFIG_FILE: .markdown-lint.yml
PYTHON_BLACK_CONFIG_FILE: pyproject.toml
PYTHON_FLAKE8_CONFIG_FILE: .flake8
PYTHON_ISORT_CONFIG_FILE: pyproject.toml
build:
continue-on-error: ${{ matrix.docker_from == 'alpine:edge' }}
strategy:
@ -23,7 +46,7 @@ jobs:
- alpine:edge
fail-fast: false
runs-on: ubuntu-latest
name: Builds new Netbox Docker Images
name: Builds new NetBox Docker Images
steps:
- id: git-checkout
name: Checkout

View File

@ -18,7 +18,7 @@ jobs:
- ./build.sh develop
fail-fast: false
runs-on: ubuntu-latest
name: Builds new Netbox Docker Images
name: Builds new NetBox Docker Images
steps:
- id: git-checkout
name: Checkout

1
.gitignore vendored
View File

@ -9,3 +9,4 @@ configuration/*
configuration/ldap/*
!configuration/ldap/ldap_config.py
prometheus.yml
super-linter.log

3
.hadolint.yaml Normal file
View File

@ -0,0 +1,3 @@
ignored:
- DL3006
- DL3018

2
.markdown-lint.yml Normal file
View File

@ -0,0 +1,2 @@
MD013: false
MD041: false

View File

@ -4,23 +4,31 @@ FROM ${FROM} as builder
RUN apk add --no-cache \
bash \
build-base \
cargo \
ca-certificates \
cyrus-sasl-dev \
graphviz \
jpeg-dev \
libevent-dev \
libffi-dev \
libressl-dev \
libxslt-dev \
musl-dev \
openldap-dev \
postgresql-dev \
py3-pip \
python3-dev \
&& python3 -m venv /opt/netbox/venv \
&& /opt/netbox/venv/bin/python3 -m pip install --upgrade pip setuptools
&& /opt/netbox/venv/bin/python3 -m pip install --upgrade \
pip \
setuptools \
wheel
ARG NETBOX_PATH
COPY ${NETBOX_PATH}/requirements.txt requirements-container.txt /
RUN /opt/netbox/venv/bin/pip install -r /requirements.txt -r /requirements-container.txt
RUN /opt/netbox/venv/bin/pip install \
-r /requirements.txt \
-r /requirements-container.txt
###
# Main stage
@ -81,8 +89,8 @@ LABEL ORIGINAL_TAG="" \
# Also https://microbadger.com/labels
org.label-schema.schema-version="1.0" \
org.label-schema.build-date="" \
org.label-schema.name="Netbox Docker" \
org.label-schema.description="A container based distribution of Netbox, the free and open IPAM and DCIM solution." \
org.label-schema.name="NetBox Docker" \
org.label-schema.description="A container based distribution of NetBox, the free and open IPAM and DCIM solution." \
org.label-schema.vendor="The netbox-docker contributors." \
org.label-schema.url="https://github.com/netbox-community/netbox-docker" \
org.label-schema.usage="https://github.com/netbox-community/netbox-docker/wiki" \
@ -91,8 +99,8 @@ LABEL ORIGINAL_TAG="" \
org.label-schema.version="snapshot" \
# See https://github.com/opencontainers/image-spec/blob/master/annotations.md#pre-defined-annotation-keys
org.opencontainers.image.created="" \
org.opencontainers.image.title="Netbox Docker" \
org.opencontainers.image.description="A container based distribution of Netbox, the free and open IPAM and DCIM solution." \
org.opencontainers.image.title="NetBox Docker" \
org.opencontainers.image.description="A container based distribution of NetBox, the free and open IPAM and DCIM solution." \
org.opencontainers.image.licenses="Apache-2.0" \
org.opencontainers.image.authors="The netbox-docker contributors." \
org.opencontainers.image.vendor="The netbox-docker contributors." \

View File

@ -9,7 +9,7 @@
[![MicroBadger Size](https://img.shields.io/microbadger/image-size/netboxcommunity/netbox)][netbox-docker-microbadger]
[![GitHub license](https://img.shields.io/github/license/netbox-community/netbox-docker)][netbox-docker-license]
[The Github repository](netbox-docker-github) houses the components needed to build Netbox as a Docker container.
[The Github repository](netbox-docker-github) houses the components needed to build NetBox as a Docker container.
Images are built using this code and are released to [Docker Hub][netbox-dockerhub] and [Quay.io][netbox-quayio] once a day.
Do you have any questions?
@ -27,14 +27,14 @@ Before opening an issue on Github, please join the [Network To Code][ntc-slack]
## Docker Tags
* `vX.Y.Z`: These are release builds, automatically built from [the corresponding releases of Netbox][netbox-releases].
* `latest`: These are release builds, automatically built from [the `master` branch of Netbox][netbox-master].
* `snapshot`: These are pre-release builds, automatically built from the [`develop` branch of Netbox][netbox-develop].
* `develop-X.Y`: These are pre-release builds, automatically built from the corresponding [branch of Netbox][netbox-branches].
* `vX.Y.Z`: These are release builds, automatically built from [the corresponding releases of NetBox][netbox-releases].
* `latest`: These are release builds, automatically built from [the `master` branch of NetBox][netbox-master].
* `snapshot`: These are pre-release builds, automatically built from the [`develop` branch of NetBox][netbox-develop].
* `develop-X.Y`: These are pre-release builds, automatically built from the corresponding [branch of NetBox][netbox-branches].
Then there is currently one extra tags for each of the above tags:
* `-ldap`: Contains additional dependencies and configurations for connecting Netbox to an LDAP directory.
* `-ldap`: Contains additional dependencies and configurations for connecting NetBox to an LDAP directory.
[Learn more about that in our wiki][netbox-docker-ldap].
New images are built and published automatically every ~24h.
@ -47,7 +47,7 @@ New images are built and published automatically every ~24h.
## Quickstart
To get Netbox Docker up and running run the following commands.
To get NetBox Docker up and running run the following commands.
There is a more complete [_Getting Started_ guide on our wiki][wiki-getting-started] which explains every step.
```bash
@ -66,7 +66,7 @@ docker-compose up
The whole application will be available after a few minutes.
Open the URL `http://0.0.0.0:8000/` in a web-browser.
You should see the Netbox homepage.
You should see the NetBox homepage.
In the top-right corner you can login.
The default credentials are:
@ -79,7 +79,7 @@ The default credentials are:
## Documentation
Please refer [to our wiki on Github][netbox-docker-wiki] for further information on how to use this Netbox Docker image properly.
Please refer [to our wiki on Github][netbox-docker-wiki] for further information on how to use this NetBox Docker image properly.
It covers advanced topics such as using files for secrets, deployment to Kubernetes, monitoring and configuring NAPALM or LDAP.
[netbox-docker-wiki]: https://github.com/netbox-community/netbox-docker/wiki/
@ -89,7 +89,7 @@ It covers advanced topics such as using files for secrets, deployment to Kuberne
Feel free to ask questions in our [Github Community][netbox-community] or join [our Slack channel `#netbox-docker`][netbox-docker-slack] on the [Network To Code Slack][ntc-slack],
which is free to use and where there are almost always people online that can help you in the Slack channel.
If you need help with using Netbox or developing for it or against it's API you may find the `#netbox` channel on the same Slack instance very helpful.
If you need help with using NetBox or developing for it or against it's API you may find the `#netbox` channel on the same Slack instance very helpful.
[netbox-community]: https://github.com/netbox-community/netbox-docker/discussions
@ -97,36 +97,11 @@ If you need help with using Netbox or developing for it or against it's API you
This project relies only on *Docker* and *docker-compose* meeting these requirements:
* The *Docker version* must be at least `17.05`.
* The *docker-compose version* must be at least `1.17.0`.
* The *Docker version* must be at least `19.03`.
* The *docker-compose version* must be at least `1.28.0`.
To check the version installed on your system run `docker --version` and `docker-compose --version`.
## Use a Specific Netbox Version
The `docker-compose.yml` file is prepared to run a specific version of Netbox, instead of `latest`.
To use this feature, set and export the environment-variable `VERSION` before launching `docker-compose`, as shown below.
`VERSION` may be set to the name of
[any tag of the `netboxcommunity/netbox` Docker image on Docker Hub][netbox-dockerhub] or [Quay.io][netbox-quayio].
```bash
export VERSION=v2.7.1
docker-compose pull netbox
docker-compose up -d
```
You can also build a specific version of the Netbox Docker image yourself.
`VERSION` can be any valid [git ref][git-ref] in that case.
```bash
export VERSION=v2.7.1
./build.sh $VERSION
docker-compose up -d
```
[git-ref]: https://git-scm.com/book/en/v2/Git-Internals-Git-References
[netbox-github]: https://github.com/netbox-community/netbox/releases
## Breaking Changes
From time to time it might become necessary to re-engineer the structure of this setup.
@ -151,7 +126,7 @@ For more details on custom builds [consult our wiki][netbox-docker-wiki-build].
## Tests
We have a test script.
It runs Netbox's own unit tests and ensures that all initializers work:
It runs NetBox's own unit tests and ensures that all initializers work:
```bash
IMAGE=netboxcommunity/netbox:latest ./test.sh

View File

@ -1 +1 @@
1.0.1
1.0.2

View File

@ -45,16 +45,16 @@ _get_image_configuration() {
--silent \
--location \
--header "Authorization: Bearer $token" \
"https://registry-1.docker.io/v2/$image/blobs/$digest" \
| jq -r ".config.Labels.\"$label\""
"https://registry-1.docker.io/v2/$image/blobs/$digest" |
jq -r ".config.Labels.\"$label\""
}
_get_token() {
local image=$1
curl \
--silent \
"https://auth.docker.io/token?scope=repository:$image:pull&service=registry.docker.io" \
| jq -r '.token'
"https://auth.docker.io/token?scope=repository:$image:pull&service=registry.docker.io" |
jq -r '.token'
}
_get_digest() {
@ -65,8 +65,8 @@ _get_digest() {
--silent \
--header "Accept: application/vnd.docker.distribution.manifest.v2+json" \
--header "Authorization: Bearer $token" \
"https://registry-1.docker.io/v2/$image/manifests/$tag" \
| jq -r '.config.digest'
"https://registry-1.docker.io/v2/$image/manifests/$tag" |
jq -r '.config.digest'
}
_get_layers() {
@ -77,6 +77,6 @@ _get_layers() {
--silent \
--header "Accept: application/vnd.docker.distribution.manifest.v2+json" \
--header "Authorization: Bearer $token" \
"https://registry-1.docker.io/v2/$image/manifests/$tag" \
| jq -r '.layers[].digest'
"https://registry-1.docker.io/v2/$image/manifests/$tag" |
jq -r '.layers[].digest'
}

View File

@ -60,8 +60,9 @@ if [ "${PRERELEASE}" == "true" ]; then
# shellcheck disable=SC2003
MINOR_UNSTABLE=$(expr match "${VERSION}" 'v[0-9]\+\.\([0-9]\+\)')
if { [ "${MAJOR_STABLE}" -eq "${MAJOR_UNSTABLE}" ] \
&& [ "${MINOR_STABLE}" -ge "${MINOR_UNSTABLE}" ];
if {
[ "${MAJOR_STABLE}" -eq "${MAJOR_UNSTABLE}" ] &&
[ "${MINOR_STABLE}" -ge "${MINOR_UNSTABLE}" ]
} || [ "${MAJOR_STABLE}" -gt "${MAJOR_UNSTABLE}" ]; then
echo "❎ Latest unstable version '${VERSION}' is not higher than the latest stable version '$STABLE_VERSION'."

View File

@ -1,5 +1,5 @@
#!/bin/bash
# Builds develop, develop-* and master branches of Netbox
# Builds develop, develop-* and master branches of NetBox
echo "▶️ $0 $*"

View File

@ -1,5 +1,5 @@
#!/bin/bash
# Clones the Netbox repository with git from Github and builds the Dockerfile
# Clones the NetBox repository with git from Github and builds the Dockerfile
echo "▶️ $0 $*"
@ -106,7 +106,7 @@ else
fi
###
# Variables for fetching the Netbox source
# Variables for fetching the NetBox source
###
SRC_ORG="${SRC_ORG-netbox-community}"
SRC_REPO="${SRC_REPO-netbox}"
@ -115,10 +115,10 @@ URL="${URL-https://github.com/${SRC_ORG}/${SRC_REPO}.git}"
NETBOX_PATH="${NETBOX_PATH-.netbox}"
###
# Fetching the Netbox source
# Fetching the NetBox source
###
if [ "${2}" != "--push-only" ] && [ -z "${SKIP_GIT}" ] ; then
echo "🌐 Checking out '${NETBOX_BRANCH}' of Netbox from the url '${URL}' into '${NETBOX_PATH}'"
if [ "${2}" != "--push-only" ] && [ -z "${SKIP_GIT}" ]; then
echo "🌐 Checking out '${NETBOX_BRANCH}' of NetBox from the url '${URL}' into '${NETBOX_PATH}'"
if [ ! -d "${NETBOX_PATH}" ]; then
$DRY git clone -q --depth 10 -b "${NETBOX_BRANCH}" "${URL}" "${NETBOX_PATH}"
fi
@ -135,7 +135,7 @@ if [ "${2}" != "--push-only" ] && [ -z "${SKIP_GIT}" ] ; then
$DRY git checkout -qf FETCH_HEAD
$DRY git prune
)
echo "✅ Checked out Netbox"
echo "✅ Checked out NetBox"
fi
###
@ -174,9 +174,18 @@ PROJECT_VERSION="${PROJECT_VERSION-$(sed -e 's/^[[:space:]]*//' -e 's/[[:space:]
# Get the Git information from the netbox directory
if [ -d "${NETBOX_PATH}/.git" ]; then
NETBOX_GIT_REF=$(cd "${NETBOX_PATH}"; git rev-parse HEAD)
NETBOX_GIT_BRANCH=$(cd "${NETBOX_PATH}"; git rev-parse --abbrev-ref HEAD)
NETBOX_GIT_URL=$(cd "${NETBOX_PATH}"; git remote get-url origin)
NETBOX_GIT_REF=$(
cd "${NETBOX_PATH}"
git rev-parse HEAD
)
NETBOX_GIT_BRANCH=$(
cd "${NETBOX_PATH}"
git rev-parse --abbrev-ref HEAD
)
NETBOX_GIT_URL=$(
cd "${NETBOX_PATH}"
git remote get-url origin
)
fi
###
@ -186,19 +195,22 @@ DOCKER_REGISTRY="${DOCKER_REGISTRY-docker.io}"
DOCKER_ORG="${DOCKER_ORG-netboxcommunity}"
DOCKER_REPO="${DOCKER_REPO-netbox}"
case "${NETBOX_BRANCH}" in
master)
TAG="${TAG-latest}";;
develop)
TAG="${TAG-snapshot}";;
*)
TAG="${TAG-$NETBOX_BRANCH}";;
master)
TAG="${TAG-latest}"
;;
develop)
TAG="${TAG-snapshot}"
;;
*)
TAG="${TAG-$NETBOX_BRANCH}"
;;
esac
###
# Determine targets to build
###
DEFAULT_DOCKER_TARGETS=("main" "ldap")
DOCKER_TARGETS=( "${DOCKER_TARGET:-"${DEFAULT_DOCKER_TARGETS[@]}"}")
DOCKER_TARGETS=("${DOCKER_TARGET:-"${DEFAULT_DOCKER_TARGETS[@]}"}")
echo "🏭 Building the following targets:" "${DOCKER_TARGETS[@]}"
###
@ -216,7 +228,7 @@ for DOCKER_TARGET in "${DOCKER_TARGETS[@]}"; do
TARGET_DOCKER_TAG="${TARGET_DOCKER_TAG}-${DOCKER_TARGET}"
fi
if [ -n "${GH_ACTION}" ]; then
echo "FINAL_DOCKER_TAG=${TARGET_DOCKER_TAG}" >> $GITHUB_ENV
echo "FINAL_DOCKER_TAG=${TARGET_DOCKER_TAG}" >>"$GITHUB_ENV"
echo "::set-output name=skipped::false"
fi
@ -242,7 +254,7 @@ for DOCKER_TARGET in "${DOCKER_TARGETS[@]}"; do
###
# Proceeding to buils stage, except if `--push-only` is passed
###
if [ "${2}" != "--push-only" ] ; then
if [ "${2}" != "--push-only" ]; then
###
# Checking if the build is necessary,
# meaning build only if one of those values changed:
@ -259,7 +271,7 @@ for DOCKER_TARGET in "${DOCKER_TARGETS[@]}"; do
BUILD_REASON="${BUILD_REASON} interactive"
elif [ "$DOCKER_REGISTRY" = "docker.io" ]; then
source ./build-functions/get-public-image-config.sh
IFS=':' read -ra DOCKER_FROM_SPLIT <<< "${DOCKER_FROM}"
IFS=':' read -ra DOCKER_FROM_SPLIT <<<"${DOCKER_FROM}"
if ! [[ ${DOCKER_FROM_SPLIT[0]} =~ .*/.* ]]; then
# Need to use "library/..." for images the have no two part name
DOCKER_FROM_SPLIT[0]="library/${DOCKER_FROM_SPLIT[0]}"
@ -295,8 +307,8 @@ for DOCKER_TARGET in "${DOCKER_TARGETS[@]}"; do
-t "${TARGET_DOCKER_TAG}"
)
if [ -n "${TARGET_DOCKER_SHORT_TAG}" ]; then
DOCKER_BUILD_ARGS+=( -t "${TARGET_DOCKER_SHORT_TAG}" )
DOCKER_BUILD_ARGS+=( -t "${TARGET_DOCKER_LATEST_TAG}" )
DOCKER_BUILD_ARGS+=(-t "${TARGET_DOCKER_SHORT_TAG}")
DOCKER_BUILD_ARGS+=(-t "${TARGET_DOCKER_LATEST_TAG}")
fi
# --label
@ -323,22 +335,22 @@ for DOCKER_TARGET in "${DOCKER_TARGETS[@]}"; do
)
fi
if [ -n "${BUILD_REASON}" ]; then
BUILD_REASON=$(sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' <<< "$BUILD_REASON")
DOCKER_BUILD_ARGS+=( --label "BUILD_REASON=${BUILD_REASON}" )
BUILD_REASON=$(sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' <<<"$BUILD_REASON")
DOCKER_BUILD_ARGS+=(--label "BUILD_REASON=${BUILD_REASON}")
fi
# --build-arg
DOCKER_BUILD_ARGS+=( --build-arg "NETBOX_PATH=${NETBOX_PATH}" )
DOCKER_BUILD_ARGS+=(--build-arg "NETBOX_PATH=${NETBOX_PATH}")
if [ -n "${DOCKER_FROM}" ]; then
DOCKER_BUILD_ARGS+=( --build-arg "FROM=${DOCKER_FROM}" )
DOCKER_BUILD_ARGS+=(--build-arg "FROM=${DOCKER_FROM}")
fi
if [ -n "${HTTP_PROXY}" ]; then
DOCKER_BUILD_ARGS+=( --build-arg "http_proxy=${HTTP_PROXY}" )
DOCKER_BUILD_ARGS+=( --build-arg "https_proxy=${HTTPS_PROXY}" )
DOCKER_BUILD_ARGS+=(--build-arg "http_proxy=${HTTP_PROXY}")
DOCKER_BUILD_ARGS+=(--build-arg "https_proxy=${HTTPS_PROXY}")
fi
if [ -n "${NO_PROXY}" ]; then
DOCKER_BUILD_ARGS+=( --build-arg "no_proxy=${NO_PROXY}" )
DOCKER_BUILD_ARGS+=(--build-arg "no_proxy=${NO_PROXY}")
fi
###
@ -360,7 +372,7 @@ for DOCKER_TARGET in "${DOCKER_TARGETS[@]}"; do
###
# Pushing the docker images if either `--push` or `--push-only` are passed
###
if [ "${2}" == "--push" ] || [ "${2}" == "--push-only" ] ; then
if [ "${2}" == "--push" ] || [ "${2}" == "--push-only" ]; then
source ./build-functions/docker-functions.sh
push_image_to_registry "${TARGET_DOCKER_TAG}"

View File

@ -5,9 +5,8 @@
####
import re
from os.path import dirname, abspath, join
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

View File

@ -1,9 +1,10 @@
import ldap
from django_auth_ldap.config import LDAPSearch
from importlib import import_module
from os import environ
import ldap
from django_auth_ldap.config import LDAPSearch
# Read secret from file
def _read_secret(secret_name, default=None):
try:
@ -47,9 +48,11 @@ LDAP_IGNORE_CERT_ERRORS = environ.get('LDAP_IGNORE_CERT_ERRORS', 'False').lower(
AUTH_LDAP_USER_SEARCH_BASEDN = environ.get('AUTH_LDAP_USER_SEARCH_BASEDN', '')
AUTH_LDAP_USER_SEARCH_ATTR = environ.get('AUTH_LDAP_USER_SEARCH_ATTR', 'sAMAccountName')
AUTH_LDAP_USER_SEARCH = LDAPSearch(AUTH_LDAP_USER_SEARCH_BASEDN,
AUTH_LDAP_USER_SEARCH = LDAPSearch(
AUTH_LDAP_USER_SEARCH_BASEDN,
ldap.SCOPE_SUBTREE,
"(" + AUTH_LDAP_USER_SEARCH_ATTR + "=%(user)s)")
"(" + AUTH_LDAP_USER_SEARCH_ATTR + "=%(user)s)"
)
# This search ought to return all groups to which the user belongs. django_auth_ldap uses this to determine group
# heirarchy.

View File

@ -4,10 +4,10 @@
#
# They can be imported by other code (see `ldap_config.py` for an example).
from os.path import abspath, isfile
from os import scandir
import importlib.util
import sys
from os import scandir
from os.path import abspath, isfile
def _filename(f):
@ -15,7 +15,7 @@ def _filename(f):
def _import(module_name, path, loaded_configurations):
spec = importlib.util.spec_from_file_location('', path)
spec = importlib.util.spec_from_file_location("", path)
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
sys.modules[module_name] = module
@ -28,9 +28,9 @@ def _import(module_name, path, loaded_configurations):
def read_configurations(config_module, config_dir, main_config):
loaded_configurations = []
main_config_path = abspath(f'{config_dir}/{main_config}.py')
main_config_path = abspath(f"{config_dir}/{main_config}.py")
if isfile(main_config_path):
_import(f'{config_module}.{main_config}', main_config_path, loaded_configurations)
_import(f"{config_module}.{main_config}", main_config_path, loaded_configurations)
else:
print(f"⚠️ Main configuration '{main_config_path}' not found.")
@ -39,13 +39,13 @@ def read_configurations(config_module, config_dir, main_config):
if not f.is_file():
continue
if f.name.startswith('__'):
if f.name.startswith("__"):
continue
if not f.name.endswith('.py'):
if not f.name.endswith(".py"):
continue
if f.name == f'{config_dir}.py':
if f.name == f"{config_dir}.py":
continue
module_name = f"{config_module}.{f.name[:-len('.py')]}".replace(".", "_")
@ -66,9 +66,10 @@ def read_configurations(config_module, config_dir, main_config):
_loaded_configurations = read_configurations(
config_dir = '/etc/netbox/config/',
config_module = 'netbox.configuration',
main_config = 'configuration')
config_dir="/etc/netbox/config/",
config_module="netbox.configuration",
main_config="configuration",
)
def __getattr__(name):

View File

@ -1,13 +1,14 @@
#!/bin/bash
# Runs on every start of the Netbox Docker container
# Runs on every start of the NetBox Docker container
# Stop when an error occures
set -e
# Allows Netbox to be run as non-root users
# Allows NetBox to be run as non-root users
umask 002
# Load correct Python3 env
# shellcheck disable=SC1091
source /opt/netbox/venv/bin/activate
# Try to connect to the DB
@ -17,7 +18,7 @@ CUR_DB_WAIT_TIME=0
while ! ./manage.py migrate 2>&1 && [ "${CUR_DB_WAIT_TIME}" -lt "${MAX_DB_WAIT_TIME}" ]; do
echo "⏳ Waiting on DB... (${CUR_DB_WAIT_TIME}s / ${MAX_DB_WAIT_TIME}s)"
sleep "${DB_WAIT_TIMEOUT}"
CUR_DB_WAIT_TIME=$(( CUR_DB_WAIT_TIME + DB_WAIT_TIMEOUT ))
CUR_DB_WAIT_TIME=$((CUR_DB_WAIT_TIME + DB_WAIT_TIMEOUT))
done
if [ "${CUR_DB_WAIT_TIME}" -ge "${MAX_DB_WAIT_TIME}" ]; then
echo "❌ Waited ${MAX_DB_WAIT_TIME}s or more for the DB to become ready."
@ -35,17 +36,17 @@ else
SUPERUSER_EMAIL='admin@example.com'
fi
if [ -f "/run/secrets/superuser_password" ]; then
SUPERUSER_PASSWORD="$(< /run/secrets/superuser_password)"
SUPERUSER_PASSWORD="$(</run/secrets/superuser_password)"
elif [ -z ${SUPERUSER_PASSWORD+x} ]; then
SUPERUSER_PASSWORD='admin'
fi
if [ -f "/run/secrets/superuser_api_token" ]; then
SUPERUSER_API_TOKEN="$(< /run/secrets/superuser_api_token)"
SUPERUSER_API_TOKEN="$(</run/secrets/superuser_api_token)"
elif [ -z ${SUPERUSER_API_TOKEN+x} ]; then
SUPERUSER_API_TOKEN='0123456789abcdef0123456789abcdef01234567'
fi
./manage.py shell --interface python << END
./manage.py shell --interface python <<END
from django.contrib.auth.models import User
from users.models import Token
if not User.objects.filter(username='${SUPERUSER_NAME}'):

View File

@ -22,9 +22,10 @@ load_configuration() {
# this curl call will get a reply once unit is fully launched
curl --silent --output /dev/null --request GET --unix-socket $UNIT_SOCKET http://localhost/
echo "⚙️ Applying configuration from $UNIT_CONFIG";
echo "⚙️ Applying configuration from $UNIT_CONFIG"
RESP_CODE=$(curl \
RESP_CODE=$(
curl \
--silent \
--output /dev/null \
--write-out '%{http_code}' \

View File

@ -1,9 +1,10 @@
from .configuration import read_configurations
_loaded_configurations = read_configurations(
config_dir = '/etc/netbox/config/ldap/',
config_module = 'netbox.configuration.ldap',
main_config = 'ldap_config')
config_dir="/etc/netbox/config/ldap/",
config_module="netbox.configuration.ldap",
main_config="ldap_config",
)
def __getattr__(name):
@ -14,6 +15,7 @@ def __getattr__(name):
pass
raise AttributeError
def __dir__():
names = []
for config in _loaded_configurations:

38
env/netbox.env vendored
View File

@ -1,39 +1,39 @@
CORS_ORIGIN_ALLOW_ALL=True
DB_NAME=netbox
DB_USER=netbox
DB_PASSWORD=J5brHrAXFLQSif0K
DB_HOST=postgres
EMAIL_SERVER=localhost
EMAIL_PORT=25
EMAIL_USERNAME=netbox
EMAIL_PASSWORD=
EMAIL_TIMEOUT=5
DB_NAME=netbox
DB_PASSWORD=J5brHrAXFLQSif0K
DB_USER=netbox
EMAIL_FROM=netbox@bar.com
EMAIL_PASSWORD=
EMAIL_PORT=25
EMAIL_SERVER=localhost
EMAIL_SSL_CERTFILE=
EMAIL_SSL_KEYFILE=
EMAIL_TIMEOUT=5
EMAIL_USERNAME=netbox
# EMAIL_USE_SSL and EMAIL_USE_TLS are mutually exclusive, i.e. they can't both be `true`!
EMAIL_USE_SSL=false
EMAIL_USE_TLS=false
EMAIL_SSL_CERTFILE=
EMAIL_SSL_KEYFILE=
MAX_PAGE_SIZE=1000
MEDIA_ROOT=/opt/netbox/netbox/media
METRICS_ENABLED=false
NAPALM_USERNAME=
NAPALM_PASSWORD=
NAPALM_TIMEOUT=10
REDIS_HOST=redis
REDIS_PASSWORD=H733Kdjndks81
REDIS_DATABASE=0
REDIS_SSL=false
NAPALM_USERNAME=
REDIS_CACHE_DATABASE=1
REDIS_CACHE_HOST=redis-cache
REDIS_CACHE_PASSWORD=t4Ph722qJ5QHeQ1qfu36
REDIS_CACHE_DATABASE=1
REDIS_CACHE_SSL=false
REDIS_DATABASE=0
REDIS_HOST=redis
REDIS_PASSWORD=H733Kdjndks81
REDIS_SSL=false
RELEASE_CHECK_URL=https://api.github.com/repos/netbox-community/netbox/releases
SECRET_KEY=r8OwDznj!!dci#P9ghmRfdu1Ysxm0AiPeDCQhKE+N_rClfWNj
SKIP_STARTUP_SCRIPTS=false
SKIP_SUPERUSER=false
SUPERUSER_NAME=admin
SUPERUSER_EMAIL=admin@example.com
SUPERUSER_PASSWORD=admin
SUPERUSER_API_TOKEN=0123456789abcdef0123456789abcdef01234567
SUPERUSER_EMAIL=admin@example.com
SUPERUSER_NAME=admin
SUPERUSER_PASSWORD=admin
WEBHOOKS_ENABLED=true

4
env/postgres.env vendored
View File

@ -1,3 +1,3 @@
POSTGRES_USER=netbox
POSTGRES_PASSWORD=J5brHrAXFLQSif0K
POSTGRES_DB=netbox
POSTGRES_PASSWORD=J5brHrAXFLQSif0K
POSTGRES_USER=netbox

View File

@ -0,0 +1,21 @@
## Possible Choices:
## new_window:
## - True
## - False
## content_type:
## - device
## - site
## - any-other-content-type
##
## Examples:
# - name: link_to_repo
# text: 'Link to Netbox Docker'
# url: 'https://github.com/netbox-community/netbox-docker'
# new_window: False
# content_type: device
# - name: link_to_localhost
# text: 'Link to localhost'
# url: 'http://localhost'
# new_window: True
# content_type: device

27
initializers/webhooks.yml Normal file
View File

@ -0,0 +1,27 @@
## Possible Choices:
## object_types:
## - device
## - site
## - any-other-content-type
## types:
## - type_create
## - type_update
## - type_delete
## Examples:
# - name: device_creation
# payload_url: 'http://localhost:8080'
# object_types:
# - device
# - cable
# type_create: True
# - name: device_update
# payload_url: 'http://localhost:8080'
# object_types:
# - device
# type_update: True
# - name: device_delete
# payload_url: 'http://localhost:8080'
# object_types:
# - device
# type_delete: True

26
pyproject.toml Normal file
View File

@ -0,0 +1,26 @@
[tool.black]
line_length = 100
target-version = ['py38']
include = '\.pyi?$'
exclude = '''
(
/(
\.git
| \.venv
| \.netbox
| \.vscode
| configuration
)/
)
'''
[tool.isort]
profile = "black"
multi_line_output = 3
line_length = 100
[tool.pylint.messages_control]
disable = "C0330, C0326"
[tool.pylint.format]
max-line-length = "100"

View File

@ -1,4 +1,4 @@
napalm==3.2.0
ruamel.yaml==0.16.12
django-auth-ldap==2.2.0
django-storages==1.11.1
django-storages[azure,boto3,dropbox,google,libcloud,sftp]==1.11.1

View File

@ -4,20 +4,21 @@ from django.contrib.auth.models import User
from startup_script_utils import load_yaml, set_permissions
from users.models import Token
users = load_yaml('/opt/netbox/initializers/users.yml')
users = load_yaml("/opt/netbox/initializers/users.yml")
if users is None:
sys.exit()
for username, user_details in users.items():
if not User.objects.filter(username=username):
user = User.objects.create_user(
username = username,
password = user_details.get('password', 0) or User.objects.make_random_password())
username=username,
password=user_details.get("password", 0) or User.objects.make_random_password(),
)
print("👤 Created user",username)
print("👤 Created user", username)
if user_details.get('api_token', 0):
Token.objects.create(user=user, key=user_details['api_token'])
if user_details.get("api_token", 0):
Token.objects.create(user=user, key=user_details["api_token"])
yaml_permissions = user_details.get('permissions', [])
yaml_permissions = user_details.get("permissions", [])
set_permissions(user.user_permissions, yaml_permissions)

View File

@ -3,7 +3,7 @@ import sys
from django.contrib.auth.models import Group, User
from startup_script_utils import load_yaml, set_permissions
groups = load_yaml('/opt/netbox/initializers/groups.yml')
groups = load_yaml("/opt/netbox/initializers/groups.yml")
if groups is None:
sys.exit()
@ -13,11 +13,11 @@ for groupname, group_details in groups.items():
if created:
print("👥 Created group", groupname)
for username in group_details.get('users', []):
for username in group_details.get("users", []):
user = User.objects.get(username=username)
if user:
user.groups.add(group)
yaml_permissions = group_details.get('permissions', [])
yaml_permissions = group_details.get("permissions", [])
set_permissions(group.permissions, yaml_permissions)

View File

@ -3,8 +3,10 @@ import sys
from extras.models import CustomField
from startup_script_utils import load_yaml
def get_class_for_class_path(class_path):
import importlib
from django.contrib.contenttypes.models import ContentType
module_name, class_name = class_path.rsplit(".", 1)
@ -12,44 +14,48 @@ def get_class_for_class_path(class_path):
clazz = getattr(module, class_name)
return ContentType.objects.get_for_model(clazz)
customfields = load_yaml('/opt/netbox/initializers/custom_fields.yml')
customfields = load_yaml("/opt/netbox/initializers/custom_fields.yml")
if customfields is None:
sys.exit()
for cf_name, cf_details in customfields.items():
custom_field, created = CustomField.objects.get_or_create(name = cf_name)
custom_field, created = CustomField.objects.get_or_create(name=cf_name)
if created:
if cf_details.get('default', False):
custom_field.default = cf_details['default']
if cf_details.get("default", False):
custom_field.default = cf_details["default"]
if cf_details.get('description', False):
custom_field.description = cf_details['description']
if cf_details.get("description", False):
custom_field.description = cf_details["description"]
if cf_details.get('label', False):
custom_field.label = cf_details['label']
if cf_details.get("label", False):
custom_field.label = cf_details["label"]
for object_type in cf_details.get('on_objects', []):
for object_type in cf_details.get("on_objects", []):
custom_field.content_types.add(get_class_for_class_path(object_type))
if cf_details.get('required', False):
custom_field.required = cf_details['required']
if cf_details.get("required", False):
custom_field.required = cf_details["required"]
if cf_details.get('type', False):
custom_field.type = cf_details['type']
if cf_details.get("type", False):
custom_field.type = cf_details["type"]
if cf_details.get('weight', -1) >= 0:
custom_field.weight = cf_details['weight']
if cf_details.get("weight", -1) >= 0:
custom_field.weight = cf_details["weight"]
if cf_details.get('choices', False):
if cf_details.get("choices", False):
custom_field.choices = []
for choice_detail in cf_details.get('choices', []):
if isinstance(choice_detail, dict) and 'value' in choice_detail:
for choice_detail in cf_details.get("choices", []):
if isinstance(choice_detail, dict) and "value" in choice_detail:
# legacy mode
print(f"⚠️ Please migrate the choice '{choice_detail['value']}' of '{cf_name}' to the new format, as 'weight' is no longer supported!")
custom_field.choices.append(choice_detail['value'])
print(
f"⚠️ Please migrate the choice '{choice_detail['value']}' of '{cf_name}'"
+ " to the new format, as 'weight' is no longer supported!"
)
custom_field.choices.append(choice_detail["value"])
else:
custom_field.choices.append(choice_detail)

View File

@ -1,21 +1,21 @@
from extras.models import Tag
from utilities.choices import ColorChoices
from startup_script_utils import load_yaml
import sys
tags = load_yaml('/opt/netbox/initializers/tags.yml')
from extras.models import Tag
from startup_script_utils import load_yaml
from utilities.choices import ColorChoices
tags = load_yaml("/opt/netbox/initializers/tags.yml")
if tags is None:
sys.exit()
for params in tags:
if 'color' in params:
color = params.pop('color')
if "color" in params:
color = params.pop("color")
for color_tpl in ColorChoices:
if color in color_tpl:
params['color'] = color_tpl[0]
params["color"] = color_tpl[0]
tag, created = Tag.objects.get_or_create(**params)

View File

@ -1,22 +1,21 @@
from dcim.models import Region
from startup_script_utils import load_yaml
import sys
regions = load_yaml('/opt/netbox/initializers/regions.yml')
from dcim.models import Region
from startup_script_utils import load_yaml
regions = load_yaml("/opt/netbox/initializers/regions.yml")
if regions is None:
sys.exit()
optional_assocs = {
'parent': (Region, 'name')
}
optional_assocs = {"parent": (Region, "name")}
for params in regions:
for assoc, details in optional_assocs.items():
if assoc in params:
model, field = details
query = { field: params.pop(assoc) }
query = {field: params.pop(assoc)}
params[assoc] = model.objects.get(**query)

View File

@ -1,18 +1,15 @@
import sys
from dcim.models import Region, Site
from startup_script_utils import *
from startup_script_utils import load_yaml, pop_custom_fields, set_custom_fields_values
from tenancy.models import Tenant
sites = load_yaml('/opt/netbox/initializers/sites.yml')
sites = load_yaml("/opt/netbox/initializers/sites.yml")
if sites is None:
sys.exit()
optional_assocs = {
'region': (Region, 'name'),
'tenant': (Tenant, 'name')
}
optional_assocs = {"region": (Region, "name"), "tenant": (Tenant, "name")}
for params in sites:
custom_field_data = pop_custom_fields(params)
@ -20,7 +17,7 @@ for params in sites:
for assoc, details in optional_assocs.items():
if assoc in params:
model, field = details
query = { field: params.pop(assoc) }
query = {field: params.pop(assoc)}
params[assoc] = model.objects.get(**query)

View File

@ -1,8 +1,9 @@
from dcim.models import Manufacturer
from startup_script_utils import load_yaml
import sys
manufacturers = load_yaml('/opt/netbox/initializers/manufacturers.yml')
from dcim.models import Manufacturer
from startup_script_utils import load_yaml
manufacturers = load_yaml("/opt/netbox/initializers/manufacturers.yml")
if manufacturers is None:
sys.exit()

View File

@ -1,36 +1,31 @@
import sys
from dcim.models import DeviceType, Manufacturer, Region
from startup_script_utils import *
from startup_script_utils import load_yaml, pop_custom_fields, set_custom_fields_values
from tenancy.models import Tenant
device_types = load_yaml('/opt/netbox/initializers/device_types.yml')
device_types = load_yaml("/opt/netbox/initializers/device_types.yml")
if device_types is None:
sys.exit()
required_assocs = {
'manufacturer': (Manufacturer, 'name')
}
required_assocs = {"manufacturer": (Manufacturer, "name")}
optional_assocs = {
'region': (Region, 'name'),
'tenant': (Tenant, 'name')
}
optional_assocs = {"region": (Region, "name"), "tenant": (Tenant, "name")}
for params in device_types:
custom_field_data = pop_custom_fields(params)
for assoc, details in required_assocs.items():
model, field = details
query = { field: params.pop(assoc) }
query = {field: params.pop(assoc)}
params[assoc] = model.objects.get(**query)
for assoc, details in optional_assocs.items():
if assoc in params:
model, field = details
query = { field: params.pop(assoc) }
query = {field: params.pop(assoc)}
params[assoc] = model.objects.get(**query)

View File

@ -1,21 +1,21 @@
from dcim.models import RackRole
from utilities.choices import ColorChoices
from startup_script_utils import load_yaml
import sys
rack_roles = load_yaml('/opt/netbox/initializers/rack_roles.yml')
from dcim.models import RackRole
from startup_script_utils import load_yaml
from utilities.choices import ColorChoices
rack_roles = load_yaml("/opt/netbox/initializers/rack_roles.yml")
if rack_roles is None:
sys.exit()
for params in rack_roles:
if 'color' in params:
color = params.pop('color')
if "color" in params:
color = params.pop("color")
for color_tpl in ColorChoices:
if color in color_tpl:
params['color'] = color_tpl[0]
params["color"] = color_tpl[0]
rack_role, created = RackRole.objects.get_or_create(**params)

View File

@ -1,25 +1,23 @@
from dcim.models import Site,RackGroup
from startup_script_utils import load_yaml
import sys
rack_groups = load_yaml('/opt/netbox/initializers/rack_groups.yml')
from dcim.models import RackGroup, Site
from startup_script_utils import load_yaml
rack_groups = load_yaml("/opt/netbox/initializers/rack_groups.yml")
if rack_groups is None:
sys.exit()
required_assocs = {
'site': (Site, 'name')
}
required_assocs = {"site": (Site, "name")}
for params in rack_groups:
for assoc, details in required_assocs.items():
model, field = details
query = { field: params.pop(assoc) }
query = {field: params.pop(assoc)}
params[assoc] = model.objects.get(**query)
rack_group, created = RackGroup.objects.get_or_create(**params)
if created:
print("🎨 Created rack group", rack_group.name)

View File

@ -1,22 +1,20 @@
import sys
from dcim.models import Site, RackRole, Rack, RackGroup
from startup_script_utils import *
from dcim.models import Rack, RackGroup, RackRole, Site
from startup_script_utils import load_yaml, pop_custom_fields, set_custom_fields_values
from tenancy.models import Tenant
racks = load_yaml('/opt/netbox/initializers/racks.yml')
racks = load_yaml("/opt/netbox/initializers/racks.yml")
if racks is None:
sys.exit()
required_assocs = {
'site': (Site, 'name')
}
required_assocs = {"site": (Site, "name")}
optional_assocs = {
'role': (RackRole, 'name'),
'tenant': (Tenant, 'name'),
'group': (RackGroup, 'name')
"role": (RackRole, "name"),
"tenant": (Tenant, "name"),
"group": (RackGroup, "name"),
}
for params in racks:
@ -24,14 +22,14 @@ for params in racks:
for assoc, details in required_assocs.items():
model, field = details
query = { field: params.pop(assoc) }
query = {field: params.pop(assoc)}
params[assoc] = model.objects.get(**query)
for assoc, details in optional_assocs.items():
if assoc in params:
model, field = details
query = { field: params.pop(assoc) }
query = {field: params.pop(assoc)}
params[assoc] = model.objects.get(**query)

View File

@ -1,22 +1,22 @@
from dcim.models import DeviceRole
from utilities.choices import ColorChoices
from startup_script_utils import load_yaml
import sys
device_roles = load_yaml('/opt/netbox/initializers/device_roles.yml')
from dcim.models import DeviceRole
from startup_script_utils import load_yaml
from utilities.choices import ColorChoices
device_roles = load_yaml("/opt/netbox/initializers/device_roles.yml")
if device_roles is None:
sys.exit()
for params in device_roles:
if 'color' in params:
color = params.pop('color')
if "color" in params:
color = params.pop("color")
for color_tpl in ColorChoices:
if color in color_tpl:
params['color'] = color_tpl[0]
params["color"] = color_tpl[0]
device_role, created = DeviceRole.objects.get_or_create(**params)

View File

@ -1,14 +1,15 @@
from dcim.models import Manufacturer, Platform
from startup_script_utils import load_yaml
import sys
platforms = load_yaml('/opt/netbox/initializers/platforms.yml')
from dcim.models import Manufacturer, Platform
from startup_script_utils import load_yaml
platforms = load_yaml("/opt/netbox/initializers/platforms.yml")
if platforms is None:
sys.exit()
optional_assocs = {
'manufacturer': (Manufacturer, 'name'),
"manufacturer": (Manufacturer, "name"),
}
for params in platforms:
@ -16,7 +17,7 @@ for params in platforms:
for assoc, details in optional_assocs.items():
if assoc in params:
model, field = details
query = { field: params.pop(assoc) }
query = {field: params.pop(assoc)}
params[assoc] = model.objects.get(**query)

View File

@ -1,8 +1,9 @@
from tenancy.models import TenantGroup
from startup_script_utils import load_yaml
import sys
tenant_groups = load_yaml('/opt/netbox/initializers/tenant_groups.yml')
from startup_script_utils import load_yaml
from tenancy.models import TenantGroup
tenant_groups = load_yaml("/opt/netbox/initializers/tenant_groups.yml")
if tenant_groups is None:
sys.exit()

View File

@ -1,16 +1,14 @@
import sys
from startup_script_utils import *
from startup_script_utils import load_yaml, pop_custom_fields, set_custom_fields_values
from tenancy.models import Tenant, TenantGroup
tenants = load_yaml('/opt/netbox/initializers/tenants.yml')
tenants = load_yaml("/opt/netbox/initializers/tenants.yml")
if tenants is None:
sys.exit()
optional_assocs = {
'group': (TenantGroup, 'name')
}
optional_assocs = {"group": (TenantGroup, "name")}
for params in tenants:
custom_field_data = pop_custom_fields(params)
@ -18,7 +16,7 @@ for params in tenants:
for assoc, details in optional_assocs.items():
if assoc in params:
model, field = details
query = { field: params.pop(assoc) }
query = {field: params.pop(assoc)}
params[assoc] = model.objects.get(**query)

View File

@ -1,8 +1,9 @@
from virtualization.models import ClusterType
from startup_script_utils import load_yaml
import sys
cluster_types = load_yaml('/opt/netbox/initializers/cluster_types.yml')
from startup_script_utils import load_yaml
from virtualization.models import ClusterType
cluster_types = load_yaml("/opt/netbox/initializers/cluster_types.yml")
if cluster_types is None:
sys.exit()

View File

@ -1,8 +1,9 @@
from virtualization.models import ClusterGroup
from startup_script_utils import load_yaml
import sys
cluster_groups = load_yaml('/opt/netbox/initializers/cluster_groups.yml')
from startup_script_utils import load_yaml
from virtualization.models import ClusterGroup
cluster_groups = load_yaml("/opt/netbox/initializers/cluster_groups.yml")
if cluster_groups is None:
sys.exit()

View File

@ -1,23 +1,21 @@
import sys
from dcim.models import Site
from startup_script_utils import *
from virtualization.models import Cluster, ClusterType, ClusterGroup
from startup_script_utils import load_yaml, pop_custom_fields, set_custom_fields_values
from tenancy.models import Tenant
from virtualization.models import Cluster, ClusterGroup, ClusterType
clusters = load_yaml('/opt/netbox/initializers/clusters.yml')
clusters = load_yaml("/opt/netbox/initializers/clusters.yml")
if clusters is None:
sys.exit()
required_assocs = {
'type': (ClusterType, 'name')
}
required_assocs = {"type": (ClusterType, "name")}
optional_assocs = {
'site': (Site, 'name'),
'group': (ClusterGroup, 'name'),
'tenant': (Tenant, 'name')
"site": (Site, "name"),
"group": (ClusterGroup, "name"),
"tenant": (Tenant, "name"),
}
for params in clusters:
@ -25,14 +23,14 @@ for params in clusters:
for assoc, details in required_assocs.items():
model, field = details
query = { field: params.pop(assoc) }
query = {field: params.pop(assoc)}
params[assoc] = model.objects.get(**query)
for assoc, details in optional_assocs.items():
if assoc in params:
model, field = details
query = { field: params.pop(assoc) }
query = {field: params.pop(assoc)}
params[assoc] = model.objects.get(**query)

View File

@ -1,23 +1,21 @@
import sys
from dcim.models import Site
from startup_script_utils import *
from virtualization.models import Cluster, ClusterType, ClusterGroup
from startup_script_utils import load_yaml, pop_custom_fields, set_custom_fields_values
from tenancy.models import Tenant
from virtualization.models import Cluster, ClusterGroup, ClusterType
clusters = load_yaml('/opt/netbox/initializers/clusters.yml')
clusters = load_yaml("/opt/netbox/initializers/clusters.yml")
if clusters is None:
sys.exit()
required_assocs = {
'type': (ClusterType, 'name')
}
required_assocs = {"type": (ClusterType, "name")}
optional_assocs = {
'site': (Site, 'name'),
'group': (ClusterGroup, 'name'),
'tenant': (Tenant, 'name')
"site": (Site, "name"),
"group": (ClusterGroup, "name"),
"tenant": (Tenant, "name"),
}
for params in clusters:
@ -25,14 +23,14 @@ for params in clusters:
for assoc, details in required_assocs.items():
model, field = details
query = { field: params.pop(assoc) }
query = {field: params.pop(assoc)}
params[assoc] = model.objects.get(**query)
for assoc, details in optional_assocs.items():
if assoc in params:
model, field = details
query = { field: params.pop(assoc) }
query = {field: params.pop(assoc)}
params[assoc] = model.objects.get(**query)

View File

@ -1,45 +1,45 @@
import sys
from dcim.models import Site, Rack, DeviceRole, DeviceType, Device, Platform
from startup_script_utils import *
from dcim.models import Device, DeviceRole, DeviceType, Platform, Rack, Site
from startup_script_utils import load_yaml, pop_custom_fields, set_custom_fields_values
from tenancy.models import Tenant
from virtualization.models import Cluster
devices = load_yaml('/opt/netbox/initializers/devices.yml')
devices = load_yaml("/opt/netbox/initializers/devices.yml")
if devices is None:
sys.exit()
required_assocs = {
'device_role': (DeviceRole, 'name'),
'device_type': (DeviceType, 'model'),
'site': (Site, 'name')
"device_role": (DeviceRole, "name"),
"device_type": (DeviceType, "model"),
"site": (Site, "name"),
}
optional_assocs = {
'tenant': (Tenant, 'name'),
'platform': (Platform, 'name'),
'rack': (Rack, 'name'),
'cluster': (Cluster, 'name')
"tenant": (Tenant, "name"),
"platform": (Platform, "name"),
"rack": (Rack, "name"),
"cluster": (Cluster, "name"),
}
for params in devices:
custom_field_data = pop_custom_fields(params)
# primary ips are handled later in `270_primary_ips.py`
params.pop('primary_ip4', None)
params.pop('primary_ip6', None)
params.pop("primary_ip4", None)
params.pop("primary_ip6", None)
for assoc, details in required_assocs.items():
model, field = details
query = { field: params.pop(assoc) }
query = {field: params.pop(assoc)}
params[assoc] = model.objects.get(**query)
for assoc, details in optional_assocs.items():
if assoc in params:
model, field = details
query = { field: params.pop(assoc) }
query = {field: params.pop(assoc)}
params[assoc] = model.objects.get(**query)

View File

@ -1,45 +1,45 @@
import sys
from dcim.models import Site, Rack, DeviceRole, DeviceType, Device, Platform
from startup_script_utils import *
from dcim.models import Device, DeviceRole, DeviceType, Platform, Rack, Site
from startup_script_utils import load_yaml, pop_custom_fields, set_custom_fields_values
from tenancy.models import Tenant
from virtualization.models import Cluster
devices = load_yaml('/opt/netbox/initializers/devices.yml')
devices = load_yaml("/opt/netbox/initializers/devices.yml")
if devices is None:
sys.exit()
required_assocs = {
'device_role': (DeviceRole, 'name'),
'device_type': (DeviceType, 'model'),
'site': (Site, 'name')
"device_role": (DeviceRole, "name"),
"device_type": (DeviceType, "model"),
"site": (Site, "name"),
}
optional_assocs = {
'tenant': (Tenant, 'name'),
'platform': (Platform, 'name'),
'rack': (Rack, 'name'),
'cluster': (Cluster, 'name')
"tenant": (Tenant, "name"),
"platform": (Platform, "name"),
"rack": (Rack, "name"),
"cluster": (Cluster, "name"),
}
for params in devices:
custom_field_data = pop_custom_fields(params)
# primary ips are handled later in `270_primary_ips.py`
params.pop('primary_ip4', None)
params.pop('primary_ip6', None)
params.pop("primary_ip4", None)
params.pop("primary_ip6", None)
for assoc, details in required_assocs.items():
model, field = details
query = { field: params.pop(assoc) }
query = {field: params.pop(assoc)}
params[assoc] = model.objects.get(**query)
for assoc, details in optional_assocs.items():
if assoc in params:
model, field = details
query = { field: params.pop(assoc) }
query = {field: params.pop(assoc)}
params[assoc] = model.objects.get(**query)

View File

@ -1,8 +1,9 @@
from ipam.models import RIR
from startup_script_utils import load_yaml
import sys
rirs = load_yaml('/opt/netbox/initializers/rirs.yml')
from ipam.models import RIR
from startup_script_utils import load_yaml
rirs = load_yaml("/opt/netbox/initializers/rirs.yml")
if rirs is None:
sys.exit()

View File

@ -1,38 +1,36 @@
import sys
from ipam.models import Aggregate, RIR
from ipam.models import RIR, Aggregate
from netaddr import IPNetwork
from startup_script_utils import *
from startup_script_utils import load_yaml, pop_custom_fields, set_custom_fields_values
from tenancy.models import Tenant
aggregates = load_yaml('/opt/netbox/initializers/aggregates.yml')
aggregates = load_yaml("/opt/netbox/initializers/aggregates.yml")
if aggregates is None:
sys.exit()
required_assocs = {
'rir': (RIR, 'name')
}
required_assocs = {"rir": (RIR, "name")}
optional_assocs = {
'tenant': (Tenant, 'name'),
"tenant": (Tenant, "name"),
}
for params in aggregates:
custom_field_data = pop_custom_fields(params)
params['prefix'] = IPNetwork(params['prefix'])
params["prefix"] = IPNetwork(params["prefix"])
for assoc, details in required_assocs.items():
model, field = details
query = { field: params.pop(assoc) }
query = {field: params.pop(assoc)}
params[assoc] = model.objects.get(**query)
for assoc, details in optional_assocs.items():
if assoc in params:
model, field = details
query = { field: params.pop(assoc) }
query = {field: params.pop(assoc)}
params[assoc] = model.objects.get(**query)

View File

@ -1,8 +1,9 @@
from virtualization.models import ClusterGroup
from startup_script_utils import load_yaml
import sys
cluster_groups = load_yaml('/opt/netbox/initializers/cluster_groups.yml')
from startup_script_utils import load_yaml
from virtualization.models import ClusterGroup
cluster_groups = load_yaml("/opt/netbox/initializers/cluster_groups.yml")
if cluster_groups is None:
sys.exit()

View File

@ -1,17 +1,15 @@
import sys
from ipam.models import RouteTarget
from startup_script_utils import *
from startup_script_utils import load_yaml, pop_custom_fields, set_custom_fields_values
from tenancy.models import Tenant
route_targets = load_yaml('/opt/netbox/initializers/route_targets.yml')
route_targets = load_yaml("/opt/netbox/initializers/route_targets.yml")
if route_targets is None:
sys.exit()
optional_assocs = {
'tenant': (Tenant, 'name')
}
optional_assocs = {"tenant": (Tenant, "name")}
for params in route_targets:
custom_field_data = pop_custom_fields(params)
@ -19,7 +17,7 @@ for params in route_targets:
for assoc, details in optional_assocs.items():
if assoc in params:
model, field = details
query = { field: params.pop(assoc) }
query = {field: params.pop(assoc)}
params[assoc] = model.objects.get(**query)

View File

@ -1,17 +1,15 @@
import sys
from ipam.models import VRF
from startup_script_utils import *
from startup_script_utils import load_yaml, pop_custom_fields, set_custom_fields_values
from tenancy.models import Tenant
vrfs = load_yaml('/opt/netbox/initializers/vrfs.yml')
vrfs = load_yaml("/opt/netbox/initializers/vrfs.yml")
if vrfs is None:
sys.exit()
optional_assocs = {
'tenant': (Tenant, 'name')
}
optional_assocs = {"tenant": (Tenant, "name")}
for params in vrfs:
custom_field_data = pop_custom_fields(params)
@ -19,7 +17,7 @@ for params in vrfs:
for assoc, details in optional_assocs.items():
if assoc in params:
model, field = details
query = { field: params.pop(assoc) }
query = {field: params.pop(assoc)}
params[assoc] = model.objects.get(**query)

View File

@ -1,8 +1,9 @@
from ipam.models import Role
from startup_script_utils import load_yaml
import sys
roles = load_yaml('/opt/netbox/initializers/prefix_vlan_roles.yml')
from ipam.models import Role
from startup_script_utils import load_yaml
roles = load_yaml("/opt/netbox/initializers/prefix_vlan_roles.yml")
if roles is None:
sys.exit()

View File

@ -2,16 +2,14 @@ import sys
from dcim.models import Site
from ipam.models import VLANGroup
from startup_script_utils import *
from startup_script_utils import load_yaml, pop_custom_fields, set_custom_fields_values
vlan_groups = load_yaml('/opt/netbox/initializers/vlan_groups.yml')
vlan_groups = load_yaml("/opt/netbox/initializers/vlan_groups.yml")
if vlan_groups is None:
sys.exit()
optional_assocs = {
'site': (Site, 'name')
}
optional_assocs = {"site": (Site, "name")}
for params in vlan_groups:
custom_field_data = pop_custom_fields(params)
@ -19,7 +17,7 @@ for params in vlan_groups:
for assoc, details in optional_assocs.items():
if assoc in params:
model, field = details
query = { field: params.pop(assoc) }
query = {field: params.pop(assoc)}
params[assoc] = model.objects.get(**query)

View File

@ -1,21 +1,21 @@
import sys
from dcim.models import Site
from ipam.models import VLAN, VLANGroup, Role
from startup_script_utils import *
from ipam.models import VLAN, Role, VLANGroup
from startup_script_utils import load_yaml, pop_custom_fields, set_custom_fields_values
from tenancy.models import Tenant, TenantGroup
vlans = load_yaml('/opt/netbox/initializers/vlans.yml')
vlans = load_yaml("/opt/netbox/initializers/vlans.yml")
if vlans is None:
sys.exit()
optional_assocs = {
'site': (Site, 'name'),
'tenant': (Tenant, 'name'),
'tenant_group': (TenantGroup, 'name'),
'group': (VLANGroup, 'name'),
'role': (Role, 'name')
"site": (Site, "name"),
"tenant": (Tenant, "name"),
"tenant_group": (TenantGroup, "name"),
"group": (VLANGroup, "name"),
"role": (Role, "name"),
}
for params in vlans:
@ -24,7 +24,7 @@ for params in vlans:
for assoc, details in optional_assocs.items():
if assoc in params:
model, field = details
query = { field: params.pop(assoc) }
query = {field: params.pop(assoc)}
params[assoc] = model.objects.get(**query)

View File

@ -1,34 +1,34 @@
import sys
from dcim.models import Site
from ipam.models import Prefix, VLAN, Role, VRF
from ipam.models import VLAN, VRF, Prefix, Role
from netaddr import IPNetwork
from startup_script_utils import *
from startup_script_utils import load_yaml, pop_custom_fields, set_custom_fields_values
from tenancy.models import Tenant, TenantGroup
prefixes = load_yaml('/opt/netbox/initializers/prefixes.yml')
prefixes = load_yaml("/opt/netbox/initializers/prefixes.yml")
if prefixes is None:
sys.exit()
optional_assocs = {
'site': (Site, 'name'),
'tenant': (Tenant, 'name'),
'tenant_group': (TenantGroup, 'name'),
'vlan': (VLAN, 'name'),
'role': (Role, 'name'),
'vrf': (VRF, 'name')
"site": (Site, "name"),
"tenant": (Tenant, "name"),
"tenant_group": (TenantGroup, "name"),
"vlan": (VLAN, "name"),
"role": (Role, "name"),
"vrf": (VRF, "name"),
}
for params in prefixes:
custom_field_data = pop_custom_fields(params)
params['prefix'] = IPNetwork(params['prefix'])
params["prefix"] = IPNetwork(params["prefix"])
for assoc, details in optional_assocs.items():
if assoc in params:
model, field = details
query = { field: params.pop(assoc) }
query = {field: params.pop(assoc)}
params[assoc] = model.objects.get(**query)
prefix, created = Prefix.objects.get_or_create(**params)

View File

@ -1,42 +1,40 @@
import sys
from dcim.models import Platform, DeviceRole
from startup_script_utils import *
from dcim.models import DeviceRole, Platform
from startup_script_utils import load_yaml, pop_custom_fields, set_custom_fields_values
from tenancy.models import Tenant
from virtualization.models import Cluster, VirtualMachine
virtual_machines = load_yaml('/opt/netbox/initializers/virtual_machines.yml')
virtual_machines = load_yaml("/opt/netbox/initializers/virtual_machines.yml")
if virtual_machines is None:
sys.exit()
required_assocs = {
'cluster': (Cluster, 'name')
}
required_assocs = {"cluster": (Cluster, "name")}
optional_assocs = {
'tenant': (Tenant, 'name'),
'platform': (Platform, 'name'),
'role': (DeviceRole, 'name')
"tenant": (Tenant, "name"),
"platform": (Platform, "name"),
"role": (DeviceRole, "name"),
}
for params in virtual_machines:
custom_field_data = pop_custom_fields(params)
# primary ips are handled later in `270_primary_ips.py`
params.pop('primary_ip4', None)
params.pop('primary_ip6', None)
params.pop("primary_ip4", None)
params.pop("primary_ip6", None)
for assoc, details in required_assocs.items():
model, field = details
query = { field: params.pop(assoc) }
query = {field: params.pop(assoc)}
params[assoc] = model.objects.get(**query)
for assoc, details in optional_assocs.items():
if assoc in params:
model, field = details
query = { field: params.pop(assoc) }
query = {field: params.pop(assoc)}
params[assoc] = model.objects.get(**query)

View File

@ -1,23 +1,21 @@
import sys
from startup_script_utils import *
from startup_script_utils import load_yaml, pop_custom_fields, set_custom_fields_values
from virtualization.models import VirtualMachine, VMInterface
interfaces = load_yaml('/opt/netbox/initializers/virtualization_interfaces.yml')
interfaces = load_yaml("/opt/netbox/initializers/virtualization_interfaces.yml")
if interfaces is None:
sys.exit()
required_assocs = {
'virtual_machine': (VirtualMachine, 'name')
}
required_assocs = {"virtual_machine": (VirtualMachine, "name")}
for params in interfaces:
custom_field_data = pop_custom_fields(params)
for assoc, details in required_assocs.items():
model, field = details
query = { field: params.pop(assoc) }
query = {field: params.pop(assoc)}
params[assoc] = model.objects.get(**query)

View File

@ -1,23 +1,21 @@
import sys
from dcim.models import Interface, Device
from startup_script_utils import *
from dcim.models import Device, Interface
from startup_script_utils import load_yaml, pop_custom_fields, set_custom_fields_values
interfaces = load_yaml('/opt/netbox/initializers/dcim_interfaces.yml')
interfaces = load_yaml("/opt/netbox/initializers/dcim_interfaces.yml")
if interfaces is None:
sys.exit()
required_assocs = {
'device': (Device, 'name')
}
required_assocs = {"device": (Device, "name")}
for params in interfaces:
custom_field_data = pop_custom_fields(params)
for assoc, details in required_assocs.items():
model, field = details
query = { field: params.pop(assoc) }
query = {field: params.pop(assoc)}
params[assoc] = model.objects.get(**query)

View File

@ -5,30 +5,32 @@ from django.contrib.contenttypes.models import ContentType
from django.db.models import Q
from ipam.models import VRF, IPAddress
from netaddr import IPNetwork
from startup_script_utils import *
from startup_script_utils import load_yaml, pop_custom_fields, set_custom_fields_values
from tenancy.models import Tenant
from virtualization.models import VirtualMachine, VMInterface
ip_addresses = load_yaml('/opt/netbox/initializers/ip_addresses.yml')
ip_addresses = load_yaml("/opt/netbox/initializers/ip_addresses.yml")
if ip_addresses is None:
sys.exit()
optional_assocs = {
'tenant': (Tenant, 'name'),
'vrf': (VRF, 'name'),
'interface': (None, None)
"tenant": (Tenant, "name"),
"vrf": (VRF, "name"),
"interface": (None, None),
}
vm_interface_ct = ContentType.objects.filter(Q(app_label='virtualization', model='vminterface')).first()
interface_ct = ContentType.objects.filter(Q(app_label='dcim', model='interface')).first()
vm_interface_ct = ContentType.objects.filter(
Q(app_label="virtualization", model="vminterface")
).first()
interface_ct = ContentType.objects.filter(Q(app_label="dcim", model="interface")).first()
for params in ip_addresses:
custom_field_data = pop_custom_fields(params)
vm = params.pop('virtual_machine', None)
device = params.pop('device', None)
params['address'] = IPNetwork(params['address'])
vm = params.pop("virtual_machine", None)
device = params.pop("device", None)
params["address"] = IPNetwork(params["address"])
if vm and device:
print("IP Address can only specify one of the following: virtual_machine or device.")
@ -37,19 +39,19 @@ for params in ip_addresses:
for assoc, details in optional_assocs.items():
if assoc in params:
model, field = details
if assoc == 'interface':
if assoc == "interface":
if vm:
vm_id = VirtualMachine.objects.get(name=vm).id
query = { 'name': params.pop(assoc), "virtual_machine_id": vm_id }
params['assigned_object_type'] = vm_interface_ct
params['assigned_object_id'] = VMInterface.objects.get(**query).id
query = {"name": params.pop(assoc), "virtual_machine_id": vm_id}
params["assigned_object_type"] = vm_interface_ct
params["assigned_object_id"] = VMInterface.objects.get(**query).id
elif device:
dev_id = Device.objects.get(name=device).id
query = { 'name': params.pop(assoc), "device_id": dev_id }
params['assigned_object_type'] = interface_ct
params['assigned_object_id'] = Interface.objects.get(**query).id
query = {"name": params.pop(assoc), "device_id": dev_id}
params["assigned_object_type"] = interface_ct
params["assigned_object_id"] = Interface.objects.get(**query).id
else:
query = { field: params.pop(assoc) }
query = {field: params.pop(assoc)}
params[assoc] = model.objects.get(**query)
ip_address, created = IPAddress.objects.get_or_create(**params)

View File

@ -1,19 +1,21 @@
import sys
from dcim.models import Device
from ipam.models import IPAddress
from virtualization.models import VirtualMachine
from startup_script_utils import load_yaml
import sys
from virtualization.models import VirtualMachine
def link_primary_ip(assets, asset_model):
for params in assets:
primary_ip_fields = set(params) & {'primary_ip4', 'primary_ip6'}
primary_ip_fields = set(params) & {"primary_ip4", "primary_ip6"}
if not primary_ip_fields:
continue
for assoc, details in optional_assocs.items():
if assoc in params:
model, field = details
query = { field: params.pop(assoc) }
query = {field: params.pop(assoc)}
try:
params[assoc] = model.objects.get(**query)
@ -21,19 +23,20 @@ def link_primary_ip(assets, asset_model):
primary_ip_fields -= {assoc}
print(f"⚠️ IP Address '{query[field]}' not found")
asset = asset_model.objects.get(name=params['name'])
asset = asset_model.objects.get(name=params["name"])
for field in primary_ip_fields:
if getattr(asset, field) != params[field]:
setattr(asset, field, params[field])
print(f"🔗 Define primary IP '{params[field].address}' on '{asset.name}'")
asset.save()
devices = load_yaml('/opt/netbox/initializers/devices.yml')
virtual_machines = load_yaml('/opt/netbox/initializers/virtual_machines.yml')
devices = load_yaml("/opt/netbox/initializers/devices.yml")
virtual_machines = load_yaml("/opt/netbox/initializers/virtual_machines.yml")
optional_assocs = {
'primary_ip4': (IPAddress, 'address'),
'primary_ip6': (IPAddress, 'address')
"primary_ip4": (IPAddress, "address"),
"primary_ip6": (IPAddress, "address"),
}
if devices is None and virtual_machines is None:

View File

@ -0,0 +1,33 @@
import sys
from django.contrib.contenttypes.models import ContentType
from extras.models import CustomLink
from startup_script_utils import load_yaml
custom_links = load_yaml("/opt/netbox/initializers/custom_links.yml")
if custom_links is None:
sys.exit()
def get_content_type_id(content_type):
try:
return ContentType.objects.get(model=content_type).id
except ContentType.DoesNotExist:
pass
for link in custom_links:
content_type = link.pop("content_type")
link["content_type_id"] = get_content_type_id(content_type)
if link["content_type_id"] is None:
print(
"⚠️ Unable to create Custom Link '{0}': The content_type '{1}' is unknown".format(
link.name, content_type
)
)
continue
custom_link, created = CustomLink.objects.get_or_create(**link)
if created:
print("🔗 Created Custom Link '{0}'".format(custom_link.name))

View File

@ -1,8 +1,9 @@
from circuits.models import Provider
from startup_script_utils import *
import sys
providers = load_yaml('/opt/netbox/initializers/providers.yml')
from circuits.models import Provider
from startup_script_utils import load_yaml, pop_custom_fields, set_custom_fields_values
providers = load_yaml("/opt/netbox/initializers/providers.yml")
if providers is None:
sys.exit()

View File

@ -1,8 +1,9 @@
from circuits.models import CircuitType
from startup_script_utils import *
import sys
circuit_types = load_yaml('/opt/netbox/initializers/circuit_types.yml')
from circuits.models import CircuitType
from startup_script_utils import load_yaml, pop_custom_fields, set_custom_fields_values
circuit_types = load_yaml("/opt/netbox/initializers/circuit_types.yml")
if circuit_types is None:
sys.exit()

View File

@ -0,0 +1,34 @@
import sys
from django.contrib.contenttypes.models import ContentType
from extras.models import Webhook
from startup_script_utils import load_yaml
webhooks = load_yaml("/opt/netbox/initializers/webhooks.yml")
if webhooks is None:
sys.exit()
def get_content_type_id(hook_name, content_type):
try:
return ContentType.objects.get(model=content_type).id
except ContentType.DoesNotExist as ex:
print("⚠️ Webhook '{0}': The object_type '{1}' is unknown.".format(hook_name, content_type))
raise ex
for hook in webhooks:
obj_types = hook.pop("object_types")
try:
obj_type_ids = [get_content_type_id(hook["name"], obj) for obj in obj_types]
except ContentType.DoesNotExist:
continue
webhook, created = Webhook.objects.get_or_create(**hook)
if created:
webhook.content_types.set(obj_type_ids)
webhook.save()
print("🪝 Created Webhook {0}".format(webhook.name))

View File

@ -1,35 +1,31 @@
from circuits.models import Circuit, Provider, CircuitType
from tenancy.models import Tenant
from startup_script_utils import *
import sys
circuits = load_yaml('/opt/netbox/initializers/circuits.yml')
from circuits.models import Circuit, CircuitType, Provider
from startup_script_utils import load_yaml, pop_custom_fields, set_custom_fields_values
from tenancy.models import Tenant
circuits = load_yaml("/opt/netbox/initializers/circuits.yml")
if circuits is None:
sys.exit()
required_assocs = {
'provider': (Provider, 'name'),
'type': (CircuitType, 'name')
}
required_assocs = {"provider": (Provider, "name"), "type": (CircuitType, "name")}
optional_assocs = {
'tenant': (Tenant, 'name')
}
optional_assocs = {"tenant": (Tenant, "name")}
for params in circuits:
custom_field_data = pop_custom_fields(params)
for assoc, details in required_assocs.items():
model, field = details
query = { field: params.pop(assoc) }
query = {field: params.pop(assoc)}
params[assoc] = model.objects.get(**query)
for assoc, details in optional_assocs.items():
if assoc in params:
model, field = details
query = { field: params.pop(assoc) }
query = {field: params.pop(assoc)}
params[assoc] = model.objects.get(**query)

View File

@ -1,8 +1,9 @@
from secrets.models import SecretRole
from startup_script_utils import load_yaml
import sys
from secrets.models import SecretRole
secret_roles = load_yaml('/opt/netbox/initializers/secret_roles.yml')
from startup_script_utils import load_yaml
secret_roles = load_yaml("/opt/netbox/initializers/secret_roles.yml")
if secret_roles is None:
sys.exit()

View File

@ -1,17 +1,18 @@
from ipam.models import Service
from dcim.models import Device
from virtualization.models import VirtualMachine
from startup_script_utils import load_yaml
import sys
services = load_yaml('/opt/netbox/initializers/services.yml')
from dcim.models import Device
from ipam.models import Service
from startup_script_utils import load_yaml
from virtualization.models import VirtualMachine
services = load_yaml("/opt/netbox/initializers/services.yml")
if services is None:
sys.exit()
optional_assocs = {
'device': (Device, 'name'),
'virtual_machine': (VirtualMachine, 'name')
"device": (Device, "name"),
"virtual_machine": (VirtualMachine, "name"),
}
for params in services:
@ -19,7 +20,7 @@ for params in services:
for assoc, details in optional_assocs.items():
if assoc in params:
model, field = details
query = { field: params.pop(assoc) }
query = {field: params.pop(assoc)}
params[assoc] = model.objects.get(**query)

View File

@ -1,35 +1,30 @@
import sys
from dcim.models import Site, RackGroup, PowerPanel
from startup_script_utils import *
from tenancy.models import Tenant
from dcim.models import PowerPanel, RackGroup, Site
from startup_script_utils import load_yaml, pop_custom_fields, set_custom_fields_values
power_panels = load_yaml('/opt/netbox/initializers/power_panels.yml')
power_panels = load_yaml("/opt/netbox/initializers/power_panels.yml")
if power_panels is None:
sys.exit()
required_assocs = {
'site': (Site, 'name')
}
required_assocs = {"site": (Site, "name")}
optional_assocs = {
'rack_group': (RackGroup, 'name')
}
optional_assocs = {"rack_group": (RackGroup, "name")}
for params in power_panels:
custom_field_data = pop_custom_fields(params)
for assoc, details in required_assocs.items():
model, field = details
query = { field: params.pop(assoc) }
query = {field: params.pop(assoc)}
params[assoc] = model.objects.get(**query)
for assoc, details in optional_assocs.items():
if assoc in params:
model, field = details
query = { field: params.pop(assoc) }
query = {field: params.pop(assoc)}
params[assoc] = model.objects.get(**query)

View File

@ -1,35 +1,30 @@
import sys
from dcim.models import Rack, PowerPanel, PowerFeed
from startup_script_utils import *
from tenancy.models import Tenant
from dcim.models import PowerFeed, PowerPanel, Rack
from startup_script_utils import load_yaml, pop_custom_fields, set_custom_fields_values
power_feeds = load_yaml('/opt/netbox/initializers/power_feeds.yml')
power_feeds = load_yaml("/opt/netbox/initializers/power_feeds.yml")
if power_feeds is None:
sys.exit()
required_assocs = {
'power_panel': (PowerPanel, 'name')
}
required_assocs = {"power_panel": (PowerPanel, "name")}
optional_assocs = {
'rack': (Rack, 'name')
}
optional_assocs = {"rack": (Rack, "name")}
for params in power_feeds:
custom_field_data = pop_custom_fields(params)
for assoc, details in required_assocs.items():
model, field = details
query = { field: params.pop(assoc) }
query = {field: params.pop(assoc)}
params[assoc] = model.objects.get(**query)
for assoc, details in optional_assocs.items():
if assoc in params:
model, field = details
query = { field: params.pop(assoc) }
query = {field: params.pop(assoc)}
params[assoc] = model.objects.get(**query)

View File

@ -2,22 +2,24 @@
import runpy
from os import scandir
from os.path import dirname, abspath
from os.path import abspath, dirname
this_dir = dirname(abspath(__file__))
def filename(f):
return f.name
with scandir(this_dir) as it:
for f in sorted(it, key = filename):
for f in sorted(it, key=filename):
if not f.is_file():
continue
if f.name.startswith('__'):
if f.name.startswith("__"):
continue
if not f.name.endswith('.py'):
if not f.name.endswith(".py"):
continue
print(f"▶️ Running the startup script {f.path}")

View File

@ -1,3 +1,3 @@
from .custom_fields import pop_custom_fields, set_custom_fields_values
from .load_yaml import load_yaml
from .permissions import set_permissions
from .custom_fields import set_custom_fields_values, pop_custom_fields

View File

@ -5,11 +5,12 @@ def set_custom_fields_values(entity, custom_field_data):
entity.custom_field_data = custom_field_data
return entity.save()
def pop_custom_fields(params):
if 'custom_field_data' in params:
return params.pop('custom_field_data')
elif 'custom_fields' in params:
if "custom_field_data" in params:
return params.pop("custom_field_data")
elif "custom_fields" in params:
print("⚠️ Please rename 'custom_fields' to 'custom_field_data'!")
return params.pop('custom_fields')
return params.pop("custom_fields")
return None

View File

@ -1,4 +1,5 @@
from pathlib import Path
from ruamel.yaml import YAML

View File

@ -9,7 +9,11 @@ def set_permissions(subject, permission_filters):
if "*" in permission_filter:
permission_filter_regex = "^" + permission_filter.replace("*", ".*") + "$"
permissions = Permission.objects.filter(codename__iregex=permission_filter_regex)
print(" ⚿ Granting", permissions.count(), "permissions matching '" + permission_filter + "'")
print(
" ⚿ Granting",
permissions.count(),
"permissions matching '" + permission_filter + "'",
)
else:
permissions = Permission.objects.filter(codename=permission_filter)
print(" ⚿ Granting permission", permission_filter)

View File

@ -1,5 +1,5 @@
#!/bin/bash
# Runs the original Netbox unit tests and tests whether all initializers work.
# Runs the original NetBox unit tests and tests whether all initializers work.
# Usage:
# ./test.sh latest
# ./test.sh v2.9.7
@ -28,7 +28,7 @@ if [ -z "${IMAGE}" ]; then
echo "⚠️ No image defined"
if [ -z "${DEBUG}" ]; then
exit 1;
exit 1
else
echo "⚠️ Would 'exit 1' here, but DEBUG is '${DEBUG}'."
fi
@ -49,13 +49,13 @@ test_setup() {
(
cd initializers
for script in *.yml; do
sed -E 's/^# //' "${script}" > "../${INITIALIZERS_DIR}/${script}"
sed -E 's/^# //' "${script}" >"../${INITIALIZERS_DIR}/${script}"
done
)
}
test_netbox_unit_tests() {
echo "⏱ Running Netbox Unit Tests"
echo "⏱ Running NetBox Unit Tests"
SKIP_STARTUP_SCRIPTS=true $doco run --rm netbox ./manage.py test
}