Merge "NSXv3: Neutron LBaaS nsxv3 support"

This commit is contained in:
Jenkins 2017-07-19 08:13:36 +00:00 committed by Gerrit Code Review
commit 48e46e4c03
17 changed files with 1717 additions and 1 deletions

View File

@ -484,3 +484,112 @@ def save_certificate(session, purpose, cert, pk):
def delete_certificate(session, purpose):
return (session.query(nsx_models.NsxCertificateRepository).
filter_by(purpose=purpose).delete())
def add_nsx_lbaas_loadbalancer_binding(session, loadbalancer_id,
lb_service_id, lb_router_id,
vip_address):
with session.begin(subtransactions=True):
binding = nsx_models.NsxLbaasLoadbalancer(
loadbalancer_id=loadbalancer_id, lb_service_id=lb_service_id,
lb_router_id=lb_router_id, vip_address=vip_address)
session.add(binding)
return binding
def get_nsx_lbaas_loadbalancer_binding(session, loadbalancer_id):
try:
return session.query(
nsx_models.NsxLbaasLoadbalancer).filter_by(
loadbalancer_id=loadbalancer_id).one()
except exc.NoResultFound:
return
def get_nsx_lbaas_loadbalancer_binding_by_service(session, lb_service_id):
return session.query(
nsx_models.NsxLbaasLoadbalancer).filter_by(
lb_service_id=lb_service_id).all()
def delete_nsx_lbaas_loadbalancer_binding(session, loadbalancer_id):
return (session.query(nsx_models.NsxLbaasLoadbalancer).
filter_by(loadbalancer_id=loadbalancer_id).delete())
def add_nsx_lbaas_listener_binding(session, loadbalancer_id, listener_id,
app_profile_id, lb_vs_id):
with session.begin(subtransactions=True):
binding = nsx_models.NsxLbaasListener(
loadbalancer_id=loadbalancer_id, listener_id=listener_id,
app_profile_id=app_profile_id,
lb_vs_id=lb_vs_id)
session.add(binding)
return binding
def get_nsx_lbaas_listener_binding(session, loadbalancer_id, listener_id):
try:
return session.query(
nsx_models.NsxLbaasListener).filter_by(
loadbalancer_id=loadbalancer_id,
listener_id=listener_id).one()
except exc.NoResultFound:
return
def delete_nsx_lbaas_listener_binding(session, loadbalancer_id, listener_id):
return (session.query(nsx_models.NsxLbaasListener).
filter_by(loadbalancer_id=loadbalancer_id,
listener_id=listener_id).delete())
def add_nsx_lbaas_pool_binding(session, loadbalancer_id, pool_id, lb_pool_id,
lb_vs_id):
with session.begin(subtransactions=True):
binding = nsx_models.NsxLbaasPool(loadbalancer_id=loadbalancer_id,
pool_id=pool_id,
lb_pool_id=lb_pool_id,
lb_vs_id=lb_vs_id)
session.add(binding)
return binding
def get_nsx_lbaas_pool_binding(session, loadbalancer_id, pool_id):
try:
return session.query(nsx_models.NsxLbaasPool).filter_by(
loadbalancer_id=loadbalancer_id, pool_id=pool_id).one()
except exc.NoResultFound:
return
def delete_nsx_lbaas_pool_binding(session, loadbalancer_id, pool_id):
return (session.query(nsx_models.NsxLbaasPool).
filter_by(loadbalancer_id=loadbalancer_id,
pool_id=pool_id).delete())
def add_nsx_lbaas_monitor_binding(session, loadbalancer_id, pool_id, hm_id,
lb_monitor_id, lb_pool_id):
with session.begin(subtransactions=True):
binding = nsx_models.NsxLbaasMonitor(
loadbalancer_id=loadbalancer_id, pool_id=pool_id, hm_id=hm_id,
lb_monitor_id=lb_monitor_id, lb_pool_id=lb_pool_id)
session.add(binding)
return binding
def get_nsx_lbaas_monitor_binding(session, loadbalancer_id, pool_id, hm_id):
try:
return session.query(nsx_models.NsxLbaasMonitor).filter_by(
loadbalancer_id=loadbalancer_id,
pool_id=pool_id, hm_id=hm_id).one()
except exc.NoResultFound:
return
def delete_nsx_lbaas_monitor_binding(session, loadbalancer_id, pool_id,
hm_id):
return (session.query(nsx_models.NsxLbaasMonitor).
filter_by(loadbalancer_id=loadbalancer_id,
pool_id=pool_id, hm_id=hm_id).delete())

View File

@ -1 +1 @@
53eb497903a4
ea7a72ab9643

View File

@ -0,0 +1,94 @@
# Copyright 2017 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.
from alembic import op
import sqlalchemy as sa
from neutron.db import migration
"""nsxv3_lbaas_mapping
Revision ID: ea7a72ab9643
Revises: 53eb497903a4
Create Date: 2017-06-12 16:59:48.021909
"""
# revision identifiers, used by Alembic.
revision = 'ea7a72ab9643'
down_revision = '53eb497903a4'
def upgrade():
op.create_table(
'nsxv3_lbaas_loadbalancers',
sa.Column('loadbalancer_id', sa.String(36), nullable=False),
sa.Column('lb_router_id', sa.String(36), nullable=False),
sa.Column('lb_service_id', sa.String(36), nullable=False),
sa.Column('vip_address', sa.String(36), nullable=False),
sa.Column('created_at', sa.DateTime(), nullable=True),
sa.Column('updated_at', sa.DateTime(), nullable=True),
sa.PrimaryKeyConstraint('loadbalancer_id'))
op.create_table(
'nsxv3_lbaas_listeners',
sa.Column('loadbalancer_id', sa.String(36), nullable=False),
sa.Column('listener_id', sa.String(36), nullable=False),
sa.Column('app_profile_id', sa.String(36), nullable=False),
sa.Column('lb_vs_id', sa.String(36), nullable=False),
sa.Column('created_at', sa.DateTime(), nullable=True),
sa.Column('updated_at', sa.DateTime(), nullable=True),
sa.PrimaryKeyConstraint('loadbalancer_id', 'listener_id'))
op.create_table(
'nsxv3_lbaas_pools',
sa.Column('loadbalancer_id', sa.String(36), nullable=False),
sa.Column('pool_id', sa.String(36), nullable=False),
sa.Column('lb_pool_id', sa.String(36), nullable=False),
sa.Column('lb_vs_id', sa.String(36), nullable=False),
sa.Column('created_at', sa.DateTime(), nullable=True),
sa.Column('updated_at', sa.DateTime(), nullable=True),
sa.PrimaryKeyConstraint('loadbalancer_id', 'pool_id'))
op.create_table(
'nsxv3_lbaas_monitors',
sa.Column('loadbalancer_id', sa.String(36), nullable=False),
sa.Column('pool_id', sa.String(36), nullable=False),
sa.Column('hm_id', sa.String(36), nullable=False),
sa.Column('lb_monitor_id', sa.String(36), nullable=False),
sa.Column('lb_pool_id', sa.String(36), nullable=False),
sa.Column('created_at', sa.DateTime(), nullable=True),
sa.Column('updated_at', sa.DateTime(), nullable=True),
sa.PrimaryKeyConstraint('loadbalancer_id', 'pool_id', 'hm_id'))
if migration.schema_has_table('lbaas_loadbalancers'):
op.create_foreign_key(
'fk_nsxv3_lbaas_loadbalancers_id', 'nsxv3_lbaas_loadbalancers',
'lbaas_loadbalancers', ['loadbalancer_id'], ['id'],
ondelete='CASCADE')
if migration.schema_has_table('lbaas_listeners'):
op.create_foreign_key(
'fk_nsxv3_lbaas_listeners_id', 'nsxv3_lbaas_listeners',
'lbaas_listeners', ['listener_id'], ['id'], ondelete='CASCADE')
if migration.schema_has_table('lbaas_pools'):
op.create_foreign_key(
'fk_nsxv3_lbaas_pools_id', 'nsxv3_lbaas_pools',
'lbaas_pools', ['pool_id'], ['id'], ondelete='CASCADE')
if migration.schema_has_table('lbaas_healthmonitors'):
op.create_foreign_key(
'fk_nsxv3_lbaas_healthmonitors_id', 'nsxv3_lbaas_monitors',
'lbaas_healthmonitors', ['hm_id'], ['id'], ondelete='CASCADE')

