¡@

Home 

OpenStack Study: netscaler_driver.py

OpenStack Index

**** CubicPower OpenStack Study ****

# Copyright 2014 Citrix Systems, Inc.

#

# Licensed under the Apache License, Version 2.0 (the "License"); you may

# not use this file except in compliance with the License. You may obtain

# a copy of the License at

#

# http://www.apache.org/licenses/LICENSE-2.0

#

# Unless required by applicable law or agreed to in writing, software

# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT

# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the

# License for the specific language governing permissions and limitations

# under the License.

from oslo.config import cfg

from neutron.api.v2 import attributes

from neutron.db.loadbalancer import loadbalancer_db

from neutron.openstack.common import log as logging

from neutron.plugins.common import constants

from neutron.services.loadbalancer.drivers import abstract_driver

from neutron.services.loadbalancer.drivers.netscaler import ncc_client

LOG = logging.getLogger(__name__)

NETSCALER_CC_OPTS = [

cfg.StrOpt(

'netscaler_ncc_uri',

help=_('The URL to reach the NetScaler Control Center Server.'),

),

cfg.StrOpt(

'netscaler_ncc_username',

help=_('Username to login to the NetScaler Control Center Server.'),

),

cfg.StrOpt(

'netscaler_ncc_password',

help=_('Password to login to the NetScaler Control Center Server.'),

)

]

cfg.CONF.register_opts(NETSCALER_CC_OPTS, 'netscaler_driver')

VIPS_RESOURCE = 'vips'

VIP_RESOURCE = 'vip'

POOLS_RESOURCE = 'pools'

POOL_RESOURCE = 'pool'

POOLMEMBERS_RESOURCE = 'members'

POOLMEMBER_RESOURCE = 'member'

MONITORS_RESOURCE = 'healthmonitors'

MONITOR_RESOURCE = 'healthmonitor'

POOLSTATS_RESOURCE = 'statistics'

PROV_SEGMT_ID = 'provider:segmentation_id'

PROV_NET_TYPE = 'provider:network_type'

DRIVER_NAME = 'netscaler_driver'

**** CubicPower OpenStack Study ****

class NetScalerPluginDriver(abstract_driver.LoadBalancerAbstractDriver):

"""NetScaler LBaaS Plugin driver class."""

**** CubicPower OpenStack Study ****

    def __init__(self, plugin):

        self.plugin = plugin

        ncc_uri = cfg.CONF.netscaler_driver.netscaler_ncc_uri

        ncc_username = cfg.CONF.netscaler_driver.netscaler_ncc_username

        ncc_password = cfg.CONF.netscaler_driver.netscaler_ncc_password

        self.client = ncc_client.NSClient(ncc_uri,

                                          ncc_username,

                                          ncc_password)

**** CubicPower OpenStack Study ****

    def create_vip(self, context, vip):

        """Create a vip on a NetScaler device."""

        network_info = self._get_vip_network_info(context, vip)

        ncc_vip = self._prepare_vip_for_creation(vip)

        ncc_vip = dict(ncc_vip.items() + network_info.items())

        msg = _("NetScaler driver vip creation: %s") % repr(ncc_vip)

        LOG.debug(msg)

        status = constants.ACTIVE

        try:

            self.client.create_resource(context.tenant_id, VIPS_RESOURCE,

                                        VIP_RESOURCE, ncc_vip)

        except ncc_client.NCCException:

            status = constants.ERROR

        self.plugin.update_status(context, loadbalancer_db.Vip, vip["id"],

                                  status)

**** CubicPower OpenStack Study ****

    def update_vip(self, context, old_vip, vip):

        """Update a vip on a NetScaler device."""

        update_vip = self._prepare_vip_for_update(vip)

        resource_path = "%s/%s" % (VIPS_RESOURCE, vip["id"])

        msg = (_("NetScaler driver vip %(vip_id)s update: %(vip_obj)s") %

               {"vip_id": vip["id"], "vip_obj": repr(vip)})

        LOG.debug(msg)

        status = constants.ACTIVE

        try:

            self.client.update_resource(context.tenant_id, resource_path,

                                        VIP_RESOURCE, update_vip)

        except ncc_client.NCCException:

            status = constants.ERROR

        self.plugin.update_status(context, loadbalancer_db.Vip, old_vip["id"],

                                  status)

