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