borgend/config.py

changeset 80
a409242121d5
parent 79
b075b3db3044
child 86
2fe66644c50d
equal deleted inserted replaced
79:b075b3db3044 80:a409242121d5
1 #!/usr/local/bin/python3
2 #
3 # Borgend configuration loader
4 #
5
6 import yaml
7 import io
8 import os
9 import string
10 import logging
11 import platform
12 from functools import reduce
13
14 from . import loggers
15 from . import branding
16 from . import locations
17
18 logger=loggers.get(__name__)
19
20 #
21 # Defaults
22 #
23
24 defaults={
25 # borg
26 # Default: backup every 6 hours (21600 seconds)
27 'backup_interval': 21600,
28 # Default: retry every 15 minutes if unable to connect / unfinished backup
29 'retry_interval': 900,
30 # Extract passphrases at startup or on demand?
31 'extract_passphrases_at_startup': True,
32 # Do not insert a quit menu entry (useful for installing on computers of
33 # inexperienced users)
34 'no_quit_menu_entry': False,
35 # Borg settings
36 'borg': {
37 'executable': 'borg',
38 'common_parameters': [],
39 'create_parameters': [],
40 'prune_parameters': [],
41 }
42 }
43
44 #
45 # Type checking etc.
46 #
47
48 def error(x):
49 raise AssertionError(x)
50
51 def check_field(cfg, field, descr, loc, default, check):
52 if field in cfg:
53 tmp=cfg[field]
54 if not check(tmp):
55 error("%s is of invalid type for %s" % (field, loc))
56 return tmp
57 else:
58 if default is not None:
59 return default
60 else:
61 error("%s is not configured for %s" % (field, loc))
62
63 def check_bool(cfg, field, descr, loc, default=None):
64 return check_field(cfg, field, descr, loc, default,
65 lambda x: isinstance(x, bool))
66
67 def check_string(cfg, field, descr, loc, default=None):
68 return check_field(cfg, field, descr, loc, default,
69 lambda x: isinstance(x, str))
70
71 def check_dict(cfg, field, descr, loc, default=None):
72 return check_field(cfg, field, descr, loc, default,
73 lambda x: isinstance(x, dict))
74
75 def check_list(cfg, field, descr, loc, default=None):
76 return check_field(cfg, field, descr, loc, default,
77 lambda x: isinstance(x, list))
78
79 def is_list_of(x, chk):
80 if x is None:
81 return True
82 elif isinstance(x, list):
83 return reduce(lambda y, z: y and chk(z), x, True)
84 else:
85 return False
86
87 def check_list_of_dicts(cfg, field, descr, loc, default=None):
88 return check_field(cfg, field, descr, loc, default,
89 lambda x: is_list_of(x, lambda z: isinstance(z, dict)))
90
91 def check_list_of_strings(cfg, field, descr, loc, default=None):
92 return check_field(cfg, field, descr, loc, default,
93 lambda x: is_list_of(x, lambda z: isinstance(z, str)))
94
95 def check_nonempty_list_of_strings(cfg, field, descr, loc):
96 return check_list_of_strings(cfg, field, descr, loc) and cfg[field]
97
98
99 def check_nonneg_int(cfg, field, descr, loc, default=None):
100 return check_field(cfg, field, descr, loc, default,
101 lambda x: isinstance(x, int) and x>=0)
102
103
104 #
105 # Borg command line parameter configuration helper routines and classes
106 #
107
108 class BorgParameters:
109 def __init__(self, common, create, prune):
110 self.common=common or []
111 self.create=create or []
112 self.prune=prune or []
113
114 def from_config(cfg, loc):
115 common=check_list_of_dicts(cfg, 'common_parameters',
116 'Borg parameters', loc, default=[])
117
118 create=check_list_of_dicts(cfg, 'create_parameters',
119 'Create parameters', loc, default=[])
120
121 prune=check_list_of_dicts(cfg, 'prune_parameters',
122 'Prune parameters', loc, default=[])
123
124 return BorgParameters(common, create, prune)
125
126 def __add__(self, other):
127 common=self.common+other.common
128 create=self.create+other.create
129 prune=self.prune+other.prune
130 return BorgParameters(common, create, prune)
131
132 #
133 # Load config on module load
134 #
135
136 def expand_env(cfg, env):
137 if isinstance(cfg, dict):
138 out={key: expand_env(val, env) for key, val in cfg.items()}
139 elif isinstance(cfg, list):
140 out=[expand_env(val, env) for val in cfg]
141 elif isinstance(cfg, str):
142 out=string.Template(cfg).substitute(os.environ)
143 else:
144 out=cfg
145
146 return out
147
148 if not (os.path.exists(locations.cfgfile) and os.path.isfile(locations.cfgfile)):
149 raise SystemExit("Configuration file required: %s" % locations.cfgfile)
150
151 logger.info("Reading configuration %s missing" % locations.cfgfile)
152
153 with io.open(locations.cfgfile, 'r') as file:
154 settings=expand_env(yaml.load(file), os.environ);
155
156 #
157 # Verify basic settings
158 #
159
160 def check_and_set(cfg, field, loc, defa, fn):
161 cfg[field]=fn(cfg, field, field, loc, defa[field])
162
163 check_and_set(settings, 'backup_interval', 'top-level', defaults, check_nonneg_int)
164 check_and_set(settings, 'retry_interval', 'top-level', defaults, check_nonneg_int)
165 check_and_set(settings, 'extract_passphrases_at_startup', 'top-level', defaults, check_nonneg_int)
166 check_and_set(settings, 'no_quit_menu_entry', 'top-level', defaults, check_bool)
167 check_and_set(settings, 'borg', 'top-level', defaults, check_dict)
168 # Check parameters within 'borg'
169 if True:
170 check_and_set(settings['borg'], 'executable', 'borg',
171 defaults['borg'], check_string)
172
173 borg_parameters=BorgParameters.from_config(settings['borg'], "top-level")
174

mercurial