6dcf4907c9da3805eb8f13b117d397069b8df67c
[nepi.git] / examples / multicast_overlay.py
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3
4 import getpass
5 from optparse import OptionParser
6 import os
7 import sys
8 import shutil
9 import tempfile
10 import time
11 import struct
12 import socket
13 import operator
14 import ipaddr
15 import gzip
16 import random
17 import math
18
19 sys.path.append("../../src")
20
21 from nepi.core.design import ExperimentDescription, FactoriesProvider
22 from nepi.core.execute import ExperimentController
23 from nepi.util import proxy
24 from nepi.util.constants import DeploymentConfiguration as DC, ATTR_NEPI_TESTBED_ENVIRONMENT_SETUP
25 from nepi.testbeds.planetlab import util as plutil
26
27 class PlanetLabMulticastOverlay:
28     testbed_id = "planetlab"
29     slicename = "inria_nepi12"
30     plchost = "www.planet-lab.eu"
31     plkey = os.environ.get(
32             "PL_SSH_KEY",
33             "%s/.ssh/id_rsa_planetlab" % (os.environ['HOME'],) )
34     pluser = os.environ.get("PL_USER")
35     plpass = os.environ.get("PL_PASS")
36     vnet = "192.168.2.0"
37     
38     port_base = 2000 + (os.getpid() % 1000) * 13
39     
40     def setUp(self):
41         self.root_dir = tempfile.mkdtemp()
42         self.__class__.port_base = self.__class__.port_base + 100
43
44     def tearDown(self):
45         try:
46             shutil.rmtree(self.root_dir)
47         except:
48             # retry
49             time.sleep(0.1)
50             shutil.rmtree(self.root_dir)
51
52     def make_experiment_desc(self):
53         testbed_id = self.testbed_id
54         slicename = self.slicename
55         plchost = self.plchost
56         pl_ssh_key = self.plkey
57         pl_user = self.pluser
58         pl_pwd = self.plpass
59
60         exp_desc = ExperimentDescription()
61         pl_provider = FactoriesProvider(testbed_id)
62         pl_desc = exp_desc.add_testbed_description(pl_provider)
63         pl_desc.set_attribute_value("homeDirectory", self.root_dir)
64         pl_desc.set_attribute_value("slice", slicename)
65         pl_desc.set_attribute_value("sliceSSHKey", pl_ssh_key)
66         pl_desc.set_attribute_value("authUser", pl_user)
67         pl_desc.set_attribute_value("authPass", pl_pwd)
68         pl_desc.set_attribute_value("plcHost", plchost)
69         pl_desc.set_attribute_value("tapPortBase", self.port_base)
70         pl_desc.set_attribute_value("p2pDeployment", True)
71         pl_desc.set_attribute_value("dedicatedSlice", True)
72         pl_desc.set_attribute_value("plLogLevel", "INFO")
73    
74         netns_provider = FactoriesProvider("netns")
75         netns = exp_desc.add_testbed_description(netns_provider)
76         netns.set_attribute_value("homeDirectory", self.root_dir)
77         netns.set_attribute_value(DC.DEPLOYMENT_MODE, DC.MODE_DAEMON)
78         netns_root_dir = os.path.join(self.root_dir, "netns")
79         os.mkdir(netns_root_dir)
80         netns.set_attribute_value(DC.ROOT_DIRECTORY, netns_root_dir)
81         netns.set_attribute_value(DC.LOG_LEVEL, DC.DEBUG_LEVEL)
82         netns.set_attribute_value(DC.USE_SUDO, True)
83
84         return pl_desc, netns, exp_desc
85
86     def make_pl_tapnode(self, pl, ip, inet = None, label = None, hostname = None, routes = None, mcast = False, mcastrouter = False):
87         if not isinstance(ip, list):
88             ips = [ip]
89         else:
90             ips = ip
91         node1 = pl.create("Node")
92         if label: 
93             node1.set_attribute_value("label", label)
94         if hostname: 
95             node1.set_attribute_value("hostname", hostname)
96         iface1 = pl.create("NodeInterface")
97         if label:
98             iface1.set_attribute_value("label", label+"iface")
99         tap1 = []
100         tap1ip = []
101         for i,ip in enumerate(ips):
102             _tap1 = pl.create("TapInterface")
103             _tap1.set_attribute_value("multicast", True)
104             _tap1.enable_trace("pcap") # for error output
105             if label:
106                 _tap1.set_attribute_value("label", label+"tap"+(str(i+1) if i else ""))
107         
108             _tap1ip = self.add_ip_address(_tap1, ip, 32)
109             
110             node1.connector("devs").connect(_tap1.connector("node"))
111             
112             tap1.append(_tap1)
113             tap1ip.append(_tap1ip)
114             
115         inet = inet or pl.create("Internet")
116         node1.connector("devs").connect(iface1.connector("node"))
117         iface1.connector("inet").connect(inet.connector("devs"))
118         
119         for destip, destprefix, nexthop in routes:
120             r1 = self.add_route(node1, destip, destprefix, nexthop)
121         
122         if mcast:
123             fwd = pl.create("MulticastForwarder")
124             fwd.enable_trace("stderr")
125             fwd.connector("node").connect(node1.connector("apps"))
126             if mcastrouter:
127                 mrt = pl.create("MulticastRouter")
128                 mrt.connector("fwd").connect(fwd.connector("router"))
129                 mrt.enable_trace("stderr")
130                 
131         return node1, iface1, tap1, tap1ip, inet
132
133     def add_ip_address(self, iface, address, netprefix, broadcast = False):
134         ip = iface.add_address()
135         ip.set_attribute_value("Address", address)
136         ip.set_attribute_value("NetPrefix", netprefix)
137         ip.set_attribute_value("Broadcast", broadcast)
138         return ip
139
140     def add_route(self, node, destination, netprefix, nexthop):
141         route = node.add_route()
142         route.set_attribute_value("Destination", destination)
143         route.set_attribute_value("NetPrefix", netprefix)
144         route.set_attribute_value("NextHop", nexthop)
145         return route
146
147     def add_vlc_base(self, pl, node):
148         app = pl.create("Application")
149         app.set_attribute_value("rpmFusion", True)
150         app.set_attribute_value("depends", "vlc")
151         app.set_attribute_value("command", "vlc --version")
152         app.enable_trace("stdout")
153         app.enable_trace("stderr")
154         node.connector("apps").connect(app.connector("node"))
155         return app
156     
157     def add_vlc_restreamer(self, pl, node):
158         hostname = node.get_attribute_value("hostname")
159         app = self.add_vlc_base(pl, node)
160         app.set_attribute_value("label","vlc_restreamer_%d" % (node.guid,))
161         app.set_attribute_value("command",
162             "vlc -vvv -I dummy"
163             " udp://@239.255.12.42"
164             " --sout '#rtp{port=6060,sdp=rtsp://"+hostname+":8080/test.sdp}'")
165         return app
166     
167     def add_vlc_dumper(self, pl, node):
168         app = self.add_vlc_base(pl, node)
169         app.set_attribute_value("label","vlc_dumper_%d" % (node.guid,))
170         app.set_attribute_value("command",
171             "vlc -vvv -I dummy"
172             " udp://@239.255.12.42"
173             " --sout output")
174         app.enable_trace("output")
175         return app
176     
177     def add_vlc_source(self, netns, node, iflabel):
178         app = netns.create("Application")
179         app.set_attribute_value("user", self.user)
180         app.set_attribute_value("label","vlc_source_%d" % (node.guid,))
181         app.set_attribute_value("command",
182             "vlc -vvv -I dummy "
183             +os.path.basename(self.movie_source)
184             +"--miface-addr {#[%s].addr[0].[Address]#} " % (iflabel,)
185             +"--sout '#udp{dst=239.255.12.42,ttl=64}'")
186         app.connector("node").connect(node.connector("apps"))
187         return app
188     
189     def add_net_monitor(self, pl, node):
190         app = pl.create("Application")
191         app.set_attribute_value("label","network_monitor_%d" % (node.guid,))
192         app.set_attribute_value("command", 
193             r"""head -n 2 /proc/net/dev ; while true ; do cat /proc/net/dev | sed -r 's/.*/'"$(date -R)"': \0/' | grep eth0 ; sleep 1 ; done""")
194         app.enable_trace("stdout")
195         node.connector("apps").connect(app.connector("node"))
196         return app
197     
198     def make_ns_in_pl(self, pl, exp, node1, iface1, root):
199         ns3_testbed_id = "ns3"
200         
201         # Add NS3 support in node1
202         plnepi = pl.create("NepiDependency")
203         plns3 = pl.create("NS3Dependency")
204         plnepi.connector("node").connect(node1.connector("deps"))
205         plns3.connector("node").connect(node1.connector("deps"))
206
207         # Create NS3 testbed running in node1
208         ns3_provider = FactoriesProvider(ns3_testbed_id)
209         ns = exp.add_testbed_description(ns3_provider)
210         ns.set_attribute_value("rootDirectory", root)
211         ns.set_attribute_value("SimulatorImplementationType", "ns3::RealtimeSimulatorImpl")
212         ns.set_attribute_value("ChecksumEnabled", True)
213         ns.set_attribute_value(DC.DEPLOYMENT_HOST, "{#[%s].addr[0].[Address]#}" % (
214             iface1.get_attribute_value("label"),))
215         ns.set_attribute_value(DC.DEPLOYMENT_USER, 
216             pl.get_attribute_value("slice"))
217         ns.set_attribute_value(DC.DEPLOYMENT_KEY, 
218             pl.get_attribute_value("sliceSSHKey"))
219         ns.set_attribute_value(DC.DEPLOYMENT_MODE, DC.MODE_DAEMON)
220         ns.set_attribute_value(DC.DEPLOYMENT_COMMUNICATION, DC.ACCESS_SSH)
221         ns.set_attribute_value(DC.DEPLOYMENT_ENVIRONMENT_SETUP,
222             "{#[%s].[%s]#}" % (
223                 node1.get_attribute_value("label"),
224                 ATTR_NEPI_TESTBED_ENVIRONMENT_SETUP,))
225         ns.set_attribute_value(DC.LOG_LEVEL, DC.DEBUG_LEVEL)
226         
227         return ns
228   
229     def add_pl_ns_node(self, pl_desc, inet, label_prefix):
230         node = pl_desc.create("Node")
231         node.set_attribute_value("label", label_prefix)
232         iface = pl_desc.create("NodeInterface")
233         iface.set_attribute_value("label", label_prefix+"iface")
234         iface.connector("inet").connect(inet.connector("devs"))
235         node.connector("devs").connect(iface.connector("node"))
236         forwarder = pl_desc.create("MulticastForwarder")
237         forwarder.enable_trace("stderr")
238         node.connector("apps").connect(forwarder.connector("node"))
239         return node, iface
240
241     def add_pl_ns_connection(self, pl_desc, pl_node, pl_addr,
242             ns, ns_node, ns_addr):
243         pl_tap = pl_desc.create("TapInterface")
244         pl_tap.set_attribute_value("tun_cipher", "PLAIN") 
245         self.add_ip_address(pl_tap, pl_addr, 31)
246         pl_node.connector("devs").connect(pl_tap.connector("node"))
247         ns_fdnd = ns.create("ns3::FdNetDevice")
248         ns_node.connector("devs").connect(ns_fdnd.connector("node"))
249         self.add_ip_address(ns_fdnd, ns_addr, 31)
250         pl_tap.connector("fd->").connect(ns_fdnd.connector("->fd"))
251
252     def add_pl_ns_tunchan_connection(self, pl_desc, pl_node, pl_addr,
253             ns, ns_node, ns_addr):
254         pl_tap = pl_desc.create("TunInterface")
255         self.add_ip_address(pl_tap, pl_addr, 31)
256         pl_node.connector("devs").connect(pl_tap.connector("node"))
257         ns_fdnd = ns.create("ns3::FdNetDevice")
258         ns_fdnd.enable_trace("FdPcapTrace")
259         self.add_ip_address(ns_fdnd, ns_addr, 31)
260         ns_node.connector("devs").connect(ns_fdnd.connector("node"))
261         ns_tc = ns.create("ns3::Nepi::TunChannel")
262         ns_tc.connector("fd->").connect(ns_fdnd.connector("->fd"))
263         pl_tap.connector("tcp").connect(ns_tc.connector("tcp"))
264
265     def make_netns_node(self, netns):
266         node = netns.create("Node")
267         node.set_attribute_value("forward_X11", True)
268         command = "xterm" 
269         app = netns.create("Application")
270         app.set_attribute_value("command", command)
271         app.set_attribute_value("user", self.user)
272         app.connector("node").connect(node.connector("apps"))
273         return node
274
275     def make_pl_netns_connection(self, pl_desc, pl_node, netns,
276             netns_node, netns_iface_label):
277         base=struct.unpack('!L',socket.inet_aton(self.vnet))[0]
278         netns_addr = socket.inet_ntoa(struct.pack('!L',(base | 1)))
279         pl_addr = socket.inet_ntoa(struct.pack('!L',(base | 2)))
280         pl_tap = pl_desc.create("TunInterface")
281         pl_tap.set_attribute_value("multicast", True) 
282         #pl_tap.set_attribute_value("tun_cipher", "PLAIN") 
283         #pl_tap.enable_trace("pcap")
284         #pl_tap.enable_trace("packets")
285         self.add_ip_address(pl_tap, pl_addr, 31)
286         pl_node.connector("devs").connect(pl_tap.connector("node"))
287         
288         netns_tap = netns.create("TunNodeInterface")
289         netns_tap.set_attribute_value("label", netns_iface_label)
290         netns_tap.set_attribute_value("up", True)
291         netns_tap.set_attribute_value("mtu", 1448)
292         self.add_ip_address(netns_tap, netns_addr, 31)
293         self.add_route(netns_node, self.vnet, 24, pl_addr)
294         netns_node.connector("devs").connect(netns_tap.connector("node"))
295
296         netns_tunchannel = netns.create("TunChannel")
297         #netns_tunchannel.set_attribute_value("tun_cipher", "PLAIN") 
298         netns_tunchannel.connector("->fd").connect(netns_tap.connector("fd->"))
299         pl_tap.connector("tcp").connect(netns_tunchannel.connector("tcp"))
300
301     def add_ns_fdnd(self, ns, node):
302         fdnd = ns.create("ns3::FdNetDevice")
303         node.connector("devs").connect(fdnd.connector("node"))
304         #fdnd.enable_trace("FdPcapTrace")
305         return fdnd
306
307     def add_ns_node(self, ns):
308         node = ns.create("ns3::Node")
309         ipv4 = ns.create("ns3::Ipv4L3Protocol")
310         arp  = ns.create("ns3::ArpL3Protocol")
311         icmp = ns.create("ns3::Icmpv4L4Protocol")
312         udp = ns.create("ns3::UdpL4Protocol")
313         node.connector("protos").connect(ipv4.connector("node"))
314         node.connector("protos").connect(arp.connector("node"))
315         node.connector("protos").connect(icmp.connector("node"))
316         node.connector("protos").connect(udp.connector("node"))
317         return node
318
319     def add_ns_wifi_dev(self, ns, node, access_point = False):
320         wifi = ns.create("ns3::WifiNetDevice")
321         node.connector("devs").connect(wifi.connector("node"))
322
323         phy = ns.create("ns3::YansWifiPhy")
324         error = ns.create("ns3::NistErrorRateModel")
325         manager = ns.create("ns3::ArfWifiManager")
326         if access_point:
327             mac = ns.create("ns3::ApWifiMac")
328         else:
329             mac = ns.create("ns3::StaWifiMac")
330
331         phy.set_attribute_value("Standard", "WIFI_PHY_STANDARD_80211a")
332         mac.set_attribute_value("Standard", "WIFI_PHY_STANDARD_80211a")
333         phy.connector("err").connect(error.connector("phy"))
334         wifi.connector("phy").connect(phy.connector("dev"))
335         wifi.connector("mac").connect(mac.connector("dev"))
336         wifi.connector("manager").connect(manager.connector("dev"))
337
338         #phy.enable_trace("YansWifiPhyPcapTrace")
339         return wifi, phy
340
341     def add_ns_constant_mobility(self, ns, node, x, y, z):
342         mobility = ns.create("ns3::ConstantPositionMobilityModel") 
343         position = "%d:%d:%d" % (x, y, z)
344         mobility.set_attribute_value("Position", position)
345         node.connector("mobility").connect(mobility.connector("node"))
346         return mobility
347
348     def add_ns_wifi_channel(self, ns):
349         channel = ns.create("ns3::YansWifiChannel")
350         delay = ns.create("ns3::ConstantSpeedPropagationDelayModel")
351         loss  = ns.create("ns3::LogDistancePropagationLossModel")
352         channel.connector("delay").connect(delay.connector("chan"))
353         channel.connector("loss").connect(loss.connector("prev"))
354         return channel
355
356     def make_ns_wifi(self, ns, pl, pl_ns_root, inet, numwifinodes, nextip): 
357         base=struct.unpack('!L',socket.inet_aton(self.vnet))[0]
358         error = False
359         for i in xrange(2, 6):
360             nr = int(math.pow(2, i))
361             if nr <= (numwifinodes + 2):
362                 break
363         else:
364             error = True
365         
366         # how many IPs will we need?
367         # 1 for the AP, 2 for each station and one for each extra PL node
368         # BUT we need to also reserve IPs to sum up to a posible subnetwork
369         # number of nodes: 2, 4, 8, 16, etc ...
370         # And finally, we need 2 extra IPs for the PL-AP iface
371        
372         nrips = (1 + 2*numwifinodes + nr + 2)
373         if nrips + nextip[0] > 255:
374             error = True
375         if error:
376             raise RuntimeError("There are not enough IP addresses for the wireless network", )
377         
378         netprefix = 32 - i
379         _nextwifiip = [254]
380         def nextwifiip():
381             ip = socket.inet_ntoa(struct.pack('!L',(base | _nextwifiip[0])))
382             _nextwifiip[0] -= 1
383             return ip
384
385         _nextnstapip = [(254 - nr -1)]
386         def nextnstapip():
387             ip = socket.inet_ntoa(struct.pack('!L',(base | _nextnstapip[0])))
388             _nextnstapip[0] -= 1
389             return ip
390
391         _nexttapip = [(254 - nr - 1 - numwifinodes)]
392         def nexttapip():
393             ip = socket.inet_ntoa(struct.pack('!L',(base | _nexttapip[0])))
394             _nexttapip[0] -= 1
395             return ip
396
397         # WIFI network
398         wifi_chan = self.add_ns_wifi_channel(ns)
399         
400         # AP node
401         ap_node = self.add_ns_node(ns)
402         self.add_ns_constant_mobility(ns, ap_node, 0, 0, 0)
403         ap_wifi, ap_phy = self.add_ns_wifi_dev(ns, ap_node, access_point = True)
404         ap_phy.connector("chan").connect(wifi_chan.connector("phys"))
405
406         # connect AP to PL
407         _nextplip = (254 - nrips)
408         pl_ip = socket.inet_ntoa(struct.pack('!L',(base | _nextplip)))
409         print "PL IP %s" % pl_ip
410         _nextplip -= 1
411         ns_ip = socket.inet_ntoa(struct.pack('!L',(base | _nextplip)))
412         print "NS IP %s" % ns_ip
413         self.add_pl_ns_connection(pl, pl_ns_root, pl_ip, ns, ap_node, ns_ip)
414
415         # routes in and out ns
416         self.add_route(ap_node, self.vnet, 24, pl_ip)
417         net = 256 - nr
418         ip = socket.inet_ntoa(struct.pack('!L',(base | net)))
419         self.add_route(pl_ns_root, ip, netprefix, ns_ip)
420         
421         ap_ip = nextwifiip()
422         print "AP IP %s" % ap_ip
423         self.add_ip_address(ap_wifi, ap_ip, netprefix)
424         
425         r = 50
426         # STA nodes
427         for i in xrange(0, numwifinodes):
428             stai = self.add_ns_node(ns)
429             angi = (360/numwifinodes)*i
430             xi = r*math.cos(angi)
431             yi = r*math.sin(angi)
432             self.add_ns_constant_mobility(ns, stai, xi, yi, 0)
433             wifi, phy = self.add_ns_wifi_dev(ns, stai, access_point = False)
434             phy.connector("chan").connect(wifi_chan.connector("phys"))
435             
436             wifi_ip = nextwifiip()
437             print "WIFI IP %s" % wifi_ip
438             self.add_ip_address(wifi, wifi_ip, netprefix)
439             self.add_route(stai, self.vnet, 24, ap_ip)
440             
441             """
442             pl_nodei, pl_ifacei = self.add_pl_ns_node(pl, inet, 
443                     "node2%d_pl"%i)
444            
445             pl_addr = (self.base_addr%(net+1))
446             ns3_addr = (self.base_addr%(net+2))
447             self.add_pl_ns_tunchan_connection(pl_desc, pl_nodei, pl_addr,
448                 ns, stai, ns3_addr)
449             self.add_route(pl_nodei, (self.base_addr%32), 27, ns3_addr)
450             self.add_route(pl_nodei, (self.base_addr%0), 30, ns3_addr)
451             self.add_route(pl_nodei, (self.base_addr%4), 30, ns3_addr)
452
453             network = (self.base_addr%net)
454             self.add_route(netns_node, network, 30, (self.base_addr%2))
455             self.add_route(pl_node1, network, 30, (self.base_addr%6))
456             self.add_route(ap_node, network, 30, wifi_addr)
457             """
458
459     def make_pl_overlay(self, numnodes, numwifinodes):
460         print "make_pl_overlay ..."
461         ns3_testbed_id = "ns3"
462         
463         pl, netns, exp = self.make_experiment_desc()
464         # We'll make a distribution spanning tree using prefix matching as a distance
465         api = plutil.getAPI(self.pluser, self.plpass)
466         nodes = plutil.getNodes(api, numnodes, operatingSystem = 'f12')
467         root = min(nodes, key=operator.attrgetter('hostname'))
468         links = list(plutil.getSpanningTree(nodes, root=root))
469       
470         for node in nodes:
471             node.vif_ips = set()
472             node.children = []
473             node.childips = set()
474         
475         # Build an explicit tree
476         for slave, master in links:
477             master.children.append(slave)
478         
479         # We have to assign IPs and routes.
480         # The IP will be assigned sequentially, depth-first.
481         # This will result in rather compact routing rules
482         nextip = [128-numnodes]
483         def traverse(traverse, node, parent=None, base=struct.unpack('!L',socket.inet_aton(self.vnet))[0]):
484             if nextip[0] >= 254:
485                 raise RuntimeError, "Too many IPs to assign!"
486             
487             node.vif_addr = base | (nextip[0])
488             nips = 1+len(node.children) # one vif per child, plus one for the parent
489             nextip[0] += nips
490             
491             for i in xrange(nips):
492                 node.vif_ips.add(node.vif_addr+i)
493
494             if parent:
495                 parent.childips.update(node.vif_ips)
496
497             for i,child in enumerate(node.children):
498                 traverse(traverse, child, node, base)
499                 
500             if parent:
501                 parent.childips.update(node.childips)
502                 
503         print "traverse..."
504         traverse(traverse, root)
505         
506         def printtree(printtree, node, indent=''):
507             print indent, '-', socket.inet_ntoa(struct.pack('!L',node.vif_addr)), node.country, node.city, node.site
508             for child in node.children:
509                 childips = map(ipaddr.IPAddress, child.childips)
510                 childnets = ipaddr.collapse_address_list(childips)
511                 cip = ipaddr.IPAddress(child.vif_addr)
512                 for cnet in childnets:
513                     print indent, '|- R', cnet, '->', cip
514                 printtree(printtree, child, indent+' | ')
515         printtree(printtree, root)
516         
517         inet = pl.create("Internet")
518        
519         ns_chosen = []
520
521         def maketree(maketree, node, parent=None, parentIp=None):
522             routes = []
523             ctaps = []
524             for i,child in enumerate(node.children):
525                 childips = map(ipaddr.IPAddress, child.childips)
526                 childnets = ipaddr.collapse_address_list(childips)
527                 cip = ipaddr.IPAddress(child.vif_addr)
528                 pip = ipaddr.IPAddress(node.vif_addr+1+i)
529                 for cnet in childnets:
530                     routes.append((cnet.ip.exploded, cnet.prefixlen, cip.exploded))
531                 ctaps.append( maketree(maketree, child, node, pip) )
532
533             if parentIp:
534                 routes.append((self.vnet,24,parentIp))
535             
536             if not parent:
537                 label = "root"
538             else:
539                 label = None
540                 if not ns_chosen and node.children:
541                     ns_chosen.append(True)
542                     label = "ns_root"
543             ips = [ ipaddr.IPAddress(node.vif_addr+i) for i in xrange(1+len(node.children)) ]
544             node1, iface1, tap1, tap1ip, _ = self.make_pl_tapnode(pl, ips, inet, 
545                 hostname = node.hostname,
546                 routes = routes,
547                 mcastrouter = bool(node.children),
548                 mcast = True,
549                 label = label )
550             
551             for tap, ctap in zip(tap1[1:], ctaps):
552                 tap.connector("udp").connect(ctap.connector("udp"))
553
554             self.add_net_monitor(pl, node1)
555             self.add_vlc_restreamer(pl, node1)
556             if random.random() < 0.1 and parent:
557                 self.add_vlc_dumper(pl, node1)
558             
559             return tap1[0]
560         
561         print "maketree..."
562         maketree(maketree, root)
563
564         # create a netns node and connect it to the root pl node
565         pl_root = exp.get_element_by_label("root")
566         netns_source = self.make_netns_node(netns)
567         iflabel = "source-iface"
568         self.make_pl_netns_connection(pl, pl_root, netns, 
569                 netns_source, iflabel)
570         self.add_vlc_source(netns, netns_source, iflabel)
571  
572         # add ns wireless network 
573         pl_ns_root = exp.get_element_by_label("ns_root")
574         pl_ns_root_iface = exp.get_element_by_label("ns_rootiface")
575         ns = self.make_ns_in_pl(pl, exp, pl_ns_root, pl_ns_root_iface, "ns3")
576         self.make_ns_wifi(ns, pl, pl_ns_root, inet, numwifinodes, nextip)
577
578         xml = exp.to_xml()
579         test_dir = "./results"
580
581         try:
582             controller = ExperimentController(xml, self.root_dir)
583             controller.start()
584             
585             print >>sys.stderr, "Press CTRL-C to shut down"
586             try:
587                 while True:
588                     time.sleep(10)
589             except KeyboardInterrupt:
590                 pass
591             
592             # download results
593             for testbed_guid, guids in controller.traces_info().iteritems():
594                 for guid, traces in guids.iteritems():
595                     for name, data in traces.iteritems():
596                         path = data["filepath"]
597                         
598                         if not path:
599                             continue
600                         
601                         print >>sys.stderr, "Downloading trace", path
602                         
603                         filepath = os.path.join(test_dir, path)
604                         
605                         try:
606                             trace = controller.trace(guid, name)
607                         except:
608                             traceback.print_exc(file=sys.stderr)
609                             continue
610                         try:
611                             if not os.path.exists(os.path.dirname(filepath)):
612                                 os.makedirs(os.path.dirname(filepath))
613                         except:
614                             traceback.print_exc(file=sys.stderr)
615                         
616                         try:
617                             if len(trace) >= 2**20:
618                                 # Bigger than 1M, compress
619                                 tracefile = gzip.GzipFile(filepath+".gz", "wb")
620                             else:
621                                 tracefile = open(filepath,"wb")
622                             try:
623                                 tracefile.write(trace)
624                             finally:
625                                 tracefile.close()
626                         except:
627                             traceback.print_exc(file=sys.stderr)
628         finally:
629             try:
630                 controller.stop()
631             except:
632                 import traceback
633                 traceback.print_exc()
634             try:
635                 controller.shutdown()
636             except:
637                 import traceback
638                 traceback.print_exc()
639
640
641 if __name__ == '__main__':
642     usage = "usage: %prog -n number_sta -m movie -u user"
643     parser = OptionParser(usage=usage)
644     parser.add_option("-u", "--user", dest="user", help="Valid linux system user (not root).", type="str", default=os.getlogin())
645     parser.add_option("-m", "--movie", dest="movie", help="Path to movie file to play", type="str")
646     parser.add_option("-n", "--nsta", dest="nsta", help="Number of wifi stations", type="int")
647     parser.add_option("-N", "--nodes", dest="nsta", help="Number of overlay nodes", type="int")
648     parser.add_option("-a", "--base_addr", dest="base_addr", help="Base address segment for the experiment", type="str")
649     parser.add_option("-s", "--slicename", dest="slicename", help="PlanetLab slice", type="str")
650     (options, args) = parser.parse_args()
651     if not options.movie:
652         parser.error("Missing 'movie' option.")
653     if options.user == 'root':
654         parser.error("Missing or invalid 'user' option.")
655     if options.nsta and options.nsta > 8:
656         parser.error("Try a number of stations under 9.")
657
658     exp = PlanetLabMulticastOverlay()
659     exp.movie_source = options.movie
660     exp.user = options.user
661     try:
662         exp.setUp()
663         exp.make_pl_overlay(5, 2)
664     finally:
665         exp.tearDown()
666