From: Frank Brehm Date: Wed, 15 Aug 2018 14:52:24 +0000 (+0200) Subject: Writing module status and dependency-dot-file X-Git-Tag: 0.1.2~6^2~10^2^2~11 X-Git-Url: https://git.uhu-banane.net/?a=commitdiff_plain;h=2b06faba8371599d8087249e4a1bce932243000b;p=pixelpark%2Fadmin-tools.git Writing module status and dependency-dot-file --- diff --git a/pp_lib/check_puppet_env_app.py b/pp_lib/check_puppet_env_app.py index 43966f8..351fd98 100644 --- a/pp_lib/check_puppet_env_app.py +++ b/pp_lib/check_puppet_env_app.py @@ -19,9 +19,11 @@ import socket import pwd import sys import glob +import datetime # Third party modules import six +import yaml from six import StringIO from six.moves import configparser @@ -39,7 +41,7 @@ from .merge import merge_structure from .app import PpApplication -__version__ = '0.4.1' +__version__ = '0.5.1' LOG = logging.getLogger(__name__) @@ -84,6 +86,7 @@ class CheckPuppetEnvApp(PpApplication): self.modules_root_dir = None self.modules = {} self.dependencies = [] + self.rev_dep = {} super(CheckPuppetEnvApp, self).__init__( appname=appname, verbose=verbose, version=version, base_dir=base_dir, @@ -268,6 +271,47 @@ class CheckPuppetEnvApp(PpApplication): self.collect_modules() self.print_modules() self.verify_dependencies() + self.write_modinfo_yaml() + self.print_not_depended() + self.write_dependencies() + + # ------------------------------------------------------------------------- + def write_modinfo_yaml(self): + + outfile_base = 'modules-info.{e}.{d}.yaml'.format( + e=self.env_name, d=datetime.datetime.utcnow().strftime('%Y-%m-%d_%H-%M-%S')) + out_file = os.path.join(self.out_dir, outfile_base) + + LOG.info("Writing information about modules in {!r}...".format(out_file)) + + with open(out_file, 'w', **self.open_args) as fh: + fh.write(yaml.dump(self.modules, width=240)) + + # ------------------------------------------------------------------------- + def print_not_depended(self): + + print() + print("Module, von denen keine anderen Module abhängen:") + print("================================================") + print() + + len_base = 1 + for b_name in self.modules.keys(): + base_name = str(b_name) + if len(base_name) > len_base: + len_base = len(base_name) + + template = ' * {{b:<{}}} -> {{n}}'.format(len_base) + + for b_name in sorted(self.modules.keys(), key=str.lower): + + module_info = self.modules[b_name] + base_name = str(b_name) + + if base_name not in self.rev_dep or not self.rev_dep[base_name]: + print(template.format(b=base_name, n=module_info['name'])) + + print() # ------------------------------------------------------------------------- def verify_dependencies(self): @@ -276,6 +320,7 @@ class CheckPuppetEnvApp(PpApplication): mods = {} self.dependencies = [] + self.rev_dep = {} re_name_split = re.compile(r'([^/_-]+)[/_-](.*)') connectors = ('-', '_', '/') @@ -291,6 +336,8 @@ class CheckPuppetEnvApp(PpApplication): for b_name in self.modules.keys(): module_info = self.modules[b_name] base_name = str(b_name) + if base_name not in self.rev_dep: + self.rev_dep[base_name] = [] if not module_info['dependencies']: continue if not module_info['name']: @@ -299,13 +346,23 @@ class CheckPuppetEnvApp(PpApplication): if not module_info['vendor']: LOG.warn("Did not found vendor of module {!r}.".format(base_name)) mod_name = module_info['name'] + if self.verbose > 1: + LOG.debug("Checking dependencies of module {!r}...".format(mod_name)) for dep_key in module_info['dependencies'].keys(): dep_mod = str(dep_key) if dep_mod in mods: dep = (dep_mod, mod_name) self.dependencies.append(dep) + if mods[dep_mod] not in self.rev_dep: + self.rev_dep[mods[dep_mod]] = [] + if base_name not in self.rev_dep[mods[dep_mod]]: + self.rev_dep[mods[dep_mod]].append(base_name) + module_info['dependencies'][dep_key]['module'] = mods[dep_mod] continue + if self.verbose > 2: + LOG.debug("Dependency to {d!r} of module {m!r} wrong formatted.".format( + d=dep_mod, m=mod_name)) match = re_name_split.match(dep_mod) found = False if match: @@ -316,15 +373,94 @@ class CheckPuppetEnvApp(PpApplication): if dep_mod_name in mods: dep = (dep_mod_name, mod_name) self.dependencies.append(dep) + if mods[dep_mod_name] not in self.rev_dep: + self.rev_dep[mods[dep_mod_name]] = [] + if base_name not in self.rev_dep[mods[dep_mod_name]]: + self.rev_dep[mods[dep_mod_name]].append(base_name) + module_info['dependencies'][dep_key]['module'] = mods[dep_mod_name] found = True break if found: - break + continue LOG.warn("Did not found dependency to {d!r} of module {m!r}.".format( d=dep_mod, m=mod_name)) if self.verbose > 2: LOG.debug("Found dependencies:\n{}".format(pp(self.dependencies))) + LOG.debug("Reverse dependencies:\n{}".format(pp(self.rev_dep))) + + # ------------------------------------------------------------------------- + def write_dependencies(self): + + outfile_base = 'modules-deps.{e}.{d}.dot'.format( + e=self.env_name, d=datetime.datetime.utcnow().strftime('%Y-%m-%d_%H-%M-%S')) + out_file = os.path.join(self.out_dir, outfile_base) + + LOG.info("Writing graphviz dot file about module dependecies in {!r}...".format(out_file)) + + header_lines = ( + 'digraph Dependencies {', + '', + '\t// Graph attributes', + '\tnodesep=0.7;', + ) + + def printout(fh, line): + if self.verbose: + print(line) + fh.write(line + '\n') + + with open(out_file, 'w', **self.open_args) as fh: + + # File header + for line in header_lines: + printout(fh, line) + + # Print nodes + line = '\n\t// Modules as nodes' + printout(fh, line) + + for b_name in sorted(self.modules.keys(), key=str.lower): + + module_info = self.modules[b_name] + base_name = str(b_name) + + mod_name = base_name + if module_info['name']: + mod_name = module_info['name'] + tgt_dot_id = module_info['dot_id'] + + line = '\t{};'.format(tgt_dot_id) + printout(fh, line) + + line = '\n\t// #############################\n\t// Dependencies' + printout(fh, line) + + # Print dependencies as edges + for b_name in sorted(self.modules.keys(), key=str.lower): + + module_info = self.modules[b_name] + base_name = str(b_name) + + mod_name = base_name + if module_info['name']: + mod_name = module_info['name'] + tgt_dot_id = module_info['dot_id'] + + line = '\n\t// {i} ({n})'.format(i=tgt_dot_id, n=mod_name) + printout(fh, line) + + for dep_key in module_info['dependencies'].keys(): + dep_mod = str(dep_key) + src_module = module_info['dependencies'][dep_key]['module'] + if src_module in self.modules: + src_dot_id = self.modules[src_module]['dot_id'] + line = '\t{src} -> {tgt};'.format( + src=src_dot_id, tgt=tgt_dot_id) + printout(fh, line) + + # File footer + printout(fh, '\n}\n') # ------------------------------------------------------------------------- def print_modules(self): @@ -427,6 +563,8 @@ class CheckPuppetEnvApp(PpApplication): LOG.warn("Path {!r} is not a directory".format(module_dir)) return None + re_dot_id = re.compile(r'[/-]+') + module_info = {} module_info['base_name'] = os.path.basename(module_dir) metadata_file = os.path.join(module_dir, 'metadata.json') @@ -454,6 +592,7 @@ class CheckPuppetEnvApp(PpApplication): return None module_info['name'] = None + module_info['dot_id'] = None module_info['vendor'] = None module_info['version'] = None module_info['dependencies'] = {} @@ -463,6 +602,9 @@ class CheckPuppetEnvApp(PpApplication): match = re.match(pat_vendor, module_info['name']) if match: module_info['vendor'] = match.group(1) + module_info['dot_id'] = re_dot_id.sub('_', module_info['name']) + else: + module_info['dot_id'] = re_dot_id.sub('_', module_info['base_name']) if 'version' in meta_info: module_info['version'] = meta_info['version'] @@ -472,7 +614,8 @@ class CheckPuppetEnvApp(PpApplication): if 'name' in dep: dep_info = { 'name': dep['name'], - 'version': None + 'version': None, + 'module': None, } if 'version_requirement' in dep: dep_info['version'] = dep['version_requirement']