
The LBaaS V2 plugin expects the driver to update the LB objects operating status from a separate process/thread. When the user requests the LB status (or just the LB object itself with GET), the operating status is retrived from the LBaaS DB, without calling the driver. To avoid adding a process to actively query and update all objects statuses, this patch creates a new LBaaSV2 plugin, to be used instead of the default one. This plugin (vmware_nsx_lbaasv2) will issue a get-statuses call to the driver, update the current statuses in the DB, and call the original plugin. Depends-on: I71a56b87144aad743795ad1295ec636b17429035 Change-Id: I3c4e75d92a1bacdb14292a8db727deb4923a85d9
241 lines
10 KiB
Python
241 lines
10 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
|
|
|
|
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 vcns_const.SERVICE_SIZE_MAPPING['lb']
|
|
else:
|
|
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()
|
|
else:
|
|
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']],
|
|
'device_owner': [constants.DEVICE_OWNER_NEUTRON_PREFIX + 'LB']}
|
|
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'])
|
|
|
|
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 refresh(self, context, lb):
|
|
# TODO(kobis): implememnt
|
|
pass
|
|
|
|
def stats(self, context, lb):
|
|
stats = {'bytes_in': 0,
|
|
'bytes_out': 0,
|
|
'active_connections': 0,
|
|
'total_connections': 0}
|
|
|
|
binding = nsxv_db.get_nsxv_lbaas_loadbalancer_binding(context.session,
|
|
lb['id'])
|
|
|
|
try:
|
|
lb_stats = self.vcns.get_loadbalancer_statistics(
|
|
binding['edge_id'])
|
|
|
|
except nsxv_exc.VcnsApiException:
|
|
msg = (_('Failed to read load balancer statistics, edge: %s') %
|
|
binding['edge_id'])
|
|
raise n_exc.BadRequest(resource='edge-lbaas', msg=msg)
|
|
|
|
pools_stats = lb_stats[1].get('pool', [])
|
|
for pool_stats in pools_stats:
|
|
stats['bytes_in'] += pool_stats.get('bytesIn', 0)
|
|
stats['bytes_out'] += pool_stats.get('bytesOut', 0)
|
|
stats['active_connections'] += pool_stats.get('curSessions', 0)
|
|
stats['total_connections'] += pool_stats.get('totalSessions', 0)
|
|
|
|
return stats
|
|
|
|
def get_operating_status(self, context, id):
|
|
"""Return a map of the operating status of all connected LB objects """
|
|
#TODO(asarfaty) implement
|
|
return {}
|
|
|
|
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]}
|
|
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']:
|
|
edge_bind = nsxv_db.get_nsxv_lbaas_loadbalancer_binding(
|
|
context.session, lb_port['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
|