61a8902f66f45a45d45c53ddb5577dfe591d61db
[nepi.git] / test / testbeds / planetlab / integration_multi.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, ATTR_NEPI_TESTBED_ENVIRONMENT_SETUP
9 import os
10 import re
11 import shutil
12 import tempfile
13 import test_util
14 import time
15 import unittest
16
17 class PlanetLabMultiIntegrationTestCase(unittest.TestCase):
18     testbed_id = "planetlab"
19     
20     slicename1 = "inria_nepi"
21     plchost1 = "nepiplc.pl.sophia.inria.fr"
22     plcvnet1 = "192.168.2"
23
24     slicename2 = "inria_nepi2"
25     plchost2 = "nepiplc.pl.sophia.inria.fr"
26     plcvnet2 = "192.168.3"
27     
28     host1pl1 = "nepi1.pl.sophia.inria.fr"
29     host2pl1 = "nepi2.pl.sophia.inria.fr"
30
31     host1pl2 = "nepi3.pl.sophia.inria.fr"
32     host2pl2 = "nepi5.pl.sophia.inria.fr"
33
34     port_base = 2000 + (os.getpid() % 1000) * 13
35     
36     def setUp(self):
37         self.root_dir = tempfile.mkdtemp()
38         self.__class__.port_base = self.port_base + 100
39
40     def tearDown(self):
41         try:
42             shutil.rmtree(self.root_dir)
43         except:
44             # retry
45             time.sleep(0.1)
46             shutil.rmtree(self.root_dir)
47
48     def make_experiment_desc(self):
49         testbed_id = self.testbed_id
50         
51         slicename1 = self.slicename1
52         plchost1 = self.plchost1
53         
54         slicename2 = self.slicename2
55         plchost2 = self.plchost2
56         
57         pl_ssh_key = os.environ.get(
58             "PL_SSH_KEY",
59             "%s/.ssh/id_rsa_planetlab" % (os.environ['HOME'],) )
60         pl_user, pl_pwd = test_util.pl_auth()
61
62         exp_desc = ExperimentDescription()
63         pl_provider = FactoriesProvider(testbed_id)
64         pl_desc = exp_desc.add_testbed_description(pl_provider)
65         pl_desc.set_attribute_value("homeDirectory", self.root_dir)
66         pl_desc.set_attribute_value("slice", slicename1)
67         pl_desc.set_attribute_value("sliceSSHKey", pl_ssh_key)
68         pl_desc.set_attribute_value("authUser", pl_user)
69         pl_desc.set_attribute_value("authPass", pl_pwd)
70         pl_desc.set_attribute_value("plcHost", plchost1)
71         pl_desc.set_attribute_value("tapPortBase", self.port_base)
72         pl_desc.set_attribute_value("p2pDeployment", False) # it's interactive, we don't want it in tests
73         pl_desc.set_attribute_value("dedicatedSlice", True)
74         pl_desc.set_attribute_value("plLogLevel", "DEBUG")
75
76         pl_desc2 = exp_desc.add_testbed_description(pl_provider)
77         pl_desc2.set_attribute_value("homeDirectory", self.root_dir+"v2")
78         pl_desc2.set_attribute_value("slice", slicename2)
79         pl_desc2.set_attribute_value("sliceSSHKey", pl_ssh_key)
80         pl_desc2.set_attribute_value("authUser", pl_user)
81         pl_desc2.set_attribute_value("authPass", pl_pwd)
82         pl_desc2.set_attribute_value("plcHost", plchost2)
83         pl_desc2.set_attribute_value("tapPortBase", self.port_base+500)
84         pl_desc2.set_attribute_value("p2pDeployment", False) # it's interactive, we don't want it in tests
85         pl_desc2.set_attribute_value("dedicatedSlice", True)
86         pl_desc2.set_attribute_value("plLogLevel", "DEBUG")
87         
88         return pl_desc, pl_desc2, exp_desc
89     
90     def make_pl_tapnode(self, pl, tapip, hostname, label_prefix):
91         node1 = pl.create("Node")
92         node1.set_attribute_value("hostname", hostname)
93         node1.set_attribute_value("label", label_prefix)
94         iface1 = pl.create("NodeInterface")
95         iface1.set_attribute_value("label", label_prefix+"iface")
96         tap1 = pl.create("TapInterface")
97         tap1.enable_trace("packets") # for error output
98         tap1.set_attribute_value("label", label_prefix+"tap")
99         inet = pl.create("Internet")
100         node1.connector("devs").connect(iface1.connector("node"))
101         node1.connector("devs").connect(tap1.connector("node"))
102         iface1.connector("inet").connect(inet.connector("devs"))
103         
104         tap1ip = tap1.add_address()
105         tap1ip.set_attribute_value("Address", tapip)
106         tap1ip.set_attribute_value("NetPrefix", 24)
107         tap1ip.set_attribute_value("Broadcast", False)
108         
109         return node1, iface1, tap1, tap1ip, inet
110     
111     def _test_plpl_crossconnect(self, proto, recover = False):
112         pl, pl2, exp = self.make_experiment_desc()
113         
114         if recover:
115             pl.set_attribute_value(DC.RECOVERY_POLICY, DC.POLICY_RECOVER)
116             pl2.set_attribute_value(DC.RECOVERY_POLICY, DC.POLICY_RECOVER)
117         
118         # Create PL node, ifaces, assign addresses
119         node1, iface1, tap1, tap1ip, inet1 = self.make_pl_tapnode(pl, 
120             self.plcvnet1+".2", self.host1pl1, "node1")
121         node2, iface2, tap2, tap2ip, inet2 = self.make_pl_tapnode(pl2, 
122             self.plcvnet2+".3", self.host1pl2, "node2")
123             
124         # Connect the two
125         tap1.connector(proto).connect(tap2.connector(proto))
126         tap1.set_attribute_value("pointopoint", "{#[node2tap].addr[0].[Address]#}")
127         tap2.set_attribute_value("pointopoint", "{#[node1tap].addr[0].[Address]#}")
128         
129         # Disable encryption for GRE
130         if proto == "gre":
131             tap1.set_attribute_value("tun_cipher", "PLAIN")
132             tap2.set_attribute_value("tun_cipher", "PLAIN")
133         
134         # Create PlanetLab ping application, pinging the from one PL to another
135         ping = pl.create("Application")
136         ping.set_attribute_value("command", "ping -qc10 {#[node2tap].addr[0].[Address]#}")
137         ping.enable_trace("stdout")
138         ping.enable_trace("stderr")
139         ping.connector("node").connect(node1.connector("apps"))
140
141         comp_result = r"""PING .* \(.*\) \d*\(\d*\) bytes of data.
142
143 --- .* ping statistics ---
144 10 packets transmitted, 10 received, 0% packet loss, time \d*ms.*
145 """
146
147         xml = exp.to_xml()
148
149         try:
150             controller = ExperimentController(xml, self.root_dir)
151             controller.start()
152             
153             if recover:
154                 controller = None
155                 controller = ExperimentController(None, self.root_dir)
156                 controller.recover()
157
158             while not controller.is_finished(ping.guid):
159                 time.sleep(0.5)
160               
161             ping_result = controller.trace(ping.guid, "stdout")
162             tap_trace = controller.trace(tap1.guid, "packets")
163             tap2_trace = controller.trace(tap2.guid, "packets")
164         
165         finally:
166             if controller is not None:
167                 try:
168                     controller.stop()
169                 except:
170                     import traceback
171                     traceback.print_exc()
172                 try:
173                     controller.shutdown()
174                 except:
175                     import traceback
176                     traceback.print_exc()
177
178         # asserts at the end, to make sure there's proper cleanup
179         self.assertTrue(re.match(comp_result, ping_result, re.MULTILINE),
180             "Unexpected trace:\n%s\nTap trace at origin:\n%s\nTap trace at destination:\n%s\n" % (
181                 ping_result,
182                 tap_trace,
183                 tap2_trace) )
184
185     @test_util.skipUnless(test_util.pl_auth() is not None, 
186         "Test requires PlanetLab authentication info (PL_USER and PL_PASS environment variables)")
187     def test_plpl_crossconnect_udp(self):
188         self._test_plpl_crossconnect("udp")
189
190     @test_util.skipUnless(test_util.pl_auth() is not None, 
191         "Test requires PlanetLab authentication info (PL_USER and PL_PASS environment variables)")
192     def test_plpl_crossconnect_tcp(self):
193         self._test_plpl_crossconnect("tcp")
194
195     @test_util.skipUnless(test_util.pl_auth() is not None, 
196         "Test requires PlanetLab authentication info (PL_USER and PL_PASS environment variables)")
197     def test_plpl_crossconnect_gre(self):
198         self._test_plpl_crossconnect("gre")
199
200     @test_util.skipUnless(test_util.pl_auth() is not None, 
201         "Test requires PlanetLab authentication info (PL_USER and PL_PASS environment variables)")
202     def test_plpl_crossconnect_udp_recover(self):
203         self._test_plpl_crossconnect("udp", 
204             recover = True)
205
206
207 if __name__ == '__main__':
208     unittest.main()
209