blob: 9152220773df79abc150f73de5300f49ce82a2e8 [file] [log] [blame]
Elena Ezhovaa5105e62013-11-26 20:46:52 +04001# Copyright 2014 Mirantis.inc
2# All Rights Reserved.
3#
4# Licensed under the Apache License, Version 2.0 (the "License"); you may
5# not use this file except in compliance with the License. You may obtain
6# a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13# License for the specific language governing permissions and limitations
14# under the License.
15
16import time
17import urllib
18
19from tempest.api.network import common as net_common
Elena Ezhovaa5105e62013-11-26 20:46:52 +040020from tempest import config
21from tempest import exceptions
22from tempest.scenario import manager
23from tempest import test
24
25config = config.CONF
26
27
28class TestLoadBalancerBasic(manager.NetworkScenarioTest):
29
30 """
31 This test checks basic load balancing.
32
33 The following is the scenario outline:
34 1. Create an instance
35 2. SSH to the instance and start two servers
36 3. Create a load balancer with two members and with ROUND_ROBIN algorithm
37 associate the VIP with a floating ip
38 4. Send 10 requests to the floating ip and check that they are shared
39 between the two servers and that both of them get equal portions
40 of the requests
41 """
42
43 @classmethod
44 def check_preconditions(cls):
45 super(TestLoadBalancerBasic, cls).check_preconditions()
46 cfg = config.network
47 if not test.is_extension_enabled('lbaas', 'network'):
48 msg = 'LBaaS Extension is not enabled'
49 cls.enabled = False
50 raise cls.skipException(msg)
51 if not (cfg.tenant_networks_reachable or cfg.public_network_id):
52 msg = ('Either tenant_networks_reachable must be "true", or '
53 'public_network_id must be defined.')
54 cls.enabled = False
55 raise cls.skipException(msg)
56
57 @classmethod
58 def setUpClass(cls):
59 super(TestLoadBalancerBasic, cls).setUpClass()
60 cls.check_preconditions()
Elena Ezhovaa5105e62013-11-26 20:46:52 +040061 cls.servers_keypairs = {}
Elena Ezhovaa5105e62013-11-26 20:46:52 +040062 cls.members = []
Elena Ezhovaa5105e62013-11-26 20:46:52 +040063 cls.floating_ips = {}
Elena Ezhova4a27b462014-04-09 15:25:46 +040064 cls.server_ips = {}
Elena Ezhovaa5105e62013-11-26 20:46:52 +040065 cls.port1 = 80
66 cls.port2 = 88
67
Elena Ezhova4a27b462014-04-09 15:25:46 +040068 def setUp(self):
69 super(TestLoadBalancerBasic, self).setUp()
70 self.server_ips = {}
Darragh O'Reilly7c8176e2014-04-26 16:03:23 +000071 self.server_fixed_ips = {}
Elena Ezhova4a27b462014-04-09 15:25:46 +040072 self._create_security_group()
73
74 def cleanup_wrapper(self, resource):
75 self.cleanup_resource(resource, self.__class__.__name__)
76
77 def _create_security_group(self):
78 self.security_group = self._create_security_group_neutron(
79 tenant_id=self.tenant_id)
Eugene Nikanorov55d13142014-03-24 15:39:21 +040080 self._create_security_group_rules_for_port(self.port1)
81 self._create_security_group_rules_for_port(self.port2)
Elena Ezhova4a27b462014-04-09 15:25:46 +040082 self.addCleanup(self.cleanup_wrapper, self.security_group)
Eugene Nikanorov55d13142014-03-24 15:39:21 +040083
84 def _create_security_group_rules_for_port(self, port):
85 rule = {
86 'direction': 'ingress',
87 'protocol': 'tcp',
88 'port_range_min': port,
89 'port_range_max': port,
90 }
91 self._create_security_group_rule(
92 client=self.network_client,
Elena Ezhova4a27b462014-04-09 15:25:46 +040093 secgroup=self.security_group,
Eugene Nikanorov55d13142014-03-24 15:39:21 +040094 tenant_id=self.tenant_id,
95 **rule)
Elena Ezhovaa5105e62013-11-26 20:46:52 +040096
Elena Ezhova4a27b462014-04-09 15:25:46 +040097 def _create_server(self, name):
Elena Ezhovaa5105e62013-11-26 20:46:52 +040098 keypair = self.create_keypair(name='keypair-%s' % name)
Elena Ezhova4a27b462014-04-09 15:25:46 +040099 self.addCleanup(self.cleanup_wrapper, keypair)
100 security_groups = [self.security_group.name]
Elena Ezhova91531102014-02-07 17:25:58 +0400101 net = self._list_networks(tenant_id=self.tenant_id)[0]
Yair Fried2d2f3fe2014-02-24 16:19:20 +0200102 create_kwargs = {
103 'nics': [
Elena Ezhova91531102014-02-07 17:25:58 +0400104 {'net-id': net['id']},
Yair Fried2d2f3fe2014-02-24 16:19:20 +0200105 ],
106 'key_name': keypair.name,
107 'security_groups': security_groups,
108 }
109 server = self.create_server(name=name,
110 create_kwargs=create_kwargs)
Elena Ezhova4a27b462014-04-09 15:25:46 +0400111 self.addCleanup(self.cleanup_wrapper, server)
112 self.servers_keypairs[server.id] = keypair
Elena Ezhova91531102014-02-07 17:25:58 +0400113 if (config.network.public_network_id and not
114 config.network.tenant_networks_reachable):
115 public_network_id = config.network.public_network_id
116 floating_ip = self._create_floating_ip(
117 server, public_network_id)
Elena Ezhova4a27b462014-04-09 15:25:46 +0400118 self.addCleanup(self.cleanup_wrapper, floating_ip)
Elena Ezhova91531102014-02-07 17:25:58 +0400119 self.floating_ips[floating_ip] = server
Elena Ezhova4a27b462014-04-09 15:25:46 +0400120 self.server_ips[server.id] = floating_ip.floating_ip_address
Elena Ezhova91531102014-02-07 17:25:58 +0400121 else:
Zhi Kun Liucd2e3772014-04-15 21:18:18 -0500122 self.server_ips[server.id] = server.networks[net['name']][0]
Darragh O'Reilly7c8176e2014-04-26 16:03:23 +0000123 self.server_fixed_ips[server.id] = server.networks[net['name']][0]
Elena Ezhovaa5105e62013-11-26 20:46:52 +0400124 self.assertTrue(self.servers_keypairs)
Elena Ezhova91531102014-02-07 17:25:58 +0400125 return server
Elena Ezhovaa5105e62013-11-26 20:46:52 +0400126
Elena Ezhova4a27b462014-04-09 15:25:46 +0400127 def _create_servers(self):
128 for count in range(2):
129 self._create_server(name=("server%s" % (count + 1)))
130 self.assertEqual(len(self.servers_keypairs), 2)
131
132 def _start_servers(self):
Elena Ezhovaa5105e62013-11-26 20:46:52 +0400133 """
Elena Ezhova4a27b462014-04-09 15:25:46 +0400134 Start two backends
135
Elena Ezhovaa5105e62013-11-26 20:46:52 +0400136 1. SSH to the instance
Elena Ezhova91531102014-02-07 17:25:58 +0400137 2. Start two http backends listening on ports 80 and 88 respectively
Elena Ezhova4a27b462014-04-09 15:25:46 +0400138 In case there are two instances, each backend is created on a separate
139 instance.
140
141 The backends are the inetd services. To start them we need to edit
142 /etc/inetd.conf in the following way:
143 www stream tcp nowait root /bin/sh sh /home/cirros/script_name
144
145 Where /home/cirros/script_name is a path to a script which
146 echoes the responses:
147 echo -e 'HTTP/1.0 200 OK\r\n\r\nserver_name
148
149 If we want the server to listen on port 88, then we use
150 "kerberos" instead of "www".
Elena Ezhovaa5105e62013-11-26 20:46:52 +0400151 """
Elena Ezhovaa5105e62013-11-26 20:46:52 +0400152
Elena Ezhova4a27b462014-04-09 15:25:46 +0400153 for server_id, ip in self.server_ips.iteritems():
154 private_key = self.servers_keypairs[server_id].private_key
155 server_name = self.compute_client.servers.get(server_id).name
156 ssh_client = self.get_remote_client(
157 server_or_ip=ip,
158 private_key=private_key)
159 ssh_client.validate_authentication()
160 # Create service for inetd
161 create_script = """sudo sh -c "echo -e \\"echo -e 'HTTP/1.0 """ \
162 """200 OK\\\\\\r\\\\\\n\\\\\\r\\\\\\n""" \
163 """%(server)s'\\" >>/home/cirros/%(script)s\""""
Elena Ezhovaa5105e62013-11-26 20:46:52 +0400164
Elena Ezhova4a27b462014-04-09 15:25:46 +0400165 cmd = create_script % {
166 'server': server_name,
167 'script': 'script1'}
168 ssh_client.exec_command(cmd)
169 # Configure inetd
170 configure_inetd = """sudo sh -c "echo -e \\"%(service)s """ \
171 """stream tcp nowait root /bin/sh sh """ \
172 """/home/cirros/%(script)s\\" >> """ \
173 """/etc/inetd.conf\""""
174 # "www" stands for port 80
175 cmd = configure_inetd % {'service': 'www',
176 'script': 'script1'}
177 ssh_client.exec_command(cmd)
178
179 if len(self.server_ips) == 1:
180 cmd = create_script % {'server': 'server2',
181 'script': 'script2'}
182 ssh_client.exec_command(cmd)
183 # "kerberos" stands for port 88
184 cmd = configure_inetd % {'service': 'kerberos',
185 'script': 'script2'}
186 ssh_client.exec_command(cmd)
187
188 # Get PIDs of inetd
189 pids = ssh_client.get_pids('inetd')
190 if pids != ['']:
191 # If there are any inetd processes, reload them
192 kill_cmd = "sudo kill -HUP %s" % ' '.join(pids)
193 ssh_client.exec_command(kill_cmd)
194 else:
195 # In other case start inetd
196 start_inetd = "sudo /usr/sbin/inetd /etc/inetd.conf"
197 ssh_client.exec_command(start_inetd)
198
199 def _check_connection(self, check_ip, port=80):
200 def try_connect(ip, port):
Elena Ezhovaa5105e62013-11-26 20:46:52 +0400201 try:
Elena Ezhova4a27b462014-04-09 15:25:46 +0400202 resp = urllib.urlopen("http://{0}:{1}/".format(ip, port))
203 if resp.getcode() == 200:
204 return True
205 return False
Elena Ezhovaa5105e62013-11-26 20:46:52 +0400206 except IOError:
207 return False
208 timeout = config.compute.ping_timeout
Elena Ezhova4a27b462014-04-09 15:25:46 +0400209 start = time.time()
210 while not try_connect(check_ip, port):
211 if (time.time() - start) > timeout:
Elena Ezhovaa5105e62013-11-26 20:46:52 +0400212 message = "Timed out trying to connect to %s" % check_ip
213 raise exceptions.TimeoutException(message)
214
215 def _create_pool(self):
216 """Create a pool with ROUND_ROBIN algorithm."""
Yair Fried2d2f3fe2014-02-24 16:19:20 +0200217 # get tenant subnet and verify there's only one
218 subnet = self._list_subnets(tenant_id=self.tenant_id)[0]
219 self.subnet = net_common.DeletableSubnet(client=self.network_client,
Elena Ezhova91531102014-02-07 17:25:58 +0400220 **subnet)
Yair Fried2d2f3fe2014-02-24 16:19:20 +0200221 self.pool = super(TestLoadBalancerBasic, self)._create_pool(
Elena Ezhova4a27b462014-04-09 15:25:46 +0400222 lb_method='ROUND_ROBIN',
223 protocol='HTTP',
224 subnet_id=self.subnet.id)
225 self.addCleanup(self.cleanup_wrapper, self.pool)
Yair Fried2d2f3fe2014-02-24 16:19:20 +0200226 self.assertTrue(self.pool)
Elena Ezhovaa5105e62013-11-26 20:46:52 +0400227
Elena Ezhova4a27b462014-04-09 15:25:46 +0400228 def _create_members(self):
Elena Ezhovaa5105e62013-11-26 20:46:52 +0400229 """
230 Create two members.
231
232 In case there is only one server, create both members with the same ip
233 but with different ports to listen on.
234 """
Elena Ezhova4a27b462014-04-09 15:25:46 +0400235
Darragh O'Reilly7c8176e2014-04-26 16:03:23 +0000236 for server_id, ip in self.server_fixed_ips.iteritems():
237 if len(self.server_fixed_ips) == 1:
Elena Ezhova4a27b462014-04-09 15:25:46 +0400238 member1 = self._create_member(address=ip,
239 protocol_port=self.port1,
240 pool_id=self.pool.id)
241 self.addCleanup(self.cleanup_wrapper, member1)
242 member2 = self._create_member(address=ip,
243 protocol_port=self.port2,
244 pool_id=self.pool.id)
245 self.addCleanup(self.cleanup_wrapper, member2)
246 self.members.extend([member1, member2])
247 else:
248 member = self._create_member(address=ip,
249 protocol_port=self.port1,
250 pool_id=self.pool.id)
251 self.addCleanup(self.cleanup_wrapper, member)
252 self.members.append(member)
Elena Ezhovaa5105e62013-11-26 20:46:52 +0400253 self.assertTrue(self.members)
254
255 def _assign_floating_ip_to_vip(self, vip):
256 public_network_id = config.network.public_network_id
Yair Fried2d2f3fe2014-02-24 16:19:20 +0200257 port_id = vip.port_id
Yair Frieda2e3b2c2014-02-17 10:56:10 +0200258 floating_ip = self._create_floating_ip(vip, public_network_id,
259 port_id=port_id)
Elena Ezhova4a27b462014-04-09 15:25:46 +0400260 self.addCleanup(self.cleanup_wrapper, floating_ip)
Yair Fried2d2f3fe2014-02-24 16:19:20 +0200261 self.floating_ips.setdefault(vip.id, [])
262 self.floating_ips[vip.id].append(floating_ip)
Elena Ezhovaa5105e62013-11-26 20:46:52 +0400263
264 def _create_load_balancer(self):
265 self._create_pool()
Elena Ezhova4a27b462014-04-09 15:25:46 +0400266 self._create_members()
267 self.vip = self._create_vip(protocol='HTTP',
268 protocol_port=80,
269 subnet_id=self.subnet.id,
270 pool_id=self.pool.id)
271 self.addCleanup(self.cleanup_wrapper, self.vip)
272 self.status_timeout(NeutronRetriever(self.network_client,
273 self.network_client.vip_path,
274 net_common.DeletableVip),
275 self.vip.id,
276 expected_status='ACTIVE')
Elena Ezhova91531102014-02-07 17:25:58 +0400277 if (config.network.public_network_id and not
278 config.network.tenant_networks_reachable):
279 self._assign_floating_ip_to_vip(self.vip)
280 self.vip_ip = self.floating_ips[
281 self.vip.id][0]['floating_ip_address']
282 else:
283 self.vip_ip = self.vip.address
Elena Ezhovaa5105e62013-11-26 20:46:52 +0400284
285 def _check_load_balancing(self):
286 """
Elena Ezhova4a27b462014-04-09 15:25:46 +0400287 1. Send 100 requests on the floating ip associated with the VIP
Elena Ezhovaa5105e62013-11-26 20:46:52 +0400288 2. Check that the requests are shared between
289 the two servers and that both of them get equal portions
290 of the requests
291 """
292
Elena Ezhova91531102014-02-07 17:25:58 +0400293 self._check_connection(self.vip_ip)
Elena Ezhova4a27b462014-04-09 15:25:46 +0400294 resp = self._send_requests(self.vip_ip)
295 self.assertEqual(set(["server1\n", "server2\n"]), set(resp))
296 self.assertEqual(50, resp.count("server1\n"))
297 self.assertEqual(50, resp.count("server2\n"))
298
299 def _send_requests(self, vip_ip):
Elena Ezhovaa5105e62013-11-26 20:46:52 +0400300 resp = []
Elena Ezhova4a27b462014-04-09 15:25:46 +0400301 for count in range(100):
Elena Ezhovaa5105e62013-11-26 20:46:52 +0400302 resp.append(
303 urllib.urlopen(
Elena Ezhova4a27b462014-04-09 15:25:46 +0400304 "http://{0}/".format(vip_ip)).read())
305 return resp
Elena Ezhovaa5105e62013-11-26 20:46:52 +0400306
Joe Gordon66fdac62014-04-29 16:42:52 -0700307 @test.skip_because(bug='1295165')
Elena Ezhovaa5105e62013-11-26 20:46:52 +0400308 @test.attr(type='smoke')
309 @test.services('compute', 'network')
310 def test_load_balancer_basic(self):
Elena Ezhova4a27b462014-04-09 15:25:46 +0400311 self._create_server('server1')
312 self._start_servers()
Elena Ezhovaa5105e62013-11-26 20:46:52 +0400313 self._create_load_balancer()
314 self._check_load_balancing()
315
316
317class NeutronRetriever(object):
Elena Ezhova4a27b462014-04-09 15:25:46 +0400318 """
319 Helper class to make possible handling neutron objects returned by GET
320 requests as attribute dicts.
321
322 Whet get() method is called, the returned dictionary is wrapped into
323 a corresponding DeletableResource class which provides attribute access
324 to dictionary values.
325
326 Usage:
327 This retriever is used to allow using status_timeout from
328 tempest.manager with Neutron objects.
329 """
330
Elena Ezhovaa5105e62013-11-26 20:46:52 +0400331 def __init__(self, network_client, path, resource):
332 self.network_client = network_client
333 self.path = path
334 self.resource = resource
335
336 def get(self, thing_id):
337 obj = self.network_client.get(self.path % thing_id)
338 return self.resource(client=self.network_client, **obj.values()[0])