Kobi Samoray af3fd782a1 NSXV: Catch exceptions on delete-cascade
While cascade-deleting a LB, there are cases where the backend is
out of sync with the OpenStack DBs.
When the entire LB edge is missing, the result is various exceptions
which should be handled.

Change-Id: I4af46d6cb16dd5ea638b2e53a804b89538d38221
2021-07-01 13:27:38 +00:00

231 lines
9.9 KiB
Python

# Copyright 2015 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.
from neutron.services.flavors import flavors_plugin
from neutron_lib.callbacks import events
from neutron_lib.callbacks import registry
from neutron_lib.callbacks import resources
from neutron_lib import constants
from neutron_lib import exceptions as n_exc
from oslo_config import cfg
from oslo_log import helpers as log_helpers
from oslo_log import log as logging
from oslo_utils import excutils
from vmware_nsx._i18n import _
from vmware_nsx.db import nsxv_db
from vmware_nsx.plugins.nsx_v.vshield.common import (
constants as vcns_const)
from vmware_nsx.plugins.nsx_v.vshield.common import exceptions as nsxv_exc
from vmware_nsx.services.lbaas import base_mgr
from vmware_nsx.services.lbaas.nsx_v import lbaas_common as lb_common
from vmware_nsx.services.lbaas.octavia import constants as oct_const
LOG = logging.getLogger(__name__)
class EdgeLoadBalancerManagerFromDict(base_mgr.EdgeLoadbalancerBaseManager):
@log_helpers.log_method_call
def __init__(self, vcns_driver):
super(EdgeLoadBalancerManagerFromDict, self).__init__(vcns_driver)
registry.subscribe(
self._handle_subnet_gw_change,
resources.SUBNET, events.AFTER_UPDATE)
def _get_lb_flavor_size(self, context, flavor_id):
if not flavor_id:
return self.core_plugin.edge_manager.get_service_edge_size('lb')
flavor = flavors_plugin.FlavorsPlugin.get_flavor(
self.flavor_plugin, context, flavor_id)
flavor_size = flavor['name']
if flavor_size.lower() in vcns_const.ALLOWED_EDGE_SIZES:
return flavor_size.lower()
err_msg = (_("Invalid flavor size %(flavor)s, only %(sizes)s "
"are supported") %
{'flavor': flavor_size,
'sizes': vcns_const.ALLOWED_EDGE_SIZES})
raise n_exc.InvalidInput(error_message=err_msg)
def create(self, context, lb, completor):
sub_id = lb['vip_subnet_id']
if cfg.CONF.nsxv.use_routers_as_lbaas_platform:
edge_id = lb_common.get_lbaas_edge_id_for_subnet(
context, self.core_plugin, sub_id, lb['tenant_id'])
if not edge_id:
msg = _('No suitable Edge found for subnet %s') % sub_id
raise n_exc.BadRequest(resource='edge-lbaas', msg=msg)
else:
lb_size = self._get_lb_flavor_size(context, lb.get('flavor_id'))
edge_id = lb_common.get_lbaas_edge_id(
context, self.core_plugin, lb['id'], lb['vip_address'],
sub_id, lb['tenant_id'], lb_size)
if not edge_id:
msg = _('Failed to allocate Edge on subnet %(sub)s for '
'loadbalancer %(lb)s') % {'sub': sub_id, 'lb': lb['id']}
raise n_exc.BadRequest(resource='edge-lbaas', msg=msg)
try:
if cfg.CONF.nsxv.use_routers_as_lbaas_platform:
if not nsxv_db.get_nsxv_lbaas_loadbalancer_binding_by_edge(
context.session, edge_id):
lb_common.enable_edge_acceleration(self.vcns, edge_id)
lb_common.add_vip_as_secondary_ip(self.vcns, edge_id,
lb['vip_address'])
else:
lb_common.enable_edge_acceleration(self.vcns, edge_id)
edge_fw_rule_id = lb_common.add_vip_fw_rule(
self.vcns, edge_id, lb['id'], lb['vip_address'])
# set LB default rule
if not cfg.CONF.nsxv.use_routers_as_lbaas_platform:
lb_common.set_lb_firewall_default_rule(self.vcns, edge_id,
'accept')
nsxv_db.add_nsxv_lbaas_loadbalancer_binding(
context.session, lb['id'], edge_id, edge_fw_rule_id,
lb['vip_address'])
completor(success=True)
except nsxv_exc.VcnsApiException:
with excutils.save_and_reraise_exception():
completor(success=False)
LOG.error('Failed to create loadbalancer %s', lb['id'])
def update(self, context, old_lb, new_lb, completor):
completor(success=True)
def delete(self, context, lb, completor):
# Discard any ports which are associated with LB
filters = {
'device_id': [lb['id'], oct_const.DEVICE_ID_PREFIX + lb['id']],
'device_owner': [lb_common.LBAAS_DEVICE_OWNER]}
lb_ports = self.core_plugin.get_ports(context.elevated(),
filters=filters)
for lb_port in lb_ports:
self.core_plugin.delete_port(context.elevated(), lb_port['id'],
allow_delete_lb_if=True)
binding = nsxv_db.get_nsxv_lbaas_loadbalancer_binding(
context.session, lb['id'])
if binding:
edge_binding = nsxv_db.get_nsxv_router_binding_by_edge(
context.session, binding['edge_id'])
# set LB default rule
try:
lb_common.set_lb_firewall_default_rule(
self.vcns, binding['edge_id'], 'deny')
except nsxv_exc.VcnsApiException as e:
LOG.error('Failed to set loadbalancer %(lb)s '
'FW rule. exception is %(exc)s',
{'lb': lb['id'], 'exc': e})
if edge_binding:
old_lb = lb_common.is_lb_on_router_edge(
context, self.core_plugin, binding['edge_id'])
if not old_lb:
resource_id = lb_common.get_lb_resource_id(lb['id'])
self.core_plugin.edge_manager.delete_lrouter(
context, resource_id, dist=False)
else:
# Edge was created on an exclusive router with the old code
try:
lb_common.del_vip_fw_rule(
self.vcns, binding['edge_id'],
binding['edge_fw_rule_id'])
except nsxv_exc.VcnsApiException as e:
LOG.error('Failed to delete loadbalancer %(lb)s '
'FW rule. exception is %(exc)s',
{'lb': lb['id'], 'exc': e})
try:
lb_common.del_vip_as_secondary_ip(self.vcns,
binding['edge_id'],
lb['vip_address'])
except Exception as e:
LOG.error('Failed to delete loadbalancer %(lb)s '
'interface IP. exception is %(exc)s',
{'lb': lb['id'], 'exc': e})
nsxv_db.del_nsxv_lbaas_loadbalancer_binding(
context.session, lb['id'])
completor(success=True)
def delete_cascade(self, context, lb, completor):
#TODO(asarfaty): implement a better delete cascade for NSX-V
self.delete(context, lb, completor)
def _handle_subnet_gw_change(self, *args, **kwargs):
# As the Edge appliance doesn't use DHCP, we should change the
# default gateway here when the subnet GW changes.
context = kwargs.get('context')
orig = kwargs['original_subnet']
updated = kwargs['subnet']
if (orig['gateway_ip'] == updated['gateway_ip'] and
self._routes_equal(orig['host_routes'], updated['host_routes'])):
return
subnet_id = updated['id']
subnet = self.core_plugin.get_subnet(context.elevated(), subnet_id)
filters = {'fixed_ips': {'subnet_id': [subnet_id]},
'device_owner': [constants.DEVICE_OWNER_LOADBALANCERV2,
oct_const.DEVICE_OWNER_OCTAVIA]}
lb_ports = self.core_plugin.get_ports(context.elevated(),
filters=filters)
if lb_ports:
for lb_port in lb_ports:
if lb_port['device_id']:
device_id = lb_port['device_id']
if device_id.startswith(oct_const.DEVICE_ID_PREFIX):
device_id = device_id[len(oct_const.DEVICE_ID_PREFIX):]
edge_bind = nsxv_db.get_nsxv_lbaas_loadbalancer_binding(
context.session, device_id)
edge_id = edge_bind['edge_id']
routes = [{'cidr': r['destination'],
'nexthop': r['nexthop']} for r in
subnet['host_routes']]
self.core_plugin.nsx_v.update_routes(
edge_id, subnet['gateway_ip'], routes)
def _routes_equal(self, a, b):
if len(a) != len(b):
return False
for a_item in a:
found = False
for b_item in b:
# compare values as keysets should be same
if set(a_item.values()) == set(b_item.values()):
found = True
if not found:
return False
return True
def get_supported_flavor_metadata(self):
return None
def validate_flavor(self, flavor_metadata):
return None
def get_supported_availability_zone_metadata(self):
return None
def validate_availability_zone(self, availability_zone_metadata):
return None