Support of the handler is added.

handlers
Иванов Денис 4 years ago
parent 6561bb2c00
commit 8959bddcdc

@ -96,7 +96,7 @@ class ParametersProcessor:
'notify'}
inheritable_parameters = {'chmod', 'chown', 'autoupdate', 'env',
'package', 'action'}
'package', 'action', 'handler'}
# Параметры по умолчанию для файлов --
# будут заполняться из __datavars__
@ -158,7 +158,8 @@ class ParametersProcessor:
'source': self.check_postparse_source,
'autoupdate': self.check_postparse_autoupdate,
'run': self.check_postparse_run,
'exec': self.check_postparse_exec})
'exec': self.check_postparse_exec,
'handler': self.check_postparse_handler})
# Если параметр является наследуемым только при некоторых условиях --
# указываем здесь эти условия.
@ -447,10 +448,10 @@ class ParametersProcessor:
f" {type(parameter_value)}.")
def check_handler_parameter(self, parameter_value):
if isinstance(parameter_value, str):
return parameter_value
raise IncorrectParameter("'handler' parameter must be string"
f" value not {type(parameter_value)}.")
if not isinstance(parameter_value, str):
raise IncorrectParameter("'handler' parameter must be string"
f" value not {type(parameter_value)}.")
return parameter_value
def check_notify_parameter(self, parameter_value):
if isinstance(parameter_value, list):
@ -516,6 +517,17 @@ class ParametersProcessor:
raise IncorrectParameter("'unbound' parameter is incompatible"
" with 'autoupdate' parameter")
def check_postparse_handler(self, parameter_rvalue):
if self._parameters_container.merge:
raise IncorrectParameter("'merge' parameter is not available"
" in handler templates.")
elif (self._parameters_container.package and
not self._parameters_container.is_inherited('package')):
raise IncorrectParameter("'package' parameter is not available"
" in handler templates.")
# Методы для проверки того, являются ли параметры наследуемыми.
def is_chmod_inheritable(self, parameter_value):
chmod_regex = re.compile(r'\d+')
@ -739,6 +751,10 @@ class ParametersContainer(MutableMapping):
print('Inherited:')
pprint(self.__inheritable)
def is_inherited(self, parameter_name):
return (parameter_name not in self.__parameters
and parameter_name in self.__inheritable)
def _clear_container(self):
self.__parameters.clear()
self.__inheritable.clear()

