def get_acl(cls, obj=None): # pylint: disable=bad-staticmethod-argument handler_class_name = settings.ACL['MODEL_HANDLERS'].get( cls.__name__, settings.ACL['MODEL_HANDLERS'].get("Default")) return import_class(handler_class_name)(cls, obj)
class _Base: __slots__ = 'options', 'proj', 'path' regex = r"(^[\w\d\.\-_]{1,})\.yml" handler_class = import_class(settings.PROJECT_CI_HANDLER_CLASS) def __init__(self, project: Project, **options): self.options = options self.proj = project self.path = self.proj.path def _set_status(self, status) -> NoReturn: self.proj.set_status(status) @raise_context() def _load_yaml(self) -> Dict[Text, Any]: ''' Loading `.polemarch.yaml` data. :return: Data from `.polemarch.yaml` file. :type ret: dict ''' self.proj.get_yaml_subcache().clear() return self.proj.get_yaml() def _path_exists(self, path: Text) -> bool: return os.path.exists(path) def _dir_exists(self, path_dir: Text) -> bool: return self._path_exists(path_dir) and os.path.isdir(path_dir) def message(self, message: Any, level: Text = 'debug') -> NoReturn: getattr(logger, level.lower(), logger.debug)('Syncing project [{}] - {}'.format( self.proj.id, message)) def pm_handle_sync_on_run(self, feature: Text, data: bool) -> NoReturn: ''' Set sync_on_run if it is setted in `.polemarch.yaml`. :param feature: feature name :param data: all data from file ''' value = str(data[feature]) _, created = self.proj.variables.update_or_create( key='repo_sync_on_run', defaults=dict(value=value)) self.message('{} repo_sync_on_run to {}'.format( 'Set' if created else 'Update', value)) @raise_context() def __create_template(self, template_name: Text, template_data: Dict) -> Template: ''' Creates one template from `.polemarch.yaml`. :param template_name: Template name :param template_data: Template data :return: created Template object ''' self.message( 'Loading template[{}] into the project.'.format(template_name)) return self.proj.template.create(name=template_name, **template_data) def pm_handle_templates(self, feature: Text, data: Dict) -> NoReturn: ''' Get and create (if is not existed) templates from `.polemarch.yaml`. :param feature: feature name :param data: all data from file ''' rewrite = data.get('templates_rewrite', False) data = data[feature] qs_existed = self.proj.template.filter(name__in=data.keys()) existed = qs_existed.values_list('name', flat=True) for template_name, template_data in data.items(): if not rewrite and template_name in existed: self.message( 'Template[{}] already in project.'.format(template_name)) continue self.__create_template(template_name, template_data) def pm_handle_view(self, feature: Text, data: Dict) -> NoReturn: ''' Clear view data from cache :param feature: feature name :param data: all data from file ''' # pylint: disable=unused-argument self.proj.get_yaml_subcache('view').clear() self.message(self.proj.execute_view_data, 'debug') def pm_handle_unknown(self, feature: Text, data: Any) -> NoReturn: # nocv ''' Logging unknowing data from `.polemarch.yaml`. ''' self.message('{} - this feature is not realised yet.'.format(feature), 'info') logger.debug(str(data)) def _handle_yaml(self, data: Union[Dict, None]) -> NoReturn: """ Loads and returns data from `.polemarch.yaml` file """ for feature in data.keys(): if feature in [ 'templates_rewrite', ]: continue self.message( 'Set settings from ".polemarch.yaml" - {}.'.format(feature)) feature_name = 'pm_handle_{}'.format(feature) getattr(self, feature_name, self.pm_handle_unknown)(feature, data) def _set_tasks_list(self, playbooks_names: Iterable[pathlib.Path]) -> NoReturn: """ Updates playbooks in project. """ # pylint: disable=invalid-name project = self.proj project.playbook.all().delete() PlaybookModel = self.proj.playbook.model hidden = project.hidden split = str.split playbook_objects = (PlaybookModel(name=split(str(p), ".yml")[0], playbook=p, hidden=hidden, project=project) for p in playbooks_names) PlaybookModel.objects.bulk_create( playbook_objects) if playbook_objects else None def __get_project_modules( self, module_path: Iterable[Text]) -> List[Union[Text, Dict]]: valid_paths = tuple(filter(self._dir_exists, module_path)) if not valid_paths: return [] modules = AnsibleModules(detailed=False, paths=valid_paths) modules.clear_cache() modules_list = modules.all() modules_list.sort() return modules_list @raise_context() def _set_project_modules(self) -> None: ''' Update project modules ''' # pylint: disable=invalid-name project = self.proj project.get_ansible_config_parser().clear_cache() project.modules.all().delete() ModuleClass = self.proj.modules.model paths = project.config.get('DEFAULT_MODULE_PATH', []) paths = filter(lambda mp: project.path in mp, paths) modules = self.__get_project_modules(paths) ModuleClass.objects.bulk_create( [ModuleClass(path=path, project=project) for path in modules]) def _update_tasks(self, files: Iterable[pathlib.Path]) -> NoReturn: ''' Find and update playbooks in project. :param files: list of filenames. ''' # reg = re.compile(self.regex) # playbooks = filter(reg.match, files) self._set_tasks_list(files) def _get_files(self, repo: Any = None) -> pathlib.Path: ''' Get all files, where playbooks should be. :param repo: Repo object :type repo: object, None :return: list of files in dir :rtype: list ''' # pylint: disable=unused-argument return pathlib.Path(self.path) def search_files(self, repo: Any = None, pattern: Text = '**/*') -> Iterable[pathlib.Path]: return self._get_files(repo).glob(pattern) def _operate(self, operation: Callable, **kwargs) -> Any: return operation(kwargs) def _make_operations(self, operation: Callable) -> Any: ''' Handle VCS operations and sync data from project. :param operation: function that should be hdandled. :return: tuple with repo-object and fetch-results ''' self._set_status("SYNC") try: with transaction.atomic(): result = self._operate(operation) self._set_status("OK") self._update_tasks(self.search_files(result[0], '*.yml')) self._set_project_modules() self._handle_yaml(self._load_yaml() or dict()) except Exception as err: logger.debug(traceback.format_exc()) self.message('Sync error: {}'.format(err), 'error') self._set_status("ERROR") raise else: with raise_context(verbose=True): self.handler_class(self, result).trigger_execution() return result def make_clone(self, options): # pragma: no cover ''' Make operations for clone repo :param options: any options, like env variables or any thing :return: tuple object with 2 args: repo obj and fetch results ''' raise NotImplementedError def make_update(self, options): # pragma: no cover ''' Make operation for fetch repo tree :param options: any options, like env variables or any thing :return: tuple object with 2 args: repo obj and fetch results ''' raise NotImplementedError def get_revision(self, *args, **kwargs) -> Text: # pylint: disable=unused-argument return "NO VCS" def get_branch_name(self) -> Text: return "NO VCS" def delete(self) -> Text: ''' Handler, which removes project data directory. :return: user message ''' if os.path.exists(self.path): if os.path.isfile(self.path): os.remove(self.path) # nocv else: shutil.rmtree(self.path) return "Repository removed!" return "Repository does not exists." # nocv def clone(self) -> Text: # pylint: disable=broad-except attempt = 2 for __ in range(attempt): try: repo = self._make_operations(self.make_clone)[0] return "Received {} files.".format( len(list(self.search_files(repo)))) except: self.delete() raise Exception("Clone didn't perform by {} attempts.".format(attempt)) def get(self) -> Any: # pylint: disable=broad-except attempt = 2 for __ in range(attempt): try: return self._make_operations(self.make_update) except: self.delete() raise Exception("Upd didn't perform by {} attempts.".format(attempt)) def check(self): pass # nocv def revision(self) -> Text: return self._operate(self.get_revision)
def _get_mod_info(self, key, sub): try: path = "{}.{}.{}".format(self.mod.__name__, key, sub) return import_class(path) except BaseException as exception_object: return exception_object