blob: 1f54ff3a19fde1daa4eac57b598db3588e2cab1f [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
Alex3ebc5632019-04-18 16:47:18 -05009
10import jinja2
11
12import six
savex4448e132018-04-25 15:51:14 +020013
14pkg_dir = os.path.dirname(__file__)
Alex Savatieiev6d010be2019-03-11 10:36:59 -050015pkg_dir = os.path.join(pkg_dir, os.pardir, os.pardir)
savex4448e132018-04-25 15:51:14 +020016pkg_dir = os.path.normpath(pkg_dir)
17
Alex836fac82019-08-22 13:36:16 -050018# % threshhold values
19_disk_warn = 80
20_disk_critical = 90
21_ram_warn = 5
22_ram_critical = 3
Alex1839bbf2019-08-22 17:17:21 -050023_softnet_interval = 5
24
25UP = const.NODE_UP
26DOWN = const.NODE_DOWN
Alexe9908f72020-05-19 16:04:53 -050027SKIP = const.NODE_SKIP
Alex836fac82019-08-22 13:36:16 -050028
savex4448e132018-04-25 15:51:14 +020029
savex4448e132018-04-25 15:51:14 +020030def line_breaks(text):
31 # replace python linebreaks with html breaks
32 return text.replace("\n", "<br />")
33
34
Alexdcb792f2021-10-04 14:24:21 -050035def tabstops(text):
36 # replace python linebreaks with html breaks
37 return text.replace("\t", "&#9;")
38
39
Alex41485522019-04-12 17:26:18 -050040def get_sorted_keys(td):
41 # detect if we can sort by desc
42 # Yes, this is slow, but bullet-proof from empty desc
43 _desc = all([bool(td[k]['desc']) for k in td.keys()])
44 # Get sorted list
45 if not _desc:
46 return sorted(td.keys())
47 else:
48 return sorted(
49 td.keys(),
50 key=lambda k: (
Alexd0391d42019-05-21 18:48:55 -050051 td[k]['desc']['section'],
Alex41485522019-04-12 17:26:18 -050052 td[k]['desc']['app'],
53 k
54 )
55 )
56
57
58def get_max(_list):
59 return sorted(_list)[-1]
60
61
Alex836fac82019-08-22 13:36:16 -050062def make_pkg_action_label(act):
Alex41485522019-04-12 17:26:18 -050063 _act_labels = {
64 const.ACT_UPGRADE: "Upgrade possible",
65 const.ACT_NEED_UP: "Needs upgrade",
66 const.ACT_NEED_DOWN: "Needs downgrade",
Alex9e4bfaf2019-06-11 15:21:59 -050067 const.ACT_REPO: "Repo update",
Alex41485522019-04-12 17:26:18 -050068 const.ACT_NA: ""
69 }
70 return _act_labels[act]
71
72
Alex836fac82019-08-22 13:36:16 -050073def make_pkg_action_class(act):
Alex41485522019-04-12 17:26:18 -050074 _act_classes = {
75 const.ACT_UPGRADE: "possible",
76 const.ACT_NEED_UP: "needs_up",
77 const.ACT_NEED_DOWN: "needs_down",
78 const.ACT_REPO: "needs_repo",
79 const.ACT_NA: ""
80 }
81 return _act_classes[act]
82
83
Alex836fac82019-08-22 13:36:16 -050084def make_pkg_status_label(sts):
Alex41485522019-04-12 17:26:18 -050085 _status_labels = {
86 const.VERSION_OK: "OK",
87 const.VERSION_UP: "Upgraded",
88 const.VERSION_DOWN: "Downgraded",
Alex26b8a8c2019-10-09 17:09:07 -050089 const.VERSION_WARN: "WARNING",
Alex41485522019-04-12 17:26:18 -050090 const.VERSION_ERR: "ERROR",
91 const.VERSION_NA: "N/A"
92 }
93 return _status_labels[sts]
94
95
Alex836fac82019-08-22 13:36:16 -050096def make_pkg_status_class(sts):
97 return const.all_pkg_statuses[sts]
98
99
100def make_node_status(sts):
101 return const.node_status[sts]
Alex41485522019-04-12 17:26:18 -0500102
103
Alexd0391d42019-05-21 18:48:55 -0500104def make_repo_info(repos):
105 _text = ""
106 for r in repos:
107 # tag
108 _text += r['tag'] + ": "
109 # repo header
110 _text += " ".join([
111 r['subset'],
112 r['release'],
113 r['ubuntu-release'],
114 r['type'],
115 r['arch']
116 ]) + ", "
117 # maintainer w/o email
Alex3bc95f62020-03-05 17:00:04 -0600118 _text += ascii(r['maintainer'][:r['maintainer'].find('<')-1])
Alexd0391d42019-05-21 18:48:55 -0500119 # newline
120 _text += "<br />"
121 return _text
122
123
Alexdcb792f2021-10-04 14:24:21 -0500124def to_gb(bytes_str):
125 _bytes = int(bytes_str)
126 _gb = _bytes / 1024 / 1024 / 1024
127 return "{}".format(round(_gb, 2))
128
129
130def to_mb(bytes_str):
131 _bytes = int(bytes_str)
132 _mb = _bytes / 1024 / 1024
133 return "{}".format(round(_mb, 2))
134
135
136def get_bucket_item_name(id, cmap):
137 for buck in cmap["buckets"]:
138 if id == buck["id"]:
139 return buck["name"]
140 for dev in cmap["devices"]:
141 if id == dev["id"]:
142 return dev["name"]
143 return id
144
145
146def get_rule_steps(steps):
147 _steps = []
148 for step in steps:
149 _ops = step.pop("op").split('_')
150 if "take" in _ops:
151 _steps.append(
152 "step {} {}".format(
153 " ".join(_ops),
154 step["item_name"]
155 )
156 )
157 else:
158 _steps.append(
159 "step {} {}".format(
160 " ".join(_ops),
161 " ".join(["{} {}".format(k, v) for k, v in step.items()])
162 )
163 )
164 return _steps
165
166
167def get_osdmap(cs):
168 _osdmap = cs
169 while True:
170 _keys = list(_osdmap.keys())
171 for _k in _keys:
172 if _k == "osdmap":
173 _osdmap = _osdmap[_k]
174 break
175 elif _k == 'epoch':
176 return _osdmap
177 return {
178 "epoch": 0,
179 "num_osds": 0,
180 "num_up_osds": 0,
181 "osd_up_since": 0,
182 "num_in_osds": 0,
183 "osd_in_since": 0,
184 "num_remapped_pgs": 0
185 }
186
187
188def get_pool_stats(id, pgdump):
189 _stats = {}
190 for pool in pgdump["pg_map"]["pool_stats"]:
191 if id == pool["poolid"]:
192 _stats = pool
193 return _stats
194
195
savex4448e132018-04-25 15:51:14 +0200196@six.add_metaclass(abc.ABCMeta)
197class _Base(object):
Alex1f90e7b2021-09-03 15:31:28 -0500198 def __init__(self, master=None):
savex4448e132018-04-25 15:51:14 +0200199 self.jinja2_env = self.init_jinja2_env()
Alex1f90e7b2021-09-03 15:31:28 -0500200 self.master = master
savex4448e132018-04-25 15:51:14 +0200201
202 @abc.abstractmethod
203 def __call__(self, payload):
204 pass
205
206 @staticmethod
207 def init_jinja2_env():
208 return jinja2.Environment(
209 loader=jinja2.FileSystemLoader(os.path.join(pkg_dir, 'templates')),
210 trim_blocks=True,
211 lstrip_blocks=True)
212
213
214class _TMPLBase(_Base):
215 @abc.abstractproperty
216 def tmpl(self):
217 pass
218
219 @staticmethod
220 def _count_totals(data):
221 data['counters']['total_nodes'] = len(data['nodes'])
222
Alex Savatieiev36b938d2019-01-21 11:01:18 +0100223 def __call__(self, payload):
savex4448e132018-04-25 15:51:14 +0200224 # init data structures
225 data = self.common_data()
Alex41485522019-04-12 17:26:18 -0500226 # payload should have pre-sorted structure according to report called
227 # nodes, openstack_release, mcp_release, etc...
228 data.update(payload)
savex4448e132018-04-25 15:51:14 +0200229
230 # add template specific data
231 self._extend_data(data)
232
233 # do counts global
234 self._count_totals(data)
235
236 # specific filters
savex4448e132018-04-25 15:51:14 +0200237 self.jinja2_env.filters['linebreaks'] = line_breaks
Alex41485522019-04-12 17:26:18 -0500238 self.jinja2_env.filters['get_max'] = get_max
239
240 self.jinja2_env.filters['get_sorted_keys'] = get_sorted_keys
Alex836fac82019-08-22 13:36:16 -0500241 self.jinja2_env.filters['pkg_status_label'] = make_pkg_status_label
242 self.jinja2_env.filters['pkg_status_class'] = make_pkg_status_class
243 self.jinja2_env.filters['pkg_action_label'] = make_pkg_action_label
244 self.jinja2_env.filters['pkg_action_class'] = make_pkg_action_class
245 self.jinja2_env.filters['node_status_class'] = make_node_status
Alexc6566d82019-09-23 16:07:17 -0500246 self.jinja2_env.filters['pkg_repo_info'] = make_repo_info
Alexdcb792f2021-10-04 14:24:21 -0500247 self.jinja2_env.filters['to_gb'] = to_gb
248 self.jinja2_env.filters['to_mb'] = to_mb
249 self.jinja2_env.filters['get_bucket_item_name'] = get_bucket_item_name
250 self.jinja2_env.filters['get_rule_steps'] = get_rule_steps
251 self.jinja2_env.filters['get_pool_stats'] = get_pool_stats
252 self.jinja2_env.filters['get_osdmap'] = get_osdmap
savex4448e132018-04-25 15:51:14 +0200253
254 # render!
Alex41485522019-04-12 17:26:18 -0500255 logger_cli.info("-> Using template: {}".format(self.tmpl))
savex4448e132018-04-25 15:51:14 +0200256 tmpl = self.jinja2_env.get_template(self.tmpl)
Alex41485522019-04-12 17:26:18 -0500257 logger_cli.info("-> Rendering")
savex4448e132018-04-25 15:51:14 +0200258 return tmpl.render(data)
259
260 def common_data(self):
261 return {
262 'counters': {},
Alex41485522019-04-12 17:26:18 -0500263 'salt_info': {},
264 'gen_date': time.strftime("%m/%d/%Y %H:%M:%S")
savex4448e132018-04-25 15:51:14 +0200265 }
266
267 def _extend_data(self, data):
268 pass
269
270
Alex41485522019-04-12 17:26:18 -0500271# HTML Package versions report
272class CSVAllPackages(_TMPLBase):
273 tmpl = "pkg_versions_csv.j2"
274
275
276# HTML Package versions report
savexce010ba2018-04-27 09:49:23 +0200277class HTMLPackageCandidates(_TMPLBase):
Alex41485522019-04-12 17:26:18 -0500278 tmpl = "pkg_versions_html.j2"
savex4448e132018-04-25 15:51:14 +0200279
280
Alexdcb792f2021-10-04 14:24:21 -0500281# HTML Ceph information report
282class HTMLCephInfo(_TMPLBase):
283 tmpl = "ceph_info_html.j2"
284
285
Alex Savatieievd48994d2018-12-13 12:13:00 +0100286# Package versions report
287class HTMLModelCompare(_TMPLBase):
288 tmpl = "model_tree_cmp_tmpl.j2"
289
290 def _extend_data(self, data):
Alex Savatieiev36b938d2019-01-21 11:01:18 +0100291 # move names into separate place
Alexb8af13a2019-04-16 18:38:12 -0500292 data["names"] = data["diffs"].pop("diff_names")
293 data["tabs"] = data.pop("diffs")
Alex3ebc5632019-04-18 16:47:18 -0500294
Alex Savatieievd48994d2018-12-13 12:13:00 +0100295 # counters - mdl_diff
Alex Savatieiev4f149d02019-02-28 17:15:29 -0600296 for _tab in data["tabs"].keys():
297 data['counters'][_tab] = len(data["tabs"][_tab]["diffs"].keys())
Alex Savatieievd48994d2018-12-13 12:13:00 +0100298
299
Alex Savatieiev9b2f6512019-02-20 18:05:00 -0600300class HTMLNetworkReport(_TMPLBase):
301 tmpl = "network_check_tmpl.j2"
302
Alex836fac82019-08-22 13:36:16 -0500303 def _extend_data(self, data):
304 def get_bytes(value):
Alex3bc95f62020-03-05 17:00:04 -0600305 _char = value[-1]
306 _ord = ord(_char)
307 if _ord > 47 and _ord < 58:
308 # bytes comes with no Char
Alex836fac82019-08-22 13:36:16 -0500309 return int(value)
Alex3bc95f62020-03-05 17:00:04 -0600310 else:
311 _sizes = ["*", "K", "M", "G", "T"]
312 _flo = float(value[:-1])
313 _pwr = 1
314 if _char in _sizes:
315 _pwr = _sizes.index(_char)
316 return int(_flo**_pwr)
Alex836fac82019-08-22 13:36:16 -0500317
Alex2e213b22019-12-05 10:40:29 -0600318 def _dmidecode(_dict, type=0):
Alex3bc95f62020-03-05 17:00:04 -0600319 # _key = "dmi"
Alex2e213b22019-12-05 10:40:29 -0600320 _key_r = "dmi_r"
Alex1f90e7b2021-09-03 15:31:28 -0500321 _f_cmd = self.master.get_cmd_for_nodes
Alex2e213b22019-12-05 10:40:29 -0600322 _cmd = "dmidecode -t {}".format(type)
323 _f_cmd(_cmd, _key_r, target_dict=_dict)
324 # TODO: parse BIOS output or given type
325 pass
326
327 def _lsblk(_dict):
Alex3bc95f62020-03-05 17:00:04 -0600328 # _key = "lsblk"
Alex2e213b22019-12-05 10:40:29 -0600329 _key_r = "lsblk_raw"
Alex1f90e7b2021-09-03 15:31:28 -0500330 _f_cmd = self.master.get_cmd_for_nodes
Alex2e213b22019-12-05 10:40:29 -0600331 _columns = [
332 "NAME",
333 "HCTL",
334 "TYPE",
335 "SIZE",
336 "VENDOR",
337 "MODEL",
338 "SERIAL",
339 "REV",
340 "TRAN"
341 ]
342 _cmd = "lsblk -S --output {}".format(",".join(_columns))
343 _f_cmd(_cmd, _key_r, target_dict=_dict)
344 # TODO: parse lsblk output
345 pass
346
Alex1839bbf2019-08-22 17:17:21 -0500347 def _lscpu(_dict):
348 _key = "lscpu"
349 _key_r = "lscpu_raw"
350 # get all of the values
Alex1f90e7b2021-09-03 15:31:28 -0500351 _f_cmd = self.master.get_cmd_for_nodes
Alex1839bbf2019-08-22 17:17:21 -0500352 _cmd = "lscpu | sed -n '/\\:/s/ \\+/ /gp'"
353 _f_cmd(_cmd, _key_r, target_dict=_dict)
354 # parse them and put into dict
Alex3bc95f62020-03-05 17:00:04 -0600355 for node, dt in _dict.items():
Alex1839bbf2019-08-22 17:17:21 -0500356 dt[_key] = {}
Alexe9908f72020-05-19 16:04:53 -0500357 if dt['status'] == DOWN or dt['status'] == SKIP:
Alexe65ff4e2019-10-11 14:45:09 -0500358 continue
359 if not dt[_key_r]:
360 # no stats collected, put negatives
361 dt.pop(_key_r)
Alex1839bbf2019-08-22 17:17:21 -0500362 continue
363 lines = dt[_key_r].splitlines()
364 for line in lines:
365 li = line.split(':')
366 _var_name = li[0].lower()
367 _var_name = re.sub(' ', '_', _var_name)
368 _var_name = re.sub('|'.join(['\\(', '\\)']), '', _var_name)
369 _var_value = li[1].strip()
370 dt[_key][_var_name] = _var_value
371 dt.pop(_key_r)
372 # detect virtual nodes
373 if "hypervisor_vendor" in dt[_key]:
374 dt['node_type'] = "virtual"
375 else:
376 dt['node_type'] = "physical"
Alex836fac82019-08-22 13:36:16 -0500377
Alex1839bbf2019-08-22 17:17:21 -0500378 def _free(_dict):
379 _key = "ram"
380 _key_r = "ram_raw"
Alex1f90e7b2021-09-03 15:31:28 -0500381 _f_cmd = self.master.get_cmd_for_nodes
Alex1839bbf2019-08-22 17:17:21 -0500382 _cmd = "free -h | sed -n '/Mem/s/ \\+/ /gp'"
383 _f_cmd(_cmd, _key_r, target_dict=_dict)
384 # parse them and put into dict
Alex3bc95f62020-03-05 17:00:04 -0600385 for node, dt in _dict.items():
Alex1839bbf2019-08-22 17:17:21 -0500386 dt[_key] = {}
Alexe9908f72020-05-19 16:04:53 -0500387 if dt['status'] == DOWN or dt['status'] == SKIP:
Alexe65ff4e2019-10-11 14:45:09 -0500388 continue
389 if not dt[_key_r]:
390 # no stats collected, put negatives
391 dt.pop(_key_r)
Alex1839bbf2019-08-22 17:17:21 -0500392 continue
393 li = dt[_key_r].split()
394 dt[_key]['total'] = li[1]
395 dt[_key]['used'] = li[2]
396 dt[_key]['free'] = li[3]
397 dt[_key]['shared'] = li[4]
398 dt[_key]['cache'] = li[5]
399 dt[_key]['available'] = li[6]
400
401 _total = get_bytes(li[1])
402 _avail = get_bytes(li[6])
403 _m = _avail * 100.0 / _total
404 if _m < _ram_critical:
405 dt[_key]["status"] = "fail"
406 elif _m < _ram_warn:
407 dt[_key]["status"] = "warn"
408 else:
409 dt[_key]["status"] = ""
Alex836fac82019-08-22 13:36:16 -0500410
411 def _services(_dict):
412 _key = "services"
413 _key_r = "services_raw"
Alex1f90e7b2021-09-03 15:31:28 -0500414 _f_cmd = self.master.get_cmd_for_nodes
Alex836fac82019-08-22 13:36:16 -0500415 _cmd = "service --status-all"
416 _f_cmd(_cmd, _key_r, target_dict=_dict)
Alex3bc95f62020-03-05 17:00:04 -0600417 for node, dt in _dict.items():
Alex836fac82019-08-22 13:36:16 -0500418 dt[_key] = {}
Alexe9908f72020-05-19 16:04:53 -0500419 if dt['status'] == DOWN or dt['status'] == SKIP:
Alexe65ff4e2019-10-11 14:45:09 -0500420 continue
421 if not dt[_key_r]:
422 # no stats collected, put negatives
423 dt.pop(_key_r)
Alex1839bbf2019-08-22 17:17:21 -0500424 continue
Alex836fac82019-08-22 13:36:16 -0500425 lines = dt[_key_r].splitlines()
426 for line in lines:
427 li = line.split()
428 _status = li[1]
429 _name = li[3]
430 if _status == '-':
431 dt[_key][_name] = False
432 elif _status == '+':
433 dt[_key][_name] = True
434 else:
435 dt[_key][_name] = None
436 dt.pop(_key_r)
437
Alex1839bbf2019-08-22 17:17:21 -0500438 def _vcp_status(_dict):
439 _key = "virsh"
440 _key_r = "virsh_raw"
Alex1f90e7b2021-09-03 15:31:28 -0500441 self.master.get_cmd_for_nodes(
Alex1839bbf2019-08-22 17:17:21 -0500442 "virsh list --all | sed -n -e '/[0-9]/s/ \\+/ /gp'",
443 _key_r,
444 target_dict=_dict,
445 nodes="kvm*"
446 )
447 _kvm = filter(lambda x: x.find("kvm") >= 0, _dict.keys())
448 for node in _kvm:
449 dt = _dict[node]
450 dt[_key] = {}
Alexe9908f72020-05-19 16:04:53 -0500451 if dt['status'] == DOWN or dt['status'] == SKIP:
Alexe65ff4e2019-10-11 14:45:09 -0500452 continue
453 if not dt[_key_r]:
454 # no stats collected, put negatives
455 dt.pop(_key_r)
Alex1839bbf2019-08-22 17:17:21 -0500456 continue
457 lines = dt[_key_r].splitlines()
458 for line in lines:
459 li = line.split()
460 _id = li[0]
461 _name = li[1]
462 _status = li[2]
463 dt[_key][_name] = {
464 'id': _id,
465 'status': _status
466 }
467 dt.pop(_key_r)
468
469 # query per-cpu and count totals
470 # total (0), dropped(1), squeezed (2), collision (7)
471 def _soft_net_stats(_dict):
472 _key = "net_stats"
473 _key_r = "net_stats_raw"
Alex1f90e7b2021-09-03 15:31:28 -0500474 _f_cmd = self.master.get_cmd_for_nodes
Alex1839bbf2019-08-22 17:17:21 -0500475 _cmd = "cat /proc/net/softnet_stat; echo \\#; " \
476 "sleep {}; cat /proc/net/softnet_stat".format(
477 _softnet_interval
478 )
479 _f_cmd(_cmd, _key_r, target_dict=_dict)
Alex3bc95f62020-03-05 17:00:04 -0600480 for node, dt in _dict.items():
Alex1839bbf2019-08-22 17:17:21 -0500481 _cpuindex = 1
482 _add_mode = True
Alex1839bbf2019-08-22 17:17:21 -0500483 # totals for start mark
484 _ts = [0, 0, 0, 0]
485 # skip if node is down
Alexe9908f72020-05-19 16:04:53 -0500486 if dt['status'] == DOWN or dt['status'] == SKIP:
Alexc96fdd32019-10-15 12:48:59 -0500487 dt[_key] = {
488 "total": [-1, -1, -1, -1]
489 }
Alex1839bbf2019-08-22 17:17:21 -0500490 continue
Alexf3dbe862019-10-07 15:17:04 -0500491 if not dt[_key_r]:
492 # no stats collected, put negatives
493 dt.pop(_key_r)
494 dt[_key] = {
495 "total": [-1, -1, -1, -1]
496 }
497 continue
498 # final totals
499 dt[_key] = {
500 "total": [0, 0, 0, 0]
501 }
Alex1839bbf2019-08-22 17:17:21 -0500502 lines = dt[_key_r].splitlines()
503 for line in lines:
504 if line.startswith("#"):
505 _add_mode = False
506 _cpuindex = 1
507 continue
508 li = line.split()
509 _c = [
510 int(li[0], 16),
511 int(li[1], 16),
512 int(li[2], 16),
513 int(li[7], 16)
514 ]
515 _id = "cpu{:02}".format(_cpuindex)
516 if _id not in dt[_key]:
517 dt[_key][_id] = []
518 _dc = dt[_key][_id]
519 if _add_mode:
520 # saving values and adding totals
521 dt[_key][_id] = _c
522 # save start totals
523 _ts = [_ts[i]+_c[i] for i in range(0, len(_c))]
524 else:
525 # this is second measurement
526 # subtract all values
527 for i in range(len(_c)):
528 dt[_key][_id][i] = _c[i] - _dc[i]
529 dt[_key]["total"][i] += _c[i]
530 _cpuindex += 1
531 # finally, subtract initial totals
Alex3bc95f62020-03-05 17:00:04 -0600532 for k, v in dt[_key].items():
Alex1839bbf2019-08-22 17:17:21 -0500533 if k != "total":
534 dt[_key][k] = [v[i] / 5. for i in range(len(v))]
535 else:
536 dt[_key][k] = [(v[i]-_ts[i])/5. for i in range(len(v))]
537 dt.pop(_key_r)
538
539 # prepare yellow and red marker values
Alex836fac82019-08-22 13:36:16 -0500540 data["const"] = {
Alex1839bbf2019-08-22 17:17:21 -0500541 "net_interval": _softnet_interval,
Alex836fac82019-08-22 13:36:16 -0500542 "ram_warn": _ram_warn,
543 "ram_critical": _ram_critical,
544 "disk_warn": _disk_warn,
Alex1839bbf2019-08-22 17:17:21 -0500545 "disk_critical": _disk_critical,
546 "services": read_file_as_lines(
547 os.path.join(
548 pkg_dir,
549 'etc',
550 'services.list'
551 )
552 )
Alex836fac82019-08-22 13:36:16 -0500553 }
554
555 # get kernel version
Alex1f90e7b2021-09-03 15:31:28 -0500556 self.master.get_cmd_for_nodes(
Alex836fac82019-08-22 13:36:16 -0500557 "uname -r",
558 "kernel",
559 target_dict=data["nodes"]
560 )
Alex1839bbf2019-08-22 17:17:21 -0500561 # process lscpu data
562 _lscpu(data["nodes"])
Alex836fac82019-08-22 13:36:16 -0500563
564 # free ram
565 # sample: 16425392 14883144 220196
Alex1839bbf2019-08-22 17:17:21 -0500566 _free(data["nodes"])
Alex836fac82019-08-22 13:36:16 -0500567
568 # disk space
569 # sample: /dev/vda1 78G 33G 45G 43%
Alexe65ff4e2019-10-11 14:45:09 -0500570 _key = "disk"
571 _key_r = "disk_raw"
Alex1f90e7b2021-09-03 15:31:28 -0500572 self.master.get_cmd_for_nodes(
Alex836fac82019-08-22 13:36:16 -0500573 "df -h | sed -n '/^\\/dev/s/ \\+/ /gp' | cut -d\" \" -f 1-5",
574 "disk_raw",
575 target_dict=data["nodes"]
576 )
Alex3bc95f62020-03-05 17:00:04 -0600577 for dt in data["nodes"].values():
Alexc96fdd32019-10-15 12:48:59 -0500578 dt["disk"] = {}
579 dt["disk_max_dev"] = None
Alexe65ff4e2019-10-11 14:45:09 -0500580 if dt['status'] == DOWN:
Alexc96fdd32019-10-15 12:48:59 -0500581 dt["disk"]["unknown"] = {}
582 dt["disk_max_dev"] = "unknown"
Alexe65ff4e2019-10-11 14:45:09 -0500583 continue
Alexe9908f72020-05-19 16:04:53 -0500584 if dt['status'] == SKIP:
585 dt["disk"]["skipped"] = {}
586 dt["disk_max_dev"] = "skipped"
587 continue
Alexe65ff4e2019-10-11 14:45:09 -0500588 if not dt[_key_r]:
589 # no stats collected, put negatives
590 dt.pop(_key_r)
591 dt[_key] = {}
592 continue
Alex836fac82019-08-22 13:36:16 -0500593 # show first device row by default
Alexe65ff4e2019-10-11 14:45:09 -0500594 _d = dt["disk"]
595 _r = dt["disk_raw"]
Alex836fac82019-08-22 13:36:16 -0500596 _r = _r.splitlines()
597 _max = -1
598 for idx in range(0, len(_r)):
599 _t = _r[idx].split()
600 _d[_t[0]] = {}
601 _d[_t[0]]['v'] = _t[1:]
602 _chk = int(_t[-1].split('%')[0])
603 if _chk > _max:
Alexe65ff4e2019-10-11 14:45:09 -0500604 dt["disk_max_dev"] = _t[0]
Alex836fac82019-08-22 13:36:16 -0500605 _max = _chk
606 if _chk > _disk_critical:
607 _d[_t[0]]['f'] = "fail"
608 elif _chk > _disk_warn:
609 _d[_t[0]]['f'] = "warn"
610 else:
611 _d[_t[0]]['f'] = ""
612
613 # prepare networks data for report
Alex3bc95f62020-03-05 17:00:04 -0600614 for net, net_v in data['map'].items():
615 for node, ifs in net_v.items():
Alex836fac82019-08-22 13:36:16 -0500616 for d in ifs:
Alex1f90e7b2021-09-03 15:31:28 -0500617 _e = "fail"
618 d['interface_error'] = _e if 'interface_error' in d else ""
619 d['mtu_error'] = _e if 'mtu_error' in d else ""
620 d['status_error'] = _e if 'status_error' in d else ""
Alex836fac82019-08-22 13:36:16 -0500621 d['subnet_gateway_error'] = \
Alex1f90e7b2021-09-03 15:31:28 -0500622 _e if 'subnet_gateway_error' in d else ""
Alex836fac82019-08-22 13:36:16 -0500623
624 _services(data["nodes"])
Alex1839bbf2019-08-22 17:17:21 -0500625 # vcp status
626 # query virsh and prepare for report
627 _vcp_status(data["nodes"])
628
629 # soft net stats
630 _soft_net_stats(data["nodes"])
Alex836fac82019-08-22 13:36:16 -0500631
Alex Savatieiev9b2f6512019-02-20 18:05:00 -0600632
savex4448e132018-04-25 15:51:14 +0200633class ReportToFile(object):
634 def __init__(self, report, target):
635 self.report = report
636 self.target = target
637
638 def __call__(self, payload):
639 payload = self.report(payload)
640
641 if isinstance(self.target, six.string_types):
642 self._wrapped_dump(payload)
643 else:
644 self._dump(payload, self.target)
645
646 def _wrapped_dump(self, payload):
647 with open(self.target, 'wt') as target:
648 self._dump(payload, target)
649
650 @staticmethod
651 def _dump(payload, target):
652 target.write(payload)