|
|
|
|
# vim: fileencoding=utf-8
|
|
|
|
|
#
|
|
|
|
|
import os
|
|
|
|
|
import re
|
|
|
|
|
import stat
|
|
|
|
|
import shutil
|
|
|
|
|
from ..utils.files import join_paths
|
|
|
|
|
from importlib import import_module
|
|
|
|
|
from .template_engine import TemplateEngine, Variables, ConditionFailed
|
|
|
|
|
from ..utils.io_module import IOModule
|
|
|
|
|
from collections import OrderedDict
|
|
|
|
|
from ..utils.mount import Mounts
|
|
|
|
|
from ..utils.package import PackageAtom, PackageAtomError
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class IncorrectParameter(Exception):
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class DefaultParameterError(Exception):
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class TemplateActionError(Exception):
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class TemplateError(Exception):
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Типы шаблона: директория или файл.
|
|
|
|
|
DIR, FILE = range(2)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class TemplateParameters:
|
|
|
|
|
'''Класс для хранения, проверки и разбора параметров шаблона.'''
|
|
|
|
|
available_parameters = {'name', 'path', 'append', 'chmod', 'chown',
|
|
|
|
|
'autoupdate', 'env', 'force', 'source', 'format',
|
|
|
|
|
'protected', 'mirror', 'run', 'exec', 'env',
|
|
|
|
|
'package', 'merge', 'postmerge', 'action',
|
|
|
|
|
'rebuild', 'restart', 'stop', 'start'}
|
|
|
|
|
|
|
|
|
|
inheritable_parameters = {'chmod': '', 'chown': '', 'autoupdate': False,
|
|
|
|
|
'env': '', 'package': '', 'action': ''}
|
|
|
|
|
|
|
|
|
|
available_appends = set()
|
|
|
|
|
|
|
|
|
|
directory_default_values = {'chown': 'root:root',
|
|
|
|
|
'chmod': '755'}
|
|
|
|
|
|
|
|
|
|
file_default_values = {'chown': 'root:root',
|
|
|
|
|
'chmod': '644'}
|
|
|
|
|
|
|
|
|
|
available_formats = set()
|
|
|
|
|
format_is_inspected = False
|
|
|
|
|
|
|
|
|
|
chmod_value_regular = re.compile(
|
|
|
|
|
r'([r-][w-][x-])([r-][w-][x-])([r-][w-][x-])')
|
|
|
|
|
|
|
|
|
|
package_atom_parser = PackageAtom()
|
|
|
|
|
|
|
|
|
|
def __init__(self, parameters: dict, template_type, chroot_path='/'):
|
|
|
|
|
self.template_type = template_type
|
|
|
|
|
self.chroot_path = chroot_path
|
|
|
|
|
|
|
|
|
|
self._parameters_dictionary = {}
|
|
|
|
|
self._inspect_formats_package()
|
|
|
|
|
|
|
|
|
|
self.checkers_list = OrderedDict({
|
|
|
|
|
'package': self.check_package_parameter,
|
|
|
|
|
'append': self.check_append_parameter,
|
|
|
|
|
'rebuild': self.check_rebuild_parameter,
|
|
|
|
|
'restart': self.check_restart_parameter,
|
|
|
|
|
'stop': self.check_stop_parameter,
|
|
|
|
|
'start': self.check_start_parameter,
|
|
|
|
|
'chown': self.check_chown_parameter,
|
|
|
|
|
'chmod': self.check_chmod_parameter,
|
|
|
|
|
'autoupdate': self.check_autoupdate_parameter,
|
|
|
|
|
'source': self.check_source_parameter,
|
|
|
|
|
'force': self.check_force_parameter,
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
if template_type == DIR:
|
|
|
|
|
self.check_template_parameters(self.directory_default_values)
|
|
|
|
|
elif template_type == FILE:
|
|
|
|
|
self.check_template_parameters(self.file_default_values)
|
|
|
|
|
except IncorrectParameter as error:
|
|
|
|
|
raise TemplateError('Default values error: {}'.format(str(error)))
|
|
|
|
|
|
|
|
|
|
self.check_template_parameters(parameters)
|
|
|
|
|
|
|
|
|
|
def __getattr__(self, parameter_name):
|
|
|
|
|
if parameter_name not in self.available_parameters:
|
|
|
|
|
raise IncorrectParameter("Unknown parameter: '{}'".
|
|
|
|
|
format(parameter_name))
|
|
|
|
|
elif parameter_name not in self._parameters_dictionary:
|
|
|
|
|
return False
|
|
|
|
|
else:
|
|
|
|
|
return self._parameters_dictionary[parameter_name]
|
|
|
|
|
|
|
|
|
|
def check_template_parameters(self, parameters):
|
|
|
|
|
for parameter_name in parameters:
|
|
|
|
|
# Если параметр наследуем и уже встречался до этого --
|
|
|
|
|
# второй раз не проверяем его.
|
|
|
|
|
if (parameter_name in self.inheritable_parameters and
|
|
|
|
|
not isinstance(self.inheritable_parameters[parameter_name],
|
|
|
|
|
bool) and
|
|
|
|
|
parameters[parameter_name] ==
|
|
|
|
|
self.inheritable_parameters[parameter_name]):
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
if parameter_name not in self.available_parameters:
|
|
|
|
|
raise IncorrectParameter("Unknown parameter '{0}'".
|
|
|
|
|
format(parameter_name))
|
|
|
|
|
elif parameter_name in self.checkers_list:
|
|
|
|
|
parameter_value = self.checkers_list[parameter_name](
|
|
|
|
|
parameters[parameter_name]
|
|
|
|
|
)
|
|
|
|
|
self._parameters_dictionary[parameter_name] = parameter_value
|
|
|
|
|
if parameter_name in self.inheritable_parameters:
|
|
|
|
|
self.inheritable_parameters[parameter_name] =\
|
|
|
|
|
parameter_value
|
|
|
|
|
|
|
|
|
|
def check_package_parameter(self, parameter_value):
|
|
|
|
|
try:
|
|
|
|
|
self.package_atom_parser.parse_package_parameter(parameter_value)
|
|
|
|
|
except PackageAtomError as error:
|
|
|
|
|
raise IncorrectParameter(str(error))
|
|
|
|
|
parameter_value = self.package_atom_parser.atom_dictionary
|
|
|
|
|
return parameter_value
|
|
|
|
|
|
|
|
|
|
def check_append_parameter(self, parameter_value):
|
|
|
|
|
if parameter_value not in self.available_appends:
|
|
|
|
|
raise IncorrectParameter("Unacceptable value '{}' of parameter"
|
|
|
|
|
" 'append'".format(parameter_value))
|
|
|
|
|
return parameter_value
|
|
|
|
|
|
|
|
|
|
def check_rebuild_parameter(self, parameter_value):
|
|
|
|
|
if isinstance(parameter_value, bool):
|
|
|
|
|
raise IncorrectParameter("'rebuild' parameter value is not bool")
|
|
|
|
|
elif 'package' not in self._parameters_dictionary:
|
|
|
|
|
raise IncorrectParameter(("'source' parameter is set without "
|
|
|
|
|
"'package' parameter"))
|
|
|
|
|
return parameter_value
|
|
|
|
|
|
|
|
|
|
def check_restart_parameter(self, parameter_value):
|
|
|
|
|
if parameter_value and isinstance(parameter_value, str):
|
|
|
|
|
return parameter_value
|
|
|
|
|
else:
|
|
|
|
|
raise IncorrectParameter(
|
|
|
|
|
"'restart' parameter value is not correct")
|
|
|
|
|
|
|
|
|
|
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")
|
|
|
|
|
|
|
|
|
|
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")
|
|
|
|
|
|
|
|
|
|
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 nkt correct")
|
|
|
|
|
|
|
|
|
|
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")
|
|
|
|
|
|
|
|
|
|
def check_chown_parameter(self, parameter_value):
|
|
|
|
|
if not parameter_value or isinstance(parameter_value, bool):
|
|
|
|
|
raise IncorrectParameter("'chown' parameter value is empty.")
|
|
|
|
|
parameter_value = self.get_chown_values(parameter_value)
|
|
|
|
|
return parameter_value
|
|
|
|
|
|
|
|
|
|
def check_chmod_parameter(self, parameter_value):
|
|
|
|
|
result = self.chmod_value_regular.search(parameter_value)
|
|
|
|
|
if result:
|
|
|
|
|
parameter_value = ''
|
|
|
|
|
for group_number in range(3):
|
|
|
|
|
current_group = result.groups()[group_number]
|
|
|
|
|
num = ''
|
|
|
|
|
for sym_number in range(3):
|
|
|
|
|
if current_group[sym_number] != '-':
|
|
|
|
|
num = num + '1'
|
|
|
|
|
else:
|
|
|
|
|
num = num + '0'
|
|
|
|
|
parameter_value = parameter_value + num
|
|
|
|
|
return int(parameter_value, 2)
|
|
|
|
|
elif parameter_value.isdigit():
|
|
|
|
|
parameter_value = int(parameter_value, 8)
|
|
|
|
|
return parameter_value
|
|
|
|
|
else:
|
|
|
|
|
raise IncorrectParameter("'chmod' parameter value is not correct")
|
|
|
|
|
|
|
|
|
|
def check_source_parameter(self, parameter_value):
|
|
|
|
|
if self.chroot_path != '/':
|
|
|
|
|
real_path = join_paths(self.chroot_path, parameter_value)
|
|
|
|
|
else:
|
|
|
|
|
real_path = parameter_value
|
|
|
|
|
|
|
|
|
|
if not parameter_value or isinstance(parameter_value, bool):
|
|
|
|
|
raise IncorrectParameter("'source' parameter value is empty")
|
|
|
|
|
elif (self.template_type == DIR and
|
|
|
|
|
('append' not in self._parameters_dictionary or
|
|
|
|
|
self._parameters_dictionary['append'] != 'link')):
|
|
|
|
|
raise IncorrectParameter(
|
|
|
|
|
("'source' parameter is set without "
|
|
|
|
|
"'append = link' for directory template")
|
|
|
|
|
)
|
|
|
|
|
elif not os.path.exists(real_path):
|
|
|
|
|
raise IncorrectParameter(
|
|
|
|
|
"File from 'source' parameter does not exist")
|
|
|
|
|
return os.path.normpath(real_path)
|
|
|
|
|
|
|
|
|
|
def check_force_parameter(self, parameter_value):
|
|
|
|
|
if isinstance(parameter_value, bool):
|
|
|
|
|
return parameter_value
|
|
|
|
|
else:
|
|
|
|
|
raise IncorrectParameter("'force' parameter value is not bool")
|
|
|
|
|
|
|
|
|
|
def check_autoupdate_parameter(self, parameter_value):
|
|
|
|
|
print('autoupdate value = {}'.format(parameter_value))
|
|
|
|
|
if isinstance(parameter_value, bool):
|
|
|
|
|
return parameter_value
|
|
|
|
|
else:
|
|
|
|
|
raise IncorrectParameter(
|
|
|
|
|
"'autoupdate' parameter value is not bool")
|
|
|
|
|
|
|
|
|
|
def get_chown_values(self, chown: str):
|
|
|
|
|
"""Получить значения uid и gid из параметра chown."""
|
|
|
|
|
if chown and ':' in chown:
|
|
|
|
|
user_name, group_name = chown.split(':')
|
|
|
|
|
|
|
|
|
|
if user_name.isdigit():
|
|
|
|
|
uid = int(user_name)
|
|
|
|
|
else:
|
|
|
|
|
import pwd
|
|
|
|
|
try:
|
|
|
|
|
if self.chroot_path == '/':
|
|
|
|
|
uid = pwd.getpwnam(user_name).pw_uid
|
|
|
|
|
else:
|
|
|
|
|
uid = self.get_uid_from_passwd(user_name)
|
|
|
|
|
except (KeyError, TypeError):
|
|
|
|
|
self.output.set_error(
|
|
|
|
|
format(user_name))
|
|
|
|
|
raise IncorrectParameter(
|
|
|
|
|
("'chown' value '{0}' is not correct:"
|
|
|
|
|
"no such user in the system: {1}").
|
|
|
|
|
format(chown, user_name))
|
|
|
|
|
if group_name.isdigit():
|
|
|
|
|
gid = int(group_name)
|
|
|
|
|
else:
|
|
|
|
|
import grp
|
|
|
|
|
try:
|
|
|
|
|
if self.chroot_path == '/':
|
|
|
|
|
gid = grp.getgrnam(group_name).gr_gid
|
|
|
|
|
else:
|
|
|
|
|
gid = self.get_gid_from_group(group_name)
|
|
|
|
|
except (KeyError, TypeError):
|
|
|
|
|
raise IncorrectParameter(
|
|
|
|
|
("'chown' value '{0}' is not correct:"
|
|
|
|
|
"no such group in the system: {1}").
|
|
|
|
|
format(chown, group_name))
|
|
|
|
|
return {'uid': uid, 'gid': gid}
|
|
|
|
|
else:
|
|
|
|
|
raise IncorrectParameter("'chown' value '{0}' is not correct".
|
|
|
|
|
format(chown, self.template_path))
|
|
|
|
|
|
|
|
|
|
def get_uid_from_passwd(self, user_name: str):
|
|
|
|
|
"""Взять uid из chroot passwd файла."""
|
|
|
|
|
passwd_file_path = os.path.join(self.chroot_path, 'etc/passwd')
|
|
|
|
|
passwd_dictionary = []
|
|
|
|
|
if os.path.exists(passwd_file_path):
|
|
|
|
|
with open(passwd_file_path, 'r') as passwd_file:
|
|
|
|
|
for line in passwd_file:
|
|
|
|
|
if line.startswith('#'):
|
|
|
|
|
continue
|
|
|
|
|
passwd_item = tuple(line.split(':')[0:3:2])
|
|
|
|
|
if (len(passwd_item) > 1 and passwd_item[0]
|
|
|
|
|
and passwd_item[0]):
|
|
|
|
|
passwd_dictionary.append(passwd_item)
|
|
|
|
|
passwd_dictionary = dict(passwd_dictionary)
|
|
|
|
|
return int(passwd_dictionary[user_name])
|
|
|
|
|
else:
|
|
|
|
|
IncorrectParameter("passwd file was not found in {}".
|
|
|
|
|
format(passwd_file_path))
|
|
|
|
|
|
|
|
|
|
def get_gid_from_group(self, group_name: str):
|
|
|
|
|
"""Взять gid из chroot group файла."""
|
|
|
|
|
group_file_path = os.path.join(self.chroot_path, 'etc/group')
|
|
|
|
|
group_dictionary = []
|
|
|
|
|
if os.path.exists(group_file_path):
|
|
|
|
|
with open(group_file_path, 'r') as group_file:
|
|
|
|
|
for line in group_file:
|
|
|
|
|
if line.startswith('#'):
|
|
|
|
|
continue
|
|
|
|
|
group_item = tuple(line.split(':')[0:3:2])
|
|
|
|
|
if len(group_item) > 1 and group_item[0] and group_item[1]:
|
|
|
|
|
group_dictionary.append(group_item)
|
|
|
|
|
group_dictionary = dict(group_dictionary)
|
|
|
|
|
if group_name in group_dictionary:
|
|
|
|
|
return int(group_dictionary[group_name])
|
|
|
|
|
else:
|
|
|
|
|
IncorrectParameter("'{0}' gid was not found in {1}".
|
|
|
|
|
format(group_name, group_file_path))
|
|
|
|
|
else:
|
|
|
|
|
IncorrectParameter("group file was not found in {}".
|
|
|
|
|
format(group_file_path))
|
|
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
|
def _inspect_formats_package(cls):
|
|
|
|
|
'''Метод для определения множества доступных форматов и
|
|
|
|
|
предоставляемых ими параметров.'''
|
|
|
|
|
if cls.format_is_inspected:
|
|
|
|
|
return
|
|
|
|
|
parameters_set = set()
|
|
|
|
|
format_set = set()
|
|
|
|
|
format_directory_path = os.path.join(os.path.dirname(__file__),
|
|
|
|
|
'format')
|
|
|
|
|
|
|
|
|
|
for module_name in os.listdir(format_directory_path):
|
|
|
|
|
if (os.path.isdir(os.path.join('format', module_name)) or
|
|
|
|
|
module_name == '__init__.py'):
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
if module_name.endswith('.py'):
|
|
|
|
|
module_name = module_name[:-3]
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
module = import_module('calculate.templates.format.{}'.
|
|
|
|
|
format(module_name))
|
|
|
|
|
for obj in dir(module):
|
|
|
|
|
if obj.endswith('Format') and obj != 'BaseFormat':
|
|
|
|
|
format_class = getattr(module, obj, False)
|
|
|
|
|
|
|
|
|
|
if format_class:
|
|
|
|
|
format_set.add(format_class.FORMAT)
|
|
|
|
|
parameters = getattr(format_class,
|
|
|
|
|
'FORMAT_PARAMETERS', set())
|
|
|
|
|
parameters_set.update(parameters)
|
|
|
|
|
except Exception:
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
cls.available_formats = format_set
|
|
|
|
|
cls.available_parameters.update(parameters_set)
|
|
|
|
|
cls.formats_inspected = True
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class TemplateAction:
|
|
|
|
|
def __init__(self, output_module=IOModule(),
|
|
|
|
|
chroot_path='/', mounts=None):
|
|
|
|
|
self.output = output_module
|
|
|
|
|
self.mounts = mounts
|
|
|
|
|
self.chroot_path = chroot_path
|
|
|
|
|
self.exec_list = OrderedDict()
|
|
|
|
|
|
|
|
|
|
self.DIRECTORY_APPENDS = {
|
|
|
|
|
'remove': self._append_remove_directory,
|
|
|
|
|
'clear': self._append_clear_directory,
|
|
|
|
|
'replace': self._append_replace_directory,
|
|
|
|
|
'link': self._append_link_directory,
|
|
|
|
|
'join': self._append_join_directory,
|
|
|
|
|
'skip': self._append_skip_directory
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
self.FILE_APPENDS = {
|
|
|
|
|
'replace': self._append_replace_file,
|
|
|
|
|
'remove': self._append_remove_file,
|
|
|
|
|
'clear': self._append_clear_file,
|
|
|
|
|
'link': self._append_link_file,
|
|
|
|
|
'join': self._append_join_file,
|
|
|
|
|
'before': self._append_before_file,
|
|
|
|
|
'after': self._append_after_file,
|
|
|
|
|
'skip': self._append_skip_file
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
appends_set = set(self.DIRECTORY_APPENDS.keys()).union(
|
|
|
|
|
set(self.FILE_APPENDS.keys()))
|
|
|
|
|
|
|
|
|
|
TemplateParameters.available_appends = appends_set
|
|
|
|
|
TemplateParameters._inspect_formats_package()
|
|
|
|
|
|
|
|
|
|
self.available_parameters = TemplateParameters.available_parameters
|
|
|
|
|
self.available_formats = TemplateParameters.available_formats
|
|
|
|
|
|
|
|
|
|
def process_template_directory(self, target_path, template_path,
|
|
|
|
|
parameters={}):
|
|
|
|
|
'''Метод для выполнения шаблона директории.'''
|
|
|
|
|
self.template_path = template_path
|
|
|
|
|
self.target_path = target_path
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
self.template_parameters = TemplateParameters(
|
|
|
|
|
parameters,
|
|
|
|
|
template_type=DIR,
|
|
|
|
|
chroot_path=self.chroot_path)
|
|
|
|
|
except IncorrectParameter as error:
|
|
|
|
|
self.output.set_error('Incorrect parameter: {}'.format(str(error)))
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
self.output.console_output.print_default(
|
|
|
|
|
'With parameters: {}\n'.
|
|
|
|
|
format(self.template_parameters._parameters_dictionary)
|
|
|
|
|
)
|
|
|
|
|
self.output.set_info('Template path: {}'.format(self.template_path))
|
|
|
|
|
self.output.set_info('Target directory: {}'.format(self.target_path))
|
|
|
|
|
|
|
|
|
|
# Методы соответствующие разным значениям append для директорий.
|
|
|
|
|
def _append_join_directory(self):
|
|
|
|
|
'''Создать каталог.'''
|
|
|
|
|
self._create_directory()
|
|
|
|
|
|
|
|
|
|
def _append_remove_directory(self):
|
|
|
|
|
'''Удалить каталог и все его содержимое.'''
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
def _append_clear_directory(self):
|
|
|
|
|
'''Очистить содержимое каталога, если каталог есть.'''
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
def _append_replace_directory(self):
|
|
|
|
|
'''Очистить содержимое каталога или создать его.'''
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
def _append_link_directory(self):
|
|
|
|
|
'''Создать символическую ссылку на указанный каталог.'''
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
def _append_skip_directory(self):
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
# Непосредственно действия с директориями.
|
|
|
|
|
def _create_directory(self, target_path=''):
|
|
|
|
|
if not target_path:
|
|
|
|
|
target_path = self.target_path
|
|
|
|
|
|
|
|
|
|
if os.access(target_path, os.F_OK):
|
|
|
|
|
if self.template_parameters.chmod:
|
|
|
|
|
self.chmod_directory(target_path)
|
|
|
|
|
if self.template_parameters.chown:
|
|
|
|
|
self.chown_directory(target_path)
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
directory = target_path
|
|
|
|
|
directories_to_create = [target_path]
|
|
|
|
|
directory = os.path.dirname(directory)
|
|
|
|
|
while not os.access(directory, os.F_OK) and directory:
|
|
|
|
|
directories_to_create.append(directory)
|
|
|
|
|
directory = os.path.dirname(directory)
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
current_mod, current_uid, current_gid = self.get_file_info(
|
|
|
|
|
directory,
|
|
|
|
|
'all')
|
|
|
|
|
current_chown = {'uid': current_uid, 'gid': current_gid}
|
|
|
|
|
except OSError:
|
|
|
|
|
self.output.set_error('No access to the directory: {}'.format(
|
|
|
|
|
directory))
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
directories_to_create.reverse()
|
|
|
|
|
|
|
|
|
|
for directory_path in directories_to_create:
|
|
|
|
|
try:
|
|
|
|
|
os.mkdir(directory_path)
|
|
|
|
|
|
|
|
|
|
if self.template_parameters.chmod:
|
|
|
|
|
self.chmod_directory(directory_path)
|
|
|
|
|
else:
|
|
|
|
|
self.chmod_directory(directory_path,
|
|
|
|
|
chmod_value=current_mod)
|
|
|
|
|
|
|
|
|
|
if self.template_parameters.chown:
|
|
|
|
|
self.chown_directory
|
|
|
|
|
else:
|
|
|
|
|
self.chown_directory(directory_path,
|
|
|
|
|
chown_value=current_chown)
|
|
|
|
|
|
|
|
|
|
except OSError as error:
|
|
|
|
|
self.output.set_error('Failed to create directory: {}'.
|
|
|
|
|
format(directory_path))
|
|
|
|
|
self.output.set_error('Reason: {}'.format(str(error)))
|
|
|
|
|
return False
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
def _remove_directory(self, target_path):
|
|
|
|
|
if os.path.isdir(target_path) or os.path.exists(target_path):
|
|
|
|
|
try:
|
|
|
|
|
if os.path.islink(target_path):
|
|
|
|
|
os.unlink(target_path)
|
|
|
|
|
else:
|
|
|
|
|
shutil.rmtree(target_path)
|
|
|
|
|
return True
|
|
|
|
|
except Exception as error:
|
|
|
|
|
self.output.set_error("Failed to delete the directory: {}".
|
|
|
|
|
format(target_path))
|
|
|
|
|
self.output.set_error("Reason: {}".
|
|
|
|
|
format(str(error)))
|
|
|
|
|
return False
|
|
|
|
|
else:
|
|
|
|
|
self.output.set_error("Failed to delete the directory: {}".
|
|
|
|
|
format(target_path))
|
|
|
|
|
self.output.set_error(
|
|
|
|
|
"Target file is not directory or not exists.".
|
|
|
|
|
format(target_path))
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
def _clear_directory(self, target_path):
|
|
|
|
|
if os.path.isdir(target_path) and os.path.exists(target_path):
|
|
|
|
|
for node in os.scandir(target_path):
|
|
|
|
|
if node.is_dir():
|
|
|
|
|
if self._remove_directory(node.path):
|
|
|
|
|
return True
|
|
|
|
|
else:
|
|
|
|
|
self.output.set_error('Failed to delete directory: {}'.
|
|
|
|
|
format(target_path))
|
|
|
|
|
else:
|
|
|
|
|
try:
|
|
|
|
|
os.remove(node.path)
|
|
|
|
|
return True
|
|
|
|
|
except OSError:
|
|
|
|
|
self.output.set_error('Failed to delete directory: {}'.
|
|
|
|
|
format(target_path))
|
|
|
|
|
else:
|
|
|
|
|
self.output.set_error('failed to delete directory: {}'.
|
|
|
|
|
format(target_path))
|
|
|
|
|
self.output.set_error(
|
|
|
|
|
'target file is not directory or does not exist: {}'.
|
|
|
|
|
format(target_path))
|
|
|
|
|
|
|
|
|
|
def _link_directory(self, target_path):
|
|
|
|
|
if not self.source:
|
|
|
|
|
self.output.set_error("'source' parameter is not defined for "
|
|
|
|
|
"'append = link' in template {}".
|
|
|
|
|
format(self.template_path))
|
|
|
|
|
self.output.set_error('Failed to create symlink: {0} -> {1}'.
|
|
|
|
|
format(target_path, self.source))
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
if not os.path.exists(self.source):
|
|
|
|
|
self.output.set_error("'source' file does not exist for "
|
|
|
|
|
"'append = link' in template {}".
|
|
|
|
|
format(self.template_path))
|
|
|
|
|
self.output.set_error('Failed to create symlink: {0} -> {1}'.
|
|
|
|
|
format(target_path, self.source))
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
os.symlink(self.source, target_path, target_is_directory=True)
|
|
|
|
|
print('linked: {0} -> {1}'.format(os.path.basename(target_path),
|
|
|
|
|
os.path.basename(self.source)))
|
|
|
|
|
return True
|
|
|
|
|
except OSError:
|
|
|
|
|
self.output.set_error('Template error: {}'.
|
|
|
|
|
format(self.template_path))
|
|
|
|
|
self.output.set_error('Failed to create symlink: {0} -> {1}'.
|
|
|
|
|
format(target_path, self.source))
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
def process_template_file(self, target_path, template_path,
|
|
|
|
|
template_text='', parameters={}):
|
|
|
|
|
self.template_text = template_text
|
|
|
|
|
self.template_path = template_path
|
|
|
|
|
self.target_path = target_path
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
self.template_parameters = TemplateParameters(
|
|
|
|
|
parameters,
|
|
|
|
|
template_type=FILE,
|
|
|
|
|
chroot_path=self.chroot_path)
|
|
|
|
|
except IncorrectParameter as error:
|
|
|
|
|
self.output.set_error('Incorrect parameter: {}'.format(str(error)))
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
self.output.console_output.print_default(
|
|
|
|
|
'With parameters: {}\n'.
|
|
|
|
|
format(self.template_parameters._parameters_dictionary)
|
|
|
|
|
)
|
|
|
|
|
self.output.set_info('Template path: {}'.format(self.template_path))
|
|
|
|
|
self.output.set_info('Target file: {}.\nTemplate text:'.
|
|
|
|
|
format(self.target_path))
|
|
|
|
|
self.output.console_output.print_default(self.template_text + '\n\n')
|
|
|
|
|
|
|
|
|
|
# Методы соответствующие значениями параметра append для файлов шаблонов.
|
|
|
|
|
def _append_replace_file(self):
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
def _append_remove_file(self):
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
def _append_clear_file(self):
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
def _append_link_file(self):
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
def _append_join_file(self):
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
def _append_before_file(self):
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
def _append_after_file(self):
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
def _append_skip_file(self):
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
# Методы для работы непосредственно с файлами.
|
|
|
|
|
def join_file(self, target_path, option=False):
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
def remove_file(self, target_path):
|
|
|
|
|
try:
|
|
|
|
|
if os.path.islink(target_path):
|
|
|
|
|
try:
|
|
|
|
|
os.unlink(target_path)
|
|
|
|
|
return True
|
|
|
|
|
except OSError:
|
|
|
|
|
self.output.set_error('Template error: {}'.
|
|
|
|
|
format(target_path))
|
|
|
|
|
self.output.set_error('Failed to delete the link: {}'.
|
|
|
|
|
format(target_path))
|
|
|
|
|
return False
|
|
|
|
|
if os.path.isfile(target_path):
|
|
|
|
|
try:
|
|
|
|
|
os.remove(target_path)
|
|
|
|
|
return True
|
|
|
|
|
except OSError:
|
|
|
|
|
self.output.set_error('Template error: {}'.
|
|
|
|
|
format(target_path))
|
|
|
|
|
self.output.set_error('Failed to delete the file: {}'.
|
|
|
|
|
format(target_path))
|
|
|
|
|
finally:
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
def clear_file(self, target_path):
|
|
|
|
|
try:
|
|
|
|
|
with open(target_path, 'w') as f:
|
|
|
|
|
f.truncate(0)
|
|
|
|
|
except IOError:
|
|
|
|
|
self.output.set_error("Template error: {}".
|
|
|
|
|
format(self.template_path))
|
|
|
|
|
self.output.set_error("Failed to clear the file: {}".
|
|
|
|
|
format(target_path))
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
def link_file(self, target_path):
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
def chown_directory(self, target_path, chown_value={}):
|
|
|
|
|
"""Сменить владельца директории."""
|
|
|
|
|
if not chown_value:
|
|
|
|
|
chown_value = self.template_parameters.chown
|
|
|
|
|
print('chown value = {}'.format(chown_value))
|
|
|
|
|
try:
|
|
|
|
|
os.chown(target_path, chown_value['uid'], chown_value['gid'])
|
|
|
|
|
except (OSError, Exception) as error:
|
|
|
|
|
# возможно потребуются дополнительные проверки.
|
|
|
|
|
self.output.set_error('Can not chown directory: {}'.
|
|
|
|
|
format(target_path))
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
def chmod_directory(self, target_path, chmod_value=False):
|
|
|
|
|
"""Сменить права доступа к директории."""
|
|
|
|
|
if not chmod_value:
|
|
|
|
|
chmod_value = self.template_parameters.chmod
|
|
|
|
|
print('chmod value = {}'.format(chmod_value))
|
|
|
|
|
try:
|
|
|
|
|
os.chmod(target_path, chmod_value)
|
|
|
|
|
except (OSError, Exception) as error:
|
|
|
|
|
# возможно потребуются дополнительные проверки.
|
|
|
|
|
self.output.set_error('Can not chmod directory: {}'.
|
|
|
|
|
format(target_path))
|
|
|
|
|
self.output.set_error('reason: {}'.format(str(error)))
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
def chown_file(self, target_path, check_existation=True):
|
|
|
|
|
"""Сменить владельца файла."""
|
|
|
|
|
try:
|
|
|
|
|
if check_existation and not os.path.exists(target_path):
|
|
|
|
|
open(target_path, 'w').close()
|
|
|
|
|
os.lchown(target_path, self.chown['uid'], self.chown['gid'])
|
|
|
|
|
return True
|
|
|
|
|
except (OSError, Exception) as error:
|
|
|
|
|
# возможно потребуются дополнительные проверки.
|
|
|
|
|
self.output.set_error('Can not chown file: {}'.format(target_path))
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
def chmod_file(self, target_path, check_existation=True):
|
|
|
|
|
"""Сменить права доступа к директории."""
|
|
|
|
|
try:
|
|
|
|
|
if check_existation and not os.path.exists(target_path):
|
|
|
|
|
open(target_path, 'w').close()
|
|
|
|
|
os.chmod(target_path, self.chmod)
|
|
|
|
|
return True
|
|
|
|
|
except (OSError, Exception) as error:
|
|
|
|
|
# возможно потребуются дополнительные проверки.
|
|
|
|
|
self.output.set_error('Can not chmod file: {}'.format(target_path))
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
def get_file_info(self, path, info='all'):
|
|
|
|
|
file_stat = os.stat(path)
|
|
|
|
|
if info == 'all':
|
|
|
|
|
return stat.S_IMODE(file_stat.st_mode), file_stat.st_uid,\
|
|
|
|
|
file_stat.st_gid
|
|
|
|
|
if info == 'mode':
|
|
|
|
|
return stat.S_IMODE(file_stat.st_mode)
|
|
|
|
|
if info == 'owner':
|
|
|
|
|
return file_stat.st_uid, file_stat.st_gid
|
|
|
|
|
|
|
|
|
|
def set_uid_gid_error(self, path, uid, gid, template_path=''):
|
|
|
|
|
import pwd
|
|
|
|
|
import grp
|
|
|
|
|
try:
|
|
|
|
|
user_name = pwd.getpwuid(uid).pw_name
|
|
|
|
|
except (TypeError, KeyError):
|
|
|
|
|
user_name = str(uid)
|
|
|
|
|
try:
|
|
|
|
|
group_name = grp.getgrgid(gid).gr_name
|
|
|
|
|
except (TypeError, KeyError):
|
|
|
|
|
group_name = str(gid)
|
|
|
|
|
owner = '{0}:{1}'.format(user_name, group_name)
|
|
|
|
|
if template_path:
|
|
|
|
|
self.output.set_error('Failed to process template file {}'.
|
|
|
|
|
template_path)
|
|
|
|
|
# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
|
|
|
|
# !! описать ошибку !!
|
|
|
|
|
# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
|
|
|
|
self.output.set_error('error with owner: {}'.format(owner))
|
|
|
|
|
|
|
|
|
|
def is_vfat(self, path):
|
|
|
|
|
if self.mounts is None:
|
|
|
|
|
self.mounts = Mounts()
|
|
|
|
|
if self.mounts.get_from_fstab(what=self.mounts.TYPE,
|
|
|
|
|
where=self.mounts.DIR,
|
|
|
|
|
is_in=path) in ('vfat', 'ntfs-3g',
|
|
|
|
|
'ntfs'):
|
|
|
|
|
return True
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
def check_os_error(self, error, path):
|
|
|
|
|
if hasattr(error, 'errno') and error.errno == os.errno.EPERM:
|
|
|
|
|
if self.is_vfat(path):
|
|
|
|
|
return True
|
|
|
|
|
if hasattr(error, 'errno') and error.errno == os.errno.EACCES and\
|
|
|
|
|
'var/calculate/remote' in path:
|
|
|
|
|
return True
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
def _clear_parameters(self):
|
|
|
|
|
'''Метод для удаления данных о шаблоне директории после
|
|
|
|
|
его выполнения.'''
|
|
|
|
|
try:
|
|
|
|
|
del(self.template_parameters)
|
|
|
|
|
except NameError:
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class DirectoryProcessor:
|
|
|
|
|
chmod_regex = re.compile(r'\d{3}')
|
|
|
|
|
|
|
|
|
|
def __init__(self, action, datavars_module=Variables(), package='',
|
|
|
|
|
output_module=IOModule(), test_mode=False):
|
|
|
|
|
self.action = action
|
|
|
|
|
self.chroot_path = datavars_module.main.cl_chroot_path
|
|
|
|
|
if not test_mode:
|
|
|
|
|
self.cl_chroot_path = datavars_module.main.cl_chroot_path
|
|
|
|
|
else:
|
|
|
|
|
self.cl_chroot_path = datavars_module.test.test_root
|
|
|
|
|
|
|
|
|
|
self.datavars_module = datavars_module
|
|
|
|
|
self.output = output_module
|
|
|
|
|
self.template_action = TemplateAction(output_module=self.output,
|
|
|
|
|
chroot_path=self.chroot_path)
|
|
|
|
|
|
|
|
|
|
self.parameters_set = self.template_action.available_parameters
|
|
|
|
|
self.formats_set = self.template_action.available_formats
|
|
|
|
|
self.inheritable_parameters =\
|
|
|
|
|
set(TemplateParameters.inheritable_parameters)
|
|
|
|
|
|
|
|
|
|
self.template_engine = TemplateEngine(
|
|
|
|
|
datavars_module=datavars_module,
|
|
|
|
|
parameters_set=self.parameters_set
|
|
|
|
|
)
|
|
|
|
|
self.template_paths = (self.datavars_module.
|
|
|
|
|
main.cl_template_path.split(','))
|
|
|
|
|
|
|
|
|
|
self.for_package = package
|
|
|
|
|
self.processed_packages = []
|
|
|
|
|
|
|
|
|
|
self.packages_to_merge = []
|
|
|
|
|
self.packages_file_paths = {}
|
|
|
|
|
|
|
|
|
|
def process_template_directories(self):
|
|
|
|
|
# Проходим каталоги из main.cl_template.path
|
|
|
|
|
for directory_path in self.template_paths:
|
|
|
|
|
directory_path = directory_path.strip()
|
|
|
|
|
entries = os.scandir(directory_path)
|
|
|
|
|
for node in entries:
|
|
|
|
|
self.walk_directory_tree(node.path,
|
|
|
|
|
self.cl_chroot_path,
|
|
|
|
|
{}, set())
|
|
|
|
|
|
|
|
|
|
if self.for_package:
|
|
|
|
|
self.output.set_info('Processing packages from merge parameter...')
|
|
|
|
|
self.processed_packages.append(self.for_package)
|
|
|
|
|
self.merge_packages()
|
|
|
|
|
|
|
|
|
|
def merge_packages(self):
|
|
|
|
|
not_merged_packages = []
|
|
|
|
|
while self.packages_to_merge:
|
|
|
|
|
self.for_package = self.packages_to_merge.pop()
|
|
|
|
|
if self.for_package not in self.packages_file_paths:
|
|
|
|
|
self.output.set_error(
|
|
|
|
|
"Error: package '{0}' not found for action '{1}'.".
|
|
|
|
|
format(self.for_package, self.action)
|
|
|
|
|
)
|
|
|
|
|
not_merged_packages.append(self.for_package)
|
|
|
|
|
continue
|
|
|
|
|
for template_path in self.packages_file_paths[self.for_package]:
|
|
|
|
|
base_directory, queue = self.get_directories_queue(
|
|
|
|
|
template_path
|
|
|
|
|
)
|
|
|
|
|
first_directory = queue.pop()
|
|
|
|
|
first_directory_path = os.path.join(base_directory,
|
|
|
|
|
first_directory)
|
|
|
|
|
self.walk_directory_tree(first_directory_path,
|
|
|
|
|
self.cl_chroot_path, {}, set(),
|
|
|
|
|
directories_queue=queue)
|
|
|
|
|
|
|
|
|
|
self.processed_packages.append(self.for_package)
|
|
|
|
|
if not_merged_packages:
|
|
|
|
|
self.output.set_error('Packages {} is not merged.'.
|
|
|
|
|
format(','.join(self.packages_to_merge)))
|
|
|
|
|
else:
|
|
|
|
|
self.output.set_success('All packages are merged...')
|
|
|
|
|
|
|
|
|
|
def get_directories_queue(self, path):
|
|
|
|
|
directories_queue = []
|
|
|
|
|
for base_dir in self.template_paths:
|
|
|
|
|
base_dir = base_dir.strip()
|
|
|
|
|
if path.startswith(base_dir):
|
|
|
|
|
base_directory = base_dir
|
|
|
|
|
break
|
|
|
|
|
|
|
|
|
|
while path != base_directory:
|
|
|
|
|
path, dir_name = os.path.split(path)
|
|
|
|
|
directories_queue.append(dir_name)
|
|
|
|
|
|
|
|
|
|
return base_directory, directories_queue
|
|
|
|
|
|
|
|
|
|
def walk_directory_tree(self, current_directory_path, target_path,
|
|
|
|
|
directory_parameters, directory_env,
|
|
|
|
|
directories_queue=[]):
|
|
|
|
|
template_files = []
|
|
|
|
|
template_directories = []
|
|
|
|
|
directory_name = os.path.basename(current_directory_path)
|
|
|
|
|
|
|
|
|
|
current_env = directory_env.copy()
|
|
|
|
|
current_target_path = target_path
|
|
|
|
|
|
|
|
|
|
directory_parameters = directory_parameters.copy()
|
|
|
|
|
|
|
|
|
|
entries = os.scandir(current_directory_path)
|
|
|
|
|
self.template_engine.change_directory(current_directory_path)
|
|
|
|
|
for node in entries:
|
|
|
|
|
# Временное, вместо этого добавить переменную ignore_files.
|
|
|
|
|
if node.name.endswith('.swp'):
|
|
|
|
|
continue
|
|
|
|
|
if node.is_dir():
|
|
|
|
|
template_directories.append(node.path)
|
|
|
|
|
elif node.is_file():
|
|
|
|
|
template_files.append(node.name)
|
|
|
|
|
|
|
|
|
|
# обрабатываем в первую очередь шаблон директории.
|
|
|
|
|
if '.calculate_directory' in template_files:
|
|
|
|
|
template_files.remove('.calculate_directory')
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
self.template_engine.process_template('.calculate_directory',
|
|
|
|
|
env=current_env)
|
|
|
|
|
except ConditionFailed:
|
|
|
|
|
self.output.set_warning('Condition failed in {}'.
|
|
|
|
|
format(current_directory_path))
|
|
|
|
|
self.output.console_output.print_default('\n')
|
|
|
|
|
return
|
|
|
|
|
except Exception as error:
|
|
|
|
|
self.output.set_error('Template error: {0} \nTemplate: {1},'
|
|
|
|
|
' lineno: {2}'.
|
|
|
|
|
format(str(error),
|
|
|
|
|
current_directory_path,
|
|
|
|
|
error.lineno))
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
directory_parameters.update(self.template_engine.parameters)
|
|
|
|
|
|
|
|
|
|
# Если есть параметр name -- меняем имя текущего каталога.
|
|
|
|
|
if 'name' in directory_parameters:
|
|
|
|
|
directory_name = directory_parameters['name']
|
|
|
|
|
|
|
|
|
|
# Если есть параметр path -- меняем текущий путь к целевому
|
|
|
|
|
# каталогу.
|
|
|
|
|
if 'path' in directory_parameters:
|
|
|
|
|
current_target_path = join_paths(self.cl_chroot_path,
|
|
|
|
|
directory_parameters['path'])
|
|
|
|
|
|
|
|
|
|
if directory_parameters.get('append', None) != 'skip':
|
|
|
|
|
current_target_path = os.path.join(current_target_path,
|
|
|
|
|
directory_name)
|
|
|
|
|
if directory_parameters.get('action', None) != self.action:
|
|
|
|
|
self.output.set_error(
|
|
|
|
|
'Action parameter not found or not relevant: {}'.
|
|
|
|
|
format(directory_name))
|
|
|
|
|
self.output.console_output.print_default('\n')
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
# Если шаблоны обрабатываются для одного пакета -- проверяем
|
|
|
|
|
# параметр package и собираем пути к директориям других
|
|
|
|
|
# пакетов, необходимые для merge.
|
|
|
|
|
if self.for_package:
|
|
|
|
|
if 'package' not in directory_parameters:
|
|
|
|
|
self.output.set_warning(
|
|
|
|
|
"'package' is not defined for {}".
|
|
|
|
|
format(current_directory_path)
|
|
|
|
|
)
|
|
|
|
|
# считаем наличие параметра package необязательным.
|
|
|
|
|
# return
|
|
|
|
|
elif directory_parameters['package'] != self.for_package:
|
|
|
|
|
package_name = directory_parameters['package']
|
|
|
|
|
if package_name in self.packages_file_paths:
|
|
|
|
|
self.packages_file_paths[package_name].append(
|
|
|
|
|
current_directory_path
|
|
|
|
|
)
|
|
|
|
|
else:
|
|
|
|
|
self.packages_file_paths[package_name] =\
|
|
|
|
|
[current_directory_path]
|
|
|
|
|
self.output.set_warning(
|
|
|
|
|
"'package' parameter is not actual in {}".
|
|
|
|
|
format(current_directory_path)
|
|
|
|
|
)
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
# Если параметр env содержит несуществующий модуль -- шаблон не
|
|
|
|
|
# выполняем.
|
|
|
|
|
if 'env' in directory_parameters:
|
|
|
|
|
env_to_check = directory_parameters['env'].split(',')
|
|
|
|
|
for name in env_to_check:
|
|
|
|
|
if name.strip() not in self.datavars_module:
|
|
|
|
|
return
|
|
|
|
|
del(directory_parameters['env'])
|
|
|
|
|
|
|
|
|
|
# Если есть параметр merge -- собираем указанные в нем пакеты
|
|
|
|
|
# в списке имен пакетов для последующей обработки.
|
|
|
|
|
if self.for_package and 'merge' in directory_parameters:
|
|
|
|
|
merge_list = directory_parameters['merge'].split(',')
|
|
|
|
|
del(directory_parameters['merge'])
|
|
|
|
|
for package in merge_list:
|
|
|
|
|
self.packages_to_merge.append(package.strip())
|
|
|
|
|
|
|
|
|
|
# Выполняем действие с директорией.
|
|
|
|
|
self.output.set_success('Processing directory: {}'.
|
|
|
|
|
format(current_directory_path))
|
|
|
|
|
|
|
|
|
|
self.template_action.process_template_directory(
|
|
|
|
|
current_target_path,
|
|
|
|
|
template_path=current_directory_path,
|
|
|
|
|
parameters=directory_parameters)
|
|
|
|
|
|
|
|
|
|
# Оставляем только наследуемые параметры.
|
|
|
|
|
directory_parameters = {name: value for name, value in
|
|
|
|
|
directory_parameters.items()
|
|
|
|
|
if name in self.inheritable_parameters}
|
|
|
|
|
|
|
|
|
|
if ('chmod' in directory_parameters and
|
|
|
|
|
directory_parameters['chmod'].isdigit() and
|
|
|
|
|
len(directory_parameters['chmod']) <= 4):
|
|
|
|
|
del(directory_parameters['chmod'])
|
|
|
|
|
|
|
|
|
|
if directories_queue:
|
|
|
|
|
next_node = directories_queue.pop()
|
|
|
|
|
if next_node in template_files:
|
|
|
|
|
template_files = [next_node]
|
|
|
|
|
template_directories = []
|
|
|
|
|
else:
|
|
|
|
|
next_directory_path = os.path.join(current_directory_path,
|
|
|
|
|
next_node)
|
|
|
|
|
template_files = []
|
|
|
|
|
template_directories = [next_directory_path]
|
|
|
|
|
|
|
|
|
|
# обрабатываем файлы шаблонов хранящихся в директории.
|
|
|
|
|
for template_name in template_files:
|
|
|
|
|
template_parameters = {}
|
|
|
|
|
template_parameters = directory_parameters.copy()
|
|
|
|
|
template_path = os.path.join(current_directory_path, template_name)
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
self.template_engine.process_template(template_name,
|
|
|
|
|
env=current_env.copy())
|
|
|
|
|
except ConditionFailed:
|
|
|
|
|
self.output.set_warning('Condition failed for: {}'.
|
|
|
|
|
format(template_name))
|
|
|
|
|
continue
|
|
|
|
|
except Exception as error:
|
|
|
|
|
self.output.set_error('Template error: {0} in template {1}'.
|
|
|
|
|
format(str(error), template_path))
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
template_parameters.update(self.template_engine.parameters)
|
|
|
|
|
template_text = self.template_engine.template_text
|
|
|
|
|
|
|
|
|
|
if template_parameters.get('action', None) != self.action:
|
|
|
|
|
self.output.set_warning(
|
|
|
|
|
'Action parameter not found or not relevant.'
|
|
|
|
|
)
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
if 'action' in template_parameters:
|
|
|
|
|
del(template_parameters['action'])
|
|
|
|
|
|
|
|
|
|
if self.for_package:
|
|
|
|
|
if 'package' not in template_parameters:
|
|
|
|
|
self.output.set_warning("'package' is not defined for {}".
|
|
|
|
|
format(template_path))
|
|
|
|
|
# считаем параметр package необязательным.
|
|
|
|
|
# continue
|
|
|
|
|
elif template_parameters['package'] != self.for_package:
|
|
|
|
|
package_name = template_parameters['package']
|
|
|
|
|
if package_name in self.packages_file_paths:
|
|
|
|
|
self.packages_file_paths[package_name].append(
|
|
|
|
|
template_path
|
|
|
|
|
)
|
|
|
|
|
else:
|
|
|
|
|
self.packages_file_paths[package_name] =\
|
|
|
|
|
[template_path]
|
|
|
|
|
self.output.set_warning(
|
|
|
|
|
"'package' parameter is not actual in {}".
|
|
|
|
|
format(template_name)
|
|
|
|
|
)
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
if 'merge' in template_parameters:
|
|
|
|
|
merge_list = template_parameters['merge'].split(',')
|
|
|
|
|
del(template_parameters['merge'])
|
|
|
|
|
for package_name in merge_list:
|
|
|
|
|
if package_name not in self.processed_packages:
|
|
|
|
|
self.packages_to_merge.append(package_name.strip())
|
|
|
|
|
|
|
|
|
|
# Если параметр env содержит несуществующий модуль -- шаблон не
|
|
|
|
|
# выполняем.
|
|
|
|
|
if 'env' in template_parameters:
|
|
|
|
|
env_to_check = template_parameters['env'].split(',')
|
|
|
|
|
for name in env_to_check:
|
|
|
|
|
if name.strip() not in self.datavars_module:
|
|
|
|
|
continue
|
|
|
|
|
del(template_parameters['env'])
|
|
|
|
|
|
|
|
|
|
if 'name' in template_parameters:
|
|
|
|
|
template_name = template_parameters['name']
|
|
|
|
|
|
|
|
|
|
if 'path' in template_parameters:
|
|
|
|
|
target_file_path = join_paths(self.cl_chroot_path,
|
|
|
|
|
template_parameters['path'],
|
|
|
|
|
template_name)
|
|
|
|
|
else:
|
|
|
|
|
target_file_path = join_paths(current_target_path,
|
|
|
|
|
template_name)
|
|
|
|
|
|
|
|
|
|
if ('format' in template_parameters and
|
|
|
|
|
template_parameters['format'] not in self.formats_set):
|
|
|
|
|
self.output.set_error(
|
|
|
|
|
"Unknown parameter '{0}' in template: {1}".
|
|
|
|
|
format(template_parameters['format'],
|
|
|
|
|
target_file_path)
|
|
|
|
|
)
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
# Выполняем действие с использованием шаблона.
|
|
|
|
|
self.output.set_success('Processing template: {}...'.
|
|
|
|
|
format(template_name))
|
|
|
|
|
self.template_action.process_template_file(
|
|
|
|
|
target_file_path,
|
|
|
|
|
template_path,
|
|
|
|
|
template_text=template_text,
|
|
|
|
|
parameters=template_parameters)
|
|
|
|
|
|
|
|
|
|
# проходимся далее по директориям.
|
|
|
|
|
for directory_path in template_directories:
|
|
|
|
|
self.walk_directory_tree(directory_path, current_target_path,
|
|
|
|
|
directory_parameters, current_env,
|
|
|
|
|
directories_queue=directories_queue)
|