12 # Import the class used (Slice, Host, Device, Link, HostLink)
13 from classDefinition import *
15 from exceptionDefinition import SSHConnectError
19 def __init__(self, confFile = 'Conf.xml', linkFile = 'Link.xml', prefix = '', port = 22, keyFile = '~/.ssh/id_rsa'):
20 self.__currentNodeConf__ = None
21 self.__currentNodeLink__ = None
22 self.__currentNodeRoute__= None
23 self.__currentNodeService__ = None
24 self.__sliceList__ = []
25 self.__linkList__ = []
27 self.readXml(prefix+confFile, prefix+linkFile)
30 self.xmlToSliceObject()
31 self.xmlToLinkObject()
33 def readXml(self, confPath, linkPath):
34 from xml.dom.minidom import parse
35 if not(os.path.isfile(confPath) and os.path.isfile(linkPath)):
36 print "Error file "+confPath+" or "+linkPath+" does not exist"
38 self.conf = parse(confPath)
39 self.link = parse(linkPath)
41 def getRootElementConf(self):
42 if self.__currentNodeConf__ == None:
43 self.__currentNodeConf__ = self.conf.documentElement
44 return self.__currentNodeConf__
46 def getRootElementLink(self):
47 if self.__currentNodeLink__ == None:
48 self.__currentNodeLink__ = self.link.documentElement
49 return self.__currentNodeLink__
51 def xmlToServiceObject(self, rootService, host, slice_name):
53 newService = Service(slice_name, host.url, host.ssh)
54 for service_i in rootService.getElementsByTagName("service"):
56 service = self.getText(service_i)
58 print "The tag service is missig"
59 servicePart = service.partition(':')
60 newService.services.append([servicePart[0], servicePart[2]])
61 host.services = newService
63 def setServices(self):
64 print "Now setting the service"
65 for slice_i in self.__sliceList__:
66 for host_i in slice_i.hosts:
67 host_i.services.setService()
69 def xmlToRouteObject(self, rootRoute, host, sliceName):
70 for route_i in rootRoute.getElementsByTagName("route"):
71 newRoute = Route(sliceName, host.url, host.ssh, host.id_host)
73 newRoute.subnet = self.getText(route_i.getElementsByTagName("subnet")[0])
74 newRoute.gateway = self.getText(route_i.getElementsByTagName("gateway")[0])
75 devId = self.getText(route_i.getElementsByTagName("device")[0])
77 print "The tag subnet / gateway or device is missing"
79 newRoute.device = self.getTapId(newRoute, devId, host)
80 host.routes.append(newRoute)
83 for slice_i in self.__sliceList__:
84 for host_i in slice_i.hosts:
85 for route_i in host_i.routes:
88 def xmlToSliceObject(self):
90 for slice_i in self.getRootElementConf().getElementsByTagName("slice"):
94 newSlice.slice_name.value = self.getText(slice_i.getElementsByTagName("slice_name")[0])
96 print "The tag slice_name is missing"
98 for host_i in slice_i.getElementsByTagName("host"):
102 newHost.hostType.value = self.getText(host_i.getElementsByTagName("type")[0])
103 newHost.id_host = self.getText(host_i.getElementsByTagName("id")[0])
104 newHost.url.value = self.getText(host_i.getElementsByTagName("url")[0])
105 except Exception, why:
106 print "Tag id or url is missing"
107 print "Slice",newSlice.slice_name," Host ",newHost.url," Interface ",str(k)
110 newHost.ssh = self.sshCheck(newHost.url.value, newSlice.slice_name.value)
111 for interface_i in host_i.getElementsByTagName("interface"):
115 newDevice.id_dev = self.getText(interface_i.getElementsByTagName("bridge_name")[0])
116 newDevice.ip = self.getText(interface_i.getElementsByTagName("ip")[0])
118 print "Tag bridge_name or ip is missing"
120 newHost.devices.append(newDevice)
122 serviceRoot = host_i.getElementsByTagName("services")[0]
123 self.xmlToServiceObject(serviceRoot, newHost, newSlice.slice_name)
125 newHost.services = Service("","","")
127 routeRoot = host_i.getElementsByTagName("routes")[0]
128 self.xmlToRouteObject(routeRoot, newHost, newSlice.slice_name)
130 print "No additionnal route for host "+str(newSlice.slice_name)+"@"+str(newHost.url)
131 newSlice.hosts.append(newHost)
132 self.__sliceList__.append(newSlice)
134 def setSliceConf(self):
135 for slice_i in self.__sliceList__:
136 for host_i in slice_i.hosts:
137 if len(host_i.devices) > 0:
138 print "\tOn "+str(slice_i.slice_name)+"@"+str(host_i.url)
139 self.execute("sudo -S sliver-ovs start_db", host_i.ssh, display = True)
140 self.execute("sudo -S sliver-ovs start_switch", host_i.ssh, display = True)
141 for interface_i in host_i.devices:
144 while returnCode != 0:
145 returnCode = self.execute("sudo -S sliver-ovs create_bridge "+interface_i.id_dev+" "+interface_i.ip,host_i.ssh)
149 self.execute("sudo -S sliver-ovs del_bridge "+interface_i.id_dev, host_i.ssh)
151 print "I have make ",i," iteration"
154 print "I make",i,"iteration before successfully create the interface"
156 def xmlToLinkObject(self):
157 for link_i in self.getRootElementLink().getElementsByTagName("link"):
159 newHost1 = HostLink()
160 newHost2 = HostLink()
162 host1 = link_i.getElementsByTagName("host1")[0]
163 host2 = link_i.getElementsByTagName("host2")[0]
165 print "Tag host1/host2 is missing"
168 newHost1.slice_name = self.getText(host1.getElementsByTagName("slice")[0])
169 newHost1.id_host = self.getText(host1.getElementsByTagName("id")[0])
170 newHost1.bridge_name = self.getText(host1.getElementsByTagName("bridge_name")[0])
172 print "Tag slice, id or bridge_name is missing for host1"
175 newHost2.slice_name = self.getText(host2.getElementsByTagName("slice")[0])
176 newHost2.id_host = self.getText(host2.getElementsByTagName("id")[0])
177 newHost2.bridge_name = self.getText(host2.getElementsByTagName("bridge_name")[0])
179 print "Tag slice, id or bridge_name is missing for host2"
181 newHost1.ssh, newHost1.url = self.getSSHAccessUrl(newHost1.slice_name, newHost1.id_host )
182 newHost2.ssh, newHost2.url = self.getSSHAccessUrl(newHost2.slice_name, newHost2.id_host )
183 newLink.host1 = newHost1
184 newLink.host2 = newHost2
185 self.__linkList__.append(newLink)
189 print "Creating Links"
190 for link_i in self.__linkList__:
193 link_name_host1 = host1.slice_name+"@"+str(host1.url)+"@"+host1.bridge_name
194 link_name_host2 = host2.slice_name+"@"+str(host2.url)+"@"+host2.bridge_name
195 link_name = link_name_host1+"---"+link_name_host2
196 print "\tOn "+str(host1.slice_name)+"@"+str(host1.url)
197 self.execute("sudo -S sliver-ovs create_port "+host1.bridge_name+" "+link_name, host1.ssh)
198 print "\tOn "+str(host2.slice_name)+"@"+str(host2.url)
199 self.execute("sudo -S sliver-ovs create_port "+host2.bridge_name+" "+link_name, host2.ssh)
200 proc = subprocess.Popen(["host "+str(host1.url)+" | sed -n 's/^.*has address *//p'"], stdout=subprocess.PIPE, shell=True)
201 (out, err) = proc.communicate()
202 ip1 = out.replace('\n','')
203 proc = subprocess.Popen(["host "+str(host2.url)+" | sed -n 's/^.*has address *//p'"], stdout=subprocess.PIPE, shell=True)
204 (out, err) = proc.communicate()
205 ip2 = out.replace('\n','')
206 portUDP1 = self.execute("sudo -S sliver-ovs get_local_endpoint "+link_name, host1.ssh, retour=True).replace('\n','')
207 portUDP2 = self.execute("sudo -S sliver-ovs get_local_endpoint "+link_name, host2.ssh, retour=True).replace('\n','')
208 print "\tPort UDP1 = "+str(portUDP1)+" Port UDP2 = "+str(portUDP2)
209 self.execute("sudo -S sliver-ovs set_remote_endpoint "+link_name+" "+ip2+" "+portUDP2, host1.ssh)
210 self.execute("sudo -S sliver-ovs set_remote_endpoint "+link_name+" "+ip1+" "+portUDP1, host2.ssh)
212 def getSSHAccessUrl(self, slice_name, id_host):
213 host_search = self.getHost(slice_name, id_host)
214 return host_search.ssh, host_search.url
216 def getHost(self, slice_name, host_id):
218 slice_search = self.__sliceList__[i]
220 while slice_search.slice_name.value != slice_name:
222 slice_search = self.__sliceList__[i]
223 host_search = slice_search.hosts[j]
224 while host_search.id_host != host_id:
226 host_search = slice_search.hosts[j]
228 print "The host in slice ->",slice_name,"and host id ->",host_id,"doesn't exist"
229 print "\tAn IndexError occured"
233 def getUrl(self, slice_name, id_host):
234 host_search = self.getHost(slice_name, id_host)
235 return host_search.url
237 def getTapId(self, route, idDev, host):
238 idUser = self.execute("id -u", route.host_ssh, retour = True).replace('\n','')
240 dev_searched = host.devices[i]
242 while dev_searched.id_dev != idDev:
244 dev_searched = host_searched.devices[i]
246 print "Error while setting the route"
247 print "For slice ->",host_searched.slice_name," id host ->",host_searched.id_host," the following device does not exist ->",idDev
249 return "tap"+idUser+"-"+str(i)
251 #TODO change the function for online mode
252 def sshCheck(self, host, slice_name):
253 ssh = paramiko.SSHClient()
254 ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
256 ssh.connect(host, username=slice_name, port=self.sshPort, key_filename=self.sshKey)
258 # This error is raised if the ssh key has not already been installed on the node
259 except paramiko.PasswordRequiredException, why:
260 print "Password required exception"
262 raise SSHConnectError(host, slice_name)
263 except paramiko.SSHException:
264 print "An SSH exception occured"
265 raise SSHConnectError(host, slice_name)
266 except Exception, why:
267 print "An error occured for host "+str(host)+" on slice "+str(slice_name)
269 raise SSHConnectError(host, slice_name)
272 def timeout(signum, frame):
273 raise TimeOutException, "Command ran for too long"
275 def getText(self, node):
276 return node.childNodes[0].nodeValue
278 def execute(self, command, ssh, display=False, retour=False):
280 stdin, stdout, stderr = ssh.exec_command(command)
282 while not stdout.channel.exit_status_ready():
286 splitted = err.splitlines()
287 if len(splitted) > 0:
288 print "\tError in execution"
289 for line in splitted:
292 for line in stdout.read().splitlines() :
296 return stdout.channel.recv_exit_status()
299 print "Removing the topology configuration"
300 for slice_i in self.__sliceList__:
301 for host_i in slice_i.hosts:
302 self.sshCheck(host_i.url.value, slice_i.slice_name.value)
303 print "\tOn "+str(slice_i.slice_name)+"@"+str(host_i.url)+" clearing the conf :"
305 for device_i in host_i.devices:
306 self.execute("sudo -S sliver-ovs del_bridge "+device_i.id_dev, sshHost, display=True)
307 self.execute("sudo -S sliver-ovs stop_switch", sshHost, display=True)
308 self.execute("sudo -S sliver-ovs stop_db", sshHost, display=True)
309 host_i.services.removeService()
311 def printSlice(self):
312 print "List of slice/host/interface"
313 for slice_i in self.__sliceList__:
315 for host_i in slice_i.hosts:
316 print "\t"+str(host_i)
317 for dev_i in host_i.devices:
318 print "\t\t"+str(dev_i)
321 print "\nList of link"
322 for link_i in self.__linkList__:
325 def printRoute(self):
326 print "\nList of additionnal route"
327 for slice_i in self.__sliceList__:
328 for host_i in slice_i.hosts:
329 for route_i in host_i.routes:
332 def printService(self):
333 print "\nList of deployed service"
334 for slice_i in self.__sliceList__:
335 for host_i in slice_i.hosts:
336 serviceStr = str(host_i.services)
338 print host_i.services
340 def getSliceList(self):
341 return self.__sliceList__
344 def helpOption(self):
345 print "You can use the following options:"
346 print "\t setTopology : to create the topology"
347 print "\t clearTopology : to clear the topology"
348 print "\t printTopology : to print the topology"
350 print "\t $2 : configuration xml file"
351 print "\t $3 : link xml file"
352 print "\t $2 : prefix to find the xml file"
353 print "\t If not given default are Conf.xml, Link.xml"
357 if not os.path.exists(x) and x!=os.path.expanduser('~/.ssh/id_rsa'):
358 raise argparse.ArgumentError("{0} does not exist".format(x))
361 if __name__ == "__main__":
362 parser = argparse.ArgumentParser()
363 #parser = argparse.ArgumentParser("This script allow you to deploy a configured topology on planetlab node, the topology has to be descript in xml file\nYou have to use one of the action option (-c -s -p)\nYou have to specify the input file that need to be used")
364 parser.add_argument("action", help="The action you want to perform choices are print, set or clear the topology", choices = ['print','set','clear'], default='print', nargs='?')
365 parser.add_argument("-k", "--key", help="The location of the ssh private key file (default value is ~/.ssh/id_rsa)", default=os.path.expanduser('~/.ssh/id_rsa'), type=keyFile)
366 parser.add_argument('-p', '--port', help='The port used for the ssh connection (default is 22)', default=22, type = int)
368 groupInput = parser.add_mutually_exclusive_group()
369 groupInput.add_argument("-f", "--file", help="The xml file that will be used, Link xml file then Configuration xml file", type=str, default='', nargs=2)
370 groupInput.add_argument("-d", "--directory", help="The directory used to find the xml file the script will look at prefix+Conf.xml and prefix+Link.xml", type=str, default='./', dest='prefix')
373 args = parser.parse_args()
374 except Exception, why:
375 print "An error occured while parsing the argument"
381 x = TransformXml(confFile = args.file[0], linkFile = args.file[1], port = args.port, keyFile = args.key)
383 x = TransformXml(prefix = args.prefix, port = args.port, keyFile = args.key )
385 if args.action == "set" :
390 elif args.action == "clear" :
392 elif args.action == "print" :