working on examples/planetlab_ccnd.py
[nepi.git] / examples / planetlab_ccnd.py
1 #!/usr/bin/env python
2
3 from nepi.core.design import ExperimentDescription, FactoriesProvider
4 from nepi.core.execute import ExperimentController
5 import ipaddr
6 import math
7 from optparse import OptionParser, SUPPRESS_HELP
8 import os
9 import signal
10 import string
11 import tempfile
12 import time
13
14 # Trak SIGTERM, and set global termination flag instead of dying
15 TERMINATE = []
16 def _finalize(sig,frame):
17     global TERMINATE
18     TERMINATE.append(None)
19 signal.signal(signal.SIGTERM, _finalize)
20 signal.signal(signal.SIGINT, _finalize)
21
22 def create_slice_desc(slicename, plc_host, pl_user, pl_pwd, pl_ssh_key, 
23         port_base, root_dir, exp_desc):
24     pl_provider = FactoriesProvider("planetlab")
25     slice_desc = exp_desc.add_testbed_description(pl_provider)
26     slice_desc.set_attribute_value("homeDirectory", root_dir)
27     slice_desc.set_attribute_value("slice", slicename)
28     slice_desc.set_attribute_value("sliceSSHKey", pl_ssh_key)
29     slice_desc.set_attribute_value("authUser", pl_user)
30     slice_desc.set_attribute_value("authPass", pl_pwd)
31     slice_desc.set_attribute_value("plcHost", plc_host)
32     slice_desc.set_attribute_value("tapPortBase", port_base)
33     # Kills all running processes before starting the experiment
34     slice_desc.set_attribute_value("dedicatedSlice", True)
35     # Delets all directories in the slice's home directory of each node
36     # ATTENTION: This will remove result files from previous runs!!
37     slice_desc.set_attribute_value("cleanRoot", True)
38     slice_desc.set_attribute_value("plLogLevel", "DEBUG")
39     return slice_desc
40  
41 def create_node(hostname, pl_inet, slice_desc):
42     pl_node = slice_desc.create("Node")
43     pl_node.set_attribute_value("hostname", hostname)
44     pl_node.set_attribute_value("label", hostname)
45     pl_iface = slice_desc.create("NodeInterface")
46     #pl_iface.set_attribute_value("label", "iface_%s" % node)
47     pl_iface.connector("inet").connect(pl_inet.connector("devs"))
48     pl_node.connector("devs").connect(pl_iface.connector("node"))
49     return pl_node
50
51 def create_tunnel(node, peer, pl_nodes, slice_desc, subnet):
52     pl_node = pl_nodes[node]
53     pl_peer = pl_nodes[peer]
54
55     pl_tun = slice_desc.create("TunInterface")
56     pl_tun.set_attribute_value("label", "tun_%s%s" % (node, peer))
57     pl_node.connector("devs").connect(pl_tun.connector("node"))
58
59     pl_tunpeer = slice_desc.create("TunInterface")
60     pl_tunpeer.set_attribute_value("label", "tun_%s%s" % (peer, node))
61     pl_peer.connector("devs").connect(pl_tunpeer.connector("node"))
62
63     pl_tun.connector("udp").connect(pl_tunpeer.connector("udp"))
64     
65     iterhosts = subnet.iterhosts()
66     addr = iterhosts.next()
67     ip = pl_tun.add_address()
68     ip.set_attribute_value("Address", addr.exploded)
69     ip.set_attribute_value("NetPrefix", subnet.prefixlen)
70
71     peeraddr = iterhosts.next()
72     peerip = pl_tunpeer.add_address()
73     peerip.set_attribute_value("Address", peeraddr.exploded)
74     peerip.set_attribute_value("NetPrefix", subnet.prefixlen)
75
76 def create_ccnd(pl_node, slice_desc):
77     pl_app = slice_desc.create("CCNxDaemon")
78     # Add multicast ccn route
79     pl_app.set_attribute_value("ccnroutes", "udp 224.0.0.204 2869")
80     pl_app.enable_trace("stdout")
81     pl_app.enable_trace("stderr")
82     pl_app.connector("node").connect(pl_node.connector("apps"))
83
84 def create_ccnpoke(pl_node, slice_desc):
85     pl_app = slice_desc.create("Application")
86     path_to_video = os.path.join(os.path.dirname(os.path.abspath(__file__)),
87         "big_buck_bunny_240p_mpeg4_lq.ts")
88     pl_app.set_attribute_value("stdin", path_to_video)
89     pl_app.set_attribute_value("command", "ccnsendchunks ccnx:/VIDEO")
90     pl_app.enable_trace("stdout")
91     pl_app.enable_trace("stderr")
92     pl_app.connector("node").connect(pl_node.connector("apps"))
93
94 def create_local_ccncatchunks(hostname, ccn_bin):
95     path = os.path.join(os.getcwd(), "ccncatchunks.sh")
96     exec_ = ( "export PATH=$PATH:%(path_to_ccn_bin)s ; "
97               "ccndstart ; "
98               "ccndc add ccnx:/ udp %(hostname)s ; " 
99               "ccncatchunks2 ccnx:/VIDEO | vlc -"
100             ) % dict( 
101                     hostname = hostname,
102                     path_to_ccn_bin = ccn_bin,
103                     )
104
105     f = open(path, "w")
106     f.write(exec_)
107     f.close()
108
109 def create_ed(hostnames, vsys_vnet, slicename, plc_host, pl_user, pl_pwd, pl_ssh_key, 
110         port_base, root_dir, ccn_bin):
111
112     # Create the experiment description object
113     exp_desc = ExperimentDescription()
114
115     # Create the slice description object
116     slice_desc = create_slice_desc(slicename, plc_host, pl_user, pl_pwd, pl_ssh_key, 
117         port_base, root_dir, exp_desc)
118     
119     # Create the Internet box object
120     pl_inet = slice_desc.create("Internet")
121     
122     # Create the Node boxes
123     pl_nodes = dict()
124     for hostname in hostnames:
125         pl_node = create_node(hostname, pl_inet, slice_desc)
126         pl_nodes[hostname] = pl_node
127
128     # Get the base network segment (slice vsys_vnet) to assign all the IP addresses
129     # to the virtual interfaces
130     base = ipaddr.IPNetwork(vsys_vnet)
131
132     # Calculate the number of virtual networks required to connect all the nodes 
133     # with all other nodes as the binomial coeficient C(n, 2), with n = #nodes
134     n = len(hostnames)
135     c = math.factorial(n) / (2 * math.factorial(n-2)) 
136
137     # Validate that we can get 'c' /30 subnetworks
138     if c > math.pow(2, (30 - base.prefixlen)):
139         raise RuntimeError("Insufficient address segment %s for experiment", vsys_vnet)
140             
141     # Create the subnetwors iterator 
142     iter_sub = base.iter_subnets(new_prefix=30)
143
144     # Create tunnels between nodes
145     for i, node in enumerate(hostnames):
146         peers = hostnames[i+1:]
147         for peer in peers:
148             subnet = iter_sub.next()
149             create_tunnel(node, peer, pl_nodes, slice_desc, subnet)
150
151     # Create ccnd daemons in all nodes
152     for pl_node in pl_nodes.values():
153         create_ccnd(pl_node, slice_desc)
154
155     # Create a ccnpoke in a chosen node to publish some content
156     hostname = pl_nodes.keys()[0]
157     pl_node = pl_nodes[hostname]
158     create_ccnpoke(pl_node, slice_desc)
159
160     create_local_ccncatchunks(hostname, ccn_bin)
161
162     return exp_desc
163
164 def run(hostnames, vsys_vnet, slicename, plc_host, pl_user, pl_pwd, pl_ssh_key, 
165         port_base, root_dir, ccn_bin):
166
167     exp_desc = create_ed(hostnames, vsys_vnet, slicename, plc_host, pl_user, 
168             pl_pwd, pl_ssh_key, port_base, root_dir, ccn_bin)
169
170     xml = exp_desc.to_xml()
171     controller = ExperimentController(xml, root_dir)
172     controller.start()
173     while not TERMINATE: 
174         time.sleep(0.5)
175
176     controller.stop()
177     controller.shutdown()
178
179 if __name__ == '__main__':
180     root_dir = tempfile.mkdtemp()
181     slicename = os.environ.get("PL_SLICE")
182     pl_host = os.environ.get("PL_HOST", "www.planet-lab.eu")
183     port_base = 2000 + (os.getpid() % 1000) * 13
184     pl_ssh_key = os.environ.get(
185         "PL_SSH_KEY",
186         "%s/.ssh/id_rsa_planetlab" % (os.environ['HOME'],) )
187     pl_user = os.environ.get('PL_USER')
188     pl_pwd = os.environ.get('PL_PASS')
189     pl_vsys_vnet = os.environ.get('PL_VSYS_NET')
190     pl_ccn_bin = os.environ.get('PL_CCN_BIN')
191     pl_hostnames = os.environ.get('PL_HOSTNAMES')
192     default_hostnames = ['openlab02.pl.sophia.inria.fr',
193                  'ple4.ipv6.lip6.fr',
194                  'planetlab2.di.unito.it',
195                  'merkur.planetlab.haw-hamburg.de',
196                  #'planetlab1.cs.uit.no',
197                  #'planetlab3.cs.st-andrews.ac.uk',
198                  #'planetlab2.cs.uoi.gr',
199                  #'planetlab3.xeno.cl.cam.ac.uk',
200                  #'planet2.inf.tu-dresden.de',
201                  #'planetlab2.csg.uzh.ch',
202                  #'planetlab2.upm.ro',
203                  #'planetlab-um00.di.uminho.pt',
204                  #'planetlabpc2.upf.edu',
205                  #'planet2.elte.hu',
206                  'planetlab2.esprit-tn.com' ]
207
208     usage = "usage: %prog -s <pl_slice> -H <pl_host> -b <ccn_bin> -k <ssh_key> -u <pl_user> -p <pl_password> -v <vsys_vnet> -N <host_names> "
209
210     parser = OptionParser(usage=usage)
211     parser.add_option("-s", "--slicename", dest="slicename", 
212             help="PlanetLab slicename", default=slicename, type="str")
213     parser.add_option("-H", "--pl-host", dest="pl_host", 
214             help="PlanetLab site (e.g. www.planet-lab.eu)", 
215             default=pl_host, type="str")
216     parser.add_option("-b", "--ccn-bin", dest="ccn_bin", 
217             help="Path to ccnx bin directory", default=pl_ccn_bin, type="str")
218     parser.add_option("-k", "--ssh-key", dest="pl_ssh_key", 
219             help="Path to private ssh key used for PlanetLab authentication", 
220             default=pl_ssh_key, type="str")
221     parser.add_option("-u", "--pl-user", dest="pl_user", 
222             help="PlanetLab account user (i.e. Registration email address)", 
223             default=pl_user, type="str")
224     parser.add_option("-p", "--pl-pwd", dest="pl_pwd", 
225             help="PlanetLab account password", default=pl_pwd, type="str")
226     parser.add_option("-v", "--vsys-vnet", dest="vsys_vnet", 
227             help="Value of the vsys_vnet tag addigned to your slice. (e.g. 192.168.3.0/16)", 
228             default=pl_vsys_vnet, type="str")
229     parser.add_option("-N", "--host-names", dest="hostnames", 
230             help="Comma separated list of PlanetLab hostnames to use", 
231             default=pl_hostnames, type="str")
232     (options, args) = parser.parse_args()
233
234     hostnames = map(string.strip, options.hostnames.split(",")) if options.hostnames else default_hostnames 
235     vsys_vnet = options.vsys_vnet
236     slicename = options.slicename
237     pl_host = options.pl_host
238     pl_user= options.pl_user
239     pl_pwd = options.pl_pwd
240     pl_ssh_key = options.pl_ssh_key
241     ccn_bin = options.ccn_bin
242
243     run(hostnames, vsys_vnet, slicename, pl_host, pl_user, pl_pwd, pl_ssh_key, 
244             port_base, root_dir, ccn_bin)
245