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