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