**** CubicPower OpenStack Study ****

    def delete_vip(self, context, vip):

        """Delete a vip on a NetScaler device."""

        resource_path = "%s/%s" % (VIPS_RESOURCE, vip["id"])

        msg = _("NetScaler driver vip removal: %s") % vip["id"]

        LOG.debug(msg)

        try:

            self.client.remove_resource(context.tenant_id, resource_path)

        except ncc_client.NCCException:

            self.plugin.update_status(context, loadbalancer_db.Vip,

                                      vip["id"],

                                      constants.ERROR)

        else:

            self.plugin._delete_db_vip(context, vip['id'])

**** CubicPower OpenStack Study ****

    def create_pool(self, context, pool):

        """Create a pool on a NetScaler device."""

        network_info = self._get_pool_network_info(context, pool)

        #allocate a snat port/ipaddress on the subnet if one doesn't exist

        self._create_snatport_for_subnet_if_not_exists(context,

                                                       pool['tenant_id'],

                                                       pool['subnet_id'],

                                                       network_info)

        ncc_pool = self._prepare_pool_for_creation(pool)

        ncc_pool = dict(ncc_pool.items() + network_info.items())

        msg = _("NetScaler driver pool creation: %s") % repr(ncc_pool)

        LOG.debug(msg)

        status = constants.ACTIVE

        try:

            self.client.create_resource(context.tenant_id, POOLS_RESOURCE,

                                        POOL_RESOURCE, ncc_pool)

        except ncc_client.NCCException:

            status = constants.ERROR

        self.plugin.update_status(context, loadbalancer_db.Pool,

                                  ncc_pool["id"], status)

**** CubicPower OpenStack Study ****

    def update_pool(self, context, old_pool, pool):

        """Update a pool on a NetScaler device."""

        ncc_pool = self._prepare_pool_for_update(pool)

        resource_path = "%s/%s" % (POOLS_RESOURCE, old_pool["id"])

        msg = (_("NetScaler driver pool %(pool_id)s update: %(pool_obj)s") %

               {"pool_id": old_pool["id"], "pool_obj": repr(ncc_pool)})

        LOG.debug(msg)

        status = constants.ACTIVE

        try:

            self.client.update_resource(context.tenant_id, resource_path,

                                        POOL_RESOURCE, ncc_pool)

        except ncc_client.NCCException:

            status = constants.ERROR

        self.plugin.update_status(context, loadbalancer_db.Pool,

                                  old_pool["id"], status)

**** CubicPower OpenStack Study ****

    def delete_pool(self, context, pool):

        """Delete a pool on a NetScaler device."""

        resource_path = "%s/%s" % (POOLS_RESOURCE, pool['id'])

        msg = _("NetScaler driver pool removal: %s") % pool["id"]

        LOG.debug(msg)

        try:

            self.client.remove_resource(context.tenant_id, resource_path)

        except ncc_client.NCCException:

            self.plugin.update_status(context, loadbalancer_db.Pool,

                                      pool["id"],

                                      constants.ERROR)

        else:

            self.plugin._delete_db_pool(context, pool['id'])

            self._remove_snatport_for_subnet_if_not_used(context,

                                                         pool['tenant_id'],

                                                         pool['subnet_id'])

**** CubicPower OpenStack Study ****

    def create_member(self, context, member):

        """Create a pool member on a NetScaler device."""

        ncc_member = self._prepare_member_for_creation(member)

        msg = (_("NetScaler driver poolmember creation: %s") %

               repr(ncc_member))

        LOG.info(msg)

        status = constants.ACTIVE

        try:

            self.client.create_resource(context.tenant_id,

                                        POOLMEMBERS_RESOURCE,

                                        POOLMEMBER_RESOURCE,

                                        ncc_member)

        except ncc_client.NCCException:

            status = constants.ERROR

        self.plugin.update_status(context, loadbalancer_db.Member,

                                  member["id"], status)

