vmware-nsx-tempest-plugin/vmware_nsx_tempest_plugin/tests/nsxv/scenario/test_lbaas_round_robin_ops.py

473 lines
20 KiB
Python

# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import re
import tempfile
import time
from tempest.common import utils
from tempest.common.utils.linux import remote_client
from tempest.common import waiters
from tempest import config
from tempest.lib.common.utils import data_utils
from tempest.lib.common.utils import test_utils
from tempest.lib import decorators
import urllib3
from vmware_nsx_tempest_plugin.services.lbaas import health_monitors_client
from vmware_nsx_tempest_plugin.services.lbaas import listeners_client
from vmware_nsx_tempest_plugin.services.lbaas import load_balancers_client
from vmware_nsx_tempest_plugin.services.lbaas import members_client
from vmware_nsx_tempest_plugin.services.lbaas import pools_client
from vmware_nsx_tempest_plugin.services import nsxv_client
from vmware_nsx_tempest_plugin.tests.nsxv.scenario import (
manager_topo_deployment as dmgr)
CONF = config.CONF
LOG = dmgr.manager.log.getLogger(__name__)
class LBaasRoundRobinBaseTest(dmgr.TopoDeployScenarioManager):
"""Base class to support LBaaS ROUND-ROBIN test.
It provides the methods to create loadbalancer network, and
start web servers.
Default lb_algorithm is ROUND_ROBIND.
"""
tenant_router_attrs = {'router_type': 'exclusive'}
@classmethod
def skip_checks(cls):
super(LBaasRoundRobinBaseTest, cls).skip_checks()
cfg = CONF.network
if not utils.is_extension_enabled('lbaasv2', 'network'):
msg = 'lbaasv2 extension is not enabled.'
raise cls.skipException(msg)
if not (cfg.project_networks_reachable or cfg.public_network_id):
msg = ('Either project_networks_reachable must be "true", or '
'public_network_id must be defined.')
raise cls.skipException(msg)
@classmethod
def resource_setup(cls):
super(LBaasRoundRobinBaseTest, cls).resource_setup()
cls.create_lbaas_clients(cls.manager)
if CONF.network.backend == "nsxv":
manager_ip = re.search(r"(\d{1,3}\.){3}\d{1,3}",
CONF.nsxv.manager_uri).group(0)
cls.vsm = nsxv_client.VSMClient(
manager_ip, CONF.nsxv.user, CONF.nsxv.password)
@classmethod
def create_lbaas_clients(cls, mgr):
cls.load_balancers_client = load_balancers_client.get_client(mgr)
cls.listeners_client = listeners_client.get_client(mgr)
cls.pools_client = pools_client.get_client(mgr)
cls.members_client = members_client.get_client(mgr)
cls.health_monitors_client = health_monitors_client.get_client(mgr)
@classmethod
def setup_credentials(cls):
# Ask framework to not create network resources for these tests.
cls.set_network_resources()
super(LBaasRoundRobinBaseTest, cls).setup_credentials()
def setUp(self):
super(LBaasRoundRobinBaseTest, self).setUp()
CONF.validation.ssh_shell_prologue = ''
self.namestart = 'lbaas-ops'
self.poke_counters = 10
self.protocol_type = 'HTTP'
self.protocol_port = 80
self.lb_algorithm = "ROUND_ROBIN"
self.hm_delay = 4
self.hm_max_retries = 3
self.hm_timeout = 10
self.hm_type = 'PING'
self.server_names = []
self.loadbalancer = None
self.vip_fip = None
self.web_service_start_delay = 2.5
self.time_in_adding_members = 120
def tearDown(self):
if self.vip_fip:
LOG.debug("tearDown lbass vip fip")
self.disassociate_floatingip(self.vip_fip, and_delete=True)
if self.loadbalancer:
LOG.debug("tearDown lbass")
lb_id = self.loadbalancer['id']
self.delete_loadbalancer_resources(lb_id)
# make sure servers terminated before teardown network resources
LOG.debug("tearDown lbaas servers")
server_id_list = []
for servid in ['server1', 'server2', 'server7', 'server8']:
server = getattr(self, servid, None)
if server:
if '_floating_ip' in server:
fip = server['_floating_ip']
self.disassociate_floatingip(fip, and_delete=True)
self.manager.servers_client.delete_server(server['id'])
server_id_list.append(server['id'])
for server_id in server_id_list:
waiters.wait_for_server_termination(
self.manager.servers_client, server_id)
# delete lbaas network before handing back to framework
super(LBaasRoundRobinBaseTest, self).tearDown()
LOG.debug("tearDown lbaas exiting...")
def delete_loadbalancer_resources(self, lb_id):
lb_client = self.load_balancers_client
statuses = lb_client.show_load_balancer_status_tree(lb_id)
statuses = statuses.get('statuses', statuses)
lb = statuses.get('loadbalancer')
for listener in lb.get('listeners', []):
for policy in listener.get('l7policies'):
test_utils.call_and_ignore_notfound_exc(
self.l7policies_client.delete_policy,
policy.get('id'))
for pool in listener.get('pools'):
self.delete_lb_pool_resources(lb_id, pool)
test_utils.call_and_ignore_notfound_exc(
self.listeners_client.delete_listener,
listener.get('id'))
self.wait_for_load_balancer_status(lb_id)
# delete pools not attached to listener, but loadbalancer
for pool in lb.get('pools', []):
self.delete_lb_pool_resources(lb_id, pool)
test_utils.call_and_ignore_notfound_exc(
lb_client.delete_load_balancer, lb_id)
self.load_balancers_client.wait_for_load_balancer_status(
lb_id, is_delete_op=True)
lbs = lb_client.list_load_balancers()['loadbalancers']
self.assertEqual(0, len(lbs))
def delete_lb_pool_resources(self, lb_id, pool):
pool_id = pool.get('id')
hm = pool.get('healthmonitor')
if hm:
test_utils.call_and_ignore_notfound_exc(
self.health_monitors_client.delete_health_monitor,
pool.get('healthmonitor').get('id'))
self.wait_for_load_balancer_status(lb_id)
test_utils.call_and_ignore_notfound_exc(
self.pools_client.delete_pool, pool.get('id'))
self.wait_for_load_balancer_status(lb_id)
for member in pool.get('members', []):
test_utils.call_and_ignore_notfound_exc(
self.members_client.delete_member,
pool_id, member.get('id'))
self.wait_for_load_balancer_status(lb_id)
def wait_for_load_balancer_status(self, lb_id):
# Wait for load balancer become ONLINE and ACTIVE
self.load_balancers_client.wait_for_load_balancer_status(lb_id)
def create_lbaas_networks(self):
"""Create network, subnet and router for lbaasv2 environment."""
self.network, self.subnet, self.router = self.setup_project_network(
self.public_network_id, client_mgr=self.manager,
namestart=self.namestart)
self._create_security_group_for_test()
security_groups = [{'name': self.security_group['id']}]
self.keypair = self.create_keypair()
key_name = self.keypair['name']
network_name = self.network['name']
self.server1 = self.create_server_on_network(
self.network, name=(network_name + "-1"),
security_groups=security_groups,
key_name=key_name, wait_on_boot=False,
servers_client=self.manager.servers_client)
self.server2 = self.create_server_on_network(
self.network, name=(network_name + "-2"),
security_groups=security_groups,
key_name=key_name,
servers_client=self.manager.servers_client)
self.rr_server_list = [self.server1, self.server2]
self.wait_for_servers_become_active(self.rr_server_list)
def wait_for_servers_become_active(self, server_list):
for serv in server_list:
waiters.wait_for_server_status(
self.manager.servers_client,
serv['id'], 'ACTIVE')
def _create_security_group_for_test(self):
self.security_group = self._create_security_group()
self._create_security_group_rules_for_port(self.protocol_port)
def _create_security_group_rules_for_port(self, port):
rule = {
'direction': 'ingress',
'protocol': 'tcp',
'port_range_min': port,
'port_range_max': port,
}
self._create_security_group_rule(
secgroup=self.security_group,
**rule)
def start_netcat_session(self, protocol_port=1212):
private_key = self.keypair['private_key']
ssh_client1 = self.get_remote_client(self.server1_fip,
private_key=private_key)
ssh_client2 = self.get_remote_client(self.server2_fip,
private_key=private_key)
vip = self.loadbalancer['vip_address']
cmd = ('nc -l -p %s &' % (protocol_port))
ssh_client1.exec_command(cmd)
cmd = ('nc %s %s &' % (vip, protocol_port))
ssh_client2.exec_command(cmd)
def start_web_servers(self, server_list=None):
"""Start predefined servers:
1. SSH to the instance
2. Start http backends listening on port 80
"""
server_list = server_list or self.rr_server_list
for server in server_list:
fip = self.create_floatingip_for_server(
server, self.public_network_id,
client_mgr=self.manager)
server['_floating_ip'] = fip
server_fip = fip['floating_ip_address']
self.start_web_server(server, server_fip, server['name'])
# need to wait for web server to be able to response
time.sleep(self.web_service_start_delay)
for server in server_list:
server_name = server['name'].encode()
fip = server['_floating_ip']
web_fip = fip['floating_ip_address']
response = self.send_request(web_fip)
# by design, each lbaas member server response its server_name
self.assertEqual(response, server_name)
self.server_names.append(server_name.decode())
def start_web_server(self, server, server_fip, server_name):
"""start server's web service which return its server_name."""
private_key = self.keypair['private_key']
username = CONF.validation.image_ssh_user
ssh_client = self.get_remote_client(
server_fip, private_key=private_key)
# Write a backend's response into a file
resp = ('echo -ne "HTTP/1.1 200 OK\r\nContent-Length: %d\r\n'
'Connection: close\r\nContent-Type: text/html; '
'charset=UTF-8\r\n\r\n%s"; cat >/dev/null')
with tempfile.NamedTemporaryFile() as script:
server_name = server_name.encode('utf-8')
script.write(resp.encode('utf-8') % (len(server_name),
server_name))
script.flush()
with tempfile.NamedTemporaryFile() as key:
key.write(private_key.encode('utf-8'))
key.flush()
dmgr.copy_file_to_host(script.name,
"/tmp/script",
server_fip, username, key.name)
# Start netcat
start_server = ('while true; do '
'sudo nc -ll -p %(port)s -e sh /tmp/%(script)s; '
'done > /dev/null &')
cmd = start_server % {'port': self.protocol_port,
'script': 'script'}
ssh_client.exec_command(cmd)
return server_name
def send_request(self, web_ip):
try:
url_path = "http://{0}/".format(web_ip)
# lbaas servers use nc, might be slower to response
http = urllib3.PoolManager(retries=10)
resp = http.request('GET', url_path)
return resp.data.strip()
except Exception:
return None
def create_project_lbaas(self, member_count=2,
protocol_type='HTTP',
protocol_port=80):
vip_subnet_id = self.subnet['id']
lb_name = data_utils.rand_name(self.namestart)
self.loadbalancer = self.load_balancers_client.create_load_balancer(
name=lb_name, vip_subnet_id=vip_subnet_id)['loadbalancer']
lb_id = self.loadbalancer['id']
self.wait_for_load_balancer_status(lb_id)
self.listener = self.listeners_client.create_listener(
loadbalancer_id=lb_id, protocol=protocol_type,
protocol_port=protocol_port, name=lb_name)['listener']
self.wait_for_load_balancer_status(lb_id)
self.pool = self.pools_client .create_pool(
listener_id=self.listener['id'],
lb_algorithm=self.lb_algorithm, protocol=protocol_type,
name=lb_name)['pool']
self.wait_for_load_balancer_status(lb_id)
pool_id = self.pool['id']
self.healthmonitor = (
self.health_monitors_client.create_health_monitor(
pool_id=pool_id, type=self.hm_type,
delay=self.hm_delay, max_retries=self.hm_max_retries,
timeout=self.hm_timeout))
self.wait_for_load_balancer_status(lb_id)
self.members = []
if member_count == 2:
for server in [self.server1, self.server2]:
fip = server['_floating_ip']
fixed_ip_address = fip['fixed_ip_address']
member = self.members_client.create_member(
pool_id, subnet_id=vip_subnet_id,
address=fixed_ip_address,
protocol_port=protocol_port,
protocol=protocol_type)
self.wait_for_load_balancer_status(lb_id)
self.members.append(member)
else:
for server in [self.server1]:
fip = server['_floating_ip']
fixed_ip_address = fip['fixed_ip_address']
member = self.members_client.create_member(
pool_id, subnet_id=vip_subnet_id,
address=fixed_ip_address,
protocol_port=protocol_port)
self.wait_for_load_balancer_status(lb_id)
self.members.append(member)
# Currently the ovs-agent is not enforcing security groups on the
# vip port - see https://bugs.launchpad.net/neutron/+bug/1163569
# However the linuxbridge-agent does, and it is necessary to add a
# security group with a rule that allows tcp port 80 to the vip port.
# NSX-v lbaasv2 OK, but for upstream neutron-lbaas needs this.
self.ports_client.update_port(
self.loadbalancer['vip_port_id'],
security_groups=[self.security_group['id']])
# create lbaas public interface
self.vip_fip = self.create_floatingip_for_server(
self.loadbalancer, self.public_network_id,
port_id=self.loadbalancer['vip_port_id'],
client_mgr=self.manager)
self.vip_ip_address = self.vip_fip['floating_ip_address']
for x in range(1, 8):
time.sleep(2)
resp = self.send_request(self.vip_ip_address)
if resp:
break
LOG.debug('#%d LBaaS-VIP get NO response from its members', x)
return self.vip_ip_address
def do_http_request(self, start_path='', send_counts=None):
statuses = self.load_balancers_client.show_load_balancer_status_tree(
self.loadbalancer['id'])
statuses = statuses.get('statuses', statuses)
self.http_cnt = {}
http = urllib3.PoolManager(retries=10)
send_counts = send_counts or self.poke_counters
send_counts = (send_counts * 2) / 2
url_path = "http://{0}/{1}".format(self.vip_ip_address, start_path)
for x in range(int(send_counts)):
resp = http.request('GET', url_path)
if resp.status == 200:
self.count_response(resp.data.strip())
else:
self.count_response(str(resp.status))
return self.http_cnt
def check_project_lbaas(self):
time.sleep(self.time_in_adding_members)
self.do_http_request(send_counts=self.poke_counters)
# should response from 2 servers
self.assertEqual(2, len(self.http_cnt))
# ROUND_ROUBIN, so equal counts
s0 = self.server_names[0]
s1 = self.server_names[1]
self.assertEqual(self.http_cnt[s0], self.http_cnt[s1])
def count_response(self, response):
response = response.decode()
if response in self.http_cnt:
self.http_cnt[response] += 1
else:
self.http_cnt[response] = 1
def verify_session_edge(self, vip):
remote_ip = self.router['_ext_ip']
ssh_client = remote_client.RemoteClient(remote_ip, 'admin',
'Admin!23Admin')
cmd = 'show flowtable topN 20 '
output = ssh_client.exec_command(cmd)
vip_in_output = vip in output
self.assertTrue(vip_in_output)
class TestLBaasRoundRobinOps(LBaasRoundRobinBaseTest):
"""This test checks basic load balancer V2 ROUND-ROBIN operation.
The following is the scenario outline:
1. Create network with exclusive router, and 2 servers
2. SSH to each instance and start web server
3. Create a load balancer with 1 listener, 1 pool, 1 healthmonitor
and 2 members and with ROUND_ROBIN algorithm.
4. Associate loadbalancer's vip_address with a floating ip
5. Send NUM requests to vip's floating ip and check that they are shared
between the two servers.
"""
@decorators.idempotent_id('077d2a5c-4938-448f-a80f-8e65f5cc49d7')
@utils.services('compute', 'network')
def test_lbaas_round_robin_ops(self):
self.create_lbaas_networks()
self.start_web_servers(self.rr_server_list)
self.create_project_lbaas()
self.check_project_lbaas()
@decorators.idempotent_id('188d2a5c-4938-448f-a80f-8e65f5cc49d7')
@utils.services('compute', 'network')
def test_lbaas_round_robin_ops_create_delete_second_lb(self):
self.create_lbaas_networks()
server_list = self.rr_server_list
fip1 = self.create_floatingip_for_server(
server_list[0], self.public_network_id,
client_mgr=self.manager)
server_list[0]['_floating_ip'] = fip1
self.server1 = server_list[0]
fip2 = self.create_floatingip_for_server(
server_list[1], self.public_network_id,
client_mgr=self.manager)
server_list[1]['_floating_ip'] = fip2
self.server2 = server_list[1]
self.server1_fip = fip1['floating_ip_address']
self.server2_fip = fip2['floating_ip_address']
self.create_project_lbaas(member_count=1, protocol_port=1212,
protocol_type='TCP')
self.start_netcat_session(protocol_port=1212)
vip = self.loadbalancer['vip_address']
self.vsm.enable_ssh_on_edge(self.router['name'], self.router['id'])
self.verify_session_edge(vip)
vip_subnet_id = self.subnet['id']
lb_name2 = data_utils.rand_name(self.namestart)
self.loadbalancer1 = self.load_balancers_client.create_load_balancer(
name=lb_name2, vip_subnet_id=vip_subnet_id)['loadbalancer']
lb2_id = self.loadbalancer1['id']
self.load_balancers_client.delete_load_balancer(lb2_id)
self.verify_session_edge(vip)