Added support for 'run' and 'exec' parameters. Implementation of the files appends is almost done.

packages
Иванов Денис 4 years ago
parent 2b2af024ba
commit 8229f8c677

@ -10,7 +10,8 @@ class DiffFormat(BaseFormat):
FORMAT = 'diff'
EXECUTABLE = True
def __init__(self, document_text: str):
def __init__(self, document_text: str,
join_before=False):
self._patch_text = document_text
self._root_path = ''
self._last_level = 0

@ -14,7 +14,7 @@ class PatchFormat(BaseFormat):
FORMAT_PARAMETERS = {'multiline', 'dotall', 'comment'}
def __init__(self, document_text: str, multiline=False, dotall=False,
comment_symbol=''):
comment_symbol='', join_before=False):
processing_methods = OrderedDict()
super().__init__(processing_methods)

@ -19,7 +19,7 @@ class ProcmailFormat(BaseFormat):
def __init__(self, document_text: str,
ignore_comments=False,
join_before=True,
join_before=False,
comment_symbol=''):
processing_methods = [self._parse_comment_line,
self._parse_parameter_line,
@ -28,6 +28,7 @@ class ProcmailFormat(BaseFormat):
super().__init__(processing_methods)
self._ignore_comments = ignore_comments
self._comments_processing = True
self._join_before = join_before
self._last_comments_list = []

@ -42,6 +42,7 @@ class ProFTPDFormat(BaseFormat):
self._ignore_comments = ignore_comments
self._need_finish = True
self._comments_processing = True
self._join_before = join_before
self._section_stack = []
self._actions_stack = []

@ -21,7 +21,7 @@ class XMLGConfFormat(BaseFormat):
cls._initialize_parser()
return super().__new__(cls)
def __init__(self, document_text: str):
def __init__(self, document_text: str, join_before=False):
processing_methods = OrderedDict({'gconf': self._gconf,
'entry': self._entry,
'dir': self._dir,
@ -34,6 +34,7 @@ class XMLGConfFormat(BaseFormat):
super().__init__(processing_methods)
self._initialize_parser()
self._join_before = join_before
self._parse_xml_to_dictionary(document_text)

@ -21,13 +21,15 @@ class XMLXfceFormat(BaseFormat):
cls._initialize_parser()
return super().__new__(cls)
def __init__(self, document_text: str, ignore_comments=False):
def __init__(self, document_text: str, ignore_comments=False,
join_before=False):
processing_methods = OrderedDict({'channel': self._channel,
'property': self._property,
'value': self._value,
'unknown': self._unknown})
super().__init__(processing_methods)
self._initialize_parser()
self._join_before = join_before
if document_text == '':
self._document_dictionary = OrderedDict()

@ -15,7 +15,8 @@ import os
from ..utils.package import PackageAtomParser, PackageAtomError, NOTEXIST,\
Version
from ..utils.files import join_paths
from ..utils.files import join_paths, check_directory_link, check_command,\
FilesError
# Типы шаблона: директория или файл.
@ -134,7 +135,9 @@ class ParametersProcessor:
self.postparse_checkers_list = OrderedDict({
'append': self.check_postparse_append,
'source': self.check_postparse_source,
'autoupdate': self.check_postparse_autoupdate})
'autoupdate': self.check_postparse_autoupdate,
'run': self.check_postparse_run,
'exec': self.check_postparse_exec})
# Если параметр является наследуемым только при некоторых условиях --
# указываем здесь эти условия.
@ -279,28 +282,34 @@ class ParametersProcessor:
"'format' parameter value is not available")
def check_stop_parameter(self, parameter_value):
if parameter_value and isinstance(parameter_value, str):
return parameter_value
else:
raise IncorrectParameter("'stop' parameter value is not correct")
if not parameter_value and isinstance(parameter_value, bool):
raise IncorrectParameter("'stop' parameter value is empty")
return parameter_value
def check_start_parameter(self, parameter_value):
if parameter_value and isinstance(parameter_value, str):
return parameter_value
else:
raise IncorrectParameter("'start' parameter value is not correct")
if not parameter_value and isinstance(parameter_value, bool):
raise IncorrectParameter("'start' parameter value is empty")
return parameter_value
def check_run_parameter(self, parameter_value):
if parameter_value and isinstance(parameter_value, str):
return parameter_value
else:
raise IncorrectParameter("'run' parameter value is not correct")
if not parameter_value and isinstance(parameter_value, bool):
raise IncorrectParameter("'run' parameter value is empty")
try:
interpreter_path = check_command(parameter_value)
except FilesError as error:
raise IncorrectParameter("interpreter from 'run' parameter not"
" found")
return interpreter_path
def check_exec_parameter(self, parameter_value):
if parameter_value and isinstance(parameter_value, str):
return parameter_value
else:
raise IncorrectParameter("'exec' parameter value is not correct")
if not parameter_value and isinstance(parameter_value, bool):
raise IncorrectParameter("'exec' parameter value is empty")
try:
interpreter_path = check_command(parameter_value)
except FilesError as error:
raise IncorrectParameter("interpreter from 'exec' parameter not"
" found")
return interpreter_path
def check_chown_parameter(self, parameter_value):
if not parameter_value or isinstance(parameter_value, bool):
@ -331,6 +340,9 @@ class ParametersProcessor:
raise IncorrectParameter("'chmod' parameter value is not correct")
def check_source_parameter(self, parameter_value):
if not parameter_value or isinstance(parameter_value, bool):
raise IncorrectParameter("'source' parameter value is empty")
if self.chroot_path != '/':
real_path = join_paths(self.chroot_path, parameter_value)
else:
@ -342,15 +354,18 @@ class ParametersProcessor:
source_file_type = DIR if os.path.isdir(real_path) else FILE
if not parameter_value or isinstance(parameter_value, bool):
raise IncorrectParameter("'source' parameter value is empty")
if (self._parameters_container.append == 'link' and
self.template_type != source_file_type):
# Проверяем, совпадают ли типы шаблона и файла, указанного в source
if (self.template_type != source_file_type):
raise IncorrectParameter(
"the type of the 'source' file does not match"
" the type of the template file")
# Проверяем, не является ли файл из source зацикленной ссылкой.
if (source_file_type == DIR and os.path.islink(real_path)
and not check_directory_link(real_path)):
raise IncorrectParameter(
"the link from 'source' parameter is cycled")
return os.path.normpath(real_path)
def check_env_parameter(self, parameter_value):
@ -395,6 +410,32 @@ class ParametersProcessor:
raise IncorrectParameter("append = 'link' without source "
"parameter.")
if self._parameters_container.run:
raise IncorrectParameter("'append' parameter is not 'compatible' "
"with the 'run' parameter")
if self._parameters_container.exec:
raise IncorrectParameter("'append' parameter is not 'compatible' "
"with the 'exec' parameter")
def check_postparse_run(self, parameter_value):
if self._parameters_container.append:
raise IncorrectParameter("'run' parameter is not 'compatible' "
"with the 'append' parameter")
if self._parameters_container.exec:
raise IncorrectParameter("'run' parameter is not 'compatible' "
"with the 'exec' parameter")
def check_postparse_exec(self, parameter_value):
if self._parameters_container.append:
raise IncorrectParameter("'exec' parameter is not 'compatible' "
"with the 'append' parameter")
if self._parameters_container.run:
raise IncorrectParameter("'exec' parameter is not 'compatible' "
"with the 'run' parameter")
def check_postparse_source(self, parameter_value):
# Если файл по пути source не существует, но присутствует параметр
# mirror -- пропускаем шаблон для того, чтобы целевой файл мог быть

