commit
b45934cd9e
|
@ -17,7 +17,9 @@ jobs:
|
||||||
with:
|
with:
|
||||||
# Full git history is needed to get a proper list of changed files within `super-linter`
|
# Full git history is needed to get a proper list of changed files within `super-linter`
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- uses: actions/setup-python@v3
|
- uses: actions/setup-python@v4
|
||||||
|
with:
|
||||||
|
python-version: '3.9'
|
||||||
- name: Lint Code Base
|
- name: Lint Code Base
|
||||||
uses: github/super-linter@v4
|
uses: github/super-linter@v4
|
||||||
env:
|
env:
|
||||||
|
|
|
@ -42,7 +42,7 @@ jobs:
|
||||||
# docker.io
|
# docker.io
|
||||||
-
|
-
|
||||||
name: Login to docker.io
|
name: Login to docker.io
|
||||||
uses: docker/login-action@v1
|
uses: docker/login-action@v2
|
||||||
with:
|
with:
|
||||||
registry: docker.io
|
registry: docker.io
|
||||||
username: ${{ secrets.dockerhub_username }}
|
username: ${{ secrets.dockerhub_username }}
|
||||||
|
@ -56,7 +56,7 @@ jobs:
|
||||||
# quay.io
|
# quay.io
|
||||||
-
|
-
|
||||||
name: Login to Quay.io
|
name: Login to Quay.io
|
||||||
uses: docker/login-action@v1
|
uses: docker/login-action@v2
|
||||||
with:
|
with:
|
||||||
registry: quay.io
|
registry: quay.io
|
||||||
username: ${{ secrets.quayio_username }}
|
username: ${{ secrets.quayio_username }}
|
||||||
|
@ -72,7 +72,7 @@ jobs:
|
||||||
# ghcr.io
|
# ghcr.io
|
||||||
-
|
-
|
||||||
name: Login to GitHub Container Registry
|
name: Login to GitHub Container Registry
|
||||||
uses: docker/login-action@v1
|
uses: docker/login-action@v2
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: ${{ github.repository_owner }}
|
username: ${{ github.repository_owner }}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
ignored:
|
ignored:
|
||||||
- DL3006
|
- DL3006
|
||||||
- DL3018
|
- DL3008
|
||||||
|
- DL3003
|
||||||
|
|
122
Dockerfile
122
Dockerfile
|
@ -1,45 +1,27 @@
|
||||||
ARG FROM
|
ARG FROM
|
||||||
FROM ${FROM} as builder
|
FROM ${FROM} as builder
|
||||||
|
|
||||||
RUN apk add --no-cache \
|
RUN export DEBIAN_FRONTEND=noninteractive \
|
||||||
bash \
|
&& apt-get update -qq \
|
||||||
build-base \
|
&& apt-get upgrade \
|
||||||
cargo \
|
--yes -qq --no-install-recommends \
|
||||||
|
&& apt-get install \
|
||||||
|
--yes -qq --no-install-recommends \
|
||||||
|
build-essential \
|
||||||
ca-certificates \
|
ca-certificates \
|
||||||
cmake \
|
libldap-dev \
|
||||||
cyrus-sasl-dev \
|
libpq-dev \
|
||||||
git \
|
libsasl2-dev \
|
||||||
graphviz \
|
libssl-dev \
|
||||||
jpeg-dev \
|
|
||||||
libevent-dev \
|
|
||||||
libffi-dev \
|
|
||||||
libxslt-dev \
|
|
||||||
make \
|
|
||||||
musl-dev \
|
|
||||||
openldap-dev \
|
|
||||||
postgresql-dev \
|
|
||||||
py3-pip \
|
|
||||||
python3-dev \
|
python3-dev \
|
||||||
&& python3 -m venv /opt/netbox/venv \
|
python3-pip \
|
||||||
&& /opt/netbox/venv/bin/python3 -m pip install --upgrade \
|
python3-venv \
|
||||||
|
&& python3 -m venv /opt/netbox/venv \
|
||||||
|
&& /opt/netbox/venv/bin/python3 -m pip install --upgrade \
|
||||||
pip \
|
pip \
|
||||||
setuptools \
|
setuptools \
|
||||||
wheel
|
wheel
|
||||||
|
|
||||||
# Build libcrc32c for google-crc32c python module
|
|
||||||
RUN git clone https://github.com/google/crc32c \
|
|
||||||
&& cd crc32c \
|
|
||||||
&& git submodule update --init --recursive \
|
|
||||||
&& mkdir build \
|
|
||||||
&& cd build \
|
|
||||||
&& cmake \
|
|
||||||
-DCMAKE_BUILD_TYPE=Release \
|
|
||||||
-DCRC32C_BUILD_TESTS=no \
|
|
||||||
-DCRC32C_BUILD_BENCHMARKS=no \
|
|
||||||
-DBUILD_SHARED_LIBS=yes \
|
|
||||||
.. \
|
|
||||||
&& make all install
|
|
||||||
|
|
||||||
ARG NETBOX_PATH
|
ARG NETBOX_PATH
|
||||||
COPY ${NETBOX_PATH}/requirements.txt requirements-container.txt /
|
COPY ${NETBOX_PATH}/requirements.txt requirements-container.txt /
|
||||||
RUN /opt/netbox/venv/bin/pip install \
|
RUN /opt/netbox/venv/bin/pip install \
|
||||||
|
@ -53,35 +35,37 @@ RUN /opt/netbox/venv/bin/pip install \
|
||||||
ARG FROM
|
ARG FROM
|
||||||
FROM ${FROM} as main
|
FROM ${FROM} as main
|
||||||
|
|
||||||
RUN apk add --no-cache \
|
RUN export DEBIAN_FRONTEND=noninteractive \
|
||||||
bash \
|
&& apt-get update -qq \
|
||||||
|
&& apt-get upgrade \
|
||||||
|
--yes -qq --no-install-recommends \
|
||||||
|
&& apt-get install \
|
||||||
|
--yes -qq --no-install-recommends \
|
||||||
ca-certificates \
|
ca-certificates \
|
||||||
curl \
|
curl \
|
||||||
graphviz \
|
libpq5 \
|
||||||
libevent \
|
|
||||||
libffi \
|
|
||||||
libjpeg-turbo \
|
|
||||||
libxslt \
|
|
||||||
openssl \
|
openssl \
|
||||||
postgresql-client \
|
|
||||||
postgresql-libs \
|
|
||||||
py3-pip \
|
|
||||||
python3 \
|
python3 \
|
||||||
|
python3-distutils \
|
||||||
tini \
|
tini \
|
||||||
unit \
|
&& curl -sL https://nginx.org/keys/nginx_signing.key \
|
||||||
unit-python3
|
> /etc/apt/trusted.gpg.d/nginx.asc && \
|
||||||
|
echo "deb https://packages.nginx.org/unit/debian/ bullseye unit" \
|
||||||
|
> /etc/apt/sources.list.d/unit.list \
|
||||||
|
&& apt-get update -qq \
|
||||||
|
&& apt-get install \
|
||||||
|
--yes -qq --no-install-recommends \
|
||||||
|
unit=1.27.0-1~bullseye \
|
||||||
|
unit-python3.9=1.27.0-1~bullseye \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
WORKDIR /opt
|
|
||||||
|
|
||||||
COPY --from=builder /usr/local/lib/libcrc32c.* /usr/local/lib/
|
|
||||||
COPY --from=builder /usr/local/include/crc32c /usr/local/include
|
|
||||||
COPY --from=builder /usr/local/lib/cmake/Crc32c /usr/local/lib/cmake/
|
|
||||||
COPY --from=builder /opt/netbox/venv /opt/netbox/venv
|
COPY --from=builder /opt/netbox/venv /opt/netbox/venv
|
||||||
|
|
||||||
ARG NETBOX_PATH
|
ARG NETBOX_PATH
|
||||||
COPY ${NETBOX_PATH} /opt/netbox
|
COPY ${NETBOX_PATH} /opt/netbox
|
||||||
|
|
||||||
COPY docker/configuration.docker.py /opt/netbox/netbox/netbox/configuration.py
|
COPY docker/configuration.docker.py /opt/netbox/netbox/netbox/configuration.py
|
||||||
|
COPY docker/ldap_config.docker.py /opt/netbox/netbox/netbox/ldap_config.py
|
||||||
COPY docker/docker-entrypoint.sh /opt/netbox/docker-entrypoint.sh
|
COPY docker/docker-entrypoint.sh /opt/netbox/docker-entrypoint.sh
|
||||||
COPY docker/housekeeping.sh /opt/netbox/housekeeping.sh
|
COPY docker/housekeeping.sh /opt/netbox/housekeeping.sh
|
||||||
COPY docker/launch-netbox.sh /opt/netbox/launch-netbox.sh
|
COPY docker/launch-netbox.sh /opt/netbox/launch-netbox.sh
|
||||||
|
@ -101,26 +85,15 @@ RUN mkdir -p static /opt/unit/state/ /opt/unit/tmp/ \
|
||||||
--config-file /opt/netbox/mkdocs.yml --site-dir /opt/netbox/netbox/project-static/docs/ \
|
--config-file /opt/netbox/mkdocs.yml --site-dir /opt/netbox/netbox/project-static/docs/ \
|
||||||
&& SECRET_KEY="dummy" /opt/netbox/venv/bin/python /opt/netbox/netbox/manage.py collectstatic --no-input
|
&& SECRET_KEY="dummy" /opt/netbox/venv/bin/python /opt/netbox/netbox/manage.py collectstatic --no-input
|
||||||
|
|
||||||
ENTRYPOINT [ "/sbin/tini", "--" ]
|
ENV LANG=C.UTF-8
|
||||||
|
ENTRYPOINT [ "/usr/bin/tini", "--" ]
|
||||||
|
|
||||||
CMD [ "/opt/netbox/docker-entrypoint.sh", "/opt/netbox/launch-netbox.sh" ]
|
CMD [ "/opt/netbox/docker-entrypoint.sh", "/opt/netbox/launch-netbox.sh" ]
|
||||||
|
|
||||||
LABEL ORIGINAL_TAG="" \
|
LABEL netbox.original-tag="" \
|
||||||
NETBOX_GIT_BRANCH="" \
|
netbox.git-branch="" \
|
||||||
NETBOX_GIT_REF="" \
|
netbox.git-ref="" \
|
||||||
NETBOX_GIT_URL="" \
|
netbox.git-url="" \
|
||||||
# See http://label-schema.org/rc1/#build-time-labels
|
|
||||||
# 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.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" \
|
|
||||||
org.label-schema.vcs-url="https://github.com/netbox-community/netbox-docker.git" \
|
|
||||||
org.label-schema.vcs-ref="" \
|
|
||||||
org.label-schema.version="snapshot" \
|
|
||||||
# See https://github.com/opencontainers/image-spec/blob/master/annotations.md#pre-defined-annotation-keys
|
# See https://github.com/opencontainers/image-spec/blob/master/annotations.md#pre-defined-annotation-keys
|
||||||
org.opencontainers.image.created="" \
|
org.opencontainers.image.created="" \
|
||||||
org.opencontainers.image.title="NetBox Docker" \
|
org.opencontainers.image.title="NetBox Docker" \
|
||||||
|
@ -132,17 +105,4 @@ LABEL ORIGINAL_TAG="" \
|
||||||
org.opencontainers.image.documentation="https://github.com/netbox-community/netbox-docker/wiki" \
|
org.opencontainers.image.documentation="https://github.com/netbox-community/netbox-docker/wiki" \
|
||||||
org.opencontainers.image.source="https://github.com/netbox-community/netbox-docker.git" \
|
org.opencontainers.image.source="https://github.com/netbox-community/netbox-docker.git" \
|
||||||
org.opencontainers.image.revision="" \
|
org.opencontainers.image.revision="" \
|
||||||
org.opencontainers.image.version="snapshot"
|
org.opencontainers.image.version=""
|
||||||
|
|
||||||
#####
|
|
||||||
## LDAP specific configuration
|
|
||||||
#####
|
|
||||||
|
|
||||||
FROM main as ldap
|
|
||||||
|
|
||||||
RUN apk add --no-cache \
|
|
||||||
libsasl \
|
|
||||||
libldap \
|
|
||||||
util-linux
|
|
||||||
|
|
||||||
COPY docker/ldap_config.docker.py /opt/netbox/netbox/netbox/ldap_config.py
|
|
||||||
|
|
45
build.sh
45
build.sh
|
@ -49,10 +49,10 @@ if [ "${1}x" == "x" ] || [ "${1}" == "--help" ] || [ "${1}" == "-h" ]; then
|
||||||
echo " DOCKERFILE The name of Dockerfile to use."
|
echo " DOCKERFILE The name of Dockerfile to use."
|
||||||
echo " Default: Dockerfile"
|
echo " Default: Dockerfile"
|
||||||
echo " DOCKER_FROM The base image to use."
|
echo " DOCKER_FROM The base image to use."
|
||||||
echo " Default: 'alpine:3.14'"
|
echo " Default: 'debian:11-slim'"
|
||||||
echo " DOCKER_TARGET A specific target to build."
|
echo " DOCKER_TARGET A specific target to build."
|
||||||
echo " It's currently not possible to pass multiple targets."
|
echo " It's currently not possible to pass multiple targets."
|
||||||
echo " Default: main ldap"
|
echo " Default: main"
|
||||||
echo " HTTP_PROXY The proxy to use for http requests."
|
echo " HTTP_PROXY The proxy to use for http requests."
|
||||||
echo " Example: http://proxy.domain.tld:3128"
|
echo " Example: http://proxy.domain.tld:3128"
|
||||||
echo " Default: undefined"
|
echo " Default: undefined"
|
||||||
|
@ -170,7 +170,7 @@ fi
|
||||||
# Determining the value for DOCKER_FROM
|
# Determining the value for DOCKER_FROM
|
||||||
###
|
###
|
||||||
if [ -z "$DOCKER_FROM" ]; then
|
if [ -z "$DOCKER_FROM" ]; then
|
||||||
DOCKER_FROM="alpine:3.14"
|
DOCKER_FROM="debian:11-slim"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
###
|
###
|
||||||
|
@ -178,7 +178,7 @@ fi
|
||||||
###
|
###
|
||||||
BUILD_DATE="$(date -u '+%Y-%m-%dT%H:%M+00:00')"
|
BUILD_DATE="$(date -u '+%Y-%m-%dT%H:%M+00:00')"
|
||||||
|
|
||||||
if [ -d ".git" ]; then
|
if [ -d ".git" ] && [ -z "${SKIP_GIT}" ]; then
|
||||||
GIT_REF="$(git rev-parse HEAD)"
|
GIT_REF="$(git rev-parse HEAD)"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
@ -186,7 +186,7 @@ fi
|
||||||
PROJECT_VERSION="${PROJECT_VERSION-$(sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' VERSION)}"
|
PROJECT_VERSION="${PROJECT_VERSION-$(sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' VERSION)}"
|
||||||
|
|
||||||
# Get the Git information from the netbox directory
|
# Get the Git information from the netbox directory
|
||||||
if [ -d "${NETBOX_PATH}/.git" ]; then
|
if [ -d "${NETBOX_PATH}/.git" ] && [ -z "${SKIP_GIT}" ]; then
|
||||||
NETBOX_GIT_REF=$(
|
NETBOX_GIT_REF=$(
|
||||||
cd "${NETBOX_PATH}"
|
cd "${NETBOX_PATH}"
|
||||||
git rev-parse HEAD
|
git rev-parse HEAD
|
||||||
|
@ -222,7 +222,7 @@ esac
|
||||||
###
|
###
|
||||||
# Determine targets to build
|
# Determine targets to build
|
||||||
###
|
###
|
||||||
DEFAULT_DOCKER_TARGETS=("main" "ldap")
|
DEFAULT_DOCKER_TARGETS=("main")
|
||||||
DOCKER_TARGETS=("${DOCKER_TARGET:-"${DEFAULT_DOCKER_TARGETS[@]}"}")
|
DOCKER_TARGETS=("${DOCKER_TARGET:-"${DEFAULT_DOCKER_TARGETS[@]}"}")
|
||||||
echo "🏭 Building the following targets:" "${DOCKER_TARGETS[@]}"
|
echo "🏭 Building the following targets:" "${DOCKER_TARGETS[@]}"
|
||||||
|
|
||||||
|
@ -277,9 +277,9 @@ for DOCKER_TARGET in "${DOCKER_TARGETS[@]}"; do
|
||||||
###
|
###
|
||||||
# Checking if the build is necessary,
|
# Checking if the build is necessary,
|
||||||
# meaning build only if one of those values changed:
|
# meaning build only if one of those values changed:
|
||||||
# - Python base image digest (Label: PYTHON_BASE_DIGEST)
|
# - base image digest
|
||||||
# - netbox git ref (Label: NETBOX_GIT_REF)
|
# - netbox git ref (Label: netbox.git-ref)
|
||||||
# - netbox-docker git ref (Label: org.label-schema.vcs-ref)
|
# - netbox-docker git ref (Label: org.opencontainers.image.revision)
|
||||||
###
|
###
|
||||||
# Load information from registry (only for docker.io)
|
# Load information from registry (only for docker.io)
|
||||||
SHOULD_BUILD="false"
|
SHOULD_BUILD="false"
|
||||||
|
@ -295,14 +295,14 @@ for DOCKER_TARGET in "${DOCKER_TARGETS[@]}"; do
|
||||||
# Need to use "library/..." for images the have no two part name
|
# Need to use "library/..." for images the have no two part name
|
||||||
DOCKER_FROM_SPLIT[0]="library/${DOCKER_FROM_SPLIT[0]}"
|
DOCKER_FROM_SPLIT[0]="library/${DOCKER_FROM_SPLIT[0]}"
|
||||||
fi
|
fi
|
||||||
PYTHON_LAST_LAYER=$(get_image_last_layer "${DOCKER_FROM_SPLIT[0]}" "${DOCKER_FROM_SPLIT[1]}")
|
BASE_LAST_LAYER=$(get_image_last_layer "${DOCKER_FROM_SPLIT[0]}" "${DOCKER_FROM_SPLIT[1]}")
|
||||||
mapfile -t IMAGES_LAYERS_OLD < <(get_image_layers "${DOCKER_ORG}"/"${DOCKER_REPO}" "${TAG}")
|
mapfile -t IMAGES_LAYERS_OLD < <(get_image_layers "${DOCKER_ORG}"/"${DOCKER_REPO}" "${TAG}")
|
||||||
NETBOX_GIT_REF_OLD=$(get_image_label NETBOX_GIT_REF "${DOCKER_ORG}"/"${DOCKER_REPO}" "${TAG}")
|
NETBOX_GIT_REF_OLD=$(get_image_label netbox.git-ref "${DOCKER_ORG}"/"${DOCKER_REPO}" "${TAG}")
|
||||||
GIT_REF_OLD=$(get_image_label org.label-schema.vcs-ref "${DOCKER_ORG}"/"${DOCKER_REPO}" "${TAG}")
|
GIT_REF_OLD=$(get_image_label org.opencontainers.image.revision "${DOCKER_ORG}"/"${DOCKER_REPO}" "${TAG}")
|
||||||
|
|
||||||
if ! printf '%s\n' "${IMAGES_LAYERS_OLD[@]}" | grep -q -P "^${PYTHON_LAST_LAYER}\$"; then
|
if ! printf '%s\n' "${IMAGES_LAYERS_OLD[@]}" | grep -q -P "^${BASE_LAST_LAYER}\$"; then
|
||||||
SHOULD_BUILD="true"
|
SHOULD_BUILD="true"
|
||||||
BUILD_REASON="${BUILD_REASON} alpine"
|
BUILD_REASON="${BUILD_REASON} debian"
|
||||||
fi
|
fi
|
||||||
if [ "${NETBOX_GIT_REF}" != "${NETBOX_GIT_REF_OLD}" ]; then
|
if [ "${NETBOX_GIT_REF}" != "${NETBOX_GIT_REF_OLD}" ]; then
|
||||||
SHOULD_BUILD="true"
|
SHOULD_BUILD="true"
|
||||||
|
@ -335,30 +335,25 @@ for DOCKER_TARGET in "${DOCKER_TARGETS[@]}"; do
|
||||||
|
|
||||||
# --label
|
# --label
|
||||||
DOCKER_BUILD_ARGS+=(
|
DOCKER_BUILD_ARGS+=(
|
||||||
--label "ORIGINAL_TAG=${TARGET_DOCKER_TAG_PROJECT}"
|
--label "netbox.original-tag=${TARGET_DOCKER_TAG_PROJECT}"
|
||||||
|
|
||||||
--label "org.label-schema.build-date=${BUILD_DATE}"
|
|
||||||
--label "org.opencontainers.image.created=${BUILD_DATE}"
|
--label "org.opencontainers.image.created=${BUILD_DATE}"
|
||||||
|
|
||||||
--label "org.label-schema.version=${PROJECT_VERSION}"
|
|
||||||
--label "org.opencontainers.image.version=${PROJECT_VERSION}"
|
--label "org.opencontainers.image.version=${PROJECT_VERSION}"
|
||||||
)
|
)
|
||||||
if [ -d ".git" ]; then
|
if [ -d ".git" ]; then
|
||||||
DOCKER_BUILD_ARGS+=(
|
DOCKER_BUILD_ARGS+=(
|
||||||
--label "org.label-schema.vcs-ref=${GIT_REF}"
|
|
||||||
--label "org.opencontainers.image.revision=${GIT_REF}"
|
--label "org.opencontainers.image.revision=${GIT_REF}"
|
||||||
)
|
)
|
||||||
fi
|
fi
|
||||||
if [ -d "${NETBOX_PATH}/.git" ]; then
|
if [ -d "${NETBOX_PATH}/.git" ]; then
|
||||||
DOCKER_BUILD_ARGS+=(
|
DOCKER_BUILD_ARGS+=(
|
||||||
--label "NETBOX_GIT_BRANCH=${NETBOX_GIT_BRANCH}"
|
--label "netbox.git-branch=${NETBOX_GIT_BRANCH}"
|
||||||
--label "NETBOX_GIT_REF=${NETBOX_GIT_REF}"
|
--label "netbox.git-ref=${NETBOX_GIT_REF}"
|
||||||
--label "NETBOX_GIT_URL=${NETBOX_GIT_URL}"
|
--label "netbox.git-url=${NETBOX_GIT_URL}"
|
||||||
)
|
)
|
||||||
fi
|
fi
|
||||||
if [ -n "${BUILD_REASON}" ]; then
|
if [ -n "${BUILD_REASON}" ]; then
|
||||||
BUILD_REASON=$(sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' <<<"$BUILD_REASON")
|
BUILD_REASON=$(sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' <<<"$BUILD_REASON")
|
||||||
DOCKER_BUILD_ARGS+=(--label "BUILD_REASON=${BUILD_REASON}")
|
DOCKER_BUILD_ARGS+=(--label "netbox.build-reason=${BUILD_REASON}")
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# --build-arg
|
# --build-arg
|
||||||
|
@ -385,7 +380,7 @@ for DOCKER_TARGET in "${DOCKER_TARGETS[@]}"; do
|
||||||
$DRY docker build "${DOCKER_BUILD_ARGS[@]}" .
|
$DRY docker build "${DOCKER_BUILD_ARGS[@]}" .
|
||||||
echo "✅ Finished building the Docker images '${TARGET_DOCKER_TAG_PROJECT}'"
|
echo "✅ Finished building the Docker images '${TARGET_DOCKER_TAG_PROJECT}'"
|
||||||
echo "🔎 Inspecting labels on '${TARGET_DOCKER_TAG_PROJECT}'"
|
echo "🔎 Inspecting labels on '${TARGET_DOCKER_TAG_PROJECT}'"
|
||||||
$DRY docker inspect "${TARGET_DOCKER_TAG_PROJECT}" --format "{{json .Config.Labels}}"
|
$DRY docker inspect "${TARGET_DOCKER_TAG_PROJECT}" --format "{{json .Config.Labels}}" | jq
|
||||||
else
|
else
|
||||||
echo "Build skipped because sources didn't change"
|
echo "Build skipped because sources didn't change"
|
||||||
echo "::set-output name=skipped::true"
|
echo "::set-output name=skipped::true"
|
||||||
|
|
|
@ -120,6 +120,11 @@ CORS_ORIGIN_ALLOW_ALL = environ.get('CORS_ORIGIN_ALLOW_ALL', 'False').lower() ==
|
||||||
CORS_ORIGIN_WHITELIST = list(filter(None, environ.get('CORS_ORIGIN_WHITELIST', 'https://localhost').split(' ')))
|
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(' ')))]
|
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
|
# Set to True to enable server debugging. WARNING: Debugging introduces a substantial performance penalty and may reveal
|
||||||
# sensitive information about your installation. Only enable debugging while performing testing. Never enable debugging
|
# sensitive information about your installation. Only enable debugging while performing testing. Never enable debugging
|
||||||
# on a production system.
|
# on a production system.
|
||||||
|
@ -165,6 +170,9 @@ LOGIN_TIMEOUT = int(environ.get('LOGIN_TIMEOUT', 1209600))
|
||||||
# Setting this to True will display a "maintenance mode" banner at the top of every page.
|
# Setting this to True will display a "maintenance mode" banner at the top of every page.
|
||||||
MAINTENANCE_MODE = environ.get('MAINTENANCE_MODE', 'False').lower() == 'true'
|
MAINTENANCE_MODE = environ.get('MAINTENANCE_MODE', 'False').lower() == 'true'
|
||||||
|
|
||||||
|
# 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.
|
# An API consumer can request an arbitrary number of objects =by appending the "limit" parameter to the URL (e.g.
|
||||||
# "?limit=1000"). This setting defines the maximum limit. Setting it to 0 or None will allow an API consumer to request
|
# "?limit=1000"). This setting defines the maximum limit. Setting it to 0 or None will allow an API consumer to request
|
||||||
# all objects by specifying "?limit=0".
|
# all objects by specifying "?limit=0".
|
||||||
|
|
|
@ -21,14 +21,14 @@ services:
|
||||||
image: postgres:14-alpine
|
image: postgres:14-alpine
|
||||||
env_file: env/postgres.env
|
env_file: env/postgres.env
|
||||||
redis:
|
redis:
|
||||||
image: redis:6-alpine
|
image: redis:7-alpine
|
||||||
command:
|
command:
|
||||||
- sh
|
- sh
|
||||||
- -c # this is to evaluate the $REDIS_PASSWORD from the env
|
- -c # this is to evaluate the $REDIS_PASSWORD from the env
|
||||||
- redis-server --appendonly yes --requirepass $$REDIS_PASSWORD ## $$ because of docker-compose
|
- redis-server --appendonly yes --requirepass $$REDIS_PASSWORD ## $$ because of docker-compose
|
||||||
env_file: env/redis.env
|
env_file: env/redis.env
|
||||||
redis-cache:
|
redis-cache:
|
||||||
image: redis:6-alpine
|
image: redis:7-alpine
|
||||||
command:
|
command:
|
||||||
- sh
|
- sh
|
||||||
- -c # this is to evaluate the $REDIS_PASSWORD from the env
|
- -c # this is to evaluate the $REDIS_PASSWORD from the env
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
version: '3.4'
|
version: '3.4'
|
||||||
services:
|
services:
|
||||||
netbox: &netbox
|
netbox: &netbox
|
||||||
image: netboxcommunity/netbox:${VERSION-v3.2-1.6.1}
|
image: netboxcommunity/netbox:${VERSION-v3.2-2.0.0}
|
||||||
depends_on:
|
depends_on:
|
||||||
- postgres
|
- postgres
|
||||||
- redis
|
- redis
|
||||||
|
@ -42,7 +42,7 @@ services:
|
||||||
|
|
||||||
# redis
|
# redis
|
||||||
redis:
|
redis:
|
||||||
image: redis:6-alpine
|
image: redis:7-alpine
|
||||||
command:
|
command:
|
||||||
- sh
|
- sh
|
||||||
- -c # this is to evaluate the $REDIS_PASSWORD from the env
|
- -c # this is to evaluate the $REDIS_PASSWORD from the env
|
||||||
|
@ -51,7 +51,7 @@ services:
|
||||||
volumes:
|
volumes:
|
||||||
- netbox-redis-data:/data
|
- netbox-redis-data:/data
|
||||||
redis-cache:
|
redis-cache:
|
||||||
image: redis:6-alpine
|
image: redis:7-alpine
|
||||||
command:
|
command:
|
||||||
- sh
|
- sh
|
||||||
- -c # this is to evaluate the $REDIS_PASSWORD from the env
|
- -c # this is to evaluate the $REDIS_PASSWORD from the env
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
"uri": "/static/*"
|
"uri": "/static/*"
|
||||||
},
|
},
|
||||||
"action": {
|
"action": {
|
||||||
"share": "/opt/netbox/netbox"
|
"share": "/opt/netbox/netbox${uri}"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,71 @@
|
||||||
|
# # Required parameters for termination X ('a' or 'b'):
|
||||||
|
# #
|
||||||
|
# # ```
|
||||||
|
# # termination_x_name -> name of interface
|
||||||
|
# # termination_x_device -> name of the device interface belongs to
|
||||||
|
# # termination_x_class -> required if different than 'Interface' which is the default
|
||||||
|
# # ```
|
||||||
|
# #
|
||||||
|
# # Supported termination classes: Interface, ConsolePort, ConsoleServerPort, FrontPort, RearPort, PowerPort, PowerOutlet
|
||||||
|
# #
|
||||||
|
# #
|
||||||
|
# # If a termination is a circuit then the required parameter is termination_x_circuit.
|
||||||
|
# # Required parameters for a circuit termination:
|
||||||
|
# #
|
||||||
|
# # ```
|
||||||
|
# # termination_x_circuit:
|
||||||
|
# # term_side -> termination side of a circuit. Must be A or B
|
||||||
|
# # cid -> circuit ID value
|
||||||
|
# # site OR provider_network -> name of Site or ProviderNetwork respectively. If both provided, Site takes precedence
|
||||||
|
# # ```
|
||||||
|
# #
|
||||||
|
# # If a termination is a power feed then the required parameter is termination_x_feed.
|
||||||
|
# #
|
||||||
|
# # ```
|
||||||
|
# # termination_x_feed:
|
||||||
|
# # name -> name of the PowerFeed object
|
||||||
|
# # power_panel:
|
||||||
|
# # name -> name of the PowerPanel the PowerFeed is attached to
|
||||||
|
# # site -> name of the Site in which the PowerPanel is present
|
||||||
|
# # ```
|
||||||
|
# #
|
||||||
|
# # Any other Cable parameters supported by Netbox are supported as the top level keys, e.g. 'type', 'status', etc.
|
||||||
|
# #
|
||||||
|
# # - termination_a_name: console
|
||||||
|
# # termination_a_device: spine
|
||||||
|
# # termination_a_class: ConsolePort
|
||||||
|
# # termination_b_name: tty9
|
||||||
|
# # termination_b_device: console-server
|
||||||
|
# # termination_b_class: ConsoleServerPort
|
||||||
|
# # type: cat6
|
||||||
|
# #
|
||||||
|
# - termination_a_name: to-server02
|
||||||
|
# termination_a_device: server01
|
||||||
|
# termination_b_name: to-server01
|
||||||
|
# termination_b_device: server02
|
||||||
|
# status: planned
|
||||||
|
# type: mmf
|
||||||
|
|
||||||
|
# - termination_a_name: eth0
|
||||||
|
# termination_a_device: server02
|
||||||
|
# termination_b_circuit:
|
||||||
|
# term_side: A
|
||||||
|
# cid: Circuit_ID-1
|
||||||
|
# site: AMS 1
|
||||||
|
# type: cat6
|
||||||
|
|
||||||
|
# - termination_a_name: psu0
|
||||||
|
# termination_a_device: server04
|
||||||
|
# termination_a_class: PowerPort
|
||||||
|
# termination_b_feed:
|
||||||
|
# name: power feed 1
|
||||||
|
# power_panel:
|
||||||
|
# name: power panel AMS 1
|
||||||
|
# site: AMS 1
|
||||||
|
|
||||||
|
# - termination_a_name: outlet1
|
||||||
|
# termination_a_device: server04
|
||||||
|
# termination_a_class: PowerOutlet
|
||||||
|
# termination_b_name: psu1
|
||||||
|
# termination_b_device: server04
|
||||||
|
# termination_b_class: PowerPort
|
|
@ -0,0 +1,7 @@
|
||||||
|
# - name: Network-Team
|
||||||
|
# slug: network-team
|
||||||
|
# description: This is a new contact group for the Network-Team
|
||||||
|
# - name: New Contact Group
|
||||||
|
# slug: new-contact-group
|
||||||
|
# description: This is a new contact group sub under of Network-Team
|
||||||
|
# parent: Network-Team
|
|
@ -0,0 +1,3 @@
|
||||||
|
# - name: New Contact Role
|
||||||
|
# slug: new-contact-role
|
||||||
|
# description: This is a new contact role description
|
|
@ -0,0 +1,20 @@
|
||||||
|
# - name: Lee Widget
|
||||||
|
# title: CEO of Widget Corp
|
||||||
|
# phone: 221-555-1212
|
||||||
|
# email: widgetCEO@widgetcorp.com
|
||||||
|
# address: 1200 Nowhere Blvd, Scranton NJ, 555111
|
||||||
|
# comments: This is a very important contact
|
||||||
|
# - name: Ali Gator
|
||||||
|
# group: Network-Team
|
||||||
|
# title: Consultant for Widget Corp
|
||||||
|
# phone: 221-555-1213
|
||||||
|
# email: Consultant@widgetcorp.com
|
||||||
|
# address: 1200 Nowhere Blvd, Scranton NJ, 555111
|
||||||
|
# comments: This is a very important contact
|
||||||
|
# - name: Karlchen Maier
|
||||||
|
# group: New Contact Group
|
||||||
|
# title: COO of Widget Corp
|
||||||
|
# phone: 221-555-1214
|
||||||
|
# email: Karlchen@widgetcorp.com
|
||||||
|
# address: 1200 Nowhere Blvd, Scranton NJ, 555111
|
||||||
|
# comments: This is a very important contact
|
|
@ -8,11 +8,28 @@
|
||||||
##
|
##
|
||||||
## Examples:
|
## Examples:
|
||||||
|
|
||||||
|
# - device: server01
|
||||||
|
# name: ath0
|
||||||
|
# type: 1000base-t
|
||||||
|
# lag: ae0
|
||||||
|
# bridge: br0
|
||||||
|
# - device: server01
|
||||||
|
# name: ath1
|
||||||
|
# type: 1000base-t
|
||||||
|
# parent: ath0
|
||||||
# - device: server01
|
# - device: server01
|
||||||
# enabled: true
|
# enabled: true
|
||||||
# type: virtual
|
# type: 1000base-x-sfp
|
||||||
# name: to-server02
|
# name: to-server02
|
||||||
# - device: server02
|
# - device: server02
|
||||||
# enabled: true
|
# enabled: true
|
||||||
# type: virtual
|
# type: 1000base-x-sfp
|
||||||
# name: to-server01
|
# name: to-server01
|
||||||
|
# - device: server02
|
||||||
|
# enabled: true
|
||||||
|
# type: 1000base-t
|
||||||
|
# name: eth0
|
||||||
|
# - device: server02
|
||||||
|
# enabled: true
|
||||||
|
# type: virtual
|
||||||
|
# name: loopback
|
||||||
|
|
|
@ -21,3 +21,37 @@
|
||||||
# slug: other
|
# slug: other
|
||||||
# custom_field_data:
|
# custom_field_data:
|
||||||
# text_field: Description
|
# text_field: Description
|
||||||
|
# interfaces:
|
||||||
|
# - name: eth0
|
||||||
|
# type: 1000base-t
|
||||||
|
# mgmt_only: True
|
||||||
|
# - name: eth1
|
||||||
|
# type: 1000base-t
|
||||||
|
# console_server_ports:
|
||||||
|
# - name_template: ttyS[1-48]
|
||||||
|
# type: rj-45
|
||||||
|
# power_ports:
|
||||||
|
# - name_template: psu[0,1]
|
||||||
|
# type: iec-60320-c14
|
||||||
|
# maximum_draw: 35
|
||||||
|
# allocated_draw: 35
|
||||||
|
# front_ports:
|
||||||
|
# - name_template: front[1,2]
|
||||||
|
# type: 8p8c
|
||||||
|
# rear_port_template: rear[0,1]
|
||||||
|
# rear_port_position_template: "[1,2]"
|
||||||
|
# rear_ports:
|
||||||
|
# - name_template: rear[0,1]
|
||||||
|
# type: 8p8c
|
||||||
|
# positions_template: "[3,2]"
|
||||||
|
# device_bays:
|
||||||
|
# - name: bay0 # both non-template and template field specified; non-template field takes precedence
|
||||||
|
# name_template: bay[0-9]
|
||||||
|
# label: test0
|
||||||
|
# label_template: test[0-5,9,6-8]
|
||||||
|
# description: Test description
|
||||||
|
# power_outlets:
|
||||||
|
# - name_template: outlet[0,1]
|
||||||
|
# type: iec-60320-c5
|
||||||
|
# power_port: psu0
|
||||||
|
# feed_leg: B
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
# password: reader
|
# password: reader
|
||||||
# writer:
|
# writer:
|
||||||
# password: writer
|
# password: writer
|
||||||
|
# api_token: "" # a token is generated automatically unless the value is explicity set to empty
|
||||||
# jdoe:
|
# jdoe:
|
||||||
# first_name: John
|
# first_name: John
|
||||||
# last_name: Doe
|
# last_name: Doe
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
django-auth-ldap==4.0.0
|
django-auth-ldap==4.1.0
|
||||||
django-storages[azure,boto3,dropbox,google,libcloud,sftp]==1.12.3
|
django-storages[azure,boto3,dropbox,google,libcloud,sftp]==1.12.3
|
||||||
google-crc32c==1.3.0
|
napalm==4.0.0
|
||||||
napalm==3.4.0
|
psycopg2==2.9.3
|
||||||
ruamel.yaml==0.17.21
|
ruamel.yaml==0.17.21
|
||||||
|
|
|
@ -9,13 +9,17 @@ if users is None:
|
||||||
sys.exit()
|
sys.exit()
|
||||||
|
|
||||||
for username, user_details in users.items():
|
for username, user_details in users.items():
|
||||||
if not User.objects.filter(username=username):
|
|
||||||
user = User.objects.create_user(
|
api_token = user_details.pop("api_token", Token.generate_key())
|
||||||
username=username,
|
password = user_details.pop("password", User.objects.make_random_password())
|
||||||
password=user_details.get("password", 0) or User.objects.make_random_password(),
|
|
||||||
)
|
user, created = User.objects.get_or_create(username=username, defaults=user_details)
|
||||||
|
|
||||||
|
if created:
|
||||||
|
user.set_password(password)
|
||||||
|
user.save()
|
||||||
|
|
||||||
|
if api_token:
|
||||||
|
Token.objects.get_or_create(user=user, key=api_token)
|
||||||
|
|
||||||
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"])
|
|
||||||
|
|
|
@ -14,9 +14,11 @@ for permission_name, permission_details in object_permissions.items():
|
||||||
|
|
||||||
object_permission, created = ObjectPermission.objects.get_or_create(
|
object_permission, created = ObjectPermission.objects.get_or_create(
|
||||||
name=permission_name,
|
name=permission_name,
|
||||||
description=permission_details["description"],
|
defaults={
|
||||||
enabled=permission_details["enabled"],
|
"description": permission_details["description"],
|
||||||
actions=permission_details["actions"],
|
"enabled": permission_details["enabled"],
|
||||||
|
"actions": permission_details["actions"],
|
||||||
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
if permission_details.get("object_types", 0):
|
if permission_details.get("object_types", 0):
|
||||||
|
|
|
@ -2,7 +2,7 @@ import sys
|
||||||
|
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
from extras.models import CustomLink
|
from extras.models import CustomLink
|
||||||
from startup_script_utils import load_yaml
|
from startup_script_utils import load_yaml, split_params
|
||||||
|
|
||||||
custom_links = load_yaml("/opt/netbox/initializers/custom_links.yml")
|
custom_links = load_yaml("/opt/netbox/initializers/custom_links.yml")
|
||||||
|
|
||||||
|
@ -28,6 +28,8 @@ for link in custom_links:
|
||||||
)
|
)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
custom_link, created = CustomLink.objects.get_or_create(**link)
|
matching_params, defaults = split_params(link)
|
||||||
|
custom_link, created = CustomLink.objects.get_or_create(**matching_params, defaults=defaults)
|
||||||
|
|
||||||
if created:
|
if created:
|
||||||
print("🔗 Created Custom Link '{0}'".format(custom_link.name))
|
print("🔗 Created Custom Link '{0}'".format(custom_link.name))
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from extras.models import Tag
|
from extras.models import Tag
|
||||||
from startup_script_utils import load_yaml
|
from startup_script_utils import load_yaml, split_params
|
||||||
from utilities.choices import ColorChoices
|
from utilities.choices import ColorChoices
|
||||||
|
|
||||||
tags = load_yaml("/opt/netbox/initializers/tags.yml")
|
tags = load_yaml("/opt/netbox/initializers/tags.yml")
|
||||||
|
@ -17,7 +17,8 @@ for params in tags:
|
||||||
if color in color_tpl:
|
if color in color_tpl:
|
||||||
params["color"] = color_tpl[0]
|
params["color"] = color_tpl[0]
|
||||||
|
|
||||||
tag, created = Tag.objects.get_or_create(**params)
|
matching_params, defaults = split_params(params)
|
||||||
|
tag, created = Tag.objects.get_or_create(**matching_params, defaults=defaults)
|
||||||
|
|
||||||
if created:
|
if created:
|
||||||
print("🎨 Created Tag", tag.name)
|
print("🎨 Created Tag", tag.name)
|
||||||
|
|
|
@ -2,7 +2,7 @@ import sys
|
||||||
|
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
from extras.models import Webhook
|
from extras.models import Webhook
|
||||||
from startup_script_utils import load_yaml
|
from startup_script_utils import load_yaml, split_params
|
||||||
|
|
||||||
webhooks = load_yaml("/opt/netbox/initializers/webhooks.yml")
|
webhooks = load_yaml("/opt/netbox/initializers/webhooks.yml")
|
||||||
|
|
||||||
|
@ -26,7 +26,9 @@ for hook in webhooks:
|
||||||
except ContentType.DoesNotExist:
|
except ContentType.DoesNotExist:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
webhook, created = Webhook.objects.get_or_create(**hook)
|
matching_params, defaults = split_params(hook)
|
||||||
|
webhook, created = Webhook.objects.get_or_create(**matching_params, defaults=defaults)
|
||||||
|
|
||||||
if created:
|
if created:
|
||||||
webhook.content_types.set(obj_type_ids)
|
webhook.content_types.set(obj_type_ids)
|
||||||
webhook.save()
|
webhook.save()
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from startup_script_utils import load_yaml
|
from startup_script_utils import load_yaml, split_params
|
||||||
from tenancy.models import TenantGroup
|
from tenancy.models import TenantGroup
|
||||||
|
|
||||||
tenant_groups = load_yaml("/opt/netbox/initializers/tenant_groups.yml")
|
tenant_groups = load_yaml("/opt/netbox/initializers/tenant_groups.yml")
|
||||||
|
@ -9,7 +9,8 @@ if tenant_groups is None:
|
||||||
sys.exit()
|
sys.exit()
|
||||||
|
|
||||||
for params in tenant_groups:
|
for params in tenant_groups:
|
||||||
tenant_group, created = TenantGroup.objects.get_or_create(**params)
|
matching_params, defaults = split_params(params)
|
||||||
|
tenant_group, created = TenantGroup.objects.get_or_create(**matching_params, defaults=defaults)
|
||||||
|
|
||||||
if created:
|
if created:
|
||||||
print("🔳 Created Tenant Group", tenant_group.name)
|
print("🔳 Created Tenant Group", tenant_group.name)
|
||||||
|
|
|
@ -1,6 +1,11 @@
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from startup_script_utils import load_yaml, pop_custom_fields, set_custom_fields_values
|
from startup_script_utils import (
|
||||||
|
load_yaml,
|
||||||
|
pop_custom_fields,
|
||||||
|
set_custom_fields_values,
|
||||||
|
split_params,
|
||||||
|
)
|
||||||
from tenancy.models import Tenant, TenantGroup
|
from tenancy.models import Tenant, TenantGroup
|
||||||
|
|
||||||
tenants = load_yaml("/opt/netbox/initializers/tenants.yml")
|
tenants = load_yaml("/opt/netbox/initializers/tenants.yml")
|
||||||
|
@ -20,9 +25,10 @@ for params in tenants:
|
||||||
|
|
||||||
params[assoc] = model.objects.get(**query)
|
params[assoc] = model.objects.get(**query)
|
||||||
|
|
||||||
tenant, created = Tenant.objects.get_or_create(**params)
|
matching_params, defaults = split_params(params)
|
||||||
|
tenant, created = Tenant.objects.get_or_create(**matching_params, defaults=defaults)
|
||||||
|
|
||||||
if created:
|
if created:
|
||||||
set_custom_fields_values(tenant, custom_field_data)
|
|
||||||
|
|
||||||
print("👩💻 Created Tenant", tenant.name)
|
print("👩💻 Created Tenant", tenant.name)
|
||||||
|
|
||||||
|
set_custom_fields_values(tenant, custom_field_data)
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from dcim.models import Region
|
from dcim.models import Region
|
||||||
from startup_script_utils import load_yaml
|
from startup_script_utils import load_yaml, split_params
|
||||||
|
|
||||||
regions = load_yaml("/opt/netbox/initializers/regions.yml")
|
regions = load_yaml("/opt/netbox/initializers/regions.yml")
|
||||||
|
|
||||||
|
@ -19,7 +19,8 @@ for params in regions:
|
||||||
|
|
||||||
params[assoc] = model.objects.get(**query)
|
params[assoc] = model.objects.get(**query)
|
||||||
|
|
||||||
region, created = Region.objects.get_or_create(**params)
|
matching_params, defaults = split_params(params)
|
||||||
|
region, created = Region.objects.get_or_create(**matching_params, defaults=defaults)
|
||||||
|
|
||||||
if created:
|
if created:
|
||||||
print("🌐 Created region", region.name)
|
print("🌐 Created region", region.name)
|
||||||
|
|
|
@ -1,7 +1,12 @@
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from dcim.models import Region, Site
|
from dcim.models import Region, Site
|
||||||
from startup_script_utils import load_yaml, pop_custom_fields, set_custom_fields_values
|
from startup_script_utils import (
|
||||||
|
load_yaml,
|
||||||
|
pop_custom_fields,
|
||||||
|
set_custom_fields_values,
|
||||||
|
split_params,
|
||||||
|
)
|
||||||
from tenancy.models import Tenant
|
from tenancy.models import Tenant
|
||||||
|
|
||||||
sites = load_yaml("/opt/netbox/initializers/sites.yml")
|
sites = load_yaml("/opt/netbox/initializers/sites.yml")
|
||||||
|
@ -21,9 +26,10 @@ for params in sites:
|
||||||
|
|
||||||
params[assoc] = model.objects.get(**query)
|
params[assoc] = model.objects.get(**query)
|
||||||
|
|
||||||
site, created = Site.objects.get_or_create(**params)
|
matching_params, defaults = split_params(params)
|
||||||
|
site, created = Site.objects.get_or_create(**matching_params, defaults=defaults)
|
||||||
|
|
||||||
if created:
|
if created:
|
||||||
set_custom_fields_values(site, custom_field_data)
|
|
||||||
|
|
||||||
print("📍 Created site", site.name)
|
print("📍 Created site", site.name)
|
||||||
|
|
||||||
|
set_custom_fields_values(site, custom_field_data)
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from dcim.models import Location, Site
|
from dcim.models import Location, Site
|
||||||
from startup_script_utils import load_yaml
|
from startup_script_utils import load_yaml, split_params
|
||||||
|
|
||||||
rack_groups = load_yaml("/opt/netbox/initializers/locations.yml")
|
rack_groups = load_yaml("/opt/netbox/initializers/locations.yml")
|
||||||
|
|
||||||
if rack_groups is None:
|
if rack_groups is None:
|
||||||
sys.exit()
|
sys.exit()
|
||||||
|
|
||||||
|
match_params = ["name", "slug", "site"]
|
||||||
required_assocs = {"site": (Site, "name")}
|
required_assocs = {"site": (Site, "name")}
|
||||||
|
|
||||||
for params in rack_groups:
|
for params in rack_groups:
|
||||||
|
@ -17,7 +18,8 @@ for params in rack_groups:
|
||||||
query = {field: params.pop(assoc)}
|
query = {field: params.pop(assoc)}
|
||||||
params[assoc] = model.objects.get(**query)
|
params[assoc] = model.objects.get(**query)
|
||||||
|
|
||||||
location, created = Location.objects.get_or_create(**params)
|
matching_params, defaults = split_params(params, match_params)
|
||||||
|
location, created = Location.objects.get_or_create(**matching_params, defaults=defaults)
|
||||||
|
|
||||||
if created:
|
if created:
|
||||||
print("🎨 Created location", location.name)
|
print("🎨 Created location", location.name)
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from dcim.models import RackRole
|
from dcim.models import RackRole
|
||||||
from startup_script_utils import load_yaml
|
from startup_script_utils import load_yaml, split_params
|
||||||
from utilities.choices import ColorChoices
|
from utilities.choices import ColorChoices
|
||||||
|
|
||||||
rack_roles = load_yaml("/opt/netbox/initializers/rack_roles.yml")
|
rack_roles = load_yaml("/opt/netbox/initializers/rack_roles.yml")
|
||||||
|
@ -17,7 +17,8 @@ for params in rack_roles:
|
||||||
if color in color_tpl:
|
if color in color_tpl:
|
||||||
params["color"] = color_tpl[0]
|
params["color"] = color_tpl[0]
|
||||||
|
|
||||||
rack_role, created = RackRole.objects.get_or_create(**params)
|
matching_params, defaults = split_params(params)
|
||||||
|
rack_role, created = RackRole.objects.get_or_create(**matching_params, defaults=defaults)
|
||||||
|
|
||||||
if created:
|
if created:
|
||||||
print("🎨 Created rack role", rack_role.name)
|
print("🎨 Created rack role", rack_role.name)
|
||||||
|
|
|
@ -1,7 +1,12 @@
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from dcim.models import Location, Rack, RackRole, Site
|
from dcim.models import Location, Rack, RackRole, Site
|
||||||
from startup_script_utils import load_yaml, pop_custom_fields, set_custom_fields_values
|
from startup_script_utils import (
|
||||||
|
load_yaml,
|
||||||
|
pop_custom_fields,
|
||||||
|
set_custom_fields_values,
|
||||||
|
split_params,
|
||||||
|
)
|
||||||
from tenancy.models import Tenant
|
from tenancy.models import Tenant
|
||||||
|
|
||||||
racks = load_yaml("/opt/netbox/initializers/racks.yml")
|
racks = load_yaml("/opt/netbox/initializers/racks.yml")
|
||||||
|
@ -9,8 +14,8 @@ racks = load_yaml("/opt/netbox/initializers/racks.yml")
|
||||||
if racks is None:
|
if racks is None:
|
||||||
sys.exit()
|
sys.exit()
|
||||||
|
|
||||||
|
match_params = ["name", "site"]
|
||||||
required_assocs = {"site": (Site, "name")}
|
required_assocs = {"site": (Site, "name")}
|
||||||
|
|
||||||
optional_assocs = {
|
optional_assocs = {
|
||||||
"role": (RackRole, "name"),
|
"role": (RackRole, "name"),
|
||||||
"tenant": (Tenant, "name"),
|
"tenant": (Tenant, "name"),
|
||||||
|
@ -33,9 +38,10 @@ for params in racks:
|
||||||
|
|
||||||
params[assoc] = model.objects.get(**query)
|
params[assoc] = model.objects.get(**query)
|
||||||
|
|
||||||
rack, created = Rack.objects.get_or_create(**params)
|
matching_params, defaults = split_params(params, match_params)
|
||||||
|
rack, created = Rack.objects.get_or_create(**matching_params, defaults=defaults)
|
||||||
|
|
||||||
if created:
|
if created:
|
||||||
set_custom_fields_values(rack, custom_field_data)
|
|
||||||
|
|
||||||
print("🔳 Created rack", rack.site, rack.name)
|
print("🔳 Created rack", rack.site, rack.name)
|
||||||
|
|
||||||
|
set_custom_fields_values(rack, custom_field_data)
|
||||||
|
|
|
@ -1,15 +1,20 @@
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from dcim.models import Location, PowerPanel, Site
|
from dcim.models import Location, PowerPanel, Site
|
||||||
from startup_script_utils import load_yaml, pop_custom_fields, set_custom_fields_values
|
from startup_script_utils import (
|
||||||
|
load_yaml,
|
||||||
|
pop_custom_fields,
|
||||||
|
set_custom_fields_values,
|
||||||
|
split_params,
|
||||||
|
)
|
||||||
|
|
||||||
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:
|
if power_panels is None:
|
||||||
sys.exit()
|
sys.exit()
|
||||||
|
|
||||||
|
match_params = ["name", "site"]
|
||||||
required_assocs = {"site": (Site, "name")}
|
required_assocs = {"site": (Site, "name")}
|
||||||
|
|
||||||
optional_assocs = {"location": (Location, "name")}
|
optional_assocs = {"location": (Location, "name")}
|
||||||
|
|
||||||
for params in power_panels:
|
for params in power_panels:
|
||||||
|
@ -28,9 +33,10 @@ for params in power_panels:
|
||||||
|
|
||||||
params[assoc] = model.objects.get(**query)
|
params[assoc] = model.objects.get(**query)
|
||||||
|
|
||||||
power_panel, created = PowerPanel.objects.get_or_create(**params)
|
matching_params, defaults = split_params(params, match_params)
|
||||||
|
power_panel, created = PowerPanel.objects.get_or_create(**matching_params, defaults=defaults)
|
||||||
|
|
||||||
if created:
|
if created:
|
||||||
set_custom_fields_values(power_panel, custom_field_data)
|
|
||||||
|
|
||||||
print("⚡ Created Power Panel", power_panel.site, power_panel.name)
|
print("⚡ Created Power Panel", power_panel.site, power_panel.name)
|
||||||
|
|
||||||
|
set_custom_fields_values(power_panel, custom_field_data)
|
||||||
|
|
|
@ -1,15 +1,20 @@
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from dcim.models import PowerFeed, PowerPanel, Rack
|
from dcim.models import PowerFeed, PowerPanel, Rack
|
||||||
from startup_script_utils import load_yaml, pop_custom_fields, set_custom_fields_values
|
from startup_script_utils import (
|
||||||
|
load_yaml,
|
||||||
|
pop_custom_fields,
|
||||||
|
set_custom_fields_values,
|
||||||
|
split_params,
|
||||||
|
)
|
||||||
|
|
||||||
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:
|
if power_feeds is None:
|
||||||
sys.exit()
|
sys.exit()
|
||||||
|
|
||||||
|
match_params = ["name", "power_panel"]
|
||||||
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:
|
for params in power_feeds:
|
||||||
|
@ -28,9 +33,10 @@ for params in power_feeds:
|
||||||
|
|
||||||
params[assoc] = model.objects.get(**query)
|
params[assoc] = model.objects.get(**query)
|
||||||
|
|
||||||
power_feed, created = PowerFeed.objects.get_or_create(**params)
|
matching_params, defaults = split_params(params, match_params)
|
||||||
|
power_feed, created = PowerFeed.objects.get_or_create(**matching_params, defaults=defaults)
|
||||||
|
|
||||||
if created:
|
if created:
|
||||||
set_custom_fields_values(power_feed, custom_field_data)
|
|
||||||
|
|
||||||
print("⚡ Created Power Feed", power_feed.name)
|
print("⚡ Created Power Feed", power_feed.name)
|
||||||
|
|
||||||
|
set_custom_fields_values(power_feed, custom_field_data)
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from dcim.models import Manufacturer
|
from dcim.models import Manufacturer
|
||||||
from startup_script_utils import load_yaml
|
from startup_script_utils import load_yaml, split_params
|
||||||
|
|
||||||
manufacturers = load_yaml("/opt/netbox/initializers/manufacturers.yml")
|
manufacturers = load_yaml("/opt/netbox/initializers/manufacturers.yml")
|
||||||
|
|
||||||
|
@ -9,7 +9,8 @@ if manufacturers is None:
|
||||||
sys.exit()
|
sys.exit()
|
||||||
|
|
||||||
for params in manufacturers:
|
for params in manufacturers:
|
||||||
manufacturer, created = Manufacturer.objects.get_or_create(**params)
|
matching_params, defaults = split_params(params)
|
||||||
|
manufacturer, created = Manufacturer.objects.get_or_create(**matching_params, defaults=defaults)
|
||||||
|
|
||||||
if created:
|
if created:
|
||||||
print("🏭 Created Manufacturer", manufacturer.name)
|
print("🏭 Created Manufacturer", manufacturer.name)
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from dcim.models import DeviceRole
|
from dcim.models import DeviceRole
|
||||||
from startup_script_utils import load_yaml
|
from startup_script_utils import load_yaml, split_params
|
||||||
from utilities.choices import ColorChoices
|
from utilities.choices import ColorChoices
|
||||||
|
|
||||||
device_roles = load_yaml("/opt/netbox/initializers/device_roles.yml")
|
device_roles = load_yaml("/opt/netbox/initializers/device_roles.yml")
|
||||||
|
@ -18,7 +18,8 @@ for params in device_roles:
|
||||||
if color in color_tpl:
|
if color in color_tpl:
|
||||||
params["color"] = color_tpl[0]
|
params["color"] = color_tpl[0]
|
||||||
|
|
||||||
device_role, created = DeviceRole.objects.get_or_create(**params)
|
matching_params, defaults = split_params(params)
|
||||||
|
device_role, created = DeviceRole.objects.get_or_create(**matching_params, defaults=defaults)
|
||||||
|
|
||||||
if created:
|
if created:
|
||||||
print("🎨 Created device role", device_role.name)
|
print("🎨 Created device role", device_role.name)
|
||||||
|
|
|
@ -1,20 +1,95 @@
|
||||||
import sys
|
import sys
|
||||||
|
from typing import List
|
||||||
|
|
||||||
from dcim.models import DeviceType, Manufacturer, Region
|
from dcim.models import DeviceType, Manufacturer, Region
|
||||||
from startup_script_utils import load_yaml, pop_custom_fields, set_custom_fields_values
|
from dcim.models.device_component_templates import (
|
||||||
|
ConsolePortTemplate,
|
||||||
|
ConsoleServerPortTemplate,
|
||||||
|
DeviceBayTemplate,
|
||||||
|
FrontPortTemplate,
|
||||||
|
InterfaceTemplate,
|
||||||
|
PowerOutletTemplate,
|
||||||
|
PowerPortTemplate,
|
||||||
|
RearPortTemplate,
|
||||||
|
)
|
||||||
|
from startup_script_utils import (
|
||||||
|
load_yaml,
|
||||||
|
pop_custom_fields,
|
||||||
|
set_custom_fields_values,
|
||||||
|
split_params,
|
||||||
|
)
|
||||||
from tenancy.models import Tenant
|
from tenancy.models import Tenant
|
||||||
|
from utilities.forms.utils import expand_alphanumeric_pattern
|
||||||
|
|
||||||
|
|
||||||
|
def expand_templates(params: List[dict], device_type: DeviceType) -> List[dict]:
|
||||||
|
templateable_fields = ["name", "label", "positions", "rear_port", "rear_port_position"]
|
||||||
|
|
||||||
|
expanded = []
|
||||||
|
for param in params:
|
||||||
|
param["device_type"] = device_type
|
||||||
|
expanded_fields = {}
|
||||||
|
has_plain_fields = False
|
||||||
|
|
||||||
|
for field in templateable_fields:
|
||||||
|
template_value = param.pop(f"{field}_template", None)
|
||||||
|
|
||||||
|
if field in param:
|
||||||
|
has_plain_fields = True
|
||||||
|
elif template_value:
|
||||||
|
expanded_fields[field] = list(expand_alphanumeric_pattern(template_value))
|
||||||
|
|
||||||
|
if expanded_fields and has_plain_fields:
|
||||||
|
raise ValueError(f"Mix of plain and template keys provided for {templateable_fields}")
|
||||||
|
elif not expanded_fields:
|
||||||
|
expanded.append(param)
|
||||||
|
continue
|
||||||
|
|
||||||
|
elements = list(expanded_fields.values())
|
||||||
|
master_len = len(elements[0])
|
||||||
|
if not all([len(elem) == master_len for elem in elements]):
|
||||||
|
raise ValueError(
|
||||||
|
f"Number of elements in template fields "
|
||||||
|
f"{list(expanded_fields.keys())} must be equal"
|
||||||
|
)
|
||||||
|
|
||||||
|
for idx in range(master_len):
|
||||||
|
tmp = param.copy()
|
||||||
|
for field, value in expanded_fields.items():
|
||||||
|
if field in nested_assocs:
|
||||||
|
model, match_key = nested_assocs[field]
|
||||||
|
query = {match_key: value[idx], "device_type": device_type}
|
||||||
|
tmp[field] = model.objects.get(**query)
|
||||||
|
else:
|
||||||
|
tmp[field] = value[idx]
|
||||||
|
expanded.append(tmp)
|
||||||
|
return expanded
|
||||||
|
|
||||||
|
|
||||||
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:
|
if device_types is None:
|
||||||
sys.exit()
|
sys.exit()
|
||||||
|
|
||||||
|
match_params = ["manufacturer", "model", "slug"]
|
||||||
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")}
|
||||||
|
nested_assocs = {"rear_port": (RearPortTemplate, "name"), "power_port": (PowerPortTemplate, "name")}
|
||||||
|
|
||||||
|
supported_components = {
|
||||||
|
"interfaces": (InterfaceTemplate, ["name"]),
|
||||||
|
"console_ports": (ConsolePortTemplate, ["name"]),
|
||||||
|
"console_server_ports": (ConsoleServerPortTemplate, ["name"]),
|
||||||
|
"power_ports": (PowerPortTemplate, ["name"]),
|
||||||
|
"power_outlets": (PowerOutletTemplate, ["name"]),
|
||||||
|
"rear_ports": (RearPortTemplate, ["name"]),
|
||||||
|
"front_ports": (FrontPortTemplate, ["name"]),
|
||||||
|
"device_bays": (DeviceBayTemplate, ["name"]),
|
||||||
|
}
|
||||||
|
|
||||||
for params in device_types:
|
for params in device_types:
|
||||||
custom_field_data = pop_custom_fields(params)
|
custom_field_data = pop_custom_fields(params)
|
||||||
|
components = [(v[0], v[1], params.pop(k, [])) for k, v in supported_components.items()]
|
||||||
|
|
||||||
for assoc, details in required_assocs.items():
|
for assoc, details in required_assocs.items():
|
||||||
model, field = details
|
model, field = details
|
||||||
|
@ -29,9 +104,36 @@ for params in device_types:
|
||||||
|
|
||||||
params[assoc] = model.objects.get(**query)
|
params[assoc] = model.objects.get(**query)
|
||||||
|
|
||||||
device_type, created = DeviceType.objects.get_or_create(**params)
|
matching_params, defaults = split_params(params, match_params)
|
||||||
|
device_type, created = DeviceType.objects.get_or_create(**matching_params, defaults=defaults)
|
||||||
|
|
||||||
if created:
|
if created:
|
||||||
set_custom_fields_values(device_type, custom_field_data)
|
|
||||||
|
|
||||||
print("🔡 Created device type", device_type.manufacturer, device_type.model)
|
print("🔡 Created device type", device_type.manufacturer, device_type.model)
|
||||||
|
|
||||||
|
set_custom_fields_values(device_type, custom_field_data)
|
||||||
|
|
||||||
|
for component in components:
|
||||||
|
c_model, c_match_params, c_params = component
|
||||||
|
c_match_params.append("device_type")
|
||||||
|
|
||||||
|
if not c_params:
|
||||||
|
continue
|
||||||
|
|
||||||
|
expanded_c_params = expand_templates(c_params, device_type)
|
||||||
|
|
||||||
|
for n_assoc, n_details in nested_assocs.items():
|
||||||
|
n_model, n_field = n_details
|
||||||
|
for c_param in expanded_c_params:
|
||||||
|
if n_assoc in c_param:
|
||||||
|
n_query = {n_field: c_param[n_assoc], "device_type": device_type}
|
||||||
|
c_param[n_assoc] = n_model.objects.get(**n_query)
|
||||||
|
|
||||||
|
for new_param in expanded_c_params:
|
||||||
|
new_matching_params, new_defaults = split_params(new_param, c_match_params)
|
||||||
|
new_obj, new_obj_created = c_model.objects.get_or_create(
|
||||||
|
**new_matching_params, defaults=new_defaults
|
||||||
|
)
|
||||||
|
if new_obj_created:
|
||||||
|
print(
|
||||||
|
f"🧷 Created {c_model._meta} {new_obj} component for device type {device_type}"
|
||||||
|
)
|
||||||
|
|
|
@ -1,7 +1,12 @@
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from dcim.models import Device, DeviceRole, DeviceType, Location, Platform, Rack, Site
|
from dcim.models import Device, DeviceRole, DeviceType, Location, Platform, Rack, Site
|
||||||
from startup_script_utils import load_yaml, pop_custom_fields, set_custom_fields_values
|
from startup_script_utils import (
|
||||||
|
load_yaml,
|
||||||
|
pop_custom_fields,
|
||||||
|
set_custom_fields_values,
|
||||||
|
split_params,
|
||||||
|
)
|
||||||
from tenancy.models import Tenant
|
from tenancy.models import Tenant
|
||||||
from virtualization.models import Cluster
|
from virtualization.models import Cluster
|
||||||
|
|
||||||
|
@ -10,12 +15,12 @@ devices = load_yaml("/opt/netbox/initializers/devices.yml")
|
||||||
if devices is None:
|
if devices is None:
|
||||||
sys.exit()
|
sys.exit()
|
||||||
|
|
||||||
|
match_params = ["device_type", "name", "site"]
|
||||||
required_assocs = {
|
required_assocs = {
|
||||||
"device_role": (DeviceRole, "name"),
|
"device_role": (DeviceRole, "name"),
|
||||||
"device_type": (DeviceType, "model"),
|
"device_type": (DeviceType, "model"),
|
||||||
"site": (Site, "name"),
|
"site": (Site, "name"),
|
||||||
}
|
}
|
||||||
|
|
||||||
optional_assocs = {
|
optional_assocs = {
|
||||||
"tenant": (Tenant, "name"),
|
"tenant": (Tenant, "name"),
|
||||||
"platform": (Platform, "name"),
|
"platform": (Platform, "name"),
|
||||||
|
@ -27,7 +32,7 @@ optional_assocs = {
|
||||||
for params in devices:
|
for params in devices:
|
||||||
custom_field_data = pop_custom_fields(params)
|
custom_field_data = pop_custom_fields(params)
|
||||||
|
|
||||||
# primary ips are handled later in `270_primary_ips.py`
|
# primary ips are handled later in `380_primary_ips.py`
|
||||||
params.pop("primary_ip4", None)
|
params.pop("primary_ip4", None)
|
||||||
params.pop("primary_ip6", None)
|
params.pop("primary_ip6", None)
|
||||||
|
|
||||||
|
@ -44,9 +49,10 @@ for params in devices:
|
||||||
|
|
||||||
params[assoc] = model.objects.get(**query)
|
params[assoc] = model.objects.get(**query)
|
||||||
|
|
||||||
device, created = Device.objects.get_or_create(**params)
|
matching_params, defaults = split_params(params, match_params)
|
||||||
|
device, created = Device.objects.get_or_create(**matching_params, defaults=defaults)
|
||||||
|
|
||||||
if created:
|
if created:
|
||||||
set_custom_fields_values(device, custom_field_data)
|
|
||||||
|
|
||||||
print("🖥️ Created device", device.name)
|
print("🖥️ Created device", device.name)
|
||||||
|
|
||||||
|
set_custom_fields_values(device, custom_field_data)
|
||||||
|
|
|
@ -1,27 +1,70 @@
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from dcim.models import Device, Interface
|
from dcim.models import Device, Interface
|
||||||
from startup_script_utils import load_yaml, pop_custom_fields, set_custom_fields_values
|
from startup_script_utils import (
|
||||||
|
load_yaml,
|
||||||
|
pop_custom_fields,
|
||||||
|
set_custom_fields_values,
|
||||||
|
split_params,
|
||||||
|
)
|
||||||
|
|
||||||
interfaces = load_yaml("/opt/netbox/initializers/dcim_interfaces.yml")
|
interfaces = load_yaml("/opt/netbox/initializers/dcim_interfaces.yml")
|
||||||
|
|
||||||
if interfaces is None:
|
if interfaces is None:
|
||||||
sys.exit()
|
sys.exit()
|
||||||
|
|
||||||
|
match_params = ["device", "name"]
|
||||||
required_assocs = {"device": (Device, "name")}
|
required_assocs = {"device": (Device, "name")}
|
||||||
|
related_assocs = {
|
||||||
|
"bridge": (Interface, "name"),
|
||||||
|
"lag": (Interface, "name"),
|
||||||
|
"parent": (Interface, "name"),
|
||||||
|
}
|
||||||
|
|
||||||
for params in interfaces:
|
for params in interfaces:
|
||||||
custom_field_data = pop_custom_fields(params)
|
custom_field_data = pop_custom_fields(params)
|
||||||
|
|
||||||
|
related_interfaces = {k: params.pop(k, None) for k in related_assocs}
|
||||||
|
|
||||||
for assoc, details in required_assocs.items():
|
for assoc, details in required_assocs.items():
|
||||||
model, field = details
|
model, field = details
|
||||||
query = {field: params.pop(assoc)}
|
query = {field: params.pop(assoc)}
|
||||||
|
|
||||||
params[assoc] = model.objects.get(**query)
|
params[assoc] = model.objects.get(**query)
|
||||||
|
|
||||||
interface, created = Interface.objects.get_or_create(**params)
|
matching_params, defaults = split_params(params, match_params)
|
||||||
|
interface, created = Interface.objects.get_or_create(**matching_params, defaults=defaults)
|
||||||
|
|
||||||
if created:
|
if created:
|
||||||
set_custom_fields_values(interface, custom_field_data)
|
print(f"🧷 Created interface {interface} on {interface.device}")
|
||||||
|
|
||||||
print("🧷 Created interface", interface.name, interface.device.name)
|
set_custom_fields_values(interface, custom_field_data)
|
||||||
|
|
||||||
|
for related_field, related_value in related_interfaces.items():
|
||||||
|
if not related_value:
|
||||||
|
continue
|
||||||
|
|
||||||
|
r_model, r_field = related_assocs[related_field]
|
||||||
|
|
||||||
|
if related_field == "parent" and not interface.parent_id:
|
||||||
|
query = {r_field: related_value, "device": interface.device}
|
||||||
|
try:
|
||||||
|
related_obj = r_model.objects.get(**query)
|
||||||
|
except Interface.DoesNotExist:
|
||||||
|
print(f"⚠️ Could not find parent interface with: {query} for interface {interface}")
|
||||||
|
raise
|
||||||
|
|
||||||
|
interface.parent_id = related_obj.id
|
||||||
|
interface.save()
|
||||||
|
print(
|
||||||
|
f"🧷 Attached interface {interface} on {interface.device} "
|
||||||
|
f"to parent {related_obj}"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
query = {r_field: related_value, "device": interface.device, "type": related_field}
|
||||||
|
related_obj, rel_obj_created = r_model.objects.get_or_create(**query)
|
||||||
|
|
||||||
|
if rel_obj_created:
|
||||||
|
setattr(interface, f"{related_field}_id", related_obj.id)
|
||||||
|
interface.save()
|
||||||
|
print(f"🧷 Created {related_field} interface {interface} on {interface.device}")
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from dcim.models import Manufacturer, Platform
|
from dcim.models import Manufacturer, Platform
|
||||||
from startup_script_utils import load_yaml
|
from startup_script_utils import load_yaml, split_params
|
||||||
|
|
||||||
platforms = load_yaml("/opt/netbox/initializers/platforms.yml")
|
platforms = load_yaml("/opt/netbox/initializers/platforms.yml")
|
||||||
|
|
||||||
|
@ -21,7 +21,8 @@ for params in platforms:
|
||||||
|
|
||||||
params[assoc] = model.objects.get(**query)
|
params[assoc] = model.objects.get(**query)
|
||||||
|
|
||||||
platform, created = Platform.objects.get_or_create(**params)
|
matching_params, defaults = split_params(params)
|
||||||
|
platform, created = Platform.objects.get_or_create(**matching_params, defaults=defaults)
|
||||||
|
|
||||||
if created:
|
if created:
|
||||||
print("💾 Created platform", platform.name)
|
print("💾 Created platform", platform.name)
|
||||||
|
|
|
@ -1,7 +1,12 @@
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from ipam.models import RouteTarget
|
from ipam.models import RouteTarget
|
||||||
from startup_script_utils import load_yaml, pop_custom_fields, set_custom_fields_values
|
from startup_script_utils import (
|
||||||
|
load_yaml,
|
||||||
|
pop_custom_fields,
|
||||||
|
set_custom_fields_values,
|
||||||
|
split_params,
|
||||||
|
)
|
||||||
from tenancy.models import Tenant
|
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")
|
||||||
|
@ -21,9 +26,10 @@ for params in route_targets:
|
||||||
|
|
||||||
params[assoc] = model.objects.get(**query)
|
params[assoc] = model.objects.get(**query)
|
||||||
|
|
||||||
route_target, created = RouteTarget.objects.get_or_create(**params)
|
matching_params, defaults = split_params(params)
|
||||||
|
route_target, created = RouteTarget.objects.get_or_create(**matching_params, defaults=defaults)
|
||||||
|
|
||||||
if created:
|
if created:
|
||||||
set_custom_fields_values(route_target, custom_field_data)
|
|
||||||
|
|
||||||
print("🎯 Created Route Target", route_target.name)
|
print("🎯 Created Route Target", route_target.name)
|
||||||
|
|
||||||
|
set_custom_fields_values(route_target, custom_field_data)
|
||||||
|
|
|
@ -1,7 +1,12 @@
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from ipam.models import VRF
|
from ipam.models import VRF
|
||||||
from startup_script_utils import load_yaml, pop_custom_fields, set_custom_fields_values
|
from startup_script_utils import (
|
||||||
|
load_yaml,
|
||||||
|
pop_custom_fields,
|
||||||
|
set_custom_fields_values,
|
||||||
|
split_params,
|
||||||
|
)
|
||||||
from tenancy.models import Tenant
|
from tenancy.models import Tenant
|
||||||
|
|
||||||
vrfs = load_yaml("/opt/netbox/initializers/vrfs.yml")
|
vrfs = load_yaml("/opt/netbox/initializers/vrfs.yml")
|
||||||
|
@ -9,6 +14,7 @@ vrfs = load_yaml("/opt/netbox/initializers/vrfs.yml")
|
||||||
if vrfs is None:
|
if vrfs is None:
|
||||||
sys.exit()
|
sys.exit()
|
||||||
|
|
||||||
|
match_params = ["name", "rd"]
|
||||||
optional_assocs = {"tenant": (Tenant, "name")}
|
optional_assocs = {"tenant": (Tenant, "name")}
|
||||||
|
|
||||||
for params in vrfs:
|
for params in vrfs:
|
||||||
|
@ -21,9 +27,10 @@ for params in vrfs:
|
||||||
|
|
||||||
params[assoc] = model.objects.get(**query)
|
params[assoc] = model.objects.get(**query)
|
||||||
|
|
||||||
vrf, created = VRF.objects.get_or_create(**params)
|
matching_params, defaults = split_params(params)
|
||||||
|
vrf, created = VRF.objects.get_or_create(**matching_params, defaults=defaults)
|
||||||
|
|
||||||
if created:
|
if created:
|
||||||
set_custom_fields_values(vrf, custom_field_data)
|
|
||||||
|
|
||||||
print("📦 Created VRF", vrf.name)
|
print("📦 Created VRF", vrf.name)
|
||||||
|
|
||||||
|
set_custom_fields_values(vrf, custom_field_data)
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from ipam.models import RIR
|
from ipam.models import RIR
|
||||||
from startup_script_utils import load_yaml
|
from startup_script_utils import load_yaml, split_params
|
||||||
|
|
||||||
rirs = load_yaml("/opt/netbox/initializers/rirs.yml")
|
rirs = load_yaml("/opt/netbox/initializers/rirs.yml")
|
||||||
|
|
||||||
|
@ -9,7 +9,8 @@ if rirs is None:
|
||||||
sys.exit()
|
sys.exit()
|
||||||
|
|
||||||
for params in rirs:
|
for params in rirs:
|
||||||
rir, created = RIR.objects.get_or_create(**params)
|
matching_params, defaults = split_params(params)
|
||||||
|
rir, created = RIR.objects.get_or_create(**matching_params, defaults=defaults)
|
||||||
|
|
||||||
if created:
|
if created:
|
||||||
print("🗺️ Created RIR", rir.name)
|
print("🗺️ Created RIR", rir.name)
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from ipam.models import ASN, RIR
|
from ipam.models import ASN, RIR
|
||||||
from startup_script_utils import load_yaml
|
from startup_script_utils import load_yaml, split_params
|
||||||
from tenancy.models import Tenant
|
from tenancy.models import Tenant
|
||||||
|
|
||||||
asns = load_yaml("/opt/netbox/initializers/asns.yml")
|
asns = load_yaml("/opt/netbox/initializers/asns.yml")
|
||||||
|
@ -9,8 +9,8 @@ asns = load_yaml("/opt/netbox/initializers/asns.yml")
|
||||||
if asns is None:
|
if asns is None:
|
||||||
sys.exit()
|
sys.exit()
|
||||||
|
|
||||||
|
match_params = ["asn", "rir"]
|
||||||
required_assocs = {"rir": (RIR, "name")}
|
required_assocs = {"rir": (RIR, "name")}
|
||||||
|
|
||||||
optional_assocs = {"tenant": (Tenant, "name")}
|
optional_assocs = {"tenant": (Tenant, "name")}
|
||||||
|
|
||||||
for params in asns:
|
for params in asns:
|
||||||
|
@ -27,7 +27,8 @@ for params in asns:
|
||||||
|
|
||||||
params[assoc] = model.objects.get(**query)
|
params[assoc] = model.objects.get(**query)
|
||||||
|
|
||||||
asn, created = ASN.objects.get_or_create(**params)
|
matching_params, defaults = split_params(params, match_params)
|
||||||
|
asn, created = ASN.objects.get_or_create(**matching_params, defaults=defaults)
|
||||||
|
|
||||||
if created:
|
if created:
|
||||||
print(f"🔡 Created ASN {asn.asn}")
|
print(f"🔡 Created ASN {asn.asn}")
|
||||||
|
|
|
@ -2,7 +2,12 @@ import sys
|
||||||
|
|
||||||
from ipam.models import RIR, Aggregate
|
from ipam.models import RIR, Aggregate
|
||||||
from netaddr import IPNetwork
|
from netaddr import IPNetwork
|
||||||
from startup_script_utils import load_yaml, pop_custom_fields, set_custom_fields_values
|
from startup_script_utils import (
|
||||||
|
load_yaml,
|
||||||
|
pop_custom_fields,
|
||||||
|
set_custom_fields_values,
|
||||||
|
split_params,
|
||||||
|
)
|
||||||
from tenancy.models import Tenant
|
from tenancy.models import Tenant
|
||||||
|
|
||||||
aggregates = load_yaml("/opt/netbox/initializers/aggregates.yml")
|
aggregates = load_yaml("/opt/netbox/initializers/aggregates.yml")
|
||||||
|
@ -10,8 +15,8 @@ aggregates = load_yaml("/opt/netbox/initializers/aggregates.yml")
|
||||||
if aggregates is None:
|
if aggregates is None:
|
||||||
sys.exit()
|
sys.exit()
|
||||||
|
|
||||||
|
match_params = ["prefix", "rir"]
|
||||||
required_assocs = {"rir": (RIR, "name")}
|
required_assocs = {"rir": (RIR, "name")}
|
||||||
|
|
||||||
optional_assocs = {
|
optional_assocs = {
|
||||||
"tenant": (Tenant, "name"),
|
"tenant": (Tenant, "name"),
|
||||||
}
|
}
|
||||||
|
@ -34,9 +39,10 @@ for params in aggregates:
|
||||||
|
|
||||||
params[assoc] = model.objects.get(**query)
|
params[assoc] = model.objects.get(**query)
|
||||||
|
|
||||||
aggregate, created = Aggregate.objects.get_or_create(**params)
|
matching_params, defaults = split_params(params, match_params)
|
||||||
|
aggregate, created = Aggregate.objects.get_or_create(**matching_params, defaults=defaults)
|
||||||
|
|
||||||
if created:
|
if created:
|
||||||
set_custom_fields_values(aggregate, custom_field_data)
|
|
||||||
|
|
||||||
print("🗞️ Created Aggregate", aggregate.prefix)
|
print("🗞️ Created Aggregate", aggregate.prefix)
|
||||||
|
|
||||||
|
set_custom_fields_values(aggregate, custom_field_data)
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from ipam.models import Role
|
from ipam.models import Role
|
||||||
from startup_script_utils import load_yaml
|
from startup_script_utils import load_yaml, split_params
|
||||||
|
|
||||||
roles = load_yaml("/opt/netbox/initializers/prefix_vlan_roles.yml")
|
roles = load_yaml("/opt/netbox/initializers/prefix_vlan_roles.yml")
|
||||||
|
|
||||||
|
@ -9,7 +9,8 @@ if roles is None:
|
||||||
sys.exit()
|
sys.exit()
|
||||||
|
|
||||||
for params in roles:
|
for params in roles:
|
||||||
role, created = Role.objects.get_or_create(**params)
|
matching_params, defaults = split_params(params)
|
||||||
|
role, created = Role.objects.get_or_create(**matching_params, defaults=defaults)
|
||||||
|
|
||||||
if created:
|
if created:
|
||||||
print("⛹️ Created Prefix/VLAN Role", role.name)
|
print("⛹️ Created Prefix/VLAN Role", role.name)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from startup_script_utils import load_yaml
|
from startup_script_utils import load_yaml, split_params
|
||||||
from virtualization.models import ClusterType
|
from virtualization.models import ClusterType
|
||||||
|
|
||||||
cluster_types = load_yaml("/opt/netbox/initializers/cluster_types.yml")
|
cluster_types = load_yaml("/opt/netbox/initializers/cluster_types.yml")
|
||||||
|
@ -9,7 +9,8 @@ if cluster_types is None:
|
||||||
sys.exit()
|
sys.exit()
|
||||||
|
|
||||||
for params in cluster_types:
|
for params in cluster_types:
|
||||||
cluster_type, created = ClusterType.objects.get_or_create(**params)
|
matching_params, defaults = split_params(params)
|
||||||
|
cluster_type, created = ClusterType.objects.get_or_create(**matching_params, defaults=defaults)
|
||||||
|
|
||||||
if created:
|
if created:
|
||||||
print("🧰 Created Cluster Type", cluster_type.name)
|
print("🧰 Created Cluster Type", cluster_type.name)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from startup_script_utils import load_yaml
|
from startup_script_utils import load_yaml, split_params
|
||||||
from virtualization.models import ClusterGroup
|
from virtualization.models import ClusterGroup
|
||||||
|
|
||||||
cluster_groups = load_yaml("/opt/netbox/initializers/cluster_groups.yml")
|
cluster_groups = load_yaml("/opt/netbox/initializers/cluster_groups.yml")
|
||||||
|
@ -9,7 +9,10 @@ if cluster_groups is None:
|
||||||
sys.exit()
|
sys.exit()
|
||||||
|
|
||||||
for params in cluster_groups:
|
for params in cluster_groups:
|
||||||
cluster_group, created = ClusterGroup.objects.get_or_create(**params)
|
matching_params, defaults = split_params(params)
|
||||||
|
cluster_group, created = ClusterGroup.objects.get_or_create(
|
||||||
|
**matching_params, defaults=defaults
|
||||||
|
)
|
||||||
|
|
||||||
if created:
|
if created:
|
||||||
print("🗄️ Created Cluster Group", cluster_group.name)
|
print("🗄️ Created Cluster Group", cluster_group.name)
|
||||||
|
|
|
@ -1,7 +1,12 @@
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from dcim.models import Site
|
from dcim.models import Site
|
||||||
from startup_script_utils import load_yaml, pop_custom_fields, set_custom_fields_values
|
from startup_script_utils import (
|
||||||
|
load_yaml,
|
||||||
|
pop_custom_fields,
|
||||||
|
set_custom_fields_values,
|
||||||
|
split_params,
|
||||||
|
)
|
||||||
from tenancy.models import Tenant
|
from tenancy.models import Tenant
|
||||||
from virtualization.models import Cluster, ClusterGroup, ClusterType
|
from virtualization.models import Cluster, ClusterGroup, ClusterType
|
||||||
|
|
||||||
|
@ -10,8 +15,8 @@ clusters = load_yaml("/opt/netbox/initializers/clusters.yml")
|
||||||
if clusters is None:
|
if clusters is None:
|
||||||
sys.exit()
|
sys.exit()
|
||||||
|
|
||||||
|
match_params = ["name", "type"]
|
||||||
required_assocs = {"type": (ClusterType, "name")}
|
required_assocs = {"type": (ClusterType, "name")}
|
||||||
|
|
||||||
optional_assocs = {
|
optional_assocs = {
|
||||||
"site": (Site, "name"),
|
"site": (Site, "name"),
|
||||||
"group": (ClusterGroup, "name"),
|
"group": (ClusterGroup, "name"),
|
||||||
|
@ -34,9 +39,10 @@ for params in clusters:
|
||||||
|
|
||||||
params[assoc] = model.objects.get(**query)
|
params[assoc] = model.objects.get(**query)
|
||||||
|
|
||||||
cluster, created = Cluster.objects.get_or_create(**params)
|
matching_params, defaults = split_params(params, match_params)
|
||||||
|
cluster, created = Cluster.objects.get_or_create(**matching_params, defaults=defaults)
|
||||||
|
|
||||||
if created:
|
if created:
|
||||||
set_custom_fields_values(cluster, custom_field_data)
|
|
||||||
|
|
||||||
print("🗄️ Created cluster", cluster.name)
|
print("🗄️ Created cluster", cluster.name)
|
||||||
|
|
||||||
|
set_custom_fields_values(cluster, custom_field_data)
|
||||||
|
|
|
@ -2,7 +2,12 @@ import sys
|
||||||
|
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
from ipam.models import VLANGroup
|
from ipam.models import VLANGroup
|
||||||
from startup_script_utils import load_yaml, pop_custom_fields, set_custom_fields_values
|
from startup_script_utils import (
|
||||||
|
load_yaml,
|
||||||
|
pop_custom_fields,
|
||||||
|
set_custom_fields_values,
|
||||||
|
split_params,
|
||||||
|
)
|
||||||
|
|
||||||
vlan_groups = load_yaml("/opt/netbox/initializers/vlan_groups.yml")
|
vlan_groups = load_yaml("/opt/netbox/initializers/vlan_groups.yml")
|
||||||
|
|
||||||
|
@ -32,9 +37,11 @@ for params in vlan_groups:
|
||||||
)
|
)
|
||||||
continue
|
continue
|
||||||
params["scope_id"] = ct.model_class().objects.get(**query).id
|
params["scope_id"] = ct.model_class().objects.get(**query).id
|
||||||
vlan_group, created = VLANGroup.objects.get_or_create(**params)
|
|
||||||
|
matching_params, defaults = split_params(params)
|
||||||
|
vlan_group, created = VLANGroup.objects.get_or_create(**matching_params, defaults=defaults)
|
||||||
|
|
||||||
if created:
|
if created:
|
||||||
set_custom_fields_values(vlan_group, custom_field_data)
|
|
||||||
|
|
||||||
print("🏘️ Created VLAN Group", vlan_group.name)
|
print("🏘️ Created VLAN Group", vlan_group.name)
|
||||||
|
|
||||||
|
set_custom_fields_values(vlan_group, custom_field_data)
|
||||||
|
|
|
@ -2,7 +2,12 @@ import sys
|
||||||
|
|
||||||
from dcim.models import Site
|
from dcim.models import Site
|
||||||
from ipam.models import VLAN, Role, VLANGroup
|
from ipam.models import VLAN, Role, VLANGroup
|
||||||
from startup_script_utils import load_yaml, pop_custom_fields, set_custom_fields_values
|
from startup_script_utils import (
|
||||||
|
load_yaml,
|
||||||
|
pop_custom_fields,
|
||||||
|
set_custom_fields_values,
|
||||||
|
split_params,
|
||||||
|
)
|
||||||
from tenancy.models import Tenant, TenantGroup
|
from tenancy.models import Tenant, TenantGroup
|
||||||
|
|
||||||
vlans = load_yaml("/opt/netbox/initializers/vlans.yml")
|
vlans = load_yaml("/opt/netbox/initializers/vlans.yml")
|
||||||
|
@ -10,6 +15,7 @@ vlans = load_yaml("/opt/netbox/initializers/vlans.yml")
|
||||||
if vlans is None:
|
if vlans is None:
|
||||||
sys.exit()
|
sys.exit()
|
||||||
|
|
||||||
|
match_params = ["name", "vid"]
|
||||||
optional_assocs = {
|
optional_assocs = {
|
||||||
"site": (Site, "name"),
|
"site": (Site, "name"),
|
||||||
"tenant": (Tenant, "name"),
|
"tenant": (Tenant, "name"),
|
||||||
|
@ -28,9 +34,10 @@ for params in vlans:
|
||||||
|
|
||||||
params[assoc] = model.objects.get(**query)
|
params[assoc] = model.objects.get(**query)
|
||||||
|
|
||||||
vlan, created = VLAN.objects.get_or_create(**params)
|
matching_params, defaults = split_params(params, match_params)
|
||||||
|
vlan, created = VLAN.objects.get_or_create(**matching_params, defaults=defaults)
|
||||||
|
|
||||||
if created:
|
if created:
|
||||||
set_custom_fields_values(vlan, custom_field_data)
|
|
||||||
|
|
||||||
print("🏠 Created VLAN", vlan.name)
|
print("🏠 Created VLAN", vlan.name)
|
||||||
|
|
||||||
|
set_custom_fields_values(vlan, custom_field_data)
|
||||||
|
|
|
@ -1,7 +1,12 @@
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from dcim.models import DeviceRole, Platform
|
from dcim.models import DeviceRole, Platform
|
||||||
from startup_script_utils import load_yaml, pop_custom_fields, set_custom_fields_values
|
from startup_script_utils import (
|
||||||
|
load_yaml,
|
||||||
|
pop_custom_fields,
|
||||||
|
set_custom_fields_values,
|
||||||
|
split_params,
|
||||||
|
)
|
||||||
from tenancy.models import Tenant
|
from tenancy.models import Tenant
|
||||||
from virtualization.models import Cluster, VirtualMachine
|
from virtualization.models import Cluster, VirtualMachine
|
||||||
|
|
||||||
|
@ -10,8 +15,8 @@ virtual_machines = load_yaml("/opt/netbox/initializers/virtual_machines.yml")
|
||||||
if virtual_machines is None:
|
if virtual_machines is None:
|
||||||
sys.exit()
|
sys.exit()
|
||||||
|
|
||||||
|
match_params = ["cluster", "name"]
|
||||||
required_assocs = {"cluster": (Cluster, "name")}
|
required_assocs = {"cluster": (Cluster, "name")}
|
||||||
|
|
||||||
optional_assocs = {
|
optional_assocs = {
|
||||||
"tenant": (Tenant, "name"),
|
"tenant": (Tenant, "name"),
|
||||||
"platform": (Platform, "name"),
|
"platform": (Platform, "name"),
|
||||||
|
@ -38,9 +43,12 @@ for params in virtual_machines:
|
||||||
|
|
||||||
params[assoc] = model.objects.get(**query)
|
params[assoc] = model.objects.get(**query)
|
||||||
|
|
||||||
virtual_machine, created = VirtualMachine.objects.get_or_create(**params)
|
matching_params, defaults = split_params(params, match_params)
|
||||||
|
virtual_machine, created = VirtualMachine.objects.get_or_create(
|
||||||
|
**matching_params, defaults=defaults
|
||||||
|
)
|
||||||
|
|
||||||
if created:
|
if created:
|
||||||
set_custom_fields_values(virtual_machine, custom_field_data)
|
|
||||||
|
|
||||||
print("🖥️ Created virtual machine", virtual_machine.name)
|
print("🖥️ Created virtual machine", virtual_machine.name)
|
||||||
|
|
||||||
|
set_custom_fields_values(virtual_machine, custom_field_data)
|
||||||
|
|
|
@ -1,6 +1,11 @@
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from startup_script_utils import load_yaml, pop_custom_fields, set_custom_fields_values
|
from startup_script_utils import (
|
||||||
|
load_yaml,
|
||||||
|
pop_custom_fields,
|
||||||
|
set_custom_fields_values,
|
||||||
|
split_params,
|
||||||
|
)
|
||||||
from virtualization.models import VirtualMachine, VMInterface
|
from virtualization.models import VirtualMachine, VMInterface
|
||||||
|
|
||||||
interfaces = load_yaml("/opt/netbox/initializers/virtualization_interfaces.yml")
|
interfaces = load_yaml("/opt/netbox/initializers/virtualization_interfaces.yml")
|
||||||
|
@ -8,6 +13,7 @@ interfaces = load_yaml("/opt/netbox/initializers/virtualization_interfaces.yml")
|
||||||
if interfaces is None:
|
if interfaces is None:
|
||||||
sys.exit()
|
sys.exit()
|
||||||
|
|
||||||
|
match_params = ["name", "virtual_machine"]
|
||||||
required_assocs = {"virtual_machine": (VirtualMachine, "name")}
|
required_assocs = {"virtual_machine": (VirtualMachine, "name")}
|
||||||
|
|
||||||
for params in interfaces:
|
for params in interfaces:
|
||||||
|
@ -19,9 +25,10 @@ for params in interfaces:
|
||||||
|
|
||||||
params[assoc] = model.objects.get(**query)
|
params[assoc] = model.objects.get(**query)
|
||||||
|
|
||||||
interface, created = VMInterface.objects.get_or_create(**params)
|
matching_params, defaults = split_params(params, match_params)
|
||||||
|
interface, created = VMInterface.objects.get_or_create(**matching_params, defaults=defaults)
|
||||||
|
|
||||||
if created:
|
if created:
|
||||||
set_custom_fields_values(interface, custom_field_data)
|
|
||||||
|
|
||||||
print("🧷 Created interface", interface.name, interface.virtual_machine.name)
|
print("🧷 Created interface", interface.name, interface.virtual_machine.name)
|
||||||
|
|
||||||
|
set_custom_fields_values(interface, custom_field_data)
|
||||||
|
|
|
@ -3,7 +3,12 @@ import sys
|
||||||
from dcim.models import Site
|
from dcim.models import Site
|
||||||
from ipam.models import VLAN, VRF, Prefix, Role
|
from ipam.models import VLAN, VRF, Prefix, Role
|
||||||
from netaddr import IPNetwork
|
from netaddr import IPNetwork
|
||||||
from startup_script_utils import load_yaml, pop_custom_fields, set_custom_fields_values
|
from startup_script_utils import (
|
||||||
|
load_yaml,
|
||||||
|
pop_custom_fields,
|
||||||
|
set_custom_fields_values,
|
||||||
|
split_params,
|
||||||
|
)
|
||||||
from tenancy.models import Tenant, TenantGroup
|
from tenancy.models import Tenant, TenantGroup
|
||||||
|
|
||||||
prefixes = load_yaml("/opt/netbox/initializers/prefixes.yml")
|
prefixes = load_yaml("/opt/netbox/initializers/prefixes.yml")
|
||||||
|
@ -11,6 +16,7 @@ prefixes = load_yaml("/opt/netbox/initializers/prefixes.yml")
|
||||||
if prefixes is None:
|
if prefixes is None:
|
||||||
sys.exit()
|
sys.exit()
|
||||||
|
|
||||||
|
match_params = ["prefix", "site", "vrf", "vlan"]
|
||||||
optional_assocs = {
|
optional_assocs = {
|
||||||
"site": (Site, "name"),
|
"site": (Site, "name"),
|
||||||
"tenant": (Tenant, "name"),
|
"tenant": (Tenant, "name"),
|
||||||
|
@ -31,9 +37,10 @@ for params in prefixes:
|
||||||
query = {field: params.pop(assoc)}
|
query = {field: params.pop(assoc)}
|
||||||
params[assoc] = model.objects.get(**query)
|
params[assoc] = model.objects.get(**query)
|
||||||
|
|
||||||
prefix, created = Prefix.objects.get_or_create(**params)
|
matching_params, defaults = split_params(params, match_params)
|
||||||
|
prefix, created = Prefix.objects.get_or_create(**matching_params, defaults=defaults)
|
||||||
|
|
||||||
if created:
|
if created:
|
||||||
set_custom_fields_values(prefix, custom_field_data)
|
|
||||||
|
|
||||||
print("📌 Created Prefix", prefix.prefix)
|
print("📌 Created Prefix", prefix.prefix)
|
||||||
|
|
||||||
|
set_custom_fields_values(prefix, custom_field_data)
|
||||||
|
|
|
@ -5,7 +5,12 @@ from django.contrib.contenttypes.models import ContentType
|
||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
from ipam.models import VRF, IPAddress
|
from ipam.models import VRF, IPAddress
|
||||||
from netaddr import IPNetwork
|
from netaddr import IPNetwork
|
||||||
from startup_script_utils import load_yaml, pop_custom_fields, set_custom_fields_values
|
from startup_script_utils import (
|
||||||
|
load_yaml,
|
||||||
|
pop_custom_fields,
|
||||||
|
set_custom_fields_values,
|
||||||
|
split_params,
|
||||||
|
)
|
||||||
from tenancy.models import Tenant
|
from tenancy.models import Tenant
|
||||||
from virtualization.models import VirtualMachine, VMInterface
|
from virtualization.models import VirtualMachine, VMInterface
|
||||||
|
|
||||||
|
@ -14,10 +19,11 @@ ip_addresses = load_yaml("/opt/netbox/initializers/ip_addresses.yml")
|
||||||
if ip_addresses is None:
|
if ip_addresses is None:
|
||||||
sys.exit()
|
sys.exit()
|
||||||
|
|
||||||
|
match_params = ["address", "vrf"]
|
||||||
optional_assocs = {
|
optional_assocs = {
|
||||||
"tenant": (Tenant, "name"),
|
"tenant": (Tenant, "name"),
|
||||||
"vrf": (VRF, "name"),
|
"vrf": (VRF, "name"),
|
||||||
"interface": (None, None),
|
"interface": (Interface, "name"),
|
||||||
}
|
}
|
||||||
|
|
||||||
vm_interface_ct = ContentType.objects.filter(
|
vm_interface_ct = ContentType.objects.filter(
|
||||||
|
@ -55,9 +61,10 @@ for params in ip_addresses:
|
||||||
|
|
||||||
params[assoc] = model.objects.get(**query)
|
params[assoc] = model.objects.get(**query)
|
||||||
|
|
||||||
ip_address, created = IPAddress.objects.get_or_create(**params)
|
matching_params, defaults = split_params(params, match_params)
|
||||||
|
ip_address, created = IPAddress.objects.get_or_create(**matching_params, defaults=defaults)
|
||||||
|
|
||||||
if created:
|
if created:
|
||||||
set_custom_fields_values(ip_address, custom_field_data)
|
|
||||||
|
|
||||||
print("🧬 Created IP Address", ip_address.address)
|
print("🧬 Created IP Address", ip_address.address)
|
||||||
|
|
||||||
|
set_custom_fields_values(ip_address, custom_field_data)
|
||||||
|
|
|
@ -2,7 +2,7 @@ import sys
|
||||||
|
|
||||||
from dcim.models import Device
|
from dcim.models import Device
|
||||||
from ipam.models import Service
|
from ipam.models import Service
|
||||||
from startup_script_utils import load_yaml
|
from startup_script_utils import load_yaml, split_params
|
||||||
from virtualization.models import VirtualMachine
|
from virtualization.models import VirtualMachine
|
||||||
|
|
||||||
services = load_yaml("/opt/netbox/initializers/services.yml")
|
services = load_yaml("/opt/netbox/initializers/services.yml")
|
||||||
|
@ -10,6 +10,7 @@ services = load_yaml("/opt/netbox/initializers/services.yml")
|
||||||
if services is None:
|
if services is None:
|
||||||
sys.exit()
|
sys.exit()
|
||||||
|
|
||||||
|
match_params = ["name", "device", "virtual_machine"]
|
||||||
optional_assocs = {
|
optional_assocs = {
|
||||||
"device": (Device, "name"),
|
"device": (Device, "name"),
|
||||||
"virtual_machine": (VirtualMachine, "name"),
|
"virtual_machine": (VirtualMachine, "name"),
|
||||||
|
@ -24,6 +25,7 @@ for params in services:
|
||||||
|
|
||||||
params[assoc] = model.objects.get(**query)
|
params[assoc] = model.objects.get(**query)
|
||||||
|
|
||||||
|
matching_params, defaults = split_params(params, match_params)
|
||||||
service, created = Service.objects.get_or_create(**params)
|
service, created = Service.objects.get_or_create(**params)
|
||||||
|
|
||||||
if created:
|
if created:
|
||||||
|
|
|
@ -1,7 +1,12 @@
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from circuits.models import Provider
|
from circuits.models import Provider
|
||||||
from startup_script_utils import load_yaml, pop_custom_fields, set_custom_fields_values
|
from startup_script_utils import (
|
||||||
|
load_yaml,
|
||||||
|
pop_custom_fields,
|
||||||
|
set_custom_fields_values,
|
||||||
|
split_params,
|
||||||
|
)
|
||||||
|
|
||||||
providers = load_yaml("/opt/netbox/initializers/providers.yml")
|
providers = load_yaml("/opt/netbox/initializers/providers.yml")
|
||||||
|
|
||||||
|
@ -11,9 +16,10 @@ if providers is None:
|
||||||
for params in providers:
|
for params in providers:
|
||||||
custom_field_data = pop_custom_fields(params)
|
custom_field_data = pop_custom_fields(params)
|
||||||
|
|
||||||
provider, created = Provider.objects.get_or_create(**params)
|
matching_params, defaults = split_params(params)
|
||||||
|
provider, created = Provider.objects.get_or_create(**matching_params, defaults=defaults)
|
||||||
|
|
||||||
if created:
|
if created:
|
||||||
set_custom_fields_values(provider, custom_field_data)
|
|
||||||
|
|
||||||
print("📡 Created provider", provider.name)
|
print("📡 Created provider", provider.name)
|
||||||
|
|
||||||
|
set_custom_fields_values(provider, custom_field_data)
|
||||||
|
|
|
@ -1,7 +1,12 @@
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from circuits.models import CircuitType
|
from circuits.models import CircuitType
|
||||||
from startup_script_utils import load_yaml, pop_custom_fields, set_custom_fields_values
|
from startup_script_utils import (
|
||||||
|
load_yaml,
|
||||||
|
pop_custom_fields,
|
||||||
|
set_custom_fields_values,
|
||||||
|
split_params,
|
||||||
|
)
|
||||||
|
|
||||||
circuit_types = load_yaml("/opt/netbox/initializers/circuit_types.yml")
|
circuit_types = load_yaml("/opt/netbox/initializers/circuit_types.yml")
|
||||||
|
|
||||||
|
@ -11,9 +16,10 @@ if circuit_types is None:
|
||||||
for params in circuit_types:
|
for params in circuit_types:
|
||||||
custom_field_data = pop_custom_fields(params)
|
custom_field_data = pop_custom_fields(params)
|
||||||
|
|
||||||
circuit_type, created = CircuitType.objects.get_or_create(**params)
|
matching_params, defaults = split_params(params)
|
||||||
|
circuit_type, created = CircuitType.objects.get_or_create(**matching_params, defaults=defaults)
|
||||||
|
|
||||||
if created:
|
if created:
|
||||||
set_custom_fields_values(circuit_type, custom_field_data)
|
|
||||||
|
|
||||||
print("⚡ Created Circuit Type", circuit_type.name)
|
print("⚡ Created Circuit Type", circuit_type.name)
|
||||||
|
|
||||||
|
set_custom_fields_values(circuit_type, custom_field_data)
|
||||||
|
|
|
@ -1,7 +1,12 @@
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from circuits.models import Circuit, CircuitType, Provider
|
from circuits.models import Circuit, CircuitType, Provider
|
||||||
from startup_script_utils import load_yaml, pop_custom_fields, set_custom_fields_values
|
from startup_script_utils import (
|
||||||
|
load_yaml,
|
||||||
|
pop_custom_fields,
|
||||||
|
set_custom_fields_values,
|
||||||
|
split_params,
|
||||||
|
)
|
||||||
from tenancy.models import Tenant
|
from tenancy.models import Tenant
|
||||||
|
|
||||||
circuits = load_yaml("/opt/netbox/initializers/circuits.yml")
|
circuits = load_yaml("/opt/netbox/initializers/circuits.yml")
|
||||||
|
@ -9,8 +14,8 @@ circuits = load_yaml("/opt/netbox/initializers/circuits.yml")
|
||||||
if circuits is None:
|
if circuits is None:
|
||||||
sys.exit()
|
sys.exit()
|
||||||
|
|
||||||
|
match_params = ["cid", "provider", "type"]
|
||||||
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:
|
for params in circuits:
|
||||||
|
@ -29,9 +34,10 @@ for params in circuits:
|
||||||
|
|
||||||
params[assoc] = model.objects.get(**query)
|
params[assoc] = model.objects.get(**query)
|
||||||
|
|
||||||
circuit, created = Circuit.objects.get_or_create(**params)
|
matching_params, defaults = split_params(params, match_params)
|
||||||
|
circuit, created = Circuit.objects.get_or_create(**matching_params, defaults=defaults)
|
||||||
|
|
||||||
if created:
|
if created:
|
||||||
set_custom_fields_values(circuit, custom_field_data)
|
|
||||||
|
|
||||||
print("⚡ Created Circuit", circuit.cid)
|
print("⚡ Created Circuit", circuit.cid)
|
||||||
|
|
||||||
|
set_custom_fields_values(circuit, custom_field_data)
|
||||||
|
|
|
@ -0,0 +1,227 @@
|
||||||
|
import sys
|
||||||
|
from typing import Tuple
|
||||||
|
|
||||||
|
from circuits.models import Circuit, CircuitTermination, ProviderNetwork
|
||||||
|
from dcim.models import (
|
||||||
|
Cable,
|
||||||
|
ConsolePort,
|
||||||
|
ConsoleServerPort,
|
||||||
|
FrontPort,
|
||||||
|
Interface,
|
||||||
|
PowerFeed,
|
||||||
|
PowerOutlet,
|
||||||
|
PowerPanel,
|
||||||
|
PowerPort,
|
||||||
|
RearPort,
|
||||||
|
Site,
|
||||||
|
)
|
||||||
|
from django.contrib.contenttypes.models import ContentType
|
||||||
|
from django.db.models import Q
|
||||||
|
from startup_script_utils import load_yaml
|
||||||
|
|
||||||
|
CONSOLE_PORT_TERMINATION = ContentType.objects.get_for_model(ConsolePort)
|
||||||
|
CONSOLE_SERVER_PORT_TERMINATION = ContentType.objects.get_for_model(ConsoleServerPort)
|
||||||
|
FRONT_PORT_TERMINATION = ContentType.objects.get_for_model(FrontPort)
|
||||||
|
REAR_PORT_TERMINATION = ContentType.objects.get_for_model(RearPort)
|
||||||
|
FRONT_AND_REAR = [FRONT_PORT_TERMINATION, REAR_PORT_TERMINATION]
|
||||||
|
POWER_PORT_TERMINATION = ContentType.objects.get_for_model(PowerPort)
|
||||||
|
POWER_OUTLET_TERMINATION = ContentType.objects.get_for_model(PowerOutlet)
|
||||||
|
POWER_FEED_TERMINATION = ContentType.objects.get_for_model(PowerFeed)
|
||||||
|
POWER_TERMINATIONS = [POWER_PORT_TERMINATION, POWER_OUTLET_TERMINATION, POWER_FEED_TERMINATION]
|
||||||
|
|
||||||
|
VIRTUAL_INTERFACES = ["bridge", "lag", "virtual"]
|
||||||
|
|
||||||
|
|
||||||
|
def get_termination_object(params: dict, side: str):
|
||||||
|
klass = params.pop(f"termination_{side}_class")
|
||||||
|
name = params.pop(f"termination_{side}_name", None)
|
||||||
|
device = params.pop(f"termination_{side}_device", None)
|
||||||
|
feed_params = params.pop(f"termination_{side}_feed", None)
|
||||||
|
circuit_params = params.pop(f"termination_{side}_circuit", {})
|
||||||
|
|
||||||
|
if name and device:
|
||||||
|
termination = klass.objects.get(name=name, device__name=device)
|
||||||
|
return termination
|
||||||
|
elif feed_params:
|
||||||
|
q = {
|
||||||
|
"name": feed_params["power_panel"]["name"],
|
||||||
|
"site__name": feed_params["power_panel"]["site"],
|
||||||
|
}
|
||||||
|
power_panel = PowerPanel.objects.get(**q)
|
||||||
|
termination = PowerFeed.objects.get(name=feed_params["name"], power_panel=power_panel)
|
||||||
|
return termination
|
||||||
|
elif circuit_params:
|
||||||
|
circuit = Circuit.objects.get(cid=circuit_params.pop("cid"))
|
||||||
|
term_side = circuit_params.pop("term_side").upper()
|
||||||
|
|
||||||
|
site_name = circuit_params.pop("site", None)
|
||||||
|
provider_network = circuit_params.pop("provider_network", None)
|
||||||
|
|
||||||
|
if site_name:
|
||||||
|
circuit_params["site"] = Site.objects.get(name=site_name)
|
||||||
|
elif provider_network:
|
||||||
|
circuit_params["provider_network"] = ProviderNetwork.objects.get(name=provider_network)
|
||||||
|
else:
|
||||||
|
raise ValueError(
|
||||||
|
f"⚠️ Missing one of required parameters: 'site' or 'provider_network' "
|
||||||
|
f"for side {term_side} of circuit {circuit}"
|
||||||
|
)
|
||||||
|
|
||||||
|
termination, created = CircuitTermination.objects.get_or_create(
|
||||||
|
circuit=circuit, term_side=term_side, defaults=circuit_params
|
||||||
|
)
|
||||||
|
if created:
|
||||||
|
print(f"⚡ Created new CircuitTermination {termination}")
|
||||||
|
|
||||||
|
return termination
|
||||||
|
|
||||||
|
raise ValueError(
|
||||||
|
f"⚠️ Missing parameters for termination_{side}. "
|
||||||
|
"Need termination_{side}_name AND termination_{side}_device OR termination_{side}_circuit"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def get_termination_class_by_name(port_class: str):
|
||||||
|
if not port_class:
|
||||||
|
return Interface
|
||||||
|
|
||||||
|
return globals()[port_class]
|
||||||
|
|
||||||
|
|
||||||
|
def cable_in_cables(term_a: tuple, term_b: tuple) -> bool:
|
||||||
|
"""Check if cable exist for given terminations.
|
||||||
|
Each tuple should consist termination object and termination type
|
||||||
|
"""
|
||||||
|
|
||||||
|
cable = Cable.objects.filter(
|
||||||
|
Q(
|
||||||
|
termination_a_id=term_a[0].id,
|
||||||
|
termination_a_type=term_a[1],
|
||||||
|
termination_b_id=term_b[0].id,
|
||||||
|
termination_b_type=term_b[1],
|
||||||
|
)
|
||||||
|
| Q(
|
||||||
|
termination_a_id=term_b[0].id,
|
||||||
|
termination_a_type=term_b[1],
|
||||||
|
termination_b_id=term_a[0].id,
|
||||||
|
termination_b_type=term_a[1],
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return cable.exists()
|
||||||
|
|
||||||
|
|
||||||
|
def check_termination_types(type_a, type_b) -> Tuple[bool, str]:
|
||||||
|
if type_a in POWER_TERMINATIONS and type_b in POWER_TERMINATIONS:
|
||||||
|
if type_a == type_b:
|
||||||
|
return False, "Can't connect the same power terminations together"
|
||||||
|
elif (
|
||||||
|
type_a == POWER_OUTLET_TERMINATION
|
||||||
|
and type_b == POWER_FEED_TERMINATION
|
||||||
|
or type_a == POWER_FEED_TERMINATION
|
||||||
|
and type_b == POWER_OUTLET_TERMINATION
|
||||||
|
):
|
||||||
|
return False, "PowerOutlet can't be connected with PowerFeed"
|
||||||
|
elif type_a in POWER_TERMINATIONS or type_b in POWER_TERMINATIONS:
|
||||||
|
return False, "Can't mix power terminations with port terminations"
|
||||||
|
elif type_a in FRONT_AND_REAR or type_b in FRONT_AND_REAR:
|
||||||
|
return True, ""
|
||||||
|
elif (
|
||||||
|
type_a == CONSOLE_PORT_TERMINATION
|
||||||
|
and type_b != CONSOLE_SERVER_PORT_TERMINATION
|
||||||
|
or type_b == CONSOLE_PORT_TERMINATION
|
||||||
|
and type_a != CONSOLE_SERVER_PORT_TERMINATION
|
||||||
|
):
|
||||||
|
return False, "ConsolePorts can only be connected to ConsoleServerPorts or Front/Rear ports"
|
||||||
|
return True, ""
|
||||||
|
|
||||||
|
|
||||||
|
def get_cable_name(termination_a: tuple, termination_b: tuple) -> str:
|
||||||
|
"""Returns name of a cable in format:
|
||||||
|
device_a interface_a <---> interface_b device_b
|
||||||
|
or for circuits:
|
||||||
|
circuit_a termination_a <---> termination_b circuit_b
|
||||||
|
"""
|
||||||
|
cable_name = []
|
||||||
|
|
||||||
|
for is_side_b, termination in enumerate([termination_a, termination_b]):
|
||||||
|
try:
|
||||||
|
power_panel_id = getattr(termination[0], "power_panel_id", None)
|
||||||
|
if power_panel_id:
|
||||||
|
power_feed = PowerPanel.objects.get(id=power_panel_id)
|
||||||
|
segment = [f"{power_feed}", f"{termination[0]}"]
|
||||||
|
else:
|
||||||
|
segment = [f"{termination[0].device}", f"{termination[0]}"]
|
||||||
|
except AttributeError:
|
||||||
|
segment = [f"{termination[0].circuit.cid}", f"{termination[0]}"]
|
||||||
|
|
||||||
|
if is_side_b:
|
||||||
|
segment.reverse()
|
||||||
|
|
||||||
|
cable_name.append(" ".join(segment))
|
||||||
|
|
||||||
|
return " <---> ".join(cable_name)
|
||||||
|
|
||||||
|
|
||||||
|
def check_interface_types(*args):
|
||||||
|
for termination in args:
|
||||||
|
try:
|
||||||
|
if termination.type in VIRTUAL_INTERFACES:
|
||||||
|
raise Exception(
|
||||||
|
f"⚠️ Virtual interfaces are not supported for cabling. "
|
||||||
|
f"Termination {termination.device} {termination} {termination.type}"
|
||||||
|
)
|
||||||
|
except AttributeError:
|
||||||
|
# CircuitTermination doesn't have a type field
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def check_terminations_are_free(*args):
|
||||||
|
any_failed = False
|
||||||
|
for termination in args:
|
||||||
|
if termination.cable_id:
|
||||||
|
any_failed = True
|
||||||
|
print(
|
||||||
|
f"⚠️ Termination {termination} is already occupied "
|
||||||
|
f"with cable #{termination.cable_id}"
|
||||||
|
)
|
||||||
|
if any_failed:
|
||||||
|
raise Exception("⚠️ At least one end of the cable is already occupied.")
|
||||||
|
|
||||||
|
|
||||||
|
cables = load_yaml("/opt/netbox/initializers/cables.yml")
|
||||||
|
|
||||||
|
if cables is None:
|
||||||
|
sys.exit()
|
||||||
|
|
||||||
|
for params in cables:
|
||||||
|
params["termination_a_class"] = get_termination_class_by_name(params.get("termination_a_class"))
|
||||||
|
params["termination_b_class"] = get_termination_class_by_name(params.get("termination_b_class"))
|
||||||
|
|
||||||
|
term_a = get_termination_object(params, side="a")
|
||||||
|
term_b = get_termination_object(params, side="b")
|
||||||
|
|
||||||
|
check_interface_types(term_a, term_b)
|
||||||
|
|
||||||
|
term_a_ct = ContentType.objects.get_for_model(term_a)
|
||||||
|
term_b_ct = ContentType.objects.get_for_model(term_b)
|
||||||
|
|
||||||
|
types_ok, msg = check_termination_types(term_a_ct, term_b_ct)
|
||||||
|
cable_name = get_cable_name((term_a, term_a_ct), (term_b, term_b_ct))
|
||||||
|
|
||||||
|
if not types_ok:
|
||||||
|
print(f"⚠️ Invalid termination types for {cable_name}. {msg}")
|
||||||
|
continue
|
||||||
|
|
||||||
|
if cable_in_cables((term_a, term_a_ct), (term_b, term_b_ct)):
|
||||||
|
continue
|
||||||
|
|
||||||
|
check_terminations_are_free(term_a, term_b)
|
||||||
|
|
||||||
|
params["termination_a_id"] = term_a.id
|
||||||
|
params["termination_b_id"] = term_b.id
|
||||||
|
params["termination_a_type"] = term_a_ct
|
||||||
|
params["termination_b_type"] = term_b_ct
|
||||||
|
|
||||||
|
cable = Cable.objects.create(**params)
|
||||||
|
|
||||||
|
print(f"🧷 Created cable {cable} {cable_name}")
|
|
@ -0,0 +1,36 @@
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from startup_script_utils import (
|
||||||
|
load_yaml,
|
||||||
|
pop_custom_fields,
|
||||||
|
set_custom_fields_values,
|
||||||
|
split_params,
|
||||||
|
)
|
||||||
|
from tenancy.models import ContactGroup
|
||||||
|
|
||||||
|
contact_groups = load_yaml("/opt/netbox/initializers/contact_groups.yml")
|
||||||
|
|
||||||
|
if contact_groups is None:
|
||||||
|
sys.exit()
|
||||||
|
|
||||||
|
optional_assocs = {"parent": (ContactGroup, "name")}
|
||||||
|
|
||||||
|
for params in contact_groups:
|
||||||
|
custom_field_data = pop_custom_fields(params)
|
||||||
|
|
||||||
|
for assoc, details in optional_assocs.items():
|
||||||
|
if assoc in params:
|
||||||
|
model, field = details
|
||||||
|
query = {field: params.pop(assoc)}
|
||||||
|
|
||||||
|
params[assoc] = model.objects.get(**query)
|
||||||
|
|
||||||
|
matching_params, defaults = split_params(params)
|
||||||
|
contact_group, created = ContactGroup.objects.get_or_create(
|
||||||
|
**matching_params, defaults=defaults
|
||||||
|
)
|
||||||
|
|
||||||
|
if created:
|
||||||
|
print("🔳 Created Contact Group", contact_group.name)
|
||||||
|
|
||||||
|
set_custom_fields_values(contact_group, custom_field_data)
|
|
@ -0,0 +1,26 @@
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from startup_script_utils import (
|
||||||
|
load_yaml,
|
||||||
|
pop_custom_fields,
|
||||||
|
set_custom_fields_values,
|
||||||
|
split_params,
|
||||||
|
)
|
||||||
|
from tenancy.models import ContactRole
|
||||||
|
|
||||||
|
contact_roles = load_yaml("/opt/netbox/initializers/contact_roles.yml")
|
||||||
|
|
||||||
|
if contact_roles is None:
|
||||||
|
sys.exit()
|
||||||
|
|
||||||
|
|
||||||
|
for params in contact_roles:
|
||||||
|
custom_field_data = pop_custom_fields(params)
|
||||||
|
|
||||||
|
matching_params, defaults = split_params(params)
|
||||||
|
contact_role, created = ContactRole.objects.get_or_create(**matching_params, defaults=defaults)
|
||||||
|
|
||||||
|
if created:
|
||||||
|
print("🔳 Created Contact Role", contact_role.name)
|
||||||
|
|
||||||
|
set_custom_fields_values(contact_role, custom_field_data)
|
|
@ -0,0 +1,34 @@
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from startup_script_utils import (
|
||||||
|
load_yaml,
|
||||||
|
pop_custom_fields,
|
||||||
|
set_custom_fields_values,
|
||||||
|
split_params,
|
||||||
|
)
|
||||||
|
from tenancy.models import Contact, ContactGroup
|
||||||
|
|
||||||
|
contacts = load_yaml("/opt/netbox/initializers/contacts.yml")
|
||||||
|
|
||||||
|
if contacts is None:
|
||||||
|
sys.exit()
|
||||||
|
|
||||||
|
optional_assocs = {"group": (ContactGroup, "name")}
|
||||||
|
|
||||||
|
for params in contacts:
|
||||||
|
custom_field_data = pop_custom_fields(params)
|
||||||
|
|
||||||
|
for assoc, details in optional_assocs.items():
|
||||||
|
if assoc in params:
|
||||||
|
model, field = details
|
||||||
|
query = {field: params.pop(assoc)}
|
||||||
|
|
||||||
|
params[assoc] = model.objects.get(**query)
|
||||||
|
|
||||||
|
matching_params, defaults = split_params(params)
|
||||||
|
contact, created = Contact.objects.get_or_create(**matching_params, defaults=defaults)
|
||||||
|
|
||||||
|
if created:
|
||||||
|
print("👩💻 Created Contact", contact.name)
|
||||||
|
|
||||||
|
set_custom_fields_values(contact, custom_field_data)
|
|
@ -1,2 +1,3 @@
|
||||||
from .custom_fields import pop_custom_fields, set_custom_fields_values
|
from .custom_fields import pop_custom_fields, set_custom_fields_values
|
||||||
from .load_yaml import load_yaml
|
from .load_yaml import load_yaml
|
||||||
|
from .utils import split_params
|
||||||
|
|
|
@ -1,9 +1,38 @@
|
||||||
|
from django.contrib.contenttypes.models import ContentType
|
||||||
|
from django.core.exceptions import ObjectDoesNotExist
|
||||||
|
from extras.models import CustomField
|
||||||
|
|
||||||
|
|
||||||
def set_custom_fields_values(entity, custom_field_data):
|
def set_custom_fields_values(entity, custom_field_data):
|
||||||
if not custom_field_data:
|
if not custom_field_data:
|
||||||
return
|
return
|
||||||
|
|
||||||
entity.custom_field_data = custom_field_data
|
missing_cfs = []
|
||||||
return entity.save()
|
save = False
|
||||||
|
for key, value in custom_field_data.items():
|
||||||
|
try:
|
||||||
|
cf = CustomField.objects.get(name=key)
|
||||||
|
except ObjectDoesNotExist:
|
||||||
|
missing_cfs.append(key)
|
||||||
|
else:
|
||||||
|
ct = ContentType.objects.get_for_model(entity)
|
||||||
|
if ct not in cf.content_types.all():
|
||||||
|
print(
|
||||||
|
f"⚠️ Custom field {key} is not enabled for {entity}'s model!"
|
||||||
|
"Please check the 'on_objects' for that custom field in custom_fields.yml"
|
||||||
|
)
|
||||||
|
elif key not in entity.custom_field_data:
|
||||||
|
entity.custom_field_data[key] = value
|
||||||
|
save = True
|
||||||
|
|
||||||
|
if missing_cfs:
|
||||||
|
raise Exception(
|
||||||
|
f"⚠️ Custom field(s) '{missing_cfs}' requested for {entity} but not found in Netbox!"
|
||||||
|
"Please chceck the custom_fields.yml"
|
||||||
|
)
|
||||||
|
|
||||||
|
if save:
|
||||||
|
entity.save()
|
||||||
|
|
||||||
|
|
||||||
def pop_custom_fields(params):
|
def pop_custom_fields(params):
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
from typing import Tuple
|
||||||
|
|
||||||
|
|
||||||
|
def split_params(params: dict, unique_params: list = None) -> Tuple[dict, dict]:
|
||||||
|
"""Split params dict into dict with matching params and a dict with default values"""
|
||||||
|
|
||||||
|
if unique_params is None:
|
||||||
|
unique_params = ["name", "slug"]
|
||||||
|
|
||||||
|
matching_params = {}
|
||||||
|
for unique_param in unique_params:
|
||||||
|
param = params.pop(unique_param, None)
|
||||||
|
if param:
|
||||||
|
matching_params[unique_param] = param
|
||||||
|
return matching_params, params
|
Loading…
Reference in New Issue