Bump pre-commit flake8 to 6.1.0
[integration/test.git] / csit / libraries / netvirt / 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/NETVIRT-972",
12         "id": "ConflictingModificationAppliedException",
13         "context": [
14             "Node was created by other transaction",
15             "Optimistic lock failed for path /(urn:opendaylight:inventory?revision=2013-08-19)nodes/node/node"
16             + "[{(urn:opendaylight:inventory?revision=2013-08-19)id=openflow",
17             "table/table[{(urn:opendaylight:flow:inventory?revision=2013-08-19)id=21}]/flow/flow"
18             + "[{(urn:opendaylight:flow:inventory?revision=2013-08-19)id=L3.",
19         ],
20     },
21     # oxygen
22     {
23         "issue": "https://jira.opendaylight.org/browse/NETVIRT-972",
24         "id": "ConflictingModificationAppliedException",
25         "context": [
26             "Node was created by other transaction",
27             "OptimisticLockFailedException: Optimistic lock failed."
28             "Conflicting modification for path /(urn:opendaylight:inventory?revision=2013-08-19)nodes/node/node"
29             + "[{(urn:opendaylight:inventory?revision=2013-08-19)id=",
30             "table/table[{(urn:opendaylight:flow:inventory?revision=2013-08-19)id=21}]/flow/flow"
31             + "[{(urn:opendaylight:flow:inventory?revision=2013-08-19)id=L3.",
32             ".21.",
33             ".42.",
34         ],
35     },
36     {
37         "issue": "https://jira.opendaylight.org/browse/NETVIRT-1135",
38         "id": "ConflictingModificationAppliedException",
39         "context": [
40             "Node was created by other transaction",
41             "Optimistic lock failed for path /(urn:opendaylight:inventory?revision=2013-08-19)nodes/node/node"
42             + "[{(urn:opendaylight:inventory?revision=2013-08-19)id=openflow:",
43         ],
44     },
45     # oxygen
46     {
47         "issue": "https://jira.opendaylight.org/browse/NETVIRT-1135",
48         "id": "ConflictingModificationAppliedException",
49         "context": [
50             "OptimisticLockFailedException: Optimistic lock failed."
51             "Conflicting modification for path /(urn:opendaylight:inventory?revision=2013-08-19)nodes/node/node"
52             + "[{(urn:opendaylight:inventory?revision=2013-08-19)id=openflow:",
53             "table/table[{(urn:opendaylight:flow:inventory?revision=2013-08-19)id=47}]/flow/flow"
54             + "[{(urn:opendaylight:flow:inventory?revision=2013-08-19)id=SNAT.",
55             ".47.",
56         ],
57     },
58     {
59         "issue": "https://jira.opendaylight.org/browse/NETVIRT-1136",
60         "id": "ConflictingModificationAppliedException",
61         "context": [
62             "Node was deleted by other transaction",
63             "Optimistic lock failed for path /(urn:opendaylight:netvirt:elan?revision=2015-06-02)elan-"
64             + "forwarding-tables/mac-table/mac-table[{(urn:opendaylight:netvirt:elan?revision=2015-06-02)"
65             + "elan-instance-name=",
66         ],
67     },
68     # oxygen version of NETVIRT-1136
69     {
70         "issue": "https://jira.opendaylight.org/browse/NETVIRT-1136",
71         "id": "ConflictingModificationAppliedException",
72         "context": [
73             "Node was deleted by other transaction",
74             "OptimisticLockFailedException: Optimistic lock failed.",
75             "Conflicting modification for path /(urn:opendaylight:netvirt:elan?revision=2015-06-02)elan-"
76             + "forwarding-tables/mac-table/mac-table[{(urn:opendaylight:netvirt:elan?revision=2015-06-02)"
77             + "elan-instance-name=",
78         ],
79     },
80     {
81         "issue": "https://jira.opendaylight.org/browse/NETVIRT-1260",
82         "id": "ConflictingModificationAppliedException",
83         "context": [
84             "Optimistic lock failed for path /(urn:ietf:params:xml:ns:yang:ietf-interfaces?revision=2014-05-08)"
85             + "interfaces/interface/interface[{(urn:ietf:params:xml:ns:yang:ietf-interfaces?revision=2014-05-08)name="
86         ],
87     },
88     {
89         "issue": "https://jira.opendaylight.org/browse/NETVIRT-1270",
90         "id": "ConflictingModificationAppliedException",
91         "context": [
92             "OptimisticLockFailedException",
93             "/(urn:opendaylight:netvirt:l3vpn?revision=2013-09-11)"
94             + "vpn-instance-op-data/vpn-instance-op-data-entry/vpn-instance-op-data-entry"
95             + "[{(urn:opendaylight:netvirt:l3vpn?revision=2013-09-11)vrf-id=",
96             "vrf-id=",
97             "/vpn-to-dpn-list/vpn-to-dpn-list",
98             "dpnId=",
99         ],
100     },
101     {
102         "issue": "https://jira.opendaylight.org/browse/NETVIRT-1270",
103         "id": "ExecutionException",
104         "context": [
105             "OptimisticLockFailedException: Optimistic lock failed",
106             "removeOrUpdateVpnToDpnList: Error removing from dpnToVpnList for vpn ",
107         ],
108     },
109     {
110         "issue": "https://jira.opendaylight.org/browse/NETVIRT-1270",
111         "id": "OptimisticLockFailedException",
112         "context": [
113             "OptimisticLockFailedException",
114             "VpnInterfaceOpListener",
115             "Direct Exception (not failed Future) when executing job, won't even retry: JobEntry{key='VPNINTERFACE-",
116             "vpn-instance-op-data/vpn-instance-op-data-entry/vpn-instance-op-data-entry"
117             + "[{(urn:opendaylight:netvirt:l3vpn?revision=2013-09-11)vrf-id=",
118             "vrf-id=",
119             "/vpn-to-dpn-list/vpn-to-dpn-list",
120             "dpnId=",
121         ],
122     },
123     {
124         "issue": "https://jira.opendaylight.org/browse/NETVIRT-1281",
125         "id": "OptimisticLockFailedException",
126         "context": [
127             "OptimisticLockFailedException: Optimistic lock failed.",
128             "ConflictingModificationAppliedException: Node children was modified by other transaction",
129             "Direct Exception (not failed Future) when executing job, won't even retry: JobEntry{key='VPNINTERFACE-",
130         ],
131     },
132     {
133         "issue": "https://jira.opendaylight.org/browse/NETVIRT-1304",
134         "id": "ModifiedNodeDoesNotExistException",
135         "context": [
136             "ModifiedNodeDoesNotExistException",
137             "/(urn:opendaylight:netvirt:fibmanager?revision=2015-03-30)fibEntries/"
138             + "vrfTables/vrfTables[{(urn:opendaylight:netvirt:fibmanager?revision=2015-03-30)routeDistinguisher=",
139         ],
140     },
141     {
142         "issue": "https://jira.opendaylight.org/browse/NETVIRT-1304",
143         "id": "TransactionCommitFailedException",
144         "context": [
145             "TransactionCommitFailedException",
146             "/(urn:opendaylight:netvirt:fibmanager?revision=2015-03-30)fibEntries/"
147             + "vrfTables/vrfTables[{(urn:opendaylight:netvirt:fibmanager?revision=2015-03-30)routeDistinguisher=",
148         ],
149     },
150     {
151         "issue": "https://jira.opendaylight.org/browse/NETVIRT-1427",
152         "id": "ModifiedNodeDoesNotExistException",
153         "context": [
154             "/(urn:huawei:params:xml:ns:yang:l3vpn?revision=2014-08-15)vpn-interfaces/vpn-interface/vpn-interface"
155             + "[{(urn:huawei:params:xml:ns:yang:l3vpn?revision=2014-08-15)name=",
156             "AugmentationIdentifier{childNames=[(urn:opendaylight:netvirt:l3vpn?revision=2013-09-11)adjacency]}",
157         ],
158     },
159     {
160         "issue": "https://jira.opendaylight.org/browse/NETVIRT-1428",
161         "id": "ModifiedNodeDoesNotExistException",
162         "context": [
163             "/(urn:huawei:params:xml:ns:yang:l3vpn?revision=2014-08-15)vpn-interfaces/vpn-interface/vpn-interface"
164             + "[{(urn:huawei:params:xml:ns:yang:l3vpn?revision=2014-08-15)name="
165         ],
166     },
167     {
168         "issue": "https://jira.opendaylight.org/browse/NEUTRON-157",
169         "id": "ConflictingModificationAppliedException",
170         "context": [
171             "Optimistic lock failed for path /(urn:opendaylight:neutron?revision=2015-07-12)"
172             + "neutron/networks/network/network[{(urn:opendaylight:neutron?revision=2015-07-12)uuid=",
173             "Conflicting modification for path /(urn:opendaylight:neutron?revision=2015-07-12)"
174             + "neutron/networks/network/network[{(urn:opendaylight:neutron?revision=2015-07-12)uuid=",
175         ],
176     },
177     {
178         "issue": "https://jira.opendaylight.org/browse/NEUTRON-157",
179         "id": "OptimisticLockFailedException",
180         "context": [
181             "Got OptimisticLockFailedException",
182             "AbstractTranscriberInterface",
183         ],
184     },
185     {
186         "issue": "https://jira.opendaylight.org/browse/NEUTRON-157",
187         "id": "ConflictingModificationAppliedException",
188         "context": [
189             "Optimistic lock failed for path /(urn:opendaylight:neutron?revision=2015-07-12)neutron"
190         ],
191     },
192     # oxygen
193     {
194         "issue": "https://jira.opendaylight.org/browse/NEUTRON-157",
195         "id": "ConflictingModificationAppliedException",
196         "context": [
197             "OptimisticLockFailedException: Optimistic lock failed.",
198             "Conflicting modification for path /(urn:opendaylight:neutron?revision=2015-07-12)"
199             + "neutron/networks/network/network[{(urn:opendaylight:neutron?revision=2015-07-12)uuid=",
200         ],
201     },
202     {
203         "issue": "https://jira.opendaylight.org/browse/OPNFLWPLUG-917",
204         "id": "IllegalStateException",
205         "context": [
206             "java.lang.IllegalStateException: Deserializer for key: msgVersion: 4 objectClass: "
207             + "org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.oxm.rev150225.match.entries.grouping.MatchEntry "
208             + "msgType: 1 oxm_field: 33 experimenterID: null was not found "
209             + "- please verify that all needed deserializers ale loaded correctly"
210         ],
211     },
212     {
213         "issue": "https://jira.opendaylight.org/browse/NETVIRT-1640",
214         "id": "ElasticsearchAppender",
215         "context": [
216             "Can't append into Elasticsearch",
217             "org.apache.karaf.decanter.appender.elasticsearch - 1.0.0",
218         ],
219     },
220 ]
221
222 _re_ts = re.compile(r"^[0-9]{4}(-[0-9]{2}){2}T([0-9]{2}:){2}[0-9]{2},[0-9]{3}")
223 _re_ts_we = re.compile(
224     r"^[0-9]{4}(-[0-9]{2}){2}T([0-9]{2}:){2}[0-9]{2},[0-9]{3}( \| ERROR \| | \| WARN  \| )"
225 )
226 _re_ex = re.compile(r"(?i)exception")
227 _ex_map = collections.OrderedDict()
228 _ts_list = []
229 _fail = []
230
231
232 def get_exceptions(lines):
233     """
234     Create a map of exceptions that also has a list of warnings and errors preceeding
235     the exception to use as context.
236
237     The lines are parsed to create a list where all lines related to a timestamp
238     are aggregated. Timestamped lines with exception (case insensitive) are copied
239     to the exception map keyed to the index of the timestamp line. Each exception value
240     also has a list containing WARN and ERROR lines proceeding the exception.
241
242     :param list lines:
243     :return OrderedDict _ex_map: map of exceptions
244     """
245     global _ex_map
246     _ex_map = collections.OrderedDict()
247     global _ts_list
248     _ts_list = []
249     cur_list = []
250     warnerr_deq = collections.deque(maxlen=5)
251
252     for line in lines:
253         ts = _re_ts.search(line)
254
255         # Check if this is the start or continuation of a timestamp line
256         if ts:
257             cur_list = [line]
258             _ts_list.append(cur_list)
259             ts_we = _re_ts_we.search(line)
260             # Track WARN and ERROR lines
261             if ts_we:
262                 warn_err_index = len(_ts_list) - 1
263                 warnerr_deq.append(warn_err_index)
264         # Append to current timestamp line since this is not a timestamp line
265         else:
266             cur_list.append(line)
267
268         # Add the timestamp line to the exception map if it has an exception
269         ex = _re_ex.search(line)
270         if ex:
271             index = len(_ts_list) - 1
272             if index not in _ex_map:
273                 _ex_map[index] = {"warnerr_list": list(warnerr_deq), "lines": cur_list}
274                 warnerr_deq.clear()  # reset the deque to only track new ERROR and WARN lines
275
276     return _ex_map
277
278
279 def check_exceptions():
280     """
281     Return a list of exceptions that were not in the whitelist.
282
283     Each exception found is compared against all the patterns
284     in the whitelist.
285
286     :return list _fail: list of exceptions not in the whitelist
287     """
288     global _fail
289     _fail = []
290     _match = []
291     for ex_idx, ex in _ex_map.items():
292         ex_str = "__".join(ex.get("lines"))
293         for whitelist in _whitelist:
294             # skip the current whitelist exception if not in the current exception
295             if whitelist.get("id") not in ex_str:
296                 continue
297             whitelist_contexts = whitelist.get("context")
298             num_context_matches = 0
299             for whitelist_context in whitelist_contexts:
300                 for exwe_index in reversed(ex.get("warnerr_list")):
301                     exwe_str = "__".join(_ts_list[exwe_index])
302                     if whitelist_context in exwe_str:
303                         num_context_matches += 1
304             # Mark this exception as a known issue if all the context's matched
305             if num_context_matches >= len(whitelist_contexts):
306                 ex["issue"] = whitelist.get("issue")
307                 _match.append(ex)
308                 logging.info("known exception was seen: {}".format(ex["issue"]))
309                 break
310         # A new exception when it isn't marked with a known issue.
311         if "issue" not in ex:
312             _fail.append(ex)
313     return _fail, _match
314
315
316 def verify_exceptions(lines):
317     """
318     Return a list of exceptions not in the whitelist for the given lines.
319
320     :param list lines: list of lines from a log
321     :return list, list: one list of exceptions not in the whitelist, and a second with matching issues
322     """
323     if not lines:
324         return
325     get_exceptions(lines)
326     return check_exceptions()
327
328
329 def write_exceptions_map_to_file(testname, filename, mode="a+"):
330     """
331     Write the exceptions map to a file under the testname header. The output
332     will include all lines in the exception itself as well as any previous
333     contextual warning or error lines. The output will be appended or overwritten
334     depending on the mode parameter. It is assumed that the caller has called
335     verify_exceptions() earlier to populate the exceptions map, otherwise only
336     the testname and header will be printed to the file.
337
338     :param str testname: The name of the test
339     :param str filename: The file to open for writing
340     :param str mode: Append (a+) or overwrite (w+)
341     """
342     try:
343         os.makedirs(os.path.dirname(filename))
344     except OSError as exception:
345         if exception.errno != errno.EEXIST:
346             raise
347
348     with open(filename, mode) as fp:
349         fp.write("{}\n".format("=" * 60))
350         fp.write("Starting test: {}\n".format(testname))
351         for ex_idx, ex in _ex_map.items():
352             fp.write("{}\n".format("-" * 40))
353             if "issue" in ex:
354                 fp.write("Exception was matched to: {}\n".format(ex.get("issue")))
355             else:
356                 fp.write("Exception is new\n")
357             for exwe_index in ex.get("warnerr_list")[:-1]:
358                 for line in _ts_list[exwe_index]:
359                     fp.write("{}\n".format(line))
360             fp.writelines(ex.get("lines"))
361             fp.write("\n")