**** CubicPower OpenStack Study ****

    def update_member(self, context, old_member, member):

        """Update a pool member on a NetScaler device."""

        ncc_member = self._prepare_member_for_update(member)

        resource_path = "%s/%s" % (POOLMEMBERS_RESOURCE, old_member["id"])

        msg = (_("NetScaler driver poolmember %(member_id)s update:"

                 " %(member_obj)s") %

               {"member_id": old_member["id"],

                "member_obj": repr(ncc_member)})

        LOG.debug(msg)

        status = constants.ACTIVE

        try:

            self.client.update_resource(context.tenant_id, resource_path,

                                        POOLMEMBER_RESOURCE, ncc_member)

        except ncc_client.NCCException:

            status = constants.ERROR

        self.plugin.update_status(context, loadbalancer_db.Member,

                                  old_member["id"], status)

**** CubicPower OpenStack Study ****

    def delete_member(self, context, member):

        """Delete a pool member on a NetScaler device."""

        resource_path = "%s/%s" % (POOLMEMBERS_RESOURCE, member['id'])

        msg = (_("NetScaler driver poolmember removal: %s") %

               member["id"])

        LOG.debug(msg)

        try:

            self.client.remove_resource(context.tenant_id, resource_path)

        except ncc_client.NCCException:

            self.plugin.update_status(context, loadbalancer_db.Member,

                                      member["id"],

                                      constants.ERROR)

        else:

            self.plugin._delete_db_member(context, member['id'])

**** CubicPower OpenStack Study ****

    def create_pool_health_monitor(self, context, health_monitor, pool_id):

        """Create a pool health monitor on a NetScaler device."""

        ncc_hm = self._prepare_healthmonitor_for_creation(health_monitor,

                                                          pool_id)

        resource_path = "%s/%s/%s" % (POOLS_RESOURCE, pool_id,

                                      MONITORS_RESOURCE)

        msg = (_("NetScaler driver healthmonitor creation for pool %(pool_id)s"

                 ": %(monitor_obj)s") %

               {"pool_id": pool_id,

                "monitor_obj": repr(ncc_hm)})

        LOG.debug(msg)

        status = constants.ACTIVE

        try:

            self.client.create_resource(context.tenant_id, resource_path,

                                        MONITOR_RESOURCE,

                                        ncc_hm)

        except ncc_client.NCCException:

            status = constants.ERROR

        self.plugin.update_pool_health_monitor(context,

                                               health_monitor['id'],

                                               pool_id,

                                               status, "")

**** CubicPower OpenStack Study ****

    def update_pool_health_monitor(self, context, old_health_monitor,

                                   health_monitor, pool_id):

        """Update a pool health monitor on a NetScaler device."""

        ncc_hm = self._prepare_healthmonitor_for_update(health_monitor)

        resource_path = "%s/%s" % (MONITORS_RESOURCE,

                                   old_health_monitor["id"])

        msg = (_("NetScaler driver healthmonitor %(monitor_id)s update: "

                 "%(monitor_obj)s") %

               {"monitor_id": old_health_monitor["id"],

                "monitor_obj": repr(ncc_hm)})

        LOG.debug(msg)

        status = constants.ACTIVE

        try:

            self.client.update_resource(context.tenant_id, resource_path,

                                        MONITOR_RESOURCE, ncc_hm)

        except ncc_client.NCCException:

            status = constants.ERROR

        self.plugin.update_pool_health_monitor(context,

                                               old_health_monitor['id'],

                                               pool_id,

                                               status, "")

**** CubicPower OpenStack Study ****

    def delete_pool_health_monitor(self, context, health_monitor, pool_id):

        """Delete a pool health monitor on a NetScaler device."""

        resource_path = "%s/%s/%s/%s" % (POOLS_RESOURCE, pool_id,

                                         MONITORS_RESOURCE,

                                         health_monitor["id"])

        msg = (_("NetScaler driver healthmonitor %(monitor_id)s"

                 "removal for pool %(pool_id)s") %

               {"monitor_id": health_monitor["id"],

                "pool_id": pool_id})

        LOG.debug(msg)

        try:

            self.client.remove_resource(context.tenant_id, resource_path)

        except ncc_client.NCCException:

            self.plugin.update_pool_health_monitor(context,

                                                   health_monitor['id'],

                                                   pool_id,

                                                   constants.ERROR, "")

        else:

            self.plugin._delete_db_pool_health_monitor(context,

                                                       health_monitor['id'],

                                                       pool_id)

