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, proxy, 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)
49 slice_desc.set_attribute_value("proxy", proxy)
50 slice_desc.set_attribute_value("tapPortBase", port_base)
51 slice_desc.set_attribute_value("p2pDeployment", True)
52 # Kills all running processes before starting the experiment
53 slice_desc.set_attribute_value("cleanProc", True)
54 # NOTICE: Setting 'cleanHome' to 'True' will erase all previous
55 # folders in the sliver Home directory, including result files!
56 #slice_desc.set_attribute_value("cleanHome", True)
57 slice_desc.set_attribute_value("plLogLevel", "DEBUG")
60 def create_node(hostname, pl_inet, slice_desc):
61 pl_node = slice_desc.create("Node")
62 pl_node.set_attribute_value("hostname", hostname)
63 pl_node.set_attribute_value("label", hostname)
64 pl_iface = slice_desc.create("NodeInterface")
65 pl_iface.set_attribute_value("label", "iface_%s" % hostname)
66 pl_iface.connector("inet").connect(pl_inet.connector("devs"))
67 pl_node.connector("devs").connect(pl_iface.connector("node"))
70 def create_ccnd(pl_node, port, routes, slice_desc):
71 pl_app = slice_desc.create("CCNxDaemon")
73 # We can specify a default ccnx version to be either ccnx-0.5.1 or ccnx-0.6.0
74 #pl_app.set_attribute_value("ccnxVersion", "ccnx-0.5.1")
75 # We can also specify a custom local source and build and install directives
76 path_to_source = os.path.join(os.path.dirname(os.path.abspath(__file__)),
77 "ccnx-0.6.0rc3.tar.gz")
78 pl_app.set_attribute_value("sources", path_to_source)
79 pl_app.set_attribute_value("build",
80 "tar xzf ${SOURCES}/ccnx-0.6.0rc3.tar.gz && "
81 "cd ./ccnx-0.6.0rc3 && "
82 "./configure && make ")
83 pl_app.set_attribute_value("install", "cp -r ./ccnx-0.6.0rc3/bin ${SOURCES}")
85 # We use a wildcard to replace the public IP address of the node during runtime,
86 # once this IP is known
87 routes = "|".join(map(lambda route: "udp {#[iface_%s].addr[0].[Address]#}" % route, routes))
89 # Add unicast ccn routes
90 pl_app.set_attribute_value("ccnRoutes", routes)
92 # Use a specific port to bind the CCNx daemon
94 pl_app.set_attribute_value("ccnLocalPort", port)
96 pl_app.enable_trace("stdout")
97 pl_app.enable_trace("stderr")
98 pl_app.connector("node").connect(pl_node.connector("apps"))
100 def create_ccnsendchunks(pl_node, port, slice_desc):
101 pl_app = slice_desc.create("Application")
102 path_to_video = os.path.join(os.path.dirname(os.path.abspath(__file__)),
103 "../big_buck_bunny_240p_mpeg4_lq.ts")
104 pl_app.set_attribute_value("stdin", path_to_video)
106 command = "ccnsendchunks ccnx:/VIDEO"
108 command = "CCN_LOCAL_PORT=%d %s " % (port, command)
109 pl_app.set_attribute_value("command", command)
111 pl_app.enable_trace("stdout")
112 pl_app.enable_trace("stderr")
113 pl_app.connector("node").connect(pl_node.connector("apps"))
116 def exec_ccncatchunks(slicename, port, hostname):
117 print "Getting video chunks from %s ..." % hostname
119 command = 'PATH=$PATH:$(ls | egrep nepi-ccnd- | head -1)/bin;'
121 command += "CCN_LOCAL_PORT=%d " % port
122 command += ' ccncatchunks2 ccnx:/VIDEO'
124 login = "%s@%s" % (slicename, hostname)
125 proc1 = subprocess.Popen(['ssh', login, command], stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell = False)
127 proc2 = subprocess.Popen(['vlc',
128 '--sub-filter', 'marq',
130 '(c) copyright 2008, Blender Foundation / www.bigbuckbunny.org',
132 '--no-video-title-show', '-'],
133 stdin=proc1.stdout, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
136 def create_ed(hostnames, vsys_vnet, slicename, plc_host, pl_user, pl_pwd, pl_ssh_key,
137 port_base, root_dir, delay, port, proxy):
139 # Create the experiment description object
140 exp_desc = ExperimentDescription()
142 # Create the slice description object
143 slice_desc = create_slice_desc(slicename, plc_host, pl_user, pl_pwd, pl_ssh_key,
144 port_base, root_dir, proxy, exp_desc)
146 # Create the Internet box object
147 pl_inet = slice_desc.create("Internet")
149 # Create the Node boxes
153 for hostname in hostnames:
154 pl_node = create_node(hostname, pl_inet, slice_desc)
155 pl_nodes[hostname] = pl_node
157 ccn_routes[hostname] = list()
159 ccn_routes[hostname].append(prev_hostname)
160 ccn_routes[prev_hostname].append(hostname)
161 prev_hostname = hostname
163 for hostname in hostnames:
164 pl_node = pl_nodes[hostname]
165 routes = ccn_routes[hostname]
166 create_ccnd(pl_node, port, routes, slice_desc)
168 # Create a ccnsendchunks application box in the first node
169 hostname = hostnames[0]
170 pl_node = pl_nodes[hostname]
171 pl_app = create_ccnsendchunks(pl_node, port, slice_desc)
173 return exp_desc, pl_nodes, hostname, pl_app
175 def run(hostnames, vsys_vnet, slicename, plc_host, pl_user, pl_pwd, pl_ssh_key,
176 port_base, root_dir, delay, port, proxy):
178 exp_desc, pl_nodes, hostname, pl_app = create_ed(hostnames, vsys_vnet,
179 slicename, plc_host, pl_user, pl_pwd, pl_ssh_key, port_base,
180 root_dir, delay, port, proxy)
182 xml = exp_desc.to_xml()
183 controller = ExperimentController(xml, root_dir)
186 while not TERMINATE and controller.status(pl_app.guid) == AS.STATUS_NOT_STARTED:
191 hostname = hostnames[-1]
192 proc1 = exec_ccncatchunks(slicename, port, hostname)
194 if not TERMINATE and proc1:
199 hostname = hostnames[-2]
200 proc2 = exec_ccncatchunks(slicename, port, hostname)
202 while not TERMINATE and proc1 and proc2 and proc2.poll() is None:
207 err = proc1.stderr.read()
208 print "Stream 1 ERROR ", err
210 out = proc1.stdout.read()
211 print "Stream 1 OUTPUT ", out
215 err = proc2.stderr.read()
216 print "Stream 2 ERROR ", err
218 out = proc2.stdout.read()
219 print "Stream 2 OUTPUT ", out
222 controller.shutdown()
224 if __name__ == '__main__':
225 root_dir = tempfile.mkdtemp()
226 slicename = os.environ.get("PL_SLICE")
227 pl_host = os.environ.get("PL_HOST", "www.planet-lab.eu")
228 port_base = 2000 + (os.getpid() % 1000) * 13
229 pl_ssh_key = os.environ.get(
231 "%s/.ssh/id_rsa_planetlab" % (os.environ['HOME'],) )
232 pl_user = os.environ.get('PL_USER')
233 pl_pwd = os.environ.get('PL_PASS')
234 pl_vsys_vnet = os.environ.get('PL_VSYS_NET')
235 pl_hostnames = os.environ.get('PL_HOSTNAMES')
236 default_hostnames = ['openlab02.pl.sophia.inria.fr',
238 'planetlab2.di.unito.it',
239 'merkur.planetlab.haw-hamburg.de',
240 'planetlab1.cs.uit.no',
241 'planetlab3.cs.st-andrews.ac.uk',
242 'planetlab2.cs.uoi.gr',
243 'planetlab3.xeno.cl.cam.ac.uk',
244 'planet2.inf.tu-dresden.de',
245 'planetlab2.csg.uzh.ch',
247 'planetlab-um00.di.uminho.pt',
248 'planetlabpc2.upf.edu',
250 'planetlab2.esprit-tn.com' ]
251 ccn_local_port = os.environ.get('CCN_LOCAL_PORT')
253 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>"
255 parser = OptionParser(usage=usage)
256 parser.add_option("-s", "--slicename", dest="slicename",
257 help="PlanetLab slicename", default=slicename, type="str")
258 parser.add_option("-H", "--pl-host", dest="pl_host",
259 help="PlanetLab site (e.g. www.planet-lab.eu)",
260 default=pl_host, type="str")
261 parser.add_option("-k", "--ssh-key", dest="pl_ssh_key",
262 help="Path to private ssh key used for PlanetLab authentication",
263 default=pl_ssh_key, type="str")
264 parser.add_option("-u", "--pl-user", dest="pl_user",
265 help="PlanetLab account user (i.e. Registration email address)",
266 default=pl_user, type="str")
267 parser.add_option("-p", "--pl-pwd", dest="pl_pwd",
268 help="PlanetLab account password", default=pl_pwd, type="str")
269 parser.add_option("-v", "--vsys-vnet", dest="vsys_vnet",
270 help="Value of the vsys_vnet tag addigned to your slice. (e.g. 192.168.3.0/16)",
271 default=pl_vsys_vnet, type="str")
272 parser.add_option("-N", "--host-names", dest="hostnames",
273 help="Comma separated list of PlanetLab hostnames to use",
274 default=pl_hostnames, type="str")
275 parser.add_option("-c", "--node-count", dest="node_count",
276 help="Number of nodes to use",
277 default=9, type="int")
278 parser.add_option("-d", "--delay", dest="delay",
279 help="Time to wait before retrieveing the second video stream in seconds",
280 default=40, type="int")
281 parser.add_option("-P", "--ccn-local-port", dest="port",
282 help="Port to bind the CCNx daemon",
283 default=ccn_local_port, type="int")
284 parser.add_option("-x", "--proxy", dest="proxy",
285 help="Https proxy between here and PlanetLab machines",
286 default=None, type="str")
287 (options, args) = parser.parse_args()
289 hostnames = map(string.strip, options.hostnames.split(",")) if options.hostnames else default_hostnames
290 if options.node_count > 0 and options.node_count < len(hostnames):
291 hostnames = hostnames[0:options.node_count]
293 vsys_vnet = options.vsys_vnet
294 slicename = options.slicename
295 pl_host = options.pl_host
296 pl_user= options.pl_user
297 pl_pwd = options.pl_pwd
298 pl_ssh_key = options.pl_ssh_key
299 delay = options.delay
301 proxy = options.proxy
303 run(hostnames, vsys_vnet, slicename, pl_host, pl_user, pl_pwd, pl_ssh_key,
304 port_base, root_dir, delay, port, proxy)