@ -14,6 +14,7 @@ from calculate.variables.loader import Datavars
from .format.base_format import Format
from ..utils.io_module import IOModule
from collections import OrderedDict, abc
from contextlib import contextmanager
from ..utils.mount import Mounts
import hashlib
import fnmatch
@ -357,7 +358,8 @@ class TemplateWrapper:
# Если для шаблона и целевого файла никаким образом не удается
# определить пакет и есть параметр append -- шаблон пропускаем.
if parameter_package is None and file_package is None:
if self.parameters.append and self.parameters.append != 'skip':
if (self.parameters.append and self.parameters.append != 'skip'
and not self.parameters.handler):
raise TemplateCollisionError(
"'package' parameter is not defined for"
" template with 'append' parameter.")
@ -367,6 +369,12 @@ class TemplateWrapper:
self.target_package_name = file_package
elif file_package is None:
if self.parameters.handler:
raise TemplateCollisionError((
"The template is a handler while target"
" file belongs {} package").format(
file_package.atom
))
self.target_package_name = parameter_package
elif file_package != parameter_package and self.template_type != DIR:
@ -393,7 +401,7 @@ class TemplateWrapper:
# Проверим, является ли файл защищенным.
# Сначала проверяем по переменной CONFIG_PROTECT.
if self.dbpkg:
if self.dbpkg and not self.parameters.handler:
for protected_path in self._protected_set:
if self.target_path.startswith(protected_path):
self.protected = True
@ -1689,7 +1697,9 @@ class DirectoryProcessor:
self.packages_file_trees = {}
# Список обработчиков.
self.handlers = OrderedDict()
self._handlers = {}
self._handlers_queue = []
self._handling = None
def _get_cl_ignore_files(self) -> list:
'''Метод для получения из соответствующей переменной списка паттернов
@ -1756,11 +1766,69 @@ class DirectoryProcessor:
self.processed_packages.append(self.for_package)
self._merge_packages()
if self._handlers_queue:
self._execute_handlers()
if self.template_executor.execute_files:
self._run_exec_files()
self.template_executor.save_changes()
def _execute_handlers(self):
self.output.set_info('Processing handlers...')
print('CURRENT HANDLERS QUEUE:', ', '.join(self._handlers_queue))
index = 0
while index < len(self._handlers_queue):
handler = self._handlers_queue[index]
index += 1
if handler in self._handlers:
handler_type, handler_path = self._handlers[handler]
else:
self.output.set_warning(
f"Handler '{handler}' is not found.")
continue
with self._start_handling(handler):
if handler_type is DIR:
self.directory_tree = {}
self._walk_directory_tree(
handler_path,
self.cl_chroot_path,
ParametersContainer(),
directory_tree=self.directory_tree)
self.output.set_success(
f"Processed handler directory '{handler}'."
f"Path: '{handler_path}'")
elif handler_type is FILE:
handler_dir, handler_name = os.path.split(handler_path)
self.template_engine.change_directory(handler_dir)
parameters = ParametersContainer()
handler_text = self._parse_template(parameters,
handler_name,
FILE, handler_dir)
if handler_text is False:
continue
if parameters.notify:
for handler_id in parameters.notify:
if handler_id not in self._handlers_queue:
self._handlers_queue.append(handler_id)
# Корректируем путь к целевому файлу.
target_file_path = self._make_target_path(
self.cl_chroot_path,
handler_name,
parameters)
# Выполняем действия, указанные в обработчике.
self._execute_template(target_file_path, parameters,
FILE, handler_path,
template_text=handler_text)
self.output.set_success(
f"Processed handler file '{handler}'."
f" Path: '{handler_path}'")
def _merge_packages(self):
'''Метод для выполнения шаблонов относящихся к пакетам, указанным во
всех встреченных значениях параметра merge.'''
@ -1877,15 +1945,26 @@ class DirectoryProcessor:
# Если нужно заполнять дерево директорий, отправляем в метод для
# проверки параметров package и action текущее дерево.
if not self._check_package_and_action(
directory_parameters,
current_directory_path,
directory_tree=(directory_tree if
self.fill_trees else None)):
# Если проверка не пройдена и включено заполнение дерева, то,
# используя нынешнее состояние дерева директорий, обновляем
# дерево пакета текущего шаблона директории.
if self.fill_trees:
if (self._handling is None
and not self._check_package_and_action(
directory_parameters,
current_directory_path,
directory_tree=(directory_tree if
self.fill_trees else None))):
if directory_parameters.handler:
# Если директория шаблонов является обработчиком и параметр
# handler -- не унаследован, что свидетельствует о том,
# что директория не расположена внутри другой
# директории-обработчика добавляем ее в словарь
# обработчиков и пропускаем ее.
self._handlers.update({directory_parameters.handler:
(DIR, current_directory_path)})
if self.fill_trees:
directory_tree = {}
elif self.fill_trees:
# Если проверка не пройдена и включено заполнение дерева,
# то, используя нынешнее состояние дерева директорий,
# обновляем дерево пакета текущего шаблона директории.
self._update_package_tree(directory_parameters.package,
directory_tree[directory_name])
# Перед выходом из директории очищаем текущий уровень
@ -1893,11 +1972,23 @@ class DirectoryProcessor:
directory_tree = {}
return
if self._handling is not None and directory_parameters.handler:
if directory_parameters.handler != self._handling:
self.output.set_error("'handler' parameter is not"
" available in other handler"
f" '{self._handling}'")
return
# Если есть параметр merge -- сохраняем присутствующие в нем пакеты
# для последующей обработки.
if self.for_package and directory_parameters.merge:
self.packages_to_merge.extend(directory_parameters.merge)
if directory_parameters.notify:
for handler_id in directory_parameters.notify:
if handler_id not in self._handlers_queue:
self._handlers_queue.append(handler_id)
# Если присутствует параметр package -- проверяем, изменился ли он
# и был ли задан до этого. Если не был задан или изменился, меняем
# текущий пакет ветки шаблонов на этот.
@ -1909,20 +2000,6 @@ class DirectoryProcessor:
else:
# Если .calculate_directory отсутствует -- создаем директорию,
# используя унаследованные параметры и имя самой директории.
if not self._check_package_and_action(
directory_parameters,
current_directory_path,
directory_tree=(directory_tree if
self.fill_trees else None)):
# Обновляем дерево директорий для данного пакета.
if self.fill_trees:
self._update_package_tree(directory_parameters.package,
directory_tree[directory_name])
# Перед выходом из директории очищаем текущий уровень
# дерева.
directory_tree = {}
return
# Для того чтобы директория была создана, просто добавляем параметр
# append = join.
directory_parameters.set_parameter({'append': 'join'})
@ -1971,18 +2048,36 @@ class DirectoryProcessor:
# Если находимся на стадии заполнения дерева директорий --
# проверяем параметры package и action с заполнением дерева.
if not self._check_package_and_action(
if (self._handling is None
and not self._check_package_and_action(
template_parameters,
template_path,
directory_tree=(directory_tree[directory_name] if
self.fill_trees else None)):
self.fill_trees else None))):
if template_parameters.handler:
# Если директория шаблонов является обработчиком --
# добавляем ее в словарь обработчиков и пропускаем ее.
self._handlers.update({template_parameters.handler:
(FILE, template_path)})
continue
if self._handling is not None and template_parameters.handler:
if template_parameters.handler != self._handling:
self.output.set_error("'handler' parameter is not"
" available in other handler"
f" '{self._handling}'")
continue
# Если есть параметр merge добавляем его содержимое в список
# пакетов для последующей обработки.
if self.for_package and template_parameters.merge:
self.packages_to_merge.extend(template_parameters.merge)
if template_parameters.notify:
for handler_id in template_parameters.notify:
if handler_id not in self._handlers_queue:
self._handlers_queue.append(handler_id)
# Корректируем путь к целевому файлу.
target_file_path = self._make_target_path(current_target_path,
template_name,
@ -2216,6 +2311,12 @@ class DirectoryProcessor:
каталогов с шаблонами. Если среди аргументов указано также
дерево каталогов, то в случае несовпадения значений package для файла
или директории, им в дереве присваивается значение None.'''
if parameters.handler:
print('\nHANDLER WAS FOUND:', template_path)
print('NOW HANDLERS:', *list(self._handlers.keys()))
print()
return False
if parameters.append != 'skip' or parameters.action:
if not parameters.action:
self.output.set_warning(
@ -2233,10 +2334,6 @@ class DirectoryProcessor:
template_path))
return False
if parameters.handler:
self.handlers.update({parameters.handler: template_path})
return False
if self.for_package:
if not parameters.package:
if self.for_package is not NonePackage:
@ -2260,3 +2357,11 @@ class DirectoryProcessor:
return False
return True
@contextmanager
def _start_handling(self, handler):
try:
self._handling = handler
yield self
finally:
self._handling = None

