3b5a673a1a2cffda704df5337293f858d967f876
[nepi.git] / examples / ccnx / planetlab_ccnx_unicast.py
1 #!/usr/bin/env python
2
3 ##
4 ## Experiment topology:
5 ## 
6 ##  ccncatchunks                                ccnsendchunks
7 ##       |                                            |
8 ##       .->  node1 -- .. -- nodei -- .. -- nodeN   <-.
9 ##    
10 ##
11 ##  - Nodes are connected through Intenet
12 ##  - On each node runs a CCNx daemon
13 ##  - Static entries are added to the CCNx FIB on each node to communicate them in series.
14 ##    (Nodes only have FIB entries to at most two nodes)
15 ##
16
17 from nepi.core.design import ExperimentDescription, FactoriesProvider
18 from nepi.core.execute import ExperimentController
19 from nepi.util.constants import ApplicationStatus as AS
20 import ipaddr
21 import math
22 from optparse import OptionParser, SUPPRESS_HELP
23 import os
24 import signal
25 import string
26 import subprocess
27 import tempfile
28 import time
29
30 # Trak SIGTERM, and set global termination flag instead of dying
31 TERMINATE = []
32 def _finalize(sig,frame):
33     global TERMINATE
34     TERMINATE.append(None)
35 signal.signal(signal.SIGTERM, _finalize)
36 signal.signal(signal.SIGINT, _finalize)
37
38 def create_slice_desc(slicename, plc_host, pl_user, pl_pwd, pl_ssh_key, 
39         port_base, root_dir, exp_desc):
40     pl_provider = FactoriesProvider("planetlab")
41     slice_desc = exp_desc.add_testbed_description(pl_provider)
42     slice_desc.set_attribute_value("homeDirectory", root_dir)
43     slice_desc.set_attribute_value("slice", slicename)
44     slice_desc.set_attribute_value("sliceSSHKey", pl_ssh_key)
45     slice_desc.set_attribute_value("authUser", pl_user)
46     slice_desc.set_attribute_value("authPass", pl_pwd)
47     slice_desc.set_attribute_value("plcHost", plc_host)
48     slice_desc.set_attribute_value("tapPortBase", port_base)
49     # Kills all running processes before starting the experiment
50     slice_desc.set_attribute_value("dedicatedSlice", True)
51     slice_desc.set_attribute_value("plLogLevel", "DEBUG")
52     return slice_desc
53  
54 def create_node(hostname, pl_inet, slice_desc):
55     pl_node = slice_desc.create("Node")
56     pl_node.set_attribute_value("hostname", hostname)
57     pl_node.set_attribute_value("label", hostname)
58     pl_iface = slice_desc.create("NodeInterface")
59     pl_iface.set_attribute_value("label", "iface_%s" % hostname)
60     pl_iface.connector("inet").connect(pl_inet.connector("devs"))
61     pl_node.connector("devs").connect(pl_iface.connector("node"))
62     return pl_node
63
64 def create_ccnd(pl_node, routes, slice_desc):
65     pl_app = slice_desc.create("CCNxDaemon")
66     # We use a wildcard to replace the public IP address of the node during runtime
67     routes = "|".join(map(lambda route: "udp {#[iface_%s].addr[0].[Address]#}" % route, routes))
68     # Add multicast ccn routes 
69     pl_app.set_attribute_value("ccnroutes", routes)
70     pl_app.enable_trace("stdout")
71     pl_app.enable_trace("stderr")
72     pl_app.connector("node").connect(pl_node.connector("apps"))
73
74 def create_ccnsendchunks(pl_node, slice_desc):
75     pl_app = slice_desc.create("Application")
76     path_to_video = os.path.join(os.path.dirname(os.path.abspath(__file__)),
77         "../big_buck_bunny_240p_mpeg4_lq.ts")
78     pl_app.set_attribute_value("stdin", path_to_video)
79     pl_app.set_attribute_value("command", "ccnsendchunks ccnx:/VIDEO")
80     pl_app.enable_trace("stdout")
81     pl_app.enable_trace("stderr")
82     pl_app.connector("node").connect(pl_node.connector("apps"))
83     return pl_app
84
85 def exec_ccncatchunks(slicename, hostname):
86     print "Getting video chunks from %s ..." % hostname
87     login = "%s@%s" % (slicename, hostname)
88     command = 'PATH=$PATH:$(ls | egrep nepi-ccnd- | head -1)/bin; ccncatchunks2 ccnx:/VIDEO'
89     proc1 = subprocess.Popen(['ssh', login, command], stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell = False)
90     proc2 = subprocess.Popen(['vlc', '-'], stdin=proc1.stdout, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
91     return proc2
92
93 def create_ed(hostnames, vsys_vnet, slicename, plc_host, pl_user, pl_pwd, pl_ssh_key, 
94         port_base, root_dir):
95
96     # Create the experiment description object
97     exp_desc = ExperimentDescription()
98
99     # Create the slice description object
100     slice_desc = create_slice_desc(slicename, plc_host, pl_user, pl_pwd, pl_ssh_key, 
101         port_base, root_dir, exp_desc)
102     
103     # Create the Internet box object
104     pl_inet = slice_desc.create("Internet")
105     
106     # Create the Node boxes
107     pl_nodes = dict()
108     ccn_routes = dict()
109     prev_hostname = None
110     for hostname in hostnames:
111         pl_node = create_node(hostname, pl_inet, slice_desc)
112         pl_nodes[hostname] = pl_node
113
114         ccn_routes[hostname] = list()
115         if prev_hostname:
116             ccn_routes[hostname].append(prev_hostname)
117             ccn_routes[prev_hostname].append(hostname)
118         prev_hostname = hostname
119      
120     for hostname in hostnames:
121         pl_node = pl_nodes[hostname] 
122         routes = ccn_routes[hostname]
123         create_ccnd(pl_node, routes, slice_desc)
124
125     # Create a ccnsendchunks application box in the first node
126     hostname = hostnames[0]
127     pl_node = pl_nodes[hostname]
128     pl_app = create_ccnsendchunks(pl_node, slice_desc)
129
130     return exp_desc, pl_nodes, hostname, pl_app
131
132 def run(hostnames, vsys_vnet, slicename, plc_host, pl_user, pl_pwd, pl_ssh_key, 
133         port_base, root_dir):
134
135     exp_desc, pl_nodes, hostname, pl_app = create_ed(hostnames, vsys_vnet, 
136             slicename, plc_host, pl_user, pl_pwd, pl_ssh_key, port_base, 
137             root_dir)
138
139     xml = exp_desc.to_xml()
140     controller = ExperimentController(xml, root_dir)
141     controller.start()
142     
143     while not TERMINATE and controller.status(pl_app.guid) == AS.STATUS_NOT_STARTED:
144         time.sleep(0.5)
145
146     proc = None
147     if not TERMINATE:
148         hostname = hostnames[-1]
149         proc = exec_ccncatchunks(slicename, hostname)
150
151     while not TERMINATE and proc and proc.poll() is None:
152         time.sleep(0.5)
153     
154     if proc:
155         if proc.poll() < 1:
156            err = proc.stderr.read()
157            print "ERROR ", err
158         else:   
159            out = proc.stdout.read()
160            print "OUTPUT ", out
161
162     controller.stop()
163     controller.shutdown()
164
165 if __name__ == '__main__':
166     root_dir = tempfile.mkdtemp()
167     slicename = os.environ.get("PL_SLICE")
168     pl_host = os.environ.get("PL_HOST", "www.planet-lab.eu")
169     port_base = 2000 + (os.getpid() % 1000) * 13
170     pl_ssh_key = os.environ.get(
171         "PL_SSH_KEY",
172         "%s/.ssh/id_rsa_planetlab" % (os.environ['HOME'],) )
173     pl_user = os.environ.get('PL_USER')
174     pl_pwd = os.environ.get('PL_PASS')
175     pl_vsys_vnet = os.environ.get('PL_VSYS_NET')
176     pl_hostnames = os.environ.get('PL_HOSTNAMES')
177     default_hostnames = ['openlab02.pl.sophia.inria.fr',
178                  'ple4.ipv6.lip6.fr',
179                  'planetlab2.di.unito.it',
180                  'merkur.planetlab.haw-hamburg.de',
181                  'planetlab1.cs.uit.no',
182                  'planetlab3.cs.st-andrews.ac.uk',
183                  'planetlab2.cs.uoi.gr',
184                  'planetlab3.xeno.cl.cam.ac.uk',
185                  'planet2.inf.tu-dresden.de',
186                  'planetlab2.csg.uzh.ch',
187                  'planetlab2.upm.ro',
188                  'planetlab-um00.di.uminho.pt',
189                  'planetlabpc2.upf.edu',
190                  'planet2.elte.hu',
191                  'planetlab2.esprit-tn.com' ]
192
193     usage = "usage: %prog -s <pl_slice> -H <pl_host> -k <ssh_key> -u <pl_user> -p <pl_password> -v <vsys_vnet> -N <host_names> -c <node_count>"
194
195     parser = OptionParser(usage=usage)
196     parser.add_option("-s", "--slicename", dest="slicename", 
197             help="PlanetLab slicename", default=slicename, type="str")
198     parser.add_option("-H", "--pl-host", dest="pl_host", 
199             help="PlanetLab site (e.g. www.planet-lab.eu)", 
200             default=pl_host, type="str")
201     parser.add_option("-k", "--ssh-key", dest="pl_ssh_key", 
202             help="Path to private ssh key used for PlanetLab authentication", 
203             default=pl_ssh_key, type="str")
204     parser.add_option("-u", "--pl-user", dest="pl_user", 
205             help="PlanetLab account user (i.e. Registration email address)", 
206             default=pl_user, type="str")
207     parser.add_option("-p", "--pl-pwd", dest="pl_pwd", 
208             help="PlanetLab account password", default=pl_pwd, type="str")
209     parser.add_option("-v", "--vsys-vnet", dest="vsys_vnet", 
210             help="Value of the vsys_vnet tag addigned to your slice. (e.g. 192.168.3.0/16)", 
211             default=pl_vsys_vnet, type="str")
212     parser.add_option("-N", "--host-names", dest="hostnames", 
213             help="Comma separated list of PlanetLab hostnames to use", 
214             default=pl_hostnames, type="str")
215     parser.add_option("-c", "--node-count", dest="node_count", 
216             help="Number of nodes to use", 
217             default=5, type="str")
218     (options, args) = parser.parse_args()
219
220     hostnames = map(string.strip, options.hostnames.split(",")) if options.hostnames else default_hostnames
221     if options.node_count > 0 and options.node_count < len(hostnames):
222        hostnames = hostnames[0:options.node_count]
223     vsys_vnet = options.vsys_vnet
224     slicename = options.slicename
225     pl_host = options.pl_host
226     pl_user= options.pl_user
227     pl_pwd = options.pl_pwd
228     pl_ssh_key = options.pl_ssh_key
229
230     """
231     hostnames = ['nepi1.pl.sophia.inria.fr',
232         'nepi2.pl.sophia.inria.fr',
233         'nepi3.pl.sophia.inria.fr',
234         'nepi5.pl.sophia.inria.fr']
235
236     pl_host = "nepiplc.pl.sophia.inria.fr"
237     vsys_vnet = "192.168.2.0/24"
238     """
239
240     run(hostnames, vsys_vnet, slicename, pl_host, pl_user, pl_pwd, pl_ssh_key, 
241             port_base, root_dir)
242