View File

@ -388,3 +388,63 @@ class NsxCertificateRepository(model_base.BASEV2, models.TimestampMixin):
primary_key=True)
certificate = sa.Column(sa.String(9216), nullable=False)
private_key = sa.Column(sa.String(5120), nullable=False)
class NsxLbaasLoadbalancer(model_base.BASEV2, models.TimestampMixin):
"""Stores mapping of LBaaS loadbalancer and NSX LB service and router
Since in NSXv3, multiple loadbalancers may share the same LB service
on NSX backend. And the in turn LB service attaches to a logical router.
This stores the mapping between LBaaS loadbalancer and NSX LB service id
and NSX logical router id.
"""
__tablename__ = 'nsxv3_lbaas_loadbalancers'
fk_name = 'fk_nsxv3_lbaas_loadbalancers_id'
loadbalancer_id = sa.Column(sa.String(36),
sa.ForeignKey('lbaas_loadbalancers.id',
name=fk_name,
ondelete="CASCADE"),
primary_key=True)
lb_router_id = sa.Column(sa.String(36), nullable=False)
lb_service_id = sa.Column(sa.String(36), nullable=False)
vip_address = sa.Column(sa.String(36), nullable=False)
class NsxLbaasListener(model_base.BASEV2, models.TimestampMixin):
"""Stores the mapping between LBaaS listener and NSX LB virtual server"""
__tablename__ = 'nsxv3_lbaas_listeners'
loadbalancer_id = sa.Column(sa.String(36), primary_key=True)
listener_id = sa.Column(sa.String(36),
sa.ForeignKey('lbaas_listeners.id',
name='fk_nsxv3_lbaas_listeners_id',
ondelete="CASCADE"),
primary_key=True)
app_profile_id = sa.Column(sa.String(36), nullable=False)
lb_vs_id = sa.Column(sa.String(36), nullable=False)
class NsxLbaasPool(model_base.BASEV2, models.TimestampMixin):
"""Stores the mapping between LBaaS pool and NSX LB Pool"""
__tablename__ = 'nsxv3_lbaas_pools'
loadbalancer_id = sa.Column(sa.String(36), primary_key=True)
pool_id = sa.Column(sa.String(36),
sa.ForeignKey('lbaas_pools.id',
name='fk_nsxv3_lbaas_pools_id',
ondelete="CASCADE"),
primary_key=True)
lb_pool_id = sa.Column(sa.String(36), nullable=False)
lb_vs_id = sa.Column(sa.String(36), nullable=False)
class NsxLbaasMonitor(model_base.BASEV2, models.TimestampMixin):
"""Stores the mapping between LBaaS monitor and NSX LB monitor"""
__tablename__ = 'nsxv3_lbaas_monitors'
loadbalancer_id = sa.Column(sa.String(36), primary_key=True)
pool_id = sa.Column(sa.String(36), primary_key=True)
hm_id = sa.Column(sa.String(36),
sa.ForeignKey('lbaas_healthmonitors.id',
name='fk_nsxv3_lbaas_healthmonitors_id',
ondelete="CASCADE"),
primary_key=True)
lb_monitor_id = sa.Column(sa.String(36), nullable=False)
lb_pool_id = sa.Column(sa.String(36), nullable=False)

View File

