Ticket #14: WIP, only intra-PL TUN connections, required groundwork for cross-backend...
[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, TIME_NOW
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
15 class PlanetLabExecuteTestCase(unittest.TestCase):
16     def setUp(self):
17         self.root_dir = tempfile.mkdtemp()
18         
19     def tearDown(self):
20         shutil.rmtree(self.root_dir)
21
22     def make_instance(self):
23         testbed_version = "01"
24         instance = planetlab.TestbedController(testbed_version)
25         slicename = "inria_nepi12"
26         pl_user, pl_pwd = test_util.pl_auth()
27         
28         instance.defer_configure("homeDirectory", self.root_dir)
29         instance.defer_configure("slice", slicename)
30         instance.defer_configure("sliceSSHKey", "/user/%s/home/.ssh/id_rsa_planetlab" % (getpass.getuser(),))
31         instance.defer_configure("authUser", pl_user)
32         instance.defer_configure("authPass", pl_pwd)
33         
34         return instance
35
36     @test_util.skipUnless(test_util.pl_auth() is not None, "Test requires PlanetLab authentication info (PL_USER and PL_PASS environment variables)")
37     def test_simple(self):
38         instance = self.make_instance()
39         
40         instance.defer_create(2, "Node")
41         instance.defer_create_set(2, "hostname", "onelab11.pl.sophia.inria.fr")
42         instance.defer_create(3, "Node")
43         instance.defer_create_set(3, "hostname", "onelab10.pl.sophia.inria.fr")
44         instance.defer_create(4, "NodeInterface")
45         instance.defer_connect(2, "devs", 4, "node")
46         instance.defer_create(5, "NodeInterface")
47         instance.defer_connect(3, "devs", 5, "node")
48         instance.defer_create(6, "Internet")
49         instance.defer_connect(4, "inet", 6, "devs")
50         instance.defer_connect(5, "inet", 6, "devs")
51         instance.defer_create(7, "Application")
52         instance.defer_create_set(7, "command", "ping -qc1 {#[GUID-5].addr[0].[Address]#}")
53         instance.defer_add_trace(7, "stdout")
54         instance.defer_add_trace(7, "stderr")
55         instance.defer_connect(7, "node", 2, "apps")
56
57         instance.do_setup()
58         instance.do_create()
59         instance.do_connect()
60         instance.do_preconfigure()
61         
62         # Manually replace netref
63         instance.set(TIME_NOW, 7, "command",
64             instance.get(TIME_NOW, 7, "command")
65                 .replace("{#[GUID-5].addr[0].[Address]#}", 
66                     instance.get_address(5, 0, "Address") )
67         )
68
69         instance.do_configure()
70         
71         instance.start()
72         while instance.status(7) != STATUS_FINISHED:
73             time.sleep(0.5)
74         ping_result = instance.trace(7, "stdout") or ""
75         comp_result = r"""PING .* \(.*\) \d*\(\d*\) bytes of data.
76
77 --- .* ping statistics ---
78 1 packets transmitted, 1 received, 0% packet loss, time \d*ms.*
79 """
80         self.assertTrue(re.match(comp_result, ping_result, re.MULTILINE),
81             "Unexpected trace:\n" + ping_result)
82         instance.stop()
83         instance.shutdown()
84         
85     @test_util.skipUnless(test_util.pl_auth() is not None, "Test requires PlanetLab authentication info (PL_USER and PL_PASS environment variables)")
86     def test_depends(self):
87         instance = self.make_instance()
88         
89         instance.defer_create(2, "Node")
90         instance.defer_create_set(2, "hostname", "onelab11.pl.sophia.inria.fr")
91         instance.defer_create(3, "NodeInterface")
92         instance.defer_connect(2, "devs", 3, "node")
93         instance.defer_create(4, "Internet")
94         instance.defer_connect(3, "inet", 4, "devs")
95         instance.defer_create(5, "Application")
96         instance.defer_create_set(5, "command", "gfortran --version")
97         instance.defer_create_set(5, "depends", "gcc-gfortran")
98         instance.defer_add_trace(5, "stdout")
99         instance.defer_add_trace(5, "stderr")
100         instance.defer_connect(5, "node", 2, "apps")
101
102         instance.do_setup()
103         instance.do_create()
104         instance.do_connect()
105         instance.do_preconfigure()
106         instance.do_configure()
107         
108         instance.start()
109         while instance.status(5) != STATUS_FINISHED:
110             time.sleep(0.5)
111         ping_result = instance.trace(5, "stdout") or ""
112         comp_result = r".*GNU Fortran \(GCC\).*"
113         self.assertTrue(re.match(comp_result, ping_result, re.MULTILINE),
114             "Unexpected trace:\n" + ping_result)
115         instance.stop()
116         instance.shutdown()
117         
118     @test_util.skipUnless(test_util.pl_auth() is not None, "Test requires PlanetLab authentication info (PL_USER and PL_PASS environment variables)")
119     def test_build(self):
120         instance = self.make_instance()
121         
122         instance.defer_create(2, "Node")
123         instance.defer_create_set(2, "hostname", "onelab11.pl.sophia.inria.fr")
124         instance.defer_create(3, "NodeInterface")
125         instance.defer_connect(2, "devs", 3, "node")
126         instance.defer_create(4, "Internet")
127         instance.defer_connect(3, "inet", 4, "devs")
128         instance.defer_create(10, "Application")
129         instance.defer_create_set(10, "command", "./consts")
130         instance.defer_create_set(10, "buildDepends", "gcc")
131         instance.defer_create_set(10, "build", "gcc ${SOURCES}/consts.c -o consts")
132         instance.defer_create_set(10, "install", "cp consts ${SOURCES}/consts")
133         instance.defer_create_set(10, "sources", os.path.join(os.path.dirname(planetlab.__file__),'scripts','consts.c'))
134         instance.defer_add_trace(10, "stdout")
135         instance.defer_add_trace(10, "stderr")
136         instance.defer_connect(10, "node", 2, "apps")
137
138         instance.do_setup()
139         instance.do_create()
140         instance.do_connect()
141         instance.do_preconfigure()
142         instance.do_configure()
143         
144         instance.start()
145         while instance.status(10) != STATUS_FINISHED:
146             time.sleep(0.5)
147         ping_result = instance.trace(10, "stdout") or ""
148         comp_result = \
149 r""".*ETH_P_ALL = 0x[0-9a-fA-F]{8}
150 ETH_P_IP = 0x[0-9a-fA-F]{8}
151 TUNSETIFF = 0x[0-9a-fA-F]{8}
152 IFF_NO_PI = 0x[0-9a-fA-F]{8}
153 IFF_TAP = 0x[0-9a-fA-F]{8}
154 IFF_TUN = 0x[0-9a-fA-F]{8}
155 IFF_VNET_HDR = 0x[0-9a-fA-F]{8}
156 TUN_PKT_STRIP = 0x[0-9a-fA-F]{8}
157 IFHWADDRLEN = 0x[0-9a-fA-F]{8}
158 IFNAMSIZ = 0x[0-9a-fA-F]{8}
159 IFREQ_SZ = 0x[0-9a-fA-F]{8}
160 FIONREAD = 0x[0-9a-fA-F]{8}.*
161 """
162         self.assertTrue(re.match(comp_result, ping_result, re.MULTILINE),
163             "Unexpected trace:\n" + ping_result)
164         instance.stop()
165         instance.shutdown()
166         
167     @test_util.skipUnless(test_util.pl_auth() is not None, "Test requires PlanetLab authentication info (PL_USER and PL_PASS environment variables)")
168     def test_simple_vsys(self):
169         instance = self.make_instance()
170         
171         instance.defer_create(2, "Node")
172         instance.defer_create_set(2, "hostname", "onelab11.pl.sophia.inria.fr")
173         instance.defer_create_set(2, "emulation", True) # require emulation
174         instance.defer_create(3, "NodeInterface")
175         instance.defer_connect(2, "devs", 3, "node")
176         instance.defer_create(4, "Internet")
177         instance.defer_connect(3, "inet", 4, "devs")
178         instance.defer_create(5, "TunInterface")
179         instance.defer_add_address(5, "192.168.2.2", 24, False)
180         instance.defer_connect(2, "devs", 5, "node")
181         instance.defer_create(6, "Application")
182         instance.defer_create_set(6, "command", """
183 set -e
184 netconfig help > /dev/null
185 test -e /vsys/vif_up.in > /dev/null
186 test -e /vsys/vif_up.out > /dev/null
187 test -e /vsys/fd_tuntap.control > /dev/null
188 echo 'OKIDOKI'
189 """)
190         instance.defer_create_set(6, "sudo", True) # only sudo has access to /vsys
191         instance.defer_add_trace(6, "stdout")
192         instance.defer_add_trace(6, "stderr")
193         instance.defer_connect(6, "node", 2, "apps")
194
195         instance.do_setup()
196         instance.do_create()
197         instance.do_connect()
198         instance.do_preconfigure()
199         instance.do_configure()
200         
201         instance.start()
202         while instance.status(6) != STATUS_FINISHED:
203             time.sleep(0.5)
204         test_result = (instance.trace(6, "stdout") or "").strip()
205         comp_result = "OKIDOKI"
206         self.assertEqual(comp_result, test_result)
207         instance.stop()
208         instance.shutdown()
209
210     @test_util.skipUnless(test_util.pl_auth() is not None, "Test requires PlanetLab authentication info (PL_USER and PL_PASS environment variables)")
211     def test_emulation(self):
212         instance = self.make_instance()
213         
214         instance.defer_create(2, "Node")
215         instance.defer_create_set(2, "hostname", "onelab11.pl.sophia.inria.fr")
216         instance.defer_create_set(2, "emulation", True) # require emulation
217         instance.defer_create(3, "NodeInterface")
218         instance.defer_connect(2, "devs", 3, "node")
219         instance.defer_create(4, "Internet")
220         instance.defer_connect(3, "inet", 4, "devs")
221         instance.defer_create(7, "NetPipe")
222         instance.defer_create_set(7, "mode", "CLIENT")
223         instance.defer_create_set(7, "portList", "80")
224         instance.defer_create_set(7, "bwOut", 12.0/1024.0) # 12kbps
225         instance.defer_create_set(7, "bwIn", 64.0/1024.0) # 64kbps
226         instance.defer_create_set(7, "plrOut", 0.01) # 1% plr outbound - high loss
227         instance.defer_create_set(7, "plrIn", 0.001) # 0.1% plr inbound - regular loss
228         instance.defer_create_set(7, "delayOut", int(1500 * 8 / (12.0/1024.0) / 1000)) # tx delay at 12kbps in ms
229         instance.defer_create_set(7, "delayIn", int(1500 * 8 / (64.0/1024.0) / 1000)) # rx delay at 64kbps in ms
230         instance.defer_add_trace(7, "netpipeStats")
231         instance.defer_connect(2, "pipes", 7, "node")
232         instance.defer_create(8, "Application")
233         instance.defer_create_set(8, "command", "time wget -q -O /dev/null http://www.google.com/") # Fetch ~10kb
234         instance.defer_add_trace(8, "stdout")
235         instance.defer_add_trace(8, "stderr")
236         instance.defer_connect(8, "node", 2, "apps")
237
238         instance.do_setup()
239         instance.do_create()
240         instance.do_connect()
241         instance.do_preconfigure()
242         instance.do_configure()
243         
244         instance.start()
245         while instance.status(8) != STATUS_FINISHED:
246             time.sleep(0.5)
247         test_result = (instance.trace(8, "stderr") or "").strip()
248         comp_result = r".*real\s*(?P<min>[0-9]+)m(?P<sec>[0-9]+[.][0-9]+)s.*"
249         netpipe_stats = instance.trace(7, "netpipeStats")
250         
251         instance.stop()
252         instance.shutdown()
253
254         # asserts at the end, to make sure there's proper cleanup
255         match = re.match(comp_result, test_result, re.MULTILINE)
256         self.assertTrue(match, "Unexpected output: %s" % (test_result,))
257         
258         minutes = int(match.group("min"))
259         seconds = float(match.group("sec"))
260         self.assertTrue((minutes * 60 + seconds) > 1.0, "Emulation not effective: %s" % (test_result,))
261
262         self.assertTrue(netpipe_stats, "Unavailable netpipe stats")
263
264     @test_util.skipUnless(test_util.pl_auth() is not None, "Test requires PlanetLab authentication info (PL_USER and PL_PASS environment variables)")
265     def test_tun_emulation_requirement(self):
266         instance = self.make_instance()
267         
268         instance.defer_create(2, "Node")
269         instance.defer_create_set(2, "hostname", "onelab11.pl.sophia.inria.fr")
270         instance.defer_create(3, "NodeInterface")
271         instance.defer_connect(2, "devs", 3, "node")
272         instance.defer_create(4, "Internet")
273         instance.defer_connect(3, "inet", 4, "devs")
274         instance.defer_create(5, "TunInterface")
275         instance.defer_add_address(5, "192.168.2.2", 24, False)
276         instance.defer_connect(2, "devs", 5, "node")
277         instance.defer_create(6, "Application")
278         instance.defer_create_set(6, "command", "false")
279         instance.defer_add_trace(6, "stdout")
280         instance.defer_add_trace(6, "stderr")
281         instance.defer_connect(6, "node", 2, "apps")
282
283         try:
284             instance.do_setup()
285             instance.do_create()
286             instance.do_connect()
287             instance.do_preconfigure()
288             instance.do_configure()
289             self.fail("Usage of TUN without emulation should fail")
290         except Exception,e:
291             pass
292
293     @test_util.skipUnless(test_util.pl_auth() is not None, "Test requires PlanetLab authentication info (PL_USER and PL_PASS environment variables)")
294     def test_tun_ping(self):
295         instance = self.make_instance()
296         
297         instance.defer_create(2, "Node")
298         instance.defer_create_set(2, "hostname", "onelab11.pl.sophia.inria.fr")
299         instance.defer_create_set(2, "emulation", True) # require emulation
300         instance.defer_create(3, "Node")
301         instance.defer_create_set(3, "hostname", "onelab10.pl.sophia.inria.fr")
302         instance.defer_create_set(3, "emulation", True) # require emulation
303         instance.defer_create(4, "NodeInterface")
304         instance.defer_connect(2, "devs", 4, "node")
305         instance.defer_create(5, "Internet")
306         instance.defer_connect(4, "inet", 5, "devs")
307         instance.defer_create(6, "NodeInterface")
308         instance.defer_connect(3, "devs", 6, "node")
309         instance.defer_connect(6, "inet", 5, "devs")
310         instance.defer_create(7, "TunInterface")
311         instance.defer_add_address(7, "192.168.2.2", 24, False)
312         instance.defer_connect(2, "devs", 7, "node")
313         instance.defer_create(8, "TunInterface")
314         instance.defer_add_address(8, "192.168.2.3", 24, False)
315         instance.defer_connect(3, "devs", 8, "node")
316         instance.defer_connect(7, "tcp", 8, "tcp")
317         instance.defer_create(9, "Application")
318         instance.defer_create_set(9, "command", "ping -qc1 {#[GUID-8].addr[0].[Address]#}")
319         instance.defer_add_trace(9, "stdout")
320         instance.defer_add_trace(9, "stderr")
321         instance.defer_connect(9, "node", 2, "apps")
322
323         instance.do_setup()
324         instance.do_create()
325         instance.do_connect()
326         instance.do_preconfigure()
327         
328         # Manually replace netref
329         instance.set(TIME_NOW, 9, "command",
330             instance.get(TIME_NOW, 9, "command")
331                 .replace("{#[GUID-8].addr[0].[Address]#}", 
332                     instance.get_address(8, 0, "Address") )
333         )
334         
335         instance.do_configure()
336         
337         instance.start()
338         while instance.status(9) != STATUS_FINISHED:
339             time.sleep(0.5)
340         ping_result = instance.trace(9, "stdout") or ""
341         comp_result = r"""PING .* \(.*\) \d*\(\d*\) bytes of data.
342
343 --- .* ping statistics ---
344 1 packets transmitted, 1 received, 0% packet loss, time \d*ms.*
345 """
346         instance.stop()
347         instance.shutdown()
348
349         # asserts at the end, to make sure there's proper cleanup
350         self.assertTrue(re.match(comp_result, ping_result, re.MULTILINE),
351             "Unexpected trace:\n" + ping_result)
352         
353
354 if __name__ == '__main__':
355     unittest.main()
356