Add TUNGETIFF to constants, they're useful
[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.start()
101             while instance.status(7) != STATUS_FINISHED:
102                 time.sleep(0.5)
103             ping_result = instance.trace(7, "stdout") or ""
104             instance.stop()
105         finally:
106             instance.shutdown()
107
108         # asserts at the end, to make sure there's proper cleanup
109         self.assertTrue(re.match(comp_result, ping_result, re.MULTILINE),
110             "Unexpected trace:\n" + ping_result)
111         
112     @test_util.skipUnless(test_util.pl_auth() is not None, "Test requires PlanetLab authentication info (PL_USER and PL_PASS environment variables)")
113     def test_depends(self):
114         instance = self.make_instance()
115         
116         instance.defer_create(2, "Node")
117         instance.defer_create_set(2, "hostname", self.host1)
118         instance.defer_create(3, "NodeInterface")
119         instance.defer_connect(2, "devs", 3, "node")
120         instance.defer_create(4, "Internet")
121         instance.defer_connect(3, "inet", 4, "devs")
122         instance.defer_create(5, "Application")
123         instance.defer_create_set(5, "command", "gfortran --version")
124         instance.defer_create_set(5, "depends", "gcc-gfortran")
125         instance.defer_add_trace(5, "stdout")
126         instance.defer_add_trace(5, "stderr")
127         instance.defer_connect(5, "node", 2, "apps")
128
129         try:
130             instance.do_setup()
131             instance.do_create()
132             instance.do_connect_init()
133             instance.do_connect_compl()
134             instance.do_preconfigure()
135             instance.do_configure()
136             
137             instance.start()
138             while instance.status(5) != 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.start()
195             while instance.status(10) != STATUS_FINISHED:
196                 time.sleep(0.5)
197             ping_result = instance.trace(10, "stdout") or ""
198             instance.stop()
199         finally:
200             instance.shutdown()
201
202         # asserts at the end, to make sure there's proper cleanup
203         self.assertTrue(re.match(comp_result, ping_result, re.MULTILINE),
204             "Unexpected trace:\n" + ping_result)
205         
206     @test_util.skipUnless(test_util.pl_auth() is not None, "Test requires PlanetLab authentication info (PL_USER and PL_PASS environment variables)")
207     def test_simple_vsys(self):
208         instance = self.make_instance()
209         
210         instance.defer_create(2, "Node")
211         instance.defer_create_set(2, "hostname", self.host1)
212         instance.defer_create_set(2, "emulation", True) # require emulation
213         instance.defer_create(3, "NodeInterface")
214         instance.defer_connect(2, "devs", 3, "node")
215         instance.defer_create(4, "Internet")
216         instance.defer_connect(3, "inet", 4, "devs")
217         instance.defer_create(5, "TunInterface")
218         instance.defer_add_address(5, "192.168.2.2", 24, False)
219         instance.defer_connect(2, "devs", 5, "node")
220         instance.defer_create(6, "Application")
221         instance.defer_create_set(6, "command", """
222 set -e
223 netconfig help > /dev/null
224 test -e /vsys/vif_up.in > /dev/null
225 test -e /vsys/vif_up.out > /dev/null
226 test -e /vsys/fd_tuntap.control > /dev/null
227 echo 'OKIDOKI'
228 """)
229         instance.defer_create_set(6, "sudo", True) # only sudo has access to /vsys
230         instance.defer_add_trace(6, "stdout")
231         instance.defer_add_trace(6, "stderr")
232         instance.defer_connect(6, "node", 2, "apps")
233
234         try:
235             instance.do_setup()
236             instance.do_create()
237             instance.do_connect_init()
238             instance.do_connect_compl()
239             instance.do_preconfigure()
240             instance.do_configure()
241             
242             instance.start()
243             while instance.status(6) != STATUS_FINISHED:
244                 time.sleep(0.5)
245             test_result = (instance.trace(6, "stdout") or "").strip()
246             comp_result = "OKIDOKI"
247             instance.stop()
248         finally:
249             instance.shutdown()
250
251         # asserts at the end, to make sure there's proper cleanup
252         self.assertEqual(comp_result, test_result)
253
254     @test_util.skipUnless(test_util.pl_auth() is not None, "Test requires PlanetLab authentication info (PL_USER and PL_PASS environment variables)")
255     def test_emulation(self):
256         instance = self.make_instance()
257         
258         instance.defer_create(2, "Node")
259         instance.defer_create_set(2, "hostname", self.host1)
260         instance.defer_create_set(2, "emulation", True) # require emulation
261         instance.defer_create(3, "NodeInterface")
262         instance.defer_connect(2, "devs", 3, "node")
263         instance.defer_create(4, "Internet")
264         instance.defer_connect(3, "inet", 4, "devs")
265         instance.defer_create(7, "NetPipe")
266         instance.defer_create_set(7, "mode", "CLIENT")
267         instance.defer_create_set(7, "portList", "80")
268         instance.defer_create_set(7, "bwOut", 12.0/1024.0) # 12kbps
269         instance.defer_create_set(7, "bwIn", 64.0/1024.0) # 64kbps
270         instance.defer_create_set(7, "plrOut", 0.01) # 1% plr outbound - high loss
271         instance.defer_create_set(7, "plrIn", 0.001) # 0.1% plr inbound - regular loss
272         instance.defer_create_set(7, "delayOut", int(1500 * 8 / (12.0/1024.0) / 1000)) # tx delay at 12kbps in ms
273         instance.defer_create_set(7, "delayIn", int(1500 * 8 / (64.0/1024.0) / 1000)) # rx delay at 64kbps in ms
274         instance.defer_add_trace(7, "netpipeStats")
275         instance.defer_connect(2, "pipes", 7, "node")
276         instance.defer_create(8, "Application")
277         instance.defer_create_set(8, "command", "time wget -q -O /dev/null http://www.google.com/") # Fetch ~10kb
278         instance.defer_add_trace(8, "stdout")
279         instance.defer_add_trace(8, "stderr")
280         instance.defer_connect(8, "node", 2, "apps")
281
282         try:
283             instance.do_setup()
284             instance.do_create()
285             instance.do_connect_init()
286             instance.do_connect_compl()
287             instance.do_preconfigure()
288             instance.do_configure()
289             
290             instance.start()
291             while instance.status(8) != STATUS_FINISHED:
292                 time.sleep(0.5)
293             test_result = (instance.trace(8, "stderr") or "").strip()
294             comp_result = r".*real\s*(?P<min>[0-9]+)m(?P<sec>[0-9]+[.][0-9]+)s.*"
295             netpipe_stats = instance.trace(7, "netpipeStats")
296             
297             instance.stop()
298         finally:
299             instance.shutdown()
300
301         # asserts at the end, to make sure there's proper cleanup
302         match = re.match(comp_result, test_result, re.MULTILINE)
303         self.assertTrue(match, "Unexpected output: %s" % (test_result,))
304         
305         minutes = int(match.group("min"))
306         seconds = float(match.group("sec"))
307         self.assertTrue((minutes * 60 + seconds) > 1.0, "Emulation not effective: %s" % (test_result,))
308
309         self.assertTrue(netpipe_stats, "Unavailable netpipe stats")
310
311     @test_util.skipUnless(test_util.pl_auth() is not None, "Test requires PlanetLab authentication info (PL_USER and PL_PASS environment variables)")
312     def test_tun_emulation_requirement(self):
313         instance = self.make_instance()
314         
315         instance.defer_create(2, "Node")
316         instance.defer_create_set(2, "hostname", self.host1)
317         instance.defer_create(3, "NodeInterface")
318         instance.defer_connect(2, "devs", 3, "node")
319         instance.defer_create(4, "Internet")
320         instance.defer_connect(3, "inet", 4, "devs")
321         instance.defer_create(5, "TunInterface")
322         instance.defer_add_address(5, "192.168.2.2", 24, False)
323         instance.defer_connect(2, "devs", 5, "node")
324         instance.defer_create(6, "Application")
325         instance.defer_create_set(6, "command", "false")
326         instance.defer_add_trace(6, "stdout")
327         instance.defer_add_trace(6, "stderr")
328         instance.defer_connect(6, "node", 2, "apps")
329
330         try:
331             instance.do_setup()
332             instance.do_create()
333             instance.do_connect_init()
334             instance.do_connect_compl()
335             instance.do_preconfigure()
336             instance.do_configure()
337             self.fail("Usage of TUN without emulation should fail")
338         except Exception,e:
339             pass
340
341     @test_util.skipUnless(test_util.pl_auth() is not None, "Test requires PlanetLab authentication info (PL_USER and PL_PASS environment variables)")
342     def _pingtest(self, TunClass, ConnectionProto):
343         instance = self.make_instance()
344         
345         instance.defer_create(2, "Node")
346         instance.defer_create_set(2, "hostname", self.host1)
347         instance.defer_create_set(2, "emulation", True) # require emulation
348         instance.defer_create(3, "Node")
349         instance.defer_create_set(3, "hostname", self.host2)
350         instance.defer_create_set(3, "emulation", True) # require emulation
351         instance.defer_create(4, "NodeInterface")
352         instance.defer_connect(2, "devs", 4, "node")
353         instance.defer_create(5, "Internet")
354         instance.defer_connect(4, "inet", 5, "devs")
355         instance.defer_create(6, "NodeInterface")
356         instance.defer_connect(3, "devs", 6, "node")
357         instance.defer_connect(6, "inet", 5, "devs")
358         instance.defer_create(7, TunClass)
359         instance.defer_add_trace(7, "packets")
360         instance.defer_add_address(7, "192.168.2.2", 24, False)
361         instance.defer_connect(2, "devs", 7, "node")
362         instance.defer_create(8, TunClass)
363         instance.defer_add_trace(8, "packets")
364         instance.defer_add_address(8, "192.168.2.3", 24, False)
365         instance.defer_connect(3, "devs", 8, "node")
366         instance.defer_connect(7, ConnectionProto, 8, ConnectionProto)
367         instance.defer_create(9, "Application")
368         instance.defer_create_set(9, "command", "ping -qc1 {#[GUID-8].addr[0].[Address]#}")
369         instance.defer_add_trace(9, "stdout")
370         instance.defer_add_trace(9, "stderr")
371         instance.defer_connect(9, "node", 2, "apps")
372
373         comp_result = r"""PING .* \(.*\) \d*\(\d*\) bytes of data.
374
375 --- .* ping statistics ---
376 1 packets transmitted, 1 received, 0% packet loss, time \d*ms.*
377 """
378
379         try:
380             instance.do_setup()
381             instance.do_create()
382             instance.do_connect_init()
383             instance.do_connect_compl()
384             instance.do_preconfigure()
385             
386             # Manually replace netref
387             instance.set(9, "command",
388                 instance.get(9, "command")
389                     .replace("{#[GUID-8].addr[0].[Address]#}", 
390                         instance.get_address(8, 0, "Address") )
391             )
392             
393             instance.do_configure()
394             
395             instance.start()
396             while instance.status(9) != STATUS_FINISHED:
397                 time.sleep(0.5)
398             ping_result = instance.trace(9, "stdout") or ""
399             packets1 = instance.trace(7, "packets") or ""
400             packets2 = instance.trace(8, "packets") or ""
401             instance.stop()
402         finally:
403             instance.shutdown()
404
405         # asserts at the end, to make sure there's proper cleanup
406         self.assertTrue(re.match(comp_result, ping_result, re.MULTILINE),
407             "Unexpected trace:\n%s\nPackets @ source:\n%s\nPackets @ target:\n%s" % (
408                 ping_result,
409                 packets1,
410                 packets2))
411
412     @test_util.skipUnless(test_util.pl_auth() is not None, "Test requires PlanetLab authentication info (PL_USER and PL_PASS environment variables)")
413     def test_tun_ping(self):
414         self._pingtest("TunInterface", "tcp")
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_udp(self):
418         self._pingtest("TunInterface", "udp")
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_tap_ping(self):
422         self._pingtest("TapInterface", "tcp")
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_udp(self):
426         self._pingtest("TapInterface", "udp")
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_nepi_depends(self):
430         instance = self.make_instance()
431         
432         instance.defer_create(2, "Node")
433         instance.defer_create_set(2, "hostname", self.host1)
434         instance.defer_create(3, "NodeInterface")
435         instance.defer_connect(2, "devs", 3, "node")
436         instance.defer_create(4, "Internet")
437         instance.defer_connect(3, "inet", 4, "devs")
438         instance.defer_create(5, "NepiDependency")
439         instance.defer_connect(5, "node", 2, "deps")
440         instance.defer_create(12, "Application")
441         instance.defer_connect(12, "node", 2, "apps")
442         instance.defer_create_set(12, "command", "python -c 'import nepi'")
443         instance.defer_add_trace(12, "stderr")
444
445         try:
446             instance.do_setup()
447             instance.do_create()
448             instance.do_connect_init()
449             instance.do_connect_compl()
450             instance.do_preconfigure()
451             instance.do_configure()
452             
453             instance.start()
454             while instance.status(12) != STATUS_FINISHED:
455                 time.sleep(0.5)
456             ping_result = (instance.trace(12, "stderr") or "").strip()
457             instance.stop()
458         finally:
459             instance.shutdown()
460         
461         # asserts at the end, to make sure there's proper cleanup
462         self.assertEqual(ping_result, "")
463
464     @test_util.skipUnless(test_util.pl_auth() is not None, 
465         "Test requires PlanetLab authentication info (PL_USER and PL_PASS environment variables)")
466     @test_util.skipUnless(os.environ.get('NEPI_FULL_TESTS','').lower() in ('1','yes','true','on'),
467         "Test is expensive, requires NEPI_FULL_TESTS=yes")
468     def test_ns3_depends(self):
469         instance = self.make_instance()
470         
471         instance.defer_create(2, "Node")
472         instance.defer_create_set(2, "hostname", self.host1)
473         instance.defer_create(3, "NodeInterface")
474         instance.defer_connect(2, "devs", 3, "node")
475         instance.defer_create(4, "Internet")
476         instance.defer_connect(3, "inet", 4, "devs")
477         instance.defer_create(5, "NepiDependency")
478         instance.defer_connect(5, "node", 2, "deps")
479         instance.defer_create(6, "NS3Dependency")
480         instance.defer_connect(6, "node", 2, "deps")
481         instance.defer_create(12, "Application")
482         instance.defer_connect(12, "node", 2, "apps")
483         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()'")
484         instance.defer_add_trace(12, "stderr")
485
486         try:
487             instance.do_setup()
488             instance.do_create()
489             instance.do_connect_init()
490             instance.do_connect_compl()
491             instance.do_preconfigure()
492             instance.do_configure()
493             
494             instance.start()
495             while instance.status(12) != STATUS_FINISHED:
496                 time.sleep(0.5)
497             ping_result = (instance.trace(12, "stderr") or "").strip()
498             instance.stop()
499         finally:
500             instance.shutdown()
501         
502         # asserts at the end, to make sure there's proper cleanup
503         self.assertEqual(ping_result, "")
504         
505
506 if __name__ == '__main__':
507     unittest.main()
508