@ -895,28 +895,85 @@ class TestDirectoryProcessor:
assert '/etc/dir_35/file_0' in test_package
def test_handlers_basic(self):
datavars.main['cl_template_path'] = os.path.join(CHROOT_PATH,
'templates_35')
directory_processor = DirectoryProcessor(
'install',
datavars_module=datavars,
package='test-category/test-package'
)
directory_processor.process_template_directories()
test_package = Package(test_package_name, chroot_path=CHROOT_PATH)
other_package = Package(other_package_name, chroot_path=CHROOT_PATH)
assert os.path.exists(join_paths(CHROOT_PATH,
'etc/dir_37'))
assert os.path.exists(join_paths(CHROOT_PATH,
'etc/dir_37/file_0'))
assert '/etc/dir_37/file_0' in test_package
assert os.path.exists(join_paths(CHROOT_PATH,
'etc/dir_38'))
assert os.path.exists(join_paths(CHROOT_PATH,
'etc/dir_38/file_0'))
assert '/etc/dir_38/file_0' in other_package
try:
datavars.main['cl_template_path'] = os.path.join(CHROOT_PATH,
'templates_35')
directory_processor = DirectoryProcessor(
'install',
datavars_module=datavars,
package='test-category/test-package'
)
directory_processor.process_template_directories()
test_package = Package(test_package_name, chroot_path=CHROOT_PATH)
# Тестируем handler_0 -- это просто обработчик-скрипт.
assert os.path.exists(join_paths(CHROOT_PATH,
'etc/dir_37'))
assert os.path.exists(join_paths(CHROOT_PATH,
'etc/dir_37/file_0'))
assert '/etc/dir_37/file_0' in test_package
# Тестируем handler_0 -- это просто обработчик-шаблон с notify.
assert os.path.exists(join_paths(CHROOT_PATH,
'etc/dir_38'))
assert os.path.exists(join_paths(CHROOT_PATH,
'etc/dir_38/file_0'))
# Тестируем handler_1 -- это просто обработчик-шаблон с notify.
assert os.path.exists(join_paths(CHROOT_PATH,
'etc/dir_39'))
assert os.path.exists(join_paths(CHROOT_PATH,
'etc/dir_39/file_0'))
# Тестируем handler_2 -- это просто обработчик-скрипт с notify.
assert os.path.exists(join_paths(CHROOT_PATH,
'etc/dir_40'))
assert os.path.exists(join_paths(CHROOT_PATH,
'etc/dir_40/file_0'))
# Тестируем handler_3 -- это обработчик-шаблон с notify к
# вызвавшему его handler_2 и handler_4, спрятанный в директории,
# не являющейся обработчиком.
assert os.path.exists(join_paths(CHROOT_PATH,
'etc/dir_41'))
assert os.path.exists(join_paths(CHROOT_PATH,
'etc/dir_41/file_0'))
# Тестируем handler_4 -- это обработчик с exec и notify.
assert os.path.exists(join_paths(CHROOT_PATH,
'etc/dir_42'))
assert os.path.exists(join_paths(CHROOT_PATH,
'etc/dir_42/file_0'))
# Тестируем handler_5 -- это обработчик-директория.
assert os.path.exists(join_paths(CHROOT_PATH,
'etc/dir_43'))
assert os.path.exists(join_paths(CHROOT_PATH,
'etc/dir_43/file_0'))
# Тестируем handler_6 -- это обработчик из обработчика-директрии.
# Этот обработчик не выполняется.
assert not os.path.exists(join_paths(CHROOT_PATH,
'etc/dir_44'))
# Тестируем handler_7 -- это обработчик с параметром package.
# Этот обработчик не выполняется.
assert not os.path.exists(join_paths(CHROOT_PATH,
'etc/dir_45'))
# Тестируем handler_8 -- это обработчик с параметром merge.
# Этот обработчик не выполняется.
assert not os.path.exists(join_paths(CHROOT_PATH,
'etc/dir_46'))
# Тестируем handler_9 -- это обработчик замещенный другим.
# Этот обработчик не выполняется.
assert not os.path.exists(join_paths(CHROOT_PATH,
'etc/dir_47'))
assert os.path.exists(join_paths(CHROOT_PATH,
'etc/dir_48'))
except KeyboardInterrupt:
assert False
def test_view_tree(self):
list_path = join_paths(CHROOT_PATH, '/etc')

