2 import xml.etree.cElementTree as ET
3 from subprocess import Popen
8 from odltools.ovs import flows
10 logger = logging.getLogger("csit.robotfiles")
14 OUTDIR = "/tmp/robotjob"
16 DUMP_FLOWS = "sudo ovs-ofctl dump-flows br-int -OOpenFlow13"
19 def __init__(self, infile, outdir):
21 self.outdir = RobotFiles.TMP
24 self.datafilepath = infile
26 self.re_normalize_text = re.compile(r"( \n)|(\[A\[C.*)")
27 # uri=restconf/config/interface-service-bindings:service-bindings, headers=None json=None</msg>
28 self.re_uri = re.compile(r"uri=(?P<uri>.*),")
29 logger.info("RobotFiles created")
32 infile = self.datafilepath
33 basename = os.path.splitext(os.path.basename(self.datafilepath))[0]
34 self.datafilepath = "{}/{}".format(self.outdir, basename)
35 Popen("gunzip -cfk {} > {}".format(infile, self.datafilepath), shell=True).wait()
36 logger.info("gunzip -cfk %s > %s", infile, self.datafilepath)
38 def mkdir(self, path):
42 if not os.path.isdir(path):
46 self.mkdir(self.outdir)
47 logger.info("mk_outdir: %s created", self.outdir)
49 def read_chunks(self, fp):
51 data = fp.read(RobotFiles.CHUNK_SIZE)
56 def parse_data_file(self):
57 re_st = re.compile(r"dump-flows")
59 with open(self.datafilepath, 'rb') as fp:
60 for chunk in self.read_chunks(fp):
61 for m in re_st.finditer(chunk):
62 logger.info("%02d-%02d: %s", m.start(), m.end(), m.group(0))
64 logger.info("total matches: %d", cnt)
85 def normalize(self, intext):
86 outtext = self.re_normalize_text.sub("", intext)
89 def print_config(self):
90 logger.info("datafilepath: %s, outdir: %s", self.datafilepath, self.outdir)
92 # scan until test id= is seen. This indicates the start of a new test -> state=test
93 # - scan until Get Test Teardown Debugs -> state=debugs
94 # - scan until Get DumpFlows And Ovsconfig -> state=nodes
95 # - scan until Get Model Dump -> state=models
96 def process_element(self, state, event, element):
99 attribs = element.attrib
100 if logger.isEnabledFor(logging.DEBUG) and text is not None and attribs:
101 logger.debug("process_element: %s - %s - %s - %s - %s - %s", state.state, state.command, event, tag, (text is not None), attribs)
103 if element.tag == "test":
104 state.pdata['nodes'] = state.nodes
105 state.pdata['models'] = state.models
106 self.pdata[state.test_id] = state.pdata
109 elif element.tag != "msg":
112 if event == "start" and state.state == "init":
113 # <test id="s1-s1-s1-t1" name="Create VLAN Network (l2_network_1)">
114 # <test id="s1-t1" name="Create VLAN Network net_1">
115 if element.tag == "test":
116 state.test_id = element.get("id")
117 state.pdata["name"] = element.get("name")
120 elif event == "start" and state.state == "test":
121 # <kw type="teardown" name="Get Test Teardown Debugs" library="OpenStackOperations">
122 if element.tag == "kw" and element.get("name") == "Get Test Teardown Debugs":
123 state.state = "debugs"
125 elif event == "start" and state.state == "debugs":
126 # <arg>Get DumpFlows And Ovsconfig</arg>
127 if element.tag == "kw" and element.get("name") == "Get DumpFlows And Ovsconfig":
128 state.state = "nodes"
130 # <arg>Get Model Dump</arg>
131 if element.tag == "kw" and element.get("name") == "Get Model Dump":
132 state.state = "models"
134 elif event == "start" and state.state == "nodes":
135 # <arg>${OS_CONTROL_NODE_IP}</arg>
136 if element.tag == "arg" and element.text is not None and "${OS_" in element.text:
137 state.node = element.text[element.text.find("{") + 1:element.text.find("}")]
138 state.nodes[state.node] = {}
139 state.state = "nodes2"
141 elif event == "start" and state.state == "nodes2":
142 # <kw name="Write Commands Until Expected Prompt" library="Utils">
143 if element.tag == "kw" and element.get("name") == "Write Commands Until Expected Prompt":
146 elif event == "start" and state.state == "kw":
147 # <arg>ip -o link</arg>
148 if element.tag == "arg" and element.text is not None:
149 state.command = element.text
150 state.state = "command"
151 # only use the string before the ${...} since we don't know the ...
152 # <arg>sudo ip netns exec ${line} ip -o link</arg>
153 command_split = state.command.split("$")
154 if len(command_split) > 1:
155 state.command = command_split[0]
156 elif event == "start" and state.state == "command":
157 # <msg timestamp="20170414 07:31:21.769" level="INFO">ip -o link</msg>
158 if element.tag == "msg" and element.text is not None:
159 text = self.normalize(element.text)
160 if text.find(state.command) != -1:
161 # <msg timestamp="20170414 07:31:34.425" level="INFO">sudo ip netns exec
162 # [jenkins@rele ^Mng-36618-350-devstack-newton-0 ~]> ip -o link</msg>
163 if text.find("jenkins") != -1:
164 state.state = "nodes2"
169 elif state.state == "msg":
170 # <msg timestamp="20170414 07:31:21.786" level="INFO">
171 if element.tag == "msg" and element.text is not None:
172 state.nodes[state.node][state.command] = element.text
173 # are we at the end of the debugs for the node?
174 # this command is the last one
175 if state.command == "sudo ovs-ofctl dump-group-stats br-int -OOpenFlow13":
176 state.state = "debugs"
179 # still more debugs for this node
180 state.state = "nodes2"
181 elif state.state == "models2":
182 # <msg timestamp="20170813 08:20:11.806" level="INFO">Get Request using : alias=model_dump_session,
183 # uri=restconf/config/interface-service-bindings:service-bindings, headers=None json=None</msg>
184 if element.tag == "msg" and element.text is not None and element.text.find("uri") != -1:
185 uri = self.re_uri.search(element.text)
186 if uri is not None and "uri" in uri.group():
188 state.command = uri.group("uri")
189 elif event == "start" and state.state == "models":
190 # <kw type="foritem" name="${model} = config/neutronvpn:router-interfaces-map">
191 if element.tag == "kw" and "name" in element.attrib:
192 name_split = element.attrib["name"].split("${model} = ", 1)
194 if len(name_split) == 2:
195 model = name_split[1]
196 if model is not None:
198 state.command = model
199 elif state.state == "uri":
200 if element.tag == "msg" and element.text is not None and element.text.find("pretty_output") != -1:
202 # do not clear the state.command
203 elif state.state == "dump":
204 if element.tag == "msg" and element.text is not None:
205 state.models[state.command] = element.text
206 # if state.command == "restconf/operational/rendered-service-path:rendered-service-path":
207 if state.command == "restconf/operational/opendaylight-inventory:nodes":
211 state.state = "models"
214 def parse_xml_data_file(self):
216 with open(self.datafilepath, 'rb') as fp:
217 iterparser = ET.iterparse(fp, events=("start", "end"))
218 _, root = iterparser.next()
219 for event, element in iterparser:
220 self.process_element(state, event, element)
222 # debugging code to stop after the named test case is processed
223 # if "s1-t1" in self.pdata:
227 def write_pdata(self):
228 for tindex, (testid, test) in enumerate(self.pdata.items()):
229 tdir = self.outdir + "/" + testid + "_" + test["name"].replace(" ", "_")
231 for nindex, (nodeid, node) in enumerate(test['nodes'].items()):
232 ndir = tdir + "/" + nodeid
234 for cindex, (cmdid, cmd) in enumerate(node.items()):
235 filename = ndir + "/" + self.fix_command_names(cmdid) + ".txt"
236 with open(filename, 'w') as fp:
239 mdir = tdir + "/models"
241 for mindex, (model, mdata) in enumerate(test['models'].items()):
242 filename = mdir + "/" + self.fix_model_name(model) + ".json"
243 with open(filename, 'w') as fp:
244 if mdata is not None:
247 def write_debug_pdata(self):
248 for tindex, (testid, test) in enumerate(self.pdata.items()):
249 tdir = self.outdir + "/" + testid + "_" + test["name"].replace(" ", "_")
250 for nindex, (nodeid, node) in enumerate(test['nodes'].items()):
251 ndir = tdir + "/" + nodeid
252 if RobotFiles.DUMP_FLOWS not in node:
254 filename = ndir + "/" + self.fix_command_names(RobotFiles.DUMP_FLOWS)
255 logger.info("Processing: %s", filename)
256 filename = filename + ".f.txt"
257 dump_flows = node[RobotFiles.DUMP_FLOWS]
258 fls = flows.Flows(dump_flows)
259 fls.write_fdata(filename)
261 def fix_command_names(self, cmd):
262 return cmd.replace(" ", "_")
264 def fix_model_name(self, model):
265 name = model.replace("/", "___")
266 name = name.replace(":", "__")
271 robotfile = RobotFiles(args.infile, args.outdir)
272 robotfile.print_config()
273 robotfile.mk_outdir()
276 robotfile.print_config()
277 robotfile.parse_xml_data_file()
278 robotfile.write_pdata()
280 robotfile.write_debug_pdata()