2d24e3e29768630efa217298e708e35bed208ad2
[nepi.git] / test / testbeds / planetlab / integration.py
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3
4 import getpass
5 from nepi.core.design import ExperimentDescription, FactoriesProvider
6 from nepi.core.execute import ExperimentController
7 from nepi.util import proxy
8 from nepi.util.constants import DeploymentConfiguration as DC
9 import os
10 import shutil
11 import tempfile
12 import test_util
13 import time
14 import unittest
15 import re
16 import sys
17
18 class PlanetLabIntegrationTestCase(unittest.TestCase):
19     testbed_id = "planetlab"
20     slicename = "inria_nepi"
21     plchost = "nepiplc.pl.sophia.inria.fr"
22     
23     host1 = "nepi1.pl.sophia.inria.fr"
24     host2 = "nepi2.pl.sophia.inria.fr"
25     host3 = "nepi3.pl.sophia.inria.fr"
26     host4 = "nepi5.pl.sophia.inria.fr"
27
28     port_base = 2000 + (os.getpid() % 1000) * 13
29     
30     def setUp(self):
31         self.root_dir = tempfile.mkdtemp()
32         self.__class__.port_base = self.port_base + 100
33
34     def tearDown(self):
35         try:
36             shutil.rmtree(self.root_dir)
37         except:
38             # retry
39             time.sleep(0.1)
40             shutil.rmtree(self.root_dir)
41
42     def make_experiment_desc(self):
43         testbed_id = self.testbed_id
44         slicename = self.slicename
45         plchost = self.plchost
46         pl_ssh_key = os.environ.get(
47             "PL_SSH_KEY",
48             "%s/.ssh/id_rsa_planetlab" % (os.environ['HOME'],) )
49         pl_user, pl_pwd = test_util.pl_auth()
50
51         exp_desc = ExperimentDescription()
52         pl_provider = FactoriesProvider(testbed_id)
53         pl_desc = exp_desc.add_testbed_description(pl_provider)
54         pl_desc.set_attribute_value("homeDirectory", self.root_dir)
55         pl_desc.set_attribute_value("slice", slicename)
56         pl_desc.set_attribute_value("sliceSSHKey", pl_ssh_key)
57         pl_desc.set_attribute_value("authUser", pl_user)
58         pl_desc.set_attribute_value("authPass", pl_pwd)
59         pl_desc.set_attribute_value("plcHost", plchost)
60         pl_desc.set_attribute_value("tapPortBase", self.port_base)
61         pl_desc.set_attribute_value("p2pDeployment", False) # it's interactive, we don't want it in tests
62         
63         return pl_desc, exp_desc
64     
65     def _test_simple(self, daemonize_testbed, controller_access_configuration, environ = None):
66         pl, exp = self.make_experiment_desc()
67         
68         node1 = pl.create("Node")
69         node2 = pl.create("Node")
70         node1.set_attribute_value("hostname", self.host1)
71         node2.set_attribute_value("hostname", self.host2)
72         iface1 = pl.create("NodeInterface")
73         iface2 = pl.create("NodeInterface")
74         iface2.set_attribute_value("label", "node2iface")
75         inet = pl.create("Internet")
76         node1.connector("devs").connect(iface1.connector("node"))
77         node2.connector("devs").connect(iface2.connector("node"))
78         iface1.connector("inet").connect(inet.connector("devs"))
79         iface2.connector("inet").connect(inet.connector("devs"))
80         app = pl.create("Application")
81         app.set_attribute_value("command", "ping -qc1 {#[node2iface].addr[0].[Address]#}")
82         app.enable_trace("stdout")
83         app.connector("node").connect(node1.connector("apps"))
84
85         if daemonize_testbed:
86             pl.set_attribute_value(DC.DEPLOYMENT_MODE, DC.MODE_DAEMON)
87             inst_root_dir = os.path.join(self.root_dir, "instance")
88             os.mkdir(inst_root_dir)
89             pl.set_attribute_value(DC.ROOT_DIRECTORY, inst_root_dir)
90             pl.set_attribute_value(DC.LOG_LEVEL, DC.DEBUG_LEVEL)
91
92             if environ:
93                 pl.set_attribute_value(DC.DEPLOYMENT_ENVIRONMENT_SETUP, environ)
94
95         xml = exp.to_xml()
96
97         if controller_access_configuration:
98             controller = proxy.create_experiment_controller(xml, 
99                 controller_access_configuration)
100         else:
101             controller = ExperimentController(xml, self.root_dir)
102         
103         try:
104             controller.start()
105             while not controller.is_finished(app.guid):
106                 time.sleep(0.5)
107             ping_result = controller.trace(app.guid, "stdout")
108             comp_result = r"""PING .* \(.*\) \d*\(\d*\) bytes of data.
109
110 --- .* ping statistics ---
111 1 packets transmitted, 1 received, 0% packet loss, time \d*ms.*
112 """
113             self.assertTrue(re.match(comp_result, ping_result, re.MULTILINE),
114                 "Unexpected trace:\n" + ping_result)
115         
116         finally:
117             controller.stop()
118             controller.shutdown()
119
120     @test_util.skipUnless(test_util.pl_auth() is not None, "Test requires PlanetLab authentication info (PL_USER and PL_PASS environment variables)")
121     @test_util.skipUnless(os.environ.get('NEPI_FULL_TESTS','').lower() in ('1','yes','true','on'),
122         "Test is interactive, requires NEPI_FULL_TESTS=yes")
123     def test_spanning_deployment(self):
124         pl, exp = self.make_experiment_desc()
125
126         pl.set_attribute_value("p2pDeployment", True) # we do want it here - even if interactive
127         
128         from nepi.testbeds import planetlab as plpackage
129         
130         nodes = [ pl.create("Node") for i in xrange(4) ]
131         ifaces = [ pl.create("NodeInterface") for node in nodes ]
132         inet = pl.create("Internet")
133         for node, iface in zip(nodes,ifaces):
134             node.connector("devs").connect(iface.connector("node"))
135             iface.connector("inet").connect(inet.connector("devs"))
136         
137         apps = []
138         for node in nodes:
139             app = pl.create("Application")
140             app.set_attribute_value("command", "./consts")
141             app.set_attribute_value("buildDepends", "gcc")
142             app.set_attribute_value("build", "gcc ${SOURCES}/consts.c -o consts")
143             app.set_attribute_value("install", "cp consts ${SOURCES}/consts")
144             app.set_attribute_value("sources", os.path.join(
145                 os.path.dirname(plpackage.__file__),'scripts','consts.c'))
146             app.enable_trace("stdout")
147             app.enable_trace("stderr")
148             app.enable_trace("buildlog")
149             node.connector("apps").connect(app.connector("node"))
150             apps.append(app)
151
152         comp_result = \
153 r""".*ETH_P_ALL = 0x[0-9a-fA-F]{8}
154 ETH_P_IP = 0x[0-9a-fA-F]{8}
155 TUNGETIFF = 0x[0-9a-fA-F]{8}
156 TUNSETIFF = 0x[0-9a-fA-F]{8}
157 IFF_NO_PI = 0x[0-9a-fA-F]{8}
158 IFF_TAP = 0x[0-9a-fA-F]{8}
159 IFF_TUN = 0x[0-9a-fA-F]{8}
160 IFF_VNET_HDR = 0x[0-9a-fA-F]{8}
161 TUN_PKT_STRIP = 0x[0-9a-fA-F]{8}
162 IFHWADDRLEN = 0x[0-9a-fA-F]{8}
163 IFNAMSIZ = 0x[0-9a-fA-F]{8}
164 IFREQ_SZ = 0x[0-9a-fA-F]{8}
165 FIONREAD = 0x[0-9a-fA-F]{8}.*
166 """
167
168         comp_build = r".*(Identity added|gcc).*"
169
170         xml = exp.to_xml()
171
172         controller = ExperimentController(xml, self.root_dir)
173         try:
174             controller.start()
175             while not all(controller.is_finished(app.guid) for app in apps):
176                 time.sleep(0.5)
177             
178             for app in apps:
179                 app_result = controller.trace(app.guid, "stdout") or ""
180                 self.assertTrue(re.match(comp_result, app_result, re.MULTILINE),
181                     "Unexpected trace:\n" + app_result)
182
183                 build_result = controller.trace(app.guid, "buildlog") or ""
184                 self.assertTrue(re.match(comp_build, build_result, re.MULTILINE | re.DOTALL),
185                     "Unexpected trace:\n" + build_result)
186         
187         finally:
188             controller.stop()
189             controller.shutdown()
190
191     @test_util.skipUnless(test_util.pl_auth() is not None, "Test requires PlanetLab authentication info (PL_USER and PL_PASS environment variables)")
192     def test_simple(self):
193         self._test_simple(
194             daemonize_testbed = False,
195             controller_access_configuration = None)
196
197     @test_util.skipUnless(test_util.pl_auth() is not None, "Test requires PlanetLab authentication info (PL_USER and PL_PASS environment variables)")
198     def test_simple_daemonized(self):
199         access_config = proxy.AccessConfiguration({
200             DC.DEPLOYMENT_MODE : DC.MODE_DAEMON,
201             DC.ROOT_DIRECTORY : self.root_dir,
202         })
203
204         self._test_simple(
205             daemonize_testbed = False,
206             controller_access_configuration = access_config)
207
208     @test_util.skipUnless(test_util.pl_auth() is not None, "Test requires PlanetLab authentication info (PL_USER and PL_PASS environment variables)")
209     def test_z_simple_ssh(self): # _z_ cause we want it last - it messes up the process :(
210         # Recreate environment
211         environ = ' ; '.join( map("export %s=%r".__mod__, os.environ.iteritems()) )
212
213         env = test_util.test_environment()
214
215         access_config = proxy.AccessConfiguration({
216             DC.DEPLOYMENT_MODE : DC.MODE_DAEMON,
217             DC.ROOT_DIRECTORY : self.root_dir,
218             DC.LOG_LEVEL : DC.DEBUG_LEVEL,
219             DC.DEPLOYMENT_COMMUNICATION : DC.ACCESS_SSH,
220             DC.DEPLOYMENT_PORT : env.port,
221             DC.USE_AGENT : True,
222             DC.DEPLOYMENT_ENVIRONMENT_SETUP : environ,
223         })
224
225         self._test_simple(
226             daemonize_testbed = False,
227             controller_access_configuration = access_config,
228             environ = environ)
229         
230
231 if __name__ == '__main__':
232     unittest.main()
233