Merge "LBaaS legacy mode bugfix"

This commit is contained in:
Zuul 2018-06-20 07:41:12 +00:00 committed by Gerrit Code Review
commit b91995ab0f
6 changed files with 206 additions and 7 deletions

View File

@ -45,6 +45,19 @@ class EdgeMemberManagerFromDict(base_mgr.EdgeLoadbalancerBaseManager):
lb_id = member['pool']['loadbalancer']['id'] lb_id = member['pool']['loadbalancer']['id']
return lb_id return lb_id
def _get_pool_member_ips(self, pool, operation, address):
member_ips = [member['address'] for member in pool['members']]
if operation == 'add' and address not in member_ips:
member_ips.append(address)
elif operation == 'del' and address in member_ips:
member_ips.remove(address)
return member_ips
def _get_lbaas_fw_section_id(self):
if not self._fw_section_id:
self._fw_section_id = lb_common.get_lbaas_fw_section_id(self.vcns)
return self._fw_section_id
def create(self, context, member, completor): def create(self, context, member, completor):
lb_id = self._get_pool_lb_id(member) lb_id = self._get_pool_lb_id(member)
lb_binding = nsxv_db.get_nsxv_lbaas_loadbalancer_binding( lb_binding = nsxv_db.get_nsxv_lbaas_loadbalancer_binding(
@ -61,11 +74,10 @@ class EdgeMemberManagerFromDict(base_mgr.EdgeLoadbalancerBaseManager):
raise n_exc.BadRequest(resource='edge-lbaas', msg=msg) raise n_exc.BadRequest(resource='edge-lbaas', msg=msg)
edge_pool_id = pool_binding['edge_pool_id'] edge_pool_id = pool_binding['edge_pool_id']
old_lb = lb_common.is_lb_on_router_edge(
context, self.core_plugin, edge_id)
with locking.LockManager.get_lock(edge_id): with locking.LockManager.get_lock(edge_id):
if (not cfg.CONF.nsxv.use_routers_as_lbaas_platform and if not cfg.CONF.nsxv.use_routers_as_lbaas_platform and not old_lb:
not lb_common.is_lb_on_router_edge(context.elevated(),
self.core_plugin,
edge_id)):
# Verify that Edge appliance is connected to the member's # Verify that Edge appliance is connected to the member's
# subnet (only if this is a dedicated loadbalancer edge) # subnet (only if this is a dedicated loadbalancer edge)
if not lb_common.get_lb_interface( if not lb_common.get_lb_interface(
@ -93,6 +105,16 @@ class EdgeMemberManagerFromDict(base_mgr.EdgeLoadbalancerBaseManager):
self.vcns.update_pool(edge_id, edge_pool_id, edge_pool) self.vcns.update_pool(edge_id, edge_pool_id, edge_pool)
completor(success=True) completor(success=True)
if old_lb:
member_ips = self._get_pool_member_ips(member['pool'],
'add',
member['address'])
lb_common.update_pool_fw_rule(
self.vcns, member['pool_id'],
edge_id,
self._get_lbaas_fw_section_id(),
member_ips)
except nsxv_exc.VcnsApiException: except nsxv_exc.VcnsApiException:
with excutils.save_and_reraise_exception(): with excutils.save_and_reraise_exception():
completor(success=False) completor(success=False)
@ -150,6 +172,9 @@ class EdgeMemberManagerFromDict(base_mgr.EdgeLoadbalancerBaseManager):
context.session, lb_id, member['pool_id']) context.session, lb_id, member['pool_id'])
edge_id = lb_binding['edge_id'] edge_id = lb_binding['edge_id']
old_lb = lb_common.is_lb_on_router_edge(
context, self.core_plugin, edge_id)
with locking.LockManager.get_lock(edge_id): with locking.LockManager.get_lock(edge_id):
if not cfg.CONF.nsxv.use_routers_as_lbaas_platform: if not cfg.CONF.nsxv.use_routers_as_lbaas_platform:
# we should remove LB subnet interface if no members are # we should remove LB subnet interface if no members are
@ -182,6 +207,16 @@ class EdgeMemberManagerFromDict(base_mgr.EdgeLoadbalancerBaseManager):
try: try:
self.vcns.update_pool(edge_id, edge_pool_id, edge_pool) self.vcns.update_pool(edge_id, edge_pool_id, edge_pool)
if old_lb:
member_ips = self._get_pool_member_ips(member['pool'],
'del',
member['address'])
lb_common.update_pool_fw_rule(
self.vcns, member['pool_id'],
edge_id,
self._get_lbaas_fw_section_id(),
member_ips)
completor(success=True) completor(success=True)
except nsxv_exc.VcnsApiException: except nsxv_exc.VcnsApiException:

View File

@ -34,6 +34,7 @@ class EdgePoolManagerFromDict(base_mgr.EdgeLoadbalancerBaseManager):
@log_helpers.log_method_call @log_helpers.log_method_call
def __init__(self, vcns_driver): def __init__(self, vcns_driver):
super(EdgePoolManagerFromDict, self).__init__(vcns_driver) super(EdgePoolManagerFromDict, self).__init__(vcns_driver)
self._fw_section_id = None
def create(self, context, pool, completor): def create(self, context, pool, completor):
@ -191,6 +192,20 @@ class EdgePoolManagerFromDict(base_mgr.EdgeLoadbalancerBaseManager):
listener_mgr.update_app_profile( listener_mgr.update_app_profile(
self.vcns, context, listener, edge_id) self.vcns, context, listener, edge_id)
old_lb = lb_common.is_lb_on_router_edge(
context, self.core_plugin, lb_binding['edge_id'])
if old_lb:
lb_common.update_pool_fw_rule(self.vcns, pool['id'],
edge_id,
self._get_lbaas_fw_section_id(),
[])
except nsxv_exc.VcnsApiException: except nsxv_exc.VcnsApiException:
completor(success=False) completor(success=False)
LOG.error('Failed to delete pool %s', pool['id']) LOG.error('Failed to delete pool %s', pool['id'])
def _get_lbaas_fw_section_id(self):
if not self._fw_section_id:
self._fw_section_id = lb_common.get_lbaas_fw_section_id(self.vcns)
return self._fw_section_id

View File

@ -13,22 +13,25 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
import netaddr import xml.etree.ElementTree as et
from oslo_log import log as logging import netaddr
from neutron_lib import constants from neutron_lib import constants
from neutron_lib import exceptions as n_exc from neutron_lib import exceptions as n_exc
from oslo_log import log as logging
from vmware_nsx._i18n import _ from vmware_nsx._i18n import _
from vmware_nsx.common import locking from vmware_nsx.common import locking
from vmware_nsx.db import nsxv_db from vmware_nsx.db import nsxv_db
from vmware_nsx.plugins.nsx_v.vshield import edge_utils from vmware_nsx.plugins.nsx_v.vshield import edge_utils
from vmware_nsx.plugins.nsx_v.vshield import vcns as nsxv_api
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
MEMBER_ID_PFX = 'member-' MEMBER_ID_PFX = 'member-'
RESOURCE_ID_PFX = 'lbaas-' RESOURCE_ID_PFX = 'lbaas-'
LBAAS_FW_SECTION_NAME = 'LBaaS FW Rules'
def get_member_id(member_id): def get_member_id(member_id):
@ -300,6 +303,64 @@ def get_edge_ip_addresses(vcns, edge_id):
return edge_ips return edge_ips
def update_pool_fw_rule(vcns, pool_id, edge_id, section_id, member_ips):
edge_ips = get_edge_ip_addresses(vcns, edge_id)
with locking.LockManager.get_lock('lbaas-fw-section'):
section_uri = '%s/%s/%s' % (nsxv_api.FIREWALL_PREFIX,
'layer3sections',
section_id)
xml_section = vcns.get_section(section_uri)[1]
section = et.fromstring(xml_section)
pool_rule = None
for rule in section.iter('rule'):
if rule.find('name').text == pool_id:
pool_rule = rule
if member_ips:
pool_rule.find('sources').find('source').find(
'value').text = (','.join(edge_ips))
pool_rule.find('destinations').find(
'destination').find('value').text = ','.join(
member_ips)
else:
section.remove(pool_rule)
break
if member_ips and pool_rule is None:
pool_rule = et.SubElement(section, 'rule')
et.SubElement(pool_rule, 'name').text = pool_id
et.SubElement(pool_rule, 'action').text = 'allow'
sources = et.SubElement(pool_rule, 'sources')
sources.attrib['excluded'] = 'false'
source = et.SubElement(sources, 'source')
et.SubElement(source, 'type').text = 'Ipv4Address'
et.SubElement(source, 'value').text = ','.join(edge_ips)
destinations = et.SubElement(pool_rule, 'destinations')
destinations.attrib['excluded'] = 'false'
destination = et.SubElement(destinations, 'destination')
et.SubElement(destination, 'type').text = 'Ipv4Address'
et.SubElement(destination, 'value').text = ','.join(member_ips)
vcns.update_section(section_uri,
et.tostring(section, encoding="us-ascii"),
None)
def get_lbaas_fw_section_id(vcns):
# Avoid concurrent creation of section by multiple neutron
# instances
with locking.LockManager.get_lock('lbaas-fw-section'):
fw_section_id = vcns.get_section_id(LBAAS_FW_SECTION_NAME)
if not fw_section_id:
section = et.Element('section')
section.attrib['name'] = LBAAS_FW_SECTION_NAME
sect = vcns.create_section('ip', et.tostring(section))[1]
fw_section_id = et.fromstring(sect).attrib['id']
return fw_section_id
def enable_edge_acceleration(vcns, edge_id): def enable_edge_acceleration(vcns, edge_id):
with locking.LockManager.get_lock(edge_id): with locking.LockManager.get_lock(edge_id):
# Query the existing load balancer config in case metadata lb is set # Query the existing load balancer config in case metadata lb is set

View File

@ -0,0 +1,80 @@
# Copyright 2018 VMware, Inc.
# All Rights Reserved
#
# 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 logging
import xml.etree.ElementTree as et
from neutron_lbaas.db.loadbalancer import models as nlbaas_v2
from neutron_lib.callbacks import registry
from vmware_nsx.common import locking
from vmware_nsx.plugins.nsx_v.vshield import vcns as nsxv_api
from vmware_nsx.shell.admin.plugins.common import constants
from vmware_nsx.shell.admin.plugins.common.utils import output_header
from vmware_nsx.shell.admin.plugins.nsxv.resources import utils as utils
from vmware_nsx.shell.resources import Operations
LBAAS_FW_SECTION_NAME = 'LBaaS FW Rules'
LOG = logging.getLogger(__name__)
@output_header
def sync_lbaas_dfw_rules(resource, event, trigger, **kwargs):
vcns = utils.get_nsxv_client()
with locking.LockManager.get_lock('lbaas-fw-section'):
fw_section_id = vcns.get_section_id(LBAAS_FW_SECTION_NAME)
if not fw_section_id:
section = et.Element('section')
section.attrib['name'] = LBAAS_FW_SECTION_NAME
sect = vcns.create_section('ip', et.tostring(section))[1]
fw_section_id = et.fromstring(sect).attrib['id']
if not fw_section_id:
LOG.error('No LBaaS FW Section id found')
return
neutron_db = utils.NeutronDbClient()
pools = neutron_db.context.session.query(nlbaas_v2.PoolV2).all()
pool_ids = [pool['id'] for pool in pools]
section_uri = '%s/%s/%s' % (nsxv_api.FIREWALL_PREFIX,
'layer3sections',
fw_section_id)
xml_section_data = vcns.get_section(section_uri)
if xml_section_data:
xml_section = xml_section_data[1]
else:
LOG.info('LBaaS XML section was not found!')
return
section = et.fromstring(xml_section)
for rule in section.findall('.//rule'):
if rule.find('name').text in pool_ids:
LOG.info('Rule %s found and valid', rule.find('name').text)
else:
section.remove(rule)
LOG.info('Rule %s is stale and removed',
rule.find('name').text)
vcns.update_section(section_uri,
et.tostring(section, encoding="us-ascii"),
None)
registry.subscribe(sync_lbaas_dfw_rules,
constants.LBAAS,
Operations.NSX_UPDATE.value)

View File

@ -215,7 +215,9 @@ nsxv_resources = {
Operations.DELETE.value]), Operations.DELETE.value]),
constants.BGP_NEIGHBOUR: Resource(constants.BGP_NEIGHBOUR, constants.BGP_NEIGHBOUR: Resource(constants.BGP_NEIGHBOUR,
[Operations.CREATE.value, [Operations.CREATE.value,
Operations.DELETE.value]) Operations.DELETE.value]),
constants.LBAAS: Resource(constants.LBAAS,
[Operations.NSX_UPDATE.value]),
} }

