Coverage for netbox/scaffold.py: 0%

34 statements  

« 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. 

2 

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""" 

11 

12import argparse 

13import sys 

14from importlib.resources import files 

15from pathlib import Path 

16 

17 

18def _bundled_data_dir(): 

19 return files('netbox') / '_data' 

20 

21 

22def _config_template(): 

23 return files('netbox') / 'configuration_example.py' 

24 

25 

26def _contrib_dir(): 

27 return files('netbox') / '_data' / 'contrib' 

28 

29 

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 

38 

39 

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)] 

59 

60 

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) 

75 

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}')") 

86 

87 scaffold_instance(args.target) 

88 return 0