diff --git a/src/rougail/update.py b/src/rougail/update.py index 6173b47e..19076048 100644 --- a/src/rougail/update.py +++ b/src/rougail/update.py @@ -25,10 +25,13 @@ from os.path import join, isfile, basename from os import listdir from lxml.etree import DTD, parse, XMLParser, XMLSyntaxError # pylint: disable=E0611 from lxml.etree import Element, SubElement, tostring +from ast import parse as ast_parse from .i18n import _ from .error import UpgradeError +from .utils import normalize_family + VERSIONS = {'creole': ['1'], 'rougail': ['0.9'], @@ -40,7 +43,7 @@ def get_function_name(root, version): return f'update_{root}_{version}' -FUNCTION_VERSIONS = [get_function_name(root, version) for root, versions in VERSIONS.items() for version in versions] +FUNCTION_VERSIONS = [(root, version, get_function_name(root, version)) for root, versions in VERSIONS.items() for version in versions] class RougailUpgrade: @@ -53,6 +56,7 @@ class RougailUpgrade: def load_xml_from_folders(self, srcfolder: str, dstfolder: str, + namespace: str, ): """Loads all the XML files located in the xmlfolders' list @@ -71,10 +75,13 @@ class RougailUpgrade: root = document.getroot() search_function_name = get_function_name(root.tag, root.attrib.get('version', '1')) function_found = False - for function_version in FUNCTION_VERSIONS: + for root_name, version, function_version in FUNCTION_VERSIONS: if function_found and hasattr(self, function_version): + print(f' - convert {filename} to version {version}') upgrade_help = self.upgrade_help.get(function_version, {}).get(filename, {}) - root = getattr(self, function_version)(root, upgrade_help) + if upgrade_help.get('remove') is True: + continue + root = getattr(self, function_version)(root, upgrade_help, namespace) if function_version == search_function_name: function_found = True with open(xmldst, 'wb') as xmlfh: @@ -90,6 +97,7 @@ class RougailUpgrade: def update_rougail_0_9(self, root: 'Element', upgrade_help: dict, + namespace: str, ) -> 'Element': # rename root root.tag = 'rougail' @@ -111,6 +119,9 @@ class RougailUpgrade: if not isinstance(subelement.tag, str): # XML comment continue + if subelement.tag == 'family_action': + root.remove(subelement) + continue for subsubelement in subelement: if not isinstance(subsubelement.tag, str): # XML comment @@ -130,16 +141,27 @@ class RougailUpgrade: if subsubsubelement.attrib['name'] in upgrade_help.get('variables', {}).get('hidden', {}).get('remove', []): self.remove(subsubsubelement, 'hidden', optional=True) if subsubsubelement.attrib['name'] in upgrade_help.get('variables', {}).get('mandatory', {}).get('remove', []): - self.remove(subsubsubelement, 'mandatory', optional=True) - variables[subsubsubelement.attrib['name']] = subsubsubelement + self.remove(subsubsubelement, 'mandatory') + if subsubsubelement.attrib['name'] in upgrade_help.get('variables', {}).get('mandatory', {}).get('add', []): + subsubsubelement.attrib['mandatory'] = 'True' + if subsubsubelement.attrib['name'] in upgrade_help.get('variables', {}).get('type', {}): + subsubsubelement.attrib['type'] = upgrade_help.get('variables', {}).get('type', {})[subsubsubelement.attrib['name']] + if namespace == 'configuration': + path = subsubsubelement.attrib['name'] + npath = normalize_family(subsubsubelement.attrib['name']) + else: + path = namespace + '.' + subsubelement.attrib['name'] + '.' + subsubsubelement.attrib['name'] + npath = normalize_family(namespace) + '.' + normalize_family(subsubelement.attrib['name']) + '.' + normalize_family(subsubsubelement.attrib['name']) + variables[path] = subsubsubelement + variables[npath] = subsubsubelement type = subsubsubelement.attrib.get('type') if type in ['oui/non', 'yes/no', 'on/off']: - variables_auto_valid_enum.setdefault(subsubsubelement.attrib['type'], []).append(subsubsubelement.attrib['name']) + variables_auto_valid_enum.setdefault(subsubsubelement.attrib['type'], []).append(path) del subsubsubelement.attrib['type'] elif type == 'hostname_strict': subsubsubelement.attrib['type'] = 'hostname' - elif type == 'domain_strict': - subsubsubelement.attrib['type'] = 'domain' + elif type in ['domain', 'domain_strict']: + subsubsubelement.attrib['type'] = 'domainname' elif type == 'string': del subsubsubelement.attrib['type'] if self.test and subsubsubelement.attrib.get('auto_freeze') == 'True': @@ -153,17 +175,36 @@ class RougailUpgrade: subsubsubelement.attrib['remove_check'] = 'True' if subsubsubelement.attrib['name'] in upgrade_help.get('variables', {}).get('mode', {}).get('modify', {}): subsubsubelement.attrib['mode'] = upgrade_help.get('variables', {}).get('mode', {}).get('modify', {})[subsubsubelement.attrib['name']] + if subsubsubelement.attrib['name'] in upgrade_help.get('variables', {}).get('type', {}).get('modify', {}): + subsubsubelement.attrib['type'] = upgrade_help.get('variables', {}).get('type', {}).get('modify', {})[subsubsubelement.attrib['name']] + type = subsubsubelement.attrib['type'] if subsubsubelement.attrib['name'] in upgrade_help.get('variables', {}).get('test', {}): subsubsubelement.attrib['test'] = upgrade_help.get('variables', {}).get('test', {})[subsubsubelement.attrib['name']] -# for value in subsubsubelement: -# if value.text is None: -# value.attrib['type'] = 'nil' + if subsubsubelement.attrib['name'] in upgrade_help.get('variables', {}).get('remove_value', []): + for value in subsubsubelement: + subsubsubelement.remove(value) + if subsubsubelement.attrib['name'] != normalize_family(subsubsubelement.attrib['name']): + if "description" not in subsubsubelement.attrib: + subsubsubelement.attrib['description'] = subsubsubelement.attrib['name'] + subsubsubelement.attrib['name'] = normalize_family(subsubsubelement.attrib['name']) elif subsubsubelement.tag == 'param': + if subsubelement.tag == 'check' and subsubelement.attrib['target'] in upgrade_help.get('check', {}).get('remove', []): + continue type = subsubsubelement.attrib.get('type') if type == 'eole': subsubsubelement.attrib['type'] = 'variable' type = 'variable' - elif type in ('container', 'context', 'python'): + if type == 'python': + subsubsubelement.attrib['type'] = 'function' + if subsubsubelement.text.startswith('range('): + func_ast = ast_parse(subsubsubelement.text) + subsubsubelement.text = 'range' + for arg in func_ast.body[0].value.args: + SubElement(subsubelement, 'param', type='number').text = str(arg.value) + else: + raise Exception(f'{subsubsubelement.text} is not a supported function') + type = 'function' + elif type in ('container', 'context'): raise UpgradeError(_(f'cannot convert param with type "{type}"')) if subsubelement.attrib['name'] == 'valid_entier' and not 'type' in subsubsubelement.attrib: subsubsubelement.attrib['type'] = 'number' @@ -172,10 +213,14 @@ class RougailUpgrade: continue if subsubelement.attrib['name'] == 'valid_enum' and not type: if subsubsubelement.attrib.get('name') == 'checkval': + if subsubelement.attrib['target'] in upgrade_help.get('check', {}).get('valid_enums', {}).get('checkval', {}).get('remove', []): + subsubelement.remove(subsubsubelement) + continue raise UpgradeError(_('checkval in valid_enum is no more supported')) - for val in eval(subsubsubelement.text): - SubElement(subsubelement, 'param').text = str(val) - subsubelement.remove(subsubsubelement) + if subsubsubelement.text.startswith('['): + for val in eval(subsubsubelement.text): + SubElement(subsubelement, 'param').text = str(val) + subsubelement.remove(subsubsubelement) self.move(subsubsubelement, 'hidden', 'propertyerror', optional=True) if type == 'variable' and subsubsubelement.text in upgrade_help.get('variables', {}).get('rename', []): subsubsubelement.text = upgrade_help.get('variables', {}).get('rename', [])[subsubsubelement.text] @@ -189,16 +234,18 @@ class RougailUpgrade: type = subsubsubelement.attrib.get('type') if type in ['service_accesslist', 'service_restrictionlist', 'interfacelist', 'hostlist']: subsubelement.remove(subsubsubelement) -# elif self.test and type in ['filelist', 'variable', 'servicelist']: -# subsubsubelement.attrib['optional'] = "True" elif (not type or type == 'variable') and subsubsubelement.text in upgrade_help.get('variables', {}).get('remove', []): subsubelement.remove(subsubsubelement) elif type == 'family' and subsubsubelement.text in upgrade_help.get('families', {}).get('remove', []): subsubelement.remove(subsubsubelement) - up = upgrade_help.get('targets', {}).get(subsubsubelement.text) - if up: - if 'optional' in up: - subsubsubelement.attrib['optional'] = up['optional'] + elif type == 'actionlist': +# for family in root.find('variables'): +# family_list = SubElement(subsubelement, 'target') +# family_list.attrib['type'] = 'family' +# family_list.text = namespace + '.' + family.attrib['name'] + subsubelement.remove(subsubsubelement) + if upgrade_help.get('targets', {}).get(subsubsubelement.text, {}).get('optional'): + subsubsubelement.attrib['optional'] = upgrade_help.get('targets', {}).get(subsubsubelement.text, {}).get('optional') has_target = False for target in subsubelement: if target.tag == 'target': @@ -208,7 +255,11 @@ class RougailUpgrade: subelement.remove(subsubelement) continue elif subsubsubelement.tag == 'slave': + if subsubsubelement.text in upgrade_help.get('variables', {}).get('remove', []): + subsubelement.remove(subsubsubelement) + continue subsubsubelement.tag = 'follower' + subsubsubelement.text = normalize_family(subsubsubelement.text) if subelement.tag == 'containers': current_service = self.upgrade_container(subsubsubelement, current_service, files, ips, servicelists, upgrade_help) subsubelement.remove(subsubsubelement) @@ -283,6 +334,12 @@ class RougailUpgrade: subelement.remove(subsubelement) else: self.move(subsubelement, 'master', 'leader') + for follower in subsubelement: + if '.' in subsubelement.attrib['leader']: + path = subsubelement.attrib['leader'].rsplit('.', 1)[0] + '.' + follower.text + else: + path = follower.text + self.remove(variables[path], 'multi', optional=True) if subsubelement.attrib['leader'] in upgrade_help.get('groups', {}).get('reorder', {}): for follower in subsubelement: subsubelement.remove(follower) @@ -302,6 +359,10 @@ class RougailUpgrade: continue if subsubelement.attrib['name'] in upgrade_help.get('families', {}).get('hidden', {}).get('add', []): subsubelement.attrib['hidden'] = 'True' + if subsubelement.attrib['name'] != normalize_family(subsubelement.attrib['name']): + if "description" not in subsubelement.attrib: + subsubelement.attrib['description'] = subsubelement.attrib['name'] + subsubelement.attrib['name'] = normalize_family(subsubelement.attrib['name']) families[subsubelement.attrib['name']] = subsubelement # if empty, remove if is_empty(subelement) or subelement.tag in ['containers', 'files', 'help']: @@ -394,7 +455,9 @@ class RougailUpgrade: for name, text in variables_help.items(): variables[name].attrib['help'] = text for name, text in families_help.items(): - families[name].attrib['help'] = text + if name in upgrade_help.get('families', {}).get('rename', {}): + name = upgrade_help.get('families', {}).get('rename', {})[name] + families[normalize_family(name)].attrib['help'] = text for auto in autos: if auto in variables: variables[auto].attrib['hidden'] = 'True' @@ -436,6 +499,8 @@ class RougailUpgrade: service.attrib['name'] = service_name if service_name == 'unknown': service.attrib['manage'] = 'False' + if service_name in upgrade_help.get('services', {}).get('unmanage', []): + service.attrib['manage'] = 'False' service_elt[service_name] = service if upgrade_help.get('servicelists', {}).get(service_name): service.attrib['servicelist'] = upgrade_help.get('servicelists', {}).get(service_name) @@ -450,7 +515,15 @@ class RougailUpgrade: elt.attrib['engine'] = 'creole_legacy' if (not 'instance_mode' in elt.attrib or elt.attrib['instance_mode'] != 'when_container') and \ elt.text not in upgrade_help.get('files', {}).get('remove', {}): - files[current_service][elt.text] = elt + if elt.attrib.get('filelist') in upgrade_help.get('services', {}).get('filelist_service', {}): + elt_service = upgrade_help.get('services', {}).get('filelist_service', {})[elt.attrib['filelist']] + if elt_service in files: + service = elt_service + else: + service = current_service + else: + service = current_service + files[service][elt.text] = elt elif elt.tag in ['host', 'disknod', 'fstab', 'interface', 'package', 'service_access']: pass elif elt.tag == 'service_restriction': @@ -458,7 +531,15 @@ class RougailUpgrade: if restriction.tag == 'ip' and restriction.text != '0.0.0.0': self.remove(restriction, 'ip_type', optional=True) self.remove(restriction, 'netmask_type', optional=True) - ips[current_service].append(restriction) + if elt.attrib['service'] in upgrade_help.get('services', {}).get('rename', {}): + elt_service = upgrade_help.get('services', {}).get('rename', {})[elt.attrib['service']] + else: + elt_service = elt.attrib['service'] + if elt_service in ips: + service = elt_service + else: + service = current_service + ips[service].append(restriction) elif elt.tag == 'service': new_name = elt.text if current_service == 'unknown':