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

View file

@ -266,8 +266,12 @@ class ContainerAnnotator:
disknod.permission = 'allow'
def _update_file(self, file_, index, container_path):
if not hasattr(file_, 'source'):
file_.source = basename(file_.name)
if file_.name_type == "UnicodeOption":
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):
"""for example::
@ -483,6 +487,7 @@ class SpaceAnnotator(object):
self.remove_empty_families()
self.change_variable_mode()
self.change_family_mode()
self.dynamic_families()
self.filter_separators()
self.absolute_path_for_symlink_in_containers()
self.convert_helps()
@ -611,6 +616,17 @@ class SpaceAnnotator(object):
else:
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):
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]

View file

@ -6,7 +6,7 @@ from os import listdir
#from ast import literal_eval
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,
NetworkOption, NetmaskOption, DomainnameOption, BroadcastOption,
URLOption, EmailOption, FilenameOption, UsernameOption, DateOption,
@ -117,9 +117,9 @@ class PopulateTiramisuObjects(object):
def make_tiramisu_objects(self, xmlroot, creolefunc_file):
elt = Elt({'name': 'baseoption'})
family = Family(elt, self.booleans, self.storage)
self.storage.add('.', family)
self.eosfunc = imp.load_source('eosfunc', creolefunc_file)
family = Family(elt, self.booleans, self.storage, self.eosfunc)
self.storage.add('.', family)
elts = {}
for elt in xmlroot:
@ -167,7 +167,7 @@ class PopulateTiramisuObjects(object):
force_icon = False
else:
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)
self.storage.add(path, family)
return family
@ -181,7 +181,7 @@ class PopulateTiramisuObjects(object):
def _iter_leader(self, leader, subpath):
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()
self.storage.add(subpath, family)
leader_name = None
@ -474,7 +474,7 @@ class Variable(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.attrib = {}
self.is_leader = False
@ -484,6 +484,7 @@ class Family(Common):
self.informations = {}
self.children = []
self.storage = storage
self.eosfunc = eosfunc
self.attrib['properties'] = []
for key, value in elt.attrib.items():
if key in booleans:
@ -528,11 +529,16 @@ class Family(Common):
self.attrib['children'].append(child.get())
self.build_properties()
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)
else:
option = Leadership(**self.attrib)
#option = OptionDescription(**self.attrib)
except Exception as err:
raise CreoleLoaderError(_('cannot create optiondescription {}: {}').format(self.attrib['name'], err))
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):
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 json import load
#from creole.xmlreflector import CreoleObjSpace, CreoleDictConsistencyError
#from creole import xmlreflector
from rougail import objspace, annotator
from creole.xml_compare import xml_compare
from creole.error import CreoleDictConsistencyError
from rougail.xml_compare import xml_compare
from rougail.error import CreoleDictConsistencyError
from rougail.config import dtdfilename
@ -85,7 +83,7 @@ def launch_flattener(test_dir):
eolobj.save(destfile)
result_file = join(test_dir, 'result/00-base.xml')
if isfile(result_file):
# eolobj.save(result_file)
# eolobj.save(result_file)
compare_xml(destfile, result_file)

View file

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

View file

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