fixed shebangs in non executable .py files
[nepi.git] / test / testbeds / planetlab / integration.py
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3
4 import getpass
5 import logging
6 from nepi.core.design import ExperimentDescription, FactoriesProvider
7 from nepi.core.execute import ExperimentController
8 from nepi.util import proxy
9 from nepi.util.constants import DeploymentConfiguration as DC
10 import os
11 import re
12 import shutil
13 import sys
14 import tempfile
15 import test_util
16 import time
17 import unittest
18
19 class PlanetLabIntegrationTestCase(unittest.TestCase):
20     testbed_id = "planetlab"
21     slicename = "inria_nepi"
22     plchost = "nepiplc.pl.sophia.inria.fr"
23     
24     host1 = "nepi1.pl.sophia.inria.fr"
25     host2 = "nepi2.pl.sophia.inria.fr"
26     host3 = "nepi3.pl.sophia.inria.fr"
27     host4 = "nepi5.pl.sophia.inria.fr"
28
29     port_base = 2000 + (os.getpid() % 1000) * 13
30     
31     def setUp(self):
32         self.root_dir = tempfile.mkdtemp()
33         self.__class__.port_base = self.port_base + 100
34
35     def tearDown(self):
36         try:
37             shutil.rmtree(self.root_dir)
38         except:
39             # retry
40             time.sleep(0.1)
41             shutil.rmtree(self.root_dir)
42
43     def make_experiment_desc(self):
44         testbed_id = self.testbed_id
45         slicename = self.slicename
46         plchost = self.plchost
47         pl_ssh_key = os.environ.get(
48             "PL_SSH_KEY",
49             "%s/.ssh/id_rsa_planetlab" % (os.environ['HOME'],) )
50         pl_user, pl_pwd = test_util.pl_auth()
51
52         exp_desc = ExperimentDescription()
53         pl_provider = FactoriesProvider(testbed_id)
54         pl_desc = exp_desc.add_testbed_description(pl_provider)
55         pl_desc.set_attribute_value("homeDirectory", self.root_dir)
56         pl_desc.set_attribute_value("slice", slicename)
57         pl_desc.set_attribute_value("sliceSSHKey", pl_ssh_key)
58         pl_desc.set_attribute_value("authUser", pl_user)
59         pl_desc.set_attribute_value("authPass", pl_pwd)
60         pl_desc.set_attribute_value("plcHost", plchost)
61         pl_desc.set_attribute_value("tapPortBase", self.port_base)
62         pl_desc.set_attribute_value("p2pDeployment", False) # it's interactive, we don't want it in tests
63         pl_desc.set_attribute_value("dedicatedSlice", True)
64         #pl_desc.set_attribute_value("plLogLevel", "DEBUG")
65         
66         return pl_desc, exp_desc
67     
68     def _test_simple(self, daemonize_testbed, controller_access_configuration, environ = None):
69         pl, exp = self.make_experiment_desc()
70         
71         node1 = pl.create("Node")
72         node2 = pl.create("Node")
73         node1.set_attribute_value("hostname", self.host1)
74         node2.set_attribute_value("hostname", self.host2)
75         iface1 = pl.create("NodeInterface")
76         iface2 = pl.create("NodeInterface")
77         iface2.set_attribute_value("label", "node2iface")
78         inet = pl.create("Internet")
79         node1.connector("devs").connect(iface1.connector("node"))
80         node2.connector("devs").connect(iface2.connector("node"))
81         iface1.connector("inet").connect(inet.connector("devs"))
82         iface2.connector("inet").connect(inet.connector("devs"))
83         app = pl.create("Application")
84         app.set_attribute_value("command", "ping -qc1 {#[node2iface].addr[0].[Address]#}")
85         app.enable_trace("stdout")
86         app.connector("node").connect(node1.connector("apps"))
87
88         if daemonize_testbed:
89             pl.set_attribute_value(DC.DEPLOYMENT_MODE, DC.MODE_DAEMON)
90             inst_root_dir = os.path.join(self.root_dir, "instance")
91             os.mkdir(inst_root_dir)
92             pl.set_attribute_value(DC.ROOT_DIRECTORY, inst_root_dir)
93             pl.set_attribute_value(DC.LOG_LEVEL, DC.DEBUG_LEVEL)
94
95             if environ:
96                 pl.set_attribute_value(DC.DEPLOYMENT_ENVIRONMENT_SETUP, environ)
97
98         xml = exp.to_xml()
99
100         if controller_access_configuration:
101             controller = proxy.create_experiment_controller(xml, 
102                 controller_access_configuration)
103         else:
104             controller = ExperimentController(xml, self.root_dir)
105         
106         try:
107             controller.start()
108             while not controller.is_finished(app.guid):
109                 time.sleep(0.5)
110             ping_result = controller.trace(app.guid, "stdout")
111             comp_result = r"""PING .* \(.*\) \d*\(\d*\) bytes of data.
112
113 --- .* ping statistics ---
114 1 packets transmitted, 1 received, 0% packet loss, time \d*ms.*
115 """
116             self.assertTrue(re.match(comp_result, ping_result, re.MULTILINE),
117                 "Unexpected trace:\n" + ping_result)
118         
119         finally:
120             try:
121                 controller.stop()
122             except:
123                 import traceback
124                 traceback.print_exc()
125             try:
126                 controller.shutdown()
127             except:
128                 import traceback
129                 traceback.print_exc()
130
131     @test_util.skipUnless(test_util.pl_auth() is not None, "Test requires PlanetLab authentication info (PL_USER and PL_PASS environment variables)")
132     @test_util.skipUnless(os.environ.get('NEPI_FULL_TESTS','').lower() in ('1','yes','true','on'),
133         "Test is interactive, requires NEPI_FULL_TESTS=yes")
134     def test_spanning_deployment(self):
135         pl, exp = self.make_experiment_desc()
136
137         pl.set_attribute_value("p2pDeployment", True) # we do want it here - even if interactive
138         
139         from nepi.testbeds import planetlab as plpackage
140         
141         nodes = [ pl.create("Node") for i in xrange(4) ]
142         ifaces = [ pl.create("NodeInterface") for node in nodes ]
143         inet = pl.create("Internet")
144         for node, iface in zip(nodes,ifaces):
145             node.connector("devs").connect(iface.connector("node"))
146             iface.connector("inet").connect(inet.connector("devs"))
147         
148         apps = []
149         for node in nodes:
150             app = pl.create("Application")
151             app.set_attribute_value("command", "./consts")
152             app.set_attribute_value("buildDepends", "gcc")
153             app.set_attribute_value("build", "gcc ${SOURCES}/consts.c -o consts")
154             app.set_attribute_value("install", "cp consts ${SOURCES}/consts")
155             app.set_attribute_value("sources", os.path.join(
156                 os.path.dirname(plpackage.__file__),'scripts','consts.c'))
157             app.enable_trace("stdout")
158             app.enable_trace("stderr")
159             app.enable_trace("buildlog")
160             node.connector("apps").connect(app.connector("node"))
161             apps.append(app)
162
163         comp_result = \
164 r""".*ETH_P_ALL = 0x[0-9a-fA-F]{8}
165 ETH_P_IP = 0x[0-9a-fA-F]{8}
166 TUNGETIFF = 0x[0-9a-fA-F]{8}
167 TUNSETIFF = 0x[0-9a-fA-F]{8}
168 IFF_NO_PI = 0x[0-9a-fA-F]{8}
169 IFF_TAP = 0x[0-9a-fA-F]{8}
170 IFF_TUN = 0x[0-9a-fA-F]{8}
171 IFF_VNET_HDR = 0x[0-9a-fA-F]{8}
172 TUN_PKT_STRIP = 0x[0-9a-fA-F]{8}
173 IFHWADDRLEN = 0x[0-9a-fA-F]{8}
174 IFNAMSIZ = 0x[0-9a-fA-F]{8}
175 IFREQ_SZ = 0x[0-9a-fA-F]{8}
176 FIONREAD = 0x[0-9a-fA-F]{8}.*
177 """
178
179         comp_build = r".*(Identity added|gcc).*"
180
181         xml = exp.to_xml()
182
183         controller = ExperimentController(xml, self.root_dir)
184         try:
185             controller.start()
186             while not all(controller.is_finished(app.guid) for app in apps):
187                 time.sleep(0.5)
188             
189             for app in apps:
190                 app_result = controller.trace(app.guid, "stdout") or ""
191                 self.assertTrue(re.match(comp_result, app_result, re.MULTILINE),
192                     "Unexpected trace:\n" + app_result)
193
194                 build_result = controller.trace(app.guid, "buildlog") or ""
195                 self.assertTrue(re.match(comp_build, build_result, re.MULTILINE | re.DOTALL),
196                     "Unexpected trace:\n" + build_result)
197         
198         finally:
199             try:
200                 controller.stop()
201             except:
202                 import traceback
203                 traceback.print_exc()
204             try:
205                 controller.shutdown()
206             except:
207                 import traceback
208                 traceback.print_exc()
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_simple(self):
212         self._test_simple(
213             daemonize_testbed = False,
214             controller_access_configuration = None)
215
216     @test_util.skipUnless(test_util.pl_auth() is not None, "Test requires PlanetLab authentication info (PL_USER and PL_PASS environment variables)")
217     def test_simple_daemonized(self):
218         access_config = proxy.AccessConfiguration({
219             DC.DEPLOYMENT_MODE : DC.MODE_DAEMON,
220             DC.ROOT_DIRECTORY : self.root_dir,
221             DC.LOG_LEVEL : DC.DEBUG_LEVEL,
222         })
223
224         self._test_simple(
225             daemonize_testbed = False,
226             controller_access_configuration = access_config)
227
228     @test_util.skipUnless(test_util.pl_auth() is not None, "Test requires PlanetLab authentication info (PL_USER and PL_PASS environment variables)")
229     def test_z_simple_ssh(self): # _z_ cause we want it last - it messes up the process :(
230         # Recreate environment
231         environ = ' ; '.join( map("export %s=%r".__mod__, os.environ.iteritems()) )
232
233         env = test_util.test_environment()
234
235         access_config = proxy.AccessConfiguration({
236             DC.DEPLOYMENT_MODE : DC.MODE_DAEMON,
237             DC.ROOT_DIRECTORY : self.root_dir,
238             DC.LOG_LEVEL : DC.DEBUG_LEVEL,
239             DC.DEPLOYMENT_COMMUNICATION : DC.ACCESS_SSH,
240             DC.DEPLOYMENT_PORT : env.port,
241             DC.USE_AGENT : True,
242             DC.DEPLOYMENT_ENVIRONMENT_SETUP : environ,
243         })
244
245         self._test_simple(
246             daemonize_testbed = False,
247             controller_access_configuration = access_config,
248             environ = environ)
249
250
251     def _test_recover(self, daemonize_testbed, controller_access_configuration, environ = None):
252         pl, exp = self.make_experiment_desc()
253         
254         pl.set_attribute_value(DC.RECOVERY_POLICY, DC.POLICY_RECOVER)
255         
256         node1 = pl.create("Node")
257         node2 = pl.create("Node")
258         node1.set_attribute_value("hostname", self.host1)
259         node2.set_attribute_value("hostname", self.host2)
260         
261         iface1 = pl.create("NodeInterface")
262         iface2 = pl.create("NodeInterface")
263         inet = pl.create("Internet")
264         node1.connector("devs").connect(iface1.connector("node"))
265         node2.connector("devs").connect(iface2.connector("node"))
266         iface1.connector("inet").connect(inet.connector("devs"))
267         iface2.connector("inet").connect(inet.connector("devs"))
268         
269         tap1 = pl.create("TapInterface")
270         tap2 = pl.create("TapInterface")
271         node1.connector("devs").connect(tap1.connector("node"))
272         node2.connector("devs").connect(tap2.connector("node"))
273         tap1.connector("udp").connect(tap2.connector("udp"))
274         
275         tap1ip = tap1.add_address()
276         tap1ip.set_attribute_value("Address", "192.168.2.2")
277         tap1ip.set_attribute_value("NetPrefix", 24)
278         tap1ip.set_attribute_value("Broadcast", False)
279
280         tap2ip = tap2.add_address()
281         tap2ip.set_attribute_value("Address", "192.168.2.3")
282         tap2ip.set_attribute_value("NetPrefix", 24)
283         tap2ip.set_attribute_value("Broadcast", False)
284         
285         app = pl.create("Application")
286         app.set_attribute_value("command", "ping -qc10 192.168.2.3")
287         app.enable_trace("stdout")
288         app.connector("node").connect(node1.connector("apps"))
289
290         if daemonize_testbed:
291             pl.set_attribute_value(DC.DEPLOYMENT_MODE, DC.MODE_DAEMON)
292             inst_root_dir = os.path.join(self.root_dir, "instance")
293             os.mkdir(inst_root_dir)
294             pl.set_attribute_value(DC.ROOT_DIRECTORY, inst_root_dir)
295             pl.set_attribute_value(DC.LOG_LEVEL, DC.DEBUG_LEVEL)
296
297             if environ:
298                 pl.set_attribute_value(DC.DEPLOYMENT_ENVIRONMENT_SETUP, environ)
299
300         xml = exp.to_xml()
301
302         if controller_access_configuration:
303             controller = proxy.create_experiment_controller(xml, 
304                 controller_access_configuration)
305         else:
306             controller = ExperimentController(xml, self.root_dir)
307         
308         try:
309             controller.start()
310             
311             # purposedly break connection
312             controller = None
313             
314             # recover
315             if controller_access_configuration:
316                 controller_access_configuration.set_attribute_value(
317                     DC.RECOVER, True)
318                 controller = proxy.create_experiment_controller(None, 
319                     controller_access_configuration)
320             else:
321                 controller = ExperimentController(None, self.root_dir)
322                 controller.recover()
323             
324             while not controller.is_finished(app.guid):
325                 time.sleep(0.5)
326             ping_result = controller.trace(app.guid, "stdout")
327             comp_result = r"""PING .* \(.*\) \d*\(\d*\) bytes of data.
328
329 --- .* ping statistics ---
330 10 packets transmitted, 10 received, 0% packet loss, time \d*ms.*
331 """
332             self.assertTrue(re.match(comp_result, ping_result, re.MULTILINE),
333                 "Unexpected trace:\n" + ping_result)
334         
335         finally:
336             if controller is not None:
337                 try:
338                     controller.stop()
339                 except:
340                     import traceback
341                     traceback.print_exc()
342                 try:
343                     controller.shutdown()
344                 except:
345                     import traceback
346                     traceback.print_exc()
347
348     @test_util.skipUnless(test_util.pl_auth() is not None, "Test requires PlanetLab authentication info (PL_USER and PL_PASS environment variables)")
349     def test_recover(self):
350         self._test_recover(
351             daemonize_testbed = False,
352             controller_access_configuration = None)
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 test_recover_daemonized(self):
356         access_config = proxy.AccessConfiguration({
357             DC.DEPLOYMENT_MODE : DC.MODE_DAEMON,
358             DC.ROOT_DIRECTORY : self.root_dir,
359             DC.LOG_LEVEL : DC.DEBUG_LEVEL,
360         })
361
362         self._test_recover(
363             daemonize_testbed = False,
364             controller_access_configuration = access_config)
365         
366
367 if __name__ == '__main__':
368     unittest.main()
369