dynamic option description and filename with calculation

This commit is contained in:
Emmanuel Garette 2019-12-21 12:21:42 +01:00
parent 729b35d372
commit 024fecddbb
14 changed files with 172 additions and 23 deletions

View file

@ -149,6 +149,7 @@
<!ELEMENT file EMPTY> <!ELEMENT file EMPTY>
<!ATTLIST file name CDATA #REQUIRED > <!ATTLIST file name CDATA #REQUIRED >
<!ATTLIST file name_type (UnicodeOption|SymLinkOption) "UnicodeOption">
<!ATTLIST file source CDATA #IMPLIED> <!ATTLIST file source CDATA #IMPLIED>
<!ATTLIST file mode CDATA #IMPLIED > <!ATTLIST file mode CDATA #IMPLIED >
<!ATTLIST file owner CDATA #IMPLIED > <!ATTLIST file owner CDATA #IMPLIED >
@ -166,6 +167,7 @@
<!ATTLIST family mode (basic|normal|expert) "basic"> <!ATTLIST family mode (basic|normal|expert) "basic">
<!ATTLIST family icon CDATA #IMPLIED> <!ATTLIST family icon CDATA #IMPLIED>
<!ATTLIST family hidden (True|False) "False"> <!ATTLIST family hidden (True|False) "False">
<!ATTLIST family dynamic CDATA #IMPLIED>
<!ELEMENT variable (#PCDATA | value)*> <!ELEMENT variable (#PCDATA | value)*>
<!ATTLIST variable name CDATA #REQUIRED> <!ATTLIST variable name CDATA #REQUIRED>

View file

@ -266,8 +266,12 @@ class ContainerAnnotator:
disknod.permission = 'allow' disknod.permission = 'allow'
def _update_file(self, file_, index, container_path): def _update_file(self, file_, index, container_path):
if not hasattr(file_, 'source'): if file_.name_type == "UnicodeOption":
file_.source = basename(file_.name) if not hasattr(file_, 'source'):
file_.source = basename(file_.name)
elif not hasattr(file_, 'source'):
raise CreoleDictConsistencyError(_('attribute source mandatory for file with SymLinkOption name '
'for {}').format(file_.name))
def _split_elts(self, name, key, value, elt): def _split_elts(self, name, key, value, elt):
"""for example:: """for example::
@ -483,6 +487,7 @@ class SpaceAnnotator(object):
self.remove_empty_families() self.remove_empty_families()
self.change_variable_mode() self.change_variable_mode()
self.change_family_mode() self.change_family_mode()
self.dynamic_families()
self.filter_separators() self.filter_separators()
self.absolute_path_for_symlink_in_containers() self.absolute_path_for_symlink_in_containers()
self.convert_helps() self.convert_helps()
@ -611,6 +616,17 @@ class SpaceAnnotator(object):
else: else:
family.mode = mode family.mode = mode
def dynamic_families(self): # pylint: disable=C0111
if not hasattr(self.space, 'variables'):
return
for family in self.space.variables.values():
if hasattr(family, 'family'):
for family in family.family.values():
if 'dynamic' in vars(family):
namespace = self.paths.get_variable_namespace(family.dynamic)
varpath = self.paths.get_variable_path(family.dynamic, namespace)
family.dynamic = varpath
def _annotate_variable(self, variable, family_mode, path, is_follower=False): def _annotate_variable(self, variable, family_mode, path, is_follower=False):
if (HIGH_COMPATIBILITY and variable.type == 'choice' and variable.mode != modes_level[-1] and variable.mandatory is True and path in self.default_has_no_value): if (HIGH_COMPATIBILITY and variable.type == 'choice' and variable.mode != modes_level[-1] and variable.mandatory is True and path in self.default_has_no_value):
variable.mode = modes_level[0] variable.mode = modes_level[0]

View file

@ -6,7 +6,7 @@ from os import listdir
#from ast import literal_eval #from ast import literal_eval
from lxml.etree import parse, DTD from lxml.etree import parse, DTD
from tiramisu.option import (StrOption, OptionDescription, PortOption, from tiramisu.option import (StrOption, OptionDescription, DynOptionDescription, PortOption,
IntOption, ChoiceOption, BoolOption, SymLinkOption, IPOption, IntOption, ChoiceOption, BoolOption, SymLinkOption, IPOption,
NetworkOption, NetmaskOption, DomainnameOption, BroadcastOption, NetworkOption, NetmaskOption, DomainnameOption, BroadcastOption,
URLOption, EmailOption, FilenameOption, UsernameOption, DateOption, URLOption, EmailOption, FilenameOption, UsernameOption, DateOption,
@ -117,9 +117,9 @@ class PopulateTiramisuObjects(object):
def make_tiramisu_objects(self, xmlroot, creolefunc_file): def make_tiramisu_objects(self, xmlroot, creolefunc_file):
elt = Elt({'name': 'baseoption'}) elt = Elt({'name': 'baseoption'})
family = Family(elt, self.booleans, self.storage)
self.storage.add('.', family)
self.eosfunc = imp.load_source('eosfunc', creolefunc_file) self.eosfunc = imp.load_source('eosfunc', creolefunc_file)
family = Family(elt, self.booleans, self.storage, self.eosfunc)
self.storage.add('.', family)
elts = {} elts = {}
for elt in xmlroot: for elt in xmlroot:
@ -167,7 +167,7 @@ class PopulateTiramisuObjects(object):
force_icon = False force_icon = False
else: else:
force_icon = not subpath.startswith('containers') and not subpath.startswith('actions') force_icon = not subpath.startswith('containers') and not subpath.startswith('actions')
family = Family(elt, self.booleans, self.storage, force_icon) family = Family(elt, self.booleans, self.storage, self.eosfunc, force_icon)
path = self._build_path(subpath, elt) path = self._build_path(subpath, elt)
self.storage.add(path, family) self.storage.add(path, family)
return family return family
@ -181,7 +181,7 @@ class PopulateTiramisuObjects(object):
def _iter_leader(self, leader, subpath): def _iter_leader(self, leader, subpath):
subpath = self._build_path(subpath, leader) subpath = self._build_path(subpath, leader)
family = Family(leader, self.booleans, self.storage) family = Family(leader, self.booleans, self.storage, self.eosfunc)
family.set_leader() family.set_leader()
self.storage.add(subpath, family) self.storage.add(subpath, family)
leader_name = None leader_name = None
@ -474,7 +474,7 @@ class Variable(Common):
class Family(Common): class Family(Common):
def __init__(self, elt, booleans, storage, force_icon=False): def __init__(self, elt, booleans, storage, eosfunc, force_icon=False):
self.option = None self.option = None
self.attrib = {} self.attrib = {}
self.is_leader = False self.is_leader = False
@ -484,6 +484,7 @@ class Family(Common):
self.informations = {} self.informations = {}
self.children = [] self.children = []
self.storage = storage self.storage = storage
self.eosfunc = eosfunc
self.attrib['properties'] = [] self.attrib['properties'] = []
for key, value in elt.attrib.items(): for key, value in elt.attrib.items():
if key in booleans: if key in booleans:
@ -528,11 +529,16 @@ class Family(Common):
self.attrib['children'].append(child.get()) self.attrib['children'].append(child.get())
self.build_properties() self.build_properties()
try: try:
if not self.is_leader: if 'dynamic' in self.attrib:
dynamic = self.storage.get(self.attrib['dynamic']).get()
del self.attrib['dynamic']
self.attrib['suffixes'] = Calculation(self.eosfunc.calc_value,
Params((ParamOption(dynamic),)))
option = DynOptionDescription(**self.attrib)
elif not self.is_leader:
option = OptionDescription(**self.attrib) option = OptionDescription(**self.attrib)
else: else:
option = Leadership(**self.attrib) option = Leadership(**self.attrib)
#option = OptionDescription(**self.attrib)
except Exception as err: except Exception as err:
raise CreoleLoaderError(_('cannot create optiondescription {}: {}').format(self.attrib['name'], err)) raise CreoleLoaderError(_('cannot create optiondescription {}: {}').format(self.attrib['name'], err))
for key, value in self.informations.items(): for key, value in self.informations.items():

View file

@ -1,4 +1,4 @@
from tiramisu import valid_not_equal, valid_ip_netmask from tiramisu import valid_not_equal, valid_ip_netmask, calc_value
def calc_val(*args, **kwargs): def calc_val(*args, **kwargs):
pass pass

View file

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<creole>
<variables>
<family name='general'>
<variable name='varname' type='string' description="No change" multi="True">
<value>val1</value>
<value>val2</value>
</variable>
</family>
<family name='dyn' dynamic="varname">
<variable name='vardyn' type='string' description="No change"/>
</family>
</variables>
</creole>

View file

@ -0,0 +1 @@
{"creole.general.varname": ["val1", "val2"], "creole.dynval1.vardynval1": null, "creole.dynval2.vardynval2": null}

View file

@ -0,0 +1,20 @@
<?xml version='1.0' encoding='UTF-8'?>
<creole>
<family doc="" name="creole">
<family doc="general" name="general">
<property>normal</property>
<variable doc="No change" multi="True" name="varname" type="string">
<property>mandatory</property>
<property>normal</property>
<value>val1</value>
<value>val2</value>
</variable>
</family>
<family dynamic="creole.general.varname" doc="dyn" name="dyn">
<property>normal</property>
<variable doc="No change" multi="False" name="vardyn" type="string">
<property>normal</property>
</variable>
</family>
</family>
</creole>

View file

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<creole>
<containers>
<container name='test' id='23'>
<file name='file_name' name_type="SymLinkOption" source="mailname"/>
</container>
</containers>
<variables>
<family name='général'>
<variable name='mode_conteneur_actif' type='oui/non' description="No change" hidden="True">
<value>oui</value>
</variable>
<variable name='file_name' type='string' multi='True'>
<value>/etc/mailname</value>
<value>/etc/mailname2</value>
</variable>
</family>
<separators/>
</variables>
</creole>

View file

@ -0,0 +1 @@
{"creole.general.file_name": ["/etc/mailname", "/etc/mailname2"], "containers.container0.files.file0.mkdir": false, "containers.container0.files.file0.name": ["/etc/mailname", "/etc/mailname2"], "containers.container0.files.file0.rm": false, "containers.container0.files.file0.source": "mailname", "containers.container0.files.file0.activate": true}

View file

@ -0,0 +1,47 @@
<?xml version='1.0' encoding='UTF-8'?>
<creole>
<family name="containers">
<family name="container0" doc="test">
<family doc="files" name="files">
<family doc="file0" name="file0">
<variable doc="" multi="False" name="mkdir" type="boolean">
<value>False</value>
</variable>
<variable multi="False" name="name" opt="creole.general.file_name" type="symlink"/>
<variable doc="" multi="False" name="rm" type="boolean">
<value>False</value>
</variable>
<variable doc="" multi="False" name="source" type="string">
<value>mailname</value>
</variable>
<variable doc="" multi="False" name="activate" type="boolean">
<value>True</value>
</variable>
</family>
</family>
<property>basic</property>
</family>
</family>
<family doc="" name="creole">
<family doc="général" name="general">
<property>normal</property>
<variable doc="No change" multi="False" name="mode_conteneur_actif" type="choice">
<choice type="string">oui</choice>
<choice type="string">non</choice>
<property>force_default_on_freeze</property>
<property>frozen</property>
<property>hidden</property>
<property>mandatory</property>
<property>normal</property>
<value type="string">oui</value>
</variable>
<variable doc="file_name" multi="True" name="file_name" type="string">
<property>mandatory</property>
<property>normal</property>
<value>/etc/mailname</value>
<value>/etc/mailname2</value>
</variable>
</family>
<separators/>
</family>
</creole>

View file

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<creole>
<containers>
<container name='test' id='23'>
<file name='file_name' name_type="SymLinkOption"/>
</container>
</containers>
<variables>
<family name='général'>
<variable name='mode_conteneur_actif' type='oui/non' description="No change" hidden="True">
<value>oui</value>
</variable>
<variable name='file_name' type='string'>
<value>/etc/mailname</value>
</variable>
</family>
<separators/>
</variables>
</creole>

View file

@ -4,11 +4,9 @@ from pytest import fixture, raises
from os import listdir from os import listdir
from json import load from json import load
#from creole.xmlreflector import CreoleObjSpace, CreoleDictConsistencyError
#from creole import xmlreflector
from rougail import objspace, annotator from rougail import objspace, annotator
from creole.xml_compare import xml_compare from rougail.xml_compare import xml_compare
from creole.error import CreoleDictConsistencyError from rougail.error import CreoleDictConsistencyError
from rougail.config import dtdfilename from rougail.config import dtdfilename
@ -85,7 +83,7 @@ def launch_flattener(test_dir):
eolobj.save(destfile) eolobj.save(destfile)
result_file = join(test_dir, 'result/00-base.xml') result_file = join(test_dir, 'result/00-base.xml')
if isfile(result_file): if isfile(result_file):
# eolobj.save(result_file) # eolobj.save(result_file)
compare_xml(destfile, result_file) compare_xml(destfile, result_file)

View file

@ -24,7 +24,7 @@ for test in listdir(dico_dirs):
excludes = set([]) excludes = set([])
excludes = set([]) excludes = set([])
test_ok -= excludes test_ok -= excludes
#test_ok = ['70container_all'] #test_ok = ['20family_dynamic']
test_ok = list(test_ok) test_ok = list(test_ok)

View file

@ -22,13 +22,11 @@ def test_dictionary(test_dir):
test_dir = join(template_dirs, test_dir) test_dir = join(template_dirs, test_dir)
tmp_dir = join(test_dir, 'tmp') tmp_dir = join(test_dir, 'tmp')
funcs_file = join(template_dirs, '../eosfunc/test.py') funcs_file = join(template_dirs, '../eosfunc/test.py')
template.distrib_dir = join(test_dir, 'tmpl') distrib_dir = join(test_dir, 'tmpl')
template.templatedir = tmp_dir
if isdir(tmp_dir): if isdir(tmp_dir):
rmtree(tmp_dir) rmtree(tmp_dir)
mkdir(tmp_dir) mkdir(tmp_dir)
dest_dir = join(test_dir, 'dest') dest_dir = join(test_dir, 'dest')
template.dest_dir = dest_dir
if isdir(dest_dir): if isdir(dest_dir):
rmtree(dest_dir) rmtree(dest_dir)
mkdir(dest_dir) mkdir(dest_dir)
@ -41,13 +39,19 @@ def test_dictionary(test_dir):
config = Config(optiondescription) config = Config(optiondescription)
config.property.read_only() config.property.read_only()
template.generate(config, template.generate(config,
funcs_file) funcs_file,
distrib_dir,
tmp_dir,
dest_dir)
list_templates = set(listdir(dest_dir)) if isdir(join(dest_dir, 'test')):
list_templates = set(listdir(join(dest_dir, 'test')))
else:
list_templates = set()
list_results = set(listdir(join(test_dir, 'result'))) list_results = set(listdir(join(test_dir, 'result')))
assert list_templates == list_results assert list_templates == list_results
for result in listdir(join(test_dir, 'result')): for result in listdir(join(test_dir, 'result')):
template_file = join(dest_dir, result) template_file = join(dest_dir, 'test', result)
if not isfile(template_file): if not isfile(template_file):
raise Exception(f'{template_file} is not generated') raise Exception(f'{template_file} is not generated')
with open(join(test_dir, 'result', result), 'r') as fh: with open(join(test_dir, 'result', result), 'r') as fh: