]> review.fuel-infra Code Review - openstack-build/neutron-build.git/commitdiff
Support advanced NVP LBaaS Service
authorlinb <linb@vmware.com>
Tue, 20 Aug 2013 05:35:10 +0000 (13:35 +0800)
committerSalvatore Orlando <salv.orlando@gmail.com>
Tue, 17 Sep 2013 18:07:28 +0000 (11:07 -0700)
The patch adds NVP advanced LBaaS service support for NVP with
VCNS:
        * NVP LBaaS is an advanced Service of NVP depending on NVP
          advanced service router
            - Once an advanced router id created, one corresponding
              vshield edge will be deployed, and then we can configure
              LB service On the vshield edge
        * NVP LBaaS service plugin still uses LBaaS DB service logic,
          while finally calling vShield Edge to support FWaaS service
            - When creating VIP object, service attaches the object to
              the advanced router with routedserviceinsertion service.
              Then before pushing VIP VCNS call, the server would first
              pushing associated POOL VCNS call and associated Monitor
              VCNS call to vShield Edge. Deleting VIP is opposite
              operation
            - Refering to CUD operation of other objects, service would
              first find the associated VIP object and then find the edge bound to
              the router which vip inserted. Then service would push corresponding
              VCNS call to Vshield Edge
        * on driver part, the driver will first convert the object
          to VSM known object input, and then send a synchronous JSON
          calling to VSM, and receive the result

Implements: blueprint nvp-lbaas-plugin
Change-Id: Iec41f2dc103daddf3bed4d09c147df3865b3dccd

17 files changed:
neutron/db/migration/alembic_migrations/versions/3d6fae8b70b0_nvp_lbaas_plugin.py [new file with mode: 0644]
neutron/plugins/nicira/NeutronServicePlugin.py
neutron/plugins/nicira/common/exceptions.py
neutron/plugins/nicira/dbexts/vcns_db.py
neutron/plugins/nicira/dbexts/vcns_models.py
neutron/plugins/nicira/vshield/__init__.py
neutron/plugins/nicira/vshield/common/__init__.py
neutron/plugins/nicira/vshield/edge_appliance_driver.py
neutron/plugins/nicira/vshield/edge_loadbalancer_driver.py [new file with mode: 0644]
neutron/plugins/nicira/vshield/vcns.py
neutron/plugins/nicira/vshield/vcns_driver.py
neutron/tests/unit/db/loadbalancer/test_db_loadbalancer.py
neutron/tests/unit/nicira/test_edge_router.py
neutron/tests/unit/nicira/test_lbaas_plugin.py [new file with mode: 0644]
neutron/tests/unit/nicira/test_nicira_plugin.py
neutron/tests/unit/nicira/vshield/fake_vcns.py
neutron/tests/unit/nicira/vshield/test_loadbalancer_driver.py [new file with mode: 0644]

diff --git a/neutron/db/migration/alembic_migrations/versions/3d6fae8b70b0_nvp_lbaas_plugin.py b/neutron/db/migration/alembic_migrations/versions/3d6fae8b70b0_nvp_lbaas_plugin.py
new file mode 100644 (file)
index 0000000..cc4807f
--- /dev/null
@@ -0,0 +1,82 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# Copyright 2013 OpenStack Foundation
+#
+#    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.
+#
+
+"""nvp lbaas plugin
+
+Revision ID: 3d6fae8b70b0
+Revises: 3ed8f075e38a
+Create Date: 2013-09-13 19:34:41.522665
+
+"""
+
+# revision identifiers, used by Alembic.
+revision = '3d6fae8b70b0'
+down_revision = '3ed8f075e38a'
+
+# Change to ['*'] if this migration applies to all plugins
+
+migration_for_plugins = [
+    'neutron.plugins.nicira.NeutronServicePlugin.NvpAdvancedPlugin'
+]
+
+from alembic import op
+import sqlalchemy as sa
+
+from neutron.db import migration
+
+
+def upgrade(active_plugins=None, options=None):
+    if not migration.should_run(active_plugins, migration_for_plugins):
+        return
+
+    op.create_table(
+        'vcns_edge_pool_bindings',
+        sa.Column('pool_id', sa.String(length=36), nullable=False),
+        sa.Column('edge_id', sa.String(length=36), nullable=False),
+        sa.Column('pool_vseid', sa.String(length=36), nullable=True),
+        sa.ForeignKeyConstraint(['pool_id'], ['pools.id'],
+                                ondelete='CASCADE'),
+        sa.PrimaryKeyConstraint('pool_id', 'edge_id')
+    )
+    op.create_table(
+        'vcns_edge_monitor_bindings',
+        sa.Column('monitor_id', sa.String(length=36), nullable=False),
+        sa.Column('edge_id', sa.String(length=36), nullable=False),
+        sa.Column('monitor_vseid', sa.String(length=36), nullable=True),
+        sa.ForeignKeyConstraint(['monitor_id'], ['healthmonitors.id'],
+                                ondelete='CASCADE'),
+        sa.PrimaryKeyConstraint('monitor_id', 'edge_id')
+    )
+    op.create_table(
+        'vcns_edge_vip_bindings',
+        sa.Column('vip_id', sa.String(length=36), nullable=False),
+        sa.Column('edge_id', sa.String(length=36), nullable=True),
+        sa.Column('vip_vseid', sa.String(length=36), nullable=True),
+        sa.Column('app_profileid', sa.String(length=36), nullable=True),
+        sa.ForeignKeyConstraint(['vip_id'], ['vips.id'],
+                                ondelete='CASCADE'),
+        sa.PrimaryKeyConstraint('vip_id')
+    )
+
+
+def downgrade(active_plugins=None, options=None):
+    if not migration.should_run(active_plugins, migration_for_plugins):
+        return
+
+    op.drop_table('vcns_edge_vip_bindings')
+    op.drop_table('vcns_edge_monitor_bindings')
+    op.drop_table('vcns_edge_pool_bindings')
index 7161341ed5b90cacf552cb7505f10a30e60c069d..576e047303fbc3d3f251355fcd72bf0d818a26a7 100644 (file)
@@ -22,8 +22,10 @@ from oslo.config import cfg
 from neutron.common import exceptions as q_exc
 from neutron.db.firewall import firewall_db
 from neutron.db import l3_db
+from neutron.db.loadbalancer import loadbalancer_db
 from neutron.db import routedserviceinsertion_db as rsi_db
 from neutron.extensions import firewall as fw_ext
+from neutron.openstack.common import excutils
 from neutron.openstack.common import log as logging
 from neutron.plugins.common import constants as service_constants
 from neutron.plugins.nicira.common import config  # noqa
@@ -39,6 +41,7 @@ from neutron.plugins.nicira.vshield.common import (
     constants as vcns_const)
 from neutron.plugins.nicira.vshield.common.constants import RouterStatus
 from neutron.plugins.nicira.vshield.common import exceptions
+from neutron.plugins.nicira.vshield.tasks.constants import TaskState
 from neutron.plugins.nicira.vshield.tasks.constants import TaskStatus
 from neutron.plugins.nicira.vshield import vcns_driver
 from sqlalchemy.orm import exc as sa_exc
@@ -73,12 +76,15 @@ class NvpAdvancedPlugin(sr_db.ServiceRouter_mixin,
                         NeutronPlugin.NvpPluginV2,
                         rsi_db.RoutedServiceInsertionDbMixin,
                         firewall_db.Firewall_db_mixin,
+                        loadbalancer_db.LoadBalancerPluginDb
                         ):
+
     supported_extension_aliases = (
         NeutronPlugin.NvpPluginV2.supported_extension_aliases + [
             "service-router",
             "routed-service-insertion",
-            "fwaas"
+            "fwaas",
+            "lbaas"
         ])
 
     def __init__(self):
@@ -257,7 +263,7 @@ class NvpAdvancedPlugin(sr_db.ServiceRouter_mixin,
                                           binding['edge_id'],
                                           snat, dnat)
 
-    def _update_interface(self, context, router):
+    def _update_interface(self, context, router, sync=False):
         addr, mask, nexthop = self._get_external_attachment_info(
             context, router)
 
