Compare commits

...

2 Commits

20 changed files with 169 additions and 32 deletions

View File

@ -9,8 +9,8 @@ from shutil import copy
import logging import logging
from typing import Dict, Any from typing import Dict, Any
from subprocess import call from subprocess import call
from os import listdir, makedirs from os import listdir, makedirs, getcwd, chdir
from os.path import dirname, join, isfile, abspath, normpath from os.path import dirname, join, isfile, abspath, normpath, relpath
from Cheetah.Template import Template as ChtTemplate from Cheetah.Template import Template as ChtTemplate
from Cheetah.NameMapper import NotFound as CheetahNotFound from Cheetah.NameMapper import NotFound as CheetahNotFound
@ -79,7 +79,8 @@ class CheetahTemplate(ChtTemplate):
context, context,
eosfunc: Dict, eosfunc: Dict,
destfilename, destfilename,
variable): variable,
):
"""Initialize Creole CheetahTemplate """Initialize Creole CheetahTemplate
""" """
extra_context = {'is_defined' : IsDefined(context), extra_context = {'is_defined' : IsDefined(context),
@ -93,11 +94,15 @@ class CheetahTemplate(ChtTemplate):
searchList=[context, eosfunc, extra_context]) searchList=[context, eosfunc, extra_context])
# FORK of Cheetah fonction, do not replace '\\' by '/' # FORK of Cheetah fonction, do not replace '\\' by '/'
def serverSidePath(self, path=None, def serverSidePath(self,
path=None,
normpath=normpath, normpath=normpath,
abspath=abspath abspath=abspath
): ):
# strange...
if path is None and isinstance(self, str):
path = self
if path: if path:
return normpath(abspath(path)) return normpath(abspath(path))
# return normpath(abspath(path.replace("\\", '/'))) # return normpath(abspath(path.replace("\\", '/')))
@ -107,7 +112,6 @@ class CheetahTemplate(ChtTemplate):
return None return None
class CreoleLeader: class CreoleLeader:
def __init__(self, value, follower=None, index=None): def __init__(self, value, follower=None, index=None):
""" """
@ -302,37 +306,43 @@ class CreoleTemplateEngine:
return CreoleExtra(families) return CreoleExtra(families)
def patch_template(self, def patch_template(self,
filename: str): filename: str,
tmp_dir: str,
patch_dir: str,
) -> None:
"""Apply patch to a template """Apply patch to a template
""" """
patch_cmd = ['patch', '-d', self.tmp_dir, '-N', '-p1'] patch_cmd = ['patch', '-d', tmp_dir, '-N', '-p1']
patch_no_debug = ['-s', '-r', '-', '--backup-if-mismatch'] patch_no_debug = ['-s', '-r', '-', '--backup-if-mismatch']
# patches variante + locaux patch_file = join(patch_dir, f'{filename}.patch')
for directory in [join(Config['patch_dir'], 'variante'), Config['patch_dir']]: if isfile(patch_file):
patch_file = join(directory, f'{filename}.patch') log.info(_("Patching template '{filename}' with '{patch_file}'"))
if isfile(patch_file): rel_patch_file = relpath(patch_file, tmp_dir)
log.info(_("Patching template '{filename}' with '{patch_file}'")) ret = call(patch_cmd + patch_no_debug + ['-i', rel_patch_file])
ret = call(patch_cmd + patch_no_debug + ['-i', patch_file]) if ret:
if ret: patch_cmd_err = ' '.join(patch_cmd + ['-i', rel_patch_file])
patch_cmd_err = ' '.join(patch_cmd + ['-i', patch_file]) log.error(_(f"Error applying patch: '{rel_patch_file}'\nTo reproduce and fix this error {patch_cmd_err}"))
log.error(_(f"Error applying patch: '{patch_file}'\nTo reproduce and fix this error {patch_cmd_err}")) copy(join(self.distrib_dir, filename), tmp_dir)
copy(filename, self.tmp_dir)
def prepare_template(self, def prepare_template(self,
filename: str): filename: str,
tmp_dir: str,
patch_dir: str,
) -> None:
"""Prepare template source file """Prepare template source file
""" """
log.info(_("Copy template: '{filename}' -> '{self.tmp_dir}'")) log.info(_("Copy template: '{filename}' -> '{tmp_dir}'"))
copy(filename, self.tmp_dir) copy(filename, tmp_dir)
self.patch_template(filename) self.patch_template(filename, tmp_dir, patch_dir)
def process(self, def process(self,
source: str, source: str,
true_destfilename: str, true_destfilename: str,
destfilename: str, destfilename: str,
filevar: Dict, filevar: Dict,
variable: Any): variable: Any,
):
"""Process a cheetah template """Process a cheetah template
""" """
# full path of the destination file # full path of the destination file
@ -356,7 +366,10 @@ class CreoleTemplateEngine:
def instance_file(self, def instance_file(self,
filevar: Dict, filevar: Dict,
service_name: str) -> None: service_name: str,
tmp_dir: str,
dest_dir: str,
) -> None:
"""Run templatisation on one file """Run templatisation on one file
""" """
log.info(_("Instantiating file '{filename}'")) log.info(_("Instantiating file '{filename}'"))
@ -370,13 +383,13 @@ class CreoleTemplateEngine:
if variable: if variable:
variable = [variable] variable = [variable]
for idx, filename in enumerate(filenames): for idx, filename in enumerate(filenames):
destfilename = join(self.dest_dir, filename[1:]) destfilename = join(dest_dir, filename[1:])
makedirs(dirname(destfilename), exist_ok=True) makedirs(dirname(destfilename), exist_ok=True)
if variable: if variable:
var = variable[idx] var = variable[idx]
else: else:
var = None var = None
source = join(self.tmp_dir, filevar['source']) source = join(tmp_dir, filevar['source'])
if filevar['templating']: if filevar['templating']:
self.process(source, self.process(source,
filename, filename,
@ -389,6 +402,11 @@ class CreoleTemplateEngine:
async def instance_files(self) -> None: async def instance_files(self) -> None:
"""Run templatisation on all files """Run templatisation on all files
""" """
ori_dir = getcwd()
tmp_dir = relpath(self.tmp_dir, self.distrib_dir)
dest_dir = relpath(self.dest_dir, self.distrib_dir)
patch_dir = relpath(Config['patch_dir'], self.distrib_dir)
chdir(self.distrib_dir)
for option in await self.config.option.list(type='all'): for option in await self.config.option.list(type='all'):
namespace = await option.option.name() namespace = await option.option.name()
if namespace == Config['variable_namespace']: if namespace == Config['variable_namespace']:
@ -397,8 +415,8 @@ class CreoleTemplateEngine:
families = await self.load_eole_variables(namespace, families = await self.load_eole_variables(namespace,
option) option)
self.rougail_variables_dict[namespace] = families self.rougail_variables_dict[namespace] = families
for template in listdir(self.distrib_dir): for template in listdir('.'):
self.prepare_template(join(self.distrib_dir, template)) self.prepare_template(template, tmp_dir, patch_dir)
for service_obj in await self.config.option('services').list('all'): for service_obj in await self.config.option('services').list('all'):
service_name = await service_obj.option.doc() service_name = await service_obj.option.doc()
for fills in await service_obj.list('all'): for fills in await service_obj.list('all'):
@ -406,15 +424,17 @@ class CreoleTemplateEngine:
for fill_obj in await fills.list('all'): for fill_obj in await fills.list('all'):
fill = await fill_obj.value.dict() fill = await fill_obj.value.dict()
filename = fill['source'] filename = fill['source']
distib_file = join(self.distrib_dir, filename) if not isfile(filename):
if not isfile(distib_file): raise FileNotFound(_(f"File {filename} does not exist."))
raise FileNotFound(_(f"File {distib_file} does not exist."))
if fill.get('activate', False): if fill.get('activate', False):
self.instance_file(fill, self.instance_file(fill,
service_name, service_name,
tmp_dir,
dest_dir,
) )
else: else:
log.debug(_("Instantiation of file '{filename}' disabled")) log.debug(_("Instantiation of file '{filename}' disabled"))
chdir(ori_dir)
async def generate(config: Config, async def generate(config: Config,

View File

@ -0,0 +1,20 @@
<?xml version='1.0' encoding='UTF-8'?>
<rougail>
<services>
<service name="test">
<file name="/etc/file"/>
</service>
</services>
<variables>
<family name="general">
<variable name="mode_conteneur_actif" type="oui/non" description="Description">
<value>non</value>
</variable>
</family>
<separators/>
</variables>
</rougail>
<!-- vim: ts=4 sw=4 expandtab
-->

View File

@ -0,0 +1 @@
{"rougail.general.mode_conteneur_actif": "non", "services.test.files.file.group": "root", "services.test.files.file.mode": "0644", "services.test.files.file.name": "/etc/file", "services.test.files.file.owner": "root", "services.test.files.file.source": "file", "services.test.files.file.templating": true, "services.test.files.file.activate": true}

View File

@ -0,0 +1 @@
non

View File

@ -0,0 +1,26 @@
import imp
func = imp.load_source('func', 'tests/dictionaries/../eosfunc/test.py')
for key, value in dict(locals()).items():
if key != ['imp', 'func']:
setattr(func, key, value)
try:
from tiramisu3 import *
except:
from tiramisu import *
from rougail.tiramisu import ConvertDynOptionDescription
option_3 = ChoiceOption(properties=frozenset({'mandatory', 'normal'}), name='mode_conteneur_actif', doc='Description', multi=False, default='non', values=('oui', 'non'))
option_2 = OptionDescription(name='general', doc='general', properties=frozenset({'normal'}), children=[option_3])
option_1 = OptionDescription(name='rougail', doc='rougail', children=[option_2])
option_8 = StrOption(name='group', doc='group', multi=False, default='root')
option_9 = StrOption(name='mode', doc='mode', multi=False, default='0644')
option_10 = StrOption(name='name', doc='name', multi=False, default='/etc/file')
option_11 = StrOption(name='owner', doc='owner', multi=False, default='root')
option_12 = StrOption(name='source', doc='source', multi=False, default='file')
option_13 = BoolOption(name='templating', doc='templating', multi=False, default=True)
option_14 = BoolOption(name='activate', doc='activate', multi=False, default=True)
option_7 = OptionDescription(name='file', doc='file', children=[option_8, option_9, option_10, option_11, option_12, option_13, option_14])
option_6 = OptionDescription(name='files', doc='files', children=[option_7])
option_5 = OptionDescription(name='test', doc='test', children=[option_6])
option_5.impl_set_information("manage", True)
option_4 = OptionDescription(name='services', doc='services', properties=frozenset({'hidden'}), children=[option_5])
option_0 = OptionDescription(name='baseoption', doc='baseoption', children=[option_1, option_4])

View File

@ -0,0 +1 @@
%include "incfile"

View File

@ -0,0 +1 @@
%%mode_conteneur_actif

View File

@ -0,0 +1,20 @@
<?xml version='1.0' encoding='UTF-8'?>
<rougail>
<services>
<service name="test">
<file name="/etc/file"/>
</service>
</services>
<variables>
<family name="general">
<variable name="mode_conteneur_actif" type="oui/non" description="Description">
<value>non</value>
</variable>
</family>
<separators/>
</variables>
</rougail>
<!-- vim: ts=4 sw=4 expandtab
-->

View File

@ -0,0 +1 @@
{"rougail.general.mode_conteneur_actif": "non", "services.test.files.file.group": "root", "services.test.files.file.mode": "0644", "services.test.files.file.name": "/etc/file", "services.test.files.file.owner": "root", "services.test.files.file.source": "file", "services.test.files.file.templating": true, "services.test.files.file.activate": true}

View File

@ -0,0 +1,5 @@
--- tmpl/file 2020-11-20 07:44:38.588472784 +0100
+++ dest/file 2020-11-20 07:44:54.588536011 +0100
@@ -1 +1 @@
-unpatched
+patched

View File

@ -0,0 +1 @@
patched

View File

@ -0,0 +1,26 @@
import imp
func = imp.load_source('func', 'tests/dictionaries/../eosfunc/test.py')
for key, value in dict(locals()).items():
if key != ['imp', 'func']:
setattr(func, key, value)
try:
from tiramisu3 import *
except:
from tiramisu import *
from rougail.tiramisu import ConvertDynOptionDescription
option_3 = ChoiceOption(properties=frozenset({'mandatory', 'normal'}), name='mode_conteneur_actif', doc='Description', multi=False, default='non', values=('oui', 'non'))
option_2 = OptionDescription(name='general', doc='general', properties=frozenset({'normal'}), children=[option_3])
option_1 = OptionDescription(name='rougail', doc='rougail', children=[option_2])
option_8 = StrOption(name='group', doc='group', multi=False, default='root')
option_9 = StrOption(name='mode', doc='mode', multi=False, default='0644')
option_10 = StrOption(name='name', doc='name', multi=False, default='/etc/file')
option_11 = StrOption(name='owner', doc='owner', multi=False, default='root')
option_12 = StrOption(name='source', doc='source', multi=False, default='file')
option_13 = BoolOption(name='templating', doc='templating', multi=False, default=True)
option_14 = BoolOption(name='activate', doc='activate', multi=False, default=True)
option_7 = OptionDescription(name='file', doc='file', children=[option_8, option_9, option_10, option_11, option_12, option_13, option_14])
option_6 = OptionDescription(name='files', doc='files', children=[option_7])
option_5 = OptionDescription(name='test', doc='test', children=[option_6])
option_5.impl_set_information("manage", True)
option_4 = OptionDescription(name='services', doc='services', properties=frozenset({'hidden'}), children=[option_5])
option_0 = OptionDescription(name='baseoption', doc='baseoption', children=[option_1, option_4])

View File

@ -0,0 +1 @@
unpatched

View File

@ -1,4 +1,5 @@
from lxml import etree from lxml import etree
from os import getcwd
from os.path import isfile, join, isdir from os.path import isfile, join, isdir
from pytest import fixture, raises from pytest import fixture, raises
from os import listdir from os import listdir
@ -25,11 +26,14 @@ for test in listdir(dico_dirs):
test_raise.add(test) test_raise.add(test)
excludes = set([]) excludes = set([])
#excludes = set(['01base_file_utfchar'])
test_ok -= excludes test_ok -= excludes
test_raise -= excludes test_raise -= excludes
#test_ok = ['10leadership_autoleader'] #test_ok = ['10leadership_autoleader']
#test_raise = [] #test_raise = []
ORI_DIR = getcwd()
debug = False debug = False
#debug = True #debug = True
@ -80,6 +84,7 @@ def launch_flattener(test_dir, test_ok=False):
if isdir(subfolder): if isdir(subfolder):
eolobj.create_or_populate_from_xml('extra1', [subfolder]) eolobj.create_or_populate_from_xml('extra1', [subfolder])
eosfunc = join(dico_dirs, '../eosfunc/test.py') eosfunc = join(dico_dirs, '../eosfunc/test.py')
Config['patch_dir'] = join(test_dir, 'patches')
eolobj.space_visitor(eosfunc) eolobj.space_visitor(eosfunc)
tiramisu_objects = eolobj.save() tiramisu_objects = eolobj.save()
tiramisu_dir = join(test_dir, 'tiramisu') tiramisu_dir = join(test_dir, 'tiramisu')
@ -109,14 +114,18 @@ def teardown_module(module):
def test_dictionary(test_dir): def test_dictionary(test_dir):
assert getcwd() == ORI_DIR
test_dir = join(dico_dirs, test_dir) test_dir = join(dico_dirs, test_dir)
launch_flattener(test_dir, True) launch_flattener(test_dir, True)
assert getcwd() == ORI_DIR
def test_error_dictionary(test_dir_error): def test_error_dictionary(test_dir_error):
assert getcwd() == ORI_DIR
test_dir = join(dico_dirs, test_dir_error) test_dir = join(dico_dirs, test_dir_error)
with raises(DictConsistencyError): with raises(DictConsistencyError):
launch_flattener(test_dir) launch_flattener(test_dir)
assert getcwd() == ORI_DIR
def test_no_dtd(): def test_no_dtd():

View File

@ -20,7 +20,7 @@ for test in listdir(dico_dirs):
debug = False debug = False
#debug = True #debug = True
excludes = set([]) excludes = set([])
#excludes = set(['70container_services']) #excludes = set(['01base_file_utfchar'])
test_ok -= excludes test_ok -= excludes
#test_ok = ['10check_valid_ipnetmask'] #test_ok = ['10check_valid_ipnetmask']

View File

@ -9,7 +9,10 @@ from rougail import template
template_dirs = 'tests/dictionaries' template_dirs = 'tests/dictionaries'
test_ok = [f for f in listdir(template_dirs) if not f.startswith('_') and isdir(join(template_dirs, f, 'tmpl'))] excludes = set([])
#excludes = set(['01base_file_utfchar'])
test_ok = {f for f in listdir(template_dirs) if not f.startswith('_') and isdir(join(template_dirs, f, 'tmpl'))}
test_ok -= excludes
#test_ok = ['60extra_group'] #test_ok = ['60extra_group']
@ -55,6 +58,7 @@ async def test_dictionary(test_dir):
if isdir(dest_dir): if isdir(dest_dir):
rmtree(dest_dir) rmtree(dest_dir)
mkdir(dest_dir) mkdir(dest_dir)
template.Config['patch_dir'] = join(test_dir, 'patches')
await template.generate(config, await template.generate(config,
funcs_file, funcs_file,
distrib_dir, distrib_dir,