blob: 426ada329fea98eabd97edfd549ab597f8c5458f [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
Elena Ezhova6e73f462014-04-18 17:38:13 +040016
Elena Ezhova6e73f462014-04-18 17:38:13 +040017import tempfile
Elena Ezhovaa5105e62013-11-26 20:46:52 +040018import time
Elena Ezhova6e73f462014-04-18 17:38:13 +040019import urllib2
Elena Ezhovaa5105e62013-11-26 20:46:52 +040020
Elena Ezhova6e73f462014-04-18 17:38:13 +040021from tempest.common import commands
Elena Ezhovaa5105e62013-11-26 20:46:52 +040022from tempest import config
23from tempest import exceptions
24from tempest.scenario import manager
Yair Fried8186f812014-09-28 09:39:39 +030025from tempest.services.network import resources as net_resources
Elena Ezhovaa5105e62013-11-26 20:46:52 +040026from tempest import test
27
28config = config.CONF
29
30
Andrea Frittoli4971fc82014-09-25 10:22:20 +010031class TestLoadBalancerBasic(manager.NetworkScenarioTest):
Elena Ezhovaa5105e62013-11-26 20:46:52 +040032
33 """
34 This test checks basic load balancing.
35
36 The following is the scenario outline:
37 1. Create an instance
38 2. SSH to the instance and start two servers
39 3. Create a load balancer with two members and with ROUND_ROBIN algorithm
40 associate the VIP with a floating ip
armando-migliaccio29bd43b2014-09-10 11:43:15 -070041 4. Send NUM requests to the floating ip and check that they are shared
42 between the two servers.
Elena Ezhovaa5105e62013-11-26 20:46:52 +040043 """
44
45 @classmethod
46 def check_preconditions(cls):
47 super(TestLoadBalancerBasic, cls).check_preconditions()
48 cfg = config.network
49 if not test.is_extension_enabled('lbaas', 'network'):
50 msg = 'LBaaS Extension is not enabled'
Elena Ezhovaa5105e62013-11-26 20:46:52 +040051 raise cls.skipException(msg)
52 if not (cfg.tenant_networks_reachable or cfg.public_network_id):
53 msg = ('Either tenant_networks_reachable must be "true", or '
54 'public_network_id must be defined.')
Elena Ezhovaa5105e62013-11-26 20:46:52 +040055 raise cls.skipException(msg)
56
57 @classmethod
Andrea Frittoliac20b5e2014-09-15 13:31:14 +010058 def resource_setup(cls):
59 super(TestLoadBalancerBasic, cls).resource_setup()
Elena Ezhovaa5105e62013-11-26 20:46:52 +040060 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
armando-migliaccio29bd43b2014-09-10 11:43:15 -070067 cls.num = 50
Elena Ezhovaa5105e62013-11-26 20:46:52 +040068
Elena Ezhova4a27b462014-04-09 15:25:46 +040069 def setUp(self):
70 super(TestLoadBalancerBasic, self).setUp()
71 self.server_ips = {}
Darragh O'Reilly7c8176e2014-04-26 16:03:23 +000072 self.server_fixed_ips = {}
Miguel Lavalle02ba8cd2014-09-01 19:23:22 -050073 self._create_security_group_for_test()
Adam Gandelmanbbf5c472014-08-12 16:46:12 -070074 self._set_net_and_subnet()
75
76 def _set_net_and_subnet(self):
77 """
78 Query and set appropriate network and subnet attributes to be used
79 for the test. Existing tenant networks are used if they are found.
80 The configured private network and associated subnet is used as a
81 fallback in absence of tenant networking.
82 """
83 try:
84 tenant_net = self._list_networks(tenant_id=self.tenant_id)[0]
85 except IndexError:
86 tenant_net = None
87
88 if tenant_net:
89 tenant_subnet = self._list_subnets(tenant_id=self.tenant_id)[0]
Yair Fried8186f812014-09-28 09:39:39 +030090 self.subnet = net_resources.DeletableSubnet(
Adam Gandelmanbbf5c472014-08-12 16:46:12 -070091 client=self.network_client,
92 **tenant_subnet)
93 self.network = tenant_net
94 else:
95 self.network = self._get_network_by_name(
96 config.compute.fixed_network_name)
97 # TODO(adam_g): We are assuming that the first subnet associated
98 # with the fixed network is the one we want. In the future, we
99 # should instead pull a subnet id from config, which is set by
100 # devstack/admin/etc.
101 subnet = self._list_subnets(network_id=self.network['id'])[0]
Yair Fried8186f812014-09-28 09:39:39 +0300102 self.subnet = net_resources.AttributeDict(subnet)
Elena Ezhova4a27b462014-04-09 15:25:46 +0400103
Miguel Lavalle02ba8cd2014-09-01 19:23:22 -0500104 def _create_security_group_for_test(self):
105 self.security_group = self._create_security_group(
Elena Ezhova4a27b462014-04-09 15:25:46 +0400106 tenant_id=self.tenant_id)
Eugene Nikanorov55d13142014-03-24 15:39:21 +0400107 self._create_security_group_rules_for_port(self.port1)
108 self._create_security_group_rules_for_port(self.port2)
109
110 def _create_security_group_rules_for_port(self, port):
111 rule = {
112 'direction': 'ingress',
113 'protocol': 'tcp',
114 'port_range_min': port,
115 'port_range_max': port,
116 }
117 self._create_security_group_rule(
Elena Ezhova4a27b462014-04-09 15:25:46 +0400118 secgroup=self.security_group,
Eugene Nikanorov55d13142014-03-24 15:39:21 +0400119 tenant_id=self.tenant_id,
120 **rule)
Elena Ezhovaa5105e62013-11-26 20:46:52 +0400121
Elena Ezhova4a27b462014-04-09 15:25:46 +0400122 def _create_server(self, name):
Miguel Lavalle02ba8cd2014-09-01 19:23:22 -0500123 keypair = self.create_keypair()
Ken'ichi Ohmichi1b3461e2014-12-02 03:41:07 +0000124 security_groups = [{'name': self.security_group['name']}]
Yair Fried2d2f3fe2014-02-24 16:19:20 +0200125 create_kwargs = {
Sergey Shnaidmancb85dcd2014-11-26 20:01:11 +0300126 'networks': [
127 {'uuid': self.network['id']},
Yair Fried2d2f3fe2014-02-24 16:19:20 +0200128 ],
Miguel Lavalle02ba8cd2014-09-01 19:23:22 -0500129 'key_name': keypair['name'],
Yair Fried2d2f3fe2014-02-24 16:19:20 +0200130 'security_groups': security_groups,
131 }
Adam Gandelmanbbf5c472014-08-12 16:46:12 -0700132 net_name = self.network['name']
Miguel Lavalle02ba8cd2014-09-01 19:23:22 -0500133 server = self.create_server(name=name, create_kwargs=create_kwargs)
134 self.servers_keypairs[server['id']] = keypair
Elena Ezhova91531102014-02-07 17:25:58 +0400135 if (config.network.public_network_id and not
136 config.network.tenant_networks_reachable):
137 public_network_id = config.network.public_network_id
Yair Friedae0e73d2014-11-24 11:56:26 +0200138 floating_ip = self.create_floating_ip(
Elena Ezhova91531102014-02-07 17:25:58 +0400139 server, public_network_id)
140 self.floating_ips[floating_ip] = server
Miguel Lavalle02ba8cd2014-09-01 19:23:22 -0500141 self.server_ips[server['id']] = floating_ip.floating_ip_address
Elena Ezhova91531102014-02-07 17:25:58 +0400142 else:
Miguel Lavalle02ba8cd2014-09-01 19:23:22 -0500143 self.server_ips[server['id']] =\
144 server['addresses'][net_name][0]['addr']
145 self.server_fixed_ips[server['id']] =\
146 server['addresses'][net_name][0]['addr']
Elena Ezhovaa5105e62013-11-26 20:46:52 +0400147 self.assertTrue(self.servers_keypairs)
Elena Ezhova91531102014-02-07 17:25:58 +0400148 return server
Elena Ezhovaa5105e62013-11-26 20:46:52 +0400149
Elena Ezhova4a27b462014-04-09 15:25:46 +0400150 def _create_servers(self):
151 for count in range(2):
152 self._create_server(name=("server%s" % (count + 1)))
153 self.assertEqual(len(self.servers_keypairs), 2)
154
155 def _start_servers(self):
Elena Ezhovaa5105e62013-11-26 20:46:52 +0400156 """
Elena Ezhova4a27b462014-04-09 15:25:46 +0400157 Start two backends
158
Elena Ezhovaa5105e62013-11-26 20:46:52 +0400159 1. SSH to the instance
Elena Ezhova91531102014-02-07 17:25:58 +0400160 2. Start two http backends listening on ports 80 and 88 respectively
Elena Ezhovaa5105e62013-11-26 20:46:52 +0400161 """
Elena Ezhova4a27b462014-04-09 15:25:46 +0400162 for server_id, ip in self.server_ips.iteritems():
Miguel Lavalle02ba8cd2014-09-01 19:23:22 -0500163 private_key = self.servers_keypairs[server_id]['private_key']
David Kranz0fb14292015-02-11 15:55:20 -0500164 server_name = self.servers_client.get_server(server_id)['name']
Elena Ezhova6e73f462014-04-18 17:38:13 +0400165 username = config.scenario.ssh_user
Elena Ezhova4a27b462014-04-09 15:25:46 +0400166 ssh_client = self.get_remote_client(
167 server_or_ip=ip,
168 private_key=private_key)
Elena Ezhovaa5105e62013-11-26 20:46:52 +0400169
Robert Mizielskie1d88992014-07-15 15:28:09 +0200170 # Write a backend's response into a file
Sergey Shnaidmancb85dcd2014-11-26 20:01:11 +0300171 resp = ('echo -ne "HTTP/1.1 200 OK\r\nContent-Length: 7\r\n'
172 'Connection: close\r\nContent-Type: text/html; '
173 'charset=UTF-8\r\n\r\n%s"; cat >/dev/null')
Elena Ezhova0c8e3292014-06-05 12:15:39 +0400174
Elena Ezhova6e73f462014-04-18 17:38:13 +0400175 with tempfile.NamedTemporaryFile() as script:
176 script.write(resp % server_name)
177 script.flush()
178 with tempfile.NamedTemporaryFile() as key:
179 key.write(private_key)
180 key.flush()
181 commands.copy_file_to_host(script.name,
Elena Ezhova0c8e3292014-06-05 12:15:39 +0400182 "/tmp/script1",
Elena Ezhova6e73f462014-04-18 17:38:13 +0400183 ip,
184 username, key.name)
Elena Ezhova0c8e3292014-06-05 12:15:39 +0400185
Elena Ezhova6e73f462014-04-18 17:38:13 +0400186 # Start netcat
Sergey Shnaidmancb85dcd2014-11-26 20:01:11 +0300187 start_server = ('while true; do '
Attila Fazekas63838592015-01-09 09:34:40 +0100188 'sudo nc -ll -p %(port)s -e sh /tmp/%(script)s; '
Sergey Shnaidmancb85dcd2014-11-26 20:01:11 +0300189 'done &')
Elena Ezhova6e73f462014-04-18 17:38:13 +0400190 cmd = start_server % {'port': self.port1,
191 'script': 'script1'}
Elena Ezhova4a27b462014-04-09 15:25:46 +0400192 ssh_client.exec_command(cmd)
Elena Ezhova0c8e3292014-06-05 12:15:39 +0400193
Elena Ezhova4a27b462014-04-09 15:25:46 +0400194 if len(self.server_ips) == 1:
Elena Ezhova6e73f462014-04-18 17:38:13 +0400195 with tempfile.NamedTemporaryFile() as script:
196 script.write(resp % 'server2')
197 script.flush()
198 with tempfile.NamedTemporaryFile() as key:
199 key.write(private_key)
200 key.flush()
201 commands.copy_file_to_host(script.name,
Elena Ezhova0c8e3292014-06-05 12:15:39 +0400202 "/tmp/script2", ip,
Elena Ezhova6e73f462014-04-18 17:38:13 +0400203 username, key.name)
204 cmd = start_server % {'port': self.port2,
205 'script': 'script2'}
Elena Ezhova4a27b462014-04-09 15:25:46 +0400206 ssh_client.exec_command(cmd)
Elena Ezhova4a27b462014-04-09 15:25:46 +0400207
208 def _check_connection(self, check_ip, port=80):
209 def try_connect(ip, port):
Elena Ezhovaa5105e62013-11-26 20:46:52 +0400210 try:
Elena Ezhova6e73f462014-04-18 17:38:13 +0400211 resp = urllib2.urlopen("http://{0}:{1}/".format(ip, port))
Elena Ezhova4a27b462014-04-09 15:25:46 +0400212 if resp.getcode() == 200:
213 return True
214 return False
Elena Ezhovaa5105e62013-11-26 20:46:52 +0400215 except IOError:
216 return False
Sergey Shnaidmancb85dcd2014-11-26 20:01:11 +0300217 except urllib2.HTTPError:
218 return False
Elena Ezhovaa5105e62013-11-26 20:46:52 +0400219 timeout = config.compute.ping_timeout
Elena Ezhova4a27b462014-04-09 15:25:46 +0400220 start = time.time()
221 while not try_connect(check_ip, port):
222 if (time.time() - start) > timeout:
Elena Ezhovaa5105e62013-11-26 20:46:52 +0400223 message = "Timed out trying to connect to %s" % check_ip
224 raise exceptions.TimeoutException(message)
225
226 def _create_pool(self):
227 """Create a pool with ROUND_ROBIN algorithm."""
Yair Fried2d2f3fe2014-02-24 16:19:20 +0200228 self.pool = super(TestLoadBalancerBasic, self)._create_pool(
Elena Ezhova4a27b462014-04-09 15:25:46 +0400229 lb_method='ROUND_ROBIN',
230 protocol='HTTP',
231 subnet_id=self.subnet.id)
Yair Fried2d2f3fe2014-02-24 16:19:20 +0200232 self.assertTrue(self.pool)
Elena Ezhovaa5105e62013-11-26 20:46:52 +0400233
Elena Ezhova4a27b462014-04-09 15:25:46 +0400234 def _create_members(self):
Elena Ezhovaa5105e62013-11-26 20:46:52 +0400235 """
236 Create two members.
237
238 In case there is only one server, create both members with the same ip
239 but with different ports to listen on.
240 """
Elena Ezhova4a27b462014-04-09 15:25:46 +0400241
Darragh O'Reilly7c8176e2014-04-26 16:03:23 +0000242 for server_id, ip in self.server_fixed_ips.iteritems():
243 if len(self.server_fixed_ips) == 1:
Elena Ezhova4a27b462014-04-09 15:25:46 +0400244 member1 = self._create_member(address=ip,
245 protocol_port=self.port1,
246 pool_id=self.pool.id)
Elena Ezhova4a27b462014-04-09 15:25:46 +0400247 member2 = self._create_member(address=ip,
248 protocol_port=self.port2,
249 pool_id=self.pool.id)
Elena Ezhova4a27b462014-04-09 15:25:46 +0400250 self.members.extend([member1, member2])
251 else:
252 member = self._create_member(address=ip,
253 protocol_port=self.port1,
254 pool_id=self.pool.id)
Elena Ezhova4a27b462014-04-09 15:25:46 +0400255 self.members.append(member)
Elena Ezhovaa5105e62013-11-26 20:46:52 +0400256 self.assertTrue(self.members)
257
258 def _assign_floating_ip_to_vip(self, vip):
259 public_network_id = config.network.public_network_id
Yair Fried2d2f3fe2014-02-24 16:19:20 +0200260 port_id = vip.port_id
Yair Friedae0e73d2014-11-24 11:56:26 +0200261 floating_ip = self.create_floating_ip(vip, public_network_id,
262 port_id=port_id)
Yair Fried2d2f3fe2014-02-24 16:19:20 +0200263 self.floating_ips.setdefault(vip.id, [])
264 self.floating_ips[vip.id].append(floating_ip)
Elena Ezhovaa5105e62013-11-26 20:46:52 +0400265
266 def _create_load_balancer(self):
267 self._create_pool()
Elena Ezhova4a27b462014-04-09 15:25:46 +0400268 self._create_members()
269 self.vip = self._create_vip(protocol='HTTP',
270 protocol_port=80,
271 subnet_id=self.subnet.id,
272 pool_id=self.pool.id)
Miguel Lavalle02ba8cd2014-09-01 19:23:22 -0500273 self.vip.wait_for_status('ACTIVE')
Elena Ezhova91531102014-02-07 17:25:58 +0400274 if (config.network.public_network_id and not
275 config.network.tenant_networks_reachable):
276 self._assign_floating_ip_to_vip(self.vip)
277 self.vip_ip = self.floating_ips[
278 self.vip.id][0]['floating_ip_address']
279 else:
280 self.vip_ip = self.vip.address
Elena Ezhovaa5105e62013-11-26 20:46:52 +0400281
Darragh O'Reilly44fc88a2014-04-26 15:42:47 +0000282 # Currently the ovs-agent is not enforcing security groups on the
283 # vip port - see https://bugs.launchpad.net/neutron/+bug/1163569
284 # However the linuxbridge-agent does, and it is necessary to add a
285 # security group with a rule that allows tcp port 80 to the vip port.
Miguel Lavalle02ba8cd2014-09-01 19:23:22 -0500286 self.network_client.update_port(
287 self.vip.port_id, security_groups=[self.security_group.id])
Darragh O'Reilly44fc88a2014-04-26 15:42:47 +0000288
Elena Ezhovaa5105e62013-11-26 20:46:52 +0400289 def _check_load_balancing(self):
290 """
armando-migliaccio29bd43b2014-09-10 11:43:15 -0700291 1. Send NUM requests on the floating ip associated with the VIP
292 2. Check that the requests are shared between the two servers
Elena Ezhovaa5105e62013-11-26 20:46:52 +0400293 """
294
Elena Ezhova91531102014-02-07 17:25:58 +0400295 self._check_connection(self.vip_ip)
armando-migliaccio29bd43b2014-09-10 11:43:15 -0700296 self._send_requests(self.vip_ip, ["server1", "server2"])
Elena Ezhova4a27b462014-04-09 15:25:46 +0400297
armando-migliaccio29bd43b2014-09-10 11:43:15 -0700298 def _send_requests(self, vip_ip, servers):
299 counters = dict.fromkeys(servers, 0)
300 for i in range(self.num):
Sergey Shnaidmancb85dcd2014-11-26 20:01:11 +0300301 try:
302 server = urllib2.urlopen("http://{0}/".format(vip_ip)).read()
303 counters[server] += 1
304 # HTTP exception means fail of server, so don't increase counter
305 # of success and continue connection tries
306 except urllib2.HTTPError:
307 continue
armando-migliaccio29bd43b2014-09-10 11:43:15 -0700308 # Assert that each member of the pool gets balanced at least once
309 for member, counter in counters.iteritems():
310 self.assertGreater(counter, 0, 'Member %s never balanced' % member)
Elena Ezhovaa5105e62013-11-26 20:46:52 +0400311
Elena Ezhovaa5105e62013-11-26 20:46:52 +0400312 @test.services('compute', 'network')
313 def test_load_balancer_basic(self):
Elena Ezhova4a27b462014-04-09 15:25:46 +0400314 self._create_server('server1')
315 self._start_servers()
Elena Ezhovaa5105e62013-11-26 20:46:52 +0400316 self._create_load_balancer()
317 self._check_load_balancing()