Coverage for netbox/scaffold.py: 0%
34 statements
« prev ^ index » next coverage.py v7.15.2, created at 2026-10-10 18:35 +0000
« prev ^ index » next coverage.py v7.15.2, created at 2026-10-10 18:35 +0000
1"""`netbox setup`: create the local configuration files for a pip-installed NetBox instance.
3Runs before Django or a local configuration exists (dispatched from the `netbox` console
4script, netbox.cli). Scaffolds conf/__init__.py and conf/configuration.py (copied verbatim
5from the bundled configuration_example.py template) and an empty local_requirements.txt,
6then copies the bundled deployment examples (gunicorn, systemd units, nginx, apache, uwsgi,
7netbox.env) unmodified into <target>/contrib/. Nothing is generated or rewritten; adapting
8and installing the examples (paths, systemd, the web server) remains the administrator's
9job. Existing files are never overwritten.
10"""
12import argparse
13import sys
14from importlib.resources import files
15from pathlib import Path
18def _bundled_data_dir():
19 return files('netbox') / '_data'
22def _config_template():
23 return files('netbox') / 'configuration_example.py'
26def _contrib_dir():
27 return files('netbox') / '_data' / 'contrib'
30def _write(destination, data):
31 if destination.exists():
32 print(f'Skipping existing {destination}')
33 return False
34 destination.parent.mkdir(parents=True, exist_ok=True)
35 destination.write_bytes(data)
36 print(f'Wrote {destination}')
37 return True
40def scaffold_instance(target):
41 target = Path(target)
42 conf = target / 'conf'
43 items = [
44 (conf / '__init__.py', b''),
45 (conf / 'configuration.py', _config_template().read_bytes()),
46 # An empty local_requirements.txt makes the optional plugin step discoverable and
47 # the documented "pip install -r local_requirements.txt" safe.
48 (target / 'local_requirements.txt', b''),
49 # Bundled deployment examples, copied byte-verbatim; adapting them is the admin's job.
50 # is_file() skips __pycache__: pip's post-install bytecode compilation of gunicorn.py
51 # (the only .py file among the examples) leaves one alongside the real files.
52 *(
53 (target / 'contrib' / example.name, example.read_bytes())
54 for example in sorted(_contrib_dir().iterdir(), key=lambda entry: entry.name)
55 if example.is_file()
56 ),
57 ]
58 return [str(destination) for destination, data in items if _write(destination, data)]
61def main(argv=None, *, prog='netbox setup'):
62 parser = argparse.ArgumentParser(
63 prog=prog,
64 description=(
65 'Create the local configuration files for a pip-installed NetBox instance '
66 '(conf/configuration.py copied verbatim from the bundled template, plus an empty '
67 'local_requirements.txt), and copy the bundled deployment examples (gunicorn, '
68 'systemd units, nginx, apache, uwsgi, netbox.env) unmodified into '
69 "<target>/contrib/. Nothing is generated or rewritten; adapting and installing "
70 "the examples is the administrator's job. Existing files are never overwritten."
71 ),
72 )
73 parser.add_argument('--target', default='/opt/netbox', help='NetBox instance root (NETBOX_ROOT).')
74 args = parser.parse_args(argv)
76 if not _bundled_data_dir().is_dir():
77 print(
78 f'{prog}: this NetBox installation does not include the bundled package data. '
79 'This command is available only from the installed netbox package (pip/wheel); '
80 'for an archive or Git installation, follow the standard installation guide instead.',
81 file=sys.stderr,
82 )
83 return 1
84 if not Path(args.target).is_absolute():
85 parser.error(f"--target must be an absolute path (got '{args.target}')")
87 scaffold_instance(args.target)
88 return 0