mirror of
https://gitverse.ru/kpa39l/netbox.git
synced 2026-09-29 09:55:11 +00:00
First working version
This commit is contained in:
+17
@@ -0,0 +1,17 @@
|
||||
*.sql.gz
|
||||
.netbox
|
||||
.python-version
|
||||
docker-compose.override.yml
|
||||
*.pem
|
||||
configuration/*
|
||||
!configuration/configuration.py
|
||||
!configuration/extra.py
|
||||
configuration/ldap/*
|
||||
!configuration/ldap/extra.py
|
||||
!configuration/ldap/ldap_config.py
|
||||
!configuration/logging.py
|
||||
!configuration/plugins.py
|
||||
super-linter.log
|
||||
env/netbox.env
|
||||
env/postgres.env
|
||||
yadisk
|
||||
+117
@@ -0,0 +1,117 @@
|
||||
ARG FROM
|
||||
FROM ${FROM} AS builder
|
||||
|
||||
COPY --from=ghcr.io/astral-sh/uv:0.11 /uv /usr/local/bin/
|
||||
RUN export DEBIAN_FRONTEND=noninteractive \
|
||||
&& apt-get update -qq \
|
||||
&& apt-get upgrade \
|
||||
--yes -qq --no-install-recommends \
|
||||
&& apt-get install \
|
||||
--yes -qq --no-install-recommends \
|
||||
build-essential \
|
||||
ca-certificates \
|
||||
libldap-dev \
|
||||
libpq-dev \
|
||||
libsasl2-dev \
|
||||
libssl-dev \
|
||||
libxml2-dev \
|
||||
libxmlsec1-1 \
|
||||
libxmlsec1-dev \
|
||||
libxmlsec1-openssl1 \
|
||||
libxslt-dev \
|
||||
pkg-config \
|
||||
python3-dev \
|
||||
&& /usr/local/bin/uv venv /opt/netbox/venv
|
||||
|
||||
ARG NETBOX_PATH
|
||||
COPY ${NETBOX_PATH}/requirements.txt requirements-container.txt /
|
||||
ENV VIRTUAL_ENV=/opt/netbox/venv
|
||||
RUN \
|
||||
# Gunicorn is not needed because we use Granian
|
||||
sed -i -e '/gunicorn/d' /requirements.txt && \
|
||||
# We need 'social-auth-core[all]' in the Docker image. But if we put it in our own requirements-container.txt
|
||||
# we have potential version conflicts and the build will fail.
|
||||
# That's why we just replace it in the original requirements.txt.
|
||||
sed -i -e 's/social-auth-core/social-auth-core\[all\]/g' /requirements.txt && \
|
||||
# The same is true for 'django-storages'
|
||||
sed -i -e 's/django-storages/django-storages\[azure,boto3,dropbox,google,libcloud,sftp\]/g' /requirements.txt && \
|
||||
/usr/local/bin/uv pip install \
|
||||
-r /requirements.txt \
|
||||
-r /requirements-container.txt
|
||||
|
||||
###
|
||||
# Main stage
|
||||
###
|
||||
|
||||
ARG FROM
|
||||
FROM ${FROM} AS main
|
||||
|
||||
RUN export DEBIAN_FRONTEND=noninteractive \
|
||||
&& apt-get update -qq \
|
||||
&& apt-get upgrade \
|
||||
--yes -qq --no-install-recommends \
|
||||
&& apt-get install \
|
||||
--yes -qq --no-install-recommends \
|
||||
bzip2 \
|
||||
ca-certificates \
|
||||
curl \
|
||||
libldap-common \
|
||||
libpq5 \
|
||||
libxmlsec1-openssl1 \
|
||||
openssh-client \
|
||||
openssl \
|
||||
python3 \
|
||||
tini \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
COPY --from=builder /usr/local/bin/uv /usr/local/bin/
|
||||
COPY --from=builder /opt/netbox/venv /opt/netbox/venv
|
||||
|
||||
ARG NETBOX_PATH
|
||||
COPY ${NETBOX_PATH} /opt/netbox
|
||||
# Copy the modified 'requirements*.txt' files, to have the files actually used during installation
|
||||
COPY --from=builder /requirements.txt /requirements-container.txt /opt/netbox/
|
||||
|
||||
COPY docker/configuration.docker.py /opt/netbox/netbox/netbox/configuration.py
|
||||
COPY docker/ldap_config.docker.py /opt/netbox/netbox/netbox/ldap_config.py
|
||||
COPY docker/docker-entrypoint.sh /opt/netbox/docker-entrypoint.sh
|
||||
COPY docker/launch-netbox.sh /opt/netbox/launch-netbox.sh
|
||||
COPY docker/super_user.py /opt/netbox/super_user.py
|
||||
COPY configuration/ /etc/netbox/config/
|
||||
COPY docker/granian.py /opt/netbox/netbox/netbox/granian.py
|
||||
COPY VERSION /opt/netbox/VERSION
|
||||
|
||||
WORKDIR /opt/netbox/netbox
|
||||
|
||||
# Must set permissions for '/opt/netbox/netbox/media' directory
|
||||
# to g+w so that pictures can be uploaded to netbox.
|
||||
RUN useradd --home-dir /opt/netbox/ --no-create-home --no-user-group --system --shell /bin/false --uid 999 --gid 0 netbox \
|
||||
&& mkdir -p static media local \
|
||||
&& chown -R netbox:root media reports scripts \
|
||||
&& chmod -R g+w media reports scripts \
|
||||
&& cd /opt/netbox/ && SECRET_KEY="dummyKeyWithMinimumLength-------------------------" /opt/netbox/venv/bin/zensical build \
|
||||
--config-file /opt/netbox/mkdocs.yml \
|
||||
&& DEBUG="true" SECRET_KEY="dummyKeyWithMinimumLength-------------------------" /opt/netbox/venv/bin/python /opt/netbox/netbox/manage.py collectstatic --no-input \
|
||||
&& echo "build: Docker-$(cat /opt/netbox/VERSION)" > /opt/netbox/netbox/local/release.yaml
|
||||
|
||||
ENV LANG=C.utf8 PATH=/opt/netbox/venv/bin:$PATH VIRTUAL_ENV=/opt/netbox/venv UV_NO_CACHE=1
|
||||
ENTRYPOINT [ "/usr/bin/tini", "--" ]
|
||||
|
||||
CMD [ "/opt/netbox/docker-entrypoint.sh", "/opt/netbox/launch-netbox.sh" ]
|
||||
|
||||
LABEL netbox.original-tag="" \
|
||||
netbox.git-branch="" \
|
||||
netbox.git-ref="" \
|
||||
netbox.git-url="" \
|
||||
# See https://github.com/opencontainers/image-spec/blob/master/annotations.md#pre-defined-annotation-keys
|
||||
org.opencontainers.image.created="" \
|
||||
org.opencontainers.image.title="NetBox Docker" \
|
||||
org.opencontainers.image.description="A container based distribution of NetBox, the free and open IPAM and DCIM solution." \
|
||||
org.opencontainers.image.licenses="Apache-2.0" \
|
||||
org.opencontainers.image.authors="The netbox-docker contributors." \
|
||||
org.opencontainers.image.vendor="The netbox-docker contributors." \
|
||||
org.opencontainers.image.url="https://github.com/netbox-community/netbox-docker" \
|
||||
org.opencontainers.image.documentation="https://github.com/netbox-community/netbox-docker/wiki" \
|
||||
org.opencontainers.image.source="https://github.com/netbox-community/netbox-docker.git" \
|
||||
org.opencontainers.image.revision="" \
|
||||
org.opencontainers.image.version=""
|
||||
@@ -4,4 +4,8 @@ RUN /usr/local/bin/uv pip install -r /opt/netbox/plugin_requirements.txt
|
||||
# Скопируйте конфигурацию, если она у вас есть
|
||||
COPY configuration/configuration.py /etc/netbox/config/configuration.py
|
||||
COPY configuration/plugins.py /etc/netbox/config/plugins.py
|
||||
#RUN python /opt/netbox/netbox/manage.py collectstatic --no-input
|
||||
|
||||
# Временно устанавливаем SECRET_KEY для сборки (будет заменён при запуске)
|
||||
ENV SECRET_KEY=temp-key-for-building-only-50-characters-minimum!!!!!
|
||||
RUN python /opt/netbox/netbox/manage.py collectstatic --no-input
|
||||
|
||||
@@ -0,0 +1,178 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
# Maintainers of _NetBox Docker_
|
||||
|
||||
This file lists all currently recognized maintainers of the _NetBox Docker_ project in alphabetical order:
|
||||
|
||||
- @cimnine
|
||||
- @tobiasge
|
||||
|
||||
## Stepping Down
|
||||
|
||||
Every maintainer is a volunteer and may step down as maintainer at any time without providing any reason.
|
||||
To make this explicit, the maintainer is asked to update this file.
|
||||
|
||||
The last maintainer stepping down is asked to archive the project on GitHub to indicate that the project is no longer maintained.
|
||||
|
||||
## Signing up
|
||||
|
||||
Everyone is welcome to sign up as maintainer by creating a PR and add their own username to the list.
|
||||
The current maintainers shall discuss the application.
|
||||
They may turn down an application if they don't feel confident that the new maintainer is a positive addition.
|
||||
@@ -0,0 +1,71 @@
|
||||
# Development, Maintenance and Community Principals for _NetBox Docker_
|
||||
|
||||
These principals shall guide the development and the maintenance of _NetBox Docker_.
|
||||
|
||||
## Basic principals
|
||||
|
||||
This project is maintained on voluntary basis.
|
||||
Everyone is asked to respect that.
|
||||
|
||||
This means, that …
|
||||
|
||||
- … sometimes features are not implemented as fast as one might like -- or not at all.
|
||||
- … sometimes nobody is looking at bugs, or they are not fixed as fast as one might like -- or not at all.
|
||||
- … sometimes PRs are not reviewed for an extended period.
|
||||
|
||||
Everyone is welcome to provide improvements and bugfixes to the benefit of everyone else.
|
||||
|
||||
## Development Principals
|
||||
|
||||
The goal of the _NetBox Docker_ project is to provide a container to run the basic NetBox project.
|
||||
The container should feel like a native container -- as if it were provided by NetBox itself:
|
||||
|
||||
- Configuration via environment variables where feasible.
|
||||
- Except: Whenever a complex type such as a `dict` is required as value of a configuration setting,
|
||||
then it shall not be provided through an environment variable.
|
||||
- Configuration of secrets via secret files.
|
||||
- Log output to standard out (STDOUT/`&1`) / standard error (STDERR/`&2`).
|
||||
- Volumes for data and cache directories.
|
||||
- Otherwise, no mounts shall be necessary.
|
||||
- Runs a non-root user by default.
|
||||
- One process / role for each instance.
|
||||
|
||||
The container generally does not provide more features than the basic NetBox project itself provides.
|
||||
It may provide additional Python dependencies than the upstream project,
|
||||
so that all configurable features of NetBox can be used in the container without further modification.
|
||||
The container may provide helpers, so that it feels and behaves like a native container.
|
||||
|
||||
The container does not bundle any community plugins.
|
||||
|
||||
## Maintenance Principals
|
||||
|
||||
The main goals of maintaining _NetBox Docker_ are:
|
||||
|
||||
- Keeping the project at a high quality level.
|
||||
- Keeping the maintenance effort minimal.
|
||||
- Coordinating development efforts.
|
||||
|
||||
The following guidelines help us to achieve these goals:
|
||||
|
||||
- As many maintenance tasks as possible shall be automated or scripted.
|
||||
- All manual tasks must be documented.
|
||||
- All changes are reviewed by at least one maintainer.
|
||||
- Changes of maintainers are reviewed by at least one other maintainer.
|
||||
(Except if there's only one maintainer left.)
|
||||
- The infrastructure beyond what GitHub provides shall be kept to a minimum.
|
||||
- On request, every maintainer shall get access to infrastructure that is beyond GitHub
|
||||
(at the time of writing that's _Docker Hub_ and _Quay_ in particular).
|
||||
|
||||
## Community Principals
|
||||
|
||||
This project is developed by the NetBox community for the NetBox community.
|
||||
We welcome contributions, as long as they are in line with the principals above.
|
||||
|
||||
The maintainers of NetBox Docker are not the support team.
|
||||
The community is expected to help each other out.
|
||||
|
||||
Always remember:
|
||||
Behind every screen (or screen-reader) on the other end is a fellow human.
|
||||
Be nice and respectful, thankful for help,
|
||||
and value ideas and contributions,
|
||||
even when they don't fit the goals.
|
||||
@@ -0,0 +1,163 @@
|
||||
# netbox-docker
|
||||
|
||||
[][github-release]
|
||||
[][github-stargazers]
|
||||

|
||||

|
||||

|
||||
[][netbox-docker-license]
|
||||
|
||||
[The GitHub repository][netbox-docker-github] houses the components needed to build NetBox as a container.
|
||||
Images are built regularly using the code in that repository
|
||||
and are pushed to [Docker Hub][netbox-dockerhub],
|
||||
[Quay.io][netbox-quayio] and [GitHub Container Registry][netbox-ghcr].
|
||||
_NetBox Docker_ is a project developed and maintained by the _NetBox_ community.
|
||||
|
||||
Do you have any questions?
|
||||
Before opening an issue on GitHub,
|
||||
please join [our Slack][netbox-docker-slack]
|
||||
and ask for help in the [`#netbox-docker`][netbox-docker-slack-channel] channel,
|
||||
or start a new [GitHub Discussion][github-discussions].
|
||||
|
||||
[github-stargazers]: https://github.com/netbox-community/netbox-docker/stargazers
|
||||
[github-release]: https://github.com/netbox-community/netbox-docker/releases
|
||||
[netbox-dockerhub]: https://hub.docker.com/r/netboxcommunity/netbox/
|
||||
[netbox-quayio]: https://quay.io/repository/netboxcommunity/netbox
|
||||
[netbox-ghcr]: https://github.com/netbox-community/netbox-docker/pkgs/container/netbox
|
||||
[netbox-docker-github]: https://github.com/netbox-community/netbox-docker/
|
||||
[netbox-docker-slack]: https://join.slack.com/t/netdev-community/shared_invite/zt-mtts8g0n-Sm6Wutn62q_M4OdsaIycrQ
|
||||
[netbox-docker-slack-channel]: https://netdev-community.slack.com/archives/C01P0GEVBU7
|
||||
[netbox-slack-channel]: https://netdev-community.slack.com/archives/C01P0FRSXRV
|
||||
[netbox-docker-license]: https://github.com/netbox-community/netbox-docker/blob/release/LICENSE
|
||||
[github-discussions]: https://github.com/netbox-community/netbox-docker/discussions
|
||||
|
||||
## Quickstart
|
||||
|
||||
To get _NetBox Docker_ up and running run the following commands.
|
||||
There is a more complete [_Getting Started_ guide on our wiki][wiki-getting-started] which explains every step.
|
||||
|
||||
```bash
|
||||
git clone -b release https://github.com/netbox-community/netbox-docker.git
|
||||
cd netbox-docker
|
||||
# Copy the example override file
|
||||
cp docker-compose.override.yml.example docker-compose.override.yml
|
||||
# Read and edit the file to your liking
|
||||
docker compose pull
|
||||
docker compose up
|
||||
```
|
||||
|
||||
The whole application will be available after a few minutes.
|
||||
Open the URL `http://0.0.0.0:8000/` in a web-browser.
|
||||
You should see the NetBox homepage.
|
||||
|
||||
To create the first admin user run this command:
|
||||
|
||||
```bash
|
||||
docker compose exec netbox /opt/netbox/netbox/manage.py createsuperuser
|
||||
```
|
||||
|
||||
If you need to restart Netbox from an empty database often,
|
||||
you can also set the `SUPERUSER_*` variables in your `docker-compose.override.yml`.
|
||||
|
||||
[wiki-getting-started]: https://github.com/netbox-community/netbox-docker/wiki/Getting-Started
|
||||
|
||||
## Container Image Tags
|
||||
|
||||
New container images are built and published automatically every ~24h.
|
||||
|
||||
> We recommend to use either the `vX.Y.Z-a.b.c` tags or the `vX.Y-a.b.c` tags in production!
|
||||
|
||||
- `vX.Y.Z-a.b.c`, `vX.Y-a.b.c`:
|
||||
These are release builds containing _NetBox version_ `vX.Y.Z`.
|
||||
They contain the support files of _NetBox Docker version_ `a.b.c`.
|
||||
You must use _NetBox Docker version_ `a.b.c` to guarantee the compatibility.
|
||||
These images are automatically built from [the corresponding releases of NetBox][netbox-releases].
|
||||
- `latest-a.b.c`:
|
||||
These are release builds, containing the latest stable version of NetBox.
|
||||
They contain the support files of _NetBox Docker version_ `a.b.c`.
|
||||
You must use _NetBox Docker version_ `a.b.c` to guarantee the compatibility.
|
||||
- `snapshot-a.b.c`:
|
||||
These are prerelease builds.
|
||||
They contain the support files of _NetBox Docker version_ `a.b.c`.
|
||||
You must use _NetBox Docker version_ `a.b.c` to guarantee the compatibility.
|
||||
These images are automatically built from the [`main` branch of NetBox][netbox-main].
|
||||
|
||||
For each of the above tag, there is an extra tag:
|
||||
|
||||
- `vX.Y.Z`, `vX.Y`:
|
||||
This is the same version as `vX.Y.Z-a.b.c` (or `vX.Y-a.b.c`, respectively).
|
||||
- `latest`
|
||||
This is the same version as `latest-a.b.c`.
|
||||
It always points to the latest version of _NetBox Docker_.
|
||||
- `snapshot`
|
||||
This is the same version as `snapshot-a.b.c`.
|
||||
It always points to the latest version of _NetBox Docker_.
|
||||
|
||||
[netbox-releases]: https://github.com/netbox-community/netbox/releases
|
||||
[netbox-main]: https://github.com/netbox-community/netbox/tree/main
|
||||
|
||||
## Documentation
|
||||
|
||||
Please refer [to our wiki on GitHub][netbox-docker-wiki] for further information on how to use the NetBox Docker image properly.
|
||||
The wiki covers advanced topics such as using files for secrets, configuring TLS, deployment to Kubernetes, monitoring and configuring LDAP.
|
||||
|
||||
Our wiki is a community effort.
|
||||
Feel free to correct errors, update outdated information or provide additional guides and insights.
|
||||
|
||||
[netbox-docker-wiki]: https://github.com/netbox-community/netbox-docker/wiki/
|
||||
|
||||
## Getting Help
|
||||
|
||||
Feel free to ask questions in our [GitHub Community][netbox-community]
|
||||
or [join our Slack][netbox-docker-slack] and ask [in our channel `#netbox-docker`][netbox-docker-slack-channel],
|
||||
which is free to use and where there are almost always people online that can help you.
|
||||
|
||||
If you need help with using NetBox or developing for it or against it's API
|
||||
you may find [the `#netbox` channel][netbox-slack-channel] on the same Slack instance very helpful.
|
||||
|
||||
[netbox-community]: https://github.com/netbox-community/netbox-docker/discussions
|
||||
|
||||
## Dependencies
|
||||
|
||||
This project relies only on _Docker_ and _docker-compose_ meeting these requirements:
|
||||
|
||||
- The _Docker version_ must be at least `20.10.10`.
|
||||
- The _containerd version_ must be at least `1.5.6`.
|
||||
- The _docker-compose version_ must be at least `1.28.0`.
|
||||
|
||||
To check the version installed on your system run `docker --version` and `docker compose version`.
|
||||
|
||||
## Updating
|
||||
|
||||
Please read [the release notes][releases] carefully when updating to a new image version.
|
||||
Note that the version of the NetBox Docker container image must stay in sync with the version of the Git repository.
|
||||
|
||||
If you update for the first time, be sure [to follow our _How To Update NetBox Docker_ guide in the wiki][netbox-docker-wiki-updating].
|
||||
|
||||
[releases]: https://github.com/netbox-community/netbox-docker/releases
|
||||
[netbox-docker-wiki-updating]: https://github.com/netbox-community/netbox-docker/wiki/Updating
|
||||
|
||||
## Rebuilding the Image
|
||||
|
||||
`./build.sh` can be used to rebuild the container image.
|
||||
See `./build.sh --help` for more information or `./build-latest.sh` for an example.
|
||||
|
||||
For more details on custom builds [consult our wiki][netbox-docker-wiki-build].
|
||||
|
||||
[netbox-docker-wiki-build]: https://github.com/netbox-community/netbox-docker/wiki/Build
|
||||
|
||||
## Tests
|
||||
|
||||
We have a test script.
|
||||
It runs NetBox's own unit tests and ensures that NetBox starts:
|
||||
|
||||
```bash
|
||||
IMAGE=docker.io/netboxcommunity/netbox:latest ./test.sh
|
||||
```
|
||||
|
||||
## Support
|
||||
|
||||
This repository is currently maintained by the community.
|
||||
The community is expected to help each other.
|
||||
|
||||
Please consider sponsoring the maintainers of this project.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
paths:
|
||||
.github/workflows/**/*.{yml,yaml}:
|
||||
ignore:
|
||||
- ".*ubuntu-24.04-arm.*"
|
||||
@@ -0,0 +1,72 @@
|
||||
#!/bin/bash
|
||||
# Скрипт резервного копирования NetBox Docker с отправкой на Яндекс.Диск
|
||||
|
||||
BACKUP_DIR="/opt/netbox/backups"
|
||||
YADISK_MOUNT="/opt/netbox/yadisk"
|
||||
DATE=$(date +%Y%m%d_%H%M%S)
|
||||
|
||||
# Создаем папку для временных бэкапов (с правами текущего пользователя)
|
||||
mkdir -p ${BACKUP_DIR}
|
||||
|
||||
echo "🔄 [$(date)] Начинаем резервное копирование NetBox..."
|
||||
|
||||
# Проверяем, примонтирован ли Яндекс.Диск
|
||||
if ! mountpoint -q ${YADISK_MOUNT}; then
|
||||
echo " ❌ ОШИБКА: Яндекс.Диск не примонтирован!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Создаём папку для бэкапов на Яндекс.Диске (если её нет)
|
||||
mkdir -p ${YADISK_MOUNT}/netbox-backups 2>/dev/null
|
||||
|
||||
# 1. Бэкап базы данных PostgreSQL
|
||||
echo " → Бэкап базы данных..."
|
||||
cd /opt/netbox
|
||||
docker compose exec -T postgres sh -c 'pg_dump -cU $POSTGRES_USER $POSTGRES_DB' | gzip > ${BACKUP_DIR}/netbox_db_${DATE}.sql.gz
|
||||
|
||||
if [ $? -eq 0 ] && [ -s ${BACKUP_DIR}/netbox_db_${DATE}.sql.gz ]; then
|
||||
echo " ✅ База данных: $(du -h ${BACKUP_DIR}/netbox_db_${DATE}.sql.gz | cut -f1)"
|
||||
else
|
||||
echo " ❌ Ошибка бэкапа базы данных!"
|
||||
fi
|
||||
|
||||
# 2. Бэкап медиа-файлов
|
||||
echo " → Бэкап медиа-файлов..."
|
||||
docker compose exec -T netbox tar czf - -C /opt/netbox/netbox/media . 2>/dev/null > ${BACKUP_DIR}/netbox_media_${DATE}.tar.gz
|
||||
|
||||
if [ -s ${BACKUP_DIR}/netbox_media_${DATE}.tar.gz ]; then
|
||||
echo " ✅ Медиа-файлы: $(du -h ${BACKUP_DIR}/netbox_media_${DATE}.tar.gz | cut -f1)"
|
||||
fi
|
||||
|
||||
# 3. Бэкап .env файлов
|
||||
echo " → Бэкап .env файлов..."
|
||||
tar czf ${BACKUP_DIR}/netbox_env_${DATE}.tar.gz -C /opt/netbox env/ 2>/dev/null
|
||||
|
||||
if [ -s ${BACKUP_DIR}/netbox_env_${DATE}.tar.gz ]; then
|
||||
echo " ✅ .env файлы: $(du -h ${BACKUP_DIR}/netbox_env_${DATE}.tar.gz | cut -f1)"
|
||||
fi
|
||||
|
||||
# 4. Копирование на Яндекс.Диск
|
||||
echo "☁️ Копирование на Яндекс.Диск..."
|
||||
|
||||
# Копируем файлы, если они существуют
|
||||
for file in netbox_db_${DATE}.sql.gz netbox_media_${DATE}.tar.gz netbox_env_${DATE}.tar.gz; do
|
||||
if [ -f ${BACKUP_DIR}/${file} ]; then
|
||||
cp ${BACKUP_DIR}/${file} ${YADISK_MOUNT}/netbox-backups/
|
||||
echo " ✅ ${file}"
|
||||
fi
|
||||
done
|
||||
|
||||
# 5. Очистка старых бэкапов
|
||||
# Локально: оставляем 7 дней
|
||||
find ${BACKUP_DIR} -type f -name "netbox_*" -mtime +7 -delete 2>/dev/null
|
||||
|
||||
# На Яндекс.Диске: оставляем 30 дней
|
||||
find ${YADISK_MOUNT}/netbox-backups -type f -name "netbox_*" -mtime +30 -delete 2>/dev/null
|
||||
|
||||
echo "✅ [$(date)] Резервное копирование завершено!"
|
||||
|
||||
# Показываем список созданных бэкапов
|
||||
echo ""
|
||||
echo "📊 Созданные бэкапы:"
|
||||
ls -lh ${BACKUP_DIR}/netbox_*_${DATE}.* 2>/dev/null || echo " (нет файлов)"
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,9 @@
|
||||
#!/bin/bash
|
||||
|
||||
NEEDED_COMMANDS="curl jq docker skopeo"
|
||||
for c in $NEEDED_COMMANDS; do
|
||||
if ! command -v "$c" &>/dev/null; then
|
||||
echo "⚠️ '$c' is not installed. Can't proceed with build."
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
@@ -0,0 +1,18 @@
|
||||
#!/bin/bash
|
||||
|
||||
check_if_tags_exists() {
|
||||
local image=$1
|
||||
local tag=$2
|
||||
skopeo list-tags "docker://$image" | jq -r ".Tags | contains([\"$tag\"])"
|
||||
}
|
||||
|
||||
get_image_label() {
|
||||
local label=$1
|
||||
local image=$2
|
||||
skopeo inspect "docker://$image" | jq -r ".Labels[\"$label\"]"
|
||||
}
|
||||
|
||||
get_image_last_layer() {
|
||||
local image=$1
|
||||
skopeo inspect "docker://$image" | jq -r ".Layers | last"
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
#!/bin/bash
|
||||
|
||||
###
|
||||
# A regular echo, that only prints if ${GH_ACTION} is defined.
|
||||
###
|
||||
gh_echo() {
|
||||
if [ -n "${GH_ACTION}" ]; then
|
||||
echo "${@}"
|
||||
fi
|
||||
}
|
||||
|
||||
###
|
||||
# Prints the output to the file defined in ${GITHUB_ENV}.
|
||||
# Only executes if ${GH_ACTION} is defined.
|
||||
# Example Usage: gh_env "FOO_VAR=bar_value"
|
||||
###
|
||||
gh_env() {
|
||||
if [ -n "${GH_ACTION}" ]; then
|
||||
echo "${@}" >>"${GITHUB_ENV}"
|
||||
fi
|
||||
}
|
||||
|
||||
###
|
||||
# Prints the output to the file defined in ${GITHUB_OUTPUT}.
|
||||
# Only executes if ${GH_ACTION} is defined.
|
||||
# Example Usage: gh_env "FOO_VAR=bar_value"
|
||||
###
|
||||
gh_out() {
|
||||
if [ -n "${GH_ACTION}" ]; then
|
||||
echo "${@}" >>"$GITHUB_OUTPUT"
|
||||
fi
|
||||
}
|
||||
Executable
+85
@@ -0,0 +1,85 @@
|
||||
#!/bin/bash
|
||||
# Builds the latest released version
|
||||
|
||||
# Check if we have everything needed for the build
|
||||
source ./build-functions/check-commands.sh
|
||||
|
||||
source ./build-functions/gh-functions.sh
|
||||
|
||||
echo "▶️ $0 $*"
|
||||
|
||||
CURL_ARGS=(
|
||||
--silent
|
||||
)
|
||||
|
||||
###
|
||||
# Checking for the presence of GITHUB_TOKEN
|
||||
###
|
||||
if [ -n "${GITHUB_TOKEN}" ]; then
|
||||
echo "🗝 Performing authenticated Github API calls."
|
||||
CURL_ARGS+=(
|
||||
--header "Authorization: Bearer ${GITHUB_TOKEN}"
|
||||
)
|
||||
else
|
||||
echo "🕶 Performing unauthenticated Github API calls. This might result in lower Github rate limits!"
|
||||
fi
|
||||
|
||||
###
|
||||
# Checking if PRERELEASE is either unset, 'true' or 'false'
|
||||
###
|
||||
if [ -n "${PRERELEASE}" ] &&
|
||||
{ [ "${PRERELEASE}" != "true" ] && [ "${PRERELEASE}" != "false" ]; }; then
|
||||
|
||||
if [ -z "${DEBUG}" ]; then
|
||||
echo "⚠️ PRERELEASE must be either unset, 'true' or 'false', but was '${PRERELEASE}'!"
|
||||
exit 1
|
||||
else
|
||||
echo "⚠️ Would exit here with code '1', but DEBUG is enabled."
|
||||
fi
|
||||
fi
|
||||
|
||||
###
|
||||
# Calling Github to get the latest version
|
||||
###
|
||||
ORIGINAL_GITHUB_REPO="netbox-community/netbox"
|
||||
GITHUB_REPO="${GITHUB_REPO-$ORIGINAL_GITHUB_REPO}"
|
||||
URL_RELEASES="https://api.github.com/repos/${GITHUB_REPO}/releases"
|
||||
|
||||
# Composing the JQ commans to extract the most recent version number
|
||||
JQ_LATEST="group_by(.prerelease) | .[] | sort_by(.published_at) | reverse | .[0] | select(.prerelease==${PRERELEASE-false}) | .tag_name"
|
||||
|
||||
CURL="curl"
|
||||
|
||||
# Querying the Github API to fetch the most recent version number
|
||||
VERSION=$($CURL "${CURL_ARGS[@]}" "${URL_RELEASES}" | jq -r "${JQ_LATEST}" 2>/dev/null)
|
||||
|
||||
###
|
||||
# Check if the prerelease version is actually higher than stable version
|
||||
###
|
||||
if [ "${PRERELEASE}" == "true" ]; then
|
||||
JQ_STABLE="group_by(.prerelease) | .[] | sort_by(.published_at) | reverse | .[0] | select(.prerelease==false) | .tag_name"
|
||||
STABLE_VERSION=$($CURL "${CURL_ARGS[@]}" "${URL_RELEASES}" | jq -r "${JQ_STABLE}" 2>/dev/null)
|
||||
|
||||
MAJOR_STABLE=$(expr "${STABLE_VERSION}" : 'v\([0-9]\+\)')
|
||||
MINOR_STABLE=$(expr "${STABLE_VERSION}" : 'v[0-9]\+\.\([0-9]\+\)')
|
||||
MAJOR_UNSTABLE=$(expr "${VERSION}" : 'v\([0-9]\+\)')
|
||||
MINOR_UNSTABLE=$(expr "${VERSION}" : 'v[0-9]\+\.\([0-9]\+\)')
|
||||
|
||||
if {
|
||||
[ "${MAJOR_STABLE}" -eq "${MAJOR_UNSTABLE}" ] &&
|
||||
[ "${MINOR_STABLE}" -ge "${MINOR_UNSTABLE}" ]
|
||||
} || [ "${MAJOR_STABLE}" -gt "${MAJOR_UNSTABLE}" ]; then
|
||||
|
||||
echo "❎ Latest unstable version '${VERSION}' is not higher than the latest stable version '$STABLE_VERSION'."
|
||||
if [ -z "$DEBUG" ]; then
|
||||
gh_out "skipped=true"
|
||||
exit 0
|
||||
else
|
||||
echo "⚠️ Would exit here with code '0', but DEBUG is enabled."
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# shellcheck disable=SC2068
|
||||
./build.sh "${VERSION}" $@
|
||||
exit $?
|
||||
@@ -0,0 +1,460 @@
|
||||
#!/bin/bash
|
||||
# Clones the NetBox repository with git from Github and builds the Dockerfile
|
||||
|
||||
echo "▶️ $0 $*"
|
||||
|
||||
set -e
|
||||
|
||||
if [ "${1}x" == "x" ] || [ "${1}" == "--help" ] || [ "${1}" == "-h" ]; then
|
||||
_BOLD=$(tput bold)
|
||||
_GREEN=$(tput setaf 2)
|
||||
_CYAN=$(tput setaf 6)
|
||||
_CLEAR=$(tput sgr0)
|
||||
|
||||
cat <<END_OF_HELP
|
||||
${_BOLD}Usage:${_CLEAR} ${0} <branch> [--push]
|
||||
|
||||
branch The branch or tag to build. Required.
|
||||
--push Pushes the built container image to the registry.
|
||||
|
||||
${_BOLD}You can use the following ENV variables to customize the build:${_CLEAR}
|
||||
|
||||
SRC_ORG Which fork of netbox to use (i.e. github.com/\${SRC_ORG}/\${SRC_REPO}).
|
||||
${_GREEN}Default:${_CLEAR} netbox-community
|
||||
|
||||
SRC_REPO The name of the repository to use (i.e. github.com/\${SRC_ORG}/\${SRC_REPO}).
|
||||
${_GREEN}Default:${_CLEAR} netbox
|
||||
|
||||
URL Where to fetch the code from.
|
||||
Must be a git repository. Can be private.
|
||||
${_GREEN}Default:${_CLEAR} https://github.com/\${SRC_ORG}/\${SRC_REPO}.git
|
||||
|
||||
NETBOX_PATH The path where netbox will be checkout out.
|
||||
Must not be outside of the netbox-docker repository (because of Docker)!
|
||||
${_GREEN}Default:${_CLEAR} .netbox
|
||||
|
||||
SKIP_GIT If defined, git is not invoked and \${NETBOX_PATH} will not be altered.
|
||||
This may be useful, if you are manually managing the NETBOX_PATH.
|
||||
${_GREEN}Default:${_CLEAR} undefined
|
||||
|
||||
TAG The version part of the image tag.
|
||||
${_GREEN}Default:${_CLEAR}
|
||||
When <branch>=main: snapshot
|
||||
Else: same as <branch>
|
||||
|
||||
IMAGE_NAMES The names used for the image including the registry
|
||||
Used for tagging the image.
|
||||
${_GREEN}Default:${_CLEAR} docker.io/netboxcommunity/netbox
|
||||
${_CYAN}Example:${_CLEAR} 'docker.io/netboxcommunity/netbox quay.io/netboxcommunity/netbox'
|
||||
|
||||
DOCKER_TAG The name of the tag which is applied to the image.
|
||||
Useful for pushing into another registry than hub.docker.com.
|
||||
${_GREEN}Default:${_CLEAR} \${DOCKER_REGISTRY}/\${DOCKER_ORG}/\${DOCKER_REPO}:\${TAG}
|
||||
|
||||
DOCKER_SHORT_TAG The name of the short tag which is applied to the
|
||||
image. This is used to tag all patch releases to their
|
||||
containing version e.g. v2.5.1 -> v2.5
|
||||
${_GREEN}Default:${_CLEAR} \${DOCKER_REGISTRY}/\${DOCKER_ORG}/\${DOCKER_REPO}:<MAJOR>.<MINOR>
|
||||
|
||||
DOCKERFILE The name of Dockerfile to use.
|
||||
${_GREEN}Default:${_CLEAR} Dockerfile
|
||||
|
||||
DOCKER_FROM The base image to use.
|
||||
${_GREEN}Default:${_CLEAR} 'ubuntu:26.04'
|
||||
|
||||
BUILDX_PLATFORM
|
||||
Specifies the platform(s) to build the image for.
|
||||
${_CYAN}Example:${_CLEAR} 'linux/amd64,linux/arm64'
|
||||
${_GREEN}Default:${_CLEAR} 'linux/amd64'
|
||||
|
||||
BUILDX_BUILDER_NAME
|
||||
If defined, the image build will be assigned to the given builder.
|
||||
If you specify this variable, make sure that the builder exists.
|
||||
If this value is not defined, a new builx builder with the directory name of the
|
||||
current directory (i.e. '$(basename "${PWD}")') is created."
|
||||
${_CYAN}Example:${_CLEAR} 'clever_lovelace'
|
||||
${_GREEN}Default:${_CLEAR} undefined
|
||||
|
||||
BUILDX_REMOVE_BUILDER
|
||||
If defined (and only if BUILDX_BUILDER_NAME is undefined),
|
||||
then the buildx builder created by this script will be removed after use.
|
||||
This is useful if you build NetBox Docker on an automated system that does
|
||||
not manage the builders for you.
|
||||
${_CYAN}Example:${_CLEAR} 'on'
|
||||
${_GREEN}Default:${_CLEAR} undefined
|
||||
|
||||
HTTP_PROXY The proxy to use for http requests.
|
||||
${_CYAN}Example:${_CLEAR} http://proxy.domain.tld:3128
|
||||
${_GREEN}Default:${_CLEAR} undefined
|
||||
|
||||
NO_PROXY Comma-separated list of domain extensions proxy should not be used for.
|
||||
${_CYAN}Example:${_CLEAR} .domain1.tld,.domain2.tld
|
||||
${_GREEN}Default:${_CLEAR} undefined
|
||||
|
||||
DEBUG If defined, the script does not stop when certain checks are unsatisfied.
|
||||
${_GREEN}Default:${_CLEAR} undefined
|
||||
|
||||
DRY_RUN Prints all build statements instead of running them.
|
||||
${_GREEN}Default:${_CLEAR} undefined
|
||||
|
||||
GH_ACTION If defined, special 'echo' statements are enabled that set the
|
||||
following environment variables in Github Actions:
|
||||
- FINAL_DOCKER_TAG: The final value of the DOCKER_TAG env variable
|
||||
${_GREEN}Default:${_CLEAR} undefined
|
||||
|
||||
CHECK_ONLY Only checks if the build is needed and sets the GH Action output.
|
||||
${_GREEN}Default:${_CLEAR} undefined
|
||||
|
||||
${_BOLD}Examples:${_CLEAR}
|
||||
|
||||
${0} main
|
||||
This will fetch the latest 'main' branch, build a Docker Image and tag it
|
||||
'netboxcommunity/netbox:snapshot'.
|
||||
|
||||
${0} v4.2.0
|
||||
This will fetch the 'v4.2.0' tag, build a Docker Image and tag it
|
||||
'netboxcommunity/netbox:v4.2.0' and 'netboxcommunity/netbox:v4.2'.
|
||||
|
||||
${0} feature
|
||||
This will fetch the 'feature' branch, build a Docker Image and tag it
|
||||
'netboxcommunity/netbox:feature'.
|
||||
|
||||
SRC_ORG=cimnine ${0} feature-x
|
||||
This will fetch the 'feature-x' branch from https://github.com/cimnine/netbox.git,
|
||||
build a Docker Image and tag it 'netboxcommunity/netbox:feature-x'.
|
||||
|
||||
SRC_ORG=cimnine DOCKER_ORG=cimnine ${0} feature-x
|
||||
This will fetch the 'feature-x' branch from https://github.com/cimnine/netbox.git,
|
||||
build a Docker Image and tag it 'cimnine/netbox:feature-x'.
|
||||
END_OF_HELP
|
||||
|
||||
if [ "${1}x" == "x" ]; then
|
||||
exit 1
|
||||
else
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
|
||||
# Check if we have everything needed for the build
|
||||
source ./build-functions/check-commands.sh
|
||||
# Load all build functions
|
||||
source ./build-functions/get-public-image-config.sh
|
||||
source ./build-functions/gh-functions.sh
|
||||
|
||||
IMAGE_NAMES="${IMAGE_NAMES-docker.io/netboxcommunity/netbox}"
|
||||
IFS=' ' read -ra IMAGE_NAMES <<<"${IMAGE_NAMES}"
|
||||
|
||||
###
|
||||
# Enabling dry-run mode
|
||||
###
|
||||
if [ -z "${DRY_RUN}" ]; then
|
||||
DRY=""
|
||||
else
|
||||
echo "⚠️ DRY_RUN MODE ON ⚠️"
|
||||
DRY="echo"
|
||||
fi
|
||||
|
||||
gh_echo "::group::⤵️ Fetching the NetBox source code"
|
||||
|
||||
###
|
||||
# Variables for fetching the NetBox source
|
||||
###
|
||||
SRC_ORG="${SRC_ORG-netbox-community}"
|
||||
SRC_REPO="${SRC_REPO-netbox}"
|
||||
NETBOX_BRANCH="${1}"
|
||||
URL="${URL-https://github.com/${SRC_ORG}/${SRC_REPO}.git}"
|
||||
NETBOX_PATH="${NETBOX_PATH-.netbox}"
|
||||
|
||||
###
|
||||
# Fetching the NetBox source
|
||||
###
|
||||
if [ "${2}" != "--push-only" ] && [ -z "${SKIP_GIT}" ]; then
|
||||
REMOTE_EXISTS=$(git ls-remote --heads --tags "${URL}" "${NETBOX_BRANCH}" | wc -l)
|
||||
if [ "${REMOTE_EXISTS}" == "0" ]; then
|
||||
echo "❌ Remote branch '${NETBOX_BRANCH}' not found in '${URL}'; Nothing to do"
|
||||
gh_out "skipped=true"
|
||||
exit 0
|
||||
fi
|
||||
echo "🌐 Checking out '${NETBOX_BRANCH}' of NetBox from the url '${URL}' into '${NETBOX_PATH}'"
|
||||
if [ ! -d "${NETBOX_PATH}" ]; then
|
||||
$DRY git clone -q --depth 10 -b "${NETBOX_BRANCH}" "${URL}" "${NETBOX_PATH}"
|
||||
fi
|
||||
|
||||
(
|
||||
$DRY cd "${NETBOX_PATH}"
|
||||
# shellcheck disable=SC2030
|
||||
if [ -n "${HTTP_PROXY}" ]; then
|
||||
git config http.proxy "${HTTP_PROXY}"
|
||||
fi
|
||||
|
||||
$DRY git remote set-url origin "${URL}"
|
||||
$DRY git fetch -qp --depth 10 origin "${NETBOX_BRANCH}"
|
||||
$DRY git checkout -qf FETCH_HEAD
|
||||
$DRY git prune
|
||||
)
|
||||
echo "✅ Checked out NetBox"
|
||||
fi
|
||||
|
||||
gh_echo "::endgroup::"
|
||||
gh_echo "::group::🧮 Calculating Values"
|
||||
|
||||
###
|
||||
# Determining the value for DOCKERFILE
|
||||
# and checking whether it exists
|
||||
###
|
||||
DOCKERFILE="${DOCKERFILE-Dockerfile}"
|
||||
if [ ! -f "${DOCKERFILE}" ]; then
|
||||
echo "🚨 The Dockerfile ${DOCKERFILE} doesn't exist."
|
||||
|
||||
if [ -z "${DEBUG}" ]; then
|
||||
exit 1
|
||||
else
|
||||
echo "⚠️ Would exit here with code '1', but DEBUG is enabled."
|
||||
fi
|
||||
fi
|
||||
|
||||
###
|
||||
# Determining the value for DOCKER_FROM
|
||||
###
|
||||
if [ -z "$DOCKER_FROM" ]; then
|
||||
DOCKER_FROM="docker.io/ubuntu:26.04"
|
||||
fi
|
||||
|
||||
###
|
||||
# Variables for labelling the docker image
|
||||
###
|
||||
BUILD_DATE="$(date -u '+%Y-%m-%dT%H:%M:%S+00:00')"
|
||||
|
||||
if [ -d ".git" ] && [ -z "${SKIP_GIT}" ]; then
|
||||
GIT_REF="$(git rev-parse HEAD)"
|
||||
fi
|
||||
|
||||
# Read the project version from the `VERSION` file and trim it, see https://stackoverflow.com/a/3232433/172132
|
||||
PROJECT_VERSION="${PROJECT_VERSION-$(sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' VERSION)}"
|
||||
|
||||
# Get the Git information from the netbox directory
|
||||
if [ -d "${NETBOX_PATH}/.git" ] && [ -z "${SKIP_GIT}" ]; then
|
||||
NETBOX_GIT_REF=$(
|
||||
cd "${NETBOX_PATH}"
|
||||
git rev-parse HEAD
|
||||
)
|
||||
NETBOX_GIT_BRANCH=$(
|
||||
cd "${NETBOX_PATH}"
|
||||
git rev-parse --abbrev-ref HEAD
|
||||
)
|
||||
NETBOX_GIT_URL=$(
|
||||
cd "${NETBOX_PATH}"
|
||||
git remote get-url origin
|
||||
)
|
||||
fi
|
||||
|
||||
###
|
||||
# Variables for tagging the docker image
|
||||
###
|
||||
DOCKER_REGISTRY="${DOCKER_REGISTRY-docker.io}"
|
||||
DOCKER_ORG="${DOCKER_ORG-netboxcommunity}"
|
||||
DOCKER_REPO="${DOCKER_REPO-netbox}"
|
||||
case "${NETBOX_BRANCH}" in
|
||||
main)
|
||||
TAG="${TAG-snapshot}"
|
||||
;;
|
||||
*)
|
||||
TAG="${TAG-$NETBOX_BRANCH}"
|
||||
;;
|
||||
esac
|
||||
|
||||
###
|
||||
# composing the final TARGET_DOCKER_TAG
|
||||
###
|
||||
TARGET_DOCKER_TAG="${DOCKER_TAG-${TAG}}"
|
||||
TARGET_DOCKER_TAG_PROJECT="${TARGET_DOCKER_TAG}-${PROJECT_VERSION}"
|
||||
|
||||
###
|
||||
# composing the additional DOCKER_SHORT_TAG,
|
||||
# i.e. "v4.2.0" becomes "v4.2",
|
||||
# which is only relevant for version tags
|
||||
# Also let "latest" follow the highest version
|
||||
###
|
||||
if [[ "${TAG}" =~ ^v([0-9]+)\.([0-9]+)\.[0-9]+$ ]]; then
|
||||
MAJOR=${BASH_REMATCH[1]}
|
||||
MINOR=${BASH_REMATCH[2]}
|
||||
|
||||
TARGET_DOCKER_SHORT_TAG="${DOCKER_SHORT_TAG-v${MAJOR}.${MINOR}}"
|
||||
TARGET_DOCKER_LATEST_TAG="latest"
|
||||
TARGET_DOCKER_SHORT_TAG_PROJECT="${TARGET_DOCKER_SHORT_TAG}-${PROJECT_VERSION}"
|
||||
TARGET_DOCKER_LATEST_TAG_PROJECT="${TARGET_DOCKER_LATEST_TAG}-${PROJECT_VERSION}"
|
||||
fi
|
||||
|
||||
IMAGE_NAME_TAGS=()
|
||||
for IMAGE_NAME in "${IMAGE_NAMES[@]}"; do
|
||||
IMAGE_NAME_TAGS+=("${IMAGE_NAME}:${TARGET_DOCKER_TAG}")
|
||||
IMAGE_NAME_TAGS+=("${IMAGE_NAME}:${TARGET_DOCKER_TAG_PROJECT}")
|
||||
done
|
||||
if [ -n "${TARGET_DOCKER_SHORT_TAG}" ]; then
|
||||
for IMAGE_NAME in "${IMAGE_NAMES[@]}"; do
|
||||
IMAGE_NAME_TAGS+=("${IMAGE_NAME}:${TARGET_DOCKER_SHORT_TAG}")
|
||||
IMAGE_NAME_TAGS+=("${IMAGE_NAME}:${TARGET_DOCKER_SHORT_TAG_PROJECT}")
|
||||
IMAGE_NAME_TAGS+=("${IMAGE_NAME}:${TARGET_DOCKER_LATEST_TAG}")
|
||||
IMAGE_NAME_TAGS+=("${IMAGE_NAME}:${TARGET_DOCKER_LATEST_TAG_PROJECT}")
|
||||
done
|
||||
fi
|
||||
|
||||
FINAL_DOCKER_TAG="${IMAGE_NAME_TAGS[0]}"
|
||||
gh_env "FINAL_DOCKER_TAG=${IMAGE_NAME_TAGS[0]}"
|
||||
|
||||
###
|
||||
# Checking if the build is necessary,
|
||||
# meaning build only if one of those values changed:
|
||||
# - a new tag is being created
|
||||
# - base image digest
|
||||
# - netbox git ref (Label: netbox.git-ref)
|
||||
# - netbox-docker git ref (Label: org.opencontainers.image.revision)
|
||||
###
|
||||
# Load information from registry (only for first registry in "IMAGE_NAMES")
|
||||
SHOULD_BUILD="false"
|
||||
BUILD_REASON=""
|
||||
if [ -z "${GH_ACTION}" ]; then
|
||||
# Assuming non Github builds should always proceed
|
||||
SHOULD_BUILD="true"
|
||||
BUILD_REASON="${BUILD_REASON} interactive"
|
||||
elif [ "false" == "$(check_if_tags_exists "${IMAGE_NAMES[0]}" "$TARGET_DOCKER_TAG")" ]; then
|
||||
SHOULD_BUILD="true"
|
||||
BUILD_REASON="${BUILD_REASON} newtag"
|
||||
else
|
||||
echo "Checking labels for '${FINAL_DOCKER_TAG}'"
|
||||
BASE_LAST_LAYER=$(get_image_last_layer "${DOCKER_FROM}")
|
||||
OLD_BASE_LAST_LAYER=$(get_image_label netbox.last-base-image-layer "${FINAL_DOCKER_TAG}")
|
||||
NETBOX_GIT_REF_OLD=$(get_image_label netbox.git-ref "${FINAL_DOCKER_TAG}")
|
||||
GIT_REF_OLD=$(get_image_label org.opencontainers.image.revision "${FINAL_DOCKER_TAG}")
|
||||
|
||||
if [ "${BASE_LAST_LAYER}" != "${OLD_BASE_LAST_LAYER}" ]; then
|
||||
SHOULD_BUILD="true"
|
||||
BUILD_REASON="${BUILD_REASON} ubuntu"
|
||||
fi
|
||||
if [ "${NETBOX_GIT_REF}" != "${NETBOX_GIT_REF_OLD}" ]; then
|
||||
SHOULD_BUILD="true"
|
||||
BUILD_REASON="${BUILD_REASON} netbox"
|
||||
fi
|
||||
if [ "${GIT_REF}" != "${GIT_REF_OLD}" ]; then
|
||||
SHOULD_BUILD="true"
|
||||
BUILD_REASON="${BUILD_REASON} netbox-docker"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "${SHOULD_BUILD}" != "true" ]; then
|
||||
echo "Build skipped because sources didn't change"
|
||||
gh_out "skipped=true"
|
||||
exit 0 # Nothing to do -> exit
|
||||
else
|
||||
gh_out "skipped=false"
|
||||
fi
|
||||
gh_echo "::endgroup::"
|
||||
|
||||
if [ "${CHECK_ONLY}" = "true" ]; then
|
||||
echo "Only check if build needed was requested. Exiting"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
###
|
||||
# Build the image
|
||||
###
|
||||
gh_echo "::group::🏗 Building the image"
|
||||
###
|
||||
# Composing all arguments for `docker build`
|
||||
###
|
||||
DOCKER_BUILD_ARGS=(
|
||||
--pull
|
||||
--target main
|
||||
-f "${DOCKERFILE}"
|
||||
)
|
||||
for IMAGE_NAME in "${IMAGE_NAME_TAGS[@]}"; do
|
||||
DOCKER_BUILD_ARGS+=(-t "${IMAGE_NAME}")
|
||||
done
|
||||
|
||||
# --label
|
||||
DOCKER_BUILD_ARGS+=(
|
||||
--label "netbox.original-tag=${TARGET_DOCKER_TAG_PROJECT}"
|
||||
--label "org.opencontainers.image.created=${BUILD_DATE}"
|
||||
--label "org.opencontainers.image.version=${PROJECT_VERSION}"
|
||||
)
|
||||
if [ -d ".git" ] && [ -z "${SKIP_GIT}" ]; then
|
||||
DOCKER_BUILD_ARGS+=(
|
||||
--label "org.opencontainers.image.revision=${GIT_REF}"
|
||||
)
|
||||
fi
|
||||
if [ -d "${NETBOX_PATH}/.git" ] && [ -z "${SKIP_GIT}" ]; then
|
||||
DOCKER_BUILD_ARGS+=(
|
||||
--label "netbox.git-branch=${NETBOX_GIT_BRANCH}"
|
||||
--label "netbox.git-ref=${NETBOX_GIT_REF}"
|
||||
--label "netbox.git-url=${NETBOX_GIT_URL}"
|
||||
)
|
||||
fi
|
||||
if [ -n "${BUILD_REASON}" ]; then
|
||||
BUILD_REASON=$(sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' <<<"$BUILD_REASON")
|
||||
DOCKER_BUILD_ARGS+=(--label "netbox.build-reason=${BUILD_REASON}")
|
||||
DOCKER_BUILD_ARGS+=(--label "netbox.last-base-image-layer=${BASE_LAST_LAYER}")
|
||||
fi
|
||||
|
||||
# --build-arg
|
||||
DOCKER_BUILD_ARGS+=(--build-arg "NETBOX_PATH=${NETBOX_PATH}")
|
||||
|
||||
if [ -n "${DOCKER_FROM}" ]; then
|
||||
DOCKER_BUILD_ARGS+=(--build-arg "FROM=${DOCKER_FROM}")
|
||||
fi
|
||||
# shellcheck disable=SC2031
|
||||
if [ -n "${HTTP_PROXY}" ]; then
|
||||
DOCKER_BUILD_ARGS+=(--build-arg "http_proxy=${HTTP_PROXY}")
|
||||
fi
|
||||
if [ -n "${HTTPS_PROXY}" ]; then
|
||||
DOCKER_BUILD_ARGS+=(--build-arg "https_proxy=${HTTPS_PROXY}")
|
||||
fi
|
||||
if [ -n "${NO_PROXY}" ]; then
|
||||
DOCKER_BUILD_ARGS+=(--build-arg "no_proxy=${NO_PROXY}")
|
||||
fi
|
||||
|
||||
DOCKER_BUILD_ARGS+=(--platform "${BUILDX_PLATFORM-linux/amd64}")
|
||||
if [ "${2}" == "--push" ]; then
|
||||
# output type=docker does not work with pushing
|
||||
DOCKER_BUILD_ARGS+=(
|
||||
--output=type=image
|
||||
--push
|
||||
)
|
||||
else
|
||||
DOCKER_BUILD_ARGS+=(
|
||||
--output=type=docker
|
||||
)
|
||||
fi
|
||||
|
||||
###
|
||||
# Building the docker image
|
||||
###
|
||||
if [ -z "${BUILDX_BUILDER_NAME}" ]; then
|
||||
BUILDX_BUILDER_NAME="$(basename "${PWD}")"
|
||||
fi
|
||||
if ! docker buildx ls | grep --quiet --word-regexp "${BUILDX_BUILDER_NAME}"; then
|
||||
echo "👷 Creating new Buildx Builder '${BUILDX_BUILDER_NAME}'"
|
||||
$DRY docker buildx create --name "${BUILDX_BUILDER_NAME}"
|
||||
BUILDX_BUILDER_CREATED="yes"
|
||||
fi
|
||||
|
||||
echo "🐳 Building the Docker image '${TARGET_DOCKER_TAG_PROJECT}'."
|
||||
echo " Build reason set to: ${BUILD_REASON}"
|
||||
$DRY docker buildx \
|
||||
--builder "${BUILDX_BUILDER_NAME}" \
|
||||
build \
|
||||
"${DOCKER_BUILD_ARGS[@]}" \
|
||||
.
|
||||
echo "✅ Finished building the Docker images"
|
||||
gh_echo "::endgroup::" # End group for Build
|
||||
|
||||
gh_echo "::group::🏗 Image Labels"
|
||||
echo "🔎 Inspecting labels on '${IMAGE_NAME_TAGS[0]}'"
|
||||
$DRY docker inspect "${IMAGE_NAME_TAGS[0]}" --format "{{json .Config.Labels}}" | jq
|
||||
gh_echo "::endgroup::"
|
||||
|
||||
gh_echo "::group::🏗 Clean up"
|
||||
if [ -n "${BUILDX_REMOVE_BUILDER}" ] && [ "${BUILDX_BUILDER_CREATED}" == "yes" ]; then
|
||||
echo "👷 Removing Buildx Builder '${BUILDX_BUILDER_NAME}'"
|
||||
$DRY docker buildx rm "${BUILDX_BUILDER_NAME}"
|
||||
fi
|
||||
gh_echo "::endgroup::"
|
||||
@@ -0,0 +1,393 @@
|
||||
####
|
||||
## We recommend to not edit this file.
|
||||
## Create separate files to overwrite the settings.
|
||||
## See `extra.py` as an example.
|
||||
####
|
||||
|
||||
import re
|
||||
from collections.abc import Callable
|
||||
from os import environ
|
||||
from os.path import abspath, dirname, join
|
||||
from typing import Any
|
||||
|
||||
# For reference see https://docs.netbox.dev/en/stable/configuration/
|
||||
# Based on https://github.com/netbox-community/netbox/blob/develop/netbox/netbox/configuration_example.py
|
||||
|
||||
###
|
||||
# NetBox-Docker Helper functions
|
||||
###
|
||||
|
||||
|
||||
# Read secret from file
|
||||
def _read_secret(secret_name: str, default: str | None = None) -> str | None:
|
||||
try:
|
||||
with open('/run/secrets/' + secret_name, encoding='utf-8') as f:
|
||||
return f.readline().strip()
|
||||
except OSError:
|
||||
return default
|
||||
|
||||
|
||||
# If the `map_fn` isn't defined, then the value that is read from the environment (or the default value if not found) is returned.
|
||||
# If the `map_fn` is defined, then `map_fn` is invoked and the value (that was read from the environment or the default value if not found)
|
||||
# is passed to it as a parameter. The value returned from `map_fn` is then the return value of this function.
|
||||
# The `map_fn` is not invoked, if the value (that was read from the environment or the default value if not found) is None.
|
||||
def _environ_get_and_map(
|
||||
variable_name: str, default: str | None = None, map_fn: Callable[[str], Any | None] | None = None
|
||||
) -> Any | None:
|
||||
env_value = environ.get(variable_name, default)
|
||||
|
||||
if env_value == None:
|
||||
return env_value
|
||||
|
||||
if not map_fn:
|
||||
return env_value
|
||||
|
||||
return map_fn(env_value)
|
||||
|
||||
|
||||
_AS_BOOL = lambda value: value.lower() == 'true'
|
||||
_AS_INT = lambda value: int(value)
|
||||
_AS_LIST = lambda value: list(filter(None, value.split(' ')))
|
||||
|
||||
_BASE_DIR = dirname(dirname(abspath(__file__)))
|
||||
|
||||
#########################
|
||||
# #
|
||||
# Required settings #
|
||||
# #
|
||||
#########################
|
||||
|
||||
# This is a list of valid fully-qualified domain names (FQDNs) for the NetBox server. NetBox will not permit write
|
||||
# access to the server via any other hostnames. The first FQDN in the list will be treated as the preferred name.
|
||||
#
|
||||
# Example: ALLOWED_HOSTS = ['netbox.example.com', 'netbox.internal.local']
|
||||
ALLOWED_HOSTS = environ.get('ALLOWED_HOSTS', '*').split(' ')
|
||||
# ensure that '*' or 'localhost' is always in ALLOWED_HOSTS (needed for health checks)
|
||||
if '*' not in ALLOWED_HOSTS and 'localhost' not in ALLOWED_HOSTS:
|
||||
ALLOWED_HOSTS.append('localhost')
|
||||
|
||||
# PostgreSQL database configuration. See the Django documentation for a complete list of available parameters:
|
||||
# https://docs.djangoproject.com/en/stable/ref/settings/#databases
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'NAME': environ.get('DB_NAME', 'netbox'), # Database name
|
||||
'USER': environ.get('DB_USER', ''), # PostgreSQL username
|
||||
'PASSWORD': _read_secret('db_password', environ.get('DB_PASSWORD', '')),
|
||||
# PostgreSQL password
|
||||
'HOST': environ.get('DB_HOST', 'localhost'), # Database server
|
||||
'PORT': environ.get('DB_PORT', ''), # Database port (leave blank for default)
|
||||
'OPTIONS': {'sslmode': environ.get('DB_SSLMODE', 'prefer')},
|
||||
# Database connection SSLMODE
|
||||
'CONN_MAX_AGE': _environ_get_and_map('DB_CONN_MAX_AGE', '300', _AS_INT),
|
||||
# Max database connection age
|
||||
'DISABLE_SERVER_SIDE_CURSORS': _environ_get_and_map('DB_DISABLE_SERVER_SIDE_CURSORS', 'False', _AS_BOOL),
|
||||
# Disable the use of server-side cursors transaction pooling
|
||||
}
|
||||
}
|
||||
|
||||
# Redis database settings. Redis is used for caching and for queuing background tasks such as webhook events. A separate
|
||||
# configuration exists for each. Full connection details are required in both sections, and it is strongly recommended
|
||||
# to use two separate database IDs.
|
||||
REDIS = {
|
||||
'tasks': {
|
||||
'HOST': environ.get('REDIS_HOST', 'localhost'),
|
||||
'PORT': _environ_get_and_map('REDIS_PORT', 6379, _AS_INT),
|
||||
'SENTINELS': [
|
||||
tuple(uri.split(':')) for uri in _environ_get_and_map('REDIS_SENTINELS', '', _AS_LIST) if uri != ''
|
||||
],
|
||||
'SENTINEL_SERVICE': environ.get('REDIS_SENTINEL_SERVICE', 'default'),
|
||||
'SENTINEL_TIMEOUT': _environ_get_and_map('REDIS_SENTINEL_TIMEOUT', 10, _AS_INT),
|
||||
'USERNAME': environ.get('REDIS_USERNAME', ''),
|
||||
'PASSWORD': _read_secret('redis_password', environ.get('REDIS_PASSWORD', '')),
|
||||
'DATABASE': _environ_get_and_map('REDIS_DATABASE', 0, _AS_INT),
|
||||
'SSL': _environ_get_and_map('REDIS_SSL', 'False', _AS_BOOL),
|
||||
'INSECURE_SKIP_TLS_VERIFY': _environ_get_and_map('REDIS_INSECURE_SKIP_TLS_VERIFY', 'False', _AS_BOOL),
|
||||
},
|
||||
'caching': {
|
||||
'HOST': environ.get('REDIS_CACHE_HOST', environ.get('REDIS_HOST', 'localhost')),
|
||||
'PORT': _environ_get_and_map('REDIS_CACHE_PORT', environ.get('REDIS_PORT', '6379'), _AS_INT),
|
||||
'SENTINELS': [
|
||||
tuple(uri.split(':')) for uri in _environ_get_and_map('REDIS_CACHE_SENTINELS', '', _AS_LIST) if uri != ''
|
||||
],
|
||||
'SENTINEL_SERVICE': environ.get(
|
||||
'REDIS_CACHE_SENTINEL_SERVICE', environ.get('REDIS_SENTINEL_SERVICE', 'default')
|
||||
),
|
||||
'USERNAME': environ.get('REDIS_CACHE_USERNAME', environ.get('REDIS_USERNAME', '')),
|
||||
'PASSWORD': _read_secret(
|
||||
'redis_cache_password', environ.get('REDIS_CACHE_PASSWORD', environ.get('REDIS_PASSWORD', ''))
|
||||
),
|
||||
'DATABASE': _environ_get_and_map('REDIS_CACHE_DATABASE', '1', _AS_INT),
|
||||
'SSL': _environ_get_and_map('REDIS_CACHE_SSL', environ.get('REDIS_SSL', 'False'), _AS_BOOL),
|
||||
'INSECURE_SKIP_TLS_VERIFY': _environ_get_and_map(
|
||||
'REDIS_CACHE_INSECURE_SKIP_TLS_VERIFY', environ.get('REDIS_INSECURE_SKIP_TLS_VERIFY', 'False'), _AS_BOOL
|
||||
),
|
||||
},
|
||||
}
|
||||
|
||||
# This key is used for secure generation of random numbers and strings. It must never be exposed outside of this file.
|
||||
# For optimal security, SECRET_KEY should be at least 50 characters in length and contain a mix of letters, numbers, and
|
||||
# symbols. NetBox will not run without this defined. For more information, see
|
||||
# https://docs.djangoproject.com/en/stable/ref/settings/#std:setting-SECRET_KEY
|
||||
SECRET_KEY = _read_secret('secret_key', environ.get('SECRET_KEY', ''))
|
||||
|
||||
API_TOKEN_PEPPERS = {}
|
||||
if api_token_pepper := _read_secret('api_token_pepper_1', environ.get('API_TOKEN_PEPPER_1', '')):
|
||||
API_TOKEN_PEPPERS.update({1: api_token_pepper})
|
||||
|
||||
|
||||
#########################
|
||||
# #
|
||||
# Optional settings #
|
||||
# #
|
||||
#########################
|
||||
|
||||
# # Specify one or more name and email address tuples representing NetBox administrators. These people will be notified of
|
||||
# # application errors (assuming correct email settings are provided).
|
||||
# ADMINS = [
|
||||
# # ['John Doe', 'jdoe@example.com'],
|
||||
# ]
|
||||
|
||||
if 'ALLOWED_URL_SCHEMES' in environ:
|
||||
ALLOWED_URL_SCHEMES = _environ_get_and_map('ALLOWED_URL_SCHEMES', None, _AS_LIST)
|
||||
|
||||
# Optionally display a persistent banner at the top and/or bottom of every page. HTML is allowed. To display the same
|
||||
# content in both banners, define BANNER_TOP and set BANNER_BOTTOM = BANNER_TOP.
|
||||
if 'BANNER_TOP' in environ:
|
||||
BANNER_TOP = environ.get('BANNER_TOP', None)
|
||||
if 'BANNER_BOTTOM' in environ:
|
||||
BANNER_BOTTOM = environ.get('BANNER_BOTTOM', None)
|
||||
|
||||
# Text to include on the login page above the login form. HTML is allowed.
|
||||
if 'BANNER_LOGIN' in environ:
|
||||
BANNER_LOGIN = environ.get('BANNER_LOGIN', None)
|
||||
|
||||
# Maximum number of days to retain logged changes. Set to 0 to retain changes indefinitely. (Default: 90)
|
||||
if 'CHANGELOG_RETENTION' in environ:
|
||||
CHANGELOG_RETENTION = _environ_get_and_map('CHANGELOG_RETENTION', None, _AS_INT)
|
||||
|
||||
# Maximum number of days to retain job results (scripts and reports). Set to 0 to retain job results in the database indefinitely. (Default: 90)
|
||||
if 'JOB_RETENTION' in environ:
|
||||
JOB_RETENTION = _environ_get_and_map('JOB_RETENTION', None, _AS_INT)
|
||||
# JOBRESULT_RETENTION was renamed to JOB_RETENTION in the v3.5.0 release of NetBox. For backwards compatibility, map JOBRESULT_RETENTION to JOB_RETENTION
|
||||
elif 'JOBRESULT_RETENTION' in environ:
|
||||
JOB_RETENTION = _environ_get_and_map('JOBRESULT_RETENTION', None, _AS_INT)
|
||||
|
||||
# API Cross-Origin Resource Sharing (CORS) settings. If CORS_ORIGIN_ALLOW_ALL is set to True, all origins will be
|
||||
# allowed. Otherwise, define a list of allowed origins using either CORS_ORIGIN_WHITELIST or
|
||||
# CORS_ORIGIN_REGEX_WHITELIST. For more information, see https://github.com/ottoyiu/django-cors-headers
|
||||
CORS_ORIGIN_ALLOW_ALL = _environ_get_and_map('CORS_ORIGIN_ALLOW_ALL', 'False', _AS_BOOL)
|
||||
CORS_ORIGIN_WHITELIST = _environ_get_and_map('CORS_ORIGIN_WHITELIST', 'https://localhost', _AS_LIST)
|
||||
CORS_ORIGIN_REGEX_WHITELIST = [re.compile(r) for r in _environ_get_and_map('CORS_ORIGIN_REGEX_WHITELIST', '', _AS_LIST)]
|
||||
|
||||
# Set to True to enable server debugging. WARNING: Debugging introduces a substantial performance penalty and may reveal
|
||||
# sensitive information about your installation. Only enable debugging while performing testing.
|
||||
# Never enable debugging on a production system.
|
||||
DEBUG = _environ_get_and_map('DEBUG', 'False', _AS_BOOL)
|
||||
|
||||
# This parameter serves as a safeguard to prevent some potentially dangerous behavior,
|
||||
# such as generating new database schema migrations.
|
||||
# Set this to True only if you are actively developing the NetBox code base.
|
||||
DEVELOPER = _environ_get_and_map('DEVELOPER', 'False', _AS_BOOL)
|
||||
|
||||
# Email settings
|
||||
EMAIL = {
|
||||
'SERVER': environ.get('EMAIL_SERVER', 'localhost'),
|
||||
'PORT': _environ_get_and_map('EMAIL_PORT', 25, _AS_INT),
|
||||
'USERNAME': environ.get('EMAIL_USERNAME', ''),
|
||||
'PASSWORD': _read_secret('email_password', environ.get('EMAIL_PASSWORD', '')),
|
||||
'USE_SSL': _environ_get_and_map('EMAIL_USE_SSL', 'False', _AS_BOOL),
|
||||
'USE_TLS': _environ_get_and_map('EMAIL_USE_TLS', 'False', _AS_BOOL),
|
||||
'SSL_CERTFILE': environ.get('EMAIL_SSL_CERTFILE', ''),
|
||||
'SSL_KEYFILE': environ.get('EMAIL_SSL_KEYFILE', ''),
|
||||
'TIMEOUT': _environ_get_and_map('EMAIL_TIMEOUT', 10, _AS_INT), # seconds
|
||||
'FROM_EMAIL': environ.get('EMAIL_FROM', ''),
|
||||
}
|
||||
|
||||
# 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.
|
||||
if 'ENFORCE_GLOBAL_UNIQUE' in environ:
|
||||
ENFORCE_GLOBAL_UNIQUE = _environ_get_and_map('ENFORCE_GLOBAL_UNIQUE', None, _AS_BOOL)
|
||||
|
||||
# By default, netbox sends census reporting data using a single HTTP request each time a worker starts.
|
||||
# This data enables the project maintainers to estimate how many NetBox deployments exist and track the adoption of new versions over time.
|
||||
# The only data reported by this function are the NetBox version, Python version, and a pseudorandom unique identifier.
|
||||
# To opt out of census reporting, set CENSUS_REPORTING_ENABLED to False.
|
||||
if 'CENSUS_REPORTING_ENABLED' in environ:
|
||||
CENSUS_REPORTING_ENABLED = _environ_get_and_map('CENSUS_REPORTING_ENABLED', None, _AS_BOOL)
|
||||
|
||||
# Exempt certain models from the enforcement of view permissions. Models listed here will be viewable by all users and
|
||||
# by anonymous users. List models in the form `<app>.<model>`. Add '*' to this list to exempt all models.
|
||||
EXEMPT_VIEW_PERMISSIONS = _environ_get_and_map('EXEMPT_VIEW_PERMISSIONS', '', _AS_LIST)
|
||||
|
||||
# HTTP proxies NetBox should use when sending outbound HTTP requests (e.g. for webhooks).
|
||||
HTTP_PROXIES = {
|
||||
'http': environ.get('HTTP_PROXY', None),
|
||||
'https': environ.get('HTTPS_PROXY', None),
|
||||
}
|
||||
|
||||
# IP addresses recognized as internal to the system. The debugging toolbar will be available only to clients accessing
|
||||
# NetBox from an internal IP.
|
||||
INTERNAL_IPS = _environ_get_and_map('INTERNAL_IPS', '127.0.0.1 ::1', _AS_LIST)
|
||||
|
||||
# Enable GraphQL API.
|
||||
if 'GRAPHQL_ENABLED' in environ:
|
||||
GRAPHQL_ENABLED = _environ_get_and_map('GRAPHQL_ENABLED', None, _AS_BOOL)
|
||||
|
||||
# # Enable custom logging. Please see the Django documentation for detailed guidance on configuring custom logs:
|
||||
# # https://docs.djangoproject.com/en/stable/topics/logging/
|
||||
# LOGGING = {}
|
||||
|
||||
# Automatically reset the lifetime of a valid session upon each authenticated request. Enables users to remain
|
||||
# authenticated to NetBox indefinitely.
|
||||
LOGIN_PERSISTENCE = _environ_get_and_map('LOGIN_PERSISTENCE', 'False', _AS_BOOL)
|
||||
|
||||
# When enabled, only authenticated users are permitted to access any part of NetBox.
|
||||
# Disabling this will allow unauthenticated users to access most areas of NetBox (but not make any changes).
|
||||
LOGIN_REQUIRED = _environ_get_and_map('LOGIN_REQUIRED', 'True', _AS_BOOL)
|
||||
|
||||
# The length of time (in seconds) for which a user will remain logged into the web UI before being prompted to
|
||||
# re-authenticate. (Default: 1209600 [14 days])
|
||||
LOGIN_TIMEOUT = _environ_get_and_map('LOGIN_TIMEOUT', 1209600, _AS_INT)
|
||||
|
||||
# Setting this to True will display a "maintenance mode" banner at the top of every page.
|
||||
if 'MAINTENANCE_MODE' in environ:
|
||||
MAINTENANCE_MODE = _environ_get_and_map('MAINTENANCE_MODE', None, _AS_BOOL)
|
||||
|
||||
# Maps provider
|
||||
if 'MAPS_URL' in environ:
|
||||
MAPS_URL = environ.get('MAPS_URL', None)
|
||||
|
||||
# An API consumer can request an arbitrary number of objects =by appending the "limit" parameter to the URL (e.g.
|
||||
# "?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".
|
||||
if 'MAX_PAGE_SIZE' in environ:
|
||||
MAX_PAGE_SIZE = _environ_get_and_map('MAX_PAGE_SIZE', None, _AS_INT)
|
||||
|
||||
# The file path where uploaded media such as image attachments are stored. A trailing slash is not needed. Note that
|
||||
# the default value of this setting is derived from the installed location.
|
||||
MEDIA_ROOT = environ.get('MEDIA_ROOT', join(_BASE_DIR, 'media'))
|
||||
|
||||
# Expose Prometheus monitoring metrics at the HTTP endpoint '/metrics'
|
||||
METRICS_ENABLED = _environ_get_and_map('METRICS_ENABLED', 'False', _AS_BOOL)
|
||||
|
||||
# Determine how many objects to display per page within a list. (Default: 50)
|
||||
if 'PAGINATE_COUNT' in environ:
|
||||
PAGINATE_COUNT = _environ_get_and_map('PAGINATE_COUNT', None, _AS_INT)
|
||||
|
||||
# # Enable installed plugins. Add the name of each plugin to the list.
|
||||
# PLUGINS = []
|
||||
|
||||
# # Plugins configuration settings. These settings are used by various plugins that the user may have installed.
|
||||
# # Each key in the dictionary is the name of an installed plugin and its value is a dictionary of settings.
|
||||
# PLUGINS_CONFIG = {
|
||||
# }
|
||||
|
||||
# When determining the primary IP address for a device, IPv6 is preferred over IPv4 by default. Set this to True to
|
||||
# prefer IPv4 instead.
|
||||
if 'PREFER_IPV4' in environ:
|
||||
PREFER_IPV4 = _environ_get_and_map('PREFER_IPV4', None, _AS_BOOL)
|
||||
|
||||
# The default value for the amperage field when creating new power feeds.
|
||||
if 'POWERFEED_DEFAULT_AMPERAGE' in environ:
|
||||
POWERFEED_DEFAULT_AMPERAGE = _environ_get_and_map('POWERFEED_DEFAULT_AMPERAGE', None, _AS_INT)
|
||||
|
||||
# The default value (percentage) for the max_utilization field when creating new power feeds.
|
||||
if 'POWERFEED_DEFAULT_MAX_UTILIZATION' in environ:
|
||||
POWERFEED_DEFAULT_MAX_UTILIZATION = _environ_get_and_map('POWERFEED_DEFAULT_MAX_UTILIZATION', None, _AS_INT)
|
||||
|
||||
# The default value for the voltage field when creating new power feeds.
|
||||
if 'POWERFEED_DEFAULT_VOLTAGE' in environ:
|
||||
POWERFEED_DEFAULT_VOLTAGE = _environ_get_and_map('POWERFEED_DEFAULT_VOLTAGE', None, _AS_INT)
|
||||
|
||||
# Rack elevation size defaults, in pixels. For best results, the ratio of width to height should be roughly 10:1.
|
||||
if 'RACK_ELEVATION_DEFAULT_UNIT_HEIGHT' in environ:
|
||||
RACK_ELEVATION_DEFAULT_UNIT_HEIGHT = _environ_get_and_map('RACK_ELEVATION_DEFAULT_UNIT_HEIGHT', None, _AS_INT)
|
||||
if 'RACK_ELEVATION_DEFAULT_UNIT_WIDTH' in environ:
|
||||
RACK_ELEVATION_DEFAULT_UNIT_WIDTH = _environ_get_and_map('RACK_ELEVATION_DEFAULT_UNIT_WIDTH', None, _AS_INT)
|
||||
|
||||
# Remote authentication support
|
||||
REMOTE_AUTH_AUTO_CREATE_GROUPS = _environ_get_and_map('REMOTE_AUTH_AUTO_CREATE_GROUPS', 'False', _AS_BOOL)
|
||||
REMOTE_AUTH_AUTO_CREATE_USER = _environ_get_and_map('REMOTE_AUTH_AUTO_CREATE_USER', 'False', _AS_BOOL)
|
||||
REMOTE_AUTH_BACKEND = _environ_get_and_map('REMOTE_AUTH_BACKEND', 'netbox.authentication.RemoteUserBackend', _AS_LIST)
|
||||
REMOTE_AUTH_DEFAULT_GROUPS = _environ_get_and_map('REMOTE_AUTH_DEFAULT_GROUPS', '', _AS_LIST)
|
||||
# REMOTE_AUTH_DEFAULT_PERMISSIONS = {} # dicts can't be configured via environment variables. See extra.py instead.
|
||||
REMOTE_AUTH_ENABLED = _environ_get_and_map('REMOTE_AUTH_ENABLED', 'False', _AS_BOOL)
|
||||
REMOTE_AUTH_GROUP_HEADER = _environ_get_and_map('REMOTE_AUTH_GROUP_HEADER', 'HTTP_REMOTE_USER_GROUP')
|
||||
REMOTE_AUTH_GROUP_SEPARATOR = _environ_get_and_map('REMOTE_AUTH_GROUP_SEPARATOR', '|')
|
||||
REMOTE_AUTH_GROUP_SYNC_ENABLED = _environ_get_and_map('REMOTE_AUTH_GROUP_SYNC_ENABLED', 'False', _AS_BOOL)
|
||||
REMOTE_AUTH_HEADER = environ.get('REMOTE_AUTH_HEADER', 'HTTP_REMOTE_USER')
|
||||
REMOTE_AUTH_USER_EMAIL = environ.get('REMOTE_AUTH_USER_EMAIL', 'HTTP_REMOTE_USER_EMAIL')
|
||||
REMOTE_AUTH_USER_FIRST_NAME = environ.get('REMOTE_AUTH_USER_FIRST_NAME', 'HTTP_REMOTE_USER_FIRST_NAME')
|
||||
REMOTE_AUTH_USER_LAST_NAME = environ.get('REMOTE_AUTH_USER_LAST_NAME', 'HTTP_REMOTE_USER_LAST_NAME')
|
||||
REMOTE_AUTH_SUPERUSER_GROUPS = _environ_get_and_map('REMOTE_AUTH_SUPERUSER_GROUPS', '', _AS_LIST)
|
||||
REMOTE_AUTH_SUPERUSERS = _environ_get_and_map('REMOTE_AUTH_SUPERUSERS', '', _AS_LIST)
|
||||
REMOTE_AUTH_STAFF_GROUPS = _environ_get_and_map('REMOTE_AUTH_STAFF_GROUPS', '', _AS_LIST)
|
||||
REMOTE_AUTH_STAFF_USERS = _environ_get_and_map('REMOTE_AUTH_STAFF_USERS', '', _AS_LIST)
|
||||
# SSO Configuration
|
||||
SOCIAL_AUTH_OKTA_OPENIDCONNECT_KEY = environ.get('SOCIAL_AUTH_OKTA_OPENIDCONNECT_KEY')
|
||||
SOCIAL_AUTH_OKTA_OPENIDCONNECT_SECRET = _read_secret(
|
||||
'okta_openidconnect_secret', environ.get('SOCIAL_AUTH_OKTA_OPENIDCONNECT_SECRET', '')
|
||||
)
|
||||
SOCIAL_AUTH_OKTA_OPENIDCONNECT_API_URL = environ.get('SOCIAL_AUTH_OKTA_OPENIDCONNECT_API_URL')
|
||||
SOCIAL_AUTH_GOOGLE_OAUTH2_KEY = environ.get('SOCIAL_AUTH_GOOGLE_OAUTH2_KEY')
|
||||
SOCIAL_AUTH_GOOGLE_OAUTH2_SECRET = _read_secret(
|
||||
'google_oauth2_secret', environ.get('SOCIAL_AUTH_GOOGLE_OAUTH2_SECRET', '')
|
||||
)
|
||||
|
||||
# OIDC Configuration
|
||||
SOCIAL_AUTH_OIDC_OIDC_ENDPOINT = environ.get('SOCIAL_AUTH_OIDC_OIDC_ENDPOINT')
|
||||
SOCIAL_AUTH_OIDC_KEY = environ.get('SOCIAL_AUTH_OIDC_KEY')
|
||||
SOCIAL_AUTH_OIDC_SECRET = _read_secret('oidc_secret', environ.get('SOCIAL_AUTH_OIDC_SECRET', ''))
|
||||
SOCIAL_AUTH_OIDC_SCOPE = _environ_get_and_map('SOCIAL_AUTH_OIDC_SCOPE', '', _AS_LIST)
|
||||
LOGOUT_REDIRECT_URL = environ.get('LOGOUT_REDIRECT_URL', '/')
|
||||
SOCIAL_AUTH_OIDC_JWT_ALGORITHMS = _environ_get_and_map('SOCIAL_AUTH_OIDC_JWT_ALGORITHMS', 'RS256', _AS_LIST)
|
||||
|
||||
# This repository is used to check whether there is a new release of NetBox available. Set to None to disable the
|
||||
# version check or use the URL below to check for release in the official NetBox repository.
|
||||
RELEASE_CHECK_URL = environ.get('RELEASE_CHECK_URL', None)
|
||||
# RELEASE_CHECK_URL = 'https://api.github.com/repos/netbox-community/netbox/releases'
|
||||
|
||||
# Maximum execution time for background tasks, in seconds.
|
||||
RQ_DEFAULT_TIMEOUT = _environ_get_and_map('RQ_DEFAULT_TIMEOUT', 300, _AS_INT)
|
||||
|
||||
# The name to use for the csrf token cookie.
|
||||
CSRF_COOKIE_NAME = environ.get('CSRF_COOKIE_NAME', 'csrftoken')
|
||||
|
||||
# Cross-Site-Request-Forgery-Attack settings. If Netbox is sitting behind a reverse proxy, you might need to set the CSRF_TRUSTED_ORIGINS flag.
|
||||
# Django 4.0 requires to specify the URL Scheme in this setting. An example environment variable could be specified like:
|
||||
# CSRF_TRUSTED_ORIGINS=https://demo.netbox.dev http://demo.netbox.dev
|
||||
CSRF_TRUSTED_ORIGINS = _environ_get_and_map('CSRF_TRUSTED_ORIGINS', '', _AS_LIST)
|
||||
|
||||
# The name to use for the session cookie.
|
||||
SESSION_COOKIE_NAME = environ.get('SESSION_COOKIE_NAME', 'sessionid')
|
||||
|
||||
# If true, the `includeSubDomains` directive will be included in the HTTP Strict Transport Security (HSTS) header.
|
||||
# This directive instructs the browser to apply the HSTS policy to all subdomains of the current domain.
|
||||
SECURE_HSTS_INCLUDE_SUBDOMAINS = _environ_get_and_map('SECURE_HSTS_INCLUDE_SUBDOMAINS', 'False', _AS_BOOL)
|
||||
|
||||
# If true, the `preload` directive will be included in the HTTP Strict Transport Security (HSTS) header.
|
||||
# This directive instructs the browser to preload the site in HTTPS. Browsers that use the HSTS preload list will force the
|
||||
# site to be accessed via HTTPS even if the user types HTTP in the address bar.
|
||||
SECURE_HSTS_PRELOAD = _environ_get_and_map('SECURE_HSTS_PRELOAD', 'False', _AS_BOOL)
|
||||
|
||||
# If set to a non-zero integer value, the SecurityMiddleware sets the HTTP Strict Transport Security (HSTS) header on all
|
||||
# responses that do not already have it. This will instruct the browser that the website must be accessed via HTTPS,
|
||||
# blocking any HTTP request.
|
||||
SECURE_HSTS_SECONDS = _environ_get_and_map('SECURE_HSTS_SECONDS', 0, _AS_INT)
|
||||
|
||||
# If true, all non-HTTPS requests will be automatically redirected to use HTTPS.
|
||||
SECURE_SSL_REDIRECT = _environ_get_and_map('SECURE_SSL_REDIRECT', 'False', _AS_BOOL)
|
||||
|
||||
# By default, NetBox will store session data in the database. Alternatively, a file path can be specified here to use
|
||||
# local file storage instead. (This can be useful for enabling authentication on a standby instance with read-only
|
||||
# database access.) Note that the user as which NetBox runs must have read and write permissions to this path.
|
||||
SESSION_FILE_PATH = environ.get('SESSION_FILE_PATH', environ.get('SESSIONS_ROOT', None))
|
||||
|
||||
# Time zone (default: UTC)
|
||||
TIME_ZONE = environ.get('TIME_ZONE', 'UTC')
|
||||
|
||||
# If true disables miscellaneous functionality which depends on access to the Internet.
|
||||
ISOLATED_DEPLOYMENT = _environ_get_and_map('ISOLATED_DEPLOYMENT', 'False', _AS_BOOL)
|
||||
|
||||
# Enables or disables the NetBox Copilot agent globally. When enabled, users can opt to toggle the agent individually.
|
||||
COPILOT_ENABLED = _environ_get_and_map('COPILOT_ENABLED', 'True', _AS_BOOL)
|
||||
@@ -0,0 +1,56 @@
|
||||
####
|
||||
## This file contains extra configuration options that can't be configured
|
||||
## directly through environment variables.
|
||||
####
|
||||
|
||||
## Specify one or more name and email address tuples representing NetBox administrators. These people will be notified of
|
||||
## application errors (assuming correct email settings are provided).
|
||||
# ADMINS = [
|
||||
# # ['John Doe', 'jdoe@example.com'],
|
||||
# ]
|
||||
|
||||
|
||||
## URL schemes that are allowed within links in NetBox
|
||||
# ALLOWED_URL_SCHEMES = (
|
||||
# 'file', 'ftp', 'ftps', 'http', 'https', 'irc', 'mailto', 'sftp', 'ssh', 'tel', 'telnet', 'tftp', 'vnc', 'xmpp',
|
||||
# )
|
||||
|
||||
## Enable installed plugins. Add the name of each plugin to the list.
|
||||
# from netbox.configuration.configuration import PLUGINS
|
||||
# PLUGINS.append('my_plugin')
|
||||
|
||||
## Plugins configuration settings. These settings are used by various plugins that the user may have installed.
|
||||
## Each key in the dictionary is the name of an installed plugin and its value is a dictionary of settings.
|
||||
# from netbox.configuration.configuration import PLUGINS_CONFIG
|
||||
# PLUGINS_CONFIG['my_plugin'] = {
|
||||
# 'foo': 'bar',
|
||||
# 'buzz': 'bazz'
|
||||
# }
|
||||
|
||||
|
||||
## Remote authentication support
|
||||
# REMOTE_AUTH_DEFAULT_PERMISSIONS = {}
|
||||
|
||||
|
||||
## By default uploaded media is stored on the local filesystem. Using Django-storages is also supported. Provide the
|
||||
## class path of the storage driver and any configuration options in STORAGES. For example:
|
||||
# STORAGES = {
|
||||
# 'default': {
|
||||
# 'BACKEND': 'storages.backends.s3boto3.S3Boto3Storage',
|
||||
# 'OPTIONS': {
|
||||
# 'access_key': 'Key ID',
|
||||
# 'secret_key': 'Secret',
|
||||
# 'bucket_name': 'netbox',
|
||||
# 'region_name': 'us-west-1',
|
||||
# }
|
||||
# },
|
||||
# 'staticfiles': {
|
||||
# 'BACKEND': 'django.contrib.staticfiles.storage.StaticFilesStorage',
|
||||
# }
|
||||
# }
|
||||
|
||||
|
||||
## This file can contain arbitrary Python code, e.g.:
|
||||
# from datetime import datetime
|
||||
# now = datetime.now().strftime("%d/%m/%Y %H:%M:%S")
|
||||
# BANNER_TOP = f'<marquee width="200px">This instance started on {now}.</marquee>'
|
||||
@@ -0,0 +1,55 @@
|
||||
# # Remove first comment(#) on each line to implement this working logging example.
|
||||
# # Add LOGLEVEL environment variable to netbox if you use this example & want a different log level.
|
||||
# from os import environ
|
||||
|
||||
# # Set LOGLEVEL in netbox.env or docker-compose.overide.yml to override a logging level of INFO.
|
||||
# LOGLEVEL = environ.get('LOGLEVEL', 'INFO')
|
||||
|
||||
# LOGGING = {
|
||||
|
||||
# 'version': 1,
|
||||
# 'disable_existing_loggers': False,
|
||||
# 'formatters': {
|
||||
# 'verbose': {
|
||||
# 'format': '{levelname} {asctime} {module} {process:d} {thread:d} {message}',
|
||||
# 'style': '{',
|
||||
# },
|
||||
# 'simple': {
|
||||
# 'format': '{levelname} {message}',
|
||||
# 'style': '{',
|
||||
# },
|
||||
# },
|
||||
# 'filters': {
|
||||
# 'require_debug_false': {
|
||||
# '()': 'django.utils.log.RequireDebugFalse',
|
||||
# },
|
||||
# },
|
||||
# 'handlers': {
|
||||
# 'console': {
|
||||
# 'level': LOGLEVEL,
|
||||
# 'filters': ['require_debug_false'],
|
||||
# 'class': 'logging.StreamHandler',
|
||||
# 'formatter': 'simple'
|
||||
# },
|
||||
# 'mail_admins': {
|
||||
# 'level': 'ERROR',
|
||||
# 'class': 'django.utils.log.AdminEmailHandler',
|
||||
# 'filters': ['require_debug_false']
|
||||
# }
|
||||
# },
|
||||
# 'loggers': {
|
||||
# 'django': {
|
||||
# 'handlers': ['console'],
|
||||
# 'propagate': True,
|
||||
# },
|
||||
# 'django.request': {
|
||||
# 'handlers': ['mail_admins'],
|
||||
# 'level': 'ERROR',
|
||||
# 'propagate': False,
|
||||
# },
|
||||
# 'django_auth_ldap': {
|
||||
# 'handlers': ['console',],
|
||||
# 'level': LOGLEVEL,
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
@@ -0,0 +1,196 @@
|
||||
# Add your plugins and plugin settings here.
|
||||
# Of course uncomment this file out.
|
||||
|
||||
# To learn how to build images with your required plugins
|
||||
# See https://github.com/netbox-community/netbox-docker/wiki/Using-Netbox-Plugins
|
||||
|
||||
# PLUGINS = ["netbox_bgp"]
|
||||
PLUGINS = [
|
||||
"netbox_prometheus_sd",
|
||||
"netbox_lists",
|
||||
"netbox_inventory",
|
||||
"netbox_interface_synchronization",
|
||||
"netbox_documents",
|
||||
"netbox_contract",
|
||||
"netbox_data_flows",
|
||||
"netbox_config_diff",
|
||||
"netbox_attachments",
|
||||
"netbox_topology_views",
|
||||
"netbox_reorder_rack",
|
||||
"netbox_secrets",
|
||||
"netbox_qrcode",
|
||||
"netbox_floorplan"
|
||||
]
|
||||
|
||||
# PLUGINS_CONFIG = {
|
||||
# "netbox_bgp": {
|
||||
# ADD YOUR SETTINGS HERE
|
||||
# }
|
||||
# }
|
||||
PLUGINS_CONFIG = {
|
||||
"netbox_prometheus_sd": {
|
||||
# https://github.com/FlxPeters/netbox-plugin-prometheus-sd
|
||||
# The plugin has not further plugin configuration.
|
||||
},
|
||||
"netbox_lists": {
|
||||
# https://github.com/devon-mar/netbox-lists
|
||||
# Return IPs as /32 or /128.
|
||||
# Default: True
|
||||
"as_cidr": True,
|
||||
# For services without any explicit IPs configured,
|
||||
# use the primary IPs of the associated device/vm.
|
||||
# Default: True
|
||||
"service_primary_ips": True,
|
||||
# Summarize responses
|
||||
"summarize": True,
|
||||
# A list of attributes for the devices-vms-attrs endpoint
|
||||
#
|
||||
# Attributes will be joined with "__" in the returned object.
|
||||
# eg. ("primary_ip", "address") -> primary_ip__address
|
||||
"devices_vms_attrs": [
|
||||
("id",),
|
||||
("name",),
|
||||
("role", "slug"),
|
||||
("platform", "slug"),
|
||||
("primary_ip", "address"),
|
||||
("tags",),
|
||||
],
|
||||
# Tuple/list of attributes to use for Prometheus VM SD target. Defaults are shown.
|
||||
#
|
||||
# If all attributes return None, the device's name will be used.
|
||||
"prometheus_vm_sd_target": (
|
||||
# For a custom field
|
||||
# ("cf", "fqdn"),
|
||||
# If this returns none, try Name.
|
||||
("primary_ip", "address", "ip"),
|
||||
("name",), # not necessary
|
||||
),
|
||||
# Dictionary of label to VM attribute for Prometheus VM SD. Defaults are shown.
|
||||
"prometheus_vm_sd_labels": {
|
||||
"__meta_netbox_id": ("id",),
|
||||
"__meta_netbox_name": ("name",),
|
||||
"__meta_netbox_status": ("status",),
|
||||
"__meta_netbox_cluster_name": ("cluster", "name"),
|
||||
"__meta_netbox_site_name": ("site", "name"),
|
||||
"__meta_netbox_role_name": ("role", "name"),
|
||||
"__meta_netbox_platform_name": ("platform", "name"),
|
||||
"__meta_netbox_primary_ip": ("primary_ip", "address", "ip"),
|
||||
"__meta_netbox_primary_ip4": ("primary_ip4", "address", "ip"),
|
||||
"__meta_netbox_primary_ip6": ("primary_ip6", "address", "ip"),
|
||||
# A custom field. Will be an empty string if None.
|
||||
# "__meta_netbox_fqdn": ("cf", "fqdn"),
|
||||
},
|
||||
# Tuple/list of attributes to use for Prometheus device SD target. Defaults are shown.
|
||||
#
|
||||
# If all attributes return None, the device's name will be used.
|
||||
"prometheus_device_sd_target": (
|
||||
# For a custom field
|
||||
# ("cf", "fqdn"),
|
||||
("primary_ip", "address", "ip"),
|
||||
("name",), # not necessary
|
||||
),
|
||||
# Dictionary of label to device attribute for Prometheus device SD. Defaults are shown.
|
||||
"prometheus_device_sd_labels": {
|
||||
"__meta_netbox_id": ("id",),
|
||||
"__meta_netbox_name": ("name",),
|
||||
"__meta_netbox_status": ("status",),
|
||||
"__meta_netbox_site_name": ("site", "name"),
|
||||
"__meta_netbox_platform_name": ("platform", "name"),
|
||||
"__meta_netbox_primary_ip": ("primary_ip", "address", "ip"),
|
||||
"__meta_netbox_primary_ip4": ("primary_ip4", "address", "ip"),
|
||||
"__meta_netbox_primary_ip6": ("primary_ip6", "address", "ip"),
|
||||
"__meta_netbox_serial": ("serial",),
|
||||
# A custom field. Will be an empty string if None.
|
||||
# "__meta_netbox_fqdn": ("cf", "fqdn"),
|
||||
},
|
||||
# Tuple/list of attributes to use for Prometheus IP address SD target. Defaults are shown.
|
||||
#
|
||||
# If all attributes return None, the address in CIDR format will be used.
|
||||
"prometheus_ipaddress_sd_target": (
|
||||
("address", "ip"),
|
||||
),
|
||||
# Dictionary of label to IP address attribute for Prometheus ip address SD. Defaults are shown.
|
||||
"prometheus_ipaddress_sd_labels": {
|
||||
"__meta_netbox_id": ("id",),
|
||||
"__meta_netbox_role": ("role",),
|
||||
"__meta_netbox_dns_name": ("dns_name",),
|
||||
"__meta_netbox_status": ("status",),
|
||||
# For addresses assigned to interfaces
|
||||
#"__meta_netbox_device": ("assigned_object", "device", "name"),
|
||||
#"__meta_netbox_interface": ("assigned_object", "name"),
|
||||
},
|
||||
},
|
||||
"netbox_inventory": {
|
||||
# https://github.com/ArnesSI/netbox-inventory
|
||||
# Example settings below, see "Available settings"
|
||||
# in README.md for all possible settings
|
||||
"used_status_name": "used",
|
||||
"stored_status_name": "stored",
|
||||
"sync_hardware_serial_asset_tag": True,
|
||||
},
|
||||
"netbox-interface-synchronization": {
|
||||
# https://github.com/NetTech2001/netbox-interface-synchronization
|
||||
'exclude_virtual_interfaces': True
|
||||
},
|
||||
"netbox-documents": {
|
||||
# https://github.com/jasonyates/netbox-documents
|
||||
#All settings are optional -- the defaults are shown below
|
||||
# Enable the global navigation menu
|
||||
'enable_navigation_menu': True,
|
||||
|
||||
# Location of the documents panel on object detail pages (left/right)
|
||||
'documents_location': 'left',
|
||||
|
||||
# Custom document types (see below)
|
||||
'custom_doc_types': [],
|
||||
|
||||
# Per-model document type filtering (see below)
|
||||
'allowed_doc_types': {},
|
||||
},
|
||||
"netbox-contract": {
|
||||
# https://github.com/mlebreuil/netbox-contract
|
||||
},
|
||||
"netbox-data-flows": {
|
||||
# https://github.com/Alef-Burzmali/netbox-data-flows/
|
||||
},
|
||||
"netbox-config-diff": {
|
||||
# https://github.com/miaow2/netbox-config-diff
|
||||
#define credentials for devices connection:
|
||||
"USERNAME": "foo",
|
||||
"PASSWORD": "bar",
|
||||
"AUTH_SECONDARY": "foobar", # define here password for accessing Privileged EXEC mode, this variable is optional
|
||||
"PATH_TO_SSH_CONFIG_FILE": "/home/.ssh/config", # define here PATH to SSH config file, it will be used for device connections, this variable is optional
|
||||
},
|
||||
"netbox-attachments": {
|
||||
# https://github.com/Kani999/netbox-attachments
|
||||
"applied_scope": "model",
|
||||
"scope_filter": ["dcim.device", "ipam.prefix", "tenancy"],
|
||||
"display_default": "right_page",
|
||||
"create_add_button": True, # show top "Attachments" dropdown in additional_tab mode
|
||||
"display_setting": {"ipam.vlan": "left_page"},
|
||||
},
|
||||
"netbox-topology-views": {
|
||||
# https://github.com/netbox-community/netbox-topology-views
|
||||
'static_image_directory': 'netbox_topology_views/img',
|
||||
'allow_coordinates_saving': True,
|
||||
'always_save_coordinates': True
|
||||
},
|
||||
"netbox-reorder-rack": {
|
||||
# https://github.com/minitriga/netbox-reorder-rack/
|
||||
},
|
||||
"netbox-secrets": {
|
||||
# https://github.com/Onemind-Services-LLC/netbox-secrets/blob/master/docs
|
||||
# A list of NetBox models where secrets can be assigned and displayed. Each entry is app_label.model.
|
||||
'apps': [
|
||||
'dcim.device',
|
||||
'virtualization.virtualmachine',
|
||||
],
|
||||
},
|
||||
"netbox-qrcode": {
|
||||
# https://github.com/netbox-community/netbox-qrcode
|
||||
},
|
||||
"netbox-floorplan": {
|
||||
# https://github.com/netbox-community/netbox-floorplan-plugin
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
services:
|
||||
netbox:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile-Plugins
|
||||
image: my-netbox-image:latest
|
||||
netbox-worker:
|
||||
image: my-netbox-image:latest
|
||||
netbox-housekeeping:
|
||||
image: my-netbox-image:latest
|
||||
@@ -0,0 +1,33 @@
|
||||
services:
|
||||
netbox:
|
||||
ports:
|
||||
- "8000:8080"
|
||||
# healthcheck:
|
||||
# Time for which the health check can fail after the container is started.
|
||||
# This depends mostly on the performance of your database. On the first start,
|
||||
# when all tables need to be created the start_period should be higher than on
|
||||
# subsequent starts. For the first start after major version upgrades of NetBox
|
||||
# the start_period might also need to be set higher.
|
||||
# Default value in our docker-compose.yml is 60s
|
||||
# start_period: 90s
|
||||
# environment:
|
||||
# SKIP_SUPERUSER: "false"
|
||||
# SUPERUSER_API_TOKEN: ""
|
||||
# SUPERUSER_EMAIL: ""
|
||||
# SUPERUSER_NAME: ""
|
||||
# SUPERUSER_PASSWORD: ""
|
||||
# SSO Configuration
|
||||
# SOCIAL_AUTH_OKTA_OPENIDCONNECT_KEY: "your_okta_client_id"
|
||||
# SOCIAL_AUTH_OKTA_OPENIDCONNECT_API_URL: "https://your-domain.okta.com"
|
||||
# SOCIAL_AUTH_GOOGLE_OAUTH2_KEY: "your_google_client_id"
|
||||
# secrets:
|
||||
# - okta_openidconnect_secret
|
||||
# - google_oauth2_secret
|
||||
|
||||
# Uncomment to use Docker secrets for SSO credentials
|
||||
# secrets:
|
||||
# okta_openidconnect_secret:
|
||||
# file: ./secrets/okta_secret.txt
|
||||
# google_oauth2_secret:
|
||||
# file: ./secrets/google_secret.txt
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
services:
|
||||
netbox:
|
||||
ports:
|
||||
- "127.0.0.1:8000:8080"
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
services:
|
||||
netbox: &netbox
|
||||
image: ${IMAGE-docker.io/netboxcommunity/netbox:latest}
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
redis:
|
||||
condition: service_healthy
|
||||
redis-cache:
|
||||
condition: service_healthy
|
||||
env_file: env/netbox.env
|
||||
user: "netbox:root"
|
||||
volumes:
|
||||
- ./test-configuration/test_config.py:/etc/netbox/config/test_config.py:z,ro
|
||||
healthcheck:
|
||||
test: curl -f http://localhost:8080/login/ || exit 1
|
||||
start_period: ${NETBOX_START_PERIOD-120s}
|
||||
timeout: 3s
|
||||
interval: 15s
|
||||
netbox-worker:
|
||||
<<: *netbox
|
||||
command:
|
||||
- /opt/netbox/venv/bin/python
|
||||
- /opt/netbox/netbox/manage.py
|
||||
- rqworker
|
||||
healthcheck:
|
||||
test: ps -aux | grep -v grep | grep -q rqworker || exit 1
|
||||
start_period: 40s
|
||||
timeout: 3s
|
||||
interval: 15s
|
||||
|
||||
postgres:
|
||||
image: docker.io/postgres:18-alpine
|
||||
env_file: env/postgres.env
|
||||
healthcheck:
|
||||
test: pg_isready -q -t 2 -d $$POSTGRES_DB -U $$POSTGRES_USER ## $$ because of docker-compose
|
||||
start_period: 20s
|
||||
interval: 1s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
redis: &redis
|
||||
image: docker.io/valkey/valkey:9.0-alpine
|
||||
command:
|
||||
- sh
|
||||
- -c # this is to evaluate the $REDIS_PASSWORD from the env
|
||||
- valkey-server --save "" --appendonly no --requirepass $$REDIS_PASSWORD ## $$ because of docker-compose
|
||||
env_file: env/redis.env
|
||||
healthcheck:
|
||||
test: '[ $$(valkey-cli --pass "$${REDIS_PASSWORD}" ping) = ''PONG'' ]'
|
||||
start_period: 5s
|
||||
timeout: 3s
|
||||
interval: 1s
|
||||
retries: 5
|
||||
redis-cache:
|
||||
<<: *redis
|
||||
env_file: env/redis-cache.env
|
||||
|
||||
volumes:
|
||||
netbox-media-files:
|
||||
driver: local
|
||||
@@ -0,0 +1,87 @@
|
||||
services:
|
||||
netbox: &netbox
|
||||
image: docker.io/netboxcommunity/netbox:${VERSION-v4.6-5.0.1}
|
||||
depends_on:
|
||||
- postgres
|
||||
- redis
|
||||
- redis-cache
|
||||
env_file: env/netbox.env
|
||||
user: "netbox:root"
|
||||
healthcheck:
|
||||
test: curl -f http://localhost:8080/login/ || exit 1
|
||||
start_period: 90s
|
||||
timeout: 3s
|
||||
interval: 15s
|
||||
volumes:
|
||||
- ./configuration:/etc/netbox/config:z,ro
|
||||
- netbox-media-files:/opt/netbox/netbox/media:rw
|
||||
- netbox-reports-files:/opt/netbox/netbox/reports:rw
|
||||
- netbox-scripts-files:/opt/netbox/netbox/scripts:rw
|
||||
netbox-worker:
|
||||
<<: *netbox
|
||||
depends_on:
|
||||
netbox:
|
||||
condition: service_healthy
|
||||
command:
|
||||
- /opt/netbox/venv/bin/python
|
||||
- /opt/netbox/netbox/manage.py
|
||||
- rqworker
|
||||
healthcheck:
|
||||
test: ps -aux | grep -v grep | grep -q rqworker || exit 1
|
||||
start_period: 20s
|
||||
timeout: 3s
|
||||
interval: 15s
|
||||
|
||||
# postgres
|
||||
postgres:
|
||||
image: docker.io/postgres:18-alpine
|
||||
healthcheck:
|
||||
test: pg_isready -q -t 2 -d $$POSTGRES_DB -U $$POSTGRES_USER
|
||||
start_period: 20s
|
||||
timeout: 30s
|
||||
interval: 10s
|
||||
retries: 5
|
||||
env_file: env/postgres.env
|
||||
volumes:
|
||||
- netbox-postgres:/var/lib/postgresql
|
||||
|
||||
# redis
|
||||
redis:
|
||||
image: docker.io/valkey/valkey:9.0-alpine
|
||||
command:
|
||||
- sh
|
||||
- -c # this is to evaluate the $REDIS_PASSWORD from the env
|
||||
- valkey-server --appendonly yes --requirepass $$REDIS_PASSWORD ## $$ because of docker-compose
|
||||
healthcheck: &redis-healthcheck
|
||||
test: '[ $$(valkey-cli --pass "$${REDIS_PASSWORD}" ping) = ''PONG'' ]'
|
||||
start_period: 5s
|
||||
timeout: 3s
|
||||
interval: 1s
|
||||
retries: 5
|
||||
env_file: env/redis.env
|
||||
volumes:
|
||||
- netbox-redis-data:/data
|
||||
redis-cache:
|
||||
image: docker.io/valkey/valkey:9.0-alpine
|
||||
command:
|
||||
- sh
|
||||
- -c # this is to evaluate the $REDIS_PASSWORD from the env
|
||||
- valkey-server --requirepass $$REDIS_PASSWORD ## $$ because of docker-compose
|
||||
healthcheck: *redis-healthcheck
|
||||
env_file: env/redis-cache.env
|
||||
volumes:
|
||||
- netbox-redis-cache-data:/data
|
||||
|
||||
volumes:
|
||||
netbox-media-files:
|
||||
driver: local
|
||||
netbox-postgres:
|
||||
driver: local
|
||||
netbox-redis-cache-data:
|
||||
driver: local
|
||||
netbox-redis-data:
|
||||
driver: local
|
||||
netbox-reports-files:
|
||||
driver: local
|
||||
netbox-scripts-files:
|
||||
driver: local
|
||||
@@ -0,0 +1,91 @@
|
||||
## Generic Parts
|
||||
# These functions are providing the functionality to load
|
||||
# arbitrary configuration files.
|
||||
#
|
||||
# They can be imported by other code (see `ldap_config.py` for an example).
|
||||
|
||||
import importlib.util
|
||||
import sys
|
||||
from os import scandir
|
||||
from os.path import abspath, isfile
|
||||
|
||||
|
||||
def _filename(f):
|
||||
return f.name
|
||||
|
||||
|
||||
def _import(module_name, path, loaded_configurations):
|
||||
spec = importlib.util.spec_from_file_location('', path)
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(module)
|
||||
sys.modules[module_name] = module
|
||||
|
||||
loaded_configurations.insert(0, module)
|
||||
|
||||
print(f"🧬 loaded config '{path}'")
|
||||
|
||||
|
||||
def read_configurations(config_module, config_dir, main_config):
|
||||
loaded_configurations = []
|
||||
|
||||
main_config_path = abspath(f'{config_dir}/{main_config}.py')
|
||||
if isfile(main_config_path):
|
||||
_import(f'{config_module}.{main_config}', main_config_path, loaded_configurations)
|
||||
else:
|
||||
print(f"⚠️ Main configuration '{main_config_path}' not found.")
|
||||
|
||||
with scandir(config_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
|
||||
|
||||
if f.name == f'{main_config}.py':
|
||||
continue
|
||||
|
||||
if f.name == f'{config_dir}.py':
|
||||
continue
|
||||
|
||||
module_name = f'{config_module}.{f.name[: -len(".py")]}'.replace('.', '_')
|
||||
_import(module_name, f.path, loaded_configurations)
|
||||
|
||||
if len(loaded_configurations) == 0:
|
||||
print(f"‼️ No configuration files found in '{config_dir}'.")
|
||||
raise ImportError(f"No configuration files found in '{config_dir}'.")
|
||||
|
||||
return loaded_configurations
|
||||
|
||||
|
||||
## Specific Parts
|
||||
# This section's code actually loads the various configuration files
|
||||
# into the module with the given name.
|
||||
# It contains the logic to resolve arbitrary configuration options by
|
||||
# levaraging dynamic programming using `__getattr__`.
|
||||
|
||||
|
||||
_loaded_configurations = read_configurations(
|
||||
config_dir='/etc/netbox/config/',
|
||||
config_module='netbox.configuration',
|
||||
main_config='configuration',
|
||||
)
|
||||
|
||||
|
||||
def __getattr__(name):
|
||||
for config in _loaded_configurations:
|
||||
try:
|
||||
return getattr(config, name)
|
||||
except:
|
||||
pass
|
||||
raise AttributeError
|
||||
|
||||
|
||||
def __dir__():
|
||||
names = []
|
||||
for config in _loaded_configurations:
|
||||
names.extend(config.__dir__())
|
||||
return names
|
||||
Executable
+65
@@ -0,0 +1,65 @@
|
||||
#!/bin/bash
|
||||
# Runs on every start of the NetBox Docker container
|
||||
|
||||
# Stop when an error occures
|
||||
set -e
|
||||
|
||||
# Allows NetBox to be run as non-root users
|
||||
umask 002
|
||||
|
||||
# Load correct Python3 env
|
||||
# shellcheck disable=SC1091
|
||||
source /opt/netbox/venv/bin/activate
|
||||
|
||||
# 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 [ "${CUR_DB_WAIT_TIME}" -lt "${MAX_DB_WAIT_TIME}" ]; do
|
||||
# Read and truncate connection error tracebacks to last line by default
|
||||
exec {psfd}< <(./manage.py showmigrations 2>&1)
|
||||
read -rd '' DB_ERR <&$psfd || :
|
||||
exec {psfd}<&-
|
||||
wait $! && break
|
||||
if [ -n "$DB_WAIT_DEBUG" ]; then
|
||||
echo "$DB_ERR"
|
||||
else
|
||||
readarray -tn 0 DB_ERR_LINES <<<"$DB_ERR"
|
||||
echo "${DB_ERR_LINES[@]: -1}"
|
||||
echo "[ Use DB_WAIT_DEBUG=1 in netbox.env to print full traceback for errors here ]"
|
||||
fi
|
||||
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
|
||||
# Check if update is needed
|
||||
if ! ./manage.py migrate --check >/dev/null 2>&1; then
|
||||
echo "⚙️ Applying database migrations"
|
||||
./manage.py migrate --no-input
|
||||
echo "⚙️ Running trace_paths"
|
||||
./manage.py trace_paths --no-input
|
||||
echo "⚙️ Removing stale content types"
|
||||
./manage.py remove_stale_contenttypes --no-input
|
||||
echo "⚙️ Removing expired user sessions"
|
||||
./manage.py clearsessions
|
||||
echo "⚙️ Building search index (lazy)"
|
||||
./manage.py reindex --lazy
|
||||
fi
|
||||
|
||||
# Create Superuser if required
|
||||
if [ "$SKIP_SUPERUSER" == "true" ]; then
|
||||
echo "↩️ Skip creating the superuser"
|
||||
else
|
||||
./manage.py shell --no-startup --no-imports --interface python \
|
||||
</opt/netbox/super_user.py
|
||||
fi
|
||||
|
||||
echo "✅ Initialisation is done."
|
||||
|
||||
# Launch whatever is passed by docker
|
||||
# (i.e. the RUN instruction in the Dockerfile)
|
||||
exec "$@"
|
||||
@@ -0,0 +1,13 @@
|
||||
from granian.utils.proxies import wrap_wsgi_with_proxy_headers
|
||||
from netbox.wsgi import application
|
||||
|
||||
application = wrap_wsgi_with_proxy_headers(
|
||||
application,
|
||||
trusted_hosts=[
|
||||
'10.0.0.0/8',
|
||||
'172.16.0.0/12',
|
||||
'192.168.0.0/16',
|
||||
'fc00::/7',
|
||||
'fe80::/10',
|
||||
],
|
||||
)
|
||||
Executable
+21
@@ -0,0 +1,21 @@
|
||||
#!/bin/bash
|
||||
|
||||
exec granian \
|
||||
--host "::" \
|
||||
--port "8080" \
|
||||
--interface "wsgi" \
|
||||
--no-ws \
|
||||
--workers "${GRANIAN_WORKERS:-4}" \
|
||||
--respawn-failed-workers \
|
||||
--backpressure "${GRANIAN_BACKPRESSURE:-${GRANIAN_WORKERS:-4}}" \
|
||||
--loop "uvloop" \
|
||||
--log \
|
||||
--log-level "info" \
|
||||
--access-log \
|
||||
--working-dir "/opt/netbox/netbox/" \
|
||||
--static-path-route "/static" \
|
||||
--static-path-mount "/opt/netbox/netbox/static/" \
|
||||
--static-path-dir-to-file index.html \
|
||||
--pid-file "/tmp/granian.pid" \
|
||||
"${GRANIAN_EXTRA_ARGS[@]}" \
|
||||
"netbox.granian:application"
|
||||
@@ -0,0 +1,23 @@
|
||||
from .configuration import read_configurations
|
||||
|
||||
_loaded_configurations = read_configurations(
|
||||
config_dir='/etc/netbox/config/ldap/',
|
||||
config_module='netbox.configuration.ldap',
|
||||
main_config='ldap_config',
|
||||
)
|
||||
|
||||
|
||||
def __getattr__(name):
|
||||
for config in _loaded_configurations:
|
||||
try:
|
||||
return getattr(config, name)
|
||||
except:
|
||||
pass
|
||||
raise AttributeError
|
||||
|
||||
|
||||
def __dir__():
|
||||
names = []
|
||||
for config in _loaded_configurations:
|
||||
names.extend(config.__dir__())
|
||||
return names
|
||||
@@ -0,0 +1,45 @@
|
||||
import sys
|
||||
from os import environ
|
||||
|
||||
from django.conf import settings
|
||||
from users.choices import TokenVersionChoices
|
||||
from users.models import Token, User
|
||||
|
||||
|
||||
# Read secret from file
|
||||
def _read_secret(secret_name: str, default: str | None = None) -> str | None:
|
||||
try:
|
||||
with open('/run/secrets/' + secret_name, encoding='utf-8') as f:
|
||||
return f.readline().strip()
|
||||
except OSError:
|
||||
return default
|
||||
|
||||
|
||||
su_name = environ.get('SUPERUSER_NAME', 'admin')
|
||||
su_email = environ.get('SUPERUSER_EMAIL', 'admin@example.com')
|
||||
su_password = _read_secret('superuser_password', environ.get('SUPERUSER_PASSWORD'))
|
||||
su_api_token = _read_secret('superuser_api_token', environ.get('SUPERUSER_API_TOKEN'))
|
||||
su_api_key = _read_secret('superuser_api_key', environ.get('SUPERUSER_API_KEY'))
|
||||
|
||||
if User.objects.filter(username=su_name).exists():
|
||||
print(f'User with name "{su_name}" already exists.')
|
||||
sys.exit(0)
|
||||
|
||||
if not su_password:
|
||||
print(
|
||||
'⚠️ Warning: No superuser password provided. Please set the SUPERUSER_PASSWORD environment variable or provide a secret file. The superuser will not be created.'
|
||||
)
|
||||
sys.exit(0)
|
||||
|
||||
u = User.objects.create_superuser(su_name, su_email, su_password)
|
||||
if not settings.API_TOKEN_PEPPERS:
|
||||
print('⚠️ No API token was created as API_TOKEN_PEPPERS is not set')
|
||||
print(f'💡 Superuser Username: {su_name}, E-Mail: {su_email}')
|
||||
else:
|
||||
if su_api_key and su_api_token:
|
||||
t = Token.objects.create(user=u, token=su_api_token, version=TokenVersionChoices.V2, key=su_api_key)
|
||||
print(f'💡 Superuser Username: {su_name}, E-Mail: {su_email},')
|
||||
print(f"💡 API Token: use with '{t.get_auth_header_prefix()}<Your token>'")
|
||||
else:
|
||||
print('⚠️ No API token was created for the superuser as SUPERUSER_API_TOKEN and SUPERUSER_API_KEY are not set')
|
||||
print(f'💡 Superuser Username: {su_name}, E-Mail: {su_email}')
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
REDIS_PASSWORD=t4Ph722qJ5QHeQ1qfu36
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
REDIS_PASSWORD=H733Kdjndks81
|
||||
+14
-2
@@ -1,2 +1,14 @@
|
||||
netbox-plugin-circuit-maintenance==0.5.0
|
||||
netbox-documents==0.7.4
|
||||
netbox-plugin-prometheus-sd
|
||||
netbox-lists
|
||||
netbox-inventory
|
||||
netbox-interface-synchronization
|
||||
netbox-documents
|
||||
netbox-contract
|
||||
netbox-data-flows
|
||||
netbox-config-diff
|
||||
netbox-attachments
|
||||
netbox-topology-views
|
||||
netbox-reorder-rack
|
||||
netbox-secrets
|
||||
netbox-qrcode
|
||||
netbox-floorplan-plugin
|
||||
|
||||
Executable
+188
@@ -0,0 +1,188 @@
|
||||
#!/bin/bash
|
||||
|
||||
DEFAULT_REPO=netbox-community/netbox-docker
|
||||
REPO="${REPO-${DEFAULT_REPO}}"
|
||||
|
||||
echomoji() {
|
||||
EMOJI=${1}
|
||||
TEXT=${2}
|
||||
shift 2
|
||||
if [ -z "$DISABLE_EMOJI" ]; then
|
||||
echo "${EMOJI}" "${@}"
|
||||
else
|
||||
echo "${TEXT}" "${@}"
|
||||
fi
|
||||
}
|
||||
|
||||
echo_nok() {
|
||||
echomoji "❌" "!" "${@}"
|
||||
}
|
||||
echo_ok() {
|
||||
echomoji "✅" "-" "${@}"
|
||||
}
|
||||
echo_hint() {
|
||||
echomoji "👉" ">" "${@}"
|
||||
}
|
||||
|
||||
# check errors shall exit with code 1
|
||||
|
||||
check_clean_repo() {
|
||||
changes=$(git status --porcelain 2>/dev/null)
|
||||
if [ ${?} ] && [ -n "$changes" ]; then
|
||||
echo_nok "There are git changes pending:"
|
||||
echo "$changes"
|
||||
echo_hint "Please clean the repository before continueing: git stash --include-untracked"
|
||||
exit 1
|
||||
fi
|
||||
echo_ok "Repository has no pending changes."
|
||||
}
|
||||
|
||||
check_branch() {
|
||||
expected_branch="${1}"
|
||||
actual_branch=$(git rev-parse --abbrev-ref HEAD 2>/dev/null)
|
||||
if [ ${?} ] && [ "${actual_branch}" != "${expected_branch}" ]; then
|
||||
echo_nok "Current branch should be '${expected_branch}', but is '${actual_branch}'."
|
||||
echo_hint "Please change to the '${expected_branch}' branch: git checkout ${expected_branch}"
|
||||
exit 1
|
||||
fi
|
||||
echo_ok "The current branch is '${actual_branch}'."
|
||||
}
|
||||
|
||||
check_upstream() {
|
||||
expected_upstream_branch="origin/${1}"
|
||||
actual_upstream_branch=$(git rev-parse --abbrev-ref '@{upstream}' 2>/dev/null)
|
||||
if [ ${?} ] && [ "${actual_upstream_branch}" != "${expected_upstream_branch}" ]; then
|
||||
echo_nok "Current upstream branch should be '${expected_upstream_branch}', but is '${actual_upstream_branch}'."
|
||||
echo_hint "Please set '${expected_upstream_branch}' as the upstream branch: git branch --set-upstream-to=${expected_upstream_branch}"
|
||||
exit 1
|
||||
fi
|
||||
echo_ok "The current upstream branch is '${actual_upstream_branch}'."
|
||||
}
|
||||
|
||||
check_origin() {
|
||||
expected_origin="git@github.com:${REPO}.git"
|
||||
actual_origin=$(git remote get-url origin 2>/dev/null)
|
||||
if [ ${?} ] && [ "${actual_origin}" != "${expected_origin}" ]; then
|
||||
echo_nok "The url of origin is '${actual_origin}', but '${expected_origin}' is expected."
|
||||
echo_hint "Please set '${expected_origin}' as the url for origin: git origin set-url '${expected_origin}'"
|
||||
exit 1
|
||||
fi
|
||||
echo_ok "The current origin url is '${actual_origin}'."
|
||||
}
|
||||
|
||||
check_latest() {
|
||||
git fetch --tags origin
|
||||
|
||||
local_head_commit=$(git rev-parse HEAD 2>/dev/null)
|
||||
remote_head_commit=$(git rev-parse FETCH_HEAD 2>/dev/null)
|
||||
if [ "${local_head_commit}" != "${remote_head_commit}" ]; then
|
||||
echo_nok "HEAD is at '${local_head_commit}', but FETCH_HEAD is at '${remote_head_commit}'."
|
||||
echo_hint "Please ensure that you have pushed and pulled all the latest chanegs: git pull --prune --rebase origin; git push origin"
|
||||
exit 1
|
||||
fi
|
||||
echo_ok "HEAD and FETCH_HEAD both point to '${local_head_commit}'."
|
||||
}
|
||||
|
||||
check_tag() {
|
||||
local tag
|
||||
|
||||
tag=$(<VERSION)
|
||||
if git rev-parse "${tag}" 2>/dev/null >/dev/null; then
|
||||
echo_nok "The tag '${tag}' already points to '$(git rev-parse "${tag}" 2>/dev/null)'."
|
||||
echo_hint "Please ensure that the 'VERSION' file has been updated before trying to release: echo X.Y.Z > VERSION"
|
||||
exit 1
|
||||
fi
|
||||
echo_ok "The tag '${tag}' does not exist yet."
|
||||
}
|
||||
|
||||
check_develop() {
|
||||
echomoji 📋 "?" "Checking 'develop' branch"
|
||||
|
||||
check_branch develop
|
||||
check_upstream develop
|
||||
check_clean_repo
|
||||
check_latest
|
||||
}
|
||||
|
||||
check_release() {
|
||||
echomoji 📋 "?" "Checking 'release' branch"
|
||||
|
||||
check_upstream release
|
||||
check_clean_repo
|
||||
check_latest
|
||||
}
|
||||
|
||||
# git errors shall exit with code 2
|
||||
|
||||
git_switch() {
|
||||
echomoji 🔀 "≈" "Switching to '${1}' branch…"
|
||||
if ! git checkout "${1}" >/dev/null; then
|
||||
echo_nok "It was not possible to switch to the branch '${1}'."
|
||||
exit 2
|
||||
fi
|
||||
echo_ok "The branch is now '${1}'."
|
||||
}
|
||||
|
||||
git_tag() {
|
||||
echomoji 🏷 "X" "Tagging version '${1}'…"
|
||||
if ! git tag "${1}"; then
|
||||
echo_nok "The tag '${1}' was not created because of an error."
|
||||
exit 2
|
||||
fi
|
||||
echo_ok "The tag '$(<VERSION)' was created."
|
||||
}
|
||||
|
||||
git_push() {
|
||||
echomoji ⏩ "»" "Pushing the tag '${2}' to '${1}'…"
|
||||
if ! git push "${1}" "${2}"; then
|
||||
echo_nok "The tag '${2}' could not be pushed to '${1}'."
|
||||
exit 2
|
||||
fi
|
||||
echo_ok "The tag '${2}' was pushed."
|
||||
}
|
||||
|
||||
git_merge() {
|
||||
echomoji ⏩ "»" "Merging '${1}'…"
|
||||
if ! git merge --no-ff "${1}"; then
|
||||
echo_nok "The branch '${1}' could not be merged."
|
||||
exit 2
|
||||
fi
|
||||
echo_ok "The branch '${2}' was merged."
|
||||
}
|
||||
|
||||
git_merge() {
|
||||
echomoji ⏩ "»" "Rebasing onto '${1}'…"
|
||||
if ! git rebase "${1}"; then
|
||||
echo_nok "Could not rebase onto '${1}'."
|
||||
exit 2
|
||||
fi
|
||||
echo_ok "Rebased onto '${2}'."
|
||||
}
|
||||
|
||||
###
|
||||
# MAIN
|
||||
###
|
||||
|
||||
echomoji 📋 "▶︎" "Checking pre-requisites for releasing '$(<VERSION)'"
|
||||
|
||||
check_origin
|
||||
|
||||
check_develop
|
||||
check_tag
|
||||
|
||||
git_switch release
|
||||
check_release
|
||||
|
||||
echomoji 📋 "▶︎" "Releasing '$(<VERSION)'"
|
||||
|
||||
git_merge develop
|
||||
check_tag
|
||||
git_tag "$(<VERSION)"
|
||||
|
||||
git_push "origin" release
|
||||
git_push "origin" "$(<VERSION)"
|
||||
|
||||
git_switch develop
|
||||
git_rebase release
|
||||
|
||||
echomoji ✅ "◼︎" "The release of '$(<VERSION)' is complete."
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"extends": [
|
||||
"config:base",
|
||||
":disableDependencyDashboard"
|
||||
],
|
||||
"enabled": true,
|
||||
"labels": ["maintenance"],
|
||||
"baseBranches": ["develop"],
|
||||
"pip_requirements": {
|
||||
"fileMatch": [
|
||||
"requirements-container.txt"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
django-auth-ldap==5.3.0
|
||||
dulwich==1.2.1
|
||||
granian[uvloop]==2.7.4
|
||||
python3-saml==1.16.0
|
||||
--no-binary lxml
|
||||
--no-binary xmlsec
|
||||
sentry-sdk[django]==2.59.0
|
||||
Executable
+242
@@ -0,0 +1,242 @@
|
||||
#!/bin/bash
|
||||
# Скрипт восстановления NetBox из бэкапа
|
||||
|
||||
set -e # Останавливаемся при любой ошибке
|
||||
|
||||
BACKUP_DIR="/opt/netbox/backups"
|
||||
YADISK_MOUNT="/opt/netbox/yadisk"
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Функция для вывода цветных сообщений
|
||||
print_error() { echo -e "${RED}❌ $1${NC}"; }
|
||||
print_success() { echo -e "${GREEN}✅ $1${NC}"; }
|
||||
print_warning() { echo -e "${YELLOW}⚠️ $1${NC}"; }
|
||||
print_info() { echo -e "ℹ️ $1"; }
|
||||
|
||||
# Показать доступные бэкапы
|
||||
show_backups() {
|
||||
echo ""
|
||||
echo "📁 Доступные бэкапы:"
|
||||
echo "===================="
|
||||
|
||||
# Локальные бэкапы
|
||||
if [ -d "$BACKUP_DIR" ] && [ -n "$(ls -A $BACKUP_DIR 2>/dev/null)" ]; then
|
||||
echo -e "\n${GREEN}Локальные бэкапы:${NC}"
|
||||
ls -lh $BACKUP_DIR/netbox_db_*.sql.gz 2>/dev/null | awk '{print " " $9 " (" $5 ")"}'
|
||||
fi
|
||||
|
||||
# Бэкапы на Яндекс.Диске
|
||||
if mountpoint -q ${YADISK_MOUNT} && [ -d "$YADISK_MOUNT/netbox-backups" ]; then
|
||||
echo -e "\n${GREEN}Бэкапы на Яндекс.Диске:${NC}"
|
||||
ls -lh $YADISK_MOUNT/netbox-backups/netbox_db_*.sql.gz 2>/dev/null | awk '{print " " $9 " (" $5 ")"}'
|
||||
fi
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Выбор бэкапа
|
||||
select_backup() {
|
||||
local backup_files=()
|
||||
|
||||
# Собираем все доступные бэкапы
|
||||
if [ -d "$BACKUP_DIR" ]; then
|
||||
for f in $BACKUP_DIR/netbox_db_*.sql.gz; do
|
||||
[ -f "$f" ] && backup_files+=("$f")
|
||||
done
|
||||
fi
|
||||
|
||||
if mountpoint -q ${YADISK_MOUNT} && [ -d "$YADISK_MOUNT/netbox-backups" ]; then
|
||||
for f in $YADISK_MOUNT/netbox-backups/netbox_db_*.sql.gz; do
|
||||
[ -f "$f" ] && backup_files+=("$f")
|
||||
done
|
||||
fi
|
||||
|
||||
if [ ${#backup_files[@]} -eq 0 ]; then
|
||||
print_error "Нет доступных бэкапов!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Выберите бэкап для восстановления:"
|
||||
for i in "${!backup_files[@]}"; do
|
||||
local size=$(du -h "${backup_files[$i]}" | cut -f1)
|
||||
local date=$(basename "${backup_files[$i]}" | sed 's/netbox_db_\(.*\)\.sql.gz/\1/')
|
||||
echo " $((i+1)). $date (${size}) - $(basename $(dirname ${backup_files[$i]}))"
|
||||
done
|
||||
echo " 0. Отмена"
|
||||
echo ""
|
||||
read -p "Введите номер [1-${#backup_files[@]}]: " choice
|
||||
|
||||
if [ "$choice" == "0" ]; then
|
||||
echo "Восстановление отменено."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ "$choice" -ge 1 ] && [ "$choice" -le ${#backup_files[@]} ]; then
|
||||
SELECTED_BACKUP="${backup_files[$((choice-1))]}"
|
||||
BACKUP_DATE=$(basename "$SELECTED_BACKUP" | sed 's/netbox_db_\(.*\)\.sql.gz/\1/')
|
||||
BACKUP_SOURCE=$(basename $(dirname "$SELECTED_BACKUP"))
|
||||
print_success "Выбран бэкап от $BACKUP_DATE (источник: $BACKUP_SOURCE)"
|
||||
else
|
||||
print_error "Неверный выбор!"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Проверка, что NetBox не запущен
|
||||
check_netbox_stopped() {
|
||||
if docker compose ps | grep -q "Up"; then
|
||||
print_warning "NetBox контейнеры запущены!"
|
||||
read -p "Остановить их перед восстановлением? [y/N]: " stop_containers
|
||||
if [[ $stop_containers =~ ^[Yy]$ ]]; then
|
||||
print_info "Останавливаем контейнеры..."
|
||||
docker compose down
|
||||
else
|
||||
print_error "Пожалуйста, остановите контейнеры вручную: docker compose down"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# Основной процесс восстановления
|
||||
restore_backup() {
|
||||
echo ""
|
||||
print_info "Начинаем восстановление из бэкапа $BACKUP_DATE"
|
||||
echo "=========================================="
|
||||
|
||||
# 1. Распаковка и восстановление базы данных
|
||||
echo ""
|
||||
print_info "1. Восстановление базы данных PostgreSQL..."
|
||||
|
||||
# Запускаем только PostgreSQL
|
||||
docker compose up -d postgres redis redis-cache
|
||||
sleep 5
|
||||
|
||||
# Проверяем, что PostgreSQL готов
|
||||
until docker compose exec -T postgres pg_isready -U netbox; do
|
||||
echo " Ожидаем запуска PostgreSQL..."
|
||||
sleep 2
|
||||
done
|
||||
|
||||
# Восстанавливаем базу данных
|
||||
echo " Восстанавливаем базу данных из $SELECTED_BACKUP..."
|
||||
gunzip -c "$SELECTED_BACKUP" | docker compose exec -T postgres psql -U netbox -d netbox
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
print_success "База данных восстановлена"
|
||||
else
|
||||
print_error "Ошибка восстановления базы данных"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 2. Восстановление медиа-файлов
|
||||
MEDIA_BACKUP="${BACKUP_DIR}/netbox_media_${BACKUP_DATE}.tar.gz"
|
||||
if [ ! -f "$MEDIA_BACKUP" ] && mountpoint -q ${YADISK_MOUNT}; then
|
||||
MEDIA_BACKUP="${YADISK_MOUNT}/netbox-backups/netbox_media_${BACKUP_DATE}.tar.gz"
|
||||
fi
|
||||
|
||||
if [ -f "$MEDIA_BACKUP" ]; then
|
||||
echo ""
|
||||
print_info "2. Восстановление медиа-файлов..."
|
||||
docker compose exec -T netbox rm -rf /opt/netbox/netbox/media/* 2>/dev/null
|
||||
docker compose exec -T netbox tar xzf - -C /opt/netbox/netbox/media < "$MEDIA_BACKUP"
|
||||
print_success "Медиа-файлы восстановлены"
|
||||
else
|
||||
print_warning "Медиа-бэкап не найден: netbox_media_${BACKUP_DATE}.tar.gz"
|
||||
fi
|
||||
|
||||
# 3. Восстановление .env файлов
|
||||
ENV_BACKUP="${BACKUP_DIR}/netbox_env_${BACKUP_DATE}.tar.gz"
|
||||
if [ ! -f "$ENV_BACKUP" ] && mountpoint -q ${YADISK_MOUNT}; then
|
||||
ENV_BACKUP="${YADISK_MOUNT}/netbox-backups/netbox_env_${BACKUP_DATE}.tar.gz"
|
||||
fi
|
||||
|
||||
if [ -f "$ENV_BACKUP" ]; then
|
||||
echo ""
|
||||
print_info "3. Восстановление .env файлов..."
|
||||
tar xzf "$ENV_BACKUP" -C /opt/netbox
|
||||
print_success ".env файлы восстановлены"
|
||||
else
|
||||
print_warning ".env бэкап не найден: netbox_env_${BACKUP_DATE}.tar.gz"
|
||||
fi
|
||||
|
||||
# 4. Запуск миграций и сбор статики
|
||||
echo ""
|
||||
print_info "4. Запуск миграций и сбор статики..."
|
||||
docker compose up -d netbox
|
||||
sleep 10
|
||||
docker compose exec netbox python /opt/netbox/netbox/manage.py migrate
|
||||
docker compose exec netbox python /opt/netbox/netbox/manage.py collectstatic --no-input
|
||||
|
||||
# 5. Запуск всех контейнеров
|
||||
echo ""
|
||||
print_info "5. Запуск всех сервисов..."
|
||||
docker compose up -d
|
||||
|
||||
echo ""
|
||||
print_success "Восстановление завершено!"
|
||||
echo ""
|
||||
print_info "Проверьте логи: docker compose logs netbox --tail 50"
|
||||
print_info "Веб-интерфейс: http://localhost:8080"
|
||||
}
|
||||
|
||||
# Функция для быстрого восстановления последнего бэкапа
|
||||
quick_restore() {
|
||||
local latest_backup=$(ls -t $BACKUP_DIR/netbox_db_*.sql.gz 2>/dev/null | head -1)
|
||||
if [ -z "$latest_backup" ] && mountpoint -q ${YADISK_MOUNT}; then
|
||||
latest_backup=$(ls -t $YADISK_MOUNT/netbox-backups/netbox_db_*.sql.gz 2>/dev/null | head -1)
|
||||
fi
|
||||
|
||||
if [ -z "$latest_backup" ]; then
|
||||
print_error "Нет доступных бэкапов!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
SELECTED_BACKUP="$latest_backup"
|
||||
BACKUP_DATE=$(basename "$SELECTED_BACKUP" | sed 's/netbox_db_\(.*\)\.sql.gz/\1/')
|
||||
BACKUP_SOURCE=$(basename $(dirname "$SELECTED_BACKUP"))
|
||||
print_info "Восстанавливаем последний бэкап: $BACKUP_DATE (источник: $BACKUP_SOURCE)"
|
||||
restore_backup
|
||||
}
|
||||
|
||||
# Основное меню
|
||||
main() {
|
||||
cd /opt/netbox
|
||||
|
||||
echo "🔄 NetBox Recovery Script"
|
||||
echo "========================"
|
||||
echo ""
|
||||
echo "1. Показать доступные бэкапы"
|
||||
echo "2. Восстановить из выбранного бэкапа"
|
||||
echo "3. Быстрое восстановление (последний бэкап)"
|
||||
echo "0. Выход"
|
||||
echo ""
|
||||
read -p "Выберите действие [0-3]: " action
|
||||
|
||||
case $action in
|
||||
1)
|
||||
show_backups
|
||||
;;
|
||||
2)
|
||||
show_backups
|
||||
select_backup
|
||||
check_netbox_stopped
|
||||
restore_backup
|
||||
;;
|
||||
3)
|
||||
quick_restore
|
||||
;;
|
||||
0)
|
||||
echo "Выход."
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
print_error "Неверный выбор!"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Запуск
|
||||
main "$@"
|
||||
@@ -0,0 +1,48 @@
|
||||
# Ruff configuration
|
||||
####################
|
||||
exclude = [
|
||||
".git",
|
||||
".netbox",
|
||||
".ruff_cache",
|
||||
".venv",
|
||||
"venv",
|
||||
]
|
||||
line-length = 120
|
||||
indent-width = 4
|
||||
respect-gitignore = true
|
||||
target-version = "py312"
|
||||
|
||||
[lint]
|
||||
preview = true
|
||||
extend-select = [
|
||||
"E1", # pycodestyle errors: indentation-related (e.g., unexpected/missing indent)
|
||||
"E2", # pycodestyle errors: whitespace-related (e.g., missing whitespace, extra spaces)
|
||||
"E3", # pycodestyle errors: blank lines / spacing around definitions
|
||||
"E501", # pycodestyle: line too long (enforced with `line-length` above)
|
||||
"W", # pycodestyle warnings (various style warnings, often whitespace/newlines)
|
||||
"I", # import sorting (isort-equivalent)
|
||||
"RET", # return semantics (flake8-return family: consistent/explicit returns; remove redundant else/assign before return)
|
||||
"UP", # pyupgrade: modernize syntax for your target Python (e.g., f-strings, built-in generics, newer stdlib idioms)
|
||||
"RUF022", # ruff: enforce sorted `__all__` lists
|
||||
]
|
||||
|
||||
ignore = [
|
||||
"E266", # pycodestyle: too many leading '#' for block comment
|
||||
"S110" # ruff try-except-pass
|
||||
]
|
||||
|
||||
[lint.per-file-ignores]
|
||||
"configuration/*" = [
|
||||
"E501", # pycodestyle: line too long (enforced with `line-length` above)
|
||||
"E722" # pycodestyle: do not use bare `except`
|
||||
]
|
||||
"docker/*" = [
|
||||
"E501", # pycodestyle: line too long (enforced with `line-length` above)
|
||||
"E722" # pycodestyle: do not use bare `except`
|
||||
]
|
||||
|
||||
|
||||
[format]
|
||||
quote-style = "single"
|
||||
indent-style = "space"
|
||||
line-ending = "lf"
|
||||
@@ -0,0 +1,17 @@
|
||||
LOGGING = {'version': 1, 'disable_existing_loggers': True}
|
||||
|
||||
PLUGINS = [
|
||||
'netbox.tests.dummy_plugin',
|
||||
]
|
||||
|
||||
ALLOW_TOKEN_RETRIEVAL = True
|
||||
|
||||
DEFAULT_PERMISSIONS = {}
|
||||
|
||||
API_TOKEN_PEPPERS = {
|
||||
1: 'TEST-VALUE-DO-NOT-USE-TEST-VALUE-DO-NOT-USE-TEST-VALUE-DO-NOT-USE',
|
||||
}
|
||||
|
||||
RQ = {
|
||||
'COMMIT_MODE': 'auto',
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
#!/bin/bash
|
||||
# Runs the original NetBox unit tests and tests whether all initializers work.
|
||||
# Usage:
|
||||
# ./test.sh latest
|
||||
# ./test.sh v2.9.7
|
||||
# ./test.sh develop-2.10
|
||||
# IMAGE='netboxcommunity/netbox:latest' ./test.sh
|
||||
# IMAGE='netboxcommunity/netbox:v2.9.7' ./test.sh
|
||||
# IMAGE='netboxcommunity/netbox:develop-2.10' ./test.sh
|
||||
# export IMAGE='netboxcommunity/netbox:latest'; ./test.sh
|
||||
# export IMAGE='netboxcommunity/netbox:v2.9.7'; ./test.sh
|
||||
# export IMAGE='netboxcommunity/netbox:develop-2.10'; ./test.sh
|
||||
|
||||
# exit when a command exits with an exit code != 0
|
||||
set -e
|
||||
|
||||
source ./build-functions/gh-functions.sh
|
||||
|
||||
# IMAGE is used by `docker-compose.yml` do determine the tag
|
||||
# of the Docker Image that is to be used
|
||||
if [ "${1}x" != "x" ]; then
|
||||
# Use the command line argument
|
||||
export IMAGE="netboxcommunity/netbox:${1}"
|
||||
else
|
||||
export IMAGE="${IMAGE-netboxcommunity/netbox:latest}"
|
||||
fi
|
||||
|
||||
# Ensure that an IMAGE is defined
|
||||
if [ -z "${IMAGE}" ]; then
|
||||
echo "⚠️ No image defined"
|
||||
|
||||
if [ -z "${DEBUG}" ]; then
|
||||
exit 1
|
||||
else
|
||||
echo "⚠️ Would 'exit 1' here, but DEBUG is '${DEBUG}'."
|
||||
fi
|
||||
fi
|
||||
|
||||
# The docker compose command to use
|
||||
doco="docker compose --file docker-compose.test.yml --file docker-compose.test.override.yml --project-name netbox_docker_test"
|
||||
|
||||
test_setup() {
|
||||
gh_echo "::group:: Test setup"
|
||||
echo "🏗 Setup up test environment"
|
||||
$doco up --detach --quiet-pull --wait --force-recreate --renew-anon-volumes --no-start
|
||||
$doco start postgres
|
||||
$doco start redis
|
||||
$doco start redis-cache
|
||||
gh_echo "::endgroup::"
|
||||
}
|
||||
|
||||
test_netbox_unit_tests() {
|
||||
gh_echo "::group:: Netbox unit tests"
|
||||
echo "⏱ Running NetBox Unit Tests"
|
||||
$doco run --rm netbox /opt/netbox/venv/bin/python /opt/netbox/netbox/manage.py test
|
||||
gh_echo "::endgroup::"
|
||||
}
|
||||
|
||||
test_compose_db_setup() {
|
||||
gh_echo "::group:: Netbox DB migrations"
|
||||
echo "⏱ Running NetBox DB migrations"
|
||||
$doco run --rm netbox /opt/netbox/venv/bin/python /opt/netbox/netbox/manage.py migrate
|
||||
gh_echo "::endgroup::"
|
||||
}
|
||||
|
||||
test_netbox_start() {
|
||||
gh_echo "::group:: Start Netbox service"
|
||||
echo "⏱ Starting NetBox services"
|
||||
$doco up --detach --wait
|
||||
gh_echo "::endgroup::"
|
||||
}
|
||||
|
||||
test_netbox_web() {
|
||||
gh_echo "::group:: Web service test"
|
||||
echo "⏱ Starting web service test"
|
||||
RESP_CODE=$(
|
||||
curl \
|
||||
--silent \
|
||||
--output /dev/null \
|
||||
--write-out '%{http_code}' \
|
||||
--request GET \
|
||||
--connect-timeout 5 \
|
||||
--max-time 10 \
|
||||
--retry 5 \
|
||||
--retry-delay 0 \
|
||||
--retry-max-time 40 \
|
||||
http://127.0.0.1:8000/login/
|
||||
)
|
||||
if [ "$RESP_CODE" == "200" ]; then
|
||||
echo "Webservice running"
|
||||
else
|
||||
echo "⚠️ Got response code '$RESP_CODE' but expected '200'"
|
||||
exit 1
|
||||
fi
|
||||
gh_echo "::endgroup::"
|
||||
}
|
||||
|
||||
test_cleanup() {
|
||||
echo "💣 Cleaning Up"
|
||||
gh_echo "::group:: Docker compose logs"
|
||||
$doco logs --no-color
|
||||
gh_echo "::endgroup::"
|
||||
gh_echo "::group:: Docker compose down"
|
||||
$doco down --volumes
|
||||
gh_echo "::endgroup::"
|
||||
}
|
||||
|
||||
echo "🐳🐳🐳 Start testing '${IMAGE}'"
|
||||
|
||||
# Make sure the cleanup script is executed
|
||||
trap test_cleanup EXIT ERR
|
||||
test_setup
|
||||
|
||||
test_netbox_unit_tests
|
||||
test_compose_db_setup
|
||||
test_netbox_start
|
||||
test_netbox_web
|
||||
|
||||
echo "🐳🐳🐳 Done testing '${IMAGE}'"
|
||||
Reference in New Issue
Block a user