use print() - import print_function - should be fine for both py2 and py3
[nepi.git] / src / nepi / util / parsers / xml_parser.py
1 #
2 #    NEPI, a framework to manage network experiments
3 #    Copyright (C) 2013 INRIA
4 #
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;
8 #
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.
13 #
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/>.
16 #
17 # Author: Alina Quereilhac <alina.quereilhac@inria.fr>
18
19 from __future__ import print_function
20
21 from nepi.util.netgraph import NetGraph, TopologyType 
22 from nepi.util.timefuncs import stformat, tsformat
23
24 from xml.dom import minidom
25
26 import datetime
27 import sys
28 import os
29
30 STRING = "string"
31 BOOL = "bool"
32 INTEGER = "integer"
33 DOUBLE = "float"
34
35 def xmlencode(s):
36     if isinstance(s, str):
37         rv = s.decode("latin1")
38     if isinstance(s, datetime.datetime):
39         rv = tsformat(s)
40     elif not isinstance(s, unicode):
41         rv = unicode(s)
42     else:
43         rv = s
44     return rv.replace(u'\x00',u'&#0000;')
45
46 def xmldecode(s, cast = str):
47     ret = s.replace(u'&#0000',u'\x00').encode("ascii")
48     ret = cast(ret)
49     if s == "None":
50         return None
51     return ret
52
53 def from_type(value):
54     if isinstance(value, bool):
55         return BOOL
56     if isinstance(value, int):
57         return INTEGER
58     if isinstance(value, float):
59         return DOUBLE
60
61     return STRING
62
63 def to_type(type, value):
64     if not value:
65         return value
66
67     if type == STRING:
68         return str(value)
69     if type == BOOL:
70         return value == "True"
71     if type == INTEGER:
72         return int(value)
73     if type == DOUBLE:
74         return float(value)
75
76 class ECXMLParser(object):
77     def to_xml(self, ec):
78         
79         doc = minidom.Document()
80         
81         self._ec_to_xml(doc, ec)
82        
83         try:
84             xml = doc.toprettyxml(indent="    ", encoding="UTF-8")
85         except:
86             print("Oops: generating XML from %s" % (data,), file=sys.stderr)
87             raise
88         
89         return xml
90
91     def _ec_to_xml(self, doc, ec):
92         ecnode = doc.createElement("experiment")
93         ecnode.setAttribute("exp_id", xmlencode(ec.exp_id))
94         ecnode.setAttribute("run_id", xmlencode(ec.run_id))
95         ecnode.setAttribute("nthreads", xmlencode(ec.nthreads))
96         ecnode.setAttribute("local_dir", xmlencode(ec.local_dir))
97         doc.appendChild(ecnode)
98
99         if ec.netgraph != None:
100             self._netgraph_to_xml(doc, ecnode, ec)
101
102         rmsnode = doc.createElement("rms")
103         ecnode.appendChild(rmsnode)
104
105         for guid, rm in ec._resources.iteritems():
106             self._rm_to_xml(doc, rmsnode, ec, guid, rm)
107
108         return doc
109     
110     def _netgraph_to_xml(self, doc, ecnode, ec):
111         ngnode = doc.createElement("topology")
112         ngnode.setAttribute("topo-type", xmlencode(ec.netgraph.topo_type))
113         ecnode.appendChild(ngnode)
114         
115         self. _netgraph_nodes_to_xml(doc, ngnode, ec)
116         self. _netgraph_edges_to_xml(doc, ngnode, ec)
117         
118     def _netgraph_nodes_to_xml(self, doc, ngnode, ec):
119         ngnsnode = doc.createElement("nodes")
120         ngnode.appendChild(ngnsnode)
121
122         for nid in ec.netgraph.nodes():
123             ngnnode = doc.createElement("node")
124             ngnnode.setAttribute("nid", xmlencode(nid))
125             ngnnode.setAttribute("nid-type", from_type(nid))
126             ngnsnode.appendChild(ngnnode)
127
128             # Mark ources and targets
129             if ec.netgraph.is_source(nid):
130                 ngnnode.setAttribute("source", xmlencode(True))
131
132             if ec.netgraph.is_target(nid):
133                 ngnnode.setAttribute("target", xmlencode(True))
134
135             # Node annotations
136             annosnode = doc.createElement("node-annotations")
137             add_annotations = False
138             for name in ec.netgraph.node_annotations(nid):
139                 add_annotations = True
140                 value = ec.netgraph.node_annotation(nid, name)
141                 annonode = doc.createElement("node-annotation")
142                 annonode.setAttribute("name", xmlencode(name))
143                 annonode.setAttribute("value", xmlencode(value))
144                 annonode.setAttribute("type", from_type(value))
145                 annosnode.appendChild(annonode)
146
147             if add_annotations:
148                 ngnnode.appendChild(annosnode)
149
150     def _netgraph_edges_to_xml(self, doc, ngnode, ec):
151         ngesnode = doc.createElement("edges")
152         ngnode.appendChild(ngesnode)
153
154         for nid1, nid2 in ec.netgraph.edges():
155             ngenode = doc.createElement("edge")
156             ngenode.setAttribute("nid1", xmlencode(nid1))
157             ngenode.setAttribute("nid1-type", from_type(nid1))
158             ngenode.setAttribute("nid2", xmlencode(nid2))
159             ngenode.setAttribute("nid2-type", from_type(nid2))
160             ngesnode.appendChild(ngenode)
161
162             # Edge annotations
163             annosnode = doc.createElement("edge-annotations")
164             add_annotations = False
165             for name in ec.netgraph.edge_annotations(nid1, nid2):
166                 add_annotations = True
167                 value = ec.netgraph.edge_annotation(nid1, nid2, name)
168                 annonode = doc.createElement("edge-annotation")
169                 annonode.setAttribute("name", xmlencode(name))
170                 annonode.setAttribute("value", xmlencode(value))
171                 annonode.setAttribute("type", from_type(value))
172                 annosnode.appendChild(annonode)
173
174             if add_annotations:
175                 ngenode.appendChild(annosnode)
176
177     def _rm_to_xml(self, doc, rmsnode, ec, guid, rm):
178         rmnode = doc.createElement("rm")
179         rmnode.setAttribute("guid", xmlencode(guid))
180         rmnode.setAttribute("rtype", xmlencode(rm._rtype))
181         rmnode.setAttribute("state", xmlencode(rm._state))
182         if rm._start_time:
183             rmnode.setAttribute("start_time", xmlencode(rm._start_time))
184         if rm._stop_time:
185             rmnode.setAttribute("stop_time", xmlencode(rm._stop_time))
186         if rm._discover_time:
187             rmnode.setAttribute("discover_time", xmlencode(rm._discover_time))
188         if rm._provision_time:    
189             rmnode.setAttribute("provision_time", xmlencode(rm._provision_time))
190         if rm._ready_time:
191             rmnode.setAttribute("ready_time", xmlencode(rm._ready_time))
192         if rm._release_time:
193             rmnode.setAttribute("release_time", xmlencode(rm._release_time))
194         if rm._failed_time:
195             rmnode.setAttribute("failed_time", xmlencode(rm._failed_time))
196         rmsnode.appendChild(rmnode)
197
198         anode = doc.createElement("attributes")
199         attributes = False
200
201         for attr in rm._attrs.values():
202             if attr.has_changed:
203                 attributes = True
204                 aanode = doc.createElement("attribute")
205                 aanode.setAttribute("name", xmlencode(attr.name))
206                 aanode.setAttribute("value", xmlencode(attr.value))
207                 aanode.setAttribute("type", from_type(attr.value))
208                 anode.appendChild(aanode)
209     
210         if attributes: 
211             rmnode.appendChild(anode)
212
213         cnode = doc.createElement("connections")
214         connections = False
215         
216         for guid in rm._connections:
217             connections = True
218             ccnode = doc.createElement("connection")
219             ccnode.setAttribute("guid", xmlencode(guid))
220             cnode.appendChild(ccnode)
221         
222         if connections:
223            rmnode.appendChild(cnode)
224
225         cnnode = doc.createElement("conditions")
226         conditions = False
227
228         for action, conds in rm._conditions.iteritems():
229             conditions = True
230             for (group, state, time) in conds:
231                 ccnnode = doc.createElement("condition")
232                 ccnnode.setAttribute("action", xmlencode(action))
233                 ccnnode.setAttribute("group", xmlencode(group))
234                 ccnnode.setAttribute("state", xmlencode(state))
235                 ccnnode.setAttribute("time", xmlencode(time))
236                 cnnode.appendChild(ccnnode)
237         
238         if conditions:
239            rmnode.appendChild(cnnode)
240
241         tnode = doc.createElement("traces")
242         traces = False
243
244         for trace in rm._trcs.values():
245             if trace.enabled:
246                 traces = True
247                 ttnode = doc.createElement("trace")
248                 ttnode.setAttribute("name", xmlencode(trace.name))
249                 tnode.appendChild(ttnode)
250     
251         if traces: 
252             rmnode.appendChild(tnode)
253
254     def from_xml(self, xml):
255         doc = minidom.parseString(xml)
256         return self._ec_from_xml(doc)
257
258     def _ec_from_xml(self, doc):
259         from nepi.execution.ec import ExperimentController
260         ec = None
261         
262         ecnode_list = doc.getElementsByTagName("experiment")
263         for ecnode in ecnode_list:
264             if ecnode.nodeType == doc.ELEMENT_NODE:
265                 exp_id = xmldecode(ecnode.getAttribute("exp_id"))
266                 run_id = xmldecode(ecnode.getAttribute("run_id"))
267                 local_dir = xmldecode(ecnode.getAttribute("local_dir"))
268
269                 # Configure number of preocessing threads
270                 nthreads = xmldecode(ecnode.getAttribute("nthreads"))
271                 os.environ["NEPI_NTHREADS"] = nthreads
272
273                 # Deserialize netgraph
274                 topology = None
275                 topo_type = None
276
277                 netgraph = self._netgraph_from_xml(doc, ecnode)
278                 
279                 if netgraph:
280                     topo_type = netgraph.topo_type
281                     topology = netgraph.topology
282
283                 # Instantiate EC
284                 ec = ExperimentController(exp_id = exp_id, local_dir = local_dir, 
285                         topology = topology, topo_type = topo_type)
286
287                 connections = set()
288
289                 rmsnode_list = ecnode.getElementsByTagName("rms")
290                 if rmsnode_list:
291                     rmnode_list = rmsnode_list[0].getElementsByTagName("rm") 
292                     for rmnode in rmnode_list:
293                         if rmnode.nodeType == doc.ELEMENT_NODE:
294                             self._rm_from_xml(doc, rmnode, ec, connections)
295
296                 for (guid1, guid2) in connections:
297                     ec.register_connection(guid1, guid2)
298
299                 break
300
301         return ec
302
303     def _netgraph_from_xml(self, doc, ecnode):
304         netgraph = None
305
306         topology = ecnode.getElementsByTagName("topology")
307         if topology:
308             topology = topology[0]
309             topo_type = xmldecode(topology.getAttribute("topo-type"))
310
311             netgraph = NetGraph(topo_type = topo_type)
312
313             ngnsnode_list = topology.getElementsByTagName("nodes")
314             if ngnsnode_list:
315                 ngnsnode = ngnsnode_list[0].getElementsByTagName("node") 
316                 for ngnnode in ngnsnode:
317                     nid = xmldecode(ngnnode.getAttribute("nid"))
318                     tipe = xmldecode(ngnnode.getAttribute("nid-type"))
319                     nid = to_type(tipe, nid)
320                     netgraph.add_node(nid)
321
322                     if ngnnode.hasAttribute("source"):
323                         netgraph.set_source(nid)
324                     if ngnnode.hasAttribute("target"):
325                         netgraph.set_target(nid)
326
327                     annosnode_list = ngnnode.getElementsByTagName("node-annotations")
328                     
329                     if annosnode_list:
330                         annosnode = annosnode_list[0].getElementsByTagName("node-annotation") 
331                         for annonode in annosnode:
332                             name = xmldecode(annonode.getAttribute("name"))
333
334                             if name == "ips":
335                                 ips = xmldecode(annonode.getAttribute("value"), eval) # list
336                                 for ip in ips:
337                                     netgraph.annotate_node_ip(nid, ip)
338                             else:
339                                 value = xmldecode(annonode.getAttribute("value"))
340                                 tipe = xmldecode(annonode.getAttribute("type"))
341                                 value = to_type(tipe, value)
342                                 netgraph.annotate_node(nid, name, value)
343
344             ngesnode_list = topology.getElementsByTagName("edges") 
345             if ngesnode_list:
346                 ngesnode = ngesnode_list[0].getElementsByTagName("edge") 
347                 for ngenode in ngesnode:
348                     nid1 = xmldecode(ngenode.getAttribute("nid1"))
349                     tipe1 = xmldecode(ngenode.getAttribute("nid1-type"))
350                     nid1 = to_type(tipe1, nid1)
351
352                     nid2 = xmldecode(ngenode.getAttribute("nid2"))
353                     tipe2 = xmldecode(ngenode.getAttribute("nid2-type"))
354                     nid2 = to_type(tipe2, nid2)
355
356                     netgraph.add_edge(nid1, nid2)
357
358                     annosnode_list = ngenode.getElementsByTagName("edge-annotations")
359                     if annosnode_list:
360                         annosnode = annosnode_list[0].getElementsByTagName("edge-annotation") 
361                         for annonode in annosnode:
362                             name = xmldecode(annonode.getAttribute("name"))
363
364                             if name == "net":
365                                 net = xmldecode(annonode.getAttribute("value"), eval) # dict
366                                 netgraph.annotate_edge_net(nid1, nid2, net[nid1], net[nid2], 
367                                         net["mask"], net["network"], net["prefix"])
368                             else:
369                                 value = xmldecode(annonode.getAttribute("value"))
370                                 tipe = xmldecode(annonode.getAttribute("type"))
371                                 value = to_type(tipe, value)
372                                 netgraph.annotate_edge(nid1, nid2, name, value)
373         return netgraph
374
375     def _rm_from_xml(self, doc, rmnode, ec, connections):
376         start_time = None
377         stop_time = None
378         discover_time = None
379         provision_time = None
380         ready_time = None
381         release_time = None
382         failed_time = None
383
384         guid = xmldecode(rmnode.getAttribute("guid"), int)
385         rtype = xmldecode(rmnode.getAttribute("rtype"))
386
387         # FOR NOW ONLY STATE NEW IS ALLOWED
388         state = 0
389         """
390         state = xmldecode(rmnode.getAttribute("state"), int)
391
392         if rmnode.hasAttribute("start_time"):
393             start_time = xmldecode(rmnode.getAttribute("start_time"), 
394                     datetime.datetime)
395         if rmnode.hasAttribute("stop_time"):
396             stop_time = xmldecode(rmnode.getAttribute("stop_time"), 
397                     datetime.datetime)
398         if rmnode.hasAttribute("discover_time"):
399             dicover_time = xmldecode(rmnode.getAttribute("discover_time"), 
400                     datetime.datetime)
401         if rmnode.hasAttribute("provision_time"):
402             provision_time = xmldecode(rmnode.getAttribute("provision_time"),
403                     datetime.datetime)
404         if rmnode.hasAttribute("ready_time"):
405             ready_time = xmldecode(rmnode.getAttribute("ready_time"),
406                     datetime.datetime)
407         if rmnode.hasAttribute("release_time"):
408             release_time = xmldecode(rmnode.getAttribute("release_time"),
409                     datetime.datetime)
410         if rmnode.hasAttribute("failed_time"):
411             failed_time = xmldecode(rmnode.getAttribute("failed_time"),
412                     datetime.datetime)
413         """
414
415         ec.register_resource(rtype, guid = guid)
416         rm = ec.get_resource(guid)
417         rm.set_state_time(state, "_start_time", start_time)
418         rm.set_state_time(state, "_stop_time", stop_time)
419         rm.set_state_time(state, "_discover_time", discover_time)
420         rm.set_state_time(state, "_provision_time", provision_time)
421         rm.set_state_time(state, "_ready_time", ready_time)
422         rm.set_state_time(state, "_release_time", release_time)
423         rm.set_state_time(state, "_failed_time", failed_time)
424         
425         anode_list = rmnode.getElementsByTagName("attributes")
426         if anode_list:
427             aanode_list = anode_list[0].getElementsByTagName("attribute") 
428             for aanode in aanode_list:
429                 name = xmldecode(aanode.getAttribute("name"))
430                 value = xmldecode(aanode.getAttribute("value"))
431                 tipe = xmldecode(aanode.getAttribute("type"))
432                 value = to_type(tipe, value)
433                 rm.set(name, value)
434
435         cnode_list = rmnode.getElementsByTagName("connections")
436         if cnode_list:
437             ccnode_list = cnode_list[0].getElementsByTagName("connection") 
438             for ccnode in ccnode_list:
439                 guid2 = xmldecode(ccnode.getAttribute("guid"), int)
440                 connections.add((guid, guid2))
441
442         tnode_list = rmnode.getElementsByTagName("traces")
443         if tnode_list:
444             ttnode_list = tnode_list[0].getElementsByTagName("trace") 
445             for ttnode in ttnode_list:
446                 name = xmldecode(ttnode.getAttribute("name"))
447                 ec.enable_trace(guid, name)
448
449         cnnode_list = rmnode.getElementsByTagName("conditions")
450         if cnnode_list:
451             ccnnode_list = cnnode_list[0].getElementsByTagName("condition") 
452             for ccnnode in ccnnode_list:
453                 action = xmldecode(ccnnode.getAttribute("action"), int)
454                 group = xmldecode(ccnnode.getAttribute("group"), eval) # list
455                 state = xmldecode(ccnnode.getAttribute("state"), int)
456                 time = xmldecode(ccnnode.getAttribute("time"))
457                 time = to_type('STRING', time)
458                 ec.register_condition(guid, action, group, state, time = time)
459