mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-27 02:48:38 -06:00
Merge upstream v2.10.3 into develop
# Conflicts: # netbox/dcim/api/nested_serializers.py # netbox/dcim/api/serializers.py # netbox/dcim/api/views.py # netbox/dcim/choices.py # netbox/dcim/forms.py # netbox/dcim/models/__init__.py # netbox/dcim/tests/test_api.py # netbox/extras/tests/test_filters.py # netbox/netbox/settings.py # netbox/netbox/urls.py # netbox/utilities/forms.py # requirements.txt
This commit is contained in:
commit
c3bce3c7a1
4
.gitattributes
vendored
4
.gitattributes
vendored
@ -1 +1,5 @@
|
||||
*.sh text eol=lf
|
||||
# Treat minified or packed JS/CSS files as binary, as they're not meant to be human-readable
|
||||
*.min.* binary
|
||||
*.map binary
|
||||
*.pack.js binary
|
||||
|
8
.github/ISSUE_TEMPLATE/bug_report.md
vendored
8
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@ -7,11 +7,9 @@ about: Report a reproducible bug in the current release of NetBox
|
||||
<!--
|
||||
NOTE: IF YOUR ISSUE DOES NOT FOLLOW THIS TEMPLATE, IT WILL BE CLOSED.
|
||||
|
||||
This form is only for reproducible bugs. If you need assistance with
|
||||
NetBox installation, or if you have a general question, DO NOT open an
|
||||
issue. Instead, post to our mailing list:
|
||||
|
||||
https://groups.google.com/forum/#!forum/netbox-discuss
|
||||
This form is only for reporting reproducible bugs. If you need assistance
|
||||
with NetBox installation, or if you have a general question, please start a
|
||||
discussion instead: https://github.com/netbox-community/netbox/discussions
|
||||
|
||||
Please describe the environment in which you are running NetBox. Be sure
|
||||
that you are running an unmodified instance of the latest stable release
|
||||
|
2
.github/ISSUE_TEMPLATE/config.yml
vendored
2
.github/ISSUE_TEMPLATE/config.yml
vendored
@ -5,5 +5,5 @@ contact_links:
|
||||
url: https://github.com/netbox-community/netbox/blob/develop/CONTRIBUTING.md
|
||||
about: Please read through our contributing policy before opening an issue or pull request
|
||||
- name: 💬 Discussion Group
|
||||
url: https://groups.google.com/forum/#!forum/netbox-discuss
|
||||
url: https://groups.google.com/g/netbox-discuss
|
||||
about: Join our discussion group for assistance with installation issues and other problems
|
||||
|
10
.github/ISSUE_TEMPLATE/feature_request.md
vendored
10
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@ -8,10 +8,8 @@ about: Propose a new NetBox feature or enhancement
|
||||
NOTE: IF YOUR ISSUE DOES NOT FOLLOW THIS TEMPLATE, IT WILL BE CLOSED.
|
||||
|
||||
This form is only for proposing specific new features or enhancements.
|
||||
If you have a general idea or question, please post to our mailing list
|
||||
instead of opening an issue:
|
||||
|
||||
https://groups.google.com/forum/#!forum/netbox-discuss
|
||||
If you have a general idea or question, please start a discussion instead:
|
||||
https://github.com/netbox-community/netbox/discussions
|
||||
|
||||
NOTE: Due to an excessive backlog of feature requests, we are not currently
|
||||
accepting any proposals which significantly extend NetBox's feature scope.
|
||||
@ -21,8 +19,8 @@ about: Propose a new NetBox feature or enhancement
|
||||
before submitting a bug report.
|
||||
-->
|
||||
### Environment
|
||||
* Python version: <!-- Example: 3.6.9 -->
|
||||
* NetBox version: <!-- Example: 2.7.3 -->
|
||||
* Python version:
|
||||
* NetBox version:
|
||||
|
||||
<!--
|
||||
Describe in detail the new functionality you are proposing. Include any
|
||||
|
23
.github/lock.yml
vendored
23
.github/lock.yml
vendored
@ -1,23 +0,0 @@
|
||||
# Configuration for Lock (https://github.com/apps/lock)
|
||||
|
||||
# Number of days of inactivity before a closed issue or pull request is locked
|
||||
daysUntilLock: 90
|
||||
|
||||
# Skip issues and pull requests created before a given timestamp. Timestamp must
|
||||
# follow ISO 8601 (`YYYY-MM-DD`). Set to `false` to disable
|
||||
skipCreatedBefore: false
|
||||
|
||||
# Issues and pull requests with these labels will be ignored. Set to `[]` to disable
|
||||
exemptLabels: []
|
||||
|
||||
# Label to add before locking, such as `outdated`. Set to `false` to disable
|
||||
lockLabel: false
|
||||
|
||||
# Comment to post before locking. Set to `false` to disable
|
||||
lockComment: false
|
||||
|
||||
# Assign `resolved` as the reason for locking. Set to `false` to disable
|
||||
setLockReason: true
|
||||
|
||||
# Limit to only `issues` or `pulls`
|
||||
# only: issues
|
8
.github/stale.yml
vendored
8
.github/stale.yml
vendored
@ -4,19 +4,19 @@
|
||||
only: issues
|
||||
|
||||
# Number of days of inactivity before an issue becomes stale
|
||||
daysUntilStale: 14
|
||||
daysUntilStale: 45
|
||||
|
||||
# Number of days of inactivity before a stale issue is closed
|
||||
daysUntilClose: 7
|
||||
daysUntilClose: 15
|
||||
|
||||
# Issues with these labels will never be considered stale
|
||||
exemptLabels:
|
||||
- "status: accepted"
|
||||
- "status: gathering feedback"
|
||||
- "status: blocked"
|
||||
- "status: needs milestone"
|
||||
|
||||
# Label to use when marking an issue as stale
|
||||
staleLabel: wontfix
|
||||
staleLabel: "pending closure"
|
||||
|
||||
# Comment to post when marking an issue as stale. Set to `false` to disable
|
||||
markComment: >
|
||||
|
50
.github/workflows/ci.yml
vendored
Normal file
50
.github/workflows/ci.yml
vendored
Normal file
@ -0,0 +1,50 @@
|
||||
name: CI
|
||||
on: [push, pull_request]
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: [3.6, 3.7, 3.8]
|
||||
services:
|
||||
redis:
|
||||
image: redis
|
||||
ports:
|
||||
- 6379:6379
|
||||
postgres:
|
||||
image: postgres
|
||||
env:
|
||||
POSTGRES_USER: netbox
|
||||
POSTGRES_PASSWORD: netbox
|
||||
options: >-
|
||||
--health-cmd pg_isready
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 5
|
||||
ports:
|
||||
- 5432:5432
|
||||
|
||||
steps:
|
||||
- name: Check out repo
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
|
||||
- name: Install dependencies & set up configuration
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install -r requirements.txt
|
||||
pip install pycodestyle coverage
|
||||
ln -s configuration.testing.py netbox/netbox/configuration.py
|
||||
|
||||
- name: Check PEP8 compliance
|
||||
run: pycodestyle --ignore=W504,E501 netbox/
|
||||
|
||||
- name: Run tests
|
||||
run: coverage run --source="netbox/" netbox/manage.py test netbox/
|
||||
|
||||
- name: Show coverage report
|
||||
run: coverage report --skip-covered --omit *migrations*
|
21
.github/workflows/lock.yml
vendored
Normal file
21
.github/workflows/lock.yml
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
# lock-threads (https://github.com/marketplace/actions/lock-threads)
|
||||
name: 'Lock threads'
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 3 * * *'
|
||||
|
||||
jobs:
|
||||
lock:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: dessant/lock-threads@v2
|
||||
with:
|
||||
github-token: ${{ github.token }}
|
||||
issue-lock-inactive-days: '90'
|
||||
issue-exclude-created-before: ''
|
||||
issue-exclude-labels: ''
|
||||
issue-lock-labels: ''
|
||||
issue-lock-comment: ''
|
||||
issue-lock-reason: 'resolved'
|
||||
process-only: 'issues'
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -9,6 +9,7 @@
|
||||
/netbox/static
|
||||
/venv/
|
||||
/*.sh
|
||||
local_requirements.txt
|
||||
!upgrade.sh
|
||||
fabfile.py
|
||||
gunicorn.py
|
||||
|
19
.travis.yml
19
.travis.yml
@ -1,19 +0,0 @@
|
||||
sudo: required
|
||||
services:
|
||||
- postgresql
|
||||
- redis-server
|
||||
addons:
|
||||
postgresql: "9.6"
|
||||
language: python
|
||||
python:
|
||||
- "3.6"
|
||||
- "3.7"
|
||||
install:
|
||||
- pip install -r requirements.txt
|
||||
- pip install pycodestyle
|
||||
- pip install coverage
|
||||
before_script:
|
||||
- psql --version
|
||||
- psql -U postgres -c 'SELECT version();'
|
||||
script:
|
||||
- ./scripts/cibuild.sh
|
@ -4,15 +4,31 @@ If you encounter any issues installing or using NetBox, try one of the
|
||||
following resources to get assistance. Please **do not** open a GitHub issue
|
||||
except to report bugs or request features.
|
||||
|
||||
### GitHub Discussions
|
||||
|
||||
GitHub's discussions are the best place to get help or propose rough ideas for
|
||||
new functionality. Their integration with GitHub allows for easily cross-
|
||||
referencing and converting posts to issues as needed. There are several
|
||||
categories for discussions:
|
||||
|
||||
* **General** - General community discussion
|
||||
* **Ideas** - Ideas for new functionality that isn't yet ready for a formal
|
||||
feature request
|
||||
* **Q&A** - Request help with installing or using NetBox
|
||||
|
||||
### Mailing List
|
||||
|
||||
We have established a Google Groups Mailing List for issues and general
|
||||
discussion. This is the best forum for obtaining assistance with NetBox
|
||||
installation. You can find us [here](https://groups.google.com/forum/#!forum/netbox-discuss).
|
||||
We also have a Google Groups [mailing list](https://groups.google.com/g/netbox-discuss)
|
||||
for general discussion, however we're encouraging people to use GitHub
|
||||
discussions where possible, as it's much easier for newcomers to review past
|
||||
discussions.
|
||||
|
||||
### Slack
|
||||
|
||||
For real-time discussion, you can join the #netbox Slack channel on [NetworkToCode](https://slack.networktocode.com/).
|
||||
For real-time chat, you can join the **#netbox** Slack channel on [NetworkToCode](https://slack.networktocode.com/).
|
||||
Unfortunately, the Slack channel does not provide long-term retention of chat
|
||||
history, so try to avoid it for any discussions would benefit from being
|
||||
preserved for future reference.
|
||||
|
||||
## Reporting Bugs
|
||||
|
||||
@ -93,20 +109,32 @@ appropriate labels will be applied for categorization.
|
||||
|
||||
## Submitting Pull Requests
|
||||
|
||||
* If you're interested in contributing to NetBox, be sure to check out our
|
||||
[getting started](https://netbox.readthedocs.io/en/stable/development/getting-started/)
|
||||
documentation for tips on setting up your development environment.
|
||||
|
||||
* Be sure to open an issue **before** starting work on a pull request, and
|
||||
discuss your idea with the NetBox maintainers before beginning work. This will
|
||||
help prevent wasting time on something that might we might not be able to
|
||||
implement. When suggesting a new feature, also make sure it won't conflict with
|
||||
any work that's already in progress.
|
||||
|
||||
* Any pull request which does _not_ relate to an accepted issue will be closed.
|
||||
* Once you've opened or identified an issue you'd like to work on, ask that it
|
||||
be assigned to you so that others are aware it's being worked on. A maintainer
|
||||
will then mark the issue as "accepted."
|
||||
|
||||
* All major new functionality must include relevant tests where applicable.
|
||||
* Any pull request which does _not_ relate to an **accepted** issue will be closed.
|
||||
|
||||
* All new functionality must include relevant tests where applicable.
|
||||
|
||||
* When submitting a pull request, please be sure to work off of the `develop`
|
||||
branch, rather than `master`. The `develop` branch is used for ongoing
|
||||
development, while `master` is used for tagging stable releases.
|
||||
|
||||
* In most cases, it is not necessary to add a changelog entry: A maintainer will
|
||||
take care of this when the PR is merged. (This helps avoid merge conflicts
|
||||
resulting from multiple PRs being submitted simultaneously.)
|
||||
|
||||
* All code submissions should meet the following criteria (CI will enforce
|
||||
these checks):
|
||||
|
||||
@ -132,18 +160,17 @@ accumulating a large backlog of work.
|
||||
The core maintainers group has chosen to make use of GitHub's [Stale bot](https://github.com/apps/stale)
|
||||
to aid in issue management.
|
||||
|
||||
* Issues will be marked as stale after 14 days of no activity.
|
||||
* Then after 7 more days of inactivity, the issue will be closed.
|
||||
* Issues will be marked as stale after 45 days of no activity.
|
||||
* Then after 15 more days of inactivity, the issue will be closed.
|
||||
* Any issue bearing one of the following labels will be exempt from all Stale
|
||||
bot actions:
|
||||
* `status: accepted`
|
||||
* `status: gathering feedback`
|
||||
* `status: blocked`
|
||||
* `status: needs milestone`
|
||||
|
||||
It is natural that some new issues get more attention than others. Often this
|
||||
is a metric of an issues's overall value to the project. In other cases in
|
||||
which issues merely get lost in the shuffle, notifications from Stale bot can
|
||||
bring renewed attention to potentially meaningful issues.
|
||||
It is natural that some new issues get more attention than others. Stale bot
|
||||
helps bring renewed attention to potentially valuable issues that may have been
|
||||
overlooked.
|
||||
|
||||
## Maintainer Guidance
|
||||
|
||||
@ -160,8 +187,8 @@ bring renewed attention to potentially meaningful issues.
|
||||
|
||||
* Official channels for communication include:
|
||||
|
||||
* GitHub issues/pull requests
|
||||
* The [netbox-discuss](https://groups.google.com/forum/#!forum/netbox-discuss) mailing list
|
||||
* GitHub issues, pull requests, and discussions
|
||||
* The [netbox-discuss](https://groups.google.com/g/netbox-discuss) mailing list
|
||||
* The **#netbox** channel on [NetworkToCode Slack](https://networktocode.slack.com/)
|
||||
|
||||
* Maintainers with no substantial recorded activity in a 60-day period will be
|
||||
|
29
README.md
29
README.md
@ -7,20 +7,20 @@ to address the needs of network and infrastructure engineers. It is intended to
|
||||
function as a domain-specific source of truth for network operations.
|
||||
|
||||
NetBox runs as a web application atop the [Django](https://www.djangoproject.com/)
|
||||
Python framework with a [PostgreSQL](http://www.postgresql.org/) database. For a
|
||||
Python framework with a [PostgreSQL](https://www.postgresql.org/) database. For a
|
||||
complete list of requirements, see `requirements.txt`. The code is available [on GitHub](https://github.com/netbox-community/netbox).
|
||||
|
||||
The complete documentation for NetBox can be found at [Read the Docs](http://netbox.readthedocs.io/en/stable/).
|
||||
The complete documentation for NetBox can be found at [Read the Docs](https://netbox.readthedocs.io/en/stable/).
|
||||
|
||||
Questions? Comments? Please subscribe to [the netbox-discuss mailing list](https://groups.google.com/forum/#!forum/netbox-discuss),
|
||||
or join us in the #netbox Slack channel on [NetworkToCode](https://networktocode.slack.com/)!
|
||||
Questions? Comments? Please start a [discussion on GitHub](https://github.com/netbox-community/netbox/discussions),
|
||||
or join us in the **#netbox** Slack channel on [NetworkToCode](https://networktocode.slack.com/)!
|
||||
|
||||
### Build Status
|
||||
|
||||
| | status |
|
||||
|-------------|------------|
|
||||
| **master** | [](https://travis-ci.com/netbox-community/netbox/) |
|
||||
| **develop** | [](https://travis-ci.com/netbox-community/netbox/) |
|
||||
| **master** |  |
|
||||
| **develop** |  |
|
||||
|
||||
### Screenshots
|
||||
|
||||
@ -36,19 +36,22 @@ or join us in the #netbox Slack channel on [NetworkToCode](https://networktocode
|
||||
|
||||
## Installation
|
||||
|
||||
Please see [the documentation](http://netbox.readthedocs.io/en/stable/) for
|
||||
instructions on installing NetBox. To upgrade NetBox, please download the [latest release](https://github.com/netbox-community/netbox/releases)
|
||||
and run `upgrade.sh`.
|
||||
Please see [the documentation](https://netbox.readthedocs.io/en/stable/) for
|
||||
instructions on installing NetBox. To upgrade NetBox, please download the
|
||||
[latest release](https://github.com/netbox-community/netbox/releases) and
|
||||
run `upgrade.sh`.
|
||||
|
||||
## Providing Feedback
|
||||
|
||||
Feature requests and bug reports must be submitted as GiHub issues. (Please be
|
||||
sure to use the [appropriate template](https://github.com/netbox-community/netbox/issues/new/choose).)
|
||||
For general discussion, please consider joining our [mailing list](https://groups.google.com/forum/#!forum/netbox-discuss).
|
||||
The best platform for general feedback, assistance, and other discussion is our
|
||||
[GitHub discussions](https://github.com/netbox-community/netbox/discussions).
|
||||
To report a bug or request a specific feature, please open a GitHub issue using
|
||||
the [appropriate template](https://github.com/netbox-community/netbox/issues/new/choose).
|
||||
|
||||
If you are interested in contributing to the development of NetBox, please read
|
||||
our [contributing guide](CONTRIBUTING.md) prior to beginning any work.
|
||||
|
||||
## Related projects
|
||||
|
||||
Please see [our wiki](https://github.com/netbox-community/netbox/wiki/Community-Contributions) for a list of relevant community projects.
|
||||
Please see [our wiki](https://github.com/netbox-community/netbox/wiki/Community-Contributions)
|
||||
for a list of relevant community projects.
|
||||
|
@ -42,10 +42,6 @@ django-tables2
|
||||
# https://github.com/alex/django-taggit
|
||||
django-taggit
|
||||
|
||||
# A Django REST Framework serializer which represents tags
|
||||
# https://github.com/glemmaPaul/django-taggit-serializer
|
||||
django-taggit-serializer
|
||||
|
||||
# A Django field for representing time zones
|
||||
# https://github.com/mfogel/django-timezone-field/
|
||||
django-timezone-field
|
||||
|
@ -1,37 +1,45 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# wait shortly and then run db migrations (retry on error)
|
||||
while ! ./manage.py migrate 2>&1; do
|
||||
echo "⏳ Waiting on DB..."
|
||||
sleep 3
|
||||
done
|
||||
# Allows Netbox to be run as non-root users
|
||||
umask 002
|
||||
|
||||
# create superuser silently
|
||||
if [ -z ${SUPERUSER_NAME+x} ]; then
|
||||
SUPERUSER_NAME='admin'
|
||||
# Try to connect to the DB
|
||||
DB_WAIT_TIMEOUT=${DB_WAIT_TIMEOUT-3}
|
||||
MAX_DB_WAIT_TIME=${MAX_DB_WAIT_TIME-30}
|
||||
CUR_DB_WAIT_TIME=0
|
||||
while ! ./manage.py migrate 2>&1 && [ "${CUR_DB_WAIT_TIME}" -lt "${MAX_DB_WAIT_TIME}" ]; do
|
||||
echo "⏳ Waiting on DB... (${CUR_DB_WAIT_TIME}s / ${MAX_DB_WAIT_TIME}s)"
|
||||
sleep "${DB_WAIT_TIMEOUT}"
|
||||
CUR_DB_WAIT_TIME=$(( CUR_DB_WAIT_TIME + DB_WAIT_TIMEOUT ))
|
||||
done
|
||||
if [ "${CUR_DB_WAIT_TIME}" -ge "${MAX_DB_WAIT_TIME}" ]; then
|
||||
echo "❌ Waited ${MAX_DB_WAIT_TIME}s or more for the DB to become ready."
|
||||
exit 1
|
||||
fi
|
||||
if [ -z ${SUPERUSER_EMAIL+x} ]; then
|
||||
SUPERUSER_EMAIL='admin@example.com'
|
||||
fi
|
||||
if [ -z ${SUPERUSER_PASSWORD+x} ]; then
|
||||
|
||||
# Create Superuser if required
|
||||
if [ "$SKIP_SUPERUSER" == "true" ]; then
|
||||
echo "↩️ Skip creating the superuser"
|
||||
else
|
||||
if [ -z ${SUPERUSER_NAME+x} ]; then
|
||||
SUPERUSER_NAME='admin'
|
||||
fi
|
||||
if [ -z ${SUPERUSER_EMAIL+x} ]; then
|
||||
SUPERUSER_EMAIL='admin@example.com'
|
||||
fi
|
||||
if [ -f "/run/secrets/superuser_password" ]; then
|
||||
SUPERUSER_PASSWORD="$(< /run/secrets/superuser_password)"
|
||||
else
|
||||
elif [ -z ${SUPERUSER_PASSWORD+x} ]; then
|
||||
SUPERUSER_PASSWORD='admin'
|
||||
fi
|
||||
fi
|
||||
if [ -z ${SUPERUSER_API_TOKEN+x} ]; then
|
||||
if [ -f "/run/secrets/superuser_api_token" ]; then
|
||||
SUPERUSER_API_TOKEN="$(< /run/secrets/superuser_api_token)"
|
||||
else
|
||||
elif [ -z ${SUPERUSER_API_TOKEN+x} ]; then
|
||||
SUPERUSER_API_TOKEN='0123456789abcdef0123456789abcdef01234567'
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "💡 Username: ${SUPERUSER_NAME}, E-Mail: ${SUPERUSER_EMAIL}"
|
||||
|
||||
./manage.py shell --interface python << END
|
||||
./manage.py shell --interface python << END
|
||||
from django.contrib.auth.models import User
|
||||
from users.models import Token
|
||||
if not User.objects.filter(username='${SUPERUSER_NAME}'):
|
||||
@ -39,17 +47,15 @@ if not User.objects.filter(username='${SUPERUSER_NAME}'):
|
||||
Token.objects.create(user=u, key='${SUPERUSER_API_TOKEN}')
|
||||
END
|
||||
|
||||
startup_scripts=/opt/netbox/startup_scripts/*.py
|
||||
|
||||
if [ "$SKIP_STARTUP_SCRIPTS" == "true" ]; then
|
||||
echo "☇ Skipping startup scripts"
|
||||
startup_scripts=/opt/netbox/startup_scripts/*required*.py
|
||||
echo "💡 Superuser Username: ${SUPERUSER_NAME}, E-Mail: ${SUPERUSER_EMAIL}"
|
||||
fi
|
||||
|
||||
for script in $startup_scripts; do
|
||||
echo "⚙️ Executing '$script'"
|
||||
./manage.py shell --interface python < "${script}"
|
||||
done
|
||||
# Run the startup scripts (and initializers)
|
||||
if [ "$SKIP_STARTUP_SCRIPTS" == "true" ]; then
|
||||
echo "↩️ Skipping startup scripts"
|
||||
else
|
||||
echo "import runpy; runpy.run_path('../startup_scripts')" | ./manage.py shell --interface python
|
||||
fi
|
||||
|
||||
# copy static files
|
||||
./manage.py collectstatic --no-input
|
||||
@ -58,4 +64,4 @@ echo "✅ Initialisation is done."
|
||||
|
||||
# launch whatever is passed by docker
|
||||
# (i.e. the RUN instruction in the Dockerfile)
|
||||
exec ${@}
|
||||
exec $@
|
||||
|
2
docker/env/netbox.env
vendored
2
docker/env/netbox.env
vendored
@ -21,7 +21,7 @@ REDIS_CACHE_HOST=redis-cache
|
||||
REDIS_CACHE_DATABASE=0
|
||||
REDIS_CACHE_SSL=false
|
||||
SECRET_KEY=r8OwDznj!!dci#P9ghmRfdu1Ysxm0AiPeDCQhKE+N_rClfWNj
|
||||
SKIP_STARTUP_SCRIPTS=false
|
||||
SKIP_STARTUP_SCRIPTS=true
|
||||
SKIP_SUPERUSER=false
|
||||
SUPERUSER_NAME=admin
|
||||
SUPERUSER_EMAIL=admin@example.com
|
||||
|
@ -8,7 +8,7 @@
|
||||
face: front
|
||||
name: EFR2.EMUL8R.000
|
||||
site: Local Emulator (EMUL8R.000)
|
||||
- custom_fields:
|
||||
- custom_field_data:
|
||||
sf_id: a6d8c2a0-b4cf-4940-a53f-e356354d78c9
|
||||
device_role: Customer Compute Locker
|
||||
device_type: 9U Locker
|
||||
@ -18,7 +18,7 @@
|
||||
rack: R1 Zone A
|
||||
site: Local Emulator (EMUL8R.000)
|
||||
tenant: vaportest
|
||||
- custom_fields:
|
||||
- custom_field_data:
|
||||
sf_id: 6ba5ba20-5c2d-4210-adee-46e425756b43
|
||||
device_role: Customer Network Locker
|
||||
device_type: 9U Locker
|
||||
@ -27,7 +27,7 @@
|
||||
position: 1
|
||||
rack: R1 Zone B
|
||||
site: Local Emulator (EMUL8R.000)
|
||||
- custom_fields:
|
||||
- custom_field_data:
|
||||
sf_id: 34f87c0c-5009-4c3f-99aa-26a9ade66294
|
||||
device_role: Customer Compute Locker
|
||||
device_type: 9U Locker
|
||||
@ -37,7 +37,7 @@
|
||||
rack: R1 Zone A
|
||||
site: Local Emulator (EMUL8R.000)
|
||||
tenant: VaporProvider
|
||||
- custom_fields:
|
||||
- custom_field_data:
|
||||
sf_id: 62d02859-b027-4cc8-afea-492e67525e49
|
||||
device_role: Customer Network Locker
|
||||
device_type: 9U Locker
|
||||
@ -47,7 +47,7 @@
|
||||
rack: R1 Zone B
|
||||
site: Local Emulator (EMUL8R.000)
|
||||
tenant: VaporProvider
|
||||
- custom_fields:
|
||||
- custom_field_data:
|
||||
sf_id: 7ecacf66-0b0b-4bca-909f-92888560209a
|
||||
device_role: Customer Compute Locker
|
||||
device_type: 9U Locker
|
||||
@ -57,7 +57,7 @@
|
||||
rack: R1 Zone A
|
||||
site: Local Emulator (EMUL8R.000)
|
||||
tenant: VaporProvider
|
||||
- custom_fields:
|
||||
- custom_field_data:
|
||||
sf_id: b9bf83a8-1ebf-45ce-8ffb-978870e32ddd
|
||||
device_role: Customer Network Locker
|
||||
device_type: 9U Locker
|
||||
@ -67,7 +67,7 @@
|
||||
rack: R1 Zone B
|
||||
site: Local Emulator (EMUL8R.000)
|
||||
tenant: VaporProvider
|
||||
- custom_fields:
|
||||
- custom_field_data:
|
||||
sf_id: 910b806a-737e-409f-a54a-847acdfd3e83
|
||||
device_role: Customer Compute Locker
|
||||
device_type: 9U Locker
|
||||
@ -76,7 +76,7 @@
|
||||
position: 28
|
||||
rack: R1 Zone A
|
||||
site: Local Emulator (EMUL8R.000)
|
||||
- custom_fields:
|
||||
- custom_field_data:
|
||||
sf_id: 8a6824a0-dd8c-4b8a-a258-0127a9898ecf
|
||||
device_role: Customer Network Locker
|
||||
device_type: 9U Locker
|
||||
@ -85,7 +85,7 @@
|
||||
position: 28
|
||||
rack: R1 Zone B
|
||||
site: Local Emulator (EMUL8R.000)
|
||||
- custom_fields:
|
||||
- custom_field_data:
|
||||
sf_id: c5fc8fa1-e713-411b-80e5-92f2fef62bdd
|
||||
device_role: Customer Compute Locker
|
||||
device_type: 9U Locker
|
||||
@ -94,7 +94,7 @@
|
||||
position: 1
|
||||
rack: R2 Zone A
|
||||
site: Local Emulator (EMUL8R.000)
|
||||
- custom_fields:
|
||||
- custom_field_data:
|
||||
sf_id: a9678550-58b4-41ed-8670-fce340145bc4
|
||||
device_role: Customer Network Locker
|
||||
device_type: 9U Locker
|
||||
@ -103,7 +103,7 @@
|
||||
position: 1
|
||||
rack: R2 Zone B
|
||||
site: Local Emulator (EMUL8R.000)
|
||||
- custom_fields:
|
||||
- custom_field_data:
|
||||
sf_id: 32ec365c-ed18-4ac7-bc5e-ad7653b20863
|
||||
device_role: Customer Compute Locker
|
||||
device_type: 9U Locker
|
||||
@ -113,7 +113,7 @@
|
||||
rack: R2 Zone A
|
||||
site: Local Emulator (EMUL8R.000)
|
||||
tenant: vaportest
|
||||
- custom_fields:
|
||||
- custom_field_data:
|
||||
sf_id: 36249382-fe42-4212-8497-de70422238f7
|
||||
device_role: Customer Network Locker
|
||||
device_type: 9U Locker
|
||||
@ -123,7 +123,7 @@
|
||||
rack: R2 Zone B
|
||||
site: Local Emulator (EMUL8R.000)
|
||||
tenant: vaportest
|
||||
- custom_fields:
|
||||
- custom_field_data:
|
||||
sf_id: e95e48fd-790e-4033-9b4a-9a2b4d5b2588
|
||||
device_role: Customer Compute Locker
|
||||
device_type: 9U Locker
|
||||
@ -132,7 +132,7 @@
|
||||
position: 19
|
||||
rack: R2 Zone A
|
||||
site: Local Emulator (EMUL8R.000)
|
||||
- custom_fields:
|
||||
- custom_field_data:
|
||||
sf_id: b9f7b04c-fb2a-4549-b481-f47b7017fc84
|
||||
device_role: Customer Network Locker
|
||||
device_type: 9U Locker
|
||||
@ -142,7 +142,7 @@
|
||||
rack: R2 Zone B
|
||||
site: Local Emulator (EMUL8R.000)
|
||||
tenant: vaportest
|
||||
- custom_fields:
|
||||
- custom_field_data:
|
||||
sf_id: 136bcb0c-7177-4601-9f81-5df7fdce3829
|
||||
device_role: Customer Compute Locker
|
||||
device_type: 9U Locker
|
||||
@ -152,7 +152,7 @@
|
||||
rack: R2 Zone A
|
||||
site: Local Emulator (EMUL8R.000)
|
||||
tenant: vaportest
|
||||
- custom_fields:
|
||||
- custom_field_data:
|
||||
sf_id: e189281e-e2e7-49fd-a2a1-62f7082aca83
|
||||
device_role: Customer Network Locker
|
||||
device_type: 9U Locker
|
||||
@ -162,7 +162,7 @@
|
||||
rack: R2 Zone B
|
||||
site: Local Emulator (EMUL8R.000)
|
||||
tenant: vaportest
|
||||
- custom_fields:
|
||||
- custom_field_data:
|
||||
sf_id: efafbd9e-6b48-429d-8d58-26b2fda96f58
|
||||
device_role: Customer Compute Locker
|
||||
device_type: 9U Locker
|
||||
@ -172,7 +172,7 @@
|
||||
rack: R3 Zone A
|
||||
site: Local Emulator (EMUL8R.000)
|
||||
tenant: vaportest
|
||||
- custom_fields:
|
||||
- custom_field_data:
|
||||
sf_id: 3fda0ec6-4d06-4500-a9a7-4e5266b500e5
|
||||
device_role: Customer Network Locker
|
||||
device_type: 9U Locker
|
||||
@ -182,7 +182,7 @@
|
||||
rack: R3 Zone B
|
||||
site: Local Emulator (EMUL8R.000)
|
||||
tenant: vaportest
|
||||
- custom_fields:
|
||||
- custom_field_data:
|
||||
sf_id: a29efaca-08c5-403e-a352-30237a675f1c
|
||||
device_role: Customer Compute Locker
|
||||
device_type: 9U Locker
|
||||
@ -192,7 +192,7 @@
|
||||
rack: R3 Zone A
|
||||
site: Local Emulator (EMUL8R.000)
|
||||
tenant: vaportest
|
||||
- custom_fields:
|
||||
- custom_field_data:
|
||||
sf_id: 2ed96b2f-f1d5-4604-843d-32f0e98b65ca
|
||||
device_role: Customer Network Locker
|
||||
device_type: 9U Locker
|
||||
@ -202,7 +202,7 @@
|
||||
rack: R3 Zone B
|
||||
site: Local Emulator (EMUL8R.000)
|
||||
tenant: vaportest
|
||||
- custom_fields:
|
||||
- custom_field_data:
|
||||
sf_id: f6f52fcf-9f42-4070-9062-3b1339d19841
|
||||
device_role: Customer Compute Locker
|
||||
device_type: 9U Locker
|
||||
@ -212,7 +212,7 @@
|
||||
rack: R3 Zone A
|
||||
site: Local Emulator (EMUL8R.000)
|
||||
tenant: vaportest
|
||||
- custom_fields:
|
||||
- custom_field_data:
|
||||
sf_id: 7792510a-192a-4809-b982-2a852c57ddd1
|
||||
device_role: Customer Network Locker
|
||||
device_type: 9U Locker
|
||||
@ -222,7 +222,7 @@
|
||||
rack: R3 Zone B
|
||||
site: Local Emulator (EMUL8R.000)
|
||||
tenant: vaportest
|
||||
- custom_fields:
|
||||
- custom_field_data:
|
||||
sf_id: d60d3b5e-62d4-496d-a02a-1dc6a70bd39b
|
||||
device_role: Customer Compute Locker
|
||||
device_type: 9U Locker
|
||||
@ -232,7 +232,7 @@
|
||||
rack: R3 Zone A
|
||||
site: Local Emulator (EMUL8R.000)
|
||||
tenant: vaportest
|
||||
- custom_fields:
|
||||
- custom_field_data:
|
||||
sf_id: 6023f5dd-0bc1-4a16-987d-994141032a5e
|
||||
device_role: Customer Network Locker
|
||||
device_type: 9U Locker
|
||||
@ -242,7 +242,7 @@
|
||||
rack: R3 Zone B
|
||||
site: Local Emulator (EMUL8R.000)
|
||||
tenant: vaportest
|
||||
- custom_fields:
|
||||
- custom_field_data:
|
||||
sf_id: ac7561d2-4c37-42c6-843b-749e8957c1e0
|
||||
device_role: Customer Compute Locker
|
||||
device_type: 9U Locker
|
||||
@ -252,7 +252,7 @@
|
||||
rack: R4 Zone A
|
||||
site: Local Emulator (EMUL8R.000)
|
||||
tenant: vaportest
|
||||
- custom_fields:
|
||||
- custom_field_data:
|
||||
sf_id: 628de521-b70f-4597-8f54-a3d241d67c8a
|
||||
device_role: Customer Network Locker
|
||||
device_type: 9U Locker
|
||||
@ -262,7 +262,7 @@
|
||||
rack: R4 Zone B
|
||||
site: Local Emulator (EMUL8R.000)
|
||||
tenant: vaportest
|
||||
- custom_fields:
|
||||
- custom_field_data:
|
||||
sf_id: 16d10add-bcaa-4abd-b269-c73892567607
|
||||
device_role: Customer Compute Locker
|
||||
device_type: 9U Locker
|
||||
@ -272,7 +272,7 @@
|
||||
rack: R4 Zone A
|
||||
site: Local Emulator (EMUL8R.000)
|
||||
tenant: vaportest
|
||||
- custom_fields:
|
||||
- custom_field_data:
|
||||
sf_id: 73243081-2e35-42be-b2a6-dcf664a7d4ba
|
||||
device_role: Customer Network Locker
|
||||
device_type: 9U Locker
|
||||
@ -282,7 +282,7 @@
|
||||
rack: R4 Zone B
|
||||
site: Local Emulator (EMUL8R.000)
|
||||
tenant: vaportest
|
||||
- custom_fields:
|
||||
- custom_field_data:
|
||||
sf_id: d7b70ba7-0ef9-40da-a35e-be03e649706a
|
||||
device_role: Customer Compute Locker
|
||||
device_type: 9U Locker
|
||||
@ -292,7 +292,7 @@
|
||||
rack: R4 Zone A
|
||||
site: Local Emulator (EMUL8R.000)
|
||||
tenant: vaportest
|
||||
- custom_fields:
|
||||
- custom_field_data:
|
||||
sf_id: 79fe0fd4-0f03-4f46-9b4a-a4634277fc2a
|
||||
device_role: Customer Network Locker
|
||||
device_type: 9U Locker
|
||||
@ -302,7 +302,7 @@
|
||||
rack: R4 Zone B
|
||||
site: Local Emulator (EMUL8R.000)
|
||||
tenant: vaportest
|
||||
- custom_fields:
|
||||
- custom_field_data:
|
||||
sf_id: faf2e908-2b4b-4d87-9cbf-a8c7cae110fc
|
||||
device_role: Customer Compute Locker
|
||||
device_type: 9U Locker
|
||||
@ -312,7 +312,7 @@
|
||||
rack: R4 Zone A
|
||||
site: Local Emulator (EMUL8R.000)
|
||||
tenant: vaportest
|
||||
- custom_fields:
|
||||
- custom_field_data:
|
||||
sf_id: cc00b009-1d1f-40b1-99ea-f5ab1f155779
|
||||
device_role: Customer Network Locker
|
||||
device_type: 9U Locker
|
||||
@ -322,7 +322,7 @@
|
||||
rack: R4 Zone B
|
||||
site: Local Emulator (EMUL8R.000)
|
||||
tenant: vaportest
|
||||
- custom_fields:
|
||||
- custom_field_data:
|
||||
sf_id: fb13c35a-1780-48d3-92da-ff66a9b9bf08
|
||||
device_role: Customer Compute Locker
|
||||
device_type: 9U Locker
|
||||
@ -332,7 +332,7 @@
|
||||
rack: R5 Zone A
|
||||
site: Local Emulator (EMUL8R.000)
|
||||
tenant: vaportest
|
||||
- custom_fields:
|
||||
- custom_field_data:
|
||||
sf_id: abd3767e-9a1b-4f34-8e4b-1255d0eef4d8
|
||||
device_role: Customer Network Locker
|
||||
device_type: 9U Locker
|
||||
@ -342,7 +342,7 @@
|
||||
rack: R5 Zone B
|
||||
site: Local Emulator (EMUL8R.000)
|
||||
tenant: vaportest
|
||||
- custom_fields:
|
||||
- custom_field_data:
|
||||
sf_id: bda2f9d9-9364-42ba-974b-76f2fbf2ccd7
|
||||
device_role: Customer Compute Locker
|
||||
device_type: 9U Locker
|
||||
@ -352,7 +352,7 @@
|
||||
rack: R5 Zone A
|
||||
site: Local Emulator (EMUL8R.000)
|
||||
tenant: vaportest
|
||||
- custom_fields:
|
||||
- custom_field_data:
|
||||
sf_id: 39740509-f89b-4c82-884e-ae1bb87c88f5
|
||||
device_role: Customer Network Locker
|
||||
device_type: 9U Locker
|
||||
@ -362,7 +362,7 @@
|
||||
rack: R5 Zone B
|
||||
site: Local Emulator (EMUL8R.000)
|
||||
tenant: vaportest
|
||||
- custom_fields:
|
||||
- custom_field_data:
|
||||
sf_id: 9eb87161-7df5-45d1-8ca0-356550f7f024
|
||||
device_role: Customer Compute Locker
|
||||
device_type: 9U Locker
|
||||
@ -372,7 +372,7 @@
|
||||
rack: R5 Zone A
|
||||
site: Local Emulator (EMUL8R.000)
|
||||
tenant: vaportest
|
||||
- custom_fields:
|
||||
- custom_field_data:
|
||||
sf_id: cf7d4e9d-6803-4587-8cd1-be27309d1414
|
||||
device_role: Customer Network Locker
|
||||
device_type: 9U Locker
|
||||
@ -382,7 +382,7 @@
|
||||
rack: R5 Zone B
|
||||
site: Local Emulator (EMUL8R.000)
|
||||
tenant: vaportest
|
||||
- custom_fields:
|
||||
- custom_field_data:
|
||||
sf_id: b5103b46-4467-49c5-9b65-1a0285d41422
|
||||
device_role: Customer Compute Locker
|
||||
device_type: 9U Locker
|
||||
@ -392,7 +392,7 @@
|
||||
rack: R5 Zone A
|
||||
site: Local Emulator (EMUL8R.000)
|
||||
tenant: vaportest
|
||||
- custom_fields:
|
||||
- custom_field_data:
|
||||
sf_id: da922d8c-d491-4b15-a737-ad7649482188
|
||||
device_role: Customer Network Locker
|
||||
device_type: 9U Locker
|
||||
@ -402,7 +402,7 @@
|
||||
rack: R5 Zone B
|
||||
site: Local Emulator (EMUL8R.000)
|
||||
tenant: vaportest
|
||||
- custom_fields:
|
||||
- custom_field_data:
|
||||
sf_id: d0c11e46-ace9-43d6-a545-ada55eb05b03
|
||||
device_role: Customer Compute Locker
|
||||
device_type: 9U Locker
|
||||
@ -412,7 +412,7 @@
|
||||
rack: R6 Zone A
|
||||
site: Local Emulator (EMUL8R.000)
|
||||
tenant: vaportest
|
||||
- custom_fields:
|
||||
- custom_field_data:
|
||||
sf_id: 1bca6a2e-0810-404b-8eaa-f3ad12165894
|
||||
device_role: Customer Network Locker
|
||||
device_type: 9U Locker
|
||||
@ -422,7 +422,7 @@
|
||||
rack: R6 Zone B
|
||||
site: Local Emulator (EMUL8R.000)
|
||||
tenant: vaportest
|
||||
- custom_fields:
|
||||
- custom_field_data:
|
||||
sf_id: ca2fd52a-053e-4883-aafa-a11e8da4c108
|
||||
device_role: Customer Compute Locker
|
||||
device_type: 9U Locker
|
||||
@ -432,7 +432,7 @@
|
||||
rack: R6 Zone A
|
||||
site: Local Emulator (EMUL8R.000)
|
||||
tenant: vaportest
|
||||
- custom_fields:
|
||||
- custom_field_data:
|
||||
sf_id: a5ef8dc0-01cf-4648-8cd3-3ba29c7d9eb9
|
||||
device_role: Customer Network Locker
|
||||
device_type: 9U Locker
|
||||
@ -442,7 +442,7 @@
|
||||
rack: R6 Zone B
|
||||
site: Local Emulator (EMUL8R.000)
|
||||
tenant: vaportest
|
||||
- custom_fields:
|
||||
- custom_field_data:
|
||||
sf_id: 3d3328fa-3853-4cf7-afff-2b174f5f1c60
|
||||
device_role: Customer Compute Locker
|
||||
device_type: 9U Locker
|
||||
@ -452,7 +452,7 @@
|
||||
rack: R6 Zone A
|
||||
site: Local Emulator (EMUL8R.000)
|
||||
tenant: vaportest
|
||||
- custom_fields:
|
||||
- custom_field_data:
|
||||
sf_id: 52bba559-5fec-4640-af10-ce358240f1d2
|
||||
device_role: Customer Network Locker
|
||||
device_type: 9U Locker
|
||||
@ -462,7 +462,7 @@
|
||||
rack: R6 Zone B
|
||||
site: Local Emulator (EMUL8R.000)
|
||||
tenant: vaportest
|
||||
- custom_fields:
|
||||
- custom_field_data:
|
||||
sf_id: 1536cf6e-991a-44c0-88f0-bdce489adb4c
|
||||
device_role: Customer Compute Locker
|
||||
device_type: 9U Locker
|
||||
@ -472,7 +472,7 @@
|
||||
rack: R6 Zone A
|
||||
site: Local Emulator (EMUL8R.000)
|
||||
tenant: vaportest
|
||||
- custom_fields:
|
||||
- custom_field_data:
|
||||
sf_id: 8d3cd060-ff10-4b97-b51c-4c524895aeb7
|
||||
device_role: Customer Network Locker
|
||||
device_type: 9U Locker
|
||||
|
@ -3,26 +3,26 @@
|
||||
slug: EMUL8R-000
|
||||
region: 000 (000)
|
||||
status: active
|
||||
custom_fields:
|
||||
custom_field_data:
|
||||
sf_id: 51f4ca13-2658-4169-8a06-51f87d62b16f
|
||||
|
||||
- name: Future Pending Site
|
||||
slug: FUT-000
|
||||
region: GCP (GCP)
|
||||
status: active
|
||||
custom_fields:
|
||||
custom_field_data:
|
||||
sf_id: 73727fc7-138b-4cbe-a184-311500cb3992
|
||||
|
||||
- name: Virtual VEM-150
|
||||
slug: virt-01
|
||||
region: GCP (GCP)
|
||||
status: active
|
||||
custom_fields:
|
||||
custom_field_data:
|
||||
sf_id: 5b385220-c9d6-11e9-a32f-2a2ae2dbcce4
|
||||
|
||||
- name: Cloud Virtual Chamber
|
||||
slug: cloud-virtual-chamber
|
||||
status: active
|
||||
region: GCP (GCP)
|
||||
custom_fields:
|
||||
custom_field_data:
|
||||
sf_id: 2e3b4b3d-111e-41f8-aef8-8ebdf19dfbf8
|
||||
|
@ -1,34 +1,23 @@
|
||||
from django.contrib.auth.models import Permission, Group, User
|
||||
from users.models import Token
|
||||
|
||||
from ruamel.yaml import YAML
|
||||
from pathlib import Path
|
||||
import sys
|
||||
|
||||
file = Path('/opt/netbox/initializers/users.yml')
|
||||
if not file.is_file():
|
||||
from django.contrib.auth.models import User
|
||||
from startup_script_utils import load_yaml, set_permissions
|
||||
from users.models import Token
|
||||
|
||||
users = load_yaml('/opt/netbox/initializers/users.yml')
|
||||
if users is None:
|
||||
sys.exit()
|
||||
|
||||
with file.open('r') as stream:
|
||||
yaml=YAML(typ='safe')
|
||||
users = yaml.load(stream)
|
||||
for username, user_details in users.items():
|
||||
if not User.objects.filter(username=username):
|
||||
user = User.objects.create_user(
|
||||
username = username,
|
||||
password = user_details.get('password', 0) or User.objects.make_random_password())
|
||||
|
||||
if users is not None:
|
||||
for username, user_details in users.items():
|
||||
if not User.objects.filter(username=username):
|
||||
user = User.objects.create_user(
|
||||
username = username,
|
||||
password = user_details.get('password', 0) or User.objects.make_random_password)
|
||||
print("👤 Created user",username)
|
||||
|
||||
print("👤 Created user ",username)
|
||||
if user_details.get('api_token', 0):
|
||||
Token.objects.create(user=user, key=user_details['api_token'])
|
||||
|
||||
if user_details.get('api_token', 0):
|
||||
Token.objects.create(user=user, key=user_details['api_token'])
|
||||
|
||||
user_permissions = user_details.get('permissions', [])
|
||||
if user_permissions:
|
||||
user.user_permissions.clear()
|
||||
for permission_codename in user_details.get('permissions', []):
|
||||
for permission in Permission.objects.filter(codename=permission_codename):
|
||||
user.user_permissions.add(permission)
|
||||
user.save()
|
||||
yaml_permissions = user_details.get('permissions', [])
|
||||
set_permissions(user.user_permissions, yaml_permissions)
|
||||
|
@ -1,20 +1,30 @@
|
||||
from tenancy.models import Tenant
|
||||
from ruamel.yaml import YAML
|
||||
from pathlib import Path
|
||||
import sys
|
||||
|
||||
file = Path('/opt/netbox/initializers/tenants.yml')
|
||||
if not file.is_file():
|
||||
from startup_script_utils import *
|
||||
from tenancy.models import Tenant, TenantGroup
|
||||
|
||||
tenants = load_yaml('/opt/netbox/initializers/tenants.yml')
|
||||
|
||||
if tenants is None:
|
||||
sys.exit()
|
||||
|
||||
with file.open('r') as stream:
|
||||
yaml = YAML(typ='safe')
|
||||
tenants = yaml.load(stream)
|
||||
optional_assocs = {
|
||||
'group': (TenantGroup, 'name')
|
||||
}
|
||||
|
||||
if tenants is not None:
|
||||
for params in tenants:
|
||||
for params in tenants:
|
||||
custom_field_data = pop_custom_fields(params)
|
||||
|
||||
tenant, created = Tenant.objects.update_or_create(name=params['name'], defaults=params)
|
||||
for assoc, details in optional_assocs.items():
|
||||
if assoc in params:
|
||||
model, field = details
|
||||
query = { field: params.pop(assoc) }
|
||||
|
||||
if created:
|
||||
print("🏠 Created tenant", tenant.name)
|
||||
params[assoc] = model.objects.get(**query)
|
||||
|
||||
tenant, created = Tenant.objects.get_or_create(**params)
|
||||
|
||||
if created:
|
||||
set_custom_fields_values(tenant, custom_field_data)
|
||||
|
||||
print("👩💻 Created Tenant", tenant.name)
|
||||
|
@ -1,33 +1,23 @@
|
||||
from django.contrib.auth.models import Permission, Group, User
|
||||
from ruamel.yaml import YAML
|
||||
from pathlib import Path
|
||||
import sys
|
||||
|
||||
file = Path('/opt/netbox/initializers/groups.yml')
|
||||
if not file.is_file():
|
||||
from django.contrib.auth.models import Group, User
|
||||
from startup_script_utils import load_yaml, set_permissions
|
||||
|
||||
groups = load_yaml('/opt/netbox/initializers/groups.yml')
|
||||
if groups is None:
|
||||
sys.exit()
|
||||
|
||||
with file.open('r') as stream:
|
||||
yaml=YAML(typ='safe')
|
||||
groups = yaml.load(stream)
|
||||
for groupname, group_details in groups.items():
|
||||
group, created = Group.objects.get_or_create(name=groupname)
|
||||
|
||||
if groups is not None:
|
||||
for groupname, group_details in groups.items():
|
||||
group, created = Group.objects.get_or_create(name=groupname)
|
||||
if created:
|
||||
print("👥 Created group", groupname)
|
||||
|
||||
if created:
|
||||
print("👥 Created group", groupname)
|
||||
for username in group_details.get('users', []):
|
||||
user = User.objects.get(username=username)
|
||||
|
||||
for username in group_details.get('users', []):
|
||||
user = User.objects.get(username=username)
|
||||
if user:
|
||||
user.groups.add(group)
|
||||
|
||||
if user:
|
||||
user.groups.add(group)
|
||||
|
||||
group_permissions = group_details.get('permissions', [])
|
||||
if group_permissions:
|
||||
group.permissions.clear()
|
||||
print("Permissions:", group.permissions.all())
|
||||
for permission_codename in group_details.get('permissions', []):
|
||||
for permission in Permission.objects.filter(codename=permission_codename):
|
||||
group.permissions.add(permission)
|
||||
yaml_permissions = group_details.get('permissions', [])
|
||||
set_permissions(group.permissions, yaml_permissions)
|
||||
|
@ -1,9 +1,8 @@
|
||||
from extras.models import CustomField, CustomFieldChoice
|
||||
|
||||
from ruamel.yaml import YAML
|
||||
from pathlib import Path
|
||||
import sys
|
||||
|
||||
from extras.models import CustomField
|
||||
from startup_script_utils import load_yaml
|
||||
|
||||
def get_class_for_class_path(class_path):
|
||||
import importlib
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
@ -13,47 +12,47 @@ def get_class_for_class_path(class_path):
|
||||
clazz = getattr(module, class_name)
|
||||
return ContentType.objects.get_for_model(clazz)
|
||||
|
||||
file = Path('/opt/netbox/initializers/custom_fields.yml')
|
||||
if not file.is_file():
|
||||
customfields = load_yaml('/opt/netbox/initializers/custom_fields.yml')
|
||||
|
||||
if customfields is None:
|
||||
sys.exit()
|
||||
|
||||
with file.open('r') as stream:
|
||||
yaml = YAML(typ='safe')
|
||||
customfields = yaml.load(stream)
|
||||
for cf_name, cf_details in customfields.items():
|
||||
custom_field, created = CustomField.objects.get_or_create(name = cf_name)
|
||||
|
||||
if customfields is not None:
|
||||
for cf_name, cf_details in customfields.items():
|
||||
custom_field, created = CustomField.objects.get_or_create(name = cf_name)
|
||||
if created:
|
||||
if cf_details.get('default', False):
|
||||
custom_field.default = cf_details['default']
|
||||
|
||||
if created:
|
||||
if cf_details.get('default', 0):
|
||||
custom_field.default = cf_details['default']
|
||||
if cf_details.get('description', False):
|
||||
custom_field.description = cf_details['description']
|
||||
|
||||
if cf_details.get('description', 0):
|
||||
custom_field.description = cf_details['description']
|
||||
if cf_details.get('label', False):
|
||||
custom_field.label = cf_details['label']
|
||||
|
||||
if cf_details.get('label', 0):
|
||||
custom_field.label = cf_details['label']
|
||||
for object_type in cf_details.get('on_objects', []):
|
||||
custom_field.content_types.add(get_class_for_class_path(object_type))
|
||||
|
||||
for object_type in cf_details.get('on_objects', []):
|
||||
custom_field.obj_type.add(get_class_for_class_path(object_type))
|
||||
if cf_details.get('required', False):
|
||||
custom_field.required = cf_details['required']
|
||||
|
||||
if cf_details.get('required', 0):
|
||||
custom_field.required = cf_details['required']
|
||||
if cf_details.get('type', False):
|
||||
custom_field.type = cf_details['type']
|
||||
|
||||
if cf_details.get('type', 0):
|
||||
custom_field.type = cf_details['type']
|
||||
if cf_details.get('weight', -1) >= 0:
|
||||
custom_field.weight = cf_details['weight']
|
||||
|
||||
if cf_details.get('weight', 0):
|
||||
custom_field.weight = cf_details['weight']
|
||||
if cf_details.get('choices', False):
|
||||
custom_field.choices = []
|
||||
|
||||
custom_field.save()
|
||||
for choice_detail in cf_details.get('choices', []):
|
||||
if isinstance(choice_detail, dict) and 'value' in choice_detail:
|
||||
# legacy mode
|
||||
print(f"⚠️ Please migrate the choice '{choice_detail['value']}' of '{cf_name}' to the new format, as 'weight' is no longer supported!")
|
||||
custom_field.choices.append(choice_detail['value'])
|
||||
else:
|
||||
custom_field.choices.append(choice_detail)
|
||||
|
||||
for idx, choice_details in enumerate(cf_details.get('choices', [])):
|
||||
choice, _ = CustomFieldChoice.objects.get_or_create(
|
||||
field=custom_field,
|
||||
value=choice_details['value'],
|
||||
defaults={'weight': idx * 10}
|
||||
)
|
||||
custom_field.save()
|
||||
|
||||
print("🔧 Created custom field", cf_name)
|
||||
print("🔧 Created custom field", cf_name)
|
||||
|
@ -1,31 +1,26 @@
|
||||
from dcim.models import Region
|
||||
from ruamel.yaml import YAML
|
||||
from pathlib import Path
|
||||
from startup_script_utils import load_yaml
|
||||
import sys
|
||||
|
||||
file = Path('/opt/netbox/initializers/regions.yml')
|
||||
if not file.is_file():
|
||||
regions = load_yaml('/opt/netbox/initializers/regions.yml')
|
||||
|
||||
if regions is None:
|
||||
sys.exit()
|
||||
|
||||
with file.open('r') as stream:
|
||||
yaml=YAML(typ='safe')
|
||||
regions = yaml.load(stream)
|
||||
optional_assocs = {
|
||||
'parent': (Region, 'name')
|
||||
}
|
||||
|
||||
optional_assocs = {
|
||||
'parent': (Region, 'name')
|
||||
}
|
||||
for params in regions:
|
||||
|
||||
if regions is not None:
|
||||
for params in regions:
|
||||
for assoc, details in optional_assocs.items():
|
||||
if assoc in params:
|
||||
model, field = details
|
||||
query = { field: params.pop(assoc) }
|
||||
|
||||
for assoc, details in optional_assocs.items():
|
||||
if assoc in params:
|
||||
model, field = details
|
||||
query = { field: params.pop(assoc) }
|
||||
params[assoc] = model.objects.get(**query)
|
||||
|
||||
params[assoc] = model.objects.get(**query)
|
||||
region, created = Region.objects.get_or_create(**params)
|
||||
|
||||
region, created = Region.objects.update_or_create(name=params['name'], defaults=params)
|
||||
|
||||
if created:
|
||||
print("🌐 Created region", region.name)
|
||||
if created:
|
||||
print("🌐 Created region", region.name)
|
||||
|
@ -1,46 +1,32 @@
|
||||
from dcim.models import Region, Site
|
||||
from extras.models import CustomField, CustomFieldValue
|
||||
from tenancy.models import Tenant
|
||||
from ruamel.yaml import YAML
|
||||
from pathlib import Path
|
||||
import sys
|
||||
|
||||
file = Path('/opt/netbox/initializers/sites.yml')
|
||||
if not file.is_file():
|
||||
from dcim.models import Region, Site
|
||||
from startup_script_utils import *
|
||||
from tenancy.models import Tenant
|
||||
|
||||
sites = load_yaml('/opt/netbox/initializers/sites.yml')
|
||||
|
||||
if sites is None:
|
||||
sys.exit()
|
||||
|
||||
with file.open('r') as stream:
|
||||
yaml = YAML(typ='safe')
|
||||
sites = yaml.load(stream)
|
||||
optional_assocs = {
|
||||
'region': (Region, 'name'),
|
||||
'tenant': (Tenant, 'name')
|
||||
}
|
||||
|
||||
optional_assocs = {
|
||||
'region': (Region, 'name'),
|
||||
'tenant': (Tenant, 'name')
|
||||
}
|
||||
for params in sites:
|
||||
custom_field_data = pop_custom_fields(params)
|
||||
|
||||
if sites is not None:
|
||||
for params in sites:
|
||||
custom_fields = params.pop('custom_fields', None)
|
||||
for assoc, details in optional_assocs.items():
|
||||
if assoc in params:
|
||||
model, field = details
|
||||
query = { field: params.pop(assoc) }
|
||||
|
||||
for assoc, details in optional_assocs.items():
|
||||
if assoc in params:
|
||||
model, field = details
|
||||
query = { field: params.pop(assoc) }
|
||||
params[assoc] = model.objects.get(**query)
|
||||
|
||||
params[assoc] = model.objects.get(**query)
|
||||
site, created = Site.objects.get_or_create(**params)
|
||||
|
||||
site, created = Site.objects.update_or_create(name=params['name'], defaults=params)
|
||||
if created:
|
||||
set_custom_fields_values(site, custom_field_data)
|
||||
|
||||
if created:
|
||||
if custom_fields is not None:
|
||||
for cf_name, cf_value in custom_fields.items():
|
||||
custom_field = CustomField.objects.get(name=cf_name)
|
||||
custom_field_value = CustomFieldValue.objects.create(
|
||||
field=custom_field,
|
||||
obj=site,
|
||||
value=cf_value
|
||||
)
|
||||
|
||||
site.custom_field_values.add(custom_field_value)
|
||||
|
||||
print("📍 Created site", site.name)
|
||||
print("📍 Created site", site.name)
|
||||
|
@ -1,19 +1,14 @@
|
||||
from dcim.models import Manufacturer
|
||||
from ruamel.yaml import YAML
|
||||
from pathlib import Path
|
||||
from startup_script_utils import load_yaml
|
||||
import sys
|
||||
|
||||
file = Path('/opt/netbox/initializers/manufacturers.yml')
|
||||
if not file.is_file():
|
||||
manufacturers = load_yaml('/opt/netbox/initializers/manufacturers.yml')
|
||||
|
||||
if manufacturers is None:
|
||||
sys.exit()
|
||||
|
||||
with file.open('r') as stream:
|
||||
yaml = YAML(typ='safe')
|
||||
manufacturers = yaml.load(stream)
|
||||
for params in manufacturers:
|
||||
manufacturer, created = Manufacturer.objects.get_or_create(**params)
|
||||
|
||||
if manufacturers is not None:
|
||||
for params in manufacturers:
|
||||
manufacturer, created = Manufacturer.objects.update_or_create(name=params['name'], defaults=params)
|
||||
|
||||
if created:
|
||||
print("🏭 Created Manufacturer", manufacturer.name)
|
||||
if created:
|
||||
print("🏭 Created Manufacturer", manufacturer.name)
|
||||
|
@ -1,56 +1,42 @@
|
||||
from dcim.models import DeviceType, Manufacturer, Region
|
||||
from tenancy.models import Tenant
|
||||
from extras.models import CustomField, CustomFieldValue
|
||||
from ruamel.yaml import YAML
|
||||
from pathlib import Path
|
||||
import sys
|
||||
|
||||
file = Path('/opt/netbox/initializers/device_types.yml')
|
||||
if not file.is_file():
|
||||
from dcim.models import DeviceType, Manufacturer, Region
|
||||
from startup_script_utils import *
|
||||
from tenancy.models import Tenant
|
||||
|
||||
device_types = load_yaml('/opt/netbox/initializers/device_types.yml')
|
||||
|
||||
if device_types is None:
|
||||
sys.exit()
|
||||
|
||||
with file.open('r') as stream:
|
||||
yaml = YAML(typ='safe')
|
||||
device_types = yaml.load(stream)
|
||||
required_assocs = {
|
||||
'manufacturer': (Manufacturer, 'name')
|
||||
}
|
||||
|
||||
required_assocs = {
|
||||
'manufacturer': (Manufacturer, 'name')
|
||||
}
|
||||
optional_assocs = {
|
||||
'region': (Region, 'name'),
|
||||
'tenant': (Tenant, 'name')
|
||||
}
|
||||
|
||||
optional_assocs = {
|
||||
'region': (Region, 'name'),
|
||||
'tenant': (Tenant, 'name')
|
||||
}
|
||||
for params in device_types:
|
||||
custom_field_data = pop_custom_fields(params)
|
||||
|
||||
if device_types is not None:
|
||||
for params in device_types:
|
||||
custom_fields = params.pop('custom_fields', None)
|
||||
for assoc, details in required_assocs.items():
|
||||
model, field = details
|
||||
query = { field: params.pop(assoc) }
|
||||
|
||||
for assoc, details in required_assocs.items():
|
||||
model, field = details
|
||||
query = { field: params.pop(assoc) }
|
||||
params[assoc] = model.objects.get(**query)
|
||||
|
||||
params[assoc] = model.objects.get(**query)
|
||||
for assoc, details in optional_assocs.items():
|
||||
if assoc in params:
|
||||
model, field = details
|
||||
query = { field: params.pop(assoc) }
|
||||
|
||||
for assoc, details in optional_assocs.items():
|
||||
if assoc in params:
|
||||
model, field = details
|
||||
query = { field: params.pop(assoc) }
|
||||
params[assoc] = model.objects.get(**query)
|
||||
|
||||
params[assoc] = model.objects.get(**query)
|
||||
device_type, created = DeviceType.objects.get_or_create(**params)
|
||||
|
||||
device_type, created = DeviceType.objects.update_or_create(model=params['model'], manufacturer=params['manufacturer'], defaults=params)
|
||||
if created:
|
||||
set_custom_fields_values(device_type, custom_field_data)
|
||||
|
||||
if created:
|
||||
if custom_fields is not None:
|
||||
for cf_name, cf_value in custom_fields.items():
|
||||
custom_field = CustomField.objects.get(name=cf_name)
|
||||
custom_field_value = CustomFieldValue.objects.create(
|
||||
field=custom_field,
|
||||
obj=device_type,
|
||||
value=cf_value
|
||||
)
|
||||
|
||||
device_type.custom_field_values.add(custom_field_value)
|
||||
|
||||
print("🔡 Created device type", device_type.manufacturer, device_type.model)
|
||||
print("🔡 Created device type", device_type.manufacturer, device_type.model)
|
||||
|
@ -1,28 +1,23 @@
|
||||
from dcim.models import RackRole
|
||||
from ruamel.yaml import YAML
|
||||
from utilities.choices import ColorChoices
|
||||
|
||||
from pathlib import Path
|
||||
from startup_script_utils import load_yaml
|
||||
import sys
|
||||
|
||||
file = Path('/opt/netbox/initializers/rack_roles.yml')
|
||||
if not file.is_file():
|
||||
rack_roles = load_yaml('/opt/netbox/initializers/rack_roles.yml')
|
||||
|
||||
if rack_roles is None:
|
||||
sys.exit()
|
||||
|
||||
with file.open('r') as stream:
|
||||
yaml=YAML(typ='safe')
|
||||
rack_roles = yaml.load(stream)
|
||||
for params in rack_roles:
|
||||
if 'color' in params:
|
||||
color = params.pop('color')
|
||||
|
||||
if rack_roles is not None:
|
||||
for params in rack_roles:
|
||||
if 'color' in params:
|
||||
color = params.pop('color')
|
||||
for color_tpl in ColorChoices:
|
||||
if color in color_tpl:
|
||||
params['color'] = color_tpl[0]
|
||||
|
||||
for color_tpl in ColorChoices:
|
||||
if color in color_tpl:
|
||||
params['color'] = color_tpl[0]
|
||||
rack_role, created = RackRole.objects.get_or_create(**params)
|
||||
|
||||
rack_role, created = RackRole.objects.update_or_create(name=params['name'], defaults=params)
|
||||
|
||||
if created:
|
||||
print("🎨 Created rack role", rack_role.name)
|
||||
if created:
|
||||
print("🎨 Created rack role", rack_role.name)
|
||||
|
@ -1,31 +1,24 @@
|
||||
from dcim.models import Site, RackGroup
|
||||
from ruamel.yaml import YAML
|
||||
|
||||
from pathlib import Path
|
||||
from dcim.models import Site,RackGroup
|
||||
from startup_script_utils import load_yaml
|
||||
import sys
|
||||
|
||||
file = Path('/opt/netbox/initializers/rack_groups.yml')
|
||||
if not file.is_file():
|
||||
rack_groups = load_yaml('/opt/netbox/initializers/rack_groups.yml')
|
||||
|
||||
if rack_groups is None:
|
||||
sys.exit()
|
||||
|
||||
with file.open('r') as stream:
|
||||
yaml=YAML(typ='safe')
|
||||
rack_groups = yaml.load(stream)
|
||||
required_assocs = {
|
||||
'site': (Site, 'name')
|
||||
}
|
||||
|
||||
required_assocs = {
|
||||
'site': (Site, 'name')
|
||||
}
|
||||
for params in rack_groups:
|
||||
|
||||
if rack_groups is not None:
|
||||
for params in rack_groups:
|
||||
for assoc, details in required_assocs.items():
|
||||
model, field = details
|
||||
query = { field: params.pop(assoc) }
|
||||
params[assoc] = model.objects.get(**query)
|
||||
|
||||
for assoc, details in required_assocs.items():
|
||||
model, field = details
|
||||
query = { field: params.pop(assoc) }
|
||||
rack_group, created = RackGroup.objects.get_or_create(**params)
|
||||
|
||||
params[assoc] = model.objects.get(**query)
|
||||
|
||||
rack_group, created = RackGroup.objects.get_or_create(**params)
|
||||
|
||||
if created:
|
||||
print("👪 Created rack group", rack_group.name)
|
||||
if created:
|
||||
print("🎨 Created rack group", rack_group.name)
|
||||
|
@ -1,57 +1,43 @@
|
||||
from dcim.models import Site, RackRole, Rack, RackGroup
|
||||
from tenancy.models import Tenant
|
||||
from extras.models import CustomField, CustomFieldValue
|
||||
from ruamel.yaml import YAML
|
||||
from pathlib import Path
|
||||
import sys
|
||||
|
||||
file = Path('/opt/netbox/initializers/racks.yml')
|
||||
if not file.is_file():
|
||||
from dcim.models import Site, RackRole, Rack, RackGroup
|
||||
from startup_script_utils import *
|
||||
from tenancy.models import Tenant
|
||||
|
||||
racks = load_yaml('/opt/netbox/initializers/racks.yml')
|
||||
|
||||
if racks is None:
|
||||
sys.exit()
|
||||
|
||||
with file.open('r') as stream:
|
||||
yaml = YAML(typ='safe')
|
||||
racks = yaml.load(stream)
|
||||
required_assocs = {
|
||||
'site': (Site, 'name')
|
||||
}
|
||||
|
||||
required_assocs = {
|
||||
'site': (Site, 'name')
|
||||
}
|
||||
optional_assocs = {
|
||||
'role': (RackRole, 'name'),
|
||||
'tenant': (Tenant, 'name'),
|
||||
'group': (RackGroup, 'name')
|
||||
}
|
||||
|
||||
optional_assocs = {
|
||||
'role': (RackRole, 'name'),
|
||||
'tenant': (Tenant, 'name'),
|
||||
'group': (RackGroup, 'name')
|
||||
}
|
||||
for params in racks:
|
||||
custom_field_data = pop_custom_fields(params)
|
||||
|
||||
if racks is not None:
|
||||
for params in racks:
|
||||
custom_fields = params.pop('custom_fields', None)
|
||||
for assoc, details in required_assocs.items():
|
||||
model, field = details
|
||||
query = { field: params.pop(assoc) }
|
||||
|
||||
for assoc, details in required_assocs.items():
|
||||
model, field = details
|
||||
query = { field: params.pop(assoc) }
|
||||
params[assoc] = model.objects.get(**query)
|
||||
|
||||
params[assoc] = model.objects.get(**query)
|
||||
for assoc, details in optional_assocs.items():
|
||||
if assoc in params:
|
||||
model, field = details
|
||||
query = { field: params.pop(assoc) }
|
||||
|
||||
for assoc, details in optional_assocs.items():
|
||||
if assoc in params:
|
||||
model, field = details
|
||||
query = { field: params.pop(assoc) }
|
||||
params[assoc] = model.objects.get(**query)
|
||||
|
||||
params[assoc] = model.objects.get(**query)
|
||||
rack, created = Rack.objects.get_or_create(**params)
|
||||
|
||||
rack, created = Rack.objects.update_or_create(name=params['name'], site=params['site'], defaults=params)
|
||||
if created:
|
||||
set_custom_fields_values(rack, custom_field_data)
|
||||
|
||||
if created:
|
||||
if custom_fields is not None:
|
||||
for cf_name, cf_value in custom_fields.items():
|
||||
custom_field = CustomField.objects.get(name=cf_name)
|
||||
custom_field_value = CustomFieldValue.objects.create(
|
||||
field=custom_field,
|
||||
obj=rack,
|
||||
value=cf_value
|
||||
)
|
||||
|
||||
rack.custom_field_values.add(custom_field_value)
|
||||
|
||||
print("🔳 Created rack", rack.site, rack.name)
|
||||
print("🔳 Created rack", rack.site, rack.name)
|
||||
|
@ -1,29 +1,24 @@
|
||||
from dcim.models import DeviceRole
|
||||
from ruamel.yaml import YAML
|
||||
from utilities.choices import ColorChoices
|
||||
|
||||
from pathlib import Path
|
||||
from startup_script_utils import load_yaml
|
||||
import sys
|
||||
|
||||
file = Path('/opt/netbox/initializers/device_roles.yml')
|
||||
if not file.is_file():
|
||||
device_roles = load_yaml('/opt/netbox/initializers/device_roles.yml')
|
||||
|
||||
if device_roles is None:
|
||||
sys.exit()
|
||||
|
||||
with file.open('r') as stream:
|
||||
yaml=YAML(typ='safe')
|
||||
device_roles = yaml.load(stream)
|
||||
for params in device_roles:
|
||||
|
||||
if device_roles is not None:
|
||||
for params in device_roles:
|
||||
if 'color' in params:
|
||||
color = params.pop('color')
|
||||
|
||||
if 'color' in params:
|
||||
color = params.pop('color')
|
||||
for color_tpl in ColorChoices:
|
||||
if color in color_tpl:
|
||||
params['color'] = color_tpl[0]
|
||||
|
||||
for color_tpl in ColorChoices:
|
||||
if color in color_tpl:
|
||||
params['color'] = color_tpl[0]
|
||||
device_role, created = DeviceRole.objects.get_or_create(**params)
|
||||
|
||||
device_role, created = DeviceRole.objects.update_or_create(name=params['name'], defaults=params)
|
||||
|
||||
if created:
|
||||
print("🎨 Created device role", device_role.name)
|
||||
if created:
|
||||
print("🎨 Created device role", device_role.name)
|
||||
|
@ -1,32 +1,26 @@
|
||||
from dcim.models import Manufacturer, Platform
|
||||
from ruamel.yaml import YAML
|
||||
|
||||
from pathlib import Path
|
||||
from startup_script_utils import load_yaml
|
||||
import sys
|
||||
|
||||
file = Path('/opt/netbox/initializers/platforms.yml')
|
||||
if not file.is_file():
|
||||
platforms = load_yaml('/opt/netbox/initializers/platforms.yml')
|
||||
|
||||
if platforms is None:
|
||||
sys.exit()
|
||||
|
||||
with file.open('r') as stream:
|
||||
yaml = YAML(typ='safe')
|
||||
platforms = yaml.load(stream)
|
||||
optional_assocs = {
|
||||
'manufacturer': (Manufacturer, 'name'),
|
||||
}
|
||||
|
||||
optional_assocs = {
|
||||
'manufacturer': (Manufacturer, 'name'),
|
||||
}
|
||||
for params in platforms:
|
||||
|
||||
if platforms is not None:
|
||||
for params in platforms:
|
||||
for assoc, details in optional_assocs.items():
|
||||
if assoc in params:
|
||||
model, field = details
|
||||
query = { field: params.pop(assoc) }
|
||||
|
||||
for assoc, details in optional_assocs.items():
|
||||
if assoc in params:
|
||||
model, field = details
|
||||
query = { field: params.pop(assoc) }
|
||||
params[assoc] = model.objects.get(**query)
|
||||
|
||||
params[assoc] = model.objects.get(**query)
|
||||
platform, created = Platform.objects.get_or_create(**params)
|
||||
|
||||
platform, created = Platform.objects.get_or_create(**params)
|
||||
|
||||
if created:
|
||||
print("💾 Created platform", platform.name)
|
||||
if created:
|
||||
print("💾 Created platform", platform.name)
|
||||
|
@ -1,72 +1,51 @@
|
||||
from dcim.models import Site, Rack, DeviceRole, DeviceType, Device, Platform
|
||||
from ipam.models import IPAddress
|
||||
from virtualization.models import Cluster
|
||||
from tenancy.models import Tenant
|
||||
from extras.models import CustomField, CustomFieldValue
|
||||
from ruamel.yaml import YAML
|
||||
|
||||
from pathlib import Path
|
||||
import sys
|
||||
|
||||
file = Path('/opt/netbox/initializers/devices.yml')
|
||||
if not file.is_file():
|
||||
from dcim.models import Site, Rack, DeviceRole, DeviceType, Device, Platform
|
||||
from startup_script_utils import *
|
||||
from tenancy.models import Tenant
|
||||
from virtualization.models import Cluster
|
||||
|
||||
devices = load_yaml('/opt/netbox/initializers/devices.yml')
|
||||
|
||||
if devices is None:
|
||||
sys.exit()
|
||||
|
||||
with file.open('r') as stream:
|
||||
yaml = YAML(typ='safe')
|
||||
devices = yaml.load(stream)
|
||||
required_assocs = {
|
||||
'device_role': (DeviceRole, 'name'),
|
||||
'device_type': (DeviceType, 'model'),
|
||||
'site': (Site, 'name')
|
||||
}
|
||||
|
||||
required_assocs = {
|
||||
'device_role': (DeviceRole, 'name'),
|
||||
'device_type': (DeviceType, 'model'),
|
||||
'site': (Site, 'name')
|
||||
}
|
||||
optional_assocs = {
|
||||
'tenant': (Tenant, 'name'),
|
||||
'platform': (Platform, 'name'),
|
||||
'rack': (Rack, 'name'),
|
||||
'cluster': (Cluster, 'name')
|
||||
}
|
||||
|
||||
optional_assocs = {
|
||||
'tenant': (Tenant, 'name'),
|
||||
'platform': (Platform, 'name'),
|
||||
'rack': (Rack, 'name'),
|
||||
'cluster': (Cluster, 'name'),
|
||||
'primary_ip4': (IPAddress, 'address'),
|
||||
'primary_ip6': (IPAddress, 'address')
|
||||
}
|
||||
for params in devices:
|
||||
custom_field_data = pop_custom_fields(params)
|
||||
|
||||
if devices is not None:
|
||||
for params in devices:
|
||||
custom_fields = params.pop('custom_fields', None)
|
||||
# primary ips are handled later in `270_primary_ips.py`
|
||||
params.pop('primary_ip4', None)
|
||||
params.pop('primary_ip6', None)
|
||||
|
||||
for assoc, details in required_assocs.items():
|
||||
model, field = details
|
||||
query = { field: params.pop(assoc) }
|
||||
for assoc, details in required_assocs.items():
|
||||
model, field = details
|
||||
query = { field: params.pop(assoc) }
|
||||
|
||||
params[assoc] = model.objects.get(**query)
|
||||
params[assoc] = model.objects.get(**query)
|
||||
|
||||
for assoc, details in optional_assocs.items():
|
||||
if assoc in params:
|
||||
model, field = details
|
||||
for assoc, details in optional_assocs.items():
|
||||
if assoc in params:
|
||||
model, field = details
|
||||
query = { field: params.pop(assoc) }
|
||||
|
||||
if assoc == 'rack': # Special handling for rack query to reference rack name and site
|
||||
query = {
|
||||
'site': params.get('site'),
|
||||
field: params.pop(assoc),
|
||||
}
|
||||
else:
|
||||
query = { field: params.pop(assoc) }
|
||||
params[assoc] = model.objects.get(**query)
|
||||
|
||||
params[assoc] = model.objects.get(**query)
|
||||
device, created = Device.objects.get_or_create(**params)
|
||||
|
||||
device, created = Device.objects.update_or_create(name=params['name'], defaults=params)
|
||||
if created:
|
||||
set_custom_fields_values(device, custom_field_data)
|
||||
|
||||
if created:
|
||||
if custom_fields is not None:
|
||||
for cf_name, cf_value in custom_fields.items():
|
||||
custom_field = CustomField.objects.get(name=cf_name)
|
||||
custom_field_value = CustomFieldValue.objects.create(
|
||||
field=custom_field,
|
||||
obj=device,
|
||||
value=cf_value
|
||||
)
|
||||
|
||||
device.custom_field_values.add(custom_field_value)
|
||||
|
||||
print("🖥️ Created device", device.name)
|
||||
print("🖥️ Created device", device.name)
|
||||
|
@ -1,7 +1,6 @@
|
||||
from dcim.models import Interface, Device, DeviceRole
|
||||
|
||||
from ruamel.yaml import YAML
|
||||
from pathlib import Path
|
||||
from startup_script_utils import *
|
||||
|
||||
import sys
|
||||
|
||||
@ -45,15 +44,11 @@ templates = {
|
||||
'customer-locker': template_customer_locker,
|
||||
}
|
||||
|
||||
file = Path('/opt/netbox/initializers/interfaces.yml')
|
||||
if not file.is_file():
|
||||
config = load_yaml('/opt/netbox/initializers/interfaces.yml')
|
||||
|
||||
if config is None:
|
||||
sys.exit()
|
||||
|
||||
with file.open('r') as stream:
|
||||
yaml = YAML(typ='safe')
|
||||
config = yaml.load(stream)
|
||||
|
||||
|
||||
for c in config:
|
||||
device_role = DeviceRole.objects.get(slug=c.get('device_role'))
|
||||
|
||||
@ -66,7 +61,5 @@ for c in config:
|
||||
continue
|
||||
|
||||
port_count = c.get('ports', 0)
|
||||
|
||||
devices = Device.objects.all().filter(device_role=device_role.id)
|
||||
|
||||
templates[interface_template](devices, port_count)
|
||||
|
@ -1,51 +1,36 @@
|
||||
from dcim.models import Site
|
||||
from ipam.models import VLAN, VLANGroup, Role
|
||||
from tenancy.models import Tenant, TenantGroup
|
||||
from extras.models import CustomField, CustomFieldValue
|
||||
from ruamel.yaml import YAML
|
||||
|
||||
from pathlib import Path
|
||||
import sys
|
||||
|
||||
file = Path('/opt/netbox/initializers/vlans.yml')
|
||||
if not file.is_file():
|
||||
from dcim.models import Site
|
||||
from ipam.models import VLAN, VLANGroup, Role
|
||||
from startup_script_utils import *
|
||||
from tenancy.models import Tenant, TenantGroup
|
||||
|
||||
vlans = load_yaml('/opt/netbox/initializers/vlans.yml')
|
||||
|
||||
if vlans is None:
|
||||
sys.exit()
|
||||
|
||||
with file.open('r') as stream:
|
||||
yaml = YAML(typ='safe')
|
||||
vlans = yaml.load(stream)
|
||||
optional_assocs = {
|
||||
'site': (Site, 'name'),
|
||||
'tenant': (Tenant, 'name'),
|
||||
'tenant_group': (TenantGroup, 'name'),
|
||||
'group': (VLANGroup, 'name'),
|
||||
'role': (Role, 'name')
|
||||
}
|
||||
|
||||
optional_assocs = {
|
||||
'site': (Site, 'name'),
|
||||
'tenant': (Tenant, 'name'),
|
||||
'tenant_group': (TenantGroup, 'name'),
|
||||
'group': (VLANGroup, 'name'),
|
||||
'role': (Role, 'name')
|
||||
}
|
||||
for params in vlans:
|
||||
custom_field_data = pop_custom_fields(params)
|
||||
|
||||
if vlans is not None:
|
||||
for params in vlans:
|
||||
custom_fields = params.pop('custom_fields', None)
|
||||
for assoc, details in optional_assocs.items():
|
||||
if assoc in params:
|
||||
model, field = details
|
||||
query = { field: params.pop(assoc) }
|
||||
|
||||
for assoc, details in optional_assocs.items():
|
||||
if assoc in params:
|
||||
model, field = details
|
||||
query = { field: params.pop(assoc) }
|
||||
params[assoc] = model.objects.get(**query)
|
||||
|
||||
params[assoc] = model.objects.get(**query)
|
||||
vlan, created = VLAN.objects.get_or_create(**params)
|
||||
|
||||
vlan, created = VLAN.objects.get_or_create(**params)
|
||||
if created:
|
||||
set_custom_fields_values(vlan, custom_field_data)
|
||||
|
||||
if created:
|
||||
if custom_fields is not None:
|
||||
for cf_name, cf_value in custom_fields.items():
|
||||
custom_field = CustomField.objects.get(name=cf_name)
|
||||
custom_field_value = CustomFieldValue.objects.create(
|
||||
field=custom_field,
|
||||
obj=vlan,
|
||||
value=cf_value
|
||||
)
|
||||
|
||||
vlan.custom_field_values.add(custom_field_value)
|
||||
|
||||
print("🏠 Created VLAN", vlan.name)
|
||||
print("🏠 Created VLAN", vlan.name)
|
||||
|
@ -1,54 +1,39 @@
|
||||
from dcim.models import Site
|
||||
from ipam.models import Prefix, VLAN, Role, VRF
|
||||
from tenancy.models import Tenant, TenantGroup
|
||||
from extras.models import CustomField, CustomFieldValue
|
||||
from ruamel.yaml import YAML
|
||||
|
||||
from netaddr import IPNetwork
|
||||
from pathlib import Path
|
||||
import sys
|
||||
|
||||
file = Path('/opt/netbox/initializers/prefixes.yml')
|
||||
if not file.is_file():
|
||||
from dcim.models import Site
|
||||
from ipam.models import Prefix, VLAN, Role, VRF
|
||||
from netaddr import IPNetwork
|
||||
from startup_script_utils import *
|
||||
from tenancy.models import Tenant, TenantGroup
|
||||
|
||||
prefixes = load_yaml('/opt/netbox/initializers/prefixes.yml')
|
||||
|
||||
if prefixes is None:
|
||||
sys.exit()
|
||||
|
||||
with file.open('r') as stream:
|
||||
yaml = YAML(typ='safe')
|
||||
prefixes = yaml.load(stream)
|
||||
optional_assocs = {
|
||||
'site': (Site, 'name'),
|
||||
'tenant': (Tenant, 'name'),
|
||||
'tenant_group': (TenantGroup, 'name'),
|
||||
'vlan': (VLAN, 'name'),
|
||||
'role': (Role, 'name'),
|
||||
'vrf': (VRF, 'name')
|
||||
}
|
||||
|
||||
optional_assocs = {
|
||||
'site': (Site, 'name'),
|
||||
'tenant': (Tenant, 'name'),
|
||||
'tenant_group': (TenantGroup, 'name'),
|
||||
'vlan': (VLAN, 'name'),
|
||||
'role': (Role, 'name'),
|
||||
'vrf': (VRF, 'name')
|
||||
}
|
||||
for params in prefixes:
|
||||
custom_field_data = pop_custom_fields(params)
|
||||
|
||||
if prefixes is not None:
|
||||
for params in prefixes:
|
||||
custom_fields = params.pop('custom_fields', None)
|
||||
params['prefix'] = IPNetwork(params['prefix'])
|
||||
params['prefix'] = IPNetwork(params['prefix'])
|
||||
|
||||
for assoc, details in optional_assocs.items():
|
||||
if assoc in params:
|
||||
model, field = details
|
||||
query = { field: params.pop(assoc) }
|
||||
for assoc, details in optional_assocs.items():
|
||||
if assoc in params:
|
||||
model, field = details
|
||||
query = { field: params.pop(assoc) }
|
||||
params[assoc] = model.objects.get(**query)
|
||||
|
||||
params[assoc] = model.objects.get(**query)
|
||||
prefix, created = Prefix.objects.get_or_create(**params)
|
||||
|
||||
prefix, created = Prefix.objects.get_or_create(**params)
|
||||
if created:
|
||||
set_custom_fields_values(prefix, custom_field_data)
|
||||
|
||||
if created:
|
||||
if custom_fields is not None:
|
||||
for cf_name, cf_value in custom_fields.items():
|
||||
custom_field = CustomField.objects.get(name=cf_name)
|
||||
custom_field_value = CustomFieldValue.objects.create(
|
||||
field=custom_field,
|
||||
obj=prefix,
|
||||
value=cf_value
|
||||
)
|
||||
|
||||
prefix.custom_field_values.add(custom_field_value)
|
||||
|
||||
print("📌 Created Prefix", prefix.prefix)
|
||||
print("📌 Created Prefix", prefix.prefix)
|
||||
|
@ -1,44 +1,29 @@
|
||||
from dcim.models import Interface, Device
|
||||
from extras.models import CustomField, CustomFieldValue
|
||||
from ruamel.yaml import YAML
|
||||
|
||||
from pathlib import Path
|
||||
import sys
|
||||
|
||||
file = Path('/opt/netbox/initializers/dcim_interfaces.yml')
|
||||
if not file.is_file():
|
||||
from dcim.models import Interface, Device
|
||||
from startup_script_utils import *
|
||||
|
||||
interfaces = load_yaml('/opt/netbox/initializers/dcim_interfaces.yml')
|
||||
|
||||
if interfaces is None:
|
||||
sys.exit()
|
||||
|
||||
with file.open('r') as stream:
|
||||
yaml = YAML(typ='safe')
|
||||
interfaces = yaml.load(stream)
|
||||
required_assocs = {
|
||||
'device': (Device, 'name')
|
||||
}
|
||||
|
||||
required_assocs = {
|
||||
'device': (Device, 'name')
|
||||
}
|
||||
for params in interfaces:
|
||||
custom_field_data = pop_custom_fields(params)
|
||||
|
||||
if interfaces is not None:
|
||||
for params in interfaces:
|
||||
custom_fields = params.pop('custom_fields', None)
|
||||
for assoc, details in required_assocs.items():
|
||||
model, field = details
|
||||
query = { field: params.pop(assoc) }
|
||||
|
||||
for assoc, details in required_assocs.items():
|
||||
model, field = details
|
||||
query = { field: params.pop(assoc) }
|
||||
params[assoc] = model.objects.get(**query)
|
||||
|
||||
params[assoc] = model.objects.get(**query)
|
||||
interface, created = Interface.objects.get_or_create(**params)
|
||||
|
||||
interface, created = Interface.objects.get_or_create(**params)
|
||||
if created:
|
||||
set_custom_fields_values(interface, custom_field_data)
|
||||
|
||||
if created:
|
||||
if custom_fields is not None:
|
||||
for cf_name, cf_value in custom_fields.items():
|
||||
custom_field = CustomField.objects.get(name=cf_name)
|
||||
custom_field_value = CustomFieldValue.objects.create(
|
||||
field=custom_field,
|
||||
obj=interface,
|
||||
value=cf_value
|
||||
)
|
||||
|
||||
interface.custom_field_values.add(custom_field_value)
|
||||
|
||||
print("🧷 Created interface", interface.name, interface.device.name)
|
||||
print("🧷 Created interface", interface.name, interface.device.name)
|
||||
|
@ -1,65 +1,60 @@
|
||||
from ipam.models import IPAddress, VRF
|
||||
from dcim.models import Device, Interface
|
||||
from virtualization.models import VirtualMachine
|
||||
from tenancy.models import Tenant
|
||||
from extras.models import CustomField, CustomFieldValue
|
||||
from ruamel.yaml import YAML
|
||||
|
||||
from netaddr import IPNetwork
|
||||
from pathlib import Path
|
||||
import sys
|
||||
|
||||
file = Path('/opt/netbox/initializers/ip_addresses.yml')
|
||||
if not file.is_file():
|
||||
from dcim.models import Device, Interface
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.db.models import Q
|
||||
from ipam.models import VRF, IPAddress
|
||||
from netaddr import IPNetwork
|
||||
from startup_script_utils import *
|
||||
from tenancy.models import Tenant
|
||||
from virtualization.models import VirtualMachine, VMInterface
|
||||
|
||||
ip_addresses = load_yaml('/opt/netbox/initializers/ip_addresses.yml')
|
||||
|
||||
if ip_addresses is None:
|
||||
sys.exit()
|
||||
|
||||
with file.open('r') as stream:
|
||||
yaml = YAML(typ='safe')
|
||||
ip_addresses = yaml.load(stream)
|
||||
optional_assocs = {
|
||||
'tenant': (Tenant, 'name'),
|
||||
'vrf': (VRF, 'name'),
|
||||
'interface': (None, None)
|
||||
}
|
||||
|
||||
optional_assocs = {
|
||||
'tenant': (Tenant, 'name'),
|
||||
'vrf': (VRF, 'name'),
|
||||
'interface': (Interface, 'name')
|
||||
}
|
||||
vm_interface_ct = ContentType.objects.filter(Q(app_label='virtualization', model='vminterface')).first()
|
||||
interface_ct = ContentType.objects.filter(Q(app_label='dcim', model='interface')).first()
|
||||
|
||||
if ip_addresses is not None:
|
||||
for params in ip_addresses:
|
||||
vm = params.pop('virtual_machine', None)
|
||||
device = params.pop('device', None)
|
||||
custom_fields = params.pop('custom_fields', None)
|
||||
params['address'] = IPNetwork(params['address'])
|
||||
for params in ip_addresses:
|
||||
custom_field_data = pop_custom_fields(params)
|
||||
|
||||
if vm and device:
|
||||
print("IP Address can only specify one of the following: virtual_machine or device.")
|
||||
sys.exit()
|
||||
vm = params.pop('virtual_machine', None)
|
||||
device = params.pop('device', None)
|
||||
params['address'] = IPNetwork(params['address'])
|
||||
|
||||
for assoc, details in optional_assocs.items():
|
||||
if assoc in params:
|
||||
model, field = details
|
||||
if assoc == 'interface':
|
||||
if vm:
|
||||
vm_id = VirtualMachine.objects.get(name=vm).id
|
||||
query = { field: params.pop(assoc), "virtual_machine_id": vm_id }
|
||||
elif device:
|
||||
dev_id = Device.objects.get(name=device).id
|
||||
query = { field: params.pop(assoc), "device_id": dev_id }
|
||||
else:
|
||||
query = { field: params.pop(assoc) }
|
||||
if vm and device:
|
||||
print("IP Address can only specify one of the following: virtual_machine or device.")
|
||||
sys.exit()
|
||||
|
||||
for assoc, details in optional_assocs.items():
|
||||
if assoc in params:
|
||||
model, field = details
|
||||
if assoc == 'interface':
|
||||
if vm:
|
||||
vm_id = VirtualMachine.objects.get(name=vm).id
|
||||
query = { 'name': params.pop(assoc), "virtual_machine_id": vm_id }
|
||||
params['assigned_object_type'] = vm_interface_ct
|
||||
params['assigned_object_id'] = VMInterface.objects.get(**query).id
|
||||
elif device:
|
||||
dev_id = Device.objects.get(name=device).id
|
||||
query = { 'name': params.pop(assoc), "device_id": dev_id }
|
||||
params['assigned_object_type'] = interface_ct
|
||||
params['assigned_object_id'] = Interface.objects.get(**query).id
|
||||
else:
|
||||
query = { field: params.pop(assoc) }
|
||||
params[assoc] = model.objects.get(**query)
|
||||
|
||||
ip_address, created = IPAddress.objects.get_or_create(**params)
|
||||
ip_address, created = IPAddress.objects.get_or_create(**params)
|
||||
|
||||
if created:
|
||||
if custom_fields is not None:
|
||||
for cf_name, cf_value in custom_fields.items():
|
||||
custom_field = CustomField.objects.get(name=cf_name)
|
||||
custom_field_value = CustomFieldValue.objects.create(
|
||||
field=custom_field,
|
||||
obj=ip_address,
|
||||
value=cf_value
|
||||
)
|
||||
if created:
|
||||
set_custom_fields_values(ip_address, custom_field_data)
|
||||
|
||||
ip_address.custom_field_values.add(custom_field_value)
|
||||
|
||||
print("🧬 Created IP Address", ip_address.address)
|
||||
print("🧬 Created IP Address", ip_address.address)
|
||||
|
@ -1,12 +1,9 @@
|
||||
"""
|
||||
Automate cabling process so that all Customer Network Locker's 1-2 interfaces go to Access Switch's EFR1, and 3-4 ones go to EFR2.
|
||||
This script should only be run once and in dev environment. Mostly used for integration testing purposes.
|
||||
Automate cabling process so that all Customer Network Locker's 1-2 interfaces go to Access Switch's EFR1, and 3-4 ones go to EFR2.
|
||||
"""
|
||||
|
||||
from django.db.utils import IntegrityError
|
||||
from dcim.models import Cable
|
||||
|
||||
from ruamel.yaml import YAML
|
||||
from pathlib import Path
|
||||
from startup_script_utils import load_yaml
|
||||
|
||||
import sys
|
||||
|
||||
@ -17,34 +14,34 @@ def get_devices(role):
|
||||
return Device.objects.all().filter(device_role=DeviceRole.objects.get(slug=role).id)
|
||||
|
||||
|
||||
file = Path('/opt/netbox/initializers/cables.yml')
|
||||
if not file.is_file():
|
||||
config = load_yaml('/opt/netbox/initializers/cables.yml')
|
||||
if config is None or config.get('cable_interfaces') is None:
|
||||
sys.exit()
|
||||
|
||||
with file.open('r') as stream:
|
||||
yaml = YAML(typ='safe')
|
||||
config = yaml.load(stream)
|
||||
lockers = get_devices('customer-network-locker')
|
||||
switches = get_devices('access-switch')
|
||||
|
||||
if config.get('cable_interfaces'):
|
||||
lockers = get_devices('customer-network-locker')
|
||||
switches = get_devices('access-switch')
|
||||
i = 0
|
||||
for locker in lockers:
|
||||
# break if i is larger than switch interface's size.
|
||||
if i > len(switches[0].vc_interfaces)-1:
|
||||
break
|
||||
|
||||
i = 0
|
||||
for locker in lockers:
|
||||
# break if i is larger than switch interface's size.
|
||||
if i > len(switches[0].vc_interfaces)-1:
|
||||
break
|
||||
i12 = locker.vc_interfaces[0]
|
||||
i34 = locker.vc_interfaces[1]
|
||||
|
||||
i12 = locker.vc_interfaces[0]
|
||||
i34 = locker.vc_interfaces[1]
|
||||
efr1 = switches[0].vc_interfaces[i]
|
||||
efr2 = switches[1].vc_interfaces[i]
|
||||
|
||||
efr1 = switches[0].vc_interfaces[i]
|
||||
efr2 = switches[1].vc_interfaces[i]
|
||||
try:
|
||||
c1 = Cable.objects.create(termination_a=i12, termination_b=efr1)
|
||||
print('🔌 Created cable for {} and {}'.format(i12.name, efr1.name))
|
||||
except IntegrityError:
|
||||
pass
|
||||
|
||||
c1 = Cable.objects.create(termination_a=i12, termination_b=efr1)
|
||||
print('🔌 Created cable for {} and {}'.format(i12.name, efr1.name))
|
||||
|
||||
c2 = Cable.objects.create(termination_a=i34, termination_b=efr2)
|
||||
print('🔌 Created cable for {} and {}'.format(i34.name, efr2.name))
|
||||
|
||||
i += 1
|
||||
try:
|
||||
c2 = Cable.objects.create(termination_a=i34, termination_b=efr2)
|
||||
print('🔌 Created cable for {} and {}'.format(i34.name, efr2.name))
|
||||
except IntegrityError:
|
||||
pass
|
||||
i += 1
|
||||
|
29
docker/startup_scripts/__main__.py
Normal file
29
docker/startup_scripts/__main__.py
Normal file
@ -0,0 +1,29 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import runpy
|
||||
from os import scandir
|
||||
from os.path import dirname, abspath
|
||||
|
||||
this_dir = dirname(abspath(__file__))
|
||||
|
||||
def filename(f):
|
||||
return f.name
|
||||
|
||||
with scandir(this_dir) as it:
|
||||
for f in sorted(it, key = filename):
|
||||
if not f.is_file():
|
||||
continue
|
||||
|
||||
if f.name.startswith('__'):
|
||||
continue
|
||||
|
||||
if not f.name.endswith('.py'):
|
||||
continue
|
||||
|
||||
print(f"▶️ Running the startup script {f.path}")
|
||||
try:
|
||||
runpy.run_path(f.path)
|
||||
except SystemExit as e:
|
||||
if e.code is not None and e.code != 0:
|
||||
print(f"‼️ The startup script {f.path} returned with code {e.code}, exiting.")
|
||||
raise
|
3
docker/startup_scripts/startup_script_utils/__init__.py
Normal file
3
docker/startup_scripts/startup_script_utils/__init__.py
Normal file
@ -0,0 +1,3 @@
|
||||
from .load_yaml import load_yaml
|
||||
from .permissions import set_permissions
|
||||
from .custom_fields import set_custom_fields_values, pop_custom_fields
|
15
docker/startup_scripts/startup_script_utils/custom_fields.py
Normal file
15
docker/startup_scripts/startup_script_utils/custom_fields.py
Normal file
@ -0,0 +1,15 @@
|
||||
def set_custom_fields_values(entity, custom_field_data):
|
||||
if not custom_field_data:
|
||||
return
|
||||
|
||||
entity.custom_field_data = custom_field_data
|
||||
return entity.save()
|
||||
|
||||
def pop_custom_fields(params):
|
||||
if 'custom_field_data' in params:
|
||||
return params.pop('custom_field_data')
|
||||
elif 'custom_fields' in params:
|
||||
print("⚠️ Please rename 'custom_fields' to 'custom_field_data'!")
|
||||
return params.pop('custom_fields')
|
||||
|
||||
return None
|
11
docker/startup_scripts/startup_script_utils/load_yaml.py
Normal file
11
docker/startup_scripts/startup_script_utils/load_yaml.py
Normal file
@ -0,0 +1,11 @@
|
||||
from pathlib import Path
|
||||
from ruamel.yaml import YAML
|
||||
|
||||
|
||||
def load_yaml(yaml_file: str):
|
||||
yf = Path(yaml_file)
|
||||
if not yf.is_file():
|
||||
return None
|
||||
with yf.open("r") as stream:
|
||||
yaml = YAML(typ="safe")
|
||||
return yaml.load(stream)
|
18
docker/startup_scripts/startup_script_utils/permissions.py
Normal file
18
docker/startup_scripts/startup_script_utils/permissions.py
Normal file
@ -0,0 +1,18 @@
|
||||
from django.contrib.auth.models import Permission
|
||||
|
||||
|
||||
def set_permissions(subject, permission_filters):
|
||||
if subject is None or permission_filters is None:
|
||||
return
|
||||
subject.clear()
|
||||
for permission_filter in permission_filters:
|
||||
if "*" in permission_filter:
|
||||
permission_filter_regex = "^" + permission_filter.replace("*", ".*") + "$"
|
||||
permissions = Permission.objects.filter(codename__iregex=permission_filter_regex)
|
||||
print(" ⚿ Granting", permissions.count(), "permissions matching '" + permission_filter + "'")
|
||||
else:
|
||||
permissions = Permission.objects.filter(codename=permission_filter)
|
||||
print(" ⚿ Granting permission", permission_filter)
|
||||
|
||||
for permission in permissions:
|
||||
subject.add(permission)
|
@ -1,21 +1,25 @@
|
||||
# Caching
|
||||
|
||||
To improve performance, NetBox supports caching for most object and list views. Caching is implemented using Redis,
|
||||
and [django-cacheops](https://github.com/Suor/django-cacheops)
|
||||
NetBox supports database query caching using [django-cacheops](https://github.com/Suor/django-cacheops) and Redis. When a query is made, the results are cached in Redis for a short period of time, as defined by the [CACHE_TIMEOUT](../../configuration/optional-settings/#cache_timeout) parameter (15 minutes by default). Within that time, all recurrences of that specific query will return the pre-fetched results from the cache.
|
||||
|
||||
Several management commands are avaliable for administrators to manually invalidate cache entries in extenuating circumstances.
|
||||
If a change is made to any of the objects returned by the query within that time, or if the timeout expires, the results are automatically invalidated and the next request for those results will be sent to the database.
|
||||
|
||||
To invalidate a specifc model instance (for example a Device with ID 34):
|
||||
```
|
||||
python netbox/manage.py invalidate dcim.Device.34
|
||||
## Invalidating Cached Data
|
||||
|
||||
Although caching is performed automatically and rarely requires administrative intervention, NetBox provides the `invalidate` management command to force invalidation of cached results. This command can reference a specific object my its type and numeric ID:
|
||||
|
||||
```no-highlight
|
||||
$ python netbox/manage.py invalidate dcim.Device.34
|
||||
```
|
||||
|
||||
To invalidate all instance of a model:
|
||||
```
|
||||
python netbox/manage.py invalidate dcim.Device
|
||||
Alternatively, it can also delete all cached results for an object type:
|
||||
|
||||
```no-highlight
|
||||
$ python netbox/manage.py invalidate dcim.Device
|
||||
```
|
||||
|
||||
To flush the entire cache database:
|
||||
```
|
||||
python netbox/manage.py invalidate all
|
||||
Finally, calling it with the `all` argument will force invalidation of the entire cache database:
|
||||
|
||||
```no-highlight
|
||||
$ python netbox/manage.py invalidate all
|
||||
```
|
||||
|
@ -1,9 +1,9 @@
|
||||
# Change Logging
|
||||
|
||||
Every time an object in NetBox is created, updated, or deleted, a serialized copy of that object is saved to the database, along with meta data including the current time and the user associated with the change. These records form a running changelog both for each individual object as well as NetBox as a whole (Organization > Changelog).
|
||||
Every time an object in NetBox is created, updated, or deleted, a serialized copy of that object is saved to the database, along with meta data including the current time and the user associated with the change. These records form a persistent record of changes both for each individual object as well as NetBox as a whole. The global change log can be viewed by navigating to Other > Change Log.
|
||||
|
||||
A serialized representation is included for each object in JSON format. This is similar to how objects are conveyed within the REST API, but does not include any nested representations. For instance, the `tenant` field of a site will record only the tenant's ID, not a representation of the tenant.
|
||||
A serialized representation of the instance being modified is included in JSON format. This is similar to how objects are conveyed within the REST API, but does not include any nested representations. For instance, the `tenant` field of a site will record only the tenant's ID, not a representation of the tenant.
|
||||
|
||||
When a request is made, a random request ID is generated and attached to any change records resulting from the request. For example, editing multiple objects in bulk will create a change record for each object, and each of those objects will be assigned the same request ID. This makes it easy to identify all the change records associated with a particular request.
|
||||
When a request is made, a UUID is generated and attached to any change records resulting from that request. For example, editing three objects in bulk will create a separate change record for each (three in total), and each of those objects will be associated with the same UUID. This makes it easy to identify all the change records resulting from a particular request.
|
||||
|
||||
Change records are exposed in the API via the read-only endpoint `/api/extras/object-changes/`. They may also be exported in CSV format.
|
||||
Change records are exposed in the API via the read-only endpoint `/api/extras/object-changes/`. They may also be exported via the web UI in CSV format.
|
||||
|
@ -1,3 +0,0 @@
|
||||
# Context Data
|
||||
|
||||
{!docs/models/extras/configcontext.md!}
|
@ -1,26 +1,69 @@
|
||||
# Custom Fields
|
||||
|
||||
Each object in NetBox is represented in the database as a discrete table, and each attribute of an object exists as a column within its table. For example, sites are stored in the `dcim_site` table, which has columns named `name`, `facility`, `physical_address`, and so on. As new attributes are added to objects throughout the development of NetBox, tables are expanded to include new rows.
|
||||
Each model in NetBox is represented in the database as a discrete table, and each attribute of a model exists as a column within its table. For example, sites are stored in the `dcim_site` table, which has columns named `name`, `facility`, `physical_address`, and so on. As new attributes are added to objects throughout the development of NetBox, tables are expanded to include new rows.
|
||||
|
||||
However, some users might want to associate with objects attributes that are somewhat esoteric in nature, and that would not make sense to include in the core NetBox database schema. For instance, suppose your organization needs to associate each device with a ticket number pointing to the support ticket that was opened to have it installed. This is certainly a legitimate use for NetBox, but it's perhaps not a common enough need to warrant expanding the internal data schema. Instead, you can create a custom field to hold this data.
|
||||
However, some users might want to store additional object attributes that are somewhat esoteric in nature, and that would not make sense to include in the core NetBox database schema. For instance, suppose your organization needs to associate each device with a ticket number correlating it with an internal support system record. This is certainly a legitimate use for NetBox, but it's not a common enough need to warrant including a field for _every_ NetBox installation. Instead, you can create a custom field to hold this data.
|
||||
|
||||
Custom fields must be created through the admin UI under Extras > Custom Fields. To create a new custom field, select the object(s) to which you want it to apply, and the type of field it will be. NetBox supports six field types:
|
||||
Within the database, custom fields are stored as JSON data directly alongside each object. This alleviates the need for complex queries when retrieving objects.
|
||||
|
||||
* Free-form text (up to 255 characters)
|
||||
* Integer
|
||||
* Boolean (true/false)
|
||||
* Date
|
||||
* URL
|
||||
* Selection
|
||||
## Creating Custom Fields
|
||||
|
||||
Assign the field a name. This should be a simple database-friendly string, e.g. `tps_report`. You may optionally assign the field a human-friendly label (e.g. "TPS report") as well; the label will be displayed on forms. If a description is provided, it will appear beneath the field in a form.
|
||||
Custom fields must be created through the admin UI under Extras > Custom Fields. NetBox supports six types of custom field:
|
||||
|
||||
Marking the field as required will require the user to provide a value for the field when creating a new object or when saving an existing object. A default value for the field may also be provided. Use "true" or "false" for boolean fields. (The default value has no effect for selection fields.)
|
||||
* Text: Free-form text (up to 255 characters)
|
||||
* Integer: A whole number (positive or negative)
|
||||
* Boolean: True or false
|
||||
* Date: A date in ISO 8601 format (YYYY-MM-DD)
|
||||
* URL: This will be presented as a link in the web UI
|
||||
* Selection: A selection of one of several pre-defined custom choices
|
||||
|
||||
When creating a selection field, you should create at least two choices. These choices will be arranged first by weight, with lower weights appearing higher in the list, and then alphabetically.
|
||||
Each custom field must have a name; this should be a simple database-friendly string, e.g. `tps_report`. You may also assign a corresponding human-friendly label (e.g. "TPS report"); the label will be displayed on web forms. A weight is also required: Higher-weight fields will be ordered lower within a form. (The default weight is 100.) If a description is provided, it will appear beneath the field in a form.
|
||||
|
||||
## Using Custom Fields
|
||||
Marking a field as required will force the user to provide a value for the field when creating a new object or when saving an existing object. A default value for the field may also be provided. Use "true" or "false" for boolean fields, or the exact value of a choice for selection fields.
|
||||
|
||||
When a single object is edited, the form will include any custom fields which have been defined for the object type. These fields are included in the "Custom Fields" panel. On the backend, each custom field value is saved separately from the core object as an independent database call, so it's best to avoid adding too many custom fields per object.
|
||||
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.
|
||||
|
||||
When editing multiple objects, custom field values are saved in bulk. There is no significant difference in overhead when saving a custom field value for 100 objects versus one object. However, the bulk operation must be performed separately for each custom field.
|
||||
A custom field must be assigned to one or 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.
|
||||
|
||||
### Custom Field Validation
|
||||
|
||||
NetBox supports limited custom validation for custom field values. Following are the types of validation enforced for each field type:
|
||||
|
||||
* Text: Regular expression (optional)
|
||||
* Integer: Minimum and/or maximum value (optional)
|
||||
* Selection: Must exactly match one of the prescribed choices
|
||||
|
||||
### Custom Selection Fields
|
||||
|
||||
Each custom selection field must have at least two choices. These are specified as a comma-separated list. Choices appear in forms in the order they are listed. Note that choice values are saved exactly as they appear, so it's best to avoid superfluous punctuation or symbols where possible.
|
||||
|
||||
If a default value is specified for a selection field, it must exactly match one of the provided choices.
|
||||
|
||||
## Custom Fields and the REST API
|
||||
|
||||
When retrieving an object via the REST API, all of its custom data will be included within the `custom_fields` attribute. For example, below is the partial output of a site with two custom fields defined:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 123,
|
||||
"url": "http://localhost:8000/api/dcim/sites/123/",
|
||||
"name": "Raleigh 42",
|
||||
...
|
||||
"custom_fields": {
|
||||
"deployed": "2018-06-19",
|
||||
"site_code": "US-NC-RAL42"
|
||||
},
|
||||
...
|
||||
```
|
||||
|
||||
To set or change these values, simply include nested JSON data. For example:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "New Site",
|
||||
"slug": "new-site",
|
||||
"custom_fields": {
|
||||
"deployed": "2019-03-24"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
@ -1,6 +1,6 @@
|
||||
# Custom Links
|
||||
|
||||
Custom links allow users to place arbitrary hyperlinks within NetBox views. These are helpful for cross-referencing related records in external systems. For example, you might create a custom link on the device view which links to the current device in a network monitoring system.
|
||||
Custom links allow users to display arbitrary hyperlinks to external content within NetBox object views. These are helpful for cross-referencing related records in systems outside of NetBox. For example, you might create a custom link on the device view which links to the current device in a network monitoring system.
|
||||
|
||||
Custom links are created under the admin UI. Each link is associated with a particular NetBox object type (site, device, prefix, etc.) and will be displayed on relevant views. Each link is assigned text and a URL, both of which support Jinja2 templating. The text and URL are rendered with the context variable `obj` representing the current object.
|
||||
|
||||
@ -11,33 +11,44 @@ For example, you might define a link like this:
|
||||
|
||||
When viewing a device named Router4, this link would render as:
|
||||
|
||||
```
|
||||
```no-highlight
|
||||
<a href="https://nms.example.com/nodes/?name=Router4">View NMS</a>
|
||||
```
|
||||
|
||||
Custom links appear as buttons at the top right corner of the page. Numeric weighting can be used to influence the ordering of links.
|
||||
|
||||
## Context Data
|
||||
|
||||
The following context data is available within the template when rendering a custom link's text or URL.
|
||||
|
||||
| Variable | Description |
|
||||
|----------|-------------|
|
||||
| `obj` | The NetBox object being displayed |
|
||||
| `debug` | A boolean indicating whether debugging is enabled |
|
||||
| `request` | The current WSGI request |
|
||||
| `user` | The current user (if authenticated) |
|
||||
| `perms` | The [permissions](https://docs.djangoproject.com/en/stable/topics/auth/default/#permissions) assigned to the user |
|
||||
|
||||
## Conditional Rendering
|
||||
|
||||
Only links which render with non-empty text are included on the page. You can employ conditional Jinja2 logic to control the conditions under which a link gets rendered.
|
||||
|
||||
For example, if you only want to display a link for active devices, you could set the link text to
|
||||
|
||||
```
|
||||
{% if obj.status == 1 %}View NMS{% endif %}
|
||||
```jinja2
|
||||
{% if obj.status == 'active' %}View NMS{% endif %}
|
||||
```
|
||||
|
||||
The link will not appear when viewing a device with any status other than "active."
|
||||
|
||||
Another example, if you want to only show an object of a certain manufacturer, you could set the link text to:
|
||||
As another example, if you wanted to show only devices belonging to a certain manufacturer, you could do something like this:
|
||||
|
||||
```
|
||||
{% if obj.device_type.manufacturer.name == 'Cisco' %}View NMS {% endif %}
|
||||
```jinja2
|
||||
{% if obj.device_type.manufacturer.name == 'Cisco' %}View NMS{% endif %}
|
||||
```
|
||||
|
||||
The link will only appear when viewing a device with a manufacturer name of "Cisco."
|
||||
|
||||
## Link Groups
|
||||
|
||||
You can specify a group name to organize links into related sets. Grouped links will render as a dropdown menu beneath a
|
||||
single button bearing the name of the group.
|
||||
Group names can be specified to organize links into groups. Links with the same group name will render as a dropdown menu beneath a single button bearing the name of the group.
|
||||
|
@ -6,22 +6,24 @@ Custom scripting was introduced to provide a way for users to execute custom log
|
||||
* Create a range of new reserved prefixes or IP addresses
|
||||
* Fetch data from an external source and import it to NetBox
|
||||
|
||||
Custom scripts are Python code and exist outside of the official NetBox code base, so they can be updated and changed without interfering with the core NetBox installation. And because they're written from scratch, a custom script can be used to accomplish just about anything.
|
||||
Custom scripts are Python code and exist outside of the official NetBox code base, so they can be updated and changed without interfering with the core NetBox installation. And because they're completely custom, there is no inherent limitation on what a script can accomplish.
|
||||
|
||||
## Writing Custom Scripts
|
||||
|
||||
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
|
||||
from extras.scripts import Script
|
||||
|
||||
class MyScript(Script):
|
||||
..
|
||||
...
|
||||
```
|
||||
|
||||
Scripts comprise two core components: variables and a `run()` method. Variables allow your script to accept user input via the NetBox UI. The `run()` method is where your script's execution logic lives. (Note that your script can have as many methods as needed: this is merely the point of invocation for NetBox.)
|
||||
Scripts comprise two core components: a set of variables and a `run()` method. Variables allow your script to accept user input via the NetBox UI, but they are optional: If your script does not require any user input, there is no need to define any variables.
|
||||
|
||||
```
|
||||
The `run()` method is where your script's execution logic lives. (Note that your script can have as many methods as needed: this is merely the point of invocation for NetBox.)
|
||||
|
||||
```python
|
||||
class MyScript(Script):
|
||||
var1 = StringVar(...)
|
||||
var2 = IntegerVar(...)
|
||||
@ -37,17 +39,17 @@ The `run()` method should accept two arguments:
|
||||
* `commit` - A boolean indicating whether database changes will be committed.
|
||||
|
||||
!!! note
|
||||
The `commit` argument was introduced in NetBox v2.7.8. Backward compatibility is maintained for scripts which accept only the `data` argument, however moving forward scripts should accept both arguments.
|
||||
The `commit` argument was introduced in NetBox v2.7.8. Backward compatibility is maintained for scripts which accept only the `data` argument, however beginning with v2.10 NetBox will require the `run()` method of every script to accept both arguments. (Either argument may still be ignored within the method.)
|
||||
|
||||
Defining variables is optional: You may create a script with only a `run()` method if no user input is needed.
|
||||
Defining script variables is optional: You may create a script with only a `run()` method if no user input is needed.
|
||||
|
||||
Returning output from your script is optional. Any raw output generated by the script will be displayed under the "output" tab in the UI.
|
||||
Any output generated by the script during its execution will be displayed under the "output" tab in the UI.
|
||||
|
||||
## Module Attributes
|
||||
|
||||
### `name`
|
||||
|
||||
You can define `name` within a script module (the Python file which contains one or more scripts) to set the module name. If `name` is not defined, the filename will be used.
|
||||
You can define `name` within a script module (the Python file which contains one or more scripts) to set the module name. If `name` is not defined, the module's file name will be used.
|
||||
|
||||
## Script Attributes
|
||||
|
||||
@ -61,19 +63,11 @@ This is the human-friendly names of your script. If omitted, the class name will
|
||||
|
||||
A human-friendly description of what your script does.
|
||||
|
||||
### `field_order`
|
||||
|
||||
A list of field names indicating the order in which the form fields should appear. This is optional, and should not be required on Python 3.6 and above. For example:
|
||||
|
||||
```
|
||||
field_order = ['var1', 'var2', 'var3']
|
||||
```
|
||||
|
||||
### `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.
|
||||
|
||||
```
|
||||
```python
|
||||
commit_default = False
|
||||
```
|
||||
|
||||
@ -83,8 +77,9 @@ Details of the current HTTP request (the one being made to execute the script) a
|
||||
|
||||
```python
|
||||
username = self.request.user.username
|
||||
ip_address = self.request.META.get('HTTP_X_FORWARDED_FOR') or self.request.META.get('REMOTE_ADDR')
|
||||
self.log_info("Running as user {} (IP: {})...".format(username, ip_address))
|
||||
ip_address = self.request.META.get('HTTP_X_FORWARDED_FOR') or \
|
||||
self.request.META.get('REMOTE_ADDR')
|
||||
self.log_info(f"Running as user {username} (IP: {ip_address})...")
|
||||
```
|
||||
|
||||
For a complete list of available request parameters, please see the [Django documentation](https://docs.djangoproject.com/en/stable/ref/request-response/).
|
||||
@ -112,30 +107,40 @@ Log messages are returned to the user upon execution of the script. Markdown ren
|
||||
|
||||
## Variable Reference
|
||||
|
||||
### Default Options
|
||||
|
||||
All custom script variables support the following default options:
|
||||
|
||||
* `default` - The field's default value
|
||||
* `description` - A brief user-friendly description of the field
|
||||
* `label` - The field name to be displayed in the rendered form
|
||||
* `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/))
|
||||
|
||||
### StringVar
|
||||
|
||||
Stores a string of characters (i.e. a line of text). Options include:
|
||||
Stores a string of characters (i.e. text). Options include:
|
||||
|
||||
* `min_length` - Minimum number of characters
|
||||
* `max_length` - Maximum number of characters
|
||||
* `regex` - A regular expression against which the provided value must match
|
||||
|
||||
Note: `min_length` and `max_length` can be set to the same number to effect a fixed-length field.
|
||||
Note that `min_length` and `max_length` can be set to the same number to effect a fixed-length field.
|
||||
|
||||
### TextVar
|
||||
|
||||
Arbitrary text of any length. Renders as multi-line text input field.
|
||||
Arbitrary text of any length. Renders as a multi-line text input field.
|
||||
|
||||
### IntegerVar
|
||||
|
||||
Stored a numeric integer. Options include:
|
||||
Stores a numeric integer. Options include:
|
||||
|
||||
* `min_value` - Minimum value
|
||||
* `max_value` - Maximum value
|
||||
|
||||
### BooleanVar
|
||||
|
||||
A true/false flag. This field has no options beyond the defaults.
|
||||
A true/false flag. This field has no options beyond the defaults listed above.
|
||||
|
||||
### ChoiceVar
|
||||
|
||||
@ -154,15 +159,62 @@ CHOICES = (
|
||||
direction = ChoiceVar(choices=CHOICES)
|
||||
```
|
||||
|
||||
In the example above, selecting the choice labeled "North" will submit the value `n`.
|
||||
|
||||
### MultiChoiceVar
|
||||
|
||||
Similar to `ChoiceVar`, but allows for the selection of multiple choices.
|
||||
|
||||
### ObjectVar
|
||||
|
||||
A NetBox object. The list of available objects is defined by the queryset parameter. Each instance of this variable is limited to a single object type.
|
||||
A particular object within NetBox. Each ObjectVar must specify a particular model, and allows the user to select one of the available instances. ObjectVar accepts several arguments, listed below.
|
||||
|
||||
* `queryset` - A [Django queryset](https://docs.djangoproject.com/en/stable/topics/db/queries/)
|
||||
* `model` - The model class
|
||||
* `display_field` - The name of the REST API object field to display in the selection list (default: `'name'`)
|
||||
* `query_params` - A dictionary of query parameters to use when retrieving available options (optional)
|
||||
* `null_option` - A label representing a "null" or empty choice (optional)
|
||||
|
||||
The `display_field` argument is useful when referencing a model which does not have a `name` field. For example, when displaying a list of device types, you would likely use the `model` field:
|
||||
|
||||
```python
|
||||
device_type = ObjectVar(
|
||||
model=DeviceType,
|
||||
display_field='model'
|
||||
)
|
||||
```
|
||||
|
||||
To limit the selections available within the list, additional query parameters can be passed as the `query_params` dictionary. For example, to show only devices with an "active" status:
|
||||
|
||||
```python
|
||||
device = ObjectVar(
|
||||
model=Device,
|
||||
query_params={
|
||||
'status': 'active'
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
Multiple values can be specified by assigning a list to the dictionary key. It is also possible to reference the value of other fields in the form by prepending a dollar sign (`$`) to the variable's name.
|
||||
|
||||
```python
|
||||
region = ObjectVar(
|
||||
model=Region
|
||||
)
|
||||
site = ObjectVar(
|
||||
model=Site,
|
||||
query_params={
|
||||
'region_id': '$region'
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
### MultiObjectVar
|
||||
|
||||
Similar to `ObjectVar`, but allows for the selection of multiple objects.
|
||||
|
||||
### FileVar
|
||||
|
||||
An uploaded file. Note that uploaded files are present in memory only for the duration of the script's execution: They will not be save for future use.
|
||||
An uploaded file. Note that uploaded files are present in memory only for the duration of the script's execution: They will not be automatically saved for future use. The script is responsible for writing file contents to disk where necessary.
|
||||
|
||||
### IPAddressVar
|
||||
|
||||
@ -176,18 +228,32 @@ An IPv4 or IPv6 address with a mask. Returns a `netaddr.IPNetwork` object which
|
||||
|
||||
An IPv4 or IPv6 network with a mask. Returns a `netaddr.IPNetwork` object. Two attributes are available to validate the provided mask:
|
||||
|
||||
* `min_prefix_length` - Minimum length of the mask (default: none)
|
||||
* `max_prefix_length` - Maximum length of the mask (default: none)
|
||||
* `min_prefix_length` - Minimum length of the mask
|
||||
* `max_prefix_length` - Maximum length of the mask
|
||||
|
||||
### Default Options
|
||||
## Running Custom Scripts
|
||||
|
||||
All variables support the following default options:
|
||||
!!! note
|
||||
To run a custom script, a user must be assigned the `extras.run_script` permission. This is achieved by assigning the user (or group) a permission on the Script object and specifying the `run` action in the admin UI as shown below.
|
||||
|
||||
* `default` - The field's default value
|
||||
* `description` - A brief description of the field
|
||||
* `label` - The name of the form field
|
||||
* `required` - Indicates whether the field is mandatory (default: true)
|
||||
* `widget` - The class of form widget to use (see the [Django documentation](https://docs.djangoproject.com/en/stable/ref/forms/widgets/))
|
||||

|
||||
|
||||
### Via the Web UI
|
||||
|
||||
Custom scripts can be run via the web UI by navigating to the script, completing any required form data, and clicking the "run script" button.
|
||||
|
||||
### Via the API
|
||||
|
||||
To run a script via the REST API, issue a POST request to the script's endpoint specifying the form data and commitment. For example, to run a script named `example.MyReport`, we would make a request such as the following:
|
||||
|
||||
```no-highlight
|
||||
curl -X POST \
|
||||
-H "Authorization: Token $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Accept: application/json; indent=4" \
|
||||
http://netbox/api/extras/scripts/example.MyReport/ \
|
||||
--data '{"data": {"foo": "somevalue", "bar": 123}, "commit": true}'
|
||||
```
|
||||
|
||||
## Example
|
||||
|
||||
@ -199,11 +265,11 @@ Below is an example script that creates new objects for a planned site. The user
|
||||
|
||||
These variables are presented as a web form to be completed by the user. Once submitted, the script's `run()` method is called to create the appropriate objects.
|
||||
|
||||
```
|
||||
```python
|
||||
from django.utils.text import slugify
|
||||
|
||||
from dcim.choices import DeviceStatusChoices, SiteStatusChoices
|
||||
from dcim.models import Device, DeviceRole, DeviceType, Site
|
||||
from dcim.models import Device, DeviceRole, DeviceType, Manufacturer, Site
|
||||
from extras.scripts import *
|
||||
|
||||
|
||||
@ -220,12 +286,17 @@ class NewBranchScript(Script):
|
||||
switch_count = IntegerVar(
|
||||
description="Number of access switches to create"
|
||||
)
|
||||
manufacturer = ObjectVar(
|
||||
model=Manufacturer,
|
||||
required=False
|
||||
)
|
||||
switch_model = ObjectVar(
|
||||
description="Access switch model",
|
||||
queryset = DeviceType.objects.filter(
|
||||
manufacturer__name='Cisco',
|
||||
model__in=['Catalyst 3560X-48T', 'Catalyst 3750X-48T']
|
||||
)
|
||||
model=DeviceType,
|
||||
display_field='model',
|
||||
query_params={
|
||||
'manufacturer_id': '$manufacturer'
|
||||
}
|
||||
)
|
||||
|
||||
def run(self, data, commit):
|
||||
@ -237,20 +308,20 @@ class NewBranchScript(Script):
|
||||
status=SiteStatusChoices.STATUS_PLANNED
|
||||
)
|
||||
site.save()
|
||||
self.log_success("Created new site: {}".format(site))
|
||||
self.log_success(f"Created new site: {site}")
|
||||
|
||||
# Create access switches
|
||||
switch_role = DeviceRole.objects.get(name='Access Switch')
|
||||
for i in range(1, data['switch_count'] + 1):
|
||||
switch = Device(
|
||||
device_type=data['switch_model'],
|
||||
name='{}-switch{}'.format(site.slug, i),
|
||||
name=f'{site.slug}-switch{i}',
|
||||
site=site,
|
||||
status=DeviceStatusChoices.STATUS_PLANNED,
|
||||
device_role=switch_role
|
||||
)
|
||||
switch.save()
|
||||
self.log_success("Created new switch: {}".format(switch))
|
||||
self.log_success(f"Created new switch: {switch}")
|
||||
|
||||
# Generate a CSV table of new devices
|
||||
output = [
|
||||
|
@ -4,9 +4,11 @@ NetBox allows users to define custom templates that can be used when exporting o
|
||||
|
||||
Each export template is associated with a certain type of object. For instance, if you create an export template for VLANs, your custom template will appear under the "Export" button on the VLANs list.
|
||||
|
||||
Export templates are written in [Django's template language](https://docs.djangoproject.com/en/stable/ref/templates/language/), which is very similar to Jinja2. The list of objects returned from the database is stored in the `queryset` variable, which you'll typically want to iterate through using a `for` loop. Object properties can be access by name. For example:
|
||||
Export templates must be written in [Jinja2](https://jinja.palletsprojects.com/).
|
||||
|
||||
```
|
||||
The list of objects returned from the database when rendering an export template is stored in the `queryset` variable, which you'll typically want to iterate through using a `for` loop. Object properties can be access by name. For example:
|
||||
|
||||
```jinja2
|
||||
{% for rack in queryset %}
|
||||
Rack: {{ rack.name }}
|
||||
Site: {{ rack.site.name }}
|
||||
|
@ -1,30 +0,0 @@
|
||||
# Graphs
|
||||
|
||||
NetBox does not have the ability to generate graphs natively, but this feature allows you to embed contextual graphs from an external resources (such as a monitoring system) inside the site, provider, and interface views. Each embedded graph must be defined with the following parameters:
|
||||
|
||||
* **Type:** Site, device, provider, or interface. This determines in which view the graph will be displayed.
|
||||
* **Weight:** Determines the order in which graphs are displayed (lower weights are displayed first). Graphs with equal weights will be ordered alphabetically by name.
|
||||
* **Name:** The title to display above the graph.
|
||||
* **Source URL:** The source of the image to be embedded. The associated object will be available as a template variable named `obj`.
|
||||
* **Link URL (optional):** A URL to which the graph will be linked. The associated object will be available as a template variable named `obj`.
|
||||
|
||||
Graph names and links can be rendered using the Django or Jinja2 template languages.
|
||||
|
||||
!!! warning
|
||||
Support for the Django templating language will be removed in NetBox v2.8. Jinja2 is recommended.
|
||||
|
||||
## Examples
|
||||
|
||||
You only need to define one graph object for each graph you want to include when viewing an object. For example, if you want to include a graph of traffic through an interface over the past five minutes, your graph source might looks like this:
|
||||
|
||||
```
|
||||
https://my.nms.local/graphs/?node={{ obj.device.name }}&interface={{ obj.name }}&duration=5m
|
||||
```
|
||||
|
||||
You can define several graphs to provide multiple contexts when viewing an object. For example:
|
||||
|
||||
```
|
||||
https://my.nms.local/graphs/?type=throughput&node={{ obj.device.name }}&interface={{ obj.name }}&duration=60m
|
||||
https://my.nms.local/graphs/?type=throughput&node={{ obj.device.name }}&interface={{ obj.name }}&duration=24h
|
||||
https://my.nms.local/graphs/?type=errors&node={{ obj.device.name }}&interface={{ obj.name }}&duration=60m
|
||||
```
|
@ -1,11 +1,13 @@
|
||||
# NAPALM
|
||||
|
||||
NetBox supports integration with the [NAPALM automation](https://napalm-automation.net/) library. NAPALM allows NetBox to fetch live data from devices and return it to a requester via its REST API.
|
||||
NetBox supports integration with the [NAPALM automation](https://napalm-automation.net/) library. NAPALM allows NetBox to serve a proxy for operational data, fetching live data from network devices and returning it to a requester via its REST API. Note that NetBox does not store any NAPALM data locally.
|
||||
|
||||
!!! info
|
||||
To enable the integration, the NAPALM library must be installed. See [installation steps](../../installation/3-netbox/#napalm-automation-optional) for more information.
|
||||
!!! note
|
||||
To enable this integration, the NAPALM library must be installed. See [installation steps](../../installation/3-netbox/#napalm) for more information.
|
||||
|
||||
```
|
||||
Below is an example REST API request and response:
|
||||
|
||||
```no-highlight
|
||||
GET /api/dcim/devices/1/napalm/?method=get_environment
|
||||
|
||||
{
|
||||
@ -15,13 +17,16 @@ GET /api/dcim/devices/1/napalm/?method=get_environment
|
||||
}
|
||||
```
|
||||
|
||||
!!! note
|
||||
To make NAPALM requests via the NetBox REST API, a NetBox user must have assigned a permission granting the `napalm_read` action for the device object type.
|
||||
|
||||
## Authentication
|
||||
|
||||
By default, the [`NAPALM_USERNAME`](../../configuration/optional-settings/#napalm_username) and [`NAPALM_PASSWORD`](../../configuration/optional-settings/#napalm_password) are used for NAPALM authentication. They can be overridden for an individual API call through the `X-NAPALM-Username` and `X-NAPALM-Password` headers.
|
||||
By default, the [`NAPALM_USERNAME`](../../configuration/optional-settings/#napalm_username) and [`NAPALM_PASSWORD`](../../configuration/optional-settings/#napalm_password) configuration parameters are used for NAPALM authentication. They can be overridden for an individual API call by specifying the `X-NAPALM-Username` and `X-NAPALM-Password` headers.
|
||||
|
||||
```
|
||||
$ curl "http://localhost/api/dcim/devices/1/napalm/?method=get_environment" \
|
||||
-H "Authorization: Token f4b378553dacfcfd44c5a0b9ae49b57e29c552b5" \
|
||||
-H "Authorization: Token $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Accept: application/json; indent=4" \
|
||||
-H "X-NAPALM-Username: foo" \
|
||||
@ -30,13 +35,13 @@ $ curl "http://localhost/api/dcim/devices/1/napalm/?method=get_environment" \
|
||||
|
||||
## Method Support
|
||||
|
||||
The list of supported NAPALM methods depends on the [NAPALM driver](https://napalm.readthedocs.io/en/latest/support/index.html#general-support-matrix) configured for the platform of a device. NetBox only supports [get](https://napalm.readthedocs.io/en/latest/support/index.html#getters-support-matrix) methods.
|
||||
The list of supported NAPALM methods depends on the [NAPALM driver](https://napalm.readthedocs.io/en/latest/support/index.html#general-support-matrix) configured for the platform of a device. Because there is no granular mechanism in place for limiting potentially disruptive requests, NetBox supports only read-only [get](https://napalm.readthedocs.io/en/latest/support/index.html#getters-support-matrix) methods.
|
||||
|
||||
## Multiple Methods
|
||||
|
||||
More than one method in an API call can be invoked by adding multiple `method` parameters. For example:
|
||||
It is possible to request the output of multiple NAPALM methods in a single API request by passing multiple `method` parameters. For example:
|
||||
|
||||
```
|
||||
```no-highlight
|
||||
GET /api/dcim/devices/1/napalm/?method=get_ntp_servers&method=get_ntp_peers
|
||||
|
||||
{
|
||||
@ -51,14 +56,11 @@ GET /api/dcim/devices/1/napalm/?method=get_ntp_servers&method=get_ntp_peers
|
||||
|
||||
## Optional Arguments
|
||||
|
||||
The behavior of NAPALM drivers can be adjusted according to the [optional arguments](https://napalm.readthedocs.io/en/latest/support/index.html#optional-arguments). NetBox exposes those arguments using headers prefixed with `X-NAPALM-`.
|
||||
|
||||
|
||||
For instance, the SSH port is changed to 2222 in this API call:
|
||||
The behavior of NAPALM drivers can be adjusted according to the [optional arguments](https://napalm.readthedocs.io/en/latest/support/index.html#optional-arguments). NetBox exposes those arguments using headers prefixed with `X-NAPALM-`. For example, the SSH port is changed to 2222 in this API call:
|
||||
|
||||
```
|
||||
$ curl "http://localhost/api/dcim/devices/1/napalm/?method=get_environment" \
|
||||
-H "Authorization: Token f4b378553dacfcfd44c5a0b9ae49b57e29c552b5" \
|
||||
-H "Authorization: Token $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Accept: application/json; indent=4" \
|
||||
-H "X-NAPALM-port: 2222"
|
||||
|
@ -23,16 +23,7 @@ For the exhaustive list of exposed metrics, visit the `/metrics` endpoint on you
|
||||
|
||||
## Multi Processing Notes
|
||||
|
||||
When deploying NetBox in a multiprocess mannor--such as using Gunicorn as recomented in the installation docs--the Prometheus client library requires the use of a shared directory
|
||||
to collect metrics from all the worker processes. This can be any arbitrary directory to which the processes have read/write access. This directory is then made available by use of the
|
||||
`prometheus_multiproc_dir` environment variable.
|
||||
When deploying NetBox in a multiprocess manner (e.g. running multiple Gunicorn workers) the Prometheus client library requires the use of a shared directory to collect metrics from all worker processes. To configure this, first create or designate a local directory to which the worker processes have read and write access, and then configure your WSGI service (e.g. Gunicorn) to define this path as the `prometheus_multiproc_dir` environment variable.
|
||||
|
||||
This can be setup by first creating a shared directory and then adding this line (with the appropriate directory) to the `[program:netbox]` section of the supervisor config file.
|
||||
|
||||
```
|
||||
environment=prometheus_multiproc_dir=/tmp/prometheus_metrics
|
||||
```
|
||||
|
||||
#### Accuracy
|
||||
|
||||
If having accurate long-term metrics in a multiprocess environment is important to you then it's recommended you use the `uwsgi` library instead of `gunicorn`. The issue lies in the way `gunicorn` tracks worker processes (vs `uwsgi`) which helps manage the metrics files created by the above configurations. If you're using Netbox with gunicorn in a containerized enviroment following the one-process-per-container methodology, then you will likely not need to change to `uwsgi`. More details can be found in [issue #3779](https://github.com/netbox-community/netbox/issues/3779#issuecomment-590547562).
|
||||
!!! warning
|
||||
If having accurate long-term metrics in a multiprocess environment is crucial to your deployment, it's recommended you use the `uwsgi` library instead of `gunicorn`. The issue lies in the way `gunicorn` tracks worker processes (vs `uwsgi`) which helps manage the metrics files created by the above configurations. If you're using Netbox with gunicorn in a containerized enviroment following the one-process-per-container methodology, then you will likely not need to change to `uwsgi`. More details can be found in [issue #3779](https://github.com/netbox-community/netbox/issues/3779#issuecomment-590547562).
|
@ -101,11 +101,14 @@ Once you have created a report, it will appear in the reports list. Initially, r
|
||||
|
||||
## Running Reports
|
||||
|
||||
!!! note
|
||||
To run a report, a user must be assigned the `extras.run_report` permission. This is achieved by assigning the user (or group) a permission on the Report object and specifying the `run` action in the admin UI as shown below.
|
||||
|
||||

|
||||
|
||||
### Via the Web UI
|
||||
|
||||
Reports can be run via the web UI by navigating to the report and clicking the "run report" button at top right. Note that a user must have permission to create ReportResults in order to run reports. (Permissions can be assigned through the admin UI.)
|
||||
|
||||
Once a report has been run, its associated results will be included in the report view.
|
||||
Reports can be run via the web UI by navigating to the report and clicking the "run report" button at top right. Once a report has been run, its associated results will be included in the report view.
|
||||
|
||||
### Via the API
|
||||
|
||||
|
@ -1,3 +0,0 @@
|
||||
# Tagging
|
||||
|
||||
{!docs/models/extras/tag.md!}
|
@ -1,6 +1,6 @@
|
||||
# Webhooks
|
||||
|
||||
A webhook is a mechanism for conveying to some external system a change that took place in NetBox. For example, you may want to notify a monitoring system whenever a device status is changed in NetBox. This can be done by creating a webhook for the device model in NetBox. When NetBox detects a change to a device, an HTTP request containing the details of the change and who made it be sent to the specified receiver. Webhooks are configured in the admin UI under Extras > Webhooks.
|
||||
A webhook is a mechanism for conveying to some external system a change that took place in NetBox. For example, you may want to notify a monitoring system whenever the status of a device is updated in NetBox. This can be done by creating a webhook for the device model in NetBox and identifying the webhook receiver. When NetBox detects a change to a device, an HTTP request containing the details of the change and who made it be sent to the specified receiver. Webhooks are configured in the admin UI under Extras > Webhooks.
|
||||
|
||||
## Configuration
|
||||
|
||||
@ -8,7 +8,7 @@ A webhook is a mechanism for conveying to some external system a change that too
|
||||
* **Object type(s)** - The type or types of NetBox object that will trigger the webhook.
|
||||
* **Enabled** - If unchecked, the webhook will be inactive.
|
||||
* **Events** - A webhook may trigger on any combination of create, update, and delete events. At least one event type must be selected.
|
||||
* **HTTP method** - The type of HTTP request to send. Options include GET, POST, PUT, PATCH, and DELETE.
|
||||
* **HTTP method** - The type of HTTP request to send. Options include `GET`, `POST`, `PUT`, `PATCH`, and `DELETE`.
|
||||
* **URL** - The fuly-qualified URL of the request to be sent. This may specify a destination port number if needed.
|
||||
* **HTTP content type** - The value of the request's `Content-Type` header. (Defaults to `application/json`)
|
||||
* **Additional headers** - Any additional headers to include with the request (optional). Add one header per line in the format `Name: Value`. Jinja2 templating is supported for this field (see below).
|
||||
@ -19,13 +19,13 @@ A webhook is a mechanism for conveying to some external system a change that too
|
||||
|
||||
## Jinja2 Template Support
|
||||
|
||||
[Jinja2 templating](https://jinja.palletsprojects.com/) is supported for the `additional_headers` and `body_template` fields. This enables the user to convey change data in the request headers as well as to craft a customized request body. Request content can be crafted to enable the direct interaction with external systems by ensuring the outgoing message is in a format the receiver expects and understands.
|
||||
[Jinja2 templating](https://jinja.palletsprojects.com/) is supported for the `additional_headers` and `body_template` fields. This enables the user to convey object data in the request headers as well as to craft a customized request body. Request content can be crafted to enable the direct interaction with external systems by ensuring the outgoing message is in a format the receiver expects and understands.
|
||||
|
||||
For example, you might create a NetBox webhook to [trigger a Slack message](https://api.slack.com/messaging/webhooks) any time an IP address is created. You can accomplish this using the following configuration:
|
||||
|
||||
* Object type: IPAM > IP address
|
||||
* HTTP method: POST
|
||||
* URL: <Slack incoming webhook URL>
|
||||
* HTTP method: `POST`
|
||||
* URL: Slack incoming webhook URL
|
||||
* HTTP content type: `application/json`
|
||||
* Body template: `{"text": "IP address {{ data['address'] }} was created by {{ username }}!"}`
|
||||
|
||||
|
@ -1,12 +1,12 @@
|
||||
# The NetBox Python Shell
|
||||
|
||||
NetBox includes a Python shell within which objects can be directly queried, created, modified, and deleted. To enter the shell, run the following command:
|
||||
NetBox includes a Python management shell within which objects can be directly queried, created, modified, and deleted. To enter the shell, run the following command:
|
||||
|
||||
```
|
||||
./manage.py nbshell
|
||||
```
|
||||
|
||||
This will launch a customized version of [the built-in Django shell](https://docs.djangoproject.com/en/stable/ref/django-admin/#shell) with all relevant NetBox models pre-loaded. (If desired, the stock Django shell is also available by executing `./manage.py shell`.)
|
||||
This will launch a lightly customized version of [the built-in Django shell](https://docs.djangoproject.com/en/stable/ref/django-admin/#shell) with all relevant NetBox models pre-loaded. (If desired, the stock Django shell is also available by executing `./manage.py shell`.)
|
||||
|
||||
```
|
||||
$ ./manage.py nbshell
|
||||
@ -28,13 +28,17 @@ DCIM:
|
||||
...
|
||||
```
|
||||
|
||||
!!! warning
|
||||
The NetBox shell affords direct access to NetBox data and function with very little validation in place. As such, it is crucial to ensure that only authorized, knowledgeable users are ever granted access to it. Never perform any action in the management shell without having a full backup in place.
|
||||
|
||||
## Querying Objects
|
||||
|
||||
Objects are retrieved by forming a [Django queryset](https://docs.djangoproject.com/en/stable/topics/db/queries/#retrieving-objects). The base queryset for an object takes the form `<model>.objects.all()`, which will return a (truncated) list of all objects of that type.
|
||||
Objects are retrieved from the database using a [Django queryset](https://docs.djangoproject.com/en/stable/topics/db/queries/#retrieving-objects). The base queryset for an object takes the form `<model>.objects.all()`, which will return a (truncated) list of all objects of that type.
|
||||
|
||||
```
|
||||
>>> Device.objects.all()
|
||||
<QuerySet [<Device: TestDevice1>, <Device: TestDevice2>, <Device: TestDevice3>, <Device: TestDevice4>, <Device: TestDevice5>, '...(remaining elements truncated)...']>
|
||||
<QuerySet [<Device: TestDevice1>, <Device: TestDevice2>, <Device: TestDevice3>,
|
||||
<Device: TestDevice4>, <Device: TestDevice5>, '...(remaining elements truncated)...']>
|
||||
```
|
||||
|
||||
Use a `for` loop to cycle through all objects in the list:
|
||||
@ -43,11 +47,11 @@ Use a `for` loop to cycle through all objects in the list:
|
||||
>>> for device in Device.objects.all():
|
||||
... print(device.name, device.device_type)
|
||||
...
|
||||
(u'TestDevice1', <DeviceType: PacketThingy 9000>)
|
||||
(u'TestDevice2', <DeviceType: PacketThingy 9000>)
|
||||
(u'TestDevice3', <DeviceType: PacketThingy 9000>)
|
||||
(u'TestDevice4', <DeviceType: PacketThingy 9000>)
|
||||
(u'TestDevice5', <DeviceType: PacketThingy 9000>)
|
||||
('TestDevice1', <DeviceType: PacketThingy 9000>)
|
||||
('TestDevice2', <DeviceType: PacketThingy 9000>)
|
||||
('TestDevice3', <DeviceType: PacketThingy 9000>)
|
||||
('TestDevice4', <DeviceType: PacketThingy 9000>)
|
||||
('TestDevice5', <DeviceType: PacketThingy 9000>)
|
||||
...
|
||||
```
|
||||
|
||||
@ -67,52 +71,53 @@ To retrieve a particular object (typically by its primary key or other unique fi
|
||||
|
||||
### Filtering Querysets
|
||||
|
||||
In most cases, you want to retrieve only a specific subset of objects. To filter a queryset, replace `all()` with `filter()` and pass one or more keyword arguments. For example:
|
||||
In most cases, you will want to retrieve only a specific subset of objects. To filter a queryset, replace `all()` with `filter()` and pass one or more keyword arguments. For example:
|
||||
|
||||
```
|
||||
>>> Device.objects.filter(status=STATUS_ACTIVE)
|
||||
<QuerySet [<Device: TestDevice1>, <Device: TestDevice2>, <Device: TestDevice3>, <Device: TestDevice8>, <Device: TestDevice9>, '...(remaining elements truncated)...']>
|
||||
>>> Device.objects.filter(status="active")
|
||||
<QuerySet [<Device: TestDevice1>, <Device: TestDevice2>, <Device: TestDevice3>,
|
||||
<Device: TestDevice8>, <Device: TestDevice9>, '...(remaining elements truncated)...']>
|
||||
```
|
||||
|
||||
Querysets support slicing to return a specific range of objects.
|
||||
|
||||
```
|
||||
>>> Device.objects.filter(status=STATUS_ACTIVE)[:3]
|
||||
>>> Device.objects.filter(status="active")[:3]
|
||||
<QuerySet [<Device: TestDevice1>, <Device: TestDevice2>, <Device: TestDevice3>]>
|
||||
```
|
||||
|
||||
The `count()` method can be appended to the queryset to return a count of objects rather than the full list.
|
||||
|
||||
```
|
||||
>>> Device.objects.filter(status=STATUS_ACTIVE).count()
|
||||
>>> Device.objects.filter(status="active").count()
|
||||
982
|
||||
```
|
||||
|
||||
Relationships with other models can be traversed by concatenating field names with a double-underscore. For example, the following will return all devices assigned to the tenant named "Pied Piper."
|
||||
Relationships with other models can be traversed by concatenating attribute names with a double-underscore. For example, the following will return all devices assigned to the tenant named "Pied Piper."
|
||||
|
||||
```
|
||||
>>> Device.objects.filter(tenant__name='Pied Piper')
|
||||
>>> Device.objects.filter(tenant__name="Pied Piper")
|
||||
```
|
||||
|
||||
This approach can span multiple levels of relations. For example, the following will return all IP addresses assigned to a device in North America:
|
||||
|
||||
```
|
||||
>>> IPAddress.objects.filter(interface__device__site__region__slug='north-america')
|
||||
>>> IPAddress.objects.filter(interface__device__site__region__slug="north-america")
|
||||
```
|
||||
|
||||
!!! note
|
||||
While the above query is functional, it is very inefficient. There are ways to optimize such requests, however they are out of the scope of this document. For more information, see the [Django queryset method reference](https://docs.djangoproject.com/en/stable/ref/models/querysets/) documentation.
|
||||
While the above query is functional, it's not very efficient. There are ways to optimize such requests, however they are out of scope for this document. For more information, see the [Django queryset method reference](https://docs.djangoproject.com/en/stable/ref/models/querysets/) documentation.
|
||||
|
||||
Reverse relationships can be traversed as well. For example, the following will find all devices with an interface named "em0":
|
||||
|
||||
```
|
||||
>>> Device.objects.filter(interfaces__name='em0')
|
||||
>>> Device.objects.filter(interfaces__name="em0")
|
||||
```
|
||||
|
||||
Character fields can be filtered against partial matches using the `contains` or `icontains` field lookup (the later of which is case-insensitive).
|
||||
|
||||
```
|
||||
>>> Device.objects.filter(name__icontains='testdevice')
|
||||
>>> Device.objects.filter(name__icontains="testdevice")
|
||||
```
|
||||
|
||||
Similarly, numeric fields can be filtered by values less than, greater than, and/or equal to a given value.
|
||||
@ -124,7 +129,7 @@ Similarly, numeric fields can be filtered by values less than, greater than, and
|
||||
Multiple filters can be combined to further refine a queryset.
|
||||
|
||||
```
|
||||
>>> VLAN.objects.filter(vid__gt=2000, name__icontains='engineering')
|
||||
>>> VLAN.objects.filter(vid__gt=2000, name__icontains="engineering")
|
||||
```
|
||||
|
||||
To return the inverse of a filtered queryset, use `exclude()` instead of `filter()`.
|
||||
@ -132,18 +137,18 @@ To return the inverse of a filtered queryset, use `exclude()` instead of `filter
|
||||
```
|
||||
>>> Device.objects.count()
|
||||
4479
|
||||
>>> Device.objects.filter(status=STATUS_ACTIVE).count()
|
||||
>>> Device.objects.filter(status="active").count()
|
||||
4133
|
||||
>>> Device.objects.exclude(status=STATUS_ACTIVE).count()
|
||||
>>> Device.objects.exclude(status="active").count()
|
||||
346
|
||||
```
|
||||
|
||||
!!! info
|
||||
The examples above are intended only to provide a cursory introduction to queryset filtering. For an exhaustive list of the available filters, please consult the [Django queryset API docs](https://docs.djangoproject.com/en/stable/ref/models/querysets/).
|
||||
The examples above are intended only to provide a cursory introduction to queryset filtering. For an exhaustive list of the available filters, please consult the [Django queryset API documentation](https://docs.djangoproject.com/en/stable/ref/models/querysets/).
|
||||
|
||||
## Creating and Updating Objects
|
||||
|
||||
New objects can be created by instantiating the desired model, defining values for all required attributes, and calling `save()` on the instance.
|
||||
New objects can be created by instantiating the desired model, defining values for all required attributes, and calling `save()` on the instance. For example, we can create a new VLAN by specifying its numeric ID, name, and assigned site:
|
||||
|
||||
```
|
||||
>>> lab1 = Site.objects.get(pk=7)
|
||||
@ -151,22 +156,22 @@ New objects can be created by instantiating the desired model, defining values f
|
||||
>>> myvlan.save()
|
||||
```
|
||||
|
||||
Alternatively, the above can be performed as a single operation:
|
||||
Alternatively, the above can be performed as a single operation. (Note, however, that `save()` does _not_ return the new instance for reuse.)
|
||||
|
||||
```
|
||||
>>> VLAN(vid=123, name='MyNewVLAN', site=Site.objects.get(pk=7)).save()
|
||||
```
|
||||
|
||||
To modify an object, retrieve it, update the desired field(s), and call `save()` again.
|
||||
To modify an existing object, we retrieve it, update the desired field(s), and call `save()` again.
|
||||
|
||||
```
|
||||
>>> vlan = VLAN.objects.get(pk=1280)
|
||||
>>> vlan.name
|
||||
u'MyNewVLAN'
|
||||
'MyNewVLAN'
|
||||
>>> vlan.name = 'BetterName'
|
||||
>>> vlan.save()
|
||||
>>> VLAN.objects.get(pk=1280).name
|
||||
u'BetterName'
|
||||
'BetterName'
|
||||
```
|
||||
|
||||
!!! warning
|
||||
@ -180,7 +185,7 @@ To delete an object, simply call `delete()` on its instance. This will return a
|
||||
>>> vlan
|
||||
<VLAN: 123 (BetterName)>
|
||||
>>> vlan.delete()
|
||||
(1, {u'extras.CustomFieldValue': 0, u'ipam.VLAN': 1})
|
||||
(1, {'ipam.VLAN': 1})
|
||||
```
|
||||
|
||||
To delete multiple objects at once, call `delete()` on a filtered queryset. It's a good idea to always sanity-check the count of selected objects _before_ deleting them.
|
||||
@ -189,8 +194,10 @@ To delete multiple objects at once, call `delete()` on a filtered queryset. It's
|
||||
>>> Device.objects.filter(name__icontains='test').count()
|
||||
27
|
||||
>>> Device.objects.filter(name__icontains='test').delete()
|
||||
(35, {u'extras.CustomFieldValue': 0, u'dcim.DeviceBay': 0, u'secrets.Secret': 0, u'dcim.InterfaceConnection': 4, u'extras.ImageAttachment': 0, u'dcim.Device': 27, u'dcim.Interface': 4, u'dcim.ConsolePort': 0, u'dcim.PowerPort': 0})
|
||||
(35, {'dcim.DeviceBay': 0, 'secrets.Secret': 0, 'dcim.InterfaceConnection': 4,
|
||||
'extras.ImageAttachment': 0, 'dcim.Device': 27, 'dcim.Interface': 4,
|
||||
'dcim.ConsolePort': 0, 'dcim.PowerPort': 0})
|
||||
```
|
||||
|
||||
!!! warning
|
||||
Deletions are immediate and irreversible. Always think very carefully before calling `delete()` on an instance or queryset.
|
||||
Deletions are immediate and irreversible. Always consider the impact of deleting objects carefully before calling `delete()` on an instance or queryset.
|
||||
|
45
docs/administration/permissions.md
Normal file
45
docs/administration/permissions.md
Normal file
@ -0,0 +1,45 @@
|
||||
# Permissions
|
||||
|
||||
NetBox v2.9 introduced a new object-based permissions framework, which replace's Django's built-in permissions model. Object-based permissions enable an administrator to grant users or groups the ability to perform an action on arbitrary subsets of objects in NetBox, rather than all objects of a certain type. For example, it is possible to grant a user permission to view only sites within a particular region, or to modify only VLANs with a numeric ID within a certain range.
|
||||
|
||||
{!docs/models/users/objectpermission.md!}
|
||||
|
||||
### Example Constraint Definitions
|
||||
|
||||
| Constraints | Description |
|
||||
| ----------- | ----------- |
|
||||
| `{"status": "active"}` | Status is active |
|
||||
| `{"status__in": ["planned", "reserved"]}` | Status is active **OR** reserved |
|
||||
| `{"status": "active", "role": "testing"}` | Status is active **OR** role is testing |
|
||||
| `{"name__startswith": "Foo"}` | Name starts with "Foo" (case-sensitive) |
|
||||
| `{"name__iendswith": "bar"}` | Name ends with "bar" (case-insensitive) |
|
||||
| `{"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 |
|
||||
|
||||
## Permissions Enforcement
|
||||
|
||||
### Viewing Objects
|
||||
|
||||
Object-based permissions work by filtering the database query generated by a user's request to restrict the set of objects returned. When a request is received, NetBox first determines whether the user is authenticated and has been granted to perform the requested action. For example, if the requested URL is `/dcim/devices/`, NetBox will check for the `dcim.view_device` permission. If the user has not been assigned this permission (either directly or via a group assignment), NetBox will return a 403 (forbidden) HTTP response.
|
||||
|
||||
If the permission _has_ been granted, NetBox will compile any specified constraints for the model and action. For example, suppose two permissions have been assigned to the user granting view access to the device model, with the following constraints:
|
||||
|
||||
```json
|
||||
[
|
||||
{"site__name__in": ["NYC1", "NYC2"]},
|
||||
{"status": "offline", "tenant__isnull": true}
|
||||
]
|
||||
```
|
||||
|
||||
This grants the user access to view any device that is assigned to a site named NYC1 or NYC2, **or** which has a status of "offline" and has no tenant assigned. These constraints are equivalent to the following ORM query:
|
||||
|
||||
```no-highlight
|
||||
Site.objects.filter(
|
||||
Q(site__name__in=['NYC1', 'NYC2']),
|
||||
Q(status='active', tenant__isnull=True)
|
||||
)
|
||||
```
|
||||
|
||||
### Creating and Modifying Objects
|
||||
|
||||
The same sort of logic is in play when a user attempts to create or modify an object in NetBox, with a twist. Once validation has completed, NetBox starts an atomic database transaction to facilitate the change, and the object is created or saved normally. Next, still within the transaction, NetBox issues a second query to retrieve the newly created/updated object, filtering the restricted queryset with the object's primary key. If this query fails to return the object, NetBox knows that the new revision does not match the constraints imposed by the permission. The transaction is then rolled back, leaving the database in its original state prior to the change, and the user is informed of the violation.
|
@ -2,7 +2,7 @@
|
||||
|
||||
## Replicating the Database
|
||||
|
||||
NetBox uses [PostgreSQL](https://www.postgresql.org/) for its database, so general PostgreSQL best practices will apply to NetBox. You can dump and restore the database using the `pg_dump` and `psql` utilities, respectively.
|
||||
NetBox employs a [PostgreSQL](https://www.postgresql.org/) database, so general PostgreSQL best practices apply here. The database can be written to a file and restored using the `pg_dump` and `psql` utilities, respectively.
|
||||
|
||||
!!! note
|
||||
The examples below assume that your database is named `netbox`.
|
||||
@ -23,8 +23,10 @@ pg_dump --exclude-table-data=extras_objectchange netbox > netbox.sql
|
||||
|
||||
### Load an Exported Database
|
||||
|
||||
When restoring a database from a file, it's recommended to delete any existing database first to avoid potential conflicts.
|
||||
|
||||
!!! warning
|
||||
This will destroy and replace any existing instance of the database.
|
||||
The following will destroy and replace any existing instance of the database.
|
||||
|
||||
```no-highlight
|
||||
psql -c 'drop database netbox'
|
||||
@ -41,17 +43,15 @@ If you want to export only the database schema, and not the data itself (e.g. fo
|
||||
```no-highlight
|
||||
pg_dump -s netbox > netbox_schema.sql
|
||||
```
|
||||
If you are migrating your instance of NetBox to a different machine, please make sure you invalidate the cache by performing this command:
|
||||
|
||||
```no-highlight
|
||||
python3 manage.py invalidate all
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Replicating Media
|
||||
## Replicating Uploaded Media
|
||||
|
||||
NetBox stored uploaded files (such as image attachments) in its media directory. To fully replicate an instance of NetBox, you'll need to copy both the database and the media files.
|
||||
By default, NetBox stores uploaded files (such as image attachments) in its media directory. To fully replicate an instance of NetBox, you'll need to copy both the database and the media files.
|
||||
|
||||
!!! note
|
||||
These operations are not necessary if your installation is utilizing a [remote storage backend](../../configuration/optional-settings/#storage_backend).
|
||||
|
||||
### Archive the Media Directory
|
||||
|
||||
@ -68,3 +68,14 @@ To extract the saved archive into a new installation, run the following from the
|
||||
```no-highlight
|
||||
tar -xf netbox_media.tar.gz
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Cache Invalidation
|
||||
|
||||
If you are migrating your instance of NetBox to a different machine, be sure to first invalidate the cache on the original instance by issuing the `invalidate all` management command (within the Python virtual environment):
|
||||
|
||||
```no-highlight
|
||||
# source /opt/netbox/venv/bin/activate
|
||||
(venv) # python3 manage.py invalidate all
|
||||
```
|
||||
|
@ -1,42 +0,0 @@
|
||||
# REST API Authentication
|
||||
|
||||
The NetBox API employs token-based authentication. For convenience, cookie authentication can also be used when navigating the browsable API.
|
||||
|
||||
{!docs/models/users/token.md!}
|
||||
|
||||
## Authenticating to the API
|
||||
|
||||
By default, read operations will be available without authentication. In this case, a token may be included in the request, but is not necessary.
|
||||
|
||||
```
|
||||
$ curl -H "Accept: application/json; indent=4" http://localhost/api/dcim/sites/
|
||||
{
|
||||
"count": 10,
|
||||
"next": null,
|
||||
"previous": null,
|
||||
"results": [...]
|
||||
}
|
||||
```
|
||||
|
||||
However, if the [`LOGIN_REQUIRED`](../../configuration/optional-settings/#login_required) configuration setting has been set to `True`, all requests must be authenticated.
|
||||
|
||||
```
|
||||
$ curl -H "Accept: application/json; indent=4" http://localhost/api/dcim/sites/
|
||||
{
|
||||
"detail": "Authentication credentials were not provided."
|
||||
}
|
||||
```
|
||||
|
||||
To authenticate to the API, set the HTTP `Authorization` header to the string `Token ` (note the trailing space) followed by the token key.
|
||||
|
||||
```
|
||||
$ curl -H "Authorization: Token d2f763479f703d80de0ec15254237bc651f9cdc0" -H "Accept: application/json; indent=4" http://localhost/api/dcim/sites/
|
||||
{
|
||||
"count": 10,
|
||||
"next": null,
|
||||
"previous": null,
|
||||
"results": [...]
|
||||
}
|
||||
```
|
||||
|
||||
Additionally, the browsable interface to the API (which can be seen by navigating to the API root `/api/` in a web browser) will attempt to authenticate requests using the same cookie that the normal NetBox front end uses. Thus, if you have logged into NetBox, you will be logged into the browsable API as well.
|
@ -1,147 +0,0 @@
|
||||
# API Examples
|
||||
|
||||
Supported HTTP methods:
|
||||
|
||||
* `GET`: Retrieve an object or list of objects
|
||||
* `POST`: Create a new object
|
||||
* `PUT`: Update an existing object, all mandatory fields must be specified
|
||||
* `PATCH`: Updates an existing object, only specifying the field to be changed
|
||||
* `DELETE`: Delete an existing object
|
||||
|
||||
To authenticate a request, attach your token in an `Authorization` header:
|
||||
|
||||
```
|
||||
curl -H "Authorization: Token d2f763479f703d80de0ec15254237bc651f9cdc0"
|
||||
```
|
||||
|
||||
## Retrieving a list of sites
|
||||
|
||||
Send a `GET` request to the object list endpoint. The response contains a paginated list of JSON objects.
|
||||
|
||||
```
|
||||
$ curl -H "Accept: application/json; indent=4" http://localhost/api/dcim/sites/
|
||||
{
|
||||
"count": 14,
|
||||
"next": null,
|
||||
"previous": null,
|
||||
"results": [
|
||||
{
|
||||
"id": 6,
|
||||
"name": "Corporate HQ",
|
||||
"slug": "corporate-hq",
|
||||
"region": null,
|
||||
"tenant": null,
|
||||
"facility": "",
|
||||
"asn": null,
|
||||
"physical_address": "742 Evergreen Terrace, Springfield, USA",
|
||||
"shipping_address": "",
|
||||
"contact_name": "",
|
||||
"contact_phone": "",
|
||||
"contact_email": "",
|
||||
"comments": "",
|
||||
"custom_fields": {},
|
||||
"count_prefixes": 108,
|
||||
"count_vlans": 46,
|
||||
"count_racks": 8,
|
||||
"count_devices": 254,
|
||||
"count_circuits": 6
|
||||
},
|
||||
...
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Retrieving a single site by ID
|
||||
|
||||
Send a `GET` request to the object detail endpoint. The response contains a single JSON object.
|
||||
|
||||
```
|
||||
$ curl -H "Accept: application/json; indent=4" http://localhost/api/dcim/sites/6/
|
||||
{
|
||||
"id": 6,
|
||||
"name": "Corporate HQ",
|
||||
"slug": "corporate-hq",
|
||||
"region": null,
|
||||
"tenant": null,
|
||||
"facility": "",
|
||||
"asn": null,
|
||||
"physical_address": "742 Evergreen Terrace, Springfield, USA",
|
||||
"shipping_address": "",
|
||||
"contact_name": "",
|
||||
"contact_phone": "",
|
||||
"contact_email": "",
|
||||
"comments": "",
|
||||
"custom_fields": {},
|
||||
"count_prefixes": 108,
|
||||
"count_vlans": 46,
|
||||
"count_racks": 8,
|
||||
"count_devices": 254,
|
||||
"count_circuits": 6
|
||||
}
|
||||
```
|
||||
|
||||
## Creating a new site
|
||||
|
||||
Send a `POST` request to the site list endpoint with token authentication and JSON-formatted data. Only mandatory fields are required. This example includes one non required field, "region."
|
||||
|
||||
```
|
||||
$ curl -X POST -H "Authorization: Token d2f763479f703d80de0ec15254237bc651f9cdc0" -H "Content-Type: application/json" -H "Accept: application/json; indent=4" http://localhost:8000/api/dcim/sites/ --data '{"name": "My New Site", "slug": "my-new-site", "region": 5}'
|
||||
{
|
||||
"id": 16,
|
||||
"name": "My New Site",
|
||||
"slug": "my-new-site",
|
||||
"region": 5,
|
||||
"tenant": null,
|
||||
"facility": "",
|
||||
"asn": null,
|
||||
"physical_address": "",
|
||||
"shipping_address": "",
|
||||
"contact_name": "",
|
||||
"contact_phone": "",
|
||||
"contact_email": "",
|
||||
"comments": ""
|
||||
}
|
||||
```
|
||||
Note that in this example we are creating a site bound to a region with the ID of 5. For write API actions (`POST`, `PUT`, and `PATCH`) the integer ID value is used for `ForeignKey` (related model) relationships, instead of the nested representation that is used in the `GET` (list) action.
|
||||
|
||||
## Modify an existing site
|
||||
|
||||
Make an authenticated `PUT` request to the site detail endpoint. As with a create (`POST`) request, all mandatory fields must be included.
|
||||
|
||||
```
|
||||
$ curl -X PUT -H "Authorization: Token d2f763479f703d80de0ec15254237bc651f9cdc0" -H "Content-Type: application/json" -H "Accept: application/json; indent=4" http://localhost:8000/api/dcim/sites/16/ --data '{"name": "Renamed Site", "slug": "renamed-site"}'
|
||||
```
|
||||
|
||||
## Modify an object by changing a field
|
||||
|
||||
Make an authenticated `PATCH` request to the device endpoint. With `PATCH`, unlike `POST` and `PUT`, we only specify the field that is being changed. In this example, we add a serial number to a device.
|
||||
```
|
||||
$ curl -X PATCH -H "Authorization: Token d2f763479f703d80de0ec15254237bc651f9cdc0" -H "Content-Type: application/json" -H "Accept: application/json; indent=4" http://localhost:8000/api/dcim/devices/2549/ --data '{"serial": "FTX1123A090"}'
|
||||
```
|
||||
|
||||
## Delete an existing site
|
||||
|
||||
Send an authenticated `DELETE` request to the site detail endpoint.
|
||||
|
||||
```
|
||||
$ curl -v -X DELETE -H "Authorization: Token d2f763479f703d80de0ec15254237bc651f9cdc0" -H "Content-Type: application/json" -H "Accept: application/json; indent=4" http://localhost:8000/api/dcim/sites/16/
|
||||
* Connected to localhost (127.0.0.1) port 8000 (#0)
|
||||
> DELETE /api/dcim/sites/16/ HTTP/1.1
|
||||
> User-Agent: curl/7.35.0
|
||||
> Host: localhost:8000
|
||||
> Authorization: Token d2f763479f703d80de0ec15254237bc651f9cdc0
|
||||
> Content-Type: application/json
|
||||
> Accept: application/json; indent=4
|
||||
>
|
||||
* HTTP 1.0, assume close after body
|
||||
< HTTP/1.0 204 No Content
|
||||
< Date: Mon, 20 Mar 2017 16:13:08 GMT
|
||||
< Server: WSGIServer/0.1 Python/2.7.6
|
||||
< Vary: Accept, Cookie
|
||||
< X-Frame-Options: SAMEORIGIN
|
||||
< Allow: GET, PUT, PATCH, DELETE, OPTIONS
|
||||
<
|
||||
* Closing connection 0
|
||||
```
|
||||
|
||||
The response to a successful `DELETE` request will have code 204 (No Content); the body of the response will be empty.
|
@ -1,71 +0,0 @@
|
||||
# API Filtering
|
||||
|
||||
The NetBox API supports robust filtering of results based on the fields of each model.
|
||||
Generally speaking you are able to filter based on the attributes (fields) present in
|
||||
the response body. Please note however that certain read-only or metadata fields are not
|
||||
filterable.
|
||||
|
||||
Filtering is achieved by passing HTTP query parameters and the parameter name is the
|
||||
name of the field you wish to filter on and the value is the field value.
|
||||
|
||||
E.g. filtering based on a device's name:
|
||||
```
|
||||
/api/dcim/devices/?name=DC-SPINE-1
|
||||
```
|
||||
|
||||
## Multi Value Logic
|
||||
|
||||
While you are able to filter based on an arbitrary number of fields, you are also able to
|
||||
pass multiple values for the same field. In most cases filtering on multiple values is
|
||||
implemented as a logical OR operation. A notable exception is the `tag` filter which
|
||||
is a logical AND. Passing multiple values for one field, can be combined with other fields.
|
||||
|
||||
For example, filtering for devices with either the name of DC-SPINE-1 _or_ DC-LEAF-4:
|
||||
```
|
||||
/api/dcim/devices/?name=DC-SPINE-1&name=DC-LEAF-4
|
||||
```
|
||||
|
||||
Filtering for devices with tag `router` and `customer-a` will return only devices with
|
||||
_both_ of those tags applied:
|
||||
```
|
||||
/api/dcim/devices/?tag=router&tag=customer-a
|
||||
```
|
||||
|
||||
## Lookup Expressions
|
||||
|
||||
Certain model fields also support filtering using additional lookup expressions. This allows
|
||||
for negation and other context specific filtering.
|
||||
|
||||
These lookup expressions can be applied by adding a suffix to the desired field's name.
|
||||
E.g. `mac_address__n`. In this case, the filter expression is for negation and it is separated
|
||||
by two underscores. Below are the lookup expressions that are supported across different field
|
||||
types.
|
||||
|
||||
### Numeric Fields
|
||||
|
||||
Numeric based fields (ASN, VLAN ID, etc) support these lookup expressions:
|
||||
|
||||
- `n` - not equal (negation)
|
||||
- `lt` - less than
|
||||
- `lte` - less than or equal
|
||||
- `gt` - greater than
|
||||
- `gte` - greater than or equal
|
||||
|
||||
### String Fields
|
||||
|
||||
String based (char) fields (Name, Address, etc) support these lookup expressions:
|
||||
|
||||
- `n` - not equal (negation)
|
||||
- `ic` - case insensitive contains
|
||||
- `nic` - negated case insensitive contains
|
||||
- `isw` - case insensitive starts with
|
||||
- `nisw` - negated case insensitive starts with
|
||||
- `iew` - case insensitive ends with
|
||||
- `niew` - negated case insensitive ends with
|
||||
- `ie` - case sensitive exact match
|
||||
- `nie` - negated case sensitive exact match
|
||||
|
||||
### Foreign Keys & Other Fields
|
||||
|
||||
Certain other fields, namely foreign key relationships support just the negation
|
||||
expression: `n`.
|
@ -1,291 +0,0 @@
|
||||
# The NetBox REST API
|
||||
|
||||
## What is a REST API?
|
||||
|
||||
REST stands for [representational state transfer](https://en.wikipedia.org/wiki/Representational_state_transfer). It's a particular type of API which employs HTTP to create, retrieve, update, and delete objects from a database. (This set of operations is commonly referred to as CRUD.) Each type of operation is associated with a particular HTTP verb:
|
||||
|
||||
* `GET`: Retrieve an object or list of objects
|
||||
* `POST`: Create an object
|
||||
* `PUT` / `PATCH`: Modify an existing object. `PUT` requires all mandatory fields to be specified, while `PATCH` only expects the field that is being modified to be specified.
|
||||
* `DELETE`: Delete an existing object
|
||||
|
||||
The NetBox API represents all objects in [JavaScript Object Notation (JSON)](http://www.json.org/). This makes it very easy to interact with NetBox data on the command line with common tools. For example, we can request an IP address from NetBox and output the JSON using `curl` and `jq`. (Piping the output through `jq` isn't strictly required but makes it much easier to read.)
|
||||
|
||||
```
|
||||
$ curl -s http://localhost:8000/api/ipam/ip-addresses/2954/ | jq '.'
|
||||
{
|
||||
"custom_fields": {},
|
||||
"nat_outside": null,
|
||||
"nat_inside": null,
|
||||
"description": "An example IP address",
|
||||
"id": 2954,
|
||||
"family": 4,
|
||||
"address": "5.101.108.132/26",
|
||||
"vrf": null,
|
||||
"tenant": null,
|
||||
"status": {
|
||||
"label": "Active",
|
||||
"value": 1
|
||||
},
|
||||
"role": null,
|
||||
"interface": null
|
||||
}
|
||||
```
|
||||
|
||||
Each attribute of the NetBox object is expressed as a field in the dictionary. Fields may include their own nested objects, as in the case of the `status` field above. Every object includes a primary key named `id` which uniquely identifies it in the database.
|
||||
|
||||
## Interactive Documentation
|
||||
|
||||
Comprehensive, interactive documentation of all API endpoints is available on a running NetBox instance at `/api/docs/`. This interface provides a convenient sandbox for researching and experimenting with NetBox's various API endpoints and different request types.
|
||||
|
||||
## URL Hierarchy
|
||||
|
||||
NetBox's entire API is housed under the API root at `https://<hostname>/api/`. The URL structure is divided at the root level by application: circuits, DCIM, extras, IPAM, secrets, and tenancy. Within each application, each model has its own path. For example, the provider and circuit objects are located under the "circuits" application:
|
||||
|
||||
* /api/circuits/providers/
|
||||
* /api/circuits/circuits/
|
||||
|
||||
Likewise, the site, rack, and device objects are located under the "DCIM" application:
|
||||
|
||||
* /api/dcim/sites/
|
||||
* /api/dcim/racks/
|
||||
* /api/dcim/devices/
|
||||
|
||||
The full hierarchy of available endpoints can be viewed by navigating to the API root in a web browser.
|
||||
|
||||
Each model generally has two views associated with it: a list view and a detail view. The list view is used to request a list of multiple objects or to create a new object. The detail view is used to retrieve, update, or delete an existing object. All objects are referenced by their numeric primary key (`id`).
|
||||
|
||||
* /api/dcim/devices/ - List devices or create a new device
|
||||
* /api/dcim/devices/123/ - Retrieve, update, or delete the device with ID 123
|
||||
|
||||
Lists of objects can be filtered using a set of query parameters. For example, to find all interfaces belonging to the device with ID 123:
|
||||
|
||||
```
|
||||
GET /api/dcim/interfaces/?device_id=123
|
||||
```
|
||||
|
||||
See [filtering](filtering.md) for more details.
|
||||
|
||||
## Serialization
|
||||
|
||||
The NetBox API employs three types of serializers to represent model data:
|
||||
|
||||
* Base serializer
|
||||
* Nested serializer
|
||||
* Writable serializer
|
||||
|
||||
The base serializer is used to represent the default view of a model. This includes all database table fields which comprise the model, and may include additional metadata. A base serializer includes relationships to parent objects, but **does not** include child objects. For example, the `VLANSerializer` includes a nested representation its parent VLANGroup (if any), but does not include any assigned Prefixes.
|
||||
|
||||
```
|
||||
{
|
||||
"id": 1048,
|
||||
"site": {
|
||||
"id": 7,
|
||||
"url": "http://localhost:8000/api/dcim/sites/7/",
|
||||
"name": "Corporate HQ",
|
||||
"slug": "corporate-hq"
|
||||
},
|
||||
"group": {
|
||||
"id": 4,
|
||||
"url": "http://localhost:8000/api/ipam/vlan-groups/4/",
|
||||
"name": "Production",
|
||||
"slug": "production"
|
||||
},
|
||||
"vid": 101,
|
||||
"name": "Users-Floor1",
|
||||
"tenant": null,
|
||||
"status": {
|
||||
"value": 1,
|
||||
"label": "Active"
|
||||
},
|
||||
"role": {
|
||||
"id": 9,
|
||||
"url": "http://localhost:8000/api/ipam/roles/9/",
|
||||
"name": "User Access",
|
||||
"slug": "user-access"
|
||||
},
|
||||
"description": "",
|
||||
"display_name": "101 (Users-Floor1)",
|
||||
"custom_fields": {}
|
||||
}
|
||||
```
|
||||
|
||||
### Related Objects
|
||||
|
||||
Related objects (e.g. `ForeignKey` fields) are represented using a nested serializer. A nested serializer provides a minimal representation of an object, including only its URL and enough information to display the object to a user. When performing write API actions (`POST`, `PUT`, and `PATCH`), related objects may be specified by either numeric ID (primary key), or by a set of attributes sufficiently unique to return the desired object.
|
||||
|
||||
For example, when creating a new device, its rack can be specified by NetBox ID (PK):
|
||||
|
||||
```
|
||||
{
|
||||
"name": "MyNewDevice",
|
||||
"rack": 123,
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
Or by a set of nested attributes used to identify the rack:
|
||||
|
||||
```
|
||||
{
|
||||
"name": "MyNewDevice",
|
||||
"rack": {
|
||||
"site": {
|
||||
"name": "Equinix DC6"
|
||||
},
|
||||
"name": "R204"
|
||||
},
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
Note that if the provided parameters do not return exactly one object, a validation error is raised.
|
||||
|
||||
### Brief Format
|
||||
|
||||
Most API endpoints support an optional "brief" format, which returns only a minimal representation of each object in the response. This is useful when you need only a list of the objects themselves without any related data, such as when populating a drop-down list in a form.
|
||||
|
||||
For example, the default (complete) format of an IP address looks like this:
|
||||
|
||||
```
|
||||
GET /api/ipam/prefixes/13980/
|
||||
|
||||
{
|
||||
"id": 13980,
|
||||
"family": 4,
|
||||
"prefix": "192.0.2.0/24",
|
||||
"site": null,
|
||||
"vrf": null,
|
||||
"tenant": null,
|
||||
"vlan": null,
|
||||
"status": {
|
||||
"value": 1,
|
||||
"label": "Active"
|
||||
},
|
||||
"role": null,
|
||||
"is_pool": false,
|
||||
"description": "",
|
||||
"tags": [],
|
||||
"custom_fields": {},
|
||||
"created": "2018-12-11",
|
||||
"last_updated": "2018-12-11T16:27:55.073174-05:00"
|
||||
}
|
||||
```
|
||||
|
||||
The brief format is much more terse, but includes a link to the object's full representation:
|
||||
|
||||
```
|
||||
GET /api/ipam/prefixes/13980/?brief=1
|
||||
|
||||
{
|
||||
"id": 13980,
|
||||
"url": "https://netbox/api/ipam/prefixes/13980/",
|
||||
"family": 4,
|
||||
"prefix": "192.0.2.0/24"
|
||||
}
|
||||
```
|
||||
|
||||
The brief format is supported for both lists and individual objects.
|
||||
|
||||
## Pagination
|
||||
|
||||
API responses which contain a list of objects (for example, a request to `/api/dcim/devices/`) will be paginated to avoid unnecessary overhead. The root JSON object will contain the following attributes:
|
||||
|
||||
* `count`: The total count of all objects matching the query
|
||||
* `next`: A hyperlink to the next page of results (if applicable)
|
||||
* `previous`: A hyperlink to the previous page of results (if applicable)
|
||||
* `results`: The list of returned objects
|
||||
|
||||
Here is an example of a paginated response:
|
||||
|
||||
```
|
||||
HTTP 200 OK
|
||||
Allow: GET, POST, OPTIONS
|
||||
Content-Type: application/json
|
||||
Vary: Accept
|
||||
|
||||
{
|
||||
"count": 2861,
|
||||
"next": "http://localhost:8000/api/dcim/devices/?limit=50&offset=50",
|
||||
"previous": null,
|
||||
"results": [
|
||||
{
|
||||
"id": 123,
|
||||
"name": "DeviceName123",
|
||||
...
|
||||
},
|
||||
...
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
The default page size derives from the [`PAGINATE_COUNT`](../../configuration/optional-settings/#paginate_count) configuration setting, which defaults to 50. However, this can be overridden per request by specifying the desired `offset` and `limit` query parameters. For example, if you wish to retrieve a hundred devices at a time, you would make a request for:
|
||||
|
||||
```
|
||||
http://localhost:8000/api/dcim/devices/?limit=100
|
||||
```
|
||||
|
||||
The response will return devices 1 through 100. The URL provided in the `next` attribute of the response will return devices 101 through 200:
|
||||
|
||||
```
|
||||
{
|
||||
"count": 2861,
|
||||
"next": "http://localhost:8000/api/dcim/devices/?limit=100&offset=100",
|
||||
"previous": null,
|
||||
"results": [...]
|
||||
}
|
||||
```
|
||||
|
||||
The maximum number of objects that can be returned is limited by the [`MAX_PAGE_SIZE`](../../configuration/optional-settings/#max_page_size) setting, which is 1000 by default. Setting this to `0` or `None` will remove the maximum limit. An API consumer can then pass `?limit=0` to retrieve _all_ matching objects with a single request.
|
||||
|
||||
!!! warning
|
||||
Disabling the page size limit introduces a potential for very resource-intensive requests, since one API request can effectively retrieve an entire table from the database.
|
||||
|
||||
## Filtering
|
||||
|
||||
A list of objects retrieved via the API can be filtered by passing one or more query parameters. The same parameters used by the web UI work for the API as well. For example, to return only prefixes with a status of "Active" (identified by the slug `active`):
|
||||
|
||||
```
|
||||
GET /api/ipam/prefixes/?status=active
|
||||
```
|
||||
|
||||
The choices available for fixed choice fields such as `status` can be retrieved by sending an `OPTIONS` API request for the desired endpoint:
|
||||
|
||||
```no-highlight
|
||||
$ curl -s -X OPTIONS \
|
||||
-H "Authorization: Token $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Accept: application/json; indent=4" \
|
||||
http://localhost:8000/api/ipam/prefixes/ | jq ".actions.POST.status.choices"
|
||||
[
|
||||
{
|
||||
"value": "container",
|
||||
"display_name": "Container"
|
||||
},
|
||||
{
|
||||
"value": "active",
|
||||
"display_name": "Active"
|
||||
},
|
||||
{
|
||||
"value": "reserved",
|
||||
"display_name": "Reserved"
|
||||
},
|
||||
{
|
||||
"value": "deprecated",
|
||||
"display_name": "Deprecated"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
For most fields, when a filter is passed multiple times, objects matching _any_ of the provided values will be returned. For example, `GET /api/dcim/sites/?name=Foo&name=Bar` will return all sites named "Foo" _or_ "Bar". The exception to this rule is ManyToManyFields which may have multiple values assigned. Tags are the most common example of a ManyToManyField. For example, `GET /api/dcim/sites/?tag=foo&tag=bar` will return only sites tagged with both "foo" _and_ "bar".
|
||||
|
||||
### Custom Fields
|
||||
|
||||
To filter on a custom field, prepend `cf_` to the field name. For example, the following query will return only sites where a custom field named `foo` is equal to 123:
|
||||
|
||||
```
|
||||
GET /api/dcim/sites/?cf_foo=123
|
||||
```
|
||||
|
||||
!!! note
|
||||
Full versus partial matching when filtering is configurable per custom field. Filtering can be toggled (or disabled) for a custom field in the admin UI.
|
@ -1,6 +1,6 @@
|
||||
# NetBox Configuration
|
||||
|
||||
NetBox's local configuration is stored in `netbox/netbox/configuration.py`. An example configuration is provided at `netbox/netbox/configuration.example.py`. You may copy or rename the example configuration and make changes as appropriate. NetBox will not run without a configuration file.
|
||||
NetBox's local configuration is stored in `$INSTALL_ROOT/netbox/netbox/configuration.py`. An example configuration is provided as `configuration.example.py`. You may copy or rename the example configuration and make changes as appropriate. NetBox will not run without a configuration file.
|
||||
|
||||
While NetBox has many configuration settings, only a few of them must be defined at the time of installation.
|
||||
|
||||
@ -11,8 +11,8 @@ While NetBox has many configuration settings, only a few of them must be defined
|
||||
|
||||
## Changing the Configuration
|
||||
|
||||
Configuration settings may be changed at any time. However, the NetBox service must be restarted before the changes will take effect:
|
||||
Configuration settings may be changed at any time. However, the WSGI service (e.g. Gunicorn) must be restarted before the changes will take effect:
|
||||
|
||||
```no-highlight
|
||||
# sudo supervisorctl restart netbox
|
||||
$ sudo systemctl restart netbox
|
||||
```
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
NetBox will email details about critical errors to the administrators listed here. This should be a list of (name, email) tuples. For example:
|
||||
|
||||
```
|
||||
```python
|
||||
ADMINS = [
|
||||
['Hank Hill', 'hhill@example.com'],
|
||||
['Dale Gribble', 'dgribble@example.com'],
|
||||
@ -13,13 +13,21 @@ ADMINS = [
|
||||
|
||||
---
|
||||
|
||||
## ALLOWED_URL_SCHEMES
|
||||
|
||||
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. Note that only the schemes specified in this list will be accepted: If adding your own, be sure to replicate all of the default values as well (excluding those schemes which are not desirable).
|
||||
|
||||
---
|
||||
|
||||
## BANNER_TOP
|
||||
|
||||
## BANNER_BOTTOM
|
||||
|
||||
Setting these variables will display content in a banner at the top and/or bottom of the page, respectively. HTML is allowed. To replicate the content of the top banner in the bottom banner, set:
|
||||
Setting these variables will display custom content in a banner at the top and/or bottom of the page, respectively. HTML is allowed. To replicate the content of the top banner in the bottom banner, set:
|
||||
|
||||
```
|
||||
```python
|
||||
BANNER_TOP = 'Your banner text'
|
||||
BANNER_BOTTOM = BANNER_TOP
|
||||
```
|
||||
@ -28,7 +36,7 @@ BANNER_BOTTOM = BANNER_TOP
|
||||
|
||||
## BANNER_LOGIN
|
||||
|
||||
The value of this variable will be displayed on the login page above the login form. HTML is allowed.
|
||||
This defines custom content to be displayed on the login page above the login form. HTML is allowed.
|
||||
|
||||
---
|
||||
|
||||
@ -36,9 +44,9 @@ The value of this variable will be displayed on the login page above the login f
|
||||
|
||||
Default: None
|
||||
|
||||
The base URL path to use when accessing NetBox. Do not include the scheme or domain name. For example, if installed at http://example.com/netbox/, set:
|
||||
The base URL path to use when accessing NetBox. Do not include the scheme or domain name. For example, if installed at https://example.com/netbox/, set:
|
||||
|
||||
```
|
||||
```python
|
||||
BASE_PATH = 'netbox/'
|
||||
```
|
||||
|
||||
@ -48,7 +56,7 @@ BASE_PATH = 'netbox/'
|
||||
|
||||
Default: 900
|
||||
|
||||
The number of seconds to retain cache entries before automatically invalidating them.
|
||||
The number of seconds to cache entries will be retained before expiring.
|
||||
|
||||
---
|
||||
|
||||
@ -56,7 +64,11 @@ The number of seconds to retain cache entries before automatically invalidating
|
||||
|
||||
Default: 90
|
||||
|
||||
The number of days to retain logged changes (object creations, updates, and deletions). Set this to `0` to retain changes in the database indefinitely. (Warning: This will greatly increase database size over time.)
|
||||
The number of days to retain logged changes (object creations, updates, and deletions). Set this to `0` to retain
|
||||
changes in the database indefinitely.
|
||||
|
||||
!!! warning
|
||||
If enabling indefinite changelog retention, it is recommended to periodically delete old entries. Otherwise, the database may eventually exceed capacity.
|
||||
|
||||
---
|
||||
|
||||
@ -72,9 +84,11 @@ If True, cross-origin resource sharing (CORS) requests will be accepted from all
|
||||
|
||||
## CORS_ORIGIN_REGEX_WHITELIST
|
||||
|
||||
These settings specify a list of origins that are authorized to make cross-site API requests. Use `CORS_ORIGIN_WHITELIST` to define a list of exact hostnames, or `CORS_ORIGIN_REGEX_WHITELIST` to define a set of regular expressions. (These settings have no effect if `CORS_ORIGIN_ALLOW_ALL` is True.) For example:
|
||||
These settings specify a list of origins that are authorized to make cross-site API requests. Use
|
||||
`CORS_ORIGIN_WHITELIST` to define a list of exact hostnames, or `CORS_ORIGIN_REGEX_WHITELIST` to define a set of regular
|
||||
expressions. (These settings have no effect if `CORS_ORIGIN_ALLOW_ALL` is True.) For example:
|
||||
|
||||
```
|
||||
```python
|
||||
CORS_ORIGIN_WHITELIST = [
|
||||
'https://example.com',
|
||||
]
|
||||
@ -86,12 +100,13 @@ CORS_ORIGIN_WHITELIST = [
|
||||
|
||||
Default: False
|
||||
|
||||
This setting enables debugging. This should be done only during development or troubleshooting. Note that only clients
|
||||
which access NetBox from a recognized [internal IP address](#internal_ips) will see debugging tools in the user
|
||||
This setting enables debugging. Debugging should be enabled only during development or troubleshooting. Note that only
|
||||
clients which access NetBox from a recognized [internal IP address](#internal_ips) will see debugging tools in the user
|
||||
interface.
|
||||
|
||||
!!! warning
|
||||
Never enable debugging on a production system, as it can expose sensitive data to unauthenticated users.
|
||||
Never enable debugging on a production system, as it can expose sensitive data to unauthenticated users and impose a
|
||||
substantial performance penalty.
|
||||
|
||||
---
|
||||
|
||||
@ -105,9 +120,9 @@ This parameter serves as a safeguard to prevent some potentially dangerous behav
|
||||
|
||||
## DOCS_ROOT
|
||||
|
||||
Default: `$INSTALL_DIR/docs/`
|
||||
Default: `$INSTALL_ROOT/docs/`
|
||||
|
||||
The file path to NetBox's documentation. This is used when presenting context-sensitive documentation in the web UI. by default, this will be the `docs/` directory within the root NetBox installation path. (Set this to `None` to disable the embedded documentation.)
|
||||
The filesystem path to NetBox's documentation. This is used when presenting context-sensitive documentation in the web UI. By default, this will be the `docs/` directory within the root NetBox installation path. (Set this to `None` to disable the embedded documentation.)
|
||||
|
||||
---
|
||||
|
||||
@ -115,20 +130,23 @@ The file path to NetBox's documentation. This is used when presenting context-se
|
||||
|
||||
In order to send email, NetBox needs an email server configured. The following items can be defined within the `EMAIL` configuration parameter:
|
||||
|
||||
* `SERVER` - Host name or IP address of the email server (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`)
|
||||
* `USERNAME` - Username with which to authenticate
|
||||
* `PASSSWORD` - Password with which to authenticate
|
||||
* `USE_SSL` - Use SSL when connecting to the server (default: `False`). Mutually exclusive with `USE_TLS`.
|
||||
* `USE_TLS` - Use TLS when connecting to the server (default: `False`). Mutually exclusive with `USE_SSL`.
|
||||
* `USE_SSL` - Use SSL when connecting to the server (default: `False`)
|
||||
* `USE_TLS` - Use TLS when connecting to the server (default: `False`)
|
||||
* `SSL_CERTFILE` - Path to the PEM-formatted SSL certificate file (optional)
|
||||
* `SSL_KEYFILE` - Path to the PEM-formatted SSL private key file (optional)
|
||||
* `TIMEOUT` - Amount of time to wait for a connection, in seconds (default: `10`)
|
||||
* `FROM_EMAIL` - Sender address for emails sent by NetBox (default: `root@localhost`)
|
||||
* `FROM_EMAIL` - Sender address for emails sent by NetBox
|
||||
|
||||
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 please use the django function [send_mail()](https://docs.djangoproject.com/en/stable/topics/email/#send-mail):
|
||||
!!! note
|
||||
The `USE_SSL` and `USE_TLS` parameters are mutually exclusive.
|
||||
|
||||
```
|
||||
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) fuction accessible within the NetBox shell:
|
||||
|
||||
```no-highlight
|
||||
# python ./manage.py nbshell
|
||||
>>> from django.core.mail import send_mail
|
||||
>>> send_mail(
|
||||
@ -142,15 +160,23 @@ Email is sent from NetBox only for critical events or if configured for [logging
|
||||
|
||||
---
|
||||
|
||||
## ENFORCE_GLOBAL_UNIQUE
|
||||
|
||||
Default: False
|
||||
|
||||
By default, NetBox will permit users to create duplicate prefixes and IP addresses in the global table (that is, those which are not assigned to any VRF). This behavior can be disabled by setting `ENFORCE_GLOBAL_UNIQUE` to True.
|
||||
|
||||
---
|
||||
|
||||
## EXEMPT_VIEW_PERMISSIONS
|
||||
|
||||
Default: Empty list
|
||||
|
||||
A list of models to exempt from the enforcement of view permissions. Models listed here will be viewable by all users and by anonymous users.
|
||||
A list of NetBox models to exempt from the enforcement of view permissions. Models listed here will be viewable by all users, both authenticated and anonymous.
|
||||
|
||||
List models in the form `<app>.<model>`. For example:
|
||||
|
||||
```
|
||||
```python
|
||||
EXEMPT_VIEW_PERMISSIONS = [
|
||||
'dcim.site',
|
||||
'dcim.region',
|
||||
@ -160,17 +186,12 @@ EXEMPT_VIEW_PERMISSIONS = [
|
||||
|
||||
To exempt _all_ models from view permission enforcement, set the following. (Note that `EXEMPT_VIEW_PERMISSIONS` must be an iterable.)
|
||||
|
||||
```
|
||||
```python
|
||||
EXEMPT_VIEW_PERMISSIONS = ['*']
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ENFORCE_GLOBAL_UNIQUE
|
||||
|
||||
Default: False
|
||||
|
||||
Enforcement of unique IP space can be toggled on a per-VRF basis. To enforce unique IP space within the global table (all prefixes and IP addresses not assigned to a VRF), set `ENFORCE_GLOBAL_UNIQUE` to True.
|
||||
!!! note
|
||||
Using a wildcard will not affect certain potentially sensitive models, such as user permissions. If there is a need to exempt these models, they must be specified individually.
|
||||
|
||||
---
|
||||
|
||||
@ -178,7 +199,7 @@ Enforcement of unique IP space can be toggled on a per-VRF basis. To enforce uni
|
||||
|
||||
Default: None
|
||||
|
||||
A dictionary of HTTP proxies to use for outbound requests originating from NetBox (e.g. when sending webhooks). Proxies should be specified by schema as per the [Python requests library documentation](https://2.python-requests.org/en/master/user/advanced/). For example:
|
||||
A dictionary of HTTP proxies to use for outbound requests originating from NetBox (e.g. when sending webhook requests). Proxies should be specified by schema (HTTP and HTTPS) as per the [Python requests library documentation](https://2.python-requests.org/en/master/user/advanced/). For example:
|
||||
|
||||
```python
|
||||
HTTP_PROXIES = {
|
||||
@ -191,7 +212,7 @@ HTTP_PROXIES = {
|
||||
|
||||
## INTERNAL_IPS
|
||||
|
||||
Default: `('127.0.0.1', '::1',)`
|
||||
Default: `('127.0.0.1', '::1')`
|
||||
|
||||
A list of IP addresses recognized as internal to the system, used to control the display of debugging output. For
|
||||
example, the debugging toolbar will be viewable only when a client is accessing NetBox from one of the listed IP
|
||||
@ -201,11 +222,11 @@ addresses (and [`DEBUG`](#debug) is true).
|
||||
|
||||
## LOGGING
|
||||
|
||||
By default, all messages of INFO severity or higher will be logged to the console. Additionally, if `DEBUG` is False and email access has been configured, ERROR and CRITICAL messages will be emailed to the users defined in `ADMINS`.
|
||||
By default, all messages of INFO severity or higher will be logged to the console. Additionally, if [`DEBUG`](#debug) is False and email access has been configured, ERROR and CRITICAL messages will be emailed to the users defined in [`ADMINS`](#admins).
|
||||
|
||||
The Django framework on which NetBox runs allows for the customization of logging, e.g. to write logs to file. Please consult the [Django logging documentation](https://docs.djangoproject.com/en/stable/topics/logging/) for more information on configuring this setting. Below is an example which will write all INFO and higher messages to a file:
|
||||
The Django framework on which NetBox runs allows for the customization of logging format and destination. Please consult the [Django logging documentation](https://docs.djangoproject.com/en/stable/topics/logging/) for more information on configuring this setting. Below is an example which will write all INFO and higher messages to a local file:
|
||||
|
||||
```
|
||||
```python
|
||||
LOGGING = {
|
||||
'version': 1,
|
||||
'disable_existing_loggers': False,
|
||||
@ -227,6 +248,7 @@ LOGGING = {
|
||||
|
||||
### Available Loggers
|
||||
|
||||
* `netbox.<app>.<model>` - Generic form for model-specific log messages
|
||||
* `netbox.auth.*` - Authentication events
|
||||
* `netbox.api.views.*` - Views which handle business logic for the REST API
|
||||
* `netbox.reports.*` - Report execution (`module.name`)
|
||||
@ -247,7 +269,7 @@ Setting this to True will permit only authenticated users to access any part of
|
||||
|
||||
Default: 1209600 seconds (14 days)
|
||||
|
||||
The liftetime (in seconds) of the authentication cookie issued to a NetBox user upon login.
|
||||
The lifetime (in seconds) of the authentication cookie issued to a NetBox user upon login.
|
||||
|
||||
---
|
||||
|
||||
@ -255,7 +277,7 @@ The liftetime (in seconds) of the authentication cookie issued to a NetBox user
|
||||
|
||||
Default: False
|
||||
|
||||
Setting this to True will display a "maintenance mode" banner at the top of every page.
|
||||
Setting this to True will display a "maintenance mode" banner at the top of every page. Additionally, NetBox will no longer update a user's "last active" time upon login. This is to allow new logins when the database is in a read-only state. Recording of login times will resume when maintenance mode is disabled.
|
||||
|
||||
---
|
||||
|
||||
@ -263,13 +285,13 @@ Setting this to True will display a "maintenance mode" banner at the top of ever
|
||||
|
||||
Default: 1000
|
||||
|
||||
An API consumer can request an arbitrary number of objects by appending the "limit" parameter to the URL (e.g. `?limit=1000`). This setting defines the maximum limit. Setting it to `0` or `None` will allow an API consumer to request all objects by specifying `?limit=0`.
|
||||
A web user or API consumer can request an arbitrary number of objects by appending the "limit" parameter to the URL (e.g. `?limit=1000`). This parameter defines the maximum acceptable limit. Setting this to `0` or `None` will allow a client to retrieve _all_ matching objects at once with no limit by specifying `?limit=0`.
|
||||
|
||||
---
|
||||
|
||||
## MEDIA_ROOT
|
||||
|
||||
Default: $BASE_DIR/netbox/media/
|
||||
Default: $INSTALL_ROOT/netbox/media/
|
||||
|
||||
The file path to the location where media files (such as image attachments) are stored. By default, this is the `netbox/media/` directory within the base NetBox installation path.
|
||||
|
||||
@ -279,7 +301,7 @@ The file path to the location where media files (such as image attachments) are
|
||||
|
||||
Default: False
|
||||
|
||||
Toggle exposing Prometheus metrics at `/metrics`. See the [Prometheus Metrics](../../additional-features/prometheus-metrics/) documentation for more details.
|
||||
Toggle the availability Prometheus-compatible metrics at `/metrics`. See the [Prometheus Metrics](../../additional-features/prometheus-metrics/) documentation for more details.
|
||||
|
||||
---
|
||||
|
||||
@ -289,24 +311,25 @@ Toggle exposing Prometheus metrics at `/metrics`. See the [Prometheus Metrics](.
|
||||
|
||||
NetBox will use these credentials when authenticating to remote devices via the [NAPALM library](https://napalm-automation.net/), if installed. Both parameters are optional.
|
||||
|
||||
Note: If SSH public key authentication has been set up for the system account under which NetBox runs, these parameters are not needed.
|
||||
!!! note
|
||||
If SSH public key authentication has been set up on the remote device(s) for the system account under which NetBox runs, these parameters are not needed.
|
||||
|
||||
---
|
||||
|
||||
## NAPALM_ARGS
|
||||
|
||||
A dictionary of optional arguments to pass to NAPALM when instantiating a network driver. See the NAPALM documentation for a [complete list of optional arguments](http://napalm.readthedocs.io/en/latest/support/#optional-arguments). An example:
|
||||
A dictionary of optional arguments to pass to NAPALM when instantiating a network driver. See the NAPALM documentation for a [complete list of optional arguments](https://napalm.readthedocs.io/en/latest/support/#optional-arguments). An example:
|
||||
|
||||
```
|
||||
```python
|
||||
NAPALM_ARGS = {
|
||||
'api_key': '472071a93b60a1bd1fafb401d9f8ef41',
|
||||
'port': 2222,
|
||||
}
|
||||
```
|
||||
|
||||
Note: Some platforms (e.g. Cisco IOS) require an argument named `secret` to be passed in addition to the normal password. If desired, you can use the configured `NAPALM_PASSWORD` as the value for this argument:
|
||||
Some platforms (e.g. Cisco IOS) require an argument named `secret` to be passed in addition to the normal password. If desired, you can use the configured `NAPALM_PASSWORD` as the value for this argument:
|
||||
|
||||
```
|
||||
```python
|
||||
NAPALM_USERNAME = 'username'
|
||||
NAPALM_PASSWORD = 'MySecretPassword'
|
||||
NAPALM_ARGS = {
|
||||
@ -329,7 +352,7 @@ The amount of time (in seconds) to wait for NAPALM to connect to a device.
|
||||
|
||||
Default: 50
|
||||
|
||||
Determine how many objects to display per page within each list of objects.
|
||||
The default maximum number of objects to display per page within each list of objects.
|
||||
|
||||
---
|
||||
|
||||
@ -374,27 +397,19 @@ When determining the primary IP address for a device, IPv6 is preferred over IPv
|
||||
|
||||
---
|
||||
|
||||
## REMOTE_AUTH_ENABLED
|
||||
## RACK_ELEVATION_DEFAULT_UNIT_HEIGHT
|
||||
|
||||
Default: `False`
|
||||
Default: 22
|
||||
|
||||
NetBox can be configured to support remote user authentication by inferring user authentication from an HTTP header set by the HTTP reverse proxy (e.g. nginx or Apache). Set this to `True` to enable this functionality. (Local authentication will still take effect as a fallback.)
|
||||
Default height (in pixels) of a unit within a rack elevation. For best results, this should be approximately one tenth of `RACK_ELEVATION_DEFAULT_UNIT_WIDTH`.
|
||||
|
||||
---
|
||||
|
||||
## REMOTE_AUTH_BACKEND
|
||||
## RACK_ELEVATION_DEFAULT_UNIT_WIDTH
|
||||
|
||||
Default: `'utilities.auth_backends.RemoteUserBackend'`
|
||||
Default: 220
|
||||
|
||||
Python path to the custom [Django authentication backend](https://docs.djangoproject.com/en/stable/topics/auth/customizing/) to use for external user authentication, if not using NetBox's built-in backend. (Requires `REMOTE_AUTH_ENABLED`.)
|
||||
|
||||
---
|
||||
|
||||
## REMOTE_AUTH_HEADER
|
||||
|
||||
Default: `'HTTP_REMOTE_USER'`
|
||||
|
||||
When remote user authentication is in use, this is the name of the HTTP header which informs NetBox of the currently authenticated user. (Requires `REMOTE_AUTH_ENABLED`.)
|
||||
Default width (in pixels) of a unit within a rack elevation.
|
||||
|
||||
---
|
||||
|
||||
@ -406,6 +421,17 @@ If true, NetBox will automatically create local accounts for users authenticated
|
||||
|
||||
---
|
||||
|
||||
## REMOTE_AUTH_BACKEND
|
||||
|
||||
Default: `'netbox.authentication.RemoteUserBackend'`
|
||||
|
||||
This is the Python path to the custom [Django authentication backend](https://docs.djangoproject.com/en/stable/topics/auth/customizing/) to use for external user authentication. NetBox provides two built-in backends (listed below), though custom authentication backends may also be provided by other packages or plugins.
|
||||
|
||||
* `netbox.authentication.RemoteUserBackend`
|
||||
* `netbox.authentication.LDAPBackend`
|
||||
|
||||
---
|
||||
|
||||
## REMOTE_AUTH_DEFAULT_GROUPS
|
||||
|
||||
Default: `[]` (Empty list)
|
||||
@ -416,9 +442,25 @@ The list of groups to assign a new user account when created using remote authen
|
||||
|
||||
## REMOTE_AUTH_DEFAULT_PERMISSIONS
|
||||
|
||||
Default: `[]` (Empty list)
|
||||
Default: `{}` (Empty dictionary)
|
||||
|
||||
The list of permissions to assign a new user account when created using remote authentication. (Requires `REMOTE_AUTH_ENABLED`.)
|
||||
A mapping of permissions to assign a new user account when created using remote authentication. Each key in the dictionary should be set to a dictionary of the attributes to be applied to the permission, or `None` to allow all objects. (Requires `REMOTE_AUTH_ENABLED`.)
|
||||
|
||||
---
|
||||
|
||||
## REMOTE_AUTH_ENABLED
|
||||
|
||||
Default: `False`
|
||||
|
||||
NetBox can be configured to support remote user authentication by inferring user authentication from an HTTP header set by the HTTP reverse proxy (e.g. nginx or Apache). Set this to `True` to enable this functionality. (Local authentication will still take effect as a fallback.)
|
||||
|
||||
---
|
||||
|
||||
## REMOTE_AUTH_HEADER
|
||||
|
||||
Default: `'HTTP_REMOTE_USER'`
|
||||
|
||||
When remote user authentication is in use, this is the name of the HTTP header which informs NetBox of the currently authenticated user. For example, to use the request header `X-Remote-User` it needs to be set to `HTTP_X_REMOTE_USER`. (Requires `REMOTE_AUTH_ENABLED`.)
|
||||
|
||||
---
|
||||
|
||||
@ -432,25 +474,34 @@ The number of seconds to retain the latest version that is fetched from the GitH
|
||||
|
||||
## RELEASE_CHECK_URL
|
||||
|
||||
Default: None
|
||||
Default: None (disabled)
|
||||
|
||||
The releases of this repository are checked to detect new releases, which are shown on the home page of the web interface. You can change this to your own fork of the NetBox repository, or set it to `None` to disable the check. The URL provided **must** be compatible with the GitHub API.
|
||||
This parameter defines the URL of the repository that will be checked periodically for new NetBox releases. When a new release is detected, a message will be displayed to administrative users on the home page. This can be set to the official repository (`'https://api.github.com/repos/netbox-community/netbox/releases'`) or a custom fork. Set this to `None` to disable automatic update checks.
|
||||
|
||||
Use `'https://api.github.com/repos/netbox-community/netbox/releases'` to check for release in the official NetBox repository.
|
||||
!!! note
|
||||
The URL provided **must** be compatible with the [GitHub REST API](https://docs.github.com/en/rest).
|
||||
|
||||
---
|
||||
|
||||
## REPORTS_ROOT
|
||||
|
||||
Default: $BASE_DIR/netbox/reports/
|
||||
Default: `$INSTALL_ROOT/netbox/reports/`
|
||||
|
||||
The file path to the location where custom reports will be kept. By default, this is the `netbox/reports/` directory within the base NetBox installation path.
|
||||
|
||||
---
|
||||
|
||||
## RQ_DEFAULT_TIMEOUT
|
||||
|
||||
Default: `300`
|
||||
|
||||
The maximum execution time of a background task (such as running a custom script), in seconds.
|
||||
|
||||
---
|
||||
|
||||
## SCRIPTS_ROOT
|
||||
|
||||
Default: $BASE_DIR/netbox/scripts/
|
||||
Default: `$INSTALL_ROOT/netbox/scripts/`
|
||||
|
||||
The file path to the location where custom scripts will be kept. By default, this is the `netbox/scripts/` directory within the base NetBox installation path.
|
||||
|
||||
@ -460,7 +511,7 @@ The file path to the location where custom scripts will be kept. By default, thi
|
||||
|
||||
Default: None
|
||||
|
||||
Session data is used to track authenticated users when they access NetBox. By default, NetBox stores session data in the PostgreSQL database. However, this inhibits authentication to a standby instance of NetBox without write access to the database. Alternatively, a local file path may be specified here and NetBox will store session data as files instead of using the database. Note that the user as which NetBox runs must have read and write permissions to this path.
|
||||
HTTP session data is used to track authenticated users when they access NetBox. By default, NetBox stores session data in its PostgreSQL database. However, this inhibits authentication to a standby instance of NetBox without write access to the database. Alternatively, a local file path may be specified here and NetBox will store session data as files instead of using the database. Note that the NetBox system user must have read and write permissions to this path.
|
||||
|
||||
---
|
||||
|
||||
@ -488,21 +539,19 @@ If `STORAGE_BACKEND` is not defined, this setting will be ignored.
|
||||
|
||||
Default: UTC
|
||||
|
||||
The time zone NetBox will use when dealing with dates and times. It is recommended to use UTC time unless you have a specific need to use a local time zone. [List of available time zones](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones).
|
||||
The time zone NetBox will use when dealing with dates and times. It is recommended to use UTC time unless you have a specific need to use a local time zone. Please see the [list of available time zones](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones).
|
||||
|
||||
---
|
||||
|
||||
## Date and Time Formatting
|
||||
|
||||
You may define custom formatting for date and times. For detailed instructions on writing format strings, please see [the Django documentation](https://docs.djangoproject.com/en/stable/ref/templates/builtins/#date).
|
||||
You may define custom formatting for date and times. For detailed instructions on writing format strings, please see [the Django documentation](https://docs.djangoproject.com/en/stable/ref/templates/builtins/#date). Default formats are listed below.
|
||||
|
||||
Defaults:
|
||||
|
||||
```
|
||||
```python
|
||||
DATE_FORMAT = 'N j, Y' # June 26, 2016
|
||||
SHORT_DATE_FORMAT = 'Y-m-d' # 2016-06-27
|
||||
SHORT_DATE_FORMAT = 'Y-m-d' # 2016-06-26
|
||||
TIME_FORMAT = 'g:i a' # 1:23 p.m.
|
||||
SHORT_TIME_FORMAT = 'H:i:s' # 13:23:00
|
||||
DATETIME_FORMAT = 'N j, Y g:i a' # June 26, 2016 1:23 p.m.
|
||||
SHORT_DATETIME_FORMAT = 'Y-m-d H:i' # 2016-06-27 13:23
|
||||
SHORT_DATETIME_FORMAT = 'Y-m-d H:i' # 2016-06-26 13:23
|
||||
```
|
||||
|
@ -2,7 +2,12 @@
|
||||
|
||||
## ALLOWED_HOSTS
|
||||
|
||||
This is a list of valid fully-qualified domain names (FQDNs) that is used to reach the NetBox service. Usually this is the same as the hostname for the NetBox server, but can also be different (e.g. when using a reverse proxy serving the NetBox website under a different FQDN than the hostname of the NetBox server). NetBox will not permit access to the server via any other hostnames (or IPs). The value of this option is also used to set `CSRF_TRUSTED_ORIGINS`, which restricts `HTTP POST` to the same set of hosts (more about this [here](https://docs.djangoproject.com/en/stable/ref/settings/#std:setting-CSRF_TRUSTED_ORIGINS)). Keep in mind that NetBox, by default, has `USE_X_FORWARDED_HOST = True` (in `netbox/netbox/settings.py`) which means that if you're using a reverse proxy, it's the FQDN used to reach that reverse proxy which needs to be in this list (more about this [here](https://docs.djangoproject.com/en/stable/ref/settings/#allowed-hosts)).
|
||||
This is a list of valid fully-qualified domain names (FQDNs) and/or IP addresses that can be used to reach the NetBox service. Usually this is the same as the hostname for the NetBox server, but can also be different; for example, when using a reverse proxy serving the NetBox website under a different FQDN than the hostname of the NetBox server. To help guard against [HTTP Host header attackes](https://docs.djangoproject.com/en/3.0/topics/security/#host-headers-virtual-hosting), NetBox will not permit access to the server via any other hostnames (or IPs).
|
||||
|
||||
!!! note
|
||||
This parameter must always be defined as a list or tuple, even if only value is provided.
|
||||
|
||||
The value of this option is also used to set `CSRF_TRUSTED_ORIGINS`, which restricts POST requests to the same set of hosts (more about this [here](https://docs.djangoproject.com/en/stable/ref/settings/#std:setting-CSRF_TRUSTED_ORIGINS)). Keep in mind that NetBox, by default, sets `USE_X_FORWARDED_HOST` to true, which means that if you're using a reverse proxy, it's the FQDN used to reach that reverse proxy which needs to be in this list (more about this [here](https://docs.djangoproject.com/en/stable/ref/settings/#allowed-hosts)).
|
||||
|
||||
Example:
|
||||
|
||||
@ -10,18 +15,24 @@ Example:
|
||||
ALLOWED_HOSTS = ['netbox.example.com', '192.0.2.123']
|
||||
```
|
||||
|
||||
If you are not yet sure what the domain name and/or IP address of the NetBox installation will be, and are comfortable accepting the risks in doing so, you can set this to a wildcard (asterisk) to allow all host values:
|
||||
|
||||
```
|
||||
ALLOWED_HOSTS = ['*']
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## DATABASE
|
||||
|
||||
NetBox requires access to a PostgreSQL database service to store data. This service can run locally or on a remote system. The following parameters must be defined within the `DATABASE` dictionary:
|
||||
NetBox requires access to a PostgreSQL 9.6 or later database service to store data. This service can run locally on the NetBox server or on a remote system. The following parameters must be defined within the `DATABASE` dictionary:
|
||||
|
||||
* `NAME` - Database name
|
||||
* `USER` - PostgreSQL username
|
||||
* `PASSWORD` - PostgreSQL password
|
||||
* `HOST` - Name or IP address of the database server (use `localhost` if running locally)
|
||||
* `PORT` - TCP port of the PostgreSQL service; leave blank for default port (5432)
|
||||
* `CONN_MAX_AGE` - Lifetime of a [persistent database connection](https://docs.djangoproject.com/en/stable/ref/databases/#persistent-connections), in seconds (150-300 is recommended)
|
||||
* `PORT` - TCP port of the PostgreSQL service; leave blank for default port (TCP/5432)
|
||||
* `CONN_MAX_AGE` - Lifetime of a [persistent database connection](https://docs.djangoproject.com/en/stable/ref/databases/#persistent-connections), in seconds (300 is the default)
|
||||
|
||||
Example:
|
||||
|
||||
@ -54,10 +65,9 @@ Redis is configured using a configuration setting similar to `DATABASE` and thes
|
||||
* `PORT` - TCP port of the Redis service; leave blank for default port (6379)
|
||||
* `PASSWORD` - Redis password (if set)
|
||||
* `DATABASE` - Numeric database ID
|
||||
* `DEFAULT_TIMEOUT` - Connection timeout in seconds
|
||||
* `SSL` - Use SSL connection to Redis
|
||||
|
||||
Example:
|
||||
An example configuration is provided below:
|
||||
|
||||
```python
|
||||
REDIS = {
|
||||
@ -66,7 +76,6 @@ REDIS = {
|
||||
'PORT': 1234,
|
||||
'PASSWORD': 'foobar',
|
||||
'DATABASE': 0,
|
||||
'DEFAULT_TIMEOUT': 300,
|
||||
'SSL': False,
|
||||
},
|
||||
'caching': {
|
||||
@ -74,15 +83,15 @@ REDIS = {
|
||||
'PORT': 6379,
|
||||
'PASSWORD': '',
|
||||
'DATABASE': 1,
|
||||
'DEFAULT_TIMEOUT': 300,
|
||||
'SSL': False,
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
!!! note
|
||||
If you are upgrading from a version prior to v2.7, please note that the Redis connection configuration settings have
|
||||
changed. Manual modification to bring the `REDIS` section inline with the above specification is necessary
|
||||
If you are upgrading from a NetBox release older than v2.7.0, please note that the Redis connection configuration
|
||||
settings have changed. Manual modification to bring the `REDIS` section inline with the above specification is
|
||||
necessary
|
||||
|
||||
!!! warning
|
||||
It is highly recommended to keep the task and cache databases separate. Using the same database number on the
|
||||
@ -97,6 +106,7 @@ above and the addition of two new keys.
|
||||
* `SENTINELS`: List of tuples or tuple of tuples with each inner tuple containing the name or IP address
|
||||
of the Redis server and port for each sentinel instance to connect to
|
||||
* `SENTINEL_SERVICE`: Name of the master / service to connect to
|
||||
* `SENTINEL_TIMEOUT`: Connection timeout, in seconds
|
||||
|
||||
Example:
|
||||
|
||||
@ -105,9 +115,9 @@ REDIS = {
|
||||
'tasks': {
|
||||
'SENTINELS': [('mysentinel.redis.example.com', 6379)],
|
||||
'SENTINEL_SERVICE': 'netbox',
|
||||
'SENTINEL_TIMEOUT': 10,
|
||||
'PASSWORD': '',
|
||||
'DATABASE': 0,
|
||||
'DEFAULT_TIMEOUT': 300,
|
||||
'SSL': False,
|
||||
},
|
||||
'caching': {
|
||||
@ -118,24 +128,20 @@ REDIS = {
|
||||
'SENTINEL_SERVICE': 'netbox',
|
||||
'PASSWORD': '',
|
||||
'DATABASE': 1,
|
||||
'DEFAULT_TIMEOUT': 300,
|
||||
'SSL': False,
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
!!! note
|
||||
It is possible to have only one or the other Redis configurations to use Sentinel functionality. It is possible
|
||||
for example to have the tasks database use sentinel via `HOST`/`PORT` and for caching to use Sentinel via
|
||||
`SENTINELS`/`SENTINEL_SERVICE`.
|
||||
|
||||
It is permissible to use Sentinel for only one database and not the other.
|
||||
|
||||
---
|
||||
|
||||
## SECRET_KEY
|
||||
|
||||
This is a secret cryptographic key is used to improve the security of cookies and password resets. The key defined here should not be shared outside of the configuration file. `SECRET_KEY` can be changed at any time, however be aware that doing so will invalidate all existing sessions.
|
||||
This is a secret, random string used to assist in the creation new cryptographic hashes for passwords and HTTP cookies. The key defined here should not be shared outside of the configuration file. `SECRET_KEY` can be changed at any time, however be aware that doing so will invalidate all existing sessions.
|
||||
|
||||
Please note that this key is **not** used for hashing user passwords or for the encrypted storage of secret data in NetBox.
|
||||
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` should be at least 50 characters in length and contain a random mix of letters, digits, and symbols. The script located at `netbox/generate_secret_key.py` may be used to generate a suitable key.
|
||||
`SECRET_KEY` should be at least 50 characters in length and contain a random 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.
|
||||
|
@ -6,6 +6,7 @@
|
||||
---
|
||||
|
||||
{!docs/models/ipam/prefix.md!}
|
||||
{!docs/models/ipam/role.md!}
|
||||
|
||||
---
|
||||
|
||||
@ -14,3 +15,4 @@
|
||||
---
|
||||
|
||||
{!docs/models/ipam/vrf.md!}
|
||||
{!docs/models/ipam/routetarget.md!}
|
||||
|
@ -5,34 +5,4 @@
|
||||
|
||||
# Example Power Topology
|
||||
|
||||
Below is a simple diagram demonstrating how power is modeled in NetBox.
|
||||
|
||||
!!! note
|
||||
The power feeds are connected to the same power panel for illustrative purposes; usually, you would have such feeds diversely connected to panels to avoid the single point of failure.
|
||||
|
||||
```
|
||||
+---------------+
|
||||
| Power panel 1 |
|
||||
+---------------+
|
||||
| |
|
||||
| |
|
||||
+--------------+ +--------------+
|
||||
| Power feed 1 | | Power feed 2 |
|
||||
+--------------+ +--------------+
|
||||
| |
|
||||
| |
|
||||
| | <-- Power ports
|
||||
+---------+ +---------+
|
||||
| PDU 1 | | PDU 2 |
|
||||
+---------+ +---------+
|
||||
| \ / | <-- Power outlets
|
||||
| \ / |
|
||||
| \ / |
|
||||
| X |
|
||||
| / \ |
|
||||
| / \ |
|
||||
| / \ | <-- Power ports
|
||||
+--------+ +--------+
|
||||
| Server | | Router |
|
||||
+--------+ +--------+
|
||||
```
|
||||

|
||||
|
@ -1,4 +1,4 @@
|
||||
# Virtual Machines and Clusters
|
||||
# Virtualization
|
||||
|
||||
{!docs/models/virtualization/cluster.md!}
|
||||
{!docs/models/virtualization/clustertype.md!}
|
||||
@ -7,3 +7,4 @@
|
||||
---
|
||||
|
||||
{!docs/models/virtualization/virtualmachine.md!}
|
||||
{!docs/models/virtualization/vminterface.md!}
|
||||
|
@ -1,8 +1,10 @@
|
||||
# Application Registry
|
||||
|
||||
The registry is an in-memory data structure which houses various miscellaneous application-wide parameters, such as installed plugins. It is not exposed to the user and is not intended to be modified by any code outside of NetBox core.
|
||||
The registry is an in-memory data structure which houses various application-wide parameters, such as the list of enabled plugins. It is not exposed to the user and is not intended to be modified by any code outside of NetBox core.
|
||||
|
||||
The registry behaves essentially like a Python dictionary, with the notable exception that once a store (key) has been declared, it cannot be deleted or overwritten. The value of a store can, however, me modified; e.g. by appending a value to a list. Store values generally do not change once the application has been initialized.
|
||||
The registry behaves essentially like a Python dictionary, with the notable exception that once a store (key) has been declared, it cannot be deleted or overwritten. The value of a store can, however, be modified; e.g. by appending a value to a list. Store values generally do not change once the application has been initialized.
|
||||
|
||||
The registry can be inspected by importing `registry` from `extras.registry`.
|
||||
|
||||
## Stores
|
||||
|
||||
|
@ -1,8 +1,8 @@
|
||||
# Extending Models
|
||||
|
||||
Below is a list of items to consider when adding a new field to a model:
|
||||
Below is a list of tasks to consider when adding a new field to a core model.
|
||||
|
||||
## 1. Generate and run database migration
|
||||
## 1. Generate and run database migrations
|
||||
|
||||
Django migrations are used to express changes to the database schema. In most cases, Django can generate these automatically, however very complex changes may require manual intervention. Always remember to specify a short but descriptive name when generating a new migration.
|
||||
|
||||
@ -14,18 +14,18 @@ Django migrations are used to express changes to the database schema. In most ca
|
||||
Where possible, try to merge related changes into a single migration. For example, if three new fields are being added to different models within an app, these can be expressed in the same migration. You can merge a new migration with an existing one by combining their `operations` lists.
|
||||
|
||||
!!! note
|
||||
Migrations can only be merged within a release. Once a new release has been published, its migrations cannot be altered.
|
||||
Migrations can only be merged within a release. Once a new release has been published, its migrations cannot be altered (other than for the purpose of correcting a bug).
|
||||
|
||||
## 2. Add validation logic to `clean()`
|
||||
|
||||
If the new field introduces additional validation requirements (beyond what's included with the field itself), implement them in the model's `clean()` method. Remember to call the model's original method using `super()` before or agter your custom validation as appropriate:
|
||||
If the new field introduces additional validation requirements (beyond what's included with the field itself), implement them in the model's `clean()` method. Remember to call the model's original method using `super()` before or after your custom validation as appropriate:
|
||||
|
||||
```
|
||||
class Foo(models.Model):
|
||||
|
||||
def clean(self):
|
||||
|
||||
super(DeviceCSVForm, self).clean()
|
||||
super().clean()
|
||||
|
||||
# Custom validation goes here
|
||||
if self.bar is None:
|
||||
@ -38,38 +38,34 @@ Add the name of the new field to `csv_headers` and included a CSV-friendly repre
|
||||
|
||||
## 4. Update relevant querysets
|
||||
|
||||
If you're adding a relational field (e.g. `ForeignKey`) and intend to include the data when retreiving a list of objects, be sure to include the field using `prefetch_related()` as appropriate. This will optimize the view and avoid excessive database lookups.
|
||||
If you're adding a relational field (e.g. `ForeignKey`) and intend to include the data when retreiving a list of objects, be sure to include the field using `prefetch_related()` as appropriate. This will optimize the view and avoid extraneous database queries.
|
||||
|
||||
## 5. Update API serializer
|
||||
|
||||
Extend the model's API serializer in `<app>.api.serializers` to include the new field. In most cases, it will not be necessary to also extend the nested serializer, which produces a minimal represenation of the model.
|
||||
|
||||
## 6. Add choices to API view
|
||||
|
||||
If the new field has static choices, add it to the `FieldChoicesViewSet` for the app.
|
||||
|
||||
## 7. Add field to forms
|
||||
## 6. Add field to forms
|
||||
|
||||
Extend any forms to include the new field as appropriate. Common forms include:
|
||||
|
||||
* **Credit/edit** - Manipulating a single object
|
||||
* **Bulk edit** - Performing a change on mnay objects at once
|
||||
* **Bulk edit** - Performing a change on many objects at once
|
||||
* **CSV import** - The form used when bulk importing objects in CSV format
|
||||
* **Filter** - Displays the options available for filtering a list of objects (both UI and API)
|
||||
|
||||
## 8. Extend object filter set
|
||||
## 7. Extend object filter set
|
||||
|
||||
If the new field should be filterable, add it to the `FilterSet` for the model. If the field should be searchable, remember to reference it in the FilterSet's `search()` method.
|
||||
|
||||
## 9. Add column to object table
|
||||
## 8. Add column to object table
|
||||
|
||||
If the new field will be included in the object list view, add a column to the model's table. For simple fields, adding the field name to `Meta.fields` will be sufficient. More complex fields may require explicitly declaring a new column.
|
||||
If the new field will be included in the object list view, add a column to the model's table. For simple fields, adding the field name to `Meta.fields` will be sufficient. More complex fields may require declaring a custom column.
|
||||
|
||||
## 10. Update the UI templates
|
||||
## 9. Update the UI templates
|
||||
|
||||
Edit the object's view template to display the new field. There may also be a custom add/edit form template that needs to be updated.
|
||||
|
||||
## 11. Create/extend test cases
|
||||
## 10. Create/extend test cases
|
||||
|
||||
Create or extend the relevant test cases to verify that the new field and any accompanying validation logic perform as expected. This is especially important for relational fields. NetBox incorporates various test suites, including:
|
||||
|
||||
@ -80,3 +76,7 @@ Create or extend the relevant test cases to verify that the new field and any ac
|
||||
* View tests
|
||||
|
||||
Be diligent to ensure all of the relevant test suites are adapted or extended as necessary to test any new functionality.
|
||||
|
||||
## 11. Update the model's documentation
|
||||
|
||||
Each model has a dedicated page in the documentation, at `models/<app>/<model>.md`. Update this file to include any relevant information about the new field.
|
||||
|
140
docs/development/getting-started.md
Normal file
140
docs/development/getting-started.md
Normal file
@ -0,0 +1,140 @@
|
||||
# Getting Started
|
||||
|
||||
## Setting up a Development Environment
|
||||
|
||||
Getting started with NetBox development is pretty straightforward, and should feel very familiar to anyone with Django development experience. There are a few things you'll need:
|
||||
|
||||
* A Linux system or environment
|
||||
* A PostgreSQL server, which can be installed locally [per the documentation](/installation/1-postgresql/)
|
||||
* A Redis server, which can also be [installed locally](/installation/2-redis/)
|
||||
* A supported version of Python
|
||||
|
||||
### Fork the Repo
|
||||
|
||||
Assuming you'll be working on your own fork, your first step will be to fork the [official git repository](https://github.com/netbox-community/netbox). (If you're a maintainer who's going to be working directly with the official repo, skip this step.) You can then clone your GitHub fork locally for development:
|
||||
|
||||
```no-highlight
|
||||
$ git clone https://github.com/youruseraccount/netbox.git
|
||||
Cloning into 'netbox'...
|
||||
remote: Enumerating objects: 231, done.
|
||||
remote: Counting objects: 100% (231/231), done.
|
||||
remote: Compressing objects: 100% (147/147), done.
|
||||
remote: Total 56705 (delta 134), reused 145 (delta 84), pack-reused 56474
|
||||
Receiving objects: 100% (56705/56705), 27.96 MiB | 34.92 MiB/s, done.
|
||||
Resolving deltas: 100% (44177/44177), done.
|
||||
$ ls netbox/
|
||||
base_requirements.txt contrib docs mkdocs.yml NOTICE requirements.txt upgrade.sh
|
||||
CHANGELOG.md CONTRIBUTING.md LICENSE.txt netbox README.md scripts
|
||||
```
|
||||
|
||||
The NetBox project utilizes three persistent git branches to track work:
|
||||
|
||||
* `master` - Serves as a snapshot of the current stable release
|
||||
* `develop` - All development on the upcoming stable release occurs here
|
||||
* `feature` - Tracks work on an upcoming major release
|
||||
|
||||
Typically, you'll base pull requests off of the `develop` branch, or off of `feature` if you're working on a new major release. **Never** merge pull requests into the `master` branch, which receives merged only from the `develop` branch.
|
||||
|
||||
### Enable Pre-Commit Hooks
|
||||
|
||||
NetBox ships with a [git pre-commit hook](https://githooks.com/) script that automatically checks for style compliance and missing database migrations prior to committing changes. This helps avoid erroneous commits that result in CI test failures. You are encouraged to enable it by creating a link to `scripts/git-hooks/pre-commit`:
|
||||
|
||||
```no-highlight
|
||||
$ cd .git/hooks/
|
||||
$ ln -s ../../scripts/git-hooks/pre-commit
|
||||
```
|
||||
|
||||
### Create a Python Virtual Environment
|
||||
|
||||
A [virtual environment](https://docs.python.org/3/tutorial/venv.html) is like a container for a set of Python packages. They allow you to build environments suited to specific projects without interfering with system packages or other projects. When installed per the documentation, NetBox uses a virtual environment in production.
|
||||
|
||||
Create a virtual environment using the `venv` Python module:
|
||||
|
||||
```no-highlight
|
||||
$ mkdir ~/.venv
|
||||
$ python3 -m venv ~/.venv/netbox
|
||||
```
|
||||
|
||||
This will create a directory named `.venv/netbox/` in your home directory, which houses a virtual copy of the Python executable and its related libraries and tooling. When running NetBox for development, it will be run using the Python binary at `~/.venv/netbox/bin/python`.
|
||||
|
||||
!!! info
|
||||
Keeping virtual environments in `~/.venv/` is a common convention but entirely optional: Virtual environments can be created wherever you please.
|
||||
|
||||
Once created, activate the virtual environment:
|
||||
|
||||
```no-highlight
|
||||
$ source ~/.venv/netbox/bin/activate
|
||||
(netbox) $
|
||||
```
|
||||
|
||||
Notice that the console prompt changes to indicate the active environment. This updates the necessary system environment variables to ensure that any Python scripts are run within the virtual environment.
|
||||
|
||||
### Install Dependencies
|
||||
|
||||
With the virtual environment activated, install the project's required Python packages using the `pip` module:
|
||||
|
||||
```no-highlight
|
||||
(netbox) $ python -m pip install -r requirements.txt
|
||||
Collecting Django==3.1 (from -r requirements.txt (line 1))
|
||||
Cache entry deserialization failed, entry ignored
|
||||
Using cached https://files.pythonhosted.org/packages/2b/5a/4bd5624546912082a1bd2709d0edc0685f5c7827a278d806a20cf6adea28/Django-3.1-py3-none-any.whl
|
||||
...
|
||||
```
|
||||
|
||||
### Configure NetBox
|
||||
|
||||
Within the `netbox/netbox/` directory, copy `configuration.example.py` to `configuration.py` and update the following parameters:
|
||||
|
||||
* `ALLOWED_HOSTS`: This can be set to `['*']` for development purposes
|
||||
* `DATABASE`: PostgreSQL database connection parameters
|
||||
* `REDIS`: Redis configuration, if different from the defaults
|
||||
* `SECRET_KEY`: Set to a random string (use `generate_secret_key.py` in the parent directory to generate a suitable key)
|
||||
* `DEBUG`: Set to `True`
|
||||
* `DEVELOPER`: Set to `True` (this enables the creation of new database migrations)
|
||||
|
||||
### Start the Development Server
|
||||
|
||||
Django provides a lightweight, auto-updating HTTP/WSGI server for development use. NetBox extends this slightly to automatically import models and other utilities. Run the NetBox development server with the `nbshell` management command:
|
||||
|
||||
```no-highlight
|
||||
$ python netbox/manage.py runserver
|
||||
Performing system checks...
|
||||
|
||||
System check identified no issues (0 silenced).
|
||||
November 18, 2020 - 15:52:31
|
||||
Django version 3.1, using settings 'netbox.settings'
|
||||
Starting development server at http://127.0.0.1:8000/
|
||||
Quit the server with CONTROL-C.
|
||||
```
|
||||
|
||||
This ensures that your development environment is now complete and operational. Any changes you make to the code base will be automatically adapted by the development server.
|
||||
|
||||
## Running Tests
|
||||
|
||||
Throughout the course of development, it's a good idea to occasionally run NetBox's test suite to catch any potential errors. Tests are run using the `test` management command:
|
||||
|
||||
```no-highlight
|
||||
$ python netbox/manage.py test
|
||||
```
|
||||
|
||||
In cases where you haven't made any changes to the database (which is most of the time), you can append the `--keepdb` argument to this command to reuse the test database between runs. This cuts down on the time it takes to run the test suite since the database doesn't have to be rebuilt each time. (Note that this argument will cause errors if you've modified any model fields since the previous test run.)
|
||||
|
||||
```no-highlight
|
||||
$ python netbox/manage.py test --keepdb
|
||||
```
|
||||
|
||||
## Submitting Pull Requests
|
||||
|
||||
Once you're happy with your work and have verified that all tests pass, commit your changes and push it upstream to your fork. Always provide descriptive (but not excessively verbose) commit messages. When working on a specific issue, be sure to reference it.
|
||||
|
||||
```no-highlight
|
||||
$ git commit -m "Closes #1234: Add IPv5 support"
|
||||
$ git push origin
|
||||
```
|
||||
|
||||
Once your fork has the new commit, submit a [pull request](https://github.com/netbox-community/netbox/compare) to the NetBox repo to propose the changes. Be sure to provide a detailed accounting of the changes being made and the reasons for doing so.
|
||||
|
||||
Once submitted, a maintainer will review your pull request and either merge it or request changes. If changes are needed, you can make them via new commits to your fork: The pull request will update automatically.
|
||||
|
||||
!!! note
|
||||
Remember, pull requests are entertained only for **accepted** issues. If an issue you want to work on hasn't been approved by a maintainer yet, it's best to avoid risking your time and effort on a change that might not be accepted.
|
@ -7,7 +7,8 @@ NetBox is maintained as a [GitHub project](https://github.com/netbox-community/n
|
||||
Communication among developers should always occur via public channels:
|
||||
|
||||
* [GitHub issues](https://github.com/netbox-community/netbox/issues) - All feature requests, bug reports, and other substantial changes to the code base **must** be documented in an issue.
|
||||
* [The mailing list](https://groups.google.com/forum/#!forum/netbox-discuss) - The preferred forum for general discussion and support issues. Ideal for shaping a feature request prior to submitting an issue.
|
||||
* [GitHub discussions](https://github.com/netbox-community/netbox/discussions) - The preferred forum for general discussion and support issues. Ideal for shaping a feature request prior to submitting an issue.
|
||||
* [The mailing list](https://groups.google.com/g/netbox-discuss) - An alternative forum for general discussion (GitHub is preferred).
|
||||
* [#netbox on NetworkToCode](http://slack.networktocode.com/) - Good for quick chats. Avoid any discussion that might need to be referenced later on, as the chat history is not retained long.
|
||||
|
||||
## Governance
|
||||
@ -18,7 +19,7 @@ NetBox follows the [benevolent dictator](http://oss-watch.ac.uk/resources/benevo
|
||||
|
||||
All development of the current NetBox release occurs in the `develop` branch; releases are packaged from the `master` branch. The `master` branch should _always_ represent the current stable release in its entirety, such that installing NetBox by either downloading a packaged release or cloning the `master` branch provides the same code base.
|
||||
|
||||
NetBox components are arranged into functional subsections called _apps_ (a carryover from Django verancular). Each app holds the models, views, and templates relevant to a particular function:
|
||||
NetBox components are arranged into functional subsections called _apps_ (a carryover from Django vernacular). Each app holds the models, views, and templates relevant to a particular function:
|
||||
|
||||
* `circuits`: Communications circuits and providers (not to be confused with power circuits)
|
||||
* `dcim`: Datacenter infrastructure management (sites, racks, and devices)
|
||||
@ -26,5 +27,6 @@ NetBox components are arranged into functional subsections called _apps_ (a carr
|
||||
* `ipam`: IP address management (VRFs, prefixes, IP addresses, and VLANs)
|
||||
* `secrets`: Encrypted storage of sensitive data (e.g. login credentials)
|
||||
* `tenancy`: Tenants (such as customers) to which NetBox objects may be assigned
|
||||
* `users`: Authentication and user preferences
|
||||
* `utilities`: Resources which are not user-facing (extendable classes, etc.)
|
||||
* `virtualization`: Virtual machines and clusters
|
||||
|
@ -16,36 +16,43 @@ The other file is `requirements.txt`, which lists each of the required packages
|
||||
Every minor version release should refresh `requirements.txt` so that it lists the most recent stable release of each package. To do this:
|
||||
|
||||
1. Create a new virtual environment.
|
||||
2. Install the latest version of all required packages via pip:
|
||||
|
||||
```
|
||||
pip install -U -r base_requirements.txt
|
||||
```
|
||||
|
||||
2. Install the latest version of all required packages `pip install -U -r base_requirements.txt`).
|
||||
3. Run all tests and check that the UI and API function as expected.
|
||||
4. Update the package versions in `requirements.txt` as appropriate.
|
||||
4. Review each requirement's release notes for any breaking or otherwise noteworthy changes.
|
||||
5. Update the package versions in `requirements.txt` as appropriate.
|
||||
|
||||
### Update Static Libraries
|
||||
|
||||
Update the following static libraries to their most recent stable release:
|
||||
|
||||
* Bootstrap 3
|
||||
* Font Awesome 4
|
||||
* Material Design Icons
|
||||
* Select2
|
||||
* jQuery
|
||||
* jQuery UI
|
||||
|
||||
### Create a new Release Notes Page
|
||||
### Link to the Release Notes Page
|
||||
|
||||
Create a file at `/docs/release-notes/X.Y.md` to establish the release notes for the new release. Add the file to the table of contents within `mkdocs.yml`, and point `index.md` to the new file.
|
||||
Add the release notes (`/docs/release-notes/X.Y.md`) to the table of contents within `mkdocs.yml`, and point `index.md` to the new file.
|
||||
|
||||
### Manually Perform a New Install
|
||||
|
||||
Create a new installation of NetBox by following [the current documentation](http://netbox.readthedocs.io/en/latest/). This should be a manual process, so that issues with the documentation can be identified and corrected.
|
||||
Install `mkdocs` in your local environment, then start the documentation server:
|
||||
|
||||
```no-highlight
|
||||
$ pip install -r docs/requirements.txt
|
||||
$ mkdocs serve
|
||||
```
|
||||
|
||||
Follow these instructions to perform a new installation of NetBox. This process must _not_ be automated: The goal of this step is to catch any errors or omissions in the documentation, and ensure that it is kept up-to-date for each release. Make any necessary changes to the documentation before proceeding with the release.
|
||||
|
||||
### Close the Release Milestone
|
||||
|
||||
Close the release milestone on GitHub. Ensure that there are no remaining open issues associated with it.
|
||||
Close the release milestone on GitHub after ensuring there are no remaining open issues associated with it.
|
||||
|
||||
### Merge the Release Branch
|
||||
|
||||
Submit a pull request to merge the `feature` branch into the `develop` branch in preparation for its release.
|
||||
|
||||
---
|
||||
|
||||
@ -57,11 +64,11 @@ Ensure that continuous integration testing on the `develop` branch is completing
|
||||
|
||||
### Update Version and Changelog
|
||||
|
||||
Update the `VERSION` constant in `settings.py` to the new release version and annotate the current data in the release notes for the new version.
|
||||
Update the `VERSION` constant in `settings.py` to the new release version and annotate the current data in the release notes for the new version. Commit these changes to the `develop` branch.
|
||||
|
||||
### Submit a Pull Request
|
||||
|
||||
Submit a pull request title **"Release vX.Y.Z"** to merge the `develop` branch into `master`. Include a brief change log listing the features, improvements, and/or bugs addressed in the release.
|
||||
Submit a pull request title **"Release vX.Y.Z"** to merge the `develop` branch into `master`. Copy the documented release notes into the pull request's body.
|
||||
|
||||
Once CI has completed on the PR, merge it.
|
||||
|
||||
@ -69,20 +76,16 @@ Once CI has completed on the PR, merge it.
|
||||
|
||||
Draft a [new release](https://github.com/netbox-community/netbox/releases/new) with the following parameters.
|
||||
|
||||
* **Tag:** Current version (e.g. `v2.3.4`)
|
||||
* **Tag:** Current version (e.g. `v2.9.9`)
|
||||
* **Target:** `master`
|
||||
* **Title:** Version and date (e.g. `v2.3.4 - 2018-08-02`)
|
||||
* **Title:** Version and date (e.g. `v2.9.9 - 2020-11-09`)
|
||||
|
||||
Copy the description from the pull request into the release notes.
|
||||
Copy the description from the pull request to the release.
|
||||
|
||||
### Update the Development Version
|
||||
|
||||
On the `develop` branch, update `VERSION` in `settings.py` to point to the next release. For example, if you just released v2.3.4, set:
|
||||
On the `develop` branch, update `VERSION` in `settings.py` to point to the next release. For example, if you just released v2.9.9, set:
|
||||
|
||||
```
|
||||
VERSION = 'v2.3.5-dev'
|
||||
VERSION = 'v2.9.10-dev'
|
||||
```
|
||||
|
||||
### Announce the Release
|
||||
|
||||
Announce the release on the [mailing list](https://groups.google.com/forum/#!forum/netbox-discuss). Include a link to the release and the (HTML-formatted) release notes.
|
||||
|
@ -5,8 +5,8 @@ NetBox generally follows the [Django style guide](https://docs.djangoproject.com
|
||||
## PEP 8 Exceptions
|
||||
|
||||
* Wildcard imports (for example, `from .constants import *`) are acceptable under any of the following conditions:
|
||||
* The library being import contains only constant declarations (`constants.py`)
|
||||
* The library being imported explicitly defines `__all__` (e.g. `<app>.api.nested_serializers`)
|
||||
* The library being import contains only constant declarations (e.g. `constants.py`)
|
||||
* The library being imported explicitly defines `__all__`
|
||||
|
||||
* Maximum line length is 120 characters (E501)
|
||||
* This does not apply to HTML templates or to automatically generated code (e.g. database migrations).
|
||||
@ -45,7 +45,7 @@ When adding a new dependency, a short description of the package and the URL of
|
||||
|
||||
* When in doubt, remain consistent: It is better to be consistently incorrect than inconsistently correct. If you notice in the course of unrelated work a pattern that should be corrected, continue to follow the pattern for now and open a bug so that the entire code base can be evaluated at a later point.
|
||||
|
||||
* Prioritize readability over concision. Python is a very flexible language that typically gives us several options for expressing a given piece of logic, but some may be more friendly to the reader than others. (List comprehensions are particularly vulnerable to over-optimization.) Always remain considerate of the future reader who may need to interpret your code without the benefit of the context within which you are writing it.
|
||||
* Prioritize readability over concision. Python is a very flexible language that typically offers several options for expressing a given piece of logic, but some may be more friendly to the reader than others. (List comprehensions are particularly vulnerable to over-optimization.) Always remain considerate of the future reader who may need to interpret your code without the benefit of the context within which you are writing it.
|
||||
|
||||
* No easter eggs. While they can be fun, NetBox must be considered as a business-critical tool. The potential, however minor, for introducing a bug caused by unnecessary logic is best avoided entirely.
|
||||
|
||||
|
@ -8,4 +8,4 @@ The `users.UserConfig` model holds individual preferences for each user in the f
|
||||
| ---- | ----------- |
|
||||
| extras.configcontext.format | Preferred format when rendering config context data (JSON or YAML) |
|
||||
| pagination.per_page | The number of items to display per page of a paginated table |
|
||||
| tables.${table_name}.columns | The ordered list of columns to display when viewing the table |
|
||||
| tables.TABLE_NAME.columns | The ordered list of columns to display when viewing the table |
|
||||
|
@ -1,53 +0,0 @@
|
||||
# Utility Views
|
||||
|
||||
Utility views are reusable views that handle common CRUD tasks, such as listing and updating objects. Some views operate on individual objects, whereas others (referred to as "bulk" views) operate on multiple objects at once.
|
||||
|
||||
## Individual Views
|
||||
|
||||
### ObjectListView
|
||||
|
||||
Generates a paginated table of objects from a given queryset, which may optionally be filtered.
|
||||
|
||||
### ObjectEditView
|
||||
|
||||
Updates an object identified by a primary key (PK) or slug. If no existing object is specified, a new object will be created.
|
||||
|
||||
### ObjectDeleteView
|
||||
|
||||
Deletes an object. The user is redirected to a confirmation page before the deletion is executed.
|
||||
|
||||
## Bulk Views
|
||||
|
||||
### BulkCreateView
|
||||
|
||||
Creates multiple objects at once based on a given pattern. Currently used only for IP addresses.
|
||||
|
||||
### BulkImportView
|
||||
|
||||
Accepts CSV-formatted data and creates a new object for each line. Creation is all-or-none.
|
||||
|
||||
### BulkEditView
|
||||
|
||||
Applies changes to multiple objects at once in a two-step operation. First, the list of PKs for selected objects is POSTed and an edit form is presented to the user. On submission of that form, the specified changes are made to all selected objects.
|
||||
|
||||
### BulkDeleteView
|
||||
|
||||
Deletes multiple objects. The user selects the objects to be deleted and confirms the deletion.
|
||||
|
||||
## Component Views
|
||||
|
||||
### ComponentCreateView
|
||||
|
||||
Create one or more component objects beloning to a parent object (e.g. interfaces attached to a device).
|
||||
|
||||
### ComponentEditView
|
||||
|
||||
A subclass of `ObjectEditView`: Updates an individual component object.
|
||||
|
||||
### ComponentDeleteView
|
||||
|
||||
A subclass of `ObjectDeleteView`: Deletes an individual component object.
|
||||
|
||||
### BulkComponentCreateView
|
||||
|
||||
Create a set of components objects for each of a selected set of parent objects. This view can be used e.g. to create multiple interfaces on multiple devices at once.
|
@ -55,7 +55,7 @@ NetBox is built on the [Django](https://djangoproject.com/) Python framework and
|
||||
|
||||
## Supported Python Versions
|
||||
|
||||
NetBox supports Python 3.6 and 3.7 environments currently. (Support for Python 3.5 was removed in NetBox v2.8.)
|
||||
NetBox supports Python 3.6, 3.7, and 3.8 environments currently. (Support for Python 3.5 was removed in NetBox v2.8.)
|
||||
|
||||
## Getting Started
|
||||
|
||||
|
@ -3,32 +3,29 @@
|
||||
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
|
||||
NetBox requires PostgreSQL 9.6 or higher. Please note that MySQL and other relational databases are **not** supported.
|
||||
|
||||
The installation instructions provided here have been tested to work on Ubuntu 18.04 and CentOS 7.5. The particular commands needed to install dependencies on other distributions may vary significantly. Unfortunately, this is outside the control of the NetBox maintainers. Please consult your distribution's documentation for assistance with any errors.
|
||||
NetBox requires PostgreSQL 9.6 or higher. Please note that MySQL and other relational databases are **not** currently supported.
|
||||
|
||||
## Installation
|
||||
|
||||
#### Ubuntu
|
||||
|
||||
If a recent enough version of PostgreSQL is not available through your distribution's package manager, you'll need to install it from an official [PostgreSQL repository](https://wiki.postgresql.org/wiki/Apt).
|
||||
Install the PostgreSQL server and client development libraries using `apt`.
|
||||
|
||||
```no-highlight
|
||||
# apt-get update
|
||||
# apt-get install -y postgresql libpq-dev
|
||||
sudo apt update
|
||||
sudo apt install -y postgresql libpq-dev
|
||||
```
|
||||
|
||||
#### CentOS
|
||||
|
||||
CentOS 7.5 does not ship with a recent enough version of PostgreSQL, so it will need to be installed from an external repository. The instructions below show the installation of PostgreSQL 9.6, however you may opt to install a more recent version.
|
||||
PostgreSQL 9.6 and later are available natively on CentOS 8.2. If using an earlier CentOS release, you may need to [install it from an RPM](https://download.postgresql.org/pub/repos/yum/reporpms/EL-7-x86_64/).
|
||||
|
||||
```no-highlight
|
||||
# yum install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-7-x86_64/pgdg-redhat-repo-latest.noarch.rpm
|
||||
# yum install -y postgresql96 postgresql96-server postgresql96-devel
|
||||
# /usr/pgsql-9.6/bin/postgresql96-setup initdb
|
||||
sudo yum install -y postgresql-server libpq-devel
|
||||
sudo postgresql-setup --initdb
|
||||
```
|
||||
|
||||
CentOS users should modify the PostgreSQL configuration to accept password-based authentication by replacing `ident` with `md5` for all host entries within `/var/lib/pgsql/9.6/data/pg_hba.conf`. For example:
|
||||
CentOS configures ident host-based authentication for PostgreSQL by default. Because NetBox will need to authenticate using a username and password, modify `/var/lib/pgsql/data/pg_hba.conf` to support MD5 authentication by changing `ident` to `md5` for the lines below:
|
||||
|
||||
```no-highlight
|
||||
host all all 127.0.0.1/32 md5
|
||||
@ -38,8 +35,8 @@ host all all ::1/128 md5
|
||||
Then, start the service and enable it to run at boot:
|
||||
|
||||
```no-highlight
|
||||
# systemctl start postgresql-9.6
|
||||
# systemctl enable postgresql-9.6
|
||||
sudo systemctl start postgresql
|
||||
sudo systemctl enable postgresql
|
||||
```
|
||||
|
||||
## Database Creation
|
||||
@ -47,11 +44,11 @@ Then, start the service and enable it to run at boot:
|
||||
At a minimum, we need to create a database for NetBox and assign it a username and password for authentication. This is done with the following commands.
|
||||
|
||||
!!! danger
|
||||
DO NOT USE THE PASSWORD FROM THE EXAMPLE.
|
||||
**Do not use the password from the example.** Choose a strong, random password to ensure secure database authentication for your NetBox installation.
|
||||
|
||||
```no-highlight
|
||||
# sudo -u postgres psql
|
||||
psql (10.10)
|
||||
$ sudo -u postgres psql
|
||||
psql (12.5 (Ubuntu 12.5-0ubuntu0.20.04.1))
|
||||
Type "help" for help.
|
||||
|
||||
postgres=# CREATE DATABASE netbox;
|
||||
@ -68,7 +65,16 @@ postgres=# \q
|
||||
You can verify that authentication works issuing the following command and providing the configured password. (Replace `localhost` with your database server if using a remote database.)
|
||||
|
||||
```no-highlight
|
||||
# psql -U netbox -W -h localhost netbox
|
||||
$ psql --username netbox --password --host localhost netbox
|
||||
Password for user netbox:
|
||||
psql (12.5 (Ubuntu 12.5-0ubuntu0.20.04.1))
|
||||
SSL connection (protocol: TLSv1.3, cipher: TLS_AES_256_GCM_SHA384, bits: 256, compression: off)
|
||||
Type "help" for help.
|
||||
|
||||
netbox=> \conninfo
|
||||
You are connected to database "netbox" as user "netbox" on host "localhost" (address "127.0.0.1") at port "5432".
|
||||
SSL connection (protocol: TLSv1.3, cipher: TLS_AES_256_GCM_SHA384, bits: 256, compression: off)
|
||||
netbox=> \q
|
||||
```
|
||||
|
||||
If successful, you will enter a `netbox` prompt. Type `\q` to exit.
|
||||
If successful, you will enter a `netbox` prompt. Type `\conninfo` to confirm your connection, or type `\q` to exit.
|
||||
|
@ -4,19 +4,21 @@
|
||||
|
||||
[Redis](https://redis.io/) is an in-memory key-value store which NetBox employs for caching and queuing. This section entails the installation and configuration of a local Redis instance. If you already have a Redis service in place, skip to [the next section](3-netbox.md).
|
||||
|
||||
!!! note
|
||||
NetBox v2.9.0 and later require Redis v4.0 or higher. If your distribution does not offer a recent enough release, you will need to build Redis from source. Please see [the Redis installation documentation](https://github.com/redis/redis) for further details.
|
||||
|
||||
### Ubuntu
|
||||
|
||||
```no-highlight
|
||||
# apt-get install -y redis-server
|
||||
sudo apt install -y redis-server
|
||||
```
|
||||
|
||||
### CentOS
|
||||
|
||||
```no-highlight
|
||||
# yum install -y epel-release
|
||||
# yum install -y redis
|
||||
# systemctl start redis
|
||||
# systemctl enable redis
|
||||
sudo yum install -y redis
|
||||
sudo systemctl start redis
|
||||
sudo systemctl enable redis
|
||||
```
|
||||
|
||||
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.
|
||||
|
@ -4,43 +4,55 @@ This section of the documentation discusses installing and configuring the NetBo
|
||||
|
||||
## Install System Packages
|
||||
|
||||
Begin by installing all system packages required by NetBox and its dependencies. Note that beginning with NetBox v2.8, Python 3.6 or later is required.
|
||||
Begin by installing all system packages required by NetBox and its dependencies.
|
||||
|
||||
!!! note
|
||||
NetBox v2.8.0 and later require Python 3.6, 3.7, or 3.8. This documentation assumes Python 3.6.
|
||||
|
||||
### Ubuntu
|
||||
|
||||
```no-highlight
|
||||
# apt-get install -y python3.6 python3-pip python3-venv python3-dev build-essential libxml2-dev libxslt1-dev libffi-dev libpq-dev libssl-dev zlib1g-dev
|
||||
sudo apt install -y python3.6 python3-pip python3-venv python3-dev build-essential libxml2-dev libxslt1-dev libffi-dev libpq-dev libssl-dev zlib1g-dev
|
||||
```
|
||||
|
||||
### CentOS
|
||||
|
||||
```no-highlight
|
||||
# yum install -y gcc python36 python36-devel python36-setuptools libxml2-devel libxslt-devel libffi-devel openssl-devel redhat-rpm-config
|
||||
# easy_install-3.6 pip
|
||||
sudo yum install -y gcc python36 python36-devel python3-pip libxml2-devel libxslt-devel libffi-devel openssl-devel redhat-rpm-config
|
||||
```
|
||||
|
||||
Before continuing with either platform, update pip (Python's package management tool) to its latest release:
|
||||
|
||||
```no-highlight
|
||||
sudo pip3 install --upgrade pip
|
||||
```
|
||||
|
||||
## Download NetBox
|
||||
|
||||
You may opt to install NetBox either from a numbered release or by cloning the master branch of its repository on GitHub.
|
||||
This documentation provides two options for installing NetBox: from a downloadable archive, or from the git repository. Installing from a package (option A below) requires manually fetching and extracting the archive for every future update, whereas installation via git (option B) allows for seamless upgrades by re-pulling the `master` branch.
|
||||
|
||||
### Option A: Download a Release
|
||||
### Option A: Download a Release Archive
|
||||
|
||||
Download the [latest stable release](https://github.com/netbox-community/netbox/releases) from GitHub as a tarball or ZIP archive and 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 and extract it to your desired path. In this example, we'll use `/opt/netbox` as the NetBox root.
|
||||
|
||||
```no-highlight
|
||||
# wget https://github.com/netbox-community/netbox/archive/vX.Y.Z.tar.gz
|
||||
# tar -xzf vX.Y.Z.tar.gz -C /opt
|
||||
# cd /opt/
|
||||
# ln -s netbox-X.Y.Z/ netbox
|
||||
# cd /opt/netbox/
|
||||
$ sudo wget https://github.com/netbox-community/netbox/archive/vX.Y.Z.tar.gz
|
||||
$ sudo tar -xzf vX.Y.Z.tar.gz -C /opt
|
||||
$ sudo ln -s /opt/netbox-X.Y.Z/ /opt/netbox
|
||||
$ ls -l /opt | grep netbox
|
||||
lrwxrwxrwx 1 root root 13 Jul 20 13:44 netbox -> netbox-2.9.0/
|
||||
drwxr-xr-x 2 root root 4096 Jul 20 13:44 netbox-2.9.0
|
||||
```
|
||||
|
||||
!!! note
|
||||
It is recommended to install NetBox in a directory named for its version number. For example, NetBox v2.9.0 would be installed into `/opt/netbox-2.9.0`, and a symlink from `/opt/netbox/` would point to this location. This allows for future releases to be installed in parallel without interrupting the current installation. When changing to the new release, only the symlink needs to be updated.
|
||||
|
||||
### Option B: Clone the Git Repository
|
||||
|
||||
Create the base directory for the NetBox installation. For this guide, we'll use `/opt/netbox`.
|
||||
|
||||
```no-highlight
|
||||
# mkdir -p /opt/netbox/ && cd /opt/netbox/
|
||||
sudo mkdir -p /opt/netbox/ && cd /opt/netbox/
|
||||
```
|
||||
|
||||
If `git` is not already installed, install it:
|
||||
@ -48,19 +60,19 @@ If `git` is not already installed, install it:
|
||||
#### Ubuntu
|
||||
|
||||
```no-highlight
|
||||
# apt-get install -y git
|
||||
sudo apt install -y git
|
||||
```
|
||||
|
||||
#### CentOS
|
||||
|
||||
```no-highlight
|
||||
# yum install -y git
|
||||
sudo yum install -y git
|
||||
```
|
||||
|
||||
Next, clone the **master** branch of the NetBox GitHub repository into the current directory:
|
||||
Next, clone the **master** branch of the NetBox GitHub repository into the current directory. (This branch always holds the current stable release.)
|
||||
|
||||
```no-highlight
|
||||
# git clone -b master https://github.com/netbox-community/netbox.git .
|
||||
$ sudo git clone -b master https://github.com/netbox-community/netbox.git .
|
||||
Cloning into '.'...
|
||||
remote: Counting objects: 1994, done.
|
||||
remote: Compressing objects: 100% (150/150), done.
|
||||
@ -70,72 +82,38 @@ Resolving deltas: 100% (1495/1495), done.
|
||||
Checking connectivity... done.
|
||||
```
|
||||
|
||||
## Create the NetBox User
|
||||
|
||||
Create a system user account named `netbox`. We'll configure the WSGI and HTTP services to run under this account. We'll also assign this user ownership of the media directory. This ensures that NetBox will be able to save local files.
|
||||
|
||||
!!! note
|
||||
CentOS users may need to create the `netbox` group first.
|
||||
Installation via git also allows you to easily try out development versions of NetBox. The `develop` branch contains all work underway for the next minor release, and the `feature` branch tracks progress on the next major release.
|
||||
|
||||
## Create the NetBox System User
|
||||
|
||||
Create a system user account named `netbox`. We'll configure the WSGI and HTTP services to run under this account. We'll also assign this user ownership of the media directory. This ensures that NetBox will be able to save uploaded files.
|
||||
|
||||
#### Ubuntu
|
||||
|
||||
```
|
||||
# groupadd --system netbox
|
||||
# adduser --system --gid netbox netbox
|
||||
# chown --recursive netbox /opt/netbox/netbox/media/
|
||||
sudo adduser --system --group netbox
|
||||
sudo chown --recursive netbox /opt/netbox/netbox/media/
|
||||
```
|
||||
|
||||
## Set Up Python Environment
|
||||
#### CentOS
|
||||
|
||||
We'll use a Python [virtual environment](https://docs.python.org/3.6/tutorial/venv.html) to ensure NetBox's required packages don't conflict with anything in the base system. This will create a directory named `venv` in our NetBox root.
|
||||
|
||||
```no-highlight
|
||||
# python3 -m venv /opt/netbox/venv
|
||||
```
|
||||
|
||||
Next, activate the virtual environment and install the required Python packages. You should see your console prompt change to indicate the active environment. (Activating the virtual environment updates your command shell to use the local copy of Python that we just installed for NetBox instead of the system's Python interpreter.)
|
||||
|
||||
```no-highlight
|
||||
# source venv/bin/activate
|
||||
(venv) # pip3 install -r requirements.txt
|
||||
```
|
||||
|
||||
### NAPALM Automation (Optional)
|
||||
|
||||
NetBox supports integration with the [NAPALM automation](https://napalm-automation.net/) library. NAPALM allows NetBox to fetch live data from devices and return it to a requester via its REST API. Installation of NAPALM is optional. To enable it, install the `napalm` package:
|
||||
|
||||
```no-highlight
|
||||
(venv) # pip3 install napalm
|
||||
```
|
||||
|
||||
To ensure NAPALM is automatically re-installed during future upgrades, create a file named `local_requirements.txt` in the NetBox root directory (alongside `requirements.txt`) and list the `napalm` package:
|
||||
|
||||
```no-highlight
|
||||
# echo napalm >> local_requirements.txt
|
||||
```
|
||||
|
||||
### Remote File Storage (Optional)
|
||||
|
||||
By default, NetBox will use the local filesystem to storage uploaded files. To use a remote filesystem, install the [`django-storages`](https://django-storages.readthedocs.io/en/stable/) library and configure your [desired backend](../../configuration/optional-settings/#storage_backend) in `configuration.py`.
|
||||
|
||||
```no-highlight
|
||||
(venv) # pip3 install django-storages
|
||||
```
|
||||
|
||||
Don't forget to add the `django-storages` package to `local_requirements.txt` to ensure it gets re-installed during future upgrades:
|
||||
|
||||
```no-highlight
|
||||
# echo django-storages >> local_requirements.txt
|
||||
sudo groupadd --system netbox
|
||||
sudo adduser --system -g netbox netbox
|
||||
sudo chown --recursive netbox /opt/netbox/netbox/media/
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
Move into the NetBox configuration directory and make a copy of `configuration.example.py` named `configuration.py`.
|
||||
Move into the NetBox configuration directory and make a copy of `configuration.example.py` named `configuration.py`. This file will hold all of your local configuration parameters.
|
||||
|
||||
```no-highlight
|
||||
(venv) # cd netbox/netbox/
|
||||
(venv) # cp configuration.example.py configuration.py
|
||||
cd /opt/netbox/netbox/netbox/
|
||||
sudo cp configuration.example.py configuration.py
|
||||
```
|
||||
|
||||
Open `configuration.py` with your preferred editor and set the following variables:
|
||||
Open `configuration.py` with your preferred editor to begin configuring NetBox. NetBox offers [many configuration parameters](/configuration/), but only the following four are required for new installations:
|
||||
|
||||
* `ALLOWED_HOSTS`
|
||||
* `DATABASE`
|
||||
@ -144,19 +122,21 @@ Open `configuration.py` with your preferred editor and set the following variabl
|
||||
|
||||
### ALLOWED_HOSTS
|
||||
|
||||
This is a list of the valid hostnames by which this server can be reached. You must specify at least one name or IP address.
|
||||
|
||||
Example:
|
||||
This is a list of the valid hostnames and IP addresses by which this server can be reached. You must specify at least one name or IP address. (Note that this does not restrict the locations from which NetBox may be accessed: It is merely for [HTTP host header validation](https://docs.djangoproject.com/en/3.0/topics/security/#host-headers-virtual-hosting).)
|
||||
|
||||
```python
|
||||
ALLOWED_HOSTS = ['netbox.example.com', '192.0.2.123']
|
||||
```
|
||||
|
||||
If you are not yet sure what the domain name and/or IP address of the NetBox installation will be, you can set this to a wildcard (asterisk) to allow all host values:
|
||||
|
||||
```python
|
||||
ALLOWED_HOSTS = ['*']
|
||||
```
|
||||
|
||||
### DATABASE
|
||||
|
||||
This parameter holds the database configuration details. You must define the username and password used when you configured PostgreSQL. If the service is running on a remote host, replace `localhost` with its address. See the [configuration documentation](../../configuration/required-settings/#database) for more detail on individual parameters.
|
||||
|
||||
Example:
|
||||
This parameter holds the database configuration details. You must define the username and password used when you configured PostgreSQL. If the service is running on a remote host, update the `HOST` and `PORT` parameters accordingly. See the [configuration documentation](/configuration/required-settings/#database) for more detail on individual parameters.
|
||||
|
||||
```python
|
||||
DATABASE = {
|
||||
@ -165,30 +145,30 @@ DATABASE = {
|
||||
'PASSWORD': 'J5brHrAXFLQSif0K', # PostgreSQL password
|
||||
'HOST': 'localhost', # Database server
|
||||
'PORT': '', # Database port (leave blank for default)
|
||||
'CONN_MAX_AGE': 300, # Max database connection age
|
||||
'CONN_MAX_AGE': 300, # Max database connection age (seconds)
|
||||
}
|
||||
```
|
||||
|
||||
### REDIS
|
||||
|
||||
Redis is a in-memory key-value store required as part of the NetBox installation. It is used for features such as webhooks and caching. Redis typically requires minimal configuration; the values below should suffice for most installations. See the [configuration documentation](../../configuration/required-settings/#redis) for more detail on individual parameters.
|
||||
Redis is a in-memory key-value store used by NetBox for caching and background task queuing. Redis typically requires minimal configuration; the values below should suffice for most installations. See the [configuration documentation](/configuration/required-settings/#redis) for more detail on individual parameters.
|
||||
|
||||
Note that NetBox requires the specification of two separate Redis databases: `tasks` and `caching`. These may both be provided by the same Redis service, however each should have a unique numeric database ID.
|
||||
|
||||
```python
|
||||
REDIS = {
|
||||
'tasks': {
|
||||
'HOST': 'redis.example.com',
|
||||
'PORT': 1234,
|
||||
'PASSWORD': 'foobar',
|
||||
'DATABASE': 0,
|
||||
'DEFAULT_TIMEOUT': 300,
|
||||
'SSL': False,
|
||||
'HOST': 'localhost', # Redis server
|
||||
'PORT': 6379, # Redis port
|
||||
'PASSWORD': '', # Redis password (optional)
|
||||
'DATABASE': 0, # Database ID
|
||||
'SSL': False, # Use SSL (optional)
|
||||
},
|
||||
'caching': {
|
||||
'HOST': 'localhost',
|
||||
'PORT': 6379,
|
||||
'PASSWORD': '',
|
||||
'DATABASE': 1,
|
||||
'DEFAULT_TIMEOUT': 300,
|
||||
'DATABASE': 1, # Unique ID for second database
|
||||
'SSL': False,
|
||||
}
|
||||
}
|
||||
@ -196,38 +176,70 @@ REDIS = {
|
||||
|
||||
### SECRET_KEY
|
||||
|
||||
Generate a random secret key of at least 50 alphanumeric characters. This key must be unique to this installation and must not be shared outside the local system.
|
||||
This parameter must be assigned a randomly-generated key employed as a salt for hashing and related cryptographic functions. (Note, however, that it is _never_ directly used in the encryption of secret data.) This key must be unique to this installation and is recommended to be at least 50 characters long. It should not be shared outside the local system.
|
||||
|
||||
You may use the script located at `netbox/generate_secret_key.py` to generate a suitable key.
|
||||
|
||||
!!! note
|
||||
In the case of a highly available installation with multiple web servers, `SECRET_KEY` must be identical among all servers in order to maintain a persistent user session state.
|
||||
|
||||
## Run Database Migrations
|
||||
|
||||
Before NetBox can run, we need to install the database schema. This is done by running `python3 manage.py migrate` from the `netbox` directory (`/opt/netbox/netbox/` in our example):
|
||||
A simple Python script named `generate_secret_key.py` is provided in the parent directory to assist in generating a suitable key:
|
||||
|
||||
```no-highlight
|
||||
(venv) # cd /opt/netbox/netbox/
|
||||
(venv) # python3 manage.py migrate
|
||||
Operations to perform:
|
||||
Apply all migrations: dcim, sessions, admin, ipam, utilities, auth, circuits, contenttypes, extras, secrets, users
|
||||
Running migrations:
|
||||
Rendering model states... DONE
|
||||
Applying contenttypes.0001_initial... OK
|
||||
Applying auth.0001_initial... OK
|
||||
Applying admin.0001_initial... OK
|
||||
...
|
||||
python3 ../generate_secret_key.py
|
||||
```
|
||||
|
||||
If this step results in a PostgreSQL authentication error, ensure that the username and password created in the database match what has been specified in `configuration.py`
|
||||
!!! warning
|
||||
In the case of a highly available installation with multiple web servers, `SECRET_KEY` must be identical among all servers in order to maintain a persistent user session state.
|
||||
|
||||
When you have finished modifying the configuration, remember to save the file.
|
||||
|
||||
## Optional Requirements
|
||||
|
||||
All Python packages required by NetBox are listed in `requirements.txt` and will be installed automatically. NetBox also supports some optional packages. If desired, these packages must be listed in `local_requirements.txt` within the NetBox root directory.
|
||||
|
||||
### NAPALM
|
||||
|
||||
The [NAPALM automation](https://napalm-automation.net/) library allows NetBox to fetch live data from devices and return it to a requester via its REST API. The `NAPALM_USERNAME` and `NAPALM_PASSWORD` configuration parameters define the credentials to be used when connecting to a device.
|
||||
|
||||
```no-highlight
|
||||
sudo echo napalm >> /opt/netbox/local_requirements.txt
|
||||
```
|
||||
|
||||
### Remote File Storage
|
||||
|
||||
By default, NetBox will use the local filesystem to store uploaded files. To use a remote filesystem, install the [`django-storages`](https://django-storages.readthedocs.io/en/stable/) library and configure your [desired storage backend](/configuration/optional-settings/#storage_backend) in `configuration.py`.
|
||||
|
||||
```no-highlight
|
||||
sudo echo django-storages >> /opt/netbox/local_requirements.txt
|
||||
```
|
||||
|
||||
## Run the Upgrade Script
|
||||
|
||||
Once NetBox has been configured, we're ready to proceed with the actual installation. We'll run the packaged upgrade script (`upgrade.sh`) to perform the following actions:
|
||||
|
||||
* Create a Python virtual environment
|
||||
* Install all required Python packages
|
||||
* Run database schema migrations
|
||||
* Aggregate static resource files on disk
|
||||
|
||||
```no-highlight
|
||||
sudo /opt/netbox/upgrade.sh
|
||||
```
|
||||
|
||||
!!! note
|
||||
Upon completion, the upgrade script may warn that no existing virtual environment was detected. As this is a new installation, this warning can be safely ignored.
|
||||
|
||||
## Create a Super User
|
||||
|
||||
NetBox does not come with any predefined user accounts. You'll need to create a super user to be able to log into NetBox:
|
||||
NetBox does not come with any predefined user accounts. You'll need to create a super user (administrative account) to be able to log into NetBox. First, enter the Python virtual environment created by the upgrade script:
|
||||
|
||||
```no-highlight
|
||||
(venv) # python3 manage.py createsuperuser
|
||||
source /opt/netbox/venv/bin/activate
|
||||
```
|
||||
|
||||
Once the virtual environment has been activated, you should notice the string `(venv)` prepended to your console prompt.
|
||||
|
||||
Next, we'll create a superuser account using the `createsuperuser` Django management command (via `manage.py`). Specifying an email address for the user is not required, but be sure to use a very strong password.
|
||||
|
||||
```no-highlight
|
||||
(venv) $ cd /opt/netbox/netbox
|
||||
(venv) $ python3 manage.py createsuperuser
|
||||
Username: admin
|
||||
Email address: admin@example.com
|
||||
Password:
|
||||
@ -235,38 +247,35 @@ Password (again):
|
||||
Superuser created successfully.
|
||||
```
|
||||
|
||||
## Collect Static Files
|
||||
|
||||
```no-highlight
|
||||
(venv) # python3 manage.py collectstatic --no-input
|
||||
|
||||
959 static files copied to '/opt/netbox/netbox/static'.
|
||||
```
|
||||
|
||||
## Test the Application
|
||||
|
||||
At this point, NetBox should be able to run. We can verify this by starting a development instance:
|
||||
At this point, we should be able to run NetBox's development server for testing. We can check by starting a development instance:
|
||||
|
||||
```no-highlight
|
||||
(venv) # python3 manage.py runserver 0.0.0.0:8000 --insecure
|
||||
(venv) $ python3 manage.py runserver 0.0.0.0:8000 --insecure
|
||||
Performing system checks...
|
||||
|
||||
System check identified no issues (0 silenced).
|
||||
November 28, 2018 - 09:33:45
|
||||
Django version 2.0.9, using settings 'netbox.settings'
|
||||
November 17, 2020 - 16:08:13
|
||||
Django version 3.1.3, using settings 'netbox.settings'
|
||||
Starting development server at http://0.0.0.0:8000/
|
||||
Quit the server with CONTROL-C.
|
||||
```
|
||||
|
||||
Next, connect to the name or IP of the server (as defined in `ALLOWED_HOSTS`) on port 8000; for example, <http://127.0.0.1:8000/>. You should be greeted with the NetBox home page. Note that this built-in web service is for development and testing purposes only. **It is not suited for production use.**
|
||||
Next, connect to the name or IP of the server (as defined in `ALLOWED_HOSTS`) on port 8000; for example, <http://127.0.0.1:8000/>. You should be greeted with the NetBox home page.
|
||||
|
||||
!!! warning
|
||||
The development server is for development and testing purposes only. It is neither performant nor secure enough for production use. **Do not use it in production.**
|
||||
|
||||
!!! warning
|
||||
If the test service does not run, or you cannot reach the NetBox home page, something has gone wrong. Do not proceed with the rest of this guide until the installation has been corrected.
|
||||
|
||||
Note that the initial UI will be locked down for non-authenticated users.
|
||||
Note that the initial user interface will be locked down for non-authenticated users.
|
||||
|
||||

|
||||
|
||||
After logging in as the superuser you created earlier, all areas of the UI will be available.
|
||||
Try logging in using the superuser account we just created. Once authenticated, you'll be able to access all areas of the UI:
|
||||
|
||||

|
||||
|
||||
Type `Ctrl+c` to stop the development server.
|
||||
|
52
docs/installation/4-gunicorn.md
Normal file
52
docs/installation/4-gunicorn.md
Normal file
@ -0,0 +1,52 @@
|
||||
# Gunicorn
|
||||
|
||||
Like most Django applications, NetBox runs as a [WSGI application](https://en.wikipedia.org/wiki/Web_Server_Gateway_Interface) behind an HTTP server. This documentation shows how to install and configure [gunicorn](http://gunicorn.org/) (which is automatically installed with NetBox) for this role, however other WSGI servers are available and should work similarly well. [uWSGI](https://uwsgi-docs.readthedocs.io/en/latest/) is a popular alternative.
|
||||
|
||||
## Configuration
|
||||
|
||||
NetBox ships with a default configuration file for gunicorn. To use it, copy `/opt/netbox/contrib/gunicorn.py` to `/opt/netbox/gunicorn.py`. (We make a copy of this file rather than pointing to it directly to ensure that any local changes to it do not get overwritten by a future upgrade.)
|
||||
|
||||
```no-highlight
|
||||
sudo cp /opt/netbox/contrib/gunicorn.py /opt/netbox/gunicorn.py
|
||||
```
|
||||
|
||||
While the provided configuration should suffice for most initial installations, you may wish to edit this file to change the bound IP address and/or port number, or to make performance-related adjustments. See [the Gunicorn documentation](https://docs.gunicorn.org/en/stable/configure.html) for the available configuration parameters.
|
||||
|
||||
## systemd Setup
|
||||
|
||||
We'll use systemd to control both gunicorn and NetBox's background worker process. First, copy `contrib/netbox.service` and `contrib/netbox-rq.service` to the `/etc/systemd/system/` directory and reload the systemd dameon:
|
||||
|
||||
```no-highlight
|
||||
sudo cp -v /opt/netbox/contrib/*.service /etc/systemd/system/
|
||||
sudo systemctl daemon-reload
|
||||
```
|
||||
|
||||
Then, start the `netbox` and `netbox-rq` services and enable them to initiate at boot time:
|
||||
|
||||
```no-highlight
|
||||
sudo systemctl start netbox netbox-rq
|
||||
sudo systemctl enable netbox netbox-rq
|
||||
```
|
||||
|
||||
You can use the command `systemctl status netbox` to verify that the WSGI service is running:
|
||||
|
||||
```no-highlight
|
||||
# systemctl status netbox.service
|
||||
● netbox.service - NetBox WSGI Service
|
||||
Loaded: loaded (/etc/systemd/system/netbox.service; enabled; vendor preset: enabled)
|
||||
Active: active (running) since Tue 2020-11-17 16:18:23 UTC; 3min 35s ago
|
||||
Docs: https://netbox.readthedocs.io/en/stable/
|
||||
Main PID: 22836 (gunicorn)
|
||||
Tasks: 6 (limit: 2345)
|
||||
Memory: 339.3M
|
||||
CGroup: /system.slice/netbox.service
|
||||
├─22836 /opt/netbox/venv/bin/python3 /opt/netbox/venv/bin/gunicorn --pid>
|
||||
├─22854 /opt/netbox/venv/bin/python3 /opt/netbox/venv/bin/gunicorn --pid>
|
||||
├─22855 /opt/netbox/venv/bin/python3 /opt/netbox/venv/bin/gunicorn --pid>
|
||||
...
|
||||
```
|
||||
|
||||
!!! note
|
||||
If the NetBox service fails to start, issue the command `journalctl -eu netbox` to check for log messages that may indicate the problem.
|
||||
|
||||
Once you've verified that the WSGI workers are up and running, move on to HTTP server setup.
|
@ -1,135 +0,0 @@
|
||||
# HTTP Server Setup
|
||||
|
||||
We'll set up a simple WSGI front end using [gunicorn](http://gunicorn.org/) for the purposes of this guide. For web servers, we provide example configurations for both [nginx](https://www.nginx.com/resources/wiki/) and [Apache](http://httpd.apache.org/docs/2.4). (You are of course free to use whichever combination of HTTP and WSGI services you'd like.) We'll use systemd to enable service persistence.
|
||||
|
||||
!!! info
|
||||
For the sake of brevity, only Ubuntu 18.04 instructions are provided here, but this sort of web server and WSGI configuration is not unique to NetBox. Please consult your distribution's documentation for assistance if needed.
|
||||
|
||||
## Obtain an SSL Certificate
|
||||
|
||||
To enable HTTPS access to NetBox, you'll need a valid SSL certificate. You can purchase one from a trusted commercial provider, obtain one for free from [Let's Encrypt](https://letsencrypt.org/getting-started/), or generate your own (although self-signed certificates are generally untrusted). Both the public certificate and private key files need to be installed on your NetBox server in a location that is readable by the `netbox` user.
|
||||
|
||||
The command below can be used to generate a self-signed certificate for testing purposes, however it is strongly recommended to use a certificate from a trusted authority in production. Two files will be created: the public certificate (`netbox.crt`) and the private key (`netbox.key`). The certificate is published to the world, whereas the private key must be kept secret at all times.
|
||||
|
||||
```no-highlight
|
||||
# openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
|
||||
-keyout /etc/ssl/private/netbox.key \
|
||||
-out /etc/ssl/certs/netbox.crt
|
||||
```
|
||||
|
||||
## HTTP Daemon Installation
|
||||
|
||||
### Option A: nginx
|
||||
|
||||
The following will serve as a minimal nginx configuration. Be sure to modify your server name and installation path appropriately.
|
||||
|
||||
```no-highlight
|
||||
# apt-get install -y nginx
|
||||
```
|
||||
|
||||
Once nginx is installed, copy the default nginx configuration file to `/etc/nginx/sites-available/netbox`. Be sure to replace `netbox.example.com` with the domain name or IP address of your installation. (This should match the value configured for `ALLOWED_HOSTS` in `configuration.py`.)
|
||||
|
||||
```no-highlight
|
||||
# cp /opt/netbox/contrib/nginx.conf /etc/nginx/sites-available/netbox
|
||||
```
|
||||
|
||||
Then, delete `/etc/nginx/sites-enabled/default` and create a symlink in the `sites-enabled` directory to the configuration file you just created.
|
||||
|
||||
```no-highlight
|
||||
# cd /etc/nginx/sites-enabled/
|
||||
# rm default
|
||||
# ln -s /etc/nginx/sites-available/netbox
|
||||
```
|
||||
|
||||
Finally, restart the `nginx` service to use the new configuration.
|
||||
|
||||
```no-highlight
|
||||
# service nginx restart
|
||||
```
|
||||
|
||||
### Option B: Apache
|
||||
|
||||
Begin by installing Apache:
|
||||
|
||||
```no-highlight
|
||||
# apt-get install -y apache2
|
||||
```
|
||||
|
||||
Next, copy the default configuration file to `/etc/apache2/sites-available/`. Be sure to modify the `ServerName` parameter appropriately.
|
||||
|
||||
```no-highlight
|
||||
# cp /opt/netbox/contrib/apache.conf /etc/apache2/sites-available/netbox.conf
|
||||
```
|
||||
|
||||
Finally, ensure that the required Apache modules are enabled, enable the `netbox` site, and reload Apache:
|
||||
|
||||
```no-highlight
|
||||
# a2enmod ssl proxy proxy_http headers
|
||||
# a2ensite netbox
|
||||
# service apache2 restart
|
||||
```
|
||||
|
||||
!!! note
|
||||
Certain components of NetBox (such as the display of rack elevation diagrams) rely on the use of embedded objects. Ensure that your HTTP server configuration does not override the `X-Frame-Options` response header set by NetBox.
|
||||
|
||||
## Gunicorn Configuration
|
||||
|
||||
Copy `/opt/netbox/contrib/gunicorn.py` to `/opt/netbox/gunicorn.py`. (We make a copy of this file to ensure that any changes to it do not get overwritten by a future upgrade.)
|
||||
|
||||
```no-highlight
|
||||
# cd /opt/netbox
|
||||
# cp contrib/gunicorn.py /opt/netbox/gunicorn.py
|
||||
```
|
||||
|
||||
You may wish to edit this file to change the bound IP address or port number, or to make performance-related adjustments. See [the Gunicorn documentation](https://docs.gunicorn.org/en/stable/configure.html) for the available configuration parameters.
|
||||
|
||||
## systemd Configuration
|
||||
|
||||
We'll use systemd to control the daemonization of NetBox services. First, copy `contrib/netbox.service` and `contrib/netbox-rq.service` to the `/etc/systemd/system/` directory:
|
||||
|
||||
```no-highlight
|
||||
# cp contrib/*.service /etc/systemd/system/
|
||||
```
|
||||
|
||||
Then, start the `netbox` and `netbox-rq` services and enable them to initiate at boot time:
|
||||
|
||||
```no-highlight
|
||||
# systemctl daemon-reload
|
||||
# systemctl start netbox netbox-rq
|
||||
# systemctl enable netbox netbox-rq
|
||||
```
|
||||
|
||||
You can use the command `systemctl status netbox` to verify that the WSGI service is running:
|
||||
|
||||
```no-highlight
|
||||
# systemctl status netbox.service
|
||||
● netbox.service - NetBox WSGI Service
|
||||
Loaded: loaded (/etc/systemd/system/netbox.service; enabled; vendor preset: enabled)
|
||||
Active: active (running) since Thu 2019-12-12 19:23:40 UTC; 25s ago
|
||||
Docs: https://netbox.readthedocs.io/en/stable/
|
||||
Main PID: 11993 (gunicorn)
|
||||
Tasks: 6 (limit: 2362)
|
||||
CGroup: /system.slice/netbox.service
|
||||
├─11993 /usr/bin/python3 /usr/local/bin/gunicorn --pid /var/tmp/netbox.pid --pythonpath /opt/netbox/...
|
||||
├─12015 /usr/bin/python3 /usr/local/bin/gunicorn --pid /var/tmp/netbox.pid --pythonpath /opt/netbox/...
|
||||
├─12016 /usr/bin/python3 /usr/local/bin/gunicorn --pid /var/tmp/netbox.pid --pythonpath /opt/netbox/...
|
||||
...
|
||||
```
|
||||
|
||||
At this point, you should be able to connect to the HTTP service at the server name or IP address you provided.
|
||||
|
||||
!!! info
|
||||
Please keep in mind that the configurations provided here are bare minimums required to get NetBox up and running. You may want to make adjustments to better suit your production environment.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If you are unable to connect to the HTTP server, check that:
|
||||
|
||||
* Nginx/Apache is running and configured to listen on the correct port.
|
||||
* Access is not being blocked by a firewall. (Try connecting locally from the server itself.)
|
||||
|
||||
If you are able to connect but receive a 502 (bad gateway) error, check the following:
|
||||
|
||||
* The NetBox system process (gunicorn) is running: `systemctl status netbox`
|
||||
* 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`
|
94
docs/installation/5-http-server.md
Normal file
94
docs/installation/5-http-server.md
Normal file
@ -0,0 +1,94 @@
|
||||
# HTTP Server Setup
|
||||
|
||||
This documentation provides example configurations for both [nginx](https://www.nginx.com/resources/wiki/) and [Apache](https://httpd.apache.org/docs/current/), though any HTTP server which supports WSGI should be compatible.
|
||||
|
||||
!!! info
|
||||
For the sake of brevity, only Ubuntu 20.04 instructions are provided here. These tasks are not unique to NetBox and should carry over to other distributions with minimal changes. Please consult your distribution's documentation for assistance if needed.
|
||||
|
||||
## Obtain an SSL Certificate
|
||||
|
||||
To enable HTTPS access to NetBox, you'll need a valid SSL certificate. You can purchase one from a trusted commercial provider, obtain one for free from [Let's Encrypt](https://letsencrypt.org/getting-started/), or generate your own (although self-signed certificates are generally untrusted). Both the public certificate and private key files need to be installed on your NetBox server in a location that is readable by the `netbox` user.
|
||||
|
||||
The command below can be used to generate a self-signed certificate for testing purposes, however it is strongly recommended to use a certificate from a trusted authority in production. Two files will be created: the public certificate (`netbox.crt`) and the private key (`netbox.key`). The certificate is published to the world, whereas the private key must be kept secret at all times.
|
||||
|
||||
```no-highlight
|
||||
sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
|
||||
-keyout /etc/ssl/private/netbox.key \
|
||||
-out /etc/ssl/certs/netbox.crt
|
||||
```
|
||||
|
||||
The above command will prompt you for additional details of the certificate; all of these are optional.
|
||||
|
||||
## HTTP Server Installation
|
||||
|
||||
### Option A: nginx
|
||||
|
||||
Begin by installing nginx:
|
||||
|
||||
```no-highlight
|
||||
sudo apt install -y nginx
|
||||
```
|
||||
|
||||
Once nginx is installed, copy the nginx configuration file provided by NetBox to `/etc/nginx/sites-available/netbox`. Be sure to replace `netbox.example.com` with the domain name or IP address of your installation. (This should match the value configured for `ALLOWED_HOSTS` in `configuration.py`.)
|
||||
|
||||
```no-highlight
|
||||
sudo cp /opt/netbox/contrib/nginx.conf /etc/nginx/sites-available/netbox
|
||||
```
|
||||
|
||||
Then, delete `/etc/nginx/sites-enabled/default` and create a symlink in the `sites-enabled` directory to the configuration file you just created.
|
||||
|
||||
```no-highlight
|
||||
sudo rm /etc/nginx/sites-enabled/default
|
||||
sudo ln -s /etc/nginx/sites-available/netbox /etc/nginx/sites-enabled/netbox
|
||||
```
|
||||
|
||||
Finally, restart the `nginx` service to use the new configuration.
|
||||
|
||||
```no-highlight
|
||||
sudo systemctl restart nginx
|
||||
```
|
||||
|
||||
### Option B: Apache
|
||||
|
||||
Begin by installing Apache:
|
||||
|
||||
```no-highlight
|
||||
sudo apt install -y apache2
|
||||
```
|
||||
|
||||
Next, copy the default configuration file to `/etc/apache2/sites-available/`. Be sure to modify the `ServerName` parameter appropriately.
|
||||
|
||||
```no-highlight
|
||||
sudo cp /opt/netbox/contrib/apache.conf /etc/apache2/sites-available/netbox.conf
|
||||
```
|
||||
|
||||
Finally, ensure that the required Apache modules are enabled, enable the `netbox` site, and reload Apache:
|
||||
|
||||
```no-highlight
|
||||
sudo a2enmod ssl proxy proxy_http headers
|
||||
sudo a2ensite netbox
|
||||
sudo systemctl restart apache2
|
||||
```
|
||||
|
||||
## Confirm Connectivity
|
||||
|
||||
At this point, you should be able to connect to the HTTPS service at the server name or IP address you provided.
|
||||
|
||||
!!! info
|
||||
Please keep in mind that the configurations provided here are bare minimums required to get NetBox up and running. You may want to make adjustments to better suit your production environment.
|
||||
|
||||
!!! warning
|
||||
Certain components of NetBox (such as the display of rack elevation diagrams) rely on the use of embedded objects. Ensure that your HTTP server configuration does not override the `X-Frame-Options` response header set by NetBox.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If you are unable to connect to the HTTP server, check that:
|
||||
|
||||
* Nginx/Apache is running and configured to listen on the correct port.
|
||||
* Access is not being blocked by a firewall somewhere along the path. (Try connecting locally from the server itself.)
|
||||
|
||||
If you are able to connect but receive a 502 (bad gateway) error, check the following:
|
||||
|
||||
* 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).
|
||||
* 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`
|
@ -9,13 +9,13 @@ This guide explains how to implement LDAP authentication using an external serve
|
||||
On Ubuntu:
|
||||
|
||||
```no-highlight
|
||||
# apt-get install -y libldap2-dev libsasl2-dev libssl-dev
|
||||
sudo apt install -y libldap2-dev libsasl2-dev libssl-dev
|
||||
```
|
||||
|
||||
On CentOS:
|
||||
|
||||
```no-highlight
|
||||
# yum install -y openldap-devel
|
||||
sudo yum install -y openldap-devel
|
||||
```
|
||||
|
||||
### Install django-auth-ldap
|
||||
@ -23,20 +23,25 @@ On CentOS:
|
||||
Activate the Python virtual environment and install the `django-auth-ldap` package using pip:
|
||||
|
||||
```no-highlight
|
||||
# cd /opt/netbox/
|
||||
# source venv/bin/activate
|
||||
(venv) # pip3 install django-auth-ldap
|
||||
source /opt/netbox/venv/bin/activate
|
||||
pip3 install django-auth-ldap
|
||||
```
|
||||
|
||||
Once installed, add the package to `local_requirements.txt` to ensure it is re-installed during future rebuilds of the virtual environment:
|
||||
|
||||
```no-highlight
|
||||
(venv) # echo django-auth-ldap >> local_requirements.txt
|
||||
sudo echo django-auth-ldap >> /opt/netbox/local_requirements.txt
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
Create a file in the same directory as `configuration.py` (typically `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](http://django-auth-ldap.readthedocs.io/).
|
||||
First, enable the LDAP authentication backend in `configuration.py`. (Be sure to overwrite this definition if it is already set to `RemoteUserBackend`.)
|
||||
|
||||
```python
|
||||
REMOTE_AUTH_BACKEND = 'netbox.authentication.LDAPBackend'
|
||||
```
|
||||
|
||||
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/).
|
||||
|
||||
### General Server Configuration
|
||||
|
||||
@ -137,16 +142,28 @@ AUTH_LDAP_CACHE_TIMEOUT = 3600
|
||||
|
||||
`systemctl restart netbox` restarts the Netbox service, and initiates any changes made to `ldap_config.py`. If there are syntax errors present, the NetBox process will not spawn an instance, and errors should be logged to `/var/log/messages`.
|
||||
|
||||
For troubleshooting LDAP user/group queries, add the following lines to the start of `ldap_config.py` after `import ldap`.
|
||||
For troubleshooting LDAP user/group queries, add or merge the following [logging](/configuration/optional-settings.md#logging) configuration to `configuration.py`:
|
||||
|
||||
```python
|
||||
import logging, logging.handlers
|
||||
logfile = "/opt/netbox/logs/django-ldap-debug.log"
|
||||
my_logger = logging.getLogger('django_auth_ldap')
|
||||
my_logger.setLevel(logging.DEBUG)
|
||||
handler = logging.handlers.RotatingFileHandler(
|
||||
logfile, maxBytes=1024 * 500, backupCount=5)
|
||||
my_logger.addHandler(handler)
|
||||
LOGGING = {
|
||||
'version': 1,
|
||||
'disable_existing_loggers': False,
|
||||
'handlers': {
|
||||
'netbox_auth_log': {
|
||||
'level': 'DEBUG',
|
||||
'class': 'logging.handlers.RotatingFileHandler',
|
||||
'filename': '/opt/netbox/logs/django-ldap-debug.log',
|
||||
'maxBytes': 1024 * 500,
|
||||
'backupCount': 5,
|
||||
},
|
||||
},
|
||||
'loggers': {
|
||||
'django_auth_ldap': {
|
||||
'handlers': ['netbox_auth_log'],
|
||||
'level': 'DEBUG',
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
Ensure the file and path specified in logfile exist and are writable and executable by the application service account. Restart the netbox service and attempt to log into the site to trigger log entries to this file.
|
@ -1,12 +1,23 @@
|
||||
# Installation
|
||||
|
||||
The installation instructions provided here have been tested to work on Ubuntu 20.04 and CentOS 8.2. The particular commands needed to install dependencies on other distributions may vary significantly. Unfortunately, this is outside the control of the NetBox maintainers. Please consult your distribution's documentation for assistance with any errors.
|
||||
|
||||
The following sections detail how to set up a new instance of NetBox:
|
||||
|
||||
1. [PostgreSQL database](1-postgresql.md)
|
||||
1. [Redis](2-redis.md)
|
||||
3. [NetBox components](3-netbox.md)
|
||||
4. [HTTP daemon](4-http-daemon.md)
|
||||
5. [LDAP authentication](5-ldap.md) (optional)
|
||||
4. [Gunicorn](4-gunicorn.md)
|
||||
5. [HTTP server](5-http-server.md)
|
||||
6. [LDAP authentication](6-ldap.md) (optional)
|
||||
|
||||
## Requirements
|
||||
|
||||
| Dependency | Minimum Version |
|
||||
|------------|-----------------|
|
||||
| Python | 3.6 |
|
||||
| PostgreSQL | 9.6 |
|
||||
| Redis | 4.0 |
|
||||
|
||||
Below is a simplified overview of the NetBox application stack for reference:
|
||||
|
||||
@ -16,4 +27,5 @@ Below is a simplified overview of the NetBox application stack for reference:
|
||||
|
||||
If you are upgrading from an existing installation, please consult the [upgrading guide](upgrading.md).
|
||||
|
||||
Netbox v2.5.9 and later moved to using systemd instead of supervisord. Please see the instructions for [migrating to systemd](migrating-to-systemd.md) if you are still using supervisord.
|
||||
!!! note
|
||||
Beginning with v2.5.9, the official documentation calls for systemd to be used for managing the WSGI workers in place of supervisord. Please see the instructions for [migrating to systemd](migrating-to-systemd.md) if you are still using supervisord.
|
||||
|
@ -38,14 +38,14 @@ You can use the command `systemctl status netbox` to verify that the WSGI servic
|
||||
# systemctl status netbox.service
|
||||
● netbox.service - NetBox WSGI Service
|
||||
Loaded: loaded (/etc/systemd/system/netbox.service; enabled; vendor preset: enabled)
|
||||
Active: active (running) since Thu 2019-12-12 19:23:40 UTC; 25s ago
|
||||
Active: active (running) since Sat 2020-10-24 19:23:40 UTC; 25s ago
|
||||
Docs: https://netbox.readthedocs.io/en/stable/
|
||||
Main PID: 11993 (gunicorn)
|
||||
Tasks: 6 (limit: 2362)
|
||||
CGroup: /system.slice/netbox.service
|
||||
├─11993 /usr/bin/python3 /usr/local/bin/gunicorn --pid /var/tmp/netbox.pid --pythonpath /opt/netbox/...
|
||||
├─12015 /usr/bin/python3 /usr/local/bin/gunicorn --pid /var/tmp/netbox.pid --pythonpath /opt/netbox/...
|
||||
├─12016 /usr/bin/python3 /usr/local/bin/gunicorn --pid /var/tmp/netbox.pid --pythonpath /opt/netbox/...
|
||||
├─11993 /opt/netbox/venv/bin/python3 /opt/netbox/venv/bin/gunicorn --pid /var/tmp/netbox.pid --pythonpath /opt/netbox/...
|
||||
├─12015 /opt/netbox/venv/bin/python3 /opt/netbox/venv/bin/gunicorn --pid /var/tmp/netbox.pid --pythonpath /opt/netbox/...
|
||||
├─12016 /opt/netbox/venv/bin/python3 /opt/netbox/venv/bin/gunicorn --pid /var/tmp/netbox.pid --pythonpath /opt/netbox/...
|
||||
...
|
||||
```
|
||||
|
||||
|
@ -2,12 +2,19 @@
|
||||
|
||||
## Review the Release Notes
|
||||
|
||||
Prior to upgrading your NetBox instance, be sure to carefully review all [release notes](../../release-notes/) 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 version in which the change went into effect.
|
||||
Prior to upgrading your NetBox instance, be sure to carefully review all [release notes](../../release-notes/) 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.
|
||||
|
||||
!!! note
|
||||
Beginning with version 2.8, NetBox requires Python 3.6 or later.
|
||||
## Update Dependencies to Required Versions
|
||||
|
||||
## Install the Latest Code
|
||||
NetBox v2.9.0 and later requires the following:
|
||||
|
||||
| Dependency | Minimum Version |
|
||||
|------------|-----------------|
|
||||
| Python | 3.6 |
|
||||
| PostgreSQL | 9.6 |
|
||||
| Redis | 4.0 |
|
||||
|
||||
## Install the Latest Release
|
||||
|
||||
As with the initial installation, you can upgrade NetBox by either downloading the latest release package or by cloning the `master` branch of the git repository.
|
||||
|
||||
@ -18,41 +25,36 @@ Download the [latest stable release](https://github.com/netbox-community/netbox/
|
||||
Download and extract the latest version:
|
||||
|
||||
```no-highlight
|
||||
# wget https://github.com/netbox-community/netbox/archive/vX.Y.Z.tar.gz
|
||||
# tar -xzf vX.Y.Z.tar.gz -C /opt
|
||||
# cd /opt/
|
||||
# ln -sfn netbox-X.Y.Z/ netbox
|
||||
wget https://github.com/netbox-community/netbox/archive/vX.Y.Z.tar.gz
|
||||
sudo tar -xzf vX.Y.Z.tar.gz -C /opt
|
||||
sudo ln -sfn /opt/netbox-X.Y.Z/ /opt/netbox
|
||||
```
|
||||
|
||||
Copy the 'configuration.py' you created when first installing to the new version:
|
||||
Copy `local_requirements.txt`, `configuration.py`, and `ldap_config.py` (if present) from the current installation to the new version:
|
||||
|
||||
```no-highlight
|
||||
# cp netbox-X.Y.Z/netbox/netbox/configuration.py netbox/netbox/netbox/configuration.py
|
||||
```
|
||||
|
||||
Also copy the LDAP configuration if using LDAP:
|
||||
|
||||
```no-highlight
|
||||
# cp netbox-X.Y.Z/netbox/netbox/ldap_config.py netbox/netbox/netbox/ldap_config.py
|
||||
sudo cp /opt/netbox-X.Y.Z/local_requirements.txt /opt/netbox/
|
||||
sudo cp /opt/netbox-X.Y.Z/netbox/netbox/configuration.py /opt/netbox/netbox/netbox/
|
||||
sudo cp /opt/netbox-X.Y.Z/netbox/netbox/ldap_config.py /opt/netbox/netbox/netbox/
|
||||
```
|
||||
|
||||
Be sure to replicate your uploaded media as well. (The exact action necessary will depend on where you choose to store your media, but in general moving or copying the media directory will suffice.)
|
||||
|
||||
```no-highlight
|
||||
# cp -pr netbox-X.Y.Z/netbox/media/ netbox/netbox/
|
||||
sudo cp -pr /opt/netbox-X.Y.Z/netbox/media/ /opt/netbox/netbox/
|
||||
```
|
||||
|
||||
Also make sure to copy over any custom scripts and reports that you've made. Note that if these are stored outside the project root, you will not need to copy them. (Check the `SCRIPTS_ROOT` and `REPORTS_ROOT` parameters in the configuration file above if you're unsure.)
|
||||
Also make sure to copy or link any custom scripts and reports that you've made. Note that if these are stored outside the project root, you will not need to copy them. (Check the `SCRIPTS_ROOT` and `REPORTS_ROOT` parameters in the configuration file above if you're unsure.)
|
||||
|
||||
```no-highlight
|
||||
# cp -r /opt/netbox-X.Y.Z/netbox/scripts /opt/netbox/netbox/scripts/
|
||||
# cp -r /opt/netbox-X.Y.Z/netbox/reports /opt/netbox/netbox/reports/
|
||||
sudo cp -r /opt/netbox-X.Y.Z/netbox/scripts /opt/netbox/netbox/
|
||||
sudo cp -r /opt/netbox-X.Y.Z/netbox/reports /opt/netbox/netbox/
|
||||
```
|
||||
|
||||
If you followed the original installation guide to set up gunicorn, be sure to copy its configuration as well:
|
||||
|
||||
```no-highlight
|
||||
# cp netbox-X.Y.Z/gunicorn.py netbox/gunicorn.py
|
||||
sudo cp /opt/netbox-X.Y.Z/gunicorn.py /opt/netbox/
|
||||
```
|
||||
|
||||
### Option B: Clone the Git Repository
|
||||
@ -60,10 +62,9 @@ If you followed the original installation guide to set up gunicorn, be sure to c
|
||||
This guide assumes that NetBox is installed at `/opt/netbox`. Pull down the most recent iteration of the master branch:
|
||||
|
||||
```no-highlight
|
||||
# cd /opt/netbox
|
||||
# git checkout master
|
||||
# git pull origin master
|
||||
# git status
|
||||
cd /opt/netbox
|
||||
sudo git checkout master
|
||||
sudo git pull origin master
|
||||
```
|
||||
|
||||
## Run the Upgrade Script
|
||||
@ -71,10 +72,10 @@ This guide assumes that NetBox is installed at `/opt/netbox`. Pull down the most
|
||||
Once the new code is in place, verify that any optional Python packages required by your deployment (e.g. `napalm` or `django-auth-ldap`) are listed in `local_requirements.txt`. Then, run the upgrade script:
|
||||
|
||||
```no-highlight
|
||||
# ./upgrade.sh
|
||||
sudo ./upgrade.sh
|
||||
```
|
||||
|
||||
This script:
|
||||
This script performs the following actions:
|
||||
|
||||
* Destroys and rebuilds the Python virtual environment
|
||||
* Installs all required Python packages (listed in `requirements.txt`)
|
||||
@ -86,23 +87,20 @@ This script:
|
||||
* Clears all cached data to prevent conflicts with the new release
|
||||
|
||||
!!! note
|
||||
It's possible that the upgrade script will display a notice warning of unreflected database migrations:
|
||||
|
||||
Your models have changes that are not yet reflected in a migration, and so won't be applied.
|
||||
Run 'manage.py makemigrations' to make new migrations, and then re-run 'manage.py migrate' to apply them.
|
||||
|
||||
This may occur due to semantic differences in environment, and can be safely ignored. Never attempt to create new migrations unless you are intentionally modifying the database schema.
|
||||
If the upgrade script prompts a warning about unreflected database migrations, this indicates that some change has
|
||||
been made to your local codebase and should be investigated. Never attempt to create new migrations unless you are
|
||||
intentionally modifying the database schema.
|
||||
|
||||
## Restart the NetBox Services
|
||||
|
||||
!!! warning
|
||||
If you are upgrading from an installation that does not use a Python virtual environment, 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.
|
||||
|
||||
Finally, restart the gunicorn and RQ services:
|
||||
|
||||
```no-highlight
|
||||
# sudo systemctl restart netbox netbox-rq
|
||||
sudo systemctl restart netbox netbox-rq
|
||||
```
|
||||
|
||||
!!! note
|
||||
It's possible you are still using supervisord instead of systemd. If so, please see the instructions for [migrating to systemd](migrating-to-systemd.md).
|
||||
If upgrading from an installation that uses supervisord, please see the instructions for [migrating to systemd](migrating-to-systemd.md). The use of supervisord is no longer supported.
|
||||
|
BIN
docs/media/admin_ui_run_permission.png
Normal file
BIN
docs/media/admin_ui_run_permission.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 8.0 KiB |
BIN
docs/media/models/dcim_cable_trace.png
Normal file
BIN
docs/media/models/dcim_cable_trace.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
BIN
docs/media/power_distribution.png
Normal file
BIN
docs/media/power_distribution.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 32 KiB |
@ -1,3 +1,19 @@
|
||||
# Circuits
|
||||
|
||||
A circuit represents a single _physical_ link connecting exactly two endpoints. (A circuit with more than two endpoints is a virtual circuit, which is not currently supported by NetBox.) Each circuit belongs to a provider and must be assigned a circuit ID which is unique to that provider.
|
||||
A communications circuit represents a single _physical_ link connecting exactly two endpoints, commonly referred to as its A and Z terminations. A circuit in NetBox may have zero, one, or two terminations defined. It is common to have only one termination defined when you don't necessarily care about the details of the provider side of the circuit, e.g. for Internet access circuits. Both terminations would likely be modeled for circuits which connect one customer site to another.
|
||||
|
||||
Each circuit is associated with a provider and a user-defined type. For example, you might have Internet access circuits delivered to each site by one provider, and private MPLS circuits delivered by another. Each circuit must be assigned a circuit ID, each of which must be unique per provider.
|
||||
|
||||
Each circuit is also assigned one of the following operational statuses:
|
||||
|
||||
* Planned
|
||||
* Provisioning
|
||||
* Active
|
||||
* Offline
|
||||
* Deprovisioning
|
||||
* Decommissioned
|
||||
|
||||
Circuits also have optional fields for annotating their installation date and commit rate, and may be assigned to NetBox tenants.
|
||||
|
||||
!!! note
|
||||
NetBox currently models only physical circuits: those which have exactly two endpoints. It is common to layer virtualized constructs (_virtual circuits_) such as MPLS or EVPN tunnels on top of these, however NetBox does not yet support virtual circuit modeling.
|
||||
|
@ -1,11 +1,10 @@
|
||||
# Circuit Terminations
|
||||
|
||||
A circuit may have one or two terminations, annotated as the "A" and "Z" sides of the circuit. A single-termination circuit can be used when you don't know (or care) about the far end of a circuit (for example, an Internet access circuit which connects to a transit provider). A dual-termination circuit is useful for tracking circuits which connect two sites.
|
||||
The association of a circuit with a particular site and/or device is modeled separately as a circuit termination. A circuit may have up to two terminations, labeled A and Z. A single-termination circuit can be used when you don't know (or care) about the far end of a circuit (for example, an Internet access circuit which connects to a transit provider). A dual-termination circuit is useful for tracking circuits which connect two sites.
|
||||
|
||||
Each circuit termination is tied to a site, and may optionally be connected via a cable to a specific device interface or pass-through port. Each termination can be assigned a separate downstream and upstream speed independent from one another. Fields are also available to track cross-connect and patch panel details.
|
||||
Each circuit termination is tied to a site, and may optionally be connected via a cable to a specific device interface or port within that site. Each termination must be assigned a port speed, and can optionally be assigned an upstream speed if it differs from the downstream speed (a common scenario with e.g. DOCSIS cable modems). Fields are also available to track cross-connect and patch panel details.
|
||||
|
||||
In adherence with NetBox's philosophy of closely modeling the real world, a circuit may terminate only to a physical interface. For example, circuits may not terminate to LAG interfaces, which are virtual in nature. In such cases, a separate physical circuit is associated with each LAG member interface and each needs to be modeled discretely.
|
||||
|
||||
!!! note
|
||||
A circuit represents a physical link, and cannot have more than two endpoints. When modeling a multi-point topology, each leg of the topology must be defined as a discrete circuit.
|
||||
|
||||
!!! note
|
||||
A circuit may terminate only to a physical interface. Circuits may not terminate to LAG interfaces, which are virtual interfaces: You must define each physical circuit within a service bundle separately and terminate it to its actual physical interface.
|
||||
A circuit in NetBox represents a physical link, and cannot have more than two endpoints. When modeling a multi-point topology, each leg of the topology must be defined as a discrete circuit, with one end terminating within the provider's infrastructure.
|
||||
|
@ -1,10 +1,8 @@
|
||||
# Circuit Types
|
||||
|
||||
Circuits are classified by type. For example, you might define circuit types for:
|
||||
Circuits are classified by functional type. These types are completely customizable, and are typically used to convey the type of service being delivered over a circuit. For example, you might define circuit types for:
|
||||
|
||||
* Internet transit
|
||||
* Out-of-band connectivity
|
||||
* Peering
|
||||
* Private backhaul
|
||||
|
||||
Circuit types are fully customizable.
|
@ -1,5 +1,5 @@
|
||||
# Providers
|
||||
|
||||
A provider is any entity which provides some form of connectivity. While this obviously includes carriers which offer Internet and private transit service, it might also include Internet exchange (IX) points and even organizations with whom you peer directly.
|
||||
A circuit provider is any entity which provides some form of connectivity of among sites or organizations within a site. While this obviously includes carriers which offer Internet and private transit service, it might also include Internet exchange (IX) points and even organizations with whom you peer directly. Each circuit within NetBox must be assigned a provider and a circuit ID which is unique to that provider.
|
||||
|
||||
Each provider may be assigned an autonomous system number (ASN), an account number, and relevant contact information.
|
||||
Each provider may be assigned an autonomous system number (ASN), an account number, and contact information.
|
||||
|
@ -1,19 +1,34 @@
|
||||
# Cables
|
||||
|
||||
A cable represents a physical connection between two termination points, such as between a console port and a patch panel port, or between two network interfaces. Cables can be traced through pass-through ports to form a complete path between two endpoints. In the example below, three individual cables comprise a path between the two connected endpoints.
|
||||
All connections between device components in NetBox are represented using cables. A cable represents a direct physical connection between two termination points, such as between a console port and a patch panel port, or between two network interfaces.
|
||||
|
||||
```
|
||||
|<------------------------------------------ Cable Path ------------------------------------------->|
|
||||
Each cable must have two endpoints defined. These endpoints are sometimes referenced as A and B for clarity, however cables are direction-agnostic and the order in which terminations are made has no meaning. Cables may be connected to the following objects:
|
||||
|
||||
Device A Patch Panel A Patch Panel B Device B
|
||||
+-----------+ +-------------+ +-------------+ +-----------+
|
||||
| Interface | --- Cable --- | Front Port | | Front Port | --- Cable --- | Interface |
|
||||
+-----------+ +-------------+ +-------------+ +-----------+
|
||||
+-------------+ +-------------+
|
||||
| Rear Port | --- Cable --- | Rear Port |
|
||||
+-------------+ +-------------+
|
||||
```
|
||||
* Circuit terminations
|
||||
* Console ports
|
||||
* Console server ports
|
||||
* Interfaces
|
||||
* Pass-through ports (front and rear)
|
||||
* Power feeds
|
||||
* Power outlets
|
||||
* Power ports
|
||||
|
||||
All connections between device components in NetBox are represented using cables. However, defining the actual cable plant is optional: Components can be be directly connected using cables with no type or other attributes assigned.
|
||||
Each cable may be assigned a type, label, length, and color. Each cable is also assigned one of three operational statuses:
|
||||
|
||||
Cables are also used to associated ports and interfaces with circuit terminations. To do this, first create the circuit termination, then navigate the desired component and connect a cable between the two.
|
||||
* Active (default)
|
||||
* Planned
|
||||
* Decommissioning
|
||||
|
||||
## Tracing Cables
|
||||
|
||||
A cable may be traced from either of its endpoints by clicking the "trace" button. (A REST API endpoint also provides this functionality.) NetBox will follow the path of connected cables from this termination across the directly connected cable to the far-end termination. If the cable connects to a pass-through port, and the peer port has another cable connected, NetBox will continue following the cable path until it encounters a non-pass-through or unconnected termination point. The entire path will be displayed to the user.
|
||||
|
||||
In the example below, three individual cables comprise a path between devices A and D:
|
||||
|
||||

|
||||
|
||||
Traced from Interface 1 on Device A, NetBox will show the following path:
|
||||
|
||||
* Cable 1: Interface 1 to Front Port 1
|
||||
* Cable 2: Rear Port 1 to Rear Port 2
|
||||
* Cable 3: Front Port 2 to Interface 2
|
||||
|
@ -1,5 +1,5 @@
|
||||
## Console Ports
|
||||
|
||||
A console port provides connectivity to the physical console of a device. Console ports are typically used for temporary access by someone who is physically near the device, or for remote out-of-band access via a console server.
|
||||
A console port provides connectivity to the physical console of a device. These are typically used for temporary access by someone who is physically near the device, or for remote out-of-band access provided via a networked console server. Each console port may be assigned a physical type.
|
||||
|
||||
Console ports can be connected to console server ports.
|
||||
Cables can connect console ports to console server ports or pass-through ports.
|
||||
|
@ -1,3 +1,3 @@
|
||||
## Console Port Templates
|
||||
|
||||
A template for a console port that will be created on all instantiations of the parent device type.
|
||||
A template for a console port that will be created on all instantiations of the parent device type. Each console port can be assigned a physical type.
|
||||
|
@ -1,5 +1,5 @@
|
||||
## Console Server Ports
|
||||
|
||||
A console server is a device which provides remote access to the local consoles of connected devices. This is typically done to provide remote out-of-band access to network devices.
|
||||
A console server is a device which provides remote access to the local consoles of connected devices. They are typically used to provide remote out-of-band access to network devices. Each console server port may be assigned a physical type.
|
||||
|
||||
Console server ports can be connected to console ports.
|
||||
Cables can connect console server ports to console ports or pass-through ports.
|
||||
|
@ -1,3 +1,3 @@
|
||||
## Console Server Port Templates
|
||||
|
||||
A template for a console server port that will be created on all instantiations of the parent device type.
|
||||
A template for a console server port that will be created on all instantiations of the parent device type. Each console server port can be assigned a physical type.
|
||||
|
@ -1,7 +1,15 @@
|
||||
# Devices
|
||||
|
||||
Every piece of hardware which is installed within a rack exists in NetBox as a device. Devices are measured in rack units (U) and can be half depth or full depth. A device may have a height of 0U: These devices do not consume vertical rack space and cannot be assigned to a particular rack unit. A common example of a 0U device is a vertically-mounted PDU.
|
||||
Every piece of hardware which is installed within a site or rack exists in NetBox as a device. Devices are measured in rack units (U) and can be half depth or full depth. A device may have a height of 0U: These devices do not consume vertical rack space and cannot be assigned to a particular rack unit. A common example of a 0U device is a vertically-mounted PDU.
|
||||
|
||||
When assigning a multi-U device to a rack, it is considered to be mounted in the lowest-numbered rack unit which it occupies. For example, a 3U device which occupies U8 through U10 is said to be mounted in U8. This logic applies to racks with both ascending and descending unit numbering.
|
||||
|
||||
A device is said to be full depth if its installation on one rack face prevents the installation of any other device on the opposite face within the same rack unit(s). This could be either because the device is physically too deep to allow a device behind it, or because the installation of an opposing device would impede airflow.
|
||||
A device is said to be full-depth if its installation on one rack face prevents the installation of any other device on the opposite face within the same rack unit(s). This could be either because the device is physically too deep to allow a device behind it, or because the installation of an opposing device would impede airflow.
|
||||
|
||||
Each device must be instantiated from a pre-created device type, and its default components (console ports, power ports, interfaces, etc.) will be created automatically. (The device type associated with a device may be changed after its creation, however its components will not be updated retroactively.)
|
||||
|
||||
Each device must be assigned a site, device role, and operational status, and may optionally be assigned to a specific rack within a site. A platform, serial number, and asset tag may optionally be assigned to each device.
|
||||
|
||||
Device names must be unique within a site, unless the device has been assigned to a tenant. Devices may also be unnamed.
|
||||
|
||||
When a device has one or more interfaces with IP addresses assigned, a primary IP for the device can be designated, for both IPv4 and IPv6.
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user