mirror of
https://github.com/NixOS/nixpkgs.git
synced 2025-07-03 22:32:26 +03:00
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
This commit is contained in:
commit
a6bc6ed645
10 changed files with 650 additions and 154 deletions
|
@ -235,6 +235,10 @@ In addition to numerous new and upgraded packages, this release has the followin
|
||||||
- `services.openssh.ciphers` to `services.openssh.settings.Ciphers`
|
- `services.openssh.ciphers` to `services.openssh.settings.Ciphers`
|
||||||
- `services.openssh.gatewayPorts` to `services.openssh.settings.GatewayPorts`
|
- `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.
|
- `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.
|
- 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.
|
||||||
|
|
|
@ -4,45 +4,17 @@ with lib;
|
||||||
|
|
||||||
let
|
let
|
||||||
cfg = config.services.netbox;
|
cfg = config.services.netbox;
|
||||||
|
pythonFmt = pkgs.formats.pythonVars {};
|
||||||
staticDir = cfg.dataDir + "/static";
|
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 = ['*']
|
settingsFile = pythonFmt.generate "netbox-settings.py" cfg.settings;
|
||||||
DATABASE = {
|
extraConfigFile = pkgs.writeTextFile {
|
||||||
'NAME': 'netbox',
|
name = "netbox-extraConfig.py";
|
||||||
'USER': 'netbox',
|
text = cfg.extraConfig;
|
||||||
'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,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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: {
|
configFile = pkgs.concatText "configuration.py" [ settingsFile extraConfigFile ];
|
||||||
|
|
||||||
|
pkg = (cfg.package.overrideAttrs (old: {
|
||||||
installPhase = old.installPhase + ''
|
installPhase = old.installPhase + ''
|
||||||
ln -s ${configFile} $out/opt/netbox/netbox/netbox/configuration.py
|
ln -s ${configFile} $out/opt/netbox/netbox/netbox/configuration.py
|
||||||
'' + optionalString cfg.enableLdap ''
|
'' + optionalString cfg.enableLdap ''
|
||||||
|
@ -70,6 +42,30 @@ in {
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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 {
|
listenAddress = mkOption {
|
||||||
type = types.str;
|
type = types.str;
|
||||||
default = "[::1]";
|
default = "[::1]";
|
||||||
|
@ -78,6 +74,17 @@ in {
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
port = mkOption {
|
port = mkOption {
|
||||||
type = types.port;
|
type = types.port;
|
||||||
default = 8001;
|
default = 8001;
|
||||||
|
@ -117,7 +124,7 @@ in {
|
||||||
default = "";
|
default = "";
|
||||||
description = lib.mdDoc ''
|
description = lib.mdDoc ''
|
||||||
Additional lines of configuration appended to the `configuration.py`.
|
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.
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -138,11 +145,90 @@ in {
|
||||||
Path to the Configuration-File for LDAP-Authentication, will be loaded as `ldap_config.py`.
|
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.
|
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 {
|
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;
|
services.redis.servers.netbox.enable = true;
|
||||||
|
|
||||||
|
|
|
@ -460,7 +460,8 @@ in {
|
||||||
netdata = handleTest ./netdata.nix {};
|
netdata = handleTest ./netdata.nix {};
|
||||||
networking.networkd = handleTest ./networking.nix { networkd = true; };
|
networking.networkd = handleTest ./networking.nix { networkd = true; };
|
||||||
networking.scripted = handleTest ./networking.nix { networkd = false; };
|
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
|
# TODO: put in networking.nix after the test becomes more complete
|
||||||
networkingProxy = handleTest ./networking-proxy.nix {};
|
networkingProxy = handleTest ./networking-proxy.nix {};
|
||||||
nextcloud = handleTest ./nextcloud {};
|
nextcloud = handleTest ./nextcloud {};
|
||||||
|
|
|
@ -1,21 +1,146 @@
|
||||||
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";
|
name = "netbox";
|
||||||
|
|
||||||
meta = with lib.maintainers; {
|
meta = with lib.maintainers; {
|
||||||
maintainers = [ n0emis ];
|
maintainers = [ minijackson n0emis ];
|
||||||
};
|
};
|
||||||
|
|
||||||
nodes.machine = { ... }: {
|
nodes.machine = { config, ... }: {
|
||||||
services.netbox = {
|
services.netbox = {
|
||||||
enable = true;
|
enable = true;
|
||||||
|
package = netbox;
|
||||||
secretKeyFile = pkgs.writeText "secret" ''
|
secretKeyFile = pkgs.writeText "secret" ''
|
||||||
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789
|
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 = ''
|
testScript = let
|
||||||
machine.start()
|
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_for_unit("netbox.target")
|
||||||
machine.wait_until_succeeds("journalctl --since -1m --unit netbox --grep Listening")
|
machine.wait_until_succeeds("journalctl --since -1m --unit netbox --grep Listening")
|
||||||
|
|
||||||
|
@ -26,5 +151,167 @@ import ../make-test-python.nix ({ lib, pkgs, ... }: {
|
||||||
|
|
||||||
with subtest("Staticfiles are generated"):
|
with subtest("Staticfiles are generated"):
|
||||||
machine.succeed("test -e /var/lib/netbox/static/netbox.js")
|
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"])
|
||||||
'';
|
'';
|
||||||
})
|
})
|
||||||
|
|
|
@ -417,4 +417,39 @@ rec {
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# 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
|
||||||
|
'') {};
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,30 +1,30 @@
|
||||||
diff --git a/netbox/netbox/settings.py b/netbox/netbox/settings.py
|
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
|
--- a/netbox/netbox/settings.py
|
||||||
+++ b/netbox/netbox/settings.py
|
+++ b/netbox/netbox/settings.py
|
||||||
@@ -222,6 +222,7 @@ TASKS_REDIS_PASSWORD = TASKS_REDIS.get('PASSWORD', '')
|
@@ -236,6 +236,7 @@ TASKS_REDIS_DATABASE = TASKS_REDIS.get('DATABASE', 0)
|
||||||
TASKS_REDIS_DATABASE = TASKS_REDIS.get('DATABASE', 0)
|
|
||||||
TASKS_REDIS_SSL = TASKS_REDIS.get('SSL', False)
|
TASKS_REDIS_SSL = TASKS_REDIS.get('SSL', False)
|
||||||
TASKS_REDIS_SKIP_TLS_VERIFY = TASKS_REDIS.get('INSECURE_SKIP_TLS_VERIFY', 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')
|
+TASKS_REDIS_URL = TASKS_REDIS.get('URL')
|
||||||
|
|
||||||
# Caching
|
# Caching
|
||||||
if 'caching' not in REDIS:
|
if 'caching' not in REDIS:
|
||||||
@@ -236,11 +237,12 @@ CACHING_REDIS_SENTINELS = REDIS['caching'].get('SENTINELS', [])
|
@@ -253,11 +254,12 @@ CACHING_REDIS_SENTINEL_SERVICE = REDIS['caching'].get('SENTINEL_SERVICE', 'defau
|
||||||
CACHING_REDIS_SENTINEL_SERVICE = REDIS['caching'].get('SENTINEL_SERVICE', 'default')
|
|
||||||
CACHING_REDIS_PROTO = 'rediss' if REDIS['caching'].get('SSL', False) else 'redis'
|
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_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}')
|
+CACHING_REDIS_URL = REDIS['caching'].get('URL', f'{CACHING_REDIS_PROTO}://{CACHING_REDIS_HOST}:{CACHING_REDIS_PORT}/{CACHING_REDIS_DATABASE}')
|
||||||
|
|
||||||
CACHES = {
|
CACHES = {
|
||||||
'default': {
|
'default': {
|
||||||
'BACKEND': 'django_redis.cache.RedisCache',
|
'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,
|
+ 'LOCATION': CACHING_REDIS_URL,
|
||||||
'OPTIONS': {
|
'OPTIONS': {
|
||||||
'CLIENT_CLASS': 'django_redis.client.DefaultClient',
|
'CLIENT_CLASS': 'django_redis.client.DefaultClient',
|
||||||
'PASSWORD': CACHING_REDIS_PASSWORD,
|
'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'
|
X_FRAME_OPTIONS = 'SAMEORIGIN'
|
||||||
|
|
||||||
# Static files (CSS, JavaScript, Images)
|
# Static files (CSS, JavaScript, Images)
|
||||||
|
@ -33,7 +33,7 @@ index d5a7bfaec..68754a8c5 100644
|
||||||
STATIC_URL = f'/{BASE_PATH}static/'
|
STATIC_URL = f'/{BASE_PATH}static/'
|
||||||
STATICFILES_DIRS = (
|
STATICFILES_DIRS = (
|
||||||
os.path.join(BASE_DIR, 'project-static', 'dist'),
|
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
|
'socket_connect_timeout': TASKS_REDIS_SENTINEL_TIMEOUT
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
50
pkgs/servers/web-apps/netbox/config_3_3.patch
Normal file
50
pkgs/servers/web-apps/netbox/config_3_3.patch
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
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,
|
|
@ -1,37 +1,14 @@
|
||||||
{ lib
|
{ lib, nixosTests, callPackage, fetchpatch }:
|
||||||
, pkgs
|
|
||||||
, fetchFromGitHub
|
|
||||||
, fetchpatch
|
|
||||||
, nixosTests
|
|
||||||
, python3
|
|
||||||
|
|
||||||
, plugins ? ps: [] }:
|
|
||||||
|
|
||||||
let
|
let
|
||||||
py = python3 // {
|
generic = import ./generic.nix;
|
||||||
pkgs = python3.pkgs.overrideScope (self: super: {
|
|
||||||
django = super.django_4;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
extraBuildInputs = plugins py.pkgs;
|
|
||||||
in
|
in
|
||||||
py.pkgs.buildPythonApplication rec {
|
{
|
||||||
pname = "netbox";
|
netbox_3_3 = callPackage generic {
|
||||||
version = "3.3.9";
|
version = "3.3.10";
|
||||||
|
hash = "sha256-MeOfTU5IxNDoUh7FyvwAQNRC/CE0R6p40WnlF+3RuxA=";
|
||||||
format = "other";
|
extraPatches = [
|
||||||
|
|
||||||
src = fetchFromGitHub {
|
|
||||||
owner = "netbox-community";
|
|
||||||
repo = pname;
|
|
||||||
rev = "refs/tags/v${version}";
|
|
||||||
sha256 = "sha256-KhnxD5pjlEIgISl4RMbhLCDwgUDfGFRi88ZcP1ndMhI=";
|
|
||||||
};
|
|
||||||
|
|
||||||
patches = [
|
|
||||||
# Allow setting the STATIC_ROOT from within the configuration and setting a custom redis URL
|
# 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
|
./graphql-3_2_0.patch
|
||||||
# fix compatibility ith django 4.1
|
# fix compatibility ith django 4.1
|
||||||
(fetchpatch {
|
(fetchpatch {
|
||||||
|
@ -40,77 +17,22 @@ py.pkgs.buildPythonApplication rec {
|
||||||
})
|
})
|
||||||
];
|
];
|
||||||
|
|
||||||
propagatedBuildInputs = with py.pkgs; [
|
tests.netbox = nixosTests.netbox_3_3;
|
||||||
bleach
|
maintainers = with lib.maintainers; [ n0emis raitobezarius ];
|
||||||
django_4
|
eol = true;
|
||||||
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; [
|
netbox = callPackage generic {
|
||||||
mkdocs-material
|
version = "3.4.7";
|
||||||
mkdocs-material-extensions
|
hash = "sha256-pWHGyzLc0tqfehWbCMF1l96L1pewb5FXBUkw9EqPtP8=";
|
||||||
mkdocstrings
|
extraPatches = [
|
||||||
mkdocstrings-python
|
# Allow setting the STATIC_ROOT from within the configuration and setting a custom redis URL
|
||||||
|
./config.patch
|
||||||
];
|
];
|
||||||
|
tests = {
|
||||||
nativeBuildInputs = [
|
inherit (nixosTests) netbox;
|
||||||
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;
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
meta = with lib; {
|
maintainers = with lib.maintainers; [ minijackson n0emis raitobezarius ];
|
||||||
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 ];
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
110
pkgs/servers/web-apps/netbox/generic.nix
Normal file
110
pkgs/servers/web-apps/netbox/generic.nix
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
{ 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;
|
||||||
|
};
|
||||||
|
}
|
|
@ -10170,7 +10170,8 @@ with pkgs;
|
||||||
|
|
||||||
netbootxyz-efi = callPackage ../tools/misc/netbootxyz-efi { };
|
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;
|
netcat = libressl.nc;
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue