Merge pull request #206983 from minijackson/netbox-3.4.1

netbox: 3.3.9 -> 3.4.7, netbox_3_3: init at 3.3.10, RFC42-style options, more tests

Changed files
+650 -154
nixos
doc
manual
release-notes
modules
services
web-apps
tests
pkgs
+4
nixos/doc/manual/release-notes/rl-2305.section.md
···
- `services.openssh.ciphers` to `services.openssh.settings.Ciphers`
- `services.openssh.gatewayPorts` to `services.openssh.settings.GatewayPorts`
+
- `netbox` was updated to 3.4. NixOS' `services.netbox.package` still defaults to 3.3 if `stateVersion` is earlier than 23.05. Please review upstream's [breaking changes](https://github.com/netbox-community/netbox/releases/tag/v3.4.0), and upgrade NetBox by changing `services.netbox.package`. Database migrations will be run automatically.
+
+
- `services.netbox` now support RFC42-style options, through `services.netbox.settings`.
+
- `services.mastodon` gained a tootctl wrapped named `mastodon-tootctl` similar to `nextcloud-occ` which can be executed from any user and switches to the configured mastodon user with sudo and sources the environment variables.
- DocBook option documentation, which has been deprecated since 22.11, will now cause a warning when documentation is built. Out-of-tree modules should migrate to using CommonMark documentation as outlined in [](#sec-option-declarations) to silence this warning.
+124 -38
nixos/modules/services/web-apps/netbox.nix
···
let
cfg = config.services.netbox;
+
pythonFmt = pkgs.formats.pythonVars {};
staticDir = cfg.dataDir + "/static";
-
configFile = pkgs.writeTextFile {
-
name = "configuration.py";
-
text = ''
-
STATIC_ROOT = '${staticDir}'
-
MEDIA_ROOT = '${cfg.dataDir}/media'
-
REPORTS_ROOT = '${cfg.dataDir}/reports'
-
SCRIPTS_ROOT = '${cfg.dataDir}/scripts'
-
ALLOWED_HOSTS = ['*']
-
DATABASE = {
-
'NAME': 'netbox',
-
'USER': 'netbox',
-
'HOST': '/run/postgresql',
-
}
+
settingsFile = pythonFmt.generate "netbox-settings.py" cfg.settings;
+
extraConfigFile = pkgs.writeTextFile {
+
name = "netbox-extraConfig.py";
+
text = cfg.extraConfig;
+
};
+
configFile = pkgs.concatText "configuration.py" [ settingsFile extraConfigFile ];
-
# 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': {
-
'URL': 'unix://${config.services.redis.servers.netbox.unixSocket}?db=0',
-
'SSL': False,
-
},
-
'caching': {
-
'URL': 'unix://${config.services.redis.servers.netbox.unixSocket}?db=1',
-
'SSL': False,
-
}
-
}
-
-
with open("${cfg.secretKeyFile}", "r") as file:
-
SECRET_KEY = file.readline()
-
-
${optionalString cfg.enableLdap "REMOTE_AUTH_BACKEND = 'netbox.authentication.LDAPBackend'"}
-
-
${cfg.extraConfig}
-
'';
-
};
-
pkg = (pkgs.netbox.overrideAttrs (old: {
+
pkg = (cfg.package.overrideAttrs (old: {
installPhase = old.installPhase + ''
ln -s ${configFile} $out/opt/netbox/netbox/netbox/configuration.py
'' + optionalString cfg.enableLdap ''
···
'';
};
+
settings = lib.mkOption {
+
description = lib.mdDoc ''
+
Configuration options to set in `configuration.py`.
+
See the [documentation](https://docs.netbox.dev/en/stable/configuration/) for more possible options.
+
'';
+
+
default = { };
+
+
type = lib.types.submodule {
+
freeformType = pythonFmt.type;
+
+
options = {
+
ALLOWED_HOSTS = lib.mkOption {
+
type = with lib.types; listOf str;
+
default = ["*"];
+
description = lib.mdDoc ''
+
A list of valid fully-qualified domain names (FQDNs) and/or IP
+
addresses that can be used to reach the NetBox service.
+
'';
+
};
+
};
+
};
+
};
+
listenAddress = mkOption {
type = types.str;
default = "[::1]";
description = lib.mdDoc ''
Address the server will listen on.
+
'';
+
};
+
+
package = mkOption {
+
type = types.package;
+
default = if versionAtLeast config.system.stateVersion "23.05" then pkgs.netbox else pkgs.netbox_3_3;
+
defaultText = literalExpression ''
+
if versionAtLeast config.system.stateVersion "23.05" then pkgs.netbox else pkgs.netbox_3_3;
+
'';
+
description = lib.mdDoc ''
+
NetBox package to use.
'';
};
···
default = "";
description = lib.mdDoc ''
Additional lines of configuration appended to the `configuration.py`.
-
See the [documentation](https://netbox.readthedocs.io/en/stable/configuration/optional-settings/) for more possible options.
+
See the [documentation](https://docs.netbox.dev/en/stable/configuration/) for more possible options.
'';
};
···
Path to the Configuration-File for LDAP-Authentication, will be loaded as `ldap_config.py`.
See the [documentation](https://netbox.readthedocs.io/en/stable/installation/6-ldap/#configuration) for possible options.
'';
+
example = ''
+
import ldap
+
from django_auth_ldap.config import LDAPSearch, PosixGroupType
+
+
AUTH_LDAP_SERVER_URI = "ldaps://ldap.example.com/"
+
+
AUTH_LDAP_USER_SEARCH = LDAPSearch(
+
"ou=accounts,ou=posix,dc=example,dc=com",
+
ldap.SCOPE_SUBTREE,
+
"(uid=%(user)s)",
+
)
+
+
AUTH_LDAP_GROUP_SEARCH = LDAPSearch(
+
"ou=groups,ou=posix,dc=example,dc=com",
+
ldap.SCOPE_SUBTREE,
+
"(objectClass=posixGroup)",
+
)
+
AUTH_LDAP_GROUP_TYPE = PosixGroupType()
+
+
# Mirror LDAP group assignments.
+
AUTH_LDAP_MIRROR_GROUPS = True
+
+
# For more granular permissions, we can map LDAP groups to Django groups.
+
AUTH_LDAP_FIND_GROUP_PERMS = True
+
'';
};
};
config = mkIf cfg.enable {
-
services.netbox.plugins = mkIf cfg.enableLdap (ps: [ ps.django-auth-ldap ]);
+
services.netbox = {
+
plugins = mkIf cfg.enableLdap (ps: [ ps.django-auth-ldap ]);
+
settings = {
+
STATIC_ROOT = staticDir;
+
MEDIA_ROOT = "${cfg.dataDir}/media";
+
REPORTS_ROOT = "${cfg.dataDir}/reports";
+
SCRIPTS_ROOT = "${cfg.dataDir}/scripts";
+
+
DATABASE = {
+
NAME = "netbox";
+
USER = "netbox";
+
HOST = "/run/postgresql";
+
};
+
+
# 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 = {
+
URL = "unix://${config.services.redis.servers.netbox.unixSocket}?db=0";
+
SSL = false;
+
};
+
caching = {
+
URL = "unix://${config.services.redis.servers.netbox.unixSocket}?db=1";
+
SSL = false;
+
};
+
};
+
+
REMOTE_AUTH_BACKEND = lib.mkIf cfg.enableLdap "netbox.authentication.LDAPBackend";
+
+
LOGGING = lib.mkDefault {
+
version = 1;
+
+
formatters.precise.format = "[%(levelname)s@%(name)s] %(message)s";
+
+
handlers.console = {
+
class = "logging.StreamHandler";
+
formatter = "precise";
+
};
+
+
# log to console/systemd instead of file
+
root = {
+
level = "INFO";
+
handlers = [ "console" ];
+
};
+
};
+
};
+
+
extraConfig = ''
+
with open("${cfg.secretKeyFile}", "r") as file:
+
SECRET_KEY = file.readline()
+
'';
+
};
services.redis.servers.netbox.enable = true;
+2 -1
nixos/tests/all-tests.nix
···
netdata = handleTest ./netdata.nix {};
networking.networkd = handleTest ./networking.nix { networkd = true; };
networking.scripted = handleTest ./networking.nix { networkd = false; };
-
netbox = handleTest ./web-apps/netbox.nix {};
+
netbox = handleTest ./web-apps/netbox.nix { inherit (pkgs) netbox; };
+
netbox_3_3 = handleTest ./web-apps/netbox.nix { netbox = pkgs.netbox_3_3; };
# TODO: put in networking.nix after the test becomes more complete
networkingProxy = handleTest ./networking-proxy.nix {};
nextcloud = handleTest ./nextcloud {};
+292 -5
nixos/tests/web-apps/netbox.nix
···
-
import ../make-test-python.nix ({ lib, pkgs, ... }: {
+
let
+
ldapDomain = "example.org";
+
ldapSuffix = "dc=example,dc=org";
+
+
ldapRootUser = "admin";
+
ldapRootPassword = "foobar";
+
+
testUser = "alice";
+
testPassword = "verySecure";
+
testGroup = "netbox-users";
+
in import ../make-test-python.nix ({ lib, pkgs, netbox, ... }: {
name = "netbox";
meta = with lib.maintainers; {
-
maintainers = [ n0emis ];
+
maintainers = [ minijackson n0emis ];
};
-
nodes.machine = { ... }: {
+
nodes.machine = { config, ... }: {
services.netbox = {
enable = true;
+
package = netbox;
secretKeyFile = pkgs.writeText "secret" ''
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789
'';
+
+
enableLdap = true;
+
ldapConfigPath = pkgs.writeText "ldap_config.py" ''
+
import ldap
+
from django_auth_ldap.config import LDAPSearch, PosixGroupType
+
+
AUTH_LDAP_SERVER_URI = "ldap://localhost/"
+
+
AUTH_LDAP_USER_SEARCH = LDAPSearch(
+
"ou=accounts,ou=posix,${ldapSuffix}",
+
ldap.SCOPE_SUBTREE,
+
"(uid=%(user)s)",
+
)
+
+
AUTH_LDAP_GROUP_SEARCH = LDAPSearch(
+
"ou=groups,ou=posix,${ldapSuffix}",
+
ldap.SCOPE_SUBTREE,
+
"(objectClass=posixGroup)",
+
)
+
AUTH_LDAP_GROUP_TYPE = PosixGroupType()
+
+
# Mirror LDAP group assignments.
+
AUTH_LDAP_MIRROR_GROUPS = True
+
+
# For more granular permissions, we can map LDAP groups to Django groups.
+
AUTH_LDAP_FIND_GROUP_PERMS = True
+
'';
};
+
+
services.nginx = {
+
enable = true;
+
+
recommendedProxySettings = true;
+
+
virtualHosts.netbox = {
+
default = true;
+
locations."/".proxyPass = "http://localhost:${toString config.services.netbox.port}";
+
locations."/static/".alias = "/var/lib/netbox/static/";
+
};
+
};
+
+
# Adapted from the sssd-ldap NixOS test
+
services.openldap = {
+
enable = true;
+
settings = {
+
children = {
+
"cn=schema".includes = [
+
"${pkgs.openldap}/etc/schema/core.ldif"
+
"${pkgs.openldap}/etc/schema/cosine.ldif"
+
"${pkgs.openldap}/etc/schema/inetorgperson.ldif"
+
"${pkgs.openldap}/etc/schema/nis.ldif"
+
];
+
"olcDatabase={1}mdb" = {
+
attrs = {
+
objectClass = [ "olcDatabaseConfig" "olcMdbConfig" ];
+
olcDatabase = "{1}mdb";
+
olcDbDirectory = "/var/lib/openldap/db";
+
olcSuffix = ldapSuffix;
+
olcRootDN = "cn=${ldapRootUser},${ldapSuffix}";
+
olcRootPW = ldapRootPassword;
+
};
+
};
+
};
+
};
+
declarativeContents = {
+
${ldapSuffix} = ''
+
dn: ${ldapSuffix}
+
objectClass: top
+
objectClass: dcObject
+
objectClass: organization
+
o: ${ldapDomain}
+
+
dn: ou=posix,${ldapSuffix}
+
objectClass: top
+
objectClass: organizationalUnit
+
+
dn: ou=accounts,ou=posix,${ldapSuffix}
+
objectClass: top
+
objectClass: organizationalUnit
+
+
dn: uid=${testUser},ou=accounts,ou=posix,${ldapSuffix}
+
objectClass: person
+
objectClass: posixAccount
+
userPassword: ${testPassword}
+
homeDirectory: /home/${testUser}
+
uidNumber: 1234
+
gidNumber: 1234
+
cn: ""
+
sn: ""
+
+
dn: ou=groups,ou=posix,${ldapSuffix}
+
objectClass: top
+
objectClass: organizationalUnit
+
+
dn: cn=${testGroup},ou=groups,ou=posix,${ldapSuffix}
+
objectClass: posixGroup
+
gidNumber: 2345
+
memberUid: ${testUser}
+
'';
+
};
+
};
+
+
users.users.nginx.extraGroups = [ "netbox" ];
+
+
networking.firewall.allowedTCPPorts = [ 80 ];
};
-
testScript = ''
-
machine.start()
+
testScript = let
+
changePassword = pkgs.writeText "change-password.py" ''
+
from django.contrib.auth.models import User
+
u = User.objects.get(username='netbox')
+
u.set_password('netbox')
+
u.save()
+
'';
+
in ''
+
from typing import Any, Dict
+
import json
+
+
start_all()
machine.wait_for_unit("netbox.target")
machine.wait_until_succeeds("journalctl --since -1m --unit netbox --grep Listening")
···
with subtest("Staticfiles are generated"):
machine.succeed("test -e /var/lib/netbox/static/netbox.js")
+
+
with subtest("Superuser can be created"):
+
machine.succeed(
+
"netbox-manage createsuperuser --noinput --username netbox --email netbox@example.com"
+
)
+
# Django doesn't have a "clean" way of inputting the password from the command line
+
machine.succeed("cat '${changePassword}' | netbox-manage shell")
+
+
machine.wait_for_unit("network.target")
+
+
with subtest("Home screen loads from nginx"):
+
machine.succeed(
+
"curl -sSfL http://localhost | grep '<title>Home | NetBox</title>'"
+
)
+
+
with subtest("Staticfiles can be fetched"):
+
machine.succeed("curl -sSfL http://localhost/static/netbox.js")
+
machine.succeed("curl -sSfL http://localhost/static/docs/")
+
+
with subtest("Can interact with API"):
+
json.loads(
+
machine.succeed("curl -sSfL -H 'Accept: application/json' 'http://localhost/api/'")
+
)
+
+
def login(username: str, password: str):
+
encoded_data = json.dumps({"username": username, "password": password})
+
uri = "/users/tokens/provision/"
+
result = json.loads(
+
machine.succeed(
+
"curl -sSfL "
+
"-X POST "
+
"-H 'Accept: application/json' "
+
"-H 'Content-Type: application/json' "
+
f"'http://localhost/api{uri}' "
+
f"--data '{encoded_data}'"
+
)
+
)
+
return result["key"]
+
+
with subtest("Can login"):
+
auth_token = login("netbox", "netbox")
+
+
def get(uri: str):
+
return json.loads(
+
machine.succeed(
+
"curl -sSfL "
+
"-H 'Accept: application/json' "
+
f"-H 'Authorization: Token {auth_token}' "
+
f"'http://localhost/api{uri}'"
+
)
+
)
+
+
def delete(uri: str):
+
return machine.succeed(
+
"curl -sSfL "
+
f"-X DELETE "
+
"-H 'Accept: application/json' "
+
f"-H 'Authorization: Token {auth_token}' "
+
f"'http://localhost/api{uri}'"
+
)
+
+
+
def data_request(uri: str, method: str, data: Dict[str, Any]):
+
encoded_data = json.dumps(data)
+
return json.loads(
+
machine.succeed(
+
"curl -sSfL "
+
f"-X {method} "
+
"-H 'Accept: application/json' "
+
"-H 'Content-Type: application/json' "
+
f"-H 'Authorization: Token {auth_token}' "
+
f"'http://localhost/api{uri}' "
+
f"--data '{encoded_data}'"
+
)
+
)
+
+
def post(uri: str, data: Dict[str, Any]):
+
return data_request(uri, "POST", data)
+
+
def patch(uri: str, data: Dict[str, Any]):
+
return data_request(uri, "PATCH", data)
+
+
with subtest("Can create objects"):
+
result = post("/dcim/sites/", {"name": "Test site", "slug": "test-site"})
+
site_id = result["id"]
+
+
# Example from:
+
# http://netbox.extra.cea.fr/static/docs/integrations/rest-api/#creating-a-new-object
+
post("/ipam/prefixes/", {"prefix": "192.0.2.0/24", "site": site_id})
+
+
result = post(
+
"/dcim/manufacturers/",
+
{"name": "Test manufacturer", "slug": "test-manufacturer"}
+
)
+
manufacturer_id = result["id"]
+
+
# Had an issue with device-types before NetBox 3.4.0
+
result = post(
+
"/dcim/device-types/",
+
{
+
"model": "Test device type",
+
"manufacturer": manufacturer_id,
+
"slug": "test-device-type",
+
},
+
)
+
device_type_id = result["id"]
+
+
with subtest("Can list objects"):
+
result = get("/dcim/sites/")
+
+
assert result["count"] == 1
+
assert result["results"][0]["id"] == site_id
+
assert result["results"][0]["name"] == "Test site"
+
assert result["results"][0]["description"] == ""
+
+
result = get("/dcim/device-types/")
+
assert result["count"] == 1
+
assert result["results"][0]["id"] == device_type_id
+
assert result["results"][0]["model"] == "Test device type"
+
+
with subtest("Can update objects"):
+
new_description = "Test site description"
+
patch(f"/dcim/sites/{site_id}/", {"description": new_description})
+
result = get(f"/dcim/sites/{site_id}/")
+
assert result["description"] == new_description
+
+
with subtest("Can delete objects"):
+
# Delete a device-type since no object depends on it
+
delete(f"/dcim/device-types/{device_type_id}/")
+
+
result = get("/dcim/device-types/")
+
assert result["count"] == 0
+
+
with subtest("Can use the GraphQL API"):
+
encoded_data = json.dumps({
+
"query": "query { prefix_list { prefix, site { id, description } } }",
+
})
+
result = json.loads(
+
machine.succeed(
+
"curl -sSfL "
+
"-H 'Accept: application/json' "
+
"-H 'Content-Type: application/json' "
+
f"-H 'Authorization: Token {auth_token}' "
+
"'http://localhost/graphql/' "
+
f"--data '{encoded_data}'"
+
)
+
)
+
+
assert len(result["data"]["prefix_list"]) == 1
+
assert result["data"]["prefix_list"][0]["prefix"] == "192.0.2.0/24"
+
assert result["data"]["prefix_list"][0]["site"]["id"] == str(site_id)
+
assert result["data"]["prefix_list"][0]["site"]["description"] == new_description
+
+
with subtest("Can login with LDAP"):
+
machine.wait_for_unit("openldap.service")
+
login("alice", "${testPassword}")
+
+
with subtest("Can associate LDAP groups"):
+
result = get("/users/users/?username=${testUser}")
+
+
assert result["count"] == 1
+
assert any(group["name"] == "${testGroup}" for group in result["results"][0]["groups"])
'';
})
+35
pkgs/pkgs-lib/formats.nix
···
'';
};
+
# Outputs a succession of Python variable assignments
+
# Useful for many Django-based services
+
pythonVars = {}: {
+
type = with lib.types; let
+
valueType = nullOr(oneOf [
+
bool
+
float
+
int
+
path
+
str
+
(attrsOf valueType)
+
(listOf valueType)
+
]) // {
+
description = "Python value";
+
};
+
in attrsOf valueType;
+
generate = name: value: pkgs.callPackage ({ runCommand, python3, black }: runCommand name {
+
nativeBuildInputs = [ python3 black ];
+
value = builtins.toJSON value;
+
pythonGen = ''
+
import json
+
import os
+
+
with open(os.environ["valuePath"], "r") as f:
+
for key, value in json.load(f).items():
+
print(f"{key} = {repr(value)}")
+
'';
+
passAsFile = [ "value" "pythonGen" ];
+
} ''
+
cat "$valuePath"
+
python3 "$pythonGenPath" > $out
+
black $out
+
'') {};
+
};
+
}
+8 -8
pkgs/servers/web-apps/netbox/config.patch
···
diff --git a/netbox/netbox/settings.py b/netbox/netbox/settings.py
-
index d5a7bfaec..68754a8c5 100644
+
index 2de06dd10..00406af48 100644
--- a/netbox/netbox/settings.py
+++ b/netbox/netbox/settings.py
-
@@ -222,6 +222,7 @@ TASKS_REDIS_PASSWORD = TASKS_REDIS.get('PASSWORD', '')
-
TASKS_REDIS_DATABASE = TASKS_REDIS.get('DATABASE', 0)
+
@@ -236,6 +236,7 @@ TASKS_REDIS_DATABASE = TASKS_REDIS.get('DATABASE', 0)
TASKS_REDIS_SSL = TASKS_REDIS.get('SSL', False)
TASKS_REDIS_SKIP_TLS_VERIFY = TASKS_REDIS.get('INSECURE_SKIP_TLS_VERIFY', False)
+
TASKS_REDIS_CA_CERT_PATH = TASKS_REDIS.get('CA_CERT_PATH', False)
+TASKS_REDIS_URL = TASKS_REDIS.get('URL')
# Caching
if 'caching' not in REDIS:
-
@@ -236,11 +237,12 @@ CACHING_REDIS_SENTINELS = REDIS['caching'].get('SENTINELS', [])
-
CACHING_REDIS_SENTINEL_SERVICE = REDIS['caching'].get('SENTINEL_SERVICE', 'default')
+
@@ -253,11 +254,12 @@ CACHING_REDIS_SENTINEL_SERVICE = REDIS['caching'].get('SENTINEL_SERVICE', 'defau
CACHING_REDIS_PROTO = 'rediss' if REDIS['caching'].get('SSL', False) else 'redis'
CACHING_REDIS_SKIP_TLS_VERIFY = REDIS['caching'].get('INSECURE_SKIP_TLS_VERIFY', False)
+
CACHING_REDIS_CA_CERT_PATH = REDIS['caching'].get('CA_CERT_PATH', False)
+CACHING_REDIS_URL = REDIS['caching'].get('URL', f'{CACHING_REDIS_PROTO}://{CACHING_REDIS_HOST}:{CACHING_REDIS_PORT}/{CACHING_REDIS_DATABASE}')
CACHES = {
'default': {
'BACKEND': 'django_redis.cache.RedisCache',
-
- 'LOCATION': f'{CACHING_REDIS_PROTO}://{CACHING_REDIS_HOST}:{CACHING_REDIS_PORT}/{CACHING_REDIS_DATABASE}',
+
- 'LOCATION': f'{CACHING_REDIS_PROTO}://{CACHING_REDIS_USERNAME_HOST}:{CACHING_REDIS_PORT}/{CACHING_REDIS_DATABASE}',
+ 'LOCATION': CACHING_REDIS_URL,
'OPTIONS': {
'CLIENT_CLASS': 'django_redis.client.DefaultClient',
'PASSWORD': CACHING_REDIS_PASSWORD,
-
@@ -383,7 +385,7 @@ USE_X_FORWARDED_HOST = True
+
@@ -410,7 +412,7 @@ USE_X_FORWARDED_HOST = True
X_FRAME_OPTIONS = 'SAMEORIGIN'
# Static files (CSS, JavaScript, Images)
···
STATIC_URL = f'/{BASE_PATH}static/'
STATICFILES_DIRS = (
os.path.join(BASE_DIR, 'project-static', 'dist'),
-
@@ -562,6 +564,14 @@ if TASKS_REDIS_USING_SENTINEL:
+
@@ -640,6 +642,14 @@ if TASKS_REDIS_USING_SENTINEL:
'socket_connect_timeout': TASKS_REDIS_SENTINEL_TIMEOUT
},
}
+50
pkgs/servers/web-apps/netbox/config_3_3.patch
···
+
diff --git a/netbox/netbox/settings.py b/netbox/netbox/settings.py
+
index d5a7bfaec..68754a8c5 100644
+
--- a/netbox/netbox/settings.py
+
+++ b/netbox/netbox/settings.py
+
@@ -222,6 +222,7 @@ TASKS_REDIS_PASSWORD = TASKS_REDIS.get('PASSWORD', '')
+
TASKS_REDIS_DATABASE = TASKS_REDIS.get('DATABASE', 0)
+
TASKS_REDIS_SSL = TASKS_REDIS.get('SSL', False)
+
TASKS_REDIS_SKIP_TLS_VERIFY = TASKS_REDIS.get('INSECURE_SKIP_TLS_VERIFY', False)
+
+TASKS_REDIS_URL = TASKS_REDIS.get('URL')
+
+
# Caching
+
if 'caching' not in REDIS:
+
@@ -236,11 +237,12 @@ CACHING_REDIS_SENTINELS = REDIS['caching'].get('SENTINELS', [])
+
CACHING_REDIS_SENTINEL_SERVICE = REDIS['caching'].get('SENTINEL_SERVICE', 'default')
+
CACHING_REDIS_PROTO = 'rediss' if REDIS['caching'].get('SSL', False) else 'redis'
+
CACHING_REDIS_SKIP_TLS_VERIFY = REDIS['caching'].get('INSECURE_SKIP_TLS_VERIFY', False)
+
+CACHING_REDIS_URL = REDIS['caching'].get('URL', f'{CACHING_REDIS_PROTO}://{CACHING_REDIS_HOST}:{CACHING_REDIS_PORT}/{CACHING_REDIS_DATABASE}')
+
+
CACHES = {
+
'default': {
+
'BACKEND': 'django_redis.cache.RedisCache',
+
- 'LOCATION': f'{CACHING_REDIS_PROTO}://{CACHING_REDIS_HOST}:{CACHING_REDIS_PORT}/{CACHING_REDIS_DATABASE}',
+
+ 'LOCATION': CACHING_REDIS_URL,
+
'OPTIONS': {
+
'CLIENT_CLASS': 'django_redis.client.DefaultClient',
+
'PASSWORD': CACHING_REDIS_PASSWORD,
+
@@ -383,7 +385,7 @@ USE_X_FORWARDED_HOST = True
+
X_FRAME_OPTIONS = 'SAMEORIGIN'
+
+
# Static files (CSS, JavaScript, Images)
+
-STATIC_ROOT = BASE_DIR + '/static'
+
+STATIC_ROOT = getattr(configuration, 'STATIC_ROOT', os.path.join(BASE_DIR, 'static')).rstrip('/')
+
STATIC_URL = f'/{BASE_PATH}static/'
+
STATICFILES_DIRS = (
+
os.path.join(BASE_DIR, 'project-static', 'dist'),
+
@@ -562,6 +564,14 @@ if TASKS_REDIS_USING_SENTINEL:
+
'socket_connect_timeout': TASKS_REDIS_SENTINEL_TIMEOUT
+
},
+
}
+
+elif TASKS_REDIS_URL:
+
+ RQ_PARAMS = {
+
+ 'URL': TASKS_REDIS_URL,
+
+ 'PASSWORD': TASKS_REDIS_PASSWORD,
+
+ 'SSL': TASKS_REDIS_SSL,
+
+ 'SSL_CERT_REQS': None if TASKS_REDIS_SKIP_TLS_VERIFY else 'required',
+
+ 'DEFAULT_TIMEOUT': RQ_DEFAULT_TIMEOUT,
+
+ }
+
else:
+
RQ_PARAMS = {
+
'HOST': TASKS_REDIS_HOST,
+23 -101
pkgs/servers/web-apps/netbox/default.nix
···
-
{ lib
-
, pkgs
-
, fetchFromGitHub
-
, fetchpatch
-
, nixosTests
-
, python3
-
-
, plugins ? ps: [] }:
-
+
{ lib, nixosTests, callPackage, fetchpatch }:
let
-
py = python3 // {
-
pkgs = python3.pkgs.overrideScope (self: super: {
-
django = super.django_4;
-
});
-
};
-
-
extraBuildInputs = plugins py.pkgs;
+
generic = import ./generic.nix;
in
-
py.pkgs.buildPythonApplication rec {
-
pname = "netbox";
-
version = "3.3.9";
-
-
format = "other";
-
-
src = fetchFromGitHub {
-
owner = "netbox-community";
-
repo = pname;
-
rev = "refs/tags/v${version}";
-
sha256 = "sha256-KhnxD5pjlEIgISl4RMbhLCDwgUDfGFRi88ZcP1ndMhI=";
-
};
-
-
patches = [
+
{
+
netbox_3_3 = callPackage generic {
+
version = "3.3.10";
+
hash = "sha256-MeOfTU5IxNDoUh7FyvwAQNRC/CE0R6p40WnlF+3RuxA=";
+
extraPatches = [
# Allow setting the STATIC_ROOT from within the configuration and setting a custom redis URL
-
./config.patch
+
./config_3_3.patch
./graphql-3_2_0.patch
# fix compatibility ith django 4.1
(fetchpatch {
···
})
];
-
propagatedBuildInputs = with py.pkgs; [
-
bleach
-
django_4
-
django-cors-headers
-
django-debug-toolbar
-
django-filter
-
django-graphiql-debug-toolbar
-
django-mptt
-
django-pglocks
-
django-prometheus
-
django-redis
-
django-rq
-
django-tables2
-
django-taggit
-
django-timezone-field
-
djangorestframework
-
drf-yasg
-
swagger-spec-validator # from drf-yasg[validation]
-
graphene-django
-
jinja2
-
markdown
-
markdown-include
-
netaddr
-
pillow
-
psycopg2
-
pyyaml
-
sentry-sdk
-
social-auth-core
-
social-auth-app-django
-
svgwrite
-
tablib
-
jsonschema
-
] ++ extraBuildInputs;
+
tests.netbox = nixosTests.netbox_3_3;
+
maintainers = with lib.maintainers; [ n0emis raitobezarius ];
+
eol = true;
+
};
-
buildInputs = with py.pkgs; [
-
mkdocs-material
-
mkdocs-material-extensions
-
mkdocstrings
-
mkdocstrings-python
+
netbox = callPackage generic {
+
version = "3.4.7";
+
hash = "sha256-pWHGyzLc0tqfehWbCMF1l96L1pewb5FXBUkw9EqPtP8=";
+
extraPatches = [
+
# Allow setting the STATIC_ROOT from within the configuration and setting a custom redis URL
+
./config.patch
];
-
-
nativeBuildInputs = [
-
py.pkgs.mkdocs
-
];
-
-
postBuild = ''
-
PYTHONPATH=$PYTHONPATH:netbox/
-
python -m mkdocs build
-
'';
-
-
installPhase = ''
-
mkdir -p $out/opt/netbox
-
cp -r . $out/opt/netbox
-
chmod +x $out/opt/netbox/netbox/manage.py
-
makeWrapper $out/opt/netbox/netbox/manage.py $out/bin/netbox \
-
--prefix PYTHONPATH : "$PYTHONPATH"
-
'';
-
-
passthru = {
-
# PYTHONPATH of all dependencies used by the package
-
pythonPath = python3.pkgs.makePythonPath propagatedBuildInputs;
-
-
tests = {
-
inherit (nixosTests) netbox;
-
};
+
tests = {
+
inherit (nixosTests) netbox;
};
-
meta = with lib; {
-
homepage = "https://github.com/netbox-community/netbox";
-
description = "IP address management (IPAM) and data center infrastructure management (DCIM) tool";
-
license = licenses.asl20;
-
maintainers = with maintainers; [ n0emis raitobezarius ];
-
};
-
}
+
maintainers = with lib.maintainers; [ minijackson n0emis raitobezarius ];
+
};
+
}
+110
pkgs/servers/web-apps/netbox/generic.nix
···
+
{ lib
+
, fetchFromGitHub
+
, python3
+
, version
+
, hash
+
, plugins ? ps: []
+
, extraPatches ? []
+
, tests ? {}
+
, maintainers ? []
+
, eol ? false
+
}:
+
let
+
py = python3 // {
+
pkgs = python3.pkgs.overrideScope (self: super: {
+
django = super.django_4;
+
});
+
};
+
+
extraBuildInputs = plugins py.pkgs;
+
in
+
py.pkgs.buildPythonApplication rec {
+
pname = "netbox";
+
inherit version;
+
+
format = "other";
+
+
src = fetchFromGitHub {
+
owner = "netbox-community";
+
repo = pname;
+
rev = "refs/tags/v${version}";
+
inherit hash;
+
};
+
+
patches = extraPatches;
+
+
propagatedBuildInputs = with py.pkgs; [
+
bleach
+
django_4
+
django-cors-headers
+
django-debug-toolbar
+
django-filter
+
django-graphiql-debug-toolbar
+
django-mptt
+
django-pglocks
+
django-prometheus
+
django-redis
+
django-rq
+
django-tables2
+
django-taggit
+
django-timezone-field
+
djangorestframework
+
drf-yasg
+
swagger-spec-validator # from drf-yasg[validation]
+
graphene-django
+
jinja2
+
markdown
+
markdown-include
+
netaddr
+
pillow
+
psycopg2
+
pyyaml
+
sentry-sdk
+
social-auth-core
+
social-auth-app-django
+
svgwrite
+
tablib
+
jsonschema
+
] ++ extraBuildInputs;
+
+
buildInputs = with py.pkgs; [
+
mkdocs-material
+
mkdocs-material-extensions
+
mkdocstrings
+
mkdocstrings-python
+
];
+
+
nativeBuildInputs = [
+
py.pkgs.mkdocs
+
];
+
+
postBuild = ''
+
PYTHONPATH=$PYTHONPATH:netbox/
+
python -m mkdocs build
+
'';
+
+
installPhase = ''
+
mkdir -p $out/opt/netbox
+
cp -r . $out/opt/netbox
+
chmod +x $out/opt/netbox/netbox/manage.py
+
makeWrapper $out/opt/netbox/netbox/manage.py $out/bin/netbox \
+
--prefix PYTHONPATH : "$PYTHONPATH"
+
'';
+
+
passthru = {
+
# PYTHONPATH of all dependencies used by the package
+
pythonPath = python3.pkgs.makePythonPath propagatedBuildInputs;
+
inherit tests;
+
};
+
+
meta = {
+
homepage = "https://github.com/netbox-community/netbox";
+
description = "IP address management (IPAM) and data center infrastructure management (DCIM) tool";
+
license = lib.licenses.asl20;
+
knownVulnerabilities = (lib.optional eol "Netbox version ${version} is EOL; please upgrade by following the current release notes instructions.");
+
# Warning:
+
# Notice the missing `lib` in the inherit: it is using this function argument rather than a `with lib;` argument.
+
# If you replace this by `with lib;`, pay attention it does not inherit all maintainers in nixpkgs.
+
inherit maintainers;
+
};
+
}
+2 -1
pkgs/top-level/all-packages.nix
···
netbootxyz-efi = callPackage ../tools/misc/netbootxyz-efi { };
-
netbox = callPackage ../servers/web-apps/netbox { };
+
inherit (callPackage ../servers/web-apps/netbox { })
+
netbox_3_3 netbox;
netcat = libressl.nc;