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', '-'], stdin=proc1.stdout, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
111 def create_ed(hostnames, vsys_vnet, slicename, plc_host, pl_user, pl_pwd, pl_ssh_key,
112 port_base, root_dir):
114 # Create the experiment description object
115 exp_desc = ExperimentDescription()
117 # Create the slice description object
118 slice_desc = create_slice_desc(slicename, plc_host, pl_user, pl_pwd, pl_ssh_key,
119 port_base, root_dir, exp_desc)
121 # Create the Internet box object
122 pl_inet = slice_desc.create("Internet")
124 # Create the Node boxes
128 for hostname in hostnames:
129 pl_node = create_node(hostname, pl_inet, slice_desc)
130 pl_nodes[hostname] = pl_node
132 ccn_routes[hostname] = list()
134 ccn_routes[hostname].append(prev_hostname)
135 ccn_routes[prev_hostname].append(hostname)
136 prev_hostname = hostname
138 for hostname in hostnames:
139 pl_node = pl_nodes[hostname]
140 routes = ccn_routes[hostname]
141 create_ccnd(pl_node, routes, slice_desc)
143 # Create a ccnsendchunks application box in the first node
144 hostname = hostnames[0]
145 pl_node = pl_nodes[hostname]
146 pl_app = create_ccnsendchunks(pl_node, slice_desc)
148 return exp_desc, pl_nodes, hostname, pl_app
150 def run(hostnames, vsys_vnet, slicename, plc_host, pl_user, pl_pwd, pl_ssh_key,
151 port_base, root_dir):
153 exp_desc, pl_nodes, hostname, pl_app = create_ed(hostnames, vsys_vnet,
154 slicename, plc_host, pl_user, pl_pwd, pl_ssh_key, port_base,
157 xml = exp_desc.to_xml()
158 controller = ExperimentController(xml, root_dir)
161 while not TERMINATE and controller.status(pl_app.guid) == AS.STATUS_NOT_STARTED:
166 hostname = hostnames[-1]
167 proc = exec_ccncatchunks(slicename, hostname)
169 while not TERMINATE and proc and proc.poll() is None:
174 err = proc.stderr.read()
177 out = proc.stdout.read()
181 controller.shutdown()
183 if __name__ == '__main__':
184 root_dir = tempfile.mkdtemp()
185 slicename = os.environ.get("PL_SLICE")
186 pl_host = os.environ.get("PL_HOST", "www.planet-lab.eu")
187 port_base = 2000 + (os.getpid() % 1000) * 13
188 pl_ssh_key = os.environ.get(
190 "%s/.ssh/id_rsa_planetlab" % (os.environ['HOME'],) )
191 pl_user = os.environ.get('PL_USER')
192 pl_pwd = os.environ.get('PL_PASS')
193 pl_vsys_vnet = os.environ.get('PL_VSYS_NET')
194 pl_hostnames = os.environ.get('PL_HOSTNAMES')
195 default_hostnames = ['openlab02.pl.sophia.inria.fr',
197 'planetlab2.di.unito.it',
198 #'merkur.planetlab.haw-hamburg.de',
199 'planetlab1.cs.uit.no',
200 'planetlab3.cs.st-andrews.ac.uk',
201 'planetlab2.cs.uoi.gr',
202 'planetlab3.xeno.cl.cam.ac.uk',
203 'planet2.inf.tu-dresden.de',
204 'planetlab2.csg.uzh.ch',
206 'planetlab-um00.di.uminho.pt',
207 'planetlabpc2.upf.edu',
209 'planetlab2.esprit-tn.com' ]
211 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>"
213 parser = OptionParser(usage=usage)
214 parser.add_option("-s", "--slicename", dest="slicename",
215 help="PlanetLab slicename", default=slicename, type="str")
216 parser.add_option("-H", "--pl-host", dest="pl_host",
217 help="PlanetLab site (e.g. www.planet-lab.eu)",
218 default=pl_host, type="str")
219 parser.add_option("-k", "--ssh-key", dest="pl_ssh_key",
220 help="Path to private ssh key used for PlanetLab authentication",
221 default=pl_ssh_key, type="str")
222 parser.add_option("-u", "--pl-user", dest="pl_user",
223 help="PlanetLab account user (i.e. Registration email address)",
224 default=pl_user, type="str")
225 parser.add_option("-p", "--pl-pwd", dest="pl_pwd",
226 help="PlanetLab account password", default=pl_pwd, type="str")
227 parser.add_option("-v", "--vsys-vnet", dest="vsys_vnet",
228 help="Value of the vsys_vnet tag addigned to your slice. (e.g. 192.168.3.0/16)",
229 default=pl_vsys_vnet, type="str")
230 parser.add_option("-N", "--host-names", dest="hostnames",
231 help="Comma separated list of PlanetLab hostnames to use",
232 default=pl_hostnames, type="str")
233 parser.add_option("-c", "--node-count", dest="node_count",
234 help="Number of nodes to use",
235 default=5, type="str")
236 (options, args) = parser.parse_args()
238 hostnames = map(string.strip, options.hostnames.split(",")) if options.hostnames else default_hostnames
239 if options.node_count > 0 and options.node_count < len(hostnames):
240 hostnames = hostnames[0:options.node_count]
241 vsys_vnet = options.vsys_vnet
242 slicename = options.slicename
243 pl_host = options.pl_host
244 pl_user= options.pl_user
245 pl_pwd = options.pl_pwd
246 pl_ssh_key = options.pl_ssh_key
248 run(hostnames, vsys_vnet, slicename, pl_host, pl_user, pl_pwd, pl_ssh_key,