Merging with HEAD
[nepi.git] / test / testbeds / planetlab / execute.py
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3
4 import getpass
5 from nepi.util.constants import STATUS_FINISHED
6 from nepi.testbeds import planetlab
7 import os
8 import shutil
9 import tempfile
10 import time
11 import unittest
12 import re
13 import test_util
14 import sys
15
16 class PlanetLabExecuteTestCase(unittest.TestCase):
17     testbed_id = "planetlab"
18     testbed_version = "01"
19     slicename = "inria_nepi"
20     plchost = "nepiplc.pl.sophia.inria.fr"
21     
22     host1 = "nepi1.pl.sophia.inria.fr"
23     host2 = "nepi2.pl.sophia.inria.fr"
24
25     def setUp(self):
26         self.root_dir = tempfile.mkdtemp()
27         
28     def tearDown(self):
29         try:
30             shutil.rmtree(self.root_dir)
31         except:
32             # retry
33             time.sleep(0.1)
34             shutil.rmtree(self.root_dir)
35
36     def make_instance(self):
37         testbed_id = self.testbed_id
38         testbed_version = self.testbed_version
39         slicename = self.slicename
40         plchost = self.plchost
41         
42         instance = planetlab.TestbedController(testbed_version)
43         pl_ssh_key = os.environ.get(
44             "PL_SSH_KEY",
45             "%s/.ssh/id_rsa_planetlab" % (os.environ['HOME'],) )
46         pl_user, pl_pwd = test_util.pl_auth()
47         
48         instance.defer_configure("homeDirectory", self.root_dir)
49         instance.defer_configure("slice", slicename)
50         instance.defer_configure("sliceSSHKey", pl_ssh_key)
51         instance.defer_configure("authUser", pl_user)
52         instance.defer_configure("authPass", pl_pwd)
53         instance.defer_configure("plcHost", plchost)
54         
55         return instance
56
57     @test_util.skipUnless(test_util.pl_auth() is not None, "Test requires PlanetLab authentication info (PL_USER and PL_PASS environment variables)")
58     def test_simple(self):
59         instance = self.make_instance()
60         
61         instance.defer_create(2, "Node")
62         instance.defer_create_set(2, "hostname", self.host1)
63         instance.defer_create(3, "Node")
64         instance.defer_create_set(3, "hostname", self.host2)
65         instance.defer_create(4, "NodeInterface")
66         instance.defer_connect(2, "devs", 4, "node")
67         instance.defer_create(5, "NodeInterface")
68         instance.defer_connect(3, "devs", 5, "node")
69         instance.defer_create(6, "Internet")
70         instance.defer_connect(4, "inet", 6, "devs")
71         instance.defer_connect(5, "inet", 6, "devs")
72         instance.defer_create(7, "Application")
73         instance.defer_create_set(7, "command", "ping -qc1 {#[GUID-5].addr[0].[Address]#}")
74         instance.defer_add_trace(7, "stdout")
75         instance.defer_add_trace(7, "stderr")
76         instance.defer_connect(7, "node", 2, "apps")
77
78         comp_result = r"""PING .* \(.*\) \d*\(\d*\) bytes of data.
79
80 --- .* ping statistics ---
81 1 packets transmitted, 1 received, 0% packet loss, time \d*ms.*
82 """
83
84         try:
85             instance.do_setup()
86             instance.do_create()
87             instance.do_connect_init()
88             instance.do_connect_compl()
89             instance.do_preconfigure()
90             
91             # Manually replace netref
92             instance.set(7, "command",
93                 instance.get(7, "command")
94                     .replace("{#[GUID-5].addr[0].[Address]#}", 
95                         instance.get_address(5, 0, "Address") )
96             )
97
98             instance.do_configure()
99             
100             instance.do_prestart()
101             instance.start()
102             while instance.status(7) != STATUS_FINISHED:
103                 time.sleep(0.5)
104             ping_result = instance.trace(7, "stdout") or ""
105             instance.stop()
106         finally:
107             instance.shutdown()
108
109         # asserts at the end, to make sure there's proper cleanup
110         self.assertTrue(re.match(comp_result, ping_result, re.MULTILINE),
111             "Unexpected trace:\n" + ping_result)
112         
113     @test_util.skipUnless(test_util.pl_auth() is not None, "Test requires PlanetLab authentication info (PL_USER and PL_PASS environment variables)")
114     def test_depends(self):
115         instance = self.make_instance()
116         
117         instance.defer_create(2, "Node")
118         instance.defer_create_set(2, "hostname", self.host1)
119         instance.defer_create(3, "NodeInterface")
120         instance.defer_connect(2, "devs", 3, "node")
121         instance.defer_create(4, "Internet")
122         instance.defer_connect(3, "inet", 4, "devs")
123         instance.defer_create(5, "Application")
124         instance.defer_create_set(5, "command", "gfortran --version")
125         instance.defer_create_set(5, "depends", "gcc-gfortran")
126         instance.defer_add_trace(5, "stdout")
127         instance.defer_add_trace(5, "stderr")
128         instance.defer_connect(5, "node", 2, "apps")
129
130         try:
131             instance.do_setup()
132             instance.do_create()
133             instance.do_connect_init()
134             instance.do_connect_compl()
135             instance.do_preconfigure()
136             instance.do_configure()
137             
138             instance.do_prestart()
139             instance.start()
140             while instance.status(5) != STATUS_FINISHED:
141                 time.sleep(0.5)
142             ping_result = instance.trace(5, "stdout") or ""
143             comp_result = r".*GNU Fortran \(GCC\).*"
144             instance.stop()
145         finally:
146             instance.shutdown()
147
148         # asserts at the end, to make sure there's proper cleanup
149         self.assertTrue(re.match(comp_result, ping_result, re.MULTILINE),
150             "Unexpected trace:\n" + ping_result)
151         
152     @test_util.skipUnless(test_util.pl_auth() is not None, "Test requires PlanetLab authentication info (PL_USER and PL_PASS environment variables)")
153     def test_build(self):
154         instance = self.make_instance()
155         
156         instance.defer_create(2, "Node")
157         instance.defer_create_set(2, "hostname", self.host1)
158         instance.defer_create(3, "NodeInterface")
159         instance.defer_connect(2, "devs", 3, "node")
160         instance.defer_create(4, "Internet")
161         instance.defer_connect(3, "inet", 4, "devs")
162         instance.defer_create(10, "Application")
163         instance.defer_create_set(10, "command", "./consts")
164         instance.defer_create_set(10, "buildDepends", "gcc")
165         instance.defer_create_set(10, "build", "gcc ${SOURCES}/consts.c -o consts")
166         instance.defer_create_set(10, "install", "cp consts ${SOURCES}/consts")
167         instance.defer_create_set(10, "sources", os.path.join(os.path.dirname(planetlab.__file__),'scripts','consts.c'))
168         instance.defer_add_trace(10, "stdout")
169         instance.defer_add_trace(10, "stderr")
170         instance.defer_connect(10, "node", 2, "apps")
171
172         comp_result = \
173 r""".*ETH_P_ALL = 0x[0-9a-fA-F]{8}
174 ETH_P_IP = 0x[0-9a-fA-F]{8}
175 TUNGETIFF = 0x[0-9a-fA-F]{8}
176 TUNSETIFF = 0x[0-9a-fA-F]{8}
177 IFF_NO_PI = 0x[0-9a-fA-F]{8}
178 IFF_TAP = 0x[0-9a-fA-F]{8}
179 IFF_TUN = 0x[0-9a-fA-F]{8}
180 IFF_VNET_HDR = 0x[0-9a-fA-F]{8}
181 TUN_PKT_STRIP = 0x[0-9a-fA-F]{8}
182 IFHWADDRLEN = 0x[0-9a-fA-F]{8}
183 IFNAMSIZ = 0x[0-9a-fA-F]{8}
184 IFREQ_SZ = 0x[0-9a-fA-F]{8}
185 FIONREAD = 0x[0-9a-fA-F]{8}.*
186 """
187
188         try:
189             instance.do_setup()
190             instance.do_create()
191             instance.do_connect_init()
192             instance.do_connect_compl()
193             instance.do_preconfigure()
194             instance.do_configure()
195             
196             instance.do_prestart()
197             instance.start()
198             while instance.status(10) != STATUS_FINISHED:
199                 time.sleep(0.5)
200             ping_result = instance.trace(10, "stdout") or ""
201             instance.stop()
202         finally:
203             instance.shutdown()
204
205         # asserts at the end, to make sure there's proper cleanup
206         self.assertTrue(re.match(comp_result, ping_result, re.MULTILINE),
207             "Unexpected trace:\n" + ping_result)
208         
209     @test_util.skipUnless(test_util.pl_auth() is not None, "Test requires PlanetLab authentication info (PL_USER and PL_PASS environment variables)")
210     def test_simple_vsys(self):
211         instance = self.make_instance()
212         
213         instance.defer_create(2, "Node")
214         instance.defer_create_set(2, "hostname", self.host1)
215         instance.defer_create_set(2, "emulation", True) # require emulation
216         instance.defer_create(3, "NodeInterface")
217         instance.defer_connect(2, "devs", 3, "node")
218         instance.defer_create(4, "Internet")
219         instance.defer_connect(3, "inet", 4, "devs")
220         instance.defer_create(5, "TunInterface")
221         instance.defer_add_address(5, "192.168.2.2", 24, False)
222         instance.defer_connect(2, "devs", 5, "node")
223         instance.defer_create(6, "Application")
224         instance.defer_create_set(6, "command", """
225 set -e
226 netconfig help > /dev/null
227 test -e /vsys/vif_up.in > /dev/null
228 test -e /vsys/vif_up.out > /dev/null
229 test -e /vsys/fd_tuntap.control > /dev/null
230 echo 'OKIDOKI'
231 """)
232         instance.defer_create_set(6, "sudo", True) # only sudo has access to /vsys
233         instance.defer_add_trace(6, "stdout")
234         instance.defer_add_trace(6, "stderr")
235         instance.defer_connect(6, "node", 2, "apps")
236
237         try:
238             instance.do_setup()
239             instance.do_create()
240             instance.do_connect_init()
241             instance.do_connect_compl()
242             instance.do_preconfigure()
243             instance.do_configure()
244             
245             instance.do_prestart()
246             instance.start()
247             while instance.status(6) != STATUS_FINISHED:
248                 time.sleep(0.5)
249             test_result = (instance.trace(6, "stdout") or "").strip()
250             comp_result = "OKIDOKI"
251             instance.stop()
252         finally:
253             instance.shutdown()
254
255         # asserts at the end, to make sure there's proper cleanup
256         self.assertEqual(comp_result, test_result)
257
258     @test_util.skipUnless(test_util.pl_auth() is not None, "Test requires PlanetLab authentication info (PL_USER and PL_PASS environment variables)")
259     def test_emulation(self):
260         instance = self.make_instance()
261         
262         instance.defer_create(2, "Node")
263         instance.defer_create_set(2, "hostname", self.host1)
264         instance.defer_create_set(2, "emulation", True) # require emulation
265         instance.defer_create(3, "NodeInterface")
266         instance.defer_connect(2, "devs", 3, "node")
267         instance.defer_create(4, "Internet")
268         instance.defer_connect(3, "inet", 4, "devs")
269         instance.defer_create(7, "NetPipe")
270         instance.defer_create_set(7, "mode", "CLIENT")
271         instance.defer_create_set(7, "portList", "80")
272         instance.defer_create_set(7, "bwOut", 12.0/1024.0) # 12kbps
273         instance.defer_create_set(7, "bwIn", 64.0/1024.0) # 64kbps
274         instance.defer_create_set(7, "plrOut", 0.01) # 1% plr outbound - high loss
275         instance.defer_create_set(7, "plrIn", 0.001) # 0.1% plr inbound - regular loss
276         instance.defer_create_set(7, "delayOut", int(1500 * 8 / (12.0/1024.0) / 1000)) # tx delay at 12kbps in ms
277         instance.defer_create_set(7, "delayIn", int(1500 * 8 / (64.0/1024.0) / 1000)) # rx delay at 64kbps in ms
278         instance.defer_add_trace(7, "netpipeStats")
279         instance.defer_connect(2, "pipes", 7, "node")
280         instance.defer_create(8, "Application")
281         instance.defer_create_set(8, "command", "time wget -q -O /dev/null http://www.google.com/") # Fetch ~10kb
282         instance.defer_add_trace(8, "stdout")
283         instance.defer_add_trace(8, "stderr")
284         instance.defer_connect(8, "node", 2, "apps")
285
286         try:
287             instance.do_setup()
288             instance.do_create()
289             instance.do_connect_init()
290             instance.do_connect_compl()
291             instance.do_preconfigure()
292             instance.do_configure()
293             
294             instance.do_prestart()
295             instance.start()
296             while instance.status(8) != STATUS_FINISHED:
297                 time.sleep(0.5)
298             test_result = (instance.trace(8, "stderr") or "").strip()
299             comp_result = r".*real\s*(?P<min>[0-9]+)m(?P<sec>[0-9]+[.][0-9]+)s.*"
300             netpipe_stats = instance.trace(7, "netpipeStats")
301             
302             instance.stop()
303         finally:
304             instance.shutdown()
305
306         # asserts at the end, to make sure there's proper cleanup
307         match = re.match(comp_result, test_result, re.MULTILINE)
308         self.assertTrue(match, "Unexpected output: %s" % (test_result,))
309         
310         minutes = int(match.group("min"))
311         seconds = float(match.group("sec"))
312         self.assertTrue((minutes * 60 + seconds) > 1.0, "Emulation not effective: %s" % (test_result,))
313
314         self.assertTrue(netpipe_stats, "Unavailable netpipe stats")
315
316     @test_util.skipUnless(test_util.pl_auth() is not None, "Test requires PlanetLab authentication info (PL_USER and PL_PASS environment variables)")
317     def test_tun_emulation_requirement(self):
318         instance = self.make_instance()
319         
320         instance.defer_create(2, "Node")
321         instance.defer_create_set(2, "hostname", self.host1)
322         instance.defer_create(3, "NodeInterface")
323         instance.defer_connect(2, "devs", 3, "node")
324         instance.defer_create(4, "Internet")
325         instance.defer_connect(3, "inet", 4, "devs")
326         instance.defer_create(5, "TunInterface")
327         instance.defer_add_address(5, "192.168.2.2", 24, False)
328         instance.defer_connect(2, "devs", 5, "node")
329         instance.defer_create(6, "Application")
330         instance.defer_create_set(6, "command", "false")
331         instance.defer_add_trace(6, "stdout")
332         instance.defer_add_trace(6, "stderr")
333         instance.defer_connect(6, "node", 2, "apps")
334
335         try:
336             instance.do_setup()
337             instance.do_create()
338             instance.do_connect_init()
339             instance.do_connect_compl()
340             instance.do_preconfigure()
341             instance.do_configure()
342             self.fail("Usage of TUN without emulation should fail")
343         except Exception,e:
344             pass
345
346     @test_util.skipUnless(test_util.pl_auth() is not None, "Test requires PlanetLab authentication info (PL_USER and PL_PASS environment variables)")
347     def _pingtest(self, TunClass, ConnectionProto):
348         instance = self.make_instance()
349         
350         instance.defer_create(2, "Node")
351         instance.defer_create_set(2, "hostname", self.host1)
352         instance.defer_create_set(2, "emulation", True) # require emulation
353         instance.defer_create(3, "Node")
354         instance.defer_create_set(3, "hostname", self.host2)
355         instance.defer_create_set(3, "emulation", True) # require emulation
356         instance.defer_create(4, "NodeInterface")
357         instance.defer_connect(2, "devs", 4, "node")
358         instance.defer_create(5, "Internet")
359         instance.defer_connect(4, "inet", 5, "devs")
360         instance.defer_create(6, "NodeInterface")
361         instance.defer_connect(3, "devs", 6, "node")
362         instance.defer_connect(6, "inet", 5, "devs")
363         instance.defer_create(7, TunClass)
364         instance.defer_add_trace(7, "packets")
365         instance.defer_add_address(7, "192.168.2.2", 24, False)
366         instance.defer_connect(2, "devs", 7, "node")
367         instance.defer_create(8, TunClass)
368         instance.defer_add_trace(8, "packets")
369         instance.defer_add_address(8, "192.168.2.3", 24, False)
370         instance.defer_connect(3, "devs", 8, "node")
371         instance.defer_connect(7, ConnectionProto, 8, ConnectionProto)
372         instance.defer_create(9, "Application")
373         instance.defer_create_set(9, "command", "ping -qc1 {#[GUID-8].addr[0].[Address]#}")
374         instance.defer_add_trace(9, "stdout")
375         instance.defer_add_trace(9, "stderr")
376         instance.defer_connect(9, "node", 2, "apps")
377
378         comp_result = r"""PING .* \(.*\) \d*\(\d*\) bytes of data.
379
380 --- .* ping statistics ---
381 1 packets transmitted, 1 received, 0% packet loss, time \d*ms.*
382 """
383
384         try:
385             instance.do_setup()
386             instance.do_create()
387             instance.do_connect_init()
388             instance.do_connect_compl()
389             instance.do_preconfigure()
390             
391             # Manually replace netref
392             instance.set(9, "command",
393                 instance.get(9, "command")
394                     .replace("{#[GUID-8].addr[0].[Address]#}", 
395                         instance.get_address(8, 0, "Address") )
396             )
397             
398             instance.do_configure()
399             
400             instance.do_prestart()
401             instance.start()
402             while instance.status(9) != STATUS_FINISHED:
403                 time.sleep(0.5)
404             ping_result = instance.trace(9, "stdout") or ""
405             packets1 = instance.trace(7, "packets") or ""
406             packets2 = instance.trace(8, "packets") or ""
407             instance.stop()
408         finally:
409             instance.shutdown()
410
411         # asserts at the end, to make sure there's proper cleanup
412         self.assertTrue(re.match(comp_result, ping_result, re.MULTILINE),
413             "Unexpected trace:\n%s\nPackets @ source:\n%s\nPackets @ target:\n%s" % (
414                 ping_result,
415                 packets1,
416                 packets2))
417
418     @test_util.skipUnless(test_util.pl_auth() is not None, "Test requires PlanetLab authentication info (PL_USER and PL_PASS environment variables)")
419     def test_tun_ping(self):
420         self._pingtest("TunInterface", "tcp")
421
422     @test_util.skipUnless(test_util.pl_auth() is not None, "Test requires PlanetLab authentication info (PL_USER and PL_PASS environment variables)")
423     def test_tun_ping_udp(self):
424         self._pingtest("TunInterface", "udp")
425
426     @test_util.skipUnless(test_util.pl_auth() is not None, "Test requires PlanetLab authentication info (PL_USER and PL_PASS environment variables)")
427     def test_tap_ping(self):
428         self._pingtest("TapInterface", "tcp")
429
430     @test_util.skipUnless(test_util.pl_auth() is not None, "Test requires PlanetLab authentication info (PL_USER and PL_PASS environment variables)")
431     def test_tap_ping_udp(self):
432         self._pingtest("TapInterface", "udp")
433
434     @test_util.skipUnless(test_util.pl_auth() is not None, "Test requires PlanetLab authentication info (PL_USER and PL_PASS environment variables)")
435     def test_nepi_depends(self):
436         instance = self.make_instance()
437         
438         instance.defer_create(2, "Node")
439         instance.defer_create_set(2, "hostname", self.host1)
440         instance.defer_create(3, "NodeInterface")
441         instance.defer_connect(2, "devs", 3, "node")
442         instance.defer_create(4, "Internet")
443         instance.defer_connect(3, "inet", 4, "devs")
444         instance.defer_create(5, "NepiDependency")
445         instance.defer_connect(5, "node", 2, "deps")
446         instance.defer_create(12, "Application")
447         instance.defer_connect(12, "node", 2, "apps")
448         instance.defer_create_set(12, "command", "python -c 'import nepi'")
449         instance.defer_add_trace(12, "stderr")
450
451         try:
452             instance.do_setup()
453             instance.do_create()
454             instance.do_connect_init()
455             instance.do_connect_compl()
456             instance.do_preconfigure()
457             instance.do_configure()
458             
459             instance.do_prestart()
460             instance.start()
461             while instance.status(12) != STATUS_FINISHED:
462                 time.sleep(0.5)
463             ping_result = (instance.trace(12, "stderr") or "").strip()
464             instance.stop()
465         finally:
466             instance.shutdown()
467         
468         # asserts at the end, to make sure there's proper cleanup
469         self.assertEqual(ping_result, "")
470
471     @test_util.skipUnless(test_util.pl_auth() is not None, 
472         "Test requires PlanetLab authentication info (PL_USER and PL_PASS environment variables)")
473     @test_util.skipUnless(os.environ.get('NEPI_FULL_TESTS','').lower() in ('1','yes','true','on'),
474         "Test is expensive, requires NEPI_FULL_TESTS=yes")
475     def test_ns3_depends(self):
476         instance = self.make_instance()
477         
478         instance.defer_create(2, "Node")
479         instance.defer_create_set(2, "hostname", self.host1)
480         instance.defer_create(3, "NodeInterface")
481         instance.defer_connect(2, "devs", 3, "node")
482         instance.defer_create(4, "Internet")
483         instance.defer_connect(3, "inet", 4, "devs")
484         instance.defer_create(5, "NepiDependency")
485         instance.defer_connect(5, "node", 2, "deps")
486         instance.defer_create(6, "NS3Dependency")
487         instance.defer_connect(6, "node", 2, "deps")
488         instance.defer_create(12, "Application")
489         instance.defer_connect(12, "node", 2, "apps")
490         instance.defer_create_set(12, "command", "python -c 'import nepi.testbeds.ns3.execute ; tb = nepi.testbeds.ns3.execute.TestbedController(\"3_9_RC3\") ; mod = tb._load_ns3_module()'")
491         instance.defer_add_trace(12, "stderr")
492
493         try:
494             instance.do_setup()
495             instance.do_create()
496             instance.do_connect_init()
497             instance.do_connect_compl()
498             instance.do_preconfigure()
499             instance.do_configure()
500             
501             instance.do_prestart()
502             instance.start()
503             while instance.status(12) != STATUS_FINISHED:
504                 time.sleep(0.5)
505             ping_result = (instance.trace(12, "stderr") or "").strip()
506             instance.stop()
507         finally:
508             instance.shutdown()
509         
510         # asserts at the end, to make sure there's proper cleanup
511         self.assertEqual(ping_result, "")
512
513     @test_util.skipUnless(test_util.pl_auth() is not None, "Test requires PlanetLab authentication info (PL_USER and PL_PASS environment variables)")
514     def test_discovery(self):
515         instance = self.make_instance()
516         
517         instance.defer_create(2, "Node")
518         instance.defer_create_set(2, "operatingSystem", "f12")
519         instance.defer_create(3, "Node")
520         instance.defer_create_set(3, "operatingSystem", "f12")
521         instance.defer_create(4, "NodeInterface")
522         instance.defer_connect(2, "devs", 4, "node")
523         instance.defer_create(5, "NodeInterface")
524         instance.defer_connect(3, "devs", 5, "node")
525         instance.defer_create(6, "Internet")
526         instance.defer_connect(4, "inet", 6, "devs")
527         instance.defer_connect(5, "inet", 6, "devs")
528         instance.defer_create(7, "Application")
529         instance.defer_create_set(7, "command", "ping -qc1 {#[GUID-5].addr[0].[Address]#}")
530         instance.defer_add_trace(7, "stdout")
531         instance.defer_add_trace(7, "stderr")
532         instance.defer_connect(7, "node", 2, "apps")
533
534         comp_result = r"""PING .* \(.*\) \d*\(\d*\) bytes of data.
535
536 --- .* ping statistics ---
537 1 packets transmitted, 1 received, 0% packet loss, time \d*ms.*
538 """
539
540         try:
541             instance.do_setup()
542             instance.do_create()
543             instance.do_connect_init()
544             instance.do_connect_compl()
545             instance.do_preconfigure()
546             
547             # Manually replace netref
548             instance.set(7, "command",
549                 instance.get(7, "command")
550                     .replace("{#[GUID-5].addr[0].[Address]#}", 
551                         instance.get_address(5, 0, "Address") )
552             )
553
554             instance.do_configure()
555             
556             instance.do_prestart()
557             instance.start()
558             while instance.status(7) != STATUS_FINISHED:
559                 time.sleep(0.5)
560             ping_result = instance.trace(7, "stdout") or ""
561             instance.stop()
562         finally:
563             instance.shutdown()
564
565         # asserts at the end, to make sure there's proper cleanup
566         self.assertTrue(re.match(comp_result, ping_result, re.MULTILINE),
567             "Unexpected trace:\n" + ping_result)
568         
569         
570
571 if __name__ == '__main__':
572     unittest.main()
573