Migrate GoBgpLib.robot
[integration/test.git] / csit / libraries / excepts.py
1 import collections
2 import errno
3 import logging
4 import os
5 import re
6
7 # Make sure to have unique matches in different lines
8 # Order the list in alphabetical order based on the "issue" key
9 _whitelist = [
10     {
11         "issue": "https://jira.opendaylight.org/browse/OPNFLWPLUG-917",
12         "id": "IllegalStateException",
13         "context": [
14             "java.lang.IllegalStateException: Deserializer for key: msgVersion: 4 objectClass: "
15             + "org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.oxm.rev150225.match.entries.grouping.MatchEntry "
16             + "msgType: 1 oxm_field: 33 experimenterID: null was not found "
17             + "- please verify that all needed deserializers ale loaded correctly"
18         ],
19     },
20 ]
21
22 _re_ts = re.compile(r"^[0-9]{4}(-[0-9]{2}){2}T([0-9]{2}:){2}[0-9]{2},[0-9]{3}")
23 _re_ts_we = re.compile(
24     r"^[0-9]{4}(-[0-9]{2}){2}T([0-9]{2}:){2}[0-9]{2},[0-9]{3}( \| ERROR \| | \| WARN  \| )"
25 )
26 _re_ex = re.compile(r"(?i)exception")
27 _ex_map = collections.OrderedDict()
28 _ts_list = []
29 _fail = []
30
31
32 def get_exceptions(lines):
33     """
34     Create a map of exceptions that also has a list of warnings and errors preceeding
35     the exception to use as context.
36
37     The lines are parsed to create a list where all lines related to a timestamp
38     are aggregated. Timestamped lines with exception (case insensitive) are copied
39     to the exception map keyed to the index of the timestamp line. Each exception value
40     also has a list containing WARN and ERROR lines proceeding the exception.
41
42     :param list lines:
43     :return OrderedDict _ex_map: map of exceptions
44     """
45     global _ex_map
46     _ex_map = collections.OrderedDict()
47     global _ts_list
48     _ts_list = []
49     cur_list = []
50     warnerr_deq = collections.deque(maxlen=5)
51
52     for line in lines:
53         ts = _re_ts.search(line)
54
55         # Check if this is the start or continuation of a timestamp line
56         if ts:
57             cur_list = [line]
58             _ts_list.append(cur_list)
59             ts_we = _re_ts_we.search(line)
60             # Track WARN and ERROR lines
61             if ts_we:
62                 warn_err_index = len(_ts_list) - 1
63                 warnerr_deq.append(warn_err_index)
64         # Append to current timestamp line since this is not a timestamp line
65         else:
66             cur_list.append(line)
67
68         # Add the timestamp line to the exception map if it has an exception
69         ex = _re_ex.search(line)
70         if ex:
71             index = len(_ts_list) - 1
72             if index not in _ex_map:
73                 _ex_map[index] = {"warnerr_list": list(warnerr_deq), "lines": cur_list}
74                 warnerr_deq.clear()  # reset the deque to only track new ERROR and WARN lines
75
76     return _ex_map
77
78
79 def check_exceptions():
80     """
81     Return a list of exceptions that were not in the whitelist.
82
83     Each exception found is compared against all the patterns
84     in the whitelist.
85
86     :return list _fail: list of exceptions not in the whitelist
87     """
88     global _fail
89     _fail = []
90     _match = []
91     for ex_idx, ex in _ex_map.items():
92         ex_str = "__".join(ex.get("lines"))
93         for whitelist in _whitelist:
94             # skip the current whitelist exception if not in the current exception
95             if whitelist.get("id") not in ex_str:
96                 continue
97             whitelist_contexts = whitelist.get("context")
98             num_context_matches = 0
99             for whitelist_context in whitelist_contexts:
100                 for exwe_index in reversed(ex.get("warnerr_list")):
101                     exwe_str = "__".join(_ts_list[exwe_index])
102                     if whitelist_context in exwe_str:
103                         num_context_matches += 1
104             # Mark this exception as a known issue if all the context's matched
105             if num_context_matches >= len(whitelist_contexts):
106                 ex["issue"] = whitelist.get("issue")
107                 _match.append(ex)
108                 logging.info("known exception was seen: {}".format(ex["issue"]))
109                 break
110         # A new exception when it isn't marked with a known issue.
111         if "issue" not in ex:
112             _fail.append(ex)
113     return _fail, _match
114
115
116 def verify_exceptions(lines):
117     """
118     Return a list of exceptions not in the whitelist for the given lines.
119
120     :param list lines: list of lines from a log
121     :return list, list: one list of exceptions not in the whitelist, and a second with matching issues
122     """
123     if not lines:
124         return
125     get_exceptions(lines)
126     return check_exceptions()
127
128
129 def write_exceptions_map_to_file(testname, filename, mode="a+"):
130     """
131     Write the exceptions map to a file under the testname header. The output
132     will include all lines in the exception itself as well as any previous
133     contextual warning or error lines. The output will be appended or overwritten
134     depending on the mode parameter. It is assumed that the caller has called
135     verify_exceptions() earlier to populate the exceptions map, otherwise only
136     the testname and header will be printed to the file.
137
138     :param str testname: The name of the test
139     :param str filename: The file to open for writing
140     :param str mode: Append (a+) or overwrite (w+)
141     """
142     try:
143         os.makedirs(os.path.dirname(filename))
144     except OSError as exception:
145         if exception.errno != errno.EEXIST:
146             raise
147
148     with open(filename, mode) as fp:
149         fp.write("{}\n".format("=" * 60))
150         fp.write("Starting test: {}\n".format(testname))
151         for ex_idx, ex in _ex_map.items():
152             fp.write("{}\n".format("-" * 40))
153             if "issue" in ex:
154                 fp.write("Exception was matched to: {}\n".format(ex.get("issue")))
155             else:
156                 fp.write("Exception is new\n")
157             for exwe_index in ex.get("warnerr_list")[:-1]:
158                 for line in _ts_list[exwe_index]:
159                     fp.write("{}\n".format(line))
160             fp.writelines(ex.get("lines"))
161             fp.write("\n")