2 # NEPI, a framework to manage network experiments
3 # Copyright (C) 2013 INRIA
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License version 2 as
7 # published by the Free Software Foundation;
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
14 # You should have received a copy of the GNU General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
17 # Author: Alina Quereilhac <alina.quereilhac@inria.fr>
19 from __future__ import print_function
21 from nepi.util.netgraph import NetGraph, TopologyType
22 from nepi.util.timefuncs import stformat, tsformat
24 from xml.dom import minidom
38 # xxx old py2 code had a hack, that had 'latin1' hardcoded
39 # as the encoding for 8-byte strings
40 # this is very wrong; I keep it for now
41 # but will probably remove it altogether some day
43 """xml encoder for python2"""
44 if isinstance(s, str):
45 rv = s.decode("latin1")
46 if isinstance(s, datetime.datetime):
48 elif not isinstance(s, unicode):
52 return rv.replace(u'\x00',u'�')
54 # use sys.getdefaultencoding() to decode bytes into string
56 """xml encoder for python3"""
57 if isinstance(s, datetime.datetime):
59 elif isinstance(s, bytes):
60 rv = s.decode(sys.getdefaultencoding())
63 return rv.replace('\x00', '�')
65 def xmldecode(s, cast = str):
69 ret = s.replace(u'�', u'\x00').encode("ascii")
71 ret = s.replace('�', '\x00')
76 if isinstance(value, bool):
78 if isinstance(value, int):
80 if isinstance(value, float):
85 def to_type(type, value):
92 return value == "True"
98 class ECXMLParser(object):
101 doc = minidom.Document()
103 self._ec_to_xml(doc, ec)
106 xml = doc.toprettyxml(indent=" ", encoding="UTF-8")
108 print("Oops: generating XML from %s" % (data,), file=sys.stderr)
113 def _ec_to_xml(self, doc, ec):
114 ecnode = doc.createElement("experiment")
115 ecnode.setAttribute("exp_id", xmlencode(ec.exp_id))
116 ecnode.setAttribute("run_id", xmlencode(ec.run_id))
117 ecnode.setAttribute("nthreads", xmlencode(ec.nthreads))
118 ecnode.setAttribute("local_dir", xmlencode(ec.local_dir))
119 doc.appendChild(ecnode)
121 if ec.netgraph != None:
122 self._netgraph_to_xml(doc, ecnode, ec)
124 rmsnode = doc.createElement("rms")
125 ecnode.appendChild(rmsnode)
127 for guid, rm in ec._resources.items():
128 self._rm_to_xml(doc, rmsnode, ec, guid, rm)
132 def _netgraph_to_xml(self, doc, ecnode, ec):
133 ngnode = doc.createElement("topology")
134 ngnode.setAttribute("topo-type", xmlencode(ec.netgraph.topo_type))
135 ecnode.appendChild(ngnode)
137 self. _netgraph_nodes_to_xml(doc, ngnode, ec)
138 self. _netgraph_edges_to_xml(doc, ngnode, ec)
140 def _netgraph_nodes_to_xml(self, doc, ngnode, ec):
141 ngnsnode = doc.createElement("nodes")
142 ngnode.appendChild(ngnsnode)
144 for nid in ec.netgraph.nodes():
145 ngnnode = doc.createElement("node")
146 ngnnode.setAttribute("nid", xmlencode(nid))
147 ngnnode.setAttribute("nid-type", from_type(nid))
148 ngnsnode.appendChild(ngnnode)
150 # Mark ources and targets
151 if ec.netgraph.is_source(nid):
152 ngnnode.setAttribute("source", xmlencode(True))
154 if ec.netgraph.is_target(nid):
155 ngnnode.setAttribute("target", xmlencode(True))
158 annosnode = doc.createElement("node-annotations")
159 add_annotations = False
160 for name in ec.netgraph.node_annotations(nid):
161 add_annotations = True
162 value = ec.netgraph.node_annotation(nid, name)
163 annonode = doc.createElement("node-annotation")
164 annonode.setAttribute("name", xmlencode(name))
165 annonode.setAttribute("value", xmlencode(value))
166 annonode.setAttribute("type", from_type(value))
167 annosnode.appendChild(annonode)
170 ngnnode.appendChild(annosnode)
172 def _netgraph_edges_to_xml(self, doc, ngnode, ec):
173 ngesnode = doc.createElement("edges")
174 ngnode.appendChild(ngesnode)
176 for nid1, nid2 in ec.netgraph.edges():
177 ngenode = doc.createElement("edge")
178 ngenode.setAttribute("nid1", xmlencode(nid1))
179 ngenode.setAttribute("nid1-type", from_type(nid1))
180 ngenode.setAttribute("nid2", xmlencode(nid2))
181 ngenode.setAttribute("nid2-type", from_type(nid2))
182 ngesnode.appendChild(ngenode)
185 annosnode = doc.createElement("edge-annotations")
186 add_annotations = False
187 for name in ec.netgraph.edge_annotations(nid1, nid2):
188 add_annotations = True
189 value = ec.netgraph.edge_annotation(nid1, nid2, name)
190 annonode = doc.createElement("edge-annotation")
191 annonode.setAttribute("name", xmlencode(name))
192 annonode.setAttribute("value", xmlencode(value))
193 annonode.setAttribute("type", from_type(value))
194 annosnode.appendChild(annonode)
197 ngenode.appendChild(annosnode)
199 def _rm_to_xml(self, doc, rmsnode, ec, guid, rm):
200 rmnode = doc.createElement("rm")
201 rmnode.setAttribute("guid", xmlencode(guid))
202 rmnode.setAttribute("rtype", xmlencode(rm._rtype))
203 rmnode.setAttribute("state", xmlencode(rm._state))
205 rmnode.setAttribute("start_time", xmlencode(rm._start_time))
207 rmnode.setAttribute("stop_time", xmlencode(rm._stop_time))
208 if rm._discover_time:
209 rmnode.setAttribute("discover_time", xmlencode(rm._discover_time))
210 if rm._provision_time:
211 rmnode.setAttribute("provision_time", xmlencode(rm._provision_time))
213 rmnode.setAttribute("ready_time", xmlencode(rm._ready_time))
215 rmnode.setAttribute("release_time", xmlencode(rm._release_time))
217 rmnode.setAttribute("failed_time", xmlencode(rm._failed_time))
218 rmsnode.appendChild(rmnode)
220 anode = doc.createElement("attributes")
223 for attr in rm._attrs.values():
226 aanode = doc.createElement("attribute")
227 aanode.setAttribute("name", xmlencode(attr.name))
228 aanode.setAttribute("value", xmlencode(attr.value))
229 aanode.setAttribute("type", from_type(attr.value))
230 anode.appendChild(aanode)
233 rmnode.appendChild(anode)
235 cnode = doc.createElement("connections")
238 for guid in rm._connections:
240 ccnode = doc.createElement("connection")
241 ccnode.setAttribute("guid", xmlencode(guid))
242 cnode.appendChild(ccnode)
245 rmnode.appendChild(cnode)
247 cnnode = doc.createElement("conditions")
250 for action, conds in rm._conditions.items():
252 for (group, state, time) in conds:
253 ccnnode = doc.createElement("condition")
254 ccnnode.setAttribute("action", xmlencode(action))
255 ccnnode.setAttribute("group", xmlencode(group))
256 ccnnode.setAttribute("state", xmlencode(state))
257 ccnnode.setAttribute("time", xmlencode(time))
258 cnnode.appendChild(ccnnode)
261 rmnode.appendChild(cnnode)
263 tnode = doc.createElement("traces")
266 for trace in rm._trcs.values():
269 ttnode = doc.createElement("trace")
270 ttnode.setAttribute("name", xmlencode(trace.name))
271 tnode.appendChild(ttnode)
274 rmnode.appendChild(tnode)
276 def from_xml(self, xml):
277 doc = minidom.parseString(xml)
278 return self._ec_from_xml(doc)
280 def _ec_from_xml(self, doc):
281 from nepi.execution.ec import ExperimentController
284 ecnode_list = doc.getElementsByTagName("experiment")
285 for ecnode in ecnode_list:
286 if ecnode.nodeType == doc.ELEMENT_NODE:
287 exp_id = xmldecode(ecnode.getAttribute("exp_id"))
288 run_id = xmldecode(ecnode.getAttribute("run_id"))
289 local_dir = xmldecode(ecnode.getAttribute("local_dir"))
291 # Configure number of preocessing threads
292 nthreads = xmldecode(ecnode.getAttribute("nthreads"))
293 os.environ["NEPI_NTHREADS"] = nthreads
295 # Deserialize netgraph
299 netgraph = self._netgraph_from_xml(doc, ecnode)
302 topo_type = netgraph.topo_type
303 topology = netgraph.topology
306 ec = ExperimentController(exp_id = exp_id, local_dir = local_dir,
307 topology = topology, topo_type = topo_type)
311 rmsnode_list = ecnode.getElementsByTagName("rms")
313 rmnode_list = rmsnode_list[0].getElementsByTagName("rm")
314 for rmnode in rmnode_list:
315 if rmnode.nodeType == doc.ELEMENT_NODE:
316 self._rm_from_xml(doc, rmnode, ec, connections)
318 for (guid1, guid2) in connections:
319 ec.register_connection(guid1, guid2)
325 def _netgraph_from_xml(self, doc, ecnode):
328 topology = ecnode.getElementsByTagName("topology")
330 topology = topology[0]
331 topo_type = xmldecode(topology.getAttribute("topo-type"))
333 netgraph = NetGraph(topo_type = topo_type)
335 ngnsnode_list = topology.getElementsByTagName("nodes")
337 ngnsnode = ngnsnode_list[0].getElementsByTagName("node")
338 for ngnnode in ngnsnode:
339 nid = xmldecode(ngnnode.getAttribute("nid"))
340 tipe = xmldecode(ngnnode.getAttribute("nid-type"))
341 nid = to_type(tipe, nid)
342 netgraph.add_node(nid)
344 if ngnnode.hasAttribute("source"):
345 netgraph.set_source(nid)
346 if ngnnode.hasAttribute("target"):
347 netgraph.set_target(nid)
349 annosnode_list = ngnnode.getElementsByTagName("node-annotations")
352 annosnode = annosnode_list[0].getElementsByTagName("node-annotation")
353 for annonode in annosnode:
354 name = xmldecode(annonode.getAttribute("name"))
357 ips = xmldecode(annonode.getAttribute("value"), eval) # list
359 netgraph.annotate_node_ip(nid, ip)
361 value = xmldecode(annonode.getAttribute("value"))
362 tipe = xmldecode(annonode.getAttribute("type"))
363 value = to_type(tipe, value)
364 netgraph.annotate_node(nid, name, value)
366 ngesnode_list = topology.getElementsByTagName("edges")
368 ngesnode = ngesnode_list[0].getElementsByTagName("edge")
369 for ngenode in ngesnode:
370 nid1 = xmldecode(ngenode.getAttribute("nid1"))
371 tipe1 = xmldecode(ngenode.getAttribute("nid1-type"))
372 nid1 = to_type(tipe1, nid1)
374 nid2 = xmldecode(ngenode.getAttribute("nid2"))
375 tipe2 = xmldecode(ngenode.getAttribute("nid2-type"))
376 nid2 = to_type(tipe2, nid2)
378 netgraph.add_edge(nid1, nid2)
380 annosnode_list = ngenode.getElementsByTagName("edge-annotations")
382 annosnode = annosnode_list[0].getElementsByTagName("edge-annotation")
383 for annonode in annosnode:
384 name = xmldecode(annonode.getAttribute("name"))
387 net = xmldecode(annonode.getAttribute("value"), eval) # dict
388 netgraph.annotate_edge_net(nid1, nid2, net[nid1], net[nid2],
389 net["mask"], net["network"], net["prefix"])
391 value = xmldecode(annonode.getAttribute("value"))
392 tipe = xmldecode(annonode.getAttribute("type"))
393 value = to_type(tipe, value)
394 netgraph.annotate_edge(nid1, nid2, name, value)
397 def _rm_from_xml(self, doc, rmnode, ec, connections):
401 provision_time = None
406 guid = xmldecode(rmnode.getAttribute("guid"), int)
407 rtype = xmldecode(rmnode.getAttribute("rtype"))
409 # FOR NOW ONLY STATE NEW IS ALLOWED
412 state = xmldecode(rmnode.getAttribute("state"), int)
414 if rmnode.hasAttribute("start_time"):
415 start_time = xmldecode(rmnode.getAttribute("start_time"),
417 if rmnode.hasAttribute("stop_time"):
418 stop_time = xmldecode(rmnode.getAttribute("stop_time"),
420 if rmnode.hasAttribute("discover_time"):
421 dicover_time = xmldecode(rmnode.getAttribute("discover_time"),
423 if rmnode.hasAttribute("provision_time"):
424 provision_time = xmldecode(rmnode.getAttribute("provision_time"),
426 if rmnode.hasAttribute("ready_time"):
427 ready_time = xmldecode(rmnode.getAttribute("ready_time"),
429 if rmnode.hasAttribute("release_time"):
430 release_time = xmldecode(rmnode.getAttribute("release_time"),
432 if rmnode.hasAttribute("failed_time"):
433 failed_time = xmldecode(rmnode.getAttribute("failed_time"),
437 ec.register_resource(rtype, guid = guid)
438 rm = ec.get_resource(guid)
439 rm.set_state_time(state, "_start_time", start_time)
440 rm.set_state_time(state, "_stop_time", stop_time)
441 rm.set_state_time(state, "_discover_time", discover_time)
442 rm.set_state_time(state, "_provision_time", provision_time)
443 rm.set_state_time(state, "_ready_time", ready_time)
444 rm.set_state_time(state, "_release_time", release_time)
445 rm.set_state_time(state, "_failed_time", failed_time)
447 anode_list = rmnode.getElementsByTagName("attributes")
449 aanode_list = anode_list[0].getElementsByTagName("attribute")
450 for aanode in aanode_list:
451 name = xmldecode(aanode.getAttribute("name"))
452 value = xmldecode(aanode.getAttribute("value"))
453 tipe = xmldecode(aanode.getAttribute("type"))
454 value = to_type(tipe, value)
457 cnode_list = rmnode.getElementsByTagName("connections")
459 ccnode_list = cnode_list[0].getElementsByTagName("connection")
460 for ccnode in ccnode_list:
461 guid2 = xmldecode(ccnode.getAttribute("guid"), int)
462 connections.add((guid, guid2))
464 tnode_list = rmnode.getElementsByTagName("traces")
466 ttnode_list = tnode_list[0].getElementsByTagName("trace")
467 for ttnode in ttnode_list:
468 name = xmldecode(ttnode.getAttribute("name"))
469 ec.enable_trace(guid, name)
471 cnnode_list = rmnode.getElementsByTagName("conditions")
473 ccnnode_list = cnnode_list[0].getElementsByTagName("condition")
474 for ccnnode in ccnnode_list:
475 action = xmldecode(ccnnode.getAttribute("action"), int)
476 group = xmldecode(ccnnode.getAttribute("group"), eval) # list
477 state = xmldecode(ccnnode.getAttribute("state"), int)
478 time = xmldecode(ccnnode.getAttribute("time"))
479 time = to_type('STRING', time)
480 ec.register_condition(guid, action, group, state, time = time)