Refactored to include varios reports and checks
diff --git a/ci_checker/pkg_check.py b/ci_checker/pkg_check.py
new file mode 100644
index 0000000..b9d9ed3
--- /dev/null
+++ b/ci_checker/pkg_check.py
@@ -0,0 +1,185 @@
+import json
+import os
+import sys
+
+from copy import deepcopy
+
+import common.const as const
+import reporter
+from ci_checker.common import utils
+from ci_checker.common import base_config, logger, logger_cli, PKG_DIR
+from ci_checker.common import salt_utils
+
+node_tmpl = {
+ 'role': '',
+ 'node_group': '',
+ 'status': const.NODE_DOWN,
+ 'pillars': {},
+ 'grains': {}
+}
+
+
+class CloudPackageChecker(object):
+ _config = base_config
+
+ def __init__(self):
+ logger_cli.info("Collecting nodes for package check")
+ # simple salt rest client
+ self.salt = salt_utils.SaltRemote()
+
+ # Keys for all nodes
+ # this is not working in scope of 2016.8.3, will overide with list
+ # cls.node_keys = cls.salt.list_keys()
+
+ logger_cli.info("Collecting node names existing in the cloud")
+ self.node_keys = {
+ 'minions': base_config.all_nodes
+ }
+
+ # all that answer ping
+ _active = self.salt.get_active_nodes()
+ logger_cli.info("Nodes responded: {}".format(_active))
+ # just inventory for faster interaction
+ # iterate through all accepted nodes and create a dict for it
+ self.nodes = {}
+ for _name in self.node_keys['minions']:
+ _nc = utils.get_node_code(_name)
+ _rmap = const.all_roles_map
+ _role = _rmap[_nc] if _nc in _rmap else 'unknown'
+ _status = const.NODE_UP if _name in _active else const.NODE_DOWN
+
+ self.nodes[_name] = deepcopy(node_tmpl)
+ self.nodes[_name]['node_group'] = _nc
+ self.nodes[_name]['role'] = _role
+ self.nodes[_name]['status'] = _status
+
+ logger_cli.info("{} nodes collected".format(len(self.nodes)))
+
+ def collect_installed_packages(self):
+ """
+ Collect installed packages on each node
+ sets 'installed' dict property in the class
+
+ :return: none
+ """
+ logger_cli.info("Collecting installed packages")
+ # form an all nodes compound string to use in salt
+ _active_nodes_string = self.salt.compound_string_from_list(
+ filter(
+ lambda nd: self.nodes[nd]['status'] == const.NODE_UP,
+ self.nodes
+ )
+ )
+ # Prepare script
+ _script_filename = "pkg_versions.py"
+ _p = os.path.join(PKG_DIR, 'scripts', _script_filename)
+ with open(_p, 'rt') as fd:
+ _script = fd.read().splitlines()
+ _storage_path = os.path.join(
+ base_config.salt_file_root, base_config.salt_scripts_folder
+ )
+ logger_cli.info(
+ "Uploading script {} to master's file cache folder: '{}'".format(
+ _script_filename,
+ _storage_path
+ )
+ )
+ _result = self.salt.mkdir("cfg01*", _storage_path)
+ # Form cache, source and target path
+ _cache_path = os.path.join(_storage_path, _script_filename)
+ _source_path = os.path.join(
+ 'salt://',
+ base_config.salt_scripts_folder,
+ _script_filename
+ )
+ _target_path = os.path.join(
+ '/root',
+ base_config.salt_scripts_folder,
+ _script_filename
+ )
+
+ logger.debug("Creating file in cache '{}'".format(_cache_path))
+ _result = self.salt.f_touch_master(_cache_path)
+ _result = self.salt.f_append_master(_cache_path, _script)
+ # command salt to copy file to minions
+ logger.debug("Creating script target folder '{}'".format(_cache_path))
+ _result = self.salt.mkdir(
+ _active_nodes_string,
+ os.path.join(
+ '/root',
+ base_config.salt_scripts_folder
+ ),
+ tgt_type="compound"
+ )
+ logger_cli.info("Running script to all active nodes")
+ _result = self.salt.get_file(
+ _active_nodes_string,
+ _source_path,
+ _target_path,
+ tgt_type="compound"
+ )
+ # execute pkg collecting script
+ logger.debug("Running script to all nodes")
+ # handle results for each node
+ _result = self.salt.cmd(
+ _active_nodes_string,
+ 'cmd.run',
+ param='python {}'.format(_target_path),
+ expr_form="compound"
+ )
+ for key in self.nodes.keys():
+ # due to much data to be passed from salt, it is happening in order
+ if key in _result:
+ _text = _result[key]
+ _dict = json.loads(_text[_text.find('{'):])
+ self.nodes[key]['packages'] = _dict
+ else:
+ self.nodes[key]['packages'] = {}
+ logger_cli.info("{} has {} packages installed".format(
+ key,
+ len(self.nodes[key]['packages'].keys())
+ ))
+
+ def collect_packages(self):
+ """
+ Check package versions in repos vs installed
+
+ :return: no return values, all date put to dict in place
+ """
+ _all_packages = {}
+ for node_name, node_value in self.nodes.iteritems():
+ for package_name in node_value['packages']:
+ if package_name not in _all_packages:
+ _all_packages[package_name] = {}
+ _all_packages[package_name][node_name] = node_value
+
+ # TODO: process data for per-package basis
+
+ self.all_packages = _all_packages
+
+ def create_html_report(self, filename):
+ """
+ Create static html showing packages diff per node
+
+ :return: buff with html
+ """
+ _report = reporter.ReportToFile(
+ reporter.HTMLPackageCandidates(),
+ filename
+ )
+ _report(nodes=self.nodes)
+
+
+# init connection to salt and collect minion data
+cl = CloudPackageChecker()
+
+# collect data on installed packages
+cl.collect_installed_packages()
+
+# diff installed and candidates
+# cl.collect_packages()
+
+# report it
+cl.create_html_report("./pkg_versions.html")
+
+sys.exit(0)