blob: 47135439969313748cdd7f44eebb4c1f0c1e4814 [file] [log] [blame]
savex4448e132018-04-25 15:51:14 +02001import json
2import os
3import sys
4
5from copy import deepcopy
6
7import common.const as const
8import pkg_reporter
9from check_versions.common import utils
10from check_versions.common import base_config, logger, PKG_DIR
11from check_versions.common import salt_utils
12
13node_tmpl = {
14 'role': '',
15 'node_group': '',
16 'status': const.NODE_DOWN,
17 'pillars': {},
18 'grains': {}
19}
20
21
22class CloudPackageChecker(object):
23 _config = base_config
24
25 def __init__(self):
26 logger.info("Collecting nodes for package check")
27 # 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
34 logger.debug("Collecting node names existing in the cloud")
35 self.node_keys = {
36 'minions': base_config.all_nodes
37 }
38
39 # all that answer ping
40 _active = self.salt.get_active_nodes()
41 logger.debug("Nodes responded: {}".format(_active))
42 # 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
56 logger.debug("{} nodes collected".format(len(self.nodes)))
57
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 """
65 # form an all nodes compound string to use in salt
66 _active_nodes_string = self.salt.compound_string_from_list(
67 filter(
68 lambda nd: self.nodes[nd]['status'] == const.NODE_UP,
69 self.nodes
70 )
71 )
72 # Prepare script
73 _script_filename = "pkg_versions.py"
74 _p = os.path.join(PKG_DIR, 'scripts', _script_filename)
75 with open(_p, 'rt') as fd:
76 _script = fd.read().splitlines()
77
78 _storage_path = os.path.join(
79 base_config.salt_file_root, base_config.salt_scripts_folder
80 )
81 _result = self.salt.mkdir("cfg01*", _storage_path)
82 logger.debug(
83 "Tried to create folder on master. Salt returned: {}".format(
84 _result
85 )
86 )
87 # Form cache, source and target path
88 _cache_path = os.path.join(_storage_path, _script_filename)
89 _source_path = os.path.join(
90 'salt://',
91 base_config.salt_scripts_folder,
92 _script_filename
93 )
94 _target_path = os.path.join(
95 '/root',
96 base_config.salt_scripts_folder,
97 _script_filename
98 )
99
100 logger.debug("Creating file in cache '{}'".format(_cache_path))
101 _result = self.salt.f_touch_master(_cache_path)
102 _result = self.salt.f_append_master(_cache_path, _script)
103 # command salt to copy file to minions
104 logger.debug("Creating script target folder '{}'".format(_cache_path))
105 _result = self.salt.mkdir(
106 _active_nodes_string,
107 os.path.join(
108 '/root',
109 base_config.salt_scripts_folder
110 ),
111 tgt_type="compound"
112 )
113 logger.debug("Copying script to all nodes")
114 _result = self.salt.get_file(
115 _active_nodes_string,
116 _source_path,
117 _target_path,
118 tgt_type="compound"
119 )
120 # execute pkg collecting script
121 logger.debug("Running script to all nodes")
122 # handle results for each node
123 _result = self.salt.cmd(
124 _active_nodes_string,
125 'cmd.run',
126 param='python {}'.format(_target_path),
127 expr_form="compound"
128 )
129 for key in self.nodes.keys():
130 # due to much data to be passed from salt, it is happening in order
131 if key in _result:
132 _text = _result[key]
133 _dict = json.loads(_text[_text.find('{'):])
134 self.nodes[key]['packages'] = _dict
135 else:
136 self.nodes[key]['packages'] = {}
137 logger.info("{} has {} packages installed".format(
138 key,
139 len(self.nodes[key]['packages'].keys())
140 ))
141
142 def collect_packages(self):
143 """
144 Check package versions in repos vs installed
145
146 :return: no return values, all date put to dict in place
147 """
148 _all_packages = {}
149 for node_name, node_value in self.nodes.iteritems():
150 for package_name in node_value['packages']:
151 if package_name not in _all_packages:
152 _all_packages[package_name] = {}
153 _all_packages[package_name][node_name] = node_value
154
155 # TODO: process data for per-package basis
156
157 self.all_packages = _all_packages
158
159 def create_html_report(self, filename):
160 """
161 Create static html showing packages diff per node
162
163 :return: buff with html
164 """
165 _report = pkg_reporter.ReportToFile(
166 pkg_reporter.HTMLPackageVersions(),
167 filename
168 )
169 _report(self.nodes)
170
171
172# init connection to salt and collect minion data
173cl = CloudPackageChecker()
174
175# collect data on installed packages
176cl.collect_installed_packages()
177
178# diff installed and candidates
179# cl.collect_packages()
180
181# report it
182cl.create_html_report("./pkg_versions.html")
183
184sys.exit(0)