Merge "NSX|V3: Simplify LBaaS implementation"

This commit is contained in:
Zuul 2019-03-07 17:46:24 +00:00 committed by Gerrit Code Review
commit 4c5bed6108
7 changed files with 170 additions and 265 deletions

View File

@ -0,0 +1,7 @@
---
prelude: >
NSXv3 plugin will mark unused loadbalancers in ERROR state.
features:
- |
Upon upgrade to Stein, unused LBaaS-v2 loadbalancers, which have no members
will be marked in ERROR state, and cannot be used.

View File

@ -1 +1 @@
fc6308289aca
99bfcb6003c6

View File

@ -0,0 +1,38 @@
# Copyright 2019 VMware, Inc.
#
# 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.
"""lbaas_error_no_member
Revision ID: 99bfcb6003c6
Revises: fc6308289aca
Create Date: 2019-03-07 11:27:00.000000
"""
from alembic import op
from neutron.db import migration
# revision identifiers, used by Alembic.
revision = '99bfcb6003c6'
down_revision = 'fc6308289aca'
def upgrade():
if (migration.schema_has_table('nsxv3_lbaas_loadbalancers') and
migration.schema_has_table('lbaas_loadbalancers')):
# Mark as ERROR loadbalancers without nsx mapping
op.execute("UPDATE lbaas_loadbalancers "
"SET provisioning_status='ERROR' "
"where id not in (select loadbalancer_id from "
"nsxv3_lbaas_loadbalancers)")

View File

