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 # Kills all running processes before starting the experiment
50 slice_desc.set_attribute_value("dedicatedSlice", True)
51 slice_desc.set_attribute_value("plLogLevel", "DEBUG")
54 def create_node(hostname, pl_inet, slice_desc):
55 pl_node = slice_desc.create("Node")
56 pl_node.set_attribute_value("hostname", hostname)
57 pl_node.set_attribute_value("label", hostname)
58 pl_iface = slice_desc.create("NodeInterface")
59 pl_iface.set_attribute_value("label", "iface_%s" % hostname)
60 pl_iface.connector("inet").connect(pl_inet.connector("devs"))
61 pl_node.connector("devs").connect(pl_iface.connector("node"))
64 def create_ccnd(pl_node, routes, slice_desc):
65 pl_app = slice_desc.create("CCNxDaemon")
66 # We use a wildcard to replace the public IP address of the node during runtime
67 routes = "|".join(map(lambda route: "udp {#[iface_%s].addr[0].[Address]#}" % route, routes))
68 # Add multicast ccn routes
69 pl_app.set_attribute_value("ccnroutes", routes)
70 pl_app.enable_trace("stdout")
71 pl_app.enable_trace("stderr")
72 pl_app.connector("node").connect(pl_node.connector("apps"))
74 def create_ccnsendchunks(pl_node, slice_desc):
75 pl_app = slice_desc.create("Application")
76 path_to_video = os.path.join(os.path.dirname(os.path.abspath(__file__)),
77 "../big_buck_bunny_240p_mpeg4_lq.ts")
78 pl_app.set_attribute_value("stdin", path_to_video)
79 pl_app.set_attribute_value("command", "ccnsendchunks ccnx:/VIDEO")
80 pl_app.enable_trace("stdout")
81 pl_app.enable_trace("stderr")
82 pl_app.connector("node").connect(pl_node.connector("apps"))
85 def exec_ccncatchunks(slicename, hostname):
86 print "Getting video chunks from %s ..." % hostname
87 login = "%s@%s" % (slicename, hostname)
88 command = 'PATH=$PATH:$(ls | egrep nepi-ccnd- | head -1)/bin; ccncatchunks2 ccnx:/VIDEO'
89 proc1 = subprocess.Popen(['ssh', login, command], stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell = False)
90 proc2 = subprocess.Popen(['vlc', '-'], stdin=proc1.stdout, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
93 def create_ed(hostnames, vsys_vnet, slicename, plc_host, pl_user, pl_pwd, pl_ssh_key,
96 # Create the experiment description object
97 exp_desc = ExperimentDescription()
99 # Create the slice description object
100 slice_desc = create_slice_desc(slicename, plc_host, pl_user, pl_pwd, pl_ssh_key,
101 port_base, root_dir, exp_desc)
103 # Create the Internet box object
104 pl_inet = slice_desc.create("Internet")
106 # Create the Node boxes
110 for hostname in hostnames:
111 pl_node = create_node(hostname, pl_inet, slice_desc)
112 pl_nodes[hostname] = pl_node
114 ccn_routes[hostname] = list()
116 ccn_routes[hostname].append(prev_hostname)
117 ccn_routes[prev_hostname].append(hostname)
118 prev_hostname = hostname
120 for hostname in hostnames:
121 pl_node = pl_nodes[hostname]
122 routes = ccn_routes[hostname]
123 create_ccnd(pl_node, routes, slice_desc)
125 # Create a ccnsendchunks application box in the first node
126 hostname = hostnames[0]
127 pl_node = pl_nodes[hostname]
128 pl_app = create_ccnsendchunks(pl_node, slice_desc)
130 return exp_desc, pl_nodes, hostname, pl_app
132 def run(hostnames, vsys_vnet, slicename, plc_host, pl_user, pl_pwd, pl_ssh_key,
133 port_base, root_dir):
135 exp_desc, pl_nodes, hostname, pl_app = create_ed(hostnames, vsys_vnet,
136 slicename, plc_host, pl_user, pl_pwd, pl_ssh_key, port_base,
139 xml = exp_desc.to_xml()
140 controller = ExperimentController(xml, root_dir)
143 while not TERMINATE and controller.status(pl_app.guid) == AS.STATUS_NOT_STARTED:
148 hostname = hostnames[-1]
149 proc = exec_ccncatchunks(slicename, hostname)
151 while not TERMINATE and proc and proc.poll() is None:
156 err = proc.stderr.read()
159 out = proc.stdout.read()
163 controller.shutdown()
165 if __name__ == '__main__':
166 root_dir = tempfile.mkdtemp()
167 slicename = os.environ.get("PL_SLICE")
168 pl_host = os.environ.get("PL_HOST", "www.planet-lab.eu")
169 port_base = 2000 + (os.getpid() % 1000) * 13
170 pl_ssh_key = os.environ.get(
172 "%s/.ssh/id_rsa_planetlab" % (os.environ['HOME'],) )
173 pl_user = os.environ.get('PL_USER')
174 pl_pwd = os.environ.get('PL_PASS')
175 pl_vsys_vnet = os.environ.get('PL_VSYS_NET')
176 pl_hostnames = os.environ.get('PL_HOSTNAMES')
177 default_hostnames = ['openlab02.pl.sophia.inria.fr',
179 'planetlab2.di.unito.it',
180 'merkur.planetlab.haw-hamburg.de',
181 'planetlab1.cs.uit.no',
182 'planetlab3.cs.st-andrews.ac.uk',
183 'planetlab2.cs.uoi.gr',
184 'planetlab3.xeno.cl.cam.ac.uk',
185 'planet2.inf.tu-dresden.de',
186 'planetlab2.csg.uzh.ch',
188 'planetlab-um00.di.uminho.pt',
189 'planetlabpc2.upf.edu',
191 'planetlab2.esprit-tn.com' ]
193 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>"
195 parser = OptionParser(usage=usage)
196 parser.add_option("-s", "--slicename", dest="slicename",
197 help="PlanetLab slicename", default=slicename, type="str")
198 parser.add_option("-H", "--pl-host", dest="pl_host",
199 help="PlanetLab site (e.g. www.planet-lab.eu)",
200 default=pl_host, type="str")
201 parser.add_option("-k", "--ssh-key", dest="pl_ssh_key",
202 help="Path to private ssh key used for PlanetLab authentication",
203 default=pl_ssh_key, type="str")
204 parser.add_option("-u", "--pl-user", dest="pl_user",
205 help="PlanetLab account user (i.e. Registration email address)",
206 default=pl_user, type="str")
207 parser.add_option("-p", "--pl-pwd", dest="pl_pwd",
208 help="PlanetLab account password", default=pl_pwd, type="str")
209 parser.add_option("-v", "--vsys-vnet", dest="vsys_vnet",
210 help="Value of the vsys_vnet tag addigned to your slice. (e.g. 192.168.3.0/16)",
211 default=pl_vsys_vnet, type="str")
212 parser.add_option("-N", "--host-names", dest="hostnames",
213 help="Comma separated list of PlanetLab hostnames to use",
214 default=pl_hostnames, type="str")
215 parser.add_option("-c", "--node-count", dest="node_count",
216 help="Number of nodes to use",
217 default=5, type="str")
218 (options, args) = parser.parse_args()
220 hostnames = map(string.strip, options.hostnames.split(",")) if options.hostnames else default_hostnames
221 if options.node_count > 0 and options.node_count < len(hostnames):
222 hostnames = hostnames[0:options.node_count]
223 vsys_vnet = options.vsys_vnet
224 slicename = options.slicename
225 pl_host = options.pl_host
226 pl_user= options.pl_user
227 pl_pwd = options.pl_pwd
228 pl_ssh_key = options.pl_ssh_key
231 hostnames = ['nepi1.pl.sophia.inria.fr',
232 'nepi2.pl.sophia.inria.fr',
233 'nepi3.pl.sophia.inria.fr',
234 'nepi5.pl.sophia.inria.fr']
236 pl_host = "nepiplc.pl.sophia.inria.fr"
237 vsys_vnet = "192.168.2.0/24"
240 run(hostnames, vsys_vnet, slicename, pl_host, pl_user, pl_pwd, pl_ssh_key,