creole/creole/annotator.py

1808 lines
89 KiB
Python

# 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_<conteneur>
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_<conteneur>
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_<conteneur>
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_<conteneur>
# adresse_ip_<container> 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::
<service_access service='ntp'>
<port protocol='udp' service_accesslist='ntp_udp'>123</port>
<tcpwrapper>ntpd</tcpwrapper>
</service_access>
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)