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',
109 '--sub-filter', 'marq',
111 '(c) copyright 2008, Blender Foundation / www.bigbuckbunny.org',
113 '--no-video-title-show', '-'],
114 stdin=proc1.stdout, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
117 def create_ed(hostnames, vsys_vnet, slicename, plc_host, pl_user, pl_pwd, pl_ssh_key,
118 port_base, root_dir, delay):
120 # Create the experiment description object
121 exp_desc = ExperimentDescription()
123 # Create the slice description object
124 slice_desc = create_slice_desc(slicename, plc_host, pl_user, pl_pwd, pl_ssh_key,
125 port_base, root_dir, exp_desc)
127 # Create the Internet box object
128 pl_inet = slice_desc.create("Internet")
130 # Create the Node boxes
134 for hostname in hostnames:
135 pl_node = create_node(hostname, pl_inet, slice_desc)
136 pl_nodes[hostname] = pl_node
138 ccn_routes[hostname] = list()
140 ccn_routes[hostname].append(prev_hostname)
141 ccn_routes[prev_hostname].append(hostname)
142 prev_hostname = hostname
144 for hostname in hostnames:
145 pl_node = pl_nodes[hostname]
146 routes = ccn_routes[hostname]
147 create_ccnd(pl_node, routes, slice_desc)
149 # Create a ccnsendchunks application box in the first node
150 hostname = hostnames[0]
151 pl_node = pl_nodes[hostname]
152 pl_app = create_ccnsendchunks(pl_node, slice_desc)
154 return exp_desc, pl_nodes, hostname, pl_app
156 def run(hostnames, vsys_vnet, slicename, plc_host, pl_user, pl_pwd, pl_ssh_key,
157 port_base, root_dir, delay):
159 exp_desc, pl_nodes, hostname, pl_app = create_ed(hostnames, vsys_vnet,
160 slicename, plc_host, pl_user, pl_pwd, pl_ssh_key, port_base,
163 xml = exp_desc.to_xml()
164 controller = ExperimentController(xml, root_dir)
167 while not TERMINATE and controller.status(pl_app.guid) == AS.STATUS_NOT_STARTED:
172 hostname = hostnames[-1]
173 proc1 = exec_ccncatchunks(slicename, hostname)
175 if not TERMINATE and proc1:
180 hostname = hostnames[-2]
181 proc2 = exec_ccncatchunks(slicename, hostname)
183 while not TERMINATE and proc1 and proc2 and proc2.poll() is None:
188 err = proc1.stderr.read()
189 print "Stream 1 ERROR ", err
191 out = proc1.stdout.read()
192 print "Stream 1 OUTPUT ", out
196 err = proc2.stderr.read()
197 print "Stream 2 ERROR ", err
199 out = proc2.stdout.read()
200 print "Stream 2 OUTPUT ", out
203 controller.shutdown()
205 if __name__ == '__main__':
206 root_dir = tempfile.mkdtemp()
207 slicename = os.environ.get("PL_SLICE")
208 pl_host = os.environ.get("PL_HOST", "www.planet-lab.eu")
209 port_base = 2000 + (os.getpid() % 1000) * 13
210 pl_ssh_key = os.environ.get(
212 "%s/.ssh/id_rsa_planetlab" % (os.environ['HOME'],) )
213 pl_user = os.environ.get('PL_USER')
214 pl_pwd = os.environ.get('PL_PASS')
215 pl_vsys_vnet = os.environ.get('PL_VSYS_NET')
216 pl_hostnames = os.environ.get('PL_HOSTNAMES')
217 default_hostnames = ['openlab02.pl.sophia.inria.fr',
219 'planetlab2.di.unito.it',
220 'merkur.planetlab.haw-hamburg.de',
221 'planetlab1.cs.uit.no',
222 'planetlab3.cs.st-andrews.ac.uk',
223 #'planetlab2.cs.uoi.gr',
224 'planetlab3.xeno.cl.cam.ac.uk',
225 'planet2.inf.tu-dresden.de',
226 'planetlab2.csg.uzh.ch',
228 'planetlab-um00.di.uminho.pt',
229 'planetlabpc2.upf.edu',
231 'planetlab2.esprit-tn.com' ]
233 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>"
235 parser = OptionParser(usage=usage)
236 parser.add_option("-s", "--slicename", dest="slicename",
237 help="PlanetLab slicename", default=slicename, type="str")
238 parser.add_option("-H", "--pl-host", dest="pl_host",
239 help="PlanetLab site (e.g. www.planet-lab.eu)",
240 default=pl_host, type="str")
241 parser.add_option("-k", "--ssh-key", dest="pl_ssh_key",
242 help="Path to private ssh key used for PlanetLab authentication",
243 default=pl_ssh_key, type="str")
244 parser.add_option("-u", "--pl-user", dest="pl_user",
245 help="PlanetLab account user (i.e. Registration email address)",
246 default=pl_user, type="str")
247 parser.add_option("-p", "--pl-pwd", dest="pl_pwd",
248 help="PlanetLab account password", default=pl_pwd, type="str")
249 parser.add_option("-v", "--vsys-vnet", dest="vsys_vnet",
250 help="Value of the vsys_vnet tag addigned to your slice. (e.g. 192.168.3.0/16)",
251 default=pl_vsys_vnet, type="str")
252 parser.add_option("-N", "--host-names", dest="hostnames",
253 help="Comma separated list of PlanetLab hostnames to use",
254 default=pl_hostnames, type="str")
255 parser.add_option("-c", "--node-count", dest="node_count",
256 help="Number of nodes to use",
257 default=9, type="str")
258 parser.add_option("-d", "--delay", dest="delay",
259 help="Time to wait before retrieveing the second video stream in seconds",
260 default=40, type="int")
261 (options, args) = parser.parse_args()
263 hostnames = map(string.strip, options.hostnames.split(",")) if options.hostnames else default_hostnames
264 if options.node_count > 0 and options.node_count < len(hostnames):
265 hostnames = hostnames[0:options.node_count]
266 vsys_vnet = options.vsys_vnet
267 slicename = options.slicename
268 pl_host = options.pl_host
269 pl_user= options.pl_user
270 pl_pwd = options.pl_pwd
271 pl_ssh_key = options.pl_ssh_key
272 delay = options.delay
274 run(hostnames, vsys_vnet, slicename, pl_host, pl_user, pl_pwd, pl_ssh_key,
275 port_base, root_dir, delay)