blob: 5e83ff9c9b00f00b4329aff2c4c39f882c876a5d [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
21from tempest.api.network import common as net_common
Elena Ezhova6e73f462014-04-18 17:38:13 +040022from tempest.common import commands
Elena Ezhovaa5105e62013-11-26 20:46:52 +040023from tempest import config
24from tempest import exceptions
25from tempest.scenario import manager
26from tempest import test
27
28config = config.CONF
29
30
Miguel Lavalle02ba8cd2014-09-01 19:23:22 -050031class TestLoadBalancerBasic(manager.NeutronScenarioTest):
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
41 4. Send 10 requests to the floating ip and check that they are shared
42 between the two servers and that both of them get equal portions
43 of the requests
44 """
45
46 @classmethod
47 def check_preconditions(cls):
48 super(TestLoadBalancerBasic, cls).check_preconditions()
49 cfg = config.network
50 if not test.is_extension_enabled('lbaas', 'network'):
51 msg = 'LBaaS Extension is not enabled'
52 cls.enabled = False
53 raise cls.skipException(msg)
54 if not (cfg.tenant_networks_reachable or cfg.public_network_id):
55 msg = ('Either tenant_networks_reachable must be "true", or '
56 'public_network_id must be defined.')
57 cls.enabled = False
58 raise cls.skipException(msg)
59
60 @classmethod
61 def setUpClass(cls):
62 super(TestLoadBalancerBasic, cls).setUpClass()
63 cls.check_preconditions()
Elena Ezhovaa5105e62013-11-26 20:46:52 +040064 cls.servers_keypairs = {}
Elena Ezhovaa5105e62013-11-26 20:46:52 +040065 cls.members = []
Elena Ezhovaa5105e62013-11-26 20:46:52 +040066 cls.floating_ips = {}
Elena Ezhova4a27b462014-04-09 15:25:46 +040067 cls.server_ips = {}
Elena Ezhovaa5105e62013-11-26 20:46:52 +040068 cls.port1 = 80
69 cls.port2 = 88
70
Elena Ezhova4a27b462014-04-09 15:25:46 +040071 def setUp(self):
72 super(TestLoadBalancerBasic, self).setUp()
73 self.server_ips = {}
Darragh O'Reilly7c8176e2014-04-26 16:03:23 +000074 self.server_fixed_ips = {}
Miguel Lavalle02ba8cd2014-09-01 19:23:22 -050075 self._create_security_group_for_test()
Adam Gandelmanbbf5c472014-08-12 16:46:12 -070076 self._set_net_and_subnet()
77
78 def _set_net_and_subnet(self):
79 """
80 Query and set appropriate network and subnet attributes to be used
81 for the test. Existing tenant networks are used if they are found.
82 The configured private network and associated subnet is used as a
83 fallback in absence of tenant networking.
84 """
85 try:
86 tenant_net = self._list_networks(tenant_id=self.tenant_id)[0]
87 except IndexError:
88 tenant_net = None
89
90 if tenant_net:
91 tenant_subnet = self._list_subnets(tenant_id=self.tenant_id)[0]
92 self.subnet = net_common.DeletableSubnet(
93 client=self.network_client,
94 **tenant_subnet)
95 self.network = tenant_net
96 else:
97 self.network = self._get_network_by_name(
98 config.compute.fixed_network_name)
99 # TODO(adam_g): We are assuming that the first subnet associated
100 # with the fixed network is the one we want. In the future, we
101 # should instead pull a subnet id from config, which is set by
102 # devstack/admin/etc.
103 subnet = self._list_subnets(network_id=self.network['id'])[0]
104 self.subnet = net_common.AttributeDict(subnet)
Elena Ezhova4a27b462014-04-09 15:25:46 +0400105
Miguel Lavalle02ba8cd2014-09-01 19:23:22 -0500106 def _create_security_group_for_test(self):
107 self.security_group = self._create_security_group(
Elena Ezhova4a27b462014-04-09 15:25:46 +0400108 tenant_id=self.tenant_id)
Eugene Nikanorov55d13142014-03-24 15:39:21 +0400109 self._create_security_group_rules_for_port(self.port1)
110 self._create_security_group_rules_for_port(self.port2)
111
112 def _create_security_group_rules_for_port(self, port):
113 rule = {
114 'direction': 'ingress',
115 'protocol': 'tcp',
116 'port_range_min': port,
117 'port_range_max': port,
118 }
119 self._create_security_group_rule(
Elena Ezhova4a27b462014-04-09 15:25:46 +0400120 secgroup=self.security_group,
Eugene Nikanorov55d13142014-03-24 15:39:21 +0400121 tenant_id=self.tenant_id,
122 **rule)
Elena Ezhovaa5105e62013-11-26 20:46:52 +0400123
Elena Ezhova4a27b462014-04-09 15:25:46 +0400124 def _create_server(self, name):
Miguel Lavalle02ba8cd2014-09-01 19:23:22 -0500125 keypair = self.create_keypair()
126 security_groups = [self.security_group]
Yair Fried2d2f3fe2014-02-24 16:19:20 +0200127 create_kwargs = {
128 'nics': [
Adam Gandelmanbbf5c472014-08-12 16:46:12 -0700129 {'net-id': self.network['id']},
Yair Fried2d2f3fe2014-02-24 16:19:20 +0200130 ],
Miguel Lavalle02ba8cd2014-09-01 19:23:22 -0500131 'key_name': keypair['name'],
Yair Fried2d2f3fe2014-02-24 16:19:20 +0200132 'security_groups': security_groups,
133 }
Adam Gandelmanbbf5c472014-08-12 16:46:12 -0700134 net_name = self.network['name']
Miguel Lavalle02ba8cd2014-09-01 19:23:22 -0500135 server = self.create_server(name=name, create_kwargs=create_kwargs)
136 self.servers_keypairs[server['id']] = keypair
Elena Ezhova91531102014-02-07 17:25:58 +0400137 if (config.network.public_network_id and not
138 config.network.tenant_networks_reachable):
139 public_network_id = config.network.public_network_id
140 floating_ip = self._create_floating_ip(
141 server, public_network_id)
142 self.floating_ips[floating_ip] = server
Miguel Lavalle02ba8cd2014-09-01 19:23:22 -0500143 self.server_ips[server['id']] = floating_ip.floating_ip_address
Elena Ezhova91531102014-02-07 17:25:58 +0400144 else:
Miguel Lavalle02ba8cd2014-09-01 19:23:22 -0500145 self.server_ips[server['id']] =\
146 server['addresses'][net_name][0]['addr']
147 self.server_fixed_ips[server['id']] =\
148 server['addresses'][net_name][0]['addr']
Elena Ezhovaa5105e62013-11-26 20:46:52 +0400149 self.assertTrue(self.servers_keypairs)
Elena Ezhova91531102014-02-07 17:25:58 +0400150 return server
Elena Ezhovaa5105e62013-11-26 20:46:52 +0400151
Elena Ezhova4a27b462014-04-09 15:25:46 +0400152 def _create_servers(self):
153 for count in range(2):
154 self._create_server(name=("server%s" % (count + 1)))
155 self.assertEqual(len(self.servers_keypairs), 2)
156
157 def _start_servers(self):
Elena Ezhovaa5105e62013-11-26 20:46:52 +0400158 """
Elena Ezhova4a27b462014-04-09 15:25:46 +0400159 Start two backends
160
Elena Ezhovaa5105e62013-11-26 20:46:52 +0400161 1. SSH to the instance
Elena Ezhova91531102014-02-07 17:25:58 +0400162 2. Start two http backends listening on ports 80 and 88 respectively
Elena Ezhovaa5105e62013-11-26 20:46:52 +0400163 """
Elena Ezhova4a27b462014-04-09 15:25:46 +0400164 for server_id, ip in self.server_ips.iteritems():
Miguel Lavalle02ba8cd2014-09-01 19:23:22 -0500165 private_key = self.servers_keypairs[server_id]['private_key']
166 server_name = self.servers_client.get_server(server_id)[1]['name']
Elena Ezhova6e73f462014-04-18 17:38:13 +0400167 username = config.scenario.ssh_user
Elena Ezhova4a27b462014-04-09 15:25:46 +0400168 ssh_client = self.get_remote_client(
169 server_or_ip=ip,
170 private_key=private_key)
Elena Ezhovaa5105e62013-11-26 20:46:52 +0400171
Robert Mizielskie1d88992014-07-15 15:28:09 +0200172 # Write a backend's response into a file
Elena Ezhova0c8e3292014-06-05 12:15:39 +0400173 resp = """echo -ne "HTTP/1.1 200 OK\r\nContent-Length: 7\r\n""" \
174 """Connection: close\r\nContent-Type: text/html; """ \
175 """charset=UTF-8\r\n\r\n%s"; cat >/dev/null"""
176
Elena Ezhova6e73f462014-04-18 17:38:13 +0400177 with tempfile.NamedTemporaryFile() as script:
178 script.write(resp % server_name)
179 script.flush()
180 with tempfile.NamedTemporaryFile() as key:
181 key.write(private_key)
182 key.flush()
183 commands.copy_file_to_host(script.name,
Elena Ezhova0c8e3292014-06-05 12:15:39 +0400184 "/tmp/script1",
Elena Ezhova6e73f462014-04-18 17:38:13 +0400185 ip,
186 username, key.name)
Elena Ezhova0c8e3292014-06-05 12:15:39 +0400187
Elena Ezhova6e73f462014-04-18 17:38:13 +0400188 # Start netcat
Elena Ezhova0c8e3292014-06-05 12:15:39 +0400189 start_server = """sudo nc -ll -p %(port)s -e sh """ \
190 """/tmp/%(script)s &"""
Elena Ezhova6e73f462014-04-18 17:38:13 +0400191 cmd = start_server % {'port': self.port1,
192 'script': 'script1'}
Elena Ezhova4a27b462014-04-09 15:25:46 +0400193 ssh_client.exec_command(cmd)
Elena Ezhova0c8e3292014-06-05 12:15:39 +0400194
Elena Ezhova4a27b462014-04-09 15:25:46 +0400195 if len(self.server_ips) == 1:
Elena Ezhova6e73f462014-04-18 17:38:13 +0400196 with tempfile.NamedTemporaryFile() as script:
197 script.write(resp % 'server2')
198 script.flush()
199 with tempfile.NamedTemporaryFile() as key:
200 key.write(private_key)
201 key.flush()
202 commands.copy_file_to_host(script.name,
Elena Ezhova0c8e3292014-06-05 12:15:39 +0400203 "/tmp/script2", ip,
Elena Ezhova6e73f462014-04-18 17:38:13 +0400204 username, key.name)
205 cmd = start_server % {'port': self.port2,
206 'script': 'script2'}
Elena Ezhova4a27b462014-04-09 15:25:46 +0400207 ssh_client.exec_command(cmd)
Elena Ezhova4a27b462014-04-09 15:25:46 +0400208
209 def _check_connection(self, check_ip, port=80):
210 def try_connect(ip, port):
Elena Ezhovaa5105e62013-11-26 20:46:52 +0400211 try:
Elena Ezhova6e73f462014-04-18 17:38:13 +0400212 resp = urllib2.urlopen("http://{0}:{1}/".format(ip, port))
Elena Ezhova4a27b462014-04-09 15:25:46 +0400213 if resp.getcode() == 200:
214 return True
215 return False
Elena Ezhovaa5105e62013-11-26 20:46:52 +0400216 except IOError:
217 return False
218 timeout = config.compute.ping_timeout
Elena Ezhova4a27b462014-04-09 15:25:46 +0400219 start = time.time()
220 while not try_connect(check_ip, port):
221 if (time.time() - start) > timeout:
Elena Ezhovaa5105e62013-11-26 20:46:52 +0400222 message = "Timed out trying to connect to %s" % check_ip
223 raise exceptions.TimeoutException(message)
224
225 def _create_pool(self):
226 """Create a pool with ROUND_ROBIN algorithm."""
Yair Fried2d2f3fe2014-02-24 16:19:20 +0200227 self.pool = super(TestLoadBalancerBasic, self)._create_pool(
Elena Ezhova4a27b462014-04-09 15:25:46 +0400228 lb_method='ROUND_ROBIN',
229 protocol='HTTP',
230 subnet_id=self.subnet.id)
Yair Fried2d2f3fe2014-02-24 16:19:20 +0200231 self.assertTrue(self.pool)
Elena Ezhovaa5105e62013-11-26 20:46:52 +0400232
Elena Ezhova4a27b462014-04-09 15:25:46 +0400233 def _create_members(self):
Elena Ezhovaa5105e62013-11-26 20:46:52 +0400234 """
235 Create two members.
236
237 In case there is only one server, create both members with the same ip
238 but with different ports to listen on.
239 """
Elena Ezhova4a27b462014-04-09 15:25:46 +0400240
Darragh O'Reilly7c8176e2014-04-26 16:03:23 +0000241 for server_id, ip in self.server_fixed_ips.iteritems():
242 if len(self.server_fixed_ips) == 1:
Elena Ezhova4a27b462014-04-09 15:25:46 +0400243 member1 = self._create_member(address=ip,
244 protocol_port=self.port1,
245 pool_id=self.pool.id)
Elena Ezhova4a27b462014-04-09 15:25:46 +0400246 member2 = self._create_member(address=ip,
247 protocol_port=self.port2,
248 pool_id=self.pool.id)
Elena Ezhova4a27b462014-04-09 15:25:46 +0400249 self.members.extend([member1, member2])
250 else:
251 member = self._create_member(address=ip,
252 protocol_port=self.port1,
253 pool_id=self.pool.id)
Elena Ezhova4a27b462014-04-09 15:25:46 +0400254 self.members.append(member)
Elena Ezhovaa5105e62013-11-26 20:46:52 +0400255 self.assertTrue(self.members)
256
257 def _assign_floating_ip_to_vip(self, vip):
258 public_network_id = config.network.public_network_id
Yair Fried2d2f3fe2014-02-24 16:19:20 +0200259 port_id = vip.port_id
Yair Frieda2e3b2c2014-02-17 10:56:10 +0200260 floating_ip = self._create_floating_ip(vip, public_network_id,
261 port_id=port_id)
Yair Fried2d2f3fe2014-02-24 16:19:20 +0200262 self.floating_ips.setdefault(vip.id, [])
263 self.floating_ips[vip.id].append(floating_ip)
Elena Ezhovaa5105e62013-11-26 20:46:52 +0400264
265 def _create_load_balancer(self):
266 self._create_pool()
Elena Ezhova4a27b462014-04-09 15:25:46 +0400267 self._create_members()
268 self.vip = self._create_vip(protocol='HTTP',
269 protocol_port=80,
270 subnet_id=self.subnet.id,
271 pool_id=self.pool.id)
Miguel Lavalle02ba8cd2014-09-01 19:23:22 -0500272 self.vip.wait_for_status('ACTIVE')
Elena Ezhova91531102014-02-07 17:25:58 +0400273 if (config.network.public_network_id and not
274 config.network.tenant_networks_reachable):
275 self._assign_floating_ip_to_vip(self.vip)
276 self.vip_ip = self.floating_ips[
277 self.vip.id][0]['floating_ip_address']
278 else:
279 self.vip_ip = self.vip.address
Elena Ezhovaa5105e62013-11-26 20:46:52 +0400280
Darragh O'Reilly44fc88a2014-04-26 15:42:47 +0000281 # Currently the ovs-agent is not enforcing security groups on the
282 # vip port - see https://bugs.launchpad.net/neutron/+bug/1163569
283 # However the linuxbridge-agent does, and it is necessary to add a
284 # security group with a rule that allows tcp port 80 to the vip port.
Miguel Lavalle02ba8cd2014-09-01 19:23:22 -0500285 self.network_client.update_port(
286 self.vip.port_id, security_groups=[self.security_group.id])
Darragh O'Reilly44fc88a2014-04-26 15:42:47 +0000287
Elena Ezhovaa5105e62013-11-26 20:46:52 +0400288 def _check_load_balancing(self):
289 """
Elena Ezhova6e73f462014-04-18 17:38:13 +0400290 1. Send 10 requests on the floating ip associated with the VIP
Elena Ezhovaa5105e62013-11-26 20:46:52 +0400291 2. Check that the requests are shared between
292 the two servers and that both of them get equal portions
293 of the requests
294 """
295
Elena Ezhova91531102014-02-07 17:25:58 +0400296 self._check_connection(self.vip_ip)
Elena Ezhova6e73f462014-04-18 17:38:13 +0400297 self._send_requests(self.vip_ip, set(["server1", "server2"]))
Elena Ezhova4a27b462014-04-09 15:25:46 +0400298
Elena Ezhova6e73f462014-04-18 17:38:13 +0400299 def _send_requests(self, vip_ip, expected, num_req=10):
300 count = 0
301 while count < num_req:
Elena Ezhova0c8e3292014-06-05 12:15:39 +0400302 resp = []
303 for i in range(len(self.members)):
304 resp.append(
305 urllib2.urlopen(
306 "http://{0}/".format(vip_ip)).read())
307 count += 1
308 self.assertEqual(expected,
309 set(resp))
Elena Ezhovaa5105e62013-11-26 20:46:52 +0400310
Elena Ezhovaa5105e62013-11-26 20:46:52 +0400311 @test.services('compute', 'network')
312 def test_load_balancer_basic(self):
Elena Ezhova4a27b462014-04-09 15:25:46 +0400313 self._create_server('server1')
314 self._start_servers()
Elena Ezhovaa5105e62013-11-26 20:46:52 +0400315 self._create_load_balancer()
316 self._check_load_balancing()