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