**** CubicPower OpenStack Study ****

    def stats(self, context, pool_id):

        """Retrieve pool statistics from the NetScaler device."""

        resource_path = "%s/%s" % (POOLSTATS_RESOURCE, pool_id)

        msg = _("NetScaler driver pool stats retrieval: %s") % pool_id

        LOG.debug(msg)

        try:

            stats = self.client.retrieve_resource(context.tenant_id,

                                                  resource_path)[1]

        except ncc_client.NCCException:

            self.plugin.update_status(context, loadbalancer_db.Pool,

                                      pool_id, constants.ERROR)

        else:

            return stats

**** CubicPower OpenStack Study ****

    def _prepare_vip_for_creation(self, vip):

        creation_attrs = {

            'id': vip['id'],

            'tenant_id': vip['tenant_id'],

            'protocol': vip['protocol'],

            'address': vip['address'],

            'protocol_port': vip['protocol_port'],

        }

        if 'session_persistence' in vip:

            creation_attrs['session_persistence'] = vip['session_persistence']

        update_attrs = self._prepare_vip_for_update(vip)

        creation_attrs.update(update_attrs)

        return creation_attrs

**** CubicPower OpenStack Study ****

    def _prepare_vip_for_update(self, vip):

        return {

            'name': vip['name'],

            'description': vip['description'],

            'pool_id': vip['pool_id'],

            'connection_limit': vip['connection_limit'],

            'admin_state_up': vip['admin_state_up']

        }

**** CubicPower OpenStack Study ****

    def _prepare_pool_for_creation(self, pool):

        creation_attrs = {

            'id': pool['id'],

            'tenant_id': pool['tenant_id'],

            'vip_id': pool['vip_id'],

            'protocol': pool['protocol'],

            'subnet_id': pool['subnet_id'],

        }

        update_attrs = self._prepare_pool_for_update(pool)

        creation_attrs.update(update_attrs)

        return creation_attrs

**** CubicPower OpenStack Study ****

    def _prepare_pool_for_update(self, pool):

        return {

            'name': pool['name'],

            'description': pool['description'],

            'lb_method': pool['lb_method'],

            'admin_state_up': pool['admin_state_up']

        }

**** CubicPower OpenStack Study ****

    def _prepare_member_for_creation(self, member):

        creation_attrs = {

            'id': member['id'],

            'tenant_id': member['tenant_id'],

            'address': member['address'],

            'protocol_port': member['protocol_port'],

        }

        update_attrs = self._prepare_member_for_update(member)

        creation_attrs.update(update_attrs)

        return creation_attrs

**** CubicPower OpenStack Study ****

    def _prepare_member_for_update(self, member):

        return {

            'pool_id': member['pool_id'],

            'weight': member['weight'],

            'admin_state_up': member['admin_state_up']

        }

**** CubicPower OpenStack Study ****

    def _prepare_healthmonitor_for_creation(self, health_monitor, pool_id):

        creation_attrs = {

            'id': health_monitor['id'],

            'tenant_id': health_monitor['tenant_id'],

            'type': health_monitor['type'],

        }

        update_attrs = self._prepare_healthmonitor_for_update(health_monitor)

        creation_attrs.update(update_attrs)

        return creation_attrs

**** CubicPower OpenStack Study ****

    def _prepare_healthmonitor_for_update(self, health_monitor):

        ncc_hm = {

            'delay': health_monitor['delay'],

            'timeout': health_monitor['timeout'],

            'max_retries': health_monitor['max_retries'],

            'admin_state_up': health_monitor['admin_state_up']

        }

        if health_monitor['type'] in ['HTTP', 'HTTPS']:

            ncc_hm['http_method'] = health_monitor['http_method']

            ncc_hm['url_path'] = health_monitor['url_path']

            ncc_hm['expected_codes'] = health_monitor['expected_codes']

        return ncc_hm

**** CubicPower OpenStack Study ****

    def _get_network_info(self, context, entity):

        network_info = {}

        subnet_id = entity['subnet_id']

        subnet = self.plugin._core_plugin.get_subnet(context, subnet_id)

        network_id = subnet['network_id']

        network = self.plugin._core_plugin.get_network(context, network_id)

        network_info['network_id'] = network_id

        network_info['subnet_id'] = subnet_id

        if PROV_NET_TYPE in network:

            network_info['network_type'] = network[PROV_NET_TYPE]

        if PROV_SEGMT_ID in network:

            network_info['segmentation_id'] = network[PROV_SEGMT_ID]

        return network_info

