blob: dc9a2cfe451f9fc422808026f695842a23217ac3 [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):
Alexb78191f2021-11-02 16:35:46 -0500305 _size_i = True if value[-1] == 'i' else False
306 _char = value[-2] if _size_i else value[-1]
Alex3bc95f62020-03-05 17:00:04 -0600307 _ord = ord(_char)
308 if _ord > 47 and _ord < 58:
309 # bytes comes with no Char
Alex836fac82019-08-22 13:36:16 -0500310 return int(value)
Alex3bc95f62020-03-05 17:00:04 -0600311 else:
312 _sizes = ["*", "K", "M", "G", "T"]
Alexb78191f2021-11-02 16:35:46 -0500313 _flo = float(value[:-2]) if _size_i else float(value[:-1])
Alex3bc95f62020-03-05 17:00:04 -0600314 _pwr = 1
315 if _char in _sizes:
316 _pwr = _sizes.index(_char)
Alexb78191f2021-11-02 16:35:46 -0500317 return int(1024**_pwr*_flo)
Alex836fac82019-08-22 13:36:16 -0500318
Alex2e213b22019-12-05 10:40:29 -0600319 def _dmidecode(_dict, type=0):
Alex3bc95f62020-03-05 17:00:04 -0600320 # _key = "dmi"
Alex2e213b22019-12-05 10:40:29 -0600321 _key_r = "dmi_r"
Alex1f90e7b2021-09-03 15:31:28 -0500322 _f_cmd = self.master.get_cmd_for_nodes
Alex2e213b22019-12-05 10:40:29 -0600323 _cmd = "dmidecode -t {}".format(type)
324 _f_cmd(_cmd, _key_r, target_dict=_dict)
325 # TODO: parse BIOS output or given type
326 pass
327
328 def _lsblk(_dict):
Alex3bc95f62020-03-05 17:00:04 -0600329 # _key = "lsblk"
Alex2e213b22019-12-05 10:40:29 -0600330 _key_r = "lsblk_raw"
Alex1f90e7b2021-09-03 15:31:28 -0500331 _f_cmd = self.master.get_cmd_for_nodes
Alex2e213b22019-12-05 10:40:29 -0600332 _columns = [
333 "NAME",
334 "HCTL",
335 "TYPE",
336 "SIZE",
337 "VENDOR",
338 "MODEL",
339 "SERIAL",
340 "REV",
341 "TRAN"
342 ]
343 _cmd = "lsblk -S --output {}".format(",".join(_columns))
344 _f_cmd(_cmd, _key_r, target_dict=_dict)
345 # TODO: parse lsblk output
346 pass
347
Alex1839bbf2019-08-22 17:17:21 -0500348 def _lscpu(_dict):
349 _key = "lscpu"
350 _key_r = "lscpu_raw"
351 # get all of the values
Alex1f90e7b2021-09-03 15:31:28 -0500352 _f_cmd = self.master.get_cmd_for_nodes
Alex1839bbf2019-08-22 17:17:21 -0500353 _cmd = "lscpu | sed -n '/\\:/s/ \\+/ /gp'"
354 _f_cmd(_cmd, _key_r, target_dict=_dict)
355 # parse them and put into dict
Alex3bc95f62020-03-05 17:00:04 -0600356 for node, dt in _dict.items():
Alex1839bbf2019-08-22 17:17:21 -0500357 dt[_key] = {}
Alexe9908f72020-05-19 16:04:53 -0500358 if dt['status'] == DOWN or dt['status'] == SKIP:
Alexe65ff4e2019-10-11 14:45:09 -0500359 continue
360 if not dt[_key_r]:
361 # no stats collected, put negatives
362 dt.pop(_key_r)
Alex1839bbf2019-08-22 17:17:21 -0500363 continue
364 lines = dt[_key_r].splitlines()
365 for line in lines:
366 li = line.split(':')
367 _var_name = li[0].lower()
368 _var_name = re.sub(' ', '_', _var_name)
369 _var_name = re.sub('|'.join(['\\(', '\\)']), '', _var_name)
370 _var_value = li[1].strip()
371 dt[_key][_var_name] = _var_value
372 dt.pop(_key_r)
373 # detect virtual nodes
374 if "hypervisor_vendor" in dt[_key]:
375 dt['node_type'] = "virtual"
376 else:
377 dt['node_type'] = "physical"
Alex836fac82019-08-22 13:36:16 -0500378
Alex1839bbf2019-08-22 17:17:21 -0500379 def _free(_dict):
380 _key = "ram"
381 _key_r = "ram_raw"
Alex1f90e7b2021-09-03 15:31:28 -0500382 _f_cmd = self.master.get_cmd_for_nodes
Alexb78191f2021-11-02 16:35:46 -0500383 _cmd = "free -h | grep 'Mem' | sed -n '/Mem/s/ \\+/ /gp'"
Alex1839bbf2019-08-22 17:17:21 -0500384 _f_cmd(_cmd, _key_r, target_dict=_dict)
385 # parse them and put into dict
Alex3bc95f62020-03-05 17:00:04 -0600386 for node, dt in _dict.items():
Alex1839bbf2019-08-22 17:17:21 -0500387 dt[_key] = {}
Alexe9908f72020-05-19 16:04:53 -0500388 if dt['status'] == DOWN or dt['status'] == SKIP:
Alexe65ff4e2019-10-11 14:45:09 -0500389 continue
390 if not dt[_key_r]:
391 # no stats collected, put negatives
392 dt.pop(_key_r)
Alex1839bbf2019-08-22 17:17:21 -0500393 continue
394 li = dt[_key_r].split()
395 dt[_key]['total'] = li[1]
396 dt[_key]['used'] = li[2]
397 dt[_key]['free'] = li[3]
398 dt[_key]['shared'] = li[4]
399 dt[_key]['cache'] = li[5]
400 dt[_key]['available'] = li[6]
401
402 _total = get_bytes(li[1])
403 _avail = get_bytes(li[6])
404 _m = _avail * 100.0 / _total
405 if _m < _ram_critical:
406 dt[_key]["status"] = "fail"
407 elif _m < _ram_warn:
408 dt[_key]["status"] = "warn"
409 else:
410 dt[_key]["status"] = ""
Alex836fac82019-08-22 13:36:16 -0500411
412 def _services(_dict):
413 _key = "services"
414 _key_r = "services_raw"
Alex1f90e7b2021-09-03 15:31:28 -0500415 _f_cmd = self.master.get_cmd_for_nodes
Alex836fac82019-08-22 13:36:16 -0500416 _cmd = "service --status-all"
417 _f_cmd(_cmd, _key_r, target_dict=_dict)
Alex3bc95f62020-03-05 17:00:04 -0600418 for node, dt in _dict.items():
Alex836fac82019-08-22 13:36:16 -0500419 dt[_key] = {}
Alexe9908f72020-05-19 16:04:53 -0500420 if dt['status'] == DOWN or dt['status'] == SKIP:
Alexe65ff4e2019-10-11 14:45:09 -0500421 continue
422 if not dt[_key_r]:
423 # no stats collected, put negatives
424 dt.pop(_key_r)
Alex1839bbf2019-08-22 17:17:21 -0500425 continue
Alex836fac82019-08-22 13:36:16 -0500426 lines = dt[_key_r].splitlines()
427 for line in lines:
428 li = line.split()
429 _status = li[1]
430 _name = li[3]
431 if _status == '-':
432 dt[_key][_name] = False
433 elif _status == '+':
434 dt[_key][_name] = True
435 else:
436 dt[_key][_name] = None
437 dt.pop(_key_r)
438
Alex1839bbf2019-08-22 17:17:21 -0500439 def _vcp_status(_dict):
440 _key = "virsh"
441 _key_r = "virsh_raw"
Alex1f90e7b2021-09-03 15:31:28 -0500442 self.master.get_cmd_for_nodes(
Alex1839bbf2019-08-22 17:17:21 -0500443 "virsh list --all | sed -n -e '/[0-9]/s/ \\+/ /gp'",
444 _key_r,
445 target_dict=_dict,
446 nodes="kvm*"
447 )
448 _kvm = filter(lambda x: x.find("kvm") >= 0, _dict.keys())
449 for node in _kvm:
450 dt = _dict[node]
451 dt[_key] = {}
Alexe9908f72020-05-19 16:04:53 -0500452 if dt['status'] == DOWN or dt['status'] == SKIP:
Alexe65ff4e2019-10-11 14:45:09 -0500453 continue
454 if not dt[_key_r]:
455 # no stats collected, put negatives
456 dt.pop(_key_r)
Alex1839bbf2019-08-22 17:17:21 -0500457 continue
458 lines = dt[_key_r].splitlines()
459 for line in lines:
460 li = line.split()
461 _id = li[0]
462 _name = li[1]
463 _status = li[2]
464 dt[_key][_name] = {
465 'id': _id,
466 'status': _status
467 }
468 dt.pop(_key_r)
469
470 # query per-cpu and count totals
471 # total (0), dropped(1), squeezed (2), collision (7)
472 def _soft_net_stats(_dict):
473 _key = "net_stats"
474 _key_r = "net_stats_raw"
Alex1f90e7b2021-09-03 15:31:28 -0500475 _f_cmd = self.master.get_cmd_for_nodes
Alex1839bbf2019-08-22 17:17:21 -0500476 _cmd = "cat /proc/net/softnet_stat; echo \\#; " \
477 "sleep {}; cat /proc/net/softnet_stat".format(
478 _softnet_interval
479 )
480 _f_cmd(_cmd, _key_r, target_dict=_dict)
Alex3bc95f62020-03-05 17:00:04 -0600481 for node, dt in _dict.items():
Alex1839bbf2019-08-22 17:17:21 -0500482 _cpuindex = 1
483 _add_mode = True
Alex1839bbf2019-08-22 17:17:21 -0500484 # totals for start mark
485 _ts = [0, 0, 0, 0]
486 # skip if node is down
Alexe9908f72020-05-19 16:04:53 -0500487 if dt['status'] == DOWN or dt['status'] == SKIP:
Alexc96fdd32019-10-15 12:48:59 -0500488 dt[_key] = {
489 "total": [-1, -1, -1, -1]
490 }
Alex1839bbf2019-08-22 17:17:21 -0500491 continue
Alexf3dbe862019-10-07 15:17:04 -0500492 if not dt[_key_r]:
493 # no stats collected, put negatives
494 dt.pop(_key_r)
495 dt[_key] = {
496 "total": [-1, -1, -1, -1]
497 }
498 continue
499 # final totals
500 dt[_key] = {
501 "total": [0, 0, 0, 0]
502 }
Alex1839bbf2019-08-22 17:17:21 -0500503 lines = dt[_key_r].splitlines()
504 for line in lines:
505 if line.startswith("#"):
506 _add_mode = False
507 _cpuindex = 1
508 continue
509 li = line.split()
510 _c = [
511 int(li[0], 16),
512 int(li[1], 16),
513 int(li[2], 16),
514 int(li[7], 16)
515 ]
516 _id = "cpu{:02}".format(_cpuindex)
517 if _id not in dt[_key]:
518 dt[_key][_id] = []
519 _dc = dt[_key][_id]
520 if _add_mode:
521 # saving values and adding totals
522 dt[_key][_id] = _c
523 # save start totals
524 _ts = [_ts[i]+_c[i] for i in range(0, len(_c))]
525 else:
526 # this is second measurement
527 # subtract all values
528 for i in range(len(_c)):
529 dt[_key][_id][i] = _c[i] - _dc[i]
530 dt[_key]["total"][i] += _c[i]
531 _cpuindex += 1
532 # finally, subtract initial totals
Alex3bc95f62020-03-05 17:00:04 -0600533 for k, v in dt[_key].items():
Alex1839bbf2019-08-22 17:17:21 -0500534 if k != "total":
535 dt[_key][k] = [v[i] / 5. for i in range(len(v))]
536 else:
537 dt[_key][k] = [(v[i]-_ts[i])/5. for i in range(len(v))]
538 dt.pop(_key_r)
539
540 # prepare yellow and red marker values
Alex836fac82019-08-22 13:36:16 -0500541 data["const"] = {
Alex1839bbf2019-08-22 17:17:21 -0500542 "net_interval": _softnet_interval,
Alex836fac82019-08-22 13:36:16 -0500543 "ram_warn": _ram_warn,
544 "ram_critical": _ram_critical,
545 "disk_warn": _disk_warn,
Alex1839bbf2019-08-22 17:17:21 -0500546 "disk_critical": _disk_critical,
547 "services": read_file_as_lines(
548 os.path.join(
549 pkg_dir,
550 'etc',
551 'services.list'
552 )
553 )
Alex836fac82019-08-22 13:36:16 -0500554 }
555
556 # get kernel version
Alex1f90e7b2021-09-03 15:31:28 -0500557 self.master.get_cmd_for_nodes(
Alex836fac82019-08-22 13:36:16 -0500558 "uname -r",
559 "kernel",
560 target_dict=data["nodes"]
561 )
Alex1839bbf2019-08-22 17:17:21 -0500562 # process lscpu data
563 _lscpu(data["nodes"])
Alex836fac82019-08-22 13:36:16 -0500564
565 # free ram
566 # sample: 16425392 14883144 220196
Alex1839bbf2019-08-22 17:17:21 -0500567 _free(data["nodes"])
Alex836fac82019-08-22 13:36:16 -0500568
569 # disk space
570 # sample: /dev/vda1 78G 33G 45G 43%
Alexe65ff4e2019-10-11 14:45:09 -0500571 _key = "disk"
572 _key_r = "disk_raw"
Alex1f90e7b2021-09-03 15:31:28 -0500573 self.master.get_cmd_for_nodes(
Alex836fac82019-08-22 13:36:16 -0500574 "df -h | sed -n '/^\\/dev/s/ \\+/ /gp' | cut -d\" \" -f 1-5",
575 "disk_raw",
576 target_dict=data["nodes"]
577 )
Alex3bc95f62020-03-05 17:00:04 -0600578 for dt in data["nodes"].values():
Alexc96fdd32019-10-15 12:48:59 -0500579 dt["disk"] = {}
580 dt["disk_max_dev"] = None
Alexe65ff4e2019-10-11 14:45:09 -0500581 if dt['status'] == DOWN:
Alexc96fdd32019-10-15 12:48:59 -0500582 dt["disk"]["unknown"] = {}
583 dt["disk_max_dev"] = "unknown"
Alexe65ff4e2019-10-11 14:45:09 -0500584 continue
Alexe9908f72020-05-19 16:04:53 -0500585 if dt['status'] == SKIP:
586 dt["disk"]["skipped"] = {}
587 dt["disk_max_dev"] = "skipped"
588 continue
Alexe65ff4e2019-10-11 14:45:09 -0500589 if not dt[_key_r]:
590 # no stats collected, put negatives
591 dt.pop(_key_r)
592 dt[_key] = {}
593 continue
Alex836fac82019-08-22 13:36:16 -0500594 # show first device row by default
Alexe65ff4e2019-10-11 14:45:09 -0500595 _d = dt["disk"]
596 _r = dt["disk_raw"]
Alex836fac82019-08-22 13:36:16 -0500597 _r = _r.splitlines()
598 _max = -1
599 for idx in range(0, len(_r)):
600 _t = _r[idx].split()
601 _d[_t[0]] = {}
602 _d[_t[0]]['v'] = _t[1:]
603 _chk = int(_t[-1].split('%')[0])
604 if _chk > _max:
Alexe65ff4e2019-10-11 14:45:09 -0500605 dt["disk_max_dev"] = _t[0]
Alex836fac82019-08-22 13:36:16 -0500606 _max = _chk
607 if _chk > _disk_critical:
608 _d[_t[0]]['f'] = "fail"
609 elif _chk > _disk_warn:
610 _d[_t[0]]['f'] = "warn"
611 else:
612 _d[_t[0]]['f'] = ""
613
614 # prepare networks data for report
Alex3bc95f62020-03-05 17:00:04 -0600615 for net, net_v in data['map'].items():
616 for node, ifs in net_v.items():
Alex836fac82019-08-22 13:36:16 -0500617 for d in ifs:
Alex1f90e7b2021-09-03 15:31:28 -0500618 _e = "fail"
619 d['interface_error'] = _e if 'interface_error' in d else ""
620 d['mtu_error'] = _e if 'mtu_error' in d else ""
621 d['status_error'] = _e if 'status_error' in d else ""
Alex836fac82019-08-22 13:36:16 -0500622 d['subnet_gateway_error'] = \
Alex1f90e7b2021-09-03 15:31:28 -0500623 _e if 'subnet_gateway_error' in d else ""
Alex836fac82019-08-22 13:36:16 -0500624
625 _services(data["nodes"])
Alex1839bbf2019-08-22 17:17:21 -0500626 # vcp status
627 # query virsh and prepare for report
628 _vcp_status(data["nodes"])
629
630 # soft net stats
631 _soft_net_stats(data["nodes"])
Alex836fac82019-08-22 13:36:16 -0500632
Alex Savatieiev9b2f6512019-02-20 18:05:00 -0600633
savex4448e132018-04-25 15:51:14 +0200634class ReportToFile(object):
635 def __init__(self, report, target):
636 self.report = report
637 self.target = target
638
639 def __call__(self, payload):
640 payload = self.report(payload)
641
642 if isinstance(self.target, six.string_types):
643 self._wrapped_dump(payload)
644 else:
645 self._dump(payload, self.target)
646
647 def _wrapped_dump(self, payload):
648 with open(self.target, 'wt') as target:
649 self._dump(payload, target)
650
651 @staticmethod
652 def _dump(payload, target):
653 target.write(payload)