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