Compare commits
No commits in common. "main" and "v4.6.3" have entirely different histories.
|
|
@ -43,7 +43,7 @@ ConfigParam(
|
||||||
field=forms.BooleanField, # or IntegerField, CharField, JSONField, SimpleArrayField
|
field=forms.BooleanField, # or IntegerField, CharField, JSONField, SimpleArrayField
|
||||||
# field_kwargs only when extra widget/validation config is needed:
|
# field_kwargs only when extra widget/validation config is needed:
|
||||||
field_kwargs={
|
field_kwargs={
|
||||||
'widget': forms.Textarea(attrs={'class': 'font-monospace'}),
|
'widget': forms.Textarea(attrs={'class': 'vLargeTextField'}),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -478,9 +478,9 @@ class MyModelTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||||
**File:** `netbox/<app>/tests/test_filtersets.py`
|
**File:** `netbox/<app>/tests/test_filtersets.py`
|
||||||
|
|
||||||
```python
|
```python
|
||||||
from utilities.testing import ChangeLoggedFilterSetTestMixin
|
from utilities.testing import ChangeLoggedFilterSetTests
|
||||||
|
|
||||||
class MyModelFilterSetTestCase(TestCase, ChangeLoggedFilterSetTestMixin):
|
class MyModelFilterSetTestCase(TestCase, ChangeLoggedFilterSetTests):
|
||||||
queryset = MyModel.objects.all()
|
queryset = MyModel.objects.all()
|
||||||
filterset = MyModelFilterSet
|
filterset = MyModelFilterSet
|
||||||
|
|
||||||
|
|
@ -496,7 +496,7 @@ class MyModelFilterSetTestCase(TestCase, ChangeLoggedFilterSetTestMixin):
|
||||||
# Test FK and FK_id filters
|
# Test FK and FK_id filters
|
||||||
```
|
```
|
||||||
|
|
||||||
`ChangeLoggedFilterSetTestMixin` provides standard tests for `id`, `created`, `last_updated`, `q` search, etc. Always mix it in.
|
`ChangeLoggedFilterSetTests` provides standard tests for `id`, `created`, `last_updated`, `q` search, etc. Always mix it in.
|
||||||
|
|
||||||
## Common Gotchas
|
## Common Gotchas
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ body:
|
||||||
attributes:
|
attributes:
|
||||||
label: NetBox version
|
label: NetBox version
|
||||||
description: What version of NetBox are you currently running?
|
description: What version of NetBox are you currently running?
|
||||||
|
placeholder: v4.6.3
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: dropdown
|
- type: dropdown
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@ body:
|
||||||
attributes:
|
attributes:
|
||||||
label: NetBox Version
|
label: NetBox Version
|
||||||
description: What version of NetBox are you currently running?
|
description: What version of NetBox are you currently running?
|
||||||
|
placeholder: v4.6.3
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: dropdown
|
- type: dropdown
|
||||||
|
|
@ -70,15 +71,3 @@ body:
|
||||||
placeholder: A TypeError exception was raised
|
placeholder: A TypeError exception was raised
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: Suspected Cause
|
|
||||||
description: >
|
|
||||||
If you have identified the likely root cause(s), please detail your findings
|
|
||||||
here (optional).
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: Proposed Fix
|
|
||||||
description: >
|
|
||||||
If you would like to propose a specific fix likely to resolve this issue, please
|
|
||||||
describe it here (optional).
|
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ body:
|
||||||
attributes:
|
attributes:
|
||||||
label: NetBox Version
|
label: NetBox Version
|
||||||
description: What version of NetBox are you currently running?
|
description: What version of NetBox are you currently running?
|
||||||
|
placeholder: v4.6.3
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: dropdown
|
- type: dropdown
|
||||||
|
|
|
||||||
|
|
@ -70,7 +70,7 @@ jobs:
|
||||||
- name: Check Python linting & PEP8 compliance
|
- name: Check Python linting & PEP8 compliance
|
||||||
uses: astral-sh/ruff-action@0ce1b0bf8b818ef400413f810f8a11cdbda0034b # v4.0.0
|
uses: astral-sh/ruff-action@0ce1b0bf8b818ef400413f810f8a11cdbda0034b # v4.0.0
|
||||||
with:
|
with:
|
||||||
version: "0.15.20"
|
version: "0.15.10"
|
||||||
args: "check --output-format=github"
|
args: "check --output-format=github"
|
||||||
src: "netbox/"
|
src: "netbox/"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -33,8 +33,7 @@ jobs:
|
||||||
fetch-depth: 1
|
fetch-depth: 1
|
||||||
- name: Run Claude Code
|
- name: Run Claude Code
|
||||||
id: claude
|
id: claude
|
||||||
uses: anthropics/claude-code-action@be7b93b1907a4abad570368f3c74b6fe3807510b # v1.0.183
|
uses: anthropics/claude-code-action@11a9dadd198803a0cea6bd53da3e0e8a762fc6ea # v1.0.108
|
||||||
with:
|
with:
|
||||||
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
claude_args: --model claude-opus-5
|
|
||||||
|
|
|
||||||
|
|
@ -1,37 +0,0 @@
|
||||||
name: Enforce milestone on close
|
|
||||||
|
|
||||||
on:
|
|
||||||
issues:
|
|
||||||
types:
|
|
||||||
- closed
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
issues: write
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
check-milestone:
|
|
||||||
name: Check Milestone
|
|
||||||
if: github.repository == 'netbox-community/netbox' && github.event.issue.state_reason == 'completed'
|
|
||||||
runs-on: ubuntu-slim
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Reopen issues completed without a milestone
|
|
||||||
env:
|
|
||||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
GH_REPO: ${{ github.repository }}
|
|
||||||
ISSUE: ${{ github.event.issue.number }}
|
|
||||||
run: |
|
|
||||||
# Grace period, in case the milestone is assigned immediately after closure
|
|
||||||
sleep 90
|
|
||||||
|
|
||||||
# Re-check the issue: bail out if it has been reopened or a milestone has since been set
|
|
||||||
DATA=$(gh issue view "$ISSUE" --json state,milestone)
|
|
||||||
STATE=$(jq -r '.state' <<< "$DATA")
|
|
||||||
MILESTONE=$(jq -r '.milestone.title // ""' <<< "$DATA")
|
|
||||||
if [ "$STATE" != "CLOSED" ] || [ -n "$MILESTONE" ]; then
|
|
||||||
echo "Nothing to do (state=$STATE, milestone=${MILESTONE:-none})"
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
gh issue reopen "$ISSUE" --comment \
|
|
||||||
"This issue was closed as completed without a milestone assigned, and has been reopened automatically. Please assign the milestone for the upcoming release, then close the issue again."
|
|
||||||
|
|
@ -1,387 +0,0 @@
|
||||||
name: Build and publish Python package
|
|
||||||
|
|
||||||
# Least-privilege default for every job; the publish job grants itself id-token below.
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
|
|
||||||
on:
|
|
||||||
pull_request:
|
|
||||||
paths:
|
|
||||||
- '.github/workflows/release.yml'
|
|
||||||
- 'pyproject.toml'
|
|
||||||
- 'README.md'
|
|
||||||
- 'LICENSE.txt'
|
|
||||||
- 'base_requirements.txt'
|
|
||||||
- 'requirements.txt'
|
|
||||||
- 'upgrade.sh'
|
|
||||||
- 'contrib/**'
|
|
||||||
- 'docs/**'
|
|
||||||
- 'mkdocs.yml'
|
|
||||||
- 'netbox/**'
|
|
||||||
- 'scripts/packaging/**'
|
|
||||||
- 'scripts/verify_*.py'
|
|
||||||
- 'scripts/smoketest_configuration.py'
|
|
||||||
push:
|
|
||||||
tags:
|
|
||||||
- 'v*'
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
name: Build package artifacts
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
# Match the validator versions bundled by the pinned publishing action.
|
|
||||||
env:
|
|
||||||
EXPECTED_TWINE_VERSION: '7.0.0'
|
|
||||||
EXPECTED_PACKAGING_VERSION: '26.2'
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Check out repository
|
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
||||||
with:
|
|
||||||
persist-credentials: false
|
|
||||||
|
|
||||||
- name: Set up Python
|
|
||||||
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
|
|
||||||
with:
|
|
||||||
python-version: '3.12'
|
|
||||||
cache: pip
|
|
||||||
|
|
||||||
- name: Install build tooling
|
|
||||||
run: >-
|
|
||||||
python -m pip install --upgrade
|
|
||||||
build
|
|
||||||
"twine==$EXPECTED_TWINE_VERSION"
|
|
||||||
"packaging==$EXPECTED_PACKAGING_VERSION"
|
|
||||||
|
|
||||||
- name: Install documentation toolchain
|
|
||||||
run: python -m pip install -r requirements.txt
|
|
||||||
|
|
||||||
- name: Verify pre-publication tool versions
|
|
||||||
# Assert after all installation steps so twine check uses the expected
|
|
||||||
# validator, and reject any incompatible shared dependency constraints.
|
|
||||||
run: |
|
|
||||||
python - <<'PY'
|
|
||||||
import os
|
|
||||||
from importlib.metadata import version
|
|
||||||
|
|
||||||
expected = {
|
|
||||||
'twine': os.environ['EXPECTED_TWINE_VERSION'],
|
|
||||||
'packaging': os.environ['EXPECTED_PACKAGING_VERSION'],
|
|
||||||
}
|
|
||||||
|
|
||||||
for package, expected_version in expected.items():
|
|
||||||
installed_version = version(package)
|
|
||||||
print(f'{package}=={installed_version}')
|
|
||||||
if installed_version != expected_version:
|
|
||||||
raise SystemExit(f'{package}=={installed_version} is installed, expected {expected_version}')
|
|
||||||
|
|
||||||
print(f'build=={version("build")}')
|
|
||||||
PY
|
|
||||||
|
|
||||||
python -m pip check
|
|
||||||
|
|
||||||
- name: Render the documentation
|
|
||||||
# -c = clean cache, -s = strict (abort on warnings); verify_wheel_contents.py
|
|
||||||
# additionally guards against a partial render reaching the wheel.
|
|
||||||
run: zensical build -c -s
|
|
||||||
|
|
||||||
- name: Build sdist and wheel
|
|
||||||
run: python -m build
|
|
||||||
|
|
||||||
- name: Check package metadata
|
|
||||||
run: twine check dist/*
|
|
||||||
|
|
||||||
- name: Verify the release tag
|
|
||||||
# Both checks run here, in the unprivileged build job, against the wheel that becomes this
|
|
||||||
# run's artifact, so neither publish job has to check out the repository or execute its
|
|
||||||
# scripts while holding id-token: write. A failure here skips every downstream job.
|
|
||||||
if: startsWith(github.ref, 'refs/tags/v')
|
|
||||||
env:
|
|
||||||
TAG: ${{ github.ref_name }}
|
|
||||||
run: |
|
|
||||||
[[ "$TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+([.-][0-9A-Za-z.-]+)?$ ]] || {
|
|
||||||
echo "Ref '$TAG' is not a release tag of the form vX.Y.Z[-designation]"
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
python scripts/verify_release_tag.py "$TAG" dist/*.whl
|
|
||||||
|
|
||||||
- name: Upload package artifacts
|
|
||||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
||||||
with:
|
|
||||||
name: python-package-distributions
|
|
||||||
path: dist/
|
|
||||||
if-no-files-found: error
|
|
||||||
|
|
||||||
verify-dependencies:
|
|
||||||
name: Verify dependency pins are in sync
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs: build
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Check out repository
|
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
||||||
with:
|
|
||||||
persist-credentials: false
|
|
||||||
|
|
||||||
- name: Set up Python
|
|
||||||
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
|
|
||||||
with:
|
|
||||||
python-version: '3.12'
|
|
||||||
cache: pip
|
|
||||||
|
|
||||||
- name: Install packaging
|
|
||||||
run: python -m pip install packaging
|
|
||||||
|
|
||||||
- name: Verify requirements.txt is consistent with base_requirements.txt
|
|
||||||
run: python scripts/verify_dependencies.py
|
|
||||||
|
|
||||||
- name: Download package artifacts
|
|
||||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
|
||||||
with:
|
|
||||||
name: python-package-distributions
|
|
||||||
path: dist/
|
|
||||||
|
|
||||||
- name: Verify wheel Requires-Dist matches requirements.txt
|
|
||||||
run: python scripts/verify_wheel_metadata.py dist/*.whl
|
|
||||||
|
|
||||||
- name: Verify wheel excludes live configuration files
|
|
||||||
run: python scripts/verify_wheel_contents.py dist/*.whl
|
|
||||||
|
|
||||||
verify-sdist:
|
|
||||||
name: Verify the sdist builds a wheel
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs: build
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Check out repository
|
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
||||||
with:
|
|
||||||
persist-credentials: false
|
|
||||||
|
|
||||||
- name: Set up Python
|
|
||||||
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
|
|
||||||
with:
|
|
||||||
python-version: '3.12'
|
|
||||||
cache: pip
|
|
||||||
|
|
||||||
- name: Install tooling
|
|
||||||
run: python -m pip install --upgrade pip packaging
|
|
||||||
|
|
||||||
- name: Download package artifacts
|
|
||||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
|
||||||
with:
|
|
||||||
name: python-package-distributions
|
|
||||||
path: dist/
|
|
||||||
|
|
||||||
- name: Verify the sdist contents
|
|
||||||
run: |
|
|
||||||
python scripts/verify_sdist_contents.py dist/*.tar.gz
|
|
||||||
|
|
||||||
- name: Build a wheel from the sdist
|
|
||||||
run: |
|
|
||||||
python -m pip wheel --no-deps dist/*.tar.gz -w sdist-wheel/
|
|
||||||
|
|
||||||
- name: Verify the sdist-built wheel
|
|
||||||
run: |
|
|
||||||
python scripts/verify_wheel_metadata.py sdist-wheel/*.whl
|
|
||||||
python scripts/verify_wheel_contents.py sdist-wheel/*.whl
|
|
||||||
|
|
||||||
cli-smoke-test:
|
|
||||||
name: Smoke test wheel CLI (no dependencies)
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs: build
|
|
||||||
# The pre-configuration CLI paths are stdlib-only, so a --no-deps install suffices.
|
|
||||||
# Unlike smoke-test, this job also runs on pull requests.
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Set up Python
|
|
||||||
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
|
|
||||||
with:
|
|
||||||
python-version: '3.12'
|
|
||||||
|
|
||||||
- name: Download package artifacts
|
|
||||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
|
||||||
with:
|
|
||||||
name: python-package-distributions
|
|
||||||
path: dist/
|
|
||||||
|
|
||||||
- name: Install wheel without dependencies
|
|
||||||
run: |
|
|
||||||
python -m venv "$RUNNER_TEMP/netbox-cli-venv"
|
|
||||||
"$RUNNER_TEMP/netbox-cli-venv/bin/python" -m pip install --no-deps dist/*.whl
|
|
||||||
|
|
||||||
- name: Exercise the pre-configuration CLI
|
|
||||||
run: |
|
|
||||||
"$RUNNER_TEMP/netbox-cli-venv/bin/netbox" --version
|
|
||||||
"$RUNNER_TEMP/netbox-cli-venv/bin/netbox" version
|
|
||||||
"$RUNNER_TEMP/netbox-cli-venv/bin/python" -m netbox --version
|
|
||||||
"$RUNNER_TEMP/netbox-cli-venv/bin/netbox" secret-key | grep -Eq '^.{50}$' || { echo "secret-key not 50 chars"; exit 1; }
|
|
||||||
|
|
||||||
- name: Smoke-test netbox setup from the wheel
|
|
||||||
run: |
|
|
||||||
"$RUNNER_TEMP/netbox-cli-venv/bin/netbox" setup --target "$RUNNER_TEMP/nbroot"
|
|
||||||
for f in "$RUNNER_TEMP/nbroot/conf/__init__.py" "$RUNNER_TEMP/nbroot/conf/configuration.py" "$RUNNER_TEMP/nbroot/local_requirements.txt"; do
|
|
||||||
test -f "$f" || { echo "missing $f"; exit 1; }
|
|
||||||
done
|
|
||||||
for f in apache.conf gunicorn.py netbox-rq.service netbox.env netbox.service nginx.conf uwsgi.ini; do
|
|
||||||
test -s "$RUNNER_TEMP/nbroot/contrib/$f" || { echo "missing or empty contrib/$f"; exit 1; }
|
|
||||||
done
|
|
||||||
|
|
||||||
smoke-test:
|
|
||||||
name: Smoke test wheel install
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs: build
|
|
||||||
# The wheel install + database migration is expensive; only run it for tag
|
|
||||||
# pushes and manual dispatch, not on every packaging-related pull request.
|
|
||||||
# cli-smoke-test provides lightweight, dependency-free CLI coverage on every PR instead.
|
|
||||||
if: github.event_name != 'pull_request'
|
|
||||||
|
|
||||||
services:
|
|
||||||
postgres:
|
|
||||||
image: postgres:17
|
|
||||||
env:
|
|
||||||
POSTGRES_DB: netbox
|
|
||||||
POSTGRES_USER: netbox
|
|
||||||
POSTGRES_PASSWORD: netbox
|
|
||||||
ports:
|
|
||||||
- 5432:5432
|
|
||||||
options: >-
|
|
||||||
--health-cmd "pg_isready -U netbox -d netbox"
|
|
||||||
--health-interval 10s
|
|
||||||
--health-timeout 5s
|
|
||||||
--health-retries 5
|
|
||||||
redis:
|
|
||||||
image: redis:7
|
|
||||||
ports:
|
|
||||||
- 6379:6379
|
|
||||||
options: >-
|
|
||||||
--health-cmd "redis-cli ping"
|
|
||||||
--health-interval 10s
|
|
||||||
--health-timeout 5s
|
|
||||||
--health-retries 5
|
|
||||||
|
|
||||||
env:
|
|
||||||
NETBOX_CONFIGURATION: smoketest_configuration
|
|
||||||
POSTGRES_DB: netbox
|
|
||||||
POSTGRES_USER: netbox
|
|
||||||
POSTGRES_PASSWORD: netbox
|
|
||||||
POSTGRES_HOST: 127.0.0.1
|
|
||||||
POSTGRES_PORT: 5432
|
|
||||||
REDIS_HOST: 127.0.0.1
|
|
||||||
REDIS_PORT: 6379
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Check out repository
|
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
||||||
with:
|
|
||||||
persist-credentials: false
|
|
||||||
|
|
||||||
- name: Set up Python
|
|
||||||
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
|
|
||||||
with:
|
|
||||||
python-version: '3.12'
|
|
||||||
cache: pip
|
|
||||||
|
|
||||||
- name: Download package artifacts
|
|
||||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
|
||||||
with:
|
|
||||||
name: python-package-distributions
|
|
||||||
path: dist/
|
|
||||||
|
|
||||||
- name: Install system build dependencies for psycopg
|
|
||||||
run: sudo apt-get update && sudo apt-get install -y libpq-dev
|
|
||||||
|
|
||||||
- name: Install wheel into a clean virtual environment
|
|
||||||
run: |
|
|
||||||
python -m venv "$RUNNER_TEMP/netbox-wheel-venv"
|
|
||||||
"$RUNNER_TEMP/netbox-wheel-venv/bin/python" -m pip install --upgrade pip
|
|
||||||
"$RUNNER_TEMP/netbox-wheel-venv/bin/python" -m pip install dist/*.whl
|
|
||||||
|
|
||||||
- name: Run NetBox smoke checks
|
|
||||||
env:
|
|
||||||
# STATIC_ROOT is not a configuration parameter; NETBOX_ROOT places it under the scratch base.
|
|
||||||
NETBOX_ROOT: ${{ runner.temp }}/netbox-smoketest
|
|
||||||
NETBOX_SMOKETEST_BASE: ${{ runner.temp }}/netbox-smoketest
|
|
||||||
PYTHONPATH: ${{ github.workspace }}/scripts
|
|
||||||
run: |
|
|
||||||
"$RUNNER_TEMP/netbox-wheel-venv/bin/netbox" check
|
|
||||||
"$RUNNER_TEMP/netbox-wheel-venv/bin/netbox" upgrade --no-input
|
|
||||||
test -f "$NETBOX_SMOKETEST_BASE/static/docs/index.html" || { echo "bundled documentation was not collected to STATIC_ROOT"; exit 1; }
|
|
||||||
test -f "$NETBOX_SMOKETEST_BASE/static/docs/models/dcim/device/index.html" || { echo "model documentation page was not collected"; exit 1; }
|
|
||||||
|
|
||||||
- name: Smoke-test netbox setup from the wheel
|
|
||||||
run: |
|
|
||||||
"$RUNNER_TEMP/netbox-wheel-venv/bin/netbox" setup --target "$RUNNER_TEMP/nbroot"
|
|
||||||
diff -q "$RUNNER_TEMP/nbroot/conf/configuration.py" netbox/netbox/configuration_example.py
|
|
||||||
for f in apache.conf gunicorn.py netbox-rq.service netbox.env netbox.service nginx.conf uwsgi.ini; do
|
|
||||||
diff -q "$RUNNER_TEMP/nbroot/contrib/$f" "contrib/$f"
|
|
||||||
done
|
|
||||||
|
|
||||||
publish-testpypi:
|
|
||||||
name: Publish package to Test PyPI
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs: [smoke-test, cli-smoke-test, verify-dependencies, verify-sdist]
|
|
||||||
# Test PyPI remains an opt-in rehearsal channel: only a manual dispatch from a v* tag publishes
|
|
||||||
# here, so the publish path can be exercised against a real index without touching production.
|
|
||||||
# A branch dispatch still runs the build, verify, and smoke-test jobs as a dry run, with both
|
|
||||||
# publish jobs skipped.
|
|
||||||
# startsWith() is only a coarse route to this job; workflow if: expressions cannot regex-match.
|
|
||||||
# The tag format and the tag-to-wheel version match are enforced in the build job, which fails
|
|
||||||
# the whole run before anything is uploaded.
|
|
||||||
if: github.event_name == 'workflow_dispatch' && startsWith(github.ref, 'refs/tags/v')
|
|
||||||
environment:
|
|
||||||
name: testpypi
|
|
||||||
url: https://test.pypi.org/p/netbox
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
id-token: write
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Download package artifacts
|
|
||||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
|
||||||
with:
|
|
||||||
name: python-package-distributions
|
|
||||||
path: dist/
|
|
||||||
|
|
||||||
- name: Publish package distributions to Test PyPI
|
|
||||||
# Bundles twine 7.0.0 and packaging 26.2 (requirements/runtime.txt).
|
|
||||||
# Keep EXPECTED_TWINE_VERSION and EXPECTED_PACKAGING_VERSION aligned when updating this action.
|
|
||||||
uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2
|
|
||||||
with:
|
|
||||||
repository-url: https://test.pypi.org/legacy/
|
|
||||||
print-hash: true
|
|
||||||
|
|
||||||
publish-pypi:
|
|
||||||
name: Publish package to PyPI
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs: [smoke-test, cli-smoke-test, verify-dependencies, verify-sdist]
|
|
||||||
# A v* tag push is the production path. Test PyPI is an opt-in rehearsal rather than a promotion
|
|
||||||
# stage, so it is deliberately absent from this job's needs: an outage, a duplicate filename, or
|
|
||||||
# a misconfiguration on a test service must not block a verified production release. The four
|
|
||||||
# verification jobs above already ran against these exact artifacts. The protected pypi
|
|
||||||
# environment supplies the deliberate approval step, and because accepted PyPI filenames cannot
|
|
||||||
# be replaced or reused, a filename the index already holds fails the job instead of being
|
|
||||||
# skipped.
|
|
||||||
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')
|
|
||||||
environment:
|
|
||||||
name: pypi
|
|
||||||
url: https://pypi.org/p/netbox
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
id-token: write
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Download package artifacts
|
|
||||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
|
||||||
with:
|
|
||||||
name: python-package-distributions
|
|
||||||
path: dist/
|
|
||||||
|
|
||||||
- name: Publish package distributions to PyPI
|
|
||||||
# Bundles twine 7.0.0 and packaging 26.2 (requirements/runtime.txt).
|
|
||||||
# Keep EXPECTED_TWINE_VERSION and EXPECTED_PACKAGING_VERSION aligned when updating this action.
|
|
||||||
uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2
|
|
||||||
with:
|
|
||||||
print-hash: true
|
|
||||||
|
|
@ -64,8 +64,3 @@ yarn-error.log*
|
||||||
.idea/
|
.idea/
|
||||||
.vscode/
|
.vscode/
|
||||||
.python-version
|
.python-version
|
||||||
|
|
||||||
# Python package build artifacts
|
|
||||||
/dist/
|
|
||||||
/build/
|
|
||||||
*.egg-info/
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
repos:
|
repos:
|
||||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||||
rev: v0.15.20
|
rev: v0.15.2
|
||||||
hooks:
|
hooks:
|
||||||
- id: ruff
|
- id: ruff
|
||||||
name: "Ruff linter"
|
name: "Ruff linter"
|
||||||
|
|
|
||||||
13
README.md
13
README.md
|
|
@ -5,7 +5,7 @@
|
||||||
<a href="https://github.com/netbox-community/netbox/blob/main/LICENSE.txt"><img src="https://img.shields.io/badge/license-Apache_2.0-blue.svg" alt="License" /></a>
|
<a href="https://github.com/netbox-community/netbox/blob/main/LICENSE.txt"><img src="https://img.shields.io/badge/license-Apache_2.0-blue.svg" alt="License" /></a>
|
||||||
<a href="https://github.com/netbox-community/netbox/graphs/contributors"><img src="https://img.shields.io/github/contributors/netbox-community/netbox?color=blue" alt="Contributors" /></a>
|
<a href="https://github.com/netbox-community/netbox/graphs/contributors"><img src="https://img.shields.io/github/contributors/netbox-community/netbox?color=blue" alt="Contributors" /></a>
|
||||||
<a href="https://github.com/netbox-community/netbox/stargazers"><img src="https://img.shields.io/github/stars/netbox-community/netbox?style=flat" alt="GitHub stars" /></a>
|
<a href="https://github.com/netbox-community/netbox/stargazers"><img src="https://img.shields.io/github/stars/netbox-community/netbox?style=flat" alt="GitHub stars" /></a>
|
||||||
<a href="https://explore.transifex.com/netbox-community/netbox/"><img src="https://img.shields.io/badge/languages-17-blue" alt="Languages supported" /></a>
|
<a href="https://explore.transifex.com/netbox-community/netbox/"><img src="https://img.shields.io/badge/languages-16-blue" alt="Languages supported" /></a>
|
||||||
<a href="https://github.com/netbox-community/netbox/actions/workflows/ci.yml"><img src="https://github.com/netbox-community/netbox/actions/workflows/ci.yml/badge.svg" alt="CI status" /></a>
|
<a href="https://github.com/netbox-community/netbox/actions/workflows/ci.yml"><img src="https://github.com/netbox-community/netbox/actions/workflows/ci.yml/badge.svg" alt="CI status" /></a>
|
||||||
<p>
|
<p>
|
||||||
<strong><a href="https://netboxlabs.com/community/">NetBox Community</a></strong> |
|
<strong><a href="https://netboxlabs.com/community/">NetBox Community</a></strong> |
|
||||||
|
|
@ -20,7 +20,6 @@ NetBox exists to empower network engineers. Since its release in 2016, it has be
|
||||||
<a href="#netboxs-role">NetBox's Role</a> |
|
<a href="#netboxs-role">NetBox's Role</a> |
|
||||||
<a href="#why-netbox">Why NetBox?</a> |
|
<a href="#why-netbox">Why NetBox?</a> |
|
||||||
<a href="#getting-started">Getting Started</a> |
|
<a href="#getting-started">Getting Started</a> |
|
||||||
<a href="#plugins">Plugins</a> |
|
|
||||||
<a href="#get-involved">Get Involved</a> |
|
<a href="#get-involved">Get Involved</a> |
|
||||||
<a href="#screenshots">Screenshots</a>
|
<a href="#screenshots">Screenshots</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
@ -86,16 +85,6 @@ NetBox automatically logs the creation, modification, and deletion of all manage
|
||||||
* The [official documentation](https://docs.netbox.dev) offers a comprehensive introduction.
|
* The [official documentation](https://docs.netbox.dev) offers a comprehensive introduction.
|
||||||
* Check out [our wiki](https://github.com/netbox-community/netbox/wiki/Community-Contributions) for even more projects to get the most out of NetBox!
|
* Check out [our wiki](https://github.com/netbox-community/netbox/wiki/Community-Contributions) for even more projects to get the most out of NetBox!
|
||||||
|
|
||||||
## Plugins
|
|
||||||
|
|
||||||
NetBox's functionality can be extended through plugins, which add new models, views, and integrations on top of the core application. A few of the most popular plugins include:
|
|
||||||
|
|
||||||
* [NetBox Branching](https://github.com/netboxlabs/netbox-branching) — Work with isolated, mergeable branches of your NetBox data
|
|
||||||
* [NetBox Custom Objects](https://github.com/netboxlabs/netbox-custom-objects) — Define entirely new object types directly in the UI
|
|
||||||
* [NetBox DNS](https://github.com/sys4/netbox-plugin-dns) — Manage DNS zones and records as an authoritative source of truth
|
|
||||||
* [NetBox BGP](https://github.com/netbox-community/netbox-bgp) — Document and manage BGP sessions and routing policies
|
|
||||||
* [Browse all plugins](https://netboxlabs.com/plugins/) — Discover the full catalog of available plugins
|
|
||||||
|
|
||||||
## Get Involved
|
## Get Involved
|
||||||
|
|
||||||
* Follow [@NetBoxOfficial](https://twitter.com/NetBoxOfficial) on Twitter!
|
* Follow [@NetBoxOfficial](https://twitter.com/NetBoxOfficial) on Twitter!
|
||||||
|
|
|
||||||
|
|
@ -22,8 +22,6 @@ If you would like to consider upgrading to NetBox Cloud or Enterprise, please co
|
||||||
|
|
||||||
## Reporting a Suspected Vulnerability
|
## Reporting a Suspected Vulnerability
|
||||||
|
|
||||||
Before reporting, please review our [Threat Model](THREAT_MODEL.md) to confirm that the behavior you've observed is an in-scope vulnerability and not an intended, privileged operation.
|
|
||||||
|
|
||||||
If you believe you've uncovered a security vulnerability and wish to report it confidentially, you may do so by emailing `security@netboxlabs.com`. Please ensure that your report meets all the following conditions:
|
If you believe you've uncovered a security vulnerability and wish to report it confidentially, you may do so by emailing `security@netboxlabs.com`. Please ensure that your report meets all the following conditions:
|
||||||
|
|
||||||
* Affects the most recent stable release of NetBox, or a current beta release
|
* Affects the most recent stable release of NetBox, or a current beta release
|
||||||
|
|
|
||||||
133
THREAT_MODEL.md
133
THREAT_MODEL.md
|
|
@ -1,133 +0,0 @@
|
||||||
# NetBox Threat Model
|
|
||||||
|
|
||||||
## Purpose & Scope
|
|
||||||
|
|
||||||
This document describes the security threat model for **NetBox Community Edition**, installed and operated according to the [official documentation](https://netboxlabs.com/docs/netbox/). Its purpose is to state explicitly who and what NetBox trusts, what a supported deployment looks like, and — most importantly — which classes of behavior are intended, privileged operations rather than security vulnerabilities.
|
|
||||||
|
|
||||||
NetBox is a feature-rich application that deliberately grants powerful capabilities (code execution, template rendering, outbound requests) to privileged users in order to support advanced network automation workflows. Many security reports we receive describe these intended capabilities as if they were defects. This document exists so that prospective reporters — and the maintainers who triage their reports — can quickly distinguish a genuine vulnerability from an authorized, privileged operation working as designed.
|
|
||||||
|
|
||||||
This document **complements** our [Security Policy](SECURITY.md); it does not replace it. The policy governs *how* to report a suspected vulnerability and the conditions a report must meet. This document governs *what* constitutes a vulnerability in the first place.
|
|
||||||
|
|
||||||
This model anchors to [OWASP's threat modeling guidance](https://owasp.org/www-community/Threat_Modeling) and uses a lightweight [STRIDE](https://en.wikipedia.org/wiki/STRIDE_%28security%29) breakdown (see below).
|
|
||||||
|
|
||||||
## Supported Deployment Model
|
|
||||||
|
|
||||||
NetBox's threat model assumes a deployment consistent with the recommendations in our [Security Policy](SECURITY.md) and [installation documentation](https://netboxlabs.com/docs/netbox/installation/):
|
|
||||||
|
|
||||||
* **Not exposed to the public Internet.** NetBox is intended to run on an internal or otherwise access-controlled network, behind a reverse proxy (e.g. nginx). It is not designed or hardened to serve as an anonymous, public-facing web application.
|
|
||||||
* **Administered by trusted operators.** The individuals who deploy, configure, and administer NetBox — including holders of the `is_superuser` flag and anyone with shell, filesystem, or database access to the host — are assumed to be trusted system administrators.
|
|
||||||
* **The database is reachable only by the application.** PostgreSQL and Redis are assumed to be accessible only to the NetBox application itself, not to arbitrary clients.
|
|
||||||
* **An authenticated user base.** NetBox is intended for use only by authenticated users. [`LOGIN_REQUIRED`](https://netboxlabs.com/docs/netbox/configuration/security/#login_required) defaults to `True`, and support for unauthenticated access is being removed entirely in NetBox v5.0.
|
|
||||||
* **The reverse proxy owns the network edge.** TLS termination, HTTP request rate limiting, and authoritative determination of the client IP address are the responsibility of the deployment's reverse proxy and surrounding infrastructure — not the application. (See [`HTTP_CLIENT_IP_HEADERS`](https://netboxlabs.com/docs/netbox/configuration/system/#http_client_ip_headers); the headers NetBox trusts for client IP are only as trustworthy as the proxy that sets them.)
|
|
||||||
|
|
||||||
Reports that assume a deployment outside this model — for example, "an anonymous Internet user can reach the login page" or "an administrator can modify the database" — describe the intended operating environment, not a vulnerability.
|
|
||||||
|
|
||||||
## Trusted vs. Untrusted Actors
|
|
||||||
|
|
||||||
The central question when evaluating any NetBox security report is: **does the attack require a privilege that NetBox already designates as trusted?**
|
|
||||||
|
|
||||||
| Actor | Trust | Notes |
|
|
||||||
| --- | --- | --- |
|
|
||||||
| The NetBox server / process | **Trusted** | Executes application code; holds secrets. |
|
|
||||||
| PostgreSQL database, Redis | **Trusted** | Assumed reachable only by the application. |
|
|
||||||
| Infrastructure operators | **Trusted** | Shell/filesystem/DB access implies total control by design. |
|
|
||||||
| Superusers (`is_superuser`) | **Trusted** | An active superuser bypasses all object-level permission checks. This is intentional. |
|
|
||||||
| Users permitted to author code-bearing objects | **Trusted** | Holders of permissions to create/modify custom scripts, export templates, config templates, custom links, or webhooks (see below). |
|
|
||||||
| Authenticated users **without** those permissions | **Untrusted** | Subject to full object-based permission enforcement. |
|
|
||||||
| Unauthenticated / network-adjacent parties | **Untrusted** | Outside the supported deployment model entirely. |
|
|
||||||
|
|
||||||
The governing principle:
|
|
||||||
|
|
||||||
> **Granting a user permission to author a custom script, export or config template, custom link, or webhook is equivalent to granting that user a degree of code execution — by design.** Abuse of such a feature by a user who holds the corresponding permission is not a vulnerability. The mitigation is administrative: grant these permissions only to trusted users, as instructed by the documentation for each feature.
|
|
||||||
|
|
||||||
## Privileged-by-Design Features
|
|
||||||
|
|
||||||
The following features deliberately allow trusted users to supply code or logic that NetBox executes or renders. Each is gated by a specific permission and carries an explicit warning in its documentation. Using these features as designed — even in ways that read like "code execution" or "data access" to an outside observer — is **not** a vulnerability.
|
|
||||||
|
|
||||||
### Custom Scripts
|
|
||||||
|
|
||||||
Custom scripts are Python modules with **unrestricted access to the NetBox ORM, database, and Python runtime**. They are gated by the `extras.run_script` permission (and authored by users who can add/modify script modules). The documentation states plainly that they are *"inherently unsafe and should be installed and run only from trusted sources"*.
|
|
||||||
|
|
||||||
### Export Templates, Config Templates, Custom Links & Webhooks (Jinja)
|
|
||||||
|
|
||||||
These features render **user-authored [Jinja templates](https://jinja.palletsprojects.com/en/stable/)** with live application objects in scope. Templates are evaluated in a Jinja [`SandboxedEnvironment`](https://jinja.palletsprojects.com/en/stable/sandbox/) (`netbox/utilities/Jinja.py`), which restricts access to unsafe attributes and operations.
|
|
||||||
|
|
||||||
It is important to be precise about where the boundary lies:
|
|
||||||
|
|
||||||
* The sandbox **is** a boundary NetBox maintains. A genuine, reproducible *escape* from the sandbox — code or attribute access the sandbox is supposed to block — **is** a vulnerability we take seriously (see "In-Scope Vulnerabilities").
|
|
||||||
* Authoring these objects is nonetheless a **privileged action**. A template author legitimately has broad read access to NetBox objects and can produce arbitrary output within the sandbox's bounds. That a template can read data the author is otherwise permitted to see, or generate HTML/configuration, is intended behavior — not an injection vulnerability.
|
|
||||||
|
|
||||||
Each feature's documentation states that the relevant permission should be granted only to trusted users:
|
|
||||||
|
|
||||||
* [Export templates](https://netboxlabs.com/docs/netbox/customization/export-templates/)
|
|
||||||
* [Custom links](https://netboxlabs.com/docs/netbox/customization/custom-links/)
|
|
||||||
* [Webhooks](https://netboxlabs.com/docs/netbox/integrations/webhooks/)
|
|
||||||
* [Configuration rendering](https://netboxlabs.com/docs/netbox/features/configuration-rendering)
|
|
||||||
|
|
||||||
### Webhooks & Event Rules (Outbound Requests)
|
|
||||||
|
|
||||||
Webhooks issue **outbound HTTP requests to operator-defined URLs**, with the URL, headers, and body all rendered from user-authored Jinja. A trusted webhook author can therefore direct requests to arbitrary endpoints. This server-side request capability is the entire purpose of the feature; it is available only to users permitted to create webhooks, and is not a server-side request forgery (SSRF) vulnerability when exercised by such a user.
|
|
||||||
|
|
||||||
### Config Contexts & Custom Fields
|
|
||||||
|
|
||||||
Config contexts store arbitrary JSON applied to devices and virtual machines; custom fields add operator-defined attributes (with optional regex/JSON-schema validation). Neither executes code directly. Config context data may, however, be consumed by config templates during rendering, so it inherits the same "template author is trusted" posture described above.
|
|
||||||
|
|
||||||
### Object-Based Permissions
|
|
||||||
|
|
||||||
NetBox enforces a robust [object-based permission system](https://netboxlabs.com/docs/netbox/features/authentication-permissions/) layered on top of Django's model permissions. Permissions combine object types, users/groups, actions, and optional JSON **constraints** (including the special `$user` token). A failure of this system to enforce a permission or constraint that it advertises **is** a vulnerability (see below).
|
|
||||||
|
|
||||||
## In-Scope Vulnerabilities
|
|
||||||
|
|
||||||
We take the following seriously. The common thread is a breach of a boundary NetBox *claims* to enforce, or harm to a user who never consented to the risk.
|
|
||||||
|
|
||||||
* **Authorization bypass** — reading or acting on objects a user has no permission to access.
|
|
||||||
* **Privilege escalation** — bypassing a permission or constraint to gain access beyond what was granted.
|
|
||||||
* **Injection that crosses a data boundary** — e.g. filter/ORM operator injection in the REST or GraphQL API exposing data a user shouldn't reach.
|
|
||||||
* **Cross-site scripting (XSS) against a non-consenting victim** — stored or DOM-based XSS that executes in another user's session.
|
|
||||||
* **Jinja sandbox escapes** — a reproducible escape from the template sandbox's intended restrictions.
|
|
||||||
* **Authentication bypass** and **unauthenticated remote code execution or data access**.
|
|
||||||
* **Dependency vulnerabilities with a realistic exploit path** through NetBox (not merely a flagged version).
|
|
||||||
|
|
||||||
## Out-of-Scope / Non-Issues
|
|
||||||
|
|
||||||
The following are **not** treated as NetBox vulnerabilities. Most describe a privileged feature used by a user the documentation already designates as trusted, or a concern that belongs to the deployment/platform layer.
|
|
||||||
|
|
||||||
| Scenario | Status | Reason |
|
|
||||||
| --- | --- |-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|
||||||
| A permitted user runs code via a custom script, Jinja template, custom link, or webhook | Not a vulnerability | These features exist to execute user-authored logic; the required permission is trusted, operator-tier by design. |
|
|
||||||
| A superuser modifies the database, reads secrets, or creates another superuser | Not a vulnerability | Superusers and infrastructure operators are trusted by design. |
|
|
||||||
| A template author reads NetBox data they are otherwise permitted to view | Not a vulnerability | Template rendering with objects in scope is the purpose of the feature; the sandbox, not permission scoping, is the boundary. |
|
|
||||||
| Missing login/request rate limiting | Out of scope | A deployment-layer concern, handled by the reverse proxy rather than the application. |
|
|
||||||
| Client IP spoofing via `X-Forwarded-For` and similar headers | Out of scope | NetBox trusts the headers the reverse proxy sets; trustworthy client IP is a proxy responsibility ([`HTTP_CLIENT_IP_HEADERS`](https://netboxlabs.com/docs/netbox/configuration/system/#http_client_ip_headers)). |
|
|
||||||
| Self-XSS (a user injecting script into their own session) | Not a vulnerability | The user is attacking only themselves; no privilege boundary is crossed. |
|
|
||||||
| CSRF on the login form | Not a vulnerability | Login CSRF is not a meaningful attack in NetBox's deployment model. |
|
|
||||||
| Automated-scanner reports that a file *may* be vulnerable | Rejected | Per our [Security Policy](SECURITY.md), we do not accept reports from automated tooling that merely suggest potential vulnerability without a confirmed reproducible exploit. |
|
|
||||||
|
|
||||||
## Lightweight STRIDE View
|
|
||||||
|
|
||||||
| Category | NetBox posture |
|
|
||||||
| --- | --- |
|
|
||||||
| **S**poofing | Authentication via local accounts, LDAP, or SSO (python-social-auth); API tokens. Authoritative client-IP determination is delegated to the reverse proxy. |
|
|
||||||
| **T**ampering | All writes are gated by object-based permissions with optional constraints, validated within atomic transactions. Code-bearing objects are writable only by trusted users. |
|
|
||||||
| **R**epudiation | Changes are recorded via the changelog and journaling; event rules can emit notifications. |
|
|
||||||
| **I**nformation disclosure | Object-based view permissions filter every queryset. Cross-boundary disclosure (e.g. API/GraphQL filter injection) is in scope; data legitimately visible to a template author is not. |
|
|
||||||
| **D**enial of service | Request rate limiting and resource controls are a deployment/reverse-proxy responsibility, not the application's. |
|
|
||||||
| **E**levation of privilege | The superuser flag is all-or-nothing and trusted. Any *unintended* escalation across the permission system (constraint bypass, action bypass) is in scope. |
|
|
||||||
|
|
||||||
## Triage & Severity
|
|
||||||
|
|
||||||
When triaging a report we assess the **CVSS environmental score**, not solely the base score. A finding with a high CVSS base score may be downgraded substantially once NetBox's deployment assumptions and trust boundaries are applied — for example, a "remote code execution" that in fact requires a permission we already designate as trusted (script or template authoring) is mitigated by design rather than by a code change.
|
|
||||||
|
|
||||||
We use [CVSS v3.1/v4.0](https://www.first.org/cvss/) for scoring and the [STRIDE](https://en.wikipedia.org/wiki/STRIDE_%28security%29) categories above to reason about boundaries. If you believe you have a fix that closes an in-scope issue without degrading the affected feature, you are welcome to propose it alongside your report.
|
|
||||||
|
|
||||||
## Reporting
|
|
||||||
|
|
||||||
Before reporting, please confirm that the behavior you've observed is an in-scope vulnerability under this document and not an intended, privileged operation, and that it is reproducible in the current stable release of NetBox.
|
|
||||||
|
|
||||||
To report a suspected vulnerability, follow the process in our [Security Policy](SECURITY.md). In summary, a report must:
|
|
||||||
|
|
||||||
* Affect the most recent stable release of NetBox, or a current beta release;
|
|
||||||
* Affect a NetBox instance installed and configured per the official documentation; and
|
|
||||||
* Be reproducible following a prescribed set of instructions.
|
|
||||||
|
|
||||||
Confidential reports may be sent to `security@netboxlabs.com`.
|
|
||||||
|
|
@ -4,7 +4,7 @@ colorama
|
||||||
|
|
||||||
# The Python web framework on which NetBox is built
|
# The Python web framework on which NetBox is built
|
||||||
# https://docs.djangoproject.com/en/stable/releases/
|
# https://docs.djangoproject.com/en/stable/releases/
|
||||||
Django==6.1.*
|
Django==6.0.*
|
||||||
|
|
||||||
# Django middleware which permits cross-domain API requests
|
# Django middleware which permits cross-domain API requests
|
||||||
# https://github.com/adamchainz/django-cors-headers/blob/main/CHANGELOG.rst
|
# https://github.com/adamchainz/django-cors-headers/blob/main/CHANGELOG.rst
|
||||||
|
|
@ -18,20 +18,20 @@ django-debug-toolbar
|
||||||
# https://github.com/carltongibson/django-filter/blob/main/CHANGES.rst
|
# https://github.com/carltongibson/django-filter/blob/main/CHANGES.rst
|
||||||
django-filter
|
django-filter
|
||||||
|
|
||||||
|
# Django Debug Toolbar extension for GraphiQL
|
||||||
|
# https://github.com/flavors/django-graphiql-debug-toolbar/blob/main/CHANGES.rst
|
||||||
|
django-graphiql-debug-toolbar
|
||||||
|
|
||||||
# HTMX utilities for Django
|
# HTMX utilities for Django
|
||||||
# https://django-htmx.readthedocs.io/en/latest/changelog.html
|
# https://django-htmx.readthedocs.io/en/latest/changelog.html
|
||||||
django-htmx
|
django-htmx
|
||||||
|
|
||||||
# Modified Preorder Tree Traversal (recursive nesting of objects)
|
# Modified Preorder Tree Traversal (recursive nesting of objects)
|
||||||
# Retained primarily for plugin backward compatibility: the deprecated
|
|
||||||
# NestedGroupModel base remains MPTT-backed for plugins still using it. Also
|
|
||||||
# required by historical migrations that pre-date the switch to PostgreSQL ltree.
|
|
||||||
# NetBox core runtime uses netbox.models.ltree.LtreeModel instead.
|
|
||||||
django-mptt
|
django-mptt
|
||||||
|
|
||||||
# Context managers for PostgreSQL advisory locks (successor to django-pglocks)
|
# Context managers for PostgreSQL advisory locks
|
||||||
# https://github.com/Xof/django-pgware
|
# https://github.com/Xof/django-pglocks/blob/master/CHANGES.txt
|
||||||
django-pgware
|
django-pglocks
|
||||||
|
|
||||||
# Prometheus metrics library for Django
|
# Prometheus metrics library for Django
|
||||||
# https://github.com/korfuri/django-prometheus/blob/master/CHANGELOG.md
|
# https://github.com/korfuri/django-prometheus/blob/master/CHANGELOG.md
|
||||||
|
|
@ -57,7 +57,8 @@ django-storages
|
||||||
|
|
||||||
# Abstraction models for rendering and paginating HTML tables
|
# Abstraction models for rendering and paginating HTML tables
|
||||||
# https://github.com/jieter/django-tables2/blob/master/CHANGELOG.md
|
# https://github.com/jieter/django-tables2/blob/master/CHANGELOG.md
|
||||||
django-tables2
|
# See #21902 for upgrading to django-tables2 v2.9+
|
||||||
|
django-tables2<2.9
|
||||||
|
|
||||||
# User-defined tags for objects
|
# User-defined tags for objects
|
||||||
# https://github.com/jazzband/django-taggit/blob/master/CHANGELOG.rst
|
# https://github.com/jazzband/django-taggit/blob/master/CHANGELOG.rst
|
||||||
|
|
@ -70,7 +71,7 @@ django-timezone-field
|
||||||
# A REST API framework for Django projects
|
# A REST API framework for Django projects
|
||||||
# https://www.django-rest-framework.org/community/release-notes/
|
# https://www.django-rest-framework.org/community/release-notes/
|
||||||
# TODO: Re-evaluate the monkey-patch of get_unique_validators() before upgrading
|
# TODO: Re-evaluate the monkey-patch of get_unique_validators() before upgrading
|
||||||
djangorestframework==3.18.0
|
djangorestframework==3.17.1
|
||||||
|
|
||||||
# Sane and flexible OpenAPI 3 schema generation for Django REST framework.
|
# Sane and flexible OpenAPI 3 schema generation for Django REST framework.
|
||||||
# https://github.com/tfranzel/drf-spectacular/blob/master/CHANGELOG.rst
|
# https://github.com/tfranzel/drf-spectacular/blob/master/CHANGELOG.rst
|
||||||
|
|
@ -139,7 +140,8 @@ PyYAML
|
||||||
|
|
||||||
# redis-py
|
# redis-py
|
||||||
# https://github.com/redis/redis-py
|
# https://github.com/redis/redis-py
|
||||||
redis
|
# Default protocol changes to RESP3 in v8.0; see #22388
|
||||||
|
redis<8.0
|
||||||
|
|
||||||
# Requests
|
# Requests
|
||||||
# https://github.com/psf/requests/blob/main/HISTORY.md
|
# https://github.com/psf/requests/blob/main/HISTORY.md
|
||||||
|
|
@ -155,7 +157,8 @@ social-auth-app-django
|
||||||
|
|
||||||
# Social authentication framework
|
# Social authentication framework
|
||||||
# https://github.com/python-social-auth/social-core/blob/master/CHANGELOG.md
|
# https://github.com/python-social-auth/social-core/blob/master/CHANGELOG.md
|
||||||
social-auth-core
|
# Need to verify that v4.9.0 does not introduce breaking changes (see #22095)
|
||||||
|
social-auth-core==4.8.*
|
||||||
|
|
||||||
# Image thumbnail generation
|
# Image thumbnail generation
|
||||||
# https://github.com/jazzband/sorl-thumbnail/blob/master/CHANGES.rst
|
# https://github.com/jazzband/sorl-thumbnail/blob/master/CHANGES.rst
|
||||||
|
|
|
||||||
|
|
@ -328,7 +328,6 @@
|
||||||
"virtual",
|
"virtual",
|
||||||
"bridge",
|
"bridge",
|
||||||
"lag",
|
"lag",
|
||||||
"channel",
|
|
||||||
"100base-fx",
|
"100base-fx",
|
||||||
"100base-lfx",
|
"100base-lfx",
|
||||||
"100base-tx",
|
"100base-tx",
|
||||||
|
|
@ -440,7 +439,6 @@
|
||||||
"100gbase-x-dsfp",
|
"100gbase-x-dsfp",
|
||||||
"100gbase-x-qsfp28",
|
"100gbase-x-qsfp28",
|
||||||
"100gbase-x-qsfpdd",
|
"100gbase-x-qsfpdd",
|
||||||
"100gbase-x-sfp112",
|
|
||||||
"100gbase-x-sfpdd",
|
"100gbase-x-sfpdd",
|
||||||
"200gbase-x-cfp2",
|
"200gbase-x-cfp2",
|
||||||
"200gbase-x-qsfp56",
|
"200gbase-x-qsfp56",
|
||||||
|
|
@ -512,18 +510,6 @@
|
||||||
"infiniband-hdr",
|
"infiniband-hdr",
|
||||||
"infiniband-ndr",
|
"infiniband-ndr",
|
||||||
"infiniband-xdr",
|
"infiniband-xdr",
|
||||||
"infiniband-hdr-2x",
|
|
||||||
"infiniband-ndr-2x",
|
|
||||||
"infiniband-xdr-2x",
|
|
||||||
"infiniband-sdr-4x",
|
|
||||||
"infiniband-ddr-4x",
|
|
||||||
"infiniband-qdr-4x",
|
|
||||||
"infiniband-fdr10-4x",
|
|
||||||
"infiniband-fdr-4x",
|
|
||||||
"infiniband-edr-4x",
|
|
||||||
"infiniband-hdr-4x",
|
|
||||||
"infiniband-ndr-4x",
|
|
||||||
"infiniband-xdr-4x",
|
|
||||||
"t1",
|
"t1",
|
||||||
"e1",
|
"e1",
|
||||||
"t3",
|
"t3",
|
||||||
|
|
@ -554,7 +540,6 @@
|
||||||
"extreme-summitstack-128",
|
"extreme-summitstack-128",
|
||||||
"extreme-summitstack-256",
|
"extreme-summitstack-256",
|
||||||
"extreme-summitstack-512",
|
"extreme-summitstack-512",
|
||||||
"hpe-synergy-interconnect-link",
|
|
||||||
"other"
|
"other"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
@ -640,7 +625,6 @@
|
||||||
"st",
|
"st",
|
||||||
"cs",
|
"cs",
|
||||||
"sn",
|
"sn",
|
||||||
"mdc",
|
|
||||||
"sma-905",
|
"sma-905",
|
||||||
"sma-906",
|
"sma-906",
|
||||||
"urm-p2",
|
"urm-p2",
|
||||||
|
|
@ -713,7 +697,6 @@
|
||||||
"st",
|
"st",
|
||||||
"cs",
|
"cs",
|
||||||
"sn",
|
"sn",
|
||||||
"mdc",
|
|
||||||
"sma-905",
|
"sma-905",
|
||||||
"sma-906",
|
"sma-906",
|
||||||
"urm-p2",
|
"urm-p2",
|
||||||
|
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
# Optional overrides for a pip-installed NetBox. Do not put secrets here.
|
|
||||||
# NetBox loads conf/configuration.py from NETBOX_ROOT automatically.
|
|
||||||
NETBOX_ROOT=/opt/netbox
|
|
||||||
47588
contrib/openapi.json
47588
contrib/openapi.json
File diff suppressed because one or more lines are too long
|
|
@ -41,12 +41,6 @@ NetBox supports single sign-on authentication via the [python-social-auth](https
|
||||||
|
|
||||||
Most remote authentication backends require some additional configuration through settings prefixed with `SOCIAL_AUTH_`. These will be automatically imported from NetBox's `configuration.py` file. Additionally, the [authentication pipeline](https://python-social-auth.readthedocs.io/en/latest/pipeline.html) can be customized via the `SOCIAL_AUTH_PIPELINE` parameter. (NetBox's default pipeline is defined in `netbox/settings.py` for your reference.)
|
Most remote authentication backends require some additional configuration through settings prefixed with `SOCIAL_AUTH_`. These will be automatically imported from NetBox's `configuration.py` file. Additionally, the [authentication pipeline](https://python-social-auth.readthedocs.io/en/latest/pipeline.html) can be customized via the `SOCIAL_AUTH_PIPELINE` parameter. (NetBox's default pipeline is defined in `netbox/settings.py` for your reference.)
|
||||||
|
|
||||||
!!! note "Content Security Policy"
|
|
||||||
Beginning an SSO login requires the browser to make a request back to NetBox before it is sent
|
|
||||||
on to the identity provider. If you serve NetBox with a Content Security Policy which does not
|
|
||||||
permit same-origin connections, SSO logins will fail: add `connect-src 'self'` (or a
|
|
||||||
`default-src` which covers it) to your policy.
|
|
||||||
|
|
||||||
#### Configuring the SSO module's appearance
|
#### Configuring the SSO module's appearance
|
||||||
|
|
||||||
The way a remote authentication backend is displayed to the user on the login
|
The way a remote authentication backend is displayed to the user on the login
|
||||||
|
|
|
||||||
|
|
@ -4,13 +4,11 @@
|
||||||
|
|
||||||
### Enabling Error Reporting
|
### Enabling Error Reporting
|
||||||
|
|
||||||
NetBox supports native integration with [Sentry](https://sentry.io/) for automatic error reporting. To enable this functionality, set `SENTRY_ENABLED` to `True` and define your unique [data source name (DSN)](https://docs.sentry.io/product/sentry-basics/concepts/dsn-explainer/) in `configuration.py` via `SENTRY_CONFIG`.
|
NetBox supports native integration with [Sentry](https://sentry.io/) for automatic error reporting. To enable this functionality, set `SENTRY_ENABLED` to `True` and define your unique [data source name (DSN)](https://docs.sentry.io/product/sentry-basics/concepts/dsn-explainer/) in `configuration.py`.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
SENTRY_ENABLED = True
|
SENTRY_ENABLED = True
|
||||||
SENTRY_CONFIG = {
|
SENTRY_DSN = "https://examplePublicKey@o0.ingest.sentry.io/0"
|
||||||
"dsn": "https://examplePublicKey@o0.ingest.sentry.io/0",
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Setting `SENTRY_ENABLED` to False will disable the Sentry integration.
|
Setting `SENTRY_ENABLED` to False will disable the Sentry integration.
|
||||||
|
|
|
||||||
|
|
@ -1,167 +0,0 @@
|
||||||
# Management Commands
|
|
||||||
|
|
||||||
In addition to Django's built-in management commands, NetBox provides several commands of its own. These are run using `manage.py`:
|
|
||||||
|
|
||||||
```
|
|
||||||
cd /opt/netbox
|
|
||||||
source /opt/netbox/venv/bin/activate
|
|
||||||
python3 netbox/manage.py <command>
|
|
||||||
```
|
|
||||||
|
|
||||||
Run any command with `--help` to see its full set of arguments.
|
|
||||||
|
|
||||||
## calculate_cached_counts
|
|
||||||
|
|
||||||
Force a recalculation of all cached counter fields (for example, the device count shown on a site). NetBox keeps these counters current automatically; this command is useful to repair them if they have drifted.
|
|
||||||
|
|
||||||
```
|
|
||||||
python3 netbox/manage.py calculate_cached_counts
|
|
||||||
```
|
|
||||||
|
|
||||||
## nbshell
|
|
||||||
|
|
||||||
Start the Django shell with all NetBox models already imported. See [NetBox Shell](./netbox-shell.md) for details.
|
|
||||||
|
|
||||||
```
|
|
||||||
python3 netbox/manage.py nbshell
|
|
||||||
```
|
|
||||||
|
|
||||||
## populate_image_sizes
|
|
||||||
|
|
||||||
Populate the cached file size for image attachments that predate the `image_size` field. Running this once after upgrading is recommended for deployments with many existing attachments on a remote storage backend (such as S3). It is safe to run on a live system and may be re-run; any file that cannot be read is skipped and retried on the next run.
|
|
||||||
|
|
||||||
```
|
|
||||||
python3 netbox/manage.py populate_image_sizes
|
|
||||||
```
|
|
||||||
|
|
||||||
## rebuild_config_context_cache
|
|
||||||
|
|
||||||
Pre-render and cache the merged config context data for all devices and virtual machines. The [upgrade script](../installation/upgrading.md) runs this automatically, so it is not usually necessary to invoke it by hand. It is useful to complete an interrupted run, or (with `--force`) to repair the cache after a bulk write which bypassed NetBox's change handling (cache invalidation is driven by model signals, which a direct `queryset.update()` does not emit).
|
|
||||||
|
|
||||||
By default, only those objects whose cache is empty are rendered, so the command is safe to interrupt and re-run. This also means that a default run will not correct a cache which is populated but stale, as a write which bypassed cache invalidation leaves it: Pass `--force` to re-render every object regardless of its current cache. Either form may be run on a live system, as any object whose cache is empty falls back to rendering its config context on demand. See [Context Data](../features/context-data.md) for details.
|
|
||||||
|
|
||||||
```
|
|
||||||
python3 netbox/manage.py rebuild_config_context_cache [--force]
|
|
||||||
```
|
|
||||||
|
|
||||||
## rebuild_ltree_paths
|
|
||||||
|
|
||||||
Recompute the `path` and `sort_path` columns of the hierarchical models (regions, site groups, locations, device roles, platforms, tenant groups, contact groups, wireless LAN groups, module bays, inventory items, and inventory item templates) from their parent relationships. These columns are maintained by PostgreSQL triggers, so this is needed only where a write bypassed them: a bulk `COPY`, a direct `UPDATE`, or a database restored from a NetBox v4.7.0 dump (see [#23130](https://github.com/netbox-community/netbox/issues/23130)).
|
|
||||||
|
|
||||||
The command has two modes. Both operate on every hierarchical model by default, or on those named as `app_label.ModelName`.
|
|
||||||
|
|
||||||
### Reporting
|
|
||||||
|
|
||||||
`--check` compares each object's stored `path` and `sort_path` against its parent's and reports which models disagree. It modifies nothing and takes no locks, so it can be run on a live system or against a replica.
|
|
||||||
|
|
||||||
```
|
|
||||||
python3 netbox/manage.py rebuild_ltree_paths --check
|
|
||||||
```
|
|
||||||
|
|
||||||
```no-highlight
|
|
||||||
dcim.location: 5 path, 5 sort_path row(s) out of date
|
|
||||||
dcim.region: 2 sort_path row(s) out of date
|
|
||||||
...
|
|
||||||
|
|
||||||
Needs rebuilding: dcim.location dcim.region
|
|
||||||
```
|
|
||||||
|
|
||||||
The counts answer whether a model needs rebuilding, not how many of its objects are wrong. Where an object has moved, the objects beneath it still agree with their own parent and are not counted, though they are equally stale. Rebuild the whole model rather than acting on the number.
|
|
||||||
|
|
||||||
A model can also be damaged in a way `--check` does not report: an object which no root reaches by following `parent_id` is compared against a parent that is itself unreachable, so it may agree and be counted clean. The rebuild detects that case and refuses (see below).
|
|
||||||
|
|
||||||
### Rebuilding
|
|
||||||
|
|
||||||
With no `--check`, each named model is rebuilt: every row's `path` and `sort_path` are recomputed from the hierarchy.
|
|
||||||
|
|
||||||
```
|
|
||||||
python3 netbox/manage.py rebuild_ltree_paths [app_label.ModelName ...]
|
|
||||||
```
|
|
||||||
|
|
||||||
```no-highlight
|
|
||||||
dcim.region: rebuilding... done
|
|
||||||
Finished.
|
|
||||||
```
|
|
||||||
|
|
||||||
A rebuild derives each object's path by walking down from the roots, so it can only repair an object which some root reaches. Where a model contains an object no root reaches — one in a cycle, one parented to itself, or one whose parent no longer exists — the command reports how many and stops without modifying that model, because a rebuild would silently skip exactly those objects:
|
|
||||||
|
|
||||||
```no-highlight
|
|
||||||
CommandError: dcim.region: 5 row(s) cannot be reached from a root by following
|
|
||||||
parent_id, so a rebuild would skip them: 1, 2, 3, 4, 5. Correct the parent
|
|
||||||
relationships, then re-run.
|
|
||||||
```
|
|
||||||
|
|
||||||
One of the listed objects is in a cycle, parented to itself, or pointing at an object which no longer exists; the rest are descended from it and are otherwise intact. Correcting the relationship is left to the operator, as only they can say what the hierarchy was meant to be. Each model is checked and rebuilt in its own transaction, so a refusal leaves that model untouched, and models already rebuilt stay rebuilt.
|
|
||||||
|
|
||||||
!!! warning
|
|
||||||
A rebuild rewrites every row of each named model in a single statement, locking those rows until it commits. On a large table this blocks concurrent writes for minutes, so run it during a maintenance window. Use `--check` first to limit the rebuild to the models which need it.
|
|
||||||
|
|
||||||
A rebuild also assumes nothing else is changing the hierarchy while it runs. An object reparented after the command has checked the model, but before it rewrites it, is not accounted for, and the check which refuses unreachable objects cannot see it either. This is another reason to run the command with writes paused rather than against a live system.
|
|
||||||
|
|
||||||
## rebuild_prefixes
|
|
||||||
|
|
||||||
Rebuild the IPAM prefix hierarchy, recalculating the depth and child counts for all prefixes.
|
|
||||||
|
|
||||||
```
|
|
||||||
python3 netbox/manage.py rebuild_prefixes
|
|
||||||
```
|
|
||||||
|
|
||||||
## reindex
|
|
||||||
|
|
||||||
Reindex objects for the search backend. Pass one or more apps or models to reindex a subset; with no arguments, all models are reindexed. See [Removing a Plugin](../plugins/removal.md) for a related use.
|
|
||||||
|
|
||||||
```
|
|
||||||
python3 netbox/manage.py reindex [app_label[.ModelName] ...]
|
|
||||||
```
|
|
||||||
|
|
||||||
## renaturalize
|
|
||||||
|
|
||||||
Recalculate natural ordering values for the affected models. Pass one or more `app_label.ModelName` arguments to limit the scope; with no arguments, all models with natural ordering fields are processed.
|
|
||||||
|
|
||||||
```
|
|
||||||
python3 netbox/manage.py renaturalize [app_label.ModelName ...]
|
|
||||||
```
|
|
||||||
|
|
||||||
## runscript
|
|
||||||
|
|
||||||
!!! warning "Deprecation Warning"
|
|
||||||
The custom scripts functionality has been deprecated beginning in NetBox v4.7, and is scheduled for removal in NetBox v5.0. This command will be removed along with it.
|
|
||||||
|
|
||||||
Run a [custom script](../customization/custom-scripts.md) from the command line, outside the web UI or API.
|
|
||||||
|
|
||||||
```
|
|
||||||
python3 netbox/manage.py runscript <module.ScriptName>
|
|
||||||
```
|
|
||||||
|
|
||||||
## rqworker
|
|
||||||
|
|
||||||
Start a background task worker to process queued jobs (provided by django-rq). At least one worker must be running for background tasks such as report and script execution, webhooks, and synchronization to be processed.
|
|
||||||
|
|
||||||
```
|
|
||||||
python3 netbox/manage.py rqworker
|
|
||||||
```
|
|
||||||
|
|
||||||
## syncdatasource
|
|
||||||
|
|
||||||
Synchronize a data source from its remote upstream. Pass one or more data source names, or `--all` to synchronize every data source.
|
|
||||||
|
|
||||||
```
|
|
||||||
python3 netbox/manage.py syncdatasource <name> [<name> ...]
|
|
||||||
python3 netbox/manage.py syncdatasource --all
|
|
||||||
```
|
|
||||||
|
|
||||||
## trace_paths
|
|
||||||
|
|
||||||
Generate any missing cable paths among all cable termination objects. This is useful after a bulk import of cabling, or to repair paths that were not generated automatically.
|
|
||||||
|
|
||||||
```
|
|
||||||
python3 netbox/manage.py trace_paths
|
|
||||||
```
|
|
||||||
|
|
||||||
## webhook_receiver
|
|
||||||
|
|
||||||
Start a simple HTTP listener that prints any requests it receives. This is a debugging aid for testing webhooks: point a webhook at the listener and inspect exactly what NetBox sends. It listens on port 9000 by default; pass `--port` to change it and `--no-headers` to suppress the request headers.
|
|
||||||
|
|
||||||
```
|
|
||||||
python3 netbox/manage.py webhook_receiver [--port PORT] [--no-headers]
|
|
||||||
```
|
|
||||||
|
|
@ -31,9 +31,6 @@ Some models have registered actions that appear as checkboxes in the "Actions" s
|
||||||
|
|
||||||
Constraints are expressed as a JSON object or list representing a [Django query filter](https://docs.djangoproject.com/en/stable/ref/models/querysets/#field-lookups). This is the same syntax that you would pass to the QuerySet `filter()` method when performing a query using the Django ORM. As with query filters, double underscores can be used to traverse related objects or invoke lookup expressions. Some example queries and their corresponding definitions are shown below.
|
Constraints are expressed as a JSON object or list representing a [Django query filter](https://docs.djangoproject.com/en/stable/ref/models/querysets/#field-lookups). This is the same syntax that you would pass to the QuerySet `filter()` method when performing a query using the Django ORM. As with query filters, double underscores can be used to traverse related objects or invoke lookup expressions. Some example queries and their corresponding definitions are shown below.
|
||||||
|
|
||||||
!!! note
|
|
||||||
Constraint definitions must be valid JSON. Because a backslash (`\`) is an escape character in a JSON string, a backslash that is part of a string value must itself be escaped. For example, a regular expression containing `\.` must be entered as `\\.` in the constraint definition.
|
|
||||||
|
|
||||||
All attributes defined within a single JSON object are applied with a logical AND. For example, suppose you assign a permission for the site model with the following constraints.
|
All attributes defined within a single JSON object are applied with a logical AND. For example, suppose you assign a permission for the site model with the following constraints.
|
||||||
|
|
||||||
```json
|
```json
|
||||||
|
|
@ -86,7 +83,6 @@ While permissions are typically assigned to specific groups and/or users, it is
|
||||||
| `{"status": "active", "role": "testing"}` | Status is active **AND** role is testing |
|
| `{"status": "active", "role": "testing"}` | Status is active **AND** role is testing |
|
||||||
| `{"name__startswith": "Foo"}` | Name starts with "Foo" (case-sensitive) |
|
| `{"name__startswith": "Foo"}` | Name starts with "Foo" (case-sensitive) |
|
||||||
| `{"name__iendswith": "bar"}` | Name ends with "bar" (case-insensitive) |
|
| `{"name__iendswith": "bar"}` | Name ends with "bar" (case-insensitive) |
|
||||||
| `{"name__regex": "^foo\\.bar$"}` | Name matches the regular expression `^foo\.bar$` |
|
|
||||||
| `{"vid__gte": 100, "vid__lt": 200}` | VLAN ID is greater than or equal to 100 **AND** less than 200 |
|
| `{"vid__gte": 100, "vid__lt": 200}` | VLAN ID is greater than or equal to 100 **AND** less than 200 |
|
||||||
| `[{"vid__lt": 200}, {"status": "reserved"}]` | VLAN ID is less than 200 **OR** status is reserved |
|
| `[{"vid__lt": 200}, {"status": "reserved"}]` | VLAN ID is less than 200 **OR** status is reserved |
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,74 +0,0 @@
|
||||||
# Repairing Hierarchical Paths
|
|
||||||
|
|
||||||
NetBox stores each hierarchical object's position in its tree in a PostgreSQL [`ltree`](https://www.postgresql.org/docs/current/ltree.html) column named `path`, and most such models additionally maintain a `sort_path` used to order children by name. Both columns are maintained by database triggers which cascade a change to an object's name or parent down to its descendants.
|
|
||||||
|
|
||||||
This page covers detecting and repairing stale values in those columns. It applies to the nested group models (region, site group, location, device role, platform, tenant group, contact group, wireless LAN group) as well as module bays, inventory items, and inventory item templates.
|
|
||||||
|
|
||||||
## Databases Restored From a v4.7.0 Dump
|
|
||||||
|
|
||||||
In NetBox v4.7.0, the cascade triggers could not be recreated when restoring a `pg_dump` of the database, because `pg_dump` resets the `search_path` and the triggers' `WHEN` clause depended on it. As `psql` does not stop on error by default, such a restore reported success while leaving the database without those triggers. Renaming or moving an affected object therefore did not update its descendants, and the stored paths drifted out of sync with the actual hierarchy. This was corrected in NetBox v4.7.1 ([#23130](https://github.com/netbox-community/netbox/issues/23130)).
|
|
||||||
|
|
||||||
Upgrading to v4.7.1 or later reinstalls the triggers, so all subsequent changes are cascaded correctly. It does **not** repair values which have already gone stale — use the checks below to determine whether a repair is needed.
|
|
||||||
|
|
||||||
!!! tip
|
|
||||||
To avoid this class of failure in general, always restore a dump with `psql -v ON_ERROR_STOP=1` (or `pg_restore --exit-on-error`), as described under [Replicating NetBox](./replicating-netbox.md#load-an-exported-database).
|
|
||||||
|
|
||||||
## Checking for Stale Paths
|
|
||||||
|
|
||||||
### After Upgrading
|
|
||||||
|
|
||||||
The [`rebuild_ltree_paths`](./management-commands.md#rebuild_ltree_paths) management command reports which models are affected without modifying anything or taking any locks:
|
|
||||||
|
|
||||||
```no-highlight
|
|
||||||
python netbox/manage.py rebuild_ltree_paths --check
|
|
||||||
```
|
|
||||||
|
|
||||||
### Before Upgrading
|
|
||||||
|
|
||||||
The same test can be run as SQL against a deployment which has not yet been upgraded. Substitute each hierarchical table in turn: `dcim_region`, `dcim_sitegroup`, `dcim_location`, `dcim_devicerole`, `dcim_platform`, `dcim_modulebay`, `dcim_inventoryitem`, `dcim_inventoryitemtemplate`, `tenancy_tenantgroup`, `tenancy_contactgroup`, and `wireless_wirelesslangroup`.
|
|
||||||
|
|
||||||
```no-highlight
|
|
||||||
SELECT count(*) FROM (
|
|
||||||
SELECT id FROM dcim_region WHERE parent_id IS NULL
|
|
||||||
AND path <> lpad(id::text, 19, '0')::ltree
|
|
||||||
UNION ALL
|
|
||||||
SELECT c.id FROM dcim_region c JOIN dcim_region p ON c.parent_id = p.id
|
|
||||||
WHERE c.path <> p.path || lpad(c.id::text, 19, '0')::ltree
|
|
||||||
) x;
|
|
||||||
```
|
|
||||||
|
|
||||||
Treat any non-zero result as "this table needs rebuilding" rather than as a count of the damage: an object whose ancestor moved is reported, but its own descendants are consistent with it and so are not, even though they are equally stale.
|
|
||||||
|
|
||||||
### Checking `sort_path`
|
|
||||||
|
|
||||||
The nine tables which order their children by name additionally maintain a `sort_path`, which can go stale on a rename even when `path` is correct. Every table in the list above except `dcim_inventoryitem` and `dcim_inventoryitemtemplate` carries one, and is checked with:
|
|
||||||
|
|
||||||
```no-highlight
|
|
||||||
SELECT count(*) FROM (
|
|
||||||
SELECT id FROM dcim_region WHERE parent_id IS NULL AND sort_path <> name
|
|
||||||
UNION ALL
|
|
||||||
SELECT c.id FROM dcim_region c JOIN dcim_region p ON c.parent_id = p.id
|
|
||||||
WHERE c.sort_path <> p.sort_path || chr(9) || c.name
|
|
||||||
) x;
|
|
||||||
```
|
|
||||||
|
|
||||||
Stale `sort_path` values affect only the order in which objects are listed. A stale `path`, by contrast, misplaces an object within the hierarchy, so it can be omitted from its ancestor's list of descendants.
|
|
||||||
|
|
||||||
## Repairing
|
|
||||||
|
|
||||||
Repair an affected table with the [`rebuild_ltree_paths`](./management-commands.md#rebuild_ltree_paths) management command, naming the models the queries above flagged:
|
|
||||||
|
|
||||||
```no-highlight
|
|
||||||
python netbox/manage.py rebuild_ltree_paths dcim.region
|
|
||||||
```
|
|
||||||
|
|
||||||
!!! warning
|
|
||||||
A rebuild rewrites every row of the named tables, locking those rows until it commits, so run it during a maintenance window.
|
|
||||||
|
|
||||||
Should the command report that a table contains rows unreachable from any root, the parent relationships themselves need correcting first: a rebuild walks down from the roots and would skip those rows.
|
|
||||||
|
|
||||||
## Plugins
|
|
||||||
|
|
||||||
Plugins which maintain their own `ltree` models via the `InstallLtreeTriggers` migration operation are affected in the same way, and their tables are not touched by NetBox's own corrective migrations. Where such a database was restored from a dump, the plugin's cascade triggers are missing entirely; where it was upgraded in place, they carry the old definition and will be lost by its next dump.
|
|
||||||
|
|
||||||
Either way, a new plugin migration applying `ReinstallLtreeTriggers` (passing the same `name_column` as the original) installs the corrected definitions. Use that operation rather than `InstallLtreeTriggers`: both drop each trigger before recreating it, so either works going forwards, but reversing the corrective migration should not undo the original installation. `InstallLtreeTriggers` reverses by dropping both triggers and their functions, which would leave the table with no path maintenance while the migration that first installed them remains applied. `ReinstallLtreeTriggers` reverses to a no-op instead.
|
|
||||||
|
|
@ -34,16 +34,9 @@ When restoring a database from a file, it's recommended to delete any existing d
|
||||||
```no-highlight
|
```no-highlight
|
||||||
psql -c 'drop database netbox'
|
psql -c 'drop database netbox'
|
||||||
psql -c 'create database netbox'
|
psql -c 'create database netbox'
|
||||||
psql -v ON_ERROR_STOP=1 netbox < netbox.sql
|
psql netbox < netbox.sql
|
||||||
```
|
```
|
||||||
|
|
||||||
!!! warning "Always restore with ON_ERROR_STOP"
|
|
||||||
By default, `psql` continues after an error and still exits with status 0. A restore which failed partway through, leaving out an index, a function, or a trigger, therefore reports success and yields a database which looks healthy but is incomplete. Passing `-v ON_ERROR_STOP=1` makes `psql` abort on the first error and exit non-zero, so check the exit status before putting the restored database into service.
|
|
||||||
|
|
||||||
This changes the behavior of the restore: a dump which previously appeared to restore successfully will now abort on its first error, including errors unrelated to NetBox's own schema (a role which already exists, an extension owned by another user, and so on). That is the intended outcome, but expect a restore which used to "succeed" to start reporting failures which were there all along.
|
|
||||||
|
|
||||||
For a dump in one of `pg_dump`'s non-plain formats, restore it with `pg_restore --exit-on-error` instead.
|
|
||||||
|
|
||||||
Keep in mind that PostgreSQL user accounts and permissions are not included with the dump: You will need to create those manually if you want to fully replicate the original database (see the [installation docs](../installation/1-postgresql.md)). When setting up a development instance of NetBox, it's strongly recommended to use different credentials anyway.
|
Keep in mind that PostgreSQL user accounts and permissions are not included with the dump: You will need to create those manually if you want to fully replicate the original database (see the [installation docs](../installation/1-postgresql.md)). When setting up a development instance of NetBox, it's strongly recommended to use different credentials anyway.
|
||||||
|
|
||||||
### Export the Database Schema
|
### Export the Database Schema
|
||||||
|
|
|
||||||
|
|
@ -21,14 +21,14 @@ flowchart BT
|
||||||
modulebay1 & modulebay2 & modulebay3 --> device[Device]
|
modulebay1 & modulebay2 & modulebay3 --> device[Device]
|
||||||
```
|
```
|
||||||
|
|
||||||
### 1. Select an SFP Module Type Profile
|
### 1. Create an SFP Module Type Profile
|
||||||
|
|
||||||
New NetBox installations include a "Transceiver" [module type profile](../models/dcim/moduletypeprofile.md), which you can select for all module types which represent a pluggable transceiver. Typically, you will need only one profile for all pluggable transceivers. If this profile is not present, or if you prefer a different set of attributes, create your own profile for SFPs instead.
|
If one has not already been defined, create a [module type profile](../models/dcim/moduletypeprofile.md) for SFPs. This profile will be assigned for all module types which represent a pluggable transceiver. Typically, you will need only one profile for all pluggable transceivers.
|
||||||
|
|
||||||
The default profile defines attributes for form factor, media, PHY, data rate, reach, and connector type. You might opt to add or replace these by editing the profile's [JSON schema](https://json-schema.org/). Profile attributes might be used to define characteristics unique to transceivers, such as optical wavelength and power ranges. Adding profile attributes is optional, and can be done at a later point.
|
You might opt to define custom attributes for the profile by defining a custom [JSON schema](https://json-schema.org/). Profile attributes might be used to define characteristics unique to transceivers, such as optical wavelength and power ranges. Adding profile attributes is optional, and can be done at a later point.
|
||||||
|
|
||||||
!!! note
|
!!! note
|
||||||
Assigning a module type profile is optional, but recommended as it allows for defining custom module attributes.
|
Creating a module type profile is optional, but recommended as it allows for defining custom module attributes.
|
||||||
|
|
||||||
### 2. Create a Module Type for Each SFP Model in Inventory
|
### 2. Create a Module Type for Each SFP Model in Inventory
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -56,20 +56,6 @@ FIELD_CHOICES = {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
In addition to plain tuples, each choice may be defined as a dictionary, which allows specifying a description (shown as a subtitle beneath the option) alongside the value, label, and color. `value` and `label` are required; `color` and `description` are optional:
|
|
||||||
|
|
||||||
```python
|
|
||||||
FIELD_CHOICES = {
|
|
||||||
'dcim.Site.status': (
|
|
||||||
{'value': 'foo', 'label': 'Foo', 'color': 'red', 'description': 'The foo status'},
|
|
||||||
{'value': 'bar', 'label': 'Bar', 'color': 'green'},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
!!! info "New in NetBox v4.7"
|
|
||||||
The dictionary-based format for declaring choices was introduced in NetBox v4.7. The tuple-based format remains supported, but will be deprecated in a future release and support for it will eventually be removed.
|
|
||||||
|
|
||||||
!!! info "Case-Insensitive Field Identifiers"
|
!!! info "Case-Insensitive Field Identifiers"
|
||||||
Field identifiers are case-insensitive. Both `dcim.Site.status` and `dcim.site.status` are valid and equivalent.
|
Field identifiers are case-insensitive. Both `dcim.Site.status` and `dcim.site.status` are valid and equivalent.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,27 @@ The default configuration is shown below:
|
||||||
|
|
||||||
Additionally, `http_proxy` and `https_proxy` are set to the HTTP and HTTPS proxies, respectively, configured for NetBox (if any).
|
Additionally, `http_proxy` and `https_proxy` are set to the HTTP and HTTPS proxies, respectively, configured for NetBox (if any).
|
||||||
|
|
||||||
|
## SENTRY_DSN
|
||||||
|
|
||||||
|
!!! warning "This parameter will be removed in NetBox v4.7."
|
||||||
|
Set this using `SENTRY_CONFIG` instead:
|
||||||
|
|
||||||
|
```
|
||||||
|
SENTRY_CONFIG = {
|
||||||
|
"dsn": "https://examplePublicKey@o0.ingest.sentry.io/0",
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Default: `None`
|
||||||
|
|
||||||
|
Defines a Sentry data source name (DSN) for automated error reporting. `SENTRY_ENABLED` must be `True` for this parameter to take effect. For example:
|
||||||
|
|
||||||
|
```
|
||||||
|
SENTRY_DSN = "https://examplePublicKey@o0.ingest.sentry.io/0"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## SENTRY_ENABLED
|
## SENTRY_ENABLED
|
||||||
|
|
||||||
Default: `False`
|
Default: `False`
|
||||||
|
|
@ -27,6 +48,43 @@ Set to `True` to enable automatic error reporting via [Sentry](https://sentry.io
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## SENTRY_SAMPLE_RATE
|
||||||
|
|
||||||
|
!!! warning "This parameter will be removed in NetBox v4.7."
|
||||||
|
Set this using `SENTRY_CONFIG` instead:
|
||||||
|
|
||||||
|
```
|
||||||
|
SENTRY_CONFIG = {
|
||||||
|
"sample_rate": 0.2,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Default: `1.0` (all)
|
||||||
|
|
||||||
|
The sampling rate for errors. Must be a value between 0 (disabled) and 1.0 (report on all errors).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## SENTRY_SEND_DEFAULT_PII
|
||||||
|
|
||||||
|
!!! warning "This parameter will be removed in NetBox v4.7."
|
||||||
|
Set this using `SENTRY_CONFIG` instead:
|
||||||
|
|
||||||
|
```
|
||||||
|
SENTRY_CONFIG = {
|
||||||
|
"send_default_pii": True,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Default: `False`
|
||||||
|
|
||||||
|
Maps to the Sentry SDK's [`send_default_pii`](https://docs.sentry.io/platforms/python/configuration/options/#send-default-pii) parameter. If enabled, certain personally identifiable information (PII) is added.
|
||||||
|
|
||||||
|
!!! warning "Sensitive data"
|
||||||
|
If you enable this option, be aware that sensitive data such as cookies and authentication tokens will be logged.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## SENTRY_TAGS
|
## SENTRY_TAGS
|
||||||
|
|
||||||
An optional dictionary of tag names and values to apply to Sentry error reports.For example:
|
An optional dictionary of tag names and values to apply to Sentry error reports.For example:
|
||||||
|
|
@ -41,3 +99,22 @@ SENTRY_TAGS = {
|
||||||
!!! warning "Reserved tag prefixes"
|
!!! warning "Reserved tag prefixes"
|
||||||
Avoid using any tag names which begin with `netbox.`, as this prefix is reserved by the NetBox application.
|
Avoid using any tag names which begin with `netbox.`, as this prefix is reserved by the NetBox application.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## SENTRY_TRACES_SAMPLE_RATE
|
||||||
|
|
||||||
|
!!! warning "This parameter will be removed in NetBox v4.7."
|
||||||
|
Set this using `SENTRY_CONFIG` instead:
|
||||||
|
|
||||||
|
```
|
||||||
|
SENTRY_CONFIG = {
|
||||||
|
"traces_sample_rate": 0.2,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Default: `0` (disabled)
|
||||||
|
|
||||||
|
The sampling rate for transactions. Must be a value between 0 (disabled) and 1.0 (report on all transactions).
|
||||||
|
|
||||||
|
!!! warning "Consider performance implications"
|
||||||
|
A high sampling rate for transactions can induce significant performance penalties. If transaction reporting is desired, it is recommended to use a relatively low sample rate of 10% to 20% (0.1 to 0.2).
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
## GRAPHQL_DEFAULT_VERSION
|
## GRAPHQL_DEFAULT_VERSION
|
||||||
|
|
||||||
|
!!! note "This parameter was introduced in NetBox v4.5."
|
||||||
|
|
||||||
Default: `1`
|
Default: `1`
|
||||||
|
|
||||||
Designates the default version of the GraphQL API served by `/graphql/`. To access a specific version, append the version number to the URL, e.g. `/graphql/v2/`.
|
Designates the default version of the GraphQL API served by `/graphql/`. To access a specific version, append the version number to the URL, e.g. `/graphql/v2/`.
|
||||||
|
|
@ -28,6 +30,8 @@ The maximum number of queries that a GraphQL API request may contain.
|
||||||
|
|
||||||
## GRAPHQL_MAX_QUERY_DEPTH
|
## GRAPHQL_MAX_QUERY_DEPTH
|
||||||
|
|
||||||
|
!!! note "This parameter was introduced in NetBox v4.6.1."
|
||||||
|
|
||||||
Default: `None` (no limit)
|
Default: `None` (no limit)
|
||||||
|
|
||||||
The maximum allowed depth of any GraphQL query. When set to a positive integer, requests containing queries that exceed this depth will be rejected. Leaving this parameter unset (or setting it to `None` or `0`) disables query depth enforcement.
|
The maximum allowed depth of any GraphQL query. When set to a positive integer, requests containing queries that exceed this depth will be rejected. Leaving this parameter unset (or setting it to `None` or `0`) disables query depth enforcement.
|
||||||
|
|
|
||||||
|
|
@ -6,9 +6,6 @@ NetBox's configuration file contains all the important parameters which control
|
||||||
|
|
||||||
The configuration file is loaded from `$INSTALL_ROOT/netbox/netbox/configuration.py` by default. An example configuration is provided at `configuration_example.py`, which you may copy to use as your default config. Note that a configuration file must be defined; NetBox will not run without one.
|
The configuration file is loaded from `$INSTALL_ROOT/netbox/netbox/configuration.py` by default. An example configuration is provided at `configuration_example.py`, which you may copy to use as your default config. Note that a configuration file must be defined; NetBox will not run without one.
|
||||||
|
|
||||||
!!! note "Python package installations (experimental)"
|
|
||||||
An experimental Python package installation loads `$NETBOX_ROOT/conf/configuration.py` by default. `NETBOX_ROOT` defaults to `/opt/netbox`. Use `netbox setup --target <path>` to scaffold the local configuration, and keep configuration and mutable instance data outside the virtual environment and installed package. The setup target is not persisted; set `NETBOX_ROOT` for all commands and services when using a non-default path.
|
|
||||||
|
|
||||||
!!! info "Customizing the Configuration Module"
|
!!! info "Customizing the Configuration Module"
|
||||||
A custom configuration module may be specified by setting the `NETBOX_CONFIGURATION` environment variable. This must be a dotted path to the desired Python module. For example, a file named `my_config.py` in the same directory as `settings.py` would be referenced as `netbox.my_config`.
|
A custom configuration module may be specified by setting the `NETBOX_CONFIGURATION` environment variable. This must be a dotted path to the desired Python module. For example, a file named `my_config.py` in the same directory as `settings.py` would be referenced as `netbox.my_config`.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -125,6 +125,8 @@ The maximum size (in bytes) of an incoming HTTP request (i.e. `GET` or `POST` da
|
||||||
|
|
||||||
## STREAMING_EXPORTS
|
## STREAMING_EXPORTS
|
||||||
|
|
||||||
|
!!! note "This parameter was introduced in NetBox v4.6."
|
||||||
|
|
||||||
Default: `False`
|
Default: `False`
|
||||||
|
|
||||||
When set to `True`, CSV bulk exports are returned as a streaming HTTP response, emitting rows to the client as they are rendered rather than buffering the entire dataset in memory first. This can significantly reduce memory usage and time-to-first-byte for very large exports.
|
When set to `True`, CSV bulk exports are returned as a streaming HTTP response, emitting rows to the client as they are rendered rather than buffering the entire dataset in memory first. This can significantly reduce memory usage and time-to-first-byte for very large exports.
|
||||||
|
|
@ -275,10 +277,7 @@ This is a wrapper for passing global configuration parameters to [Django RQ](htt
|
||||||
|
|
||||||
Default: `300`
|
Default: `300`
|
||||||
|
|
||||||
The maximum execution time of a background task (such as running a custom script), in seconds. This may also be expressed as a duration string such as `1h` or `30m`, which NetBox normalizes to seconds when comparing it against webhook timeouts. Set this to `-1` to disable the job timeout entirely.
|
The maximum execution time of a background task (such as running a custom script), in seconds.
|
||||||
|
|
||||||
!!! note
|
|
||||||
A value of zero (or `None`) does not disable the timeout: RQ falls back to its own default of 180 seconds, and NetBox validates webhook timeouts against that value accordingly.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -307,19 +306,3 @@ The base unit for disk sizes. Set this to `1024` to use binary prefixes (MiB, Gi
|
||||||
Default: `1000`
|
Default: `1000`
|
||||||
|
|
||||||
The base unit for RAM sizes. Set this to `1024` to use binary prefixes (MiB, GiB, etc.) instead of decimal prefixes (MB, GB, etc.).
|
The base unit for RAM sizes. Set this to `1024` to use binary prefixes (MiB, GiB, etc.) instead of decimal prefixes (MB, GB, etc.).
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## WEBHOOK_DEFAULT_TIMEOUT
|
|
||||||
|
|
||||||
Default: `60`
|
|
||||||
|
|
||||||
The default maximum time (in seconds) to wait for a response when sending a webhook. This value is used for any webhook which does not define its own timeout. Keeping this below [`RQ_DEFAULT_TIMEOUT`](#rq_default_timeout) gives an unresponsive receiver a chance to be cut off by the request timeout rather than by termination of the background job.
|
|
||||||
|
|
||||||
This value must be an integer between 1 and 3600, and must be less than `RQ_DEFAULT_TIMEOUT`; NetBox will refuse to start otherwise. The same upper bound is enforced on the per-webhook [timeout](../models/extras/webhook.md#timeout) field.
|
|
||||||
|
|
||||||
!!! warning "Upgrading"
|
|
||||||
If you have lowered `RQ_DEFAULT_TIMEOUT` to 60 seconds or less and have not set `WEBHOOK_DEFAULT_TIMEOUT`, NetBox will not start until you set `WEBHOOK_DEFAULT_TIMEOUT` to a value below your job timeout.
|
|
||||||
|
|
||||||
!!! note
|
|
||||||
The timeout is applied separately to establishing the connection and to waiting for data, rather than to the request as a whole. A receiver which responds slowly but continuously can therefore keep a request open for longer than the configured value. `RQ_DEFAULT_TIMEOUT` remains the ultimate upper bound on how long a webhook job can occupy a worker.
|
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,8 @@ ALLOWED_HOSTS = ['*']
|
||||||
|
|
||||||
## API_TOKEN_PEPPERS
|
## API_TOKEN_PEPPERS
|
||||||
|
|
||||||
|
!!! info "This parameter was introduced in NetBox v4.5."
|
||||||
|
|
||||||
[Cryptographic peppers](https://en.wikipedia.org/wiki/Pepper_(cryptography)) are employed to generate hashes of sensitive values on the server. This parameter defines the peppers used to hash v2 API tokens in NetBox. You must define at least one pepper before creating a v2 API token. See the [API documentation](../integrations/rest-api.md#authentication) for further information about how peppers are used.
|
[Cryptographic peppers](https://en.wikipedia.org/wiki/Pepper_(cryptography)) are employed to generate hashes of sensitive values on the server. This parameter defines the peppers used to hash v2 API tokens in NetBox. You must define at least one pepper before creating a v2 API token. See the [API documentation](../integrations/rest-api.md#authentication) for further information about how peppers are used.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
|
|
@ -37,7 +39,7 @@ API_TOKEN_PEPPERS = {
|
||||||
!!! warning "Peppers are sensitive"
|
!!! warning "Peppers are sensitive"
|
||||||
Treat pepper values as extremely sensitive. Consider populating peppers from environment variables at initialization time rather than defining them in the configuration file, if feasible.
|
Treat pepper values as extremely sensitive. Consider populating peppers from environment variables at initialization time rather than defining them in the configuration file, if feasible.
|
||||||
|
|
||||||
Peppers must be at least 50 characters in length and should comprise a random string with a diverse character set. Consider using the Python script at `$INSTALL_ROOT/netbox/generate_secret_key.py` to generate a pepper value. For a Python package installation, run the virtual environment's `netbox secret-key` command instead.
|
Peppers must be at least 50 characters in length and should comprise a random string with a diverse character set. Consider using the Python script at `$INSTALL_ROOT/netbox/generate_secret_key.py` to generate a pepper value.
|
||||||
|
|
||||||
It is recommended to start with a pepper ID of `1`. Additional peppers can be introduced later as needed to begin rotating token hashes.
|
It is recommended to start with a pepper ID of `1`. Additional peppers can be introduced later as needed to begin rotating token hashes.
|
||||||
|
|
||||||
|
|
@ -57,7 +59,7 @@ See the [`DATABASES`](#databases) configuration below for usage.
|
||||||
|
|
||||||
## DATABASES
|
## DATABASES
|
||||||
|
|
||||||
NetBox requires access to a PostgreSQL 15 or later database service to store data. This service can run locally on the NetBox server or on a remote system. Databases are defined as named dictionaries:
|
NetBox requires access to a PostgreSQL 14 or later database service to store data. Note that support for PostgreSQL 14 is deprecated and will be removed in NetBox v4.7; PostgreSQL 15 or later will be required. This service can run locally on the NetBox server or on a remote system. Databases are defined as named dictionaries:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
DATABASES = {
|
DATABASES = {
|
||||||
|
|
@ -144,9 +146,6 @@ REDIS = {
|
||||||
It is highly recommended to keep the task and cache databases separate. Using the same database number on the
|
It is highly recommended to keep the task and cache databases separate. Using the same database number on the
|
||||||
same Redis instance for both may result in queued background tasks being lost during cache flushing events.
|
same Redis instance for both may result in queued background tasks being lost during cache flushing events.
|
||||||
|
|
||||||
!!! danger "Redis is a trusted component"
|
|
||||||
NetBox's background workers deserialize and execute jobs read from the `tasks` Redis database, so any party with write access to it can run arbitrary code on a worker. Redis must be treated as trusted infrastructure, on par with the PostgreSQL database: keep it bound to a private network and require authentication.
|
|
||||||
|
|
||||||
### UNIX Socket Support
|
### UNIX Socket Support
|
||||||
|
|
||||||
Redis may alternatively be configured by specifying a complete URL instead of individual components. This approach supports the use of a UNIX socket connection. For example:
|
Redis may alternatively be configured by specifying a complete URL instead of individual components. This approach supports the use of a UNIX socket connection. For example:
|
||||||
|
|
@ -249,4 +248,4 @@ REDIS = {
|
||||||
|
|
||||||
This is a secret, pseudorandom string used to assist in the creation new cryptographic hashes for passwords and HTTP cookies. The key defined here should not be shared outside the configuration file. `SECRET_KEY` can be changed at any time without impacting stored data, however be aware that doing so will invalidate all existing user sessions. NetBox deployments comprising multiple nodes must have the same secret key configured on all nodes.
|
This is a secret, pseudorandom string used to assist in the creation new cryptographic hashes for passwords and HTTP cookies. The key defined here should not be shared outside the configuration file. `SECRET_KEY` can be changed at any time without impacting stored data, however be aware that doing so will invalidate all existing user sessions. NetBox deployments comprising multiple nodes must have the same secret key configured on all nodes.
|
||||||
|
|
||||||
`SECRET_KEY` **must** be at least 50 characters in length, and should contain a mix of letters, digits, and symbols. The script located at `$INSTALL_ROOT/netbox/generate_secret_key.py` may be used to generate a suitable key. For a Python package installation, run the virtual environment's `netbox secret-key` command instead. Please note that this key is **not** used directly for hashing user passwords or for the encrypted storage of secret data in NetBox.
|
`SECRET_KEY` **must** be at least 50 characters in length, and should contain a mix of letters, digits, and symbols. The script located at `$INSTALL_ROOT/netbox/generate_secret_key.py` may be used to generate a suitable key. Please note that this key is **not** used directly for hashing user passwords or for the encrypted storage of secret data in NetBox.
|
||||||
|
|
|
||||||
|
|
@ -6,10 +6,7 @@
|
||||||
|
|
||||||
Default: `('file', 'ftp', 'ftps', 'http', 'https', 'irc', 'mailto', 'sftp', 'ssh', 'tel', 'telnet', 'tftp', 'vnc', 'xmpp')`
|
Default: `('file', 'ftp', 'ftps', 'http', 'https', 'irc', 'mailto', 'sftp', 'ssh', 'tel', 'telnet', 'tftp', 'vnc', 'xmpp')`
|
||||||
|
|
||||||
A list of permitted URL schemes referenced when rendering links within NetBox. This list is also enforced when validating the value of URL custom fields. Note that only the schemes specified in this list will be accepted: If adding your own, be sure to replicate all the default values as well (excluding those schemes which are not desirable).
|
A list of permitted URL schemes referenced when rendering links within NetBox. Note that only the schemes specified in this list will be accepted: If adding your own, be sure to replicate all the default values as well (excluding those schemes which are not desirable).
|
||||||
|
|
||||||
!!! note
|
|
||||||
Image sources (`<img src="...">`) are limited to HTTP(S) and relative URLs, subject to `ALLOWED_URL_SCHEMES`.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -171,6 +168,9 @@ Default: `True`
|
||||||
|
|
||||||
When enabled, only authenticated users are permitted to access any part of NetBox. Disabling this will allow unauthenticated users to access most areas of NetBox (but not make any changes).
|
When enabled, only authenticated users are permitted to access any part of NetBox. Disabling this will allow unauthenticated users to access most areas of NetBox (but not make any changes).
|
||||||
|
|
||||||
|
!!! info "Changed in NetBox v4.0.2"
|
||||||
|
Prior to NetBox v4.0.2, this setting was disabled by default.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## LOGIN_TIMEOUT
|
## LOGIN_TIMEOUT
|
||||||
|
|
|
||||||
|
|
@ -12,22 +12,6 @@ BASE_PATH = 'netbox/'
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## BULK_UPDATE_CHUNK_SIZE
|
|
||||||
|
|
||||||
Default: `5000`
|
|
||||||
|
|
||||||
The maximum number of rows to affect in a single SQL `UPDATE` statement when NetBox performs a bulk update across many objects (for example, when recalculating cached counters or backfilling custom field data). On very large tables, an unbounded update spanning millions of rows can exceed the database's configured statement timeout; splitting the work into batches of at most this many rows bounds each statement while keeping the overall operation atomic.
|
|
||||||
|
|
||||||
Must be a positive integer, or `None` to disable chunking and issue each bulk update as a single unbounded statement.
|
|
||||||
|
|
||||||
This parameter also determines when a custom field operation is deferred to a background job: creating a field with a default value, or deleting a field, is performed within the request only where the field's assigned object types hold no more than this many objects in total (see [field status](../customization/custom-fields.md#field-status)). Setting it to `None` therefore defers every such operation which affects any object.
|
|
||||||
|
|
||||||
```python
|
|
||||||
BULK_UPDATE_CHUNK_SIZE = 5000
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## DATABASE_ROUTERS
|
## DATABASE_ROUTERS
|
||||||
|
|
||||||
Default: `[]` (empty list)
|
Default: `[]` (empty list)
|
||||||
|
|
@ -56,7 +40,7 @@ The filesystem path to NetBox's documentation. This is used when presenting cont
|
||||||
|
|
||||||
In order to send email, NetBox needs an email server configured. The following items can be defined within the `EMAIL` configuration parameter:
|
In order to send email, NetBox needs an email server configured. The following items can be defined within the `EMAIL` configuration parameter:
|
||||||
|
|
||||||
* `SERVER` - Hostname or IP address of the email server (required; use `localhost` if running locally)
|
* `SERVER` - Hostname or IP address of the email server (use `localhost` if running locally)
|
||||||
* `PORT` - TCP port to use for the connection (default: `25`)
|
* `PORT` - TCP port to use for the connection (default: `25`)
|
||||||
* `USERNAME` - Username with which to authenticate
|
* `USERNAME` - Username with which to authenticate
|
||||||
* `PASSWORD` - Password with which to authenticate
|
* `PASSWORD` - Password with which to authenticate
|
||||||
|
|
@ -70,9 +54,6 @@ In order to send email, NetBox needs an email server configured. The following i
|
||||||
!!! note
|
!!! note
|
||||||
The `USE_SSL` and `USE_TLS` parameters are mutually exclusive.
|
The `USE_SSL` and `USE_TLS` parameters are mutually exclusive.
|
||||||
|
|
||||||
!!! warning
|
|
||||||
`SERVER` must be defined in order to send email: A deployment which omits it raises an `InvalidMailer` exception when attempting to send. Note that this is raised at send time rather than at startup, so a misconfiguration here will not be apparent until NetBox first tries to send mail.
|
|
||||||
|
|
||||||
Email is sent from NetBox only for critical events or if configured for [logging](#logging). If you would like to test the email server configuration, Django provides a convenient [send_mail()](https://docs.djangoproject.com/en/stable/topics/email/#send-mail) function accessible within the NetBox shell:
|
Email is sent from NetBox only for critical events or if configured for [logging](#logging). If you would like to test the email server configuration, Django provides a convenient [send_mail()](https://docs.djangoproject.com/en/stable/topics/email/#send-mail) function accessible within the NetBox shell:
|
||||||
|
|
||||||
```no-highlight
|
```no-highlight
|
||||||
|
|
@ -82,7 +63,8 @@ Email is sent from NetBox only for critical events or if configured for [logging
|
||||||
'Test Email Subject',
|
'Test Email Subject',
|
||||||
'Test Email Body',
|
'Test Email Body',
|
||||||
'noreply-netbox@example.com',
|
'noreply-netbox@example.com',
|
||||||
['users@example.com']
|
['users@example.com'],
|
||||||
|
fail_silently=False
|
||||||
)
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -90,6 +72,8 @@ Email is sent from NetBox only for critical events or if configured for [logging
|
||||||
|
|
||||||
## HOSTNAME
|
## HOSTNAME
|
||||||
|
|
||||||
|
!!! info "This parameter was introduced in NetBox v4.4."
|
||||||
|
|
||||||
Default: System hostname
|
Default: System hostname
|
||||||
|
|
||||||
The hostname displayed in the user interface identifying the system on which NetBox is running. If not defined, this defaults to the system hostname as reported by Python's `platform.node()`.
|
The hostname displayed in the user interface identifying the system on which NetBox is running. If not defined, this defaults to the system hostname as reported by Python's `platform.node()`.
|
||||||
|
|
@ -98,6 +82,8 @@ The hostname displayed in the user interface identifying the system on which Net
|
||||||
|
|
||||||
## HTTP_CLIENT_IP_HEADERS
|
## HTTP_CLIENT_IP_HEADERS
|
||||||
|
|
||||||
|
!!! info "This parameter was introduced in NetBox v4.6.1."
|
||||||
|
|
||||||
Default:
|
Default:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
|
|
@ -112,9 +98,6 @@ An ordered list of HTTP request headers inspected to determine the source IP add
|
||||||
|
|
||||||
The client IP is used for source-address restrictions on API tokens and for logging failed login attempts.
|
The client IP is used for source-address restrictions on API tokens and for logging failed login attempts.
|
||||||
|
|
||||||
!!! warning "Client IP trust"
|
|
||||||
The headers listed here are trusted as the source of the client IP address. Trusting `X-Forwarded-For` (`HTTP_X_FORWARDED_FOR`) or `X-Real-IP` (`HTTP_X_REAL_IP`) is safe only when NetBox is deployed behind a reverse proxy that overwrites these headers with the real client address. If NetBox is reachable directly, or the proxy appends to or passes through a client-supplied value (NetBox uses the leftmost address, which the client controls when the proxy appends), a client can spoof its apparent IP address and defeat API token client IP restrictions. Deployments without a trusted proxy should set `HTTP_CLIENT_IP_HEADERS = ('REMOTE_ADDR',)`.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## HTTP_PROXIES
|
## HTTP_PROXIES
|
||||||
|
|
@ -142,7 +125,7 @@ A list of IP addresses recognized as internal to the system, used to control the
|
||||||
example, the debugging toolbar will be viewable only when a client is accessing NetBox from one of the listed IP
|
example, the debugging toolbar will be viewable only when a client is accessing NetBox from one of the listed IP
|
||||||
addresses (and [`DEBUG`](./development.md#debug) is `True`).
|
addresses (and [`DEBUG`](./development.md#debug) is `True`).
|
||||||
|
|
||||||
!!! info "Enabling the toolbar for all clients"
|
!!! info "New in NetBox v4.6"
|
||||||
Setting this parameter to an empty list will enable the toolbar for all requests provided debugging is enabled:
|
Setting this parameter to an empty list will enable the toolbar for all requests provided debugging is enabled:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
|
|
@ -166,7 +149,7 @@ Set this configuration parameter to `True` for NetBox deployments which do not h
|
||||||
|
|
||||||
Default: `[]`
|
Default: `[]`
|
||||||
|
|
||||||
A list of system environment variable names which may be referenced from within Jinja templates via the built-in [`env`](#jinja_filters) filter. Patterns may include wildcards (matched using Python's `fnmatch` syntax). Any variable whose name does not match an entry in this list cannot be referenced from a template. For example:
|
A list of system environment variable names which may be referenced from within Jinja2 templates via the built-in [`env`](#jinja2_filters) filter. Patterns may include wildcards (matched using Python's `fnmatch` syntax). Any variable whose name does not match an entry in this list cannot be referenced from a template. For example:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
JINJA_ENVIRONMENT_PARAMS = [
|
JINJA_ENVIRONMENT_PARAMS = [
|
||||||
|
|
@ -180,39 +163,33 @@ JINJA_ENVIRONMENT_PARAMS = [
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## JINJA_FILTERS
|
## JINJA2_FILTERS
|
||||||
|
|
||||||
!!! info "Renamed in NetBox v4.7"
|
|
||||||
This parameter was formerly named `JINJA2_FILTERS`. The old name is still supported for backward compatibility but is deprecated and will be removed in NetBox v5.0.
|
|
||||||
|
|
||||||
Default: `{}`
|
Default: `{}`
|
||||||
|
|
||||||
A dictionary of custom Jinja filters with the key being the filter name and the value being a callable. For more information see the [Jinja documentation](https://jinja.palletsprojects.com/en/3.1.x/api/#custom-filters). For example:
|
A dictionary of custom Jinja2 filters with the key being the filter name and the value being a callable. For more information see the [Jinja2 documentation](https://jinja.palletsprojects.com/en/3.1.x/api/#custom-filters). For example:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
def uppercase(x):
|
def uppercase(x):
|
||||||
return str(x).upper()
|
return str(x).upper()
|
||||||
|
|
||||||
JINJA_FILTERS = {
|
JINJA2_FILTERS = {
|
||||||
'uppercase': uppercase,
|
'uppercase': uppercase,
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
NetBox also registers the following filters by default. Any entry defined in `JINJA_FILTERS` with the same name will override the default.
|
NetBox also registers the following filters by default. Any entry defined in `JINJA2_FILTERS` with the same name will override the default.
|
||||||
|
|
||||||
| Filter | Description |
|
| Filter | Description |
|
||||||
|---|---|
|
|---|---|
|
||||||
| `env` | Returns the value of the system environment variable with the given name, provided its name matches an entry in [`JINJA_ENVIRONMENT_PARAMS`](#jinja_environment_params). Returns `None` if the variable is not defined or its name is not whitelisted. |
|
| `env` | Returns the value of the system environment variable with the given name, provided its name matches an entry in [`JINJA_ENVIRONMENT_PARAMS`](#jinja_environment_params). Returns `None` if the variable is not defined or its name is not whitelisted. |
|
||||||
|
|
||||||
For example, given `JINJA_ENVIRONMENT_PARAMS = ['WEBHOOK_TOKEN_*']`, a Jinja template may reference an environment variable as:
|
For example, given `JINJA_ENVIRONMENT_PARAMS = ['WEBHOOK_TOKEN_*']`, a Jinja2 template may reference an environment variable as:
|
||||||
|
|
||||||
```
|
```
|
||||||
Authorization: Bearer {{ 'WEBHOOK_TOKEN_3' | env }}
|
Authorization: Bearer {{ 'WEBHOOK_TOKEN_3' | env }}
|
||||||
```
|
```
|
||||||
|
|
||||||
!!! tip "Plugin-provided filters"
|
|
||||||
Plugins can also register Jinja filters without requiring instance configuration. See [Jinja Config Templates](../plugins/development/config-templates.md) in the plugin development documentation. Instance-level `JINJA_FILTERS` always takes precedence over plugin-registered filters of the same name.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## LOGGING
|
## LOGGING
|
||||||
|
|
@ -282,9 +259,6 @@ The file path to the location where [custom reports](../customization/reports.md
|
||||||
|
|
||||||
## SCRIPTS_ROOT
|
## SCRIPTS_ROOT
|
||||||
|
|
||||||
!!! warning "Deprecation Warning"
|
|
||||||
The custom scripts functionality has been deprecated beginning in NetBox v4.7, and is scheduled for removal in NetBox v5.0. This parameter will be removed along with it.
|
|
||||||
|
|
||||||
Default: `$INSTALL_ROOT/netbox/scripts/`
|
Default: `$INSTALL_ROOT/netbox/scripts/`
|
||||||
|
|
||||||
The file path to the location where [custom scripts](../customization/custom-scripts.md) will be kept. By default, this is the `netbox/scripts/` directory within the base NetBox installation path.
|
The file path to the location where [custom scripts](../customization/custom-scripts.md) will be kept. By default, this is the `netbox/scripts/` directory within the base NetBox installation path.
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ Custom fields may be created by navigating to Customization > Custom Fields. Net
|
||||||
* Boolean: True or false
|
* Boolean: True or false
|
||||||
* Date: A date in ISO 8601 format (YYYY-MM-DD)
|
* Date: A date in ISO 8601 format (YYYY-MM-DD)
|
||||||
* Date & time: A date and time in ISO 8601 format (YYYY-MM-DD HH:MM:SS)
|
* Date & time: A date and time in ISO 8601 format (YYYY-MM-DD HH:MM:SS)
|
||||||
* URL: This will be presented as a link in the web UI. Values are restricted to the schemes permitted by [`ALLOWED_URL_SCHEMES`](../configuration/security.md#allowed_url_schemes). A value entered without a scheme (e.g. `example.com`) is assumed to use `https` and stored as an absolute URL (e.g. `https://example.com`).
|
* URL: This will be presented as a link in the web UI
|
||||||
* JSON: Arbitrary data stored in JSON format
|
* JSON: Arbitrary data stored in JSON format
|
||||||
* Selection: A selection of one of several pre-defined custom choices
|
* Selection: A selection of one of several pre-defined custom choices
|
||||||
* Multiple selection: A selection field which supports the assignment of multiple values
|
* Multiple selection: A selection field which supports the assignment of multiple values
|
||||||
|
|
@ -30,42 +30,6 @@ Marking a field as required will force the user to provide a value for the field
|
||||||
|
|
||||||
A custom field must be assigned to one or more object types, or models, in NetBox. Once created, custom fields will automatically appear as part of these models in the web UI and REST API. Note that not all models support custom fields.
|
A custom field must be assigned to one or more object types, or models, in NetBox. Once created, custom fields will automatically appear as part of these models in the web UI and REST API. Note that not all models support custom fields.
|
||||||
|
|
||||||
!!! info "This behavior changed in NetBox v4.6.8."
|
|
||||||
To improve performance when creating custom fields, empty field values are no longer pre-provisioned.
|
|
||||||
|
|
||||||
Unless the field has been assigned a default value, creating a custom field does not write a value to the objects which already exist. An object which has never been assigned a value simply stores nothing for the field, and reports the field as having no value in the web UI, REST API, GraphQL API, and exports, exactly as if it stored an explicit null.
|
|
||||||
|
|
||||||
This matters only if you query the underlying `custom_field_data` JSON directly, for example in a custom script. The field's key is absent from an object's data until a value is assigned to it, so read it with `obj.cf['field_name']` or `obj.custom_field_data.get('field_name')` rather than by direct subscript.
|
|
||||||
|
|
||||||
Assigning a default value, by contrast, does write that value to every existing object at the time the field is created, so that objects can be filtered by it immediately. Note that a default added to a field which already exists is _not_ backfilled: objects with no value continue to report none until they are next saved.
|
|
||||||
|
|
||||||
### Field Status
|
|
||||||
|
|
||||||
!!! info "This behavior was introduced in NetBox v4.7.0."
|
|
||||||
|
|
||||||
Creating a custom field with a default value, and deleting a custom field, both require rewriting the stored data of the objects the field applies to. Where the field is assigned to a large number of objects, this cannot be completed within the request, so it is handed to a background job instead and the field reports its status accordingly:
|
|
||||||
|
|
||||||
| Status | Meaning |
|
|
||||||
| ------ | ------- |
|
|
||||||
| Active | The field is live and available for use. |
|
|
||||||
| Provisioning | The field's default value is being written to existing objects. |
|
|
||||||
| Deleting | The field's data is being removed from existing objects. |
|
|
||||||
|
|
||||||
Whether a background job is required is determined by the total number of objects of the field's assigned object types, measured against the [`BULK_UPDATE_CHUNK_SIZE`](../configuration/system.md#bulk_update_chunk_size) configuration parameter — not by how many of those objects actually hold a value for the field. Deleting a field assigned to a large table is therefore deferred even where the field holds no data at all: NetBox cannot count the objects holding a value without scanning the entire table, which is the cost the threshold exists to avoid.
|
|
||||||
|
|
||||||
A field is live only while active. During provisioning or deletion it does not appear on objects, in forms, in filters, or in either API, and its stored data is read and written by nothing but the job responsible for it; it becomes available (or disappears entirely) once the job completes. Objects created in the meantime are unaffected — a field being provisioned still supplies its default to new objects.
|
|
||||||
|
|
||||||
A field which is not active cannot be modified while its job runs, as its configuration must not change under the job rewriting its data. This includes assigning it further object types, and unassigning those it already carries: such a change is rejected until the field is live again.
|
|
||||||
|
|
||||||
A field pending deletion continues to occupy its name until its data has been removed, so that a new field cannot be created — and an existing field cannot be renamed — to a name whose old values are still present on objects.
|
|
||||||
|
|
||||||
These operations require a running [background worker](../features/background-jobs.md) (`rqworker`). A field left mid-operation, for example because no worker was running or because its job failed, remains in its pending status until that job runs to completion.
|
|
||||||
|
|
||||||
Such a field can always be deleted, whichever status it holds. Deleting one already pending deletion queues a fresh job to finish removing its data. A field left provisioning has no equivalent in-application retry: requeue its job from the background queues (**Admin > System > Background Tasks**, which requires a staff account), or delete the field and create it again.
|
|
||||||
|
|
||||||
!!! note
|
|
||||||
Unassigning an object type from a custom field still removes the field's data from those objects immediately, and remains subject to the request timeout on very large tables. The same applies to renaming a custom field.
|
|
||||||
|
|
||||||
### Filtering
|
### Filtering
|
||||||
|
|
||||||
The filter logic controls how values are matched when filtering objects by the custom field. Loose filtering (the default) matches on a partial value, whereas exact matching requires a complete match of the given string to a field's value. For example, exact filtering with the string "red" will only match the exact value "red", whereas loose filtering will match on the values "red", "red-orange", or "bored". Setting the filter logic to "disabled" disables filtering by the field entirely.
|
The filter logic controls how values are matched when filtering objects by the custom field. Loose filtering (the default) matches on a partial value, whereas exact matching requires a complete match of the given string to a field's value. For example, exact filtering with the string "red" will only match the exact value "red", whereas loose filtering will match on the values "red", "red-orange", or "bored". Setting the filter logic to "disabled" disables filtering by the field entirely.
|
||||||
|
|
@ -136,28 +100,6 @@ When retrieving an object via the REST API, all of its custom data will be inclu
|
||||||
...
|
...
|
||||||
```
|
```
|
||||||
|
|
||||||
Selection and multiple selection fields are returned as objects exposing both the stored value and its human-friendly label, following the same convention used by NetBox's built-in choice fields:
|
|
||||||
|
|
||||||
```json
|
|
||||||
"custom_fields": {
|
|
||||||
"site_type": {
|
|
||||||
"value": "datacenter",
|
|
||||||
"label": "Data Center"
|
|
||||||
},
|
|
||||||
"regions": [
|
|
||||||
{
|
|
||||||
"value": "us-east",
|
|
||||||
"label": "US East"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"value": "us-west",
|
|
||||||
"label": "US West"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
...
|
|
||||||
```
|
|
||||||
|
|
||||||
To set or change these values, simply include nested JSON data. For example:
|
To set or change these values, simply include nested JSON data. For example:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
|
|
@ -169,7 +111,3 @@ To set or change these values, simply include nested JSON data. For example:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
As with built-in choice fields, selection custom fields are written by passing the raw value (e.g. `"site_type": "datacenter"`), not the `{value, label}` object returned on read.
|
|
||||||
|
|
||||||
The GraphQL API's `custom_fields` field resolves selection and multiple selection values to the same `{value, label}` representation.
|
|
||||||
|
|
|
||||||
|
|
@ -28,13 +28,10 @@ The following context data is available within the template when rendering a cus
|
||||||
|-----------|-------------------------------------------------------------------------------------------------------------------|
|
|-----------|-------------------------------------------------------------------------------------------------------------------|
|
||||||
| `object` | The NetBox object being displayed |
|
| `object` | The NetBox object being displayed |
|
||||||
| `debug` | A boolean indicating whether debugging is enabled |
|
| `debug` | A boolean indicating whether debugging is enabled |
|
||||||
| `request` | A sanitized subset of the current request (see below) |
|
| `request` | The current WSGI request |
|
||||||
| `user` | The current user (if authenticated) |
|
| `user` | The current user (if authenticated) |
|
||||||
| `perms` | The [permissions](https://docs.djangoproject.com/en/stable/topics/auth/default/#permissions) assigned to the user |
|
| `perms` | The [permissions](https://docs.djangoproject.com/en/stable/topics/auth/default/#permissions) assigned to the user |
|
||||||
|
|
||||||
!!! note "Changed in NetBox v4.7"
|
|
||||||
For security, `request` no longer exposes the full WSGI request object. Only a safe subset of attributes is available: `request.id`, `request.path`, `request.path_info`, `request.method`, `request.GET` (the query parameters), and `request.user` (the username). Sensitive data such as cookies, headers, and session state is no longer accessible from within a custom link template.
|
|
||||||
|
|
||||||
While most of the context variables listed above will have consistent attributes, the object will be an instance of the specific object being viewed when the link is rendered. Different models have different fields and properties, so you may need to some research to determine the attributes available for use within your template for a specific object type.
|
While most of the context variables listed above will have consistent attributes, the object will be an instance of the specific object being viewed when the link is rendered. Different models have different fields and properties, so you may need to some research to determine the attributes available for use within your template for a specific object type.
|
||||||
|
|
||||||
Checking the REST API representation of an object is generally a convenient way to determine what attributes are available. You can also reference the NetBox source code directly for a comprehensive list.
|
Checking the REST API representation of an object is generally a convenient way to determine what attributes are available. You can also reference the NetBox source code directly for a comprehensive list.
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,5 @@
|
||||||
# Custom Scripts
|
# Custom Scripts
|
||||||
|
|
||||||
!!! warning "Deprecation Warning"
|
|
||||||
Beginning in NetBox v4.7, the custom scripts functionality built into core NetBox has been deprecated. It is being replaced by a dedicated open source plugin, which offers an expanded feature set including the organization of scripts into projects, the sharing of Python resources among scripts, and version control for individual scripts.
|
|
||||||
|
|
||||||
The core implementation will remain available and supported throughout the v4.7 and v4.8 release cycles, and is scheduled for removal in NetBox v5.0. No immediate action is required: Existing scripts will continue to work as they do today, and users may migrate to the plugin at any point during the migration period. Migration is intended to be a largely automated process which should not require rewriting scripts.
|
|
||||||
|
|
||||||
Custom scripting was introduced to provide a way for users to execute custom logic from within the NetBox UI. Custom scripts enable the user to directly and conveniently manipulate NetBox data in a prescribed fashion. They can be used to accomplish myriad tasks, such as:
|
Custom scripting was introduced to provide a way for users to execute custom logic from within the NetBox UI. Custom scripts enable the user to directly and conveniently manipulate NetBox data in a prescribed fashion. They can be used to accomplish myriad tasks, such as:
|
||||||
|
|
||||||
* Automatically populate new devices and cables in preparation for a new site deployment
|
* Automatically populate new devices and cables in preparation for a new site deployment
|
||||||
|
|
@ -28,9 +23,6 @@ Custom scripts are Python code which exists outside the NetBox code base, so the
|
||||||
|
|
||||||
## Writing Custom Scripts
|
## Writing Custom Scripts
|
||||||
|
|
||||||
!!! warning "Choose a unique file name"
|
|
||||||
A script file's name (without the `.py` extension) becomes its Python module name when the script is loaded. A script file must not share its name with a NetBox application (e.g. `circuits.py` or `dcim.py`) or any other installed Python module: the script will shadow that module in Python's import system and can break unrelated functionality. Choose a unique, descriptive file name, such as `circuit_maintenance.py`.
|
|
||||||
|
|
||||||
All custom scripts must inherit from the `extras.scripts.Script` base class. This class provides the functionality necessary to generate forms and log activity.
|
All custom scripts must inherit from the `extras.scripts.Script` base class. This class provides the functionality necessary to generate forms and log activity.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
|
|
@ -113,7 +105,7 @@ class MyScript(Script):
|
||||||
|
|
||||||
### `commit_default`
|
### `commit_default`
|
||||||
|
|
||||||
The checkbox to commit database changes when executing a script is checked by default. Set `commit_default` to False under the script's Meta class to leave this option unchecked by default. This setting controls only the initial state of the execution form.
|
The checkbox to commit database changes when executing a script is checked by default. Set `commit_default` to False under the script's Meta class to leave this option unchecked by default.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
commit_default = False
|
commit_default = False
|
||||||
|
|
@ -125,9 +117,7 @@ By default, a script can be scheduled for execution at a later time. Setting `sc
|
||||||
|
|
||||||
### `notifications_default`
|
### `notifications_default`
|
||||||
|
|
||||||
By default, a notification is generated for the user associated with the script's job each time the script finishes running. This attribute sets the initial value for the notifications field when running a script. Valid values are `always` (default), `on_failure`, and `never`.
|
By default, a notification is generated for the requesting user each time a script finishes running. This attribute sets the initial value for the notifications field when running a script. Valid values are `always` (default), `on_failure`, and `never`.
|
||||||
|
|
||||||
Scripts run from an event rule or the `runscript` management command use this value as their notification policy. For an event rule, the notification goes to the user associated with the triggering event, if there is one.
|
|
||||||
|
|
||||||
```python
|
```python
|
||||||
notifications_default = 'on_failure'
|
notifications_default = 'on_failure'
|
||||||
|
|
@ -141,7 +131,7 @@ notifications_default = 'on_failure'
|
||||||
|
|
||||||
### `job_timeout`
|
### `job_timeout`
|
||||||
|
|
||||||
Set the maximum allowed runtime for the script. If not set, `RQ_DEFAULT_TIMEOUT` will be used. Scripts run from an event rule use this value as their execution timeout.
|
Set the maximum allowed runtime for the script. If not set, `RQ_DEFAULT_TIMEOUT` will be used.
|
||||||
|
|
||||||
## Accessing Request Data
|
## Accessing Request Data
|
||||||
|
|
||||||
|
|
@ -301,9 +291,6 @@ All custom script variables support the following default options:
|
||||||
* `required` - Indicates whether the field is mandatory (all fields are required by default)
|
* `required` - Indicates whether the field is mandatory (all fields are required by default)
|
||||||
* `widget` - The class of form widget to use (see the [Django documentation](https://docs.djangoproject.com/en/stable/ref/forms/widgets/))
|
* `widget` - The class of form widget to use (see the [Django documentation](https://docs.djangoproject.com/en/stable/ref/forms/widgets/))
|
||||||
|
|
||||||
!!! warning "Reserved variable names"
|
|
||||||
The names `_commit`, `_schedule_at`, `_interval`, and `_notifications` are reserved for the execution parameters which NetBox renders alongside a script's own fields. A variable declared with one of these names shadows its execution parameter, and its value is not passed to `run()`. Choose a different name.
|
|
||||||
|
|
||||||
### StringVar
|
### StringVar
|
||||||
|
|
||||||
Stores a string of characters (i.e. text). Options include:
|
Stores a string of characters (i.e. text). Options include:
|
||||||
|
|
@ -450,24 +437,12 @@ Script modules can be uploaded to NetBox via the REST API by sending a `multipar
|
||||||
|
|
||||||
```no-highlight
|
```no-highlight
|
||||||
curl -X POST \
|
curl -X POST \
|
||||||
-H "Authorization: Bearer $TOKEN" \
|
-H "Authorization: Token $TOKEN" \
|
||||||
-H "Accept: application/json; indent=4" \
|
-H "Accept: application/json; indent=4" \
|
||||||
-F "file=@/path/to/myscript.py" \
|
-F "file=@/path/to/myscript.py" \
|
||||||
http://netbox/api/extras/scripts/upload/
|
http://netbox/api/extras/scripts/upload/
|
||||||
```
|
```
|
||||||
|
|
||||||
### Updating an Uploaded Script
|
|
||||||
|
|
||||||
An existing script module can be replaced in place by sending a `multipart/form-data` PUT or PATCH request to the module's detail URL. The module may be identified by its numeric ID or by its file name. The uploaded file name must match the existing module's file path, and the caller must have the `extras.change_scriptmodule` and `core.change_managedfile` permissions. The module's scripts are re-synchronized from the new content.
|
|
||||||
|
|
||||||
```no-highlight
|
|
||||||
curl -X PUT \
|
|
||||||
-H "Authorization: Bearer $TOKEN" \
|
|
||||||
-H "Accept: application/json; indent=4" \
|
|
||||||
-F "file=@/path/to/myscript.py" \
|
|
||||||
http://netbox/api/extras/scripts/upload/myscript.py/
|
|
||||||
```
|
|
||||||
|
|
||||||
## Running Custom Scripts
|
## Running Custom Scripts
|
||||||
|
|
||||||
!!! note
|
!!! note
|
||||||
|
|
@ -540,7 +515,7 @@ To run a script via the REST API, issue a POST request to the script's endpoint
|
||||||
|
|
||||||
```no-highlight
|
```no-highlight
|
||||||
curl -X POST \
|
curl -X POST \
|
||||||
-H "Authorization: Bearer $TOKEN" \
|
-H "Authorization: Token $TOKEN" \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
-H "Accept: application/json; indent=4" \
|
-H "Accept: application/json; indent=4" \
|
||||||
http://netbox/api/extras/scripts/example.MyReport/ \
|
http://netbox/api/extras/scripts/example.MyReport/ \
|
||||||
|
|
@ -549,9 +524,6 @@ http://netbox/api/extras/scripts/example.MyReport/ \
|
||||||
|
|
||||||
Optionally `schedule_at` can be passed in the form data with a datetime string to schedule a script at the specified date and time.
|
Optionally `schedule_at` can be passed in the form data with a datetime string to schedule a script at the specified date and time.
|
||||||
|
|
||||||
!!! note
|
|
||||||
Script input submitted through the REST API is validated against the variables declared by the script. Missing required variables or invalid values result in an HTTP 400 response, and undeclared keys are discarded rather than passed to `run()`. Existing API clients that relied on the previous pass-through behavior may need to update their requests. Scripts declaring a `FileVar` must be run via a `multipart/form-data` request, passing `data` as a JSON string alongside the uploaded file.
|
|
||||||
|
|
||||||
### Via the CLI
|
### Via the CLI
|
||||||
|
|
||||||
Scripts can be run on the CLI by invoking the management command:
|
Scripts can be run on the CLI by invoking the management command:
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,6 @@
|
||||||
!!! warning
|
!!! warning
|
||||||
Reports are deprecated beginning with NetBox v4.0, and their functionality has been merged with [custom scripts](./custom-scripts.md). While backward compatibility has been maintained, users are advised to convert legacy reports into custom scripts soon, as support for legacy reports will be removed in a future release.
|
Reports are deprecated beginning with NetBox v4.0, and their functionality has been merged with [custom scripts](./custom-scripts.md). While backward compatibility has been maintained, users are advised to convert legacy reports into custom scripts soon, as support for legacy reports will be removed in a future release.
|
||||||
|
|
||||||
Beginning with NetBox v4.7, NetBox's built-in custom scripts implementation is deprecated and is being replaced by a dedicated plugin. Converting a legacy report to a custom script remains the recommended first step. See the [custom scripts documentation](./custom-scripts.md) for details.
|
|
||||||
|
|
||||||
## Converting Reports to Scripts
|
## Converting Reports to Scripts
|
||||||
|
|
||||||
### Step 1: Update Class Definition
|
### Step 1: Update Class Definition
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,10 @@ A dictionary mapping of models to foreign keys with which cached counter fields
|
||||||
|
|
||||||
A dictionary mapping data backend types to their respective classes. These are used to interact with [remote data sources](../models/core/datasource.md).
|
A dictionary mapping data backend types to their respective classes. These are used to interact with [remote data sources](../models/core/datasource.md).
|
||||||
|
|
||||||
|
### `denormalized_fields`
|
||||||
|
|
||||||
|
Stores registration made using `netbox.denormalized.register()`. For each model, a list of related models and their field mappings is maintained to facilitate automatic updates.
|
||||||
|
|
||||||
### `filtersets`
|
### `filtersets`
|
||||||
|
|
||||||
A dictionary mapping each model (identified by its app and label) to its filterset class, if one has been registered for it. Filtersets are registered using the `@register_filterset` decorator.
|
A dictionary mapping each model (identified by its app and label) to its filterset class, if one has been registered for it. Filtersets are registered using the `@register_filterset` decorator.
|
||||||
|
|
|
||||||
|
|
@ -1,134 +0,0 @@
|
||||||
# Building the Package
|
|
||||||
|
|
||||||
NetBox package artifacts (a wheel and a source distribution) can be built and verified locally. Installing NetBox from the Python package is experimental in NetBox v4.7 and is not recommended for production use. This page is intended for maintainers and contributors working on the packaging itself. Routine development does not require building a package.
|
|
||||||
|
|
||||||
Published artifacts are always built by CI from a clean checkout (see `.github/workflows/release.yml`). A local build is useful for testing packaging changes before they are merged.
|
|
||||||
|
|
||||||
Release tags trigger the production PyPI publishing workflow. Before a release tag is pushed, confirm that the `pypi` GitHub Actions environment has required reviewers configured so the upload waits for approval after the package checks complete. Referencing the environment in the workflow does not create an approval gate by itself. See [Confirm Package Publishing Prerequisites](./release-checklist.md#confirm-package-publishing-prerequisites) and [Publish to PyPI](./release-checklist.md#publish-to-pypi) for the required repository checks and release procedure.
|
|
||||||
|
|
||||||
## Prerequisites
|
|
||||||
|
|
||||||
Install the minimum local build tooling (all three are also included in the `dev` optional dependency group):
|
|
||||||
|
|
||||||
```no-highlight
|
|
||||||
python -m pip install --upgrade build packaging twine
|
|
||||||
```
|
|
||||||
|
|
||||||
Building also requires a freshly rendered copy of the documentation site (see [Building](#building) below). The documentation toolchain, including `zensical`, `mkdocs`, `mkdocs-material`, `mkdocstrings`, and `mkdocstrings-python`, is pinned in `requirements.txt` rather than the `dev` group because it is also needed outside packaging, such as documentation previews and CI's `docs` job.
|
|
||||||
|
|
||||||
## Building
|
|
||||||
|
|
||||||
Render the documentation site at the repository root before building; both the wheel and the sdist bundle the rendered output, and the release workflow's `build` job renders in the same way:
|
|
||||||
|
|
||||||
```no-highlight
|
|
||||||
python -m pip install -r requirements.txt
|
|
||||||
zensical build -c -s
|
|
||||||
```
|
|
||||||
|
|
||||||
Always render with `-c` (clean cache) and `-s` (strict mode, abort on warnings) so a stale cache or a degraded build cannot slip into the artifacts. This writes `netbox/project-static/docs/` (gitignored). Building without a prior render fails because the rendered docs directory is a required Hatch force-include: Hatchling raises `FileNotFoundError: Forced include not found` for the missing directory. A render that exits successfully but produces a partial site is caught by `scripts/verify_wheel_contents.py`, which requires both the site root (`index.html`) and a model documentation page (`models/dcim/device/index.html`) in the wheel.
|
|
||||||
|
|
||||||
Build both the source distribution (sdist) and the wheel into `dist/`:
|
|
||||||
|
|
||||||
```no-highlight
|
|
||||||
python -m build
|
|
||||||
```
|
|
||||||
|
|
||||||
To build only the wheel (faster, and the form most useful for a quick local install test):
|
|
||||||
|
|
||||||
```no-highlight
|
|
||||||
python -m build --wheel
|
|
||||||
```
|
|
||||||
|
|
||||||
The package version and the wheel's runtime dependency metadata are both computed at build time by a Hatchling hook; see [Dynamic metadata](#dynamic-metadata) below.
|
|
||||||
|
|
||||||
## Clean-tree caveat
|
|
||||||
|
|
||||||
Always build release artifacts from a clean checkout. The Hatch configuration excludes `netbox/netbox/configuration*.py` and `netbox/netbox/ldap_config*.py`, then force-includes only the two tracked configuration templates, `configuration_example.py` and `configuration_testing.py`. The sdist additionally excludes the checkout-level `netbox/configuration.py` and `netbox/ldap_config.py` symlinks. CI verifies the complete contents of both distributions before anything is published.
|
|
||||||
|
|
||||||
These checks are defense in depth, not a license to build from a dirty tree: other untracked files under `netbox/` can still be picked up by a local build. CI builds from a clean checkout, so the published artifacts are unaffected. For a comparable local build, use a fresh `git clone` or a separate clean worktree rather than your day-to-day development tree.
|
|
||||||
|
|
||||||
## Verifying
|
|
||||||
|
|
||||||
Check the built artifacts for valid package metadata and README rendering:
|
|
||||||
|
|
||||||
```no-highlight
|
|
||||||
twine check dist/*
|
|
||||||
```
|
|
||||||
|
|
||||||
The wheel and sdist deliberately use Core Metadata 2.4, the lowest version required by NetBox's current project metadata. Both build targets pin this format as `core-metadata-version` in `pyproject.toml`, and CI verifies the emitted `METADATA` and `PKG-INFO` values against those pins (`verify_wheel_metadata.py` and `verify_sdist_contents.py`).
|
|
||||||
|
|
||||||
The release workflow's build job pins `twine` and `packaging` to the versions bundled by the pinned `pypa/gh-action-pypi-publish` revision (its `requirements/runtime.txt`), so the pre-publication check uses the same Core Metadata validator as the publisher. Hatchling remains lower-bounded rather than pinned. The explicit Core Metadata setting prevents changes to its default from changing the artifact format.
|
|
||||||
|
|
||||||
Review these settings together when updating the packaging toolchain. Keep the `twine` and `packaging` pins aligned with the publishing action, but change the Core Metadata version only when NetBox needs a newer format and the complete publishing path supports it.
|
|
||||||
|
|
||||||
Confirm the wheel's version, dependency metadata, and extras match `netbox/release.yaml`, the pinned `requirements.txt`, and the declared optional-dependency groups:
|
|
||||||
|
|
||||||
```no-highlight
|
|
||||||
python scripts/verify_wheel_metadata.py dist/*.whl
|
|
||||||
```
|
|
||||||
|
|
||||||
Confirm the artifacts ship only the two tracked configuration templates, and that the wheel carries the runtime-critical bundled data: `_data/release.yaml`, templates, translations, static assets, and the pre-rendered documentation site under `_data/docs/`. These are the same content checks CI runs before publishing:
|
|
||||||
|
|
||||||
```no-highlight
|
|
||||||
python scripts/verify_wheel_contents.py dist/*.whl
|
|
||||||
python scripts/verify_sdist_contents.py dist/*.tar.gz
|
|
||||||
```
|
|
||||||
|
|
||||||
Confirm `requirements.txt` is still consistent with the maintainer policy in `base_requirements.txt` (the same drift guard CI runs before publishing):
|
|
||||||
|
|
||||||
```no-highlight
|
|
||||||
python scripts/verify_dependencies.py
|
|
||||||
```
|
|
||||||
|
|
||||||
## Test-installing the wheel
|
|
||||||
|
|
||||||
Install the wheel into a throwaway virtual environment and run the system checks to confirm the package is importable and runnable:
|
|
||||||
|
|
||||||
```no-highlight
|
|
||||||
python -m venv /tmp/netbox-build-test
|
|
||||||
/tmp/netbox-build-test/bin/python -m pip install --upgrade pip
|
|
||||||
/tmp/netbox-build-test/bin/python -m pip install dist/*.whl
|
|
||||||
PYTHONPATH=$PWD/scripts \
|
|
||||||
NETBOX_CONFIGURATION=smoketest_configuration \
|
|
||||||
NETBOX_ROOT=/tmp/netbox-build-test-root \
|
|
||||||
NETBOX_SMOKETEST_BASE=/tmp/netbox-build-test-root \
|
|
||||||
/tmp/netbox-build-test/bin/netbox check
|
|
||||||
```
|
|
||||||
|
|
||||||
Without configuration, a wheel-installed NetBox looks for `$NETBOX_ROOT/conf/configuration.py` (default `/opt/netbox/conf/configuration.py`), which normally does not exist on a development workstation. The environment variables above point `netbox check` at the same minimal configuration module used by the release workflow's smoke-test job (`scripts/smoketest_configuration.py`); run the command from the repository root so `PYTHONPATH` can find it. `NETBOX_SMOKETEST_BASE` sets the writable scratch directory under which the module creates its media, reports, and scripts roots. `NETBOX_ROOT` sets the instance root, from which the fixed collected-static path `$NETBOX_ROOT/static` is derived. Any other importable configuration module works the same way via `NETBOX_CONFIGURATION` (and `PYTHONPATH`, if the configuration lives outside the package). To exercise the full post-install task sequence from the wheel, run `netbox upgrade --no-input` with the same environment against a throwaway database (the collected static files land under `$NETBOX_ROOT/static`); this is what the release workflow's smoke-test job does. The documentation ships pre-rendered in the wheel, so there is nothing to build on the instance; `--build-docs` remains a checkout-only convenience for rendering the documentation from its sources.
|
|
||||||
|
|
||||||
## Packaging architecture
|
|
||||||
|
|
||||||
This section is a developer-facing overview of how the package is assembled and how a pip-installed NetBox behaves at runtime. End-user installation steps live in [Install NetBox from the Python Package](../installation/3b-python-package.md).
|
|
||||||
|
|
||||||
### Dynamic metadata
|
|
||||||
|
|
||||||
`scripts/packaging/hatch_metadata.py` is a Hatchling metadata hook (wired in via `[tool.hatch.metadata.hooks.custom]`). It computes the package version from `netbox/release.yaml` and the runtime dependencies from the pinned `requirements.txt`, so the published wheel's `Requires-Dist` carries the exact versions NetBox is tested against. Both fields are declared `dynamic` in `pyproject.toml`; the optional-dependency extras stay static.
|
|
||||||
|
|
||||||
### sdist and the sdist-to-wheel guard
|
|
||||||
|
|
||||||
`python -m build` produces both an sdist and a wheel, with the wheel built from the sdist. The release workflow's `verify-sdist` job rebuilds a wheel from the candidate sdist and runs `scripts/verify_wheel_metadata.py` and `scripts/verify_wheel_contents.py` against it, so a missing build input, for example the metadata hook, `netbox/release.yaml`, or `requirements.txt`, cannot regress unnoticed. The rendered documentation site is one such build input: it reaches the sdist through its own force-include (`[tool.hatch.build.targets.sdist.force-include]`), so this guard also fails if that force-include is removed or broken.
|
|
||||||
|
|
||||||
### Wheel data layout
|
|
||||||
|
|
||||||
Source assets that are not Python modules are force-included with a `netbox/netbox/_data/` target path by `[tool.hatch.build.targets.wheel.force-include]`; because the wheel's `sources = ["netbox"]` setting strips one leading `netbox/`, they install under `netbox/_data/`: templates, translations, the compiled `project-static` bundles, `release.yaml`, the pre-rendered documentation site (rendered by `zensical build` into `netbox/project-static/docs/` before packaging; see [Building](#building) above), the bundled deployment examples (`contrib/`, seven files, unmodified), and the two tracked configuration templates.
|
|
||||||
|
|
||||||
The wheel bundles the rendered site itself, not the documentation sources. The documentation build is not run from the installed wheel, and there is nothing to build on the instance. In wheel mode, the default `DOCS_ROOT` and the STATICFILES `docs` prefix source both resolve to the same bundled `_data/docs` directory (see `resolve_install_paths()` in `netbox/netbox/settings_utils.py`), which `collectstatic` then picks up the same way it does for a checkout build. The sdist force-includes the same rendered site (`netbox/project-static/docs/`, kept alongside the markdown sources it was rendered from), so a wheel built from the sdist (the `verify-sdist` job, or `pip install <sdist>`) is identical in this respect.
|
|
||||||
|
|
||||||
At runtime `settings.py` detects the bundled `_data` directory and resolves the install mode, `BASE_DIR`, `NETBOX_ROOT`, and the documentation roots through `resolve_install_paths()` in `netbox/netbox/settings_utils.py`: a wheel install (`_data` present) keeps package data under `_data` and mutable instance files under `NETBOX_ROOT`; a source checkout (no `_data`) keeps the historical layout, where both roots are the project directory.
|
|
||||||
|
|
||||||
### Wheel-mode runtime
|
|
||||||
|
|
||||||
A pip-installed NetBox keeps mutable instance state out of the immutable, disposable virtual environment. `settings.py` resolves `NETBOX_ROOT` (default `/opt/netbox`, overridable via the environment) as the instance root, defaults the writable paths (`MEDIA_ROOT`, `REPORTS_ROOT`, `SCRIPTS_ROOT`) beneath it, and fixes `STATIC_ROOT` to `$NETBOX_ROOT/static`; `STATIC_ROOT` is intentionally not a `configuration.py` parameter, so the collected static path cannot drift from the instance layout the bundled deployment examples expect. In a checkout `NETBOX_ROOT` equals `BASE_DIR`, so archive and Git installs are unaffected.
|
|
||||||
|
|
||||||
Configuration loading is handled by `load_configuration()` in `netbox/netbox/settings_utils.py`. An explicit `NETBOX_CONFIGURATION` module always wins; otherwise, in wheel mode it prefers `NETBOX_ROOT/conf/configuration.py`, loading it by file path, and falls back to a legacy `NETBOX_ROOT/netbox/netbox/configuration.py` with a migration warning. The configuration directory is added to `sys.path` only while the configuration file executes, so sibling imports can resolve; `NETBOX_ROOT` itself is never added, which avoids a stale source tree shadowing the installed package. A checkout keeps importing `netbox.configuration`. For LDAP deployments, `settings.py` exposes the active configuration file's directory as the `CONFIGURATION_DIR` setting, and `load_ldap_config()` loads `ldap_config.py` from that same directory by default. This keeps the active LDAP configuration beside the active NetBox configuration, regardless of install method. One compatibility exception remains: in checkout mode only, when no sibling file exists, the historical `netbox/netbox/ldap_config.py` module is imported with a `RuntimeWarning`, so existing source installs that use a custom `NETBOX_CONFIGURATION` keep working.
|
|
||||||
|
|
||||||
### Console script
|
|
||||||
|
|
||||||
`pyproject.toml` registers a single entry point, `netbox` (`netbox.cli:main`). The wrapper resolves a few commands itself before importing Django, so they work without a configuration present:
|
|
||||||
|
|
||||||
* `netbox version` / `netbox --version` print the installed package version.
|
|
||||||
* `netbox setup` creates the local configuration files for the instance: `conf/__init__.py`, `conf/configuration.py` copied verbatim from the bundled `configuration_example.py` template, and an empty `local_requirements.txt`. It also copies the bundled deployment examples (gunicorn, systemd units, nginx, apache, uwsgi, `netbox.env`) unmodified into `<target>/contrib/`. The examples are copied as-is, and existing files are never overwritten; adapting and installing the examples (paths, systemd, the web server) remains the administrator's responsibility.
|
|
||||||
* `netbox secret-key` prints a new 50-character `SECRET_KEY` value.
|
|
||||||
|
|
||||||
These names are reserved by the wrapper. Every other command falls through to the Django management commands (`netbox upgrade`, `netbox check`, and so on), which require a valid configuration.
|
|
||||||
|
|
@ -97,23 +97,14 @@ Notify the [`netbox-docker`](https://github.com/netbox-community/netbox-docker)
|
||||||
|
|
||||||
### Update Python Dependencies
|
### Update Python Dependencies
|
||||||
|
|
||||||
Before each release, update each of NetBox's Python dependencies to its most recent stable version. Loose runtime constraints (and per-package descriptions) live in `base_requirements.txt`; `requirements.txt` is the pinned, top-level dependency file consumed by the release archive, the git install flow (`upgrade.sh`), and the published wheel's dependency metadata. Optional dependency groups (for example `ldap`, `saml2`) are declared in `pyproject.toml`.
|
Before each release, update each of NetBox's Python dependencies to its most recent stable version. These are defined in `requirements.txt`, which is updated from `base_requirements.txt` using `pip`. To do this:
|
||||||
|
|
||||||
To update the pinned requirements:
|
1. Upgrade the installed version of all required packages in your environment (`pip install -U -r base_requirements.txt`).
|
||||||
|
2. Run all tests and check that the UI and API function as expected.
|
||||||
|
3. Review each requirement's release notes for any breaking or otherwise noteworthy changes.
|
||||||
|
4. Update the package versions in `requirements.txt` as appropriate.
|
||||||
|
|
||||||
1. Review each constraint in `base_requirements.txt`.
|
In cases where upgrading a dependency to its most recent release is breaking, it should be constrained to its current minor version in `base_requirements.txt` with an explanatory comment and revisited for the next major NetBox release (see the [Address Constrained Dependencies](#address-constrained-dependencies) section above).
|
||||||
2. Upgrade the installed version of all required packages in your environment (`pip install -U -r base_requirements.txt`).
|
|
||||||
3. Run all tests and check that the UI and API function as expected.
|
|
||||||
4. Review each requirement's release notes for any breaking or otherwise noteworthy changes.
|
|
||||||
5. If upgrading a dependency is breaking, constrain it in `base_requirements.txt` with an explanatory comment and revisit it for the next major NetBox release (see the [Address Constrained Dependencies](#address-constrained-dependencies) section above).
|
|
||||||
6. Update the pinned versions in `requirements.txt` to the versions you just tested. Keep `requirements.txt` in the existing bare `package==version` format (one top-level package per line, the same package set as `base_requirements.txt`).
|
|
||||||
7. Verify there is no drift between the policy file and the pins:
|
|
||||||
|
|
||||||
```no-highlight
|
|
||||||
python3 scripts/verify_dependencies.py
|
|
||||||
```
|
|
||||||
|
|
||||||
The published wheel's `Requires-Dist` is generated from `requirements.txt` at build time, so the package installs the same tested pins as the archive and git flows.
|
|
||||||
|
|
||||||
### Update UI Dependencies
|
### Update UI Dependencies
|
||||||
|
|
||||||
|
|
@ -152,7 +143,8 @@ Then, compile these portable (`.po`) files for use in the application:
|
||||||
### Update Version and Changelog
|
### Update Version and Changelog
|
||||||
|
|
||||||
* Update the version number and published date in `netbox/release.yaml`. Add or remove the designation (e.g. `beta1`) if applicable.
|
* Update the version number and published date in `netbox/release.yaml`. Add or remove the designation (e.g. `beta1`) if applicable.
|
||||||
* No manual `pyproject.toml` version edit is needed: the package version is derived automatically from `release.yaml` (`version` plus any `designation`) by the build backend.
|
* Copy the version number from `release.yaml` to `pyproject.toml` in the project root.
|
||||||
|
* Update the example version numbers in the feature request, bug report, and performance templates under `.github/ISSUE_TEMPLATES/`.
|
||||||
* Add a section for this release at the top of the changelog page for the minor version (e.g. `docs/release-notes/version-4.2.md`) listing all relevant changes made in this release.
|
* Add a section for this release at the top of the changelog page for the minor version (e.g. `docs/release-notes/version-4.2.md`) listing all relevant changes made in this release.
|
||||||
|
|
||||||
!!! tip
|
!!! tip
|
||||||
|
|
@ -170,9 +162,6 @@ This will automatically update the schema file at `contrib/generated_schema.json
|
||||||
|
|
||||||
### Update the OpenAPI Schema
|
### Update the OpenAPI Schema
|
||||||
|
|
||||||
!!! warning "Disable all plugins first"
|
|
||||||
Before generating the OpenAPI schema, disable any installed plugins. This will prevent their schemas from being pulled into the generated snapshot.
|
|
||||||
|
|
||||||
Update the static OpenAPI schema definition at `contrib/openapi.json` with the management command below. If the schema file is up-to-date, only the NetBox version will be changed.
|
Update the static OpenAPI schema definition at `contrib/openapi.json` with the management command below. If the schema file is up-to-date, only the NetBox version will be changed.
|
||||||
|
|
||||||
```nohighlight
|
```nohighlight
|
||||||
|
|
@ -183,7 +172,7 @@ Update the static OpenAPI schema definition at `contrib/openapi.json` with the m
|
||||||
|
|
||||||
Keep development tooling versions consistent across the project. If you upgrade a dev-only dependency, update all places where it’s pinned so local tooling and CI run the same versions.
|
Keep development tooling versions consistent across the project. If you upgrade a dev-only dependency, update all places where it’s pinned so local tooling and CI run the same versions.
|
||||||
|
|
||||||
* Ruff
|
* Ruff:
|
||||||
* `.pre-commit-config.yaml`
|
* `.pre-commit-config.yaml`
|
||||||
* `.github/workflows/ci.yml`
|
* `.github/workflows/ci.yml`
|
||||||
|
|
||||||
|
|
@ -196,16 +185,6 @@ Once CI has completed and a colleague has reviewed the PR, merge it. This effect
|
||||||
!!! warning
|
!!! warning
|
||||||
To ensure a streamlined review process, the pull request for a release **must** be limited to the changes outlined in this document. A release PR must never include functional changes to the application: Any unrelated "cleanup" needs to be captured in a separate PR prior to the release being shipped.
|
To ensure a streamlined review process, the pull request for a release **must** be limited to the changes outlined in this document. A release PR must never include functional changes to the application: Any unrelated "cleanup" needs to be captured in a separate PR prior to the release being shipped.
|
||||||
|
|
||||||
### Confirm Package Publishing Prerequisites
|
|
||||||
|
|
||||||
Complete these checks before creating the release tag.
|
|
||||||
|
|
||||||
Confirm that the existing PyPI trusted publisher still matches this repository, `.github/workflows/release.yml`, and the `pypi` environment name. If a Test PyPI rehearsal is planned, confirm the corresponding Test PyPI trusted publisher and `testpypi` environment as well. The trusted publisher's environment name must match the publish job's `environment.name`, otherwise the index rejects the upload before any file is transferred.
|
|
||||||
|
|
||||||
Confirm that the `pypi` GitHub Actions environment has required reviewers configured so the production upload waits for approval after the package checks complete. Enable **Prevent self-review**, restrict deployments to `v*` tags, and leave administrator bypass disabled unless the maintainers deliberately require it. Referencing an environment from the workflow does not configure these protection rules; if the environment does not exist, GitHub creates it without an approval gate. The `testpypi` environment does not need an approval gate because a rehearsal run is dispatched deliberately.
|
|
||||||
|
|
||||||
The published package version is derived from `netbox/release.yaml` (the `version` field plus any `designation`, e.g. `beta1` becomes `4.7.0b1`), not from the git tag. Confirm that the intended tag and `netbox/release.yaml` agree before creating the release. The publishing workflow verifies the match again against the built wheel.
|
|
||||||
|
|
||||||
### Create a New Release
|
### Create a New Release
|
||||||
|
|
||||||
Create a [new release](https://github.com/netbox-community/netbox/releases/new) on GitHub with the following parameters.
|
Create a [new release](https://github.com/netbox-community/netbox/releases/new) on GitHub with the following parameters.
|
||||||
|
|
@ -215,56 +194,4 @@ Create a [new release](https://github.com/netbox-community/netbox/releases/new)
|
||||||
* **Title:** Version and date (e.g. `v4.2.1 - 2025-01-17`)
|
* **Title:** Version and date (e.g. `v4.2.1 - 2025-01-17`)
|
||||||
* **Description:** Copy from the pull request body, then promote the `###` headers to `##` ones
|
* **Description:** Copy from the pull request body, then promote the `###` headers to `##` ones
|
||||||
|
|
||||||
Once created, the release will become available for users to install from GitHub.
|
Once created, the release will become available for users to install.
|
||||||
|
|
||||||
### Publish to PyPI
|
|
||||||
|
|
||||||
Creating the GitHub release pushes the new tag and starts the Python package publishing workflow. With the prerequisites above in place, the workflow builds and verifies the wheel and source distribution, then holds the production upload until the `pypi` deployment is approved. Approving the deployment publishes the verified artifacts to **PyPI**. Installing NetBox from the Python package is experimental in NetBox v4.7 and is not recommended for production use.
|
|
||||||
|
|
||||||
A manual `workflow_dispatch` run from a `v*` release tag publishes to **Test PyPI** instead. This remains available as an optional rehearsal after packaging or publishing changes, but it is not required for every production release. Dispatching from a branch runs the build and verification jobs as a dry run without publishing anywhere.
|
|
||||||
|
|
||||||
Dispatch a rehearsal from the release tag with GitHub CLI:
|
|
||||||
|
|
||||||
```no-highlight
|
|
||||||
gh workflow run release.yml --ref vX.Y.Z
|
|
||||||
```
|
|
||||||
|
|
||||||
When a Test PyPI rehearsal is useful for a release, keep the production deployment awaiting approval while you dispatch the workflow from the same tag and validate the rehearsal. The rehearsal is a separate workflow run and rebuilds the distributions, so it validates the packaging and publishing path rather than the exact files waiting for production. Approve the production deployment after the rehearsal completes.
|
|
||||||
|
|
||||||
Test PyPI enforces the same filename immutability. Once it has accepted either distribution generated for a release tag, dispatching that tag again is expected to fail because the workflow rebuilds the same wheel and source distribution filenames. A further rehearsal requires a new package version and matching tag.
|
|
||||||
|
|
||||||
Official pre-release tags, including beta and release-candidate versions, are published to PyPI as well. This is intentional. Pip does not select pre-release versions by default unless the user explicitly requests one or no compatible stable release is available.
|
|
||||||
|
|
||||||
After a publish run completes:
|
|
||||||
|
|
||||||
* Verify that the build, CLI smoke-test (`cli-smoke-test`), smoke-test, dependency-verification (`verify-dependencies`), and sdist-verification (`verify-sdist`) jobs succeeded. The dependency-verification job fails the release if `requirements.txt` has drifted from `base_requirements.txt` or if the built wheel's `Requires-Dist` does not match `requirements.txt`; the sdist-verification job fails it if the sdist ships unexpected configuration files or cannot rebuild a valid wheel.
|
|
||||||
* Verify that the publish job used the expected trusted-publishing environment: `pypi` for a production release or `testpypi` for a rehearsal.
|
|
||||||
* Confirm that the new version is visible on the corresponding package index.
|
|
||||||
* Test the published wheel using the [wheel smoke-test procedure](./building-the-package.md#test-installing-the-wheel). For a production release, replace the local wheel installation command in that procedure with:
|
|
||||||
|
|
||||||
```no-highlight
|
|
||||||
/tmp/netbox-build-test/bin/python -m pip install "netbox==<version>"
|
|
||||||
```
|
|
||||||
|
|
||||||
For a Test PyPI rehearsal, install NetBox's pinned runtime dependencies from PyPI first and then install the candidate without resolving dependencies from the test index:
|
|
||||||
|
|
||||||
```no-highlight
|
|
||||||
/tmp/netbox-build-test/bin/python -m pip install -r requirements.txt
|
|
||||||
/tmp/netbox-build-test/bin/python -m pip install \
|
|
||||||
--no-deps \
|
|
||||||
--index-url https://test.pypi.org/simple/ \
|
|
||||||
"netbox==<version>"
|
|
||||||
```
|
|
||||||
|
|
||||||
Run `netbox check` with the configuration and environment variables shown in the linked procedure.
|
|
||||||
|
|
||||||
!!! warning "Production PyPI uploads are final"
|
|
||||||
Distribution files uploaded to PyPI cannot be replaced. A release may be yanked, and a release or an individual file may be deleted, but an uploaded filename can never be reused. Correcting an accepted distribution file requires publishing a new NetBox version.
|
|
||||||
|
|
||||||
If the publish job fails, check PyPI and the job log to determine whether any distribution file was accepted before deciding how to recover.
|
|
||||||
|
|
||||||
If no file was accepted and the cause can be corrected without changing the built distributions, correct it and re-run only the failed `publish-pypi` job. That reuses the package artifacts already built and verified in the original workflow run. Do not use **Re-run all jobs**, because it rebuilds the distributions.
|
|
||||||
|
|
||||||
If correcting the failure requires changing package contents or metadata, prepare a new NetBox version and release tag instead.
|
|
||||||
|
|
||||||
If PyPI accepted either distribution file, do not retry the publish job. Production publishing fails on duplicate filenames by design, so the retry fails when it reaches the already accepted file. Yank the incomplete release, record the accepted filenames and hashes, and publish a new NetBox version rather than combining files from separate builds.
|
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,7 @@ NetBox provides a REST API endpoint specifically for rendering the default confi
|
||||||
|
|
||||||
```no-highlight
|
```no-highlight
|
||||||
curl -X POST \
|
curl -X POST \
|
||||||
-H "Authorization: Bearer $TOKEN" \
|
-H "Authorization: Token $TOKEN" \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
-H "Accept: application/json; indent=4" \
|
-H "Accept: application/json; indent=4" \
|
||||||
http://netbox:8000/api/dcim/devices/123/render-config/ \
|
http://netbox:8000/api/dcim/devices/123/render-config/ \
|
||||||
|
|
@ -81,7 +81,7 @@ To render a specific config template against a device's context data - rather th
|
||||||
|
|
||||||
```no-highlight
|
```no-highlight
|
||||||
curl -X POST \
|
curl -X POST \
|
||||||
-H "Authorization: Bearer $TOKEN" \
|
-H "Authorization: Token $TOKEN" \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
-H "Accept: application/json; indent=4" \
|
-H "Accept: application/json; indent=4" \
|
||||||
http://netbox:8000/api/dcim/devices/123/render-config/ \
|
http://netbox:8000/api/dcim/devices/123/render-config/ \
|
||||||
|
|
@ -114,7 +114,7 @@ NetBox config templates can also be rendered without being tied to any specific
|
||||||
|
|
||||||
```no-highlight
|
```no-highlight
|
||||||
curl -X POST \
|
curl -X POST \
|
||||||
-H "Authorization: Bearer $TOKEN" \
|
-H "Authorization: Token $TOKEN" \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
-H "Accept: application/json; indent=4" \
|
-H "Accept: application/json; indent=4" \
|
||||||
http://netbox:8000/api/extras/config-templates/123/render/ \
|
http://netbox:8000/api/extras/config-templates/123/render/ \
|
||||||
|
|
|
||||||
|
|
@ -90,14 +90,3 @@ Devices and virtual machines may also have a local context data defined. This lo
|
||||||
A [config context profile](../models/extras/configcontextprofile.md) provides an organizational grouping for related config contexts and may optionally enforce a [JSON schema](https://json-schema.org/) describing the shape of their data. When a profile is assigned to a config context, NetBox validates the context's data against the profile's schema on save and rejects any context that fails validation. This makes it possible to constrain which keys may appear in a context, require certain keys to be present, or limit values to a defined enumeration — guarding against typos and drift as contexts proliferate.
|
A [config context profile](../models/extras/configcontextprofile.md) provides an organizational grouping for related config contexts and may optionally enforce a [JSON schema](https://json-schema.org/) describing the shape of their data. When a profile is assigned to a config context, NetBox validates the context's data against the profile's schema on save and rejects any context that fails validation. This makes it possible to constrain which keys may appear in a context, require certain keys to be present, or limit values to a defined enumeration — guarding against typos and drift as contexts proliferate.
|
||||||
|
|
||||||
A profile's schema may be authored directly in NetBox or populated from an external [data source](../models/core/datasource.md), enabling teams to maintain schemas alongside the code or configurations that consume them.
|
A profile's schema may be authored directly in NetBox or populated from an external [data source](../models/core/datasource.md), enabling teams to maintain schemas alongside the code or configurations that consume them.
|
||||||
|
|
||||||
## Pre-rendered Caching
|
|
||||||
|
|
||||||
!!! info "This feature was introduced in NetBox v4.7."
|
|
||||||
|
|
||||||
NetBox pre-renders each device's and virtual machine's merged context data and stores it on the object itself, so most reads can return the result without recomputing the full set of applicable contexts. The cache is initially populated during upgrade (the upgrade script runs the `rebuild_config_context_cache` management command) and is thereafter kept current automatically: whenever an upstream change is detected — a config context being created, modified, or deleted; a device/VM's scope-relevant attribute changing (site, role, tenant, tags, cluster, etc.); or a related object being re-routed in a way that changes which contexts apply — NetBox marks the affected caches invalid and enqueues a non-blocking [background job](./background-jobs.md) to repopulate them.
|
|
||||||
|
|
||||||
During the brief window between invalidation and re-render, requests for the affected object's config context fall back to the original on-demand rendering path, so the data returned is always correct — never stale — but may be slightly slower during that window. Once the background job completes, reads are served from the cache.
|
|
||||||
|
|
||||||
!!! note
|
|
||||||
The pre-rendered cache supersedes the previous `?exclude=config_context` REST API query parameter. Config context data is now always returned for devices and virtual machines, and the parameter is silently ignored.
|
|
||||||
|
|
|
||||||
|
|
@ -1,43 +0,0 @@
|
||||||
# Cooling
|
|
||||||
|
|
||||||
!!! info "This feature was introduced in NetBox v4.7."
|
|
||||||
|
|
||||||
As part of its DCIM feature set, NetBox supports modeling data center cooling infrastructure, from facility plant down to the coolant connections on individual devices. This is used to document liquid- and hybrid-cooled environments (chillers, cooling distribution units, manifolds, rear-door heat exchangers, and cold-plate servers) as a source of truth.
|
|
||||||
|
|
||||||
## Model Overview
|
|
||||||
|
|
||||||
Cooling infrastructure is modeled as a hierarchy running from facility plant down to individual devices:
|
|
||||||
|
|
||||||
**cooling source → cooling feed → device cooling intake / cooling outflow**
|
|
||||||
|
|
||||||
A few properties of the model are worth noting up front:
|
|
||||||
|
|
||||||
- **Connections are direct references, not cables.** Coolant hoses are not modeled as structured cabling; instead, an intake references the outflow that supplies it directly. Tracing a loop is a walk along these references.
|
|
||||||
- **A single feed represents the entire loop.** A cooling feed covers both the supply (cold) and return (warm) paths of a loop, rather than modeling each direction as a separate object.
|
|
||||||
- **Intakes and outflows both sit on the supply path.** Both device components describe the cold, coolant-distribution side of the loop: an intake receives coolant and an outflow passes it onward to downstream equipment. The warm return path is not modeled per-component — it is captured by the feed loop.
|
|
||||||
|
|
||||||
## Cooling Sources
|
|
||||||
|
|
||||||
A [cooling source](../models/dcim/coolingsource.md) is the furthest upstream cooling element modeled in NetBox, representing a chiller, cooling tower, dry cooler, or CRAC/CRAH unit. Each source is associated with a site, and may optionally be associated with a particular location within that site. A cooling source is not a device; it represents external facility plant, and records the coolant (fluid type) and total rated cooling capacity for the loops it originates.
|
|
||||||
|
|
||||||
## Cooling Feeds
|
|
||||||
|
|
||||||
A [cooling feed](../models/dcim/coolingfeed.md) represents a coolant loop running between a cooling source and a particular rack. Each feed records an operational status, a rated cooling capacity, and a rated (design) flow rate.
|
|
||||||
|
|
||||||
## Device Components
|
|
||||||
|
|
||||||
Devices participate in cooling through two component types, instantiated from templates defined on the device type:
|
|
||||||
|
|
||||||
- A [cooling intake](../models/dcim/coolingintake.md) is a coolant intake on a device, such as a server cold-plate inlet or a CDU facility intake. It records the connector type, diameter, and rated maximum flow, and optionally references the upstream [cooling outflow](../models/dcim/coolingoutflow.md) that supplies it.
|
|
||||||
- A [cooling outflow](../models/dcim/coolingoutflow.md) is a coolant supply point on a device, such as a CDU or manifold outlet. It optionally references a parent cooling intake on the same device — the device takes coolant in through its intake and passes it back out through its outflow.
|
|
||||||
|
|
||||||
!!! tip "In-rack cooling equipment is modeled as a device"
|
|
||||||
Coolant distribution units (CDUs), manifolds, and rear-door heat exchangers (RDHx) are modeled as ordinary (typically zero-U) [devices](../models/dcim/device.md) installed in the rack — exactly as a PDU is modeled as a device with power ports and outlets. The device's make and model come from its [device type](../models/dcim/devicetype.md), and its cooling connections are represented by cooling intake and outflow components. There is no dedicated CDU or RDHx model.
|
|
||||||
|
|
||||||
## Racks and Devices
|
|
||||||
|
|
||||||
Racks and devices carry lightweight cooling attributes independent of the feed/component topology:
|
|
||||||
|
|
||||||
- A [rack](../models/dcim/rack.md) records a **cooling capability** (air-only, hybrid, or liquid-only) and a **cooling capacity** in kilowatts, typically inherited from its rack type.
|
|
||||||
- A [device](../models/dcim/device.md) records a **cooling method** (air, liquid, hybrid, or immersion), inherited from its device type and overridable per device.
|
|
||||||
|
|
||||||
|
|
@ -79,9 +79,6 @@ To learn more about this feature, check out the [documentation for reports](../c
|
||||||
|
|
||||||
## Custom Scripts
|
## Custom Scripts
|
||||||
|
|
||||||
!!! warning "Deprecation Warning"
|
|
||||||
Beginning in NetBox v4.7, the custom scripts functionality built into core NetBox has been deprecated in favor of a dedicated plugin, and is scheduled for removal in NetBox v5.0. See the [custom scripts documentation](../customization/custom-scripts.md) for details.
|
|
||||||
|
|
||||||
Custom scripts are similar to reports, but more powerful. A custom script can prompt the user for input via a form (or API data), and is built to do much more than just reporting. Custom scripts are generally used to automate tasks, such as the population of new objects in NetBox, or exchanging data with external systems. As with reports, they can be run via the UI, REST API, or CLI, and be scheduled to execute at a future time.
|
Custom scripts are similar to reports, but more powerful. A custom script can prompt the user for input via a form (or API data), and is built to do much more than just reporting. Custom scripts are generally used to automate tasks, such as the population of new objects in NetBox, or exchanging data with external systems. As with reports, they can be run via the UI, REST API, or CLI, and be scheduled to execute at a future time.
|
||||||
|
|
||||||
The complete Python environment is available to a custom script, including all of NetBox's internal mechanisms: There are no artificial restrictions on what a script can do. As such, custom scripting is considered an advanced feature and requires sufficient familiarity with Python and NetBox's data model.
|
The complete Python environment is available to a custom script, including all of NetBox's internal mechanisms: There are no artificial restrictions on what a script can do. As such, custom scripting is considered an advanced feature and requires sufficient familiarity with Python and NetBox's data model.
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
# Resource Ownership
|
# Resource Ownership
|
||||||
|
|
||||||
|
!!! info "This feature was introduced in NetBox v4.5."
|
||||||
|
|
||||||
Most objects in NetBox can be assigned an owner. An owner is a set of users and/or groups who are responsible for the administration of associated objects. For example, you might designate the operations team at a site as the owner for all prefixes and VLANs deployed at that site. The users and groups assigned to an owner are referred to as its members.
|
Most objects in NetBox can be assigned an owner. An owner is a set of users and/or groups who are responsible for the administration of associated objects. For example, you might designate the operations team at a site as the owner for all prefixes and VLANs deployed at that site. The users and groups assigned to an owner are referred to as its members.
|
||||||
|
|
||||||
!!! note
|
!!! note
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
## Global Search
|
## Global Search
|
||||||
|
|
||||||
NetBox includes a powerful global search engine, providing a single convenient interface to search across its complex data model. Relevant fields on each model are indexed according to their precedence, so that the most relevant results are returned first. When objects are created, modified, or deleted, the search index is updated by a background task shortly afterward. As a result, a newly created or changed object may not appear in search results for a brief period. (When no background worker is running, the index is updated immediately as part of the request.)
|
NetBox includes a powerful global search engine, providing a single convenient interface to search across its complex data model. Relevant fields on each model are indexed according to their precedence, so that the most relevant results are returned first. When objects are created or modified, the search index is updated immediately, ensuring real-time accuracy.
|
||||||
|
|
||||||
When entering a search query, the user can choose a specific lookup type: exact match, partial match, etc. When a partial match is found, the matching portion of the applicable field value is included with each result so that the user can easily determine its relevance.
|
When entering a search query, the user can choose a specific lookup type: exact match, partial match, etc. When a partial match is found, the matching portion of the applicable field value is included with each result so that the user can easily determine its relevance.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -26,9 +26,7 @@ When viewing the CSV import form for an object type, you'll notice that the head
|
||||||
|
|
||||||
<!-- TODO: Screenshot -->
|
<!-- TODO: Screenshot -->
|
||||||
|
|
||||||
If an "id" field is added the data will be used to update existing records instead of importing new objects. When updating, only the columns present in the data are applied; all others are left unchanged. Note that some columns are interdependent: for example, updating a cable's terminations requires that the columns identifying their type and parent object be included as well.
|
If an "id" field is added the data will be used to update existing records instead of importing new objects.
|
||||||
|
|
||||||
Some columns accept multiple values, separated by commas. Because the comma also serves as the CSV field delimiter, such a value must be enclosed in double quotes, e.g. `"tag1,tag2,tag3"`. (When importing JSON- or YAML-formatted data, these columns accept a native list instead.) An object whose name itself contains a comma cannot be referenced by a multi-value column, as there is no way to distinguish it from a separator.
|
|
||||||
|
|
||||||
Note that some models (namely device types and module types) do not support CSV import. Instead, they accept YAML-formatted data to facilitate the import of both the parent object as well as child components.
|
Note that some models (namely device types and module types) do not support CSV import. Instead, they accept YAML-formatted data to facilitate the import of both the parent object as well as child components.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,11 @@
|
||||||
|
|
||||||
This section entails the installation and configuration of a local PostgreSQL database. If you already have a PostgreSQL database service in place, skip to [the next section](2-redis.md).
|
This section entails the installation and configuration of a local PostgreSQL database. If you already have a PostgreSQL database service in place, skip to [the next section](2-redis.md).
|
||||||
|
|
||||||
!!! warning "PostgreSQL 15 or later required"
|
!!! warning "PostgreSQL 14 or later required"
|
||||||
NetBox requires PostgreSQL 15 or later. Please note that MySQL and other relational databases are **not** supported.
|
NetBox requires PostgreSQL 14 or later. Please note that MySQL and other relational databases are **not** supported.
|
||||||
|
|
||||||
|
!!! warning "PostgreSQL 14 deprecation notice"
|
||||||
|
Support for PostgreSQL 14 is deprecated as of NetBox v4.6 and will be removed in NetBox v4.7. Please plan to upgrade to PostgreSQL 15 or later.
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
|
|
@ -12,7 +15,7 @@ sudo apt update
|
||||||
sudo apt install -y postgresql
|
sudo apt install -y postgresql
|
||||||
```
|
```
|
||||||
|
|
||||||
Before continuing, verify that you have installed PostgreSQL 15 or later:
|
Before continuing, verify that you have installed PostgreSQL 14 or later:
|
||||||
|
|
||||||
```no-highlight
|
```no-highlight
|
||||||
psql -V
|
psql -V
|
||||||
|
|
@ -32,6 +35,7 @@ Within the shell, enter the following commands to create the database and user (
|
||||||
CREATE DATABASE netbox;
|
CREATE DATABASE netbox;
|
||||||
CREATE USER netbox WITH PASSWORD 'J5brHrAXFLQSif0K';
|
CREATE USER netbox WITH PASSWORD 'J5brHrAXFLQSif0K';
|
||||||
ALTER DATABASE netbox OWNER TO netbox;
|
ALTER DATABASE netbox OWNER TO netbox;
|
||||||
|
-- the next two commands are needed on PostgreSQL 15 and later
|
||||||
\connect netbox;
|
\connect netbox;
|
||||||
GRANT CREATE ON SCHEMA public TO netbox;
|
GRANT CREATE ON SCHEMA public TO netbox;
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -10,18 +10,15 @@ sudo apt install -y redis-server
|
||||||
|
|
||||||
Before continuing, verify that your installed version of Redis is at least v6.0:
|
Before continuing, verify that your installed version of Redis is at least v6.0:
|
||||||
|
|
||||||
|
!!! warning "Redis v5.x is deprecated"
|
||||||
|
Support for Redis versions older than 6.0 is deprecated and will be removed in NetBox v4.7.
|
||||||
|
|
||||||
```no-highlight
|
```no-highlight
|
||||||
redis-server -v
|
redis-server -v
|
||||||
```
|
```
|
||||||
|
|
||||||
You may wish to modify the Redis configuration at `/etc/redis.conf` or `/etc/redis/redis.conf`, however in most cases the default configuration is sufficient.
|
You may wish to modify the Redis configuration at `/etc/redis.conf` or `/etc/redis/redis.conf`, however in most cases the default configuration is sufficient.
|
||||||
|
|
||||||
!!! danger "Restrict access to Redis"
|
|
||||||
NetBox's background workers execute jobs read from Redis, so anyone able to write to the `tasks` database can run
|
|
||||||
arbitrary code on a worker. Treat Redis as trusted infrastructure: keep it bound to `localhost` (the default) or a
|
|
||||||
private network, and enable authentication if it is reachable by any other host. See
|
|
||||||
[Redis configuration](../configuration/required-parameters.md#redis) for details.
|
|
||||||
|
|
||||||
## Verify Service Status
|
## Verify Service Status
|
||||||
|
|
||||||
Use the `redis-cli` utility to ensure the Redis service is functional:
|
Use the `redis-cli` utility to ensure the Redis service is functional:
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
# Install NetBox from a Release Archive or Git
|
# NetBox Installation
|
||||||
|
|
||||||
This page covers the established release archive and Git installation methods. To install NetBox from the experimental Python package instead, follow the [separate package installation guide](3b-python-package.md).
|
This section of the documentation discusses installing and configuring the NetBox application itself.
|
||||||
|
|
||||||
## Install System Packages
|
## Install System Packages
|
||||||
|
|
||||||
|
|
@ -99,7 +99,7 @@ cd /opt/netbox/netbox/netbox/
|
||||||
sudo cp configuration_example.py configuration.py
|
sudo cp configuration_example.py configuration.py
|
||||||
```
|
```
|
||||||
|
|
||||||
Open `configuration.py` with your preferred editor to begin configuring NetBox. NetBox offers [many configuration parameters](../configuration/index.md), but only the following five are required for new installations:
|
Open `configuration.py` with your preferred editor to begin configuring NetBox. NetBox offers [many configuration parameters](../configuration/index.md), but only the following four are required for new installations:
|
||||||
|
|
||||||
* `ALLOWED_HOSTS`
|
* `ALLOWED_HOSTS`
|
||||||
* `API_TOKEN_PEPPERS`
|
* `API_TOKEN_PEPPERS`
|
||||||
|
|
|
||||||
|
|
@ -1,361 +0,0 @@
|
||||||
# Install NetBox from the Python Package (Experimental)
|
|
||||||
|
|
||||||
!!! warning "Experimental in NetBox v4.7"
|
|
||||||
Installing NetBox from the Python package is experimental in NetBox v4.7 and is **not recommended for production use**. Use this workflow to evaluate the packaged installation, test upgrades and rollback procedures, and provide feedback.
|
|
||||||
|
|
||||||
The established [release archive and Git installation methods](3-netbox.md) remain supported and are not replaced by this workflow.
|
|
||||||
|
|
||||||
The Python package installs the NetBox application and its Python dependencies into a virtual environment using `pip`. Configuration, uploaded media, custom scripts and reports, collected static files, and deployment configuration remain outside the installed package.
|
|
||||||
|
|
||||||
This installation method does **not** configure PostgreSQL, Redis, a WSGI server, an HTTP server, or system services. These remain administrator-managed deployment tasks, just as they are for an archive or Git installation.
|
|
||||||
|
|
||||||
## When to Use This Installation Method
|
|
||||||
|
|
||||||
Use the Python package for a new test or evaluation deployment when you want `pip` to manage the NetBox application code in a dedicated virtual environment. While this workflow remains experimental, use a [release archive or Git checkout](3-netbox.md) for production deployments.
|
|
||||||
|
|
||||||
A package installation is also available as a migration target for an existing deployment, but it is not an in-place conversion. Follow the [migration procedure](#migrate-an-existing-archive-or-git-installation) only after validating the workflow in a separate environment.
|
|
||||||
|
|
||||||
## Understand the Installation Layout
|
|
||||||
|
|
||||||
A package installation separates the application code from the files that belong to a particular NetBox instance.
|
|
||||||
|
|
||||||
| Component | Example Location | Purpose |
|
|
||||||
|-----------|------------------|---------|
|
|
||||||
| Application code | `<venv>/lib/pythonX.Y/site-packages/` | Installed and replaced by `pip`; do not modify it directly |
|
|
||||||
| Python virtual environment | `/opt/netbox/venv/` | Contains NetBox, its dependencies, and any plugins |
|
|
||||||
| Instance root | `/opt/netbox/` | Holds local configuration and mutable instance data |
|
|
||||||
| Configuration | `/opt/netbox/conf/configuration.py` | Contains settings and credentials for this instance |
|
|
||||||
| Mutable data | `/opt/netbox/{media,reports,scripts,static}/` | Persists independently of package upgrades |
|
|
||||||
| Deployment examples | `/opt/netbox/contrib/` | Local copies to review and adapt before use |
|
|
||||||
|
|
||||||
The instance root defaults to `/opt/netbox` and may be changed with the `NETBOX_ROOT` environment variable. The virtual environment does not need to be located below the instance root; `/opt/netbox/venv` is used throughout this guide only to keep the example straightforward.
|
|
||||||
|
|
||||||
!!! note "Custom instance roots"
|
|
||||||
The `--target` option for `netbox setup` selects where the local files are created. It does not permanently set the instance root. When using a location other than `/opt/netbox`, set `NETBOX_ROOT` for all NetBox commands and services.
|
|
||||||
|
|
||||||
## Before You Begin
|
|
||||||
|
|
||||||
Complete the [PostgreSQL](1-postgresql.md) and [Redis](2-redis.md) installation steps first. Then install the same [required system packages](3-netbox.md#install-system-packages) used by the archive and Git installation methods.
|
|
||||||
|
|
||||||
## Create the System User and Instance Root
|
|
||||||
|
|
||||||
Create the `netbox` system account and the default instance root:
|
|
||||||
|
|
||||||
```no-highlight
|
|
||||||
sudo adduser --system --group netbox
|
|
||||||
sudo mkdir -p /opt/netbox
|
|
||||||
sudo chown root:netbox /opt/netbox
|
|
||||||
sudo chmod 755 /opt/netbox
|
|
||||||
```
|
|
||||||
|
|
||||||
## Create the Virtual Environment
|
|
||||||
|
|
||||||
Create a Python virtual environment and update `pip`:
|
|
||||||
|
|
||||||
```no-highlight
|
|
||||||
sudo python3 -m venv /opt/netbox/venv
|
|
||||||
sudo /opt/netbox/venv/bin/python -m pip install --upgrade pip
|
|
||||||
```
|
|
||||||
|
|
||||||
Install the desired NetBox release. Replace `X.Y.Z` with the exact version to install:
|
|
||||||
|
|
||||||
```no-highlight
|
|
||||||
sudo /opt/netbox/venv/bin/python -m pip install "netbox==X.Y.Z"
|
|
||||||
```
|
|
||||||
|
|
||||||
Pinning the version makes the installed release explicit and prevents an unintended upgrade when the command is repeated later.
|
|
||||||
|
|
||||||
## Scaffold the Instance Root
|
|
||||||
|
|
||||||
Run `netbox setup` to create the local configuration skeleton and copy the bundled deployment examples:
|
|
||||||
|
|
||||||
```no-highlight
|
|
||||||
sudo /opt/netbox/venv/bin/netbox setup --target /opt/netbox
|
|
||||||
```
|
|
||||||
|
|
||||||
The command creates the following files when they do not already exist:
|
|
||||||
|
|
||||||
```no-highlight
|
|
||||||
/opt/netbox/
|
|
||||||
├── conf/
|
|
||||||
│ ├── __init__.py
|
|
||||||
│ └── configuration.py
|
|
||||||
├── contrib/
|
|
||||||
│ ├── apache.conf
|
|
||||||
│ ├── gunicorn.py
|
|
||||||
│ ├── netbox-rq.service
|
|
||||||
│ ├── netbox.env
|
|
||||||
│ ├── netbox.service
|
|
||||||
│ ├── nginx.conf
|
|
||||||
│ └── uwsgi.ini
|
|
||||||
└── local_requirements.txt
|
|
||||||
```
|
|
||||||
|
|
||||||
`netbox setup` is intentionally non-destructive: existing files are left untouched. It does not install systemd units, configure an HTTP server, rewrite deployment examples for the local paths, or enable plugins.
|
|
||||||
|
|
||||||
Create the directories used for mutable instance data and grant the NetBox service account ownership of them:
|
|
||||||
|
|
||||||
```no-highlight
|
|
||||||
sudo mkdir -p /opt/netbox/{media,reports,scripts,static}
|
|
||||||
sudo chown --recursive netbox:netbox \
|
|
||||||
/opt/netbox/media \
|
|
||||||
/opt/netbox/reports \
|
|
||||||
/opt/netbox/scripts \
|
|
||||||
/opt/netbox/static
|
|
||||||
```
|
|
||||||
|
|
||||||
## Configure NetBox
|
|
||||||
|
|
||||||
Open the scaffolded configuration file:
|
|
||||||
|
|
||||||
```no-highlight
|
|
||||||
sudo ${EDITOR:-vi} /opt/netbox/conf/configuration.py
|
|
||||||
```
|
|
||||||
|
|
||||||
Define the five [required configuration parameters](../configuration/required-parameters.md):
|
|
||||||
|
|
||||||
* `ALLOWED_HOSTS`
|
|
||||||
* `API_TOKEN_PEPPERS`
|
|
||||||
* `DATABASES`
|
|
||||||
* `REDIS`
|
|
||||||
* `SECRET_KEY`
|
|
||||||
|
|
||||||
Generate a suitable random value for `SECRET_KEY` with the installed command:
|
|
||||||
|
|
||||||
```no-highlight
|
|
||||||
sudo /opt/netbox/venv/bin/netbox secret-key
|
|
||||||
```
|
|
||||||
|
|
||||||
Run the command again to generate an independent value for the first entry in `API_TOKEN_PEPPERS`. Treat both values as sensitive and do not reuse the examples from the documentation.
|
|
||||||
|
|
||||||
After saving the configuration, restrict access while allowing the NetBox service account to read it:
|
|
||||||
|
|
||||||
```no-highlight
|
|
||||||
sudo chown --recursive root:netbox /opt/netbox/conf
|
|
||||||
sudo chmod 750 /opt/netbox/conf
|
|
||||||
sudo chmod 640 /opt/netbox/conf/configuration.py
|
|
||||||
```
|
|
||||||
|
|
||||||
!!! note "Environment-based configuration"
|
|
||||||
Ensure that any environment variables referenced by `configuration.py` are present when running `netbox upgrade`, `netbox createsuperuser`, and other management commands, and provide the same variables to both NetBox services. The copied `contrib/netbox.env` file is an example only and is not loaded automatically.
|
|
||||||
|
|
||||||
## Install Plugins and Optional Python Packages
|
|
||||||
|
|
||||||
Plugins and any other local Python requirements must be installed into the **same virtual environment** as NetBox before running the installation or upgrade tasks. Add each package to `/opt/netbox/local_requirements.txt`, then install the file:
|
|
||||||
|
|
||||||
```no-highlight
|
|
||||||
sudo ${EDITOR:-vi} /opt/netbox/local_requirements.txt
|
|
||||||
sudo /opt/netbox/venv/bin/python -m pip install \
|
|
||||||
-r /opt/netbox/local_requirements.txt
|
|
||||||
```
|
|
||||||
|
|
||||||
Installing a plugin does not enable it. Add the plugin to the `PLUGINS` list in `/opt/netbox/conf/configuration.py` and complete any plugin-specific configuration separately.
|
|
||||||
|
|
||||||
NetBox also provides optional package extras for several common integrations. For example, install the LDAP dependencies together with the same pinned NetBox version as follows:
|
|
||||||
|
|
||||||
```no-highlight
|
|
||||||
sudo /opt/netbox/venv/bin/python -m pip install "netbox[ldap]==X.Y.Z"
|
|
||||||
```
|
|
||||||
|
|
||||||
Remember which extras are in use and specify them again when upgrading. For LDAP authentication, create `ldap_config.py` beside the active configuration file at `/opt/netbox/conf/ldap_config.py` when following the [LDAP configuration guide](6-ldap.md). Give it the same ownership and permissions as `configuration.py`:
|
|
||||||
|
|
||||||
```no-highlight
|
|
||||||
sudo chown root:netbox /opt/netbox/conf/ldap_config.py
|
|
||||||
sudo chmod 640 /opt/netbox/conf/ldap_config.py
|
|
||||||
```
|
|
||||||
|
|
||||||
When using uWSGI, install `pyuwsgi` into the same virtual environment and record it as a local requirement:
|
|
||||||
|
|
||||||
```no-highlight
|
|
||||||
sudo sh -c "echo 'pyuwsgi' >> /opt/netbox/local_requirements.txt"
|
|
||||||
sudo /opt/netbox/venv/bin/python -m pip install pyuwsgi
|
|
||||||
```
|
|
||||||
|
|
||||||
## Run the Installation Tasks
|
|
||||||
|
|
||||||
Run the packaged upgrade command to apply database migrations, collect static files, and perform the remaining application installation tasks:
|
|
||||||
|
|
||||||
```no-highlight
|
|
||||||
sudo -u netbox /opt/netbox/venv/bin/netbox upgrade --no-input
|
|
||||||
```
|
|
||||||
|
|
||||||
The `netbox upgrade` command is used for both a fresh package installation and future package upgrades. It replaces the source installation's `upgrade.sh` workflow.
|
|
||||||
|
|
||||||
For a custom instance root, pass `NETBOX_ROOT` explicitly. The virtual environment may remain elsewhere:
|
|
||||||
|
|
||||||
```no-highlight
|
|
||||||
sudo -u netbox env NETBOX_ROOT=/srv/netbox \
|
|
||||||
/opt/netbox-venv/bin/netbox upgrade --no-input
|
|
||||||
```
|
|
||||||
|
|
||||||
## Create a Superuser
|
|
||||||
|
|
||||||
Create the first administrative account:
|
|
||||||
|
|
||||||
```no-highlight
|
|
||||||
sudo -u netbox /opt/netbox/venv/bin/netbox createsuperuser
|
|
||||||
```
|
|
||||||
|
|
||||||
## Test the Application
|
|
||||||
|
|
||||||
Start Django's development server temporarily to confirm that NetBox can load its configuration and connect to its dependencies:
|
|
||||||
|
|
||||||
```no-highlight
|
|
||||||
sudo -u netbox /opt/netbox/venv/bin/netbox \
|
|
||||||
runserver 0.0.0.0:8000 --insecure
|
|
||||||
```
|
|
||||||
|
|
||||||
Connect to the server on port 8000 and log in with the superuser account. Type `Ctrl+c` to stop the development server after testing.
|
|
||||||
|
|
||||||
!!! danger "Not for production use"
|
|
||||||
The development server is intended only for installation testing. It is neither performant nor secure enough for production use.
|
|
||||||
|
|
||||||
## Adapt the Deployment Examples
|
|
||||||
|
|
||||||
The files copied to `/opt/netbox/contrib/` are the same deployment examples shipped for archive and Git installations. They are not rewritten for the package layout. Adapt them before following the shared Gunicorn, uWSGI, and HTTP server instructions.
|
|
||||||
|
|
||||||
For the default paths used in this guide, the following commands remove the source-tree references:
|
|
||||||
|
|
||||||
```no-highlight
|
|
||||||
sudo sed -i \
|
|
||||||
's| --pythonpath /opt/netbox/netbox||' \
|
|
||||||
/opt/netbox/contrib/netbox.service
|
|
||||||
|
|
||||||
sudo sed -i \
|
|
||||||
's|/opt/netbox/venv/bin/python3 /opt/netbox/netbox/manage.py|/opt/netbox/venv/bin/netbox|' \
|
|
||||||
/opt/netbox/contrib/netbox-rq.service
|
|
||||||
|
|
||||||
sudo sed -i \
|
|
||||||
's|chdir = netbox|chdir = /opt/netbox|' \
|
|
||||||
/opt/netbox/contrib/uwsgi.ini
|
|
||||||
|
|
||||||
sudo sed -i \
|
|
||||||
's|/opt/netbox/netbox/static|/opt/netbox/static|g' \
|
|
||||||
/opt/netbox/contrib/nginx.conf \
|
|
||||||
/opt/netbox/contrib/apache.conf
|
|
||||||
```
|
|
||||||
|
|
||||||
These changes have the following effect:
|
|
||||||
|
|
||||||
| File | Package Installation Change |
|
|
||||||
|------|-----------------------------|
|
|
||||||
| `netbox.service` | Imports `netbox.wsgi` from the virtual environment without a source-tree `--pythonpath` |
|
|
||||||
| `netbox-rq.service` | Runs the RQ worker through the installed `netbox` command instead of `manage.py` |
|
|
||||||
| `uwsgi.ini` | Uses the instance root rather than the absent `/opt/netbox/netbox/` source directory |
|
|
||||||
| `nginx.conf` and `apache.conf` | Serve collected static files from `/opt/netbox/static/` |
|
|
||||||
|
|
||||||
Review every file before installing it. When using a different instance root or virtual environment, update all `WorkingDirectory`, `ExecStart`, `chdir`, virtual environment, and static-file paths accordingly. Also add the following line to the `[Service]` section of both systemd units, replacing the path as needed:
|
|
||||||
|
|
||||||
```ini
|
|
||||||
Environment=NETBOX_ROOT=/srv/netbox
|
|
||||||
```
|
|
||||||
|
|
||||||
When using environment-based configuration, reference an appropriate environment file from both systemd units or define the required variables directly in each unit.
|
|
||||||
|
|
||||||
## Continue the Installation
|
|
||||||
|
|
||||||
With the deployment examples adapted, continue with either [Gunicorn](4a-gunicorn.md) or [uWSGI](4b-uwsgi.md). When using uWSGI and you installed `pyuwsgi` above, skip the **Installation** subsection on the uWSGI page and begin with its configuration steps. Then configure an [HTTP server](5-http-server.md) and, if needed, [LDAP authentication](6-ldap.md).
|
|
||||||
|
|
||||||
The shared pages copy files from `/opt/netbox/contrib/`, so make the package-specific changes above **before** copying those files into their final locations.
|
|
||||||
|
|
||||||
## Migrate an Existing Archive or Git Installation
|
|
||||||
|
|
||||||
!!! warning "Experimental migration path"
|
|
||||||
Migrating an existing deployment to the Python package changes its filesystem and upgrade model. Take a complete backup, document the current configuration, and verify a rollback procedure before proceeding.
|
|
||||||
|
|
||||||
Python package releases begin with NetBox v4.7. Before migrating an older deployment, first upgrade the existing archive or Git installation to a version that is available as a Python package.
|
|
||||||
|
|
||||||
Migrate the layout separately from a NetBox version upgrade. Install the **same NetBox version** that is currently running, validate the package-based deployment, and only then upgrade to a newer release.
|
|
||||||
|
|
||||||
The following example keeps the existing `/opt/netbox` installation in place during migration. It uses `/srv/netbox` as the new instance root and `/opt/netbox-venv` for the new virtual environment.
|
|
||||||
|
|
||||||
1. Stop the existing NetBox services after completing a backup:
|
|
||||||
|
|
||||||
```no-highlight
|
|
||||||
sudo systemctl stop netbox netbox-rq
|
|
||||||
```
|
|
||||||
|
|
||||||
2. Create the new virtual environment and install the same NetBox version as the existing deployment:
|
|
||||||
|
|
||||||
```no-highlight
|
|
||||||
sudo python3 -m venv /opt/netbox-venv
|
|
||||||
sudo /opt/netbox-venv/bin/python -m pip install --upgrade pip
|
|
||||||
sudo /opt/netbox-venv/bin/python -m pip install "netbox==X.Y.Z"
|
|
||||||
```
|
|
||||||
|
|
||||||
3. Scaffold the new instance root and create its mutable directories:
|
|
||||||
|
|
||||||
```no-highlight
|
|
||||||
sudo mkdir -p /srv/netbox
|
|
||||||
sudo chown root:netbox /srv/netbox
|
|
||||||
sudo chmod 755 /srv/netbox
|
|
||||||
sudo /opt/netbox-venv/bin/netbox setup --target /srv/netbox
|
|
||||||
sudo mkdir -p /srv/netbox/{media,reports,scripts,static}
|
|
||||||
sudo chown --recursive netbox:netbox \
|
|
||||||
/srv/netbox/media \
|
|
||||||
/srv/netbox/reports \
|
|
||||||
/srv/netbox/scripts \
|
|
||||||
/srv/netbox/static
|
|
||||||
```
|
|
||||||
|
|
||||||
4. Copy the active configuration from the existing installation. If `local_requirements.txt` exists, copy it over the empty file created by `netbox setup`:
|
|
||||||
|
|
||||||
```no-highlight
|
|
||||||
sudo cp /opt/netbox/netbox/netbox/configuration.py \
|
|
||||||
/srv/netbox/conf/configuration.py
|
|
||||||
|
|
||||||
if [ -f /opt/netbox/local_requirements.txt ]; then
|
|
||||||
sudo cp /opt/netbox/local_requirements.txt \
|
|
||||||
/srv/netbox/local_requirements.txt
|
|
||||||
fi
|
|
||||||
```
|
|
||||||
|
|
||||||
When the existing deployment uses `NETBOX_CONFIGURATION`, copy the active configuration module instead, together with any sibling modules or local files it imports. Review the copied configuration and update any filesystem paths that still reference the old source tree.
|
|
||||||
|
|
||||||
If LDAP is configured, also copy the active `ldap_config.py` to `/srv/netbox/conf/ldap_config.py`.
|
|
||||||
|
|
||||||
5. Copy locally stored media, reports, and scripts. Do not copy collected static files; `netbox upgrade` will create them again.
|
|
||||||
|
|
||||||
```no-highlight
|
|
||||||
sudo cp -a /opt/netbox/netbox/media/. /srv/netbox/media/
|
|
||||||
sudo cp -a /opt/netbox/netbox/reports/. /srv/netbox/reports/
|
|
||||||
sudo cp -a /opt/netbox/netbox/scripts/. /srv/netbox/scripts/
|
|
||||||
sudo chown --recursive netbox:netbox \
|
|
||||||
/srv/netbox/media \
|
|
||||||
/srv/netbox/reports \
|
|
||||||
/srv/netbox/scripts
|
|
||||||
```
|
|
||||||
|
|
||||||
Use the paths configured by `MEDIA_ROOT`, `REPORTS_ROOT`, and `SCRIPTS_ROOT` instead when the existing deployment stores these files elsewhere.
|
|
||||||
|
|
||||||
6. Install all plugins and local requirements into the new virtual environment **before** running the upgrade tasks:
|
|
||||||
|
|
||||||
```no-highlight
|
|
||||||
sudo /opt/netbox-venv/bin/python -m pip install \
|
|
||||||
-r /srv/netbox/local_requirements.txt
|
|
||||||
```
|
|
||||||
|
|
||||||
Repeat any NetBox package extras used by the deployment, and verify that each plugin supports the installed NetBox version.
|
|
||||||
|
|
||||||
7. Secure the configuration and run the package installation tasks against the existing database:
|
|
||||||
|
|
||||||
```no-highlight
|
|
||||||
sudo chown --recursive root:netbox /srv/netbox/conf
|
|
||||||
sudo chmod 750 /srv/netbox/conf
|
|
||||||
sudo chmod 640 /srv/netbox/conf/configuration.py
|
|
||||||
|
|
||||||
sudo -u netbox env NETBOX_ROOT=/srv/netbox \
|
|
||||||
/opt/netbox-venv/bin/netbox upgrade --no-input
|
|
||||||
```
|
|
||||||
|
|
||||||
If `ldap_config.py` was copied, also run `sudo chmod 640 /srv/netbox/conf/ldap_config.py`.
|
|
||||||
|
|
||||||
8. Follow [Adapt the Deployment Examples](#adapt-the-deployment-examples), substituting `/srv/netbox` and `/opt/netbox-venv` for the example paths. Install the updated systemd and HTTP server configuration, switch the services to the package deployment, and ensure that both systemd units define `NETBOX_ROOT=/srv/netbox`.
|
|
||||||
|
|
||||||
9. Start the services, test the web interface and background processing, and retain the previous installation until the new deployment has been validated:
|
|
||||||
|
|
||||||
```no-highlight
|
|
||||||
sudo systemctl start netbox netbox-rq
|
|
||||||
```
|
|
||||||
|
|
||||||
After the migration is complete, use the [Python package upgrade procedure](upgrading.md#upgrade-a-python-package-installation-experimental) for future releases.
|
|
||||||
|
|
@ -95,23 +95,3 @@ If you are able to connect but receive a 502 (bad gateway) error, check the foll
|
||||||
* The WSGI worker processes (gunicorn) are running (`systemctl status netbox` should show a status of "active (running)")
|
* The WSGI worker processes (gunicorn) are running (`systemctl status netbox` should show a status of "active (running)")
|
||||||
* Nginx/Apache is configured to connect to the port on which gunicorn is listening (default is 8001).
|
* Nginx/Apache is configured to connect to the port on which gunicorn is listening (default is 8001).
|
||||||
* SELinux is not preventing the reverse proxy connection. You may need to allow HTTP network connections with the command `setsebool -P httpd_can_network_connect 1`
|
* SELinux is not preventing the reverse proxy connection. You may need to allow HTTP network connections with the command `setsebool -P httpd_can_network_connect 1`
|
||||||
|
|
||||||
## What's Next?
|
|
||||||
|
|
||||||
With NetBox up and running, you may want to extend its capabilities by installing one or more plugins. Plugins are optional components that add new models, views, integrations, and other functionality on top of core NetBox. Some of the most popular plugins include:
|
|
||||||
|
|
||||||
* [**NetBox Branching**](https://github.com/netboxlabs/netbox-branching) — Create isolated, changeable branches of your NetBox data, allowing multiple users to work in parallel and merge their changes.
|
|
||||||
* [**NetBox Custom Objects**](https://github.com/netboxlabs/netbox-custom-objects) — Define entirely new object types directly in the UI, without writing any code.
|
|
||||||
* [**NetBox DNS**](https://github.com/sys4/netbox-plugin-dns) — Manage DNS zones, records, and related data as an authoritative source of truth.
|
|
||||||
* [**NetBox BGP**](https://github.com/netbox-community/netbox-bgp) — Document and manage BGP sessions, communities, and routing policies.
|
|
||||||
|
|
||||||
Installing a plugin generally involves adding its Python package to `/opt/netbox/local_requirements.txt`, enabling it in the `PLUGINS` list in `configuration.py`, and running NetBox's upgrade script:
|
|
||||||
|
|
||||||
```no-highlight
|
|
||||||
$ sudo sh -c "echo '<package>' >> /opt/netbox/local_requirements.txt"
|
|
||||||
$ sudo /opt/netbox/upgrade.sh
|
|
||||||
```
|
|
||||||
|
|
||||||
Each plugin is different and may require additional configuration or setup steps, so always consult the plugin's own documentation as well as NetBox's [plugin installation guide](../plugins/installation.md) before getting started.
|
|
||||||
|
|
||||||
To browse the full catalog of available plugins, visit [netboxlabs.com/plugins](https://netboxlabs.com/plugins/).
|
|
||||||
|
|
|
||||||
|
|
@ -12,8 +12,6 @@ sudo apt install -y libldap2-dev libsasl2-dev libssl-dev
|
||||||
|
|
||||||
### Install django-auth-ldap
|
### Install django-auth-ldap
|
||||||
|
|
||||||
=== "Release archive or Git"
|
|
||||||
|
|
||||||
Activate the Python virtual environment and install the `django-auth-ldap` package using pip:
|
Activate the Python virtual environment and install the `django-auth-ldap` package using pip:
|
||||||
|
|
||||||
```no-highlight
|
```no-highlight
|
||||||
|
|
@ -27,16 +25,6 @@ sudo apt install -y libldap2-dev libsasl2-dev libssl-dev
|
||||||
sudo sh -c "echo 'django-auth-ldap' >> /opt/netbox/local_requirements.txt"
|
sudo sh -c "echo 'django-auth-ldap' >> /opt/netbox/local_requirements.txt"
|
||||||
```
|
```
|
||||||
|
|
||||||
=== "Python package (experimental)"
|
|
||||||
|
|
||||||
Install NetBox's `ldap` optional dependency group, pinned to the installed NetBox version:
|
|
||||||
|
|
||||||
```no-highlight
|
|
||||||
sudo /opt/netbox/venv/bin/python -m pip install "netbox[ldap]==X.Y.Z"
|
|
||||||
```
|
|
||||||
|
|
||||||
Specify the `ldap` extra again when upgrading the NetBox package. See the [Python package upgrade procedure](upgrading.md#upgrade-a-python-package-installation-experimental).
|
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
First, enable the LDAP authentication backend in `configuration.py`. (Be sure to overwrite this definition if it is already set to `RemoteUserBackend`.)
|
First, enable the LDAP authentication backend in `configuration.py`. (Be sure to overwrite this definition if it is already set to `RemoteUserBackend`.)
|
||||||
|
|
@ -45,14 +33,7 @@ First, enable the LDAP authentication backend in `configuration.py`. (Be sure to
|
||||||
REMOTE_AUTH_BACKEND = 'netbox.authentication.LDAPBackend'
|
REMOTE_AUTH_BACKEND = 'netbox.authentication.LDAPBackend'
|
||||||
```
|
```
|
||||||
|
|
||||||
Next, create a file named `ldap_config.py` in the same directory as the active `configuration.py`. This is typically `/opt/netbox/netbox/netbox/` for a release archive or Git installation, or `/opt/netbox/conf/` for a Python package installation. Define all of the parameters required below in `ldap_config.py`. Complete documentation of all `django-auth-ldap` configuration options is included in the project's [official documentation](https://django-auth-ldap.readthedocs.io/).
|
Next, create a file in the same directory as `configuration.py` (typically `/opt/netbox/netbox/netbox/`) named `ldap_config.py`. Define all of the parameters required below in `ldap_config.py`. Complete documentation of all `django-auth-ldap` configuration options is included in the project's [official documentation](https://django-auth-ldap.readthedocs.io/).
|
||||||
|
|
||||||
For a Python package installation, protect the file while allowing the NetBox service account to read it:
|
|
||||||
|
|
||||||
```no-highlight
|
|
||||||
sudo chown root:netbox /opt/netbox/conf/ldap_config.py
|
|
||||||
sudo chmod 640 /opt/netbox/conf/ldap_config.py
|
|
||||||
```
|
|
||||||
|
|
||||||
### General Server Configuration
|
### General Server Configuration
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,23 +18,21 @@ The following sections detail how to set up a new instance of NetBox:
|
||||||
|
|
||||||
1. [PostgreSQL database](1-postgresql.md)
|
1. [PostgreSQL database](1-postgresql.md)
|
||||||
2. [Redis](2-redis.md)
|
2. [Redis](2-redis.md)
|
||||||
3. Install the NetBox application using either:
|
3. [NetBox components](3-netbox.md)
|
||||||
* a [release archive or Git checkout](3-netbox.md); or
|
|
||||||
* the [Python package](3b-python-package.md) (experimental)
|
|
||||||
4. [Gunicorn](4a-gunicorn.md) or [uWSGI](4b-uwsgi.md)
|
4. [Gunicorn](4a-gunicorn.md) or [uWSGI](4b-uwsgi.md)
|
||||||
5. [HTTP server](5-http-server.md)
|
5. [HTTP server](5-http-server.md)
|
||||||
6. [LDAP authentication](6-ldap.md) (optional)
|
6. [LDAP authentication](6-ldap.md) (optional)
|
||||||
|
|
||||||
!!! warning "Experimental Python package installation"
|
|
||||||
Installing NetBox from the Python package is experimental in NetBox v4.7 and is not recommended for production use. It is intended for evaluation and feedback. The release archive and Git workflows remain supported and are the established installation methods.
|
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
| Dependency | Supported Versions |
|
| Dependency | Supported Versions |
|
||||||
|------------|--------------------|
|
|------------|--------------------|
|
||||||
| Python | 3.12, 3.13, 3.14 |
|
| Python | 3.12, 3.13, 3.14 |
|
||||||
| PostgreSQL | 15+ |
|
| PostgreSQL | 14+ [^1] |
|
||||||
| Redis | 6.0+ |
|
| Redis | 5.0+ [^2] |
|
||||||
|
|
||||||
|
[^1]: Support for PostgreSQL 14 is deprecated and will be removed in NetBox v4.7. PostgreSQL 15 or later will be required.
|
||||||
|
[^2]: Support for Redis versions older than 6.0 is deprecated and will be removed in NetBox v4.7. Redis 6.0 or later will be required.
|
||||||
|
|
||||||
Below is a simplified overview of the NetBox application stack for reference:
|
Below is a simplified overview of the NetBox application stack for reference:
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,27 +22,26 @@ block-beta
|
||||||
!!! warning "Perform a Backup"
|
!!! warning "Perform a Backup"
|
||||||
Always be sure to save a backup of your current NetBox deployment prior to starting the upgrade process.
|
Always be sure to save a backup of your current NetBox deployment prior to starting the upgrade process.
|
||||||
|
|
||||||
## Review the Release Notes
|
## 1. Review the Release Notes
|
||||||
|
|
||||||
Prior to upgrading your NetBox instance, be sure to carefully review all [release notes](../release-notes/index.md) that have been published since your current version was released. Although the upgrade process typically does not involve additional work, certain releases may introduce breaking or backward-incompatible changes. These are called out in the release notes under the release in which the change went into effect.
|
Prior to upgrading your NetBox instance, be sure to carefully review all [release notes](../release-notes/index.md) that have been published since your current version was released. Although the upgrade process typically does not involve additional work, certain releases may introduce breaking or backward-incompatible changes. These are called out in the release notes under the release in which the change went into effect.
|
||||||
|
|
||||||
Before proceeding, verify that all installed plugins support the target NetBox release.
|
## 2. Update Dependencies to Required Versions
|
||||||
|
|
||||||
## Update Required Dependencies
|
|
||||||
|
|
||||||
NetBox requires the following dependencies:
|
NetBox requires the following dependencies:
|
||||||
|
|
||||||
| Dependency | Supported Versions |
|
| Dependency | Supported Versions |
|
||||||
|------------|--------------------|
|
|------------|--------------------|
|
||||||
| Python | 3.12, 3.13, 3.14 |
|
| Python | 3.12, 3.13, 3.14 |
|
||||||
| PostgreSQL | 15+ |
|
| PostgreSQL | 14+ [^1] |
|
||||||
| Redis | 6.0+ |
|
| Redis | 5.0+ |
|
||||||
|
|
||||||
|
[^1]: Support for PostgreSQL 14 is deprecated and will be removed in NetBox v4.7. PostgreSQL 15 or later will be required.
|
||||||
|
|
||||||
### Version History
|
### Version History
|
||||||
|
|
||||||
| NetBox Version | Python min | Python max | PostgreSQL min | Redis min | Documentation |
|
| NetBox Version | Python min | Python max | PostgreSQL min | Redis min | Documentation |
|
||||||
|:--------------:|:----------:|:----------:|:--------------:|:---------:|:-----------------------------------------------------------------------------------------:|
|
|:--------------:|:----------:|:----------:|:--------------:|:---------:|:-----------------------------------------------------------------------------------------:|
|
||||||
| 4.7 | 3.12 | 3.14 | 15 | 6.0 | [Link](https://github.com/netbox-community/netbox/blob/v4.7.0/docs/installation/index.md) |
|
|
||||||
| 4.6 | 3.12 | 3.14 | 14 | 5.0 | [Link](https://github.com/netbox-community/netbox/blob/v4.6.0/docs/installation/index.md) |
|
| 4.6 | 3.12 | 3.14 | 14 | 5.0 | [Link](https://github.com/netbox-community/netbox/blob/v4.6.0/docs/installation/index.md) |
|
||||||
| 4.5 | 3.12 | 3.14 | 14 | 5.0 | [Link](https://github.com/netbox-community/netbox/blob/v4.5.0/docs/installation/index.md) |
|
| 4.5 | 3.12 | 3.14 | 14 | 5.0 | [Link](https://github.com/netbox-community/netbox/blob/v4.5.0/docs/installation/index.md) |
|
||||||
| 4.4 | 3.10 | 3.12 | 14 | 5.0 | [Link](https://github.com/netbox-community/netbox/blob/v4.4.0/docs/installation/index.md) |
|
| 4.4 | 3.10 | 3.12 | 14 | 5.0 | [Link](https://github.com/netbox-community/netbox/blob/v4.4.0/docs/installation/index.md) |
|
||||||
|
|
@ -59,36 +58,7 @@ NetBox requires the following dependencies:
|
||||||
| 3.1 | 3.7 | 3.9 | 10 | 4.0 | [Link](https://github.com/netbox-community/netbox/blob/v3.1.0/docs/installation/index.md) |
|
| 3.1 | 3.7 | 3.9 | 10 | 4.0 | [Link](https://github.com/netbox-community/netbox/blob/v3.1.0/docs/installation/index.md) |
|
||||||
| 3.0 | 3.7 | 3.9 | 9.6 | 4.0 | [Link](https://github.com/netbox-community/netbox/blob/v3.0.0/docs/installation/index.md) |
|
| 3.0 | 3.7 | 3.9 | 9.6 | 4.0 | [Link](https://github.com/netbox-community/netbox/blob/v3.0.0/docs/installation/index.md) |
|
||||||
|
|
||||||
## Verify Database Permissions
|
## 3. Install the Latest Release
|
||||||
|
|
||||||
NetBox v4.7 and later require the PostgreSQL [`ltree` extension](https://www.postgresql.org/docs/current/ltree.html). NetBox installs this extension automatically when applying database migrations if it is not already present. Installing it requires that the NetBox database user hold the `CREATE` privilege on the database.
|
|
||||||
|
|
||||||
!!! note
|
|
||||||
Installations created using NetBox's PostgreSQL setup instructions already satisfy this requirement because those instructions make the NetBox user the database owner. No additional grant is needed for these installations.
|
|
||||||
|
|
||||||
If `ltree` is not already installed and the NetBox database user does not hold the `CREATE` privilege, grant it by invoking the PostgreSQL shell as the system Postgres user:
|
|
||||||
|
|
||||||
```no-highlight
|
|
||||||
sudo -u postgres psql
|
|
||||||
```
|
|
||||||
|
|
||||||
Then issue the following command, substituting the name of your database and user (role) where applicable:
|
|
||||||
|
|
||||||
```postgresql
|
|
||||||
GRANT CREATE ON DATABASE netbox TO netbox;
|
|
||||||
```
|
|
||||||
|
|
||||||
Alternatively, a database administrator can install the extension before upgrading:
|
|
||||||
|
|
||||||
```postgresql
|
|
||||||
CREATE EXTENSION IF NOT EXISTS ltree;
|
|
||||||
```
|
|
||||||
|
|
||||||
## Upgrade a Release Archive or Git Installation
|
|
||||||
|
|
||||||
The following procedure applies to NetBox installations created from a release archive or Git checkout. Complete the preparation steps above, then use the same installation method that was used for the existing deployment.
|
|
||||||
|
|
||||||
### 1. Install the Latest Release
|
|
||||||
|
|
||||||
As with the initial installation, you can upgrade NetBox by either downloading the latest release package or by checking out the latest production release from the git repository.
|
As with the initial installation, you can upgrade NetBox by either downloading the latest release package or by checking out the latest production release from the git repository.
|
||||||
|
|
||||||
|
|
@ -103,7 +73,7 @@ ls -ld /opt/netbox /opt/netbox/.git
|
||||||
|
|
||||||
If NetBox was installed from a release package, then `/opt/netbox` will be a symlink pointing to the current version, and `/opt/netbox/.git` will not exist. If it was installed from git, then `/opt/netbox` and `/opt/netbox/.git` will both exist as normal directories.
|
If NetBox was installed from a release package, then `/opt/netbox` will be a symlink pointing to the current version, and `/opt/netbox/.git` will not exist. If it was installed from git, then `/opt/netbox` and `/opt/netbox/.git` will both exist as normal directories.
|
||||||
|
|
||||||
#### Option A: Download a Release
|
### Option A: Download a Release
|
||||||
|
|
||||||
Download the [latest stable release](https://github.com/netbox-community/netbox/releases) from GitHub as a tarball or ZIP archive. Extract it to your desired path. In this example, we'll use `/opt/netbox`.
|
Download the [latest stable release](https://github.com/netbox-community/netbox/releases) from GitHub as a tarball or ZIP archive. Extract it to your desired path. In this example, we'll use `/opt/netbox`.
|
||||||
|
|
||||||
|
|
@ -146,7 +116,7 @@ If you followed the original installation guide to set up gunicorn, be sure to c
|
||||||
sudo cp /opt/netbox-$OLDVER/gunicorn.py /opt/netbox/
|
sudo cp /opt/netbox-$OLDVER/gunicorn.py /opt/netbox/
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Option B: Check Out a Git Release
|
### Option B: Check Out a Git Release
|
||||||
|
|
||||||
This guide assumes that NetBox is installed in `/opt/netbox`. First, determine the latest release either by visiting our [releases page](https://github.com/netbox-community/netbox/releases) or by running the following command:
|
This guide assumes that NetBox is installed in `/opt/netbox`. First, determine the latest release either by visiting our [releases page](https://github.com/netbox-community/netbox/releases) or by running the following command:
|
||||||
|
|
||||||
|
|
@ -165,7 +135,7 @@ sudo git fetch --tags && \
|
||||||
sudo git checkout v4.5.0
|
sudo git checkout v4.5.0
|
||||||
```
|
```
|
||||||
|
|
||||||
### 2. Run the Upgrade Script
|
## 4. Run the Upgrade Script
|
||||||
|
|
||||||
Once the new code is in place, verify that any optional Python packages required by your deployment (e.g. `django-auth-ldap`) are listed in `local_requirements.txt`. Then, run the upgrade script:
|
Once the new code is in place, verify that any optional Python packages required by your deployment (e.g. `django-auth-ldap`) are listed in `local_requirements.txt`. Then, run the upgrade script:
|
||||||
|
|
||||||
|
|
@ -199,7 +169,7 @@ This script performs the following actions:
|
||||||
been made to your local codebase and should be investigated. Never attempt to create new migrations unless you are
|
been made to your local codebase and should be investigated. Never attempt to create new migrations unless you are
|
||||||
intentionally modifying the database schema.
|
intentionally modifying the database schema.
|
||||||
|
|
||||||
### 3. Restart the NetBox Services
|
## 5. Restart the NetBox Services
|
||||||
|
|
||||||
!!! warning
|
!!! warning
|
||||||
If you are upgrading from an installation that does not use a Python virtual environment (any release prior to v2.7.9), you'll need to update the systemd service files to reference the new Python and gunicorn executables before restarting the services. These are located in `/opt/netbox/venv/bin/`. See the example service files in `/opt/netbox/contrib/` for reference.
|
If you are upgrading from an installation that does not use a Python virtual environment (any release prior to v2.7.9), you'll need to update the systemd service files to reference the new Python and gunicorn executables before restarting the services. These are located in `/opt/netbox/venv/bin/`. See the example service files in `/opt/netbox/contrib/` for reference.
|
||||||
|
|
@ -209,83 +179,3 @@ Finally, restart the gunicorn and RQ services:
|
||||||
```no-highlight
|
```no-highlight
|
||||||
sudo systemctl restart netbox netbox-rq
|
sudo systemctl restart netbox netbox-rq
|
||||||
```
|
```
|
||||||
|
|
||||||
## Upgrade a Python Package Installation (Experimental)
|
|
||||||
|
|
||||||
!!! warning "Experimental installation method"
|
|
||||||
Installing NetBox from the Python package is experimental in NetBox v4.7 and is **not recommended for production use**. Test the upgrade and rollback procedures in a non-production environment before relying on them.
|
|
||||||
|
|
||||||
This procedure applies only to a deployment created using the [Python package installation method](3b-python-package.md). A package installation does not use `upgrade.sh`; use the installed `netbox upgrade` command instead. For a release archive or Git installation, follow the [procedure above](#upgrade-a-release-archive-or-git-installation).
|
|
||||||
|
|
||||||
Complete the preparation steps at the beginning of this page before proceeding.
|
|
||||||
|
|
||||||
### 1. Stop the NetBox Services
|
|
||||||
|
|
||||||
Stop the web application and background worker services before changing packages in the virtual environment:
|
|
||||||
|
|
||||||
```no-highlight
|
|
||||||
sudo systemctl stop netbox netbox-rq
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. Upgrade NetBox and Local Requirements
|
|
||||||
|
|
||||||
Install the target NetBox version into the existing virtual environment. Replace `X.Y.Z` with the exact version being installed:
|
|
||||||
|
|
||||||
```no-highlight
|
|
||||||
sudo /opt/netbox/venv/bin/python -m pip install --upgrade "netbox==X.Y.Z"
|
|
||||||
```
|
|
||||||
|
|
||||||
If the deployment uses a package extra, include it in the upgrade command. For example, specify the `ldap` extra again when upgrading a deployment that uses LDAP authentication:
|
|
||||||
|
|
||||||
```no-highlight
|
|
||||||
sudo /opt/netbox/venv/bin/python -m pip install --upgrade \
|
|
||||||
"netbox[ldap]==X.Y.Z"
|
|
||||||
```
|
|
||||||
|
|
||||||
Install all plugins and other local Python requirements into the same virtual environment **before** running the NetBox upgrade tasks:
|
|
||||||
|
|
||||||
```no-highlight
|
|
||||||
sudo /opt/netbox/venv/bin/python -m pip install \
|
|
||||||
-r /opt/netbox/local_requirements.txt
|
|
||||||
```
|
|
||||||
|
|
||||||
!!! note "Changing the Python version"
|
|
||||||
A virtual environment cannot be moved to a different Python interpreter in place. If the target NetBox release requires another Python version, create a replacement virtual environment, install the target NetBox package and all local requirements into it, and update the service executable paths before restarting NetBox.
|
|
||||||
|
|
||||||
### 3. Run the Upgrade Tasks
|
|
||||||
|
|
||||||
Run the packaged upgrade command to apply database migrations, collect static files, and perform the remaining application upgrade tasks:
|
|
||||||
|
|
||||||
```no-highlight
|
|
||||||
sudo -u netbox /opt/netbox/venv/bin/netbox upgrade --no-input
|
|
||||||
```
|
|
||||||
|
|
||||||
For a non-default instance root or a virtual environment stored elsewhere, use the applicable paths and set `NETBOX_ROOT` explicitly:
|
|
||||||
|
|
||||||
```no-highlight
|
|
||||||
sudo -u netbox env NETBOX_ROOT=/srv/netbox \
|
|
||||||
/opt/netbox-venv/bin/netbox upgrade --no-input
|
|
||||||
```
|
|
||||||
|
|
||||||
Ensure that any environment variables referenced by the NetBox configuration are also available when running this command.
|
|
||||||
|
|
||||||
### 4. Review the Deployment Configuration
|
|
||||||
|
|
||||||
`netbox setup` is not part of a routine upgrade. It leaves existing configuration and deployment examples untouched. To compare the examples bundled with the new package against the local copies without modifying the instance root, scaffold them into a temporary directory:
|
|
||||||
|
|
||||||
```no-highlight
|
|
||||||
EXAMPLES_DIR=$(mktemp -d)
|
|
||||||
/opt/netbox/venv/bin/netbox setup --target "$EXAMPLES_DIR"
|
|
||||||
diff --recursive /opt/netbox/contrib "$EXAMPLES_DIR/contrib"
|
|
||||||
rm -rf "$EXAMPLES_DIR"
|
|
||||||
```
|
|
||||||
|
|
||||||
The comparison will also show the package-layout changes made when the deployment examples were first adapted. Distinguish these local changes from updates introduced by the new release, and merge any relevant updates into the administrator-managed systemd, WSGI, and HTTP server configuration.
|
|
||||||
|
|
||||||
### 5. Start the NetBox Services
|
|
||||||
|
|
||||||
Start the services and verify that both the web application and background workers are operating normally:
|
|
||||||
|
|
||||||
```no-highlight
|
|
||||||
sudo systemctl start netbox netbox-rq
|
|
||||||
```
|
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ NetBox provides a read-only [GraphQL](https://graphql.org/) API to complement it
|
||||||
GraphQL enables the client to specify an arbitrary nested list of fields to include in the response. All queries are made to the root `/graphql` API endpoint. For example, to return the circuit ID and provider name of each circuit with an active status, you can issue a request such as the following:
|
GraphQL enables the client to specify an arbitrary nested list of fields to include in the response. All queries are made to the root `/graphql` API endpoint. For example, to return the circuit ID and provider name of each circuit with an active status, you can issue a request such as the following:
|
||||||
|
|
||||||
```
|
```
|
||||||
curl -H "Authorization: Bearer $TOKEN" \
|
curl -H "Authorization: Token $TOKEN" \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
-H "Accept: application/json" \
|
-H "Accept: application/json" \
|
||||||
http://netbox/graphql/ \
|
http://netbox/graphql/ \
|
||||||
|
|
@ -51,6 +51,9 @@ For more detail on constructing GraphQL queries, see the [GraphQL queries docume
|
||||||
|
|
||||||
## Filtering
|
## Filtering
|
||||||
|
|
||||||
|
!!! note "Changed in NetBox v4.3"
|
||||||
|
The filtering syntax fo the GraphQL API has changed substantially in NetBox v4.3.
|
||||||
|
|
||||||
Filters can be specified as key-value pairs within parentheses immediately following the query name. For example, the following will return only active sites:
|
Filters can be specified as key-value pairs within parentheses immediately following the query name. For example, the following will return only active sites:
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
@ -136,6 +139,8 @@ The alternative approach is cursor-based pagination, which operates using absolu
|
||||||
|
|
||||||
To ensure consistent ordering, objects will always be ordered by their primary keys when cursor-based pagination is used.
|
To ensure consistent ordering, objects will always be ordered by their primary keys when cursor-based pagination is used.
|
||||||
|
|
||||||
|
!!! note "Cursor-based pagination was introduced in NetBox v4.5.2."
|
||||||
|
|
||||||
Both pagination strategies support an optional `limit` parameter specifying the maximum number of objects to include in the response. The [`MAX_PAGE_SIZE`](../configuration/miscellaneous.md#max_page_size) configuration parameter (default `1000`) sets a hard ceiling on this value; if no limit is specified, up to `MAX_PAGE_SIZE` records are returned.
|
Both pagination strategies support an optional `limit` parameter specifying the maximum number of objects to include in the response. The [`MAX_PAGE_SIZE`](../configuration/miscellaneous.md#max_page_size) configuration parameter (default `1000`) sets a hard ceiling on this value; if no limit is specified, up to `MAX_PAGE_SIZE` records are returned.
|
||||||
|
|
||||||
When `MAX_PAGE_SIZE` is set to `0` or `None`:
|
When `MAX_PAGE_SIZE` is set to `0` or `None`:
|
||||||
|
|
|
||||||
|
|
@ -168,9 +168,6 @@ Or by a set of attributes which uniquely identify the rack:
|
||||||
|
|
||||||
Note that if the provided parameters do not return exactly one object, a validation error is raised.
|
Note that if the provided parameters do not return exactly one object, a validation error is raised.
|
||||||
|
|
||||||
!!! note "Permissions"
|
|
||||||
When a related object is referenced by a set of attributes, the lookup is restricted to only those objects which the requesting user has permission to view. This prevents the enumeration of objects by their attributes. Referencing a related object directly by its numeric ID is always permitted, regardless of the user's view permissions for that object.
|
|
||||||
|
|
||||||
### Generic Relations
|
### Generic Relations
|
||||||
|
|
||||||
Some objects within NetBox have attributes which can reference an object of multiple types, known as _generic relations_. For example, an IP address can be assigned to either a device interface _or_ a virtual machine interface. When making this assignment via the REST API, we must specify two attributes:
|
Some objects within NetBox have attributes which can reference an object of multiple types, known as _generic relations_. For example, an IP address can be assigned to either a device interface _or_ a virtual machine interface. When making this assignment via the REST API, we must specify two attributes:
|
||||||
|
|
@ -182,7 +179,7 @@ Together, these values identify a unique object in NetBox. The assigned object (
|
||||||
|
|
||||||
```no-highlight
|
```no-highlight
|
||||||
curl -X POST \
|
curl -X POST \
|
||||||
-H "Authorization: Bearer $TOKEN" \
|
-H "Authorization: Token $TOKEN" \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
-H "Accept: application/json; indent=4" \
|
-H "Accept: application/json; indent=4" \
|
||||||
http://netbox/api/ipam/ip-addresses/ \
|
http://netbox/api/ipam/ip-addresses/ \
|
||||||
|
|
@ -253,6 +250,8 @@ Similarly, you can opt to omit only specific fields by passing the `omit` parame
|
||||||
GET /api/dcim/sites/?omit=circuit_count,device_count,virtualmachine_count
|
GET /api/dcim/sites/?omit=circuit_count,device_count,virtualmachine_count
|
||||||
```
|
```
|
||||||
|
|
||||||
|
!!! note "The `omit` parameter was introduced in NetBox v4.5.2."
|
||||||
|
|
||||||
Strategic use of the `fields` and `omit` parameters can drastically improve REST API performance, as the exclusion of fields which reference related objects reduces the number and complexity of underlying database queries needed to generate the response.
|
Strategic use of the `fields` and `omit` parameters can drastically improve REST API performance, as the exclusion of fields which reference related objects reduces the number and complexity of underlying database queries needed to generate the response.
|
||||||
|
|
||||||
!!! note
|
!!! note
|
||||||
|
|
@ -336,6 +335,10 @@ GET /api/ipam/prefixes/13980/?brief=true
|
||||||
|
|
||||||
The brief format is supported for both lists and individual objects.
|
The brief format is supported for both lists and individual objects.
|
||||||
|
|
||||||
|
### Excluding Config Contexts
|
||||||
|
|
||||||
|
When retrieving devices and virtual machines via the REST API, each will include its rendered [configuration context data](../features/context-data.md) by default. Users with large amounts of context data will likely observe suboptimal performance when returning multiple objects, particularly with very high page sizes. To combat this, context data may be excluded from the response data by attaching the query parameter `?exclude=config_context` to the request. This parameter works for both list and detail views.
|
||||||
|
|
||||||
## Pagination
|
## Pagination
|
||||||
|
|
||||||
API responses which contain a list of many objects will be paginated for efficiency. NetBox employs offset-based pagination by default, which forms a page by skipping the number of objects indicated by the `offset` URL parameter. The root JSON object returned by a list endpoint contains the following attributes:
|
API responses which contain a list of many objects will be paginated for efficiency. NetBox employs offset-based pagination by default, which forms a page by skipping the number of objects indicated by the `offset` URL parameter. The root JSON object returned by a list endpoint contains the following attributes:
|
||||||
|
|
@ -499,7 +502,7 @@ To create a new object, make a `POST` request to the model's _list_ endpoint wit
|
||||||
|
|
||||||
```no-highlight
|
```no-highlight
|
||||||
curl -s -X POST \
|
curl -s -X POST \
|
||||||
-H "Authorization: Bearer $TOKEN" \
|
-H "Authorization: Token $TOKEN" \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
http://netbox/api/ipam/prefixes/ \
|
http://netbox/api/ipam/prefixes/ \
|
||||||
--data '{"prefix": "192.0.2.0/24", "scope_type": "dcim.site", "scope_id": 6}' | jq '.'
|
--data '{"prefix": "192.0.2.0/24", "scope_type": "dcim.site", "scope_id": 6}' | jq '.'
|
||||||
|
|
@ -552,7 +555,7 @@ http://netbox/api/ipam/prefixes/ \
|
||||||
To create multiple instances of a model using a single request, make a `POST` request to the model's _list_ endpoint with a list of JSON objects representing each instance to be created. If successful, the response will contain a list of the newly created instances. The example below illustrates the creation of three new sites.
|
To create multiple instances of a model using a single request, make a `POST` request to the model's _list_ endpoint with a list of JSON objects representing each instance to be created. If successful, the response will contain a list of the newly created instances. The example below illustrates the creation of three new sites.
|
||||||
|
|
||||||
```no-highlight
|
```no-highlight
|
||||||
curl -X POST -H "Authorization: Bearer $TOKEN" \
|
curl -X POST -H "Authorization: Token $TOKEN" \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
-H "Accept: application/json; indent=4" \
|
-H "Accept: application/json; indent=4" \
|
||||||
http://netbox/api/dcim/sites/ \
|
http://netbox/api/dcim/sites/ \
|
||||||
|
|
@ -586,16 +589,13 @@ http://netbox/api/dcim/sites/ \
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
!!! note
|
|
||||||
The bulk creation of objects is an all-or-none operation, meaning that if NetBox fails to successfully create any of the specified objects (e.g. due to a validation error), the entire operation will be aborted and none of the objects will be created.
|
|
||||||
|
|
||||||
### Updating an Object
|
### Updating an Object
|
||||||
|
|
||||||
To modify an object which has already been created, make a `PATCH` request to the model's _detail_ endpoint specifying its unique numeric ID. Include any data which you wish to update on the object. As with object creation, the `Authorization` and `Content-Type` headers must also be specified.
|
To modify an object which has already been created, make a `PATCH` request to the model's _detail_ endpoint specifying its unique numeric ID. Include any data which you wish to update on the object. As with object creation, the `Authorization` and `Content-Type` headers must also be specified.
|
||||||
|
|
||||||
```no-highlight
|
```no-highlight
|
||||||
curl -s -X PATCH \
|
curl -s -X PATCH \
|
||||||
-H "Authorization: Bearer $TOKEN" \
|
-H "Authorization: Token $TOKEN" \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
http://netbox/api/ipam/prefixes/18691/ \
|
http://netbox/api/ipam/prefixes/18691/ \
|
||||||
--data '{"status": "reserved"}' | jq '.'
|
--data '{"status": "reserved"}' | jq '.'
|
||||||
|
|
@ -652,7 +652,7 @@ Multiple objects can be updated simultaneously by issuing a `PUT` or `PATCH` req
|
||||||
|
|
||||||
```no-highlight
|
```no-highlight
|
||||||
curl -s -X PATCH \
|
curl -s -X PATCH \
|
||||||
-H "Authorization: Bearer $TOKEN" \
|
-H "Authorization: Token $TOKEN" \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
http://netbox/api/dcim/sites/ \
|
http://netbox/api/dcim/sites/ \
|
||||||
--data '[{"id": 10, "status": "active"}, {"id": 11, "status": "active"}]'
|
--data '[{"id": 10, "status": "active"}, {"id": 11, "status": "active"}]'
|
||||||
|
|
@ -663,28 +663,10 @@ Note that there is no requirement for the attributes to be identical among objec
|
||||||
!!! note
|
!!! note
|
||||||
The bulk update of objects is an all-or-none operation, meaning that if NetBox fails to successfully update any of the specified objects (e.g. due a validation error), the entire operation will be aborted and none of the objects will be updated.
|
The bulk update of objects is an all-or-none operation, meaning that if NetBox fails to successfully update any of the specified objects (e.g. due a validation error), the entire operation will be aborted and none of the objects will be updated.
|
||||||
|
|
||||||
### Errors in Bulk Operations
|
|
||||||
|
|
||||||
!!! info "This feature was introduced in NetBox v4.7."
|
|
||||||
|
|
||||||
When a bulk creation or update fails validation, the response identifies each offending object by its index within the submitted list, so that a client can correct and resubmit only the objects which actually failed. (The operation itself remains all-or-none: No objects are written unless every object validates.)
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"detail": "1 of 3 objects failed validation.",
|
|
||||||
"errors": [
|
|
||||||
{
|
|
||||||
"index": 1,
|
|
||||||
"errors": {
|
|
||||||
"slug": ["This field may not be blank."]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Concurrent Update Protection
|
### Concurrent Update Protection
|
||||||
|
|
||||||
|
!!! info "This feature was introduced in NetBox v4.6."
|
||||||
|
|
||||||
To guard against the lost-update problem when multiple clients modify the same object, NetBox returns a weak `ETag` response header on detail-view responses (`GET`, `POST`, `PATCH`, `PUT`) for individual objects. Clients may supply this value back on a subsequent `PATCH` or `PUT` request via the `If-Match` request header. If the object's current ETag does not match any of the values supplied, the server rejects the request with a `412 Precondition Failed` response and includes the current ETag in the response so the client can retry.
|
To guard against the lost-update problem when multiple clients modify the same object, NetBox returns a weak `ETag` response header on detail-view responses (`GET`, `POST`, `PATCH`, `PUT`) for individual objects. Clients may supply this value back on a subsequent `PATCH` or `PUT` request via the `If-Match` request header. If the object's current ETag does not match any of the values supplied, the server rejects the request with a `412 Precondition Failed` response and includes the current ETag in the response so the client can retry.
|
||||||
|
|
||||||
```no-highlight
|
```no-highlight
|
||||||
|
|
@ -705,6 +687,8 @@ A literal `If-Match: *` value matches any current ETag and may be used to assert
|
||||||
|
|
||||||
### Adding and Removing Tags
|
### Adding and Removing Tags
|
||||||
|
|
||||||
|
!!! info "This feature was introduced in NetBox v4.6."
|
||||||
|
|
||||||
In addition to replacing an object's tag set wholesale via the `tags` field, taggable models accept two write-only fields, `add_tags` and `remove_tags`, which apply only the specified additions or removals without disturbing existing tags. This is convenient when concurrent clients each manage a distinct subset of an object's tags.
|
In addition to replacing an object's tag set wholesale via the `tags` field, taggable models accept two write-only fields, `add_tags` and `remove_tags`, which apply only the specified additions or removals without disturbing existing tags. This is convenient when concurrent clients each manage a distinct subset of an object's tags.
|
||||||
|
|
||||||
```no-highlight
|
```no-highlight
|
||||||
|
|
@ -730,7 +714,7 @@ To delete an object from NetBox, make a `DELETE` request to the model's _detail_
|
||||||
|
|
||||||
```no-highlight
|
```no-highlight
|
||||||
curl -s -X DELETE \
|
curl -s -X DELETE \
|
||||||
-H "Authorization: Bearer $TOKEN" \
|
-H "Authorization: Token $TOKEN" \
|
||||||
http://netbox/api/ipam/prefixes/18691/
|
http://netbox/api/ipam/prefixes/18691/
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -745,7 +729,7 @@ NetBox supports the simultaneous deletion of multiple objects of the same type b
|
||||||
|
|
||||||
```no-highlight
|
```no-highlight
|
||||||
curl -s -X DELETE \
|
curl -s -X DELETE \
|
||||||
-H "Authorization: Bearer $TOKEN" \
|
-H "Authorization: Token $TOKEN" \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
http://netbox/api/dcim/sites/ \
|
http://netbox/api/dcim/sites/ \
|
||||||
--data '[{"id": 10}, {"id": 11}, {"id": 12}]'
|
--data '[{"id": 10}, {"id": 11}, {"id": 12}]'
|
||||||
|
|
@ -754,53 +738,6 @@ http://netbox/api/dcim/sites/ \
|
||||||
!!! note
|
!!! note
|
||||||
The bulk deletion of objects is an all-or-none operation, meaning that if NetBox fails to delete any of the specified objects (e.g. due a dependency by a related object), the entire operation will be aborted and none of the objects will be deleted.
|
The bulk deletion of objects is an all-or-none operation, meaning that if NetBox fails to delete any of the specified objects (e.g. due a dependency by a related object), the entire operation will be aborted and none of the objects will be deleted.
|
||||||
|
|
||||||
## Background Processing
|
|
||||||
|
|
||||||
!!! info "This feature was introduced in NetBox v4.7."
|
|
||||||
|
|
||||||
Bulk write operations (creating, updating, or deleting multiple objects via a model's list endpoint) can optionally be processed as a [background job](../features/background-jobs.md) rather than synchronously. This is useful for large batches that would otherwise hold the connection open long enough to risk a proxy or gateway timeout.
|
|
||||||
|
|
||||||
To request background processing, append the `background=true` query parameter to a bulk write request. NetBox enqueues a job and returns an `HTTP 202 Accepted` response containing the job's ID and URL. The actual write is performed later by a worker, running the same logic (and preserving the same all-or-none transaction semantics) as the synchronous path. Note that the request payload is **not** validated before the job is enqueued; validation is deferred to the worker (see below).
|
|
||||||
|
|
||||||
```no-highlight
|
|
||||||
curl -s -X PATCH \
|
|
||||||
-H "Authorization: Token $TOKEN" \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
http://netbox/api/dcim/sites/?background=true \
|
|
||||||
--data '[{"id": 10, "status": "active"}, {"id": 11, "status": "active"}]'
|
|
||||||
```
|
|
||||||
|
|
||||||
The response identifies the enqueued job:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"job": {
|
|
||||||
"id": 42,
|
|
||||||
"url": "http://netbox/api/core/jobs/42/",
|
|
||||||
"status": "pending"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Poll the job's URL to track its progress. When the job reaches a terminal status, its `data` field holds the result and its `error` field describes any failure. The `data` field mirrors the response the synchronous request would have returned, as an object with the HTTP `status_code` and the response `data`. For example, a completed bulk update records:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"status_code": 200,
|
|
||||||
"data": [
|
|
||||||
{"id": 10, "url": "http://netbox/api/dcim/sites/10/", "status": {"value": "active"}, "...": "..."}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
A failed job records the equivalent error response, for instance `{"status_code": 400, "data": {"slug": ["This field may not be blank."]}}`, with a short summary also placed in the job's `error` field.
|
|
||||||
|
|
||||||
A `202` response indicates that the request was accepted and queued, not that it succeeded: validation (including malformed or invalid payloads) and the database write all occur when the job runs. A rejected payload is therefore reported as a failed job rather than a synchronous error response. Always inspect the job's final status to confirm the outcome. Because the result is stored on the job, any user permitted to view jobs (`core.view_job`, subject to object permissions) can read the serialized objects it contains.
|
|
||||||
|
|
||||||
Background processing applies only to bulk operations (a JSON list) on a model's list endpoint. For a single-object write the `background` parameter is ignored and the request is processed synchronously. It cannot be combined with an [`If-Match`](#if-match) precondition (which cannot be evaluated reliably once execution is deferred); such a request is rejected with an `HTTP 400` response. If no background worker is running to service the queue, the request is rejected with an `HTTP 503` response rather than enqueuing a job that would never run.
|
|
||||||
|
|
||||||
Two behaviors differ from a synchronous request and may change in a future release: field selection via [`fields`/`omit`](#specifying-fields) (and brief mode) is not applied to the stored result, and the authorization captured when the request is accepted is not re-checked if the token is later disabled or expires before the job runs.
|
|
||||||
|
|
||||||
## Changelog Messages
|
## Changelog Messages
|
||||||
|
|
||||||
Most objects in NetBox support [change logging](../features/change-logging.md), which generates a detailed record each time an object is created, modified, or deleted. Additionally, users can attach a message to the change record as well. This is accomplished via the REST API by including a `changelog_message` field in the object representation.
|
Most objects in NetBox support [change logging](../features/change-logging.md), which generates a detailed record each time an object is created, modified, or deleted. Additionally, users can attach a message to the change record as well. This is accomplished via the REST API by including a `changelog_message` field in the object representation.
|
||||||
|
|
@ -809,7 +746,7 @@ For example, the following API request will create a new site and record a messa
|
||||||
|
|
||||||
```no-highlight
|
```no-highlight
|
||||||
curl -s -X POST \
|
curl -s -X POST \
|
||||||
-H "Authorization: Bearer $TOKEN" \
|
-H "Authorization: Token $TOKEN" \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
http://netbox/api/dcim/sites/ \
|
http://netbox/api/dcim/sites/ \
|
||||||
--data '{
|
--data '{
|
||||||
|
|
@ -829,7 +766,7 @@ For example, we can upload an image attachment using the `curl` command shown be
|
||||||
|
|
||||||
```no-highlight
|
```no-highlight
|
||||||
curl -X POST \
|
curl -X POST \
|
||||||
-H "Authorization: Bearer $TOKEN" \
|
-H "Authorization: Token $TOKEN" \
|
||||||
-H "Accept: application/json; indent=4" \
|
-H "Accept: application/json; indent=4" \
|
||||||
-F "object_type=dcim.site" \
|
-F "object_type=dcim.site" \
|
||||||
-F "object_id=2" \
|
-F "object_id=2" \
|
||||||
|
|
@ -844,7 +781,7 @@ The NetBox REST API primarily employs token-based authentication. For convenienc
|
||||||
|
|
||||||
### Tokens
|
### Tokens
|
||||||
|
|
||||||
A token is a secret, unique identifier mapped to a NetBox user account. Each user may have one or more tokens which he or she can use for authentication when making REST API requests. To create a token, navigate to the API tokens page under your user profile. When creating a token, NetBox will automatically generate a random token value. This value is always generated by the server and cannot be specified by the client; any `token` value included in a creation request is ignored.
|
A token is a secret, unique identifier mapped to a NetBox user account. Each user may have one or more tokens which he or she can use for authentication when making REST API requests. To create a token, navigate to the API tokens page under your user profile. When creating a token, NetBox will automatically populate a randomly-generated token value.
|
||||||
|
|
||||||
!!! note "Tokens cannot be retrieved once created"
|
!!! note "Tokens cannot be retrieved once created"
|
||||||
Once a token has been created, its plaintext value cannot be retrieved. For this reason, you must take care to securely record the token locally immediately upon its creation. If a token plaintext is lost, it cannot be recovered: A new token must be created.
|
Once a token has been created, its plaintext value cannot be retrieved. For this reason, you must take care to securely record the token locally immediately upon its creation. If a token plaintext is lost, it cannot be recovered: A new token must be created.
|
||||||
|
|
@ -870,8 +807,6 @@ By default, a token can be used to perform all actions via the API that a user w
|
||||||
|
|
||||||
Each API token can optionally be restricted by client IP address. If one or more allowed IP prefixes/addresses is defined for a token, authentication will fail for any client connecting from an IP address outside the defined range(s). This enables restricting the use a token to a specific client. (By default, any client IP address is permitted.)
|
Each API token can optionally be restricted by client IP address. If one or more allowed IP prefixes/addresses is defined for a token, authentication will fail for any client connecting from an IP address outside the defined range(s). This enables restricting the use a token to a specific client. (By default, any client IP address is permitted.)
|
||||||
|
|
||||||
The client IP address is determined from the HTTP headers configured by [`HTTP_CLIENT_IP_HEADERS`](../configuration/system.md#http_client_ip_headers); see the security note there regarding header trust.
|
|
||||||
|
|
||||||
#### Creating Tokens for Other Users
|
#### Creating Tokens for Other Users
|
||||||
|
|
||||||
It is possible to provision authentication tokens for other users via the REST API. To do, so the requesting user must have the `users.grant_token` permission assigned. While all users have inherent permission by default to create their own tokens, this permission is required to enable the creation of tokens for other users.
|
It is possible to provision authentication tokens for other users via the REST API. To do, so the requesting user must have the `users.grant_token` permission assigned. While all users have inherent permission by default to create their own tokens, this permission is required to enable the creation of tokens for other users.
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ For example, you might create a NetBox webhook to [trigger a Slack message](http
|
||||||
* HTTP method: `POST`
|
* HTTP method: `POST`
|
||||||
* URL: Slack incoming webhook URL
|
* URL: Slack incoming webhook URL
|
||||||
* HTTP content type: `application/json`
|
* HTTP content type: `application/json`
|
||||||
* Body template: `{"text": "IP address {{ data['address'] }} was created by {{ request.user }}!"}`
|
* Body template: `{"text": "IP address {{ data['address'] }} was created by {{ username }}!"}`
|
||||||
|
|
||||||
### Available Context
|
### Available Context
|
||||||
|
|
||||||
|
|
@ -30,21 +30,16 @@ The following data is available as context for Jinja2 templates:
|
||||||
* `request.id` - The UUID associated with the request
|
* `request.id` - The UUID associated with the request
|
||||||
* `request.method` - The HTTP method (e.g. `GET` or `POST`)
|
* `request.method` - The HTTP method (e.g. `GET` or `POST`)
|
||||||
* `request.path` - The URL path (ex: `/dcim/sites/123/edit/`)
|
* `request.path` - The URL path (ex: `/dcim/sites/123/edit/`)
|
||||||
* `request.path_info` - The URL path below the application script prefix
|
|
||||||
* `request.GET` - The query parameters included in the request
|
|
||||||
* `request.user` - The name of the authenticated user who made the request (if available)
|
* `request.user` - The name of the authenticated user who made the request (if available)
|
||||||
* `data` - A detailed representation of the object in its current state. This is typically equivalent to the model's representation in NetBox's REST API.
|
* `data` - A detailed representation of the object in its current state. This is typically equivalent to the model's representation in NetBox's REST API.
|
||||||
* `snapshots` - Minimal "snapshots" of the object state both before and after the change was made; provided as a dictionary with keys named `prechange` and `postchange`. These are not as extensive as the fully serialized representation, but contain enough information to convey what has changed.
|
* `snapshots` - Minimal "snapshots" of the object state both before and after the change was made; provided as a dictionary with keys named `prechange` and `postchange`. These are not as extensive as the fully serialized representation, but contain enough information to convey what has changed.
|
||||||
|
* ⚠️ `request_id` - The unique request ID. This may be used to correlate multiple changes associated with a single request.
|
||||||
|
* ⚠️ `username` - The name of the user account associated with the change.
|
||||||
|
|
||||||
### Sanitizing Header Values
|
!!! warning "Deprecation of legacy keys"
|
||||||
|
The `request_id` and `username` keys in the webhook payload above are deprecated and should no longer be used. Support for them will be removed in NetBox v4.7.0.
|
||||||
|
|
||||||
When rendering the `additional_headers` field, a `header_safe` filter is made available for sanitizing a value for safe inclusion in a raw HTTP header. It strips newlines and other control characters from the rendered value, preventing HTTP header (CR/LF) injection.
|
Use `request.user` and `request.id` from the `request` object included in the callback context instead.
|
||||||
|
|
||||||
Whenever a header value incorporates data which may be influenced by other users (such as an object's attributes), pass it through this filter to avoid smuggling of additional headers. For example:
|
|
||||||
|
|
||||||
```
|
|
||||||
X-Object-Name: {{ data.name | header_safe }}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Default Request Body
|
### Default Request Body
|
||||||
|
|
||||||
|
|
@ -55,6 +50,8 @@ If no body template is specified, the request body will be populated with a JSON
|
||||||
"event": "created",
|
"event": "created",
|
||||||
"timestamp": "2026-03-06T15:11:23.503186+00:00",
|
"timestamp": "2026-03-06T15:11:23.503186+00:00",
|
||||||
"object_type": "dcim.site",
|
"object_type": "dcim.site",
|
||||||
|
"username": "jstretch",
|
||||||
|
"request_id": "17af32f0-852a-46ca-a7d4-33ecd0c13de6",
|
||||||
"data": {
|
"data": {
|
||||||
"id": 4,
|
"id": 4,
|
||||||
"url": "/api/dcim/sites/4/",
|
"url": "/api/dcim/sites/4/",
|
||||||
|
|
|
||||||
|
|
@ -79,5 +79,7 @@ NetBox is built on the [Django](https://djangoproject.com/) Python framework and
|
||||||
| HTTP service | nginx or Apache |
|
| HTTP service | nginx or Apache |
|
||||||
| WSGI service | gunicorn or uWSGI |
|
| WSGI service | gunicorn or uWSGI |
|
||||||
| Application | Django/Python |
|
| Application | Django/Python |
|
||||||
| Database | PostgreSQL 15+ |
|
| Database | PostgreSQL 14+ [^1] |
|
||||||
| Task queuing | Redis/django-rq |
|
| Task queuing | Redis/django-rq |
|
||||||
|
|
||||||
|
[^1]: Support for PostgreSQL 14 is deprecated and will be removed in NetBox v4.7. PostgreSQL 15 or later will be required.
|
||||||
|
|
|
||||||
|
|
@ -28,10 +28,6 @@ The interval (in minutes) at which a scheduled job should re-execute.
|
||||||
|
|
||||||
The date and time at which the job completed (if complete).
|
The date and time at which the job completed (if complete).
|
||||||
|
|
||||||
### Execution Time
|
|
||||||
|
|
||||||
The amount of time the job spent executing, calculated as the difference between its start and completion times. This is populated only once a started job has completed.
|
|
||||||
|
|
||||||
### User
|
### User
|
||||||
|
|
||||||
The user who created the job.
|
The user who created the job.
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,8 @@ The cable's operational status. Choices include:
|
||||||
|
|
||||||
### Profile
|
### Profile
|
||||||
|
|
||||||
|
!!! note "This field was introduced in NetBox v4.5."
|
||||||
|
|
||||||
The profile to which the cable conforms. The profile determines the mapping of termination between the two ends and enables logical tracing across complex connections, such as breakout cables. Supported profiles are listed below.
|
The profile to which the cable conforms. The profile determines the mapping of termination between the two ends and enables logical tracing across complex connections, such as breakout cables. Supported profiles are listed below.
|
||||||
|
|
||||||
* Straight (single position)
|
* Straight (single position)
|
||||||
|
|
@ -32,9 +34,7 @@ The profile to which the cable conforms. The profile determines the mapping of t
|
||||||
|
|
||||||
A single-position cable is allowed only one termination point at each end. There is no limit to the number of terminations a multi-position cable may have. Each end of a cable must have the same number of terminations, unless connected to a pass-through port or to a circuit termination.
|
A single-position cable is allowed only one termination point at each end. There is no limit to the number of terminations a multi-position cable may have. Each end of a cable must have the same number of terminations, unless connected to a pass-through port or to a circuit termination.
|
||||||
|
|
||||||
The assignment of a cable profile is optional. If no profile is assigned, legacy tracing behavior will be preserved. Note that a cable's profile is what maps each termination to a connector and position: a cable carrying multiple terminations on an end but having no profile assigned is permitted, but NetBox cannot map its positions across the cable. Assign a profile to model a breakout cable whose individual positions must be traced.
|
The assignment of a cable profile is optional. If no profile is assigned, legacy tracing behavior will be preserved.
|
||||||
|
|
||||||
When creating cables in bulk, each side accepts a comma-separated list of termination names, along with either a single parent device (or power panel) shared by all of them or one parent per name. Terminations are assigned to connectors in the order given, so the order of these lists determines how the cable is wired.
|
|
||||||
|
|
||||||
### Type
|
### Type
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,37 +0,0 @@
|
||||||
# Cooling Feed
|
|
||||||
|
|
||||||
A cooling feed represents a coolant loop delivered from a [cooling source](./coolingsource.md) to a particular rack or coolant distribution unit (CDU). The [cooling intakes](./coolingintake.md) a feed supplies are derived from the devices installed in the rack it serves, rather than referenced explicitly.
|
|
||||||
|
|
||||||
A single feed represents the entire loop, covering both the supply (cold) and return (warm) paths.
|
|
||||||
|
|
||||||
!!! tip
|
|
||||||
In-rack cooling equipment — coolant distribution units (CDUs), manifolds, and rear-door heat exchangers (RDHx) — is modeled as an ordinary (typically zero-U) [device](./device.md) installed in the rack. The device's make and model come from its [device type](./devicetype.md), and a [cooling intake](./coolingintake.md) component connects it to cooling. The feed serving such a device is derived from its rack.
|
|
||||||
|
|
||||||
## Fields
|
|
||||||
|
|
||||||
### Cooling Source
|
|
||||||
|
|
||||||
The [cooling source](./coolingsource.md) which supplies this feed.
|
|
||||||
|
|
||||||
### Rack
|
|
||||||
|
|
||||||
The [rack](./rack.md) which this feed serves (optional).
|
|
||||||
|
|
||||||
### Name
|
|
||||||
|
|
||||||
The feed's name or identifier. Must be unique to the assigned cooling source.
|
|
||||||
|
|
||||||
### Status
|
|
||||||
|
|
||||||
The feed's operational status.
|
|
||||||
|
|
||||||
!!! tip
|
|
||||||
Additional statuses may be defined by setting `CoolingFeed.status` under the [`FIELD_CHOICES`](../../configuration/data-validation.md#field_choices) configuration parameter.
|
|
||||||
|
|
||||||
### Cooling Capacity
|
|
||||||
|
|
||||||
The heat-removal capacity of the feed, in kilowatts (kW).
|
|
||||||
|
|
||||||
### Maximum Flow
|
|
||||||
|
|
||||||
The maximum rate of coolant flow supported by the feed, expressed as a numeric value with a selectable unit (liters per minute, cubic meters per hour, or gallons per minute). Must be a positive, non-zero value in the selected unit, or left blank.
|
|
||||||
|
|
@ -1,40 +0,0 @@
|
||||||
# Cooling Intakes
|
|
||||||
|
|
||||||
A cooling intake is a device component which consumes coolant, such as a server cold-plate inlet or a coolant distribution unit (CDU) intake. It **receives** coolant from the cold, supply side of a loop (see [cooling](../../features/cooling.md) for the overall flow model). A cooling intake optionally references the upstream [cooling outflow](./coolingoutflow.md) which supplies it.
|
|
||||||
|
|
||||||
!!! tip
|
|
||||||
Like most device components, cooling intakes are instantiated automatically from [cooling intake templates](./coolingintaketemplate.md) assigned to the selected device type when a device is created.
|
|
||||||
|
|
||||||
## Fields
|
|
||||||
|
|
||||||
### Device
|
|
||||||
|
|
||||||
The device to which this cooling intake belongs.
|
|
||||||
|
|
||||||
### Module
|
|
||||||
|
|
||||||
The installed module within the assigned device to which this cooling intake belongs (optional).
|
|
||||||
|
|
||||||
### Name
|
|
||||||
|
|
||||||
The name of the cooling intake. Must be unique to the parent device.
|
|
||||||
|
|
||||||
### Label
|
|
||||||
|
|
||||||
An alternative physical label identifying the cooling intake.
|
|
||||||
|
|
||||||
### Connector Type
|
|
||||||
|
|
||||||
The physical coolant connector type (e.g. UQD, UQDB, QDC, camlock, or threaded NPT/BSP).
|
|
||||||
|
|
||||||
### Diameter
|
|
||||||
|
|
||||||
The connector diameter, expressed as a numeric value with a selectable unit (millimeters, centimeters, or inches). Must be a positive, non-zero value in the selected unit, or left blank.
|
|
||||||
|
|
||||||
### Maximum Flow
|
|
||||||
|
|
||||||
The maximum coolant flow rate this port supports, expressed as a numeric value with a selectable unit (liters per minute, cubic meters per hour, or gallons per minute). Must be a positive, non-zero value in the selected unit, or left blank.
|
|
||||||
|
|
||||||
### Cooling Outflow
|
|
||||||
|
|
||||||
The upstream [cooling outflow](./coolingoutflow.md) which supplies this intake (optional).
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
# Cooling Intake Templates
|
|
||||||
|
|
||||||
A template for a cooling intake that will be created on all instantiations of the parent device type. See the [cooling intake](./coolingintake.md) documentation for more detail.
|
|
||||||
|
|
@ -1,38 +0,0 @@
|
||||||
# Cooling Outflows
|
|
||||||
|
|
||||||
A cooling outflow is a device component which delivers coolant to a downstream [cooling intake](./coolingintake.md), and generally represents an outlet on a coolant distribution unit (CDU) or manifold. A cooling outflow may optionally be associated with an upstream cooling intake on the same device for path tracing.
|
|
||||||
|
|
||||||
A cooling outflow is a **supply** point on the cold, coolant-distribution side of a loop: it passes coolant onward to downstream equipment. It does **not** represent the return of warmed coolant back to the cooling source. The return path is not modeled per-component; instead, a single [cooling feed](./coolingfeed.md) represents the entire loop, covering both the supply (cold) and return (warm) paths.
|
|
||||||
|
|
||||||
!!! tip
|
|
||||||
Like most device components, cooling outflows are instantiated automatically from [cooling outflow templates](./coolingoutflowtemplate.md) assigned to the selected device type when a device is created.
|
|
||||||
|
|
||||||
## Fields
|
|
||||||
|
|
||||||
### Device
|
|
||||||
|
|
||||||
The device to which this cooling outflow belongs.
|
|
||||||
|
|
||||||
### Module
|
|
||||||
|
|
||||||
The installed module within the assigned device to which this cooling outflow belongs (optional).
|
|
||||||
|
|
||||||
### Name
|
|
||||||
|
|
||||||
The name of the cooling outflow. Must be unique to the parent device.
|
|
||||||
|
|
||||||
### Label
|
|
||||||
|
|
||||||
An alternative physical label identifying the cooling outflow.
|
|
||||||
|
|
||||||
### Connector Type
|
|
||||||
|
|
||||||
The physical coolant connector type (e.g. UQD, UQDB, QDC, camlock, or threaded NPT/BSP).
|
|
||||||
|
|
||||||
### Diameter
|
|
||||||
|
|
||||||
The connector diameter, expressed as a numeric value with a selectable unit (millimeters, centimeters, or inches). Must be a positive, non-zero value in the selected unit, or left blank.
|
|
||||||
|
|
||||||
### Cooling Intake
|
|
||||||
|
|
||||||
The upstream [cooling intake](./coolingintake.md) on the same device which feeds this outlet (optional).
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
# Cooling Outflow Templates
|
|
||||||
|
|
||||||
A template for a cooling outflow that will be created on all instantiations of the parent device type. See the [cooling outflow](./coolingoutflow.md) documentation for more detail.
|
|
||||||
|
|
@ -1,36 +0,0 @@
|
||||||
# Cooling Source
|
|
||||||
|
|
||||||
A cooling source represents a facility-level source of cooling, such as a chiller, cooling tower, or dry cooler. It serves as the upstream origin for one or more [cooling feeds](./coolingfeed.md) which distribute coolant to racks and devices. A cooling source is not modeled as a device; it represents external facility plant.
|
|
||||||
|
|
||||||
## Fields
|
|
||||||
|
|
||||||
### Site
|
|
||||||
|
|
||||||
The [site](./site.md) at which the cooling source is located.
|
|
||||||
|
|
||||||
### Location
|
|
||||||
|
|
||||||
The [location](./location.md) within the site where the cooling source resides (optional).
|
|
||||||
|
|
||||||
### Name
|
|
||||||
|
|
||||||
The cooling source's name or identifier. Must be unique to the assigned site.
|
|
||||||
|
|
||||||
### Type
|
|
||||||
|
|
||||||
The type of cooling plant (e.g. chiller, cooling tower, dry cooler, CRAC, or CRAH).
|
|
||||||
|
|
||||||
### Status
|
|
||||||
|
|
||||||
The operational status of the cooling source.
|
|
||||||
|
|
||||||
!!! tip
|
|
||||||
Additional statuses may be defined by setting `CoolingSource.status` under the [`FIELD_CHOICES`](../../configuration/data-validation.md#field_choices) configuration parameter.
|
|
||||||
|
|
||||||
### Fluid Type
|
|
||||||
|
|
||||||
The coolant used by the source (e.g. water, water/glycol, dielectric fluid, or refrigerant).
|
|
||||||
|
|
||||||
### Cooling Capacity
|
|
||||||
|
|
||||||
The total heat-removal capacity of the source, expressed in kilowatts (kW).
|
|
||||||
|
|
@ -30,10 +30,6 @@ The hardware [device type](./devicetype.md) which defines the device's make & mo
|
||||||
|
|
||||||
The direction in which air circulates through the device chassis for cooling.
|
The direction in which air circulates through the device chassis for cooling.
|
||||||
|
|
||||||
### Cooling Method
|
|
||||||
|
|
||||||
The cooling method employed by the device (air, liquid, hybrid, or immersion). If not set, this is inherited from the assigned [device type](./devicetype.md) when the device is created.
|
|
||||||
|
|
||||||
### Serial Number
|
### Serial Number
|
||||||
|
|
||||||
The unique physical serial number assigned to this device by its manufacturer.
|
The unique physical serial number assigned to this device by its manufacturer.
|
||||||
|
|
|
||||||
|
|
@ -57,18 +57,10 @@ Indicates whether this is a parent type (capable of housing child devices), a ch
|
||||||
|
|
||||||
The default direction in which airflow circulates within the device chassis. This may be configured differently for instantiated devices (e.g. because of different fan modules).
|
The default direction in which airflow circulates within the device chassis. This may be configured differently for instantiated devices (e.g. because of different fan modules).
|
||||||
|
|
||||||
### Cooling Method
|
|
||||||
|
|
||||||
The default cooling method employed by devices of this type (air, liquid, hybrid, or immersion). Instantiated devices inherit this value unless overridden.
|
|
||||||
|
|
||||||
### Weight
|
### Weight
|
||||||
|
|
||||||
The numeric weight of the device, including a unit designation (e.g. 10 kilograms or 20 pounds).
|
The numeric weight of the device, including a unit designation (e.g. 10 kilograms or 20 pounds).
|
||||||
|
|
||||||
### End of Life
|
|
||||||
|
|
||||||
The date after which this device type is no longer supported by its manufacturer. This can be used to identify devices approaching or past their support horizon to aid in hardware lifecycle planning.
|
|
||||||
|
|
||||||
### Front & Rear Images
|
### Front & Rear Images
|
||||||
|
|
||||||
Users can upload illustrations of the device's front and rear panels. If present, these will be used to render the device in [rack](./rack.md) elevation diagrams.
|
Users can upload illustrations of the device's front and rear panels. If present, these will be used to render the device in [rack](./rack.md) elevation diagrams.
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
# Front Ports
|
# Front Ports
|
||||||
|
|
||||||
Front ports are pass-through ports which represent physical cable connections that comprise part of a longer path. For example, the ports on the front face of a UTP patch panel would be modeled in NetBox as front ports. Each port is assigned a physical type, and must be mapped to one or more [rear port](./rearport.md) positions on the same device. A single rear port may be mapped to multiple front ports, using numeric positions to annotate the specific alignment of each.
|
Front ports are pass-through ports which represent physical cable connections that comprise part of a longer path. For example, the ports on the front face of a UTP patch panel would be modeled in NetBox as front ports. Each port is assigned a physical type, and must be mapped to a specific [rear port](./rearport.md) on the same device. A single rear port may be mapped to multiple front ports, using numeric positions to annotate the specific alignment of each.
|
||||||
|
|
||||||
!!! tip
|
!!! tip
|
||||||
Like most device components, front ports are instantiated automatically from [front port templates](./frontporttemplate.md) assigned to the selected device type when a device is created.
|
Like most device components, front ports are instantiated automatically from [front port templates](./frontporttemplate.md) assigned to the selected device type when a device is created.
|
||||||
|
|
@ -27,16 +27,12 @@ An alternative physical label identifying the port.
|
||||||
|
|
||||||
The port's termination type.
|
The port's termination type.
|
||||||
|
|
||||||
### Positions
|
|
||||||
|
|
||||||
The number of [rear port](./rearport.md) positions to which this front port maps. For a front port which passes through to a single rear port position, set this to `1`.
|
|
||||||
|
|
||||||
### Rear Ports
|
### Rear Ports
|
||||||
|
|
||||||
The rear port and position to which this front port maps.
|
The rear port and position to which this front port maps.
|
||||||
|
|
||||||
!!! tip
|
!!! tip
|
||||||
When creating multiple front ports using a patterned name (e.g. `Port [1-12]`), select one rear port-position mapping for every position of every front port being created. For example, 12 front ports with two positions each requires 24 mappings, which are assigned to the generated ports in order.
|
When creating multiple front ports using a patterned name (e.g. `Port [1-12]`), you may select the equivalent number of rear port-position mappings from the list.
|
||||||
|
|
||||||
### Color
|
### Color
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -28,19 +28,11 @@ An alternative physical label identifying the interface.
|
||||||
|
|
||||||
### Type
|
### Type
|
||||||
|
|
||||||
The type of interface. Interfaces may be physical or virtual in nature, but only physical interfaces may be connected via cables. The generic **channel** type identifies a [channelized subinterface](#channel-id) bound to a parent interface when its specific transceiver type is not relevant; a channel subinterface may instead keep its own specific physical type (e.g. directly declaring a channel as 10GBASE-SR) to record the actual transceiver in use.
|
The type of interface. Interfaces may be physical or virtual in nature, but only physical interfaces may be connected via cables.
|
||||||
|
|
||||||
!!! note
|
!!! note
|
||||||
The interface type refers to the physical termination or port on the device. Interfaces which employ a removable optic or similar transceiver should be defined to represent the type of transceiver in use, irrespective of the physical termination to that transceiver.
|
The interface type refers to the physical termination or port on the device. Interfaces which employ a removable optic or similar transceiver should be defined to represent the type of transceiver in use, irrespective of the physical termination to that transceiver.
|
||||||
|
|
||||||
### Channels
|
|
||||||
|
|
||||||
!!! info "This field was added in NetBox v4.7."
|
|
||||||
|
|
||||||
For a channelized (breakout) interface, the number of physical channels into which the interface is divided. For example, a 40GE interface broken out into four 10GE channels would have `channels` set to four. Each channel is modeled as a channel subinterface bound to this interface via its [channel ID](#channel-id).
|
|
||||||
|
|
||||||
A single physical cable terminates to the channelized (parent) interface, occupying one connector shared by all of its channels; NetBox traces a distinct cable path for each channel subinterface. Only one layer of channelization is supported: an interface cannot be both channelized and itself bound to a channel.
|
|
||||||
|
|
||||||
### Speed
|
### Speed
|
||||||
|
|
||||||
The operating speed, in kilobits per second (kbps).
|
The operating speed, in kilobits per second (kbps).
|
||||||
|
|
@ -57,7 +49,7 @@ The [virtual routing and forwarding](../ipam/vrf.md) instance to which this inte
|
||||||
|
|
||||||
The [MAC address](./macaddress.md) assigned to this interface which is designated as its primary.
|
The [MAC address](./macaddress.md) assigned to this interface which is designated as its primary.
|
||||||
|
|
||||||
!!! note "MAC address is a property"
|
!!! note "Changed in NetBox v4.2"
|
||||||
The MAC address of an interface (formerly a concrete database field) is available as a property, `mac_address`, which reflects the value of the primary linked [MAC address](./macaddress.md) object.
|
The MAC address of an interface (formerly a concrete database field) is available as a property, `mac_address`, which reflects the value of the primary linked [MAC address](./macaddress.md) object.
|
||||||
|
|
||||||
### WWN
|
### WWN
|
||||||
|
|
@ -86,17 +78,10 @@ If selected, this component will be treated as if a cable has been connected.
|
||||||
|
|
||||||
### Parent Interface
|
### Parent Interface
|
||||||
|
|
||||||
Virtual interfaces can be bound to a physical parent interface. This is helpful for modeling virtual interfaces which employ encapsulation on a physical interface, such as an 802.1Q VLAN-tagged subinterface. A channel subinterface is likewise bound to its [channelized](#channels) parent interface, whether it uses the generic **channel** type or its own specific physical type.
|
Virtual interfaces can be bound to a physical parent interface. This is helpful for modeling virtual interfaces which employ encapsulation on a physical interface, such as an 802.1Q VLAN-tagged subinterface.
|
||||||
|
|
||||||
!!! note
|
!!! note
|
||||||
An interface with one or more child interfaces assigned cannot be deleted until all its child interfaces have been deleted or reassigned. Renaming a channelized interface updates the names of any channel subinterfaces which follow the `<name>:<channel ID>` convention, to keep their names consistent with their new parent, unless the resulting name is already in use by another interface on the device or would exceed the maximum length of the name field (in either case, that subinterface's name is left unchanged).
|
An interface with one or more child interfaces assigned cannot be deleted until all its child interfaces have been deleted or reassigned.
|
||||||
|
|
||||||
### Channel ID
|
|
||||||
|
|
||||||
The numeric channel on a [channelized](#channels) parent interface to which this subinterface is bound, identifying it as a channel subinterface. This may be set on the generic **channel** type, or on any other physical interface type (e.g. to record the specific transceiver used on that channel) — but not on a virtual or wireless interface. The channel ID must fall within the range of channels provided by the parent (e.g. one through four for a parent with four channels). A channel subinterface derives its cable connection from the parent's; it cannot be cabled directly.
|
|
||||||
|
|
||||||
!!! note "Channel IDs are one-indexed"
|
|
||||||
Channel IDs increment starting at one, even for interfaces with a zero-based identifier. This ensures that each subinterface maps cleanly to the profile of an attached cable.
|
|
||||||
|
|
||||||
### Bridged Interface
|
### Bridged Interface
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,20 +4,6 @@ A module is a field-replaceable hardware component installed within a device whi
|
||||||
|
|
||||||
Similar to devices, modules are instantiated from [module types](./moduletype.md), and any components associated with the module type are automatically instantiated on the new model. Each module must be installed within a [module bay](./modulebay.md) on a [device](./device.md), and each module bay may have only one module installed in it.
|
Similar to devices, modules are instantiated from [module types](./moduletype.md), and any components associated with the module type are automatically instantiated on the new model. Each module must be installed within a [module bay](./modulebay.md) on a [device](./device.md), and each module bay may have only one module installed in it.
|
||||||
|
|
||||||
## Moving Modules
|
|
||||||
|
|
||||||
!!! info "This feature was introduced in NetBox v4.7."
|
|
||||||
|
|
||||||
An installed module can be moved to a different module bay after creation. The destination bay must be enabled and unoccupied. Moving a module relocates its entire subtree: the components installed by the module, the module bays belonging to it, and any child modules installed within those bays.
|
|
||||||
|
|
||||||
Component names, labels, and module bay positions derived from the module type's templates (for example, names containing `{module}`) are re-resolved for the destination bay. A component is renamed only when its current name matches exactly one of the module type's templates as resolved for the source bay; components whose names do not match any template resolution (including manually renamed components) are preserved as-is. All resulting names are validated against the destination device before the move is applied. A move is rejected when a template-derived name, label, or position would exceed the destination field's maximum length. A move is also rejected when a component's current value matched a template for the source bay but that template cannot be resolved for the destination bay's nesting depth.
|
|
||||||
|
|
||||||
Moving a module to a different device is supported only when the moved components carry no active topology or device-scoped configuration. A cross-device move is rejected while any moved component is cabled or marked as connected, has attached inventory items, or any moved interface has IP addresses, FHRP group assignments, tunnel terminations, L2VPN terminations, virtual circuit terminations, wireless links, wireless LAN assignments, VLANs (untagged, tagged, or Q-in-Q service), a VLAN translation policy, VDC assignments, or a VRF. A parent, bridge, LAG, power outlet to power port, cooling outflow to cooling intake, or front/rear port mapping relation crossing the moved module's boundary in either direction also blocks the move. MAC addresses move together with their interfaces.
|
|
||||||
|
|
||||||
A cooling intake's upstream [cooling outflow](./coolingoutflow.md) is not device-scoped — an intake is routinely supplied by an outflow on another device, such as a CDU — so that assignment is preserved across a cross-device move rather than blocking it.
|
|
||||||
|
|
||||||
Via the REST API, a module can be moved by patching only `module_bay`; the device is derived from the target bay. Changing a module's type and moving it must be performed as separate operations.
|
|
||||||
|
|
||||||
## Fields
|
## Fields
|
||||||
|
|
||||||
### Device
|
### Device
|
||||||
|
|
@ -54,7 +40,3 @@ Controls whether templates module type components are automatically added when c
|
||||||
### Adopt Components
|
### Adopt Components
|
||||||
|
|
||||||
Controls whether pre-existing components assigned to the device with the same names as components that would be created automatically will be assigned to the new module.
|
Controls whether pre-existing components assigned to the device with the same names as components that would be created automatically will be assigned to the new module.
|
||||||
|
|
||||||
## Bay Type Compatibility
|
|
||||||
|
|
||||||
If the module bay has [bay types](./modulebaytype.md) assigned and the module's type also has bay types assigned, NetBox verifies that the two sets share at least one type in common. An installation that fails this check will be rejected. The `is_bay_compatible` flag is exposed in the REST API to indicate compatibility status without performing a write.
|
|
||||||
|
|
|
||||||
|
|
@ -30,10 +30,6 @@ An alternative physical label identifying the module bay.
|
||||||
|
|
||||||
The numeric position in which this module bay is situated. For example, this would be the number assigned to a slot within a chassis-based switch.
|
The numeric position in which this module bay is situated. For example, this would be the number assigned to a slot within a chassis-based switch.
|
||||||
|
|
||||||
### Bay Types
|
|
||||||
|
|
||||||
Zero or more [module bay types](./modulebaytype.md) assigned to this bay. When at least one bay type is set, only module types that share a common bay type may be installed. Leave empty to allow any module type.
|
|
||||||
|
|
||||||
### Enabled
|
### Enabled
|
||||||
|
|
||||||
Whether this module bay is enabled. Disabled module bays are not available for installation.
|
Whether this module bay is enabled. Disabled module bays are not available for installation.
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,3 @@
|
||||||
# Module Bay Templates
|
# Module Bay Templates
|
||||||
|
|
||||||
A template for a module bay that will be created on all instantiations of the parent device type. See the [module bay](./modulebay.md) documentation for more detail.
|
A template for a module bay that will be created on all instantiations of the parent device type. See the [module bay](./modulebay.md) documentation for more detail.
|
||||||
|
|
||||||
[Bay types](./modulebaytype.md) assigned to a module bay template are copied to each instantiated module bay, so constraints defined on the device type propagate automatically to all devices of that type.
|
|
||||||
|
|
||||||
Bay types are importable as part of a device type's or module type's YAML definition (`module-bays[].module_bay_types`), referenced by name. They are included when a device type is exported; a module type's exported definition omits its module bays entirely, so bay types are not carried through it. A referenced name is resolved against bay types belonging to the parent type's own manufacturer or with no manufacturer set (global); a name may match both, since a bay type's uniqueness is scoped to `(manufacturer, name)` rather than name alone, in which case the manufacturer-specific type takes precedence. A name matching only some other manufacturer's bay type is rejected rather than resolved to it.
|
|
||||||
|
|
|
||||||
|
|
@ -1,37 +0,0 @@
|
||||||
# Module Bay Types
|
|
||||||
|
|
||||||
!!! info "This feature was introduced in NetBox v4.7."
|
|
||||||
|
|
||||||
Module bay types are user-defined labels that can be assigned to [module bays](./modulebay.md) and [module types](./moduletype.md) to restrict which modules may be installed into which bays. This is useful for modeling chassis hardware where not every bay accepts every type of line card.
|
|
||||||
|
|
||||||
When **both** a module bay and the module type being installed have at least one bay type assigned, NetBox will check for a non-empty intersection. If the two sets share no bay types in common, the installation will be rejected as incompatible.
|
|
||||||
|
|
||||||
If either the bay or the module type has **no bay types assigned**, the constraint is not applied and any module type may be installed — this preserves backwards compatibility with existing data.
|
|
||||||
|
|
||||||
!!! tip
|
|
||||||
Bay types function as an allow-list: assign the same type to a bay and to the module types that fit it, and leave the type unset on bays or module types where no restriction is needed.
|
|
||||||
|
|
||||||
!!! note "GraphQL naming"
|
|
||||||
In the GraphQL API, the type for the `ModuleBay` *component* is named `ModuleBayType` (following the project's `<Model>Type` suffix convention), while the type for the `ModuleBayType` *model* is named `ModuleBayTypeType`. This is an unavoidable consequence of the naming convention colliding with this model's name.
|
|
||||||
|
|
||||||
## Fields
|
|
||||||
|
|
||||||
### Name
|
|
||||||
|
|
||||||
A unique human-readable name for the bay type (e.g. `LC Line Card`, `Power Supply`, `Fan Tray`).
|
|
||||||
|
|
||||||
### Slug
|
|
||||||
|
|
||||||
A URL-friendly identifier derived from the name.
|
|
||||||
|
|
||||||
### Manufacturer
|
|
||||||
|
|
||||||
An optional [manufacturer](./manufacturer.md) associated with this bay type. Useful when a vendor uses proprietary slot designations.
|
|
||||||
|
|
||||||
### Description
|
|
||||||
|
|
||||||
A brief description of the bay type.
|
|
||||||
|
|
||||||
### Comments
|
|
||||||
|
|
||||||
Free-form Markdown-supported notes.
|
|
||||||
|
|
@ -75,24 +75,10 @@ The numeric weight of the module, including a unit designation (e.g. 3 kilograms
|
||||||
|
|
||||||
The direction in which air circulates through the device chassis for cooling.
|
The direction in which air circulates through the device chassis for cooling.
|
||||||
|
|
||||||
### Cooling Method
|
|
||||||
|
|
||||||
The cooling method employed by modules of this type (air, liquid, hybrid, or immersion). This is useful for liquid-cooled modules such as direct-to-chip accelerator (OAM) modules or liquid-cooled line cards.
|
|
||||||
|
|
||||||
### End of Life
|
|
||||||
|
|
||||||
The date after which this module type is no longer supported by its manufacturer. This can be used to identify modules approaching or past their support horizon to aid in hardware lifecycle planning.
|
|
||||||
|
|
||||||
### Profile
|
### Profile
|
||||||
|
|
||||||
The assigned [profile](./moduletypeprofile.md) for the type of module. Profiles can be used to classify module types by function (e.g. power supply, hard disk, etc.), and they support the addition of user-configurable attributes on module types. The assignment of a module type to a profile is optional.
|
The assigned [profile](./moduletypeprofile.md) for the type of module. Profiles can be used to classify module types by function (e.g. power supply, hard disk, etc.), and they support the addition of user-configurable attributes on module types. The assignment of a module type to a profile is optional.
|
||||||
|
|
||||||
### Bay Types
|
|
||||||
|
|
||||||
Zero or more [module bay types](./modulebaytype.md) that this module type is compatible with. When at least one bay type is set, the module type may only be installed into bays that share a common type. Leave empty to allow installation into any bay.
|
|
||||||
|
|
||||||
Bay types are included, by name, in a module type's exported YAML definition, but are not currently importable back through it; re-importing an exported definition leaves this field unset.
|
|
||||||
|
|
||||||
### Attributes
|
### Attributes
|
||||||
|
|
||||||
Depending on the module type's assigned [profile](./moduletypeprofile.md) (if any), one or more user-defined attributes may be available to configure.
|
Depending on the module type's assigned [profile](./moduletypeprofile.md) (if any), one or more user-defined attributes may be available to configure.
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,8 @@ The assignment of platforms to devices and virtual machines is optional.
|
||||||
|
|
||||||
## Parent
|
## Parent
|
||||||
|
|
||||||
|
!!! "This field was introduced in NetBox v4.4."
|
||||||
|
|
||||||
The parent platform class to which this platform belongs (optional).
|
The parent platform class to which this platform belongs (optional).
|
||||||
|
|
||||||
### Name
|
### Name
|
||||||
|
|
|
||||||
|
|
@ -28,9 +28,6 @@ The rack's name or identifier. Must be unique to the rack's location, if assigne
|
||||||
|
|
||||||
The [physical type](./racktype.md) of this rack. The rack type defines physical attributes such as height and weight.
|
The [physical type](./racktype.md) of this rack. The rack type defines physical attributes such as height and weight.
|
||||||
|
|
||||||
!!! warning "Rack type assignment will become mandatory"
|
|
||||||
Beginning in NetBox v5.0, the assignment of a rack type will be required, and several physical attributes will be inferred from it rather than being set directly on the rack. See the note under [Physical Attributes](#physical-attributes) below.
|
|
||||||
|
|
||||||
### Status
|
### Status
|
||||||
|
|
||||||
Operational status.
|
Operational status.
|
||||||
|
|
@ -54,32 +51,5 @@ The unique physical serial number assigned to this rack.
|
||||||
|
|
||||||
A unique, locally-administered label used to identify hardware resources.
|
A unique, locally-administered label used to identify hardware resources.
|
||||||
|
|
||||||
### Cooling Capability
|
!!! note
|
||||||
|
Some additional fields pertaining to physical attributes such as height and weight can also be defined on each rack, but should generally be defined instead on the [rack type](./racktype.md).
|
||||||
Describes how the rack is able to cool the equipment installed in it, which indicates what kind of equipment it can accommodate:
|
|
||||||
|
|
||||||
- **Air-only**: The rack is cooled by airflow only; no coolant is delivered to it. Only air-cooled equipment can be installed.
|
|
||||||
- **Hybrid**: Coolant can be delivered to the rack (e.g. via a [cooling feed](./coolingfeed.md)), but it can also house air-cooled equipment. Suitable for mixed or hybrid deployments.
|
|
||||||
- **Liquid-only**: The rack is intended exclusively for liquid-cooled equipment (such as direct-to-chip or immersion systems) and does not provide adequate air cooling on its own.
|
|
||||||
|
|
||||||
This attribute documents the rack's intended use so that incompatible equipment—such as high-density liquid-cooled hardware in an air-only rack—can be identified. When the rack is assigned a [rack type](./racktype.md), this value is inherited from the rack type.
|
|
||||||
|
|
||||||
### Cooling Capacity
|
|
||||||
|
|
||||||
The rack's cooling capacity, expressed in kilowatts (kW). When the rack is assigned a [rack type](./racktype.md), this value is inherited from the rack type.
|
|
||||||
|
|
||||||
## Physical Attributes
|
|
||||||
|
|
||||||
Several physical attributes may be defined on each rack, including its width, height, outer dimensions, mounting depth, and weight. These should generally be defined on the [rack type](./racktype.md) assigned to the rack rather than on the rack itself.
|
|
||||||
|
|
||||||
!!! warning "Some rack fields are deprecated"
|
|
||||||
The following fields have been **deprecated** on the rack model and are planned for removal in NetBox v5.0:
|
|
||||||
|
|
||||||
* Form factor
|
|
||||||
* Width
|
|
||||||
* Outer width
|
|
||||||
* Outer height
|
|
||||||
* Outer depth
|
|
||||||
* Outer unit
|
|
||||||
|
|
||||||
In a future release, the values for these attributes will be inferred from the rack's assigned [rack type](./racktype.md), which will become a mandatory assignment. Users are strongly encouraged to define these attributes on a rack type and assign it to each rack. (Note that the U height, starting unit, descending units, and mounting depth fields will be retained on the rack model, as these may legitimately vary among individual racks of the same type.)
|
|
||||||
|
|
|
||||||
|
|
@ -54,14 +54,6 @@ The numeric weight of the rack, including a unit designation (e.g. 10 kilograms
|
||||||
|
|
||||||
The maximum total weight capacity for all installed devices, inclusive of the rack itself.
|
The maximum total weight capacity for all installed devices, inclusive of the rack itself.
|
||||||
|
|
||||||
### Cooling Capability
|
|
||||||
|
|
||||||
The rack design's coolant capability: air-only, hybrid, or liquid-only. Racks of this type inherit this value.
|
|
||||||
|
|
||||||
### Cooling Capacity
|
|
||||||
|
|
||||||
The rack design's cooling capacity, expressed in kilowatts (kW). Racks of this type inherit this value.
|
|
||||||
|
|
||||||
### Descending Units
|
### Descending Units
|
||||||
|
|
||||||
If selected, the rack's elevation will display unit 1 at the top of the rack. (Most racks use ascending numbering, with unit 1 assigned to the bottommost position.)
|
If selected, the rack's elevation will display unit 1 at the top of the rack. (Most racks use ascending numbering, with unit 1 assigned to the bottommost position.)
|
||||||
|
|
|
||||||
|
|
@ -12,10 +12,6 @@ Select the NetBox object type or types to which this custom field applies.
|
||||||
|
|
||||||
The raw field name. This will be used in the database and API, and should consist only of alphanumeric characters and underscores. (Use the `label` field to designate a human-friendly name for the custom field.)
|
The raw field name. This will be used in the database and API, and should consist only of alphanumeric characters and underscores. (Use the `label` field to designate a human-friendly name for the custom field.)
|
||||||
|
|
||||||
### Status
|
|
||||||
|
|
||||||
The field's lifecycle state: `active`, `provisioning`, or `deleting`. This is maintained by NetBox and cannot be set directly. A field is available for use only while active; see [field status](../../customization/custom-fields.md#field-status).
|
|
||||||
|
|
||||||
### Label
|
### Label
|
||||||
|
|
||||||
An optional human-friendly name for the custom field. If not defined, the field's `name` attribute will be used.
|
An optional human-friendly name for the custom field. If not defined, the field's `name` attribute will be used.
|
||||||
|
|
@ -113,10 +109,6 @@ Choice sets may optionally define colors for individual values. Colored choices
|
||||||
|
|
||||||
If enabled, values from this field will be automatically pre-populated when cloning existing objects.
|
If enabled, values from this field will be automatically pre-populated when cloning existing objects.
|
||||||
|
|
||||||
### Nulls First
|
|
||||||
|
|
||||||
When ordering objects by this custom field, controls whether objects with no value (null) are sorted before or after objects that have a value. This option is enabled by default.
|
|
||||||
|
|
||||||
### Minimum Value
|
### Minimum Value
|
||||||
|
|
||||||
For numeric custom fields only. The minimum valid value (optional).
|
For numeric custom fields only. The minimum valid value (optional).
|
||||||
|
|
|
||||||
|
|
@ -47,11 +47,6 @@ The type of action to take when the rule triggers. This must be one of the follo
|
||||||
* Custom script
|
* Custom script
|
||||||
* Notification
|
* Notification
|
||||||
|
|
||||||
!!! tip "Custom Action Types"
|
|
||||||
The above list includes only built-in action types. NetBox plugins can also [register their own custom action types](../../plugins/development/event-rule-actions.md).
|
|
||||||
|
|
||||||
If the plugin providing an event rule's action type is uninstalled or disabled, the event rule is not deleted, but it is marked as unavailable and will not run. It also cannot be saved -- even to edit an unrelated field -- until either the plugin is reinstalled or the action type is changed to a currently-available one.
|
|
||||||
|
|
||||||
### Action Data
|
### Action Data
|
||||||
|
|
||||||
An optional dictionary of JSON data to pass when executing the rule. This can be useful to include additional context data, e.g. when transmitting a webhook.
|
An optional dictionary of JSON data to pass when executing the rule. This can be useful to include additional context data, e.g. when transmitting a webhook.
|
||||||
|
|
|
||||||
|
|
@ -32,12 +32,7 @@ The events which will trigger the webhook. At least one event type must be selec
|
||||||
|
|
||||||
### URL
|
### URL
|
||||||
|
|
||||||
The URL to which the webhook HTTP request will be made. Must be `http://` or `https://`, though
|
The URL to which the webhook HTTP request will be made.
|
||||||
part or all of the value may be a Jinja2 template rendered at send time (e.g.
|
|
||||||
`http://{{ data.name }}.example.com/hook`, or `{{ data.custom_fields.callback_url }}` if the whole
|
|
||||||
URL comes from a template). A literal scheme is always validated as such, even if the rest of the
|
|
||||||
URL is templated; otherwise the value is checked only for valid Jinja2 syntax, since its rendered
|
|
||||||
value isn't known until the webhook actually fires.
|
|
||||||
|
|
||||||
### HTTP Method
|
### HTTP Method
|
||||||
|
|
||||||
|
|
@ -57,13 +52,6 @@ The content type to indicate in the outgoing HTTP request header. See [this list
|
||||||
|
|
||||||
Any additional header to include with the outgoing HTTP request. These should be defined in the format `Name: Value`, with each header on a separate line. Jinja2 templating is supported for this field.
|
Any additional header to include with the outgoing HTTP request. These should be defined in the format `Name: Value`, with each header on a separate line. Jinja2 templating is supported for this field.
|
||||||
|
|
||||||
!!! warning "Sanitize interpolated header values"
|
|
||||||
When interpolating data which may be influenced by other users (such as object attributes) into a header value, apply the `header_safe` filter to guard against HTTP header (CR/LF) injection. This filter strips newlines and other control characters which could otherwise be used to smuggle additional headers into the request. For example:
|
|
||||||
|
|
||||||
```
|
|
||||||
X-Object-Name: {{ data.name | header_safe }}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Body Template
|
### Body Template
|
||||||
|
|
||||||
Jinja2 template for a custom request body, if desired. If not defined, NetBox will populate the request body with a raw dump of the webhook context.
|
Jinja2 template for a custom request body, if desired. If not defined, NetBox will populate the request body with a raw dump of the webhook context.
|
||||||
|
|
@ -87,17 +75,6 @@ Controls whether validation of the receiver's SSL certificate is enforced when H
|
||||||
|
|
||||||
The file path to a particular certificate authority (CA) file to use when validating the receiver's SSL certificate (if not using the system defaults).
|
The file path to a particular certificate authority (CA) file to use when validating the receiver's SSL certificate (if not using the system defaults).
|
||||||
|
|
||||||
### Timeout
|
|
||||||
|
|
||||||
The maximum time (in seconds) to wait for a response from the receiver before the request is considered failed. If left blank, the global [`WEBHOOK_DEFAULT_TIMEOUT`](../../configuration/miscellaneous.md#webhook_default_timeout) configuration value is used.
|
|
||||||
|
|
||||||
The timeout must be less than [`RQ_DEFAULT_TIMEOUT`](../../configuration/miscellaneous.md#rq_default_timeout) (300 seconds by default), and NetBox will refuse to save a webhook which violates this. The background job timeout is a hard ceiling on how long a webhook request can run, so a value at or above it leaves no room for the request's own timeout to apply.
|
|
||||||
|
|
||||||
!!! note
|
|
||||||
Staying below the job timeout makes it *likely*, but does not guarantee, that the request times out on its own. The timeout is applied separately to establishing the connection and to waiting for data, rather than to the request as a whole, so a receiver which stalls at both stages — or which responds slowly but continuously — can still outlast the job timeout and be terminated by the worker instead.
|
|
||||||
|
|
||||||
When a request does time out, the failure is recorded by the `netbox.webhooks` logger and the background job is marked as failed.
|
|
||||||
|
|
||||||
## Context Data
|
## Context Data
|
||||||
|
|
||||||
The following context variables are available to the text and link templates.
|
The following context variables are available to the text and link templates.
|
||||||
|
|
@ -107,9 +84,10 @@ The following context variables are available to the text and link templates.
|
||||||
| `event` | The event type (`create`, `update`, or `delete`) |
|
| `event` | The event type (`create`, `update`, or `delete`) |
|
||||||
| `timestamp` | The time at which the event occurred |
|
| `timestamp` | The time at which the event occurred |
|
||||||
| `object_type` | The type of object impacted (`app_label.model_name`) |
|
| `object_type` | The type of object impacted (`app_label.model_name`) |
|
||||||
|
| `username` | The name of the user associated with the change |
|
||||||
|
| `request_id` | The unique request ID |
|
||||||
| `data` | A complete serialized representation of the object |
|
| `data` | A complete serialized representation of the object |
|
||||||
| `snapshots` | Pre- and post-change snapshots of the object |
|
| `snapshots` | Pre- and post-change snapshots of the object |
|
||||||
| `request` | Data about the triggering request (if available) |
|
|
||||||
|
|
||||||
!!! note
|
!!! warning "Deprecation of legacy fields"
|
||||||
The `request` variable is populated in the context only when the webhook is associated with a triggering request. It exposes `request.id` (the unique request ID) and `request.user` (the name of the user associated with the change), among other attributes.
|
The `request_id` and `username` fields in the webhook payload above are deprecated and should no longer be used. Support for them will be removed in NetBox v4.7.0. Use `request.user` and `request.id` from the `request` object included in the callback context instead. (Note that `request` is populated in the context only when the webhook is associated with a triggering request.)
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,8 @@ The IP range's operational status. Note that the status of a range does _not_ ha
|
||||||
|
|
||||||
### Mark Populated
|
### Mark Populated
|
||||||
|
|
||||||
|
!!! note "This field was added in NetBox v4.3."
|
||||||
|
|
||||||
If enabled, NetBox will treat this IP range as being fully populated when calculating available IP space. It will also prevent the creation of IP addresses which fall within the declared range (and assigned VRF, if any).
|
If enabled, NetBox will treat this IP range as being fully populated when calculating available IP space. It will also prevent the creation of IP addresses which fall within the declared range (and assigned VRF, if any).
|
||||||
|
|
||||||
### Mark Utilized
|
### Mark Utilized
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,9 @@ An application service represents a layer seven application available on a devic
|
||||||
|
|
||||||
To aid in the efficient creation of application services, users may opt to first create an [application service template](./servicetemplate.md) from which service definitions can be quickly replicated.
|
To aid in the efficient creation of application services, users may opt to first create an [application service template](./servicetemplate.md) from which service definitions can be quickly replicated.
|
||||||
|
|
||||||
!!! note "Naming"
|
!!! note "Changed in NetBox v4.4"
|
||||||
Application services are referred to simply as "services" in the name of the model and throughout NetBox's programmatic APIs. Only the UI uses the longer name, which better reflects their intended use.
|
|
||||||
|
Previously, application services were referred to simply as "services". The name has been changed in the UI to better reflect their intended use. There is no change to the name of the model or in any programmatic NetBox APIs.
|
||||||
|
|
||||||
## Fields
|
## Fields
|
||||||
|
|
||||||
|
|
@ -14,66 +15,22 @@ To aid in the efficient creation of application services, users may opt to first
|
||||||
The parent object to which the application service is assigned. This must be one of [Device](../dcim/device.md),
|
The parent object to which the application service is assigned. This must be one of [Device](../dcim/device.md),
|
||||||
[VirtualMachine](../virtualization/virtualmachine.md), or [FHRP Group](./fhrpgroup.md).
|
[VirtualMachine](../virtualization/virtualmachine.md), or [FHRP Group](./fhrpgroup.md).
|
||||||
|
|
||||||
|
!!! note "Changed in NetBox v4.3"
|
||||||
|
|
||||||
|
Previously, `parent` was a property that pointed to either a Device or Virtual Machine. With the capability to assign services to FHRP groups, this is a unified in a concrete field.
|
||||||
|
|
||||||
### Name
|
### Name
|
||||||
|
|
||||||
A service or protocol name.
|
A service or protocol name.
|
||||||
|
|
||||||
### Port Mappings
|
### Protocol
|
||||||
|
|
||||||
The protocols and ports on which the service runs. A service may expose the same port on multiple protocols — for example, DNS listening on both `tcp/53` and `udp/53`. In the UI, ports for a given protocol may be entered together using commas and/or hyphens (e.g. `80,8001-8003`).
|
The wire protocol on which the service runs. Choices include UDP, TCP, and SCTP.
|
||||||
|
|
||||||
In the REST and GraphQL APIs, port mappings are represented as a flat list of `protocol/port` strings — matching how they are stored:
|
### Ports
|
||||||
|
|
||||||
```json
|
One or more numeric ports to which the service is bound. Multiple ports can be expressed using commas and/or hyphens. For example, `80,8001-8003` specifies ports 80, 8001, 8002, and 8003.
|
||||||
[
|
|
||||||
"tcp/80",
|
|
||||||
"tcp/443",
|
|
||||||
"udp/53"
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
!!! note "Changed in NetBox v4.7"
|
|
||||||
|
|
||||||
The single-protocol `protocol` and `ports` fields have been replaced by the unified `port_mappings` field, which supports multiple protocols per service. For backward compatibility, the REST and GraphQL APIs still expose the legacy `protocol` and `ports` fields, and the REST API still accepts them on write as an alternative to `port_mappings`. They are populated for single-protocol services; a service with multiple protocols cannot be represented in the legacy format and returns `null` for both, while a service with no mappings returns `protocol: null` and `ports: []`. In other words, `ports: null` specifically signals "multiple protocols — read `port_mappings` instead." **These legacy fields are deprecated and will be removed in NetBox v5.0; use `port_mappings` instead.**
|
|
||||||
|
|
||||||
On write, `port_mappings` and the legacy `protocol`/`ports` fields may be submitted together only when they agree — as in a full-object round-trip that echoes back a read. A request whose legacy fields contradict `port_mappings` (for example, an edited `port_mappings` sent alongside the stale `protocol`/`ports` from the original read) is rejected as ambiguous; send `port_mappings` alone, or keep the legacy fields consistent with it.
|
|
||||||
|
|
||||||
At the ORM level (custom scripts and plugins), `protocol` and `ports` are now **read-only** properties derived from `port_mappings`. Assign `port_mappings` directly — e.g. `Service(parent=device, name='http', port_mappings=['tcp/80'])` — since passing `protocol=`/`ports=` to the model raises `TypeError` and setting `service.ports = [...]` raises `AttributeError`.
|
|
||||||
|
|
||||||
### Filtering by Port Mapping, Protocol, and Port
|
|
||||||
|
|
||||||
`port_mappings`, `protocol`, and `port` are all filtered against the `port_mappings` array. Each accepts multiple values (matching any of them), and `port` supports the usual numeric lookups:
|
|
||||||
|
|
||||||
| Parameter | Matches services having a mapping… |
|
|
||||||
|---|---|
|
|
||||||
| `?port_mappings=tcp/80` | that is exactly `tcp/80` |
|
|
||||||
| `?port_mappings__n=tcp/80` | *(negated)* that is exactly `tcp/80` |
|
|
||||||
| `?protocol=tcp` | whose protocol is TCP |
|
|
||||||
| `?protocol__n=tcp` | *(negated)* whose protocol is TCP |
|
|
||||||
| `?port=80` | whose port is 80 |
|
|
||||||
| `?port__n=80` | *(negated)* whose port is 80 |
|
|
||||||
| `?port__gt=` / `?port__gte=` / `?port__lt=` / `?port__lte=` | whose port is above/below the given value |
|
|
||||||
|
|
||||||
`port_mappings` is the most direct way to ask "which services expose this exact protocol and port?" — `?port_mappings=tcp/80` will not match a service that exposes only `udp/80`. Protocols may be given in any case, and leading zeros are ignored, so `?port_mappings=TCP/080` finds `tcp/80`. A value naming an unknown protocol or a malformed pair simply matches nothing rather than returning an error.
|
|
||||||
|
|
||||||
When `protocol` and one or more `port` lookups are combined, they must all be satisfied by a **single** mapping. So `?protocol=tcp&port__gt=1000` does not match a service whose only TCP mapping is `tcp/80` (even if it also exposes `udp/9999`), and `?port__gte=1000&port__lte=2000` does not match a service exposing only ports 500 and 5000. Each `port_mappings` value already names one complete pair, so it needs no such correlation and is simply combined with the other parameters.
|
|
||||||
|
|
||||||
All of these parameters are available as GraphQL filters too, under the same names — `port_mappings`, `protocol`, `port`, `port__gt`, `port__gte`, `port__lt`, `port__lte` — each accepting a list of values. For example, `filters: {port_mappings: ["tcp/80"]}` or `filters: {protocol: [TCP], port__gt: [1000]}`. The single-mapping correlation rule described above applies identically.
|
|
||||||
|
|
||||||
!!! warning "GraphQL filter change in NetBox v4.7"
|
|
||||||
|
|
||||||
The GraphQL filters for `Service` and `ServiceTemplate` have changed shape. The former `protocol` lookup and `ports` integer lookup (which nested their comparisons, e.g. `ports: {gt: 1000}`) are replaced by the flat `protocol`, `port`, `port__gt`, `port__gte`, `port__lt`, `port__lte`, and `port_mappings` parameters, each accepting a list of values and spelled the same way as the corresponding REST query parameter. Rewrite `ports: {gt: 1000}` as `port__gt: [1000]`, and `ports: {exact: 80}` as `port: [80]`. The `range` and `i_exact` lookups previously offered by the integer lookup have no direct equivalent; express a range as `port__gte`/`port__lte`, which — unlike the old lookup — requires a single mapping to satisfy both bounds.
|
|
||||||
|
|
||||||
The members of the `ServiceProtocolEnum` used by the `protocol` filter have also been renamed to drop a spurious `ROLE_` prefix: `ROLE_TCP`, `ROLE_UDP`, and `ROLE_SCTP` are now `TCP`, `UDP`, and `SCTP`.
|
|
||||||
|
|
||||||
!!! warning "REST filter change in NetBox v4.7"
|
|
||||||
|
|
||||||
Because `protocol` is now filtered against the `port_mappings` array rather than a dedicated model field, the character-based lookup variants previously auto-generated for it — `protocol__ic`, `protocol__nic`, `protocol__isw`, `protocol__empty`, etc. — are no longer available; `protocol` and `protocol__n` remain. The `port__empty` lookup is likewise gone, as a service always has at least one port mapping. As with any unrecognized query parameter, the REST API silently ignores a removed lookup rather than raising an error, so update any saved filters or scripts that relied on them.
|
|
||||||
|
|
||||||
### IP Addresses
|
### IP Addresses
|
||||||
|
|
||||||
The [IP address(es)](./ipaddress.md) to which this service is bound. If no IP addresses are bound, the service is assumed to be reachable via any assigned IP address.
|
The [IP address(es)](./ipaddress.md) to which this service is bound. If no IP addresses are bound, the service is assumed to be reachable via any assigned IP address.
|
||||||
|
|
||||||
## Bulk Import (CSV)
|
|
||||||
|
|
||||||
When importing application services or [application service templates](./servicetemplate.md) via CSV, all port mappings for a row are given in a single `port_mappings` column as a comma-separated list of `protocol/port` pairs enclosed in double quotes. For example, `"tcp/80,tcp/443,udp/53"`. A pair's port may be given as a hyphen range, for example `"tcp/8000-8010"`. Protocols may be entered in uppercase or lowercase.
|
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,9 @@
|
||||||
|
|
||||||
Application service templates can be used to instantiate [application services](./service.md) on [devices](../dcim/device.md) and [virtual machines](../virtualization/virtualmachine.md).
|
Application service templates can be used to instantiate [application services](./service.md) on [devices](../dcim/device.md) and [virtual machines](../virtualization/virtualmachine.md).
|
||||||
|
|
||||||
!!! note "Naming"
|
!!! note "Changed in NetBox v4.4"
|
||||||
|
|
||||||
Application service templates are referred to simply as "service templates" in the name of the model and throughout NetBox's programmatic APIs. Only the UI uses the longer name, which better reflects their intended use.
|
Previously, application service templates were referred to simply as "service templates". The name has been changed in the UI to better reflect their intended use. There is no change to the name of the model or in any programmatic NetBox APIs.
|
||||||
|
|
||||||
## Fields
|
## Fields
|
||||||
|
|
||||||
|
|
@ -12,10 +12,10 @@ Application service templates can be used to instantiate [application services](
|
||||||
|
|
||||||
A service or protocol name.
|
A service or protocol name.
|
||||||
|
|
||||||
### Port Mappings
|
### Protocol
|
||||||
|
|
||||||
The protocols and ports on which the service runs. See [Port Mappings](./service.md#port-mappings) on the application service model for details.
|
The wire protocol on which the service runs. Choices include UDP, TCP, and SCTP.
|
||||||
|
|
||||||
## Bulk Import (CSV)
|
### Ports
|
||||||
|
|
||||||
Application service templates are imported via CSV using the same `port_mappings` column format as application services. See [Bulk Import (CSV)](./service.md#bulk-import-csv) on the application service model for details.
|
One or more numeric ports to which the service is bound. Multiple ports can be expressed using commas and/or hyphens. For example, `80,8001-8003` specifies ports 80, 8001, 8002, and 8003.
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,6 @@
|
||||||
|
|
||||||
Tenant groups may be nested recursively to achieve a multi-level hierarchy. For example, you might have a group called "Customers" containing subgroups of individual tenants grouped by product or account team.
|
Tenant groups may be nested recursively to achieve a multi-level hierarchy. For example, you might have a group called "Customers" containing subgroups of individual tenants grouped by product or account team.
|
||||||
|
|
||||||
A tenant group cannot be deleted if ungrouping its tenants, including those of any nested groups, would result in duplicate tenant names or slugs among ungrouped tenants.
|
|
||||||
|
|
||||||
## Fields
|
## Fields
|
||||||
|
|
||||||
### Parent
|
### Parent
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,9 @@ The location or host for this VM. At least one must be specified:
|
||||||
- **Device only**: The VM runs directly on a physical host device without a cluster (e.g. containers). The site is automatically inferred from the device's site.
|
- **Device only**: The VM runs directly on a physical host device without a cluster (e.g. containers). The site is automatically inferred from the device's site.
|
||||||
- **Cluster + Device**: The VM belongs to a cluster and is pinned to a specific host device within that cluster. The device must be a registered host of the assigned cluster.
|
- **Cluster + Device**: The VM belongs to a cluster and is pinned to a specific host device within that cluster. The device must be a registered host of the assigned cluster.
|
||||||
|
|
||||||
|
!!! info "New in NetBox v4.6"
|
||||||
|
Virtual machines can now be assigned directly to a device without requiring a cluster. This is particularly useful for modeling VMs running on standalone hosts outside of a cluster.
|
||||||
|
|
||||||
### Platform
|
### Platform
|
||||||
|
|
||||||
A VM may be associated with a particular [platform](../dcim/platform.md) to indicate its operating system. If a virtual machine type defines a default platform, it will be applied when the VM is created unless an explicit platform is specified.
|
A VM may be associated with a particular [platform](../dcim/platform.md) to indicate its operating system. If a virtual machine type defines a default platform, it will be applied when the VM is created unless an explicit platform is specified.
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ If not selected, this interface will be treated as disabled/inoperative.
|
||||||
|
|
||||||
The [MAC address](../dcim/macaddress.md) assigned to this interface which is designated as its primary.
|
The [MAC address](../dcim/macaddress.md) assigned to this interface which is designated as its primary.
|
||||||
|
|
||||||
!!! note "MAC address is a property"
|
!!! note "Changed in NetBox v4.2"
|
||||||
The MAC address of an interface (formerly a concrete database field) is available as a property, `mac_address`, which reflects the value of the primary linked [MAC address](../dcim/macaddress.md) object.
|
The MAC address of an interface (formerly a concrete database field) is available as a property, `mac_address`, which reflects the value of the primary linked [MAC address](../dcim/macaddress.md) object.
|
||||||
|
|
||||||
### MTU
|
### MTU
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,8 @@ This is the human-friendly names of your background job. If omitted, the class n
|
||||||
|
|
||||||
### Logging
|
### Logging
|
||||||
|
|
||||||
|
!!! info "This feature was introduced in NetBox v4.4."
|
||||||
|
|
||||||
A Python logger is instantiated by the runner for each job. It can be utilized within a job's `run()` method as needed:
|
A Python logger is instantiated by the runner for each job. It can be utilized within a job's `run()` method as needed:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
|
|
|
||||||
|
|
@ -1,117 +0,0 @@
|
||||||
# Jinja Config Templates
|
|
||||||
|
|
||||||
!!! info "This feature was introduced in NetBox v4.7."
|
|
||||||
|
|
||||||
NetBox uses [Jinja](https://jinja.palletsprojects.com/) to render [configuration templates](../../features/configuration-rendering.md). Plugins can extend this rendering pipeline in two complementary ways:
|
|
||||||
|
|
||||||
1. **Register custom filters** — make new template filters available by name in every config template.
|
|
||||||
2. **Inject context variables** — add extra variables that are available inside every config template render.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Registering Jinja Filters
|
|
||||||
|
|
||||||
### Via `jinja_env.py` (auto-discovery)
|
|
||||||
|
|
||||||
Create a file named `jinja_env.py` in your plugin root and expose a dict called `filters`. NetBox will auto-discover and register it when the plugin loads.
|
|
||||||
|
|
||||||
```python title="my_plugin/jinja_env.py"
|
|
||||||
def prefix_list(device):
|
|
||||||
"""Return all prefixes assigned to a device's interfaces."""
|
|
||||||
return [
|
|
||||||
str(ip.address)
|
|
||||||
for iface in device.interfaces.all()
|
|
||||||
for ip in iface.ip_addresses.all()
|
|
||||||
]
|
|
||||||
|
|
||||||
filters = {
|
|
||||||
'prefix_list': prefix_list,
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
The filter is then available in any config template:
|
|
||||||
|
|
||||||
```jinja2
|
|
||||||
{% for prefix in device | prefix_list %}
|
|
||||||
network {{ prefix }}
|
|
||||||
{% endfor %}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Via `register_jinja_filters()`
|
|
||||||
|
|
||||||
You can also register filters programmatically inside your plugin's `ready()` method:
|
|
||||||
|
|
||||||
```python title="my_plugin/__init__.py"
|
|
||||||
from netbox.plugins import PluginConfig
|
|
||||||
|
|
||||||
class MyPluginConfig(PluginConfig):
|
|
||||||
name = 'my_plugin'
|
|
||||||
# ...
|
|
||||||
|
|
||||||
def ready(self):
|
|
||||||
super().ready()
|
|
||||||
from netbox.plugins.registration import register_jinja_filters
|
|
||||||
from .jinja_env import filters
|
|
||||||
register_jinja_filters(filters)
|
|
||||||
```
|
|
||||||
|
|
||||||
`register_jinja_filters()` accepts a `dict` mapping filter names to callables. It raises `TypeError` if passed a non-dict or if any value is not callable.
|
|
||||||
|
|
||||||
### Precedence
|
|
||||||
|
|
||||||
The full filter precedence from lowest to highest is: **NetBox built-in filters** (e.g. `env`) → **plugin-registered filters** → **instance [`JINJA_FILTERS`](../../configuration/system.md#jinja_filters)**. Instance-level filters always win, so site admins can override anything without touching a plugin.
|
|
||||||
|
|
||||||
If two plugins register a filter with the same name, the later-loaded plugin's version wins and NetBox will log a warning.
|
|
||||||
|
|
||||||
For example, if `my_plugin` registers a `prefix_list` filter but a site needs different behaviour, the operator can replace it in `configuration.py` without touching the plugin:
|
|
||||||
|
|
||||||
```python title="configuration.py"
|
|
||||||
def prefix_list(device):
|
|
||||||
# Site-local override: include only loopback prefixes
|
|
||||||
return [
|
|
||||||
str(ip.address)
|
|
||||||
for iface in device.interfaces.filter(type='loopback')
|
|
||||||
for ip in iface.ip_addresses.all()
|
|
||||||
]
|
|
||||||
|
|
||||||
JINJA_FILTERS = {
|
|
||||||
'prefix_list': prefix_list,
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Injecting Context Variables
|
|
||||||
|
|
||||||
Override `get_jinja_context()` in your `PluginConfig` subclass to inject additional variables into every config template render context.
|
|
||||||
|
|
||||||
```python title="my_plugin/__init__.py"
|
|
||||||
from netbox.plugins import PluginConfig
|
|
||||||
|
|
||||||
class MyPluginConfig(PluginConfig):
|
|
||||||
name = 'my_plugin'
|
|
||||||
# ...
|
|
||||||
|
|
||||||
def get_jinja_context(self):
|
|
||||||
from .utils import MyNamespace
|
|
||||||
return {
|
|
||||||
'my_plugin': MyNamespace(),
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
The returned dict is merged into the template context, so `my_plugin` becomes available by name inside every config template:
|
|
||||||
|
|
||||||
```jinja2
|
|
||||||
{% set records = my_plugin.lookup(device.name) %}
|
|
||||||
```
|
|
||||||
|
|
||||||
!!! warning "Startup cost"
|
|
||||||
`get_jinja_context()` is called on **every** config template render, not once at startup. Keep it fast. Defer expensive lookups to the object you return rather than performing them in `get_jinja_context()` itself.
|
|
||||||
|
|
||||||
!!! note "Conflict avoidance"
|
|
||||||
Choose context variable names that are unlikely to collide with NetBox's built-in template variables (`device`, `queryset`, etc.) or with those contributed by other plugins. Prefixing with your plugin name is strongly recommended.
|
|
||||||
|
|
||||||
In addition, avoid top-level app-label names (`dcim`, `ipam`, `virtualization`, etc.). The auto-populated template context maps each app label to a dict of its public model classes; returning a key like `'dcim'` from `get_jinja_context()` will silently replace that entire namespace.
|
|
||||||
|
|
||||||
!!! note "No per-render context"
|
|
||||||
`get_jinja_context()` receives no arguments — it has no access to the object being rendered or the caller-supplied context. It is intended for plugin-global namespaces (e.g. a lazily-evaluated query helper). Per-object logic belongs in the template itself or in a custom filter.
|
|
||||||
|
|
@ -1,64 +0,0 @@
|
||||||
# Event Rule Actions
|
|
||||||
|
|
||||||
!!! info "This feature was introduced in NetBox v4.7."
|
|
||||||
|
|
||||||
[Event rules](../../models/extras/eventrule.md) dispatch to an *action* when a matching event occurs, such as sending a webhook request or running a script. Plugins can register their own action types to extend the list of actions an event rule can perform, by subclassing NetBox's `EventRuleAction` class.
|
|
||||||
|
|
||||||
```python title="event_rules.py"
|
|
||||||
from django.utils.translation import gettext_lazy as _
|
|
||||||
from netbox.event_rules import EventRuleAction
|
|
||||||
|
|
||||||
from .models import Ticket
|
|
||||||
|
|
||||||
class OpenTicketAction(EventRuleAction):
|
|
||||||
slug = 'my_plugin.open_ticket'
|
|
||||||
label = _('Open ticket')
|
|
||||||
description = _('Open a ticket in the external ticketing system')
|
|
||||||
object_model = Ticket
|
|
||||||
object_required = True
|
|
||||||
|
|
||||||
def enqueue(self, *, event_rule, event_context, action_object, action_data):
|
|
||||||
...
|
|
||||||
```
|
|
||||||
|
|
||||||
To register one or more event rule actions with NetBox, define a list named `event_rule_actions` at the end of this file:
|
|
||||||
|
|
||||||
```python title="event_rules.py"
|
|
||||||
event_rule_actions = [OpenTicketAction]
|
|
||||||
```
|
|
||||||
|
|
||||||
!!! tip
|
|
||||||
The path to the list of event rule actions can be modified by setting `event_rule_actions` in the PluginConfig instance.
|
|
||||||
|
|
||||||
A dotted namespace prefix (e.g. `my_plugin.open_ticket`) is strongly recommended for `slug` to avoid collisions with other plugins or with action types added to NetBox core in the future.
|
|
||||||
|
|
||||||
`slug` must begin with a lowercase letter, and may contain only letters, digits, underscores, and dot-separated segments thereafter. **Hyphens are not allowed**, even though they're common in plugin/package names -- use an underscore instead, e.g. `my_plugin.open_ticket` as in the example above. `register_event_rule_action()` raises `ImproperlyConfigured` immediately for a slug outside this pattern, rather than allowing it to fail later during GraphQL schema assembly.
|
|
||||||
|
|
||||||
`slug`/`label` are only required at registration time, not at class definition, so an intermediate base class shared by several concrete actions may leave them unset.
|
|
||||||
|
|
||||||
!!! warning "Actions must be stateless"
|
|
||||||
Registration instantiates the class once, and that single instance serves every event rule, request, and background worker thread for the lifetime of the process. Do not stash per-event data on `self` in `enqueue()` or `validate()` -- concurrent dispatches would race over it. Everything an action needs is passed in as an argument.
|
|
||||||
|
|
||||||
## Target Objects
|
|
||||||
|
|
||||||
If an action operates against a specific object (e.g. a webhook targets a `Webhook` instance, and a script targets a `Script` instance), set `object_model` to the relevant model class. NetBox uses this to render the object-selection field on the event rule form and to validate the selected object's type. `object_required` defaults to `False` (matching `object_model`'s default of `None`); set it to `True` alongside `object_model` if the target object must always be selected. (Setting `object_required` *without* an `object_model` raises `ImproperlyConfigured` at registration, as it could never be satisfied.) Override `get_object_queryset()` to customize which objects are eligible for selection (e.g. to filter or further restrict the queryset).
|
|
||||||
|
|
||||||
The object-selection field is labeled with `object_model`'s verbose name; set `object_label` to override it.
|
|
||||||
|
|
||||||
If an action leaves `object_model` as `None`, event rules using it must not specify a target object: supplying one is rejected as a validation error rather than being silently stored.
|
|
||||||
|
|
||||||
## Bulk Import
|
|
||||||
|
|
||||||
To support resolving a target object from a CSV value during bulk import of event rules, override `resolve_import_object()`. Raise `django.core.exceptions.ObjectDoesNotExist` (or a subclass) if the supplied value doesn't resolve to an object. If this method is not overridden, event rules using this action type cannot be targeted at an object via bulk import.
|
|
||||||
|
|
||||||
## Unregistered Actions
|
|
||||||
|
|
||||||
An event rule's `action_type` is stored as a plain string, and is not validated against the set of currently-registered actions at the database level. This means an event rule can reference an action type provided by a plugin that is later uninstalled or disabled, without the row being deleted or corrupted. While its action type is unavailable:
|
|
||||||
|
|
||||||
* The event rule is skipped during event processing (it does not raise an error, and does not prevent other event rules from being processed).
|
|
||||||
* It is displayed with an "unavailable" indicator in the UI. `action_is_available` is exposed as a read-only field via the REST API, and as a filter (`?action_is_available=false`), so affected event rules can be found in bulk.
|
|
||||||
* It cannot be saved via the UI or REST API -- even to edit an unrelated field -- until its `action_type` is changed to a currently-registered value.
|
|
||||||
|
|
||||||
Reinstalling the plugin (and thereby re-registering the action type) automatically restores the event rule to working order, with no need to re-save it.
|
|
||||||
|
|
||||||
::: netbox.event_rules.EventRuleAction
|
|
||||||
|
|
@ -6,7 +6,8 @@ Filter sets define the mechanisms available for filtering or searching through a
|
||||||
|
|
||||||
To support additional functionality standard to NetBox models, such as tag assignment and custom field support, the `NetBoxModelFilterSet` class is available for use by plugins. This should be used as the base filter set class for plugin models which inherit from `NetBoxModel`. Within this class, individual filters can be declared as directed by the `django-filters` documentation. An example is provided below.
|
To support additional functionality standard to NetBox models, such as tag assignment and custom field support, the `NetBoxModelFilterSet` class is available for use by plugins. This should be used as the base filter set class for plugin models which inherit from `NetBoxModel`. Within this class, individual filters can be declared as directed by the `django-filters` documentation. An example is provided below.
|
||||||
|
|
||||||
The `register_filterset()` utility function enables plugins to register their filtersets to receive advanced functionality, such as the automatic attachment of field-specific lookup modifiers on the filter form. Registration is optional: Unregistered filtersets will continue to work as before, but will not receive the enhanced functionality.
|
!!! info "New in NetBox v4.5: FilterSet Registration"
|
||||||
|
NetBox v4.5 introduced the `register_filterset()` utility function. This enables plugins to register their filtersets to receive advanced functionality, such as the automatic attachment of field-specific lookup modifiers on the filter form. Registration is optional: Unregistered filtersets will continue to work as before, but will not receive the enhanced functionality.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
# filtersets.py
|
# filtersets.py
|
||||||
|
|
|
||||||
|
|
@ -210,37 +210,6 @@ In addition to the [form fields provided by Django](https://docs.djangoproject.c
|
||||||
options:
|
options:
|
||||||
members: false
|
members: false
|
||||||
|
|
||||||
## Static Choice Fields
|
|
||||||
|
|
||||||
!!! info "This feature was introduced in NetBox v4.7."
|
|
||||||
|
|
||||||
These fields render a standard HTML `<select>` element (as opposed to the API-backed widgets used by the dynamic object fields below). They extend Django's built-in choice fields to optionally display a short **description** beneath each option's label.
|
|
||||||
|
|
||||||
For choice set-backed fields, descriptions are defined per choice using a `Choice` object in the `ChoiceSet` and are rendered automatically. Pass `show_descriptions=False` to suppress them for a particular field.
|
|
||||||
|
|
||||||
```python
|
|
||||||
from utilities.choices import Choice, ChoiceSet
|
|
||||||
from utilities.forms.fields import ChoiceField
|
|
||||||
|
|
||||||
class StatusChoices(ChoiceSet):
|
|
||||||
ACTIVE = 'active'
|
|
||||||
RETIRED = 'retired'
|
|
||||||
CHOICES = (
|
|
||||||
Choice(ACTIVE, 'Active', description='Currently in service'),
|
|
||||||
Choice(RETIRED, 'Retired', description='No longer in service'),
|
|
||||||
)
|
|
||||||
|
|
||||||
status = ChoiceField(choices=StatusChoices)
|
|
||||||
```
|
|
||||||
|
|
||||||
::: utilities.forms.fields.ChoiceField
|
|
||||||
options:
|
|
||||||
members: false
|
|
||||||
|
|
||||||
::: utilities.forms.fields.MultipleChoiceField
|
|
||||||
options:
|
|
||||||
members: false
|
|
||||||
|
|
||||||
## Dynamic Object Fields
|
## Dynamic Object Fields
|
||||||
|
|
||||||
::: utilities.forms.fields.DynamicModelChoiceField
|
::: utilities.forms.fields.DynamicModelChoiceField
|
||||||
|
|
@ -261,20 +230,6 @@ status = ChoiceField(choices=StatusChoices)
|
||||||
options:
|
options:
|
||||||
members: false
|
members: false
|
||||||
|
|
||||||
## Generic Object Fields
|
|
||||||
|
|
||||||
!!! info "This feature was introduced in NetBox v4.7."
|
|
||||||
|
|
||||||
`GenericObjectChoiceField` represents a generic foreign key (a `content_type` plus `object_id` pair) as a single, REST API-backed form field. Pair it with `GenericObjectFormMixin` on the form to seed the field's initial value from the model's GFK descriptor and assign the selected object back to it automatically.
|
|
||||||
|
|
||||||
::: utilities.forms.fields.GenericObjectChoiceField
|
|
||||||
options:
|
|
||||||
members: false
|
|
||||||
|
|
||||||
::: utilities.forms.mixins.GenericObjectFormMixin
|
|
||||||
options:
|
|
||||||
members: false
|
|
||||||
|
|
||||||
## CSV Import Fields
|
## CSV Import Fields
|
||||||
|
|
||||||
::: utilities.forms.fields.CSVChoiceField
|
::: utilities.forms.fields.CSVChoiceField
|
||||||
|
|
|
||||||
|
|
@ -25,9 +25,9 @@ class MyModelType:
|
||||||
@strawberry.type
|
@strawberry.type
|
||||||
class MyQuery:
|
class MyQuery:
|
||||||
@strawberry.field
|
@strawberry.field
|
||||||
def mymodel(self, id: int) -> MyModelType:
|
def dummymodel(self, id: int) -> DummyModelType:
|
||||||
return None
|
return None
|
||||||
mymodel_list: list[MyModelType] = strawberry_django.field()
|
dummymodel_list: list[DummyModelType] = strawberry_django.field()
|
||||||
|
|
||||||
|
|
||||||
schema = [
|
schema = [
|
||||||
|
|
@ -35,102 +35,6 @@ schema = [
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
## Extending Core Types & Filters
|
|
||||||
|
|
||||||
!!! info "This feature was introduced in NetBox v4.7."
|
|
||||||
|
|
||||||
In addition to registering its own top-level query fields, a plugin can inject fields and filters onto NetBox's **existing** core GraphQL types (e.g. `DeviceType`). This allows a plugin's related data to be traversed within a single query rooted at a core object, rather than requiring a separate top-level query. This mirrors the `PluginTemplateExtension` mechanism used to extend core object views in the UI.
|
|
||||||
|
|
||||||
An extension is a mixin class declaring a `models` attribute: a list of the lowercased `app_label.model` labels of the core types it extends. Declare `models` as an unannotated class attribute or a `ClassVar`. An annotated `models` would be collected as a GraphQL field and is rejected. Output-type extensions are collected from `graphql_extensions.type_extensions` and filter extensions from `graphql_extensions.filter_extensions` by default. These paths can be overridden via the `graphql_type_extensions` and `graphql_filter_extensions` attributes on the PluginConfig.
|
|
||||||
|
|
||||||
By default, NetBox imports `type_extensions` and `filter_extensions` from a `graphql_extensions.py` module beside the plugin's `graphql.py`. The `PluginConfig` attributes may override each with a dotted path to a list under any attribute name.
|
|
||||||
|
|
||||||
!!! warning
|
|
||||||
Extension modules are imported while plugins initialize, before NetBox's core GraphQL types are assembled. They must not import core GraphQL modules (e.g. `dcim.graphql.types`) at module level. A premature import assembles the affected core types early, and any extension registered afterwards for one of them fails at startup. Reference core types only through `strawberry.lazy()` string annotations. Plugin schema modules (`graphql.py`) are loaded later, during schema assembly, and may import core GraphQL types freely.
|
|
||||||
|
|
||||||
### Type Extensions
|
|
||||||
|
|
||||||
An output-type extension is a `@strawberry.type` class whose fields and resolvers are spliced into the target type:
|
|
||||||
|
|
||||||
```python
|
|
||||||
# graphql_extensions.py
|
|
||||||
from typing import TYPE_CHECKING, Annotated
|
|
||||||
|
|
||||||
import strawberry
|
|
||||||
import strawberry_django
|
|
||||||
|
|
||||||
from utilities.querysets import RestrictedPrefetch
|
|
||||||
from my_plugin.models import Widget
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
from dcim.graphql.types import DeviceType
|
|
||||||
|
|
||||||
|
|
||||||
@strawberry_django.type(Widget, fields='__all__')
|
|
||||||
class WidgetType:
|
|
||||||
device: Annotated['DeviceType', strawberry.lazy('dcim.graphql.types')]
|
|
||||||
|
|
||||||
|
|
||||||
@strawberry.type
|
|
||||||
class DeviceTypeExtension:
|
|
||||||
models = ['dcim.device']
|
|
||||||
|
|
||||||
@strawberry_django.field(
|
|
||||||
prefetch_related=lambda info: RestrictedPrefetch(
|
|
||||||
'widgets', info.context.request.user, 'view', queryset=Widget.objects.all()
|
|
||||||
),
|
|
||||||
)
|
|
||||||
def widgets(self) -> list[Annotated['WidgetType', strawberry.lazy('my_plugin.graphql_extensions')]]:
|
|
||||||
return self.widgets.all()
|
|
||||||
|
|
||||||
|
|
||||||
type_extensions = [
|
|
||||||
DeviceTypeExtension,
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
!!! note
|
|
||||||
Scope any related-object resolver with `RestrictedPrefetch(..., info.context.request.user, 'view', ...)`, as shown above. Object permissions are only applied to the top-level queryset, so a plain `prefetch_related='widgets'` returns related objects the requesting user may not be permitted to see.
|
|
||||||
|
|
||||||
### Filter Extensions
|
|
||||||
|
|
||||||
A filter extension is a `@strawberry.type` class declaring additional filters - either as annotated filter fields or as custom filter methods - which are spliced into the target filter:
|
|
||||||
|
|
||||||
```python
|
|
||||||
# graphql_extensions.py
|
|
||||||
import strawberry
|
|
||||||
import strawberry_django
|
|
||||||
from django.db.models import Q
|
|
||||||
|
|
||||||
|
|
||||||
@strawberry.type
|
|
||||||
class DeviceFilterExtension:
|
|
||||||
models = ['dcim.device']
|
|
||||||
|
|
||||||
@strawberry_django.filter_field()
|
|
||||||
def has_widgets(self, value: bool, prefix) -> Q:
|
|
||||||
return Q(**{f'{prefix}widgets__isnull': not value})
|
|
||||||
|
|
||||||
|
|
||||||
filter_extensions = [
|
|
||||||
DeviceFilterExtension,
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
With both registered, a client can fetch a device and its plugin-provided data in a single query:
|
|
||||||
|
|
||||||
```graphql
|
|
||||||
query {
|
|
||||||
device_list(filters: { has_widgets: true }) {
|
|
||||||
name
|
|
||||||
widgets { id name }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
!!! note
|
|
||||||
Extensions are strictly additive. Every name an extension contributes must be new, not only its GraphQL fields and resolvers but also its helper methods and class attributes. A name the core type already provides, or that two extensions both declare, causes NetBox to fail at startup with an error naming the extension classes. This is deliberate, because Python resolves attribute lookups through the composed MRO, so a shared helper or constant name would let one plugin silently redirect another plugin's resolvers. Two extensions may inherit the same name from one shared helper base, which is not a conflict. Explicit GraphQL aliases are checked as well. Registering an extension after its target GraphQL type has been assembled also raises an error, as does an extension targeting a model that never assembles a GraphQL type or filter. Extensions are plain mixin types and may not implement GraphQL interfaces or inherit from core GraphQL classes.
|
|
||||||
|
|
||||||
## GraphQL Objects
|
## GraphQL Objects
|
||||||
|
|
||||||
NetBox provides two object type classes for use by plugins.
|
NetBox provides two object type classes for use by plugins.
|
||||||
|
|
|
||||||
|
|
@ -118,14 +118,10 @@ NetBox looks for the `config` variable within a plugin's `__init__.py` to load i
|
||||||
| `events_pipeline` | A list of handlers to add to [`EVENTS_PIPELINE`](../../configuration/miscellaneous.md#events_pipeline), identified by dotted paths |
|
| `events_pipeline` | A list of handlers to add to [`EVENTS_PIPELINE`](../../configuration/miscellaneous.md#events_pipeline), identified by dotted paths |
|
||||||
| `search_indexes` | The dotted path to the list of search index classes (default: `search.indexes`) |
|
| `search_indexes` | The dotted path to the list of search index classes (default: `search.indexes`) |
|
||||||
| `data_backends` | The dotted path to the list of data source backend classes (default: `data_backends.backends`) |
|
| `data_backends` | The dotted path to the list of data source backend classes (default: `data_backends.backends`) |
|
||||||
| `event_rule_actions` | The dotted path to the list of event rule action classes (default: `event_rules.event_rule_actions`) |
|
|
||||||
| `template_extensions` | The dotted path to the list of template extension classes (default: `template_content.template_extensions`) |
|
| `template_extensions` | The dotted path to the list of template extension classes (default: `template_content.template_extensions`) |
|
||||||
| `jinja_filters` | The dotted path to a dict of custom Jinja filter functions for use in config templates (default: `jinja_env.filters`) |
|
|
||||||
| `menu` | The dotted path to a top-level navigation menu provided by the plugin (default: `navigation.menu`) |
|
| `menu` | The dotted path to a top-level navigation menu provided by the plugin (default: `navigation.menu`) |
|
||||||
| `menu_items` | The dotted path to the list of menu items provided by the plugin (default: `navigation.menu_items`) |
|
| `menu_items` | The dotted path to the list of menu items provided by the plugin (default: `navigation.menu_items`) |
|
||||||
| `graphql_schema` | The dotted path to the plugin's GraphQL schema class, if any (default: `graphql.schema`) |
|
| `graphql_schema` | The dotted path to the plugin's GraphQL schema class, if any (default: `graphql.schema`) |
|
||||||
| `graphql_type_extensions` | The dotted path to the list of GraphQL output-type extension classes, if any (default: `graphql_extensions.type_extensions`) |
|
|
||||||
| `graphql_filter_extensions` | The dotted path to the list of GraphQL filter extension classes, if any (default: `graphql_extensions.filter_extensions`) |
|
|
||||||
| `user_preferences` | The dotted path to the dictionary mapping of user preferences defined by the plugin (default: `preferences.preferences`) |
|
| `user_preferences` | The dotted path to the dictionary mapping of user preferences defined by the plugin (default: `preferences.preferences`) |
|
||||||
|
|
||||||
All required settings must be configured by the user. If a configuration parameter is listed in both `required_settings` and `default_settings`, the default setting will be ignored.
|
All required settings must be configured by the user. If a configuration parameter is listed in both `required_settings` and `default_settings`, the default setting will be ignored.
|
||||||
|
|
|
||||||
|
|
@ -73,6 +73,9 @@ A `PluginMenuItem` has the following attributes:
|
||||||
| `staff_only` | - | Display only for superusers |
|
| `staff_only` | - | Display only for superusers |
|
||||||
| `buttons` | - | An iterable of PluginMenuButton instances to include |
|
| `buttons` | - | An iterable of PluginMenuButton instances to include |
|
||||||
|
|
||||||
|
!!! note "Changed in NetBox v4.5"
|
||||||
|
In releases prior to NetBox v4.5, `staff_only` restricted display of a menu item to only users with `is_staff` set to True. In NetBox v4.5, the `is_staff` flag was removed from the user model. Menu items with `staff_only` set to True are now displayed only for superusers.
|
||||||
|
|
||||||
## Menu Buttons
|
## Menu Buttons
|
||||||
|
|
||||||
Each menu item can include a set of buttons. These can be handy for providing shortcuts related to the menu item. For instance, most items in NetBox's navigation menu include buttons to create and import new objects.
|
Each menu item can include a set of buttons. These can be handy for providing shortcuts related to the menu item. For instance, most items in NetBox's navigation menu include buttons to create and import new objects.
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,8 @@
|
||||||
# UI Components
|
# UI Components
|
||||||
|
|
||||||
|
!!! note "New in NetBox v4.6"
|
||||||
|
All UI components described here were introduced in NetBox v4.6. Be sure to set the minimum NetBox version to 4.6.0 for your plugin before incorporating any of these resources.
|
||||||
|
|
||||||
To simplify the process of designing your plugin's user interface, and to encourage a consistent look and feel throughout the entire application, NetBox provides a set of components that enable programmatic UI design. These make it possible to declare complex page layouts with little or no custom HTML.
|
To simplify the process of designing your plugin's user interface, and to encourage a consistent look and feel throughout the entire application, NetBox provides a set of components that enable programmatic UI design. These make it possible to declare complex page layouts with little or no custom HTML.
|
||||||
|
|
||||||
## Page Layout
|
## Page Layout
|
||||||
|
|
@ -50,75 +53,6 @@ class MyView(generic.ObjectView):
|
||||||
|
|
||||||
::: netbox.ui.layout.Column
|
::: netbox.ui.layout.Column
|
||||||
|
|
||||||
## Breadcrumbs
|
|
||||||
|
|
||||||
!!! info "This feature was introduced in NetBox v4.7."
|
|
||||||
|
|
||||||
Breadcrumbs are rendered at the top of an object's page to convey its position within a hierarchy and to provide quick navigation to related objects. By default, a single breadcrumb linking to the object's list view is shown. To add object-specific breadcrumbs, pass a list of `Breadcrumb` instances to your layout, just as you would its panels.
|
|
||||||
|
|
||||||
A `Breadcrumb` typically references an _accessor_ (rather than a static value), which is resolved against the object being viewed when the page is rendered. The accessor may be a dotted attribute path or a callable. (A breadcrumb may instead define a static `label`; see below.)
|
|
||||||
|
|
||||||
```python
|
|
||||||
from netbox.ui import layout
|
|
||||||
from netbox.ui.breadcrumbs import Breadcrumb
|
|
||||||
from netbox.views import generic
|
|
||||||
|
|
||||||
class MyView(generic.ObjectView):
|
|
||||||
layout = layout.SimpleLayout(
|
|
||||||
breadcrumbs=[
|
|
||||||
Breadcrumb('site'),
|
|
||||||
Breadcrumb('location'),
|
|
||||||
Breadcrumb('rack'),
|
|
||||||
],
|
|
||||||
left_panels=[...],
|
|
||||||
right_panels=[...],
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
Each breadcrumb renders as a label (the string representation of the resolved object) and an optional link. If no explicit `url` is provided, the object's `get_absolute_url()` is used when available. A breadcrumb whose accessor resolves to `None` (or an empty iterable) renders as an empty string and is omitted, which simplifies conditional breadcrumbs (e.g. where a device may or may not be assigned to a rack).
|
|
||||||
|
|
||||||
To link a breadcrumb somewhere other than the related object's own page (for example, to a filtered list view), pass a `url`. A callable `url` receives the resolved object:
|
|
||||||
|
|
||||||
```python
|
|
||||||
from django.urls import reverse
|
|
||||||
|
|
||||||
Breadcrumb('rir', url=lambda rir: f"{reverse('ipam:asn_list')}?rir_id={rir.pk}")
|
|
||||||
```
|
|
||||||
|
|
||||||
A callable accessor which returns an iterable renders one breadcrumb per object, which is useful for representing a hierarchy of ancestors:
|
|
||||||
|
|
||||||
```python
|
|
||||||
Breadcrumb(lambda obj: obj.get_ancestors())
|
|
||||||
```
|
|
||||||
|
|
||||||
To render a breadcrumb that isn't tied to a related object, omit the accessor and pass a `label`. This is useful for linking to a parent view that isn't reachable via an attribute on the object (e.g. a user's personal token list):
|
|
||||||
|
|
||||||
```python
|
|
||||||
from django.urls import reverse_lazy
|
|
||||||
|
|
||||||
Breadcrumb(label=_('My API Tokens'), url=reverse_lazy('account:usertoken_list'))
|
|
||||||
```
|
|
||||||
|
|
||||||
The `label` may also be a callable, which receives the relevant object (the resolved related object when an accessor is given, otherwise the viewed instance). This is useful for an unlinked descriptive crumb derived from the object:
|
|
||||||
|
|
||||||
```python
|
|
||||||
Breadcrumb(label=lambda obj: f"{_('Units')} {obj.unit_list}")
|
|
||||||
```
|
|
||||||
|
|
||||||
The default root breadcrumb (linking to the object's list view) is prepended to the trail automatically. Where that list view isn't an appropriate root—for example, the global token list is admin-only, so a user's personal token page links to their own token list instead—pass `root_breadcrumb=False` to the layout and supply a replacement as the first breadcrumb:
|
|
||||||
|
|
||||||
```python
|
|
||||||
SimpleLayout(
|
|
||||||
root_breadcrumb=False,
|
|
||||||
breadcrumbs=[
|
|
||||||
Breadcrumb(label=_('My API Tokens'), url=reverse_lazy('account:usertoken_list')),
|
|
||||||
],
|
|
||||||
...
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
::: netbox.ui.breadcrumbs.Breadcrumb
|
|
||||||
|
|
||||||
## Panels
|
## Panels
|
||||||
|
|
||||||
Within each column, related blocks of content are arranged into panels. Each panel has a title and may have a set of associated actions, but the content within is otherwise arbitrary.
|
Within each column, related blocks of content are arranged into panels. Each panel has a title and may have a set of associated actions, but the content within is otherwise arbitrary.
|
||||||
|
|
|
||||||
|
|
@ -79,8 +79,14 @@ The NetBox URL registration process has two parts:
|
||||||
|
|
||||||
::: utilities.views.register_model_view
|
::: utilities.views.register_model_view
|
||||||
|
|
||||||
|
!!! note "Changed in NetBox v4.2"
|
||||||
|
In NetBox v4.2, the `register_model_view()` function was extended to support the registration of list views by passing `detail=False`.
|
||||||
|
|
||||||
::: utilities.urls.get_model_urls
|
::: utilities.urls.get_model_urls
|
||||||
|
|
||||||
|
!!! note "Changed in NetBox v4.2"
|
||||||
|
In NetBox v4.2, the `get_model_urls()` function was extended to support retrieving registered general model views (e.g. for listing objects) by passing `detail=False`.
|
||||||
|
|
||||||
### Example Usage
|
### Example Usage
|
||||||
|
|
||||||
```python
|
```python
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,8 @@ The resulting webhook payload will look like the following:
|
||||||
"event": "updated",
|
"event": "updated",
|
||||||
"timestamp": "2025-08-07T14:24:30.627321+00:00",
|
"timestamp": "2025-08-07T14:24:30.627321+00:00",
|
||||||
"object_type": "dcim.site",
|
"object_type": "dcim.site",
|
||||||
|
"username": "admin",
|
||||||
|
"request_id": "49e3e39e-7333-4b9c-a9af-19f0dc1e7dc9",
|
||||||
"data": {
|
"data": {
|
||||||
"id": 2,
|
"id": 2,
|
||||||
"url": "/api/dcim/sites/2/",
|
"url": "/api/dcim/sites/2/",
|
||||||
|
|
@ -42,6 +44,11 @@ The resulting webhook payload will look like the following:
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
!!! warning "Deprecation of legacy keys"
|
||||||
|
The `request_id` and `username` keys in the webhook payload above are deprecated and should no longer be used. Support for them will be removed in NetBox v4.7.0.
|
||||||
|
|
||||||
|
Use `request.user` and `request.id` from the `request` object included in the callback context instead.
|
||||||
|
|
||||||
!!! note "Consider namespacing webhook data"
|
!!! note "Consider namespacing webhook data"
|
||||||
The data returned from all webhook callbacks will be compiled into a single `context` dictionary. Any existing keys within this dictionary will be overwritten by subsequent callbacks which include those keys. To avoid collisions with webhook data provided by other plugins, consider namespacing your plugin's data within a nested dictionary as such:
|
The data returned from all webhook callbacks will be compiled into a single `context` dictionary. Any existing keys within this dictionary will be overwritten by subsequent callbacks which include those keys. To avoid collisions with webhook data provided by other plugins, consider namespacing your plugin's data within a nested dictionary as such:
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ A condition is expressed as a JSON object with the following keys:
|
||||||
| Key name | Required | Default | Description |
|
| Key name | Required | Default | Description |
|
||||||
|----------|----------|---------|-------------|
|
|----------|----------|---------|-------------|
|
||||||
| attr | Yes | - | Name of the key within the data being evaluated |
|
| attr | Yes | - | Name of the key within the data being evaluated |
|
||||||
| value | See note | - | The reference value to which the given data will be compared. Not used by snapshot operators (`changed`, `unchanged`). |
|
| value | Yes | - | The reference value to which the given data will be compared |
|
||||||
| op | No | `eq` | The logical operation to be performed |
|
| op | No | `eq` | The logical operation to be performed |
|
||||||
| negate | No | False | Negate (invert) the result of the condition's evaluation |
|
| negate | No | False | Negate (invert) the result of the condition's evaluation |
|
||||||
|
|
||||||
|
|
@ -22,9 +22,6 @@ A condition is expressed as a JSON object with the following keys:
|
||||||
* `lte`: Less than or equal to
|
* `lte`: Less than or equal to
|
||||||
* `in`: Is present within a list of values
|
* `in`: Is present within a list of values
|
||||||
* `contains`: Contains the specified value
|
* `contains`: Contains the specified value
|
||||||
* `regex`: Matches a regular expression
|
|
||||||
* `changed`: The attribute's value differs between the pre-change and post-change snapshots (no `value` required)
|
|
||||||
* `unchanged`: The attribute's value is the same in both snapshots (no `value` required)
|
|
||||||
|
|
||||||
### Accessing Nested Keys
|
### Accessing Nested Keys
|
||||||
|
|
||||||
|
|
@ -49,13 +46,6 @@ The following condition will evaluate as true:
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
!!! note "Missing keys and absent data"
|
|
||||||
A condition which references a key that does not exist in the data being evaluated fails closed: the condition set evaluates as false, and (for an [event rule](../features/event-rules.md)) an error is logged to `netbox.event_rules` so that a typo does not silently disable the rule.
|
|
||||||
|
|
||||||
Where the data is absent altogether rather than merely missing the referenced key — an event rule evaluating a job which recorded no data, say — the condition does not match. Such an absence is a normal property of the event rather than a mistake, so it is not treated as an error and does not affect the evaluation of the other conditions in the set. Because there is nothing to compare against, the condition does not match whatever the operator, and remains a non-match when `negate` is set: no rule fires on data an event never carried.
|
|
||||||
|
|
||||||
A snapshot which does not exist for the event type is the exception: it resolves to `null`, so that a condition can distinguish (for example) a newly created object from an updated one. See [below](#snapshot-conditions-event-rules).
|
|
||||||
|
|
||||||
### Examples
|
### Examples
|
||||||
|
|
||||||
`name` equals "foo":
|
`name` equals "foo":
|
||||||
|
|
@ -101,86 +91,6 @@ The following condition will evaluate as true:
|
||||||
!!! note "Evaluating static choice fields"
|
!!! note "Evaluating static choice fields"
|
||||||
Pay close attention when evaluating static choice fields, such as the `status` field above. These fields typically render as a dictionary specifying both the field's raw value (`value`) and its human-friendly label (`label`). Be sure to specify on which of these you want to match.
|
Pay close attention when evaluating static choice fields, such as the `status` field above. These fields typically render as a dictionary specifying both the field's raw value (`value`) and its human-friendly label (`label`). Be sure to specify on which of these you want to match.
|
||||||
|
|
||||||
## Snapshot Conditions (Event Rules)
|
|
||||||
|
|
||||||
!!! info "This feature was introduced in NetBox v4.7."
|
|
||||||
|
|
||||||
When used in an [event rule](../features/event-rules.md), conditions can also inspect the **pre-change and post-change snapshots** captured at the time of the event. This allows rules to fire only when a specific field actually changes value, rather than whenever it has a particular value.
|
|
||||||
|
|
||||||
### Snapshot Operators
|
|
||||||
|
|
||||||
The `changed` and `unchanged` operators compare an attribute's value across the two snapshots. They do not accept a `value` key.
|
|
||||||
|
|
||||||
Fire only when `status` changes (to any value):
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"attr": "status",
|
|
||||||
"op": "changed"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
An attribute which resolves in neither snapshot — a misspelling, an attribute the object type does not have, or an event which recorded no snapshots at all — leaves nothing to compare. The condition fails closed (the rule does not fire) and an error is logged to `netbox.event_rules`. This holds regardless of `negate`: negating a condition whose attribute cannot be resolved does not turn it into a match.
|
|
||||||
|
|
||||||
### Combining with Standard Conditions
|
|
||||||
|
|
||||||
The canonical use case — fire only when `status` changes **to** `active` — combines a standard value check with the `changed` operator:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"and": [
|
|
||||||
{
|
|
||||||
"attr": "status.value",
|
|
||||||
"value": "active"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"attr": "status",
|
|
||||||
"op": "changed"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Direct Snapshot Path Access
|
|
||||||
|
|
||||||
You can also read pre- or post-change values directly using the `snapshots.prechange.<attr>` and `snapshots.postchange.<attr>` dot-path syntax with any standard operator:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"attr": "snapshots.prechange.status",
|
|
||||||
"value": "planned"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
!!! warning "Snapshot serialization format"
|
|
||||||
Snapshot data uses the **model serializer format**, not the REST API format. Choice fields such as `status` are stored as raw strings (e.g. `"active"`) rather than nested objects (e.g. `{"value": "active", "label": "Active"}`). Use `status` — not `status.value` — when referencing a snapshot attribute, both in `snapshots.prechange.*`/`snapshots.postchange.*` paths and with the `changed`/`unchanged` operators. A `.value` suffix cannot be resolved against a snapshot: the condition fails closed (the rule does not fire) and an error is logged to `netbox.event_rules`.
|
|
||||||
|
|
||||||
!!! note "Snapshot availability"
|
|
||||||
For create events, `prechange` is `null`. The `changed` operator evaluates to `true` for any attribute present in the postchange snapshot (each field transitioned from non-existent to its initial value), and a `snapshots.prechange.*` path resolves to `null` — so it matches a condition testing for `null` and fails any other comparison.
|
|
||||||
|
|
||||||
For delete events, `postchange` is `null`. The `changed` operator evaluates to `true` for any attribute present in the prechange snapshot, `unchanged` evaluates to `false`, and a `snapshots.postchange.*` path resolves to `null`.
|
|
||||||
|
|
||||||
An absent snapshot excuses only the absence of the data, not a path which does not describe it. The attribute path is checked against the opposite snapshot, and fails closed (the rule does not fire, and an error is logged) if it cannot be resolved there — whether because it cannot be resolved against a snapshot at all, such as the `.value` suffix above, or because the attribute is misspelled or unknown. Otherwise a typo would resolve to `null` and fire the rule on every create or delete.
|
|
||||||
|
|
||||||
To test only whether a snapshot is absent, reference the snapshot itself rather than an attribute of it: `{"attr": "snapshots.prechange", "value": null}`.
|
|
||||||
|
|
||||||
Because an absent snapshot resolves to `null` rather than raising an error — matching a condition testing for `null` and failing any other comparison, whichever operator is used — a snapshot path can be combined safely with other conditions in a rule that also fires on create or delete. For example, this rule fires when a site is created, or when a site whose status was previously `planned` is updated:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"or": [
|
|
||||||
{
|
|
||||||
"attr": "snapshots.prechange.status",
|
|
||||||
"value": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"attr": "snapshots.prechange.status",
|
|
||||||
"value": "planned"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Condition Sets
|
## Condition Sets
|
||||||
|
|
||||||
Multiple conditions can be combined into nested sets using AND or OR logic. This is done by declaring a JSON object with a single key (`and` or `or`) containing a list of condition objects and/or child condition sets.
|
Multiple conditions can be combined into nested sets using AND or OR logic. This is done by declaring a JSON object with a single key (`and` or `or`) containing a list of condition objects and/or child condition sets.
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue