Added attribute repository to PlanetLab CCNxDaemon to run the ccnr command after...
[nepi.git] / examples / streamming / planetlab_multiple_vlc.py
1 #!/usr/bin/env python
2
3 from nepi.core.design import ExperimentDescription, FactoriesProvider
4 from nepi.core.execute import ExperimentController
5 from nepi.util.constants import ApplicationStatus as AS
6 from optparse import OptionParser, SUPPRESS_HELP
7 import os
8 import tempfile
9 import time
10 import uuid
11
12 """
13 This experiment evaluates the consumption of computer resources when using
14 VLC for Internet broasdcasting using PlanetLab nodes as both server and clients. 
15 A root node (server) streams a broadcast in a loop, while the clients retrieve
16 the same video over and over until experiment run time is elapsed.
17
18 While the experiment is running cpu and memory usage, and the amount of bytes 
19 transmitted per stream are traced to files.
20
21 """
22
23 # Trak SIGTERM, and set global termination flag instead of dying
24 import signal
25 TERMINATE = []
26 def _finalize(sig,frame):
27     global TERMINATE
28     TERMINATE.append(None)
29 signal.signal(signal.SIGTERM, _finalize)
30 signal.signal(signal.SIGINT, _finalize)
31
32 class MonitorInfo(object):
33     TYPE_ROOT = "root"
34     TYPE_LEAF = "leaf"
35
36     def __init__(self, hostname, type):
37         self.hostname = hostname
38         self.type = type
39         self.cpumem_monitor = None
40         self.net_monitor = None
41         self.vlc = None
42
43 def create_slice(exp_desc, slicename, plc_host, pl_user, pl_pwd, 
44         pl_ssh_key, root_dir):
45     pl_provider = FactoriesProvider("planetlab")
46     slice_desc = exp_desc.add_testbed_description(pl_provider)
47     slice_desc.set_attribute_value("homeDirectory", root_dir)
48     slice_desc.set_attribute_value("slice", slicename)
49     slice_desc.set_attribute_value("sliceSSHKey", pl_ssh_key)
50     slice_desc.set_attribute_value("authUser", pl_user)
51     slice_desc.set_attribute_value("authPass", pl_pwd)
52     slice_desc.set_attribute_value("plcHost", plc_host)
53     # Kills all running processes before starting the experiment
54     slice_desc.set_attribute_value("cleanProc", True)
55     # NOTICE: Setting 'cleanHome' to 'True' will erase all previous
56     # folders in the sliver Home directory, including result files!
57     slice_desc.set_attribute_value("cleanHome", True)
58     slice_desc.set_attribute_value("plLogLevel", "DEBUG")
59     return slice_desc
60  
61 def create_node(hostname, pl_inet, slice_desc):
62     pl_node = slice_desc.create("Node")
63     pl_node.set_attribute_value("hostname", hostname)
64     pl_node.set_attribute_value("label", "%d" % pl_node.guid)
65     pl_node.set_attribute_value("operatingSystem", "f12")
66     pl_iface = slice_desc.create("NodeInterface")
67     pl_iface.set_attribute_value("label", "iface_%d" % pl_node.guid)
68     pl_iface.connector("inet").connect(pl_inet.connector("devs"))
69     pl_node.connector("devs").connect(pl_iface.connector("node"))
70     return pl_node, pl_iface
71
72 def create_vlc_server(movie, pl_node, slice_desc):
73     mv = os.path.basename(movie)
74     pl_app = slice_desc.create("Application")
75     pl_app.set_attribute_value("rpmFusion", True)
76     pl_app.set_attribute_value("depends", "vlc")
77     pl_app.set_attribute_value("build", 
78     #    "echo -e 'new TEST vod enabled\\nsetup TEST input %s' > ${SOURCES}/VOD.vlm" % mv)
79        "echo -e 'new TEST broadcast enabled loop\\n"\
80        "setup TEST input %s\\n"\
81        "setup TEST output #rtp{mux=ts,sdp=rtsp://0.0.0.0:8554/TEST}\\n\\n"\
82        "new test_sched schedule enabled\\n"\
83        "setup test_sched append control TEST play' > ${SOURCES}/VOD.vlm" % mv)
84
85     pl_app.set_attribute_value("sources", "%s" % movie)
86     pl_app.set_attribute_value("command",
87         "sudo -S dbus-uuidgen --ensure ; vlc -vvv -I dummy --vlm-conf VOD.vlm")
88     pl_app.enable_trace("stdout")
89     pl_app.enable_trace("stderr")
90     pl_node.connector("apps").connect(pl_app.connector("node"))
91     return pl_app
92
93 def create_vlc_client(root_node, pl_node, slice_desc):
94     label = "%d_app" % pl_node.guid
95     hostname = root_node.get_attribute_value("hostname")
96     pl_app = slice_desc.create("Application")
97     pl_app.set_attribute_value("label", label)
98     pl_app.set_attribute_value("rpmFusion", True)
99     pl_app.set_attribute_value("depends", "vlc")
100     pl_app.set_attribute_value("command",
101        "sudo -S dbus-uuidgen --ensure ; sleep 5;" \
102        "vlc -I dummy --repeat rtsp://%s:8554/TEST --sout '#std{access=file,mux=ts,dst=/dev/null}'" % (hostname))
103     pl_app.enable_trace("stdout")
104     pl_app.enable_trace("stderr")
105     pl_node.connector("apps").connect(pl_app.connector("node"))
106     return pl_app
107
108 def create_cpumem_monitor(pl_node, slice_desc):
109     """ This function creates a monitoring application for the
110     utilization of node resources by the vlc application.
111
112     The format of the stdout trace file is the following:
113     'timestamp cpu(%) mem(%) time'
114     """
115     label = "%d_cpumem" % pl_node.guid
116     pl_app = slice_desc.create("Application")
117     pl_app.set_attribute_value("label", label)
118     pl_app.set_attribute_value("command", 
119             "while true ; do echo $(date +%Y%m%d%H%M%S%z) " \
120             " $(top -b -n 1 | grep 'vlc' | head -1 | sed 's/\s\s*/ /g' | sed 's/^\s//g' | cut -d' ' -f9,10,11)" \
121             "; sleep 1 ; done")
122     pl_app.enable_trace("stdout")
123     pl_app.enable_trace("stderr")
124     pl_node.connector("apps").connect(pl_app.connector("node"))
125     return pl_app
126
127 def create_net_monitor(pl_node, slice_desc, pl_ifaces):
128     """ This function creates a monitoring application for the
129     amount of bytes transmitted/received by the vlc application.
130
131     The format of the stdout trace file is the following:
132     'total-Mbytes total-time'
133     """
134     label = "%d_net" % pl_node.guid
135     hosts = " or ".join(map(lambda pl_iface: " ( host {#[%s].addr[0].[Address]#} ) " % 
136         pl_iface.get_attribute_value("label"), pl_ifaces))
137     pl_app = slice_desc.create("Application")
138     pl_app.set_attribute_value("label", label)
139     pl_app.set_attribute_value("rpmFusion", True)
140     pl_app.set_attribute_value("sudo", True)
141     pl_app.set_attribute_value("depends", "tcpdump pv")
142     pl_app.set_attribute_value("command", 
143             "tcpdump -l -i eth0 -nNqttf '(%s)' -w - | pv -fbt >/dev/null 2>>{#[%s].trace[stdout].[name]#}" %
144             (hosts, label))
145     pl_app.enable_trace("stdout")
146     pl_app.enable_trace("stderr")
147     pl_node.connector("apps").connect(pl_app.connector("node"))
148     return pl_app
149
150 def store_results(controller, monitors, results_dir, exp_label):
151     # create results directory for experiment
152     root_path = os.path.join(results_dir, exp_label)
153
154     print "STORING RESULTS in ", root_path
155
156     try:
157         os.makedirs(root_path)
158     except OSError:
159         pass
160
161     # collect information on nodes
162     hosts_info = ""
163
164     for mon in monitors:
165         hosts_info += "%s %s\n" % (mon.hostname, mon.type)
166
167         # create a subdir per hostname
168         node_path = os.path.join(root_path, mon.hostname)
169         try:
170             os.makedirs(node_path)
171         except OSError:
172             pass
173
174         # store monitoring results
175         cpumem_stdout = controller.trace(mon.cpumem_monitor.guid, "stdout")
176         net_stdout = controller.trace(mon.net_monitor.guid, "stdout")
177         vlc_stderr = controller.trace(mon.vlc.guid, "stderr")
178
179         results = dict({"cpumem": cpumem_stdout, "net": net_stdout, 
180             "vlc_error": vlc_stderr})
181
182         for name, stdout in results.iteritems():
183             fpath = os.path.join(node_path, name)
184             f = open(fpath, "w")
185             f.write(stdout)
186             f.close()
187
188     # store node info file
189     fpath = os.path.join(root_path, "hosts")
190     f = open(fpath, "w")
191     f.write(hosts_info)
192     f.close()
193
194 def get_options():
195     slicename = os.environ.get("PL_SLICE")
196     pl_host = os.environ.get("PL_HOST", "www.planet-lab.eu")
197     pl_ssh_key = os.environ.get(
198         "PL_SSH_KEY",
199         "%s/.ssh/id_rsa_planetlab" % (os.environ['HOME'],) )
200     pl_user = os.environ.get('PL_USER')
201     pl_pwd = os.environ.get('PL_PASS')
202     exp_label = "%s" % uuid.uuid4()
203
204     usage = "usage: %prog -s <pl_slice> -H <pl_host> -k <ssh_key> -u <pl_user> \
205             -p <pl_password> -m <movie> -r <results-dir> -l <experiment-label>"
206
207     parser = OptionParser(usage=usage)
208     parser.add_option("-s", "--slicename", dest="slicename", 
209             help="PlanetLab slicename", default=slicename, type="str")
210     parser.add_option("-H", "--pl-host", dest="pl_host", 
211             help="PlanetLab site (e.g. www.planet-lab.eu)", 
212             default=pl_host, type="str")
213     parser.add_option("-k", "--ssh-key", dest="pl_ssh_key", 
214             help="Path to private ssh key used for PlanetLab authentication", 
215             default=pl_ssh_key, type="str")
216     parser.add_option("-u", "--pl-user", dest="pl_user", 
217             help="PlanetLab account user (i.e. Registration email address)", 
218             default=pl_user, type="str")
219     parser.add_option("-p", "--pl-pwd", dest="pl_pwd", 
220             help="PlanetLab account password", default=pl_pwd, type="str")
221     parser.add_option("-m", "--movie", dest="movie", 
222             help="Stream movie", type="str")
223     parser.add_option("-r", "--results", dest="results_dir", default = "/tmp", 
224             help="Path to directory to store results", type="str")
225     parser.add_option("-l", "--label", dest="exp_label", default = exp_label, 
226             help="Label to identify experiment results", type="str")
227     parser.add_option("-t", "--time", dest="time_to_run", default = 1, 
228             help="Time to run the experiment in hours", type="float")
229
230     (options, args) = parser.parse_args()
231
232     if not options.movie:
233         parser.error("movie is a required argument")
234
235     return (options.slicename, options.pl_host, options.pl_user, 
236             options.pl_pwd, options.pl_ssh_key, options.movie,
237             options.results_dir, options.exp_label, options.time_to_run)
238
239 if __name__ == '__main__':
240     root_dir = tempfile.mkdtemp()
241     (pl_slice, 
242             pl_host, 
243             pl_user, 
244             pl_pwd, 
245             pl_ssh_key, 
246             movie, 
247             results_dir,
248             exp_label,
249             time_to_run) = get_options()
250
251     # list to store information on monitoring apps per node
252     monitors = []
253     
254     # Create the experiment description object
255     exp_desc = ExperimentDescription()
256
257     # Create slice
258     slice_desc = create_slice(exp_desc, pl_slice, pl_host, pl_user, pl_pwd,
259         pl_ssh_key, root_dir)
260    
261     # Create the Internet box object
262     pl_inet = slice_desc.create("Internet")
263
264     # Create root node
265     hostname = "ple6.ipv6.lip6.fr"
266     (root_node, root_iface) = create_node(hostname, pl_inet, slice_desc)
267
268     # Create monitor info object for root node
269     root_mon = MonitorInfo(hostname, MonitorInfo.TYPE_ROOT)
270     monitors.append(root_mon)
271
272     # Add VLC service
273     root_vlc = create_vlc_server(movie, root_node, slice_desc)
274     
275     # Add memory and cpu monitoring for root node
276     root_mon.cpumem_monitor = create_cpumem_monitor(root_node, slice_desc)
277
278     # Add reference to vlc app 
279     root_mon.vlc = root_vlc
280
281     # Create leaf nodes
282     cli_apps = []
283     cli_ifaces = []
284
285     hostnames = ["planetlab1.rd.tut.fi", 
286             "planetlab1.s3.kth.se", 
287             "planetlab1.tlm.unavarra.es", 
288             "planet1.servers.ua.pt", 
289             "onelab3.warsaw.rd.tp.pl", 
290             "gschembra3.diit.unict.it", 
291             "iraplab1.iralab.uni-karlsruhe.de", 
292             "host3-plb.loria.fr", 
293             "kostis.di.uoa.gr", 
294             "planetlab04.cnds.unibe.ch"]
295
296     for hostname in hostnames:
297         pl_node, pl_iface = create_node(hostname, pl_inet, slice_desc)
298         cli_ifaces.append(pl_iface)
299
300         # Create monitor info object for root node
301         node_mon = MonitorInfo(hostname, MonitorInfo.TYPE_LEAF)
302         monitors.append(node_mon)
303       
304         # Add memory and cpu monitoring for all nodes
305         node_mon.cpumem_monitor = create_cpumem_monitor(pl_node, slice_desc)
306
307         # Add network monitoring for all nodes
308         node_mon.net_monitor = create_net_monitor(pl_node, slice_desc, [root_iface])
309
310         # Add VLC clients
311         vlc = create_vlc_client(root_node, pl_node, slice_desc)
312         cli_apps.append(vlc)
313
314         # Add reference to vlc app 
315         node_mon.vlc = vlc
316
317     # Add network monitoring for root node
318     root_mon.net_monitor = create_net_monitor(root_node, slice_desc, cli_ifaces)
319
320     xml = exp_desc.to_xml()
321    
322     controller = ExperimentController(xml, root_dir)
323     controller.start()
324
325     start_time = time.time()
326     duration = time_to_run * 3600 # in seconds
327     while not TERMINATE:
328         time.sleep(1)
329         if (time.time() - start_time) > duration: # elapsed time
330             TERMINATE.append(None)
331
332     controller.stop()
333  
334     # store results in results dir
335     store_results(controller, monitors, results_dir, exp_label)
336    
337     controller.shutdown()
338