blob: 34907c5854741ec78aeabb972e8d58eafa748041 [file] [log] [blame]
savex4448e132018-04-25 15:51:14 +02001import json
2import os
3import sys
4
5from copy import deepcopy
6
Alex Savatieievd48994d2018-12-13 12:13:00 +01007import reporter
Alex Savatieiev5118de02019-02-20 15:50:42 -06008
9from cfg_checker.common import utils, const
10from cfg_checker.common import config, logger, logger_cli, pkg_dir
11from cfg_checker.common import salt_utils
savex4448e132018-04-25 15:51:14 +020012
13node_tmpl = {
14 'role': '',
15 'node_group': '',
16 'status': const.NODE_DOWN,
17 'pillars': {},
18 'grains': {}
19}
20
21
22class CloudPackageChecker(object):
Alex Savatieiev5118de02019-02-20 15:50:42 -060023 _config = config
savex4448e132018-04-25 15:51:14 +020024
25 def __init__(self):
savexce010ba2018-04-27 09:49:23 +020026 logger_cli.info("Collecting nodes for package check")
savex4448e132018-04-25 15:51:14 +020027 # simple salt rest client
28 self.salt = salt_utils.SaltRemote()
29
30 # Keys for all nodes
31 # this is not working in scope of 2016.8.3, will overide with list
32 # cls.node_keys = cls.salt.list_keys()
33
savexce010ba2018-04-27 09:49:23 +020034 logger_cli.info("Collecting node names existing in the cloud")
savex4448e132018-04-25 15:51:14 +020035 self.node_keys = {
36 'minions': base_config.all_nodes
37 }
38
39 # all that answer ping
40 _active = self.salt.get_active_nodes()
savexce010ba2018-04-27 09:49:23 +020041 logger_cli.info("Nodes responded: {}".format(_active))
savex4448e132018-04-25 15:51:14 +020042 # just inventory for faster interaction
43 # iterate through all accepted nodes and create a dict for it
44 self.nodes = {}
45 for _name in self.node_keys['minions']:
46 _nc = utils.get_node_code(_name)
47 _rmap = const.all_roles_map
48 _role = _rmap[_nc] if _nc in _rmap else 'unknown'
49 _status = const.NODE_UP if _name in _active else const.NODE_DOWN
50
51 self.nodes[_name] = deepcopy(node_tmpl)
52 self.nodes[_name]['node_group'] = _nc
53 self.nodes[_name]['role'] = _role
54 self.nodes[_name]['status'] = _status
55
savexce010ba2018-04-27 09:49:23 +020056 logger_cli.info("{} nodes collected".format(len(self.nodes)))
savex4448e132018-04-25 15:51:14 +020057
58 def collect_installed_packages(self):
59 """
60 Collect installed packages on each node
61 sets 'installed' dict property in the class
62
63 :return: none
64 """
savexce010ba2018-04-27 09:49:23 +020065 logger_cli.info("Collecting installed packages")
savex4448e132018-04-25 15:51:14 +020066 # form an all nodes compound string to use in salt
67 _active_nodes_string = self.salt.compound_string_from_list(
68 filter(
69 lambda nd: self.nodes[nd]['status'] == const.NODE_UP,
70 self.nodes
71 )
72 )
73 # Prepare script
74 _script_filename = "pkg_versions.py"
75 _p = os.path.join(PKG_DIR, 'scripts', _script_filename)
76 with open(_p, 'rt') as fd:
77 _script = fd.read().splitlines()
savex4448e132018-04-25 15:51:14 +020078 _storage_path = os.path.join(
79 base_config.salt_file_root, base_config.salt_scripts_folder
80 )
savexce010ba2018-04-27 09:49:23 +020081 logger_cli.info(
82 "Uploading script {} to master's file cache folder: '{}'".format(
83 _script_filename,
84 _storage_path
savex4448e132018-04-25 15:51:14 +020085 )
86 )
savexce010ba2018-04-27 09:49:23 +020087 _result = self.salt.mkdir("cfg01*", _storage_path)
savex4448e132018-04-25 15:51:14 +020088 # Form cache, source and target path
89 _cache_path = os.path.join(_storage_path, _script_filename)
90 _source_path = os.path.join(
91 'salt://',
92 base_config.salt_scripts_folder,
93 _script_filename
94 )
95 _target_path = os.path.join(
96 '/root',
97 base_config.salt_scripts_folder,
98 _script_filename
99 )
100
101 logger.debug("Creating file in cache '{}'".format(_cache_path))
102 _result = self.salt.f_touch_master(_cache_path)
103 _result = self.salt.f_append_master(_cache_path, _script)
104 # command salt to copy file to minions
105 logger.debug("Creating script target folder '{}'".format(_cache_path))
106 _result = self.salt.mkdir(
107 _active_nodes_string,
108 os.path.join(
109 '/root',
110 base_config.salt_scripts_folder
111 ),
112 tgt_type="compound"
113 )
savexce010ba2018-04-27 09:49:23 +0200114 logger_cli.info("Running script to all active nodes")
savex4448e132018-04-25 15:51:14 +0200115 _result = self.salt.get_file(
116 _active_nodes_string,
117 _source_path,
118 _target_path,
119 tgt_type="compound"
120 )
121 # execute pkg collecting script
122 logger.debug("Running script to all nodes")
123 # handle results for each node
124 _result = self.salt.cmd(
125 _active_nodes_string,
126 'cmd.run',
127 param='python {}'.format(_target_path),
128 expr_form="compound"
129 )
130 for key in self.nodes.keys():
131 # due to much data to be passed from salt, it is happening in order
132 if key in _result:
133 _text = _result[key]
134 _dict = json.loads(_text[_text.find('{'):])
135 self.nodes[key]['packages'] = _dict
136 else:
137 self.nodes[key]['packages'] = {}
savexce010ba2018-04-27 09:49:23 +0200138 logger_cli.info("{} has {} packages installed".format(
savex4448e132018-04-25 15:51:14 +0200139 key,
140 len(self.nodes[key]['packages'].keys())
141 ))
142
143 def collect_packages(self):
144 """
145 Check package versions in repos vs installed
146
147 :return: no return values, all date put to dict in place
148 """
149 _all_packages = {}
150 for node_name, node_value in self.nodes.iteritems():
151 for package_name in node_value['packages']:
152 if package_name not in _all_packages:
153 _all_packages[package_name] = {}
154 _all_packages[package_name][node_name] = node_value
155
156 # TODO: process data for per-package basis
157
158 self.all_packages = _all_packages
159
160 def create_html_report(self, filename):
161 """
162 Create static html showing packages diff per node
163
164 :return: buff with html
165 """
Alex Savatieievd48994d2018-12-13 12:13:00 +0100166 _report = reporter.ReportToFile(
167 reporter.HTMLPackageCandidates(),
savex4448e132018-04-25 15:51:14 +0200168 filename
169 )
Alex Savatieiev36b938d2019-01-21 11:01:18 +0100170 _report({
171 "nodes": self.nodes,
172 "diffs": {}
173 })
savex4448e132018-04-25 15:51:14 +0200174
175
Alex Savatieiev5118de02019-02-20 15:50:42 -0600176if __name__ == '__main__':
177 # init connection to salt and collect minion data
178 cl = CloudPackageChecker()
savex4448e132018-04-25 15:51:14 +0200179
Alex Savatieiev5118de02019-02-20 15:50:42 -0600180 # collect data on installed packages
181 cl.collect_installed_packages()
savex4448e132018-04-25 15:51:14 +0200182
Alex Savatieiev5118de02019-02-20 15:50:42 -0600183 # diff installed and candidates
184 # cl.collect_packages()
savex4448e132018-04-25 15:51:14 +0200185
Alex Savatieiev5118de02019-02-20 15:50:42 -0600186 # report it
187 cl.create_html_report("./pkg_versions.html")
savex4448e132018-04-25 15:51:14 +0200188
Alex Savatieiev5118de02019-02-20 15:50:42 -0600189 sys.exit(0)