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