blob: 6a0c97b7d6c52237b300ee6e2225e4533b132e36 [file] [log] [blame]
Alex0989ecf2022-03-29 13:43:21 -05001# Author: Alex Savatieiev (osavatieiev@mirantis.com; a.savex@gmail.com)
2# Copyright 2019-2022 Mirantis, Inc.
Alexb151fbe2019-04-22 16:53:30 -05003import os
Alex92e07ce2019-05-31 16:00:03 -05004from configparser import NoSectionError
Alexb151fbe2019-04-22 16:53:30 -05005
6from cfg_checker.common import file_utils as fu
7from cfg_checker.common import logger, logger_cli
8from cfg_checker.common.config_file import ConfigFile
Alex3ebc5632019-04-18 16:47:18 -05009from cfg_checker.common.exception import ErrorMappingException
Alexb151fbe2019-04-22 16:53:30 -050010from cfg_checker.common.settings import pkg_dir
Alex3ebc5632019-04-18 16:47:18 -050011
12
13class ErrorIndex(object):
Alexb151fbe2019-04-22 16:53:30 -050014 # logs folder filenames
15 _error_logs_folder_name = ".cfgerrors"
16 _conf_filename = "conf"
17 # config file object
18 conf = None
19 # iteration counter
20 _iteration = 0
21 # local vars for codes
Alex3ebc5632019-04-18 16:47:18 -050022 _area_code = ""
23 _delimiter = ""
24 _index = 0
25 _errors = {}
26 _types = {
27 0: "Unknown error"
28 }
29
Alex3bc95f62020-03-05 17:00:04 -060030 def __init__(self, area_code, delimiter='-', folder=None):
Alex3ebc5632019-04-18 16:47:18 -050031 self._area_code = area_code
32 self._delimiter = delimiter
33 self._index += 1
34
Alex3bc95f62020-03-05 17:00:04 -060035 # save folder
36 if folder:
37 self._error_logs_folder_name = folder
38
Alexb151fbe2019-04-22 16:53:30 -050039 # init the error log storage folder
40 _folder = os.path.join(pkg_dir, self._error_logs_folder_name)
41 self._conf_filename = os.path.join(
42 _folder,
43 self._conf_filename
44 )
45
Alex7f69a6a2019-05-31 16:53:35 -050046 logger_cli.debug(fu.ensure_folder_exists(_folder))
Alexb151fbe2019-04-22 16:53:30 -050047 if not os.path.exists(self._conf_filename):
48 # put file with init values
49 self.conf = ConfigFile(self._area_code.lower())
50 self.conf.set_value('iteration', self._iteration)
51 self.conf.save_config(filepath=self._conf_filename)
52 logger_cli.debug(
53 "... create new config file '{}'".format(
54 self._conf_filename
55 )
56 )
57 else:
58 # it exists, try to load latest run
59 self.conf = ConfigFile(
60 self._area_code.lower(),
61 filepath=self._conf_filename
62 )
Alex92e07ce2019-05-31 16:00:03 -050063 # check if there is any values there
64 try:
65 self._iteration = self.conf.get_value(
66 'iteration',
67 value_type=int
68 )
69 self._iteration += 1
70 except NoSectionError:
71 self._iteration += 1
72 self.conf.set_value('iteration', self._iteration)
73 self.conf.save_config(filepath=self._conf_filename)
74 logger_cli.debug("... updated config file")
75
Alexb151fbe2019-04-22 16:53:30 -050076 logger_cli.debug(" ... starting iteration {}".format(self._iteration))
77
78 def save_iteration_data(self):
79 # save error log
80 _filename = "-".join([self._area_code.lower(), "errors"])
81 _filename += "." + str(self._iteration)
82 _log_filename = os.path.join(
83 pkg_dir,
84 self._error_logs_folder_name,
85 _filename
86 )
87 fu.write_lines_to_file(_log_filename, self.get_errors(as_list=True))
88 fu.append_line_to_file(_log_filename, "")
89 fu.append_lines_to_file(_log_filename, self.get_summary(as_list=True))
90 logger_cli.debug("... saved errors to '{}'".format(_log_filename))
91
92 # save last iteration number
93 self.conf.set_value('iteration', self._iteration)
94 self.conf.save_config()
95
Alex3ebc5632019-04-18 16:47:18 -050096 def _format_error_code(self, index):
97 _t = "{:02d}".format(self._errors[index]['type'])
98 _i = "{:04d}".format(index)
99 _fmt = self._delimiter.join([self._area_code, _t, _i])
100 return _fmt
101
102 def _format_error(self, index):
103 # error code
104 _code = self._format_error_code(index)
105 # prepare data as string list
106 _d = self._errors[index]['data']
Alex3bc95f62020-03-05 17:00:04 -0600107 _data = [" {}: {}".format(_k, _v) for _k, _v in _d.items()]
Alex3ebc5632019-04-18 16:47:18 -0500108 # format message
Alexb151fbe2019-04-22 16:53:30 -0500109 _msg = "### {}:\n Description: {}\n{}".format(
Alex3ebc5632019-04-18 16:47:18 -0500110 _code,
Alex836fac82019-08-22 13:36:16 -0500111 self.get_error_type_text(self._errors[index]['type']),
Alex3ebc5632019-04-18 16:47:18 -0500112 "\n".join(_data)
113 )
114 return _msg
115
Alex836fac82019-08-22 13:36:16 -0500116 def get_error_type_text(self, err_type):
Alex3ebc5632019-04-18 16:47:18 -0500117 if err_type not in self._types:
118 raise ErrorMappingException(
119 "type code {} not found".format(err_type)
120 )
121 else:
122 return self._types[err_type]
123
124 def get_error_code(self, index):
125 if index in self._errors.keys():
126 return self._format_error(index)
127 else:
128 raise ErrorMappingException(
129 "no error found for index {}".format(index)
130 )
131
132 def add_error_type(self, err_type, message):
133 if err_type in self._types:
134 raise ErrorMappingException(
135 "type code {} reserved for {}".format(
136 err_type,
137 self._types[err_type]
138 )
139 )
140 else:
141 self._types[err_type] = message
142
143 def add_error(self, err_type, **kwargs):
144 # check error type
145 if err_type not in self._types.keys():
146 logger.error(
147 "Error type not listed: '{}'; unknown used".format(err_type)
148 )
149 err_type = 0
150 _err = {
151 "type": err_type,
152 "data": kwargs
153 }
154 self._errors[self._index] = _err
155 self._index += 1
156
157 def get_errors_total(self):
158 return self._index-1
159
160 def get_indices(self):
161 return self._errors.keys()
162
163 def get_error(self, index):
164 if index in self._errors.keys():
165 return self._format_error(index)
166 else:
167 return "Unknown error index of {}".format(index)
168
Alexb151fbe2019-04-22 16:53:30 -0500169 def get_summary(self, print_zeros=True, as_list=False):
Alex3ebc5632019-04-18 16:47:18 -0500170 # create summary with counts per error type
Alexb151fbe2019-04-22 16:53:30 -0500171 _list = "\n{:=^8s}\n{:^8s}\n{:=^8s}".format(
172 "=",
173 "Totals",
174 "="
175 ).splitlines()
176
Alex3ebc5632019-04-18 16:47:18 -0500177 for _type in self._types.keys():
178 _len = len(
Alex3bc95f62020-03-05 17:00:04 -0600179 list(
180 filter(
181 lambda i: self._errors[i]['type'] == _type,
182 self._errors
183 )
Alex3ebc5632019-04-18 16:47:18 -0500184 )
185 )
186 if _len:
187 _num_str = "{:5d}".format(_len)
188 elif print_zeros:
189 _num_str = "{:>5s}".format("-")
190 else:
191 continue
192 _list.append(
193 "{}: {}".format(
194 _num_str,
195 self._types[_type]
196 )
197 )
198
Alexb151fbe2019-04-22 16:53:30 -0500199 _total_errors = self.get_errors_total()
Alex3ebc5632019-04-18 16:47:18 -0500200
Alexb151fbe2019-04-22 16:53:30 -0500201 _list.append('-'*20)
Alex92e07ce2019-05-31 16:00:03 -0500202 _list.append("{:5d} total events found\n".format(_total_errors))
Alexb151fbe2019-04-22 16:53:30 -0500203 if as_list:
204 return _list
205 else:
206 return "\n".join(_list)
Alex3ebc5632019-04-18 16:47:18 -0500207
Alexb151fbe2019-04-22 16:53:30 -0500208 def get_errors(self, as_list=False):
Alex92e07ce2019-05-31 16:00:03 -0500209 _list = ["# Events"]
Alexb151fbe2019-04-22 16:53:30 -0500210 # Detailed errors
211 if self.get_errors_total() > 0:
212 # create list of strings with error messages
Alexbab1efe2019-04-23 18:51:23 -0500213 for _idx in range(1, self._index):
Alexb151fbe2019-04-22 16:53:30 -0500214 _list.append(self._format_error(_idx))
215 _list.append("\n")
216 else:
Alex92e07ce2019-05-31 16:00:03 -0500217 _list.append("-> No events saved")
Alexb151fbe2019-04-22 16:53:30 -0500218
219 if as_list:
220 return _list
221 else:
222 return "\n".join(_list)