import pwd
import sys
import glob
+import datetime
# Third party modules
import six
+import yaml
from six import StringIO
from six.moves import configparser
from .app import PpApplication
-__version__ = '0.4.1'
+__version__ = '0.5.1'
LOG = logging.getLogger(__name__)
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,
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):
mods = {}
self.dependencies = []
+ self.rev_dep = {}
re_name_split = re.compile(r'([^/_-]+)[/_-](.*)')
connectors = ('-', '_', '/')
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']:
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:
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):
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')
return None
module_info['name'] = None
+ module_info['dot_id'] = None
module_info['vendor'] = None
module_info['version'] = None
module_info['dependencies'] = {}
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']
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']