vmware-nsx/vmware_nsx/services/lbaas/nsx_v/implementation/loadbalancer_mgr.py

213 lines
9.4 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
lb_common.set_lb_firewall_default_rule(
self.vcns, binding['edge_id'], 'deny')
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