From 2b2af024ba5dc1b3702ce6b3c944dfe4592fa03d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=98=D0=B2=D0=B0=D0=BD=D0=BE=D0=B2=20=D0=94=D0=B5=D0=BD?= =?UTF-8?q?=D0=B8=D1=81?= Date: Wed, 27 May 2020 18:20:38 +0300 Subject: [PATCH] 'mirror' parameter is supporting now. --- calculate/templates/template_engine.py | 30 +++++++--- calculate/utils/package.py | 17 ++++-- template_action_draft.py | 56 ++++++++++++++++--- tests/templates/testfiles/etc/dir/file.conf | 5 ++ .../testfiles/test_root/etc/dir/file.conf | 3 +- .../test-category/test-package-1.0/CONTENTS | 2 +- .../config-archive/etc/dir/file.conf | 3 +- 7 files changed, 88 insertions(+), 28 deletions(-) create mode 100644 tests/templates/testfiles/etc/dir/file.conf diff --git a/calculate/templates/template_engine.py b/calculate/templates/template_engine.py index e9b37df..c96e606 100644 --- a/calculate/templates/template_engine.py +++ b/calculate/templates/template_engine.py @@ -188,7 +188,7 @@ class ParametersProcessor: def check_postparse_parameters(self): for parameter, parameter_checker in\ self.postparse_checkers_list.items(): - if not self._parameters_container[parameter]: + if parameter not in self._parameters_container: continue parameter_value = self._parameters_container[parameter] @@ -260,7 +260,7 @@ class ParametersProcessor: if isinstance(parameter_value, bool): raise IncorrectParameter("'rebuild' parameter value is not bool") elif 'package' not in self._parameters_container: - raise IncorrectParameter(("'source' parameter is set without " + raise IncorrectParameter(("'rebuild' parameter is set without " "'package' parameter")) return parameter_value @@ -336,9 +336,10 @@ class ParametersProcessor: else: real_path = parameter_value + # Ставим True, чтобы потом проверить этот параметр в postparse if not os.path.exists(real_path): - raise IncorrectParameter( - "File from 'source' parameter does not exist") + return True + source_file_type = DIR if os.path.isdir(real_path) else FILE if not parameter_value or isinstance(parameter_value, bool): @@ -347,7 +348,8 @@ class ParametersProcessor: if (self._parameters_container.append == 'link' and self.template_type != source_file_type): raise IncorrectParameter( - "the type of the 'source' file does not match") + "the type of the 'source' file does not match" + " the type of the template file") return os.path.normpath(real_path) @@ -385,6 +387,8 @@ class ParametersProcessor: raise IncorrectParameter( "'autoupdate' parameter value is not bool") + # Методы для проверки параметров после разбора всего шаблона. + def check_postparse_append(self, parameter_value): if parameter_value == 'link': if 'source' not in self._parameters_container: @@ -392,9 +396,16 @@ class ParametersProcessor: "parameter.") def check_postparse_source(self, parameter_value): - if (self.template_type == DIR and - ('append' not in self._parameters_container or - self._parameters_container['append'] != 'link')): + # Если файл по пути source не существует, но присутствует параметр + # mirror -- пропускаем шаблон для того, чтобы целевой файл мог быть + # удален в исполнительном модуле. + if parameter_value is True: + if not self._parameters_container.mirror: + raise IncorrectParameter( + "File from 'source' parameter does not exist") + elif (self.template_type == DIR and + ('append' not in self._parameters_container or + self._parameters_container['append'] != 'link')): raise IncorrectParameter( ("'source' parameter is set without " "append = 'link' for directory template") @@ -657,6 +668,9 @@ class ParametersContainer(MutableMapping): return ''.\ format(self.__parameters, self.__inheritable) + def __contains__(self, name): + return name in self.__parameters or name in self.__inheritable + @property def parameters(self): return self.__parameters diff --git a/calculate/utils/package.py b/calculate/utils/package.py index a875667..5efdc32 100644 --- a/calculate/utils/package.py +++ b/calculate/utils/package.py @@ -720,7 +720,7 @@ class Package: '''Метод для удаления файлов и ссылок.''' file_path = self.remove_chroot_path(file_path) if file_path in self.contents_dictionary: - self.contents_dictionary.remove(file_path) + self.contents_dictionary.pop(file_path) def remove_dir(self, file_path): '''Метод для удаления из CONTENTS файлов и директорий находящихся @@ -728,7 +728,7 @@ class Package: directory_path = self.remove_chroot_path(file_path) for file_path in self.contents_dictionary: if file_path.startswith(directory_path): - self.contents_dictionary.remove(file_path) + self.contents_dictionary.pop(file_path) def clear_dir(self, file_path): '''Метод для удаления из CONTENTS файлов и директорий находящихся @@ -738,7 +738,7 @@ class Package: if file_path == directory_path: continue if file_path.startswith(directory_path): - self.contents_dictionary.remove(file_path) + self.contents_dictionary.pop(file_path) def remove_empty_directories(self): '''Метод для удаления из CONTENTS директорий, которые после удаления @@ -753,9 +753,14 @@ class Package: while file_directory != '/': used_directories.add(file_directory) file_directory = os.path.dirname(file_directory) - for filepath, value in self.contents_dictionary.items(): - if value['type'] == 'dir' and filepath not in used_directories: - self.contents_dictionary.remove(filepath) + + paths_to_delete = [file_path for file_path, value in + self.contents_dictionary.items() + if value['type'] == 'dir' and + file_path not in used_directories] + + for file_path in paths_to_delete: + self.contents_dictionary.pop(file_path) def get_md5(self, file_path): '''Метод для получения md5 хэш-суммы указанного файла.''' diff --git a/template_action_draft.py b/template_action_draft.py index 140aebc..6c7b40a 100644 --- a/template_action_draft.py +++ b/template_action_draft.py @@ -12,7 +12,7 @@ import glob import shutil import os -template_text = '''{% calculate append = 'join' -%} +template_text = '''{% calculate append = 'link', source = '/etc/dir/dir_2' -%} {% calculate package = 'test-category/test-package', format = 'samba' -%} [section one] parameter_1 = {{ vars_1.value_1 }} @@ -20,10 +20,12 @@ parameter_1 = {{ vars_1.value_1 }} ''' backup_template_text = '''{% calculate append = 'join', format = 'samba', -autoupdate -%} +autoupdate, package = 'test-category/test-package' -%} [section one] parameter_1 = value parameter_2 = value_2 +[section two] +other_parameter = other_value ''' APPENDS_SET = TemplateAction().available_appends @@ -56,6 +58,7 @@ class TemplateCollisionError(Exception): class CalculateConfigFile: + '''Класс для работы с файлом /var/lib/calculate/config.''' def __init__(self, cl_config_path='/var/lib/calculate/config', cl_chroot_path='/'): self.chroot_path = cl_chroot_path @@ -71,6 +74,7 @@ class CalculateConfigFile: return file_path in self._config_dictionary def _get_cl_config_dictionary(self): + '''Метод для загрузки словаря файла /var/lib/calculate/config.''' config_dictionary = OrderedDict() if os.path.exists(self.cl_config_path): @@ -98,17 +102,21 @@ class CalculateConfigFile: return config_dictionary def set_files_md5(self, file_path, file_md5): + '''Метод для установки в config соответствия файла некоторой + контрольной сумме.''' file_path = self._remove_chroot(file_path) self._config_dictionary[file_path] = file_md5 self._unsaved_changes = True def remove_file(self, file_path): + '''Метод для удаления файла из config.''' file_path = self._remove_chroot(file_path) if file_path in self._config_dictionary: self._config_dictionary.pop(file_path) self._unsaved_changes = True def compare_md5(self, file_path, file_md5): + '''Метод для сравнения хэш-суммы из config и некоторой заданной.''' file_path = self._remove_chroot(file_path) if file_path in self._config_dictionary: return self._config_dictionary[file_path] == file_md5 @@ -116,6 +124,7 @@ class CalculateConfigFile: return False def save_changes(self): + '''Метод для записи изменений, внессенных в файл config.''' if not self._unsaved_changes: return config_file = write_file(self.cl_config_path) @@ -127,6 +136,7 @@ class CalculateConfigFile: self._unsaved_changes = False def _remove_chroot(self, file_path): + '''Метод для удаления корневого пути из указанного пути.''' if self.chroot_path != '/' and file_path.startswith(self.chroot_path): file_path = file_path[len(self.chroot_path):] @@ -201,7 +211,14 @@ class TemplateWrapper: self.target_type = file_type break self.target_is_link = os.path.islink(target_path) + # Если установлен параметр mirror и есть параметр source, + # содержащий несуществующий путь -- удаляем целевой файл. + if self.parameters.source is True and self.parameters.mirror: + self.remove_original = True else: + if self.parameters.mirror: + raise TemplateExecutorError("target file does not exist, while" + " 'mirror' parameter is set") self.target_type = None self.check_conflicts() @@ -313,7 +330,7 @@ class TemplateWrapper: '''Метод для проверки наличия пользовательских изменений в конфигурационных файлах.''' # Эта проверка только для файлов. - if self.target_type != FILE: + if self.template_type != FILE: return # Проверим, является ли файл защищенным. @@ -328,8 +345,6 @@ class TemplateWrapper: self.protected = False break - print('Is protected:', self.protected) - # Собираем список имеющихся ._cfg файлов. cfg_pattern = os.path.join(os.path.dirname(self.target_path), "._cfg????_{}".format( @@ -418,9 +433,11 @@ 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): @@ -443,6 +460,7 @@ class TemplateWrapper: '''Метод для получения множества защищенных директорий.''' if cls._protected_is_set: return + cls._protected_set = set() cls._protected_set.add(join_paths(cls.chroot_path, '/etc')) @@ -464,7 +482,9 @@ class TemplateWrapper: def save_changes(self): '''Метод для сохранения изменений внесенных в CONTENTS.''' - if self.target_package and not self.parameters.unbound: + 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() @@ -515,19 +535,31 @@ class TemplateExecutor: template_type, template_text=template_text) except TemplateTypeConflict as error: + print('Error: {}'.format(str(error))) return except TemplateCollisionError as error: + print('Error: {}'.format(str(error))) return # Удаляем оригинал, если это необходимо из-за наличия force или по # другим причинам. if template_object.remove_original: + print('remove original') if template_object.target_type == DIR: self._remove_directory(template_object.target_path) else: self._remove_file(template_object.target_path) + template_object.remove_from_contents() + # Если был включен mirror, то после удаления файла завершаем + # выполнение шаблона. + if template_object.parameters.mirror: + template_object.save_changes() + 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]( @@ -536,9 +568,10 @@ class TemplateExecutor: 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: @@ -650,11 +683,15 @@ 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: @@ -692,8 +729,8 @@ class TemplateExecutor: # Действия если CL совпало. Пока ничего не делаем. pass - # Обновляем CONTENTS. - template_object.add_to_contents(file_md5=output_text_md5) + # Обновляем CONTENTS. + template_object.add_to_contents(file_md5=output_text_md5) def _create_directory(self, template_object: TemplateWrapper): '''Метод для создания директории и, при необходимости, изменения @@ -954,6 +991,7 @@ 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(backup_template_text, FILE) template_parameters = template_engine.parameters diff --git a/tests/templates/testfiles/etc/dir/file.conf b/tests/templates/testfiles/etc/dir/file.conf new file mode 100644 index 0000000..dc06ef9 --- /dev/null +++ b/tests/templates/testfiles/etc/dir/file.conf @@ -0,0 +1,5 @@ +[section one] + parameter_1 = value_1 +[section two] + parameter_3 = value_3 + origin_parameter = origin_value diff --git a/tests/templates/testfiles/test_root/etc/dir/file.conf b/tests/templates/testfiles/test_root/etc/dir/file.conf index b280e03..afd6900 100644 --- a/tests/templates/testfiles/test_root/etc/dir/file.conf +++ b/tests/templates/testfiles/test_root/etc/dir/file.conf @@ -2,5 +2,4 @@ parameter_1 = value parameter_2 = value_2 [section two] - parameter_3 = value_3 - origin_parameter = origin_value + other_parameter = other_value diff --git a/tests/templates/testfiles/test_root/var/db/pkg/test-category/test-package-1.0/CONTENTS b/tests/templates/testfiles/test_root/var/db/pkg/test-category/test-package-1.0/CONTENTS index 4688bd1..a47ff84 100644 --- a/tests/templates/testfiles/test_root/var/db/pkg/test-category/test-package-1.0/CONTENTS +++ b/tests/templates/testfiles/test_root/var/db/pkg/test-category/test-package-1.0/CONTENTS @@ -1,3 +1,3 @@ dir /etc dir /etc/dir -obj /etc/dir/file.conf f050e31b0c059cc6b1edbd4871db1b91 1590505223 +obj /etc/dir/file.conf 0b87fea7f5b65cac5012baa2bf647e72 1590588845 diff --git a/tests/templates/testfiles/test_root/var/lib/calculate/config-archive/etc/dir/file.conf b/tests/templates/testfiles/test_root/var/lib/calculate/config-archive/etc/dir/file.conf index b280e03..afd6900 100644 --- a/tests/templates/testfiles/test_root/var/lib/calculate/config-archive/etc/dir/file.conf +++ b/tests/templates/testfiles/test_root/var/lib/calculate/config-archive/etc/dir/file.conf @@ -2,5 +2,4 @@ parameter_1 = value parameter_2 = value_2 [section two] - parameter_3 = value_3 - origin_parameter = origin_value + other_parameter = other_value