# coding: utf-8 from copy import copy from collections import OrderedDict from os.path import join, basename from ast import literal_eval import sys import imp from .i18n import _ from .utils import normalize_family from .config import VIRTBASE, VIRTROOT, VIRTMASTER, templatedir from .error import CreoleDictConsistencyError from .xmlreflector import HIGH_COMPATIBILITY #mode order is important modes_level = ('basic', 'normal', 'expert') class secure_eosfunc: def __init__(self, eosfunc): self.eosfunc = eosfunc def __getattribute__(self, func_name): if func_name == 'eosfunc': return super().__getattribute__('eosfunc') if func_name in self.eosfunc.func_on_zephir_context: return getattr(self.eosfunc) raise Exception(_('unknown or unauthorized function: {}'.format(func_name))) class Mode(object): def __init__(self, name, level): self.name = name self.level = level def __cmp__(self, other): return cmp(self.level, other.level) def __eq__(self, other): return self.level == other.level def __ne__(self, other): return self.level != other.level def __gt__(self, other): return other.level < self.level def __ge__(self, other): return not self.level < other.level def __le__(self, other): return not other.level < self.level def mode_factory(): mode_obj = {} for idx in range(len(modes_level)): name = modes_level[idx] mode_obj[name] = Mode(name, idx) return mode_obj modes = mode_factory() # a CreoleObjSpace's attribute has some annotations # that shall not be present in the exported (flatened) XML ERASED_ATTRIBUTES = ('redefine', 'exists', 'fallback', 'optional', 'remove_check', 'namespace', 'remove_condition', 'path', 'instance_mode', 'index', 'is_in_master', '_real_container') NOT_NEED_ACTIVATE = ('package', 'disknod') FORCE_CHOICE = {'oui/non': ['oui', 'non'], 'on/off': ['on', 'off'], 'yes/no': ['yes', 'no'], 'schedule': ['none', 'daily', 'weekly', 'monthly'], 'schedulemod': ['pre', 'post']} KEY_TYPE = {'SymLinkOption': 'symlink', 'PortOption': 'port', 'UnicodeOption': 'string', 'NetworkOption': 'network', 'NetmaskOption': 'netmask', 'URLOption': 'web_address', 'FilenameOption': 'filename'} TYPE_PARAM_CHECK = ('string', 'python', 'eole') TYPE_PARAM_CONDITION = ('string', 'python', 'number', 'eole') TYPE_PARAM_FILL = ('string', 'eole', 'number', 'container', 'context') DISKNOD_KEY_TYPE = {'major': 'number', 'minor': 'number'} ERASED_FAMILY_ACTION_ATTRIBUTES = ('index', 'action') class ContainerAnnotator(object): """Manage container's object """ def __init__(self, space, paths, objectspace): self.space = space self.paths = paths self.objectspace = objectspace self.extra_conditions = {} var_name = 'mode_conteneur_actif' self.containers_enabled = False try: family_name = self.paths.get_variable_family_name(var_name) if (hasattr(space, 'variables') and 'creole' in space.variables and hasattr(space.variables['creole'], 'family') and family_name in space.variables['creole'].family and var_name in space.variables['creole'].family[family_name].variable and hasattr(space.variables['creole'].family[family_name].variable[var_name], 'value')): # assume that mode_conteneur_actif is not a multi value = space.variables['creole'].family[family_name].variable[var_name].value[0].name self.containers_enabled = value == 'oui' except CreoleDictConsistencyError: pass root_container = self.objectspace.container() root_container.name = 'root' root_container.container = 'root' root_container.real_container = 'root' root_container.container_group = 'root' root_container.id = '1' if not hasattr(self.space, 'containers'): self.space.containers = self.objectspace.containers() if hasattr(self.space.containers, 'container'): old_container = list(self.space.containers.container.items()) old_container.insert(0, ('root', root_container)) self.space.containers.container = OrderedDict(old_container) #self.space.containers.container['root'] = root_container else: self.space.containers.container = OrderedDict({'root': root_container}) if hasattr(space, 'containers') and hasattr(space.containers, 'all'): all_container = self.objectspace.container() all_container.name = 'all' all_container.container = 'all' if self.containers_enabled: all_container.real_container = 'all' else: all_container.real_container = VIRTMASTER all_container.container_group = 'all' old_container = list(self.space.containers.container.items()) old_container.insert(1, ('all', all_container)) self.space.containers.container = OrderedDict(old_container) #self.space.containers.container['all'] = all_container if hasattr(space, 'variables') and 'creole' in space.variables: flattened_elts = dict() if hasattr(space, 'files'): for key, values in vars(self.space.files).items(): if not isinstance(values, str) and not isinstance(values, int): if isinstance(values, dict): values = values.values() for value in values: value.container = root_container flattened_elts.setdefault(key, []).append(value) # Remove "all" and dispatch informations in all containers if hasattr(space, 'containers') and hasattr(space.containers, 'all') and hasattr(space.containers, 'container'): for type_, containers in vars(space.containers.all).items(): if type_ == 'index': continue if isinstance(containers, list): for elt in containers: for container in space.containers.container.values(): if container.name not in ['root', 'all']: if not hasattr(container, type_): setattr(container, type_, []) new_elt = copy(elt) new_elt.container = container getattr(container, type_).append(new_elt) else: for name, elt in containers.items(): for container in space.containers.container.values(): if container.name not in ['root', 'all']: if not hasattr(container, type_): setattr(container, type_, OrderedDict()) old_container = getattr(container, type_) if name in old_container: raise CreoleDictConsistencyError('{}'.format(name)) new_elt = copy(elt) new_elt.container = container old_container[name] = new_elt del space.containers.all if hasattr(space, 'containers') and hasattr(space.containers, 'container'): self.generate_interfaces() groups = {} containers = space.containers.container.values() container_groups = {} update_values = True while update_values: update_values = False for container in containers: if not hasattr(container, 'group'): container.group = container.name if not hasattr(container, 'container_group'): container.container_group = container.group if HIGH_COMPATIBILITY: if self.containers_enabled: real_container = container.group else: real_container = VIRTMASTER container.real_container = real_container if container.group in container_groups: group = container_groups[container.group] else: group = container.group if container_groups.get(container.name) != group: container_groups[container.name] = group container._real_container = group if not HIGH_COMPATIBILITY and self.containers_enabled: container.real_container = group update_values = True for container in space.containers.container.values(): if not hasattr(container, 'group'): container.group = container.name groupname = container.group groups.setdefault(groupname, []).append(container) for groupname, containers in groups.items(): for container in containers: if container.name == 'all': continue #container.container_group = groupname if HIGH_COMPATIBILITY and hasattr(container, 'id'): container.group_id = container.id container.id = space.containers.container[container._real_container].id container.container = container.name for container in space.containers.container.values(): container_info = self.objectspace.container() for key, value in vars(container).items(): if isinstance(value, str): setattr(container_info, key, value) for key, values in vars(container).items(): if not isinstance(values, str) and not isinstance(values, int): if isinstance(values, dict): values = values.values() for value in values: value.container = container_info flattened_elts.setdefault(key, []).append(value) self.generate_containers() if hasattr(self.space, 'files'): del self.space.files self.convert_containers() if hasattr(self.space.containers, 'family'): raise Exception('hu?') self.space.containers.family = OrderedDict() self.generate_network_container() for elttype in self.objectspace.container_elt_attr_list: key_type_name = elttype.upper() + '_KEY_TYPE' if key_type_name in globals(): key_type = globals()[key_type_name] else: key_type = {} elt = flattened_elts.get(elttype, {}) families = self.make_group_from_elts(elttype, elt, key_type, 'containers.{}s'.format(elttype), True) if families == [] and not HIGH_COMPATIBILITY: continue family = self.objectspace.family() family.name = elttype + 's' if HIGH_COMPATIBILITY: family.doc = '' family.family = families if HIGH_COMPATIBILITY: family.mode = None self.space.containers.family[elttype + 's'] = family def _generate_container_variable(self, name, description, value, family_name, frozen=False): var_data = {'hidden': True, 'mode': 'expert', 'name': name, 'doc': description, 'value': value, 'type': 'string'} variable = self.objectspace.variable() if HIGH_COMPATIBILITY: if frozen: var_data['frozen'] = True var_data['force_default_on_freeze'] = True var_data['hidden'] = False del var_data['mode'] variable.mode = None for key, value in var_data.items(): if key == 'value': # Value is a list of objects val = self.objectspace.value() val.name = value value = [val] setattr(variable, key, value) self.paths.append('variable', variable.name, 'creole', family_name, variable) return variable def _generate_root_container(self, family_name): if self.containers_enabled: ip_br0 = u'192.0.2.1' mask_br0 = u'255.255.255.0' network_br0 = u'192.0.2.0' bcast_br0 = u'192.0.2.255' else: ip_br0 = u'127.0.0.1' mask_br0 = u'255.0.0.0' network_br0 = u'127.0.0.0' bcast_br0 = u'127.255.255.255' variables = OrderedDict() variable = self._generate_container_variable('adresse_ip_br0', _(u"Bridge IP address"), ip_br0, family_name) variables[variable.name] = variable variable = self._generate_container_variable('adresse_netmask_br0', _(u"Bridge IP subnet mask"), mask_br0, family_name) variables[variable.name] = variable if HIGH_COMPATIBILITY: msg = u"Bridge IP network_br0 address" else: msg = u"Bridge IP network address" variable = self._generate_container_variable('adresse_network_br0', _(msg), network_br0, family_name) variables[variable.name] = variable variable = self._generate_container_variable('adresse_broadcast_br0', _(u"Bridge broadcast IP address"), bcast_br0, family_name) variables[variable.name] = variable return variables def _get_containers(self): return self.space.containers.container def convert_containers(self): idx = 0 self.space.containers.containers = self.objectspace.containers() for name, container in self.space.containers.container.items(): variables = [] for key, value in vars(container).items(): if key in ['container', 'group_id'] or key in ERASED_ATTRIBUTES: continue if not isinstance(value, list) and not isinstance(value, OrderedDict): variable = self.objectspace.variable() variable.name = key variable.mode = None if key == 'id': variable.type = 'number' else: variable.type = 'string' if HIGH_COMPATIBILITY: variable.doc = '' val = self.objectspace.value() val.name = value variable.value = [val] variables.append(variable) for key in ['ip', 'path']: var_path = self.paths.get_variable_path('container_{}_{}'.format(key, name), 'creole') variable = self.objectspace.variable() variable.name = key variable.mode = None variable.opt = var_path variable.type = 'symlink' variables.append(variable) family = self.objectspace.family() family.name = 'container{}'.format(idx) if HIGH_COMPATIBILITY: family.doc = '' family.variable = variables if HIGH_COMPATIBILITY: family.mode = None setattr(self.space.containers.containers, 'container{}'.format(idx), family) idx += 1 del self.space.containers.container def generate_network_container(self): family = self.objectspace.family() family.name = 'network' if HIGH_COMPATIBILITY: family.doc = '' family.mode = None variables = [] for name in ['adresse_ip_br0', 'adresse_netmask_br0', 'adresse_network_br0', 'adresse_broadcast_br0']: var_path = self.paths.get_variable_path(name, 'creole') variable = self.objectspace.variable() variable.name = name variable.mode = 'expert' variable.opt = var_path variable.type = 'symlink' variables.append(variable) family.variable = variables self.space.containers.family['network'] = family def generate_interfaces(self): if self.containers_enabled: for name, container in self._get_containers().items(): if name in ['all', 'root']: continue interface = self.objectspace.interface() interface.name = 'containers' interface.container = name interface.linkto = 'br0' interface.method = 'bridge' interface.ip = 'container_ip_{0}'.format(name) interface.mask = 'adresse_netmask_br0' interface.bcast = 'adresse_broadcast_br0' interface.gateway = 'adresse_ip_br0' if not hasattr(container, 'interface'): container.interface = OrderedDict() container.interface['containers'] = interface else: old = list(container.interface.items()) old.insert(0, ('containers', interface)) container.interface = OrderedDict(old) def generate_containers(self): """generate the root's container informations """ family_description = 'Containers' family_name = family_description.lower() if family_name in self.space.variables: raise CreoleDictConsistencyError(_('{} family already exists').format(family_name)) variables = self._generate_root_container(family_name) self._generate_containers(variables) self.paths.append('family', family_name, 'creole') family = self.objectspace.family() family.name = family_description family.doc = _(u'Containers informations') family.hidden = True if HIGH_COMPATIBILITY: family.mode = 'normal' family.icon = 'puzzle-piece' family.variable = variables # this family must be at the beginning if hasattr(self.space.variables['creole'], 'family'): old_families = list(self.space.variables['creole'].family.items()) old_families.insert(0, (family_name, family)) self.space.variables['creole'].family = OrderedDict(old_families) def _generate_container_path(self, container): if container.name == 'all': path = None elif not self.containers_enabled or container.name == VIRTMASTER: path = u'' else: group_name = container._real_container path = join(VIRTROOT, group_name, VIRTBASE) if sys.version_info[0] < 3: group_name = unicode(group_name) path = unicode(path) return path def _generate_containers(self, variables): """generate containers informations """ containers = self._get_containers() family_name = 'containers' ids = {} for name, container in containers.items(): if not hasattr(container, 'group'): groupname = container.name else: groupname = container.group if name == 'all': ipaddr = None group_name = u'all' else: group_name = container._real_container if sys.version_info[0] < 3: group_name = unicode(group_name) if group_name not in containers: raise CreoleDictConsistencyError(_('the container "{}" does not exist').format(group_name)) if not hasattr(containers[group_name], 'id'): raise CreoleDictConsistencyError(_('mandatory attribute "id" missing for container ' '"{}"').format(group_name)) id_value = containers[group_name].id if id_value in ids and ids[id_value] != group_name: raise CreoleDictConsistencyError(_('attribute "id" must be unique, but "{}" is used for containers "{}" and "{}"' ).format(id_value, group_name, ids[id_value])) ids[id_value] = group_name if not self.containers_enabled or name == VIRTMASTER: ipaddr = u'127.0.0.1' group_name = VIRTMASTER else: group_id = id_value ipaddr = u"192.0.2.{}".format(group_id) path = self._generate_container_path(container) # Variable : container_path_ path_name = 'container_path_{0}'.format(name) variable = self._generate_container_variable(path_name, _(u'Path of container {0}').format(name), path, family_name) variables[variable.name] = variable # Variable : container_ip_ ip_name = 'container_ip_{0}'.format(name) msg = u'IP address of container {0}' variable = self._generate_container_variable(ip_name, _(msg).format( name), ipaddr, family_name) variables[variable.name] = variable # Variable : container_name_ name_name = 'container_name_{0}'.format(name) variable = self._generate_container_variable(name_name, _(u'Group name of container {0}').format( name), group_name, family_name) variables[variable.name] = variable # Variable : adresse_ip_ # adresse_ip_ added for compat 2.3 (#5701, #5868) address_name = 'adresse_ip_{0}'.format(name) if HIGH_COMPATIBILITY: msg = u'Path of container {0}' else: msg = u'IP address of container {0}' if not self.paths.path_is_defined(address_name): if not self.containers_enabled: # hack to have "localhost" in non container mode #7183 variable = self._generate_container_variable(address_name, _(msg).format( name), 'localhost', family_name, frozen=True) else: self.paths.append('variable', address_name, 'creole', family_name, variable) path = self.paths.get_variable_path(address_name, 'creole') var_path = self.paths.get_variable_path(ip_name, 'creole') variable = self.objectspace.variable() variable.name = address_name variable.path = path variable.mode = 'expert' variable.opt = var_path variable.type = 'symlink' variables[variable.name] = variable def _generate_element(self, eltname, name, value, type_, subpath, multi=False): var_data = {'name': name, 'doc': '', 'value': value, 'auto_freeze': False, 'mode': None, 'multi': multi} values = None if type_ == 'string': values = self.objectspace.forced_choice_option.get(eltname, {}).get(name) if values is not None: type_ = 'choice' var_data['type'] = type_ variable = self.objectspace.variable() if not HIGH_COMPATIBILITY: variable.mandatory = True for key, value in var_data.items(): if key == 'value': if value is None: continue if type_ == 'symlink': key = 'opt' else: # Value is a list of objects if not multi: val = self.objectspace.value() val.name = value value = [val] else: value_list = [] for valiter in value: val = self.objectspace.value() val.name = valiter.name value_list.append(val) value = value_list if key == 'doc' and type_ == 'symlink': continue setattr(variable, key, value) if values is not None: choices = [] for value in values: choice = self.objectspace.choice() if sys.version_info[0] < 3: choice.name = unicode(value, 'utf8') else: choice.name = value choices.append(choice) variable.choice = choices path = '{}.{}'.format(subpath, name) self.paths.append('variable', path, 'containers', 'containers', variable) return variable def _make_disknod_auto(self, type_, index, variable): if not hasattr(self.space.constraints, 'auto'): self.space.constraints.auto = [] auto = self.objectspace.auto() self.objectspace.index += 1 auto.index = self.objectspace.index auto.namespace = 'containers' param1 = self.objectspace.param() param1.text = type_ param2 = self.objectspace.param() param2.text = variable.name auto.param = [param1, param2] auto.name = 'cdrom_minormajor' family = 'disknod{}'.format(index) auto.target = 'containers.disknods.{}.{}'.format(family, type_) if not hasattr(self.space, 'constraints'): self.space.constraints = self.objectspace.constraints() self.space.constraints.auto.append(auto) def _make_disknod_type(self, index, variable): auto = self.objectspace.auto() self.objectspace.index += 1 auto.index = self.objectspace.index auto.namespace = 'containers' param = self.objectspace.param() param.text = variable.name auto.param = [param] auto.name = 'device_type' family = 'disknod{}'.format(index) auto.target = 'containers.disknods.{}.type'.format(family) if not hasattr(self.space, 'constraints'): self.space.constraints = self.objectspace.constraints() if not hasattr(self.space.constraints, 'auto'): self.space.constraints.auto = [] self.space.constraints.auto.append(auto) def _update_disknod(self, disknod, index): disknod.major = None disknod.minor = None disknod.type = None self._make_disknod_auto('minor', index, disknod) self._make_disknod_auto('major', index, disknod) self._make_disknod_type(index, disknod) disknod.mode = u'rwm' disknod.permission = 'allow' def _update_file(self, file_, index): # take care of os.path.join and absolute part after first # argument. filename = file_.name if filename[0] == '/': filename = filename[1:] full_name = file_.name container_path = self._generate_container_path(file_.container) if container_path: if full_name.startswith('/'): full_name = full_name[1:] full_name = join(container_path, full_name) file_.full_name = full_name if not hasattr(file_, 'source'): source = basename(filename) else: source = file_.source source = join(templatedir, source) file_.source = source def _split_elts(self, name, key, value, elt): """for example:: 123 ntpd builds a `service_access` object, but we need **two** objects `service_access`, for example one for the port and one for the tcpwrapper """ for subelt in value: new_elt = copy(elt) for subsubelt in dir(subelt): if subsubelt.startswith('_') or subsubelt == 'index': continue if hasattr(new_elt, subsubelt): if hasattr(elt, 'name'): name_ = elt.name else: name_ = elt.service raise CreoleDictConsistencyError(_('attribute {} already exists ' 'for {}').format(subsubelt, name_)) setattr(new_elt, subsubelt, getattr(subelt, subsubelt)) if hasattr(new_elt, 'node_name') or hasattr(new_elt, 'name_type'): raise CreoleDictConsistencyError(_('attribute node_name or name_type ' 'already exists for {}' '').format(name)) if hasattr(subelt, key + '_type'): type_ = getattr(subelt, key + '_type') setattr(new_elt, 'name_type', type_) setattr(new_elt, 'node_name', key) if not hasattr(new_elt, name + 'list'): setattr(new_elt, name + 'list', '___auto_{}'.format(elt.service)) else: self.extra_conditions[new_elt] = '___auto_{}'.format(elt.service) yield new_elt def _reorder_elts(self, name, elts, duplicate_list): """Reorders by index the elts (the interface, the hosts, actions...) """ dict_elts = OrderedDict() # reorder elts by index new_elts = {} not_indexed = [] for elt in elts: if not hasattr(elt, 'index'): not_indexed.append(elt) else: idx = elt.index new_elts.setdefault(idx, []).append(elt) idxes = list(new_elts.keys()) idxes.sort() elts = not_indexed for idx in idxes: elts.extend(new_elts[idx]) for idx, elt in enumerate(elts): elt_added = False for key in dir(elt): if key.startswith('_') or key.endswith('_type') or key in ERASED_ATTRIBUTES: continue value = getattr(elt, key) if isinstance(value, list) and duplicate_list: for new_elt in self._split_elts(name, key, value, elt): dict_elts.setdefault(new_elt.name, []).append({'elt_name': key, 'elt': new_elt}) elt_added = True if not elt_added: if hasattr(elt, 'name'): eltname = elt.name else: eltname = idx dict_elts.setdefault(eltname, []).append({'elt_name': name, 'elt': elt}) result_elts = [] for elt in dict_elts.values(): result_elts.extend(elt) return result_elts def make_group_from_elts(self, name, elts, key_type, path, duplicate_list): """Splits each objects into a group (and `OptionDescription`, in tiramisu terms) and build elements and its attributes (the `Options` in tiramisu terms) """ index = 0 families = [] new_elts = self._reorder_elts(name, elts, duplicate_list) for elt_info in new_elts: elt = elt_info['elt'] elt_name = elt_info['elt_name'] update_elt = '_update_' + elt_name if hasattr(self, update_elt): getattr(self, update_elt)(elt, index) if hasattr(elt, 'instance_mode'): instance_mode = elt.instance_mode else: instance_mode = 'always' if ((instance_mode == 'when_container' and not self.containers_enabled) or (instance_mode == 'when_no_container' and self.containers_enabled)): continue variables = [] subpath = '{}.{}{}'.format(path, name, index) listname = '{}list'.format(name) if name not in NOT_NEED_ACTIVATE: activate_path = '.'.join([subpath, 'activate']) if elt in self.extra_conditions: self.objectspace.list_conditions.setdefault(listname, {}).setdefault( self.extra_conditions[elt], []).append(activate_path) for key in dir(elt): if key.startswith('_') or key.endswith('_type') or key in ERASED_ATTRIBUTES: continue value = getattr(elt, key) if isinstance(value, list) and duplicate_list: continue if key == listname: if name not in NOT_NEED_ACTIVATE: self.objectspace.list_conditions.setdefault(listname, {}).setdefault( value, []).append(activate_path) if not HIGH_COMPATIBILITY: continue if key == 'container': variables.append(self._generate_element(elt_name, key, value.container, 'string', subpath)) variables.append(self._generate_element(elt_name, 'container_group', value.container_group, 'string', subpath)) if HIGH_COMPATIBILITY: if not self.containers_enabled: real_container = value.real_container else: real_container = value._real_container variables.append(self._generate_element(elt_name, 'real_container', real_container, 'string', subpath)) else: default_type = 'string' if key in self.objectspace.booleans_attributs: default_type = 'boolean' type_ = key_type.get(key, default_type) dtd_key_type = key + '_type' if hasattr(elt, dtd_key_type): type_ = KEY_TYPE[getattr(elt, dtd_key_type)] if isinstance(value, list): variables.append(self._generate_element(elt_name, key, value, type_, subpath, True)) else: variables.append(self._generate_element(elt_name, key, value, type_, subpath, False)) if name not in NOT_NEED_ACTIVATE: variables.append(self._generate_element(name, 'activate', True, 'boolean', subpath)) family = self.objectspace.family() family.name = '{}{}'.format(name, index) if HIGH_COMPATIBILITY: family.doc = '' family.variable = variables if HIGH_COMPATIBILITY: family.mode = None self.paths.append('family', subpath, 'containers', creoleobj=family) families.append(family) index += 1 return families class ActionAnnotator(ContainerAnnotator): def __init__(self, space, paths, objectspace): self.space = space self.paths = paths self.objectspace = objectspace self.extra_conditions = [] if hasattr(space, 'family_action'): actions = self.objectspace.family() actions.name = 'actions' if HIGH_COMPATIBILITY: actions.mode = None actions.family = [] self.space.actions = actions namespaces = [] for name, actions in space.family_action.items(): subpath = 'actions.{}'.format(normalize_family(name)) for action in actions.action: namespace = action.namespace if namespace in namespaces: raise CreoleDictConsistencyError(_('only one action allow for {}' '').format(namespace)) namespaces.append(namespace) action.name = action.namespace new_actions = self.make_group_from_elts('action', actions.action, {}, subpath, False) family = self.objectspace.family() family.name = actions.name family.family = new_actions if HIGH_COMPATIBILITY: family.mode = None variables = [] for key, value in vars(actions).items(): if key not in ERASED_FAMILY_ACTION_ATTRIBUTES: variables.append(self._generate_element('action', key, value, 'string', subpath)) family.variable = variables self.space.actions.family.append(family) del space.family_action class SpaceAnnotator(object): """Transformations applied on a CreoleObjSpace instance """ def __init__(self, space, paths, objectspace, eosfunc_file): self.paths = paths self.space = space self.objectspace = objectspace self.valid_enums = {} self.force_value = {} self.has_calc = [] self.force_no_value = [] self.force_not_mandatory = [] self.eosfunc = imp.load_source('eosfunc', eosfunc_file) if HIGH_COMPATIBILITY: self.default_has_no_value = [] self.has_frozen_if_in_condition = [] try: self.default_variable_options(space.variables) except AttributeError: raise CreoleDictConsistencyError(_('No configuration variables available in the configuration set')) for family in space.variables.values(): if hasattr(family, 'family'): self.change_variable_auto_freeze(family.family, family.name) if 'group' in vars(space.constraints): self.transform_groups(space.constraints.group, space) if hasattr(space.constraints, 'check'): self.filter_check(space.constraints.check) if 'condition' in vars(space.constraints): self.filter_condition(space.constraints.condition) self._parse_object_space(space, None) # valid_enums must be empty now (all information are store in objects) if self.valid_enums: raise CreoleDictConsistencyError(_('valid_enum sets for unknown variables {}').format(self.valid_enums.keys())) self.filter_autofill(space) for family in space.variables.values(): if not HIGH_COMPATIBILITY: self.remove_empty_family(family.family) if hasattr(family, 'family'): self.change_variable_mode(family.family) if not HIGH_COMPATIBILITY: self.change_family_mode(family.family) if (hasattr(family, 'separators') and hasattr(family.separators, 'separator')): self.filter_separator(family.separators.separator) self.absolute_path_for_symlink_in_containers(space.containers.family.values()) if 'help' in vars(space): self.transform_helps(space.help) def absolute_path_for_symlink_in_containers(self, families): for family in families: if hasattr(family, 'family'): for fam in family.family: for variable in fam.variable: if variable.type == 'symlink' and '.' not in variable.name: variable.opt = self.paths.get_variable_path(variable.opt, 'creole') def transform_helps(self, helps): if hasattr(helps, 'variable'): for hlp in helps.variable.values(): hlp.name = hlp.path if hasattr(helps, 'family'): for hlp in helps.family.values(): hlp.name = hlp.path def transform_groups(self, groups, space): # pylint: disable=C0111 for group in groups: master_fullname = group.master slave_names = list(group.slave.keys()) try: master_family_name = self.paths.get_variable_family_name(master_fullname) except CreoleDictConsistencyError as err: if HIGH_COMPATIBILITY: continue raise err namespace = self.paths.get_variable_namespace(master_fullname) master_name = self.paths.get_variable_name(master_fullname) master_family = space.variables[namespace].family[master_family_name] master_path = namespace + '.' + master_family_name is_master = False for variable_name, variable in list(master_family.variable.items()): if isinstance(variable, self.objectspace.Master): if variable.name == master_name: master_space = variable is_master = True else: if is_master: # slaves are multi if variable_name in slave_names: variable.multi = True slave_names.remove(variable_name) master_family.variable.pop(variable_name) master_space.variable.append(variable) # pylint: disable=E1101 if namespace == 'creole': variable_fullpath = variable_name else: variable_fullpath = master_path + '.' + variable_name self.paths.set_master(variable_fullpath, master_name) if slave_names == []: break if is_master is False and variable_name == master_name: master_space = self.objectspace.Master() master_space.variable = [] master_space.name = master_name # manage master's variable if variable.multi is not True: raise CreoleDictConsistencyError(_('the variable {} in a group must be multi').format(variable.name)) master_family.variable[master_name] = master_space master_space.variable.append(variable) # pylint: disable=E1101 self.paths.set_master(master_fullname, master_name) master_space.path = master_fullname is_master = True else: # pragma: no cover raise CreoleDictConsistencyError(_('cannot found a master {} ' 'nor a slave {}').format(master_name, slave_names)) del space.constraints.group def _parse_object_space(self, space, namespace, node_name='creole', parent_path=None, ishelp=False): space_is_help = ishelp vars_space = dict(vars(space)) for name, subspace in vars_space.items(): if namespace is None and name in ['containers', 'actions']: continue if space_is_help is False: ishelp = name == 'help' self._parse_subobject_space(name, node_name, space, subspace, parent_path, namespace, ishelp) def _parse_subobject_space(self, name, node_name, parent, space, parent_path, namespace, ishelp): # pylint: disable=R0913 keys = None if isinstance(space, dict): if namespace is None: keys = list(space.keys()) space = list(space.values()) if isinstance(space, list): for idx, subspace in enumerate(space): if keys is not None and namespace is None: if subspace.__class__.__name__ == 'Variable': current_namespace = self.paths.get_variable_namespace(keys[idx]) elif subspace.__class__.__name__ == 'Variables': current_namespace = keys[idx] else: current_namespace = self.paths.get_family_namespace(normalize_family(keys[idx], check_name=False)) else: current_namespace = namespace if hasattr(parent, 'path'): parent_path = parent.path else: parent_path = None self._parse_object_space(subspace, current_namespace, name, parent_path, ishelp) elif isinstance(space, self.objectspace.Atom): for subname, subspace in vars(space).items(): self._parse_subobject_space(subname, name, space, subspace, None, namespace, ishelp) else: self.absolute_paths_annotation(name, node_name, parent, space, parent_path, namespace, ishelp) self.uppercase_family_name(name, node_name, parent, space) def remove_empty_family(self, space): # pylint: disable=C0111,R0201 removed_families = [] for family_name, family in space.items(): if not hasattr(family, 'variable') or len(family.variable) == 0: removed_families.append(family_name) del space[family_name] # remove help too if hasattr(self.space, 'help') and hasattr(self.space.help, 'family'): for family in self.space.help.family.keys(): if family in removed_families: del self.space.help.family[family] def uppercase_family_name(self, name, node_name, parent, value): # pylint: disable=C0111,R0201 if name == 'name' and node_name == 'family': # let's preserve uppercase letters # just in case where some acronyms are present, # example : 'ARV' if not value[0].isupper(): if HIGH_COMPATIBILITY: parent.name = value else: parent.name = value.capitalize() def change_family_mode(self, families): # pylint: disable=C0111 for family in families.values(): mode = modes_level[-1] for variable in family.variable.values(): if isinstance(variable, self.objectspace.Master): variable_mode = variable.variable[0].mode variable.mode = variable_mode else: variable_mode = variable.mode if variable_mode is not None and modes[mode] > modes[variable_mode]: mode = variable_mode if HIGH_COMPATIBILITY and family.name == 'Containers': family.mode = 'normal' else: family.mode = mode def _annotate_variable(self, variable, family_mode, is_slave=False): if (HIGH_COMPATIBILITY and variable.type == 'choice' and variable.mode != modes_level[-1] and variable.mandatory is True and variable.path in self.default_has_no_value): variable.mode = modes_level[0] if variable.type == 'choice' and is_slave and family_mode == modes_level[0] and variable.mandatory is True: variable.mode = modes_level[0] # if the variable is mandatory and doesn't have any value # then the variable's mode is set to 'basic' has_value = hasattr(variable, 'value') if (variable.path not in self.has_calc and variable.mandatory is True and (not has_value or is_slave) and variable.type != 'choice'): variable.mode = modes_level[0] if has_value: if not HIGH_COMPATIBILITY or (not variable.path.startswith('creole.containers.') \ and variable.path not in self.force_no_value and variable.path not in self.force_not_mandatory): variable.mandatory = True if variable.hidden is True: variable.frozen = True if not variable.auto_save is True and 'force_default_on_freeze' not in vars(variable): variable.force_default_on_freeze = True if variable.name == 'frozen' and not variable.auto_save is True: variable.force_default_on_freeze = True if variable.mode != None and not is_slave and modes[variable.mode] < modes[family_mode]: variable.mode = family_mode if variable.mode != None and variable.mode != modes_level[0] and modes[variable.mode] < modes[family_mode]: variable.mode = family_mode if variable.name == "available_probes": variable.force_default_on_freeze = False def default_variable_options(self, variables): for families in variables.values(): if hasattr(families, 'family'): for family in families.family.values(): if hasattr(family, 'variable'): for variable in family.variable.values(): if not hasattr(variable, 'type'): variable.type = 'string' if variable.type != 'symlink' and not hasattr(variable, 'description'): variable.description = variable.name def change_variable_auto_freeze(self, families, namespace): # pylint: disable=C0111 for family in families.values(): if hasattr(family, 'variable'): for variable in family.variable.values(): if variable.auto_freeze: new_condition = self.objectspace.condition() new_condition.name = 'auto_frozen_if_in' new_condition.namespace = namespace new_condition.source = 'module_instancie' new_param = self.objectspace.param() new_param.text = 'oui' new_condition.param = [new_param] new_target = self.objectspace.target() new_target.type = 'variable' if namespace == 'creole': path = variable.name else: path = namespace + '.' + family.name + '.' + variable.name new_target.name = path new_condition.target = [new_target] self.space.constraints.condition.append(new_condition) def change_variable_mode(self, families): # pylint: disable=C0111 for family in families.values(): family_mode = family.mode if hasattr(family, 'variable'): for variable in family.variable.values(): if isinstance(variable, self.objectspace.Master): mode = modes_level[-1] for slave in variable.variable: if slave.auto_save is True: raise CreoleDictConsistencyError(_('master/slaves {} ' 'could not be ' 'auto_save').format(slave.name)) if slave.auto_freeze is True: raise CreoleDictConsistencyError(_('master/slaves {} ' 'could not be ' 'auto_freeze').format(slave.name)) if HIGH_COMPATIBILITY and variable.name != slave.name: # and variable.variable[0].mode != modes_level[0]: is_slave = True else: is_slave = False self._annotate_variable(slave, family_mode, is_slave) if HIGH_COMPATIBILITY: # master's variable are right if modes[variable.variable[0].mode] > modes[slave.mode]: slave.mode = variable.variable[0].mode else: # auto_save's variable is set in 'basic' mode if its mode is 'normal' if slave.auto_save is True and slave.mode != modes_level[-1]: slave.mode = modes_level[0] if modes[mode] > modes[slave.mode]: mode = slave.mode if not HIGH_COMPATIBILITY: # the master's mode is the lowest variable.variable[0].mode = mode variable.mode = variable.variable[0].mode else: # auto_save's variable is set in 'basic' mode if its mode is 'normal' if variable.auto_save is True and variable.mode != modes_level[-1]: variable.mode = modes_level[0] # auto_freeze's variable is set in 'basic' mode if its mode is 'normal' if variable.auto_freeze is True and variable.mode != modes_level[-1]: variable.mode = modes_level[0] self._annotate_variable(variable, family_mode) def absolute_paths_annotation(self, name, node_name, parent, value, parent_path, namespace, ishelp): # pylint: disable=C0111,R0913 if hasattr(parent, 'path'): return if name == 'name' and node_name in ['variable', 'family']: if node_name == 'family': family_name = normalize_family(value, check_name=False) subpath = self.paths.get_family_path(family_name, namespace) namespace = self.paths.get_family_namespace(family_name) else: if self.paths.path_is_defined(value): value_name = value else: value_name = parent_path + '.' + value if namespace is None: namespace = self.paths.get_variable_namespace(value) subpath = self.paths.get_variable_path(value_name, namespace) if not ishelp and hasattr(parent, 'type') and parent.type in FORCE_CHOICE: if subpath in self.valid_enums: raise CreoleDictConsistencyError(_('cannot set valid enum for variable with type {}').format(parent.type)) parent.choice = [] for value in FORCE_CHOICE[parent.type]: choice = self.objectspace.choice() if sys.version_info[0] < 3: choice.name = unicode(value, 'utf8') else: choice.name = str(value) parent.choice.append(choice) parent.type = 'choice' if not HIGH_COMPATIBILITY: parent.mandatory = True if parent.choice == []: raise CreoleDictConsistencyError(_('empty valid enum is not allowed for variable {}').format(value_name)) if hasattr(parent, 'type') and parent.type != 'choice': orig_type = parent.type else: orig_type = None if not ishelp and subpath in self.valid_enums: values = self.valid_enums[subpath]['values'] if isinstance(values, list): parent.choice = [] choices = [] for value in values: choice = self.objectspace.choice() if sys.version_info[0] < 3: choice.name = unicode(value) else: choice.name = str(value) choices.append(choice.name) choice.type = parent.type parent.choice.append(choice) if hasattr(parent, 'value'): for value in parent.value: value.type = parent.type if value.name not in choices: raise CreoleDictConsistencyError(_('value "{}" of variable "{}" is not in list of all expected values ({})').format(value.name, parent.name, choices)) if parent.choice == []: raise CreoleDictConsistencyError(_('empty valid enum is not allowed for variable {}').format(value_name)) else: # probe choice parent.choice = values parent.type = 'choice' del(self.valid_enums[subpath]) if not ishelp and subpath in self.force_value: if not hasattr(parent, 'value'): new_value = self.objectspace.value() new_value.name = self.force_value[subpath] parent.value = [new_value] self.force_no_value.append(subpath) if not ishelp and hasattr(parent, 'type') and parent.type == 'choice': # if choice with no value, set value with the first choice if not hasattr(parent, 'value'): no_value = False if HIGH_COMPATIBILITY and parent.multi: no_value = True if not no_value: new_value = self.objectspace.value() new_value.name = parent.choice[0].name new_value.type = orig_type if HIGH_COMPATIBILITY: self.default_has_no_value.append(subpath) parent.value = [new_value] self.force_no_value.append(subpath) parent.path = subpath if name == 'name' and node_name == 'separator': pass def get_variable(self, name): # pylint: disable=C0111 return self.paths.get_variable_obj(name) def filter_autofill(self, space): # pylint: disable=C0111 self.filter_duplicate_autofill(space.constraints) if 'auto' in vars(space.constraints): self.filter_auto(space.constraints.auto, space) if 'fill' in vars(space.constraints): self.filter_fill(space.constraints.fill, space) def filter_duplicate_autofill(self, constraints): """ Remove duplicate auto or fill for a variable This variable must be redefined """ fills = {} # sort fill/auto by index if 'fill' in vars(constraints): for idx, fill in enumerate(constraints.fill): if fill.index in fills: raise Exception('hu?') fills[fill.index] = {'idx': idx, 'fill': fill, 'type': 'fill'} if 'auto' in vars(constraints): for idx, fill in enumerate(constraints.auto): if fill.index in fills: raise Exception('hu?') fills[fill.index] = {'idx': idx, 'fill': fill, 'type': 'auto'} indexes = list(fills.keys()) indexes.sort() targets = {} remove_autos = [] remove_fills = [] for idx in indexes: fill = fills[idx]['fill'] if hasattr(fill, 'redefine'): redefine = bool(fill.redefine) else: redefine = False if fill.target in targets: if redefine: if targets[fill.target][1] == 'auto': remove_autos.append(targets[fill.target][0]) else: remove_fills.append(targets[fill.target][0]) else: raise CreoleDictConsistencyError(_("An auto or fill already exists " "for the target: {}").format( fill.target)) targets[fill.target] = (fills[idx]['idx'], fills[idx]['type']) remove_autos.sort(reverse=True) for idx in remove_autos: constraints.auto.pop(idx) remove_fills.sort(reverse=True) for idx in remove_fills: constraints.fill.pop(idx) def filter_auto(self, auto_space, space): # pylint: disable=C0111 for auto in auto_space: if HIGH_COMPATIBILITY and auto.target in self.has_frozen_if_in_condition: # if a variable has a 'frozen_if_in' condition # then we change the 'auto' variable as a 'fill' variable continue # an auto is a fill with "hidden" and "frozen" properties variable = self.get_variable(auto.target) if variable.auto_freeze: raise CreoleDictConsistencyError(_('variable with auto value ' 'cannot be auto_freeze').format(auto.target)) if variable.auto_save: raise CreoleDictConsistencyError(_('variable with auto value ' 'cannot be auto_save').format(auto.target)) variable.hidden = True variable.frozen = True variable.force_default_on_freeze = True if 'fill' not in vars(space.constraints): space.constraints.fill = [] space.constraints.fill.extend(auto_space) del space.constraints.auto def filter_separator(self, space): # pylint: disable=C0111,R0201 names = [] remove_separators = [] for idx, separator in enumerate(space): try: namespace = self.paths.get_variable_namespace(separator.name) subpath = self.paths.get_variable_path(separator.name, namespace) separator.name = subpath except CreoleDictConsistencyError as err: if HIGH_COMPATIBILITY: remove_separators.append(idx) continue else: raise err if separator.name in names: raise CreoleDictConsistencyError(_('{} already has a separator').format(separator.name)) names.append(separator.name) remove_separators.sort(reverse=True) for idx in remove_separators: del space[idx] def load_params_in_validenum(self, param, probe): if not probe and param.type in ['string', 'python', 'number']: if not hasattr(param, 'text') and (param.type == 'python' or param.type == 'number'): raise CreoleDictConsistencyError(_("All '{}' variables shall be set in order to calculate {}").format(param.type, 'valid_enum')) if param.type in ['string', 'number']: try: values = literal_eval(param.text) except ValueError: raise CreoleDictConsistencyError(_('Cannot load {}').format(param.text)) elif param.type == 'python': try: values = eval(param.text, {'eosfunc': secure_eosfunc(self.eosfunc), '__builtins__': {'range': range, 'str': str}}) #FIXME : eval('[str(i) for i in range(3, 13)]', {'eosfunc': eosfunc, '__builtins__': {'range': range, 'str': str}}) except NameError: raise CreoleDictConsistencyError(_('The function {} is unknown').format(param.text)) if not isinstance(values, list): raise CreoleDictConsistencyError(_('Function {} shall return a list').format(param.text)) new_values = [] for val in values: if sys.version_info[0] < 3 and isinstance(val, str): val = val.decode('utf-8') new_values.append(val) values = new_values else: values = param.text return values def filter_check(self, space): # pylint: disable=C0111 # valid param in check remove_indexes = [] for check_idx, check in enumerate(space): namespace = check.namespace if hasattr(check, 'param'): param_option_indexes = [] for idx, param in enumerate(check.param): if param.type not in TYPE_PARAM_CHECK: raise CreoleDictConsistencyError(_('cannot use {} type as a param in check for {}').format(param.type, check.target)) if param.type == 'eole': if HIGH_COMPATIBILITY and param.text.startswith('container_ip'): if param.optional is True: param_option_indexes.append(idx) try: param.text = self.paths.get_variable_path(param.text, namespace) except CreoleDictConsistencyError as err: if param.optional is True: param_option_indexes.append(idx) else: raise err param_option_indexes = list(set(param_option_indexes)) param_option_indexes.sort(reverse=True) for idx in param_option_indexes: check.param.pop(idx) if not HIGH_COMPATIBILITY and check.param == []: remove_indexes.append(check_idx) remove_indexes.sort(reverse=True) for idx in remove_indexes: del space[idx] variables = {} for index, check in enumerate(space): namespace = check.namespace if HIGH_COMPATIBILITY: if not self.paths.path_is_defined(check.target): continue check.is_in_master = self.paths.get_master(check.target) != None # let's replace the target by the path check.target = self.paths.get_variable_path(check.target, namespace) if check.target not in variables: variables[check.target] = [] variables[check.target].append((index, check)) # remove check already set for a variable remove_indexes = [] for checks in variables.values(): names = {} for idx, check in checks: if HIGH_COMPATIBILITY and check.name == 'valid_enum': redefine = True else: redefine = False #redefine = bool(check.redefine) if redefine and check.name in names: remove_indexes.append(names[check.name]) del names[check.name] names[check.name] = idx del check.index remove_indexes.sort(reverse=True) for idx in remove_indexes: del space[idx] remove_indexes = [] for idx, check in enumerate(space): if not check.name in dir(self.eosfunc): raise CreoleDictConsistencyError(_('cannot find check function {}').format(check.name)) is_probe = not check.name in self.eosfunc.func_on_zephir_context if is_probe: raise CreoleDictConsistencyError(_('cannot have a check with probe function ({})').format(check.name)) if check.name == 'valid_enum': proposed_value_type = False remove_params = [] for param_idx, param in enumerate(check.param): if hasattr(param, 'name') and param.name == 'checkval': try: proposed_value_type = self.objectspace._convert_boolean(param.text) == False remove_params.append(param_idx) except TypeError as err: raise CreoleDictConsistencyError(_('cannot load checkval value for variable {}: {}').format(check.target, err)) remove_params.sort(reverse=True) for param_idx in remove_params: del check.param[param_idx] if len(check.param) != 1: raise CreoleDictConsistencyError(_('cannot set more than one param ' 'for valid_enum for variable {}' '').format(check.target)) param = check.param[0] if proposed_value_type: if param.type != 'eole': try: values = self.load_params_in_validenum(param, check.probe) except NameError as err: raise CreoleDictConsistencyError(_('cannot load value for variable {}: {}').format(check.target, err)) add_value = True if HIGH_COMPATIBILITY and check.is_in_master: add_value = False if add_value and values: self.force_value[check.target] = values[0] else: if check.target in self.valid_enums: raise CreoleDictConsistencyError(_('valid_enum already set for {}' '').format(check.target)) values = self.load_params_in_validenum(param, check.probe) self.valid_enums[check.target] = {'type': param.type, 'values': values} remove_indexes.append(idx) remove_indexes.sort(reverse=True) for idx in remove_indexes: del space[idx] #convert level to "warnings_only" and hidden to "transitive" for check in space: if check.level == 'warning': check.warnings_only = True else: check.warnings_only = False check.level = None transitive = True if hasattr(check, 'param'): for param in check.param: if not param.hidden is True: transitive = False param.hidden = None check.transitive = transitive def filter_fill(self, fill_space, space): # pylint: disable=C0111,R0912 fills = {} # sort fill/auto by index for idx, fill in enumerate(fill_space): fills[fill.index] = {'idx': idx, 'fill': fill} del fill.index indexes = list(fills.keys()) indexes.sort() del_idx = [] for idx in indexes: fill = fills[idx]['fill'] variable = self.get_variable(fill.target) if hasattr(variable, 'value'): del variable.value namespace = fill.namespace # let's replace the target by the path fill.target = self.paths.get_variable_path(fill.target, namespace) if not fill.name in dir(self.eosfunc): raise CreoleDictConsistencyError(_('cannot find fill function {}').format(fill.name)) is_probe = not fill.name in self.eosfunc.func_on_zephir_context if hasattr(fill, 'param'): for param in fill.param: if param.type not in TYPE_PARAM_FILL: raise CreoleDictConsistencyError(_('cannot use {} type as a param ' 'in a fill/auto').format(param.type)) param_option_indexes = [] for fill_idx, param in enumerate(fill.param): if not hasattr(param, 'text') and \ (param.type == 'eole' or param.type == 'number' or \ param.type == 'container' or param.type == 'python'): raise CreoleDictConsistencyError(_("All '{}' variables shall be set in " "order to calculate {}").format( param.type, fill.target)) if param.type == 'container': param.type = 'eole' param.text = 'container_ip_{}'.format(param.text) if param.type == 'eole': if is_probe: raise CreoleDictConsistencyError(_('Function {0} used to calculate {1} ' 'is executed on remote server, ' 'so cannot depends to an ' 'other variable' ).format(fill.name, fill.target)) if HIGH_COMPATIBILITY and param.text.startswith('container_ip'): if param.optional is True: param_option_indexes.append(fill_idx) try: param.text = self.paths.get_variable_path(param.text, namespace) except CreoleDictConsistencyError as err: if param.optional is True: param_option_indexes.append(fill_idx) else: raise err param_option_indexes = list(set(param_option_indexes)) param_option_indexes.sort(reverse=True) for param_idx in param_option_indexes: fill.param.pop(param_idx) self.has_calc.append(fill.target) if is_probe: variable.force_default_on_freeze = False self.objectspace.probe_variables.append(fill) del_idx.append(fills[idx]['idx']) del_idx.sort(reverse=True) for idx in del_idx: space.constraints.fill.pop(idx) def filter_target(self, space, namespace): # pylint: disable=C0111 del_idx = [] for idx, target in enumerate(space.target): if target.type == 'variable': if (hasattr(target, 'optional') and target.optional is True and not self.paths.path_is_defined(target.name)): del_idx.append(idx) continue if space.source == target.name: raise CreoleDictConsistencyError(_('target name and source name must be different: {}').format(space.source)) target.name = self.paths.get_variable_path(target.name, namespace) elif target.type == 'family': try: target.name = self.paths.get_family_path(target.name, namespace) except KeyError: raise CreoleDictConsistencyError(_('cannot found family {}').format(target.name)) del_idx = list(set(del_idx)) del_idx.sort(reverse=True) for idx in del_idx: space.target.pop(idx) def filter_condition(self, space): # pylint: disable=C0111 remove_conditions = [] fallback_variables = [] fallback_lists = [] # automatic generation of the service_access lists # and the service_restriction lists from the servicelist for condition in space: if hasattr(condition, 'target'): new_targets = [] for target in condition.target: if target.type == 'servicelist': new_target = copy(target) new_target.type = 'service_accesslist' new_target.name = '___auto_{}'.format(new_target.name) new_targets.append(new_target) new_target = copy(target) new_target.type = 'service_restrictionlist' new_target.name = '___auto_{}'.format(new_target.name) new_targets.append(new_target) condition.target.extend(new_targets) # remove condition with target if HIGH_COMPATIBILITY: for idx, condition in enumerate(space): if not hasattr(condition, 'target'): remove_conditions.append(idx) for idx, condition in enumerate(space): if idx in remove_conditions: continue if condition.name == 'hidden_if_in': condition.name = 'disabled_if_in' elif condition.name == 'hidden_if_not_in': condition.name = 'disabled_if_not_in' # a conditon with a fallback **and** the source variable doesn't exist if (hasattr(condition, 'fallback') and condition.fallback is True and not self.paths.path_is_defined(condition.source)): for target in condition.target: if target.type in ['variable', 'family']: name = target.name.split('.')[-1] if target.type == 'variable': variable = self.get_variable(name) else: variable = self.paths.get_family_obj(name) if condition.name in ['disabled_if_in']: variable.disabled = True if condition.name in ['mandatory_if_in']: variable.mandatory = True if condition.name in ['disabled_if_in', 'disabled_if_not_in', 'frozen_if_in', 'frozen_if_not_in']: variable.hidden = False if HIGH_COMPATIBILITY: fallback_variables.append(name) else: listname = target.type if not listname.endswith('list'): raise Exception('not yet implemented') listvars = self.objectspace.list_conditions.get(listname, {}).get(target.name) if listvars: for listvar in listvars: try: variable = self.get_variable(listvar) except CreoleDictConsistencyError: variable = self.paths.get_family_obj(listvar) if condition.name in ['disabled_if_in']: variable.disabled = True if condition.name in ['mandatory_if_in']: variable.mandatory = True if condition.name in ['disabled_if_in', 'disabled_if_not_in', 'frozen_if_in', 'frozen_if_not_in']: variable.hidden = False fallback_lists.append(listvar) remove_conditions.append(idx) for condition_idx, condition in enumerate(space): if condition_idx in remove_conditions: continue namespace = condition.namespace self.filter_target(condition, namespace) # transform *list to variable or family for condition_idx, condition in enumerate(space): if condition.name in ['disabled_if_in', 'disabled_if_not_in', 'frozen_if_in', 'auto_frozen_if_in', 'frozen_if_not_in', 'mandatory_if_in', 'mandatory_if_not_in']: new_targets = [] remove_targets = [] if not hasattr(condition, 'target'): continue for target_idx, target in enumerate(condition.target): if target.type not in ['variable', 'family']: listname = target.type if not listname.endswith('list'): raise Exception('not yet implemented') listvars = self.objectspace.list_conditions.get(listname, {}).get(target.name) if listvars: for listvar in listvars: if listvar in fallback_lists: continue try: variable = self.get_variable(listvar) type_ = 'variable' except CreoleDictConsistencyError: variable = self.paths.get_family_obj(listvar) type_ = 'family' new_target = self.objectspace.target() new_target.type = type_ new_target.name = listvar new_target.index = target.index new_targets.append(new_target) remove_targets.append(target_idx) remove_targets = list(set(remove_targets)) remove_targets.sort(reverse=True) for target_idx in remove_targets: condition.target.pop(target_idx) condition.target.extend(new_targets) force_remove_targets = {} for condition_idx, condition in enumerate(space): if condition_idx in remove_conditions: continue namespace = condition.namespace src_variable = self.paths.get_variable_obj(condition.source) condition.source = self.paths.get_variable_path(condition.source, namespace, allow_source=True) for param in condition.param: if param.type not in TYPE_PARAM_CONDITION: raise CreoleDictConsistencyError(_('cannot use {} type as a param ' 'in a condition').format(param.type)) if condition.name in ['disabled_if_in', 'disabled_if_not_in', 'frozen_if_in', 'auto_frozen_if_in', 'frozen_if_not_in', 'mandatory_if_in', 'mandatory_if_not_in']: valid_enum = None # remove condition for ChoiceOption that don't have param if condition.source in self.valid_enums and \ self.valid_enums[condition.source]['type'] == 'string': valid_enum = self.valid_enums[condition.source]['values'] if src_variable.type in FORCE_CHOICE: valid_enum = FORCE_CHOICE[src_variable.type] if valid_enum is not None: remove_param = [] for param_idx, param in enumerate(condition.param): if param.text not in valid_enum: remove_param.append(param_idx) remove_param.sort(reverse=True) for idx in remove_param: del condition.param[idx] if condition.param == []: for target in condition.target: if target.name.startswith('creole.'): name = target.name.split('.')[-1] else: name = target.name if target.type == 'variable': variable = self.get_variable(name) else: variable = self.paths.get_family_obj(name) if condition.name == 'disabled_if_not_in': variable.disabled = True force_remove_targets.setdefault(condition.name, []).append(target.name) elif condition.name == 'frozen_if_not_in': variable.hidden = True force_remove_targets.setdefault(condition.name, []).append(target.name) elif condition.name == 'mandatory_if_not_in': variable.mandatory = True force_remove_targets.setdefault(condition.name, []).append(target.name) elif HIGH_COMPATIBILITY and condition.name == 'disabled_if_in': variable.hidden = False remove_conditions.append(condition_idx) remove_conditions = list(set(remove_conditions)) remove_conditions.sort(reverse=True) for idx in remove_conditions: space.pop(idx) for condition_idx, condition in enumerate(space): if condition.name in ['disabled_if_in', 'disabled_if_not_in', 'frozen_if_in', 'auto_frozen_if_in', 'frozen_if_not_in', 'mandatory_if_in', 'mandatory_if_not_in']: remove_targets = [] #parse each variable and family for target_idx, target in enumerate(condition.target): if target.name in force_remove_targets.get(condition.name, []): remove_targets.append(target_idx) if target.name.startswith('creole.'): name = target.name.split('.')[-1] else: name = target.name if target.type == 'variable': variable = self.get_variable(name) else: variable = self.paths.get_family_obj(name) if name in fallback_variables: remove_targets.append(target_idx) continue if condition.name in ['disabled_if_in', 'disabled_if_not_in', 'frozen_if_in', 'frozen_if_not_in']: variable.hidden = False if condition.name in ['mandatory_if_in', 'mandatory_if_not_in']: variable.mandatory = False if HIGH_COMPATIBILITY and condition.name in ['frozen_if_in', 'frozen_if_not_in']: self.has_frozen_if_in_condition.append(name) if condition.name in ['mandatory_if_in', 'mandatory_if_not_in']: self.force_not_mandatory.append(target.name) remove_targets = list(set(remove_targets)) remove_targets.sort(reverse=True) for target_idx in remove_targets: condition.target.pop(target_idx)