|
|
|
@ -3,7 +3,8 @@ from calculate.templates.template_engine import TemplateEngine, Variables,\
|
|
|
|
|
from calculate.templates.template_processor import TemplateAction
|
|
|
|
|
from calculate.utils.package import PackageAtomParser, Package, PackageNotFound
|
|
|
|
|
from calculate.utils.files import write_file, read_link, read_file_lines,\
|
|
|
|
|
FilesError, join_paths
|
|
|
|
|
FilesError, join_paths,\
|
|
|
|
|
check_directory_link, Process
|
|
|
|
|
from calculate.utils.mount import Mounts
|
|
|
|
|
from collections import OrderedDict
|
|
|
|
|
import hashlib
|
|
|
|
@ -12,25 +13,32 @@ import glob
|
|
|
|
|
import shutil
|
|
|
|
|
import os
|
|
|
|
|
|
|
|
|
|
template_text = '''{% calculate append = 'link', source = '/etc/dir/dir_2' -%}
|
|
|
|
|
template_text = '''{% calculate append = 'join' -%}
|
|
|
|
|
{% calculate package = 'test-category/test-package', format = 'samba' -%}
|
|
|
|
|
[section one]
|
|
|
|
|
parameter_1 = {{ vars_1.value_1 }}
|
|
|
|
|
!parameter_2
|
|
|
|
|
'''
|
|
|
|
|
|
|
|
|
|
template_to_run = '''{% calculate run = "/usr/bin/python" -%}
|
|
|
|
|
with open('etc/dir/file.conf', 'r') as input_file:
|
|
|
|
|
print(input_file.read())
|
|
|
|
|
'''
|
|
|
|
|
|
|
|
|
|
backup_template_text = '''{% calculate append = 'join', format = 'samba',
|
|
|
|
|
autoupdate, package = 'test-category/test-package' -%}
|
|
|
|
|
package = 'test-category/test-package' -%}
|
|
|
|
|
[section one]
|
|
|
|
|
parameter_1 = value
|
|
|
|
|
parameter_2 = value_2
|
|
|
|
|
[section two]
|
|
|
|
|
other_parameter = other_value
|
|
|
|
|
[!section_name]
|
|
|
|
|
'''
|
|
|
|
|
|
|
|
|
|
APPENDS_SET = TemplateAction().available_appends
|
|
|
|
|
|
|
|
|
|
vars_1 = Variables({'value_1': 'value_1'})
|
|
|
|
|
vars_1 = Variables({'value_1': 'value_1', 'value_2': 'value_to_print',
|
|
|
|
|
'value_3': 5})
|
|
|
|
|
DATAVARS_MODULE = Variables({'vars_1': vars_1})
|
|
|
|
|
|
|
|
|
|
CHROOT_PATH = os.path.join(os.getcwd(), 'tests/templates/testfiles/test_root')
|
|
|
|
@ -43,6 +51,7 @@ template_engine = TemplateEngine(datavars_module=DATAVARS_MODULE,
|
|
|
|
|
chroot_path=CHROOT_PATH)
|
|
|
|
|
|
|
|
|
|
target_path = os.path.join(CHROOT_PATH, 'etc/dir/file.conf')
|
|
|
|
|
run_target_path = os.path.join(CHROOT_PATH, 'file_to_run.py')
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class TemplateExecutorError(Exception):
|
|
|
|
@ -196,13 +205,19 @@ class TemplateWrapper:
|
|
|
|
|
# Временный флаг для определения того, является ли шаблон userspace.
|
|
|
|
|
self.is_userspace = False
|
|
|
|
|
|
|
|
|
|
# Получаем класс соответствующего формата файла.
|
|
|
|
|
if self.parameters.format:
|
|
|
|
|
self.format_class = ParametersProcessor.\
|
|
|
|
|
available_formats[self.parameters.format]
|
|
|
|
|
else:
|
|
|
|
|
# Здесь будет детектор форматов.
|
|
|
|
|
pass
|
|
|
|
|
if self.parameters.run or self.parameters.exec:
|
|
|
|
|
# Если есть параметр run или exec, то кроме текста шаблона ничего
|
|
|
|
|
# не нужно.
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
if self.parameters.append in {'join', 'before', 'after'}:
|
|
|
|
|
# Получаем класс соответствующего формата файла.
|
|
|
|
|
if self.parameters.format:
|
|
|
|
|
self.format_class = ParametersProcessor.\
|
|
|
|
|
available_formats[self.parameters.format]
|
|
|
|
|
else:
|
|
|
|
|
# Здесь будет детектор форматов.
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
# Если по этому пути что-то есть -- проверяем конфликты.
|
|
|
|
|
if os.path.exists(target_file_path):
|
|
|
|
@ -243,27 +258,26 @@ class TemplateWrapper:
|
|
|
|
|
if self.parameters.force:
|
|
|
|
|
self.remove_original = True
|
|
|
|
|
elif self.target_type == DIR:
|
|
|
|
|
raise TemplateTypeConflict(
|
|
|
|
|
"The target is a directory while the"
|
|
|
|
|
" template has append = 'link'.")
|
|
|
|
|
raise TemplateTypeConflict("the target is a directory while "
|
|
|
|
|
"the template has append = 'link'")
|
|
|
|
|
else:
|
|
|
|
|
raise TemplateTypeConflict(
|
|
|
|
|
"The target is a file while the"
|
|
|
|
|
" template has append = 'link'.")
|
|
|
|
|
raise TemplateTypeConflict("the target is a file while the"
|
|
|
|
|
" template has append = 'link'")
|
|
|
|
|
|
|
|
|
|
elif self.template_type == DIR:
|
|
|
|
|
if self.target_type == FILE:
|
|
|
|
|
if self.parameters.force:
|
|
|
|
|
self.remove_original = True
|
|
|
|
|
else:
|
|
|
|
|
raise TemplateTypeConflict("The target is a file while the"
|
|
|
|
|
" template is a directory.")
|
|
|
|
|
elif self.target_is_link and self.target_type == DIR:
|
|
|
|
|
raise TemplateTypeConflict("the target is a file while the"
|
|
|
|
|
" template is a directory")
|
|
|
|
|
elif self.target_is_link:
|
|
|
|
|
if self.parameters.force:
|
|
|
|
|
self.remove_original = True
|
|
|
|
|
else:
|
|
|
|
|
try:
|
|
|
|
|
self.target_path = read_link(self.target_path)
|
|
|
|
|
self.target_path = check_directory_link(
|
|
|
|
|
self.target_path)
|
|
|
|
|
except FilesError as error:
|
|
|
|
|
raise TemplateExecutorError("files error: {}".
|
|
|
|
|
format(str(error)))
|
|
|
|
@ -279,8 +293,8 @@ class TemplateWrapper:
|
|
|
|
|
raise TemplateExecutorError("files error: {}".
|
|
|
|
|
format(str(error)))
|
|
|
|
|
elif self.target_type == DIR:
|
|
|
|
|
raise TemplateTypeConflict("The target file is a directory"
|
|
|
|
|
" while the template is a file.")
|
|
|
|
|
raise TemplateTypeConflict("the target file is a directory"
|
|
|
|
|
" while the template is a file")
|
|
|
|
|
|
|
|
|
|
def check_package_collision(self):
|
|
|
|
|
'''Проверка на предмет коллизии, то есть конфликта пакета шаблона и
|
|
|
|
@ -433,11 +447,9 @@ class TemplateWrapper:
|
|
|
|
|
|
|
|
|
|
def remove_from_contents(self):
|
|
|
|
|
'''Метод для удаления целевого файла из CONTENTS.'''
|
|
|
|
|
print('let s remove')
|
|
|
|
|
if self.template_type == DIR:
|
|
|
|
|
self.target_package.remove_dir(self.target_path)
|
|
|
|
|
elif self.template_type == FILE:
|
|
|
|
|
print('remove as file')
|
|
|
|
|
self.target_package.remove_obj(self.target_path)
|
|
|
|
|
|
|
|
|
|
def clear_dir_contents(self):
|
|
|
|
@ -483,8 +495,6 @@ class TemplateWrapper:
|
|
|
|
|
def save_changes(self):
|
|
|
|
|
'''Метод для сохранения изменений внесенных в CONTENTS.'''
|
|
|
|
|
if self.target_package:
|
|
|
|
|
print('saving CONTENTS: {}'.format(
|
|
|
|
|
self.target_package.contents_dictionary.keys()))
|
|
|
|
|
self.target_package.remove_empty_directories()
|
|
|
|
|
self.target_package.write_contents_file()
|
|
|
|
|
|
|
|
|
@ -492,11 +502,14 @@ class TemplateWrapper:
|
|
|
|
|
class TemplateExecutor:
|
|
|
|
|
def __init__(self, datavars_module=Variables(), chroot_path='/',
|
|
|
|
|
cl_config_archive='/var/lib/calculate/config-archive',
|
|
|
|
|
cl_config_path='/var/lib/calculate/config'):
|
|
|
|
|
cl_config_path='/var/lib/calculate/config',
|
|
|
|
|
exec_dir_path='/var/lib/calculate/.execute/'):
|
|
|
|
|
self.datavars_module = datavars_module
|
|
|
|
|
|
|
|
|
|
self.chroot_path = chroot_path
|
|
|
|
|
|
|
|
|
|
self.exec_files_directory = '/var/lib/calculate/.execute/'
|
|
|
|
|
|
|
|
|
|
self.directory_default_parameters =\
|
|
|
|
|
ParametersProcessor.directory_default_parameters
|
|
|
|
|
self.file_default_parameters =\
|
|
|
|
@ -509,7 +522,14 @@ class TemplateExecutor:
|
|
|
|
|
'link': self._append_link_directory,
|
|
|
|
|
'replace': self._append_replace_directory}
|
|
|
|
|
|
|
|
|
|
self.file_appends = {'join': self._append_join_file}
|
|
|
|
|
self.file_appends = {'join': self._append_join_file,
|
|
|
|
|
'after': self._append_after_file,
|
|
|
|
|
'before': self._append_before_file,
|
|
|
|
|
'replace': self._append_replace_file,
|
|
|
|
|
'remove': self._append_remove_file,
|
|
|
|
|
'skip': self._append_skip_file,
|
|
|
|
|
'clear': self._append_clear_file,
|
|
|
|
|
'link': self._append_link_file}
|
|
|
|
|
|
|
|
|
|
self.formats_classes = ParametersProcessor.available_formats
|
|
|
|
|
self.calculate_config_file = CalculateConfigFile(
|
|
|
|
@ -530,21 +550,24 @@ class TemplateExecutor:
|
|
|
|
|
def execute_template(self, target_path, parameters, template_type,
|
|
|
|
|
template_text=''):
|
|
|
|
|
'''Метод для запуска выполнения шаблонов.'''
|
|
|
|
|
self.executor_output = {'target_path': None,
|
|
|
|
|
'stdout': None,
|
|
|
|
|
'stderr': None,
|
|
|
|
|
'exec_file': None}
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
template_object = TemplateWrapper(target_path, parameters,
|
|
|
|
|
template_type,
|
|
|
|
|
template_text=template_text)
|
|
|
|
|
except TemplateTypeConflict as error:
|
|
|
|
|
print('Error: {}'.format(str(error)))
|
|
|
|
|
return
|
|
|
|
|
raise TemplateExecutorError("type conflict: {}".format(str(error)))
|
|
|
|
|
|
|
|
|
|
except TemplateCollisionError as error:
|
|
|
|
|
print('Error: {}'.format(str(error)))
|
|
|
|
|
return
|
|
|
|
|
raise TemplateExecutorError("collision: {}".format(str(error)))
|
|
|
|
|
|
|
|
|
|
# Удаляем оригинал, если это необходимо из-за наличия force или по
|
|
|
|
|
# другим причинам.
|
|
|
|
|
if template_object.remove_original:
|
|
|
|
|
print('remove original')
|
|
|
|
|
if template_object.target_type == DIR:
|
|
|
|
|
self._remove_directory(template_object.target_path)
|
|
|
|
|
else:
|
|
|
|
@ -557,25 +580,35 @@ class TemplateExecutor:
|
|
|
|
|
return
|
|
|
|
|
template_object.target_type = None
|
|
|
|
|
|
|
|
|
|
print('input path: {}'.format(template_object.input_path))
|
|
|
|
|
print('output path: {}'.format(template_object.output_path))
|
|
|
|
|
|
|
|
|
|
# (!) Добавить поддержку run, execute и т.д.
|
|
|
|
|
if template_object.template_type == DIR:
|
|
|
|
|
self.directory_appends[template_object.parameters.append](
|
|
|
|
|
if template_object.parameters.run:
|
|
|
|
|
# Если есть параметр run -- запускаем текст шаблона.
|
|
|
|
|
self._run_template(template_object)
|
|
|
|
|
elif template_object.parameters.exec:
|
|
|
|
|
# Если есть параметр exec -- запускаем текст шаблона после
|
|
|
|
|
# обработки всех шаблонов.
|
|
|
|
|
self._exec_template(template_object)
|
|
|
|
|
elif template_object.parameters.append:
|
|
|
|
|
print('Using append: {}'.format(template_object.parameters.append))
|
|
|
|
|
print('input path: {}'.format(template_object.input_path))
|
|
|
|
|
print('output path: {}'.format(template_object.output_path))
|
|
|
|
|
|
|
|
|
|
if template_object.template_type == DIR:
|
|
|
|
|
self.directory_appends[template_object.parameters.append](
|
|
|
|
|
template_object)
|
|
|
|
|
elif template_object.template_type == FILE:
|
|
|
|
|
self.file_appends[template_object.parameters.append](
|
|
|
|
|
elif template_object.template_type == FILE:
|
|
|
|
|
self.file_appends[template_object.parameters.append](
|
|
|
|
|
template_object)
|
|
|
|
|
# Сохраняем изменения в CONTENTS внесенные согласно шаблону.
|
|
|
|
|
print('it is time to save changes')
|
|
|
|
|
template_object.save_changes()
|
|
|
|
|
|
|
|
|
|
# Возвращаем целевой путь, если он был изменен, или None если не был.
|
|
|
|
|
if template_object.target_path_is_changed:
|
|
|
|
|
return template_object.target_path
|
|
|
|
|
else:
|
|
|
|
|
return
|
|
|
|
|
# Сохраняем изменения в CONTENTS внесенные согласно шаблону.
|
|
|
|
|
template_object.save_changes()
|
|
|
|
|
|
|
|
|
|
# Возвращаем целевой путь, если он был изменен, или
|
|
|
|
|
# None если не был изменен.
|
|
|
|
|
if template_object.target_path_is_changed:
|
|
|
|
|
self.executor_output['target_path'] =\
|
|
|
|
|
template_object.target_path
|
|
|
|
|
|
|
|
|
|
return self.executor_output
|
|
|
|
|
|
|
|
|
|
def save_changes(self):
|
|
|
|
|
'''Метод для сохранения чего-нибудь после выполнения всех шаблонов.'''
|
|
|
|
@ -628,7 +661,8 @@ class TemplateExecutor:
|
|
|
|
|
self._clear_directory(template_object.target_path)
|
|
|
|
|
template_object.clear_dir_contents()
|
|
|
|
|
|
|
|
|
|
def _append_join_file(self, template_object: TemplateWrapper):
|
|
|
|
|
def _append_join_file(self, template_object: TemplateWrapper,
|
|
|
|
|
join_before=False, replace=False):
|
|
|
|
|
'''Метод описывающий действия при append = "join", если шаблон --
|
|
|
|
|
файл. Объединяет шаблон с целевым файлом.'''
|
|
|
|
|
input_path = template_object.input_path
|
|
|
|
@ -644,14 +678,15 @@ class TemplateExecutor:
|
|
|
|
|
if template_object.protected and not template_object.is_userspace:
|
|
|
|
|
output_paths.append(template_object.archive_path)
|
|
|
|
|
|
|
|
|
|
if template_object.target_type is not None:
|
|
|
|
|
if template_object.target_type is not None and not replace:
|
|
|
|
|
with open(input_path, 'r') as input_file:
|
|
|
|
|
input_text = input_file.read()
|
|
|
|
|
else:
|
|
|
|
|
input_text = ''
|
|
|
|
|
parsed_input = template_format(input_text)
|
|
|
|
|
parsed_input = template_format(input_text, join_before=join_before)
|
|
|
|
|
|
|
|
|
|
parsed_template = template_format(template_object.template_text)
|
|
|
|
|
parsed_template = template_format(template_object.template_text,
|
|
|
|
|
join_before=join_before)
|
|
|
|
|
|
|
|
|
|
parsed_input.join_template(parsed_template)
|
|
|
|
|
|
|
|
|
@ -683,20 +718,19 @@ class TemplateExecutor:
|
|
|
|
|
# Убираем целевой файл из CL.
|
|
|
|
|
self.calculate_config_file.remove_file(template_object.target_path)
|
|
|
|
|
|
|
|
|
|
print('is contents update is needed')
|
|
|
|
|
# Обновляем CONTENTS.
|
|
|
|
|
if template_object.protected:
|
|
|
|
|
print('needed')
|
|
|
|
|
if template_object.parameters.unbound:
|
|
|
|
|
print('remove')
|
|
|
|
|
template_object.remove_from_contents()
|
|
|
|
|
else:
|
|
|
|
|
print('add')
|
|
|
|
|
template_object.add_to_contents(file_md5=output_text_md5)
|
|
|
|
|
else:
|
|
|
|
|
with open(input_path, 'r') as input_file:
|
|
|
|
|
input_text = input_file.read()
|
|
|
|
|
parsed_input = template_format(input_text)
|
|
|
|
|
if template_object.target_type is not None and not replace:
|
|
|
|
|
with open(input_path, 'r') as input_file:
|
|
|
|
|
input_text = input_file.read()
|
|
|
|
|
parsed_input = template_format(input_text)
|
|
|
|
|
else:
|
|
|
|
|
input_text = ''
|
|
|
|
|
|
|
|
|
|
parsed_template = template_format(template_object.template_text)
|
|
|
|
|
parsed_input.join_template(parsed_template)
|
|
|
|
@ -732,6 +766,39 @@ class TemplateExecutor:
|
|
|
|
|
# Обновляем CONTENTS.
|
|
|
|
|
template_object.add_to_contents(file_md5=output_text_md5)
|
|
|
|
|
|
|
|
|
|
def _append_after_file(self, template_object: TemplateWrapper):
|
|
|
|
|
self._append_join_file(self, template_object, join_before=False)
|
|
|
|
|
|
|
|
|
|
def _append_before_file(self, template_object: TemplateWrapper):
|
|
|
|
|
self._append_join_file(self, template_object, join_before=True)
|
|
|
|
|
|
|
|
|
|
def _append_skip_file(self, template_object: TemplateWrapper):
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
def _append_replace_file(self, template_object: TemplateWrapper):
|
|
|
|
|
self._append_join_file(template_object, replace=True)
|
|
|
|
|
|
|
|
|
|
def _append_remove_file(self, template_object: TemplateWrapper):
|
|
|
|
|
if template_object.target_type is not None:
|
|
|
|
|
self._remove_file(template_object.target_path)
|
|
|
|
|
|
|
|
|
|
template_object.remove_from_contents()
|
|
|
|
|
|
|
|
|
|
def _append_clear_file(self, template_object: TemplateWrapper):
|
|
|
|
|
if template_object.target_type is not None:
|
|
|
|
|
self._clear_file(template_object.target_path)
|
|
|
|
|
else:
|
|
|
|
|
open(template_object.target_path, 'w').close()
|
|
|
|
|
|
|
|
|
|
template_object.add_to_contents()
|
|
|
|
|
|
|
|
|
|
def _append_link_file(self, template_object: TemplateWrapper):
|
|
|
|
|
if template_object.target_type is not None:
|
|
|
|
|
self._link_file(template_object.target_path,
|
|
|
|
|
template_object.parameters.source)
|
|
|
|
|
|
|
|
|
|
template_object.add_to_contents()
|
|
|
|
|
|
|
|
|
|
def _create_directory(self, template_object: TemplateWrapper):
|
|
|
|
|
'''Метод для создания директории и, при необходимости, изменения
|
|
|
|
|
владельца и доступа все директорий на пути к целевой.'''
|
|
|
|
@ -920,6 +987,63 @@ class TemplateExecutor:
|
|
|
|
|
raise TemplateExecutorError('Can not chmod file: {0}, reason: {1}'.
|
|
|
|
|
format(target_path, str(error)))
|
|
|
|
|
|
|
|
|
|
def _run_template(self, template_object: TemplateWrapper):
|
|
|
|
|
'''Метод для сохранения текста шаблонов, который должен быть исполнен
|
|
|
|
|
интерпретатором указанным в run прямо во время обработки шаблонов.'''
|
|
|
|
|
text_to_run = template_object.template_text
|
|
|
|
|
interpreter = template_object.parameters.run
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
run_process = Process(interpreter, cwd=self.chroot_path)
|
|
|
|
|
run_process.write(text_to_run)
|
|
|
|
|
|
|
|
|
|
if run_process.readable:
|
|
|
|
|
stdout = run_process.read()
|
|
|
|
|
if stdout:
|
|
|
|
|
print("Run output:\n{}".format(stdout))
|
|
|
|
|
self.executor_output['stdout'] = stdout
|
|
|
|
|
|
|
|
|
|
if run_process.readable_errors:
|
|
|
|
|
stderr = run_process.read_error()
|
|
|
|
|
if stderr:
|
|
|
|
|
print("Run errors:\n{}".format(stderr))
|
|
|
|
|
self.executor_output['stderr'] = stderr
|
|
|
|
|
|
|
|
|
|
except FilesError as error:
|
|
|
|
|
raise TemplateExecutorError(("can not run template using the"
|
|
|
|
|
" interpreter '{}', reason: {}").
|
|
|
|
|
format(interpreter, str(error)))
|
|
|
|
|
|
|
|
|
|
def _exec_template(self, template_object: TemplateWrapper):
|
|
|
|
|
'''Метод для сохранения текста шаблонов, который должен быть исполнен
|
|
|
|
|
интерпретатором указанным в exec после выполнения всех прочих шаблонов.
|
|
|
|
|
'''
|
|
|
|
|
text_to_run = template_object.template_text
|
|
|
|
|
interpreter = template_object.parameters.exec
|
|
|
|
|
if (self.chroot_path != '/' and not
|
|
|
|
|
self.exec_files_directory.startswith(self.chroot_path)):
|
|
|
|
|
exec_files_directory = join_paths(self.chroot_path,
|
|
|
|
|
'/var/lib/calculate/.execute/')
|
|
|
|
|
|
|
|
|
|
exec_number = 0
|
|
|
|
|
if os.path.exists(exec_files_directory):
|
|
|
|
|
exec_files_list = os.listdir(exec_files_directory)
|
|
|
|
|
if exec_files_list:
|
|
|
|
|
exec_number = int(exec_files_list[-1][-4:])
|
|
|
|
|
exec_number = str(exec_number + 1)
|
|
|
|
|
|
|
|
|
|
if len(exec_number) < 4:
|
|
|
|
|
exec_number = '0' * (4 - len(exec_number)) + exec_number
|
|
|
|
|
exec_file_name = 'exec_{}'.format(exec_number)
|
|
|
|
|
exec_file_path = join_paths(exec_files_directory,
|
|
|
|
|
exec_file_name)
|
|
|
|
|
|
|
|
|
|
exec_file = write_file(exec_file_path)
|
|
|
|
|
exec_file.write(text_to_run)
|
|
|
|
|
exec_file.close()
|
|
|
|
|
|
|
|
|
|
self.executor_output['exec_file'] = {interpreter: exec_file_name}
|
|
|
|
|
|
|
|
|
|
def get_file_info(self, path, info='all'):
|
|
|
|
|
file_stat = os.stat(path)
|
|
|
|
|
if info == 'all':
|
|
|
|
@ -1005,3 +1129,17 @@ template_executor_obj.execute_template(target_path,
|
|
|
|
|
template_parameters,
|
|
|
|
|
FILE, template_text=template_text)
|
|
|
|
|
template_executor_obj.save_changes()
|
|
|
|
|
|
|
|
|
|
input()
|
|
|
|
|
template_engine.process_template_from_string(template_to_run, FILE)
|
|
|
|
|
template_parameters = template_engine.parameters
|
|
|
|
|
template_text = template_engine.template_text
|
|
|
|
|
template_executor_obj = TemplateExecutor(
|
|
|
|
|
datavars_module=DATAVARS_MODULE,
|
|
|
|
|
chroot_path=CHROOT_PATH,
|
|
|
|
|
cl_config_archive=CL_CONFIG_ARCHIVE_PATH,
|
|
|
|
|
cl_config_path=CL_CONFIG_PATH)
|
|
|
|
|
template_executor_obj.execute_template(target_path,
|
|
|
|
|
template_parameters,
|
|
|
|
|
FILE, template_text=template_text)
|
|
|
|
|
template_executor_obj.save_changes()
|
|
|
|
|