|
|
|
@ -3,6 +3,7 @@
|
|
|
|
|
from pprint import pprint
|
|
|
|
|
from ..utils.package import (
|
|
|
|
|
PackageAtomParser,
|
|
|
|
|
PackageCreator,
|
|
|
|
|
Package,
|
|
|
|
|
PackageNotFound,
|
|
|
|
|
PackageAtomName,
|
|
|
|
@ -42,6 +43,8 @@ from typing import (
|
|
|
|
|
List,
|
|
|
|
|
Tuple,
|
|
|
|
|
Iterator,
|
|
|
|
|
NoReturn,
|
|
|
|
|
Optional,
|
|
|
|
|
Callable
|
|
|
|
|
)
|
|
|
|
|
from calculate.variables.loader import Datavars
|
|
|
|
@ -120,21 +123,21 @@ class CalculateConfigFile:
|
|
|
|
|
self._unsaved_changes = False
|
|
|
|
|
return config_dictionary
|
|
|
|
|
|
|
|
|
|
def set_files_md5(self, file_path: str, file_md5: str) -> None:
|
|
|
|
|
def set_files_md5(self, file_path: str, file_md5: str) -> NoReturn:
|
|
|
|
|
'''Метод для установки в 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: str) -> None:
|
|
|
|
|
def remove_file(self, file_path: str) -> NoReturn:
|
|
|
|
|
'''Метод для удаления файла из 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: str, file_md5: str) -> None:
|
|
|
|
|
def compare_md5(self, file_path: str, file_md5: str) -> NoReturn:
|
|
|
|
|
'''Метод для сравнения хэш-суммы из config и некоторой заданной.'''
|
|
|
|
|
file_path = self._remove_chroot(file_path)
|
|
|
|
|
if file_path in self._config_dictionary:
|
|
|
|
@ -142,7 +145,7 @@ class CalculateConfigFile:
|
|
|
|
|
else:
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
def save_changes(self) -> None:
|
|
|
|
|
def save_changes(self) -> NoReturn:
|
|
|
|
|
'''Метод для записи изменений, внессенных в файл config.'''
|
|
|
|
|
if not self._unsaved_changes:
|
|
|
|
|
return
|
|
|
|
@ -190,10 +193,11 @@ class TemplateWrapper:
|
|
|
|
|
template_type: int,
|
|
|
|
|
template_path: str,
|
|
|
|
|
template_text: str = '',
|
|
|
|
|
target_package: Union[Package, None] = None,
|
|
|
|
|
target_package: Optional[Package] = None,
|
|
|
|
|
chroot_path: str = '/',
|
|
|
|
|
config_archive_path: str = '/var/lib/calculate/config-archive',
|
|
|
|
|
dbpkg: bool = True):
|
|
|
|
|
dbpkg: bool = True,
|
|
|
|
|
pkg_autosave: bool = False):
|
|
|
|
|
self.target_path: str = target_file_path
|
|
|
|
|
self.template_path: str = template_path
|
|
|
|
|
self.chroot_path: str = chroot_path
|
|
|
|
@ -203,6 +207,8 @@ class TemplateWrapper:
|
|
|
|
|
self.package_atom_parser: PackageAtomParser = PackageAtomParser(
|
|
|
|
|
chroot_path=self.chroot_path)
|
|
|
|
|
|
|
|
|
|
self._pkg_autosave: bool = pkg_autosave
|
|
|
|
|
|
|
|
|
|
# Вспомогательный флаг, включается, если по целевому пути лежит файл,
|
|
|
|
|
# для которого не определился никакой пакет.
|
|
|
|
|
self.target_without_package: bool = False
|
|
|
|
@ -292,7 +298,8 @@ class TemplateWrapper:
|
|
|
|
|
self.target_package.package_name !=
|
|
|
|
|
self.target_package_name):
|
|
|
|
|
self.target_package = Package(self.parameters.package,
|
|
|
|
|
chroot_path=self.chroot_path)
|
|
|
|
|
chroot_path=self.chroot_path,
|
|
|
|
|
autosave=self._pkg_autosave)
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
self._check_type_conflicts()
|
|
|
|
@ -306,7 +313,7 @@ class TemplateWrapper:
|
|
|
|
|
if self.parameters.append and self.parameters.append == "replace":
|
|
|
|
|
self.remove_original = True
|
|
|
|
|
|
|
|
|
|
def _check_type_conflicts(self) -> None:
|
|
|
|
|
def _check_type_conflicts(self) -> NoReturn:
|
|
|
|
|
'''Метод для проверки конфликтов типов.'''
|
|
|
|
|
if self.parameters.append == 'link':
|
|
|
|
|
if self.parameters.force:
|
|
|
|
@ -380,7 +387,7 @@ class TemplateWrapper:
|
|
|
|
|
raise TemplateTypeConflict("the target file is a directory"
|
|
|
|
|
" while the template is a file")
|
|
|
|
|
|
|
|
|
|
def _check_package_collision(self) -> None:
|
|
|
|
|
def _check_package_collision(self) -> NoReturn:
|
|
|
|
|
'''Метод для проверки на предмет коллизии, то есть конфликта пакета
|
|
|
|
|
шаблона и целевого файла.'''
|
|
|
|
|
if self.parameters.package:
|
|
|
|
@ -437,12 +444,14 @@ class TemplateWrapper:
|
|
|
|
|
if (self.target_package is None or
|
|
|
|
|
self.target_package_name != self.target_package.package_name):
|
|
|
|
|
self.target_package = Package(self.target_package_name,
|
|
|
|
|
chroot_path=self.chroot_path)
|
|
|
|
|
|
|
|
|
|
chroot_path=self.chroot_path,
|
|
|
|
|
autosave=True)
|
|
|
|
|
# Теперь перемещаем файл в пакет со старшей версией.
|
|
|
|
|
self.source_package = Package(file_package,
|
|
|
|
|
chroot_path=self.chroot_path)
|
|
|
|
|
chroot_path=self.chroot_path,
|
|
|
|
|
autosave=self._pkg_autosave)
|
|
|
|
|
removed = self.source_package.remove_file(self.target_path)
|
|
|
|
|
|
|
|
|
|
for file_path, file_info in removed.items():
|
|
|
|
|
if file_info['type'] == 'dir':
|
|
|
|
|
self.target_package.add_dir(file_path)
|
|
|
|
@ -455,14 +464,14 @@ class TemplateWrapper:
|
|
|
|
|
file_path,
|
|
|
|
|
target_path=file_info['target'],
|
|
|
|
|
mtime=file_info['mtime'])
|
|
|
|
|
self.source_package.write_contents_file()
|
|
|
|
|
else:
|
|
|
|
|
self.target_package_name = parameter_package
|
|
|
|
|
|
|
|
|
|
if (self.target_package is None or
|
|
|
|
|
self.target_package_name != self.target_package.package_name):
|
|
|
|
|
self.target_package = Package(self.target_package_name,
|
|
|
|
|
chroot_path=self.chroot_path)
|
|
|
|
|
chroot_path=self.chroot_path,
|
|
|
|
|
autosave=self._pkg_autosave)
|
|
|
|
|
|
|
|
|
|
def _compare_packages(self, lpackage: PackageAtomName,
|
|
|
|
|
rpackage: PackageAtomName
|
|
|
|
@ -479,7 +488,7 @@ class TemplateWrapper:
|
|
|
|
|
else:
|
|
|
|
|
return rpackage
|
|
|
|
|
|
|
|
|
|
def _check_user_changes(self) -> None:
|
|
|
|
|
def _check_user_changes(self) -> NoReturn:
|
|
|
|
|
'''Метод для проверки наличия пользовательских изменений в
|
|
|
|
|
конфигурационных файлах.'''
|
|
|
|
|
# Эта проверка только для файлов.
|
|
|
|
@ -604,7 +613,7 @@ class TemplateWrapper:
|
|
|
|
|
|
|
|
|
|
return new_cfg_path
|
|
|
|
|
|
|
|
|
|
def remove_from_contents(self) -> None:
|
|
|
|
|
def remove_from_contents(self) -> NoReturn:
|
|
|
|
|
'''Метод для удаления целевого файла из CONTENTS.'''
|
|
|
|
|
if self.target_package is None:
|
|
|
|
|
return
|
|
|
|
@ -614,13 +623,13 @@ class TemplateWrapper:
|
|
|
|
|
elif self.template_type == FILE:
|
|
|
|
|
self.target_package.remove_obj(self.target_path)
|
|
|
|
|
|
|
|
|
|
def clear_dir_contents(self) -> None:
|
|
|
|
|
def clear_dir_contents(self) -> NoReturn:
|
|
|
|
|
'''Метод для удаления из CONTENTS всего содержимого директории после
|
|
|
|
|
применения append = "clear".'''
|
|
|
|
|
if self.template_type == DIR and self.target_package is not None:
|
|
|
|
|
self.target_package.clear_dir(self.target_path)
|
|
|
|
|
|
|
|
|
|
def add_to_contents(self, file_md5: Union[str, None] = None) -> None:
|
|
|
|
|
def add_to_contents(self, file_md5: Optional[str] = None) -> NoReturn:
|
|
|
|
|
'''Метод для добавления целевого файла в CONTENTS.'''
|
|
|
|
|
if self.target_package is None:
|
|
|
|
|
return
|
|
|
|
@ -643,7 +652,7 @@ class TemplateWrapper:
|
|
|
|
|
elif self.template_type == FILE:
|
|
|
|
|
self.target_package.add_obj(source_path, file_md5=file_md5)
|
|
|
|
|
|
|
|
|
|
def update_contents_from_list(self, changed_list: dict) -> None:
|
|
|
|
|
def update_contents_from_list(self, changed_list: dict) -> NoReturn:
|
|
|
|
|
'''Метод для изменения CONTENTS по списку измененных файлов.'''
|
|
|
|
|
if self.target_package is None:
|
|
|
|
|
return
|
|
|
|
@ -666,7 +675,7 @@ class TemplateWrapper:
|
|
|
|
|
self.target_package.add_dir(file_path)
|
|
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
|
def _set_protected(cls, chroot_path: str) -> None:
|
|
|
|
|
def _set_protected(cls, chroot_path: str) -> NoReturn:
|
|
|
|
|
'''Метод для получения множества защищенных директорий.'''
|
|
|
|
|
cls._protected_set = set()
|
|
|
|
|
cls._unprotected_set = set()
|
|
|
|
@ -689,7 +698,7 @@ class TemplateWrapper:
|
|
|
|
|
|
|
|
|
|
cls._protected_is_set = True
|
|
|
|
|
|
|
|
|
|
def save_changes(self) -> None:
|
|
|
|
|
def save_changes(self) -> NoReturn:
|
|
|
|
|
'''Метод для сохранения изменений внесенных в CONTENTS.'''
|
|
|
|
|
if self.target_package:
|
|
|
|
|
self.target_package.remove_empty_directories()
|
|
|
|
@ -704,7 +713,8 @@ class TemplateExecutor:
|
|
|
|
|
cl_config_archive: str = '/var/lib/calculate/config-archive',
|
|
|
|
|
cl_config_path: str = '/var/lib/calculate/config',
|
|
|
|
|
execute_archive_path: str = '/var/lib/calculate/.execute/',
|
|
|
|
|
dbpkg: bool = True):
|
|
|
|
|
dbpkg: bool = True,
|
|
|
|
|
pkg_autosave: bool = False):
|
|
|
|
|
# TODO добавить список измененных файлов.
|
|
|
|
|
self.datavars_module: Union[Datavars, Variables] = datavars_module
|
|
|
|
|
|
|
|
|
@ -718,6 +728,7 @@ class TemplateExecutor:
|
|
|
|
|
self.execute_files: OrderedDict = OrderedDict()
|
|
|
|
|
|
|
|
|
|
self.dbpkg: bool = dbpkg
|
|
|
|
|
self._pkg_autosave: bool = pkg_autosave
|
|
|
|
|
|
|
|
|
|
# Словарь с измененными файлами и статусами их изменений.
|
|
|
|
|
self.changed_files: dict = {}
|
|
|
|
@ -771,7 +782,7 @@ class TemplateExecutor:
|
|
|
|
|
parameters: ParametersContainer, template_type: int,
|
|
|
|
|
template_path: str, template_text: str = '',
|
|
|
|
|
save_changes: bool = True,
|
|
|
|
|
target_package: Union[Package, None] = None) -> dict:
|
|
|
|
|
target_package: Optional[Package] = None) -> dict:
|
|
|
|
|
'''Метод для запуска выполнения шаблонов.'''
|
|
|
|
|
# Словарь с данными о результате работы исполнительного метода.
|
|
|
|
|
self.executor_output = {'target_path': None,
|
|
|
|
@ -790,7 +801,8 @@ class TemplateExecutor:
|
|
|
|
|
target_package=target_package,
|
|
|
|
|
chroot_path=self.chroot_path,
|
|
|
|
|
config_archive_path=self.cl_config_archive_path,
|
|
|
|
|
dbpkg=self.dbpkg)
|
|
|
|
|
dbpkg=self.dbpkg,
|
|
|
|
|
pkg_autosave=self._pkg_autosave)
|
|
|
|
|
|
|
|
|
|
except TemplateTypeConflict as error:
|
|
|
|
|
raise TemplateExecutorError("type conflict: {}".format(str(error)))
|
|
|
|
@ -813,8 +825,8 @@ class TemplateExecutor:
|
|
|
|
|
# Если был включен mirror, то после удаления файла завершаем
|
|
|
|
|
# выполнение шаблона.
|
|
|
|
|
if template_object.parameters.mirror:
|
|
|
|
|
if save_changes:
|
|
|
|
|
template_object.save_changes()
|
|
|
|
|
# if save_changes:
|
|
|
|
|
# template_object.save_changes()
|
|
|
|
|
return self.executor_output
|
|
|
|
|
|
|
|
|
|
template_object.target_type = None
|
|
|
|
@ -835,8 +847,8 @@ class TemplateExecutor:
|
|
|
|
|
template_object)
|
|
|
|
|
|
|
|
|
|
# Сохраняем изменения в CONTENTS внесенные согласно шаблону.
|
|
|
|
|
if save_changes:
|
|
|
|
|
template_object.save_changes()
|
|
|
|
|
# if save_changes:
|
|
|
|
|
# template_object.save_changes()
|
|
|
|
|
|
|
|
|
|
# Возвращаем целевой путь, если он был изменен, или
|
|
|
|
|
# None если не был изменен.
|
|
|
|
@ -851,7 +863,7 @@ class TemplateExecutor:
|
|
|
|
|
|
|
|
|
|
return self.executor_output
|
|
|
|
|
|
|
|
|
|
def save_changes(self) -> None:
|
|
|
|
|
def save_changes(self) -> NoReturn:
|
|
|
|
|
'''Метод для сохранения чего-нибудь после выполнения всех шаблонов.'''
|
|
|
|
|
# Пока сохраняем только получившееся содержимое config-файла.
|
|
|
|
|
self.calculate_config_file.save_changes()
|
|
|
|
@ -879,7 +891,7 @@ class TemplateExecutor:
|
|
|
|
|
template_object.add_to_contents()
|
|
|
|
|
|
|
|
|
|
def _append_remove_directory(self, template_object: TemplateWrapper
|
|
|
|
|
) -> None:
|
|
|
|
|
) -> NoReturn:
|
|
|
|
|
'''Метод описывающий действия для append = "remove", если шаблон --
|
|
|
|
|
директория. Удаляет директорию со всем содержимым, если она есть.'''
|
|
|
|
|
if template_object.target_type is not None:
|
|
|
|
@ -901,11 +913,11 @@ class TemplateExecutor:
|
|
|
|
|
template_object.remove_from_contents()
|
|
|
|
|
|
|
|
|
|
def _append_skip_directory(self,
|
|
|
|
|
template_object: TemplateWrapper) -> None:
|
|
|
|
|
template_object: TemplateWrapper) -> NoReturn:
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
def _append_clear_directory(self,
|
|
|
|
|
template_object: TemplateWrapper) -> None:
|
|
|
|
|
template_object: TemplateWrapper) -> NoReturn:
|
|
|
|
|
'''Метод описывающий действия для append = "clear", если шаблон --
|
|
|
|
|
директория. Удаляет все содержимое директории, если она есть.'''
|
|
|
|
|
if template_object.target_type is not None:
|
|
|
|
@ -935,7 +947,8 @@ class TemplateExecutor:
|
|
|
|
|
if self.dbpkg:
|
|
|
|
|
template_object.clear_dir_contents()
|
|
|
|
|
|
|
|
|
|
def _append_link_directory(self, template_object: TemplateWrapper) -> None:
|
|
|
|
|
def _append_link_directory(self, template_object: TemplateWrapper
|
|
|
|
|
) -> NoReturn:
|
|
|
|
|
'''Метод описывающий действия для append = "link", если шаблон --
|
|
|
|
|
директория. Создает ссылку на директорию, если она есть.'''
|
|
|
|
|
self._link_directory(template_object.parameters.source,
|
|
|
|
@ -959,8 +972,8 @@ class TemplateExecutor:
|
|
|
|
|
if self.dbpkg:
|
|
|
|
|
template_object.add_to_contents()
|
|
|
|
|
|
|
|
|
|
def _append_replace_directory(self,
|
|
|
|
|
template_object: TemplateWrapper) -> None:
|
|
|
|
|
def _append_replace_directory(self, template_object: TemplateWrapper
|
|
|
|
|
) -> NoReturn:
|
|
|
|
|
'''Метод описывающий действия для append = "replace", если шаблон --
|
|
|
|
|
директория. Очищает директорию или создает, если ее нет.'''
|
|
|
|
|
if template_object.target_type is None:
|
|
|
|
@ -992,7 +1005,7 @@ class TemplateExecutor:
|
|
|
|
|
|
|
|
|
|
def _append_join_file(self, template_object: TemplateWrapper,
|
|
|
|
|
join_before: bool = False, replace: bool = False
|
|
|
|
|
) -> None:
|
|
|
|
|
) -> NoReturn:
|
|
|
|
|
'''Метод описывающий действия при append = "join", если шаблон -- файл.
|
|
|
|
|
Объединяет шаблон с целевым файлом.'''
|
|
|
|
|
output_path = template_object.output_path
|
|
|
|
@ -1206,8 +1219,8 @@ class TemplateExecutor:
|
|
|
|
|
self.calculate_config_file.remove_file(template_object.target_path)
|
|
|
|
|
|
|
|
|
|
def _copy_from_source(self, template_object: TemplateWrapper,
|
|
|
|
|
chown: Union[dict, None] = None,
|
|
|
|
|
chmod: Union[int, None] = None) -> str:
|
|
|
|
|
chown: Optional[dict] = None,
|
|
|
|
|
chmod: Optional[int] = None) -> str:
|
|
|
|
|
'''Метод для копирования файла, указанного в source.'''
|
|
|
|
|
output_path = template_object.output_path
|
|
|
|
|
source_path = template_object.input_path
|
|
|
|
@ -1248,29 +1261,32 @@ class TemplateExecutor:
|
|
|
|
|
chown = self.file_default_parameters.get('chown', False)
|
|
|
|
|
return chown
|
|
|
|
|
|
|
|
|
|
def _append_after_file(self, template_object: TemplateWrapper) -> None:
|
|
|
|
|
def _append_after_file(self, template_object: TemplateWrapper) -> NoReturn:
|
|
|
|
|
'''Метод описывающий действия при append = "after", если шаблон --
|
|
|
|
|
файл. Объединяет шаблон с целевым файлом так, чтобы текст добавлялся
|
|
|
|
|
в конец файла и в конец каждой секции файла.'''
|
|
|
|
|
self._append_join_file(template_object, join_before=False)
|
|
|
|
|
|
|
|
|
|
def _append_before_file(self, template_object: TemplateWrapper) -> None:
|
|
|
|
|
def _append_before_file(self, template_object: TemplateWrapper
|
|
|
|
|
) -> NoReturn:
|
|
|
|
|
'''Метод описывающий действия при append = "after", если шаблон --
|
|
|
|
|
файл. Объединяет шаблон с целевым файлом так, чтобы текст добавлялся
|
|
|
|
|
в начало файла и в начало каждой секции файла.'''
|
|
|
|
|
self._append_join_file(template_object, join_before=True)
|
|
|
|
|
|
|
|
|
|
def _append_skip_file(self, template_object: TemplateWrapper) -> None:
|
|
|
|
|
def _append_skip_file(self, template_object: TemplateWrapper) -> NoReturn:
|
|
|
|
|
'''Метод описывающий действия при append = "skip". Пока никаких
|
|
|
|
|
действий.'''
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
def _append_replace_file(self, template_object: TemplateWrapper) -> None:
|
|
|
|
|
def _append_replace_file(self, template_object: TemplateWrapper
|
|
|
|
|
) -> NoReturn:
|
|
|
|
|
'''Метод описывающий действия при append = "replace", если шаблон --
|
|
|
|
|
файл. Очищает файл и затем накладывает на него шаблон.'''
|
|
|
|
|
self._append_join_file(template_object, replace=True)
|
|
|
|
|
|
|
|
|
|
def _append_remove_file(self, template_object: TemplateWrapper) -> None:
|
|
|
|
|
def _append_remove_file(self, template_object: TemplateWrapper
|
|
|
|
|
) -> NoReturn:
|
|
|
|
|
'''Метод описывающий действия при append = "remove", если шаблон --
|
|
|
|
|
файл. Удаляет файл.'''
|
|
|
|
|
if template_object.target_type is not None:
|
|
|
|
@ -1286,7 +1302,7 @@ class TemplateExecutor:
|
|
|
|
|
if self.dbpkg:
|
|
|
|
|
template_object.remove_from_contents()
|
|
|
|
|
|
|
|
|
|
def _append_clear_file(self, template_object: TemplateWrapper) -> None:
|
|
|
|
|
def _append_clear_file(self, template_object: TemplateWrapper) -> NoReturn:
|
|
|
|
|
'''Метод описывающий действия при append = "clear", если шаблон --
|
|
|
|
|
файл. Очищает файл.'''
|
|
|
|
|
if template_object.target_type is not None:
|
|
|
|
@ -1308,7 +1324,7 @@ class TemplateExecutor:
|
|
|
|
|
if self.dbpkg:
|
|
|
|
|
template_object.add_to_contents()
|
|
|
|
|
|
|
|
|
|
def _append_link_file(self, template_object: TemplateWrapper) -> None:
|
|
|
|
|
def _append_link_file(self, template_object: TemplateWrapper) -> NoReturn:
|
|
|
|
|
'''Метод описывающий действия при append = "link", если шаблон --
|
|
|
|
|
файл. Создает ссылку на файл, указанный в параметре source.'''
|
|
|
|
|
input_path = template_object.input_path
|
|
|
|
@ -1390,7 +1406,7 @@ class TemplateExecutor:
|
|
|
|
|
return hashlib.md5(source_path.encode()).hexdigest()
|
|
|
|
|
|
|
|
|
|
def _create_directory(self, template_object: TemplateWrapper,
|
|
|
|
|
path_to_create: Union[str, None] = None) -> None:
|
|
|
|
|
path_to_create: Optional[str] = None) -> NoReturn:
|
|
|
|
|
'''Метод для создания директории и, при необходимости, изменения
|
|
|
|
|
владельца и доступа все директорий на пути к целевой.'''
|
|
|
|
|
if path_to_create is None:
|
|
|
|
@ -1447,7 +1463,7 @@ class TemplateExecutor:
|
|
|
|
|
'Failed to create directory: {}, reason: {}'.
|
|
|
|
|
format(create_path, str(error)))
|
|
|
|
|
|
|
|
|
|
def _remove_directory(self, target_path: str) -> None:
|
|
|
|
|
def _remove_directory(self, target_path: str) -> NoReturn:
|
|
|
|
|
'''Метод для удаления директории.'''
|
|
|
|
|
if os.path.exists(target_path):
|
|
|
|
|
if os.path.isdir(target_path):
|
|
|
|
@ -1471,7 +1487,7 @@ class TemplateExecutor:
|
|
|
|
|
"reason: {1}").format(target_path,
|
|
|
|
|
error_message))
|
|
|
|
|
|
|
|
|
|
def _clear_directory(self, target_path: str) -> None:
|
|
|
|
|
def _clear_directory(self, target_path: str) -> NoReturn:
|
|
|
|
|
'''Метод для очистки содержимого целевой директории.'''
|
|
|
|
|
if os.path.exists(target_path):
|
|
|
|
|
if os.path.isdir(target_path):
|
|
|
|
@ -1492,7 +1508,7 @@ class TemplateExecutor:
|
|
|
|
|
error_message))
|
|
|
|
|
|
|
|
|
|
def _link_directory(self, source: str, target_path: str,
|
|
|
|
|
force: bool = False) -> None:
|
|
|
|
|
force: bool = False) -> NoReturn:
|
|
|
|
|
'''Метод для создания по целевому пути ссылки на директорию
|
|
|
|
|
расположенную на пути, указанному в source.'''
|
|
|
|
|
try:
|
|
|
|
@ -1502,7 +1518,7 @@ class TemplateExecutor:
|
|
|
|
|
"failed to create symlink: {0} -> {1}, reason: {2}".
|
|
|
|
|
format(target_path, source, str(error)))
|
|
|
|
|
|
|
|
|
|
def _remove_file(self, target_path: str) -> None:
|
|
|
|
|
def _remove_file(self, target_path: str) -> NoReturn:
|
|
|
|
|
'''Метод для удаления файлов.'''
|
|
|
|
|
if os.path.exists(target_path):
|
|
|
|
|
if os.path.isfile(target_path):
|
|
|
|
@ -1533,7 +1549,7 @@ class TemplateExecutor:
|
|
|
|
|
"reason: {1}").format(target_path,
|
|
|
|
|
error_message))
|
|
|
|
|
|
|
|
|
|
def _clear_file(self, target_path: str) -> None:
|
|
|
|
|
def _clear_file(self, target_path: str) -> NoReturn:
|
|
|
|
|
'''Метод для очистки файлов.'''
|
|
|
|
|
if os.path.exists(target_path):
|
|
|
|
|
if os.path.isfile(target_path):
|
|
|
|
@ -1552,7 +1568,7 @@ class TemplateExecutor:
|
|
|
|
|
"reason: {1}").format(target_path,
|
|
|
|
|
error_message))
|
|
|
|
|
|
|
|
|
|
def _link_file(self, source: str, target_path: str) -> None:
|
|
|
|
|
def _link_file(self, source: str, target_path: str) -> NoReturn:
|
|
|
|
|
'''Метод для создания по целевому пути ссылки на файл расположенный на
|
|
|
|
|
пути, указанному в source.'''
|
|
|
|
|
try:
|
|
|
|
@ -1562,7 +1578,7 @@ class TemplateExecutor:
|
|
|
|
|
"failed to create symlink to the file: {0} -> {1}, reason: {2}".
|
|
|
|
|
format(target_path, source, str(error)))
|
|
|
|
|
|
|
|
|
|
def _run_template(self, template_object: TemplateWrapper) -> None:
|
|
|
|
|
def _run_template(self, template_object: TemplateWrapper) -> NoReturn:
|
|
|
|
|
'''Метод для сохранения текста шаблонов, который должен быть исполнен
|
|
|
|
|
интерпретатором указанным в run прямо во время обработки шаблонов.'''
|
|
|
|
|
text_to_run = template_object.template_text
|
|
|
|
@ -1600,7 +1616,7 @@ class TemplateExecutor:
|
|
|
|
|
" interpreter '{}', reason: {}").
|
|
|
|
|
format(interpreter, str(error)))
|
|
|
|
|
|
|
|
|
|
def _exec_template(self, template_object: TemplateWrapper) -> None:
|
|
|
|
|
def _exec_template(self, template_object: TemplateWrapper) -> NoReturn:
|
|
|
|
|
'''Метод для сохранения текста шаблонов, который должен быть исполнен
|
|
|
|
|
интерпретатором указанным в exec после выполнения всех прочих шаблонов.
|
|
|
|
|
'''
|
|
|
|
@ -1685,7 +1701,8 @@ class TemplateExecutor:
|
|
|
|
|
" interpreter '{}', reason: {}").
|
|
|
|
|
format(interpreter, str(error)))
|
|
|
|
|
|
|
|
|
|
def _chown_directory(self, target_path: str, chown_value: dict) -> None:
|
|
|
|
|
def _chown_directory(self, target_path: str, chown_value: dict
|
|
|
|
|
) -> NoReturn:
|
|
|
|
|
"""Метод для смены владельца директории."""
|
|
|
|
|
try:
|
|
|
|
|
if os.path.exists(target_path):
|
|
|
|
@ -1703,7 +1720,7 @@ class TemplateExecutor:
|
|
|
|
|
chown_value['gid']),
|
|
|
|
|
str(error)))
|
|
|
|
|
|
|
|
|
|
def _chmod_directory(self, target_path: str, chmod_value: int) -> None:
|
|
|
|
|
def _chmod_directory(self, target_path: str, chmod_value: int) -> NoReturn:
|
|
|
|
|
'''Метод для смены прав доступа к директории.'''
|
|
|
|
|
if isinstance(chmod_value, tuple) and not chmod_value[1]:
|
|
|
|
|
chmod_value = chmod_value[0]
|
|
|
|
@ -1724,7 +1741,7 @@ class TemplateExecutor:
|
|
|
|
|
'Can not chmod directory: {0}, reason: {1}'.
|
|
|
|
|
format(target_path, str(error)))
|
|
|
|
|
|
|
|
|
|
def _chown_file(self, target_path: str, chown_value: dict) -> None:
|
|
|
|
|
def _chown_file(self, target_path: str, chown_value: dict) -> NoReturn:
|
|
|
|
|
'''Метод для смены владельца файла.'''
|
|
|
|
|
try:
|
|
|
|
|
if os.path.exists(target_path):
|
|
|
|
@ -1742,7 +1759,7 @@ class TemplateExecutor:
|
|
|
|
|
chown_value['gid']),
|
|
|
|
|
str(error)))
|
|
|
|
|
|
|
|
|
|
def _chmod_file(self, target_path: str, chmod_value: int) -> None:
|
|
|
|
|
def _chmod_file(self, target_path: str, chmod_value: int) -> NoReturn:
|
|
|
|
|
'''Метод для смены прав доступа к директории.'''
|
|
|
|
|
try:
|
|
|
|
|
if not os.path.exists(target_path):
|
|
|
|
@ -1764,7 +1781,7 @@ class TemplateExecutor:
|
|
|
|
|
format(target_path, str(error)))
|
|
|
|
|
|
|
|
|
|
def _use_chmod_x_mask(self, chmod: Tuple[int, int],
|
|
|
|
|
current_mode: Union[int, None] = None) -> int:
|
|
|
|
|
current_mode: Optional[int] = None) -> int:
|
|
|
|
|
'''Метод для наложения X-маски, необходимой для получения значения
|
|
|
|
|
chmod, c учетом возможности наличия в нем значения "X".'''
|
|
|
|
|
if not chmod[1]:
|
|
|
|
@ -1862,7 +1879,7 @@ class TemplateExecutor:
|
|
|
|
|
else:
|
|
|
|
|
return str(gid)
|
|
|
|
|
|
|
|
|
|
def _check_os_error(self, error: Exception, path_to_check: str) -> True:
|
|
|
|
|
def _check_os_error(self, error: Exception, path_to_check: str) -> bool:
|
|
|
|
|
'''Метод для проверки причины, по которой не удалось изменить владельца
|
|
|
|
|
или права доступа файла.'''
|
|
|
|
|
if hasattr(error, 'errno') and error.errno == errno.EPERM:
|
|
|
|
@ -1872,7 +1889,7 @@ class TemplateExecutor:
|
|
|
|
|
return hasattr(error, 'errno') and error.errno == errno.EACCES and\
|
|
|
|
|
'var/calculate/remote' in path_to_check
|
|
|
|
|
|
|
|
|
|
def _is_vfat(self, path_to_check: str):
|
|
|
|
|
def _is_vfat(self, path_to_check: str) -> bool:
|
|
|
|
|
'''Метод, проверяющий является ли файловая система vfat. Нужно для того,
|
|
|
|
|
чтобы знать о возможности применения chown, chmod и т.д.'''
|
|
|
|
|
# Инициализируем объект для проверки примонтированных файловых систем.
|
|
|
|
@ -1892,7 +1909,7 @@ class DirectoryTree:
|
|
|
|
|
self.base_directory = base_directory
|
|
|
|
|
self._tree = {}
|
|
|
|
|
|
|
|
|
|
def update_tree(self, tree: dict) -> None:
|
|
|
|
|
def update_tree(self, tree: dict) -> NoReturn:
|
|
|
|
|
'''Метод, инициирующий наложение заданного дерева каталогов на данный
|
|
|
|
|
экземпляр дерева.'''
|
|
|
|
|
self._update(self._tree, tree)
|
|
|
|
@ -1908,7 +1925,7 @@ class DirectoryTree:
|
|
|
|
|
original_tree[parent] = child
|
|
|
|
|
return original_tree
|
|
|
|
|
|
|
|
|
|
def show_tree(self) -> None:
|
|
|
|
|
def show_tree(self) -> NoReturn:
|
|
|
|
|
pprint(self._tree)
|
|
|
|
|
|
|
|
|
|
def get_directory_tree(self, directory: str) -> "DirectoryTree":
|
|
|
|
@ -1927,7 +1944,7 @@ class DirectoryTree:
|
|
|
|
|
else:
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
def __setitem__(self, name: str, value: Union[None, dict]) -> None:
|
|
|
|
|
def __setitem__(self, name: str, value: Union[None, dict]) -> NoReturn:
|
|
|
|
|
self._tree[name] = value
|
|
|
|
|
|
|
|
|
|
def __iter__(self) -> Iterator[str]:
|
|
|
|
@ -1948,8 +1965,9 @@ class DirectoryProcessor:
|
|
|
|
|
def __init__(self, action: str,
|
|
|
|
|
datavars_module: Union[Datavars, Variables] = Variables(),
|
|
|
|
|
install: Union[str, PackageAtomName] = '',
|
|
|
|
|
output_module: IOModule = IOModule(), dbpkg: bool = True,
|
|
|
|
|
namespace: NamespaceNode = None, **groups: dict):
|
|
|
|
|
output_module: IOModule = IOModule(),
|
|
|
|
|
dbpkg: bool = True, pkg_autosave: bool = True,
|
|
|
|
|
namespace: Optional[NamespaceNode] = None, **groups: dict):
|
|
|
|
|
if isinstance(action, list):
|
|
|
|
|
self.action = action
|
|
|
|
|
else:
|
|
|
|
@ -1958,6 +1976,7 @@ class DirectoryProcessor:
|
|
|
|
|
self.output: IOModule = output_module
|
|
|
|
|
self.datavars_module: Variables = datavars_module
|
|
|
|
|
self._namespace: NamespaceNode = namespace
|
|
|
|
|
self._pkg_autosave: bool = pkg_autosave
|
|
|
|
|
|
|
|
|
|
# Корневая директория.
|
|
|
|
|
self.cl_chroot_path: str
|
|
|
|
@ -2013,7 +2032,8 @@ class DirectoryProcessor:
|
|
|
|
|
cl_config_archive=self.cl_config_archive,
|
|
|
|
|
cl_config_path=self.cl_config_path,
|
|
|
|
|
execute_archive_path=self.cl_exec_dir_path,
|
|
|
|
|
dbpkg=dbpkg)
|
|
|
|
|
dbpkg=dbpkg,
|
|
|
|
|
pkg_autosave=self._pkg_autosave)
|
|
|
|
|
|
|
|
|
|
# Разбираем atom имена пакетов, указанных для групп пакетов.
|
|
|
|
|
if groups:
|
|
|
|
@ -2111,7 +2131,7 @@ class DirectoryProcessor:
|
|
|
|
|
return path_to_add
|
|
|
|
|
|
|
|
|
|
def _add_package_to_group(self, group_name: str,
|
|
|
|
|
package_atom: str) -> None:
|
|
|
|
|
package_atom: str) -> NoReturn:
|
|
|
|
|
try:
|
|
|
|
|
groups_namespace = self.datavars_module.main.cl.groups
|
|
|
|
|
except (VariableNotFoundError, AttributeError):
|
|
|
|
@ -2192,7 +2212,7 @@ class DirectoryProcessor:
|
|
|
|
|
return -1
|
|
|
|
|
return -2
|
|
|
|
|
|
|
|
|
|
def _make_current_template_var(self) -> None:
|
|
|
|
|
def _make_current_template_var(self) -> NoReturn:
|
|
|
|
|
var_path = ['main', 'cl']
|
|
|
|
|
namespace = self.datavars_module
|
|
|
|
|
|
|
|
|
@ -2213,7 +2233,7 @@ class DirectoryProcessor:
|
|
|
|
|
else:
|
|
|
|
|
namespace['current_template'] = ""
|
|
|
|
|
|
|
|
|
|
def process_template_directories(self) -> None:
|
|
|
|
|
def process_template_directories(self) -> NoReturn:
|
|
|
|
|
'''Метод для обхода шаблонов, содержащихся в каталогах из
|
|
|
|
|
main.cl_template.path.'''
|
|
|
|
|
# Режим заполнения очередей директорий пакетов, необходимых для более
|
|
|
|
@ -2225,7 +2245,8 @@ class DirectoryProcessor:
|
|
|
|
|
package = self.for_package
|
|
|
|
|
else:
|
|
|
|
|
package = Package(self.for_package,
|
|
|
|
|
chroot_path=self.cl_chroot_path)
|
|
|
|
|
chroot_path=self.cl_chroot_path,
|
|
|
|
|
autosave=self._pkg_autosave)
|
|
|
|
|
else:
|
|
|
|
|
package = None
|
|
|
|
|
|
|
|
|
@ -2277,14 +2298,15 @@ class DirectoryProcessor:
|
|
|
|
|
self._run_exec_files()
|
|
|
|
|
|
|
|
|
|
self.template_executor.save_changes()
|
|
|
|
|
PackageCreator.save_all()
|
|
|
|
|
return self.template_executor.changed_files
|
|
|
|
|
|
|
|
|
|
def _run_template_from_base_directory(
|
|
|
|
|
self, template_names: List[str],
|
|
|
|
|
base_directory: str,
|
|
|
|
|
package: Union[Package, None] = None,
|
|
|
|
|
directory_tree: Union[dict, None] = None
|
|
|
|
|
) -> None:
|
|
|
|
|
package: Optional[Package] = None,
|
|
|
|
|
directory_tree: Optional[dict] = None
|
|
|
|
|
) -> NoReturn:
|
|
|
|
|
'''Метод для запуска шаблонов файлов находящихся в базовой директории.
|
|
|
|
|
'''
|
|
|
|
|
self.template_engine.change_directory(base_directory)
|
|
|
|
@ -2335,7 +2357,8 @@ class DirectoryProcessor:
|
|
|
|
|
parameters)
|
|
|
|
|
if parameters.package:
|
|
|
|
|
template_package = Package(parameters.package,
|
|
|
|
|
chroot_path=self.cl_chroot_path)
|
|
|
|
|
chroot_path=self.cl_chroot_path,
|
|
|
|
|
autosave=self._pkg_autosave)
|
|
|
|
|
else:
|
|
|
|
|
template_package = package
|
|
|
|
|
|
|
|
|
@ -2355,7 +2378,7 @@ class DirectoryProcessor:
|
|
|
|
|
template_text=template_text,
|
|
|
|
|
package=template_package)
|
|
|
|
|
|
|
|
|
|
def _execute_handlers(self) -> None:
|
|
|
|
|
def _execute_handlers(self) -> NoReturn:
|
|
|
|
|
'''Метод для запуска обработчиков добавленных в очередь обработчиков
|
|
|
|
|
с помощью параметра notify.'''
|
|
|
|
|
self.output.set_info('Processing handlers...')
|
|
|
|
@ -2421,7 +2444,7 @@ class DirectoryProcessor:
|
|
|
|
|
FILE, handler_path,
|
|
|
|
|
template_text=handler_text)
|
|
|
|
|
|
|
|
|
|
def _merge_packages(self) -> None:
|
|
|
|
|
def _merge_packages(self) -> NoReturn:
|
|
|
|
|
'''Метод для выполнения шаблонов относящихся к пакетам, указанным во
|
|
|
|
|
всех встреченных значениях параметра merge.'''
|
|
|
|
|
not_merged_packages = []
|
|
|
|
@ -2461,7 +2484,8 @@ class DirectoryProcessor:
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
package = Package(self.for_package,
|
|
|
|
|
chroot_path=self.cl_chroot_path)
|
|
|
|
|
chroot_path=self.cl_chroot_path,
|
|
|
|
|
autosave=self._pkg_autosave)
|
|
|
|
|
|
|
|
|
|
for directory_name in self.packages_file_trees[self.for_package]:
|
|
|
|
|
directory_tree = self.packages_file_trees[self.for_package].\
|
|
|
|
@ -2485,7 +2509,7 @@ class DirectoryProcessor:
|
|
|
|
|
else:
|
|
|
|
|
self.output.set_success('All packages are merged.')
|
|
|
|
|
|
|
|
|
|
def _run_exec_files(self) -> None:
|
|
|
|
|
def _run_exec_files(self) -> NoReturn:
|
|
|
|
|
'''Метод для выполнения скриптов, полученных в результате обработки
|
|
|
|
|
шаблонов с параметром exec.'''
|
|
|
|
|
for exec_file_path, exec_info in\
|
|
|
|
@ -2512,7 +2536,7 @@ class DirectoryProcessor:
|
|
|
|
|
current_target_path: str,
|
|
|
|
|
directory_parameters: ParametersContainer,
|
|
|
|
|
directory_tree: Union[dict, DirectoryTree] = {},
|
|
|
|
|
package: Union[Package, None] = None) -> None:
|
|
|
|
|
package: Optional[Package] = None) -> NoReturn:
|
|
|
|
|
'''Метод для рекурсивного обхода директорий с шаблонами, а также, при
|
|
|
|
|
необходимости, заполнения деревьев директорий шаблонов, с помощью
|
|
|
|
|
которых далее выполняются шаблоны пакетов из merge.'''
|
|
|
|
@ -2603,7 +2627,8 @@ class DirectoryProcessor:
|
|
|
|
|
if (package is None or
|
|
|
|
|
package.package_name != directory_parameters.package):
|
|
|
|
|
package = Package(directory_parameters.package,
|
|
|
|
|
chroot_path=self.cl_chroot_path)
|
|
|
|
|
chroot_path=self.cl_chroot_path,
|
|
|
|
|
autosave=self._pkg_autosave)
|
|
|
|
|
else:
|
|
|
|
|
# Если .calculate_directory отсутствует -- создаем директорию,
|
|
|
|
|
# используя унаследованные параметры и имя самой директории.
|
|
|
|
@ -2714,7 +2739,8 @@ class DirectoryProcessor:
|
|
|
|
|
if (template_package is None or
|
|
|
|
|
package.package_name != template_parameters.package):
|
|
|
|
|
template_package = Package(template_parameters.package,
|
|
|
|
|
chroot_path=self.cl_chroot_path)
|
|
|
|
|
chroot_path=self.cl_chroot_path,
|
|
|
|
|
autosave=self._pkg_autosave)
|
|
|
|
|
|
|
|
|
|
if not template_parameters.run and not template_parameters.exec:
|
|
|
|
|
if not template_parameters.format:
|
|
|
|
@ -2873,7 +2899,7 @@ class DirectoryProcessor:
|
|
|
|
|
template_type: int,
|
|
|
|
|
template_path: str,
|
|
|
|
|
template_text: str = '',
|
|
|
|
|
package: Union[Package, None] = None
|
|
|
|
|
package: Optional[Package] = None
|
|
|
|
|
) -> Union[bool, str]:
|
|
|
|
|
'''Метод для наложения шаблонов и обработки информации полученной после
|
|
|
|
|
наложения.'''
|
|
|
|
@ -2952,7 +2978,7 @@ class DirectoryProcessor:
|
|
|
|
|
|
|
|
|
|
def _check_package_and_action(self, parameters: ParametersContainer,
|
|
|
|
|
template_path: str,
|
|
|
|
|
directory_tree: Union[dict, None] = None
|
|
|
|
|
directory_tree: Optional[dict] = None
|
|
|
|
|
) -> bool:
|
|
|
|
|
'''Метод для проверки параметров action и package во время обработки
|
|
|
|
|
каталогов с шаблонами. Если среди аргументов указано также
|
|
|
|
|