f6ac95753c67282ced07a25bb2f91a455b35629b
[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                 topology = None
271                 topo_type = None
272
273                 netgraph = self._netgraph_from_xml(doc, ecnode)
274                 
275                 if netgraph:
276                     topo_type = netgraph.topo_type
277                     topology = netgraph.topology
278
279                 # Instantiate EC
280                 ec = ExperimentController(exp_id = exp_id, local_dir = local_dir, 
281                         topology = topology, topo_type = topo_type)
282
283                 connections = set()
284
285                 rmsnode_list = ecnode.getElementsByTagName("rms")
286                 if rmsnode_list:
287                     rmnode_list = rmsnode_list[0].getElementsByTagName("rm") 
288                     for rmnode in rmnode_list:
289                         if rmnode.nodeType == doc.ELEMENT_NODE:
290                             self._rm_from_xml(doc, rmnode, ec, connections)
291
292                 for (guid1, guid2) in connections:
293                     ec.register_connection(guid1, guid2)
294
295                 break
296
297         return ec
298
299     def _netgraph_from_xml(self, doc, ecnode):
300         netgraph = None
301
302         topology = ecnode.getElementsByTagName("topology")
303         if topology:
304             topology = topology[0]
305             topo_type = xmldecode(topology.getAttribute("topo-type"))
306
307             netgraph = NetGraph(topo_type = topo_type)
308
309             ngnsnode_list = topology.getElementsByTagName("nodes")
310             if ngnsnode_list:
311                 ngnsnode = ngnsnode_list[0].getElementsByTagName("node") 
312                 for ngnnode in ngnsnode:
313                     nid = xmldecode(ngnnode.getAttribute("nid"))
314                     netgraph.add_node(nid)
315
316                     if ngnnode.hasAttribute("source"):
317                         netgraph.set_source(nid)
318                     if ngnnode.hasAttribute("target"):
319                         netgraph.set_target(nid)
320
321                     annosnode_list = ngnnode.getElementsByTagName("node-annotations")
322                     
323                     if annosnode_list:
324                         annosnode = annosnode_list[0].getElementsByTagName("node-annotation") 
325                         for annonode in annosnode:
326                             name = xmldecode(annonode.getAttribute("name"))
327
328                             if name == "ips":
329                                 ips = xmldecode(annonode.getAttribute("value"), eval) # list
330                                 for ip in ips:
331                                     netgraph.annotate_node_ip(nid, ip)
332                             else:
333                                 value = xmldecode(annonode.getAttribute("value"))
334                                 tipe = xmldecode(annonode.getAttribute("type"))
335                                 value = to_type(tipe, value)
336                                 netgraph.annotate_node(nid, name, value)
337
338             ngesnode_list = topology.getElementsByTagName("edges") 
339             if ngesnode_list:
340                 ngesnode = ngesnode_list[0].getElementsByTagName("edge") 
341                 for ngenode in ngesnode:
342                     nid1 = xmldecode(ngenode.getAttribute("nid1"))
343                     nid2 = xmldecode(ngenode.getAttribute("nid2"))
344                     netgraph.add_edge(nid1, nid2)
345
346                     annosnode_list = ngenode.getElementsByTagName("edge-annotations")
347                     if annosnode_list:
348                         annosnode = annosnode_list[0].getElementsByTagName("edge-annotation") 
349                         for annonode in annosnode:
350                             name = xmldecode(annonode.getAttribute("name"))
351
352                             if name == "net":
353                                 net = xmldecode(annonode.getAttribute("value"), eval) # dict
354                                 netgraph.annotate_edge_net(nid1, nid2, net[nid1], net[nid2], 
355                                         net["mask"], net["network"], net["prefix"])
356                             else:
357                                 value = xmldecode(annonode.getAttribute("value"))
358                                 tipe = xmldecode(annonode.getAttribute("type"))
359                                 value = to_type(tipe, value)
360                                 netgraph.annotate_edge(nid1, nid2, name, value)
361         return netgraph
362
363     def _rm_from_xml(self, doc, rmnode, ec, connections):
364         start_time = None
365         stop_time = None
366         discover_time = None
367         provision_time = None
368         ready_time = None
369         release_time = None
370         failed_time = None
371
372         guid = xmldecode(rmnode.getAttribute("guid"), int)
373         rtype = xmldecode(rmnode.getAttribute("rtype"))
374
375         # FOR NOW ONLY STATE NEW IS ALLOWED
376         state = 0
377         """
378         state = xmldecode(rmnode.getAttribute("state"), int)
379
380         if rmnode.hasAttribute("start_time"):
381             start_time = xmldecode(rmnode.getAttribute("start_time"), 
382                     datetime.datetime)
383         if rmnode.hasAttribute("stop_time"):
384             stop_time = xmldecode(rmnode.getAttribute("stop_time"), 
385                     datetime.datetime)
386         if rmnode.hasAttribute("discover_time"):
387             dicover_time = xmldecode(rmnode.getAttribute("discover_time"), 
388                     datetime.datetime)
389         if rmnode.hasAttribute("provision_time"):
390             provision_time = xmldecode(rmnode.getAttribute("provision_time"),
391                     datetime.datetime)
392         if rmnode.hasAttribute("ready_time"):
393             ready_time = xmldecode(rmnode.getAttribute("ready_time"),
394                     datetime.datetime)
395         if rmnode.hasAttribute("release_time"):
396             release_time = xmldecode(rmnode.getAttribute("release_time"),
397                     datetime.datetime)
398         if rmnode.hasAttribute("failed_time"):
399             failed_time = xmldecode(rmnode.getAttribute("failed_time"),
400                     datetime.datetime)
401         """
402
403         ec.register_resource(rtype, guid = guid)
404         rm = ec.get_resource(guid)
405         rm.set_state_time(state, "_start_time", start_time)
406         rm.set_state_time(state, "_stop_time", stop_time)
407         rm.set_state_time(state, "_discover_time", discover_time)
408         rm.set_state_time(state, "_provision_time", provision_time)
409         rm.set_state_time(state, "_ready_time", ready_time)
410         rm.set_state_time(state, "_release_time", release_time)
411         rm.set_state_time(state, "_failed_time", failed_time)
412         
413         anode_list = rmnode.getElementsByTagName("attributes")
414         if anode_list:
415             aanode_list = anode_list[0].getElementsByTagName("attribute") 
416             for aanode in aanode_list:
417                 name = xmldecode(aanode.getAttribute("name"))
418                 value = xmldecode(aanode.getAttribute("value"))
419                 tipe = xmldecode(aanode.getAttribute("type"))
420                 value = to_type(tipe, value)
421                 rm.set(name, value)
422
423         cnode_list = rmnode.getElementsByTagName("connections")
424         if cnode_list:
425             ccnode_list = cnode_list[0].getElementsByTagName("connection") 
426             for ccnode in ccnode_list:
427                 guid2 = xmldecode(ccnode.getAttribute("guid"), int)
428                 connections.add((guid, guid2))
429
430         tnode_list = rmnode.getElementsByTagName("traces")
431         if tnode_list:
432             ttnode_list = tnode_list[0].getElementsByTagName("trace") 
433             for ttnode in ttnode_list:
434                 name = xmldecode(ttnode.getAttribute("name"))
435                 ec.enable_trace(guid, name)
436
437         cnnode_list = rmnode.getElementsByTagName("conditions")
438         if cnnode_list:
439             ccnnode_list = cnnode_list[0].getElementsByTagName("condition") 
440             for ccnnode in ccnnode_list:
441                 action = xmldecode(ccnnode.getAttribute("action"), int)
442                 group = xmldecode(ccnnode.getAttribute("group"), eval) # list
443                 state = xmldecode(ccnnode.getAttribute("state"), int)
444                 time = xmldecode(ccnnode.getAttribute("time"))
445                 time = to_type('STRING', time)
446                 ec.register_condition(guid, action, group, state, time = time)
447