@ -149,16 +149,20 @@ class EdgeListenerManagerFromDict(base_mgr.Nsxv3LoadbalancerBaseManager):
# server to the lb service
binding = nsx_db.get_nsx_lbaas_loadbalancer_binding(
context.session, lb_id)
if binding:
lb_service_id = binding['lb_service_id']
try:
service_client.add_virtual_server(lb_service_id,
virtual_server['id'])
except nsxlib_exc.ManagerError:
completor(success=False)
msg = _('Failed to add virtual server to lb service '
'at NSX backend')
raise n_exc.BadRequest(resource='lbaas-listener', msg=msg)
if not binding:
completor(success=False)
msg = _('Failed to get loadbalancer %s binding') % lb_id
raise n_exc.BadRequest(resource='lbaas-listener', msg=msg)
lb_service_id = binding['lb_service_id']
try:
service_client.add_virtual_server(lb_service_id,
virtual_server['id'])
except nsxlib_exc.ManagerError:
completor(success=False)
msg = _('Failed to add virtual server to lb service '
'at NSX backend')
raise n_exc.BadRequest(resource='lbaas-listener', msg=msg)
nsx_db.add_nsx_lbaas_listener_binding(
context.session, lb_id, listener['id'], app_profile_id,
@ -219,19 +223,24 @@ class EdgeListenerManagerFromDict(base_mgr.Nsxv3LoadbalancerBaseManager):
app_profile_id = binding['app_profile_id']
lb_binding = nsx_db.get_nsx_lbaas_loadbalancer_binding(
context.session, lb_id)
if lb_binding:
try:
lbs_id = lb_binding.get('lb_service_id')
lb_service = service_client.get(lbs_id)
vs_list = lb_service.get('virtual_server_ids')
if vs_list and vs_id in vs_list:
service_client.remove_virtual_server(lbs_id, vs_id)
except nsxlib_exc.ManagerError:
completor(success=False)
msg = (_('Failed to remove virtual server: %(listener)s '
'from lb service %(lbs)s') %
{'listener': listener['id'], 'lbs': lbs_id})
raise n_exc.BadRequest(resource='lbaas-listener', msg=msg)
if not lb_binding:
completor(success=False)
msg = (_('Failed to delete virtual server: %(listener)s: '
'loadbalancer %(lb)s mapping was not found') %
{'listener': listener['id'], 'lb': lb_id})
raise n_exc.BadRequest(resource='lbaas-listener', msg=msg)
try:
lbs_id = lb_binding.get('lb_service_id')
lb_service = service_client.get(lbs_id)
vs_list = lb_service.get('virtual_server_ids')
if vs_list and vs_id in vs_list:
service_client.remove_virtual_server(lbs_id, vs_id)
except nsxlib_exc.ManagerError:
completor(success=False)
msg = (_('Failed to remove virtual server: %(listener)s '
'from lb service %(lbs)s') %
{'listener': listener['id'], 'lbs': lbs_id})
raise n_exc.BadRequest(resource='lbaas-listener', msg=msg)
try:
if listener.get('default_pool_id'):
vs_client.update(vs_id, pool_id='')

View File

@ -20,6 +20,7 @@ from oslo_log import log as logging
from oslo_utils import excutils
from vmware_nsx._i18n import _
from vmware_nsx.common import exceptions as nsx_exc
from vmware_nsx.db import db as nsx_db
from vmware_nsx.services.lbaas import base_mgr
from vmware_nsx.services.lbaas import lb_const
@ -34,10 +35,8 @@ class EdgeLoadBalancerManagerFromDict(base_mgr.Nsxv3LoadbalancerBaseManager):
@log_helpers.log_method_call
def create(self, context, lb, completor):
if lb_utils.validate_lb_subnet(context, self.core_plugin,
lb['vip_subnet_id']):
completor(success=True)
else:
if not lb_utils.validate_lb_subnet(context, self.core_plugin,
lb['vip_subnet_id']):
completor(success=False)
msg = (_('Cannot create lb on subnet %(sub)s for '
'loadbalancer %(lb)s. The subnet needs to connect a '
@ -45,6 +44,80 @@ class EdgeLoadBalancerManagerFromDict(base_mgr.Nsxv3LoadbalancerBaseManager):
{'sub': lb['vip_subnet_id'], 'lb': lb['id']})
raise n_exc.BadRequest(resource='lbaas-subnet', msg=msg)
router_id = lb_utils.get_router_from_network(
context, self.core_plugin, lb['vip_subnet_id'])
nsx_router_id = nsx_db.get_nsx_router_id(context.session,
router_id)
service_client = self.core_plugin.nsxlib.load_balancer.service
lb_service = service_client.get_router_lb_service(nsx_router_id)
if not lb_service:
lb_size = lb_utils.get_lb_flavor_size(
self.flavor_plugin, context, lb.get('flavor_id'))
# Make sure the NSX service router exists
if not self.core_plugin.service_router_has_services(
context, router_id):
self.core_plugin.create_service_router(context, router_id)
lb_service = self._create_lb_service(
context, service_client, lb['tenant_id'],
router_id, nsx_router_id, lb['id'], lb_size)
if not lb_service:
completor(success=False)
msg = (_('Failed to create lb service for loadbalancer '
'%s') % lb['id'])
raise nsx_exc.NsxPluginException(err_msg=msg)
self._add_loadbalancer_binding(
context, lb['id'], lb_service['id'],
nsx_router_id, lb['vip_address'])
completor(success=True)
@log_helpers.log_method_call
def _create_lb_service(self, context, service_client, tenant_id,
router_id, nsx_router_id, lb_id, lb_size):
router = self.core_plugin.get_router(context, router_id)
if not router.get('external_gateway_info'):
msg = (_('Tenant router %(router)s does not connect to '
'external gateway') % {'router': router['id']})
raise n_exc.BadRequest(resource='lbaas-lbservice-create',
msg=msg)
lb_name = utils.get_name_and_uuid(router['name'] or 'router',
router_id)
tags = lb_utils.get_tags(self.core_plugin, router_id,
lb_const.LR_ROUTER_TYPE,
tenant_id, context.project_name)
attachment = {'target_id': nsx_router_id,
'target_type': 'LogicalRouter'}
try:
lb_service = service_client.create(display_name=lb_name,
tags=tags,
attachment=attachment,
size=lb_size)
except nsxlib_exc.ManagerError as e:
LOG.error("Failed to create LB service: %s", e)
return
# Add rule to advertise external vips
lb_utils.update_router_lb_vip_advertisement(
context, self.core_plugin, router, nsx_router_id)
return lb_service
@log_helpers.log_method_call
def _add_loadbalancer_binding(self, context, lb_id, lbs_id,
nsx_router_id, vip_address):
# First check if there is already binding for the lb.
# If there is no binding for the lb, add the db binding.
binding = nsx_db.get_nsx_lbaas_loadbalancer_binding(
context.session, lb_id)
if not binding:
nsx_db.add_nsx_lbaas_loadbalancer_binding(
context.session, lb_id, lbs_id,
nsx_router_id, vip_address)
else:
LOG.debug("LB binding has already been added, and no need to add "
"here.")
@log_helpers.log_method_call
def update(self, context, old_lb, new_lb, completor):
vs_client = self.core_plugin.nsxlib.load_balancer.virtual_server
@ -189,7 +262,8 @@ class EdgeLoadBalancerManagerFromDict(base_mgr.Nsxv3LoadbalancerBaseManager):
lb_binding = nsx_db.get_nsx_lbaas_loadbalancer_binding(
context.session, id)
if not lb_binding:
# No service yet
LOG.warning("Failed to get loadbalancer %s operating status. "
"Mapping was not found", id)
return {}
lb_service_id = lb_binding['lb_service_id']

View File

@ -23,10 +23,8 @@ from vmware_nsx.common import exceptions as nsx_exc
from vmware_nsx.common import locking
from vmware_nsx.db import db as nsx_db
from vmware_nsx.services.lbaas import base_mgr
from vmware_nsx.services.lbaas import lb_const
from vmware_nsx.services.lbaas.nsx_v3.implementation import lb_utils
from vmware_nsxlib.v3 import exceptions as nsxlib_exc
from vmware_nsxlib.v3 import utils
LOG = logging.getLogger(__name__)
@ -45,37 +43,6 @@ class EdgeMemberManagerFromDict(base_mgr.Nsxv3LoadbalancerBaseManager):
'be a floating IP') % {'fip': fip})
raise n_exc.BadRequest(resource='lbaas-vip', msg=msg)
@log_helpers.log_method_call
def _create_lb_service(self, context, service_client, tenant_id,
router_id, nsx_router_id, lb_id, lb_size):
router = self.core_plugin.get_router(context, router_id)
if not router.get('external_gateway_info'):
msg = (_('Tenant router %(router)s does not connect to '
'external gateway') % {'router': router['id']})
raise n_exc.BadRequest(resource='lbaas-lbservice-create',
msg=msg)
lb_name = utils.get_name_and_uuid(router['name'] or 'router',
router_id)
tags = lb_utils.get_tags(self.core_plugin, router_id,
lb_const.LR_ROUTER_TYPE,
tenant_id, context.project_name)
attachment = {'target_id': nsx_router_id,
'target_type': 'LogicalRouter'}
try:
lb_service = service_client.create(display_name=lb_name,
tags=tags,
attachment=attachment,
size=lb_size)
except nsxlib_exc.ManagerError as e:
LOG.error("Failed to create LB service: %s", e)
return
# Add rule to advertise external vips
lb_utils.update_router_lb_vip_advertisement(
context, self.core_plugin, router, nsx_router_id)
return lb_service
@log_helpers.log_method_call
def _get_updated_pool_members(self, context, lb_pool, member):
network = lb_utils.get_network_from_subnet(
@ -91,21 +58,6 @@ class EdgeMemberManagerFromDict(base_mgr.Nsxv3LoadbalancerBaseManager):
m['weight'] = member['weight']
return lb_pool['members']
@log_helpers.log_method_call
def _add_loadbalancer_binding(self, context, lb_id, lbs_id,
nsx_router_id, vip_address):
# First check if there is already binding for the lb.
# If there is no binding for the lb, add the db binding.
binding = nsx_db.get_nsx_lbaas_loadbalancer_binding(
context.session, lb_id)
if not binding:
nsx_db.add_nsx_lbaas_loadbalancer_binding(
context.session, lb_id, lbs_id,
nsx_router_id, vip_address)
else:
LOG.debug("LB binding has already been added, and no need "
"to add here.")
@log_helpers.log_method_call
def create(self, context, member, completor):
with locking.LockManager.get_lock(
@ -128,7 +80,6 @@ class EdgeMemberManagerFromDict(base_mgr.Nsxv3LoadbalancerBaseManager):
raise n_exc.BadRequest(resource='lbaas-subnet', msg=msg)
pool_client = self.core_plugin.nsxlib.load_balancer.pool
service_client = self.core_plugin.nsxlib.load_balancer.service
network = lb_utils.get_network_from_subnet(
context, self.core_plugin, member['subnet_id'])
@ -148,43 +99,14 @@ class EdgeMemberManagerFromDict(base_mgr.Nsxv3LoadbalancerBaseManager):
binding = nsx_db.get_nsx_lbaas_pool_binding(context.session,
lb_id, pool_id)
if binding:
vs_id = binding.get('lb_vs_id')
lb_pool_id = binding.get('lb_pool_id')
lb_binding = nsx_db.get_nsx_lbaas_loadbalancer_binding(
context.session, lb_id)
lb_service = None
if not lb_binding:
nsx_router_id = nsx_db.get_nsx_router_id(context.session,
router_id)
lb_service = service_client.get_router_lb_service(
nsx_router_id)
virtual_server_ids = (
lb_service and
lb_service.get('virtual_server_ids', []) or [])
if not lb_service:
lb_size = lb_utils.get_lb_flavor_size(
self.flavor_plugin, context,
loadbalancer.get('flavor_id'))
if not self.core_plugin.service_router_has_services(
context,
router_id):
self.core_plugin.create_service_router(context,
router_id)
lb_service = self._create_lb_service(
context, service_client, member['tenant_id'],
router_id, nsx_router_id, loadbalancer['id'], lb_size)
if not lb_service:
completor(success=False)
msg = (_('Failed to create lb service to attach '
'virtual server %(vs)s for member '
'%(member)s') %
{'vs': vs_id, 'member': member['id']})
raise nsx_exc.NsxPluginException(err_msg=msg)
lb_service_id = lb_service['id']
self._add_loadbalancer_binding(
context, loadbalancer['id'], lb_service_id,
nsx_router_id, loadbalancer['vip_address'])
completor(success=False)
msg = (_('Failed to get LB binding for member %s') %
member['id'])
raise nsx_exc.NsxPluginException(err_msg=msg)
with locking.LockManager.get_lock('pool-member-%s' % lb_pool_id):
lb_pool = pool_client.get(lb_pool_id)
@ -197,30 +119,6 @@ class EdgeMemberManagerFromDict(base_mgr.Nsxv3LoadbalancerBaseManager):
members = (old_m + new_m) if old_m else new_m
pool_client.update_pool_with_members(lb_pool_id, members)
# Check whether the virtual server should be added to the load
# balancing server. It is safe to perform this operation after the
# member has been added to the pool. This allows us to skip this
# check if there is already a member in the pool
if vs_id and not old_m:
# load the LB service if not already loaded
if not lb_service:
nsx_router_id = nsx_db.get_nsx_router_id(context.session,
router_id)
lb_service = service_client.get_router_lb_service(
nsx_router_id)
lb_service_id = lb_service['id']
virtual_server_ids = lb_service.get('virtual_server_ids',
[])
if vs_id not in virtual_server_ids:
try:
service_client.add_virtual_server(lb_service_id, vs_id)
except nsxlib_exc.ManagerError:
completor(success=False)
msg = (_('Failed to attach virtual server %(vs)s '
'to lb service %(service)s') %
{'vs': vs_id, 'service': lb_service_id})
raise n_exc.BadRequest(resource='lbaas-member',
msg=msg)
else:
completor(success=False)
msg = (_('Failed to get pool binding to add member %s') %

View File

@ -248,7 +248,12 @@ class TestEdgeLbaasV2Loadbalancer(BaseTestEdgeLbaasV2):
def test_create(self):
with mock.patch.object(lb_utils, 'validate_lb_subnet'
) as mock_validate_lb_subnet:
) as mock_validate_lb_subnet,\
mock.patch.object(lb_utils, 'get_router_from_network'),\
mock.patch.object(nsx_db, 'get_nsx_lbaas_loadbalancer_binding'),\
mock.patch.object(nsx_db, 'get_nsx_router_id'),\
mock.patch.object(self.service_client, 'get_router_lb_service'),\
mock.patch.object(nsx_db, 'add_nsx_lbaas_loadbalancer_binding'):
mock_validate_lb_subnet.return_value = True
self.edge_driver.loadbalancer.create(self.context, self.lb)
@ -371,7 +376,6 @@ class TestEdgeLbaasV2Listener(BaseTestEdgeLbaasV2):
mock_add_listener_binding.assert_called_with(
self.context.session, LB_ID, LISTENER_ID, APP_PROFILE_ID,
LB_VS_ID)
mock_successful_completion = (
self.lbv2_driver.listener.successful_completion)
mock_successful_completion.assert_called_with(self.context,
@ -889,7 +893,7 @@ class TestEdgeLbaasV2Member(BaseTestEdgeLbaasV2):
def _tested_entity(self):
return 'member'
def _test_create(self, lb_binding, pool_binding):
def test_create(self):
with mock.patch.object(lb_utils, 'validate_lb_member_subnet'
) as mock_validate_lb_subnet, \
mock.patch.object(self.lbv2_driver.plugin, 'get_pool_members'
@ -906,11 +910,6 @@ class TestEdgeLbaasV2Member(BaseTestEdgeLbaasV2):
) as mock_get_nsx_router_id, \
mock.patch.object(self.service_client, 'get_router_lb_service'
) as mock_get_lb_service, \
mock.patch.object(nsx_db, 'add_nsx_lbaas_loadbalancer_binding'
) as mock_add_loadbalancer_binding, \
mock.patch.object(self.service_client,
'add_virtual_server'
) as mock_add_vs_to_service, \
mock.patch.object(self.pool_client, 'get'
) as mock_get_pool, \
mock.patch.object(self.pool_client, 'update_pool_with_members'
@ -919,20 +918,13 @@ class TestEdgeLbaasV2Member(BaseTestEdgeLbaasV2):
mock_get_pool_members.return_value = [self.member]
mock_get_network.return_value = LB_NETWORK
mock_get_router.return_value = LB_ROUTER_ID
mock_get_pool_binding.return_value = pool_binding
mock_get_lb_binding.return_value = lb_binding
mock_get_pool_binding.return_value = POOL_BINDING
mock_get_lb_binding.return_value = LB_BINDING
mock_get_nsx_router_id.return_value = LB_ROUTER_ID
mock_get_lb_service.return_value = {'id': LB_SERVICE_ID}
mock_get_pool.return_value = LB_POOL
self.edge_driver.member.create(self.context, self.member)
if not lb_binding:
mock_add_loadbalancer_binding.assert_called_with(
self.context.session, LB_ID, LB_SERVICE_ID, LB_ROUTER_ID,
LB_VIP)
else:
mock_add_loadbalancer_binding.assert_not_called()
mock_add_vs_to_service.assert_called_with(LB_SERVICE_ID, LB_VS_ID)
mock_update_pool_with_members.assert_called_with(LB_POOL_ID,
[LB_MEMBER])
mock_successful_completion = (
@ -941,119 +933,6 @@ class TestEdgeLbaasV2Member(BaseTestEdgeLbaasV2):
self.member,
delete=False)
def test_create(self):
self._test_create(None, POOL_BINDING)
def test_create_existing_binding(self):
self._test_create(LB_BINDING, POOL_BINDING)
def test_create_with_service(self):
ext_cidr = '1.1.1.0/24'
with mock.patch.object(lb_utils, 'validate_lb_member_subnet'
) as mock_validate_lb_subnet, \
mock.patch.object(self.lbv2_driver.plugin, 'get_pool_members'
) as mock_get_pool_members, \
mock.patch.object(lb_utils, 'get_network_from_subnet'
) as mock_get_network, \
mock.patch.object(lb_utils, 'get_router_from_network'
) as mock_get_router, \
mock.patch.object(nsx_db, 'get_nsx_lbaas_pool_binding'
) as mock_get_pool_binding, \
mock.patch.object(nsx_db, 'get_nsx_lbaas_loadbalancer_binding'
) as mock_get_lb_binding, \
mock.patch.object(nsx_db, 'get_nsx_router_id'
) as mock_get_nsx_router_id, \
mock.patch.object(self.service_client, 'get_router_lb_service'
) as mock_get_lb_service, \
mock.patch.object(self.service_client, 'create'
) as mock_create_lb_service, \
mock.patch.object(nsx_db, 'add_nsx_lbaas_loadbalancer_binding'
) as mock_add_loadbalancer_bidning, \
mock.patch.object(self.service_client,
'add_virtual_server'
) as mock_add_vs_to_service, \
mock.patch.object(self.pool_client, 'get'
) as mock_get_pool, \
mock.patch.object(self.pool_client, 'update_pool_with_members'
) as mock_update_pool_with_members,\
mock.patch.object(self.core_plugin.nsxlib.logical_router,
'update_advertisement_rules') as update_adv,\
mock.patch.object(self.core_plugin, '_find_router_gw_subnets'
) as mock_get_subnets,\
mock.patch.object(self.core_plugin, 'get_router'
) as mock_core_get_router:
mock_validate_lb_subnet.return_value = True
mock_get_pool_members.return_value = [self.member]
mock_get_network.return_value = LB_NETWORK
mock_get_router.return_value = LB_ROUTER_ID
mock_get_pool_binding.return_value = POOL_BINDING
mock_get_lb_binding.return_value = None
mock_get_nsx_router_id.return_value = LB_ROUTER_ID
mock_get_lb_service.return_value = {}
mock_create_lb_service.return_value = {'id': LB_SERVICE_ID}
mock_get_pool.return_value = LB_POOL
mock_core_get_router.return_value = {
'id': LB_ROUTER_ID,
'name': 'router1',
'external_gateway_info': 'dummy'}
mock_get_subnets.return_value = [{'cidr': ext_cidr}]
self.edge_driver.member.create(self.context, self.member)
mock_add_loadbalancer_bidning.assert_called_with(
self.context.session, LB_ID, LB_SERVICE_ID, LB_ROUTER_ID,
LB_VIP)
mock_add_vs_to_service.assert_called_with(LB_SERVICE_ID, LB_VS_ID)
mock_update_pool_with_members.assert_called_with(LB_POOL_ID,
[LB_MEMBER])
update_adv.assert_called_with(
LB_ROUTER_ID,
[{'networks': [ext_cidr],
'display_name': lb_utils.ADV_RULE_NAME,
'rule_filter': {'match_route_types': ['T1_LB_VIP'],
'prefix_operator': 'GE'},
'action': 'ALLOW'}],
name_prefix=lb_utils.ADV_RULE_NAME)
mock_successful_completion = (
self.lbv2_driver.member.successful_completion)
mock_successful_completion.assert_called_with(self.context,
self.member,
delete=False)
def test_create_lbs_no_router_gateway(self):
with mock.patch.object(lb_utils, 'validate_lb_member_subnet'
) as mock_validate_lb_subnet, \
mock.patch.object(self.lbv2_driver.plugin, 'get_pool_members'
) as mock_get_pool_members, \
mock.patch.object(lb_utils, 'get_network_from_subnet'
) as mock_get_network, \
mock.patch.object(lb_utils, 'get_router_from_network'
) as mock_get_router_from_network, \
mock.patch.object(nsx_db, 'get_nsx_lbaas_pool_binding'
) as mock_get_pool_binding, \
mock.patch.object(nsx_db, 'get_nsx_lbaas_loadbalancer_binding'
) as mock_get_lb_binding, \
mock.patch.object(nsx_db, 'get_nsx_router_id'
) as mock_get_nsx_router_id, \
mock.patch.object(self.service_client, 'get_router_lb_service'
) as mock_get_lb_service, \
mock.patch.object(self.core_plugin, 'get_router'
) as mock_get_router:
mock_validate_lb_subnet.return_value = True
mock_get_pool_members.return_value = [self.member]
mock_get_network.return_value = LB_NETWORK
mock_get_router_from_network.return_value = LB_ROUTER_ID
mock_get_pool_binding.return_value = POOL_BINDING
mock_get_lb_binding.return_value = None
mock_get_nsx_router_id.return_value = LB_ROUTER_ID
mock_get_lb_service.return_value = None
mock_get_router.return_value = {'id': 'router1-xxx'}
self.assertRaises(n_exc.BadRequest,
self.edge_driver.member.create,
self.context,
self.member)
def test_create_member_different_router(self):
with mock.patch.object(self.lbv2_driver.plugin, 'get_pool_members'
) as mock_get_pool_members, \