**** CubicPower OpenStack Study ****

    def _get_vip_network_info(self, context, vip):

        network_info = self._get_network_info(context, vip)

        network_info['port_id'] = vip['port_id']

        return network_info

**** CubicPower OpenStack Study ****

    def _get_pool_network_info(self, context, pool):

        return self._get_network_info(context, pool)

**** CubicPower OpenStack Study ****

    def _get_pools_on_subnet(self, context, tenant_id, subnet_id):

        filter_dict = {'subnet_id': [subnet_id], 'tenant_id': [tenant_id]}

        return self.plugin.get_pools(context, filters=filter_dict)

**** CubicPower OpenStack Study ****

    def _get_snatport_for_subnet(self, context, tenant_id, subnet_id):

        device_id = '_lb-snatport-' + subnet_id

        subnet = self.plugin._core_plugin.get_subnet(context, subnet_id)

        network_id = subnet['network_id']

        msg = (_("Filtering ports based on network_id=%(network_id)s, "

                 "tenant_id=%(tenant_id)s, device_id=%(device_id)s") %

               {'network_id': network_id,

                'tenant_id': tenant_id,

                'device_id': device_id})

        LOG.debug(msg)

        filter_dict = {

            'network_id': [network_id],

            'tenant_id': [tenant_id],

            'device_id': [device_id],

            'device-owner': [DRIVER_NAME]

        }

        ports = self.plugin._core_plugin.get_ports(context,

                                                   filters=filter_dict)

        if ports:

            msg = _("Found an existing SNAT port for subnet %s") % subnet_id

            LOG.info(msg)

            return ports[0]

        msg = _("Found no SNAT ports for subnet %s") % subnet_id

        LOG.info(msg)

**** CubicPower OpenStack Study ****

    def _create_snatport_for_subnet(self, context, tenant_id, subnet_id,

                                    ip_address):

        subnet = self.plugin._core_plugin.get_subnet(context, subnet_id)

        fixed_ip = {'subnet_id': subnet['id']}

        if ip_address and ip_address != attributes.ATTR_NOT_SPECIFIED:

            fixed_ip['ip_address'] = ip_address

        port_data = {

            'tenant_id': tenant_id,

            'name': '_lb-snatport-' + subnet_id,

            'network_id': subnet['network_id'],

            'mac_address': attributes.ATTR_NOT_SPECIFIED,

            'admin_state_up': False,

            'device_id': '_lb-snatport-' + subnet_id,

            'device_owner': DRIVER_NAME,

            'fixed_ips': [fixed_ip],

        }

        port = self.plugin._core_plugin.create_port(context,

                                                    {'port': port_data})

        msg = _("Created SNAT port: %s") % repr(port)

        LOG.info(msg)

        return port

**** CubicPower OpenStack Study ****

    def _remove_snatport_for_subnet(self, context, tenant_id, subnet_id):

        port = self._get_snatport_for_subnet(context, tenant_id, subnet_id)

        if port:

            self.plugin._core_plugin.delete_port(context, port['id'])

            msg = _("Removed SNAT port: %s") % repr(port)

            LOG.info(msg)

**** CubicPower OpenStack Study ****

    def _create_snatport_for_subnet_if_not_exists(self, context, tenant_id,

                                                  subnet_id, network_info):

        port = self._get_snatport_for_subnet(context, tenant_id, subnet_id)

        if not port:

            msg = _("No SNAT port found for subnet %s."

                    " Creating one...") % subnet_id

            LOG.info(msg)

            port = self._create_snatport_for_subnet(context, tenant_id,

                                                    subnet_id,

                                                    ip_address=None)

        network_info['port_id'] = port['id']

        network_info['snat_ip'] = port['fixed_ips'][0]['ip_address']

        msg = _("SNAT port: %s") % repr(port)

        LOG.info(msg)

**** CubicPower OpenStack Study ****

    def _remove_snatport_for_subnet_if_not_used(self, context, tenant_id,

                                                subnet_id):

        pools = self._get_pools_on_subnet(context, tenant_id, subnet_id)

        if not pools:

            #No pools left on the old subnet.

            #We can remove the SNAT port/ipaddress

            self._remove_snatport_for_subnet(context, tenant_id, subnet_id)

            msg = _("Removing SNAT port for subnet %s "

                    "as this is the last pool using it...") % subnet_id

            LOG.info(msg)