forked from stove/risotto
split code
This commit is contained in:
parent
23adccacb3
commit
382a5322a6
5 changed files with 834 additions and 614 deletions
585
bootstrap.py
585
bootstrap.py
|
@ -1,541 +1,43 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
from asyncio import run
|
from asyncio import run
|
||||||
from os import listdir, link, makedirs, environ
|
from os import listdir, link, makedirs
|
||||||
from os.path import isdir, isfile, join
|
from os.path import isdir, join
|
||||||
from shutil import rmtree, copy2, copytree
|
from shutil import rmtree
|
||||||
from json import load as json_load
|
|
||||||
from yaml import load, SafeLoader
|
|
||||||
from toml import load as toml_load
|
|
||||||
from pprint import pprint
|
|
||||||
from typing import Any
|
|
||||||
from warnings import warn_explicit
|
|
||||||
from copy import copy
|
from copy import copy
|
||||||
|
|
||||||
from tiramisu import Config
|
from rougail import RougailSystemdTemplate
|
||||||
from tiramisu.error import ValueWarning
|
|
||||||
from rougail import RougailConfig, RougailConvert, RougailSystemdTemplate
|
|
||||||
from rougail.utils import normalize_family
|
|
||||||
|
|
||||||
from risotto.utils import MULTI_FUNCTIONS, CONFIGS, DOMAINS
|
from risotto.utils import CONFIGS, RISOTTO_CONFIG, SERVERS, value_pprint
|
||||||
|
from risotto.image import load
|
||||||
|
|
||||||
|
|
||||||
with open(environ.get('CONFIG_FILE', 'risotto.conf'), 'r') as fh:
|
INSTALL_DIR = RISOTTO_CONFIG['directories']['dest']
|
||||||
config = toml_load(fh)
|
|
||||||
|
|
||||||
|
|
||||||
DATASET_DIRECTORY = config['directories']['dataset']
|
|
||||||
INSTALL_DIR = config['directories']['dest']
|
|
||||||
FUNCTIONS = 'funcs.py'
|
|
||||||
CONFIG_DEST_DIR = 'configurations'
|
CONFIG_DEST_DIR = 'configurations'
|
||||||
|
CONFIG_DIFF_DIR = 'diff'
|
||||||
SRV_DEST_DIR = 'srv'
|
SRV_DEST_DIR = 'srv'
|
||||||
|
|
||||||
|
|
||||||
with open('servers.json', 'r') as server_fh:
|
|
||||||
jsonfile = json_load(server_fh)
|
|
||||||
SERVERS = jsonfile['servers']
|
|
||||||
MODULES = jsonfile['modules']
|
|
||||||
|
|
||||||
|
|
||||||
async def set_linked_multi_variables(value: str,
|
|
||||||
linked_server: str=None,
|
|
||||||
variable_index: int=None,
|
|
||||||
**kwargs: dict,
|
|
||||||
) -> None:
|
|
||||||
if value is not None and linked_server is not None and 'linked_value_0' not in kwargs:
|
|
||||||
kwargs['linked_value_0'] = value
|
|
||||||
elif not linked_server:
|
|
||||||
linked_server = value
|
|
||||||
if not linked_server:
|
|
||||||
return
|
|
||||||
if linked_server not in CONFIGS:
|
|
||||||
warn_explicit(ValueWarning(f'cannot find linked server "{linked_server}"'),
|
|
||||||
ValueWarning,
|
|
||||||
__file__,
|
|
||||||
3,
|
|
||||||
)
|
|
||||||
return
|
|
||||||
config = CONFIGS[linked_server][0]
|
|
||||||
dico = {}
|
|
||||||
variables = {}
|
|
||||||
for key, value in kwargs.items():
|
|
||||||
if value is None:
|
|
||||||
return
|
|
||||||
if key.startswith('linked_provider_'):
|
|
||||||
index = int(key[16])
|
|
||||||
path = await config.information.get('provider:' + value, None)
|
|
||||||
if not path:
|
|
||||||
return
|
|
||||||
if index not in variables:
|
|
||||||
variables[index] = {'path': None, 'value': None, 'variable_index': False}
|
|
||||||
variables[index]['path'] = path
|
|
||||||
elif key.startswith('linked_value_'):
|
|
||||||
index = int(key[13])
|
|
||||||
if index not in variables:
|
|
||||||
variables[index] = {'path': None, 'value': None, 'variable_index': False}
|
|
||||||
variables[index]['value'] = value
|
|
||||||
elif key.startswith('variable_index_'):
|
|
||||||
index = int(key[15])
|
|
||||||
if index not in variables:
|
|
||||||
variables[index] = {'path': None, 'value': None, 'variable_index': False}
|
|
||||||
variables[index]['variable_index'] = True
|
|
||||||
else:
|
|
||||||
raise AttributeError(f'unknown parameter {key}')
|
|
||||||
await config.property.read_write()
|
|
||||||
# print('=====================================')
|
|
||||||
# pprint(variables)
|
|
||||||
if not isinstance(variables[0]['value'], list):
|
|
||||||
variables[0]['value'] = [variables[0]['value']]
|
|
||||||
dynamic = None
|
|
||||||
try:
|
|
||||||
if variables[0]['value']:
|
|
||||||
for first_idx, first_value in enumerate(variables[0]['value']):
|
|
||||||
slave_idxes = []
|
|
||||||
dynamic = normalize_family(first_value)
|
|
||||||
for index in sorted(list(variables)):
|
|
||||||
path = variables[index]['path']
|
|
||||||
if '{suffix}' in path:
|
|
||||||
path = path.replace('{suffix}', dynamic)
|
|
||||||
elif first_idx != 0:
|
|
||||||
continue
|
|
||||||
value = variables[index]['value']
|
|
||||||
option = config.forcepermissive.option(path)
|
|
||||||
if not await option.option.isfollower():
|
|
||||||
#print('===>', path, value, await option.option.ismulti(), await option.option.issubmulti())
|
|
||||||
multi = await option.option.ismulti()
|
|
||||||
if multi:
|
|
||||||
isleader = await option.option.isleader()
|
|
||||||
if not isinstance(value, list):
|
|
||||||
value = [value]
|
|
||||||
# elif isleader:
|
|
||||||
# raise Exception('leader must not be a multi from now ...')
|
|
||||||
values = await option.value.get()
|
|
||||||
for val in value:
|
|
||||||
if val not in values:
|
|
||||||
if isleader:
|
|
||||||
slave_idxes.append(len(values))
|
|
||||||
values.append(val)
|
|
||||||
elif isleader:
|
|
||||||
slave_idxes.append(values.index(val))
|
|
||||||
await option.value.set(values)
|
|
||||||
else:
|
|
||||||
await option.value.set(value)
|
|
||||||
else:
|
|
||||||
#print('===<', path, value, await option.option.ismulti(), await option.option.issubmulti())
|
|
||||||
if not slave_idxes:
|
|
||||||
raise Exception('please declare the leader variable before the follower')
|
|
||||||
if variables[index]['variable_index']:
|
|
||||||
value = value[variable_index]
|
|
||||||
if not isinstance(value, list):
|
|
||||||
value = [value] * len(slave_idxes)
|
|
||||||
# if isinstance(value, list) and not await option.option.issubmulti():
|
|
||||||
for idx, val in enumerate(value):
|
|
||||||
option = config.forcepermissive.option(path, slave_idxes[idx])
|
|
||||||
await option.value.set(val)
|
|
||||||
except Exception as err:
|
|
||||||
await config.property.read_only()
|
|
||||||
raise err from err
|
|
||||||
await config.property.read_only()
|
|
||||||
return get_ip_from_domain(linked_server)
|
|
||||||
|
|
||||||
|
|
||||||
async def set_linked(linked_server: str,
|
|
||||||
linked_provider: str,
|
|
||||||
linked_value: str,
|
|
||||||
linked_returns: str=None,
|
|
||||||
dynamic: str=None,
|
|
||||||
):
|
|
||||||
if None in (linked_server, linked_provider, linked_value):
|
|
||||||
return
|
|
||||||
if linked_server not in CONFIGS:
|
|
||||||
warn_explicit(ValueWarning(f'cannot find linked server "{linked_server}"'),
|
|
||||||
ValueWarning,
|
|
||||||
__file__,
|
|
||||||
0,
|
|
||||||
)
|
|
||||||
return
|
|
||||||
config = CONFIGS[linked_server][0]
|
|
||||||
path = await config.information.get('provider:' + linked_provider, None)
|
|
||||||
if not path:
|
|
||||||
warn_explicit(ValueWarning(f'cannot find provider "{linked_provider}" in linked server "{linked_server}"'),
|
|
||||||
ValueWarning,
|
|
||||||
__file__,
|
|
||||||
0,
|
|
||||||
)
|
|
||||||
return
|
|
||||||
await config.property.read_write()
|
|
||||||
try:
|
|
||||||
option = config.forcepermissive.option(path)
|
|
||||||
if await option.option.ismulti():
|
|
||||||
values = await option.value.get()
|
|
||||||
if linked_value not in values:
|
|
||||||
values.append(linked_value)
|
|
||||||
await option.value.set(values)
|
|
||||||
else:
|
|
||||||
await option.value.set(linked_value)
|
|
||||||
except Exception as err:
|
|
||||||
await config.property.read_only()
|
|
||||||
raise err from err
|
|
||||||
await config.property.read_only()
|
|
||||||
if linked_returns is not None:
|
|
||||||
linked_variable = await config.information.get('provider:' + linked_returns, None)
|
|
||||||
if not linked_variable:
|
|
||||||
warn_explicit(ValueWarning(f'cannot find linked variable "{linked_returns}" in linked server "{linked_server}"'),
|
|
||||||
ValueWarning,
|
|
||||||
__file__,
|
|
||||||
0,
|
|
||||||
)
|
|
||||||
return
|
|
||||||
else:
|
|
||||||
linked_variable = None
|
|
||||||
if linked_variable is not None:
|
|
||||||
if dynamic:
|
|
||||||
linked_variable = linked_variable.replace('{suffix}', normalize_family(dynamic))
|
|
||||||
elif '{suffix}' in linked_variable:
|
|
||||||
idx = CONFIGS[linked_server][3]
|
|
||||||
linked_variable = linked_variable.replace('{suffix}', str(idx))
|
|
||||||
ret = await config.forcepermissive.option(linked_variable).value.get()
|
|
||||||
else:
|
|
||||||
ret = normalize_family(linked_value)
|
|
||||||
return ret
|
|
||||||
|
|
||||||
|
|
||||||
async def get_linked_configuration(linked_server: str,
|
|
||||||
linked_provider: str,
|
|
||||||
dynamic: str=None,
|
|
||||||
):
|
|
||||||
if linked_server not in CONFIGS:
|
|
||||||
warn_explicit(ValueWarning(f'cannot find linked server "{linked_server}"'),
|
|
||||||
ValueWarning,
|
|
||||||
__file__,
|
|
||||||
1,
|
|
||||||
)
|
|
||||||
return
|
|
||||||
config = CONFIGS[linked_server][0]
|
|
||||||
path = await config.information.get('provider:' + linked_provider, None)
|
|
||||||
if not path:
|
|
||||||
warn_explicit(ValueWarning(f'cannot find variable "{path}" in linked server "{linked_server}"'),
|
|
||||||
ValueWarning,
|
|
||||||
__file__,
|
|
||||||
1,
|
|
||||||
)
|
|
||||||
return
|
|
||||||
if dynamic:
|
|
||||||
path = path.replace('{suffix}', normalize_family(dynamic))
|
|
||||||
try:
|
|
||||||
return await config.forcepermissive.option(path).value.get()
|
|
||||||
except AttributeError as err:
|
|
||||||
warn_explicit(ValueWarning(f'cannot find get value of "{path}" in linked server "{linked_server}": {err}'),
|
|
||||||
ValueWarning,
|
|
||||||
__file__,
|
|
||||||
1,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class Empty:
|
|
||||||
pass
|
|
||||||
empty = Empty()
|
|
||||||
|
|
||||||
|
|
||||||
async def set_linked_configuration(_linked_value: Any,
|
|
||||||
linked_server: str,
|
|
||||||
linked_provider: str,
|
|
||||||
linked_value: Any=empty,
|
|
||||||
dynamic: str=None,
|
|
||||||
leader_provider: str=None,
|
|
||||||
leader_value: Any=None,
|
|
||||||
leader_index: int=None,
|
|
||||||
):
|
|
||||||
if linked_value is not empty:
|
|
||||||
_linked_value = linked_value
|
|
||||||
linked_value = _linked_value
|
|
||||||
if linked_server is None:
|
|
||||||
return
|
|
||||||
if linked_value is None or linked_server not in CONFIGS:
|
|
||||||
warn_explicit(ValueWarning(f'cannot find linked server "{linked_server}"'),
|
|
||||||
ValueWarning,
|
|
||||||
__file__,
|
|
||||||
2,
|
|
||||||
)
|
|
||||||
return
|
|
||||||
config = CONFIGS[linked_server][0]
|
|
||||||
path = await config.information.get('provider:' + linked_provider, None)
|
|
||||||
if not path:
|
|
||||||
warn_explicit(ValueWarning(f'cannot find variable "{path}" in linked server "{linked_server}"'),
|
|
||||||
ValueWarning,
|
|
||||||
__file__,
|
|
||||||
2,
|
|
||||||
)
|
|
||||||
return
|
|
||||||
if dynamic:
|
|
||||||
path = path.replace('{suffix}', normalize_family(dynamic))
|
|
||||||
await config.property.read_write()
|
|
||||||
try:
|
|
||||||
if leader_provider is not None:
|
|
||||||
leader_path = await config.information.get('provider:' + leader_provider, None)
|
|
||||||
if not leader_path:
|
|
||||||
await config.property.read_only()
|
|
||||||
warn_explicit(ValueWarning(f'cannot find leader variable with leader_provider "{leader_provider}" in linked server "{linked_server}"'),
|
|
||||||
ValueWarning,
|
|
||||||
__file__,
|
|
||||||
2,
|
|
||||||
)
|
|
||||||
return
|
|
||||||
if dynamic:
|
|
||||||
leader_path = leader_path.replace('{suffix}', normalize_family(dynamic))
|
|
||||||
values = await config.forcepermissive.option(leader_path).value.get()
|
|
||||||
if not isinstance(leader_value, list):
|
|
||||||
leader_value = [leader_value]
|
|
||||||
for lv in leader_value:
|
|
||||||
if lv in values:
|
|
||||||
slave_idx = values.index(lv)
|
|
||||||
slave_option = config.forcepermissive.option(path, slave_idx)
|
|
||||||
if await slave_option.option.issubmulti():
|
|
||||||
slave_values = await slave_option.value.get()
|
|
||||||
if linked_value not in slave_values:
|
|
||||||
slave_values.append(linked_value)
|
|
||||||
await slave_option.value.set(slave_values)
|
|
||||||
else:
|
|
||||||
await slave_option.value.set(linked_value)
|
|
||||||
else:
|
|
||||||
option = config.forcepermissive.option(path, leader_index)
|
|
||||||
if leader_index is None and await option.option.ismulti() and not isinstance(linked_value, list):
|
|
||||||
values = await option.value.get()
|
|
||||||
if linked_value not in values:
|
|
||||||
values.append(linked_value)
|
|
||||||
await option.value.set(values)
|
|
||||||
else:
|
|
||||||
await option.value.set(linked_value)
|
|
||||||
except AttributeError as err:
|
|
||||||
#raise ValueError(str(err)) from err
|
|
||||||
pass
|
|
||||||
except Exception as err:
|
|
||||||
await config.property.read_only()
|
|
||||||
raise err from err
|
|
||||||
await config.property.read_only()
|
|
||||||
|
|
||||||
|
|
||||||
def tiramisu_display_name(kls,
|
def tiramisu_display_name(kls,
|
||||||
dyn_name: 'Base'=None,
|
dyn_name: 'Base'=None,
|
||||||
suffix: str=None,
|
suffix: str=None,
|
||||||
) -> str:
|
) -> str:
|
||||||
|
# FIXME
|
||||||
if dyn_name is not None:
|
if dyn_name is not None:
|
||||||
name = kls.impl_getpath() + suffix
|
name = kls.impl_getpath() + str(suffix)
|
||||||
else:
|
else:
|
||||||
name = kls.impl_getpath()
|
name = kls.impl_getpath()
|
||||||
return name
|
return name
|
||||||
|
|
||||||
|
|
||||||
def load_applications():
|
async def templates(server_name,
|
||||||
applications = {}
|
config,
|
||||||
distrib_dir = join(DATASET_DIRECTORY, 'applicationservice')
|
templates_informations,
|
||||||
for release in listdir(distrib_dir):
|
srv=False,
|
||||||
release_dir = join(distrib_dir, release)
|
**kwargs,
|
||||||
if not isdir(release_dir):
|
):
|
||||||
continue
|
|
||||||
for applicationservice in listdir(release_dir):
|
|
||||||
applicationservice_dir = join(release_dir, applicationservice)
|
|
||||||
if not isdir(applicationservice_dir):
|
|
||||||
continue
|
|
||||||
if applicationservice in applications:
|
|
||||||
raise Exception(f'multi applicationservice: {applicationservice} ({applicationservice_dir} <=> {applications[applicationservice]})')
|
|
||||||
applications[applicationservice] = applicationservice_dir
|
|
||||||
return applications
|
|
||||||
|
|
||||||
|
|
||||||
class ModuleCfg():
|
|
||||||
def __init__(self):
|
|
||||||
self.dictionaries_dir = []
|
|
||||||
self.modules = []
|
|
||||||
self.functions_file = [FUNCTIONS]
|
|
||||||
self.templates_dir = []
|
|
||||||
self.extra_dictionaries = {}
|
|
||||||
self.servers = []
|
|
||||||
|
|
||||||
|
|
||||||
def build_module(module_name, datas, module_infos):
|
|
||||||
install_dir = join(INSTALL_DIR, module_name)
|
|
||||||
makedirs(install_dir)
|
|
||||||
applications = load_applications()
|
|
||||||
cfg = ModuleCfg()
|
|
||||||
module_infos[module_name] = cfg
|
|
||||||
def calc_depends(appname, added):
|
|
||||||
if appname in added:
|
|
||||||
return
|
|
||||||
if appname not in applications:
|
|
||||||
raise Exception(f'cannot find application dependency "{appname}" in application "{module_name}"')
|
|
||||||
as_dir = applications[appname]
|
|
||||||
cfg.modules.append(appname)
|
|
||||||
dictionaries_dir = join(as_dir, 'dictionaries')
|
|
||||||
if isdir(dictionaries_dir):
|
|
||||||
cfg.dictionaries_dir.append(dictionaries_dir)
|
|
||||||
funcs_dir = join(as_dir, 'funcs')
|
|
||||||
if isdir(funcs_dir):
|
|
||||||
for f in listdir(funcs_dir):
|
|
||||||
if f.startswith('__'):
|
|
||||||
continue
|
|
||||||
cfg.functions_file.append(join(funcs_dir, f))
|
|
||||||
templates_dir = join(as_dir, 'templates')
|
|
||||||
if isdir(templates_dir):
|
|
||||||
cfg.templates_dir.append(templates_dir)
|
|
||||||
extras_dir = join(as_dir, 'extras')
|
|
||||||
if isdir(extras_dir):
|
|
||||||
for extra in listdir(extras_dir):
|
|
||||||
extra_dir = join(extras_dir, extra)
|
|
||||||
if isdir(extra_dir):
|
|
||||||
cfg.extra_dictionaries.setdefault(extra, []).append(extra_dir)
|
|
||||||
for type in ['image', 'install']:
|
|
||||||
manual_dir = join(as_dir, 'manual', type)
|
|
||||||
if isdir(manual_dir):
|
|
||||||
for filename in listdir(manual_dir):
|
|
||||||
src_file = join(manual_dir, filename)
|
|
||||||
if type == 'image':
|
|
||||||
dst_file = join(install_dir, 'manual', filename)
|
|
||||||
verify = False
|
|
||||||
else:
|
|
||||||
dst_file= join(INSTALL_DIR, filename)
|
|
||||||
verify = True
|
|
||||||
if isdir(src_file):
|
|
||||||
if not isdir(dst_file):
|
|
||||||
makedirs(dst_file)
|
|
||||||
for subfilename in listdir(src_file):
|
|
||||||
if not verify or not isfile(dst_file):
|
|
||||||
src = join(src_file, subfilename)
|
|
||||||
dst = join(dst_file, subfilename)
|
|
||||||
if isfile(src):
|
|
||||||
copy2(src, dst)
|
|
||||||
else:
|
|
||||||
copytree(src, dst)
|
|
||||||
elif not verify or not isfile(dst_file):
|
|
||||||
src = join(manual_dir, filename)
|
|
||||||
dst = dst_file
|
|
||||||
if isfile(src):
|
|
||||||
copy2(src, dst)
|
|
||||||
else:
|
|
||||||
copytree(src, dst)
|
|
||||||
added.append(appname)
|
|
||||||
with open(join(as_dir, 'applicationservice.yml')) as yaml:
|
|
||||||
app = load(yaml, Loader=SafeLoader)
|
|
||||||
|
|
||||||
for xml in app.get('depends', []):
|
|
||||||
calc_depends(xml, added)
|
|
||||||
added = []
|
|
||||||
for applicationservice in datas['applicationservices']:
|
|
||||||
calc_depends(applicationservice, added)
|
|
||||||
|
|
||||||
|
|
||||||
def get_ip_from_domain(domain):
|
|
||||||
if not domain:
|
|
||||||
return
|
|
||||||
hostname, domainname = domain.split('.', 1)
|
|
||||||
return DOMAINS[domainname][1][DOMAINS[domainname][0].index(hostname)]
|
|
||||||
|
|
||||||
|
|
||||||
async def build(server_name, datas, module_infos):
|
|
||||||
if server_name in CONFIGS:
|
|
||||||
raise Exception(f'server "{server_name}" is duplicate')
|
|
||||||
cfg = RougailConfig.copy()
|
|
||||||
module_info = module_infos[datas['module']]
|
|
||||||
module_info.servers.append(server_name)
|
|
||||||
if datas['module'] == 'host':
|
|
||||||
cfg['tmpfile_dest_dir'] = datas['values']['rougail.host_install_dir'] + '/host/configurations/' + server_name
|
|
||||||
cfg['templates_dir'] = module_info.templates_dir
|
|
||||||
cfg['dictionaries_dir'] = module_info.dictionaries_dir
|
|
||||||
cfg['functions_file'] = module_info.functions_file
|
|
||||||
cfg['multi_functions'] = MULTI_FUNCTIONS
|
|
||||||
cfg['extra_dictionaries'] = module_info.extra_dictionaries
|
|
||||||
cfg['extra_annotators'].append('risotto.rougail')
|
|
||||||
optiondescription = {'set_linked': set_linked,
|
|
||||||
'set_linked_multi_variables': set_linked_multi_variables,
|
|
||||||
'get_linked_configuration': get_linked_configuration,
|
|
||||||
'set_linked_configuration': set_linked_configuration,
|
|
||||||
'get_ip_from_domain': get_ip_from_domain,
|
|
||||||
}
|
|
||||||
cfg['internal_functions'] = list(optiondescription.keys())
|
|
||||||
try:
|
|
||||||
eolobj = RougailConvert(cfg)
|
|
||||||
except Exception as err:
|
|
||||||
print(f'Try to load {module_info.modules}')
|
|
||||||
raise err from err
|
|
||||||
tiram_obj = eolobj.save(None)
|
|
||||||
# if server_name == 'revprox.in.silique.fr':
|
|
||||||
# print(tiram_obj)
|
|
||||||
#cfg['patches_dir'] = join(test_dir, 'patches')
|
|
||||||
cfg['tmp_dir'] = 'tmp'
|
|
||||||
cfg['destinations_dir'] = join(INSTALL_DIR, datas['module'], CONFIG_DEST_DIR, server_name)
|
|
||||||
if isdir('tmp'):
|
|
||||||
rmtree('tmp')
|
|
||||||
makedirs('tmp')
|
|
||||||
makedirs(cfg['destinations_dir'])
|
|
||||||
try:
|
|
||||||
exec(tiram_obj, None, optiondescription)
|
|
||||||
except Exception as err:
|
|
||||||
print(tiram_obj)
|
|
||||||
raise Exception(f'unknown error when load tiramisu object {err}') from err
|
|
||||||
config = await Config(optiondescription['option_0'], display_name=tiramisu_display_name)
|
|
||||||
await config.property.read_write()
|
|
||||||
try:
|
|
||||||
if await config.option('machine.add_srv').value.get():
|
|
||||||
srv = join(INSTALL_DIR, SRV_DEST_DIR, server_name)
|
|
||||||
else:
|
|
||||||
srv = None
|
|
||||||
except AttributeError:
|
|
||||||
srv = None
|
|
||||||
await config.property.read_write()
|
|
||||||
CONFIGS[server_name] = (config, cfg, srv, 0)
|
|
||||||
|
|
||||||
|
|
||||||
async def value_pprint(dico, config):
|
|
||||||
pprint_dict = {}
|
|
||||||
for path, value in dico.items():
|
|
||||||
if await config.option(path).option.type() == 'password' and value:
|
|
||||||
value = 'X' * len(value)
|
|
||||||
pprint_dict[path] = value
|
|
||||||
pprint(pprint_dict)
|
|
||||||
|
|
||||||
|
|
||||||
async def set_values(server_name, config, datas):
|
|
||||||
if 'informations' in datas:
|
|
||||||
for information, value in datas['informations'].items():
|
|
||||||
await config.information.set(information, value)
|
|
||||||
if 'extra_domainnames' in datas['informations']:
|
|
||||||
for idx, extra_domainname in enumerate(datas['informations']['extra_domainnames']):
|
|
||||||
if extra_domainname in CONFIGS:
|
|
||||||
raise Exception(f'server "{server_name}" is duplicate')
|
|
||||||
value = list(CONFIGS[server_name])
|
|
||||||
value[3] = idx + 1
|
|
||||||
CONFIGS[extra_domainname] = tuple(value)
|
|
||||||
await config.information.set('server_name', server_name)
|
|
||||||
await config.property.read_write()
|
|
||||||
try:
|
|
||||||
if 'values' in datas:
|
|
||||||
for path, value in datas['values'].items():
|
|
||||||
if isinstance(value, dict):
|
|
||||||
for idx, val in value.items():
|
|
||||||
await config.option(path, int(idx)).value.set(val)
|
|
||||||
else:
|
|
||||||
await config.option(path).value.set(value)
|
|
||||||
except Exception as err:
|
|
||||||
await value_pprint(await config.value.dict(), config)
|
|
||||||
error_msg = f'cannot configure server "{server_name}": {err}'
|
|
||||||
raise Exception(error_msg) from err
|
|
||||||
await config.property.read_only()
|
|
||||||
#await config.value.dict()
|
|
||||||
|
|
||||||
|
|
||||||
async def valid_mandatories(server_name, config):
|
|
||||||
mandatories = await config.value.mandatory()
|
|
||||||
if mandatories:
|
|
||||||
print()
|
|
||||||
print(f'=== Configuration: {server_name} ===')
|
|
||||||
await config.property.pop('mandatory')
|
|
||||||
await value_pprint(await config.value.dict(), config)
|
|
||||||
raise Exception(f'server "{server_name}" has mandatories variables without values "{", ".join(mandatories)}"')
|
|
||||||
|
|
||||||
|
|
||||||
async def templates(server_name, config, cfg, srv, int_idx):
|
|
||||||
values = await config.value.dict()
|
values = await config.value.dict()
|
||||||
engine = RougailSystemdTemplate(config, cfg)
|
engine = RougailSystemdTemplate(config, templates_informations)
|
||||||
# if server_name == 'dovecot.in.silique.fr':
|
# if server_name == 'dovecot.in.silique.fr':
|
||||||
# print()
|
# print()
|
||||||
# print(f'=== Configuration: {server_name} ===')
|
# print(f'=== Configuration: {server_name} ===')
|
||||||
|
@ -555,30 +57,43 @@ async def main():
|
||||||
if isdir(INSTALL_DIR):
|
if isdir(INSTALL_DIR):
|
||||||
rmtree(INSTALL_DIR)
|
rmtree(INSTALL_DIR)
|
||||||
makedirs(INSTALL_DIR)
|
makedirs(INSTALL_DIR)
|
||||||
module_infos = {}
|
module_infos = await load(display_name=tiramisu_display_name, clean_directories=True, copy_manual_dir=True)
|
||||||
for module_name, datas in MODULES.items():
|
# pprint(await CONFIGS['lemonldap.in.silique.fr'][0].value.dict())
|
||||||
build_module(module_name, datas, module_infos)
|
for server_name in SERVERS:
|
||||||
for server_name, datas in SERVERS.items():
|
module_name = CONFIGS[server_name]['module_name']
|
||||||
await build(server_name, datas, module_infos)
|
add_srv = CONFIGS[server_name]['add_srv']
|
||||||
|
cfg = CONFIGS[server_name]['templates_informations']
|
||||||
|
cfg['tmp_dir'] = 'tmp'
|
||||||
|
cfg['destinations_dir'] = join(INSTALL_DIR, module_name, CONFIG_DEST_DIR, server_name)
|
||||||
|
if isdir('tmp'):
|
||||||
|
rmtree('tmp')
|
||||||
|
makedirs(cfg['tmp_dir'])
|
||||||
|
makedirs(cfg['destinations_dir'])
|
||||||
|
if add_srv:
|
||||||
|
srv = join(INSTALL_DIR, SRV_DEST_DIR, server_name)
|
||||||
|
else:
|
||||||
|
srv = None
|
||||||
|
await templates(server_name, **CONFIGS[server_name], srv=srv)
|
||||||
|
for server_name in SERVERS:
|
||||||
|
config = CONFIGS[server_name]['config']
|
||||||
|
await config.property.read_write()
|
||||||
|
try:
|
||||||
|
# pass
|
||||||
|
await config.option('general.hide_secret').value.set(True)
|
||||||
|
except AttributeError:
|
||||||
|
# if rougail.general.hide_secret not exists
|
||||||
|
pass
|
||||||
|
await config.property.read_only()
|
||||||
|
for server_name in SERVERS:
|
||||||
|
module_name = CONFIGS[server_name]['module_name']
|
||||||
|
destinations_dir = join(INSTALL_DIR, module_name, CONFIG_DIFF_DIR, server_name)
|
||||||
|
makedirs(destinations_dir)
|
||||||
|
CONFIGS[server_name]['templates_informations']['destinations_dir'] = destinations_dir
|
||||||
|
await templates(server_name, **CONFIGS[server_name])
|
||||||
for module_name, cfg in module_infos.items():
|
for module_name, cfg in module_infos.items():
|
||||||
with open(join(INSTALL_DIR, module_name, 'install_machines'), 'w') as fh:
|
with open(join(INSTALL_DIR, module_name, 'install_machines'), 'w') as fh:
|
||||||
for server_name in cfg.servers:
|
for server_name in cfg.servers:
|
||||||
fh.write(f'./install_machine {module_name} {server_name}\n')
|
fh.write(f'./install_machine {module_name} {server_name}\n')
|
||||||
for server_name, datas in SERVERS.items():
|
|
||||||
await set_values(server_name, CONFIGS[server_name][0], datas)
|
|
||||||
for server_name in SERVERS:
|
|
||||||
config = CONFIGS[server_name][0]
|
|
||||||
await config.property.pop('mandatory')
|
|
||||||
try:
|
|
||||||
await config.value.dict()
|
|
||||||
except Exception as err:
|
|
||||||
raise Exception(f'cannot display config for "{server_name}": {err}')
|
|
||||||
await config.property.add('mandatory')
|
|
||||||
for server_name in SERVERS:
|
|
||||||
await valid_mandatories(server_name, CONFIGS[server_name][0])
|
|
||||||
# print(await CONFIGS['dovecot.in.silique.fr'][0].value.dict())
|
|
||||||
for server_name in SERVERS:
|
|
||||||
await templates(server_name, *CONFIGS[server_name])
|
|
||||||
|
|
||||||
|
|
||||||
run(main())
|
run(main())
|
||||||
|
|
105
funcs.py
105
funcs.py
|
@ -1,88 +1,24 @@
|
||||||
from tiramisu import valid_network_netmask, valid_ip_netmask, valid_broadcast, valid_in_network, valid_not_equal as valid_differ, valid_not_equal, calc_value
|
from tiramisu import valid_network_netmask, valid_ip_netmask, valid_broadcast, valid_in_network, valid_not_equal as valid_differ, valid_not_equal, calc_value
|
||||||
from ipaddress import ip_address
|
|
||||||
from os.path import dirname, abspath, join as _join, isdir as _isdir, isfile as _isfile
|
from os.path import dirname, abspath, join as _join, isdir as _isdir, isfile as _isfile
|
||||||
from typing import List
|
from typing import List
|
||||||
from json import load
|
|
||||||
from secrets import token_urlsafe as _token_urlsafe
|
from secrets import token_urlsafe as _token_urlsafe
|
||||||
|
|
||||||
from rougail.utils import normalize_family
|
from rougail.utils import normalize_family
|
||||||
|
|
||||||
from risotto.utils import multi_function, CONFIGS, DOMAINS
|
from risotto.utils import multi_function, DOMAINS, ZONES, load_zones, load_zones_server, load_domains, ZONES_SERVER
|
||||||
from risotto.x509 import gen_cert as _x509_gen_cert, gen_ca as _x509_gen_ca, gen_pub as _x509_gen_pub, has_pub as _x509_has_pub
|
from risotto.x509 import gen_cert as _x509_gen_cert, gen_ca as _x509_gen_ca, gen_pub as _x509_gen_pub, has_pub as _x509_has_pub
|
||||||
# =============================================================
|
|
||||||
# fork of risotto-setting/src/risotto_setting/config/config.py
|
|
||||||
|
|
||||||
with open('servers.json', 'r') as server_fh:
|
|
||||||
ZONES_SERVER = load(server_fh)
|
|
||||||
|
|
||||||
|
|
||||||
ZONES = None
|
|
||||||
HERE = dirname(abspath(__file__))
|
HERE = dirname(abspath(__file__))
|
||||||
|
|
||||||
|
|
||||||
def load_zones():
|
|
||||||
global ZONES
|
|
||||||
if ZONES is not None:
|
|
||||||
return
|
|
||||||
ZONES = ZONES_SERVER['zones']
|
|
||||||
for server_name, server in ZONES_SERVER['servers'].items():
|
|
||||||
if 'informations' not in server:
|
|
||||||
continue
|
|
||||||
server_zones = server['informations']['zones_name']
|
|
||||||
server_extra_domainnames = server['informations'].get('extra_domainnames', [])
|
|
||||||
if len(server_zones) > 1 and len(server_zones) != len(server_extra_domainnames) + 1:
|
|
||||||
raise Exception(f'the server "{server_name}" has more that one zone, please set correct number of extra_domainnames ({len(server_zones) - 1} instead of {len(server_extra_domainnames)})')
|
|
||||||
|
|
||||||
for idx, zone_name in enumerate(server_zones):
|
|
||||||
zone_domain_name = ZONES[zone_name]['domain_name']
|
|
||||||
if idx == 0:
|
|
||||||
zone_server_name = server_name
|
|
||||||
else:
|
|
||||||
zone_server_name = server_extra_domainnames[idx - 1]
|
|
||||||
server_domain_name = zone_server_name.split('.', 1)[1]
|
|
||||||
if zone_domain_name and zone_domain_name != server_domain_name:
|
|
||||||
raise Exception(f'wrong server_name "{zone_server_name}" in zone "{zone_name}" should ends with "{zone_domain_name}"')
|
|
||||||
ZONES[zone_name].setdefault('hosts', []).append(server_name)
|
|
||||||
|
|
||||||
|
|
||||||
def load_domains():
|
|
||||||
load_zones()
|
|
||||||
global DOMAINS
|
|
||||||
if DOMAINS:
|
|
||||||
return
|
|
||||||
for zone_name, zone in ZONES_SERVER['zones'].items():
|
|
||||||
if 'domain_name' in zone:
|
|
||||||
hosts = []
|
|
||||||
ips = []
|
|
||||||
for host in ZONES[zone_name].get('hosts', []):
|
|
||||||
hosts.append(host.split('.', 1)[0])
|
|
||||||
ips.append(get_ip(host, [zone_name], 0))
|
|
||||||
DOMAINS[zone['domain_name']] = (tuple(hosts), tuple(ips))
|
|
||||||
|
|
||||||
|
|
||||||
def get_ip(server_name: str,
|
|
||||||
zones_name: List[str],
|
|
||||||
index: str,
|
|
||||||
) -> str:
|
|
||||||
if server_name is None:
|
|
||||||
return
|
|
||||||
load_zones()
|
|
||||||
index = int(index)
|
|
||||||
zone_name = zones_name[index]
|
|
||||||
if zone_name not in ZONES:
|
|
||||||
raise ValueError(f"cannot set IP in unknown zone '{zone_name}'")
|
|
||||||
zone = ZONES[zone_name]
|
|
||||||
if server_name not in zone['hosts']:
|
|
||||||
raise ValueError(f"cannot set IP in unknown server '{server_name}'")
|
|
||||||
server_index = zone['hosts'].index(server_name)
|
|
||||||
# print(server_name, zones_name, index, str(ip_address(zone['start_ip']) + server_index))
|
|
||||||
return str(ip_address(zone['start_ip']) + server_index)
|
|
||||||
|
|
||||||
|
|
||||||
@multi_function
|
@multi_function
|
||||||
def get_chain(authority_cn,
|
def get_chain(authority_cn: str,
|
||||||
authority_name,
|
authority_name: str,
|
||||||
|
hide: bool,
|
||||||
):
|
):
|
||||||
|
if hide:
|
||||||
|
return "XXXXX"
|
||||||
if not authority_name or authority_name is None:
|
if not authority_name or authority_name is None:
|
||||||
if isinstance(authority_name, list):
|
if isinstance(authority_name, list):
|
||||||
return []
|
return []
|
||||||
|
@ -107,11 +43,14 @@ def get_chain(authority_cn,
|
||||||
|
|
||||||
@multi_function
|
@multi_function
|
||||||
def get_certificate(cn,
|
def get_certificate(cn,
|
||||||
authority_name,
|
authority_name: str,
|
||||||
authority_cn=None,
|
hide: bool,
|
||||||
extra_domainnames=[],
|
authority_cn: str=None,
|
||||||
type='server',
|
extra_domainnames: list=[],
|
||||||
|
type: str='server',
|
||||||
):
|
):
|
||||||
|
if hide:
|
||||||
|
return "XXXXX"
|
||||||
if isinstance(cn, list) and extra_domainnames:
|
if isinstance(cn, list) and extra_domainnames:
|
||||||
raise Exception('cn cannot be a list with extra_domainnames set')
|
raise Exception('cn cannot be a list with extra_domainnames set')
|
||||||
if not cn or authority_name is None:
|
if not cn or authority_name is None:
|
||||||
|
@ -129,11 +68,14 @@ def get_certificate(cn,
|
||||||
|
|
||||||
|
|
||||||
@multi_function
|
@multi_function
|
||||||
def get_private_key(cn,
|
def get_private_key(cn: str,
|
||||||
authority_name=None,
|
hide: bool,
|
||||||
authority_cn=None,
|
authority_name: str=None,
|
||||||
type='server',
|
authority_cn: str=None,
|
||||||
|
type: str='server',
|
||||||
):
|
):
|
||||||
|
if hide:
|
||||||
|
return "XXXXX"
|
||||||
if not cn:
|
if not cn:
|
||||||
if isinstance(cn, list):
|
if isinstance(cn, list):
|
||||||
return []
|
return []
|
||||||
|
@ -157,7 +99,11 @@ def get_private_key(cn,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def get_public_key(cn):
|
def get_public_key(cn: str,
|
||||||
|
hide: bool,
|
||||||
|
):
|
||||||
|
if hide:
|
||||||
|
return "XXXXX"
|
||||||
if not cn:
|
if not cn:
|
||||||
return
|
return
|
||||||
return _x509_gen_pub(cn,
|
return _x509_gen_pub(cn,
|
||||||
|
@ -194,6 +140,7 @@ def get_internal_zones() -> List[str]:
|
||||||
|
|
||||||
@multi_function
|
@multi_function
|
||||||
def get_zones_info(type: str) -> str:
|
def get_zones_info(type: str) -> str:
|
||||||
|
load_zones_server()
|
||||||
ret = []
|
ret = []
|
||||||
for data in ZONES_SERVER['zones'].values():
|
for data in ZONES_SERVER['zones'].values():
|
||||||
ret.append(data[type])
|
ret.append(data[type])
|
||||||
|
|
272
src/risotto/image.py
Normal file
272
src/risotto/image.py
Normal file
|
@ -0,0 +1,272 @@
|
||||||
|
from shutil import copy2, copytree, rmtree
|
||||||
|
from os import listdir, makedirs
|
||||||
|
from os.path import join, isdir, isfile
|
||||||
|
from yaml import load as yaml_load, SafeLoader
|
||||||
|
from json import load as json_load
|
||||||
|
|
||||||
|
from .utils import CONFIGS, RISOTTO_CONFIG, SERVERS, value_pprint
|
||||||
|
from .machine import load_machine_config
|
||||||
|
|
||||||
|
|
||||||
|
FUNCTIONS = 'funcs.py'
|
||||||
|
|
||||||
|
|
||||||
|
class ModuleCfg():
|
||||||
|
def __init__(self):
|
||||||
|
self.dictionaries_dir = []
|
||||||
|
self.modules = []
|
||||||
|
self.functions_file = [FUNCTIONS]
|
||||||
|
self.templates_dir = []
|
||||||
|
self.extra_dictionaries = {}
|
||||||
|
self.servers = []
|
||||||
|
|
||||||
|
|
||||||
|
def list_applications() -> dict:
|
||||||
|
"""
|
||||||
|
{<applicationservice>: applicationservice/<release>/<applicationservice>
|
||||||
|
"""
|
||||||
|
dataset_directory = RISOTTO_CONFIG['directories']['dataset']
|
||||||
|
applications = {}
|
||||||
|
distrib_dir = join(dataset_directory, 'applicationservice')
|
||||||
|
for release in listdir(distrib_dir):
|
||||||
|
release_dir = join(distrib_dir, release)
|
||||||
|
if not isdir(release_dir):
|
||||||
|
continue
|
||||||
|
for applicationservice in listdir(release_dir):
|
||||||
|
applicationservice_dir = join(release_dir, applicationservice)
|
||||||
|
if not isdir(applicationservice_dir):
|
||||||
|
continue
|
||||||
|
if applicationservice in applications:
|
||||||
|
raise Exception(f'multi applicationservice: {applicationservice} ({applicationservice_dir} <=> {applications[applicationservice]})')
|
||||||
|
applications[applicationservice] = applicationservice_dir
|
||||||
|
return applications
|
||||||
|
|
||||||
|
|
||||||
|
def applicationservice_copy(src_file: str,
|
||||||
|
dst_file: str,
|
||||||
|
copy_if_not_exists: bool,
|
||||||
|
manual_dir: str,
|
||||||
|
filename: str,
|
||||||
|
) -> None:
|
||||||
|
if isdir(src_file):
|
||||||
|
if not isdir(dst_file):
|
||||||
|
makedirs(dst_file)
|
||||||
|
for subfilename in listdir(src_file):
|
||||||
|
if not copy_if_not_exists or not isfile(dst_file):
|
||||||
|
src = join(src_file, subfilename)
|
||||||
|
dst = join(dst_file, subfilename)
|
||||||
|
if isfile(src):
|
||||||
|
copy2(src, dst)
|
||||||
|
else:
|
||||||
|
copytree(src, dst)
|
||||||
|
elif not copy_if_not_exists or not isfile(dst_file):
|
||||||
|
src = join(manual_dir, filename)
|
||||||
|
dst = dst_file
|
||||||
|
if isfile(src):
|
||||||
|
copy2(src, dst)
|
||||||
|
else:
|
||||||
|
copytree(src, dst)
|
||||||
|
|
||||||
|
|
||||||
|
def load_applicationservice_cfg(appname: str,
|
||||||
|
as_dir: str,
|
||||||
|
install_dir: str,
|
||||||
|
cfg: ModuleCfg,
|
||||||
|
copy_manual_dir: bool,
|
||||||
|
) -> None:
|
||||||
|
cfg.modules.append(appname)
|
||||||
|
# dictionaries
|
||||||
|
dictionaries_dir = join(as_dir, 'dictionaries')
|
||||||
|
if isdir(dictionaries_dir):
|
||||||
|
cfg.dictionaries_dir.append(dictionaries_dir)
|
||||||
|
# funcs
|
||||||
|
funcs_dir = join(as_dir, 'funcs')
|
||||||
|
if isdir(funcs_dir):
|
||||||
|
for f in listdir(funcs_dir):
|
||||||
|
if f.startswith('__'):
|
||||||
|
continue
|
||||||
|
cfg.functions_file.append(join(funcs_dir, f))
|
||||||
|
# templates
|
||||||
|
templates_dir = join(as_dir, 'templates')
|
||||||
|
if isdir(templates_dir):
|
||||||
|
cfg.templates_dir.append(templates_dir)
|
||||||
|
# extras
|
||||||
|
extras_dir = join(as_dir, 'extras')
|
||||||
|
if isdir(extras_dir):
|
||||||
|
for extra in listdir(extras_dir):
|
||||||
|
extra_dir = join(extras_dir, extra)
|
||||||
|
if isdir(extra_dir):
|
||||||
|
cfg.extra_dictionaries.setdefault(extra, []).append(extra_dir)
|
||||||
|
if copy_manual_dir:
|
||||||
|
# manual
|
||||||
|
for type in ['image', 'install']:
|
||||||
|
manual_dir = join(as_dir, 'manual', type)
|
||||||
|
if not isdir(manual_dir):
|
||||||
|
continue
|
||||||
|
for filename in listdir(manual_dir):
|
||||||
|
src_file = join(manual_dir, filename)
|
||||||
|
if type == 'image':
|
||||||
|
dst_file = join(install_dir, 'manual', filename)
|
||||||
|
copy_if_not_exists = False
|
||||||
|
else:
|
||||||
|
dst_file= join(install_dir, '..', filename)
|
||||||
|
copy_if_not_exists = True
|
||||||
|
applicationservice_copy(src_file,
|
||||||
|
dst_file,
|
||||||
|
copy_if_not_exists,
|
||||||
|
manual_dir,
|
||||||
|
filename,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def load_applicationservice(appname: str,
|
||||||
|
added: list,
|
||||||
|
install_dir: str,
|
||||||
|
cfg: ModuleCfg,
|
||||||
|
applications: dict,
|
||||||
|
copy_manual_dir: bool,
|
||||||
|
) -> None:
|
||||||
|
if appname not in applications:
|
||||||
|
raise Exception(f'cannot find application dependency "{appname}" in application "{module_name}"')
|
||||||
|
as_dir = applications[appname]
|
||||||
|
applicationservice_file = join(as_dir, 'applicationservice.yml')
|
||||||
|
if not isfile(applicationservice_file):
|
||||||
|
raise Exception(f'cannot find application service file "{applicationservice_file}"')
|
||||||
|
load_applicationservice_cfg(appname,
|
||||||
|
as_dir,
|
||||||
|
install_dir,
|
||||||
|
cfg,
|
||||||
|
copy_manual_dir,
|
||||||
|
)
|
||||||
|
added.append(appname)
|
||||||
|
with open(applicationservice_file) as yaml:
|
||||||
|
app = yaml_load(yaml, Loader=SafeLoader)
|
||||||
|
for xml in app.get('depends', []):
|
||||||
|
if xml in added:
|
||||||
|
continue
|
||||||
|
load_applicationservice(xml,
|
||||||
|
added,
|
||||||
|
install_dir,
|
||||||
|
cfg,
|
||||||
|
applications,
|
||||||
|
copy_manual_dir,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def load_image_informations(install_dir: str,
|
||||||
|
datas: dict,
|
||||||
|
applications: dict,
|
||||||
|
copy_manual_dir: bool,
|
||||||
|
) -> ModuleCfg:
|
||||||
|
cfg = ModuleCfg()
|
||||||
|
added = []
|
||||||
|
for applicationservice in datas['applicationservices']:
|
||||||
|
load_applicationservice(applicationservice,
|
||||||
|
added,
|
||||||
|
install_dir,
|
||||||
|
cfg,
|
||||||
|
applications,
|
||||||
|
copy_manual_dir,
|
||||||
|
)
|
||||||
|
return cfg
|
||||||
|
|
||||||
|
|
||||||
|
async def load_informations(server_name, datas, config):
|
||||||
|
await config.information.set('server_name', server_name)
|
||||||
|
if 'informations' not in datas:
|
||||||
|
return
|
||||||
|
for information, value in datas['informations'].items():
|
||||||
|
await config.information.set(information, value)
|
||||||
|
if 'extra_domainnames' in datas['informations']:
|
||||||
|
for idx, extra_domainname in enumerate(datas['informations']['extra_domainnames']):
|
||||||
|
if extra_domainname in CONFIGS:
|
||||||
|
raise Exception(f'server "{server_name}" is duplicate')
|
||||||
|
value = list(CONFIGS[server_name])
|
||||||
|
value[4] = idx + 1
|
||||||
|
CONFIGS[extra_domainname] = tuple(value)
|
||||||
|
|
||||||
|
|
||||||
|
async def set_values(server_name, config, datas):
|
||||||
|
try:
|
||||||
|
if 'values' in datas:
|
||||||
|
for path, value in datas['values'].items():
|
||||||
|
if isinstance(value, dict):
|
||||||
|
for idx, val in value.items():
|
||||||
|
await config.option(path, int(idx)).value.set(val)
|
||||||
|
else:
|
||||||
|
await config.option(path).value.set(value)
|
||||||
|
except Exception as err:
|
||||||
|
await value_pprint(await config.value.dict(), config)
|
||||||
|
error_msg = f'cannot configure server "{server_name}": {err}'
|
||||||
|
raise Exception(error_msg) from err
|
||||||
|
#await config.value.dict()
|
||||||
|
|
||||||
|
|
||||||
|
async def valid_mandatories(server_name, config):
|
||||||
|
mandatories = await config.value.mandatory()
|
||||||
|
if mandatories:
|
||||||
|
print()
|
||||||
|
print(f'=== Configuration: {server_name} ===')
|
||||||
|
await config.property.pop('mandatory')
|
||||||
|
await value_pprint(await config.value.dict(), config)
|
||||||
|
raise Exception(f'server "{server_name}" has mandatories variables without values "{", ".join(mandatories)}"')
|
||||||
|
|
||||||
|
|
||||||
|
async def load(display_name=None,
|
||||||
|
clean_directories=False,
|
||||||
|
copy_manual_dir=False,
|
||||||
|
hide_secret=False,
|
||||||
|
):
|
||||||
|
with open('servers.json', 'r') as server_fh:
|
||||||
|
jsonfile = json_load(server_fh)
|
||||||
|
SERVERS.update(jsonfile['servers'])
|
||||||
|
modules = jsonfile['modules']
|
||||||
|
module_infos = {}
|
||||||
|
applications = list_applications()
|
||||||
|
# load images
|
||||||
|
for module_name, datas in modules.items():
|
||||||
|
install_dir = join(RISOTTO_CONFIG['directories']['dest'], module_name)
|
||||||
|
if clean_directories:
|
||||||
|
if isdir(install_dir):
|
||||||
|
rmtree(install_dir)
|
||||||
|
makedirs(install_dir)
|
||||||
|
module_infos[module_name] = load_image_informations(install_dir,
|
||||||
|
datas,
|
||||||
|
applications,
|
||||||
|
copy_manual_dir,
|
||||||
|
)
|
||||||
|
# load machines
|
||||||
|
for server_name, datas in SERVERS.items():
|
||||||
|
if server_name in CONFIGS:
|
||||||
|
raise Exception(f'server "{server_name}" is duplicate')
|
||||||
|
CONFIGS[server_name] = await load_machine_config(server_name,
|
||||||
|
datas,
|
||||||
|
module_infos[datas['module']],
|
||||||
|
display_name=display_name,
|
||||||
|
)
|
||||||
|
# set servers.json values
|
||||||
|
for server_name, datas in SERVERS.items():
|
||||||
|
config = CONFIGS[server_name]['config']
|
||||||
|
await load_informations(server_name, datas, config)
|
||||||
|
await config.property.read_write()
|
||||||
|
if hide_secret:
|
||||||
|
try:
|
||||||
|
await config.option('general.hide_secret').value.set(True)
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
|
await set_values(server_name, config, datas)
|
||||||
|
await config.property.read_only()
|
||||||
|
# force calculates all values (for linked values)
|
||||||
|
for server_name in SERVERS:
|
||||||
|
config = CONFIGS[server_name]['config']
|
||||||
|
await config.property.pop('mandatory')
|
||||||
|
try:
|
||||||
|
await config.value.dict()
|
||||||
|
except Exception as err:
|
||||||
|
raise Exception(f'cannot display config for "{server_name}": {err}')
|
||||||
|
await config.property.add('mandatory')
|
||||||
|
# validate mandatories values
|
||||||
|
for server_name in SERVERS:
|
||||||
|
await valid_mandatories(server_name, CONFIGS[server_name]['config'])
|
||||||
|
|
||||||
|
return module_infos
|
394
src/risotto/machine.py
Normal file
394
src/risotto/machine.py
Normal file
|
@ -0,0 +1,394 @@
|
||||||
|
from os import makedirs
|
||||||
|
from os.path import join, isdir
|
||||||
|
from warnings import warn_explicit
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
from tiramisu import Config
|
||||||
|
from tiramisu.error import ValueWarning
|
||||||
|
from rougail import RougailConfig, RougailConvert
|
||||||
|
from .utils import MULTI_FUNCTIONS, CONFIGS, DOMAINS
|
||||||
|
from rougail.utils import normalize_family
|
||||||
|
|
||||||
|
|
||||||
|
ROUGAIL_NAMESPACE = 'general'
|
||||||
|
ROUGAIL_NAMESPACE_DESCRIPTION = 'Général'
|
||||||
|
|
||||||
|
|
||||||
|
async def set_linked_multi_variables(value: str,
|
||||||
|
linked_server: str=None,
|
||||||
|
variable_index: int=None,
|
||||||
|
linked_returns: str=None,
|
||||||
|
dynamic: str=None,
|
||||||
|
**kwargs: dict,
|
||||||
|
) -> None:
|
||||||
|
if value is not None and linked_server is not None and 'linked_value_0' not in kwargs:
|
||||||
|
kwargs['linked_value_0'] = value
|
||||||
|
elif not linked_server:
|
||||||
|
linked_server = value
|
||||||
|
if not linked_server:
|
||||||
|
return
|
||||||
|
if linked_server not in CONFIGS:
|
||||||
|
warn_explicit(ValueWarning(f'cannot find linked server "{linked_server}"'),
|
||||||
|
ValueWarning,
|
||||||
|
__file__,
|
||||||
|
3,
|
||||||
|
)
|
||||||
|
return
|
||||||
|
config = CONFIGS[linked_server]['config']
|
||||||
|
dico = {}
|
||||||
|
variables = {}
|
||||||
|
for key, kvalue in kwargs.items():
|
||||||
|
try:
|
||||||
|
index = int(key.rsplit('_', 1)[-1])
|
||||||
|
except ValueError:
|
||||||
|
raise Exception(f'unknown variable {key}')
|
||||||
|
if kvalue is None and not kwargs.get(f'allow_none_{index}', False):
|
||||||
|
return
|
||||||
|
if key.startswith('linked_provider_'):
|
||||||
|
path = await config.information.get('provider:' + kvalue, None)
|
||||||
|
if not path:
|
||||||
|
return
|
||||||
|
if index not in variables:
|
||||||
|
variables[index] = {'path': None, 'value': None, 'variable_index': False}
|
||||||
|
variables[index]['path'] = path
|
||||||
|
elif key.startswith('linked_value_'):
|
||||||
|
index = int(key[13])
|
||||||
|
if index not in variables:
|
||||||
|
variables[index] = {'path': None, 'value': None, 'variable_index': False}
|
||||||
|
variables[index]['value'] = kvalue
|
||||||
|
elif key.startswith('variable_index_'):
|
||||||
|
index = int(key[15])
|
||||||
|
if index not in variables:
|
||||||
|
variables[index] = {'path': None, 'value': None, 'variable_index': False}
|
||||||
|
variables[index]['variable_index'] = True
|
||||||
|
elif key.startswith('allow_none_'):
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
raise AttributeError(f'unknown parameter {key}')
|
||||||
|
await config.property.read_write()
|
||||||
|
if not isinstance(variables[0]['value'], list):
|
||||||
|
variables[0]['value'] = [variables[0]['value']]
|
||||||
|
if dynamic:
|
||||||
|
dynamic = normalize_family(dynamic)
|
||||||
|
_dynamic = None
|
||||||
|
try:
|
||||||
|
if variables[0]['value']:
|
||||||
|
for first_idx, first_value in enumerate(variables[0]['value']):
|
||||||
|
slave_idxes = []
|
||||||
|
if dynamic:
|
||||||
|
_dynamic = dynamic
|
||||||
|
else:
|
||||||
|
_dynamic = normalize_family(first_value)
|
||||||
|
for index in sorted(list(variables)):
|
||||||
|
path = variables[index]['path']
|
||||||
|
if '{suffix}' in path:
|
||||||
|
path = path.replace('{suffix}', _dynamic)
|
||||||
|
elif first_idx != 0:
|
||||||
|
continue
|
||||||
|
vvalue = variables[index]['value']
|
||||||
|
option = config.forcepermissive.option(path)
|
||||||
|
if not await option.option.isfollower():
|
||||||
|
#print('===>', path, vvalue, await option.option.ismulti(), await option.option.issubmulti())
|
||||||
|
multi = await option.option.ismulti()
|
||||||
|
if multi:
|
||||||
|
isleader = await option.option.isleader()
|
||||||
|
if not isinstance(vvalue, list):
|
||||||
|
vvalue = [vvalue]
|
||||||
|
# elif isleader:
|
||||||
|
# raise Exception('leader must not be a multi from now ...')
|
||||||
|
values = await option.value.get()
|
||||||
|
for val in vvalue:
|
||||||
|
if val not in values:
|
||||||
|
if isleader:
|
||||||
|
slave_idxes.append(len(values))
|
||||||
|
values.append(val)
|
||||||
|
elif isleader:
|
||||||
|
slave_idxes.append(values.index(val))
|
||||||
|
await option.value.set(values)
|
||||||
|
await option.owner.set('link')
|
||||||
|
else:
|
||||||
|
if isinstance(vvalue, list) and len(vvalue) == 1:
|
||||||
|
vvalue = vvalue[0]
|
||||||
|
await option.value.set(vvalue)
|
||||||
|
await option.owner.set('link')
|
||||||
|
else:
|
||||||
|
# print('===<', path, vvalue, await option.option.ismulti(), await option.option.issubmulti())
|
||||||
|
if not slave_idxes:
|
||||||
|
raise Exception('please declare the leader variable before the follower')
|
||||||
|
if variables[index]['variable_index']:
|
||||||
|
vvalue = vvalue[variable_index]
|
||||||
|
if not isinstance(vvalue, list):
|
||||||
|
vvalue = [vvalue] * len(slave_idxes)
|
||||||
|
# if isinstance(vvalue, list) and not await option.option.issubmulti():
|
||||||
|
for idx, val in enumerate(vvalue):
|
||||||
|
option = config.forcepermissive.option(path, slave_idxes[idx])
|
||||||
|
await option.value.set(val)
|
||||||
|
await option.owner.set('link')
|
||||||
|
except Exception as err:
|
||||||
|
await config.property.read_only()
|
||||||
|
raise err from err
|
||||||
|
await config.property.read_only()
|
||||||
|
if not dynamic:
|
||||||
|
dynamic = _dynamic
|
||||||
|
|
||||||
|
|
||||||
|
if linked_returns is not None:
|
||||||
|
linked_variable = await config.information.get('provider:' + linked_returns, None)
|
||||||
|
if not linked_variable:
|
||||||
|
warn_explicit(ValueWarning(f'cannot find linked variable "{linked_returns}" in linked server "{linked_server}"'),
|
||||||
|
ValueWarning,
|
||||||
|
__file__,
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
return
|
||||||
|
if dynamic:
|
||||||
|
linked_variable = linked_variable.replace('{suffix}', normalize_family(dynamic))
|
||||||
|
elif '{suffix}' in linked_variable:
|
||||||
|
idx = CONFIGS[linked_server]['interface_index']
|
||||||
|
linked_variable = linked_variable.replace('{suffix}', str(idx))
|
||||||
|
ret = await config.forcepermissive.option(linked_variable).value.get()
|
||||||
|
else:
|
||||||
|
ret = get_ip_from_domain(linked_server)
|
||||||
|
return ret
|
||||||
|
|
||||||
|
|
||||||
|
async def set_linked(linked_server: str,
|
||||||
|
linked_provider: str,
|
||||||
|
linked_value: str,
|
||||||
|
linked_returns: str=None,
|
||||||
|
dynamic: str=None,
|
||||||
|
):
|
||||||
|
if None in (linked_server, linked_provider, linked_value):
|
||||||
|
return
|
||||||
|
if linked_server not in CONFIGS:
|
||||||
|
warn_explicit(ValueWarning(f'cannot find linked server "{linked_server}"'),
|
||||||
|
ValueWarning,
|
||||||
|
__file__,
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
return
|
||||||
|
config = CONFIGS[linked_server]['config']
|
||||||
|
path = await config.information.get('provider:' + linked_provider, None)
|
||||||
|
if not path:
|
||||||
|
warn_explicit(ValueWarning(f'cannot find provider "{linked_provider}" in linked server "{linked_server}"'),
|
||||||
|
ValueWarning,
|
||||||
|
__file__,
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
return
|
||||||
|
await config.property.read_write()
|
||||||
|
try:
|
||||||
|
option = config.forcepermissive.option(path)
|
||||||
|
if await option.option.ismulti():
|
||||||
|
values = await option.value.get()
|
||||||
|
if linked_value not in values:
|
||||||
|
values.append(linked_value)
|
||||||
|
await option.value.set(values)
|
||||||
|
await option.owner.set('link')
|
||||||
|
else:
|
||||||
|
await option.value.set(linked_value)
|
||||||
|
await option.owner.set('link')
|
||||||
|
except Exception as err:
|
||||||
|
await config.property.read_only()
|
||||||
|
raise err from err
|
||||||
|
await config.property.read_only()
|
||||||
|
if linked_returns is not None:
|
||||||
|
linked_variable = await config.information.get('provider:' + linked_returns, None)
|
||||||
|
if not linked_variable:
|
||||||
|
warn_explicit(ValueWarning(f'cannot find linked variable "{linked_returns}" in linked server "{linked_server}"'),
|
||||||
|
ValueWarning,
|
||||||
|
__file__,
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
linked_variable = None
|
||||||
|
if linked_variable is not None:
|
||||||
|
if dynamic:
|
||||||
|
linked_variable = linked_variable.replace('{suffix}', normalize_family(dynamic))
|
||||||
|
elif '{suffix}' in linked_variable:
|
||||||
|
idx = CONFIGS[linked_server]['interface_index']
|
||||||
|
linked_variable = linked_variable.replace('{suffix}', str(idx))
|
||||||
|
ret = await config.forcepermissive.option(linked_variable).value.get()
|
||||||
|
else:
|
||||||
|
ret = normalize_family(linked_value)
|
||||||
|
return ret
|
||||||
|
|
||||||
|
|
||||||
|
async def get_linked_configuration(linked_server: str,
|
||||||
|
linked_provider: str,
|
||||||
|
dynamic: str=None,
|
||||||
|
):
|
||||||
|
if linked_server not in CONFIGS:
|
||||||
|
warn_explicit(ValueWarning(f'cannot find linked server "{linked_server}"'),
|
||||||
|
ValueWarning,
|
||||||
|
__file__,
|
||||||
|
1,
|
||||||
|
)
|
||||||
|
return
|
||||||
|
config = CONFIGS[linked_server]['config']
|
||||||
|
path = await config.information.get('provider:' + linked_provider, None)
|
||||||
|
if not path:
|
||||||
|
warn_explicit(ValueWarning(f'cannot find variable "{path}" in linked server "{linked_server}"'),
|
||||||
|
ValueWarning,
|
||||||
|
__file__,
|
||||||
|
1,
|
||||||
|
)
|
||||||
|
return
|
||||||
|
if dynamic:
|
||||||
|
path = path.replace('{suffix}', normalize_family(dynamic))
|
||||||
|
try:
|
||||||
|
return await config.forcepermissive.option(path).value.get()
|
||||||
|
except AttributeError as err:
|
||||||
|
warn_explicit(ValueWarning(f'cannot find get value of "{path}" in linked server "{linked_server}": {err}'),
|
||||||
|
ValueWarning,
|
||||||
|
__file__,
|
||||||
|
1,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class Empty:
|
||||||
|
pass
|
||||||
|
empty = Empty()
|
||||||
|
|
||||||
|
|
||||||
|
async def set_linked_configuration(_linked_value: Any,
|
||||||
|
linked_server: str,
|
||||||
|
linked_provider: str,
|
||||||
|
linked_value: Any=empty,
|
||||||
|
dynamic: str=None,
|
||||||
|
leader_provider: str=None,
|
||||||
|
leader_value: Any=None,
|
||||||
|
leader_index: int=None,
|
||||||
|
):
|
||||||
|
if linked_value is not empty:
|
||||||
|
_linked_value = linked_value
|
||||||
|
linked_value = _linked_value
|
||||||
|
if linked_server is None:
|
||||||
|
return
|
||||||
|
if linked_value is None or linked_server not in CONFIGS:
|
||||||
|
warn_explicit(ValueWarning(f'cannot find linked server "{linked_server}"'),
|
||||||
|
ValueWarning,
|
||||||
|
__file__,
|
||||||
|
2,
|
||||||
|
)
|
||||||
|
return
|
||||||
|
config = CONFIGS[linked_server]['config']
|
||||||
|
path = await config.information.get('provider:' + linked_provider, None)
|
||||||
|
if not path:
|
||||||
|
warn_explicit(ValueWarning(f'cannot find variable "{path}" in linked server "{linked_server}"'),
|
||||||
|
ValueWarning,
|
||||||
|
__file__,
|
||||||
|
2,
|
||||||
|
)
|
||||||
|
return
|
||||||
|
if dynamic:
|
||||||
|
path = path.replace('{suffix}', normalize_family(dynamic))
|
||||||
|
await config.property.read_write()
|
||||||
|
try:
|
||||||
|
if leader_provider is not None:
|
||||||
|
leader_path = await config.information.get('provider:' + leader_provider, None)
|
||||||
|
if not leader_path:
|
||||||
|
await config.property.read_only()
|
||||||
|
warn_explicit(ValueWarning(f'cannot find leader variable with leader_provider "{leader_provider}" in linked server "{linked_server}"'),
|
||||||
|
ValueWarning,
|
||||||
|
__file__,
|
||||||
|
2,
|
||||||
|
)
|
||||||
|
return
|
||||||
|
if dynamic:
|
||||||
|
leader_path = leader_path.replace('{suffix}', normalize_family(dynamic))
|
||||||
|
values = await config.forcepermissive.option(leader_path).value.get()
|
||||||
|
if not isinstance(leader_value, list):
|
||||||
|
leader_value = [leader_value]
|
||||||
|
for lv in leader_value:
|
||||||
|
if lv in values:
|
||||||
|
slave_idx = values.index(lv)
|
||||||
|
slave_option = config.forcepermissive.option(path, slave_idx)
|
||||||
|
if await slave_option.option.issubmulti():
|
||||||
|
slave_values = await slave_option.value.get()
|
||||||
|
if linked_value not in slave_values:
|
||||||
|
slave_values.append(linked_value)
|
||||||
|
await slave_option.value.set(slave_values)
|
||||||
|
await slave_option.owner.set('link')
|
||||||
|
else:
|
||||||
|
await slave_option.value.set(linked_value)
|
||||||
|
await slave_option.owner.set('link')
|
||||||
|
else:
|
||||||
|
option = config.forcepermissive.option(path, leader_index)
|
||||||
|
if leader_index is None and await option.option.ismulti() and not isinstance(linked_value, list):
|
||||||
|
values = await option.value.get()
|
||||||
|
if linked_value not in values:
|
||||||
|
values.append(linked_value)
|
||||||
|
await option.value.set(values)
|
||||||
|
await option.owner.set('link')
|
||||||
|
else:
|
||||||
|
await option.value.set(linked_value)
|
||||||
|
await option.owner.set('link')
|
||||||
|
except AttributeError as err:
|
||||||
|
if linked_provider == 'oauth2_external':
|
||||||
|
raise ValueError(str(err)) from err
|
||||||
|
pass
|
||||||
|
except Exception as err:
|
||||||
|
await config.property.read_only()
|
||||||
|
raise err from err
|
||||||
|
await config.property.read_only()
|
||||||
|
|
||||||
|
|
||||||
|
def get_ip_from_domain(domain):
|
||||||
|
if not domain:
|
||||||
|
return
|
||||||
|
hostname, domainname = domain.split('.', 1)
|
||||||
|
return DOMAINS[domainname][1][DOMAINS[domainname][0].index(hostname)]
|
||||||
|
|
||||||
|
|
||||||
|
async def load_machine_config(server_name: str,
|
||||||
|
datas: dict,
|
||||||
|
module_info: dict,
|
||||||
|
display_name,
|
||||||
|
):
|
||||||
|
optiondescription = {'set_linked': set_linked,
|
||||||
|
'set_linked_multi_variables': set_linked_multi_variables,
|
||||||
|
'get_linked_configuration': get_linked_configuration,
|
||||||
|
'set_linked_configuration': set_linked_configuration,
|
||||||
|
'get_ip_from_domain': get_ip_from_domain,
|
||||||
|
}
|
||||||
|
cfg = RougailConfig.copy()
|
||||||
|
module_info.servers.append(server_name)
|
||||||
|
cfg['variable_namespace'] = ROUGAIL_NAMESPACE
|
||||||
|
cfg['variable_namespace_description'] = ROUGAIL_NAMESPACE_DESCRIPTION
|
||||||
|
if datas['module'] == 'host':
|
||||||
|
cfg['tmpfile_dest_dir'] = datas['values'][f'{ROUGAIL_NAMESPACE}.host_install_dir'] + '/host/configurations/' + server_name
|
||||||
|
cfg['templates_dir'] = module_info.templates_dir
|
||||||
|
cfg['dictionaries_dir'] = module_info.dictionaries_dir
|
||||||
|
cfg['functions_file'] = module_info.functions_file
|
||||||
|
cfg['multi_functions'] = MULTI_FUNCTIONS
|
||||||
|
cfg['extra_dictionaries'] = module_info.extra_dictionaries
|
||||||
|
cfg['extra_annotators'].append('risotto.rougail')
|
||||||
|
cfg['internal_functions'] = list(optiondescription.keys())
|
||||||
|
try:
|
||||||
|
eolobj = RougailConvert(cfg)
|
||||||
|
except Exception as err:
|
||||||
|
print(f'Try to load {module_info.modules}')
|
||||||
|
raise err from err
|
||||||
|
tiram_obj = eolobj.save(None)
|
||||||
|
# if server_name == 'revprox.in.silique.fr':
|
||||||
|
# print(tiram_obj)
|
||||||
|
#cfg['patches_dir'] = join(test_dir, 'patches')
|
||||||
|
try:
|
||||||
|
exec(tiram_obj, None, optiondescription)
|
||||||
|
except Exception as err:
|
||||||
|
print(tiram_obj)
|
||||||
|
raise Exception(f'unknown error when load tiramisu object {err}') from err
|
||||||
|
config = await Config(optiondescription['option_0'], display_name=display_name)
|
||||||
|
await config.property.read_write()
|
||||||
|
try:
|
||||||
|
add_srv = await config.option('machine.add_srv').value.get()
|
||||||
|
except AttributeError:
|
||||||
|
add_srv = False
|
||||||
|
return {'config': config,
|
||||||
|
'templates_informations': cfg,
|
||||||
|
'interface_index': 0,
|
||||||
|
'module_name': datas['module'],
|
||||||
|
'add_srv': add_srv,
|
||||||
|
}
|
|
@ -1,6 +1,21 @@
|
||||||
|
from os import environ
|
||||||
|
from json import load
|
||||||
|
from typing import List
|
||||||
|
from ipaddress import ip_address
|
||||||
|
from toml import load as toml_load
|
||||||
|
from pprint import pprint
|
||||||
|
|
||||||
|
|
||||||
MULTI_FUNCTIONS = []
|
MULTI_FUNCTIONS = []
|
||||||
CONFIGS = {}
|
CONFIGS = {}
|
||||||
DOMAINS = {}
|
DOMAINS = {}
|
||||||
|
ZONES = {}
|
||||||
|
ZONES_SERVER = {}
|
||||||
|
SERVERS = {}
|
||||||
|
|
||||||
|
|
||||||
|
with open(environ.get('CONFIG_FILE', 'risotto.conf'), 'r') as fh:
|
||||||
|
RISOTTO_CONFIG = toml_load(fh)
|
||||||
|
|
||||||
|
|
||||||
def _(s):
|
def _(s):
|
||||||
|
@ -13,3 +28,80 @@ def multi_function(function):
|
||||||
if name not in MULTI_FUNCTIONS:
|
if name not in MULTI_FUNCTIONS:
|
||||||
MULTI_FUNCTIONS.append(name)
|
MULTI_FUNCTIONS.append(name)
|
||||||
return function
|
return function
|
||||||
|
|
||||||
|
|
||||||
|
async def value_pprint(dico, config):
|
||||||
|
pprint_dict = {}
|
||||||
|
for path, value in dico.items():
|
||||||
|
if await config.option(path).option.type() == 'password' and value:
|
||||||
|
value = 'X' * len(value)
|
||||||
|
pprint_dict[path] = value
|
||||||
|
pprint(pprint_dict)
|
||||||
|
|
||||||
|
|
||||||
|
def load_zones_server():
|
||||||
|
if ZONES_SERVER:
|
||||||
|
return
|
||||||
|
with open('servers.json', 'r') as server_fh:
|
||||||
|
ZONES_SERVER.update(load(server_fh))
|
||||||
|
|
||||||
|
|
||||||
|
def load_zones():
|
||||||
|
global ZONES
|
||||||
|
if ZONES:
|
||||||
|
return
|
||||||
|
|
||||||
|
load_zones_server()
|
||||||
|
ZONES.update(ZONES_SERVER['zones'])
|
||||||
|
for server_name, server in ZONES_SERVER['servers'].items():
|
||||||
|
if 'informations' not in server:
|
||||||
|
continue
|
||||||
|
server_zones = server['informations']['zones_name']
|
||||||
|
server_extra_domainnames = server['informations'].get('extra_domainnames', [])
|
||||||
|
if len(server_zones) > 1 and len(server_zones) != len(server_extra_domainnames) + 1:
|
||||||
|
raise Exception(f'the server "{server_name}" has more that one zone, please set correct number of extra_domainnames ({len(server_zones) - 1} instead of {len(server_extra_domainnames)})')
|
||||||
|
|
||||||
|
for idx, zone_name in enumerate(server_zones):
|
||||||
|
zone_domain_name = ZONES[zone_name]['domain_name']
|
||||||
|
if idx == 0:
|
||||||
|
zone_server_name = server_name
|
||||||
|
else:
|
||||||
|
zone_server_name = server_extra_domainnames[idx - 1]
|
||||||
|
server_domain_name = zone_server_name.split('.', 1)[1]
|
||||||
|
if zone_domain_name and zone_domain_name != server_domain_name:
|
||||||
|
raise Exception(f'wrong server_name "{zone_server_name}" in zone "{zone_name}" should ends with "{zone_domain_name}"')
|
||||||
|
ZONES[zone_name].setdefault('hosts', []).append(server_name)
|
||||||
|
|
||||||
|
|
||||||
|
def load_domains():
|
||||||
|
global DOMAINS
|
||||||
|
if DOMAINS:
|
||||||
|
return
|
||||||
|
load_zones()
|
||||||
|
for zone_name, zone in ZONES_SERVER['zones'].items():
|
||||||
|
if 'domain_name' in zone:
|
||||||
|
hosts = []
|
||||||
|
ips = []
|
||||||
|
for host in ZONES[zone_name].get('hosts', []):
|
||||||
|
hosts.append(host.split('.', 1)[0])
|
||||||
|
ips.append(_get_ip(host, [zone_name], 0))
|
||||||
|
DOMAINS[zone['domain_name']] = (tuple(hosts), tuple(ips))
|
||||||
|
|
||||||
|
|
||||||
|
def _get_ip(server_name: str,
|
||||||
|
zones_name: List[str],
|
||||||
|
index: str,
|
||||||
|
) -> str:
|
||||||
|
if server_name is None or zones_name is None:
|
||||||
|
return
|
||||||
|
load_zones()
|
||||||
|
index = int(index)
|
||||||
|
zone_name = zones_name[index]
|
||||||
|
if zone_name not in ZONES:
|
||||||
|
raise ValueError(f"cannot set IP in unknown zone '{zone_name}'")
|
||||||
|
zone = ZONES[zone_name]
|
||||||
|
if server_name not in zone['hosts']:
|
||||||
|
raise ValueError(f"cannot set IP in unknown server '{server_name}'")
|
||||||
|
server_index = zone['hosts'].index(server_name)
|
||||||
|
# print(server_name, zones_name, index, str(ip_address(zone['start_ip']) + server_index))
|
||||||
|
return str(ip_address(zone['start_ip']) + server_index)
|
||||||
|
|
Loading…
Reference in a new issue