class Registry: _doc = {} log = None config = None def __init__(self): self.config = SingleConfig() self.log = SingleLog() self._scan_for_yamls_in_project() def get_files(self): """ :return objcect structured as: { "role_name":["/abs_path/to_file","/abs_path/to_file2"], "role2_name:["abs/path/2"] } :param :return: """ return self._doc def _scan_for_yamls_in_project(self): """ Search for Yaml files depending if we are scanning a playbook or a role :return: """ base_dir = self.config.get_base_dir() base_dir_roles = base_dir + '/roles' if not self.config.is_role: self.log.debug('Scan for playbook files: ' + base_dir) self._scan_for_yamls(base_dir, is_role=False) self.log.debug('Scan for roles in the project: ' + base_dir_roles) for entry in os.scandir(base_dir_roles): try: is_dir = entry.is_dir(follow_symlinks=False) except OSError as error: print('Error calling is_dir():', error, file=sys.stderr) continue if is_dir: self._scan_for_yamls(entry.path) else: # it is a role self.log.debug('Scan for files in a role: ' + base_dir) self._scan_for_yamls(base_dir) def _scan_for_yamls(self, base, is_role=True): """ Search for the yaml files in each project/role root and append to the corresponding object :param base: directory in witch we are searching :param is_role: is this a role directory :return: None """ extensions = YAML_EXTENSIONS base_dir = base for extension in extensions: for filename in glob.iglob(base_dir + '/**/*.' + extension, recursive=True): if self._is_excluded_yaml_file(filename, base_dir, is_role=is_role): self.log.trace('Excluding: ' + filename) else: if not is_role: self.log.trace('Adding to playbook: ' + filename) self.add_role_file(filename, PLAYBOOK_ROLE_NAME) else: role_dir = os.path.basename(base_dir) self.log.trace('Adding to role:' + role_dir + ' => ' + filename) self.add_role_file(filename, role_dir) def _is_excluded_yaml_file(self, file, role_base_dir=None, is_role=True): """ sub method for handling file exclusions based on the full path starts with :param file: :param role_base_dir: :param is_role: :return: """ if is_role: base_dir = role_base_dir excluded = self.config.excluded_roles_dirs.copy() else: base_dir = self.config.get_base_dir() excluded = self.config.excluded_playbook_dirs.copy() excluded.append('roles') is_filtered = False for excluded_dir in excluded: if file.startswith(base_dir + '/' + excluded_dir): return True return is_filtered def add_role_file(self, path, role_name): self.log.trace('add_role_file(' + path + ',' + role_name + ')') if role_name not in self._doc.keys(): self._doc[role_name] = [] self._doc[role_name].append(path)
class Generator: template_files = [] extension = "j2" _parser = None def __init__(self, doc_parser): self.config = SingleConfig() self.log = SingleLog() self.log.info("Using template dir: " + self.config.get_template_base_dir()) self._parser = doc_parser self._scan_template() def _scan_template(self): """ Search for Jinja2 (.j2) files to apply to the destination :return: None """ base_dir = self.config.get_template_base_dir() for file in glob.iglob(base_dir + '/**/*.' + self.extension, recursive=True): relative_file = file[len(base_dir) + 1:] if ntpath.basename(file)[:1] != "_": self.log.trace("[GENERATOR] found template file: " + relative_file) self.template_files.append(relative_file) else: self.log.debug("[GENERATOR] ignoring template file: " + relative_file) def _create_dir(self, dir): if not self.config.dry_run: os.makedirs(dir, exist_ok=True) else: self.log.info("[GENERATOR][DRY] Creating dir: " + dir) def _write_doc(self): files_to_overwite = [] for file in self.template_files: doc_file = self.config.get_output_dir( ) + "/" + file[:-len(self.extension) - 1] if os.path.isfile(doc_file): files_to_overwite.append(doc_file) if len(files_to_overwite ) > 0 and self.config.template_overwrite is False: SingleLog.print("This files will be overwritten:", files_to_overwite) if not self.config.dry_run: resulst = FileUtils.query_yes_no("do you want to continue?") if resulst != "yes": sys.exit() for file in self.template_files: doc_file = self.config.get_output_dir( ) + "/" + file[:-len(self.extension) - 1] source_file = self.config.get_template_base_dir() + "/" + file self.log.trace("[GENERATOR] Writing doc output to: " + doc_file + " from: " + source_file) # make sure the directory exists self._create_dir(os.path.dirname(os.path.realpath(doc_file))) if os.path.exists(source_file) and os.path.isfile(source_file): with open(source_file, 'r') as template: data = template.read() if data is not None: try: data = Environment( loader=FileSystemLoader( self.config.get_template_base_dir()), lstrip_blocks=True, trim_blocks=True).from_string(data).render( self._parser.get_data(), r=self._parser) if not self.config.dry_run: with open(doc_file, 'w') as outfile: outfile.write(data) self.log.info("Writing to: " + doc_file) else: self.log.info("[GENERATOR][DRY] Writing to: " + doc_file) except jinja2.exceptions.UndefinedError as e: self.log.error( "Jinja2 templating error: <" + str(e) + "> when loading file: \"" + file + "\", run in debug mode to see full except") if self.log.log_level < 1: raise except UnicodeEncodeError as e: self.log.error( "At the moment I'm unable to print special chars: <" + str(e) + ">, run in debug mode to see full except") if self.log.log_level < 1: raise sys.exit() def print_to_cli(self): for file in self.template_files: source_file = self.config.get_template_base_dir() + "/" + file with open(source_file, 'r') as template: data = template.read() if data is not None: try: data = Environment( loader=FileSystemLoader( self.config.get_template_base_dir()), lstrip_blocks=True, trim_blocks=True).from_string(data).render( self._parser.get_data(), r=self._parser) print(data) except jinja2.exceptions.UndefinedError as e: self.log.error( "Jinja2 templating error: <" + str(e) + "> when loading file: \"" + file + "\", run in debug mode to see full except") if self.log.log_level < 1: raise except UnicodeEncodeError as e: self.log.error( "At the moment I'm unable to print special chars: <" + str(e) + ">, run in debug mode to see full except") if self.log.log_level < 1: raise except: print("Unexpected error:", sys.exc_info()[0]) raise def render(self): if self.config.use_print_template: self.print_to_cli() else: self.log.info("Using output dir: " + self.config.get_output_dir()) self._write_doc()