@ -38,7 +38,7 @@ class KeyboardInputProcess():
class Process():
'''Обертка для работы с процессами.'''
'''Класс-обертка для работы с процессами.'''
STDOUT = STDOUT
PIPE = PIPE
@ -94,6 +94,7 @@ class Process():
return self.stdin_handler
def _open_process(self):
'''Метод для открытия процесса.'''
try:
piped_stdin = self._stdin._get_stdout()
self._process = Popen(self._command,
@ -128,6 +129,7 @@ class Process():
raise FilesError('Can not open process.')
def close(self):
'''Метод для закрытия процесса.'''
if self._opened:
if self._process.stdin:
self.stdin_handler.close()
@ -135,6 +137,7 @@ class Process():
self._opened = False
def write(self, data):
'''Метод для записи данных в stdin процесса.'''
if not self._opened:
self._open_process()
self._is_read = False
@ -149,6 +152,7 @@ class Process():
raise FilesError(str(error))
def read(self):
'''Метод для чтения данных из stdout процесса.'''
if not self._opened and not self._writable:
self._open_process()
@ -173,6 +177,8 @@ class Process():
raise
def read_error(self):
'''Метод для чтения ошибок, появившихся при выполнении процесса, из его
stderr.'''
self.read()
if not self._error_cache:
try:
@ -182,6 +188,7 @@ class Process():
return self._error_cache
def kill(self):
'''Метод для удаления процесса, если он работает.'''
if self._opened:
self._process.kill()
@ -198,22 +205,28 @@ class Process():
@property
def writable(self):
'''Метод для проверки возможности записи данных в во входной поток
процесса.'''
return self._writable
@property
def readable(self):
'''Метод для проверки возможности чтения вывода процесса.'''
return self._readable
@property
def readable_errors(self):
'''Метод для проверки возможности чтения ошибок.'''
return self._readable_errors
def return_code(self):
'''Метод возвращающий код возвращенный процессом.'''
self.read()
return self._process.returncode
@property
def shell_command(self):
'''Метод для получения эквивалентной консольной команды.'''
command = ' '.join(self._command)
previous_commands = self._stdin.shell_command
if previous_commands == '':
@ -222,13 +235,17 @@ class Process():
return ' | '.join([previous_commands, command])
def success(self):
'''Метод для проверки успешности выполнения процесса.'''
return self.return_code() == 0
def failed(self):
'''Метод для проверки неуспешности выполнения процесса.'''
return self.return_code() != 0
class ProgramPathCache:
'''Класс, для поиска и кэширования путей к исполнительным файлам различных
команд.'''
def __init__(self):
self._cache = {}
@ -256,7 +273,8 @@ class ProgramPathCache:
get_program_path = ProgramPathCache()
def check_utils(*utils):
def check_command(*utils):
'''Функция для проверки наличия той или иной команды системе.'''
output = []
for util in utils:
util_path = get_program_path(util)
@ -271,6 +289,7 @@ def check_utils(*utils):
def join_paths(*paths):
'''Функция для объединения путей. Объединяет также абсолютные пути.'''
if len(paths) == 1:
return next(iter(paths))
@ -288,9 +307,12 @@ def join_paths(*paths):
def read_link(file_path):
'''Функция для получения целевого пути символьной ссылки.'''
try:
if path.exists(file_path):
return os.readlink(file_path)
else:
return None
except (OSError, IOError) as error:
mod, lineno = get_traceback_caller(*sys.exc_info())
FilesError("link read error, {}({}:{})".
@ -298,6 +320,7 @@ def read_link(file_path):
def read_file(file_path):
'''Функция для чтения файлов, возвращает текст файла.'''
try:
if path.exists(file_path):
with open(file_path, 'r') as opened_file:
@ -309,6 +332,8 @@ def read_file(file_path):
def write_file(file_path):
'''Функция для открытия и записи файлов. Создает директории на пути к
целевому файлу если это необходимо. Возвращает файловый объект.'''
directory_path = path.dirname(file_path)
if not path.exists(directory_path):
os.makedirs(directory_path)
@ -317,6 +342,7 @@ def write_file(file_path):
def read_file_lines(file_name, grab=False):
'''Функция для чтения файлов построчно.'''
try:
if path.exists(file_name):
for file_line in open(file_name, 'r'):
@ -377,6 +403,47 @@ def make_directory(directory_path, force=False):
return False
def check_directory_link(link_path):
'''Метод для проверки наличия зацикливающихся ссылок и их корректности в
целом. В случае успешной проверки возвращает целевой путь ссылки.'''
link_target = read_link(link_path)
if link_target is None:
# Ссылка не существует.
return False
if not os.path.isdir(link_target):
# Ссылка не на директорию.
return False
linked_path = os.path.abspath(link_target)
# Добавляем / к концу пути, чтобы показать, что это путь к директории.
if linked_path[-1] != '/':
linked_path = linked_path + '/'
# Пути, которые нужно проверить.
to_check = [linked_path]
# Целевые пути из встреченных ссылок.
linked_paths = {linked_path}
while to_check:
current_directory = to_check.pop()
for entry in os.scandir(current_directory):
# Обходим только директории и ссылки на директории.
if not entry.is_dir():
continue
if entry.is_symlink():
linked_path = read_link(entry.path)
if linked_path in linked_paths:
return False
linked_paths.add(linked_path)
to_check.append(linked_path)
else:
to_check.append(entry.path)
return link_target
class RealFS(GenericFS):
def __init__(self, prefix='/'):
self.prefix = prefix

@ -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()

@ -0,0 +1,3 @@
# Source file
[section_name]
rare_parameter = eternal_value

@ -1,3 +1,3 @@
dir /etc
dir /etc/dir
obj /etc/dir/file.conf 0b87fea7f5b65cac5012baa2bf647e72 1590588845
obj /etc/dir/file.conf 0b87fea7f5b65cac5012baa2bf647e72 1590678156

Loading…
Cancel
Save