@ -1,4 +1,5 @@
{% calculate append = 'join', format = 'bind', notify = 'handler_0' %}
{% calculate append = 'join', format = 'bind', notify = ['handler_0',
'handler_1'] %}
options {
parameter-1 {{ variables.variable_1 }};
parameter-2 {{ variables.variable_2 }};

@ -0,0 +1,5 @@
{% calculate handler = 'handler_3', append = 'join', path = '/etc/dir_41/',
name = 'file_0', format = 'samba', notify = 'handler_2, handler_4' %}
[section 1]
parameter 1 = {{ variables.variable_1 }}
parameter 2 = {{ variables.variable_2 }}

@ -0,0 +1,5 @@
{% calculate handler = 'handler_9', append = 'join', path = '/etc/dir_48/',
name = 'file_0', format = 'samba' %}
[section 1]
parameter 1 = {{ variables.variable_1 }}
parameter 2 = {{ variables.variable_2 }}

@ -1 +0,0 @@
{% calculate append = 'join', package = 'test-category/other-package' %}

@ -1,5 +0,0 @@
{% calculate append = 'join', format = 'bind' %}
options {
parameter-1 {{ variables.variable_1 }};
parameter-2 {{ variables.variable_2 }};
};

@ -1,3 +1,5 @@
{% calculate handler = 'handler_0', merge = 'test-category/other-package',
run = '/usr/bin/python' %}
print('This handler is needed for merging test-category.')
{% calculate handler = 'handler_0', path = '/etc', run = '/usr/bin/python' %}
import os
os.mkdir('./dir_38')
with open('./dir_38/file_0', 'w') as file_0:
file_0.write('Information number 1.')

@ -0,0 +1,5 @@
{% calculate handler = 'handler_1', append = 'join', path = '/etc/dir_39/',
name = 'file_0', format = 'samba', notify = 'handler_2' %}
[section 1]
parameter 1 = {{ variables.variable_1 }}
parameter 2 = {{ variables.variable_2 }}

@ -0,0 +1,6 @@
{% calculate handler = 'handler_2', path = '/etc', run = '/usr/bin/python',
notify = 'handler_3' %}
import os
os.mkdir('./dir_40')
with open('./dir_40/file_0', 'w') as file_0:
file_0.write('Information number 2.')

@ -0,0 +1,6 @@
{% calculate handler = 'handler_4', path = '/etc', exec = '/usr/bin/python',
notify = 'handler_5' %}
import os
os.mkdir('./dir_42')
with open('./dir_42/file_0', 'w') as file_0:
file_0.write('Information number 2.')

@ -0,0 +1,2 @@
{% calculate handler = 'handler_5', path = '/etc', name = 'dir_43',
notify = 'handler_9' %}

@ -0,0 +1,5 @@
{% calculate append = 'join', format = 'samba',
notify = 'handler_6, handler_7' %}
[section 1]
parameter 1 = {{ variables.variable_1 }}
parameter 2 = {{ variables.variable_2 }}

@ -0,0 +1,6 @@
{% calculate handler = 'handler_6', path = '/etc', run = '/usr/bin/python',
notify = 'handler_3' %}
import os
os.mkdir('./dir_44')
with open('./dir_44/file_0', 'w') as file_0:
file_0.write('Information number 3.')

@ -0,0 +1,5 @@
{% calculate handler = 'handler_7', append = 'join', path = '/etc/dir_45/',
name = 'file_0', format = 'samba', package = 'test-category/test-package' %}
[section 1]
parameter 1 = {{ variables.variable_1 }}
parameter 2 = {{ variables.variable_2 }}

@ -0,0 +1,5 @@
{% calculate handler = 'handler_8', append = 'join', path = '/etc/dir_46/',
name = 'file_0', format = 'samba', merge = 'test-category/test-package' %}
[section 1]
parameter 1 = {{ variables.variable_1 }}
parameter 2 = {{ variables.variable_2 }}

@ -0,0 +1,5 @@
{% calculate handler = 'handler_9', append = 'join', path = '/etc/dir_48/',
name = 'file_0', format = 'samba' %}
[section 1]
parameter 1 = {{ variables.variable_1 }}
parameter 2 = {{ variables.variable_2 }}
Loading…
Cancel
Save