View File

@ -611,11 +611,14 @@ class TestEdgeLbaasV2Pool(BaseTestEdgeLbaasV2):
) as mock_del_pool, \ ) as mock_del_pool, \
mock.patch.object(nsxv_db, 'del_nsxv_lbaas_pool_binding' mock.patch.object(nsxv_db, 'del_nsxv_lbaas_pool_binding'
) as mock_del_binding,\ ) as mock_del_binding,\
mock.patch.object(lb_common, 'is_lb_on_router_edge'
) as mock_lb_router, \
mock.patch.object(self.edge_driver.vcns, 'update_app_profile' mock.patch.object(self.edge_driver.vcns, 'update_app_profile'
): ):
mock_get_lb_binding.return_value = LB_BINDING mock_get_lb_binding.return_value = LB_BINDING
mock_get_pool_binding.return_value = POOL_BINDING mock_get_pool_binding.return_value = POOL_BINDING
mock_get_listener_binding.return_value = LISTENER_BINDING mock_get_listener_binding.return_value = LISTENER_BINDING
mock_lb_router.return_value = False
self.edge_driver.pool.delete(self.context, self.pool) self.edge_driver.pool.delete(self.context, self.pool)
@ -709,12 +712,15 @@ class TestEdgeLbaasV2Member(BaseTestEdgeLbaasV2):
) as mock_get_pool, \ ) as mock_get_pool, \
mock.patch.object(self.core_plugin, 'get_ports' mock.patch.object(self.core_plugin, 'get_ports'
) as mock_get_ports, \ ) as mock_get_ports, \
mock.patch.object(lb_common, 'is_lb_on_router_edge'
) as mock_lb_router, \
mock.patch.object(lb_common, 'delete_lb_interface' mock.patch.object(lb_common, 'delete_lb_interface'
) as mock_del_lb_iface, \ ) as mock_del_lb_iface, \
mock.patch.object(self.edge_driver.vcns, 'update_pool' mock.patch.object(self.edge_driver.vcns, 'update_pool'
) as mock_update_pool: ) as mock_update_pool:
mock_get_lb_binding.return_value = LB_BINDING mock_get_lb_binding.return_value = LB_BINDING
mock_get_pool_binding.return_value = POOL_BINDING mock_get_pool_binding.return_value = POOL_BINDING
mock_lb_router.return_value = False
edge_pool_def = EDGE_POOL_DEF.copy() edge_pool_def = EDGE_POOL_DEF.copy()
edge_pool_def['member'] = [EDGE_MEMBER_DEF] edge_pool_def['member'] = [EDGE_MEMBER_DEF]
mock_get_pool.return_value = (None, edge_pool_def) mock_get_pool.return_value = (None, edge_pool_def)