b42d39baffe4ea26fb040543d3ffe781eb56dfea
[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
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, str):
41         rv = str(s)
42     else:
43         rv = s
44     return rv.replace('\x00','&#0000;')
45
46 def xmldecode(s, cast = str):
47     ret = s.replace('&#0000','\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.items():
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.items():
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