@ -96,6 +96,7 @@ from vmware_nsx.plugins.common import plugin as nsx_plugin_common
from vmware_nsx.plugins.nsx_v3 import availability_zones as nsx_az
from vmware_nsx.plugins.nsx_v3 import utils as v3_utils
from vmware_nsx.services.fwaas.nsx_v3 import fwaas_callbacks
from vmware_nsx.services.lbaas.nsx_v3 import lb_driver_v2
from vmware_nsx.services.qos.common import utils as qos_com_utils
from vmware_nsx.services.qos.nsx_v3 import driver as qos_driver
from vmware_nsx.services.trunk.nsx_v3 import driver as trunk_driver
@ -187,6 +188,7 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
self._extension_manager.extension_aliases())
self.nsxlib = v3_utils.get_nsxlib_wrapper()
self.lbv2_driver = self._init_lbv2_driver()
# reinitialize the cluster upon fork for api workers to ensure each
# process has its own keepalive loops + state
registry.subscribe(
@ -269,6 +271,18 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
# Bind FWaaS callbacks to the driver
self.fwaas_callbacks = fwaas_callbacks.Nsxv3FwaasCallbacks(self.nsxlib)
def _init_lbv2_driver(self):
# Get LBaaSv2 driver during plugin initialization. If the platform
# has a version that doesn't support native loadbalancing, the driver
# will return a NotImplementedManager class.
LOG.debug("Initializing LBaaSv2.0 nsxv3 driver")
if self.nsxlib.feature_supported(nsxlib_consts.FEATURE_LOAD_BALANCER):
return lb_driver_v2.EdgeLoadbalancerDriverV2()
else:
LOG.warning("Current NSX version %(ver)s doesn't support LBaaS",
{'ver': self.nsxlib.get_version()})
return lb_driver_v2.DummyLoadbalancerDriverV2()
def init_availability_zones(self):
# availability zones are supported only with native dhcp
# if not - the default az will be loaded and used internally only
@ -2834,6 +2848,8 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
return self.get_router(context, router['id'])
def delete_router(self, context, router_id):
# TODO(tongl) Prevent router deletion if router still has load
# balancer attachment. Raise exception if router has lb attachment.
if not cfg.CONF.nsx_v3.native_dhcp_metadata:
nsx_rpc.handle_router_metadata_access(self, context, router_id,
interface=None)

View File

@ -15,11 +15,15 @@
from neutron_lib.plugins import constants as plugin_const
from neutron_lib.plugins import directory
from oslo_log import log as logging
LOG = logging.getLogger(__name__)
class LoadbalancerBaseManager(object):
_lbv2_driver = None
_core_plugin = None
_flavor_plugin = None
def __init__(self):
super(LoadbalancerBaseManager, self).__init__()
@ -45,6 +49,14 @@ class LoadbalancerBaseManager(object):
return LoadbalancerBaseManager._core_plugin
@property
def flavor_plugin(self):
if not LoadbalancerBaseManager._flavor_plugin:
LoadbalancerBaseManager._flavor_plugin = (
self._get_plugin(plugin_const.FLAVORS))
return LoadbalancerBaseManager._flavor_plugin
class EdgeLoadbalancerBaseManager(LoadbalancerBaseManager):
@ -55,3 +67,9 @@ class EdgeLoadbalancerBaseManager(LoadbalancerBaseManager):
@property
def vcns(self):
return self.vcns_driver.vcns
class Nsxv3LoadbalancerBaseManager(LoadbalancerBaseManager):
def __init__(self):
super(Nsxv3LoadbalancerBaseManager, self).__init__()

View File

@ -72,3 +72,23 @@ L7_RULE_COMPARE_TYPE_STARTS_WITH = 'STARTS_WITH'
L7_RULE_COMPARE_TYPE_ENDS_WITH = 'ENDS_WITH'
L7_RULE_COMPARE_TYPE_CONTAINS = 'CONTAINS'
L7_RULE_COMPARE_TYPE_EQUAL_TO = 'EQUAL_TO'
# Resource type for resources created on NSX backend
LB_LB_TYPE = 'os-lbaas-lb-id'
LB_LISTENER_TYPE = 'os-lbaas-listener-id'
LB_HM_TYPE = 'os-lbaas-hm-id'
LB_POOL_TYPE = 'os-lbaas-pool-type'
LB_HTTP_PROFILE = 'LbHttpProfile'
LB_TCP_PROFILE = 'LbFastTcpProfile'
NSXV3_MONITOR_MAP = {LB_HEALTH_MONITOR_PING: 'LbIcmpMonitor',
LB_HEALTH_MONITOR_TCP: 'LbTcpMonitor',
LB_HEALTH_MONITOR_HTTP: 'LbHttpMonitor',
LB_HEALTH_MONITOR_HTTPS: 'LbHttpsMonitor'}
LB_STATS_MAP = {'active_connections': 'current_sessions',
'bytes_in': 'bytes_in',
'bytes_out': 'bytes_out',
'total_connections': 'total_sessions'}
LR_ROUTER_TYPE = 'os-neutron-router-id'
LR_PORT_TYPE = 'os-neutron-rport-id'
DEFAULT_LB_SIZE = 'SMALL'
LB_FLAVOR_SIZES = ['SMALL', 'MEDIUM', 'LARGE', 'small', 'medium', 'large']

View File

@ -0,0 +1,138 @@
# Copyright 2017 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_lib import exceptions as n_exc
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 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 import lb_utils
from vmware_nsxlib.v3 import exceptions as nsxlib_exc
from vmware_nsxlib.v3 import utils
LOG = logging.getLogger(__name__)
class EdgeHealthMonitorManager(base_mgr.Nsxv3LoadbalancerBaseManager):
@log_helpers.log_method_call
def __init__(self):
super(EdgeHealthMonitorManager, self).__init__()
@log_helpers.log_method_call
def _build_monitor_args(self, hm):
if hm.type in lb_const.NSXV3_MONITOR_MAP:
monitor_type = lb_const.NSXV3_MONITOR_MAP.get(hm.type)
else:
msg = (_('Cannot create health monitor %(monitor)s with '
'type %(type)s') % {'monitor': hm.id, 'type': hm.type})
raise n_exc.InvalidInput(error_message=msg)
body = {'resource_type': monitor_type,
'interval': hm.delay,
'fall_count': hm.max_retries,
'timeout': hm.timeout}
if monitor_type in [lb_const.LB_HEALTH_MONITOR_HTTP,
lb_const.LB_HEALTH_MONITOR_HTTPS]:
if hm.http_method:
body['request_method'] = hm.http_method
if hm.url_path:
body['request_url'] = hm.url_path
# TODO(tongl): nsxv3 backend doesn't support granular control
# of expected_codes. So we ignore it and use default for now.
# Once backend supports it, we can add it back.
# if hm.expected_codes:
# body['response_status'] = hm.expected_codes
return body
@log_helpers.log_method_call
def create(self, context, hm):
lb_id = hm.pool.loadbalancer_id
pool_id = hm.pool.id
pool_client = self.core_plugin.nsxlib.load_balancer.pool
monitor_client = self.core_plugin.nsxlib.load_balancer.monitor
monitor_name = utils.get_name_and_uuid(hm.name, hm.id)
tags = lb_utils.get_tags(self.core_plugin, hm.id, lb_const.LB_HM_TYPE,
hm.tenant_id, context.project_name)
monitor_body = self._build_monitor_args(hm)
try:
lb_monitor = monitor_client.create(
display_name=monitor_name, tags=tags, **monitor_body)
except nsxlib_exc.ManagerError:
with excutils.save_and_reraise_exception():
self.lbv2_driver.health_monitor.failed_completion(context, hm)
binding = nsx_db.get_nsx_lbaas_pool_binding(
context.session, lb_id, pool_id)
if binding:
lb_pool_id = binding['lb_pool_id']
try:
pool_client.add_monitor_to_pool(lb_pool_id,
lb_monitor['id'])
except nsxlib_exc.ManagerError:
self.lbv2_driver.health_monitor.failed_completion(
context, hm)
msg = _('Failed to attach monitor %(monitor)s to pool '
'%(pool)s') % {'monitor': lb_monitor['id'],
'pool': lb_pool_id}
raise n_exc.BadRequest(resource='lbaas-hm', msg=msg)
nsx_db.add_nsx_lbaas_monitor_binding(
context.session, lb_id, pool_id, hm.id, lb_monitor['id'],
lb_pool_id)
self.lbv2_driver.health_monitor.successful_completion(context, hm)
@log_helpers.log_method_call
def update(self, context, old_hm, new_hm):
self.lbv2_driver.health_monitor.successful_completion(context, new_hm)
@log_helpers.log_method_call
def delete(self, context, hm):
lb_id = hm.pool.loadbalancer_id
pool_id = hm.pool.id
pool_client = self.core_plugin.nsxlib.load_balancer.pool
monitor_client = self.core_plugin.nsxlib.load_balancer.monitor
binding = nsx_db.get_nsx_lbaas_monitor_binding(
context.session, lb_id, pool_id, hm.id)
if binding:
lb_monitor_id = binding['lb_monitor_id']
lb_pool_id = binding['lb_pool_id']
try:
pool_client.remove_monitor_from_pool(lb_pool_id,
lb_monitor_id)
except nsxlib_exc.ManagerError:
self.lbv2_driver.health_monitor.failed_completion(
context, hm)
msg = _('Failed to remove monitor %(monitor)s from pool '
'%(pool)s') % {'monitor': lb_monitor_id,
'pool': lb_pool_id}
raise n_exc.BadRequest(resource='lbaas-hm', msg=msg)
try:
monitor_client.delete(lb_monitor_id)
except nsxlib_exc.ManagerError:
self.lbv2_driver.health_monitor.failed_completion(
context, hm)
msg = _('Failed to delete monitor %(monitor)s from '
'backend') % {'monitor': lb_monitor_id}
raise n_exc.BadRequest(resource='lbaas-hm', msg=msg)
nsx_db.delete_nsx_lbaas_monitor_binding(context.session, lb_id,
pool_id, hm.id)
self.lbv2_driver.health_monitor.successful_completion(
context, hm, delete=True)

View File

@ -0,0 +1,64 @@
# Copyright 2017 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 oslo_log import helpers as log_helpers
from oslo_log import log as logging
from vmware_nsx.services.lbaas.nsx_v3 import healthmonitor_mgr as hm_mgr
from vmware_nsx.services.lbaas.nsx_v3 import listener_mgr
from vmware_nsx.services.lbaas.nsx_v3 import loadbalancer_mgr as lb_mgr
from vmware_nsx.services.lbaas.nsx_v3 import member_mgr
from vmware_nsx.services.lbaas.nsx_v3 import pool_mgr
LOG = logging.getLogger(__name__)
class NotImplementedManager(object):
"""Helper class to make any subclass of LoadBalancerBaseDriver explode if
it is missing any of the required object managers.
"""
def create(self, context, obj):
raise NotImplementedError()
def update(self, context, old_obj, obj):
raise NotImplementedError()
def delete(self, context, obj):
raise NotImplementedError()
class EdgeLoadbalancerDriverV2(object):
@log_helpers.log_method_call
def __init__(self):
super(EdgeLoadbalancerDriverV2, self).__init__()
self.loadbalancer = lb_mgr.EdgeLoadBalancerManager()
self.listener = listener_mgr.EdgeListenerManager()
self.pool = pool_mgr.EdgePoolManager()
self.member = member_mgr.EdgeMemberManager()
self.healthmonitor = hm_mgr.EdgeHealthMonitorManager()
class DummyLoadbalancerDriverV2(object):
@log_helpers.log_method_call
def __init__(self):
self.load_balancer = NotImplementedManager()
self.listener = NotImplementedManager()
self.pool = NotImplementedManager()
self.member = NotImplementedManager()
self.health_monitor = NotImplementedManager()
self.l7policy = NotImplementedManager()
self.l7rule = NotImplementedManager()

View File

@ -0,0 +1,107 @@
# Copyright 2017 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.db import l3_db
from neutron.services.flavors import flavors_plugin
from neutron_lib import exceptions as n_exc
from vmware_nsx._i18n import _
from vmware_nsx.services.lbaas import lb_const
from vmware_nsxlib.v3 import utils
def get_tags(plugin, resource_id, resource_type, project_id, project_name):
resource = {'project_id': project_id,
'id': resource_id}
tags = plugin.nsxlib.build_v3_tags_payload(
resource, resource_type=resource_type,
project_name=project_name)
return tags
def get_nsx_resource_binding(client, name, id):
"""
:param client: nsx resource client
:param name: name of neutron object
:param id: id of neutron object
:return: return the nsx resource id
"""
nsx_name = utils.get_name_and_uuid(name, id)
nsx_resource = client.find_by_display_name(nsx_name)
if nsx_resource:
return nsx_resource[0]['id']
def get_network_from_subnet(context, plugin, subnet_id):
subnet = plugin.get_subnet(context, subnet_id)
if subnet:
return plugin.get_network(context, subnet['network_id'])
def get_router_from_network(context, plugin, subnet_id):
subnet = plugin.get_subnet(context, subnet_id)
network_id = subnet['network_id']
port_filters = {'device_owner': [l3_db.DEVICE_OWNER_ROUTER_INTF],
'network_id': [network_id]}
ports = plugin.get_ports(context, filters=port_filters)
if ports:
return ports[0]['device_id']
def get_lb_router_id(context, plugin, lb):
router_client = plugin.nsxlib.logical_router
name = utils.get_name_and_uuid(lb.name, lb.id)
tags = get_tags(plugin, lb.id, lb_const.LB_LB_TYPE, lb.tenant_id,
context.project_name)
edge_cluster_uuid = plugin._get_edge_cluster(plugin._default_tier0_router)
lb_router = router_client.create(name, tags, edge_cluster_uuid)
return lb_router
def get_lb_flavor_size(flavor_plugin, context, flavor_id):
if not flavor_id:
return lb_const.DEFAULT_LB_SIZE
else:
flavor = flavors_plugin.FlavorsPlugin.get_flavor(
flavor_plugin, context, flavor_id)
flavor_size = flavor['name']
if flavor_size in lb_const.LB_FLAVOR_SIZES:
return flavor_size.upper()
else:
err_msg = (_("Invalid flavor size %(flavor)s, only 'small', "
"'medium', or 'large' are supported") %
{'flavor': flavor_size})
raise n_exc.InvalidInput(error_message=err_msg)
def validate_lb_subnet(context, plugin, subnet_id):
'''Validate LB subnet before creating loadbalancer on it.
To create a loadbalancer, the network has to be either an external
network or private network that connects to a tenant router. It will
throw exception if the network doesn't meet this requirement.
:param context: context
:param plugin: core plugin
:param subnet_id: loadbalancer's subnet id
:return: True if subnet meet requirement, otherwise return False
'''
network = get_network_from_subnet(context, plugin, subnet_id)
router_id = get_router_from_network(
context, plugin, subnet_id)
if network.get('router:external') or router_id:
return True
else:
return False

View File

@ -0,0 +1,124 @@
# Copyright 2017 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_lib import exceptions as n_exc
from oslo_log import helpers as log_helpers
from oslo_log import log as logging
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
from vmware_nsx.services.lbaas.nsx_v3 import lb_utils
from vmware_nsxlib.v3 import exceptions as nsxlib_exc
from vmware_nsxlib.v3 import utils
LOG = logging.getLogger(__name__)
class EdgeListenerManager(base_mgr.Nsxv3LoadbalancerBaseManager):
@log_helpers.log_method_call
def __init__(self):
super(EdgeListenerManager, self).__init__()
@log_helpers.log_method_call
def create(self, context, listener, certificate=None):
lb_id = listener.loadbalancer_id
vip_address = listener.loadbalancer.vip_address
load_balancer = self.core_plugin.nsxlib.load_balancer
app_client = load_balancer.application_profile
vs_client = load_balancer.virtual_server
vs_name = utils.get_name_and_uuid(listener.name, listener.id)
tags = lb_utils.get_tags(self.core_plugin, listener.id,
lb_const.LB_LISTENER_TYPE,
listener.tenant_id,
context.project_name)
if listener.protocol == 'HTTP' or listener.protocol == 'HTTPS':
profile_type = lb_const.LB_HTTP_PROFILE
elif listener.protocol == 'TCP':
profile_type = lb_const.LB_TCP_PROFILE
else:
msg = (_('Cannot create listener %(listener)s with '
'protocol %(protocol)s') %
{'listener': listener.id,
'protocol': listener.protocol})
raise n_exc.BadRequest(resource='lbaas-listener', msg=msg)
try:
app_profile = app_client.create(
display_name=vs_name, resource_type=profile_type, tags=tags)
app_profile_id = app_profile['id']
virtual_server = vs_client.create(
display_name=vs_name,
tags=tags,
enabled=listener.admin_state_up,
ip_address=vip_address,
port=listener.protocol_port,
application_profile_id=app_profile_id)
except nsxlib_exc.ManagerError:
self.lbv2_driver.listener.failed_completion(context, listener)
msg = _('Failed to create virtual server 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,
virtual_server['id'])
self.lbv2_driver.listener.successful_completion(
context, listener)
@log_helpers.log_method_call
def update(self, context, old_listener, new_listener, certificate=None):
self.lbv2_driver.listener.successful_completion(context, new_listener)
@log_helpers.log_method_call
def delete(self, context, listener):
lb_id = listener.loadbalancer_id
load_balancer = self.core_plugin.nsxlib.load_balancer
vs_client = load_balancer.virtual_server
app_client = load_balancer.application_profile
binding = nsx_db.get_nsx_lbaas_listener_binding(
context.session, lb_id, listener.id)
if binding:
vs_id = binding['lb_vs_id']
app_profile_id = binding['app_profile_id']
try:
vs_client.delete(vs_id)
except nsx_exc.NsxResourceNotFound:
msg = (_("virtual server not found on nsx: %(vs)s") %
{'vs': vs_id})
raise n_exc.BadRequest(resource='lbaas-listener', msg=msg)
except nsxlib_exc.ManagerError:
self.lbv2_driver.listener.failed_completion(context,
listener)
msg = (_('Failed to delete virtual server: %(listener)s') %
{'listener': listener.id})
raise n_exc.BadRequest(resource='lbaas-listener', msg=msg)
try:
app_client.delete(app_profile_id)
except nsx_exc.NsxResourceNotFound:
msg = (_("application profile not found on nsx: %s") %
app_profile_id)
raise n_exc.BadRequest(resource='lbaas-listener', msg=msg)
except nsxlib_exc.ManagerError:
self.lbv2_driver.listener.failed_completion(context,
listener)
msg = (_('Failed to delete application profile: %(app)s') %
{'app': app_profile_id})
raise n_exc.BadRequest(resource='lbaas-listener', msg=msg)
nsx_db.delete_nsx_lbaas_listener_binding(
context.session, lb_id, listener.id)
self.lbv2_driver.listener.successful_completion(
context, listener, delete=True)

View File

@ -0,0 +1,110 @@
# Copyright 2017 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_lib.callbacks import events
from neutron_lib.callbacks import registry
from neutron_lib.callbacks import resources
from neutron_lib import exceptions as n_exc
from oslo_log import helpers as log_helpers
from oslo_log import log as logging
from vmware_nsx._i18n import _
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 import lb_utils
from vmware_nsxlib.v3 import exceptions as nsxlib_exc
LOG = logging.getLogger(__name__)
class EdgeLoadBalancerManager(base_mgr.Nsxv3LoadbalancerBaseManager):
@log_helpers.log_method_call
def __init__(self):
super(EdgeLoadBalancerManager, self).__init__()
registry.subscribe(
self._handle_subnet_gw_change,
resources.SUBNET, events.AFTER_UPDATE)
@log_helpers.log_method_call
def create(self, context, lb):
if lb_utils.validate_lb_subnet(context, self.core_plugin,
lb.vip_subnet_id):
self.lbv2_driver.load_balancer.successful_completion(context, lb)
else:
msg = _('Cannot create lb on subnet %(sub)s for '
'loadbalancer %(lb)s as it does not connect '
'to router') % {'sub': lb.vip_subnet_id,
'lb': lb.id}
raise n_exc.BadRequest(resource='lbaas-subnet', msg=msg)
@log_helpers.log_method_call
def update(self, context, old_lb, new_lb):
self.lbv2_driver.load_balancer.successful_completion(context, new_lb)
@log_helpers.log_method_call
def delete(self, context, lb):
# Discard any ports which are associated with LB
self.lbv2_driver.load_balancer.successful_completion(
context, lb, delete=True)
@log_helpers.log_method_call
def refresh(self, context, lb):
# TODO(tongl): implememnt
pass
@log_helpers.log_method_call
def stats(self, context, lb):
# Since multiple LBaaS loadbalancer can share the same LB service,
# get the corresponding virtual servers' stats instead of LB service.
stats = {'active_connections': 0,
'bytes_in': 0,
'bytes_out': 0,
'total_connections': 0}
service_client = self.core_plugin.nsxlib.load_balancer.service
for listener in lb.listeners:
lb_binding = nsx_db.get_nsx_lbaas_loadbalancer_binding(
context.session, lb.id)
vs_binding = nsx_db.get_nsx_lbaas_listener_binding(
context.session, lb.id, listener.id)
if lb_binding and vs_binding:
lb_service_id = lb_binding.get('lb_service_id')
vs_id = vs_binding.get('lb_vs_id')
try:
rsp = service_client.get_stats(lb_service_id)
if rsp:
vs_stats = rsp['virtual_servers'][vs_id]['statistics']
for stat in lb_const.LB_STATS_MAP:
lb_stat = lb_const.LB_STATS_MAP[stat]
if lb_stat in vs_stats:
stats[stat] += stats[stat] + vs_stats[lb_stat]
except nsxlib_exc.ManagerError:
msg = _('Failed to retrieve stats from LB service '
'for loadbalancer %(lb)s') % {'lb': lb.id}
raise n_exc.BadRequest(resource='lbaas-lb', msg=msg)
return stats
@log_helpers.log_method_call
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.
orig = kwargs['original_subnet']
updated = kwargs['subnet']
if orig['gateway_ip'] == updated['gateway_ip']:
return

View File

@ -0,0 +1,222 @@
# Copyright 2017 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_lib import exceptions as n_exc
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.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
from vmware_nsx.services.lbaas.nsx_v3 import lb_utils
from vmware_nsxlib.v3 import exceptions as nsxlib_exc
from vmware_nsxlib.v3 import utils
LOG = logging.getLogger(__name__)
class EdgeMemberManager(base_mgr.Nsxv3LoadbalancerBaseManager):
@log_helpers.log_method_call
def __init__(self):
super(EdgeMemberManager, self).__init__()
@log_helpers.log_method_call
def _get_info_from_fip(self, context, fip):
filters = {'floating_ip_address': [fip]}
floating_ips = self.core_plugin.get_floatingips(context,
filters=filters)
if floating_ips:
return (floating_ips[0]['fixed_ip_address'],
floating_ips[0]['router_id'])
else:
msg = (_('Cannot get floating ip %(fip)s provided from '
'neutron db') % {'fip': fip})
raise nsx_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)
lb_name = utils.get_name_and_uuid(router['name'],
router_id)
tags = lb_utils.get_tags(self.core_plugin, router_id,
lb_const.LR_ROUTER_TYPE,
tenant_id, context.project_name)
tags.append({'scope': 'os-lbaas-lb-id', 'tag': lb_id})
attachment = {'target_id': nsx_router_id,
'target_type': 'LogicalRouter'}
lb_service = service_client.create(display_name=lb_name,
tags=tags,
attachment=attachment,
size=lb_size)
return lb_service
@log_helpers.log_method_call
def create(self, context, member):
pool_id = member.pool.id
loadbalancer = member.pool.loadbalancer
if not lb_utils.validate_lb_subnet(context, self.core_plugin,
member.subnet_id):
msg = (_('Cannot add member %(member)s to pool as member subnet '
'%(subnet)s is neither public nor connected to router') %
{'member': member.id, 'subnet': member.subnet_id})
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
pool_members = self.lbv2_driver.plugin.get_pool_members(
context, pool_id)
network = lb_utils.get_network_from_subnet(
context, self.core_plugin, member.subnet_id)
if network.get('router:external'):
router_id, fixed_ip = self._get_info_from_fip(
context, member.address)
else:
router_id = lb_utils.get_router_from_network(
context, self.core_plugin, member.subnet_id)
fixed_ip = member.address
binding = nsx_db.get_nsx_lbaas_pool_binding(context.session,
loadbalancer.id, pool_id)
if binding:
vs_id = binding['lb_vs_id']
lb_pool_id = binding['lb_pool_id']
if len(pool_members) == 1:
nsx_router_id = nsx_db.get_nsx_router_id(context.session,
router_id)
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, loadbalancer.flavor_id)
lb_service = self._create_lb_service(
context, service_client, member.tenant_id,
router_id, nsx_router_id, loadbalancer.id, lb_size)
if lb_service:
lb_service_id = lb_service['id']
nsx_db.add_nsx_lbaas_loadbalancer_binding(
context.session, loadbalancer.id, lb_service_id,
nsx_router_id, loadbalancer.vip_address)
try:
service_client.add_virtual_server_to_service(
lb_service_id, vs_id)
except nsxlib_exc.ManagerError:
self.lbv2_driver.member.failed_completion(context,
member)
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:
msg = (_('Failed to get 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_pool = pool_client.get(lb_pool_id)
old_m = lb_pool.get('members', None)
new_m = [{'display_name': member.name,
'ip_address': fixed_ip,
'port': member.protocol_port,
'weight': member.weight}]
members = (old_m + new_m) if old_m else new_m
pool_client.update_pool_with_members(lb_pool_id, members)
else:
msg = (_('Failed to get pool binding to add member %s') %
member['id'])
raise nsx_exc.NsxPluginException(err_msg=msg)
self.lbv2_driver.member.successful_completion(context, member)
@log_helpers.log_method_call
def update(self, context, old_member, new_member):
try:
self.lbv2_driver.member.successful_completion(
context, new_member)
except nsx_exc.NsxPluginException:
with excutils.save_and_reraise_exception():
self.lbv2_driver.member.failed_completion(
context, new_member)
@log_helpers.log_method_call
def delete(self, context, member):
lb_id = member.pool.loadbalancer_id
pool_id = member.pool.id
service_client = self.core_plugin.nsxlib.load_balancer.service
pool_client = self.core_plugin.nsxlib.load_balancer.pool
pool_members = self.lbv2_driver.plugin.get_pool_members(
context, pool_id)
pool_binding = nsx_db.get_nsx_lbaas_pool_binding(
context.session, lb_id, pool_id)
if pool_binding:
lb_pool_id = pool_binding['lb_pool_id']
lb_vs_id = pool_binding['lb_vs_id']
# If this is the last member of pool, detach virtual server from
# the lb service. If this is the last load balancer for this lb
# service, delete the lb service as well.
if len(pool_members) == 1:
lb_binding = nsx_db.get_nsx_lbaas_loadbalancer_binding(
context.session, lb_id)
if lb_binding:
lb_service_id = lb_binding['lb_service_id']
try:
lb_service = service_client.get(lb_service_id)
vs_list = lb_service.get('virtual_server_ids')
if vs_list and lb_vs_id in vs_list:
vs_list.remove(lb_vs_id)
else:
LOG.error('virtual server id %s is not in the lb '
'service virtual server list %s',
lb_vs_id, vs_list)
service_client.update(lb_service_id,
virtual_server_ids=vs_list)
if not vs_list:
service_client.delete(lb_service_id)
nsx_db.delete_nsx_lbaas_loadbalancer_binding(
context.session, lb_id)
except nsxlib_exc.ManagerError:
self.lbv2_driver.member.failed_completion(context,
member)
msg = _('Failed to remove virtual server from lb '
'service on NSX backend')
raise n_exc.BadRequest(resource='lbaas-member',
msg=msg)
try:
lb_pool = pool_client.get(lb_pool_id)
network = lb_utils.get_network_from_subnet(
context, self.core_plugin, member.subnet_id)
if network.get('router:external'):
fixed_ip, router_id = self._get_info_from_fip(
context, member.address)
else:
fixed_ip = member.address
if 'members' in lb_pool:
m_list = lb_pool['members']
members = [m for m in m_list
if m['ip_address'] != fixed_ip]
pool_client.update_pool_with_members(lb_pool_id, members)
except nsxlib_exc.ManagerError:
self.lbv2_driver.member.failed_completion(context, member)
msg = _('Failed to remove member from pool on NSX backend')
raise n_exc.BadRequest(resource='lbaas-member', msg=msg)
self.lbv2_driver.member.successful_completion(
context, member, delete=True)

View File

@ -0,0 +1,115 @@
# Copyright 2017 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_lib import exceptions as n_exc
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.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
from vmware_nsx.services.lbaas.nsx_v3 import lb_utils
from vmware_nsxlib.v3 import exceptions as nsxlib_exc
from vmware_nsxlib.v3 import utils
LOG = logging.getLogger(__name__)
class EdgePoolManager(base_mgr.Nsxv3LoadbalancerBaseManager):
@log_helpers.log_method_call
def __init__(self):
super(EdgePoolManager, self).__init__()
@log_helpers.log_method_call
def create(self, context, pool):
listener_id = pool.listener.id
lb_id = pool.loadbalancer_id
pool_client = self.core_plugin.nsxlib.load_balancer.pool
vs_client = self.core_plugin.nsxlib.load_balancer.virtual_server
pool_name = utils.get_name_and_uuid(pool.name, pool.id)
tags = lb_utils.get_tags(self.core_plugin, pool.id,
lb_const.LB_POOL_TYPE, pool.tenant_id,
context.project_name)
try:
lb_pool = pool_client.create(display_name=pool_name,
tags=tags,
algorithm=pool.lb_algorithm)
except nsxlib_exc.ManagerError:
self.lbv2_driver.pool.failed_completion(context, pool)
msg = (_('Failed to create pool on NSX backend: %(pool)s') %
{'pool': pool.id})
raise n_exc.BadRequest(resource='lbaas-pool', msg=msg)
binding = nsx_db.get_nsx_lbaas_listener_binding(
context.session, lb_id, listener_id)
if binding:
vs_id = binding['lb_vs_id']
try:
vs_client.update(vs_id, pool_id=lb_pool['id'])
except nsxlib_exc.ManagerError:
with excutils.save_and_reraise_exception():
self.lbv2_driver.pool.failed_completion(context, pool)
LOG.error('Failed to attach pool %s to virtual server %s',
lb_pool['id'], vs_id)
nsx_db.add_nsx_lbaas_pool_binding(
context.session, lb_id, pool.id, lb_pool['id'], vs_id)
else:
msg = (_("Couldn't find binding on the listener: %s") %
listener_id)
raise nsx_exc.NsxPluginException(err_msg=msg)
self.lbv2_driver.pool.successful_completion(context, pool)
@log_helpers.log_method_call
def update(self, context, old_pool, new_pool):
try:
self.lbv2_driver.pool.successful_completion(context, new_pool)
except Exception:
with excutils.save_and_reraise_exception():
self.lbv2_driver.pool.failed_completion(context, new_pool)
@log_helpers.log_method_call
def delete(self, context, pool):
lb_id = pool.loadbalancer_id
pool_client = self.core_plugin.nsxlib.load_balancer.pool
vs_client = self.core_plugin.nsxlib.load_balancer.virtual_server
binding = nsx_db.get_nsx_lbaas_pool_binding(
context.session, lb_id, pool.id)
if binding:
vs_id = binding['lb_vs_id']
lb_pool_id = binding['lb_pool_id']
try:
vs_client.update(vs_id, pool_id='')
except nsxlib_exc.ManagerError:
self.lbv2_driver.pool.failed_completion(context, pool)
msg = _('Failed to remove lb pool %(pool)s from virtual '
'server %(vs)s') % {'pool': lb_pool_id,
'vs': vs_id}
raise n_exc.BadRequest(resource='lbaas-pool', msg=msg)
try:
pool_client.delete(lb_pool_id)
except nsxlib_exc.ManagerError:
self.lbv2_driver.pool.failed_completion(context, pool)
msg = (_('Failed to delete lb pool from nsx: %(pool)s') %
{'pool': lb_pool_id})
raise n_exc.BadRequest(resource='lbaas-pool', msg=msg)
nsx_db.delete_nsx_lbaas_pool_binding(context.session,
lb_id, pool.id)
self.lbv2_driver.pool.successful_completion(
context, pool, delete=True)

View File

@ -0,0 +1,519 @@
# Copyright (c) 2017 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.
import mock
from neutron.tests import base
from neutron_lbaas.services.loadbalancer import data_models as lb_models
from neutron_lib import context
from vmware_nsx.db import db as nsx_db
from vmware_nsx.services.lbaas import base_mgr
from vmware_nsx.services.lbaas.nsx_v3 import lb_driver_v2
from vmware_nsx.services.lbaas.nsx_v3 import lb_utils
LB_VIP = '10.0.0.10'
LB_ROUTER_ID = 'router-x'
LB_ID = 'xxx-xxx'
LB_TENANT_ID = 'yyy-yyy'
LB_SERVICE_ID = 'service-1'
LB_BINDING = {'loadbalancer_id': LB_ID,
'lb_service_id': LB_SERVICE_ID,
'lb_router_id': LB_ROUTER_ID,
'vip_address': LB_VIP}
LB_NETWORK = {'router:external': False,
'id': 'xxxxx',
'name': 'network-1'}
LISTENER_ID = 'listener-x'
APP_PROFILE_ID = 'appp-x'
LB_VS_ID = 'vs-x'
LB_APP_PROFILE = {
"resource_type": "LbHttpProfile",
"description": "my http profile",
"id": APP_PROFILE_ID,
"display_name": "httpprofile1",
"ntlm": False,
"request_header_size": 1024,
"http_redirect_to_https": False,
"idle_timeout": 1800,
"x_forwarded_for": "INSERT",
}
LISTENER_BINDING = {'loadbalancer_id': LB_ID,
'listener_id': LISTENER_ID,
'app_profile_id': APP_PROFILE_ID,
'lb_vs_id': LB_VS_ID}
POOL_ID = 'ppp-qqq'
LB_POOL_ID = 'pool-xx'
LB_POOL = {
"display_name": "httppool1",
"description": "my http pool",
"id": LB_POOL_ID,
"algorithm": "ROUND_ROBIN",
}
POOL_BINDING = {'loadbalancer_id': LB_ID,
'pool_id': POOL_ID,
'lb_pool_id': LB_POOL_ID,
'lb_vs_id': LB_VS_ID}
MEMBER_ID = 'mmm-mmm'
MEMBER_ADDRESS = '10.0.0.200'
LB_MEMBER = {'display_name': 'member-' + MEMBER_ID,
'weight': 1, 'ip_address': MEMBER_ADDRESS, 'port': 80}
LB_POOL_WITH_MEMBER = {
"display_name": "httppool1",
"description": "my http pool",
"id": LB_POOL_ID,
"algorithm": "ROUND_ROBIN",
"members": [
{
"display_name": "http-member1",
"ip_address": MEMBER_ADDRESS,
"port": "80",
"weight": "1",
"admin_state": "ENABLED"
}
]
}
HM_ID = 'hhh-mmm'
LB_MONITOR_ID = 'mmm-ddd'
HM_BINDING = {'loadbalancer_id': LB_ID,
'pool_id': POOL_ID,
'hm_id': HM_ID,
'lb_monitor_id': LB_MONITOR_ID,
'lb_pool_id': LB_POOL_ID}
class BaseTestEdgeLbaasV2(base.BaseTestCase):
def _tested_entity(self):
return None
def setUp(self):
super(BaseTestEdgeLbaasV2, self).setUp()
self.context = context.get_admin_context()
self.edge_driver = lb_driver_v2.EdgeLoadbalancerDriverV2()
self.lbv2_driver = mock.Mock()
self.core_plugin = mock.Mock()
base_mgr.LoadbalancerBaseManager._lbv2_driver = self.lbv2_driver
base_mgr.LoadbalancerBaseManager._core_plugin = self.core_plugin
self._patch_lb_plugin(self.lbv2_driver, self._tested_entity)
self._patch_nsxlib_lb_clients(self.core_plugin)
self.lb = lb_models.LoadBalancer(LB_ID, LB_TENANT_ID, 'lb1', '',
'some-subnet', 'port-id', LB_VIP)
self.listener = lb_models.Listener(LISTENER_ID, LB_TENANT_ID,
'listener1', '', None, LB_ID,
'HTTP', protocol_port=80,
loadbalancer=self.lb)
self.pool = lb_models.Pool(POOL_ID, LB_TENANT_ID, 'pool1', '',
None, 'HTTP', 'ROUND_ROBIN',
loadbalancer_id=LB_ID,
listener=self.listener,
listeners=[self.listener],
loadbalancer=self.lb)
self.member = lb_models.Member(MEMBER_ID, LB_TENANT_ID, POOL_ID,
MEMBER_ADDRESS, 80, 1, pool=self.pool,
name='member-mmm-mmm')
self.hm = lb_models.HealthMonitor(HM_ID, LB_TENANT_ID, 'PING', 3, 3,
1, pool=self.pool, name='hm1')
def tearDown(self):
self._unpatch_lb_plugin(self.lbv2_driver, self._tested_entity)
super(BaseTestEdgeLbaasV2, self).tearDown()
def _patch_lb_plugin(self, lb_plugin, manager):
self.real_manager = getattr(lb_plugin, manager)
lb_manager = mock.patch.object(lb_plugin, manager).start()
mock.patch.object(lb_manager, 'create').start()
mock.patch.object(lb_manager, 'update').start()
mock.patch.object(lb_manager, 'delete').start()
mock.patch.object(lb_manager, 'successful_completion').start()
def _patch_nsxlib_lb_clients(self, core_plugin):
nsxlib = mock.patch.object(core_plugin, 'nsxlib').start()
load_balancer = mock.patch.object(nsxlib, 'load_balancer').start()
self.service_client = mock.patch.object(load_balancer,
'service').start()
self.app_client = mock.patch.object(load_balancer,
'application_profile').start()
self.vs_client = mock.patch.object(load_balancer,
'virtual_server').start()
self.pool_client = mock.patch.object(load_balancer,
'pool').start()
self.monitor_client = mock.patch.object(load_balancer,
'monitor').start()
def _unpatch_lb_plugin(self, lb_plugin, manager):
setattr(lb_plugin, manager, self.real_manager)
class TestEdgeLbaasV2Loadbalancer(BaseTestEdgeLbaasV2):
def setUp(self):
super(TestEdgeLbaasV2Loadbalancer, self).setUp()
@property
def _tested_entity(self):
return 'load_balancer'
def test_create(self):
with mock.patch.object(lb_utils, 'validate_lb_subnet'
) as mock_validate_lb_subnet:
mock_validate_lb_subnet.return_value = True
self.edge_driver.loadbalancer.create(self.context, self.lb)
mock_successful_completion = (
self.lbv2_driver.load_balancer.successful_completion)
mock_successful_completion.assert_called_with(self.context,
self.lb)
def test_update(self):
new_lb = lb_models.LoadBalancer(LB_ID, 'yyy-yyy', 'lb1-new',
'new-description', 'some-subnet',
'port-id', LB_VIP)
self.edge_driver.loadbalancer.update(self.context, self.lb, new_lb)
mock_successful_completion = (
self.lbv2_driver.load_balancer.successful_completion)
mock_successful_completion.assert_called_with(self.context, new_lb)
def test_delete(self):
self.edge_driver.loadbalancer.delete(self.context, self.lb)
mock_successful_completion = (
self.lbv2_driver.load_balancer.successful_completion)
mock_successful_completion.assert_called_with(self.context,
self.lb,
delete=True)
def test_stats(self):
pass
def test_refresh(self):
pass
class TestEdgeLbaasV2Listener(BaseTestEdgeLbaasV2):
def setUp(self):
super(TestEdgeLbaasV2Listener, self).setUp()
@property
def _tested_entity(self):
return 'listener'
def test_create(self):
with mock.patch.object(self.app_client, 'create'
) as mock_create_app_profile, \
mock.patch.object(self.vs_client, 'create'
) as mock_create_virtual_server, \
mock.patch.object(nsx_db, 'add_nsx_lbaas_listener_binding'
) as mock_add_listener_binding:
mock_create_app_profile.return_value = {'id': APP_PROFILE_ID}
mock_create_virtual_server.return_value = {'id': LB_VS_ID}
self.edge_driver.listener.create(self.context, self.listener)
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,
self.listener)
def test_update(self):
new_listener = lb_models.Listener(LISTENER_ID, LB_TENANT_ID,
'listener1-new', 'new-description',
None, LB_ID, protocol_port=8000,
loadbalancer=self.lb)
self.edge_driver.listener.update(self.context, self.listener,
new_listener)
mock_successful_completion = (
self.lbv2_driver.listener.successful_completion)
mock_successful_completion.assert_called_with(self.context,
new_listener)
def test_delete(self):
with mock.patch.object(nsx_db, 'get_nsx_lbaas_listener_binding'
) as mock_get_listener_binding, \
mock.patch.object(self.app_client, 'delete'
) as mock_delete_app_profile, \
mock.patch.object(self.vs_client, 'delete'
) as mock_delete_virtual_server, \
mock.patch.object(nsx_db, 'delete_nsx_lbaas_listener_binding',
) as mock_delete_listener_binding:
mock_get_listener_binding.return_value = LISTENER_BINDING
self.edge_driver.listener.delete(self.context, self.listener)
mock_delete_virtual_server.assert_called_with(LB_VS_ID)
mock_delete_app_profile.assert_called_with(APP_PROFILE_ID)
mock_delete_listener_binding.assert_called_with(
self.context.session, LB_ID, LISTENER_ID)
mock_successful_completion = (
self.lbv2_driver.listener.successful_completion)
mock_successful_completion.assert_called_with(self.context,
self.listener,
delete=True)
class TestEdgeLbaasV2Pool(BaseTestEdgeLbaasV2):
def setUp(self):
super(TestEdgeLbaasV2Pool, self).setUp()
@property
def _tested_entity(self):
return 'pool'
def test_create(self):
with mock.patch.object(self.pool_client, 'create'
) as mock_create_pool, \
mock.patch.object(nsx_db, 'get_nsx_lbaas_listener_binding'
) as mock_get_listener_binding, \
mock.patch.object(self.vs_client, 'update', return_value=None), \
mock.patch.object(nsx_db, 'add_nsx_lbaas_pool_binding'
) as mock_add_pool_binding:
mock_create_pool.return_value = {'id': LB_POOL_ID}
mock_get_listener_binding.return_value = LISTENER_BINDING
self.edge_driver.pool.create(self.context, self.pool)
mock_add_pool_binding.assert_called_with(
self.context.session, LB_ID, POOL_ID, LB_POOL_ID, LB_VS_ID)
mock_successful_completion = (
self.lbv2_driver.pool.successful_completion)
mock_successful_completion.assert_called_with(self.context,
self.pool)
def test_update(self):
new_pool = lb_models.Pool(POOL_ID, LB_TENANT_ID, 'pool-name', '',
None, 'HTTP', 'LEAST_CONNECTIONS',
listener=self.listener)
self.edge_driver.pool.update(self.context, self.pool, new_pool)
mock_successful_completion = (
self.lbv2_driver.pool.successful_completion)
mock_successful_completion.assert_called_with(self.context, new_pool)
def test_delete(self):
with mock.patch.object(nsx_db, 'get_nsx_lbaas_pool_binding'
) as mock_get_pool_binding, \
mock.patch.object(self.vs_client, 'update', return_value=None
) as mock_update_virtual_server, \
mock.patch.object(self.pool_client, 'delete'
) as mock_delete_pool, \
mock.patch.object(nsx_db, 'delete_nsx_lbaas_pool_binding'
) as mock_delete_pool_binding:
mock_get_pool_binding.return_value = POOL_BINDING
self.edge_driver.pool.delete(self.context, self.pool)
mock_update_virtual_server.assert_called_with(LB_VS_ID,
pool_id='')
mock_delete_pool.assert_called_with(LB_POOL_ID)
mock_delete_pool_binding.assert_called_with(
self.context.session, LB_ID, POOL_ID)
mock_successful_completion = (
self.lbv2_driver.pool.successful_completion)
mock_successful_completion.assert_called_with(self.context,
self.pool,
delete=True)
class TestEdgeLbaasV2Member(BaseTestEdgeLbaasV2):
def setUp(self):
super(TestEdgeLbaasV2Member, self).setUp()
@property
def _tested_entity(self):
return 'member'
def test_create(self):
with mock.patch.object(lb_utils, 'validate_lb_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_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(nsx_db, 'add_nsx_lbaas_loadbalancer_binding'
) as mock_add_loadbalancer_bidning, \
mock.patch.object(self.service_client,
'add_virtual_server_to_service'
) 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_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_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)
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])
mock_successful_completion = (
self.lbv2_driver.member.successful_completion)
mock_successful_completion.assert_called_with(self.context,
self.member)
def test_update(self):
new_member = lb_models.Member(MEMBER_ID, LB_TENANT_ID, POOL_ID,
MEMBER_ADDRESS, 80, 1, pool=self.pool,
name='member-nnn-nnn')
self.edge_driver.member.update(self.context, self.pool, new_member)
mock_successful_completion = (
self.lbv2_driver.member.successful_completion)
mock_successful_completion.assert_called_with(self.context, new_member)
def test_delete(self):
with mock.patch.object(self.lbv2_driver.plugin, 'get_pool_members'
) as mock_get_pool_members, \
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_loadbalancer_binding, \
mock.patch.object(self.service_client, 'get'
) as mock_get_lb_service, \
mock.patch.object(self.service_client, 'update'
) as mock_update_lb_service, \
mock.patch.object(self.service_client, 'delete'
) as mock_delete_lb_service, \
mock.patch.object(nsx_db, 'delete_nsx_lbaas_loadbalancer_binding'
) as mock_delete_loadbalancer_binding, \
mock.patch.object(self.pool_client, 'get'
) as mock_get_pool, \
mock.patch.object(lb_utils, 'get_network_from_subnet'
) as mock_get_network_from_subnet, \
mock.patch.object(self.pool_client, 'update_pool_with_members'
) as mock_update_pool_with_members:
mock_get_pool_members.return_value = [self.member]
mock_get_pool_binding.return_value = POOL_BINDING
mock_get_loadbalancer_binding.return_value = LB_BINDING
mock_get_lb_service.return_value = {
'id': LB_SERVICE_ID,
'virtual_server_ids': [LB_VS_ID]}
mock_get_pool.return_value = LB_POOL_WITH_MEMBER
mock_get_network_from_subnet.return_value = LB_NETWORK
self.edge_driver.member.delete(self.context, self.member)
mock_update_lb_service.assert_called_with(LB_SERVICE_ID,
virtual_server_ids=[])
mock_delete_lb_service.assert_called_with(LB_SERVICE_ID)
mock_delete_loadbalancer_binding.assert_called_with(
self.context.session, LB_ID)
mock_update_pool_with_members.assert_called_with(LB_POOL_ID, [])
mock_successful_completion = (
self.lbv2_driver.member.successful_completion)
mock_successful_completion.assert_called_with(self.context,
self.member,
delete=True)
class TestEdgeLbaasV2HealthMonitor(BaseTestEdgeLbaasV2):
def setUp(self):
super(TestEdgeLbaasV2HealthMonitor, self).setUp()
@property
def _tested_entity(self):
return 'health_monitor'
def test_create(self):
with mock.patch.object(self.monitor_client, 'create'
) as mock_create_monitor, \
mock.patch.object(nsx_db, 'get_nsx_lbaas_pool_binding'
) as mock_get_pool_binding, \
mock.patch.object(self.pool_client, 'add_monitor_to_pool'
) as mock_add_monitor_to_pool, \
mock.patch.object(nsx_db, 'add_nsx_lbaas_monitor_binding'
) as mock_add_monitor_binding:
mock_create_monitor.return_value = {'id': LB_MONITOR_ID}
mock_get_pool_binding.return_value = POOL_BINDING
self.edge_driver.healthmonitor.create(self.context, self.hm)
mock_add_monitor_to_pool.assert_called_with(LB_POOL_ID,
LB_MONITOR_ID)
mock_add_monitor_binding.assert_called_with(
self.context.session, LB_ID, POOL_ID, HM_ID, LB_MONITOR_ID,
LB_POOL_ID)
mock_successful_completion = (
self.lbv2_driver.health_monitor.successful_completion)
mock_successful_completion.assert_called_with(self.context,
self.hm)
def test_update(self):
new_hm = lb_models.HealthMonitor(HM_ID, LB_TENANT_ID, 'PING', 3, 3,
3, pool=self.pool)
self.edge_driver.healthmonitor.update(self.context, self.pool, new_hm)
mock_successful_completion = (
self.lbv2_driver.health_monitor.successful_completion)
mock_successful_completion.assert_called_with(self.context, new_hm)
def test_delete(self):
with mock.patch.object(nsx_db, 'get_nsx_lbaas_monitor_binding'
) as mock_get_monitor_binding, \
mock.patch.object(self.pool_client, 'remove_monitor_from_pool'
) as mock_remove_monitor_from_pool, \
mock.patch.object(self.monitor_client, 'delete'
) as mock_delete_monitor, \
mock.patch.object(nsx_db, 'delete_nsx_lbaas_monitor_binding'
) as mock_delete_monitor_binding:
mock_get_monitor_binding.return_value = HM_BINDING
self.edge_driver.healthmonitor.delete(self.context, self.hm)
mock_remove_monitor_from_pool.assert_called_with(LB_POOL_ID,
LB_MONITOR_ID)
mock_delete_monitor.assert_called_with(LB_MONITOR_ID)
mock_delete_monitor_binding.assert_called_with(
self.context.session, LB_ID, POOL_ID, HM_ID)
mock_successful_completion = (
self.lbv2_driver.health_monitor.successful_completion)
mock_successful_completion.assert_called_with(self.context,
self.hm,
delete=True)