@@ -267,14 +273,20 @@ class NvpAdvancedPlugin(sr_db.ServiceRouter_mixin,
         for fip in fip_db:
             if fip.fixed_port_id:
                 secondary.append(fip.floating_ip_address)
+        #Add all vip addresses bound on the router
+        vip_addrs = self._get_all_vip_addrs_by_router_id(context,
+                                                         router['id'])
+        secondary.extend(vip_addrs)
 
         binding = vcns_db.get_vcns_router_binding(context.session,
                                                   router['id'])
-        self.vcns_driver.update_interface(
+        task = self.vcns_driver.update_interface(
             router['id'], binding['edge_id'],
             vcns_const.EXTERNAL_VNIC_INDEX,
             self.vcns_driver.external_network,
             addr, mask, secondary=secondary)
+        if sync:
+            task.wait(TaskState.RESULT)
 
     def _update_router_gw_info(self, context, router_id, info):
         if not self._is_advanced_service_router(context, router_id):
@@ -1006,6 +1018,483 @@ class NvpAdvancedPlugin(sr_db.ServiceRouter_mixin,
                 context, fwr['id'], edge_id)
         return fwp
 
+    #
+    # LBAAS service plugin implementation
+    #
+    def _get_edge_id_by_vip_id(self, context, vip_id):
+        try:
+            router_binding = self._get_resource_router_id_bindings(
+                context, loadbalancer_db.Vip, resource_ids=[vip_id])[0]
+        except Exception:
+            with excutils.save_and_reraise_exception():
+                LOG.exception(_("Failed to find the edge with "
+                                "vip_id: %s"), vip_id)
+        service_binding = vcns_db.get_vcns_router_binding(
+            context.session, router_binding.router_id)
+        return service_binding.edge_id
+
+    def _get_all_vip_addrs_by_router_id(
+        self, context, router_id):
+        vip_bindings = self._get_resource_router_id_bindings(
+            context, loadbalancer_db.Vip, router_ids=[router_id])
+        vip_addrs = []
+        for vip_binding in vip_bindings:
+            vip = self.get_vip(context, vip_binding.resource_id)
+            vip_addrs.append(vip.get('address'))
+        return vip_addrs
+
+    def _add_router_service_insertion_binding(self, context, resource_id,
+                                              router_id,
+                                              model):
+        res = {
+            'id': resource_id,
+            'router_id': router_id
+        }
+        self._process_create_resource_router_id(context, res,
+                                                model)
+
+    def _resource_set_status(self, context, model, id, status, obj=None,
+                             pool_id=None):
+        with context.session.begin(subtransactions=True):
+            try:
+                qry = context.session.query(model)
+                if issubclass(model, loadbalancer_db.PoolMonitorAssociation):
+                    res = qry.filter_by(monitor_id=id,
+                                        pool_id=pool_id).one()
+                else:
+                    res = qry.filter_by(id=id).one()
+                if status == service_constants.PENDING_UPDATE and (
+                    res.get('status') == service_constants.PENDING_DELETE):
+                    msg = (_("Operation can't be performed, Since resource "
+                             "%(model)s : %(id)s is in DELETEing status!") %
+                           {'model': model,
+                            'id': id})
+                    LOG.error(msg)
+                    raise nvp_exc.NvpServicePluginException(err_msg=msg)
+                else:
+                    res.status = status
+            except sa_exc.NoResultFound:
+                msg = (_("Resource %(model)s : %(id)s not found!") %
+                       {'model': model,
+                        'id': id})
+                LOG.exception(msg)
+                raise nvp_exc.NvpServicePluginException(err_msg=msg)
+            if obj:
+                obj['status'] = status
+
+    def _vcns_create_pool_and_monitors(self, context, pool_id, **kwargs):
+        pool = self.get_pool(context, pool_id)
+        edge_id = kwargs.get('edge_id')
+        if not edge_id:
+            edge_id = self._get_edge_id_by_vip_id(
+                context, pool['vip_id'])
+        #Check wheter the pool is already created on the router
+        #in case of future's M:N relation between Pool and Vip
+
+        #Check associated HealthMonitors and then create them
+        for monitor_id in pool.get('health_monitors'):
+            hm = self.get_health_monitor(context, monitor_id)
+            try:
+                self.vcns_driver.create_health_monitor(
+                    context, edge_id, hm)
+            except Exception:
+                with excutils.save_and_reraise_exception():
+                    LOG.exception(_("Failed to create healthmonitor "
+                                    "associated with pool id: %s!") % pool_id)
+                    for monitor_ide in pool.get('health_monitors'):
+                        if monitor_ide == monitor_id:
+                            break
+                        self.vcns_driver.delete_health_monitor(
+                            context, monitor_ide, edge_id)
+        #Create the pool on the edge
+        members = [
+            super(NvpAdvancedPlugin, self).get_member(
+                context, member_id)
+            for member_id in pool.get('members')
+        ]
+        try:
+            self.vcns_driver.create_pool(context, edge_id, pool, members)
+        except Exception:
+            with excutils.save_and_reraise_exception():
+                LOG.exception(_("Failed to create pool on vshield edge"))
+                self.vcns_driver.delete_pool(
+                    context, pool_id, edge_id)
+                for monitor_id in pool.get('health_monitors'):
+                    self.vcns_driver.delete_health_monitor(
+                        context, monitor_id, edge_id)
+
+    def _vcns_update_pool(self, context, pool, **kwargs):
+        edge_id = self._get_edge_id_by_vip_id(context, pool['vip_id'])
+        members = kwargs.get('members')
+        if not members:
+            members = [
+                super(NvpAdvancedPlugin, self).get_member(
+                    context, member_id)
+                for member_id in pool.get('members')
+            ]
+        self.vcns_driver.update_pool(context, edge_id, pool, members)
+
+    def create_vip(self, context, vip):
+        LOG.debug(_("create_vip() called"))
+        router_id = vip['vip'].get(vcns_const.ROUTER_ID)
+        if not router_id:
+            msg = _("router_id is not provided!")
+            LOG.error(msg)
+            raise q_exc.BadRequest(resource='router', msg=msg)
+
+        if not self._is_advanced_service_router(context, router_id):
+            msg = _("router_id: %s is not an advanced router!") % router_id
+            LOG.error(msg)
+            raise nvp_exc.NvpServicePluginException(err_msg=msg)
+
+        #Check whether the vip port is an external port
+        subnet_id = vip['vip']['subnet_id']
+        network_id = self.get_subnet(context, subnet_id)['network_id']
+        ext_net = self._get_network(context, network_id)
+        if not ext_net.external:
+            msg = (_("Network '%s' is not a valid external "
+                     "network") % network_id)
+            raise nvp_exc.NvpServicePluginException(err_msg=msg)
+
+        v = super(NvpAdvancedPlugin, self).create_vip(context, vip)
+        #Get edge_id for the resource
+        router_binding = vcns_db.get_vcns_router_binding(
+            context.session,
+            router_id)
+        edge_id = router_binding.edge_id
+        #Add vip_router binding
+        self._add_router_service_insertion_binding(context, v['id'],
+                                                   router_id,
+                                                   loadbalancer_db.Vip)
+        #Create the vip port on vShield Edge
+        router = self._get_router(context, router_id)
+        self._update_interface(context, router, sync=True)
+        #Create the vip and associated pool/monitor on the corresponding edge
+        try:
+            self._vcns_create_pool_and_monitors(
+                context, v['pool_id'], edge_id=edge_id)
+            self.vcns_driver.create_vip(context, edge_id, v)
+        except Exception:
+            with excutils.save_and_reraise_exception():
+                LOG.exception(_("Failed to create vip!"))
+                self._delete_resource_router_id_binding(
+                    context, v['id'], loadbalancer_db.Vip)
+                super(NvpAdvancedPlugin, self).delete_vip(context, v['id'])
+        self._resource_set_status(context, loadbalancer_db.Vip,
+                                  v['id'], service_constants.ACTIVE, v)
+
+        return v
+
+    def update_vip(self, context, id, vip):
+        edge_id = self._get_edge_id_by_vip_id(context, id)
+        old_vip = self.get_vip(context, id)
+        vip['vip']['status'] = service_constants.PENDING_UPDATE
+        v = super(NvpAdvancedPlugin, self).update_vip(context, id, vip)
+        if old_vip['pool_id'] != v['pool_id']:
+            self.vcns_driver.delete_vip(context, id)
+            #Delete old pool/monitor on the edge
+            #TODO(linb): Factor out procedure for removing pool and health
+            #separate method
+            old_pool = self.get_pool(context, old_vip['pool_id'])
+            self.vcns_driver.delete_pool(
+                context, old_vip['pool_id'], edge_id)
+            for monitor_id in old_pool.get('health_monitors'):
+                self.vcns_driver.delete_health_monitor(
+                    context, monitor_id, edge_id)
+            #Create new pool/monitor object on the edge
+            #TODO(linb): add exception handle if error
+            self._vcns_create_pool_and_monitors(
+                context, v['pool_id'], edge_id=edge_id)
+            self.vcns_driver.create_vip(context, edge_id, v)
+            return v
+        try:
+            self.vcns_driver.update_vip(context, v)
+        except Exception:
+            with excutils.save_and_reraise_exception():
+                LOG.exception(_("Failed to update vip with id: %s!"), id)
+                self._resource_set_status(context, loadbalancer_db.Vip,
+                                          id, service_constants.ERROR, v)
+
+        self._resource_set_status(context, loadbalancer_db.Vip,
+                                  v['id'], service_constants.ACTIVE, v)
+        return v
+
+    def delete_vip(self, context, id):
+        v = self.get_vip(context, id)
+        self._resource_set_status(
+            context, loadbalancer_db.Vip,
+            id, service_constants.PENDING_DELETE)
+        try:
+            self.vcns_driver.delete_vip(context, id)
+        except Exception:
+            with excutils.save_and_reraise_exception():
+                LOG.exception(_("Failed to delete vip with id: %s!"), id)
+                self._resource_set_status(context, loadbalancer_db.Vip,
+                                          id, service_constants.ERROR)
+        edge_id = self._get_edge_id_by_vip_id(context, id)
+        #Check associated HealthMonitors and then delete them
+        pool = self.get_pool(context, v['pool_id'])
+        self.vcns_driver.delete_pool(context, v['pool_id'], edge_id)
+        for monitor_id in pool.get('health_monitors'):
+            #TODO(linb): do exception handle if error
+            self.vcns_driver.delete_health_monitor(
+                context, monitor_id, edge_id)
+
+        router_binding = self._get_resource_router_id_binding(
+            context, loadbalancer_db.Vip, resource_id=id)
+        router = self._get_router(context, router_binding.router_id)
+        self._delete_resource_router_id_binding(
+            context, id, loadbalancer_db.Vip)
+        super(NvpAdvancedPlugin, self).delete_vip(context, id)
+        self._update_interface(context, router, sync=True)
+
+    def update_pool(self, context, id, pool):
+        pool['pool']['status'] = service_constants.PENDING_UPDATE
+        p = super(NvpAdvancedPlugin, self).update_pool(context, id, pool)
+        #Check whether the pool is already associated with the vip
+        if not p.get('vip_id'):
+            self._resource_set_status(context, loadbalancer_db.Pool,
+                                      p['id'], service_constants.ACTIVE, p)
+            return p
+        try:
+            self._vcns_update_pool(context, p)
+        except Exception:
+            with excutils.save_and_reraise_exception():
+                LOG.exception(_("Failed to update pool with id: %s!"), id)
+                self._resource_set_status(context, loadbalancer_db.Pool,
+                                          p['id'], service_constants.ERROR, p)
+        self._resource_set_status(context, loadbalancer_db.Pool,
+                                  p['id'], service_constants.ACTIVE, p)
+        return p
+
+    def create_member(self, context, member):
+        m = super(NvpAdvancedPlugin, self).create_member(context, member)
+        pool_id = m.get('pool_id')
+        pool = self.get_pool(context, pool_id)
+        if not pool.get('vip_id'):
+            self._resource_set_status(context, loadbalancer_db.Member,
+                                      m['id'], service_constants.ACTIVE, m)
+            return m
+        self._resource_set_status(context, loadbalancer_db.Pool,
+                                  pool_id,
+                                  service_constants.PENDING_UPDATE)
+        try:
+            self._vcns_update_pool(context, pool)
+        except Exception:
+            with excutils.save_and_reraise_exception():
+                LOG.exception(_("Failed to update pool with the member"))
+                super(NvpAdvancedPlugin, self).delete_member(context, m['id'])
+
+        self._resource_set_status(context, loadbalancer_db.Pool,
+                                  pool_id, service_constants.ACTIVE)
+        self._resource_set_status(context, loadbalancer_db.Member,
+                                  m['id'], service_constants.ACTIVE, m)
+        return m
+
+    def update_member(self, context, id, member):
+        member['member']['status'] = service_constants.PENDING_UPDATE
+        old_member = self.get_member(context, id)
+        m = super(NvpAdvancedPlugin, self).update_member(
+            context, id, member)
+
+        if m['pool_id'] != old_member['pool_id']:
+            old_pool_id = old_member['pool_id']
+            old_pool = self.get_pool(context, old_pool_id)
+            if old_pool.get('vip_id'):
+                self._resource_set_status(
+                    context, loadbalancer_db.Pool,
+                    old_pool_id, service_constants.PENDING_UPDATE)
+                try:
+                    self._vcns_update_pool(context, old_pool)
+                except Exception:
+                    with excutils.save_and_reraise_exception():
+                        LOG.exception(_("Failed to update old pool "
+                                        "with the member"))
+                        super(NvpAdvancedPlugin, self).delete_member(
+                            context, m['id'])
+                self._resource_set_status(
+                    context, loadbalancer_db.Pool,
+                    old_pool_id, service_constants.ACTIVE)
+
+        pool_id = m['pool_id']
+        pool = self.get_pool(context, pool_id)
+        if not pool.get('vip_id'):
+            self._resource_set_status(context, loadbalancer_db.Member,
+                                      m['id'], service_constants.ACTIVE, m)
+            return m
+        self._resource_set_status(context, loadbalancer_db.Pool,
+                                  pool_id,
+                                  service_constants.PENDING_UPDATE)
+        try:
+            self._vcns_update_pool(context, pool)
+        except Exception:
+            with excutils.save_and_reraise_exception():
+                LOG.exception(_("Failed to update pool with the member"))
+                super(NvpAdvancedPlugin, self).delete_member(
+                    context, m['id'])
+
+        self._resource_set_status(context, loadbalancer_db.Pool,
+                                  pool_id, service_constants.ACTIVE)
+        self._resource_set_status(context, loadbalancer_db.Member,
+                                  m['id'], service_constants.ACTIVE, m)
+        return m
+
+    def delete_member(self, context, id):
+        m = self.get_member(context, id)
+        super(NvpAdvancedPlugin, self).delete_member(context, id)
+        pool_id = m['pool_id']
+        pool = self.get_pool(context, pool_id)
+        if not pool.get('vip_id'):
+            return
+        self._resource_set_status(context, loadbalancer_db.Pool,
+                                  pool_id, service_constants.PENDING_UPDATE)
+        try:
+            self._vcns_update_pool(context, pool)
+        except Exception:
+            with excutils.save_and_reraise_exception():
+                LOG.exception(_("Failed to update pool with the member"))
+        self._resource_set_status(context, loadbalancer_db.Pool,
+                                  pool_id, service_constants.ACTIVE)
+
+    def update_health_monitor(self, context, id, health_monitor):
+        old_hm = super(NvpAdvancedPlugin, self).get_health_monitor(
+            context, id)
+        hm = super(NvpAdvancedPlugin, self).update_health_monitor(
+            context, id, health_monitor)
+        for hm_pool in hm.get('pools'):
+            pool_id = hm_pool['pool_id']
+            pool = self.get_pool(context, pool_id)
+            if pool.get('vip_id'):
+                edge_id = self._get_edge_id_by_vip_id(
+                    context, pool['vip_id'])
+                try:
+                    self.vcns_driver.update_health_monitor(
+                        context, edge_id, old_hm, hm)
+                except Exception:
+                    with excutils.save_and_reraise_exception():
+                        LOG.exception(_("Failed to update monitor "
+                                        "with id: %s!"), id)
+        return hm
+
+    def delete_health_monitor(self, context, id):
+        with context.session.begin(subtransactions=True):
+            qry = context.session.query(
+                loadbalancer_db.PoolMonitorAssociation
+            ).filter_by(monitor_id=id)
+            for assoc in qry:
+                pool_id = assoc['pool_id']
+                super(NvpAdvancedPlugin,
+                      self).delete_pool_health_monitor(context,
+                                                       id,
+                                                       pool_id)
+                pool = self.get_pool(context, pool_id)
+                if not pool.get('vip_id'):
+                    continue
+                edge_id = self._get_edge_id_by_vip_id(
+                    context, pool['vip_id'])
+                self._resource_set_status(
+                    context, loadbalancer_db.Pool,
+                    pool_id, service_constants.PENDING_UPDATE)
+                try:
+                    self._vcns_update_pool(context, pool)
+                except Exception:
+                    with excutils.save_and_reraise_exception():
+                        LOG.exception(_("Failed to update pool with monitor!"))
+                self._resource_set_status(
+                    context, loadbalancer_db.Pool,
+                    pool_id, service_constants.ACTIVE)
+                try:
+                    self.vcns_driver.delete_health_monitor(
+                        context, id, edge_id)
+                except Exception:
+                    with excutils.save_and_reraise_exception():
+                        LOG.exception(_("Failed to delete monitor "
+                                        "with id: %s!"), id)
+                        super(NvpAdvancedPlugin,
+                              self).delete_health_monitor(context, id)
+                        self._delete_resource_router_id_binding(
+                            context, id, loadbalancer_db.HealthMonitor)
+
+        super(NvpAdvancedPlugin, self).delete_health_monitor(context, id)
+        self._delete_resource_router_id_binding(
+            context, id, loadbalancer_db.HealthMonitor)
+
+    def create_pool_health_monitor(self, context,
+                                   health_monitor, pool_id):
+        monitor_id = health_monitor['health_monitor']['id']
+        pool = self.get_pool(context, pool_id)
+        monitors = pool.get('health_monitors')
+        if len(monitors) > 0:
+            msg = _("Vcns right now can only support "
+                    "one monitor per pool")
+            LOG.error(msg)
+            raise nvp_exc.NvpServicePluginException(err_msg=msg)
+        #Check whether the pool is already associated with the vip
+        if not pool.get('vip_id'):
+            res = super(NvpAdvancedPlugin,
+                        self).create_pool_health_monitor(context,
+                                                         health_monitor,
+                                                         pool_id)
+            return res
+        #Get the edge_id
+        edge_id = self._get_edge_id_by_vip_id(context, pool['vip_id'])
+        res = super(NvpAdvancedPlugin,
+                    self).create_pool_health_monitor(context,
+                                                     health_monitor,
+                                                     pool_id)
+        monitor = self.get_health_monitor(context, monitor_id)
+        #TODO(linb)Add Exception handle if error
+        self.vcns_driver.create_health_monitor(context, edge_id, monitor)
+        #Get updated pool
+        pool['health_monitors'].append(monitor['id'])
+        self._resource_set_status(
+            context, loadbalancer_db.Pool,
+            pool_id, service_constants.PENDING_UPDATE)
+        try:
+            self._vcns_update_pool(context, pool)
+        except Exception:
+            with excutils.save_and_reraise_exception():
+                LOG.exception(_("Failed to associate monitor with pool!"))
+                self._resource_set_status(
+                    context, loadbalancer_db.Pool,
+                    pool_id, service_constants.ERROR)
+                super(NvpAdvancedPlugin, self).delete_pool_health_monitor(
+                    context, monitor_id, pool_id)
+        self._resource_set_status(
+            context, loadbalancer_db.Pool,
+            pool_id, service_constants.ACTIVE)
+        self._resource_set_status(
+            context, loadbalancer_db.PoolMonitorAssociation,
+            monitor_id, service_constants.ACTIVE, res,
+            pool_id=pool_id)
+        return res
+
+    def delete_pool_health_monitor(self, context, id, pool_id):
+        super(NvpAdvancedPlugin, self).delete_pool_health_monitor(
+            context, id, pool_id)
+        pool = self.get_pool(context, pool_id)
+        #Check whether the pool is already associated with the vip
+        if pool.get('vip_id'):
+            #Delete the monitor on vshield edge
+            edge_id = self._get_edge_id_by_vip_id(context, pool['vip_id'])
+            self._resource_set_status(
+                context, loadbalancer_db.Pool,
+                pool_id, service_constants.PENDING_UPDATE)
+            try:
+                self._vcns_update_pool(context, pool)
+            except Exception:
+                with excutils.save_and_reraise_exception():
+                    LOG.exception(
+                        _("Failed to update pool with pool_monitor!"))
+                    self._resource_set_status(
+                        context, loadbalancer_db.Pool,
+                        pool_id, service_constants.ERROR)
+            #TODO(linb): Add exception handle if error
+            self.vcns_driver.delete_health_monitor(context, id, edge_id)
+            self._resource_set_status(
+                context, loadbalancer_db.Pool,
+                pool_id, service_constants.ACTIVE)
+
 
 class VcnsCallbacks(object):
     """Edge callback implementation Callback functions for
index 7da4b3323acc8754c87deed49b305579de5ef2b2..e26a49acabaaab757883a9cdf46042eeba9277f7 100644 (file)
@@ -67,7 +67,7 @@ class MaintenanceInProgress(NvpPluginException):
 class NvpServicePluginException(q_exc.NeutronException):
     """NVP Service Plugin exceptions."""
     message = _("An unexpected error happened "
-                "in the NVP Service Plugin:%(err_msg)s")
+                "in the NVP Service Plugin: %(err_msg)s")
 
 
 class NvpServiceOverQuota(q_exc.Conflict):
index f223eca4cc1510c459c26c440efe4728c6d431ba..17904eeb86fe35d844a4e847633ece85a2ebc939 100644 (file)
 
 from sqlalchemy.orm import exc
 
+from neutron.openstack.common import log as logging
 from neutron.plugins.nicira.common import exceptions as nvp_exc
 from neutron.plugins.nicira.dbexts import vcns_models
+from neutron.plugins.nicira.vshield.common import (
+    exceptions as vcns_exc)
+
+LOG = logging.getLogger(__name__)
 
 
 def add_vcns_router_binding(session, router_id, vse_id, lswitch_id, status):
@@ -55,6 +60,7 @@ def delete_vcns_router_binding(session, router_id):
 
 #
 # Edge Firewall binding methods
+#
 def add_vcns_edge_firewallrule_binding(session, map_info):
     with session.begin(subtransactions=True):
         binding = vcns_models.VcnsEdgeFirewallRuleBinding(
@@ -95,3 +101,103 @@ def cleanup_vcns_edge_firewallrule_binding(session, edge_id):
         session.query(
             vcns_models.VcnsEdgeFirewallRuleBinding).filter_by(
                 edge_id=edge_id).delete()
+
+
+def add_vcns_edge_vip_binding(session, map_info):
+    with session.begin(subtransactions=True):
+        binding = vcns_models.VcnsEdgeVipBinding(
+            vip_id=map_info['vip_id'],
+            edge_id=map_info['edge_id'],
+            vip_vseid=map_info['vip_vseid'],
+            app_profileid=map_info['app_profileid'])
+        session.add(binding)
+
+    return binding
+
+
+def get_vcns_edge_vip_binding(session, id):
+    with session.begin(subtransactions=True):
+        try:
+            qry = session.query(vcns_models.VcnsEdgeVipBinding)
+            return qry.filter_by(vip_id=id).one()
+        except exc.NoResultFound:
+            msg = _("VIP Resource binding with id:%s not found!") % id
+            LOG.exception(msg)
+            raise vcns_exc.VcnsNotFound(
+                resource='router_service_binding', msg=msg)
+
+
+def delete_vcns_edge_vip_binding(session, id):
+    with session.begin(subtransactions=True):
+        qry = session.query(vcns_models.VcnsEdgeVipBinding)
+        if not qry.filter_by(vip_id=id).delete():
+            msg = _("VIP Resource binding with id:%s not found!") % id
+            LOG.exception(msg)
+            raise nvp_exc.NvpServicePluginException(err_msg=msg)
+
+
+def add_vcns_edge_pool_binding(session, map_info):
+    with session.begin(subtransactions=True):
+        binding = vcns_models.VcnsEdgePoolBinding(
+            pool_id=map_info['pool_id'],
+            edge_id=map_info['edge_id'],
+            pool_vseid=map_info['pool_vseid'])
+        session.add(binding)
+
+    return binding
+
+
+def get_vcns_edge_pool_binding(session, id, edge_id):
+    with session.begin(subtransactions=True):
+        return (session.query(vcns_models.VcnsEdgePoolBinding).
+                filter_by(pool_id=id, edge_id=edge_id).first())
+
+
+def get_vcns_edge_pool_binding_by_vseid(session, edge_id, pool_vseid):
+    with session.begin(subtransactions=True):
+        try:
+            qry = session.query(vcns_models.VcnsEdgePoolBinding)
+            binding = qry.filter_by(edge_id=edge_id,
+                                    pool_vseid=pool_vseid).one()
+        except exc.NoResultFound:
+            msg = (_("Pool Resource binding with edge_id:%(edge_id)s "
+                     "pool_vseid:%(pool_vseid)s not found!") %
+                   {'edge_id': edge_id, 'pool_vseid': pool_vseid})
+            LOG.exception(msg)
+            raise nvp_exc.NvpServicePluginException(err_msg=msg)
+        return binding
+
+
+def delete_vcns_edge_pool_binding(session, id, edge_id):
+    with session.begin(subtransactions=True):
+        qry = session.query(vcns_models.VcnsEdgePoolBinding)
+        if not qry.filter_by(pool_id=id, edge_id=edge_id).delete():
+            msg = _("Pool Resource binding with id:%s not found!") % id
+            LOG.exception(msg)
+            raise nvp_exc.NvpServicePluginException(err_msg=msg)
+
+
+def add_vcns_edge_monitor_binding(session, map_info):
+    with session.begin(subtransactions=True):
+        binding = vcns_models.VcnsEdgeMonitorBinding(
+            monitor_id=map_info['monitor_id'],
+            edge_id=map_info['edge_id'],
+            monitor_vseid=map_info['monitor_vseid'])
+        session.add(binding)
+
+    return binding
+
+
+def get_vcns_edge_monitor_binding(session, id, edge_id):
+    with session.begin(subtransactions=True):
+        return (session.query(vcns_models.VcnsEdgeMonitorBinding).
+                filter_by(monitor_id=id, edge_id=edge_id).first())
+
+
+def delete_vcns_edge_monitor_binding(session, id, edge_id):
+    with session.begin(subtransactions=True):
+        qry = session.query(vcns_models.VcnsEdgeMonitorBinding)
+        if not qry.filter_by(monitor_id=id, edge_id=edge_id).delete():
+            msg = _("Monitor Resource binding with id:%s not found!") % id
+            LOG.exception(msg)
+            raise nvp_exc.NvpServicePluginException(err_msg=msg)
index e2be5f95377a7aafca8c776b81ff0b8cb75a9edb..c63ff221ac91f6c88aa8de2e2205c08916407085 100644 (file)
@@ -51,3 +51,41 @@ class VcnsEdgeFirewallRuleBinding(model_base.BASEV2):
                         primary_key=True)
     edge_id = sa.Column(sa.String(36), primary_key=True)
     rule_vseid = sa.Column(sa.String(36))
+
+
+class VcnsEdgePoolBinding(model_base.BASEV2):
+    """Represents the mapping between neutron pool and Edge pool."""
+
+    __tablename__ = 'vcns_edge_pool_bindings'
+
+    pool_id = sa.Column(sa.String(36),
+                        sa.ForeignKey("pools.id", ondelete="CASCADE"),
+                        primary_key=True)
+    edge_id = sa.Column(sa.String(36), primary_key=True)
+    pool_vseid = sa.Column(sa.String(36))
+
+
+class VcnsEdgeVipBinding(model_base.BASEV2):
+    """Represents the mapping between neutron vip and Edge vip."""
+
+    __tablename__ = 'vcns_edge_vip_bindings'
+
+    vip_id = sa.Column(sa.String(36),
+                       sa.ForeignKey("vips.id", ondelete="CASCADE"),
+                       primary_key=True)
+    edge_id = sa.Column(sa.String(36))
+    vip_vseid = sa.Column(sa.String(36))
+    app_profileid = sa.Column(sa.String(36))
+
+
+class VcnsEdgeMonitorBinding(model_base.BASEV2):
+    """Represents the mapping between neutron monitor and Edge monitor."""
+
+    __tablename__ = 'vcns_edge_monitor_bindings'
+
+    monitor_id = sa.Column(sa.String(36),
+                           sa.ForeignKey("healthmonitors.id",
+                                         ondelete="CASCADE"),
+                           primary_key=True)
+    edge_id = sa.Column(sa.String(36), primary_key=True)
+    monitor_vseid = sa.Column(sa.String(36))
index fc51d20020e88101c12b3b9d425765818ff51de9..6818a0c8fa46b98d25b6c17843582399a5261ac1 100644 (file)
@@ -1,6 +1,6 @@
 # vim: tabstop=4 shiftwidth=4 softtabstop=4
 
-# Copyright 2013 VMware, Inc,
+# Copyright 2013 VMware, Inc
 # All Rights Reserved.
 #
 #    Licensed under the Apache License, Version 2.0 (the "License"); you may
index 5e8da711fb13c72b3051fe0d1152fca7f2aed653..6818a0c8fa46b98d25b6c17843582399a5261ac1 100644 (file)
@@ -1,6 +1,6 @@
 # vim: tabstop=4 shiftwidth=4 softtabstop=4
 
-# Copyright 2013 OpenStack Foundation.
+# Copyright 2013 VMware, Inc
 # All Rights Reserved.
 #
 #    Licensed under the Apache License, Version 2.0 (the "License"); you may
index 246cba5873781cc481b0c6dc2efeda7a3b1475fc..9d15dc16daa11b2fc43ec0e04f2838fffde9001f 100644 (file)
@@ -17,6 +17,7 @@
 # @author: Kaiwei Fan, VMware, Inc.
 # @author: Bo Link, VMware, Inc.
 
+from neutron.openstack.common import excutils
 from neutron.openstack.common import jsonutils
 from neutron.openstack.common import log as logging
 from neutron.plugins.nicira.vshield.common import (
@@ -118,6 +119,14 @@ class EdgeApplianceDriver(object):
             status_level = RouterStatus.ROUTER_STATUS_ERROR
         return status_level
 
+    def _enable_loadbalancer(self, edge):
+        if not edge.get('featureConfigs') or (
+            not edge['featureConfigs'].get('features')):
+            edge['featureConfigs'] = {'features': []}
+        edge['featureConfigs']['features'].append(
+            {'featureType': 'loadbalancer_4.0',
+             'enabled': True})
+
     def get_edge_status(self, edge_id):
         try:
             response = self.vcns.get_edge_status(edge_id)[1]
@@ -295,7 +304,7 @@ class EdgeApplianceDriver(object):
             raise e
 
     def deploy_edge(self, router_id, name, internal_network, jobdata=None,
-                    wait_for_exec=False):
+                    wait_for_exec=False, loadbalancer_enable=True):
         task_name = 'deploying-%s' % name
         edge_name = name
         edge = self._assemble_edge(
@@ -318,6 +327,8 @@ class EdgeApplianceDriver(object):
             vcns_const.INTEGRATION_SUBNET_NETMASK,
             type="internal")
         edge['vnics']['vnics'].append(vnic_inside)
+        if loadbalancer_enable:
+            self._enable_loadbalancer(edge)
         userdata = {
             'request': edge,
             'router_name': name,
@@ -628,3 +639,24 @@ class EdgeApplianceDriver(object):
 
     def delete_lswitch(self, lswitch_id):
         self.vcns.delete_lswitch(lswitch_id)
+
+    def get_loadbalancer_config(self, edge_id):
+        try:
+            header, response = self.vcns.get_loadbalancer_config(
+                edge_id)
+        except exceptions.VcnsApiException:
+            with excutils.save_and_reraise_exception():
+                LOG.exception(_("Failed to get service config"))
+        return response
+
+    def enable_service_loadbalancer(self, edge_id):
+        config = self.get_loadbalancer_config(
+            edge_id)
+        if not config['enabled']:
+            config['enabled'] = True
+        try:
+            self.vcns.enable_service_loadbalancer(edge_id, config)
+        except exceptions.VcnsApiException:
+            with excutils.save_and_reraise_exception():
+                LOG.exception(_("Failed to enable loadbalancer "
+                                "service config"))
diff --git a/neutron/plugins/nicira/vshield/edge_loadbalancer_driver.py b/neutron/plugins/nicira/vshield/edge_loadbalancer_driver.py
new file mode 100644 (file)
index 0000000..7e3ced5
--- /dev/null
@@ -0,0 +1,338 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# Copyright 2013 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.
+#
+# @author: Leon Cui, VMware
+
+from neutron.openstack.common import excutils
+from neutron.openstack.common import log as logging
+from neutron.plugins.nicira.dbexts import vcns_db
+from neutron.plugins.nicira.vshield.common import (
+    constants as vcns_const)
+from neutron.plugins.nicira.vshield.common import (
+    exceptions as vcns_exc)
+from neutron.services.loadbalancer import constants as lb_constants
+
+LOG = logging.getLogger(__name__)
+
+BALANCE_MAP = {
+    lb_constants.LB_METHOD_ROUND_ROBIN: 'round-robin',
+    lb_constants.LB_METHOD_LEAST_CONNECTIONS: 'leastconn',
+    lb_constants.LB_METHOD_SOURCE_IP: 'source'
+}
+PROTOCOL_MAP = {
+    lb_constants.PROTOCOL_TCP: 'tcp',
+    lb_constants.PROTOCOL_HTTP: 'http',
+    lb_constants.PROTOCOL_HTTPS: 'tcp'
+}
+
+
+class EdgeLbDriver():
+    """Implementation of driver APIs for
+       Edge Loadbalancer feature configuration
+    """
+
+    def _convert_lb_vip(self, context, edge_id, vip, app_profileid):
+        pool_id = vip.get('pool_id')
+        poolid_map = vcns_db.get_vcns_edge_pool_binding(
+            context.session, pool_id, edge_id)
+        pool_vseid = poolid_map['pool_vseid']
+        return {
+            'name': vip.get('name'),
+            'ipAddress': vip.get('address'),
+            'protocol': vip.get('protocol'),
+            'port': vip.get('protocol_port'),
+            'defaultPoolId': pool_vseid,
+            'applicationProfileId': app_profileid
+        }
+
+    def _restore_lb_vip(self, context, edge_id, vip_vse):
+        pool_binding = vcns_db.get_vcns_edge_pool_binding_by_vseid(
+            context.session,
+            edge_id,
+            vip_vse['defaultPoolId'])
+
+        return {
+            'name': vip_vse['name'],
+            'address': vip_vse['ipAddress'],
+            'protocol': vip_vse['protocol'],
+            'protocol_port': vip_vse['port'],
+            'pool_id': pool_binding['pool_id']
+        }
+
+    def _convert_lb_pool(self, context, edge_id, pool, members):
+        vsepool = {
+            'name': pool.get('name'),
+            'algorithm': BALANCE_MAP.get(
+                pool.get('lb_method'),
+                'round-robin'),
+            'member': [],
+            'monitorId': []
+        }
+        for member in members:
+            vsepool['member'].append({
+                'ipAddress': member['address'],
+                'port': member['protocol_port']
+            })
+        ##TODO(linb) right now, vse only accept at most one monitor per pool
+        monitors = pool.get('health_monitors')
+        if not monitors:
+            return vsepool
+        monitorid_map = vcns_db.get_vcns_edge_monitor_binding(
+            context.session,
+            monitors[0],
+            edge_id)
+        vsepool['monitorId'].append(monitorid_map['monitor_vseid'])
+        return vsepool
+
+    def _restore_lb_pool(self, context, edge_id, pool_vse):
+        #TODO(linb): Get more usefule info
+        return {
+            'name': pool_vse['name'],
+        }
+
+    def _convert_lb_monitor(self, context, monitor):
+        return {
+            'type': PROTOCOL_MAP.get(
+                monitor.get('type'), 'http'),
+            'interval': monitor.get('delay'),
+            'timeout': monitor.get('timeout'),
+            'maxRetries': monitor.get('max_retries'),
+            'name': monitor.get('id')
+        }
+
+    def _restore_lb_monitor(self, context, edge_id, monitor_vse):
+        return {
+            'delay': monitor_vse['interval'],
+            'timeout': monitor_vse['timeout'],
+            'max_retries': monitor_vse['maxRetries'],
+            'id': monitor_vse['name']
+        }
+
+    def _convert_app_profile(self, name, app_profile):
+        #TODO(linb): convert the session_persistence to
+        #corresponding app_profile
+        return {
+            "insertXForwardedFor": False,
+            "name": name,
+            "persistence": {
+                "method": "sourceip"
+            },
+            "serverSslEnabled": False,
+            "sslPassthrough": False,
+            "template": "HTTP"
+        }
+
+    def create_vip(self, context, edge_id, vip):
+        app_profile = self._convert_app_profile(
+            vip['name'], vip.get('session_persistence'))
+        try:
+            header, response = self.vcns.create_app_profile(
+                edge_id, app_profile)
+        except vcns_exc.VcnsApiException:
+            with excutils.save_and_reraise_exception():
+                LOG.exception(_("Failed to create app profile on edge: %s"),
+                              edge_id)
+        objuri = header['location']
+        app_profileid = objuri[objuri.rfind("/") + 1:]
+
+        vip_new = self._convert_lb_vip(context, edge_id, vip, app_profileid)
+        try:
+            header, response = self.vcns.create_vip(
+                edge_id, vip_new)
+        except vcns_exc.VcnsApiException:
+            with excutils.save_and_reraise_exception():
+                LOG.exception(_("Failed to create vip on vshield edge: %s"),
+                              edge_id)
+        objuri = header['location']
+        vip_vseid = objuri[objuri.rfind("/") + 1:]
+
+        # Add the vip mapping
+        map_info = {
+            "vip_id": vip['id'],
+            "vip_vseid": vip_vseid,
+            "edge_id": edge_id,
+            "app_profileid": app_profileid
+        }
+        vcns_db.add_vcns_edge_vip_binding(context.session, map_info)
+
+    def get_vip(self, context, id):
+        vip_binding = vcns_db.get_vcns_edge_vip_binding(context.session, id)
+        edge_id = vip_binding[vcns_const.EDGE_ID]
+        vip_vseid = vip_binding['vip_vseid']
+        try:
+            response = self.vcns.get_vip(edge_id, vip_vseid)[1]
+        except vcns_exc.VcnsApiException:
+            with excutils.save_and_reraise_exception():
+                LOG.exception(_("Failed to get vip on edge"))
+        return self._restore_lb_vip(context, edge_id, response)
+
+    def update_vip(self, context, vip):
+        vip_binding = vcns_db.get_vcns_edge_vip_binding(
+            context.session, vip['id'])
+        edge_id = vip_binding[vcns_const.EDGE_ID]
+        vip_vseid = vip_binding.get('vip_vseid')
+        app_profileid = vip_binding.get('app_profileid')
+
+        vip_new = self._convert_lb_vip(context, edge_id, vip, app_profileid)
+        try:
+            self.vcns.update_vip(edge_id, vip_vseid, vip_new)
+        except vcns_exc.VcnsApiException:
+            with excutils.save_and_reraise_exception():
+                LOG.exception(_("Failed to update vip on edge: %s"), edge_id)
+
+    def delete_vip(self, context, id):
+        vip_binding = vcns_db.get_vcns_edge_vip_binding(
+            context.session, id)
+        edge_id = vip_binding[vcns_const.EDGE_ID]
+        vip_vseid = vip_binding['vip_vseid']
+        app_profileid = vip_binding['app_profileid']
+
+        try:
+            self.vcns.delete_vip(edge_id, vip_vseid)
+            self.vcns.delete_app_profile(edge_id, app_profileid)
+        except vcns_exc.VcnsApiException:
+            with excutils.save_and_reraise_exception():
+                LOG.exception(_("Failed to delete vip on edge: %s"), edge_id)
+        vcns_db.delete_vcns_edge_vip_binding(context.session, id)
+
+    def create_pool(self, context, edge_id, pool, members):
+        pool_new = self._convert_lb_pool(context, edge_id, pool, members)
+        try:
+            header = self.vcns.create_pool(edge_id, pool_new)[0]
+        except vcns_exc.VcnsApiException:
+            with excutils.save_and_reraise_exception():
+                LOG.exception(_("Failed to create pool"))
+
+        objuri = header['location']
+        pool_vseid = objuri[objuri.rfind("/") + 1:]
+
+        # update the pool mapping table
+        map_info = {
+            "pool_id": pool['id'],
+            "pool_vseid": pool_vseid,
+            "edge_id": edge_id
+        }
+        vcns_db.add_vcns_edge_pool_binding(context.session, map_info)
+
+    def get_pool(self, context, id, edge_id):
+        pool_binding = vcns_db.get_vcns_edge_pool_binding(
+            context.session, id, edge_id)
+        if not pool_binding:
+            msg = (_("pool_binding not found with id: %(id)s "
+                     "edge_id: %(edge_id)s") % {
+                   'id': id,
+                   'edge_id': edge_id})
+            LOG.error(msg)
+            raise vcns_exc.VcnsNotFound(
+                resource='router_service_binding', msg=msg)
+        pool_vseid = pool_binding['pool_vseid']
+        try:
+            response = self.vcns.get_pool(edge_id, pool_vseid)[1]
+        except vcns_exc.VcnsApiException:
+            with excutils.save_and_reraise_exception():
+                LOG.exception(_("Failed to get pool on edge"))
+        return self._restore_lb_pool(context, edge_id, response)
+
+    def update_pool(self, context, edge_id, pool, members):
+        pool_binding = vcns_db.get_vcns_edge_pool_binding(
+            context.session, pool['id'], edge_id)
+        pool_vseid = pool_binding['pool_vseid']
+        pool_new = self._convert_lb_pool(context, edge_id, pool, members)
+        try:
+            self.vcns.update_pool(edge_id, pool_vseid, pool_new)
+        except vcns_exc.VcnsApiException:
+            with excutils.save_and_reraise_exception():
+                LOG.exception(_("Failed to update pool"))
+
+    def delete_pool(self, context, id, edge_id):
+        pool_binding = vcns_db.get_vcns_edge_pool_binding(
+            context.session, id, edge_id)
+        pool_vseid = pool_binding['pool_vseid']
+        try:
+            self.vcns.delete_pool(edge_id, pool_vseid)
+        except vcns_exc.VcnsApiException:
+            with excutils.save_and_reraise_exception():
+                LOG.exception(_("Failed to delete pool"))
+        vcns_db.delete_vcns_edge_pool_binding(
+            context.session, id, edge_id)
+
+    def create_health_monitor(self, context, edge_id, health_monitor):
+        monitor_new = self._convert_lb_monitor(context, health_monitor)
+        try:
+            header = self.vcns.create_health_monitor(edge_id, monitor_new)[0]
+        except vcns_exc.VcnsApiException:
+            with excutils.save_and_reraise_exception():
+                LOG.exception(_("Failed to create monitor on edge: %s"),
+                              edge_id)
+
+        objuri = header['location']
+        monitor_vseid = objuri[objuri.rfind("/") + 1:]
+
+        # update the health_monitor mapping table
+        map_info = {
+            "monitor_id": health_monitor['id'],
+            "monitor_vseid": monitor_vseid,
+            "edge_id": edge_id
+        }
+        vcns_db.add_vcns_edge_monitor_binding(context.session, map_info)
+
+    def get_health_monitor(self, context, id, edge_id):
+        monitor_binding = vcns_db.get_vcns_edge_monitor_binding(
+            context.session, id, edge_id)
+        if not monitor_binding:
+            msg = (_("monitor_binding not found with id: %(id)s "
+                     "edge_id: %(edge_id)s") % {
+                   'id': id,
+                   'edge_id': edge_id})
+            LOG.error(msg)
+            raise vcns_exc.VcnsNotFound(
+                resource='router_service_binding', msg=msg)
+        monitor_vseid = monitor_binding['monitor_vseid']
+        try:
+            response = self.vcns.get_health_monitor(edge_id, monitor_vseid)[1]
+        except vcns_exc.VcnsApiException as e:
+            with excutils.save_and_reraise_exception():
+                LOG.exception(_("Failed to get monitor on edge: %s"),
+                              e.response)
+        return self._restore_lb_monitor(context, edge_id, response)
+
+    def update_health_monitor(self, context, edge_id,
+                              old_health_monitor, health_monitor):
+        monitor_binding = vcns_db.get_vcns_edge_monitor_binding(
+            context.session,
+            old_health_monitor['id'], edge_id)
+        monitor_vseid = monitor_binding['monitor_vseid']
+        monitor_new = self._convert_lb_monitor(
+            context, health_monitor)
+        try:
+            self.vcns.update_health_monitor(
+                edge_id, monitor_vseid, monitor_new)
+        except vcns_exc.VcnsApiException:
+            with excutils.save_and_reraise_exception():
+                LOG.exception(_("Failed to update monitor on edge: %s"),
+                              edge_id)
+
+    def delete_health_monitor(self, context, id, edge_id):
+        monitor_binding = vcns_db.get_vcns_edge_monitor_binding(
+            context.session, id, edge_id)
+        monitor_vseid = monitor_binding['monitor_vseid']
+        try:
+            self.vcns.delete_health_monitor(edge_id, monitor_vseid)
+        except vcns_exc.VcnsApiException:
+            with excutils.save_and_reraise_exception():
+                LOG.exception(_("Failed to delete monitor"))
+        vcns_db.delete_vcns_edge_monitor_binding(
+            context.session, id, edge_id)
index beace318da6da94897bf18e77dfbdf2f9d46ac6d..803b979880cbd6aae2d843c9fc75493a141b2aaa 100644 (file)
@@ -32,6 +32,13 @@ URI_PREFIX = "/api/4.0/edges"
 FIREWALL_SERVICE = "firewall/config"
 FIREWALL_RULE_RESOURCE = "rules"
 
+#LbaaS Constants
+LOADBALANCER_SERVICE = "loadbalancer/config"
+VIP_RESOURCE = "virtualservers"
+POOL_RESOURCE = "pools"
+MONITOR_RESOURCE = "monitors"
+APP_PROFILE_RESOURCE = "applicationprofiles"
+
 
 class Vcns(object):
 
@@ -111,6 +118,14 @@ class Vcns(object):
         uri = "/api/ws.v1/lswitch/%s" % lswitch_id
         return self.do_request(HTTP_DELETE, uri)
 
+    def get_loadbalancer_config(self, edge_id):
+        uri = self._build_uri_path(edge_id, LOADBALANCER_SERVICE)
+        return self.do_request(HTTP_GET, uri, decode=True)
+
+    def enable_service_loadbalancer(self, edge_id, config):
+        uri = self._build_uri_path(edge_id, LOADBALANCER_SERVICE)
+        return self.do_request(HTTP_PUT, uri, config)
+
     def update_firewall(self, edge_id, fw_req):
         uri = self._build_uri_path(
             edge_id, FIREWALL_SERVICE)
@@ -159,6 +174,96 @@ class Vcns(object):
             vcns_rule_id)
         return self.do_request(HTTP_GET, uri, decode=True)
 
+    #
+    #Edge LBAAS call helper
+    #
+    def create_vip(self, edge_id, vip_new):
+        uri = self._build_uri_path(
+            edge_id, LOADBALANCER_SERVICE,
+            VIP_RESOURCE)
+        return self.do_request(HTTP_POST, uri, vip_new)
+
+    def get_vip(self, edge_id, vip_vseid):
+        uri = self._build_uri_path(
+            edge_id, LOADBALANCER_SERVICE,
+            VIP_RESOURCE, vip_vseid)
+        return self.do_request(HTTP_GET, uri, decode=True)
+
+    def update_vip(self, edge_id, vip_vseid, vip_new):
+        uri = self._build_uri_path(
+            edge_id, LOADBALANCER_SERVICE,
+            VIP_RESOURCE, vip_vseid)
+        return self.do_request(HTTP_PUT, uri, vip_new)
+
+    def delete_vip(self, edge_id, vip_vseid):
+        uri = self._build_uri_path(
+            edge_id, LOADBALANCER_SERVICE,
+            VIP_RESOURCE, vip_vseid)
+        return self.do_request(HTTP_DELETE, uri)
+
+    def create_pool(self, edge_id, pool_new):
+        uri = self._build_uri_path(
+            edge_id, LOADBALANCER_SERVICE,
+            POOL_RESOURCE)
+        return self.do_request(HTTP_POST, uri, pool_new)
+
+    def get_pool(self, edge_id, pool_vseid):
+        uri = self._build_uri_path(
+            edge_id, LOADBALANCER_SERVICE,
+            POOL_RESOURCE, pool_vseid)
+        return self.do_request(HTTP_GET, uri, decode=True)
+
+    def update_pool(self, edge_id, pool_vseid, pool_new):
+        uri = self._build_uri_path(
+            edge_id, LOADBALANCER_SERVICE,
+            POOL_RESOURCE, pool_vseid)
+        return self.do_request(HTTP_PUT, uri, pool_new)
+
+    def delete_pool(self, edge_id, pool_vseid):
+        uri = self._build_uri_path(
+            edge_id, LOADBALANCER_SERVICE,
+            POOL_RESOURCE, pool_vseid)
+        return self.do_request(HTTP_DELETE, uri)
+
+    def create_health_monitor(self, edge_id, monitor_new):
+        uri = self._build_uri_path(
+            edge_id, LOADBALANCER_SERVICE,
+            MONITOR_RESOURCE)
+        return self.do_request(HTTP_POST, uri, monitor_new)
+
+    def get_health_monitor(self, edge_id, monitor_vseid):
+        uri = self._build_uri_path(
+            edge_id, LOADBALANCER_SERVICE,
+            MONITOR_RESOURCE, monitor_vseid)
+        return self.do_request(HTTP_GET, uri, decode=True)
+
+    def update_health_monitor(self, edge_id, monitor_vseid, monitor_new):
+        uri = self._build_uri_path(
+            edge_id, LOADBALANCER_SERVICE,
+            MONITOR_RESOURCE,
+            monitor_vseid)
+        return self.do_request(HTTP_PUT, uri, monitor_new)
+
+    def delete_health_monitor(self, edge_id, monitor_vseid):
+        uri = self._build_uri_path(
+            edge_id, LOADBALANCER_SERVICE,
+            MONITOR_RESOURCE,
+            monitor_vseid)
+        return self.do_request(HTTP_DELETE, uri)
+
+    def create_app_profile(self, edge_id, app_profile):
+        uri = self._build_uri_path(
+            edge_id, LOADBALANCER_SERVICE,
+            APP_PROFILE_RESOURCE)
+        return self.do_request(HTTP_POST, uri, app_profile)
+
+    def delete_app_profile(self, edge_id, app_profileid):
+        uri = self._build_uri_path(
+            edge_id, LOADBALANCER_SERVICE,
+            APP_PROFILE_RESOURCE,
+            app_profileid)
+        return self.do_request(HTTP_DELETE, uri)
+
     def _build_uri_path(self, edge_id,
                         service,
                         resource=None,
index 8178744a9187271f960c1c583ff51fed9118f6bb..c2c5c14992bee3bd3779377c7481d002822481a0 100644 (file)
 
 from oslo.config import cfg
 
-from neutron.plugins.nicira.common import config  # noqa
+from neutron.openstack.common import log as logging
+from neutron.plugins.nicira.common import config as nicira_cfg  # noqa
 from neutron.plugins.nicira.vshield import edge_appliance_driver
 from neutron.plugins.nicira.vshield import edge_firewall_driver
+from neutron.plugins.nicira.vshield import edge_loadbalancer_driver
 from neutron.plugins.nicira.vshield.tasks import tasks
 from neutron.plugins.nicira.vshield import vcns
 
+LOG = logging.getLogger(__name__)
+
 
 class VcnsDriver(edge_appliance_driver.EdgeApplianceDriver,
-                 edge_firewall_driver.EdgeFirewallDriver):
+                 edge_firewall_driver.EdgeFirewallDriver,
+                 edge_loadbalancer_driver.EdgeLbDriver):
+
     def __init__(self, callbacks):
         super(VcnsDriver, self).__init__()
 
index d646afd0f2a088b164481f152786aaad63b46887..c54c72939bdd6ccefd85d8cc17b7ae83602e4679 100644 (file)
@@ -64,6 +64,10 @@ class LoadBalancerTestMixin(object):
         for k in loadbalancer.RESOURCE_ATTRIBUTE_MAP.keys()
     )
 
+    def _get_vip_optional_args(self):
+        return ('description', 'subnet_id', 'address',
+                'session_persistence', 'connection_limit')
+
     def _create_vip(self, fmt, name, pool_id, protocol, protocol_port,
                     admin_state_up, expected_res_status=None, **kwargs):
         data = {'vip': {'name': name,
@@ -72,8 +76,8 @@ class LoadBalancerTestMixin(object):
                         'protocol_port': protocol_port,
                         'admin_state_up': admin_state_up,
                         'tenant_id': self._tenant_id}}
-        for arg in ('description', 'subnet_id', 'address',
-                    'session_persistence', 'connection_limit'):
+        args = self._get_vip_optional_args()
+        for arg in args:
             if arg in kwargs and kwargs[arg] is not None:
                 data['vip'][arg] = kwargs[arg]
 
@@ -255,7 +259,8 @@ class LoadBalancerTestMixin(object):
 
 class LoadBalancerPluginDbTestCase(LoadBalancerTestMixin,
                                    test_db_plugin.NeutronDbPluginV2TestCase):
-    def setUp(self, core_plugin=None, lb_plugin=None, lbaas_provider=None):
+    def setUp(self, core_plugin=None, lb_plugin=None, lbaas_provider=None,
+              ext_mgr=None):
         service_plugins = {'lb_plugin_name': DB_LB_PLUGIN_KLASS}
         if not lbaas_provider:
             lbaas_provider = (
@@ -269,12 +274,18 @@ class LoadBalancerPluginDbTestCase(LoadBalancerTestMixin,
         sdb.ServiceTypeManager._instance = None
 
         super(LoadBalancerPluginDbTestCase, self).setUp(
+            ext_mgr=ext_mgr,
             service_plugins=service_plugins
         )
 
-        self._subnet_id = _subnet_id
-
-        self.plugin = loadbalancer_plugin.LoadBalancerPlugin()
+        if not ext_mgr:
+            self.plugin = loadbalancer_plugin.LoadBalancerPlugin()
+            ext_mgr = PluginAwareExtensionManager(
+                extensions_path,
+                {constants.LOADBALANCER: self.plugin}
+            )
+            app = config.load_paste_app('extensions_test_app')
+            self.ext_api = ExtensionMiddleware(app, ext_mgr=ext_mgr)
 
         get_lbaas_agent_patcher = mock.patch(
             'neutron.services.loadbalancer.agent_scheduler'
@@ -285,12 +296,7 @@ class LoadBalancerPluginDbTestCase(LoadBalancerTestMixin,
         self.addCleanup(mock.patch.stopall)
         self.addCleanup(cfg.CONF.reset)
 
-        ext_mgr = PluginAwareExtensionManager(
-            extensions_path,
-            {constants.LOADBALANCER: self.plugin}
-        )
-        app = config.load_paste_app('extensions_test_app')
-        self.ext_api = ExtensionMiddleware(app, ext_mgr=ext_mgr)
+        self._subnet_id = _subnet_id
 
 
 class TestLoadBalancer(LoadBalancerPluginDbTestCase):
index f2deff6145c0ed8d376314095aee1129a86c9c9e..ad772d77ab35369ef4b4fce5a299da3ab9793333 100644 (file)
@@ -31,6 +31,7 @@ from neutron.tests.unit.nicira import SERVICE_PLUGIN_NAME
 from neutron.tests.unit.nicira import test_nicira_plugin
 from neutron.tests.unit.nicira import VCNS_NAME
 from neutron.tests.unit.nicira.vshield import fake_vcns
+from neutron.tests.unit import test_l3_plugin
 
 _uuid = uuidutils.generate_uuid
 
@@ -67,7 +68,8 @@ class NvpRouterTestCase(test_nicira_plugin.TestNiciraL3NatTestCase):
                                              service_plugins=service_plugins)
 
 
-class ServiceRouterTest(test_nicira_plugin.NiciraL3NatTest):
+class ServiceRouterTest(test_nicira_plugin.NiciraL3NatTest,
+                        test_l3_plugin.L3NatTestCaseMixin):
 
     def vcns_patch(self):
         instance = self.mock_vcns.start()
@@ -94,6 +96,10 @@ class ServiceRouterTest(test_nicira_plugin.NiciraL3NatTest):
             self.fc2.create_lswitch)
         instance.return_value.delete_lswitch.side_effect = (
             self.fc2.delete_lswitch)
+        instance.return_value.get_loadbalancer_config.side_effect = (
+            self.fc2.get_loadbalancer_config)
+        instance.return_value.enable_service_loadbalancer.side_effect = (
+            self.fc2.enable_service_loadbalancer)
 
     def setUp(self, ext_mgr=None, service_plugins=None):
         cfg.CONF.set_override('api_extensions_path', NVPEXT_PATH)
@@ -113,7 +119,7 @@ class ServiceRouterTest(test_nicira_plugin.NiciraL3NatTest):
 
         self.fc2.set_fake_nvpapi(self.fc)
         self.addCleanup(self.fc2.reset_all)
-        self.addCleanup(self.mock_vcns.stop)
+        self.addCleanup(mock.patch.stopall)
 
     def tearDown(self):
         plugin = NeutronManager.get_plugin()
diff --git a/neutron/tests/unit/nicira/test_lbaas_plugin.py b/neutron/tests/unit/nicira/test_lbaas_plugin.py
new file mode 100644 (file)
index 0000000..94ed0bb
--- /dev/null
@@ -0,0 +1,433 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# Copyright 2013 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.
+#
+# @author: linb, VMware
+
+import contextlib
+
+from webob import exc as web_exc
+
+from neutron.api.v2 import attributes
+from neutron import context
+from neutron.db.loadbalancer import loadbalancer_db as ldb
+from neutron.extensions import loadbalancer as lb
+from neutron import manager
+from neutron.openstack.common import uuidutils
+from neutron.tests.unit.db.loadbalancer import test_db_loadbalancer
+from neutron.tests.unit.nicira import test_edge_router
+
+_uuid = uuidutils.generate_uuid
+
+LBAAS_PLUGIN_CLASS = (
+    "neutron.plugins.nicira.NeutronServicePlugin.NvpAdvancedPlugin"
+)
+
+
+class LoadBalancerTestExtensionManager(
+        test_edge_router.ServiceRouterTestExtensionManager):
+
+    def get_resources(self):
+        # If l3 resources have been loaded and updated by main API
+        # router, update the map in the l3 extension so it will load
+        # the same attributes as the API router
+        resources = super(LoadBalancerTestExtensionManager,
+                          self).get_resources()
+        lb_attr_map = lb.RESOURCE_ATTRIBUTE_MAP.copy()
+        for res in lb.RESOURCE_ATTRIBUTE_MAP.keys():
+            attr_info = attributes.RESOURCE_ATTRIBUTE_MAP.get(res)
+            if attr_info:
+                lb.RESOURCE_ATTRIBUTE_MAP[res] = attr_info
+        lb_resources = lb.Loadbalancer.get_resources()
+        # restore the original resources once the controllers are created
+        lb.RESOURCE_ATTRIBUTE_MAP = lb_attr_map
+        resources.extend(lb_resources)
+        return resources
+
+
+class TestLoadbalancerPlugin(
+    test_db_loadbalancer.LoadBalancerPluginDbTestCase,
+    test_edge_router.ServiceRouterTest):
+
+    def vcns_loadbalancer_patch(self):
+        instance = self.vcns_instance
+        instance.return_value.create_vip.side_effect = (
+            self.fc2.create_vip)
+        instance.return_value.get_vip.side_effect = (
+            self.fc2.get_vip)
+        instance.return_value.update_vip.side_effect = (
+            self.fc2.update_vip)
+        instance.return_value.delete_vip.side_effect = (
+            self.fc2.delete_vip)
+        instance.return_value.create_pool.side_effect = (
+            self.fc2.create_pool)
+        instance.return_value.get_pool.side_effect = (
+            self.fc2.get_pool)
+        instance.return_value.update_pool.side_effect = (
+            self.fc2.update_pool)
+        instance.return_value.delete_pool.side_effect = (
+            self.fc2.delete_pool)
+        instance.return_value.create_health_monitor.side_effect = (
+            self.fc2.create_health_monitor)
+        instance.return_value.get_health_monitor.side_effect = (
+            self.fc2.get_health_monitor)
+        instance.return_value.update_health_monitor.side_effect = (
+            self.fc2.update_health_monitor)
+        instance.return_value.delete_health_monitor.side_effect = (
+            self.fc2.delete_health_monitor)
+        instance.return_value.create_app_profile.side_effect = (
+            self.fc2.create_app_profile)
+        instance.return_value.delete_app_profile.side_effect = (
+            self.fc2.delete_app_profile)
+
+    def setUp(self):
+        # Save the global RESOURCE_ATTRIBUTE_MAP
+        self.saved_attr_map = {}
+        for resource, attrs in attributes.RESOURCE_ATTRIBUTE_MAP.iteritems():
+            self.saved_attr_map[resource] = attrs.copy()
+
+        super(TestLoadbalancerPlugin, self).setUp(
+            ext_mgr=LoadBalancerTestExtensionManager(),
+            lb_plugin=LBAAS_PLUGIN_CLASS)
+        self.vcns_loadbalancer_patch()
+        self.plugin = manager.NeutronManager.get_plugin()
+        self.router_id = None
+
+    def tearDown(self):
+        super(TestLoadbalancerPlugin, self).tearDown()
+        # Restore the global RESOURCE_ATTRIBUTE_MAP
+        attributes.RESOURCE_ATTRIBUTE_MAP = self.saved_attr_map
+        self.ext_api = None
+        self.plugin = None
+
+    def _fake_router_edge_mapping(self):
+        req = self._create_router(self.fmt, self._tenant_id)
+        res = self.deserialize(self.fmt, req)
+        self.router_id = res['router']['id']
+
+    def _get_vip_optional_args(self):
+        args = super(TestLoadbalancerPlugin, self)._get_vip_optional_args()
+        return args + ('router_id',)
+
+    def test_update_healthmonitor(self):
+        self._fake_router_edge_mapping()
+
+        keys = [('type', "TCP"),
+                ('tenant_id', self._tenant_id),
+                ('delay', 20),
+                ('timeout', 20),
+                ('max_retries', 2),
+                ('admin_state_up', False)]
+
+        with contextlib.nested(
+            self.subnet(),
+            self.pool(),
+            self.health_monitor()
+        ) as (subnet, pool, health_mon):
+            net_id = subnet['subnet']['network_id']
+            self._set_net_external(net_id)
+            with self.vip(
+                router_id=self.router_id, pool=pool,
+                subnet=subnet):
+                    self.plugin.create_pool_health_monitor(
+                        context.get_admin_context(),
+                        health_mon, pool['pool']['id']
+                    )
+                    data = {'health_monitor': {'delay': 20,
+                                               'timeout': 20,
+                                               'max_retries': 2,
+                                               'admin_state_up': False}}
+                    req = self.new_update_request(
+                        "health_monitors",
+                        data,
+                        health_mon['health_monitor']['id'])
+                    res = self.deserialize(
+                        self.fmt, req.get_response(self.ext_api))
+                    for k, v in keys:
+                        self.assertEqual(res['health_monitor'][k], v)
+
+    def test_delete_healthmonitor(self):
+        ctx = context.get_admin_context()
+        self._fake_router_edge_mapping()
+        with contextlib.nested(
+            self.subnet(),
+            self.pool(),
+            self.health_monitor(no_delete=True)
+        ) as (subnet, pool, health_mon):
+            net_id = subnet['subnet']['network_id']
+            self._set_net_external(net_id)
+            with self.vip(
+                router_id=self.router_id, pool=pool,
+                subnet=subnet):
+                    self.plugin.create_pool_health_monitor(
+                        context.get_admin_context(),
+                        health_mon, pool['pool']['id']
+                    )
+
+            req = self.new_delete_request('health_monitors',
+                                          health_mon['health_monitor']['id'])
+            res = req.get_response(self.ext_api)
+            self.assertEqual(res.status_int, 204)
+            qry = ctx.session.query(ldb.HealthMonitor)
+            qry = qry.filter_by(id=health_mon['health_monitor']['id'])
+            self.assertIsNone(qry.first())
+
+    def test_create_vip(self, **extras):
+        self._fake_router_edge_mapping()
+        expected = {
+            'name': 'vip1',
+            'description': '',
+            'protocol_port': 80,
+            'protocol': 'HTTP',
+            'connection_limit': -1,
+            'admin_state_up': True,
+            'status': 'PENDING_CREATE',
+            'tenant_id': self._tenant_id,
+        }
+
+        expected.update(extras)
+
+        name = expected['name']
+
+        with contextlib.nested(
+            self.subnet(),
+            self.pool(),
+            self.health_monitor()
+        ) as (subnet, pool, monitor):
+            net_id = subnet['subnet']['network_id']
+            self._set_net_external(net_id)
+            expected['pool_id'] = pool['pool']['id']
+            self.plugin.create_pool_health_monitor(
+                context.get_admin_context(),
+                monitor, pool['pool']['id']
+            )
+            with self.vip(
+                router_id=self.router_id, name=name,
+                pool=pool, subnet=subnet, **extras) as vip:
+                for k in ('id', 'address', 'port_id', 'pool_id'):
+                    self.assertTrue(vip['vip'].get(k, None))
+                expected['status'] = 'ACTIVE'
+                self.assertEqual(
+                    dict((k, v)
+                         for k, v in vip['vip'].items() if k in expected),
+                    expected
+                )
+
+    def test_update_vip(self):
+        self._fake_router_edge_mapping()
+        name = 'new_vip'
+        keys = [('name', name),
+                ('address', "10.0.0.2"),
+                ('protocol_port', 80),
+                ('connection_limit', 100),
+                ('admin_state_up', False),
+                ('status', 'ACTIVE')]
+
+        with contextlib.nested(
+            self.subnet(),
+            self.pool(),
+            self.health_monitor()
+        ) as (subnet, pool, monitor):
+            net_id = subnet['subnet']['network_id']
+            self._set_net_external(net_id)
+            self.plugin.create_pool_health_monitor(
+                context.get_admin_context(),
+                monitor, pool['pool']['id']
+            )
+            with self.vip(
+                router_id=self.router_id, name=name,
+                pool=pool, subnet=subnet) as vip:
+                keys.append(('subnet_id', vip['vip']['subnet_id']))
+                data = {'vip': {'name': name,
+                                'connection_limit': 100,
+                                'session_persistence':
+                                {'type': "APP_COOKIE",
+                                 'cookie_name': "jesssionId"},
+                                'admin_state_up': False}}
+                req = self.new_update_request(
+                    'vips', data, vip['vip']['id'])
+                res = self.deserialize(self.fmt,
+                                       req.get_response(self.ext_api))
+                for k, v in keys:
+                    self.assertEqual(res['vip'][k], v)
+
+    def test_delete_vip(self):
+        self._fake_router_edge_mapping()
+        with contextlib.nested(
+            self.subnet(),
+            self.pool(),
+            self.health_monitor()
+        ) as (subnet, pool, monitor):
+            net_id = subnet['subnet']['network_id']
+            self._set_net_external(net_id)
+            self.plugin.create_pool_health_monitor(
+                context.get_admin_context(),
+                monitor, pool['pool']['id']
+            )
+            with self.vip(
+                router_id=self.router_id,
+                pool=pool, subnet=subnet, no_delete=True) as vip:
+                req = self.new_delete_request('vips',
+                                              vip['vip']['id'])
+                res = req.get_response(self.ext_api)
+                self.assertEqual(res.status_int, 204)
+
+    def test_update_pool(self):
+        self._fake_router_edge_mapping()
+        data = {'pool': {'name': "new_pool",
+                         'admin_state_up': False}}
+        with contextlib.nested(
+            self.subnet(),
+            self.pool(),
+            self.health_monitor()
+        ) as (subnet, pool, monitor):
+            net_id = subnet['subnet']['network_id']
+            self._set_net_external(net_id)
+            self.plugin.create_pool_health_monitor(
+                context.get_admin_context(),
+                monitor, pool['pool']['id']
+            )
+            with self.vip(
+                router_id=self.router_id,
+                pool=pool, subnet=subnet):
+                req = self.new_update_request(
+                    'pools', data, pool['pool']['id'])
+                res = self.deserialize(self.fmt,
+                                       req.get_response(self.ext_api))
+                for k, v in data['pool'].items():
+                    self.assertEqual(res['pool'][k], v)
+
+    def test_create_member(self):
+        self._fake_router_edge_mapping()
+
+        with contextlib.nested(
+            self.subnet(),
+            self.pool(),
+            self.health_monitor()
+        ) as (subnet, pool, monitor):
+            pool_id = pool['pool']['id']
+            net_id = subnet['subnet']['network_id']
+            self._set_net_external(net_id)
+            self.plugin.create_pool_health_monitor(
+                context.get_admin_context(),
+                monitor, pool['pool']['id']
+            )
+            with self.vip(
+                router_id=self.router_id,
+                pool=pool, subnet=subnet):
+                with contextlib.nested(
+                    self.member(address='192.168.1.100',
+                                protocol_port=80,
+                                pool_id=pool_id),
+                    self.member(router_id=self.router_id,
+                                address='192.168.1.101',
+                                protocol_port=80,
+                                pool_id=pool_id)) as (member1, member2):
+                        req = self.new_show_request('pools',
+                                                    pool_id,
+                                                    fmt=self.fmt)
+                        pool_update = self.deserialize(
+                            self.fmt,
+                            req.get_response(self.ext_api)
+                        )
+                        self.assertIn(member1['member']['id'],
+                                      pool_update['pool']['members'])
+                        self.assertIn(member2['member']['id'],
+                                      pool_update['pool']['members'])
+
+    def _show_pool(self, pool_id):
+        req = self.new_show_request('pools', pool_id, fmt=self.fmt)
+        res = req.get_response(self.ext_api)
+        self.assertEqual(web_exc.HTTPOk.code, res.status_int)
+        return self.deserialize(self.fmt, res)
+
+    def test_update_member(self):
+        self._fake_router_edge_mapping()
+        with contextlib.nested(
+            self.subnet(),
+            self.pool(name="pool1"),
+            self.pool(name="pool2"),
+            self.health_monitor()
+        ) as (subnet, pool1, pool2, monitor):
+            net_id = subnet['subnet']['network_id']
+            self._set_net_external(net_id)
+            self.plugin.create_pool_health_monitor(
+                context.get_admin_context(),
+                monitor, pool1['pool']['id']
+            )
+            self.plugin.create_pool_health_monitor(
+                context.get_admin_context(),
+                monitor, pool2['pool']['id']
+            )
+            with self.vip(
+                router_id=self.router_id,
+                pool=pool1, subnet=subnet):
+                keys = [('address', "192.168.1.100"),
+                        ('tenant_id', self._tenant_id),
+                        ('protocol_port', 80),
+                        ('weight', 10),
+                        ('pool_id', pool2['pool']['id']),
+                        ('admin_state_up', False),
+                        ('status', 'ACTIVE')]
+                with self.member(
+                    pool_id=pool1['pool']['id']) as member:
+
+                    pool1_update = self._show_pool(pool1['pool']['id'])
+                    self.assertEqual(len(pool1_update['pool']['members']), 1)
+                    pool2_update = self._show_pool(pool2['pool']['id'])
+                    self.assertEqual(len(pool1_update['pool']['members']), 1)
+                    self.assertFalse(pool2_update['pool']['members'])
+
+                    data = {'member': {'pool_id': pool2['pool']['id'],
+                                       'weight': 10,
+                                       'admin_state_up': False}}
+                    req = self.new_update_request('members',
+                                                  data,
+                                                  member['member']['id'])
+                    raw_res = req.get_response(self.ext_api)
+                    self.assertEquals(web_exc.HTTPOk.code, raw_res.status_int)
+                    res = self.deserialize(self.fmt, raw_res)
+                    for k, v in keys:
+                        self.assertEqual(res['member'][k], v)
+                    pool1_update = self._show_pool(pool1['pool']['id'])
+                    pool2_update = self._show_pool(pool2['pool']['id'])
+                    self.assertEqual(len(pool2_update['pool']['members']), 1)
+                    self.assertFalse(pool1_update['pool']['members'])
+
+    def test_delete_member(self):
+        self._fake_router_edge_mapping()
+        with contextlib.nested(
+            self.subnet(),
+            self.pool(),
+            self.health_monitor()
+        ) as (subnet, pool, monitor):
+            pool_id = pool['pool']['id']
+            net_id = subnet['subnet']['network_id']
+            self._set_net_external(net_id)
+            self.plugin.create_pool_health_monitor(
+                context.get_admin_context(),
+                monitor, pool['pool']['id']
+            )
+            with self.vip(
+                router_id=self.router_id,
+                pool=pool, subnet=subnet):
+                with self.member(pool_id=pool_id,
+                                 no_delete=True) as member:
+                    req = self.new_delete_request('members',
+                                                  member['member']['id'])
+                    res = req.get_response(self.ext_api)
+                    self.assertEqual(res.status_int, 204)
+                    pool_update = self._show_pool(pool['pool']['id'])
+                    self.assertFalse(pool_update['pool']['members'])
index adc6bcee8c1f9b0c46eafd496043e41459e468a8..be6c11fc1a3ff510b7cbe1c75439582518b00275 100644 (file)
@@ -124,8 +124,7 @@ class NiciraPluginV2TestCase(test_plugin.NeutronDbPluginV2TestCase):
                                                   ext_mgr=ext_mgr)
         cfg.CONF.set_override('metadata_mode', None, 'NVP')
         self.addCleanup(self.fc.reset_all)
-        self.addCleanup(self.mock_nvpapi.stop)
-        self.addCleanup(patch_sync.stop)
+        self.addCleanup(mock.patch.stopall)
 
 
 class TestNiciraBasicGet(test_plugin.TestBasicGet, NiciraPluginV2TestCase):
index 3435c3b46053602438930f4fa3bd8074d64d2393..e6999dd69d40a23620269a088abbfd57cb7afa59 100644 (file)
@@ -48,6 +48,11 @@ class FakeVcns(object):
                 "firewallRules": []
             }
         }
+        self._fake_virtualservers_dict = {}
+        self._fake_pools_dict = {}
+        self._fake_monitors_dict = {}
+        self._fake_app_profiles_dict = {}
+        self._fake_loadbalancer_config = {}
 
     def set_fake_nvpapi(self, fake_nvpapi):
         self._fake_nvpapi = fake_nvpapi
@@ -363,6 +368,173 @@ class FakeVcns(object):
                 break
         return self.return_helper(header, response)
 
+    #
+    #Fake Edge LBAAS call
+    #
+    def create_vip(self, edge_id, vip_new):
+        if not self._fake_virtualservers_dict.get(edge_id):
+            self._fake_virtualservers_dict[edge_id] = {}
+        vip_vseid = uuidutils.generate_uuid()
+        self._fake_virtualservers_dict[edge_id][vip_vseid] = vip_new
+        header = {
+            'status': 204,
+            'location': "https://host/api/4.0/edges/edge_id"
+                        "/loadbalancer/config/%s" % vip_vseid}
+        response = ""
+        return self.return_helper(header, response)
+
+    def get_vip(self, edge_id, vip_vseid):
+        header = {'status': 404}
+        response = ""
+        if not self._fake_virtualservers_dict.get(edge_id) or (
+            not self._fake_virtualservers_dict[edge_id].get(vip_vseid)):
+            return self.return_helper(header, response)
+        header = {'status': 204}
+        response = self._fake_virtualservers_dict[edge_id][vip_vseid]
+        return self.return_helper(header, response)
+
+    def update_vip(self, edge_id, vip_vseid, vip_new):
+        header = {'status': 404}
+        response = ""
+        if not self._fake_virtualservers_dict.get(edge_id) or (
+            not self._fake_virtualservers_dict[edge_id].get(vip_vseid)):
+            return self.return_helper(header, response)
+        header = {'status': 204}
+        self._fake_virtualservers_dict[edge_id][vip_vseid].update(
+            vip_new)
+        return self.return_helper(header, response)
+
+    def delete_vip(self, edge_id, vip_vseid):
+        header = {'status': 404}
+        response = ""
+        if not self._fake_virtualservers_dict.get(edge_id) or (
+            not self._fake_virtualservers_dict[edge_id].get(vip_vseid)):
+            return self.return_helper(header, response)
+        header = {'status': 204}
+        del self._fake_virtualservers_dict[edge_id][vip_vseid]
+        return self.return_helper(header, response)
+
+    def create_pool(self, edge_id, pool_new):
+        if not self._fake_pools_dict.get(edge_id):
+            self._fake_pools_dict[edge_id] = {}
+        pool_vseid = uuidutils.generate_uuid()
+        self._fake_pools_dict[edge_id][pool_vseid] = pool_new
+        header = {
+            'status': 204,
+            'location': "https://host/api/4.0/edges/edge_id"
+                        "/loadbalancer/config/%s" % pool_vseid}
+        response = ""
+        return self.return_helper(header, response)
+
+    def get_pool(self, edge_id, pool_vseid):
+        header = {'status': 404}
+        response = ""
+        if not self._fake_pools_dict.get(edge_id) or (
+            not self._fake_pools_dict[edge_id].get(pool_vseid)):
+            return self.return_helper(header, response)
+        header = {'status': 204}
+        response = self._fake_pools_dict[edge_id][pool_vseid]
+        return self.return_helper(header, response)
+
+    def update_pool(self, edge_id, pool_vseid, pool_new):
+        header = {'status': 404}
+        response = ""
+        if not self._fake_pools_dict.get(edge_id) or (
+            not self._fake_pools_dict[edge_id].get(pool_vseid)):
+            return self.return_helper(header, response)
+        header = {'status': 204}
+        self._fake_pools_dict[edge_id][pool_vseid].update(
+            pool_new)
+        return self.return_helper(header, response)
+
+    def delete_pool(self, edge_id, pool_vseid):
+        header = {'status': 404}
+        response = ""
+        if not self._fake_pools_dict.get(edge_id) or (
+            not self._fake_pools_dict[edge_id].get(pool_vseid)):
+            return self.return_helper(header, response)
+        header = {'status': 204}
+        del self._fake_pools_dict[edge_id][pool_vseid]
+        return self.return_helper(header, response)
+
+    def create_health_monitor(self, edge_id, monitor_new):
+        if not self._fake_monitors_dict.get(edge_id):
+            self._fake_monitors_dict[edge_id] = {}
+        monitor_vseid = uuidutils.generate_uuid()
+        self._fake_monitors_dict[edge_id][monitor_vseid] = monitor_new
+        header = {
+            'status': 204,
+            'location': "https://host/api/4.0/edges/edge_id"
+                        "/loadbalancer/config/%s" % monitor_vseid}
+        response = ""
+        return self.return_helper(header, response)
+
+    def get_health_monitor(self, edge_id, monitor_vseid):
+        header = {'status': 404}
+        response = ""
+        if not self._fake_monitors_dict.get(edge_id) or (
+            not self._fake_monitors_dict[edge_id].get(monitor_vseid)):
+            return self.return_helper(header, response)
+        header = {'status': 204}
+        response = self._fake_monitors_dict[edge_id][monitor_vseid]
+        return self.return_helper(header, response)
+
+    def update_health_monitor(self, edge_id, monitor_vseid, monitor_new):
+        header = {'status': 404}
+        response = ""
+        if not self._fake_monitors_dict.get(edge_id) or (
+            not self._fake_monitors_dict[edge_id].get(monitor_vseid)):
+            return self.return_helper(header, response)
+        header = {'status': 204}
+        self._fake_monitors_dict[edge_id][monitor_vseid].update(
+            monitor_new)
+        return self.return_helper(header, response)
+
+    def delete_health_monitor(self, edge_id, monitor_vseid):
+        header = {'status': 404}
+        response = ""
+        if not self._fake_monitors_dict.get(edge_id) or (
+            not self._fake_monitors_dict[edge_id].get(monitor_vseid)):
+            return self.return_helper(header, response)
+        header = {'status': 204}
+        del self._fake_monitors_dict[edge_id][monitor_vseid]
+        return self.return_helper(header, response)
+
+    def create_app_profile(self, edge_id, app_profile):
+        if not self._fake_app_profiles_dict.get(edge_id):
+            self._fake_app_profiles_dict[edge_id] = {}
+        app_profileid = uuidutils.generate_uuid()
+        self._fake_app_profiles_dict[edge_id][app_profileid] = app_profile
+        header = {
+            'status': 204,
+            'location': "https://host/api/4.0/edges/edge_id"
+                        "/loadbalancer/config/%s" % app_profileid}
+        response = ""
+        return self.return_helper(header, response)
+
+    def delete_app_profile(self, edge_id, app_profileid):
+        header = {'status': 404}
+        response = ""
+        if not self._fake_app_profiles_dict.get(edge_id) or (
+            not self._fake_app_profiles_dict[edge_id].get(app_profileid)):
+            return self.return_helper(header, response)
+        header = {'status': 204}
+        del self._fake_app_profiles_dict[edge_id][app_profileid]
+        return self.return_helper(header, response)
+
+    def get_loadbalancer_config(self, edge_id):
+        header = {'status': 204}
+        response = {'config': False}
+        if self._fake_loadbalancer_config[edge_id]:
+            response['config'] = self._fake_loadbalancer_config[edge_id]
+        return self.return_helper(header, response)
+
+    def enable_service_loadbalancer(self, edge_id, config):
+        header = {'status': 204}
+        response = ""
+        self._fake_loadbalancer_config[edge_id] = True
+        return self.return_helper(header, response)
+
     def return_helper(self, header, response):
         status = int(header['status'])
         if 200 <= status <= 300:
@@ -379,3 +551,8 @@ class FakeVcns(object):
         self._edges.clear()
         self._lswitches.clear()
         self.fake_firewall_dict = {}
+        self._fake_virtualservers_dict = {}
+        self._fake_pools_dict = {}
+        self._fake_monitors_dict = {}
+        self._fake_app_profiles_dict = {}
+        self._fake_loadbalancer_config = {}
diff --git a/neutron/tests/unit/nicira/vshield/test_loadbalancer_driver.py b/neutron/tests/unit/nicira/vshield/test_loadbalancer_driver.py
new file mode 100644 (file)
index 0000000..40a3d5c
--- /dev/null
@@ -0,0 +1,233 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# Copyright 2013 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.
+#
+# @author: linb, VMware
+
+import mock
+
+from neutron.common import config as n_config
+from neutron import context
+from neutron.openstack.common import uuidutils
+from neutron.plugins.nicira.dbexts import vcns_db
+from neutron.plugins.nicira.vshield import (
+    vcns_driver)
+from neutron.plugins.nicira.vshield.common import (
+    exceptions as vcns_exc)
+from neutron.tests.unit.db.loadbalancer import test_db_loadbalancer
+from neutron.tests.unit.nicira import get_fake_conf
+from neutron.tests.unit.nicira import VCNS_NAME
+from neutron.tests.unit.nicira.vshield import fake_vcns
+
+_uuid = uuidutils.generate_uuid
+
+VSE_ID = 'edge-1'
+POOL_MAP_INFO = {
+    'pool_id': None,
+    'edge_id': VSE_ID,
+    'pool_vseid': 'pool-1'}
+
+VCNS_CONFIG_FILE = get_fake_conf("vcns.ini.test")
+
+
+class VcnsDriverTestCase(test_db_loadbalancer.LoadBalancerPluginDbTestCase):
+
+    def vcns_loadbalancer_patch(self):
+        instance = self.mock_vcns.start()
+        instance.return_value.create_vip.side_effect = (
+            self.fc2.create_vip)
+        instance.return_value.get_vip.side_effect = (
+            self.fc2.get_vip)
+        instance.return_value.update_vip.side_effect = (
+            self.fc2.update_vip)
+        instance.return_value.delete_vip.side_effect = (
+            self.fc2.delete_vip)
+        instance.return_value.create_pool.side_effect = (
+            self.fc2.create_pool)
+        instance.return_value.get_pool.side_effect = (
+            self.fc2.get_pool)
+        instance.return_value.update_pool.side_effect = (
+            self.fc2.update_pool)
+        instance.return_value.delete_pool.side_effect = (
+            self.fc2.delete_pool)
+        instance.return_value.create_health_monitor.side_effect = (
+            self.fc2.create_health_monitor)
+        instance.return_value.get_health_monitor.side_effect = (
+            self.fc2.get_health_monitor)
+        instance.return_value.update_health_monitor.side_effect = (
+            self.fc2.update_health_monitor)
+        instance.return_value.delete_health_monitor.side_effect = (
+            self.fc2.delete_health_monitor)
+        instance.return_value.create_app_profile.side_effect = (
+            self.fc2.create_app_profile)
+        instance.return_value.delete_app_profile.side_effect = (
+            self.fc2.delete_app_profile)
+        self.pool_id = None
+        self.vip_id = None
+
+    def setUp(self):
+
+        n_config.parse(['--config-file', VCNS_CONFIG_FILE])
+        # mock vcns
+        self.fc2 = fake_vcns.FakeVcns(unique_router_name=False)
+        self.mock_vcns = mock.patch(VCNS_NAME, autospec=True)
+        self.vcns_loadbalancer_patch()
+
+        self.nvp_service_plugin_callback = mock.Mock()
+        self.driver = vcns_driver.VcnsDriver(self.nvp_service_plugin_callback)
+
+        super(VcnsDriverTestCase, self).setUp()
+        self.addCleanup(self.fc2.reset_all)
+        self.addCleanup(self.mock_vcns.stop)
+
+    def tearDown(self):
+        super(VcnsDriverTestCase, self).tearDown()
+
+
+class TestEdgeLbDriver(VcnsDriverTestCase):
+
+    def test_create_and_get_vip(self):
+        ctx = context.get_admin_context()
+        with self.pool(no_delete=True) as pool:
+            self.pool_id = pool['pool']['id']
+            POOL_MAP_INFO['pool_id'] = pool['pool']['id']
+            vcns_db.add_vcns_edge_pool_binding(ctx.session, POOL_MAP_INFO)
+            with self.vip(pool=pool) as res:
+                vip_create = res['vip']
+                self.driver.create_vip(ctx, VSE_ID, vip_create)
+                vip_get = self.driver.get_vip(ctx, vip_create['id'])
+                for k, v in vip_get.iteritems():
+                    self.assertEqual(vip_create[k], v)
+
+    def test_update_vip(self):
+        ctx = context.get_admin_context()
+        with self.pool(no_delete=True) as pool:
+            self.pool_id = pool['pool']['id']
+            POOL_MAP_INFO['pool_id'] = pool['pool']['id']
+            vcns_db.add_vcns_edge_pool_binding(ctx.session, POOL_MAP_INFO)
+            with self.vip(pool=pool) as res:
+                vip_create = res['vip']
+                self.driver.create_vip(ctx, VSE_ID, vip_create)
+                vip_update = {'id': vip_create['id'],
+                              'pool_id': pool['pool']['id'],
+                              'name': 'update_name',
+                              'description': 'description',
+                              'address': 'update_address',
+                              'port_id': 'update_port_id',
+                              'protocol_port': 'protocol_port',
+                              'protocol': 'update_protocol'}
+                self.driver.update_vip(ctx, vip_update)
+                vip_get = self.driver.get_vip(ctx, vip_create['id'])
+                for k, v in vip_get.iteritems():
+                        if k in vip_update:
+                            self.assertEqual(vip_update[k], v)
+
+    def test_delete_vip(self):
+        ctx = context.get_admin_context()
+        with self.pool(no_delete=True) as pool:
+            self.pool_id = pool['pool']['id']
+            POOL_MAP_INFO['pool_id'] = pool['pool']['id']
+            vcns_db.add_vcns_edge_pool_binding(ctx.session, POOL_MAP_INFO)
+            with self.vip(pool=pool) as res:
+                vip_create = res['vip']
+                self.driver.create_vip(ctx, VSE_ID, vip_create)
+                self.driver.delete_vip(ctx, vip_create['id'])
+                self.assertRaises(vcns_exc.VcnsNotFound,
+                                  self.driver.get_vip,
+                                  ctx,
+                                  vip_create['id'])
+
+    #Test Pool Operation
+    def test_create_and_get_pool(self):
+        ctx = context.get_admin_context()
+        with self.pool(no_delete=True) as p:
+            self.pool_id = p['pool']['id']
+            pool_create = p['pool']
+            self.driver.create_pool(ctx, VSE_ID, pool_create, [])
+            pool_get = self.driver.get_pool(ctx, pool_create['id'], VSE_ID)
+            for k, v in pool_get.iteritems():
+                self.assertEqual(pool_create[k], v)
+
+    def test_update_pool(self):
+        ctx = context.get_admin_context()
+        with self.pool(no_delete=True) as p:
+            self.pool_id = p['pool']['id']
+            pool_create = p['pool']
+            self.driver.create_pool(ctx, VSE_ID, pool_create, [])
+            pool_update = {'id': pool_create['id'],
+                           'lb_method': 'lb_method',
+                           'name': 'update_name',
+                           'members': [],
+                           'health_monitors': []}
+            self.driver.update_pool(ctx, VSE_ID, pool_update, [])
+            pool_get = self.driver.get_pool(ctx, pool_create['id'], VSE_ID)
+            for k, v in pool_get.iteritems():
+                    if k in pool_update:
+                        self.assertEqual(pool_update[k], v)
+
+    def test_delete_pool(self):
+        ctx = context.get_admin_context()
+        with self.pool(no_delete=True) as p:
+            self.pool_id = p['pool']['id']
+            pool_create = p['pool']
+            self.driver.create_pool(ctx, VSE_ID, pool_create, [])
+            self.driver.delete_pool(ctx, pool_create['id'], VSE_ID)
+            self.assertRaises(vcns_exc.VcnsNotFound,
+                              self.driver.get_pool,
+                              ctx,
+                              pool_create['id'],
+                              VSE_ID)
+
+    def test_create_and_get_monitor(self):
+        ctx = context.get_admin_context()
+        with self.health_monitor(no_delete=True) as m:
+            monitor_create = m['health_monitor']
+            self.driver.create_health_monitor(ctx, VSE_ID, monitor_create)
+            monitor_get = self.driver.get_health_monitor(
+                ctx, monitor_create['id'], VSE_ID)
+            for k, v in monitor_get.iteritems():
+                self.assertEqual(monitor_create[k], v)
+
+    def test_update_health_monitor(self):
+        ctx = context.get_admin_context()
+        with self.health_monitor(no_delete=True) as m:
+            monitor_create = m['health_monitor']
+            self.driver.create_health_monitor(
+                ctx, VSE_ID, monitor_create)
+            monitor_update = {'id': monitor_create['id'],
+                              'delay': 'new_delay',
+                              'timeout': "new_timeout",
+                              'type': 'type',
+                              'max_retries': "max_retries"}
+            self.driver.update_health_monitor(
+                ctx, VSE_ID, monitor_create, monitor_update)
+            monitor_get = self.driver.get_health_monitor(
+                ctx, monitor_create['id'], VSE_ID)
+            for k, v in monitor_get.iteritems():
+                    if k in monitor_update:
+                        self.assertEqual(monitor_update[k], v)
+
+    def test_delete_health_monitor(self):
+        ctx = context.get_admin_context()
+        with self.health_monitor(no_delete=True) as m:
+            monitor_create = m['health_monitor']
+            self.driver.create_health_monitor(ctx, VSE_ID, monitor_create)
+            self.driver.delete_health_monitor(
+                ctx, monitor_create['id'], VSE_ID)
+            self.assertRaises(vcns_exc.VcnsNotFound,
+                              self.driver.get_health_monitor,
+                              ctx,
+                              monitor_create['id'],
+                              VSE_ID)