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