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',
126 '-o', 'StrictHostKeyChecking=no',
129 stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell = False)
131 proc2 = subprocess.Popen(['vlc',
132 '--ffmpeg-threads=1',
133 '--sub-filter', 'marq',
135 '(c) copyright 2008, Blender Foundation / www.bigbuckbunny.org',
137 '--no-video-title-show', '-'],
138 stdin=proc1.stdout, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
141 def create_ed(hostnames, vsys_vnet, slicename, plc_host, pl_user, pl_pwd, pl_ssh_key,
142 port_base, root_dir, delay, port, proxy):
144 # Create the experiment description object
145 exp_desc = ExperimentDescription()
147 # Create the slice description object
148 slice_desc = create_slice_desc(slicename, plc_host, pl_user, pl_pwd, pl_ssh_key,
149 port_base, root_dir, proxy, exp_desc)
151 # Create the Internet box object
152 pl_inet = slice_desc.create("Internet")
154 # Create the Node boxes
158 for hostname in hostnames:
159 pl_node = create_node(hostname, pl_inet, slice_desc)
160 pl_nodes[hostname] = pl_node
162 ccn_routes[hostname] = list()
164 ccn_routes[hostname].append(prev_hostname)
165 ccn_routes[prev_hostname].append(hostname)
166 prev_hostname = hostname
168 for hostname in hostnames:
169 pl_node = pl_nodes[hostname]
170 routes = ccn_routes[hostname]
171 create_ccnd(pl_node, port, routes, slice_desc)
173 # Create a ccnsendchunks application box in the first node
174 hostname = hostnames[0]
175 pl_node = pl_nodes[hostname]
176 pl_app = create_ccnsendchunks(pl_node, port, slice_desc)
178 return exp_desc, pl_nodes, hostname, pl_app
180 def run(hostnames, vsys_vnet, slicename, plc_host, pl_user, pl_pwd, pl_ssh_key,
181 port_base, root_dir, delay, port, proxy):
183 exp_desc, pl_nodes, hostname, pl_app = create_ed(hostnames, vsys_vnet,
184 slicename, plc_host, pl_user, pl_pwd, pl_ssh_key, port_base,
185 root_dir, delay, port, proxy)
187 xml = exp_desc.to_xml()
188 controller = ExperimentController(xml, root_dir)
191 while not TERMINATE and controller.status(pl_app.guid) == AS.STATUS_NOT_STARTED:
196 hostname = hostnames[-1]
197 proc1 = exec_ccncatchunks(slicename, port, hostname)
199 if not TERMINATE and proc1:
204 hostname = hostnames[-2]
205 proc2 = exec_ccncatchunks(slicename, port, hostname)
207 while not TERMINATE and proc1 and proc2 and proc2.poll() is None:
212 err = proc1.stderr.read()
213 print "Stream 1 ERROR ", err
215 out = proc1.stdout.read()
216 print "Stream 1 OUTPUT ", out
220 err = proc2.stderr.read()
221 print "Stream 2 ERROR ", err
223 out = proc2.stdout.read()
224 print "Stream 2 OUTPUT ", out
227 controller.shutdown()
229 if __name__ == '__main__':
230 root_dir = tempfile.mkdtemp()
231 slicename = os.environ.get("PL_SLICE")
232 pl_host = os.environ.get("PL_HOST", "www.planet-lab.eu")
233 port_base = 2000 + (os.getpid() % 1000) * 13
234 pl_ssh_key = os.environ.get(
236 "%s/.ssh/id_rsa_planetlab" % (os.environ['HOME'],) )
237 pl_user = os.environ.get('PL_USER')
238 pl_pwd = os.environ.get('PL_PASS')
239 pl_vsys_vnet = os.environ.get('PL_VSYS_NET')
240 pl_hostnames = os.environ.get('PL_HOSTNAMES')
241 default_hostnames = ['openlab02.pl.sophia.inria.fr',
243 'planetlab2.di.unito.it',
244 'merkur.planetlab.haw-hamburg.de',
245 'planetlab1.cs.uit.no',
246 'planetlab3.cs.st-andrews.ac.uk',
247 'planetlab2.cs.uoi.gr',
248 'planetlab3.xeno.cl.cam.ac.uk',
249 'planet2.inf.tu-dresden.de',
250 'planetlab2.csg.uzh.ch',
252 'planetlab-um00.di.uminho.pt',
253 'planetlabpc2.upf.edu',
255 'planetlab2.esprit-tn.com' ]
257 ccn_local_port = os.environ.get('CCN_LOCAL_PORT')
259 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>"
261 parser = OptionParser(usage=usage)
262 parser.add_option("-s", "--slicename", dest="slicename",
263 help="PlanetLab slicename", default=slicename, type="str")
264 parser.add_option("-H", "--pl-host", dest="pl_host",
265 help="PlanetLab site (e.g. www.planet-lab.eu)",
266 default=pl_host, type="str")
267 parser.add_option("-k", "--ssh-key", dest="pl_ssh_key",
268 help="Path to private ssh key used for PlanetLab authentication",
269 default=pl_ssh_key, type="str")
270 parser.add_option("-u", "--pl-user", dest="pl_user",
271 help="PlanetLab account user (i.e. Registration email address)",
272 default=pl_user, type="str")
273 parser.add_option("-p", "--pl-pwd", dest="pl_pwd",
274 help="PlanetLab account password", default=pl_pwd, type="str")
275 parser.add_option("-v", "--vsys-vnet", dest="vsys_vnet",
276 help="Value of the vsys_vnet tag addigned to your slice. (e.g. 192.168.3.0/16)",
277 default=pl_vsys_vnet, type="str")
278 parser.add_option("-N", "--host-names", dest="hostnames",
279 help="Comma separated list of PlanetLab hostnames to use",
280 default=pl_hostnames, type="str")
281 parser.add_option("-c", "--node-count", dest="node_count",
282 help="Number of nodes to use",
283 default=9, type="int")
284 parser.add_option("-d", "--delay", dest="delay",
285 help="Time to wait before retrieveing the second video stream in seconds",
286 default=40, type="int")
287 parser.add_option("-P", "--ccn-local-port", dest="port",
288 help="Port to bind the CCNx daemon",
289 default=ccn_local_port, type="int")
290 parser.add_option("-x", "--proxy", dest="proxy",
291 help="Https proxy between here and PlanetLab machines",
292 default=None, type="str")
293 (options, args) = parser.parse_args()
295 hostnames = map(string.strip, options.hostnames.split(",")) if options.hostnames else default_hostnames
296 if options.node_count > 0 and options.node_count < len(hostnames):
297 hostnames = hostnames[0:options.node_count]
299 vsys_vnet = options.vsys_vnet
300 slicename = options.slicename
301 pl_host = options.pl_host
302 pl_user= options.pl_user
303 pl_pwd = options.pl_pwd
304 pl_ssh_key = options.pl_ssh_key
305 delay = options.delay
307 proxy = options.proxy
309 run(hostnames, vsys_vnet, slicename, pl_host, pl_user, pl_pwd, pl_ssh_key,
310 port_base, root_dir, delay, port, proxy)