Compare commits
83 Commits
Author | SHA1 | Date | |
---|---|---|---|
98833e20c8 | |||
a5e51dfb47 | |||
936d868582 | |||
9eef398a9d | |||
e83db27b91 | |||
4419646732 | |||
286bfa0248 | |||
e768cb5b87 | |||
e45773c29f | |||
220370ed76 | |||
b49e6fac14 | |||
5beb3093db | |||
df16a431ca | |||
585e411cde | |||
e17baea76c | |||
8c95f32b0c | |||
d4b394c999 | |||
9c259adc88 | |||
582064f4cd | |||
f8ea0f74ec | |||
5cf9c3b4b1 | |||
2450a70ca8 | |||
e9bea0a420 | |||
4895874845 | |||
7eca562252 | |||
fda1c272e9 | |||
609105e170 | |||
740fa8128d | |||
9fdbd2f501 | |||
c1946751ef | |||
a8b66fc082 | |||
ae9e945851 | |||
fc1507a55d | |||
c7ab15f914 | |||
1c42f14bf0 | |||
6182133c7a | |||
1e002846ef | |||
09b4937b35 | |||
4ef420d443 | |||
5a09659278 | |||
2f5d293fd6 | |||
c9d9c7349e | |||
1fa07c6dc1 | |||
c866bfff16 | |||
44cdb73cc4 | |||
20a86f0e98 | |||
1f9ea8db53 | |||
db7daee86e | |||
0bae952410 | |||
34cd2cc577 | |||
e55580e3ae | |||
aab28d03ba | |||
eb09bf5364 | |||
e46a7d2f7f | |||
03eb153da4 | |||
7675e8fc03 | |||
d92f991cbe | |||
c16f72a2ac | |||
2fe139cb3c | |||
aa68548f41 | |||
a10cd805ae | |||
8b8620864c | |||
d145e9c719 | |||
a120a95184 | |||
97477556e0 | |||
7e6edd1bf5 | |||
b53e886f8f | |||
63062a2634 | |||
2b628b9826 | |||
6cc4c67387 | |||
ab0ce20971 | |||
90ae5cf01d | |||
ebb7779b5f | |||
60f7de1898 | |||
89fddbe0ab | |||
a2b08a6ca5 | |||
86675278ab | |||
791027f77b | |||
819f325bd5 | |||
4053a714f8 | |||
f1ebd4d246 | |||
68e6a62df1 | |||
81f8c7386d |
28
.github/issue_template.md
vendored
28
.github/issue_template.md
vendored
@ -2,13 +2,19 @@
|
||||
|
||||
Before raising an issue here, answer the following questions for yourself, please:
|
||||
|
||||
* Did you read through the troubleshooting section? (https://github.com/ninech/netbox-docker/#troubleshooting)
|
||||
* Did you read through the troubleshooting section? (https://github.com/netbox-community/netbox-docker/#troubleshooting)
|
||||
* Have you updated to the latest version and tried again? (i.e. `git pull` and `docker-compose pull`)
|
||||
* Have you reset the project and tried again? (i.e. `docker-compose down -v`)
|
||||
* Are you confident that your problem is related to the Docker or Docker Compose setup this project provides?
|
||||
(Otherwise ask on the Netbox mailing list, please: https://groups.google.com/d/forum/netbox-discuss)
|
||||
* Have you looked through the issues already resolved?
|
||||
|
||||
Please try this means to get help before opening an issue here:
|
||||
|
||||
* On the networktocode Slack in the #netbox-docker channel: http://slack.networktocode.com/
|
||||
* On the networktocode Slack in the #netbox channel: http://slack.networktocode.com/
|
||||
* On the Netbox mailing list: https://groups.google.com/d/forum/netbox-discuss
|
||||
|
||||
-->
|
||||
|
||||
## Current Behavior
|
||||
@ -30,17 +36,19 @@ The output of `git rev-parse HEAD`: `XXXXX`
|
||||
The command you used to start the project: `XXXXX`
|
||||
|
||||
The output of `docker-compose logs netbox`:
|
||||
<!-- if your log is very long, create a Gist instead: https://gist.github.com -->
|
||||
|
||||
```
|
||||
LOG LOG LOG
|
||||
```
|
||||
|
||||
<!--
|
||||
If you have get any 5xx http error, else delete this section.
|
||||
If your log is very long, create a Gist instead: https://gist.github.com
|
||||
If your log is very long, create a Gist instead (and post the link to it): https://gist.github.com
|
||||
-->
|
||||
|
||||
```
|
||||
LOG LOG LOG
|
||||
```
|
||||
|
||||
The output of `docker-compose logs nginx`:
|
||||
<!--
|
||||
Only if you have gotten a 5xx http error, else delete this section.
|
||||
If your log is very long, create a Gist instead (and post the link to it): https://gist.github.com
|
||||
-->
|
||||
The output of `docker-compose logs nginx`:
|
||||
|
||||
```
|
||||
LOG LOG LOG
|
||||
|
66
DOCKER_HUB.md
Normal file
66
DOCKER_HUB.md
Normal file
@ -0,0 +1,66 @@
|
||||
# cloud.docker.com Configuration
|
||||
|
||||
The automatic build is configured in cloud.docker.com.
|
||||
|
||||
The following build configuration is expected:
|
||||
|
||||
```yaml
|
||||
Source Repository: github.com/netbox-community/netbox-docker
|
||||
Build Location: Build on Docker Hub's infrastructure
|
||||
Autotest: Internal and External Pull Requests
|
||||
Repository Links: Enable for Base Image
|
||||
Build Rules:
|
||||
- Source Type: Branch
|
||||
Source: master
|
||||
Docker Tag: branches-main
|
||||
Dockerfile location: Dockerfile
|
||||
- Source Type: Branch
|
||||
Source: master
|
||||
Docker Tag: branches-ldap
|
||||
Dockerfile location: Dockerfile.ldap
|
||||
- Source Type: Branch
|
||||
Source: master
|
||||
Docker Tag: prerelease-main
|
||||
Dockerfile location: Dockerfile
|
||||
- Source Type: Branch
|
||||
Source: master
|
||||
Docker Tag: prerelease-ldap
|
||||
Dockerfile location: Dockerfile.ldap
|
||||
- Source Type: Branch
|
||||
Source: master
|
||||
Docker Tag: release-main
|
||||
Dockerfile location: Dockerfile
|
||||
- Source Type: Branch
|
||||
Source: master
|
||||
Docker Tag: release-ldap
|
||||
Dockerfile location: Dockerfile.ldap
|
||||
Build Environment Variables:
|
||||
# Create an app on Github and use it's OATH credentials here
|
||||
- Key: GITHUB_OAUTH_CLIENT_ID
|
||||
Value: <secret>
|
||||
- Key: GITHUB_OAUTH_CLIENT_SECRET
|
||||
Value: <secret>
|
||||
Build Triggers:
|
||||
- Name: Cron Trigger
|
||||
# Use this trigger in combination with e.g. https://cron-job.org in order to regularly schedule builds
|
||||
```
|
||||
|
||||
## Background Knowledge
|
||||
|
||||
The build system of cloud.docker.com is not made for this kind of project.
|
||||
But we found a way to make it work, and this is how:
|
||||
|
||||
1. The docker hub build system [allows to overwrite the scripts that get executed
|
||||
for `build`, `test` and `push`](overwrite). See `hooks/*`.
|
||||
2. Shared functionality of the scripts `build`, `test` and `push` is extracted to `hooks/common`.
|
||||
3. The `build` script runs `run_build()` from `hooks/common`.
|
||||
This triggers either `build-branches.sh`, `build-latest.sh` or directly `build.sh`.
|
||||
4. The `test` script just invokes `docker-compose` commands.
|
||||
5. The `push` script runs `run_build()` from `hooks/common` with a `--push-only` flag.
|
||||
This causes the `build.sh` script to not re-build the Docker image, but just the just built image.
|
||||
|
||||
The _Docker Tag_ configuration setting is misused to select the type (_release_, _prerelease_, _branches_) of the build as well as the variant (_main_, _ldap_).
|
||||
|
||||
The _Dockerfile location_ configuration setting is completely ignored by the build scripts.
|
||||
|
||||
[overwrite]: https://docs.docker.com/docker-hub/builds/advanced/#override-build-test-or-push-commands
|
@ -1,4 +1,4 @@
|
||||
FROM python:3.6-alpine3.8
|
||||
FROM python:3.6-alpine3.9
|
||||
|
||||
RUN apk add --no-cache \
|
||||
bash \
|
||||
@ -25,7 +25,7 @@ RUN pip install \
|
||||
# pinning django to the version required by netbox
|
||||
# adding it here, to install the correct version of
|
||||
# django-rq
|
||||
'Django>=1.11,<2.1' \
|
||||
'Django>=2.1.5,<2.2' \
|
||||
# django-rq is used for webhooks
|
||||
django-rq
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
ARG DOCKER_ORG=ninech
|
||||
ARG DOCKER_ORG=netboxcommunity
|
||||
ARG DOCKER_REPO=netbox
|
||||
ARG FROM_TAG=latest
|
||||
FROM $DOCKER_ORG/$DOCKER_REPO:$FROM_TAG
|
||||
|
150
README.md
150
README.md
@ -1,19 +1,20 @@
|
||||
# netbox-docker
|
||||
|
||||
[][travis]
|
||||
[The Github repository](netbox-docker-github) houses the components needed to build Netbox as a Docker container.
|
||||
Images are built using this code are released to [Docker Hub][netbox-dockerhub] every night.
|
||||
|
||||
This repository houses the components needed to build NetBox as a Docker container.
|
||||
Images built using this code are released to [Docker Hub][netbox-dockerhub] every night.
|
||||
Questions? Before opening an issue on Github, please join the [Network To Code][ntc-slack] Slack and ask for help in our `#netbox-docker` channel.
|
||||
|
||||
[travis]: https://travis-ci.org/ninech/netbox-docker
|
||||
[netbox-dockerhub]: https://hub.docker.com/r/ninech/netbox/tags/
|
||||
[netbox-dockerhub]: https://hub.docker.com/r/netboxcommunity/netbox/tags/
|
||||
[netbox-docker-github]: https://github.com/netbox-community/netbox-docker/
|
||||
[ntc-slack]: http://slack.networktocode.com/
|
||||
|
||||
## Quickstart
|
||||
|
||||
To get NetBox up and running:
|
||||
To get Netbox up and running:
|
||||
|
||||
```
|
||||
$ git clone -b master https://github.com/ninech/netbox-docker.git
|
||||
$ git clone -b master https://github.com/netbox-community/netbox-docker.git
|
||||
$ cd netbox-docker
|
||||
$ docker-compose pull
|
||||
$ docker-compose up -d
|
||||
@ -41,7 +42,7 @@ Default credentials:
|
||||
* Password: **admin**
|
||||
* API Token: **0123456789abcdef0123456789abcdef01234567**
|
||||
|
||||
[docker-reception]: https://github.com/ninech/reception
|
||||
[docker-reception]: https://github.com/nxt-engineering/reception
|
||||
|
||||
## Dependencies
|
||||
|
||||
@ -59,10 +60,10 @@ These are defined in `netbox.env`.
|
||||
Read [Environment Variables in Compose][compose-env] to understand about the various possibilities to overwrite these variables.
|
||||
(The easiest solution being simply adjusting that file.)
|
||||
|
||||
To find all possible variables, have a look at the [configuration.docker.py][docker-config] and [docker-entrypoint.sh][entrypoint] files.
|
||||
Generally, the environment variables are called the same as their respective NetBox configuration variables.
|
||||
To find all possible variables, have a look at the [configuration.py][docker-config] and [docker-entrypoint.sh][entrypoint] files.
|
||||
Generally, the environment variables are called the same as their respective Netbox configuration variables.
|
||||
Variables which are arrays are usually composed by putting all the values into the same environment variables with the values separated by a whitespace ("` `").
|
||||
For example defining `ALLOWED_HOSTS=localhost ::1 127.0.0.1` would allows access to NetBox through `http://localhost:8080`, `http://[::1]:8080` and `http://127.0.0.1:8080`.
|
||||
For example defining `ALLOWED_HOSTS=localhost ::1 127.0.0.1` would allows access to Netbox through `http://localhost:8080`, `http://[::1]:8080` and `http://127.0.0.1:8080`.
|
||||
|
||||
[compose-env]: https://docs.docker.com/compose/environment-variables/
|
||||
|
||||
@ -71,7 +72,7 @@ For example defining `ALLOWED_HOSTS=localhost ::1 127.0.0.1` would allows access
|
||||
The default settings are optimized for (local) development environments.
|
||||
You should therefore adjust the configuration for production setups, at least the following variables:
|
||||
|
||||
* `ALLOWED_HOSTS`: Add all URLs that lead to your NetBox instance, space separated. E.g. `ALLOWED_HOSTS=netbox.mycorp.com server042.mycorp.com 2a02:123::42 10.0.0.42 localhost ::1 127.0.0.1` (It's good advice to always allow localhost connections for easy debugging, i.e. `localhost ::1 127.0.0.1`.)
|
||||
* `ALLOWED_HOSTS`: Add all URLs that lead to your Netbox instance, space separated. E.g. `ALLOWED_HOSTS=netbox.mycorp.com server042.mycorp.com 2a02:123::42 10.0.0.42 localhost ::1 127.0.0.1` (It's good advice to always allow localhost connections for easy debugging, i.e. `localhost ::1 127.0.0.1`.)
|
||||
* `DB_*`: Use your own persistent database. Don't use the default passwords!
|
||||
* `EMAIL_*`: Use your own mailserver.
|
||||
* `MAX_PAGE_SIZE`: Use the recommended default of 1000.
|
||||
@ -82,9 +83,14 @@ You should therefore adjust the configuration for production setups, at least th
|
||||
|
||||
You may run this image in a cluster such as Docker Swarm, Kubernetes or OpenShift, but this is advanced level.
|
||||
|
||||
In this case, we encourage you to statically configure NetBox by starting from [NetBox's example config file][default-config], and mounting it into your container in the directory `/etc/netbox/config/` using the mechanism provided by your container platform (i.e. [Docker Swarm configs][swarm-config], [Kubernetes ConfigMap][k8s-config], [OpenShift ConfigMaps][openshift-config]).
|
||||
In this case, we encourage you to statically configure Netbox by starting from [Netbox's example config file][default-config], and mounting it into your container in the directory `/etc/netbox/config/` using the mechanism provided by your container platform (i.e. [Docker Swarm configs][swarm-config], [Kubernetes ConfigMap][k8s-config], [OpenShift ConfigMaps][openshift-config]).
|
||||
|
||||
But if you rather continue to configure your application through environment variables, you may continue to use [the built-in configuration file][docker-config].
|
||||
But if you rather continue to configure your application through environment variables, you may continue to use [the built-in configuration file][
|
||||
|
||||
|
||||
|
||||
|
||||
].
|
||||
We discourage storing secrets in environment variables, as environment variable are passed on to all sub-processes and may leak easily into other systems, e.g. error collecting tools that often collect all environment variables whenever an error occurs.
|
||||
|
||||
Therefore we *strongly advise* to make use of the secrets mechanism provided by your container platform (i.e. [Docker Swarm secrets][swarm-secrets], [Kubernetes secrets][k8s-secrets], [OpenShift secrets][openshift-secrets]).
|
||||
@ -99,11 +105,11 @@ If a secret is defined by an environment variable and in the respective file at
|
||||
* `NAPALM_PASSWORD`: `/run/secrets/napalm_password`
|
||||
* `REDIS_PASSWORD`: `/run/secrets/redis_password`
|
||||
|
||||
Please also consider [the advice about running NetBox in production](#production) above!
|
||||
Please also consider [the advice about running Netbox in production](#production) above!
|
||||
|
||||
[docker-config]: https://github.com/ninech/netbox-docker/blob/master/docker/configuration.docker.py
|
||||
[docker-config]: https://github.com/netbox-community/netbox-docker/blob/master/configuration/configuration.py
|
||||
[default-config]: https://github.com/digitalocean/netbox/blob/develop/netbox/netbox/configuration.example.py
|
||||
[entrypoint]: https://github.com/ninech/netbox-docker/blob/master/docker/docker-entrypoint.sh
|
||||
[entrypoint]: https://github.com/netbox-community/netbox-docker/blob/master/docker/docker-entrypoint.sh
|
||||
[swarm-config]: https://docs.docker.com/engine/swarm/configs/
|
||||
[swarm-secrets]: https://docs.docker.com/engine/swarm/secrets/
|
||||
[openshift-config]: https://docs.openshift.org/latest/dev_guide/configmaps.html
|
||||
@ -113,9 +119,9 @@ Please also consider [the advice about running NetBox in production](#production
|
||||
|
||||
### NAPALM Configuration
|
||||
|
||||
Since v2.1.0 NAPALM has been tightly integrated into NetBox.
|
||||
NAPALM allows NetBox to fetch live data from devices and return it to a requester via its REST API.
|
||||
To learn more about what NAPALM is and how it works, please see the documentation from the [libary itself][napalm-doc] or the documentation from [NetBox][netbox-napalm-doc] on how it is integrated.
|
||||
Since v2.1.0 NAPALM has been tightly integrated into Netbox.
|
||||
NAPALM allows Netbox to fetch live data from devices and return it to a requester via its REST API.
|
||||
To learn more about what NAPALM is and how it works, please see the documentation from the [libary itself][napalm-doc] or the documentation from [Netbox][netbox-napalm-doc] on how it is integrated.
|
||||
|
||||
To enable this functionality, simply complete the following lines in `netbox.env` (or appropriate secrets mechanism) :
|
||||
|
||||
@ -128,11 +134,24 @@ However, if you don't need this functionality, leave these blank.
|
||||
[napalm-doc]: http://napalm.readthedocs.io/en/latest/index.html
|
||||
[netbox-napalm-doc]: https://netbox.readthedocs.io/en/latest/configuration/optional-settings/#napalm_username
|
||||
|
||||
### Customizable Reporting
|
||||
|
||||
Netbox includes [customized reporting][netbox-reports-doc] that allows the user to write Python code and determine the validity of the data within Netbox.
|
||||
The `REPORTS_ROOT` variable is setup as a mapped directory within this Docker container to `/reports/` and includes the example directly from the documentation for `devices.py`.
|
||||
However, it has been renamed to `devices.py.example` which prevents Netbox from recognizing it as a valid report.
|
||||
This was done to avoid unnessary issues from being opened when the default does not work for someone's expectations.
|
||||
|
||||
To re-enable this default report, simply rename `devices.py.example` to `devices.py` and browse within the WebUI to `/extras/reports/`.
|
||||
You can also dynamically add any other report to this same directory and Netbox will be able to see it without restarting the container.
|
||||
|
||||
[netbox-reports-doc]: https://netbox.readthedocs.io/en/stable/additional-features/reports/
|
||||
|
||||
### Custom Initialization Code (e.g. Automatically Setting Up Custom Fields)
|
||||
|
||||
When using `docker-compose`, all the python scripts present in `/opt/netbox/startup_scripts` will automatically be executed after the application boots in the context of `./manage.py`.
|
||||
The execution of the startup scripts can be prevented by setting the environment variable `SKIP_STARTUP_SCRIPTS` to `true`, e.g. in the file `env/netbox.env`.
|
||||
|
||||
That mechanism can be used for many things, e.g. to create NetBox custom fields:
|
||||
That mechanism can be used for many things, e.g. to create Netbox custom fields:
|
||||
|
||||
```python
|
||||
# docker/startup_scripts/load_custom_fields.py
|
||||
@ -158,7 +177,7 @@ if created:
|
||||
|
||||
#### Initializers
|
||||
|
||||
Initializers are built-in startup scripts for defining NetBox custom fields, groups and users.
|
||||
Initializers are built-in startup scripts for defining Netbox custom fields, groups, users and many other resources.
|
||||
All you need to do is to mount you own `initializers` folder ([see `docker-compose.yml`][netbox-docker-compose]).
|
||||
Look at the [`initializers` folder][netbox-docker-initializers] to learn how the files must look like.
|
||||
|
||||
@ -182,17 +201,26 @@ text_field:
|
||||
- virtualization.models.VirtualMachine
|
||||
```
|
||||
|
||||
[netbox-docker-initializers]: https://github.com/ninech/netbox-docker/tree/master/initializers
|
||||
[netbox-docker-compose]: https://github.com/ninech/netbox-docker/blob/master/docker-compose.yml
|
||||
[netbox-docker-initializers]: https://github.com/netbox-community/netbox-docker/tree/master/initializers
|
||||
[netbox-docker-compose]: https://github.com/netbox-community/netbox-docker/blob/master/docker-compose.yml
|
||||
|
||||
##### Available Groups for User/Group initializers
|
||||
|
||||
To get an up-to-date list about all the available permissions, run the following command.
|
||||
|
||||
```bash
|
||||
# Make sure the 'netbox' container is already running! If unsure, run `docker-compose up -d`
|
||||
echo "from django.contrib.auth.models import Permission\nfor p in Permission.objects.all():\n print(p.codename);" | docker-compose exec -T netbox ./manage.py shell
|
||||
```
|
||||
|
||||
#### Custom Docker Image
|
||||
|
||||
You can also build your own NetBox Docker image containing your own startup scripts, custom fields, users and groups
|
||||
You can also build your own Netbox Docker image containing your own startup scripts, custom fields, users and groups
|
||||
like this:
|
||||
|
||||
```
|
||||
ARG VERSION=latest
|
||||
FROM ninech/netbox:$VERSION
|
||||
FROM netboxcommunity/netbox:$VERSION
|
||||
|
||||
COPY startup_scripts/ /opt/netbox/startup_scripts/
|
||||
COPY initializers/ /opt/netbox/initializers/
|
||||
@ -200,10 +228,10 @@ COPY initializers/ /opt/netbox/initializers/
|
||||
|
||||
## Netbox Version
|
||||
|
||||
The `docker-compose.yml` file is prepared to run a specific version of NetBox.
|
||||
The `docker-compose.yml` file is prepared to run a specific version of Netbox.
|
||||
To use this feature, set the environment-variable `VERSION` before launching `docker-compose`, as shown below.
|
||||
`VERSION` may be set to the name of
|
||||
[any tag of the `ninech/netbox` Docker image on Docker Hub][netbox-dockerhub].
|
||||
[any tag of the `netboxcommunity/netbox` Docker image on Docker Hub][netbox-dockerhub].
|
||||
|
||||
```
|
||||
$ export VERSION=v2.2.6
|
||||
@ -211,7 +239,7 @@ $ docker-compose pull netbox
|
||||
$ docker-compose up -d
|
||||
```
|
||||
|
||||
You can also build a specific version of the NetBox image. This time, `VERSION` indicates any valid
|
||||
You can also build a specific version of the Netbox image. This time, `VERSION` indicates any valid
|
||||
[Git Reference][git-ref] declared on [the 'digitalocean/netbox' Github repository][netbox-github].
|
||||
Most commonly you will specify a tag or branch name.
|
||||
|
||||
@ -238,7 +266,7 @@ Custom values can be injected using environment variables, similar to the main c
|
||||
This section is a collection of some common issues and how to resolve them.
|
||||
If your issue is not here, look through [the existing issues][issues] and eventually create a new issue.
|
||||
|
||||
[issues]: (https://github.com/ninech/netbox-docker/issues)
|
||||
[issues]: (https://github.com/netbox-community/netbox-docker/issues)
|
||||
|
||||
### Docker Compose basics
|
||||
|
||||
@ -247,7 +275,7 @@ If your issue is not here, look through [the existing issues][issues] and eventu
|
||||
Running `docker-compose logs -f netbox` will just show the logs for netbox.
|
||||
* You can stop everything using `docker-compose stop`.
|
||||
* You can clean up everything using `docker-compose down -v --remove-orphans`. **This will also remove any related data.**
|
||||
* You can enter the shell of the running NetBox container using `docker-compose exec netbox /bin/bash`. Now you have access to `./manage.py`, e.g. to reset a password.
|
||||
* You can enter the shell of the running Netbox container using `docker-compose exec netbox /bin/bash`. Now you have access to `./manage.py`, e.g. to reset a password.
|
||||
* To access the database run `docker-compose exec postgres sh -c 'psql -U $POSTGRES_USER $POSTGRES_DB'`
|
||||
* To create a database backup run `docker-compose exec postgres sh -c 'pg_dump -cU $POSTGRES_USER $POSTGRES_DB' | gzip > db_dump.sql.gz`
|
||||
* To restore that database backup run `gunzip -c db_dump.sql.gz | docker exec -i $(docker-compose ps -q postgres) sh -c 'psql -U $POSTGRES_USER $POSTGRES_DB'`.
|
||||
@ -280,7 +308,7 @@ $ docker-compose logs -f nginx
|
||||
|
||||
### Getting a "Bad Request (400)"
|
||||
|
||||
> When connecting to the NetBox instance, I get a "Bad Request (400)" error.
|
||||
> When connecting to the Netbox instance, I get a "Bad Request (400)" error.
|
||||
|
||||
This usually happens when the `ALLOWED_HOSTS` variable is not set correctly.
|
||||
|
||||
@ -313,10 +341,10 @@ Then make sure that the `redis` container and at least one `netbox-worker` are r
|
||||
# check the container status
|
||||
$ docker-compose ps
|
||||
|
||||
Name Command State Ports
|
||||
Name Command State Ports
|
||||
--------------------------------------------------------------------------------------------------------
|
||||
netbox-docker_netbox-worker_1 /opt/netbox/docker-entrypo ... Up
|
||||
netbox-docker_netbox_1 /opt/netbox/docker-entrypo ... Up
|
||||
netbox-docker_netbox-worker_1 /opt/netbox/docker-entrypo ... Up
|
||||
netbox-docker_netbox_1 /opt/netbox/docker-entrypo ... Up
|
||||
netbox-docker_nginx_1 nginx -c /etc/netbox-nginx ... Up 80/tcp, 0.0.0.0:32776->8080/tcp
|
||||
netbox-docker_postgres_1 docker-entrypoint.sh postgres Up 5432/tcp
|
||||
netbox-docker_redis_1 docker-entrypoint.sh redis ... Up 6379/tcp
|
||||
@ -346,54 +374,48 @@ docker-compose run --rm -T redis sh -c 'redis-cli -h redis -a $REDIS_PASSWORD mo
|
||||
|
||||
If you don't see anything happening after you triggered a webhook, double-check the configuration of the `netbox` and the `netbox-worker` containers and also check the configuration of your webhook in the admin interface of Netbox.
|
||||
|
||||
### Breaking Changes
|
||||
## Breaking Changes
|
||||
|
||||
From time to time it might become necessary to re-engineer the structure of this setup.
|
||||
Things like the `docker-compose.yml` file or your Kubernetes or OpenShift configurations have to be adjusted as a consequence.
|
||||
Since April 2018 each image built from this repo contains a `NETBOX_DOCKER_PROJECT_VERSION` label.
|
||||
You can check the label of your local image by running `docker inspect ninech/netbox:v2.3.1 --format "{{json .ContainerConfig.Labels}}"`.
|
||||
You can check the label of your local image by running `docker inspect netboxcommunity/netbox:v2.3.1 --format "{{json .ContainerConfig.Labels}}"`.
|
||||
Compare the version with the list below to check whether a breaking change was introduced with that version.
|
||||
|
||||
The following is a list of breaking changes of the `netbox-docker` project:
|
||||
|
||||
* 0.5.0: Alpine was updated to 3.8
|
||||
* 0.12.0: A new flag `REDIS_SSL=false` was added to the `env/netbox.env` file. [#129][129]
|
||||
* 0.11.0: The docker-compose file now marks volumes as shared (`:z`). This should prevent SELinux problems [#131][131]
|
||||
* 0.9.0: Upgrade to at least 2.1.5
|
||||
* 0.8.0: Alpine linux was upgraded to 3.9 [#126][126]
|
||||
* 0.7.0: The value of the `MAX_PAGE_SIZE` environment variable was changed to `1000`, which is the default of Netbox.
|
||||
* 0.6.0: The naming of the default startup_scripts were changed.
|
||||
If you overwrite them, you may need to adjust these scripts.
|
||||
* 0.5.0: Alpine was updated to 3.8, `*.env` moved to `/env` folder
|
||||
* 0.4.0: In order to use Netbox webhooks you need to add Redis and a netbox-worker to your docker-compose.yml.
|
||||
* 0.3.0: Field `filterable: <boolean` was replaced with field `filter_logic: loose/exact/disabled`. It will default to `CF_FILTER_LOOSE=loose` when not defined.
|
||||
* 0.2.0: Re-organized paths: `/etc/netbox -> /etc/netbox/config` and `/etc/reports -> /etc/netbox/reports`. Fixes [#54](https://github.com/ninech/netbox-docker/issues/54).
|
||||
* 0.2.0: Re-organized paths: `/etc/netbox -> /etc/netbox/config` and `/etc/reports -> /etc/netbox/reports`. Fixes [#54][54].
|
||||
* 0.1.0: Introduction of the `NETBOX_DOCKER_PROJECT_VERSION`. (Not a breaking change per se.)
|
||||
|
||||
[54]: https://github.com/netbox-community/netbox-docker/issues/54
|
||||
[126]: https://github.com/netbox-community/netbox-docker/pull/126
|
||||
[131]: https://github.com/netbox-community/netbox-docker/pull/131
|
||||
[129]: https://github.com/netbox-community/netbox-docker/pull/129
|
||||
|
||||
## Rebuilding & Publishing images
|
||||
|
||||
`./build.sh` is used to rebuild the Docker image:
|
||||
|
||||
```
|
||||
$ ./build.sh --help
|
||||
Usage: ./build.sh <branch> [--push]
|
||||
branch The branch or tag to build. Required.
|
||||
--push Pushes built Docker image to docker hub.
|
||||
|
||||
You can use the following ENV variables to customize the build:
|
||||
BRANCH The branch to build.
|
||||
Also used for tagging the image.
|
||||
DOCKER_REPO The Docker registry (i.e. hub.docker.com/r/DOCKER_REPO/netbox)
|
||||
Also used for tagging the image.
|
||||
Default: ninech
|
||||
SRC_REPO Which fork of netbox to use (i.e. github.com/<SRC_REPO>/netbox).
|
||||
Default: digitalocean
|
||||
URL Where to fetch the package from.
|
||||
Must be a tar.gz file of the source code.
|
||||
Default: https://github.com/${SRC_REPO}/netbox/archive/$BRANCH.tar.gz
|
||||
```
|
||||
`./build.sh` can be used to rebuild the Docker image. See `./build.sh --help` for more information.
|
||||
|
||||
### Publishing Docker Images
|
||||
|
||||
New Docker Images are built and published every 24h by using travis:
|
||||
New Docker images are built and published every 24h on the [Docker Build Infrastructure][docker-build-infra].
|
||||
`DOCKER_HUB.md` contains more information about the build infrastructure.
|
||||
|
||||
[][travis]
|
||||
[docker-build-infra]: https://hub.docker.com/r/netboxcommunity/netbox/builds/
|
||||
|
||||
## Tests
|
||||
|
||||
To run the tests coming with NetBox, use the `docker-compose.yml` file as such:
|
||||
To run the tests coming with Netbox, use the `docker-compose.yml` file as such:
|
||||
|
||||
```
|
||||
$ docker-compose run netbox ./manage.py test
|
||||
@ -401,6 +423,6 @@ $ docker-compose run netbox ./manage.py test
|
||||
|
||||
## About
|
||||
|
||||
This repository is currently maintained and funded by [nine](https://nine.ch), your cloud navigator.
|
||||
This repository is currently maintained and funded by [nxt][nxt].
|
||||
|
||||
[](https://www.nine.ch)
|
||||
[nxt]: https://nxt.engineering/en/
|
||||
|
108
build-all.sh
108
build-all.sh
@ -12,9 +12,9 @@ BUILDS=("${BUILD:-"${ALL_BUILDS[@]}"}")
|
||||
|
||||
echo "⚙️ Configured builds: ${BUILDS[*]}"
|
||||
|
||||
VARIANTS=("" "ldap")
|
||||
VARIANTS=("main" "ldap")
|
||||
|
||||
if [ ! -z "${DEBUG}" ]; then
|
||||
if [ -n "${DEBUG}" ]; then
|
||||
export DEBUG
|
||||
fi
|
||||
|
||||
@ -22,67 +22,63 @@ ERROR=0
|
||||
|
||||
# Don't build if not on `master` and don't build if on a pull request,
|
||||
# but build when DEBUG is not empty
|
||||
if [ ! -z "${DEBUG}" ] || \
|
||||
( [ "$TRAVIS_BRANCH" = "master" ] && [ "$TRAVIS_PULL_REQUEST" = "false" ] ); then
|
||||
for VARIANT in "${VARIANTS[@]}"; do
|
||||
export VARIANT
|
||||
for VARIANT in "${VARIANTS[@]}"; do
|
||||
export VARIANT
|
||||
|
||||
# Checking which VARIANT to build
|
||||
if [ -z "$VARIANT" ]; then
|
||||
DOCKERFILE="Dockerfile"
|
||||
else
|
||||
DOCKERFILE="Dockerfile.${VARIANT}"
|
||||
# Checking which VARIANT to build
|
||||
if [ "${VARIANT}" == "main" ]; then
|
||||
DOCKERFILE="${DOCKERFILE_PATH-Dockerfile}"
|
||||
else
|
||||
DOCKERFILE="${DOCKERFILE_PATH-Dockerfile}.${VARIANT}"
|
||||
|
||||
# Fail fast
|
||||
if [ ! -f "${DOCKERFILE}" ]; then
|
||||
echo "🚨 The Dockerfile '${DOCKERFILE}' for variant '${VARIANT}' doesn't exist."
|
||||
ERROR=1
|
||||
# Fail fast
|
||||
if [ ! -f "${DOCKERFILE}" ]; then
|
||||
echo "🚨 The Dockerfile '${DOCKERFILE}' for variant '${VARIANT}' doesn't exist."
|
||||
ERROR=1
|
||||
|
||||
if [ -z "$DEBUG" ]; then
|
||||
continue
|
||||
else
|
||||
echo "⚠️ Would skip this, but DEBUG is enabled."
|
||||
fi
|
||||
if [ -z "$DEBUG" ]; then
|
||||
continue
|
||||
else
|
||||
echo "⚠️ Would skip this, but DEBUG is enabled."
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
for BUILD in "${BUILDS[@]}"; do
|
||||
echo "🛠 Building '$BUILD' from '$DOCKERFILE'"
|
||||
case $BUILD in
|
||||
release)
|
||||
# build the latest release
|
||||
# shellcheck disable=SC2068
|
||||
./build-latest.sh $@ || ERROR=1
|
||||
;;
|
||||
prerelease)
|
||||
# build the latest pre-release
|
||||
# shellcheck disable=SC2068
|
||||
PRERELEASE=true ./build-latest.sh $@ || ERROR=1
|
||||
;;
|
||||
branches)
|
||||
# build all branches
|
||||
# shellcheck disable=SC2068
|
||||
./build-branches.sh $@ || ERROR=1
|
||||
;;
|
||||
special)
|
||||
# special build
|
||||
# shellcheck disable=SC2068
|
||||
#SRC_ORG=lampwins TAG=webhooks-backend ./build.sh "feature/webhooks-backend" $@ || ERROR=1
|
||||
;;
|
||||
*)
|
||||
echo "🚨 Unrecognized build '$BUILD'."
|
||||
for BUILD in "${BUILDS[@]}"; do
|
||||
echo "🛠 Building '$BUILD' from '$DOCKERFILE'"
|
||||
case $BUILD in
|
||||
release)
|
||||
# build the latest release
|
||||
# shellcheck disable=SC2068
|
||||
./build-latest.sh $@ || ERROR=1
|
||||
;;
|
||||
prerelease)
|
||||
# build the latest pre-release
|
||||
# shellcheck disable=SC2068
|
||||
PRERELEASE=true ./build-latest.sh $@ || ERROR=1
|
||||
;;
|
||||
branches)
|
||||
# build all branches
|
||||
# shellcheck disable=SC2068
|
||||
./build-branches.sh $@ || ERROR=1
|
||||
;;
|
||||
special)
|
||||
# special build
|
||||
# shellcheck disable=SC2068
|
||||
#SRC_ORG=lampwins TAG=webhooks-backend ./build.sh "feature/webhooks-backend" $@ || ERROR=1
|
||||
echo "✅ No special builds today."
|
||||
;;
|
||||
*)
|
||||
echo "🚨 Unrecognized build '$BUILD'."
|
||||
|
||||
if [ -z "$DEBUG" ]; then
|
||||
exit 1
|
||||
else
|
||||
echo "⚠️ Would exit here with code '1', but DEBUG is enabled."
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
done
|
||||
if [ -z "$DEBUG" ]; then
|
||||
exit 1
|
||||
else
|
||||
echo "⚠️ Would exit here with code '1', but DEBUG is enabled."
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
done
|
||||
else
|
||||
echo "❎ Not building anything."
|
||||
fi
|
||||
done
|
||||
|
||||
exit $ERROR
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
echo "▶️ $0 $*"
|
||||
|
||||
if [ ! -z "${GITHUB_OAUTH_CLIENT_ID}" ] && [ ! -z "${GITHUB_OAUTH_CLIENT_SECRET}" ]; then
|
||||
if [ -n "${GITHUB_OAUTH_CLIENT_ID}" ] && [ -n "${GITHUB_OAUTH_CLIENT_SECRET}" ]; then
|
||||
echo "🗝 Performing authenticated Github API calls."
|
||||
GITHUB_OAUTH_PARAMS="client_id=${GITHUB_OAUTH_CLIENT_ID}&client_secret=${GITHUB_OAUTH_CLIENT_SECRET}"
|
||||
else
|
||||
@ -11,7 +11,7 @@ else
|
||||
GITHUB_OAUTH_PARAMS=""
|
||||
fi
|
||||
|
||||
ORIGINAL_GITHUB_REPO="digitalocean/netbox"
|
||||
ORIGINAL_GITHUB_REPO="${SRC_ORG-digitalocean}/${SRC_REPO-netbox}"
|
||||
GITHUB_REPO="${GITHUB_REPO-$ORIGINAL_GITHUB_REPO}"
|
||||
URL_RELEASES="https://api.github.com/repos/${GITHUB_REPO}/branches?${GITHUB_OAUTH_PARAMS}"
|
||||
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
echo "▶️ $0 $*"
|
||||
|
||||
if [ ! -z "${GITHUB_OAUTH_CLIENT_ID}" ] && [ ! -z "${GITHUB_OAUTH_CLIENT_SECRET}" ]; then
|
||||
if [ -n "${GITHUB_OAUTH_CLIENT_ID}" ] && [ -n "${GITHUB_OAUTH_CLIENT_SECRET}" ]; then
|
||||
echo "🗝 Performing authenticated Github API calls."
|
||||
GITHUB_OAUTH_PARAMS="client_id=${GITHUB_OAUTH_CLIENT_ID}&client_secret=${GITHUB_OAUTH_CLIENT_SECRET}"
|
||||
else
|
||||
@ -47,7 +47,7 @@ if [ "${PRERELEASE}" == "true" ]; then
|
||||
fi
|
||||
|
||||
# Check if that version is not already available on docker hub:
|
||||
ORIGINAL_DOCKERHUB_REPO="ninech/netbox"
|
||||
ORIGINAL_DOCKERHUB_REPO="${DOCKER_ORG-netboxcommunity}/${DOCKER_REPO-netbox}"
|
||||
DOCKERHUB_REPO="${DOCKERHUB_REPO-$ORIGINAL_DOCKERHUB_REPO}"
|
||||
URL_DOCKERHUB_TOKEN="https://auth.docker.io/token?service=registry.docker.io&scope=repository:${DOCKERHUB_REPO}:pull"
|
||||
BEARER_TOKEN="$($CURL "${URL_DOCKERHUB_TOKEN}" | jq -r .token)"
|
||||
@ -55,7 +55,7 @@ BEARER_TOKEN="$($CURL "${URL_DOCKERHUB_TOKEN}" | jq -r .token)"
|
||||
URL_DOCKERHUB_TAG="https://registry.hub.docker.com/v2/${DOCKERHUB_REPO}/tags/list"
|
||||
AUTHORIZATION_HEADER="Authorization: Bearer ${BEARER_TOKEN}"
|
||||
|
||||
if [ -z "$VARIANT" ]; then
|
||||
if [ -z "$VARIANT" ] || [ "$VARIANT" == "main" ]; then
|
||||
DOCKER_TAG="${VERSION}"
|
||||
else
|
||||
DOCKER_TAG="${VERSION}-${VARIANT}"
|
||||
@ -63,7 +63,11 @@ fi
|
||||
|
||||
ALREADY_BUILT="$($CURL -H "${AUTHORIZATION_HEADER}" "${URL_DOCKERHUB_TAG}" | jq -e ".tags | any(.==\"${DOCKER_TAG}\")")"
|
||||
|
||||
if [ "$ALREADY_BUILT" == "false" ]; then
|
||||
if [ -n "$DEBUG" ] || [ "$ALREADY_BUILT" == "false" ]; then
|
||||
if [ -n "$DEBUG" ]; then
|
||||
echo "⚠️ Would not build, because ${DOCKER_TAG} already exists on https://hub.docker.com/r/${DOCKERHUB_REPO}, but DEBUG is enabled."
|
||||
fi
|
||||
|
||||
# shellcheck disable=SC2068
|
||||
./build.sh "${VERSION}" $@
|
||||
exit $?
|
||||
|
90
build.sh
90
build.sh
@ -6,9 +6,10 @@ echo "▶️ $0 $*"
|
||||
set -e
|
||||
|
||||
if [ "${1}x" == "x" ] || [ "${1}" == "--help" ] || [ "${1}" == "-h" ]; then
|
||||
echo "Usage: ${0} <branch> [--push]"
|
||||
echo " branch The branch or tag to build. Required."
|
||||
echo " --push Pushes built Docker image to docker hub."
|
||||
echo "Usage: ${0} <branch> [--push|--push-only]"
|
||||
echo " branch The branch or tag to build. Required."
|
||||
echo " --push Pushes built the Docker image to the registry."
|
||||
echo " --push-only Does not build. Only pushes the Docker image to the registry."
|
||||
echo ""
|
||||
echo "You can use the following ENV variables to customize the build:"
|
||||
echo " DEBUG If defined, the script does not stop when certain checks are unsatisfied."
|
||||
@ -26,13 +27,16 @@ if [ "${1}x" == "x" ] || [ "${1}" == "--help" ] || [ "${1}" == "-h" ]; then
|
||||
echo " Else: same as <BRANCH>"
|
||||
echo " DOCKER_ORG The Docker registry (i.e. hub.docker.com/r/<DOCKER_ORG>/<DOCKER_REPO>) "
|
||||
echo " Also used for tagging the image."
|
||||
echo " Default: ninech"
|
||||
echo " Default: netboxcommunity"
|
||||
echo " DOCKER_REPO The Docker registry (i.e. hub.docker.com/r/<DOCKER_ORG>/<DOCKER_REPO>) "
|
||||
echo " Also used for tagging the image."
|
||||
echo " Default: netbox"
|
||||
echo " DOCKER_TAG The name of the tag which is applied to the image."
|
||||
echo " Useful for pushing into another registry than hub.docker.com."
|
||||
echo " Default: <DOCKER_ORG>/<DOCKER_REPO>:<BRANCH>"
|
||||
echo " DOCKER_SHORT_TAG The name of the short tag which is applied to the image."
|
||||
echo " This is used to tag all patch releases to their containing version e.g. v2.5.1 -> v2.5"
|
||||
echo " Default: <DOCKER_ORG>/<DOCKER_REPO>:\$MAJOR.\$MINOR"
|
||||
echo " SRC_ORG Which fork of netbox to use (i.e. github.com/<SRC_ORG>/<SRC_REPO>)."
|
||||
echo " Default: digitalocean"
|
||||
echo " SRC_REPO The name of the netbox for to use (i.e. github.com/<SRC_ORG>/<SRC_REPO>)."
|
||||
@ -44,13 +48,14 @@ if [ "${1}x" == "x" ] || [ "${1}" == "--help" ] || [ "${1}" == "-h" ]; then
|
||||
echo " The value will be used as a suffix to the \$TAG and for the Dockerfile"
|
||||
echo " selection. The TAG being build must exist for the base variant and"
|
||||
echo " corresponding Dockerfile must start with the following lines:"
|
||||
echo " ARG DOCKER_ORG=ninech"
|
||||
echo " ARG DOCKER_ORG=netboxcommunity"
|
||||
echo " ARG DOCKER_REPO=netbox"
|
||||
echo " ARG FROM_TAG=latest"
|
||||
echo " FROM \$DOCKER_ORG/\$DOCKER_REPO:\$FROM_TAG"
|
||||
echo " Example: VARIANT=ldap will result in the tag 'latest-ldap' and the"
|
||||
echo " Dockerfile 'Dockerfile.ldap' being used."
|
||||
echo " Default: empty"
|
||||
echo " Dockerfile './Dockerfile.ldap' being used."
|
||||
echo " Exception: VARIANT=main will use the './Dockerfile' Dockerfile"
|
||||
echo " Default: main"
|
||||
echo " HTTP_PROXY The proxy to use for http requests."
|
||||
echo " Example: http://proxy.domain.tld:3128"
|
||||
echo " Default: empty"
|
||||
@ -81,8 +86,27 @@ SRC_REPO="${SRC_REPO-netbox}"
|
||||
BRANCH="${1}"
|
||||
URL="${URL-https://github.com/${SRC_ORG}/${SRC_REPO}/archive/$BRANCH.tar.gz}"
|
||||
|
||||
# Checking which VARIANT to build
|
||||
VARIANT="${VARIANT-main}"
|
||||
if [ "$VARIANT" == "main" ]; then
|
||||
DOCKERFILE="Dockerfile"
|
||||
else
|
||||
DOCKERFILE="Dockerfile.${VARIANT}"
|
||||
fi
|
||||
|
||||
# Fail fast
|
||||
if [ ! -f "${DOCKERFILE}" ]; then
|
||||
echo "🚨 The Dockerfile ${DOCKERFILE} for variant '${VARIANT}' doesn't exist."
|
||||
|
||||
if [ -z "$DEBUG" ]; then
|
||||
exit 1
|
||||
else
|
||||
echo "⚠️ Would exit here with code '1', but DEBUG is enabled."
|
||||
fi
|
||||
fi
|
||||
|
||||
# variables for tagging the docker image
|
||||
DOCKER_ORG="${DOCKER_ORG-ninech}"
|
||||
DOCKER_ORG="${DOCKER_ORG-netboxcommunity}"
|
||||
DOCKER_REPO="${DOCKER_REPO-netbox}"
|
||||
case "${BRANCH}" in
|
||||
master)
|
||||
@ -92,24 +116,20 @@ case "${BRANCH}" in
|
||||
*)
|
||||
TAG="${TAG-$BRANCH}";;
|
||||
esac
|
||||
|
||||
DOCKER_TAG="${DOCKER_TAG-${DOCKER_ORG}/${DOCKER_REPO}:${TAG}}"
|
||||
|
||||
# Checking which VARIANT to build
|
||||
if [ -z "$VARIANT" ]; then
|
||||
DOCKERFILE="Dockerfile"
|
||||
else
|
||||
DOCKERFILE="Dockerfile.${VARIANT}"
|
||||
if [ "$VARIANT" != "main" ]; then
|
||||
DOCKER_TAG="${DOCKER_TAG}-${VARIANT}"
|
||||
fi
|
||||
|
||||
# Fail fast
|
||||
if [ ! -f "${DOCKERFILE}" ]; then
|
||||
echo "🚨 The Dockerfile ${DOCKERFILE} for variant '${VARIANT}' doesn't exist."
|
||||
if [[ "${TAG}" =~ ^v([0-9]+)\.([0-9]+)\.[0-9]+$ ]]; then
|
||||
MAJOR=${BASH_REMATCH[1]}
|
||||
MINOR=${BASH_REMATCH[2]}
|
||||
|
||||
if [ -z "$DEBUG" ]; then
|
||||
exit 1
|
||||
else
|
||||
echo "⚠️ Would exit here with code '1', but DEBUG is enabled."
|
||||
fi
|
||||
DOCKER_SHORT_TAG="${DOCKER_SHORT_TAG-${DOCKER_ORG}/${DOCKER_REPO}:v${MAJOR}.${MINOR}}"
|
||||
|
||||
if [ "$VARIANT" != "main" ]; then
|
||||
DOCKER_SHORT_TAG="${DOCKER_SHORT_TAG}-${VARIANT}"
|
||||
fi
|
||||
fi
|
||||
|
||||
@ -117,8 +137,8 @@ DOCKER_OPTS=("${DOCKER_OPTS[@]}")
|
||||
|
||||
# caching is only ok for version tags
|
||||
case "${TAG}" in
|
||||
v*) ;;
|
||||
*) DOCKER_OPTS+=( "--no-cache" ) ;;
|
||||
v*) ;;
|
||||
*) DOCKER_OPTS+=( "--no-cache" ) ;;
|
||||
esac
|
||||
|
||||
DOCKER_OPTS+=( "--pull" )
|
||||
@ -152,12 +172,26 @@ else
|
||||
DOCKER_CMD="echo docker"
|
||||
fi
|
||||
|
||||
echo "🐳 Building the Docker image '${DOCKER_TAG}' from the url '${URL}'."
|
||||
$DOCKER_CMD build -t "${DOCKER_TAG}" "${DOCKER_BUILD_ARGS[@]}" "${DOCKER_OPTS[@]}" -f "${DOCKERFILE}" .
|
||||
echo "✅ Finished building the Docker images '${DOCKER_TAG}'"
|
||||
if [ "${2}" != "--push-only" ] ; then
|
||||
echo "🐳 Building the Docker image '${DOCKER_TAG}' from the url '${URL}'."
|
||||
$DOCKER_CMD build -t "${DOCKER_TAG}" "${DOCKER_BUILD_ARGS[@]}" "${DOCKER_OPTS[@]}" -f "${DOCKERFILE}" .
|
||||
echo "✅ Finished building the Docker images '${DOCKER_TAG}'"
|
||||
|
||||
if [ "${2}" == "--push" ] ; then
|
||||
if [ -n "$DOCKER_SHORT_TAG" ]; then
|
||||
echo "🐳 Tagging image '${DOCKER_SHORT_TAG}'."
|
||||
$DOCKER_CMD tag "${DOCKER_TAG}" "${DOCKER_SHORT_TAG}"
|
||||
echo "✅ Tagged image '${DOCKER_SHORT_TAG}'"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "${2}" == "--push" ] || [ "${2}" == "--push-only" ] ; then
|
||||
echo "⏫ Pushing '${DOCKER_TAG}"
|
||||
$DOCKER_CMD push "${DOCKER_TAG}"
|
||||
echo "✅ Finished pushing the Docker image '${DOCKER_TAG}'."
|
||||
|
||||
if [ -n "$DOCKER_SHORT_TAG" ]; then
|
||||
echo "⏫ Pushing '${DOCKER_SHORT_TAG}'"
|
||||
$DOCKER_CMD push "${DOCKER_SHORT_TAG}"
|
||||
echo "✅ Finished pushing the Docker image '${DOCKER_SHORT_TAG}'."
|
||||
fi
|
||||
fi
|
||||
|
@ -105,10 +105,6 @@ LOGGING = {}
|
||||
# are permitted to access most data in NetBox (excluding secrets) but not make any changes.
|
||||
LOGIN_REQUIRED = os.environ.get('LOGIN_REQUIRED', 'False').lower() == 'true'
|
||||
|
||||
# Base URL path if accessing NetBox within a directory. For example, if installed at http://example.com/netbox/, set:
|
||||
# BASE_PATH = 'netbox/'
|
||||
BASE_PATH = os.environ.get('BASE_PATH', '')
|
||||
|
||||
# Setting this to True will display a "maintenance mode" banner at the top of every page.
|
||||
MAINTENANCE_MODE = os.environ.get('MAINTENANCE_MODE', 'False').lower() == 'true'
|
||||
|
||||
@ -150,6 +146,7 @@ REDIS = {
|
||||
'PASSWORD': os.environ.get('REDIS_PASSWORD', read_secret('redis_password')),
|
||||
'DATABASE': os.environ.get('REDIS_DATABASE', '0'),
|
||||
'DEFAULT_TIMEOUT': os.environ.get('REDIS_TIMEOUT', '300'),
|
||||
'SSL': os.environ.get('REDIS_SSL', 'False').lower() == 'true',
|
||||
}
|
||||
|
||||
# The file path where custom reports will be stored. A trailing slash is not needed. Note that the default value of
|
||||
|
@ -23,14 +23,18 @@ AUTH_LDAP_USER_DN_TEMPLATE = os.environ.get('AUTH_LDAP_USER_DN_TEMPLATE', None)
|
||||
# ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_NEVER)
|
||||
LDAP_IGNORE_CERT_ERRORS = os.environ.get('LDAP_IGNORE_CERT_ERRORS', 'False').lower() == 'true'
|
||||
|
||||
AUTH_LDAP_USER_SEARCH = LDAPSearch(os.environ.get('AUTH_LDAP_USER_SEARCH_BASEDN', ''),
|
||||
ldap.SCOPE_SUBTREE,
|
||||
"(sAMAccountName=%(user)s)")
|
||||
AUTH_LDAP_USER_SEARCH_BASEDN = os.environ.get('AUTH_LDAP_USER_SEARCH_BASEDN', '')
|
||||
AUTH_LDAP_USER_SEARCH_ATTR = os.environ.get('AUTH_LDAP_USER_SEARCH_ATTR', 'sAMAccountName')
|
||||
AUTH_LDAP_USER_SEARCH = LDAPSearch(AUTH_LDAP_USER_SEARCH_BASEDN,
|
||||
ldap.SCOPE_SUBTREE,
|
||||
"(" + AUTH_LDAP_USER_SEARCH_ATTR + "=%(user)s)")
|
||||
|
||||
# This search ought to return all groups to which the user belongs. django_auth_ldap uses this to determine group
|
||||
# heirarchy.
|
||||
AUTH_LDAP_GROUP_SEARCH = LDAPSearch(os.environ.get('AUTH_LDAP_GROUP_SEARCH_BASEDN', ''), ldap.SCOPE_SUBTREE,
|
||||
"(objectClass=group)")
|
||||
AUTH_LDAP_GROUP_SEARCH_BASEDN = os.environ.get('AUTH_LDAP_GROUP_SEARCH_BASEDN', '')
|
||||
AUTH_LDAP_GROUP_SEARCH_CLASS = os.environ.get('AUTH_LDAP_GROUP_SEARCH_CLASS', 'group')
|
||||
AUTH_LDAP_GROUP_SEARCH = LDAPSearch(AUTH_LDAP_GROUP_SEARCH_BASEDN, ldap.SCOPE_SUBTREE,
|
||||
"(objectClass=" + AUTH_LDAP_GROUP_SEARCH_CLASS + ")")
|
||||
AUTH_LDAP_GROUP_TYPE = GroupOfNamesType()
|
||||
|
||||
# Define a group required to login.
|
||||
|
@ -5,20 +5,20 @@ services:
|
||||
context: .
|
||||
args:
|
||||
- BRANCH=${VERSION-master}
|
||||
image: ninech/netbox:${VERSION-latest}
|
||||
image: netboxcommunity/netbox:${VERSION-latest}
|
||||
depends_on:
|
||||
- postgres
|
||||
- redis
|
||||
- netbox-worker
|
||||
env_file: env/netbox.env
|
||||
volumes:
|
||||
- ./startup_scripts:/opt/netbox/startup_scripts:ro
|
||||
- ./initializers:/opt/netbox/initializers:ro
|
||||
- ./configuration:/etc/netbox/config:ro
|
||||
- netbox-nginx-config:/etc/netbox-nginx/
|
||||
- netbox-static-files:/opt/netbox/netbox/static
|
||||
- netbox-media-files:/opt/netbox/netbox/media
|
||||
- netbox-report-files:/etc/netbox/reports:ro
|
||||
- ./startup_scripts:/opt/netbox/startup_scripts:z,ro
|
||||
- ./initializers:/opt/netbox/initializers:z,ro
|
||||
- ./configuration:/etc/netbox/config:z,ro
|
||||
- ./reports:/etc/netbox/reports:z,ro
|
||||
- netbox-nginx-config:/etc/netbox-nginx:z
|
||||
- netbox-static-files:/opt/netbox/netbox/static:z
|
||||
- netbox-media-files:/opt/netbox/netbox/media:z
|
||||
netbox-worker:
|
||||
<<: *netbox
|
||||
depends_on:
|
||||
|
@ -39,10 +39,14 @@ if not User.objects.filter(username='${SUPERUSER_NAME}'):
|
||||
Token.objects.create(user=u, key='${SUPERUSER_API_TOKEN}')
|
||||
END
|
||||
|
||||
for script in /opt/netbox/startup_scripts/*.py; do
|
||||
echo "⚙️ Executing '$script'"
|
||||
./manage.py shell --interface python < "${script}"
|
||||
done
|
||||
if [ "$SKIP_STARTUP_SCRIPTS" == "true" ]; then
|
||||
echo "☇ Skipping startup scripts"
|
||||
else
|
||||
for script in /opt/netbox/startup_scripts/*.py; do
|
||||
echo "⚙️ Executing '$script'"
|
||||
./manage.py shell --interface python < "${script}"
|
||||
done
|
||||
fi
|
||||
|
||||
# copy static files
|
||||
./manage.py collectstatic --no-input
|
||||
|
3
env/netbox.env
vendored
3
env/netbox.env
vendored
@ -12,9 +12,10 @@ MEDIA_ROOT=/opt/netbox/netbox/media
|
||||
NAPALM_USERNAME=
|
||||
NAPALM_PASSWORD=
|
||||
NAPALM_TIMEOUT=10
|
||||
MAX_PAGE_SIZE=0
|
||||
MAX_PAGE_SIZE=1000
|
||||
REDIS_HOST=redis
|
||||
REDIS_PASSWORD=H733Kdjndks81
|
||||
REDIS_SSL=false
|
||||
SECRET_KEY=r8OwDznj!!dci#P9ghmRfdu1Ysxm0AiPeDCQhKE+N_rClfWNj
|
||||
SUPERUSER_NAME=admin
|
||||
SUPERUSER_EMAIL=admin@example.com
|
||||
|
5
hooks/build
Executable file
5
hooks/build
Executable file
@ -0,0 +1,5 @@
|
||||
#!/bin/bash
|
||||
|
||||
. hooks/common
|
||||
|
||||
run_build
|
82
hooks/common
Executable file
82
hooks/common
Executable file
@ -0,0 +1,82 @@
|
||||
#!/bin/bash
|
||||
|
||||
ensure_jq() {
|
||||
echo "🛠🛠🛠 Installing JQ via apt-get"
|
||||
[ -x "$(command -v jq)" ] || ( apt-get update && apt-get install -y jq )
|
||||
}
|
||||
|
||||
ensure_dockerfile_present() {
|
||||
if [ "${VARIANT}" == "main" ]; then
|
||||
DOCKERFILE="Dockerfile"
|
||||
else
|
||||
DOCKERFILE="Dockerfile.${VARIANT}"
|
||||
|
||||
# Fail fast
|
||||
if [ ! -f "${DOCKERFILE}" ]; then
|
||||
echo "🚨 The Dockerfile '${DOCKERFILE}' for variant '${VARIANT}' doesn't exist."
|
||||
|
||||
if [ -z "$DEBUG" ]; then
|
||||
exit 1
|
||||
else
|
||||
echo "⚠️ Would skip this, but DEBUG is enabled."
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "${DOCKERFILE}" != "${DOCKERFILE_PATH}" ]; then
|
||||
echo "⚠️ The specified Dockerfile '${DOCKERFILE_PATH}' does not match the expected Dockerfile '${DOCKERFILE}'."
|
||||
echo " This script will use '${DOCKERFILE}' and ignore '${DOCKERFILE_PATH}'."
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# Passes args to the scripts
|
||||
run_build() {
|
||||
echo "🐳🐳🐳 Building '${BUILD}' images, the '${VARIANT:-main}' variant"
|
||||
case $BUILD in
|
||||
release)
|
||||
# build the latest release
|
||||
# shellcheck disable=SC2068
|
||||
./build-latest.sh $@
|
||||
;;
|
||||
prerelease)
|
||||
# build the latest pre-release
|
||||
# shellcheck disable=SC2068
|
||||
PRERELEASE=true ./build-latest.sh $@
|
||||
;;
|
||||
branches)
|
||||
# build all branches
|
||||
# shellcheck disable=SC2068
|
||||
./build-branches.sh $@
|
||||
;;
|
||||
special)
|
||||
# special build
|
||||
# shellcheck disable=SC2068
|
||||
#SRC_ORG=lampwins TAG=webhooks-backend ./build.sh "feature/webhooks-backend" $@
|
||||
echo "✅ No special builds today."
|
||||
;;
|
||||
*)
|
||||
echo "🚨 Unrecognized build '$BUILD'."
|
||||
|
||||
if [ -z "$DEBUG" ]; then
|
||||
exit 1
|
||||
else
|
||||
echo "⚠️ Would exit here with code '1', but DEBUG is enabled."
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
echo "🤖🤖🤖 Preparing build"
|
||||
export DOCKER_ORG="index.docker.io/netboxcommunity"
|
||||
export DOCKER_REPO=netbox
|
||||
export DOCKERHUB_REPO=netboxcommunity/netbox
|
||||
|
||||
# mis-using the "${DOCKER_TAG}" variable as "branch to build"
|
||||
export BUILD="${DOCKER_TAG%-*}"
|
||||
export VARIANT="${DOCKER_TAG#*-}"
|
||||
|
||||
unset DOCKER_TAG
|
||||
|
||||
ensure_dockerfile_present
|
||||
|
||||
ensure_jq
|
5
hooks/push
Executable file
5
hooks/push
Executable file
@ -0,0 +1,5 @@
|
||||
#!/bin/bash
|
||||
|
||||
. hooks/common
|
||||
|
||||
run_build --push-only
|
12
hooks/test
Executable file
12
hooks/test
Executable file
@ -0,0 +1,12 @@
|
||||
#!/bin/bash
|
||||
|
||||
. hooks/common
|
||||
|
||||
if [ "${VARIANT}" == "main" ] && [ "${BUILD}" == "BRANCHES" ]; then
|
||||
echo "🐳🐳🐳 Testing"
|
||||
docker-compose pull --parallel
|
||||
docker-compose build
|
||||
docker-compose run netbox ./manage.py test
|
||||
else
|
||||
echo "🐳🐳🐳 No tests are implemented for build '${BUILD}' with variant '${VARIANT}'."
|
||||
fi
|
@ -7,6 +7,8 @@
|
||||
# on_objects:
|
||||
# - dcim.models.Device
|
||||
# - dcim.models.Rack
|
||||
# - dcim.models.Site
|
||||
# - dcim.models.DeviceType
|
||||
# - ipam.models.IPAddress
|
||||
# - ipam.models.Prefix
|
||||
# - tenancy.models.Tenant
|
||||
|
15
initializers/device_roles.yml
Normal file
15
initializers/device_roles.yml
Normal file
@ -0,0 +1,15 @@
|
||||
# - name: switch
|
||||
# slug: switch
|
||||
# color: Grey
|
||||
# - name: router
|
||||
# slug: router
|
||||
# color: Cyan
|
||||
# - name: load-balancer
|
||||
# slug: load-balancer
|
||||
# color: Red
|
||||
# - name: server
|
||||
# slug: server
|
||||
# color: Blue
|
||||
# - name: patchpanel
|
||||
# slug: patchpanel
|
||||
# color: Black
|
23
initializers/device_types.yml
Normal file
23
initializers/device_types.yml
Normal file
@ -0,0 +1,23 @@
|
||||
# - model: Model 1
|
||||
# manufacturer: Manufacturer 1
|
||||
# slug: model-1
|
||||
# u_height: 2
|
||||
# custom_fields:
|
||||
# text_field: Description
|
||||
# - model: Model 2
|
||||
# manufacturer: Manufacturer 1
|
||||
# slug: model-2
|
||||
# custom_fields:
|
||||
# text_field: Description
|
||||
# - model: Model 3
|
||||
# manufacturer: Manufacturer 1
|
||||
# slug: model-3
|
||||
# is_full_depth: false
|
||||
# u_height: 0
|
||||
# custom_fields:
|
||||
# text_field: Description
|
||||
# - model: Other
|
||||
# manufacturer: NoName
|
||||
# slug: other
|
||||
# custom_fields:
|
||||
# text_field: Description
|
27
initializers/devices.yml
Normal file
27
initializers/devices.yml
Normal file
@ -0,0 +1,27 @@
|
||||
# - name: server01
|
||||
# device_role: server
|
||||
# device_type: Other
|
||||
# site: AMS 1
|
||||
# rack: rack-01
|
||||
# face: Front
|
||||
# position: 1
|
||||
# custom_fields:
|
||||
# text_field: Description
|
||||
# - name: server02
|
||||
# device_role: server
|
||||
# device_type: Other
|
||||
# site: AMS 2
|
||||
# rack: rack-02
|
||||
# face: Front
|
||||
# position: 2
|
||||
# custom_fields:
|
||||
# text_field: Description
|
||||
# - name: server03
|
||||
# device_role: server
|
||||
# device_type: Other
|
||||
# site: SING 1
|
||||
# rack: rack-03
|
||||
# face: Front
|
||||
# position: 3
|
||||
# custom_fields:
|
||||
# text_field: Description
|
@ -7,3 +7,10 @@
|
||||
# writers:
|
||||
# users:
|
||||
# - writer
|
||||
# permissions:
|
||||
# - add_device
|
||||
# - change_device
|
||||
# - delete_device
|
||||
# - add_virtualmachine
|
||||
# - change_virtualmachine
|
||||
# - delete_virtualmachine
|
||||
|
6
initializers/manufacturers.yml
Normal file
6
initializers/manufacturers.yml
Normal file
@ -0,0 +1,6 @@
|
||||
# - name: Manufacturer 1
|
||||
# slug: manufacturer-1
|
||||
# - name: Manufacturer 2
|
||||
# slug: manufacturer-2
|
||||
# - name: NoName
|
||||
# slug: noname
|
19
initializers/platforms.yml
Normal file
19
initializers/platforms.yml
Normal file
@ -0,0 +1,19 @@
|
||||
# # Allowed rpc clients are: juniper-junos, cisco-ios, opengear
|
||||
# - name: Platform 1
|
||||
# slug: platform-1
|
||||
# manufacturer: Manufacturer 1
|
||||
# napalm_driver: driver1
|
||||
# napalm_args: "{'arg1': 'value1', 'arg2': 'value2'}"
|
||||
# rpc_client: juniper-junos
|
||||
# - name: Platform 2
|
||||
# slug: platform-2
|
||||
# manufacturer: Manufacturer 2
|
||||
# napalm_driver: driver2
|
||||
# napalm_args: "{'arg1': 'value1', 'arg2': 'value2'}"
|
||||
# rpc_client: opengear
|
||||
# - name: Platform 3
|
||||
# slug: platform-3
|
||||
# manufacturer: NoName
|
||||
# napalm_driver: driver3
|
||||
# napalm_args: "{'arg1': 'value1', 'arg2': 'value2'}"
|
||||
# rpc_client: juniper-junos
|
12
initializers/rack_roles.yml
Normal file
12
initializers/rack_roles.yml
Normal file
@ -0,0 +1,12 @@
|
||||
# - name: Role 1
|
||||
# slug: role-1
|
||||
# color: Pink
|
||||
# - name: Role 2
|
||||
# slug: role-2
|
||||
# color: Cyan
|
||||
# - name: Role 3
|
||||
# slug: role-3
|
||||
# color: Grey
|
||||
# - name: Role 4
|
||||
# slug: role-4
|
||||
# color: Teal
|
24
initializers/racks.yml
Normal file
24
initializers/racks.yml
Normal file
@ -0,0 +1,24 @@
|
||||
# - site: AMS 1
|
||||
# name: rack-01
|
||||
# role: Role 1
|
||||
# type: 4-post cabinet
|
||||
# width: 19 inches
|
||||
# u_height: 47
|
||||
# custom_fields:
|
||||
# text_field: Description
|
||||
# - site: AMS 2
|
||||
# name: rack-02
|
||||
# role: Role 2
|
||||
# type: 4-post cabinet
|
||||
# width: 19 inches
|
||||
# u_height: 47
|
||||
# custom_fields:
|
||||
# text_field: Description
|
||||
# - site: SING 1
|
||||
# name: rack-03
|
||||
# role: Role 3
|
||||
# type: 4-post cabinet
|
||||
# width: 19 inches
|
||||
# u_height: 47
|
||||
# custom_fields:
|
||||
# text_field: Description
|
10
initializers/regions.yml
Normal file
10
initializers/regions.yml
Normal file
@ -0,0 +1,10 @@
|
||||
# - name: Singapore
|
||||
# slug: singapore
|
||||
# - name: Amsterdam
|
||||
# slug: amsterdam
|
||||
# - name: Downtown
|
||||
# slug: downtown
|
||||
# parent: Amsterdam
|
||||
# - name: Suburbs
|
||||
# slug: suburbs
|
||||
# parent: Amsterdam
|
32
initializers/sites.yml
Normal file
32
initializers/sites.yml
Normal file
@ -0,0 +1,32 @@
|
||||
# - name: AMS 1
|
||||
# slug: ams1
|
||||
# region: Downtown
|
||||
# status: 1
|
||||
# facility: Amsterdam 1
|
||||
# asn: 12345
|
||||
# custom_fields:
|
||||
# text_field: Description
|
||||
# - name: AMS 2
|
||||
# slug: ams2
|
||||
# region: Downtown
|
||||
# status: 1
|
||||
# facility: Amsterdam 2
|
||||
# asn: 54321
|
||||
# custom_fields:
|
||||
# text_field: Description
|
||||
# - name: AMS 3
|
||||
# slug: ams3
|
||||
# region: Suburbs
|
||||
# status: 1
|
||||
# facility: Amsterdam 3
|
||||
# asn: 67890
|
||||
# custom_fields:
|
||||
# text_field: Description
|
||||
# - name: SING 1
|
||||
# slug: sing1
|
||||
# region: Singapore
|
||||
# status: 1
|
||||
# facility: Singapore 1
|
||||
# asn: 09876
|
||||
# custom_fields:
|
||||
# text_field: Description
|
@ -4,3 +4,10 @@
|
||||
# password: reader
|
||||
# writer:
|
||||
# password: writer
|
||||
# permissions:
|
||||
# - add_device
|
||||
# - change_device
|
||||
# - delete_device
|
||||
# - add_virtualmachine
|
||||
# - change_virtualmachine
|
||||
# - delete_virtualmachine
|
||||
|
46
reports/devices.py.example
Normal file
46
reports/devices.py.example
Normal file
@ -0,0 +1,46 @@
|
||||
from dcim.constants import CONNECTION_STATUS_PLANNED, DEVICE_STATUS_ACTIVE
|
||||
from dcim.models import ConsolePort, Device, PowerPort
|
||||
from extras.reports import Report
|
||||
|
||||
|
||||
class DeviceConnectionsReport(Report):
|
||||
description = "Validate the minimum physical connections for each device"
|
||||
|
||||
def test_console_connection(self):
|
||||
|
||||
# Check that every console port for every active device has a connection defined.
|
||||
for console_port in ConsolePort.objects.select_related('device').filter(device__status=DEVICE_STATUS_ACTIVE):
|
||||
if console_port.connected_endpoint is None:
|
||||
self.log_failure(
|
||||
console_port.device,
|
||||
"No console connection defined for {}".format(console_port.name)
|
||||
)
|
||||
elif console_port.connection_status == CONNECTION_STATUS_PLANNED:
|
||||
self.log_warning(
|
||||
console_port.device,
|
||||
"Console connection for {} marked as planned".format(console_port.name)
|
||||
)
|
||||
else:
|
||||
self.log_success(console_port.device)
|
||||
|
||||
def test_power_connections(self):
|
||||
|
||||
# Check that every active device has at least two connected power supplies.
|
||||
for device in Device.objects.filter(status=DEVICE_STATUS_ACTIVE):
|
||||
connected_ports = 0
|
||||
for power_port in PowerPort.objects.filter(device=device):
|
||||
if power_port.connected_endpoint is not None:
|
||||
connected_ports += 1
|
||||
if power_port.connection_status == CONNECTION_STATUS_PLANNED:
|
||||
self.log_warning(
|
||||
device,
|
||||
"Power connection for {} marked as planned".format(power_port.name)
|
||||
)
|
||||
if connected_ports < 2:
|
||||
self.log_failure(
|
||||
device,
|
||||
"{} connected power supplies found (2 needed)".format(connected_ports)
|
||||
)
|
||||
else:
|
||||
self.log_success(device)
|
||||
|
34
startup_scripts/000_users.py
Normal file
34
startup_scripts/000_users.py
Normal file
@ -0,0 +1,34 @@
|
||||
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():
|
||||
sys.exit()
|
||||
|
||||
with file.open('r') as stream:
|
||||
yaml=YAML(typ='safe')
|
||||
users = yaml.load(stream)
|
||||
|
||||
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)
|
||||
|
||||
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', []):
|
||||
permission = Permission.objects.get(codename=permission_codename)
|
||||
user.user_permissions.add(permission)
|
||||
user.save()
|
@ -1,20 +0,0 @@
|
||||
from django.contrib.auth.models import Group, User
|
||||
from users.models import Token
|
||||
|
||||
from ruamel.yaml import YAML
|
||||
|
||||
with open('/opt/netbox/initializers/users.yml', 'r') as stream:
|
||||
yaml=YAML(typ='safe')
|
||||
users = yaml.load(stream)
|
||||
|
||||
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)
|
||||
|
||||
if user_details.get('api_token', 0):
|
||||
Token.objects.create(user=user, key=user_details['api_token'])
|
33
startup_scripts/010_groups.py
Normal file
33
startup_scripts/010_groups.py
Normal file
@ -0,0 +1,33 @@
|
||||
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():
|
||||
sys.exit()
|
||||
|
||||
with file.open('r') as stream:
|
||||
yaml=YAML(typ='safe')
|
||||
groups = yaml.load(stream)
|
||||
|
||||
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)
|
||||
|
||||
for username in group_details.get('users', []):
|
||||
user = User.objects.get(username=username)
|
||||
|
||||
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', []):
|
||||
permission = Permission.objects.get(codename=permission_codename)
|
||||
group.permissions.add(permission)
|
@ -2,6 +2,8 @@ from extras.constants import CF_TYPE_TEXT, CF_TYPE_INTEGER, CF_TYPE_BOOLEAN, CF_
|
||||
from extras.models import CustomField, CustomFieldChoice
|
||||
|
||||
from ruamel.yaml import YAML
|
||||
from pathlib import Path
|
||||
import sys
|
||||
|
||||
text_to_fields = {
|
||||
'boolean': CF_TYPE_BOOLEAN,
|
||||
@ -21,7 +23,11 @@ def get_class_for_class_path(class_path):
|
||||
clazz = getattr(module, class_name)
|
||||
return ContentType.objects.get_for_model(clazz)
|
||||
|
||||
with open('/opt/netbox/initializers/custom_fields.yml', 'r') as stream:
|
||||
file = Path('/opt/netbox/initializers/custom_fields.yml')
|
||||
if not file.is_file():
|
||||
sys.exit()
|
||||
|
||||
with file.open('r') as stream:
|
||||
yaml = YAML(typ='safe')
|
||||
customfields = yaml.load(stream)
|
||||
|
31
startup_scripts/030_regions.py
Normal file
31
startup_scripts/030_regions.py
Normal file
@ -0,0 +1,31 @@
|
||||
from dcim.models import Region
|
||||
from ruamel.yaml import YAML
|
||||
from pathlib import Path
|
||||
import sys
|
||||
|
||||
file = Path('/opt/netbox/initializers/regions.yml')
|
||||
if not file.is_file():
|
||||
sys.exit()
|
||||
|
||||
with file.open('r') as stream:
|
||||
yaml=YAML(typ='safe')
|
||||
regions = yaml.load(stream)
|
||||
|
||||
optional_assocs = {
|
||||
'parent': (Region, 'name')
|
||||
}
|
||||
|
||||
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) }
|
||||
|
||||
params[assoc] = model.objects.get(**query)
|
||||
|
||||
region, created = Region.objects.get_or_create(**params)
|
||||
|
||||
if created:
|
||||
print("🌐 Created region", region.name)
|
46
startup_scripts/040_sites.py
Normal file
46
startup_scripts/040_sites.py
Normal file
@ -0,0 +1,46 @@
|
||||
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():
|
||||
sys.exit()
|
||||
|
||||
with file.open('r') as stream:
|
||||
yaml = YAML(typ='safe')
|
||||
sites = yaml.load(stream)
|
||||
|
||||
optional_assocs = {
|
||||
'region': (Region, 'name'),
|
||||
'tenant': (Tenant, 'name')
|
||||
}
|
||||
|
||||
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) }
|
||||
|
||||
params[assoc] = model.objects.get(**query)
|
||||
|
||||
site, created = Site.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=site,
|
||||
value=cf_value
|
||||
)
|
||||
|
||||
site.custom_field_values.add(custom_field_value)
|
||||
|
||||
print("📍 Created site", site.name)
|
19
startup_scripts/050_manufacturers.py
Normal file
19
startup_scripts/050_manufacturers.py
Normal file
@ -0,0 +1,19 @@
|
||||
from dcim.models import Manufacturer
|
||||
from ruamel.yaml import YAML
|
||||
from pathlib import Path
|
||||
import sys
|
||||
|
||||
file = Path('/opt/netbox/initializers/manufacturers.yml')
|
||||
if not file.is_file():
|
||||
sys.exit()
|
||||
|
||||
with file.open('r') as stream:
|
||||
yaml = YAML(typ='safe')
|
||||
manufacturers = yaml.load(stream)
|
||||
|
||||
if manufacturers is not None:
|
||||
for params in manufacturers:
|
||||
manufacturer, created = Manufacturer.objects.get_or_create(**params)
|
||||
|
||||
if created:
|
||||
print("🏭 Created Manufacturer", manufacturer.name)
|
56
startup_scripts/060_device_types.py
Normal file
56
startup_scripts/060_device_types.py
Normal file
@ -0,0 +1,56 @@
|
||||
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():
|
||||
sys.exit()
|
||||
|
||||
with file.open('r') as stream:
|
||||
yaml = YAML(typ='safe')
|
||||
device_types = yaml.load(stream)
|
||||
|
||||
required_assocs = {
|
||||
'manufacturer': (Manufacturer, 'name')
|
||||
}
|
||||
|
||||
optional_assocs = {
|
||||
'region': (Region, 'name'),
|
||||
'tenant': (Tenant, 'name')
|
||||
}
|
||||
|
||||
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) }
|
||||
|
||||
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) }
|
||||
|
||||
params[assoc] = model.objects.get(**query)
|
||||
|
||||
device_type, created = DeviceType.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=device_type,
|
||||
value=cf_value
|
||||
)
|
||||
|
||||
device_type.custom_field_values.add(custom_field_value)
|
||||
|
||||
print("🔡 Created device type", device_type.manufacturer, device_type.model)
|
28
startup_scripts/070_rack_roles.py
Normal file
28
startup_scripts/070_rack_roles.py
Normal file
@ -0,0 +1,28 @@
|
||||
from dcim.models import RackRole
|
||||
from ruamel.yaml import YAML
|
||||
from utilities.forms import COLOR_CHOICES
|
||||
|
||||
from pathlib import Path
|
||||
import sys
|
||||
|
||||
file = Path('/opt/netbox/initializers/rack_roles.yml')
|
||||
if not file.is_file():
|
||||
sys.exit()
|
||||
|
||||
with file.open('r') as stream:
|
||||
yaml=YAML(typ='safe')
|
||||
rack_roles = yaml.load(stream)
|
||||
|
||||
if rack_roles is not None:
|
||||
for params in rack_roles:
|
||||
if 'color' in params:
|
||||
color = params.pop('color')
|
||||
|
||||
for color_tpl in COLOR_CHOICES:
|
||||
if color in color_tpl:
|
||||
params['color'] = color_tpl[0]
|
||||
|
||||
rack_role, created = RackRole.objects.get_or_create(**params)
|
||||
|
||||
if created:
|
||||
print("🎨 Created rack role", rack_role.name)
|
66
startup_scripts/080_racks.py
Normal file
66
startup_scripts/080_racks.py
Normal file
@ -0,0 +1,66 @@
|
||||
from dcim.models import Site, RackRole, Rack, RackGroup
|
||||
from tenancy.models import Tenant
|
||||
from extras.models import CustomField, CustomFieldValue
|
||||
from dcim.constants import RACK_TYPE_CHOICES, RACK_WIDTH_CHOICES
|
||||
from ruamel.yaml import YAML
|
||||
from pathlib import Path
|
||||
import sys
|
||||
|
||||
file = Path('/opt/netbox/initializers/racks.yml')
|
||||
if not file.is_file():
|
||||
sys.exit()
|
||||
|
||||
with file.open('r') as stream:
|
||||
yaml = YAML(typ='safe')
|
||||
racks = yaml.load(stream)
|
||||
|
||||
required_assocs = {
|
||||
'site': (Site, 'name')
|
||||
}
|
||||
|
||||
optional_assocs = {
|
||||
'role': (RackRole, 'name'),
|
||||
'tenant': (Tenant, 'name'),
|
||||
'group': (RackGroup, 'name')
|
||||
}
|
||||
|
||||
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) }
|
||||
|
||||
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) }
|
||||
|
||||
params[assoc] = model.objects.get(**query)
|
||||
|
||||
for rack_type in RACK_TYPE_CHOICES:
|
||||
if params['type'] in rack_type:
|
||||
params['type'] = rack_type[0]
|
||||
|
||||
for rack_width in RACK_WIDTH_CHOICES:
|
||||
if params['width'] in rack_width:
|
||||
params['width'] = rack_width[0]
|
||||
|
||||
rack, created = Rack.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=rack,
|
||||
value=cf_value
|
||||
)
|
||||
|
||||
rack.custom_field_values.add(custom_field_value)
|
||||
|
||||
print("🔳 Created rack", rack.site, rack.name)
|
29
startup_scripts/090_device_roles.py
Normal file
29
startup_scripts/090_device_roles.py
Normal file
@ -0,0 +1,29 @@
|
||||
from dcim.models import DeviceRole
|
||||
from ruamel.yaml import YAML
|
||||
from utilities.forms import COLOR_CHOICES
|
||||
|
||||
from pathlib import Path
|
||||
import sys
|
||||
|
||||
file = Path('/opt/netbox/initializers/device_roles.yml')
|
||||
if not file.is_file():
|
||||
sys.exit()
|
||||
|
||||
with file.open('r') as stream:
|
||||
yaml=YAML(typ='safe')
|
||||
device_roles = yaml.load(stream)
|
||||
|
||||
if device_roles is not None:
|
||||
for params in device_roles:
|
||||
|
||||
if 'color' in params:
|
||||
color = params.pop('color')
|
||||
|
||||
for color_tpl in COLOR_CHOICES:
|
||||
if color in color_tpl:
|
||||
params['color'] = color_tpl[0]
|
||||
|
||||
device_role, created = DeviceRole.objects.get_or_create(**params)
|
||||
|
||||
if created:
|
||||
print("🎨 Created device role", device_role.name)
|
32
startup_scripts/100_platforms.py
Normal file
32
startup_scripts/100_platforms.py
Normal file
@ -0,0 +1,32 @@
|
||||
from dcim.models import Manufacturer, Platform
|
||||
from ruamel.yaml import YAML
|
||||
|
||||
from pathlib import Path
|
||||
import sys
|
||||
|
||||
file = Path('/opt/netbox/initializers/platforms.yml')
|
||||
if not file.is_file():
|
||||
sys.exit()
|
||||
|
||||
with file.open('r') as stream:
|
||||
yaml = YAML(typ='safe')
|
||||
platforms = yaml.load(stream)
|
||||
|
||||
optional_assocs = {
|
||||
'manufacturer': (Manufacturer, 'name'),
|
||||
}
|
||||
|
||||
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) }
|
||||
|
||||
params[assoc] = model.objects.get(**query)
|
||||
|
||||
platform, created = Platform.objects.get_or_create(**params)
|
||||
|
||||
if created:
|
||||
print("💾 Created platform", platform.name)
|
@ -1,19 +0,0 @@
|
||||
from django.contrib.auth.models import Group, User
|
||||
from ruamel.yaml import YAML
|
||||
|
||||
with open('/opt/netbox/initializers/groups.yml', 'r') as stream:
|
||||
yaml=YAML(typ='safe')
|
||||
groups = yaml.load(stream)
|
||||
|
||||
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)
|
||||
|
||||
for username in group_details['users']:
|
||||
user = User.objects.get(username=username)
|
||||
|
||||
if user:
|
||||
user.groups.add(group)
|
71
startup_scripts/110_devices.py
Normal file
71
startup_scripts/110_devices.py
Normal file
@ -0,0 +1,71 @@
|
||||
from dcim.models import Site, Rack, DeviceRole, DeviceType, Device, Platform
|
||||
from dcim.constants import RACK_FACE_CHOICES
|
||||
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():
|
||||
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')
|
||||
}
|
||||
|
||||
optional_assocs = {
|
||||
'tenant': (Tenant, 'name'),
|
||||
'platform': (Platform, 'name'),
|
||||
'rack': (Rack, 'name'),
|
||||
'cluster': (Cluster, 'name'),
|
||||
'primary_ip4': (IPAddress, 'address'),
|
||||
'primary_ip6': (IPAddress, 'address')
|
||||
}
|
||||
|
||||
if devices is not None:
|
||||
for params in devices:
|
||||
custom_fields = params.pop('custom_fields', None)
|
||||
|
||||
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 optional_assocs.items():
|
||||
if assoc in params:
|
||||
model, field = details
|
||||
query = { field: params.pop(assoc) }
|
||||
|
||||
params[assoc] = model.objects.get(**query)
|
||||
|
||||
if 'face' in params:
|
||||
for rack_face in RACK_FACE_CHOICES:
|
||||
if params['face'] in rack_face:
|
||||
params['face'] = rack_face[0]
|
||||
|
||||
device, created = Device.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=device,
|
||||
value=cf_value
|
||||
)
|
||||
|
||||
device.custom_field_values.add(custom_field_value)
|
||||
|
||||
print("🖥️ Created device", device.name)
|
Reference in New Issue
Block a user