4 ## Experiment topology:
6 ## ccncatchunks ccnsendchunks
8 ## .-> node1 -- .. -- nodei -- .. -- nodeN <-.
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)
17 from nepi.core.design import ExperimentDescription, FactoriesProvider
18 from nepi.core.execute import ExperimentController
19 from nepi.util.constants import ApplicationStatus as AS
22 from optparse import OptionParser, SUPPRESS_HELP
30 # Trak SIGTERM, and set global termination flag instead of dying
32 def _finalize(sig,frame):
34 TERMINATE.append(None)
35 signal.signal(signal.SIGTERM, _finalize)
36 signal.signal(signal.SIGINT, _finalize)
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 slice_desc.set_attribute_value("p2pDeployment", True)
50 # Kills all running processes before starting the experiment
51 slice_desc.set_attribute_value("cleanProc", True)
52 # NOTICE: Setting 'cleanHome' to 'True' will erase all previous
53 # folders in the sliver Home directory, including result files!
54 #slice_desc.set_attribute_value("cleanHome", True)
55 slice_desc.set_attribute_value("plLogLevel", "DEBUG")
58 def create_node(hostname, pl_inet, slice_desc):
59 pl_node = slice_desc.create("Node")
60 pl_node.set_attribute_value("hostname", hostname)
61 pl_node.set_attribute_value("label", hostname)
62 pl_iface = slice_desc.create("NodeInterface")
63 pl_iface.set_attribute_value("label", "iface_%s" % hostname)
64 pl_iface.connector("inet").connect(pl_inet.connector("devs"))
65 pl_node.connector("devs").connect(pl_iface.connector("node"))
68 def create_ccnd(pl_node, routes, slice_desc):
69 pl_app = slice_desc.create("CCNxDaemon")
71 # We can specify a default ccnx version to be either ccnx-0.5.1 or ccnx-0.6.0
72 #pl_app.set_attribute_value("ccnxversion", "ccnx-0.5.1")
74 # We can also specify a custom local source and build and install directives
75 path_to_source = os.path.join(os.path.dirname(os.path.abspath(__file__)),
76 "ccnx-0.6.0rc3.tar.gz")
77 pl_app.set_attribute_value("sources", path_to_source)
78 pl_app.set_attribute_value("build",
79 "tar xzf ${SOURCES}/ccnx-0.6.0rc3.tar.gz && "
80 "cd ./ccnx-0.6.0rc3 && "
81 "./configure && make ")
82 pl_app.set_attribute_value("install", "cp -r ./ccnx-0.6.0rc3/bin ${SOURCES}")
83 # We use a wildcard to replace the public IP address of the node during runtime
84 routes = "|".join(map(lambda route: "udp {#[iface_%s].addr[0].[Address]#}" % route, routes))
86 # Add multicast ccn routes
87 pl_app.set_attribute_value("ccnroutes", routes)
88 pl_app.enable_trace("stdout")
89 pl_app.enable_trace("stderr")
90 pl_app.connector("node").connect(pl_node.connector("apps"))
92 def create_ccnsendchunks(pl_node, slice_desc):
93 pl_app = slice_desc.create("Application")
94 path_to_video = os.path.join(os.path.dirname(os.path.abspath(__file__)),
95 "../big_buck_bunny_240p_mpeg4_lq.ts")
96 pl_app.set_attribute_value("stdin", path_to_video)
97 pl_app.set_attribute_value("command", "ccnsendchunks ccnx:/VIDEO")
98 pl_app.enable_trace("stdout")
99 pl_app.enable_trace("stderr")
100 pl_app.connector("node").connect(pl_node.connector("apps"))
103 def exec_ccncatchunks(slicename, hostname):
104 print "Getting video chunks from %s ..." % hostname
105 login = "%s@%s" % (slicename, hostname)
106 command = 'PATH=$PATH:$(ls | egrep nepi-ccnd- | head -1)/bin; ccncatchunks2 ccnx:/VIDEO'
107 proc1 = subprocess.Popen(['ssh', login, command], stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell = False)
108 proc2 = subprocess.Popen(['vlc', '-'], stdin=proc1.stdout, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
111 def create_ed(hostnames, vsys_vnet, slicename, plc_host, pl_user, pl_pwd, pl_ssh_key,
112 port_base, root_dir, delay):
114 # Create the experiment description object
115 exp_desc = ExperimentDescription()
117 # Create the slice description object
118 slice_desc = create_slice_desc(slicename, plc_host, pl_user, pl_pwd, pl_ssh_key,
119 port_base, root_dir, exp_desc)
121 # Create the Internet box object
122 pl_inet = slice_desc.create("Internet")
124 # Create the Node boxes
128 for hostname in hostnames:
129 pl_node = create_node(hostname, pl_inet, slice_desc)
130 pl_nodes[hostname] = pl_node
132 ccn_routes[hostname] = list()
134 ccn_routes[hostname].append(prev_hostname)
135 ccn_routes[prev_hostname].append(hostname)
136 prev_hostname = hostname
138 for hostname in hostnames:
139 pl_node = pl_nodes[hostname]
140 routes = ccn_routes[hostname]
141 create_ccnd(pl_node, routes, slice_desc)
143 # Create a ccnsendchunks application box in the first node
144 hostname = hostnames[0]
145 pl_node = pl_nodes[hostname]
146 pl_app = create_ccnsendchunks(pl_node, slice_desc)
148 return exp_desc, pl_nodes, hostname, pl_app
150 def run(hostnames, vsys_vnet, slicename, plc_host, pl_user, pl_pwd, pl_ssh_key,
151 port_base, root_dir, delay):
153 exp_desc, pl_nodes, hostname, pl_app = create_ed(hostnames, vsys_vnet,
154 slicename, plc_host, pl_user, pl_pwd, pl_ssh_key, port_base,
157 xml = exp_desc.to_xml()
158 controller = ExperimentController(xml, root_dir)
161 while not TERMINATE and controller.status(pl_app.guid) == AS.STATUS_NOT_STARTED:
166 hostname = hostnames[-1]
167 proc1 = exec_ccncatchunks(slicename, hostname)
169 if not TERMINATE and proc1:
174 hostname = hostnames[-2]
175 proc2 = exec_ccncatchunks(slicename, hostname)
177 while not TERMINATE and proc1 and proc2 and proc2.poll() is None:
182 err = proc1.stderr.read()
183 print "Stream 1 ERROR ", err
185 out = proc1.stdout.read()
186 print "Stream 1 OUTPUT ", out
190 err = proc2.stderr.read()
191 print "Stream 2 ERROR ", err
193 out = proc2.stdout.read()
194 print "Stream 2 OUTPUT ", out
197 controller.shutdown()
199 if __name__ == '__main__':
200 root_dir = tempfile.mkdtemp()
201 slicename = os.environ.get("PL_SLICE")
202 pl_host = os.environ.get("PL_HOST", "www.planet-lab.eu")
203 port_base = 2000 + (os.getpid() % 1000) * 13
204 pl_ssh_key = os.environ.get(
206 "%s/.ssh/id_rsa_planetlab" % (os.environ['HOME'],) )
207 pl_user = os.environ.get('PL_USER')
208 pl_pwd = os.environ.get('PL_PASS')
209 pl_vsys_vnet = os.environ.get('PL_VSYS_NET')
210 pl_hostnames = os.environ.get('PL_HOSTNAMES')
211 default_hostnames = ['openlab02.pl.sophia.inria.fr',
213 'planetlab2.di.unito.it',
214 'merkur.planetlab.haw-hamburg.de',
215 'planetlab1.cs.uit.no',
216 'planetlab3.cs.st-andrews.ac.uk',
217 #'planetlab2.cs.uoi.gr',
218 'planetlab3.xeno.cl.cam.ac.uk',
219 'planet2.inf.tu-dresden.de',
220 'planetlab2.csg.uzh.ch',
222 'planetlab-um00.di.uminho.pt',
223 'planetlabpc2.upf.edu',
225 'planetlab2.esprit-tn.com' ]
227 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> -d <delay>"
229 parser = OptionParser(usage=usage)
230 parser.add_option("-s", "--slicename", dest="slicename",
231 help="PlanetLab slicename", default=slicename, type="str")
232 parser.add_option("-H", "--pl-host", dest="pl_host",
233 help="PlanetLab site (e.g. www.planet-lab.eu)",
234 default=pl_host, type="str")
235 parser.add_option("-k", "--ssh-key", dest="pl_ssh_key",
236 help="Path to private ssh key used for PlanetLab authentication",
237 default=pl_ssh_key, type="str")
238 parser.add_option("-u", "--pl-user", dest="pl_user",
239 help="PlanetLab account user (i.e. Registration email address)",
240 default=pl_user, type="str")
241 parser.add_option("-p", "--pl-pwd", dest="pl_pwd",
242 help="PlanetLab account password", default=pl_pwd, type="str")
243 parser.add_option("-v", "--vsys-vnet", dest="vsys_vnet",
244 help="Value of the vsys_vnet tag addigned to your slice. (e.g. 192.168.3.0/16)",
245 default=pl_vsys_vnet, type="str")
246 parser.add_option("-N", "--host-names", dest="hostnames",
247 help="Comma separated list of PlanetLab hostnames to use",
248 default=pl_hostnames, type="str")
249 parser.add_option("-c", "--node-count", dest="node_count",
250 help="Number of nodes to use",
251 default=9, type="str")
252 parser.add_option("-d", "--delay", dest="delay",
253 help="Time to wait before retrieveing the second video stream in seconds",
254 default=40, type="int")
255 (options, args) = parser.parse_args()
257 hostnames = map(string.strip, options.hostnames.split(",")) if options.hostnames else default_hostnames
258 if options.node_count > 0 and options.node_count < len(hostnames):
259 hostnames = hostnames[0:options.node_count]
260 vsys_vnet = options.vsys_vnet
261 slicename = options.slicename
262 pl_host = options.pl_host
263 pl_user= options.pl_user
264 pl_pwd = options.pl_pwd
265 pl_ssh_key = options.pl_ssh_key
266 delay = options.delay
268 run(hostnames, vsys_vnet, slicename, pl_host, pl_user, pl_pwd, pl_ssh_key,
269 port_base, root_dir, delay)