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
20 from optparse import OptionParser, SUPPRESS_HELP
28 # Trak SIGTERM, and set global termination flag instead of dying
30 def _finalize(sig,frame):
32 TERMINATE.append(None)
33 signal.signal(signal.SIGTERM, _finalize)
34 signal.signal(signal.SIGINT, _finalize)
36 def create_slice_desc(slicename, plc_host, pl_user, pl_pwd, pl_ssh_key,
37 port_base, root_dir, proxy, exp_desc):
38 pl_provider = FactoriesProvider("planetlab")
39 slice_desc = exp_desc.add_testbed_description(pl_provider)
40 slice_desc.set_attribute_value("homeDirectory", root_dir)
41 slice_desc.set_attribute_value("slice", slicename)
42 slice_desc.set_attribute_value("sliceSSHKey", pl_ssh_key)
43 slice_desc.set_attribute_value("authUser", pl_user)
44 slice_desc.set_attribute_value("authPass", pl_pwd)
45 slice_desc.set_attribute_value("plcHost", plc_host)
47 slice_desc.set_attribute_value("proxy", proxy)
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, port, 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")
73 # We can also specify a custom local source and build and install directives
74 path_to_source = os.path.join(os.path.dirname(os.path.abspath(__file__)),
75 "ccnx-0.6.0rc3.tar.gz")
76 pl_app.set_attribute_value("sources", path_to_source)
77 pl_app.set_attribute_value("build",
78 "tar xzf ${SOURCES}/ccnx-0.6.0rc3.tar.gz && "
79 "cd ./ccnx-0.6.0rc3 && "
80 "./configure && make ")
81 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 # once this IP is known
85 routes = "|".join(map(lambda route: "udp {#[iface_%s].addr[0].[Address]#}" % route, routes))
87 # Add unicast ccn routes
88 pl_app.set_attribute_value("ccnRoutes", routes)
90 # Use a specific port to bind the CCNx daemon
92 pl_app.set_attribute_value("ccnLocalPort", port)
94 pl_app.enable_trace("stdout")
95 pl_app.enable_trace("stderr")
96 pl_app.connector("node").connect(pl_node.connector("apps"))
98 def create_ccnsendchunks(pl_node, port, slice_desc):
99 pl_app = slice_desc.create("Application")
100 path_to_video = os.path.join(os.path.dirname(os.path.abspath(__file__)),
101 "../big_buck_bunny_240p_mpeg4_lq.ts")
102 pl_app.set_attribute_value("stdin", path_to_video)
104 command = "ccnsendchunks ccnx:/VIDEO"
106 command = "CCN_LOCAL_PORT=%d %s " % (port, command)
107 pl_app.set_attribute_value("command", command)
109 pl_app.enable_trace("stdout")
110 pl_app.enable_trace("stderr")
111 pl_app.connector("node").connect(pl_node.connector("apps"))
114 def exec_ccncatchunks(slicename, port, hostname):
115 print "Getting video chunks from %s ..." % hostname
117 command = 'PATH=$PATH:$(ls | egrep nepi-ccnd- | head -1)/bin;'
119 command += "CCN_LOCAL_PORT=%d " % port
120 command += ' ccncatchunks2 ccnx:/VIDEO'
122 login = "%s@%s" % (slicename, hostname)
123 proc1 = subprocess.Popen(['ssh',
124 '-o', 'StrictHostKeyChecking=no',
127 stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell = False)
129 proc2 = subprocess.Popen(['vlc',
130 '--ffmpeg-threads=1',
131 '--sub-filter', 'marq',
133 '(c) copyright 2008, Blender Foundation / www.bigbuckbunny.org',
135 '--no-video-title-show', '-'],
136 stdin=proc1.stdout, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
139 def create_ed(hostnames, vsys_vnet, slicename, plc_host, pl_user, pl_pwd, pl_ssh_key,
140 port_base, root_dir, delay, port, proxy):
142 # Create the experiment description object
143 exp_desc = ExperimentDescription()
145 # Create the slice description object
146 slice_desc = create_slice_desc(slicename, plc_host, pl_user, pl_pwd, pl_ssh_key,
147 port_base, root_dir, proxy, exp_desc)
149 # Create the Internet box object
150 pl_inet = slice_desc.create("Internet")
152 # Create the Node boxes
156 for hostname in hostnames:
157 pl_node = create_node(hostname, pl_inet, slice_desc)
158 pl_nodes[hostname] = pl_node
160 ccn_routes[hostname] = list()
162 ccn_routes[hostname].append(prev_hostname)
163 ccn_routes[prev_hostname].append(hostname)
164 prev_hostname = hostname
166 for hostname in hostnames:
167 pl_node = pl_nodes[hostname]
168 routes = ccn_routes[hostname]
169 create_ccnd(pl_node, port, routes, slice_desc)
171 # Create a ccnsendchunks application box in the first node
172 hostname = hostnames[0]
173 pl_node = pl_nodes[hostname]
174 pl_app = create_ccnsendchunks(pl_node, port, slice_desc)
176 return exp_desc, pl_nodes, hostname, pl_app
178 def run(hostnames, vsys_vnet, slicename, plc_host, pl_user, pl_pwd, pl_ssh_key,
179 port_base, root_dir, delay, port, proxy):
181 exp_desc, pl_nodes, hostname, pl_app = create_ed(hostnames, vsys_vnet,
182 slicename, plc_host, pl_user, pl_pwd, pl_ssh_key, port_base,
183 root_dir, delay, port, proxy)
185 xml = exp_desc.to_xml()
186 controller = ExperimentController(xml, root_dir)
189 while not TERMINATE and controller.status(pl_app.guid) == AS.STATUS_NOT_STARTED:
194 hostname = hostnames[-1]
195 proc1 = exec_ccncatchunks(slicename, port, hostname)
197 if not TERMINATE and proc1:
202 hostname = hostnames[-2]
203 proc2 = exec_ccncatchunks(slicename, port, hostname)
205 while not TERMINATE and proc1 and proc2 and proc2.poll() is None:
210 err = proc1.stderr.read()
211 print "Stream 1 ERROR ", err
213 out = proc1.stdout.read()
214 print "Stream 1 OUTPUT ", out
218 err = proc2.stderr.read()
219 print "Stream 2 ERROR ", err
221 out = proc2.stdout.read()
222 print "Stream 2 OUTPUT ", out
225 controller.shutdown()
227 if __name__ == '__main__':
228 root_dir = tempfile.mkdtemp()
229 slicename = os.environ.get("PL_SLICE")
230 pl_host = os.environ.get("PL_HOST", "www.planet-lab.eu")
231 port_base = 2000 + (os.getpid() % 1000) * 13
232 pl_ssh_key = os.environ.get(
234 "%s/.ssh/id_rsa_planetlab" % (os.environ['HOME'],) )
235 pl_user = os.environ.get('PL_USER')
236 pl_pwd = os.environ.get('PL_PASS')
237 pl_vsys_vnet = os.environ.get('PL_VSYS_NET')
238 pl_hostnames = os.environ.get('PL_HOSTNAMES')
239 default_hostnames = ['openlab02.pl.sophia.inria.fr',
241 'planetlab2.di.unito.it',
242 'merkur.planetlab.haw-hamburg.de',
243 'planetlab1.cs.uit.no',
244 'planetlab3.cs.st-andrews.ac.uk',
245 'planetlab2.cs.uoi.gr',
246 'planetlab3.xeno.cl.cam.ac.uk',
247 'planet2.inf.tu-dresden.de',
248 'planetlab2.csg.uzh.ch',
250 'planetlab-um00.di.uminho.pt',
251 'planetlabpc2.upf.edu',
253 'planetlab2.esprit-tn.com' ]
255 ccn_local_port = os.environ.get('CCN_LOCAL_PORT')
257 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> -P <ccn-local-port> -x <proxy>"
259 parser = OptionParser(usage=usage)
260 parser.add_option("-s", "--slicename", dest="slicename",
261 help="PlanetLab slicename", default=slicename, type="str")
262 parser.add_option("-H", "--pl-host", dest="pl_host",
263 help="PlanetLab site (e.g. www.planet-lab.eu)",
264 default=pl_host, type="str")
265 parser.add_option("-k", "--ssh-key", dest="pl_ssh_key",
266 help="Path to private ssh key used for PlanetLab authentication",
267 default=pl_ssh_key, type="str")
268 parser.add_option("-u", "--pl-user", dest="pl_user",
269 help="PlanetLab account user (i.e. Registration email address)",
270 default=pl_user, type="str")
271 parser.add_option("-p", "--pl-pwd", dest="pl_pwd",
272 help="PlanetLab account password", default=pl_pwd, type="str")
273 parser.add_option("-v", "--vsys-vnet", dest="vsys_vnet",
274 help="Value of the vsys_vnet tag addigned to your slice. (e.g. 192.168.3.0/16)",
275 default=pl_vsys_vnet, type="str")
276 parser.add_option("-N", "--host-names", dest="hostnames",
277 help="Comma separated list of PlanetLab hostnames to use",
278 default=pl_hostnames, type="str")
279 parser.add_option("-c", "--node-count", dest="node_count",
280 help="Number of nodes to use",
281 default=9, type="int")
282 parser.add_option("-d", "--delay", dest="delay",
283 help="Time to wait before retrieveing the second video stream in seconds",
284 default=40, type="int")
285 parser.add_option("-P", "--ccn-local-port", dest="port",
286 help="Port to bind the CCNx daemon",
287 default=ccn_local_port, type="int")
288 parser.add_option("-x", "--proxy", dest="proxy",
289 help="Https proxy between here and PlanetLab machines",
290 default=None, type="str")
291 (options, args) = parser.parse_args()
293 hostnames = map(string.strip, options.hostnames.split(",")) if options.hostnames else default_hostnames
294 if options.node_count > 0 and options.node_count < len(hostnames):
295 hostnames = hostnames[0:options.node_count]
297 vsys_vnet = options.vsys_vnet
298 slicename = options.slicename
299 pl_host = options.pl_host
300 pl_user= options.pl_user
301 pl_pwd = options.pl_pwd
302 pl_ssh_key = options.pl_ssh_key
303 delay = options.delay
305 proxy = options.proxy
307 run(hostnames, vsys_vnet, slicename, pl_host, pl_user, pl_pwd, pl_ssh_key,
308 port_base, root_dir, delay, port, proxy)