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