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")
70 # We can specify a default ccnx version to be either ccnx-0.5.1 or ccnx-0.6.0
71 #pl_app.set_attribute_value("ccnxversion", "ccnx-0.5.1")
72 # We can also specify a custom local source and build and install directives
73 path_to_source = os.path.join(os.path.dirname(os.path.abspath(__file__)),
74 "ccnx-0.6.0rc3.tar.gz")
75 pl_app.set_attribute_value("sources", path_to_source)
76 pl_app.set_attribute_value("build",
77 "tar xzf ${SOURCES}/ccnx-0.6.0rc3.tar.gz && "
78 "cd ./ccnx-0.6.0rc3 && "
79 "./configure && make ")
80 pl_app.set_attribute_value("install", "cp -r ./ccnx-0.6.0rc3/bin ${SOURCES}")
81 # We use a wildcard to replace the public IP address of the node during runtime
82 routes = "|".join(map(lambda route: "udp {#[iface_%s].addr[0].[Address]#}" % route, routes))
83 # Add multicast ccn routes
84 pl_app.set_attribute_value("ccnroutes", routes)
85 pl_app.enable_trace("stdout")
86 pl_app.enable_trace("stderr")
87 pl_app.connector("node").connect(pl_node.connector("apps"))
89 def create_ccnsendchunks(pl_node, slice_desc):
90 pl_app = slice_desc.create("Application")
91 path_to_video = os.path.join(os.path.dirname(os.path.abspath(__file__)),
92 "../big_buck_bunny_240p_mpeg4_lq.ts")
93 pl_app.set_attribute_value("stdin", path_to_video)
94 pl_app.set_attribute_value("command", "ccnsendchunks ccnx:/VIDEO")
95 pl_app.enable_trace("stdout")
96 pl_app.enable_trace("stderr")
97 pl_app.connector("node").connect(pl_node.connector("apps"))
100 def exec_ccncatchunks(slicename, hostname):
101 print "Getting video chunks from %s ..." % hostname
102 login = "%s@%s" % (slicename, hostname)
103 command = 'PATH=$PATH:$(ls | egrep nepi-ccnd- | head -1)/bin; ccncatchunks2 ccnx:/VIDEO'
104 proc1 = subprocess.Popen(['ssh', login, command], stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell = False)
105 proc2 = subprocess.Popen(['vlc', '-'], stdin=proc1.stdout, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
108 def create_ed(hostnames, vsys_vnet, slicename, plc_host, pl_user, pl_pwd, pl_ssh_key,
109 port_base, root_dir):
111 # Create the experiment description object
112 exp_desc = ExperimentDescription()
114 # Create the slice description object
115 slice_desc = create_slice_desc(slicename, plc_host, pl_user, pl_pwd, pl_ssh_key,
116 port_base, root_dir, exp_desc)
118 # Create the Internet box object
119 pl_inet = slice_desc.create("Internet")
121 # Create the Node boxes
125 for hostname in hostnames:
126 pl_node = create_node(hostname, pl_inet, slice_desc)
127 pl_nodes[hostname] = pl_node
129 ccn_routes[hostname] = list()
131 ccn_routes[hostname].append(prev_hostname)
132 ccn_routes[prev_hostname].append(hostname)
133 prev_hostname = hostname
135 for hostname in hostnames:
136 pl_node = pl_nodes[hostname]
137 routes = ccn_routes[hostname]
138 create_ccnd(pl_node, routes, slice_desc)
140 # Create a ccnsendchunks application box in the first node
141 hostname = hostnames[0]
142 pl_node = pl_nodes[hostname]
143 pl_app = create_ccnsendchunks(pl_node, slice_desc)
145 return exp_desc, pl_nodes, hostname, pl_app
147 def run(hostnames, vsys_vnet, slicename, plc_host, pl_user, pl_pwd, pl_ssh_key,
148 port_base, root_dir):
150 exp_desc, pl_nodes, hostname, pl_app = create_ed(hostnames, vsys_vnet,
151 slicename, plc_host, pl_user, pl_pwd, pl_ssh_key, port_base,
154 xml = exp_desc.to_xml()
155 controller = ExperimentController(xml, root_dir)
158 while not TERMINATE and controller.status(pl_app.guid) == AS.STATUS_NOT_STARTED:
163 hostname = hostnames[-1]
164 proc = exec_ccncatchunks(slicename, hostname)
166 while not TERMINATE and proc and proc.poll() is None:
171 err = proc.stderr.read()
174 out = proc.stdout.read()
178 controller.shutdown()
180 if __name__ == '__main__':
181 root_dir = tempfile.mkdtemp()
182 slicename = os.environ.get("PL_SLICE")
183 pl_host = os.environ.get("PL_HOST", "www.planet-lab.eu")
184 port_base = 2000 + (os.getpid() % 1000) * 13
185 pl_ssh_key = os.environ.get(
187 "%s/.ssh/id_rsa_planetlab" % (os.environ['HOME'],) )
188 pl_user = os.environ.get('PL_USER')
189 pl_pwd = os.environ.get('PL_PASS')
190 pl_vsys_vnet = os.environ.get('PL_VSYS_NET')
191 pl_hostnames = os.environ.get('PL_HOSTNAMES')
192 default_hostnames = ['openlab02.pl.sophia.inria.fr',
194 'planetlab2.di.unito.it',
195 #'merkur.planetlab.haw-hamburg.de',
196 'planetlab1.cs.uit.no',
197 'planetlab3.cs.st-andrews.ac.uk',
198 'planetlab2.cs.uoi.gr',
199 'planetlab3.xeno.cl.cam.ac.uk',
200 'planet2.inf.tu-dresden.de',
201 'planetlab2.csg.uzh.ch',
203 'planetlab-um00.di.uminho.pt',
204 'planetlabpc2.upf.edu',
206 'planetlab2.esprit-tn.com' ]
208 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>"
210 parser = OptionParser(usage=usage)
211 parser.add_option("-s", "--slicename", dest="slicename",
212 help="PlanetLab slicename", default=slicename, type="str")
213 parser.add_option("-H", "--pl-host", dest="pl_host",
214 help="PlanetLab site (e.g. www.planet-lab.eu)",
215 default=pl_host, type="str")
216 parser.add_option("-k", "--ssh-key", dest="pl_ssh_key",
217 help="Path to private ssh key used for PlanetLab authentication",
218 default=pl_ssh_key, type="str")
219 parser.add_option("-u", "--pl-user", dest="pl_user",
220 help="PlanetLab account user (i.e. Registration email address)",
221 default=pl_user, type="str")
222 parser.add_option("-p", "--pl-pwd", dest="pl_pwd",
223 help="PlanetLab account password", default=pl_pwd, type="str")
224 parser.add_option("-v", "--vsys-vnet", dest="vsys_vnet",
225 help="Value of the vsys_vnet tag addigned to your slice. (e.g. 192.168.3.0/16)",
226 default=pl_vsys_vnet, type="str")
227 parser.add_option("-N", "--host-names", dest="hostnames",
228 help="Comma separated list of PlanetLab hostnames to use",
229 default=pl_hostnames, type="str")
230 parser.add_option("-c", "--node-count", dest="node_count",
231 help="Number of nodes to use",
232 default=5, type="str")
233 (options, args) = parser.parse_args()
235 hostnames = map(string.strip, options.hostnames.split(",")) if options.hostnames else default_hostnames
236 if options.node_count > 0 and options.node_count < len(hostnames):
237 hostnames = hostnames[0:options.node_count]
238 vsys_vnet = options.vsys_vnet
239 slicename = options.slicename
240 pl_host = options.pl_host
241 pl_user= options.pl_user
242 pl_pwd = options.pl_pwd
243 pl_ssh_key = options.pl_ssh_key
246 hostnames = ['nepi1.pl.sophia.inria.fr',
247 'nepi2.pl.sophia.inria.fr',
248 'nepi3.pl.sophia.inria.fr',
249 'nepi5.pl.sophia.inria.fr']
251 pl_host = "nepiplc.pl.sophia.inria.fr"
252 vsys_vnet = "192.168.2.0/24"
255 run(hostnames, vsys_vnet, slicename, pl_host, pl_user, pl_pwd, pl_ssh_key,