a00cffea4f51644dfd0cc339511d38bcf8b6c821
[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/NEUTRON-157",
105      "id": "ConflictingModificationAppliedException",
106      "context": [
107          "Optimistic lock failed for path /(urn:opendaylight:neutron?revision=2015-07-12)" +
108          "neutron/networks/network/network[{(urn:opendaylight:neutron?revision=2015-07-12)uuid=",
109          "Conflicting modification for path /(urn:opendaylight:neutron?revision=2015-07-12)" +
110          "neutron/networks/network/network[{(urn:opendaylight:neutron?revision=2015-07-12)uuid="
111      ]},
112     {"issue": "https://jira.opendaylight.org/browse/NEUTRON-157",
113      "id": "OptimisticLockFailedException",
114      "context": [
115          "Got OptimisticLockFailedException",
116          "AbstractTranscriberInterface"
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)neutron"
122      ]},
123     # oxygen
124     {"issue": "https://jira.opendaylight.org/browse/NEUTRON-157",
125      "id": "ConflictingModificationAppliedException",
126      "context": [
127          "OptimisticLockFailedException: Optimistic lock failed.",
128          "Conflicting modification for path /(urn:opendaylight:neutron?revision=2015-07-12)" +
129          "neutron/networks/network/network[{(urn:opendaylight:neutron?revision=2015-07-12)uuid=",
130      ]},
131     {"issue": "https://jira.opendaylight.org/browse/OPNFLWPLUG-917",
132      "id": "IllegalStateException",
133      "context": [
134          "java.lang.IllegalStateException: Deserializer for key: msgVersion: 4 objectClass: " +
135          "org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.oxm.rev150225.match.entries.grouping.MatchEntry " +
136          "msgType: 1 oxm_field: 33 experimenterID: null was not found " +
137          "- please verify that all needed deserializers ale loaded correctly"
138      ]}
139 ]
140
141 _re_ts = re.compile(r"^[0-9]{4}(-[0-9]{2}){2}T([0-9]{2}:){2}[0-9]{2},[0-9]{3}")
142 _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  \| )")
143 _re_ex = re.compile(r"(?i)exception")
144 _ex_map = collections.OrderedDict()
145 _ts_list = []
146 _fail = []
147
148
149 def get_exceptions(lines):
150     """
151     Create a map of exceptions that also has a list of warnings and errors preceeding
152     the exception to use as context.
153
154     The lines are parsed to create a list where all lines related to a timestamp
155     are aggregated. Timestamped lines with exception (case insensitive) are copied
156     to the exception map keyed to the index of the timestamp line. Each exception value
157     also has a list containing WARN and ERROR lines proceeding the exception.
158
159     :param list lines:
160     :return OrderedDict _ex_map: map of exceptions
161     """
162     global _ex_map
163     _ex_map = collections.OrderedDict()
164     global _ts_list
165     _ts_list = []
166     cur_list = []
167     warnerr_deq = collections.deque(maxlen=5)
168
169     for line in lines:
170         ts = _re_ts.search(line)
171
172         # Check if this is the start or continuation of a timestamp line
173         if ts:
174             cur_list = [line]
175             _ts_list.append(cur_list)
176             ts_we = _re_ts_we.search(line)
177             # Track WARN and ERROR lines
178             if ts_we:
179                 warn_err_index = len(_ts_list) - 1
180                 warnerr_deq.append(warn_err_index)
181         # Append to current timestamp line since this is not a timestamp line
182         else:
183             cur_list.append(line)
184
185         # Add the timestamp line to the exception map if it has an exception
186         ex = _re_ex.search(line)
187         if ex:
188             index = len(_ts_list) - 1
189             if index not in _ex_map:
190                 _ex_map[index] = {"warnerr_list": list(warnerr_deq), 'lines': cur_list}
191                 warnerr_deq.clear()  # reset the deque to only track new ERROR and WARN lines
192
193     return _ex_map
194
195
196 def check_exceptions():
197     """
198     Return a list of exceptions that were not in the whitelist.
199
200     Each exception found is compared against all the patterns
201     in the whitelist.
202
203     :return list _fail: list of exceptions not in the whitelist
204     """
205     global _fail
206     _fail = []
207     _match = []
208     for ex_idx, ex in _ex_map.items():
209         ex_str = "__".join(ex.get("lines"))
210         for whitelist in _whitelist:
211             # skip the current whitelist exception if not in the current exception
212             if whitelist.get("id") not in ex_str:
213                 continue
214             whitelist_contexts = whitelist.get("context")
215             num_context_matches = 0
216             for whitelist_context in whitelist_contexts:
217                 for exwe_index in reversed(ex.get("warnerr_list")):
218                     exwe_str = "__".join(_ts_list[exwe_index])
219                     if whitelist_context in exwe_str:
220                         num_context_matches += 1
221             # Mark this exception as a known issue if all the context's matched
222             if num_context_matches >= len(whitelist_contexts):
223                 ex["issue"] = whitelist.get("issue")
224                 _match.append(ex)
225                 logging.info("known exception was seen: {}".format(ex["issue"]))
226                 break
227         # A new exception when it isn't marked with a known issue.
228         if "issue" not in ex:
229             _fail.append(ex)
230     return _fail, _match
231
232
233 def verify_exceptions(lines):
234     """
235     Return a list of exceptions not in the whitelist for the given lines.
236
237     :param list lines: list of lines from a log
238     :return list, list: one list of exceptions not in the whitelist, and a second with matching issues
239     """
240     if not lines:
241         return
242     get_exceptions(lines)
243     return check_exceptions()
244
245
246 def write_exceptions_map_to_file(testname, filename, mode="a+"):
247     """
248     Write the exceptions map to a file under the testname header. The output
249     will include all lines in the exception itself as well as any previous
250     contextual warning or error lines. The output will be appended or overwritten
251     depending on the mode parameter. It is assumed that the caller has called
252     verify_exceptions() earlier to populate the exceptions map, otherwise only
253     the testname and header will be printed to the file.
254
255     :param str testname: The name of the test
256     :param str filename: The file to open for writing
257     :param str mode: Append (a+) or overwrite (w+)
258     """
259     try:
260         os.makedirs(os.path.dirname(filename))
261     except OSError as exception:
262         if exception.errno != errno.EEXIST:
263             raise
264
265     with open(filename, mode) as fp:
266         fp.write("{}\n".format("=" * 60))
267         fp.write("Starting test: {}\n".format(testname))
268         for ex_idx, ex in _ex_map.items():
269             fp.write("{}\n".format("-" * 40))
270             if "issue" in ex:
271                 fp.write("Exception was matched to: {}\n".format(ex.get("issue")))
272             else:
273                 fp.write("Exception is new\n")
274             for exwe_index in ex.get("warnerr_list")[:-1]:
275                 for line in _ts_list[exwe_index]:
276                     fp.write("{}\n".format(line))
277             fp.writelines(ex.get("lines"))
278             fp.write("\n")