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