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