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