only one config

This commit is contained in:
Emmanuel Garette 2022-08-21 19:03:38 +02:00
parent 231125be0c
commit e3bca44f3a
7 changed files with 622 additions and 615 deletions

View file

@ -6,9 +6,10 @@ from os.path import isdir, join
from shutil import rmtree
from copy import copy
from risotto.utils import CONFIGS, RISOTTO_CONFIG, SERVERS
from risotto.image import load
from risotto.machine import templates
from risotto.utils import RISOTTO_CONFIG, SERVERS
#from risotto.image import load
from risotto.machine import templates, load, ROUGAIL_NAMESPACE
from rougail.utils import normalize_family
INSTALL_DIR = RISOTTO_CONFIG['directories']['dest']
@ -34,53 +35,84 @@ async def main():
if isdir(INSTALL_DIR):
rmtree(INSTALL_DIR)
makedirs(INSTALL_DIR)
module_infos = await load(display_name=tiramisu_display_name, clean_directories=True, copy_manual_dir=True)
try:
module_infos, rougailconfig, config = await load(display_name=tiramisu_display_name,
clean_directories=True,
copy_manual_dir=True,
copy_tests=True,
)
except Exception as err:
# import traceback
# traceback.print_exc()
print(err)
exit(1)
modules_done = []
for server_name in SERVERS:
module_name = CONFIGS[server_name]['module_name']
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)
module_name = SERVERS[server_name]['module']
module_info = module_infos[module_name]
subconfig = config.option(normalize_family(server_name))
try:
add_srv = await subconfig.option('machine.add_srv').value.get()
except AttributeError:
add_srv = False
rougailconfig['tmp_dir'] = 'tmp'
rougailconfig['destinations_dir'] = join(INSTALL_DIR, module_name, CONFIG_DEST_DIR, server_name)
rougailconfig['templates_dir'] = module_info['infos'].templates_dir
if module_name == 'host':
tmpfile = await subconfig.option(f'{ROUGAIL_NAMESPACE}.host_install_dir').value.get()
rougailconfig['tmpfile_dest_dir'] = f'{tmpfile}/host/configurations/{server_name}'
rougailconfig['default_systemd_directory'] = '/usr/local/lib/systemd'
else:
rougailconfig['tmpfile_dest_dir'] = '/usr/local/lib'
rougailconfig['default_systemd_directory'] = '/systemd'
# cfg['templates_dir'] = module_info['infos'].templates_dir
if isdir('tmp'):
rmtree('tmp')
makedirs(cfg['tmp_dir'])
makedirs(cfg['destinations_dir'])
makedirs(rougailconfig['tmp_dir'])
makedirs(rougailconfig['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 templates(server_name,
subconfig,
rougailconfig,
srv=srv,
)
#
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
await subconfig.option('general.hide_secret').value.set(True)
except AttributeError as err:
# print(err)
pass
await config.property.read_only()
for server_name in SERVERS:
config = CONFIGS[server_name]['config']
await config.value.dict()
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():
rougailconfig['destinations_dir'] = join(INSTALL_DIR, module_name, CONFIG_DIFF_DIR, server_name)
rmtree('tmp')
makedirs(rougailconfig['tmp_dir'])
makedirs(rougailconfig['destinations_dir'])
await templates(server_name,
subconfig,
rougailconfig,
)
await config.property.read_write()
try:
await subconfig.option('general.hide_secret').value.set(False)
except AttributeError as err:
pass
await config.property.read_only()
#
if module_name not in modules_done:
rougailconfig['destinations_dir'] = join(INSTALL_DIR, module_name, CONFIG_ORI_DIR)
rmtree('tmp')
makedirs(rougailconfig['tmp_dir'])
makedirs(rougailconfig['destinations_dir'])
await templates(server_name,
subconfig,
rougailconfig,
just_copy=True,
)
modules_done.append(module_name)
with open(join(INSTALL_DIR, module_name, 'install_machines'), 'w') as fh:
for idx, server_name in enumerate(cfg['infos'].servers):
if not idx:
destinations_dir = join(INSTALL_DIR, module_name, CONFIG_ORI_DIR)
makedirs(destinations_dir)
CONFIGS[server_name]['templates_informations']['destinations_dir'] = destinations_dir
await templates(server_name,
**CONFIGS[server_name],
just_copy=True,
)
fh.write(f'./install_machine {module_name} {server_name}\n')
fh.write(f'./install_machine {module_name} {server_name}\n')
run(main())

View file

@ -5,7 +5,7 @@ from secrets import token_urlsafe as _token_urlsafe
from rougail.utils import normalize_family
from risotto.utils import multi_function, DOMAINS, ZONES, load_zones, load_zones_server, load_domains, ZONES_SERVER
from risotto.utils import multi_function, DOMAINS, ZONES, load_zones, load_zones_server, load_domains, SERVERS_JSON
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
@ -19,7 +19,7 @@ def get_chain(authority_cn: str,
):
if hide:
return "XXXXX"
if not authority_name or authority_name is None:
if not authority_cn or not authority_name or authority_name is None:
if isinstance(authority_name, list):
return []
return
@ -142,7 +142,7 @@ def get_internal_zones() -> List[str]:
def get_zones_info(type: str) -> str:
load_zones_server()
ret = []
for data in ZONES_SERVER['zones'].values():
for data in SERVERS_JSON['zones'].values():
ret.append(data[type])
return ret
@ -160,19 +160,4 @@ def get_internal_zone_information(zone: str,
return ZONES[zone]['gateway'] + '/' + ZONES[zone]['network'].split('/')[-1]
return ZONES[zone][info]
def get_internal_info_in_zone(zone: str,
auto: bool,
type: str,
index: int=None,
) -> List[str]:
if not auto:
return
for domain_name, domain in DOMAINS.items():
if zone == domain_name:
if type == 'host':
return list(domain[0])
else:
return domain[1][index]
# =============================================================

View file

@ -1,25 +1,30 @@
from shutil import copy2, copytree, rmtree
from os import listdir, makedirs
from os.path import join, isdir, isfile
from os.path import join, isdir, isfile, dirname
from yaml import load as yaml_load, SafeLoader
from json import load as json_load
from .utils import CONFIGS, RISOTTO_CONFIG, SERVERS, ZONES_SERVER, value_pprint
from .machine import load_machine_config
#
from rougail import RougailConfig # , RougailConvert
#
from .utils import RISOTTO_CONFIG, SERVERS, MULTI_FUNCTIONS
FUNCTIONS = 'funcs.py'
FUNCTIONS_FILE = 'funcs.py'
class ModuleCfg():
def __init__(self):
def __init__(self, module_name):
self.module_name = module_name
self.dictionaries_dir = []
self.modules = []
self.functions_file = [FUNCTIONS]
self.functions_file = [FUNCTIONS_FILE]
self.templates_dir = []
self.extra_dictionaries = {}
self.servers = []
def __repr__(self):
return str(vars(self))
def list_applications() -> dict:
"""
@ -40,8 +45,6 @@ def list_applications() -> dict:
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):
@ -55,12 +58,13 @@ def applicationservice_copy(src_file: str,
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)
dst = dirname(dst_file)
if not isdir(dst):
makedirs(dst)
if isfile(src_file):
copy2(src_file, dst_file)
else:
copytree(src, dst)
copytree(src_file, dst_file)
def load_applicationservice_cfg(appname: str,
@ -68,6 +72,7 @@ def load_applicationservice_cfg(appname: str,
install_dir: str,
cfg: ModuleCfg,
copy_manual_dir: bool,
copy_tests: bool,
) -> None:
cfg.modules.append(appname)
# dictionaries
@ -109,8 +114,16 @@ def load_applicationservice_cfg(appname: str,
applicationservice_copy(src_file,
dst_file,
copy_if_not_exists,
manual_dir,
filename,
)
if copy_tests:
tests_dir = join(as_dir, 'tests')
if isdir(tests_dir):
for filename in listdir(tests_dir):
src_file = join(tests_dir, filename)
dst_file = join(install_dir, 'tests', filename)
applicationservice_copy(src_file,
dst_file,
False,
)
@ -120,10 +133,12 @@ def load_applicationservice(appname: str,
cfg: ModuleCfg,
applications: dict,
copy_manual_dir: bool,
copy_tests: bool,
providers: dict,
suppliers: dict,
) -> None:
if appname not in applications:
raise Exception(f'cannot find application dependency "{appname}" in application "{module_name}"')
raise Exception(f'cannot find application dependency "{appname}"')
as_dir = applications[appname]
applicationservice_file = join(as_dir, 'applicationservice.yml')
if not isfile(applicationservice_file):
@ -133,6 +148,7 @@ def load_applicationservice(appname: str,
install_dir,
cfg,
copy_manual_dir,
copy_tests,
)
added.append(appname)
with open(applicationservice_file) as yaml:
@ -142,6 +158,11 @@ def load_applicationservice(appname: str,
providers.setdefault(provider, [])
if appname not in providers[provider]:
providers[provider].append(appname)
supplier = app.get('supplier')
if supplier:
suppliers.setdefault(supplier, [])
if appname not in suppliers[supplier]:
suppliers[supplier].append(appname)
for xml in app.get('depends', []):
if xml in added:
continue
@ -151,17 +172,22 @@ def load_applicationservice(appname: str,
cfg,
applications,
copy_manual_dir,
copy_tests,
providers,
suppliers,
)
def load_image_informations(install_dir: str,
def load_image_informations(module_name: str,
install_dir: str,
datas: dict,
applications: dict,
copy_manual_dir: bool,
copy_tests: bool,
providers: dict,
suppliers: dict,
) -> ModuleCfg:
cfg = ModuleCfg()
cfg = ModuleCfg(module_name)
added = []
for applicationservice in datas['applicationservices']:
load_applicationservice(applicationservice,
@ -170,53 +196,35 @@ def load_image_informations(install_dir: str,
cfg,
applications,
copy_manual_dir,
copy_tests,
providers,
suppliers,
)
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):
async def valid_mandatories(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)}"')
server_name = None
for mandatory in mandatories:
path_server_name, path = mandatory.split('.', 1)
var_server_name = await config.option(path_server_name).option.description()
if server_name != var_server_name:
server_name = var_server_name
print()
print(f'=== Missing variables for {server_name} ===')
print(f' - {path}')
# await config.property.pop('mandatory')
# await value_pprint(await config.value.dict(), config)
exit(1)
#raise Exception('configuration has mandatories variables without values')
def load_config(copy_manual_dir=False, clean_directories=False):
def load_config(copy_manual_dir=False,
copy_tests=False,
clean_directories=False,
):
module_infos = {}
applications = list_applications()
with open('servers.json', 'r') as server_fh:
@ -225,74 +233,46 @@ def load_config(copy_manual_dir=False, clean_directories=False):
modules = jsonfile['modules']
for module_name, datas in modules.items():
providers = {}
suppliers = {}
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] = {'infos': load_image_informations(install_dir,
module_infos[module_name] = {'infos': load_image_informations(module_name,
install_dir,
datas,
applications,
copy_manual_dir,
copy_tests,
providers,
suppliers,
),
'providers': providers,
'suppliers': suppliers,
'install_dir': install_dir,
}
return module_infos
async def load(display_name=None,
clean_directories=False,
copy_manual_dir=False,
hide_secret=False,
):
# load images
module_infos = load_config(copy_manual_dir, clean_directories)
# load machines
ZONES_SERVER['providers'] = {}
for server_name, datas in SERVERS.items():
if server_name in CONFIGS:
raise Exception(f'server "{server_name}" is duplicate')
module_info = module_infos[datas['module']]
CONFIGS[server_name] = await load_machine_config(server_name,
datas,
module_info,
display_name=display_name,
)
if module_info['providers'] and 'informations' in datas and 'zones_name' in datas['informations']:
for zone_idx, zone_name in enumerate(datas['informations']['zones_name']):
if not zone_idx:
sname = server_name
else:
sname = datas['informations']['extra_domainnames'][zone_idx - 1]
ZONES_SERVER['providers'].setdefault(zone_name, {})
for provider in module_info['providers']:
ZONES_SERVER['providers'][zone_name].setdefault(provider, []).append(sname)
# 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
#
#
def load_module_config(module_name: str,
module_info: dict,
):
cfg = RougailConfig.copy()
cfg['variable_namespace'] = ROUGAIL_NAMESPACE
cfg['variable_namespace_description'] = ROUGAIL_NAMESPACE_DESCRIPTION
if module_name == 'host':
#FIXME server_name == host ?
#FIXME cfg['tmpfile_dest_dir'] = datas['values'][f'{ROUGAIL_NAMESPACE}.host_install_dir'] + '/host/configurations/host'
cfg['default_systemd_directory'] = '/usr/local/lib/systemd'
cfg['templates_dir'] = module_info['infos'].templates_dir
cfg['dictionaries_dir'] = module_info['infos'].dictionaries_dir
cfg['functions_file'] = module_info['infos'].functions_file
cfg['multi_functions'] = MULTI_FUNCTIONS
cfg['extra_dictionaries'] = module_info['infos'].extra_dictionaries
cfg['extra_annotators'] = ['risotto.rougail']
cfg['internal_functions'] = list(FUNCTIONS.keys())
cfg['force_convert_dyn_option_description'] = True
cfg['module_name'] = module_name
#cfg['patches_dir'] = join(test_dir, 'patches')
return cfg

View file

@ -1,342 +1,31 @@
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 .utils import SERVERS, SERVERS_JSON, MULTI_FUNCTIONS, load_domains
from .image import load_config, valid_mandatories # , load_modules_rougail_config
from rougail import RougailConfig, RougailConvert
from .utils import MULTI_FUNCTIONS, CONFIGS, DOMAINS, value_pprint
from .rougail.annotator import calc_providers, calc_providers_global, calc_providers_dynamic, calc_providers_dynamic_follower, calc_providers_follower
from os import makedirs
#
from tiramisu import Config
from .utils import value_pprint
from rougail.utils import normalize_family
from rougail import RougailSystemdTemplate
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:
#
#
async def set_values(server_name, config, datas):
if 'values' not in datas:
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():
server_path = normalize_family(server_name)
for vpath, value in datas['values'].items():
path = f'{server_path}.{vpath}'
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 f'linked_value_{index}' not in kwargs:
# value is disabled
continue
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')
if isinstance(value, dict):
for idx, val in value.items():
await config.option(path, int(idx)).value.set(val)
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()
await config.option(path).value.set(value)
except Exception as err:
await value_pprint(await config.value.dict(), config)
error_msg = f'cannot configure variable {vpath} for server "{server_name}": {err}'
raise Exception(error_msg) from err
def get_ip_from_domain(domain):
@ -344,59 +33,18 @@ def get_ip_from_domain(domain):
return
hostname, domainname = domain.split('.', 1)
return DOMAINS[domainname][1][DOMAINS[domainname][0].index(hostname)]
return optiondescription['option_0']
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['infos'].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['infos'].templates_dir
cfg['dictionaries_dir'] = module_info['infos'].dictionaries_dir
cfg['functions_file'] = module_info['infos'].functions_file
cfg['multi_functions'] = MULTI_FUNCTIONS
cfg['extra_dictionaries'] = module_info['infos'].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['infos'].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,
'providers': module_info['providers'],
}
ROUGAIL_NAMESPACE = 'general'
ROUGAIL_NAMESPACE_DESCRIPTION = 'Général'
FUNCTIONS = {'get_ip_from_domain': get_ip_from_domain,
'calc_providers': calc_providers,
'calc_providers_global': calc_providers_global,
'calc_providers_dynamic': calc_providers_dynamic,
'calc_providers_dynamic_follower': calc_providers_dynamic_follower,
'calc_providers_follower': calc_providers_follower,
}
async def templates(server_name,
@ -404,24 +52,100 @@ async def templates(server_name,
templates_informations,
srv=False,
just_copy=False,
**kwargs,
):
values = await config.value.dict()
engine = RougailSystemdTemplate(config, templates_informations)
if just_copy:
# for all engine to none
ori_engines = {}
for eng in engine.engines:
if eng != 'none':
engine.engines[eng] = engine.engines['none']
# if server_name == 'dovecot.in.silique.fr':
# print()
# print(f'=== Configuration: {server_name} ===')
# pprint(values)
if eng == 'none':
continue
ori_engines[eng] = engine.engines[eng]
engine.engines[eng] = engine.engines['none']
try:
await engine.instance_files()
except Exception as err:
print()
print(f'=== Configuration: {server_name} ===')
values = await config.value.dict()
await value_pprint(values, config)
raise err from err
print(err)
print(await config.option('general.nginx.nginx_default_http').value.get())
exit(1)
#raise err from err
if just_copy:
for eng, old_engine in ori_engines.items():
engine.engines[eng] = old_engine
if srv:
makedirs(srv)
async def load(display_name=None,
clean_directories=False,
copy_manual_dir=False,
copy_tests=False,
hide_secret=False,
):
#load_zones()
# # load images
#FIXME useful
module_infos = load_config(copy_manual_dir,
copy_tests,
clean_directories,
)
# modules_rougail_config = load_modules_rougail_config(module_infos)
cfg = RougailConfig.copy()
cfg['variable_namespace'] = ROUGAIL_NAMESPACE
cfg['variable_namespace_description'] = ROUGAIL_NAMESPACE_DESCRIPTION
cfg['multi_functions'] = MULTI_FUNCTIONS
cfg['extra_annotators'] = ['risotto.rougail']
cfg['internal_functions'] = list(FUNCTIONS.keys())
cfg['force_convert_dyn_option_description'] = True
# cfg['module_name'] = module_name
functions_files = set()
load_domains()
for server_name, datas in SERVERS.items():
module_info = module_infos[datas['module']]
functions_files |= set(module_info['infos'].functions_file)
cfg['functions_file'] = list(functions_files)
eolobj = RougailConvert(cfg)
cfg['risotto_globals'] = {}
for server_name, datas in SERVERS.items():
module_info = module_infos[datas['module']]
cfg['dictionaries_dir'] = module_info['infos'].dictionaries_dir
cfg['extra_dictionaries'] = module_info['infos'].extra_dictionaries
informations = SERVERS_JSON['servers'][server_name].get('informations')
if informations:
cfg['risotto_globals'][server_name] = {'global:server_name': server_name,
'global:zones_name': informations['zones_name'],
'global:zones_list': list(range(len(informations['zones_name']))),
}
values = []
for s_idx in cfg['risotto_globals'][server_name]['global:zones_list']:
if not s_idx:
values.append(server_name)
else:
values.append(informations['extra_domainnames'][s_idx - 1])
cfg['risotto_globals'][server_name]['global:server_names'] = values
else:
cfg['risotto_globals'][server_name] = {'global:server_name': server_name}
eolobj.load_dictionaries(path_prefix=server_name)
tiram_obj = eolobj.save(None)
optiondescription = FUNCTIONS.copy()
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.pop('validator')
await config.property.pop('cache')
for server_name, datas in SERVERS.items():
await set_values(server_name, config, datas)
await config.property.read_only()
await config.property.add('cache')
await valid_mandatories(config)
return module_infos, cfg, config

View file

@ -1,5 +1,122 @@
from rougail.annotator.variable import Walk
from risotto.utils import _
from risotto.utils import _, multi_function
from warnings import warn
def _parse_kwargs(provider, dns, kwargs, index=None):
values = {}
for key, value in kwargs.items():
if '_' not in key:
raise Exception(f'unknown attribute {key} in calc_providers_global with provider {provider}')
k, idx = key.rsplit('_', 1)
values.setdefault(idx, {})[k] = value
for idx, data in values.items():
if index is not None and int(idx) != index:
continue
if 'dns' not in data or (isinstance(data['dns'], list) and dns not in data['dns']) or (not isinstance(data['dns'], list) and data['dns'] != dns):
continue
del data['dns']
yield data
@multi_function
def calc_providers_global(provider, multi, value, suffix=None):
if suffix is not None:
return value[int(suffix)]
return value
@multi_function
def calc_providers_follower(provider, multi, dns, leader, index, **kwargs):
ret = []
for data in _parse_kwargs(provider, dns, kwargs):
if 'value' not in data:
continue
if 'leader' in data:
if isinstance(data['leader'], list):
for idx, leader_iter in enumerate(data['leader']):
if leader_iter == leader:
ret.append(data['value'][idx])
elif data['leader']== leader:
ret.extend(data['value'])
else:
if isinstance(data['value'], list):
for v in data['value']:
if v not in ret:
ret.append(v)
elif data['value'] not in ret:
ret.append(data['value'])
if multi:
return ret
if ret:
return ret[0]
@multi_function
def calc_providers_dynamic_follower(provider, multi, dns, leader, index, suffix, **kwargs):
ret = []
for data in _parse_kwargs(provider, dns, kwargs):
if 'value' not in data:
continue
if data['dynamic'] != suffix:
continue
if 'leader' in data:
for idx, leader_iter in enumerate(data['leader']):
if leader_iter == leader:
if isinstance(data['value'], list):
ret.append(data['value'][idx])
else:
ret.append(data['value'])
else:
if isinstance(data['value'], list):
for v in data['value']:
if v not in ret:
ret.append(v)
elif data['value'] not in ret:
ret.append(data['value'])
if multi:
return ret
if ret:
return ret[0]
@multi_function
def calc_providers_dynamic(provider, multi, dns, suffix, **kwargs):
ret = []
for data in _parse_kwargs(provider, dns, kwargs):
if 'value' not in data:
continue
if data['dynamic'] != suffix:
continue
if isinstance(data['value'], list):
for v in data['value']:
if v not in ret:
ret.append(v)
elif data['value'] not in ret:
ret.append(data['value'])
if multi:
return ret
if ret:
return ret[0]
@multi_function
def calc_providers(provider, multi, dns, suffix=None, **kwargs):
ret = []
for data in _parse_kwargs(provider, dns, kwargs):
if isinstance(data['value'], list):
for v in data['value']:
if v in ret:
continue
ret.append(v)
elif data['value'] not in ret:
ret.append(data['value'])
if multi:
return ret
if ret:
return ret[0]
class Annotator(Walk):
@ -10,48 +127,217 @@ class Annotator(Walk):
self.objectspace = objectspace
# self.convert_get_linked_information()
# self.convert_provider()
self.set_suppliers()
self.convert_providers()
self.convert_suppliers()
def convert_get_linked_information(self):
if not hasattr(self.objectspace.space, 'constraints') or \
not hasattr(self.objectspace.space.constraints, 'fill'):
return
for fill in self.objectspace.space.constraints.fill:
if fill.name == 'get_linked_configuration':
# add server_name
param = self.objectspace.param(fill.xmlfiles)
param.name = 'server_name'
param.type = 'information'
param.text = 'server_name'
fill.param.append(param)
# add current_user
param = self.objectspace.param(fill.xmlfiles)
param.name = 'current_user'
param.type = 'information'
param.text = 'current_user'
fill.param.append(param)
# add test
param = self.objectspace.param(fill.xmlfiles)
param.name = 'test'
param.type = 'target_information'
param.text = 'test'
fill.param.append(param)
def convert_provider(self):
if not hasattr(self.objectspace.space, 'variables'):
return
for family in self.get_families():
if not hasattr(family, 'provider'):
def set_suppliers(self) -> dict:
""" get supplier informations
return something like:
{'Host': ['host1.example.net', 'host2.example.net']}
"""
self.suppliers = {}
for variable in self.get_variables():
if not hasattr(variable, 'supplier') or ':' in variable.supplier:
continue
if 'dynamic' not in vars(family):
raise Exception(_(f'{family.name} is not a dynamic family so cannot have provider attribute'))
if not hasattr(family, 'information'):
family.information = self.objectspace.information(family.xmlfiles)
family.information.provider = family.provider
del family.provider
nf_dns = variable.path.split('.', 1)[0]
server_name = self.objectspace.space.variables[nf_dns].doc
self.suppliers.setdefault(variable.supplier, []).append({'option': variable, 'dns': server_name, 'path_prefix': nf_dns, 'zones': set(self.objectspace.rougailconfig['risotto_globals'][server_name]['global:zones_name'])})
def convert_suppliers(self):
for supplier, data in self.suppliers.items():
if supplier == 'Host':
continue
for s_dico in data:
if supplier not in self.providers:
continue
for p_dico in self.providers[supplier]:
if s_dico['zones'] & p_dico['zones']:
s_dico['option'].value = p_dico['dns']
new_value = self.objectspace.value(None)
new_value.name = p_dico['dns']
s_dico['option'].value = [new_value]
break
def convert_providers(self):
self.providers = {}
for variable in self.get_variables():
if not hasattr(variable, 'provider'):
continue
if not hasattr(variable, 'information'):
variable.information = self.objectspace.information(variable.xmlfiles)
variable.information.provider = variable.provider
del variable.provider
nf_dns = variable.path.split('.', 1)[0]
server_name = self.objectspace.space.variables[nf_dns].doc
provider_name = variable.provider
if ':' in provider_name:
key_name, key_type = provider_name.rsplit(':', 1)
is_provider = False
else:
key_name = key_type = provider_name
is_provider = True
if provider_name != 'Host':
self.providers.setdefault(provider_name, []).append({'option': variable, 'dns': server_name, 'path_prefix': nf_dns, 'zones': set(self.objectspace.rougailconfig['risotto_globals'][server_name]['global:zones_name'])})
if key_name != 'global' and key_name not in self.suppliers:
#warn(f'cannot find supplier "{key_name}" for "{server_name}"')
continue
# create a fill for this variable
fill = self.objectspace.fill(variable.xmlfiles)
new_target = self.objectspace.target(variable.xmlfiles)
new_target.name = variable
fill.target = [new_target]
if key_name == 'global':
fill.name = 'calc_providers_global'
elif self.objectspace.paths.is_dynamic(variable):
if self.objectspace.paths.is_follower(variable):
fill.name = 'calc_providers_dynamic_follower'
else:
fill.name = 'calc_providers_dynamic'
elif self.objectspace.paths.is_follower(variable):
fill.name = 'calc_providers_follower'
else:
fill.name = 'calc_providers'
fill.namespace = variable.namespace
fill.index = 0
# first parameter: the provider name (something link Host:incoming_ports)
param = self.objectspace.param(variable.xmlfiles)
param.name = 'provider'
param.text = provider_name
fill.param = [param]
# second parameter: current variable is a multi variable?
param = self.objectspace.param(variable.xmlfiles)
param.name = 'multi'
param.text = variable.multi
param.type = 'boolean'
fill.param.append(param)
if self.objectspace.paths.is_follower(variable):
param = self.objectspace.param(variable.xmlfiles)
param.name = 'leader'
param.text = self.objectspace.paths.get_leader(variable)
param.propertyerror = False
param.type = 'variable'
fill.param.append(param)
try:
leader_provider = self.objectspace.paths.get_leader(variable).provider
except:
leader_provider = None
#
param = self.objectspace.param(variable.xmlfiles)
param.name = 'index'
param.type = 'index'
fill.param.append(param)
if self.objectspace.paths.is_dynamic(variable):
# if dynamic: current suffix
# and add current DNS name, this is useful to known if supplier is link to this provider
param = self.objectspace.param(variable.xmlfiles)
param.name = 'suffix'
param.type = 'suffix'
fill.param.append(param)
if key_name != 'global':
param = self.objectspace.param(variable.xmlfiles)
param.name = 'dns'
param.text = server_name
fill.param.append(param)
if key_name == 'global':
param = self.objectspace.param(variable.xmlfiles)
param.text = self.objectspace.rougailconfig['risotto_globals'][server_name][provider_name]
param.name = 'value'
fill.param.append(param)
else:
# parse all supplier link to current provider
for idx, data in enumerate(self.suppliers[key_name]):
option = data['option']
dns = data['dns']
# if not provider, get the true option that we want has value
if not is_provider:
path_prefix = data['path_prefix']
try:
supplier_option = self.objectspace.paths.get_supplier(f'supplier:{provider_name}', path_prefix)
except KeyError:
#warn(f'cannot find supplier "{provider_name}" for "{dns}"')
continue
# first of all, get the supplier name
param = self.objectspace.param(variable.xmlfiles)
param.name = f'dns_{idx}'
param.text = option
param.propertyerror = False
param.type = 'variable'
fill.param.append(param)
if not is_provider and \
self.objectspace.paths.is_follower(variable):
param = self.objectspace.param(variable.xmlfiles)
param.name = f'leader_{idx}'
if fill.name == 'calc_providers_follower':
param.text = dns
else:
if self.objectspace.paths.is_follower(supplier_option):
param.text = self.objectspace.paths.get_leader(supplier_option)
else:
param.text = self.objectspace.paths.get_supplier(f'supplier:{leader_provider}', path_prefix)
param.propertyerror = False
param.type = 'variable'
fill.param.append(param)
# get the current DNS name for dynamic variable
if self.objectspace.paths.is_dynamic(variable):
param = self.objectspace.param(variable.xmlfiles)
param.name = f'dynamic_{idx}'
param.text = dns
fill.param.append(param)
# get the current value!
param = self.objectspace.param(variable.xmlfiles)
param.name = f'value_{idx}'
if is_provider:
param.text = dns
else:
param.text = supplier_option
param.propertyerror = False
param.type = 'variable'
fill.param.append(param)
if not hasattr(self.objectspace.space.variables[nf_dns], 'constraints'):
self.objectspace.space.variables[nf_dns].constraints = self.objectspace.constraints(None)
if not hasattr(self.objectspace.space.variables[nf_dns].constraints, 'fill'):
self.objectspace.space.variables[nf_dns].constraints.fill = []
self.objectspace.space.variables[nf_dns].constraints.fill.append(fill)
# def convert_get_linked_information(self):
# if not hasattr(self.objectspace.space, 'constraints') or \
# not hasattr(self.objectspace.space.constraints, 'fill'):
# return
# for fill in self.objectspace.space.constraints.fill:
# if fill.name == 'get_linked_configuration':
# # add server_name
# param = self.objectspace.param(fill.xmlfiles)
# param.name = 'server_name'
# param.type = 'information'
# param.text = 'server_name'
# fill.param.append(param)
# # add current_user
# param = self.objectspace.param(fill.xmlfiles)
# param.name = 'current_user'
# param.type = 'information'
# param.text = 'current_user'
# fill.param.append(param)
# # add test
# param = self.objectspace.param(fill.xmlfiles)
# param.name = 'test'
# param.type = 'target_information'
# param.text = 'test'
# fill.param.append(param)
#
# def convert_provider(self):
# if not hasattr(self.objectspace.space, 'variables'):
# return
# for family in self.get_families():
# if not hasattr(family, 'provider'):
# continue
# if 'dynamic' not in vars(family):
# raise Exception(_(f'{family.name} is not a dynamic family so cannot have provider attribute'))
# if not hasattr(family, 'information'):
# family.information = self.objectspace.information(family.xmlfiles)
# family.information.provider = family.provider
# del family.provider
# for variable in self.get_variables():
# if not hasattr(variable, 'provider'):
# continue
# if not hasattr(variable, 'information'):
# variable.information = self.objectspace.information(variable.xmlfiles)
# variable.information.provider = variable.provider
# del variable.provider

View file

@ -6,12 +6,13 @@ from toml import load as toml_load
from pprint import pprint
SETTINGS = {'config': None}
MULTI_FUNCTIONS = []
CONFIGS = {}
DOMAINS = {}
ZONES = {}
ZONES_SERVER = {}
SERVERS_JSON = {}
SERVERS = {}
CONFIGS = {}
with open(environ.get('CONFIG_FILE', 'risotto.conf'), 'r') as fh:
@ -40,10 +41,10 @@ async def value_pprint(dico, config):
def load_zones_server():
if 'zones' in ZONES_SERVER:
if 'zones' in SERVERS_JSON:
return
with open('servers.json', 'r') as server_fh:
ZONES_SERVER.update(load(server_fh))
SERVERS_JSON.update(load(server_fh))
def load_zones():
@ -52,8 +53,8 @@ def load_zones():
return
load_zones_server()
ZONES.update(ZONES_SERVER['zones'])
for server_name, server in ZONES_SERVER['servers'].items():
ZONES.update(SERVERS_JSON['zones'])
for server_name, server in SERVERS_JSON['servers'].items():
if 'informations' not in server:
continue
server_zones = server['informations']['zones_name']
@ -78,7 +79,7 @@ def load_domains():
if DOMAINS:
return
load_zones()
for zone_name, zone in ZONES_SERVER['zones'].items():
for zone_name, zone in SERVERS_JSON['zones'].items():
if 'domain_name' in zone:
hosts = []
ips = []
@ -103,5 +104,4 @@ def _get_ip(server_name: str,
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)

View file

@ -155,7 +155,7 @@ def gen_cert_iter(cn,
raise Exception(f'cannot find CA file "{cert_ca_name}"')
if not isfile(cert_name):
if not isfile(key_ca_name):
raise Exception(f"cannot find CA private key (\"{authority_cn}\") to sign certificat for \"{cn}\", is it a Let's Encrypt certification?")
raise Exception(f"cannot find CA private key (\"{authority_cn}\") to sign certificat for \"{cn}\" ({key_ca_name}), is it a Let's Encrypt certification?")
if not isdir(dir_name):
makedirs(dir_name)
if isfile(sn_name):