blob: 150ce6595a6abf6f4c914f62b0ecadf95b777bfe [file] [log] [blame]
savex4448e132018-04-25 15:51:14 +02001import abc
2import os
Alex1839bbf2019-08-22 17:17:21 -05003import re
Alex41485522019-04-12 17:26:18 -05004import time
savex4448e132018-04-25 15:51:14 +02005
Alex Savatieiev5118de02019-02-20 15:50:42 -06006from cfg_checker.common import const
Alex3ebc5632019-04-18 16:47:18 -05007from cfg_checker.common import logger_cli
Alex1839bbf2019-08-22 17:17:21 -05008from cfg_checker.common.file_utils import read_file_as_lines
Alexb2129542021-11-23 15:49:42 -06009from cfg_checker.modules.ceph.bench import _reformat_timestr
Alex3ebc5632019-04-18 16:47:18 -050010
11import jinja2
12
13import six
savex4448e132018-04-25 15:51:14 +020014
15pkg_dir = os.path.dirname(__file__)
Alex Savatieiev6d010be2019-03-11 10:36:59 -050016pkg_dir = os.path.join(pkg_dir, os.pardir, os.pardir)
savex4448e132018-04-25 15:51:14 +020017pkg_dir = os.path.normpath(pkg_dir)
18
Alex836fac82019-08-22 13:36:16 -050019# % threshhold values
20_disk_warn = 80
21_disk_critical = 90
22_ram_warn = 5
23_ram_critical = 3
Alex1839bbf2019-08-22 17:17:21 -050024_softnet_interval = 5
25
26UP = const.NODE_UP
27DOWN = const.NODE_DOWN
Alexe9908f72020-05-19 16:04:53 -050028SKIP = const.NODE_SKIP
Alex836fac82019-08-22 13:36:16 -050029
savex4448e132018-04-25 15:51:14 +020030
savex4448e132018-04-25 15:51:14 +020031def line_breaks(text):
32 # replace python linebreaks with html breaks
33 return text.replace("\n", "<br />")
34
35
Alexdcb792f2021-10-04 14:24:21 -050036def tabstops(text):
37 # replace python linebreaks with html breaks
38 return text.replace("\t", "&#9;")
39
40
Alex41485522019-04-12 17:26:18 -050041def get_sorted_keys(td):
42 # detect if we can sort by desc
43 # Yes, this is slow, but bullet-proof from empty desc
44 _desc = all([bool(td[k]['desc']) for k in td.keys()])
45 # Get sorted list
46 if not _desc:
47 return sorted(td.keys())
48 else:
49 return sorted(
50 td.keys(),
51 key=lambda k: (
Alexd0391d42019-05-21 18:48:55 -050052 td[k]['desc']['section'],
Alex41485522019-04-12 17:26:18 -050053 td[k]['desc']['app'],
54 k
55 )
56 )
57
58
59def get_max(_list):
60 return sorted(_list)[-1]
61
62
Alex836fac82019-08-22 13:36:16 -050063def make_pkg_action_label(act):
Alex41485522019-04-12 17:26:18 -050064 _act_labels = {
65 const.ACT_UPGRADE: "Upgrade possible",
66 const.ACT_NEED_UP: "Needs upgrade",
67 const.ACT_NEED_DOWN: "Needs downgrade",
Alex9e4bfaf2019-06-11 15:21:59 -050068 const.ACT_REPO: "Repo update",
Alex41485522019-04-12 17:26:18 -050069 const.ACT_NA: ""
70 }
71 return _act_labels[act]
72
73
Alex836fac82019-08-22 13:36:16 -050074def make_pkg_action_class(act):
Alex41485522019-04-12 17:26:18 -050075 _act_classes = {
76 const.ACT_UPGRADE: "possible",
77 const.ACT_NEED_UP: "needs_up",
78 const.ACT_NEED_DOWN: "needs_down",
79 const.ACT_REPO: "needs_repo",
80 const.ACT_NA: ""
81 }
82 return _act_classes[act]
83
84
Alex836fac82019-08-22 13:36:16 -050085def make_pkg_status_label(sts):
Alex41485522019-04-12 17:26:18 -050086 _status_labels = {
87 const.VERSION_OK: "OK",
88 const.VERSION_UP: "Upgraded",
89 const.VERSION_DOWN: "Downgraded",
Alex26b8a8c2019-10-09 17:09:07 -050090 const.VERSION_WARN: "WARNING",
Alex41485522019-04-12 17:26:18 -050091 const.VERSION_ERR: "ERROR",
92 const.VERSION_NA: "N/A"
93 }
94 return _status_labels[sts]
95
96
Alex836fac82019-08-22 13:36:16 -050097def make_pkg_status_class(sts):
98 return const.all_pkg_statuses[sts]
99
100
101def make_node_status(sts):
102 return const.node_status[sts]
Alex41485522019-04-12 17:26:18 -0500103
104
Alexd0391d42019-05-21 18:48:55 -0500105def make_repo_info(repos):
106 _text = ""
107 for r in repos:
108 # tag
109 _text += r['tag'] + ": "
110 # repo header
111 _text += " ".join([
112 r['subset'],
113 r['release'],
114 r['ubuntu-release'],
115 r['type'],
116 r['arch']
117 ]) + ", "
118 # maintainer w/o email
Alex3bc95f62020-03-05 17:00:04 -0600119 _text += ascii(r['maintainer'][:r['maintainer'].find('<')-1])
Alexd0391d42019-05-21 18:48:55 -0500120 # newline
121 _text += "<br />"
122 return _text
123
124
Alexdcb792f2021-10-04 14:24:21 -0500125def to_gb(bytes_str):
126 _bytes = int(bytes_str)
127 _gb = _bytes / 1024 / 1024 / 1024
128 return "{}".format(round(_gb, 2))
129
130
131def to_mb(bytes_str):
132 _bytes = int(bytes_str)
133 _mb = _bytes / 1024 / 1024
134 return "{}".format(round(_mb, 2))
135
136
137def get_bucket_item_name(id, cmap):
138 for buck in cmap["buckets"]:
139 if id == buck["id"]:
140 return buck["name"]
141 for dev in cmap["devices"]:
142 if id == dev["id"]:
143 return dev["name"]
144 return id
145
146
147def get_rule_steps(steps):
148 _steps = []
149 for step in steps:
150 _ops = step.pop("op").split('_')
151 if "take" in _ops:
152 _steps.append(
153 "step {} {}".format(
154 " ".join(_ops),
155 step["item_name"]
156 )
157 )
158 else:
159 _steps.append(
160 "step {} {}".format(
161 " ".join(_ops),
162 " ".join(["{} {}".format(k, v) for k, v in step.items()])
163 )
164 )
165 return _steps
166
167
Alexb2129542021-11-23 15:49:42 -0600168def time_strip(timestring):
169 return _reformat_timestr(timestring, _tchar="")
170
171
Alexdcb792f2021-10-04 14:24:21 -0500172def get_osdmap(cs):
173 _osdmap = cs
174 while True:
175 _keys = list(_osdmap.keys())
176 for _k in _keys:
177 if _k == "osdmap":
178 _osdmap = _osdmap[_k]
179 break
180 elif _k == 'epoch':
181 return _osdmap
182 return {
183 "epoch": 0,
184 "num_osds": 0,
185 "num_up_osds": 0,
186 "osd_up_since": 0,
187 "num_in_osds": 0,
188 "osd_in_since": 0,
189 "num_remapped_pgs": 0
190 }
191
192
Alexb2129542021-11-23 15:49:42 -0600193def get_pool_stats_by_id(id, pgdump):
Alexdcb792f2021-10-04 14:24:21 -0500194 _stats = {}
195 for pool in pgdump["pg_map"]["pool_stats"]:
196 if id == pool["poolid"]:
197 _stats = pool
198 return _stats
199
200
savex4448e132018-04-25 15:51:14 +0200201@six.add_metaclass(abc.ABCMeta)
202class _Base(object):
Alex1f90e7b2021-09-03 15:31:28 -0500203 def __init__(self, master=None):
savex4448e132018-04-25 15:51:14 +0200204 self.jinja2_env = self.init_jinja2_env()
Alex1f90e7b2021-09-03 15:31:28 -0500205 self.master = master
savex4448e132018-04-25 15:51:14 +0200206
207 @abc.abstractmethod
208 def __call__(self, payload):
209 pass
210
211 @staticmethod
212 def init_jinja2_env():
213 return jinja2.Environment(
214 loader=jinja2.FileSystemLoader(os.path.join(pkg_dir, 'templates')),
215 trim_blocks=True,
216 lstrip_blocks=True)
217
218
219class _TMPLBase(_Base):
220 @abc.abstractproperty
221 def tmpl(self):
222 pass
223
224 @staticmethod
225 def _count_totals(data):
226 data['counters']['total_nodes'] = len(data['nodes'])
227
Alex Savatieiev36b938d2019-01-21 11:01:18 +0100228 def __call__(self, payload):
savex4448e132018-04-25 15:51:14 +0200229 # init data structures
230 data = self.common_data()
Alex41485522019-04-12 17:26:18 -0500231 # payload should have pre-sorted structure according to report called
232 # nodes, openstack_release, mcp_release, etc...
233 data.update(payload)
savex4448e132018-04-25 15:51:14 +0200234
235 # add template specific data
236 self._extend_data(data)
237
238 # do counts global
239 self._count_totals(data)
240
241 # specific filters
savex4448e132018-04-25 15:51:14 +0200242 self.jinja2_env.filters['linebreaks'] = line_breaks
Alex41485522019-04-12 17:26:18 -0500243 self.jinja2_env.filters['get_max'] = get_max
244
245 self.jinja2_env.filters['get_sorted_keys'] = get_sorted_keys
Alex836fac82019-08-22 13:36:16 -0500246 self.jinja2_env.filters['pkg_status_label'] = make_pkg_status_label
247 self.jinja2_env.filters['pkg_status_class'] = make_pkg_status_class
248 self.jinja2_env.filters['pkg_action_label'] = make_pkg_action_label
249 self.jinja2_env.filters['pkg_action_class'] = make_pkg_action_class
250 self.jinja2_env.filters['node_status_class'] = make_node_status
Alexc6566d82019-09-23 16:07:17 -0500251 self.jinja2_env.filters['pkg_repo_info'] = make_repo_info
Alexdcb792f2021-10-04 14:24:21 -0500252 self.jinja2_env.filters['to_gb'] = to_gb
253 self.jinja2_env.filters['to_mb'] = to_mb
254 self.jinja2_env.filters['get_bucket_item_name'] = get_bucket_item_name
255 self.jinja2_env.filters['get_rule_steps'] = get_rule_steps
Alexb2129542021-11-23 15:49:42 -0600256 self.jinja2_env.filters['get_pool_stats'] = get_pool_stats_by_id
Alexdcb792f2021-10-04 14:24:21 -0500257 self.jinja2_env.filters['get_osdmap'] = get_osdmap
Alexb2129542021-11-23 15:49:42 -0600258 self.jinja2_env.filters['tstrip'] = time_strip
savex4448e132018-04-25 15:51:14 +0200259
260 # render!
Alex41485522019-04-12 17:26:18 -0500261 logger_cli.info("-> Using template: {}".format(self.tmpl))
savex4448e132018-04-25 15:51:14 +0200262 tmpl = self.jinja2_env.get_template(self.tmpl)
Alex41485522019-04-12 17:26:18 -0500263 logger_cli.info("-> Rendering")
savex4448e132018-04-25 15:51:14 +0200264 return tmpl.render(data)
265
266 def common_data(self):
267 return {
268 'counters': {},
Alex41485522019-04-12 17:26:18 -0500269 'salt_info': {},
270 'gen_date': time.strftime("%m/%d/%Y %H:%M:%S")
savex4448e132018-04-25 15:51:14 +0200271 }
272
273 def _extend_data(self, data):
274 pass
275
276
Alex41485522019-04-12 17:26:18 -0500277# HTML Package versions report
278class CSVAllPackages(_TMPLBase):
279 tmpl = "pkg_versions_csv.j2"
280
281
282# HTML Package versions report
savexce010ba2018-04-27 09:49:23 +0200283class HTMLPackageCandidates(_TMPLBase):
Alex41485522019-04-12 17:26:18 -0500284 tmpl = "pkg_versions_html.j2"
savex4448e132018-04-25 15:51:14 +0200285
286
Alexdcb792f2021-10-04 14:24:21 -0500287# HTML Ceph information report
288class HTMLCephInfo(_TMPLBase):
289 tmpl = "ceph_info_html.j2"
290
291
Alexb2129542021-11-23 15:49:42 -0600292class HTMLCephBench(_TMPLBase):
293 tmpl = "ceph_bench_html.j2"
294
295
Alex Savatieievd48994d2018-12-13 12:13:00 +0100296# Package versions report
297class HTMLModelCompare(_TMPLBase):
298 tmpl = "model_tree_cmp_tmpl.j2"
299
300 def _extend_data(self, data):
Alex Savatieiev36b938d2019-01-21 11:01:18 +0100301 # move names into separate place
Alexb8af13a2019-04-16 18:38:12 -0500302 data["names"] = data["diffs"].pop("diff_names")
303 data["tabs"] = data.pop("diffs")
Alex3ebc5632019-04-18 16:47:18 -0500304
Alex Savatieievd48994d2018-12-13 12:13:00 +0100305 # counters - mdl_diff
Alex Savatieiev4f149d02019-02-28 17:15:29 -0600306 for _tab in data["tabs"].keys():
307 data['counters'][_tab] = len(data["tabs"][_tab]["diffs"].keys())
Alex Savatieievd48994d2018-12-13 12:13:00 +0100308
309
Alex Savatieiev9b2f6512019-02-20 18:05:00 -0600310class HTMLNetworkReport(_TMPLBase):
311 tmpl = "network_check_tmpl.j2"
312
Alex836fac82019-08-22 13:36:16 -0500313 def _extend_data(self, data):
314 def get_bytes(value):
Alexb78191f2021-11-02 16:35:46 -0500315 _size_i = True if value[-1] == 'i' else False
316 _char = value[-2] if _size_i else value[-1]
Alex3bc95f62020-03-05 17:00:04 -0600317 _ord = ord(_char)
318 if _ord > 47 and _ord < 58:
319 # bytes comes with no Char
Alex836fac82019-08-22 13:36:16 -0500320 return int(value)
Alex3bc95f62020-03-05 17:00:04 -0600321 else:
322 _sizes = ["*", "K", "M", "G", "T"]
Alexb78191f2021-11-02 16:35:46 -0500323 _flo = float(value[:-2]) if _size_i else float(value[:-1])
Alex3bc95f62020-03-05 17:00:04 -0600324 _pwr = 1
325 if _char in _sizes:
326 _pwr = _sizes.index(_char)
Alexb78191f2021-11-02 16:35:46 -0500327 return int(1024**_pwr*_flo)
Alex836fac82019-08-22 13:36:16 -0500328
Alex2e213b22019-12-05 10:40:29 -0600329 def _dmidecode(_dict, type=0):
Alex3bc95f62020-03-05 17:00:04 -0600330 # _key = "dmi"
Alex2e213b22019-12-05 10:40:29 -0600331 _key_r = "dmi_r"
Alex1f90e7b2021-09-03 15:31:28 -0500332 _f_cmd = self.master.get_cmd_for_nodes
Alex2e213b22019-12-05 10:40:29 -0600333 _cmd = "dmidecode -t {}".format(type)
334 _f_cmd(_cmd, _key_r, target_dict=_dict)
335 # TODO: parse BIOS output or given type
336 pass
337
338 def _lsblk(_dict):
Alex3bc95f62020-03-05 17:00:04 -0600339 # _key = "lsblk"
Alex2e213b22019-12-05 10:40:29 -0600340 _key_r = "lsblk_raw"
Alex1f90e7b2021-09-03 15:31:28 -0500341 _f_cmd = self.master.get_cmd_for_nodes
Alex2e213b22019-12-05 10:40:29 -0600342 _columns = [
343 "NAME",
344 "HCTL",
345 "TYPE",
346 "SIZE",
347 "VENDOR",
348 "MODEL",
349 "SERIAL",
350 "REV",
351 "TRAN"
352 ]
353 _cmd = "lsblk -S --output {}".format(",".join(_columns))
354 _f_cmd(_cmd, _key_r, target_dict=_dict)
355 # TODO: parse lsblk output
356 pass
357
Alex1839bbf2019-08-22 17:17:21 -0500358 def _lscpu(_dict):
359 _key = "lscpu"
360 _key_r = "lscpu_raw"
361 # get all of the values
Alex1f90e7b2021-09-03 15:31:28 -0500362 _f_cmd = self.master.get_cmd_for_nodes
Alex1839bbf2019-08-22 17:17:21 -0500363 _cmd = "lscpu | sed -n '/\\:/s/ \\+/ /gp'"
364 _f_cmd(_cmd, _key_r, target_dict=_dict)
365 # parse them and put into dict
Alex3bc95f62020-03-05 17:00:04 -0600366 for node, dt in _dict.items():
Alex1839bbf2019-08-22 17:17:21 -0500367 dt[_key] = {}
Alexe9908f72020-05-19 16:04:53 -0500368 if dt['status'] == DOWN or dt['status'] == SKIP:
Alexe65ff4e2019-10-11 14:45:09 -0500369 continue
370 if not dt[_key_r]:
371 # no stats collected, put negatives
372 dt.pop(_key_r)
Alex1839bbf2019-08-22 17:17:21 -0500373 continue
374 lines = dt[_key_r].splitlines()
375 for line in lines:
376 li = line.split(':')
377 _var_name = li[0].lower()
378 _var_name = re.sub(' ', '_', _var_name)
379 _var_name = re.sub('|'.join(['\\(', '\\)']), '', _var_name)
380 _var_value = li[1].strip()
381 dt[_key][_var_name] = _var_value
382 dt.pop(_key_r)
383 # detect virtual nodes
384 if "hypervisor_vendor" in dt[_key]:
385 dt['node_type'] = "virtual"
386 else:
387 dt['node_type'] = "physical"
Alex836fac82019-08-22 13:36:16 -0500388
Alex1839bbf2019-08-22 17:17:21 -0500389 def _free(_dict):
390 _key = "ram"
391 _key_r = "ram_raw"
Alex1f90e7b2021-09-03 15:31:28 -0500392 _f_cmd = self.master.get_cmd_for_nodes
Alexb78191f2021-11-02 16:35:46 -0500393 _cmd = "free -h | grep 'Mem' | sed -n '/Mem/s/ \\+/ /gp'"
Alex1839bbf2019-08-22 17:17:21 -0500394 _f_cmd(_cmd, _key_r, target_dict=_dict)
395 # parse them and put into dict
Alex3bc95f62020-03-05 17:00:04 -0600396 for node, dt in _dict.items():
Alex1839bbf2019-08-22 17:17:21 -0500397 dt[_key] = {}
Alexe9908f72020-05-19 16:04:53 -0500398 if dt['status'] == DOWN or dt['status'] == SKIP:
Alexe65ff4e2019-10-11 14:45:09 -0500399 continue
400 if not dt[_key_r]:
401 # no stats collected, put negatives
402 dt.pop(_key_r)
Alex1839bbf2019-08-22 17:17:21 -0500403 continue
404 li = dt[_key_r].split()
405 dt[_key]['total'] = li[1]
406 dt[_key]['used'] = li[2]
407 dt[_key]['free'] = li[3]
408 dt[_key]['shared'] = li[4]
409 dt[_key]['cache'] = li[5]
410 dt[_key]['available'] = li[6]
411
412 _total = get_bytes(li[1])
413 _avail = get_bytes(li[6])
414 _m = _avail * 100.0 / _total
415 if _m < _ram_critical:
416 dt[_key]["status"] = "fail"
417 elif _m < _ram_warn:
418 dt[_key]["status"] = "warn"
419 else:
420 dt[_key]["status"] = ""
Alex836fac82019-08-22 13:36:16 -0500421
422 def _services(_dict):
423 _key = "services"
424 _key_r = "services_raw"
Alex1f90e7b2021-09-03 15:31:28 -0500425 _f_cmd = self.master.get_cmd_for_nodes
Alex836fac82019-08-22 13:36:16 -0500426 _cmd = "service --status-all"
427 _f_cmd(_cmd, _key_r, target_dict=_dict)
Alex3bc95f62020-03-05 17:00:04 -0600428 for node, dt in _dict.items():
Alex836fac82019-08-22 13:36:16 -0500429 dt[_key] = {}
Alexe9908f72020-05-19 16:04:53 -0500430 if dt['status'] == DOWN or dt['status'] == SKIP:
Alexe65ff4e2019-10-11 14:45:09 -0500431 continue
432 if not dt[_key_r]:
433 # no stats collected, put negatives
434 dt.pop(_key_r)
Alex1839bbf2019-08-22 17:17:21 -0500435 continue
Alex836fac82019-08-22 13:36:16 -0500436 lines = dt[_key_r].splitlines()
437 for line in lines:
438 li = line.split()
439 _status = li[1]
440 _name = li[3]
441 if _status == '-':
442 dt[_key][_name] = False
443 elif _status == '+':
444 dt[_key][_name] = True
445 else:
446 dt[_key][_name] = None
447 dt.pop(_key_r)
448
Alex1839bbf2019-08-22 17:17:21 -0500449 def _vcp_status(_dict):
450 _key = "virsh"
451 _key_r = "virsh_raw"
Alex1f90e7b2021-09-03 15:31:28 -0500452 self.master.get_cmd_for_nodes(
Alex1839bbf2019-08-22 17:17:21 -0500453 "virsh list --all | sed -n -e '/[0-9]/s/ \\+/ /gp'",
454 _key_r,
455 target_dict=_dict,
456 nodes="kvm*"
457 )
458 _kvm = filter(lambda x: x.find("kvm") >= 0, _dict.keys())
459 for node in _kvm:
460 dt = _dict[node]
461 dt[_key] = {}
Alexe9908f72020-05-19 16:04:53 -0500462 if dt['status'] == DOWN or dt['status'] == SKIP:
Alexe65ff4e2019-10-11 14:45:09 -0500463 continue
464 if not dt[_key_r]:
465 # no stats collected, put negatives
466 dt.pop(_key_r)
Alex1839bbf2019-08-22 17:17:21 -0500467 continue
468 lines = dt[_key_r].splitlines()
469 for line in lines:
470 li = line.split()
471 _id = li[0]
472 _name = li[1]
473 _status = li[2]
474 dt[_key][_name] = {
475 'id': _id,
476 'status': _status
477 }
478 dt.pop(_key_r)
479
480 # query per-cpu and count totals
481 # total (0), dropped(1), squeezed (2), collision (7)
482 def _soft_net_stats(_dict):
483 _key = "net_stats"
484 _key_r = "net_stats_raw"
Alex1f90e7b2021-09-03 15:31:28 -0500485 _f_cmd = self.master.get_cmd_for_nodes
Alex1839bbf2019-08-22 17:17:21 -0500486 _cmd = "cat /proc/net/softnet_stat; echo \\#; " \
487 "sleep {}; cat /proc/net/softnet_stat".format(
488 _softnet_interval
489 )
490 _f_cmd(_cmd, _key_r, target_dict=_dict)
Alex3bc95f62020-03-05 17:00:04 -0600491 for node, dt in _dict.items():
Alex1839bbf2019-08-22 17:17:21 -0500492 _cpuindex = 1
493 _add_mode = True
Alex1839bbf2019-08-22 17:17:21 -0500494 # totals for start mark
495 _ts = [0, 0, 0, 0]
496 # skip if node is down
Alexe9908f72020-05-19 16:04:53 -0500497 if dt['status'] == DOWN or dt['status'] == SKIP:
Alexc96fdd32019-10-15 12:48:59 -0500498 dt[_key] = {
499 "total": [-1, -1, -1, -1]
500 }
Alex1839bbf2019-08-22 17:17:21 -0500501 continue
Alexf3dbe862019-10-07 15:17:04 -0500502 if not dt[_key_r]:
503 # no stats collected, put negatives
504 dt.pop(_key_r)
505 dt[_key] = {
506 "total": [-1, -1, -1, -1]
507 }
508 continue
509 # final totals
510 dt[_key] = {
511 "total": [0, 0, 0, 0]
512 }
Alex1839bbf2019-08-22 17:17:21 -0500513 lines = dt[_key_r].splitlines()
514 for line in lines:
515 if line.startswith("#"):
516 _add_mode = False
517 _cpuindex = 1
518 continue
519 li = line.split()
520 _c = [
521 int(li[0], 16),
522 int(li[1], 16),
523 int(li[2], 16),
524 int(li[7], 16)
525 ]
526 _id = "cpu{:02}".format(_cpuindex)
527 if _id not in dt[_key]:
528 dt[_key][_id] = []
529 _dc = dt[_key][_id]
530 if _add_mode:
531 # saving values and adding totals
532 dt[_key][_id] = _c
533 # save start totals
534 _ts = [_ts[i]+_c[i] for i in range(0, len(_c))]
535 else:
536 # this is second measurement
537 # subtract all values
538 for i in range(len(_c)):
539 dt[_key][_id][i] = _c[i] - _dc[i]
540 dt[_key]["total"][i] += _c[i]
541 _cpuindex += 1
542 # finally, subtract initial totals
Alex3bc95f62020-03-05 17:00:04 -0600543 for k, v in dt[_key].items():
Alex1839bbf2019-08-22 17:17:21 -0500544 if k != "total":
545 dt[_key][k] = [v[i] / 5. for i in range(len(v))]
546 else:
547 dt[_key][k] = [(v[i]-_ts[i])/5. for i in range(len(v))]
548 dt.pop(_key_r)
549
550 # prepare yellow and red marker values
Alex836fac82019-08-22 13:36:16 -0500551 data["const"] = {
Alex1839bbf2019-08-22 17:17:21 -0500552 "net_interval": _softnet_interval,
Alex836fac82019-08-22 13:36:16 -0500553 "ram_warn": _ram_warn,
554 "ram_critical": _ram_critical,
555 "disk_warn": _disk_warn,
Alex1839bbf2019-08-22 17:17:21 -0500556 "disk_critical": _disk_critical,
557 "services": read_file_as_lines(
558 os.path.join(
559 pkg_dir,
560 'etc',
561 'services.list'
562 )
563 )
Alex836fac82019-08-22 13:36:16 -0500564 }
565
566 # get kernel version
Alex1f90e7b2021-09-03 15:31:28 -0500567 self.master.get_cmd_for_nodes(
Alex836fac82019-08-22 13:36:16 -0500568 "uname -r",
569 "kernel",
570 target_dict=data["nodes"]
571 )
Alex1839bbf2019-08-22 17:17:21 -0500572 # process lscpu data
573 _lscpu(data["nodes"])
Alex836fac82019-08-22 13:36:16 -0500574
575 # free ram
576 # sample: 16425392 14883144 220196
Alex1839bbf2019-08-22 17:17:21 -0500577 _free(data["nodes"])
Alex836fac82019-08-22 13:36:16 -0500578
579 # disk space
580 # sample: /dev/vda1 78G 33G 45G 43%
Alexe65ff4e2019-10-11 14:45:09 -0500581 _key = "disk"
582 _key_r = "disk_raw"
Alex1f90e7b2021-09-03 15:31:28 -0500583 self.master.get_cmd_for_nodes(
Alex836fac82019-08-22 13:36:16 -0500584 "df -h | sed -n '/^\\/dev/s/ \\+/ /gp' | cut -d\" \" -f 1-5",
585 "disk_raw",
586 target_dict=data["nodes"]
587 )
Alex3bc95f62020-03-05 17:00:04 -0600588 for dt in data["nodes"].values():
Alexc96fdd32019-10-15 12:48:59 -0500589 dt["disk"] = {}
590 dt["disk_max_dev"] = None
Alexe65ff4e2019-10-11 14:45:09 -0500591 if dt['status'] == DOWN:
Alexc96fdd32019-10-15 12:48:59 -0500592 dt["disk"]["unknown"] = {}
593 dt["disk_max_dev"] = "unknown"
Alexe65ff4e2019-10-11 14:45:09 -0500594 continue
Alexe9908f72020-05-19 16:04:53 -0500595 if dt['status'] == SKIP:
596 dt["disk"]["skipped"] = {}
597 dt["disk_max_dev"] = "skipped"
598 continue
Alexe65ff4e2019-10-11 14:45:09 -0500599 if not dt[_key_r]:
600 # no stats collected, put negatives
601 dt.pop(_key_r)
602 dt[_key] = {}
603 continue
Alex836fac82019-08-22 13:36:16 -0500604 # show first device row by default
Alexe65ff4e2019-10-11 14:45:09 -0500605 _d = dt["disk"]
606 _r = dt["disk_raw"]
Alex836fac82019-08-22 13:36:16 -0500607 _r = _r.splitlines()
608 _max = -1
609 for idx in range(0, len(_r)):
610 _t = _r[idx].split()
611 _d[_t[0]] = {}
612 _d[_t[0]]['v'] = _t[1:]
613 _chk = int(_t[-1].split('%')[0])
614 if _chk > _max:
Alexe65ff4e2019-10-11 14:45:09 -0500615 dt["disk_max_dev"] = _t[0]
Alex836fac82019-08-22 13:36:16 -0500616 _max = _chk
617 if _chk > _disk_critical:
618 _d[_t[0]]['f'] = "fail"
619 elif _chk > _disk_warn:
620 _d[_t[0]]['f'] = "warn"
621 else:
622 _d[_t[0]]['f'] = ""
623
624 # prepare networks data for report
Alex3bc95f62020-03-05 17:00:04 -0600625 for net, net_v in data['map'].items():
626 for node, ifs in net_v.items():
Alex836fac82019-08-22 13:36:16 -0500627 for d in ifs:
Alex1f90e7b2021-09-03 15:31:28 -0500628 _e = "fail"
629 d['interface_error'] = _e if 'interface_error' in d else ""
630 d['mtu_error'] = _e if 'mtu_error' in d else ""
631 d['status_error'] = _e if 'status_error' in d else ""
Alex836fac82019-08-22 13:36:16 -0500632 d['subnet_gateway_error'] = \
Alex1f90e7b2021-09-03 15:31:28 -0500633 _e if 'subnet_gateway_error' in d else ""
Alex836fac82019-08-22 13:36:16 -0500634
635 _services(data["nodes"])
Alex1839bbf2019-08-22 17:17:21 -0500636 # vcp status
637 # query virsh and prepare for report
638 _vcp_status(data["nodes"])
639
640 # soft net stats
641 _soft_net_stats(data["nodes"])
Alex836fac82019-08-22 13:36:16 -0500642
Alex Savatieiev9b2f6512019-02-20 18:05:00 -0600643
savex4448e132018-04-25 15:51:14 +0200644class ReportToFile(object):
645 def __init__(self, report, target):
646 self.report = report
647 self.target = target
648
649 def __call__(self, payload):
650 payload = self.report(payload)
651
652 if isinstance(self.target, six.string_types):
653 self._wrapped_dump(payload)
654 else:
655 self._dump(payload, self.target)
656
657 def _wrapped_dump(self, payload):
658 with open(self.target, 'wt') as target:
659 self._dump(payload, target)
660
661 @staticmethod
662 def _dump(payload, target):
663 target.write(payload)