Merge non-handshake stuff
[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 shutil
11 import tempfile
12 import test_util
13 import time
14 import unittest
15 import re
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
75         pl_desc2 = exp_desc.add_testbed_description(pl_provider)
76         pl_desc2.set_attribute_value("homeDirectory", self.root_dir+"v2")
77         pl_desc2.set_attribute_value("slice", slicename2)
78         pl_desc2.set_attribute_value("sliceSSHKey", pl_ssh_key)
79         pl_desc2.set_attribute_value("authUser", pl_user)
80         pl_desc2.set_attribute_value("authPass", pl_pwd)
81         pl_desc2.set_attribute_value("plcHost", plchost2)
82         pl_desc2.set_attribute_value("tapPortBase", self.port_base+500)
83         pl_desc2.set_attribute_value("p2pDeployment", False) # it's interactive, we don't want it in tests
84         pl_desc2.set_attribute_value("dedicatedSlice", True)
85         
86         return pl_desc, pl_desc2, exp_desc
87     
88     def make_pl_tapnode(self, pl, tapip, hostname, label_prefix):
89         node1 = pl.create("Node")
90         node1.set_attribute_value("hostname", hostname)
91         node1.set_attribute_value("label", label_prefix)
92         iface1 = pl.create("NodeInterface")
93         iface1.set_attribute_value("label", label_prefix+"iface")
94         tap1 = pl.create("TapInterface")
95         tap1.enable_trace("packets") # for error output
96         tap1.set_attribute_value("label", label_prefix+"tap")
97         inet = pl.create("Internet")
98         node1.connector("devs").connect(iface1.connector("node"))
99         node1.connector("devs").connect(tap1.connector("node"))
100         iface1.connector("inet").connect(inet.connector("devs"))
101         
102         tap1ip = tap1.add_address()
103         tap1ip.set_attribute_value("Address", tapip)
104         tap1ip.set_attribute_value("NetPrefix", 24)
105         tap1ip.set_attribute_value("Broadcast", False)
106         
107         return node1, iface1, tap1, tap1ip, inet
108     
109     def _test_plpl_crossconnect(self, proto, recover = False):
110         pl, pl2, exp = self.make_experiment_desc()
111         
112         if recover:
113             pl.set_attribute_value(DC.RECOVERY_POLICY, DC.POLICY_RECOVER)
114             pl2.set_attribute_value(DC.RECOVERY_POLICY, DC.POLICY_RECOVER)
115         
116         # Create PL node, ifaces, assign addresses
117         node1, iface1, tap1, tap1ip, inet1 = self.make_pl_tapnode(pl, 
118             self.plcvnet1+".2", self.host1pl1, "node1")
119         node2, iface2, tap2, tap2ip, inet2 = self.make_pl_tapnode(pl2, 
120             self.plcvnet2+".3", self.host1pl2, "node2")
121             
122         # Connect the two
123         tap1.connector(proto).connect(tap2.connector(proto))
124         tap1.set_attribute_value("pointopoint", "{#[node2tap].addr[0].[Address]#}")
125         tap2.set_attribute_value("pointopoint", "{#[node1tap].addr[0].[Address]#}")
126         
127         # Disable encryption for GRE
128         if proto == "gre":
129             tap1.set_attribute_value("tun_cipher", "PLAIN")
130             tap2.set_attribute_value("tun_cipher", "PLAIN")
131         
132         # Create PlanetLab ping application, pinging the from one PL to another
133         ping = pl.create("Application")
134         ping.set_attribute_value("command", "ping -qc10 {#[node2tap].addr[0].[Address]#}")
135         ping.enable_trace("stdout")
136         ping.enable_trace("stderr")
137         ping.connector("node").connect(node1.connector("apps"))
138
139         comp_result = r"""PING .* \(.*\) \d*\(\d*\) bytes of data.
140
141 --- .* ping statistics ---
142 10 packets transmitted, 10 received, 0% packet loss, time \d*ms.*
143 """
144
145         xml = exp.to_xml()
146
147         try:
148             controller = ExperimentController(xml, self.root_dir)
149             controller.start()
150             
151             if recover:
152                 controller = None
153                 controller = ExperimentController(None, self.root_dir)
154                 controller.recover()
155
156             while not controller.is_finished(ping.guid):
157                 time.sleep(0.5)
158               
159             ping_result = controller.trace(ping.guid, "stdout")
160             tap_trace = controller.trace(tap1.guid, "packets")
161             tap2_trace = controller.trace(tap2.guid, "packets")
162         
163         finally:
164             if controller is not None:
165                 try:
166                     controller.stop()
167                 except:
168                     import traceback
169                     traceback.print_exc()
170                 try:
171                     controller.shutdown()
172                 except:
173                     import traceback
174                     traceback.print_exc()
175
176         # asserts at the end, to make sure there's proper cleanup
177         self.assertTrue(re.match(comp_result, ping_result, re.MULTILINE),
178             "Unexpected trace:\n%s\nTap trace at origin:\n%s\nTap trace at destination:\n%s\n" % (
179                 ping_result,
180                 tap_trace,
181                 tap2_trace) )
182
183     @test_util.skipUnless(test_util.pl_auth() is not None, 
184         "Test requires PlanetLab authentication info (PL_USER and PL_PASS environment variables)")
185     def test_plpl_crossconnect_udp(self):
186         self._test_plpl_crossconnect("udp")
187
188     @test_util.skipUnless(test_util.pl_auth() is not None, 
189         "Test requires PlanetLab authentication info (PL_USER and PL_PASS environment variables)")
190     def test_plpl_crossconnect_tcp(self):
191         self._test_plpl_crossconnect("tcp")
192
193     @test_util.skipUnless(test_util.pl_auth() is not None, 
194         "Test requires PlanetLab authentication info (PL_USER and PL_PASS environment variables)")
195     def test_plpl_crossconnect_gre(self):
196         self._test_plpl_crossconnect("gre")
197
198     @test_util.skipUnless(test_util.pl_auth() is not None, 
199         "Test requires PlanetLab authentication info (PL_USER and PL_PASS environment variables)")
200     def test_plpl_crossconnect_udp_recover(self):
201         self._test_plpl_crossconnect("udp", 
202             recover = True)
203
204
205 if __name__ == '__main__':
206     unittest.main()
207