¡@

Home 

OpenStack Study: sdnve_neutron_plugin.py

OpenStack Index

**** CubicPower OpenStack Study ****

# Copyright 2014 IBM Corp.

#

# All Rights Reserved.

#

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

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

# a copy of the License at

#

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

#

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

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

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

# License for the specific language governing permissions and limitations

# under the License.

#

# @author: Mohammad Banikazemi, IBM Corp.

import functools

from oslo.config import cfg

from neutron.common import constants as q_const

from neutron.common import exceptions as n_exc

from neutron.common import rpc as q_rpc

from neutron.common import topics

from neutron.db import agents_db

from neutron.db import db_base_plugin_v2

from neutron.db import external_net_db

from neutron.db import l3_gwmode_db

from neutron.db import portbindings_db

from neutron.extensions import portbindings

from neutron.openstack.common import excutils

from neutron.openstack.common import log as logging

from neutron.openstack.common import rpc

from neutron.openstack.common.rpc import proxy

from neutron.plugins.ibm.common import config # noqa

from neutron.plugins.ibm.common import constants

from neutron.plugins.ibm.common import exceptions as sdnve_exc

from neutron.plugins.ibm import sdnve_api as sdnve

from neutron.plugins.ibm import sdnve_api_fake as sdnve_fake

LOG = logging.getLogger(__name__)

**** CubicPower OpenStack Study ****

class SdnveRpcCallbacks():

**** CubicPower OpenStack Study ****

    def __init__(self, notifier):

        self.notifier = notifier  # used to notify the agent

**** CubicPower OpenStack Study ****

    def create_rpc_dispatcher(self):

        '''Get the rpc dispatcher for this manager.

        If a manager would like to set an rpc API version, or support more than

        one class as the target of rpc messages, override this method.

        '''

        return q_rpc.PluginRpcDispatcher([self,

                                          agents_db.AgentExtRpcCallback()])

**** CubicPower OpenStack Study ****

    def sdnve_info(self, rpc_context, **kwargs):

        '''Update new information.'''

        info = kwargs.get('info')

        # Notify all other listening agents

        self.notifier.info_update(rpc_context, info)

        return info

**** CubicPower OpenStack Study ****

class AgentNotifierApi(proxy.RpcProxy):

'''Agent side of the SDN-VE rpc API.'''

BASE_RPC_API_VERSION = '1.0'

**** CubicPower OpenStack Study ****

    def __init__(self, topic):

        super(AgentNotifierApi, self).__init__(

            topic=topic, default_version=self.BASE_RPC_API_VERSION)

        self.topic_info_update = topics.get_topic_name(topic,

                                                       constants.INFO,

                                                       topics.UPDATE)

**** CubicPower OpenStack Study ****

    def info_update(self, context, info):

        self.fanout_cast(context,

                         self.make_msg('info_update',

                                       info=info),

                         topic=self.topic_info_update)

def _ha(func):

    '''Supports the high availability feature of the controller.'''

    @functools.wraps(func)

**** CubicPower OpenStack Study ****

def _ha(func):

    '''Supports the high availability feature of the controller.'''

    @functools.wraps(func)

**** CubicPower OpenStack Study ****

    def hawrapper(self, *args, **kwargs):

        '''This wrapper sets the new controller if necessary

        When a controller is detected to be not responding, and a

        new controller is chosen to be used in its place, this decorator

        makes sure the existing integration bridges are set to point

        to the new controleer by calling the set_controller method.

        '''

        ret_func = func(self, *args, **kwargs)

        self.set_controller(args[0])

        return ret_func

    return hawrapper

**** CubicPower OpenStack Study ****

class SdnvePluginV2(db_base_plugin_v2.NeutronDbPluginV2, external_net_db.External_net_db_mixin, portbindings_db.PortBindingMixin, l3_gwmode_db.L3_NAT_db_mixin, agents_db.AgentDbMixin, ):

'''

Implement the Neutron abstractions using SDN-VE SDN Controller.

'''

__native_bulk_support = False

__native_pagination_support = False

__native_sorting_support = False

supported_extension_aliases = ["binding", "router", "external-net",

"agent"]

**** CubicPower OpenStack Study ****

    def __init__(self, configfile=None):

        self.base_binding_dict = {

            portbindings.VIF_TYPE: portbindings.VIF_TYPE_OVS,

            portbindings.VIF_DETAILS: {portbindings.CAP_PORT_FILTER: False}}

        super(SdnvePluginV2, self).__init__()

        self.setup_rpc()

        self.sdnve_controller_select()

        if self.fake_controller:

            self.sdnve_client = sdnve_fake.FakeClient()

        else:

            self.sdnve_client = sdnve.Client()

**** CubicPower OpenStack Study ****

    def sdnve_controller_select(self):

        self.fake_controller = cfg.CONF.SDNVE.use_fake_controller

**** CubicPower OpenStack Study ****

    def setup_rpc(self):

        # RPC support

        self.topic = topics.PLUGIN

        self.conn = rpc.create_connection(new=True)

        self.notifier = AgentNotifierApi(topics.AGENT)

        self.callbacks = SdnveRpcCallbacks(self.notifier)

        self.dispatcher = self.callbacks.create_rpc_dispatcher()

        self.conn.create_consumer(self.topic, self.dispatcher,

                                  fanout=False)

        # Consume from all consumers in a thread

        self.conn.consume_in_thread()

**** CubicPower OpenStack Study ****

    def _update_base_binding_dict(self, tenant_type):

        if tenant_type == constants.TENANT_TYPE_OVERLAY:

            self.base_binding_dict[

                portbindings.VIF_TYPE] = portbindings.VIF_TYPE_BRIDGE

        if tenant_type == constants.TENANT_TYPE_OF:

            self.base_binding_dict[

                portbindings.VIF_TYPE] = portbindings.VIF_TYPE_OVS

**** CubicPower OpenStack Study ****

    def set_controller(self, context):

        LOG.info(_("Set a new controller if needed."))

        new_controller = self.sdnve_client.sdnve_get_controller()

        if new_controller:

            self.notifier.info_update(

                context,

                {'new_controller': new_controller})

            LOG.info(_("Set the controller to a new controller: %s"),

                     new_controller)

**** CubicPower OpenStack Study ****

    def _process_request(self, request, current):

        new_request = dict(

            (k, v) for k, v in request.items()

            if v != current.get(k))

        msg = _("Original SDN-VE HTTP request: %(orig)s; New request: %(new)s")

        LOG.debug(msg, {'orig': request, 'new': new_request})

        return new_request

    #

    # Network

    #

    @_ha

**** CubicPower OpenStack Study ****

    def create_network(self, context, network):

        LOG.debug(_("Create network in progress: %r"), network)

        session = context.session

        tenant_id = self._get_tenant_id_for_create(context, network['network'])

        # Create a new SDN-VE tenant if need be

        sdnve_tenant = self.sdnve_client.sdnve_check_and_create_tenant(

            tenant_id)

        if sdnve_tenant is None:

            raise sdnve_exc.SdnveException(

                msg=_('Create net failed: no SDN-VE tenant.'))

        with session.begin(subtransactions=True):

            net = super(SdnvePluginV2, self).create_network(context, network)

            self._process_l3_create(context, net, network['network'])

        # Create SDN-VE network

        (res, data) = self.sdnve_client.sdnve_create('network', net)

        if res not in constants.HTTP_ACCEPTABLE:

            super(SdnvePluginV2, self).delete_network(context, net['id'])

            raise sdnve_exc.SdnveException(

                msg=(_('Create net failed in SDN-VE: %s') % res))

        LOG.debug(_("Created network: %s"), net['id'])

        return net

    @_ha

**** CubicPower OpenStack Study ****

    def update_network(self, context, id, network):

        LOG.debug(_("Update network in progress: %r"), network)

        session = context.session

        processed_request = {}

        with session.begin(subtransactions=True):

            original_network = super(SdnvePluginV2, self).get_network(

                context, id)

            processed_request['network'] = self._process_request(

                network['network'], original_network)

            net = super(SdnvePluginV2, self).update_network(

                context, id, network)

            self._process_l3_update(context, net, network['network'])

        if processed_request['network']:

            (res, data) = self.sdnve_client.sdnve_update(

                'network', id, processed_request['network'])

            if res not in constants.HTTP_ACCEPTABLE:

                net = super(SdnvePluginV2, self).update_network(

                    context, id, {'network': original_network})

                raise sdnve_exc.SdnveException(

                    msg=(_('Update net failed in SDN-VE: %s') % res))

        return net

    @_ha

**** CubicPower OpenStack Study ****

    def delete_network(self, context, id):

        LOG.debug(_("Delete network in progress: %s"), id)

        super(SdnvePluginV2, self).delete_network(context, id)

        (res, data) = self.sdnve_client.sdnve_delete('network', id)

        if res not in constants.HTTP_ACCEPTABLE:

            LOG.error(

                _("Delete net failed after deleting the network in DB: %s"),

                res)

    @_ha

**** CubicPower OpenStack Study ****

    def get_network(self, context, id, fields=None):

        LOG.debug(_("Get network in progress: %s"), id)

        return super(SdnvePluginV2, self).get_network(context, id, fields)

    @_ha

**** CubicPower OpenStack Study ****

    def get_networks(self, context, filters=None, fields=None, sorts=None,

                     limit=None, marker=None, page_reverse=False):

        LOG.debug(_("Get networks in progress"))

        return super(SdnvePluginV2, self).get_networks(

            context, filters, fields, sorts, limit, marker, page_reverse)

    #

    # Port

    #

    @_ha

**** CubicPower OpenStack Study ****

    def create_port(self, context, port):

        LOG.debug(_("Create port in progress: %r"), port)

        session = context.session

        # Set port status as 'ACTIVE' to avoid needing the agent

        port['port']['status'] = q_const.PORT_STATUS_ACTIVE

        port_data = port['port']

        with session.begin(subtransactions=True):

            port = super(SdnvePluginV2, self).create_port(context, port)

            if 'id' not in port:

                return port

            # If the tenant_id is set to '' by create_port, add the id to

            # the request being sent to the controller as the controller

            # requires a tenant id

            tenant_id = port.get('tenant_id')

            if not tenant_id:

                LOG.debug(_("Create port does not have tenant id info"))

                original_network = super(SdnvePluginV2, self).get_network(

                    context, port['network_id'])

                original_tenant_id = original_network['tenant_id']

                port['tenant_id'] = original_tenant_id

                LOG.debug(

                    _("Create port does not have tenant id info; "

                      "obtained is: %s"),

                    port['tenant_id'])

            os_tenant_id = tenant_id

            id_na, tenant_type = self.sdnve_client.sdnve_get_tenant_byid(

                os_tenant_id)

            self._update_base_binding_dict(tenant_type)

            self._process_portbindings_create_and_update(context,

                                                         port_data, port)

        # NOTE(mb): Remove this block when controller is updated

        # Remove the information that the controller does not accept

        sdnve_port = port.copy()

        sdnve_port.pop('device_id', None)

        sdnve_port.pop('device_owner', None)

        (res, data) = self.sdnve_client.sdnve_create('port', sdnve_port)

        if res not in constants.HTTP_ACCEPTABLE:

            super(SdnvePluginV2, self).delete_port(context, port['id'])

            raise sdnve_exc.SdnveException(

                msg=(_('Create port failed in SDN-VE: %s') % res))

        LOG.debug(_("Created port: %s"), port.get('id', 'id not found'))

        return port

    @_ha

**** CubicPower OpenStack Study ****

    def update_port(self, context, id, port):

        LOG.debug(_("Update port in progress: %r"), port)

        session = context.session

        processed_request = {}

        with session.begin(subtransactions=True):

            original_port = super(SdnvePluginV2, self).get_port(

                context, id)

            processed_request['port'] = self._process_request(

                port['port'], original_port)

            updated_port = super(SdnvePluginV2, self).update_port(

                context, id, port)

            os_tenant_id = updated_port['tenant_id']

            id_na, tenant_type = self.sdnve_client.sdnve_get_tenant_byid(

                os_tenant_id)

            self._update_base_binding_dict(tenant_type)

            self._process_portbindings_create_and_update(context,

                                                         port['port'],

                                                         updated_port)

        if processed_request['port']:

            (res, data) = self.sdnve_client.sdnve_update(

                'port', id, processed_request['port'])

            if res not in constants.HTTP_ACCEPTABLE:

                updated_port = super(SdnvePluginV2, self).update_port(

                    context, id, {'port': original_port})

                raise sdnve_exc.SdnveException(

                    msg=(_('Update port failed in SDN-VE: %s') % res))

        return updated_port

    @_ha

**** CubicPower OpenStack Study ****

    def delete_port(self, context, id, l3_port_check=True):

        LOG.debug(_("Delete port in progress: %s"), id)

        # if needed, check to see if this is a port owned by

        # an l3-router.  If so, we should prevent deletion.

        if l3_port_check:

            self.prevent_l3_port_deletion(context, id)

        self.disassociate_floatingips(context, id)

        super(SdnvePluginV2, self).delete_port(context, id)

        (res, data) = self.sdnve_client.sdnve_delete('port', id)

        if res not in constants.HTTP_ACCEPTABLE:

            LOG.error(

                _("Delete port operation failed in SDN-VE "

                  "after deleting the port from DB: %s"), res)

    #

    # Subnet

    #

    @_ha

**** CubicPower OpenStack Study ****

    def create_subnet(self, context, subnet):

        LOG.debug(_("Create subnet in progress: %r"), subnet)

        new_subnet = super(SdnvePluginV2, self).create_subnet(context, subnet)

        # Note(mb): Use of null string currently required by controller

        sdnve_subnet = new_subnet.copy()

        if subnet.get('gateway_ip') is None:

            sdnve_subnet['gateway_ip'] = 'null'

        (res, data) = self.sdnve_client.sdnve_create('subnet', sdnve_subnet)

        if res not in constants.HTTP_ACCEPTABLE:

            super(SdnvePluginV2, self).delete_subnet(context,

                                                     new_subnet['id'])

            raise sdnve_exc.SdnveException(

                msg=(_('Create subnet failed in SDN-VE: %s') % res))

        LOG.debug(_("Subnet created: %s"), new_subnet['id'])

        return new_subnet

    @_ha

**** CubicPower OpenStack Study ****

    def update_subnet(self, context, id, subnet):

        LOG.debug(_("Update subnet in progress: %r"), subnet)

        session = context.session

        processed_request = {}

        with session.begin(subtransactions=True):

            original_subnet = super(SdnvePluginV2, self).get_subnet(

                context, id)

            processed_request['subnet'] = self._process_request(

                subnet['subnet'], original_subnet)

            updated_subnet = super(SdnvePluginV2, self).update_subnet(

                context, id, subnet)

        if processed_request['subnet']:

            # Note(mb): Use of string containing null required by controller

            if 'gateway_ip' in processed_request['subnet']:

                if processed_request['subnet'].get('gateway_ip') is None:

                    processed_request['subnet']['gateway_ip'] = 'null'

            (res, data) = self.sdnve_client.sdnve_update(

                'subnet', id, processed_request['subnet'])

            if res not in constants.HTTP_ACCEPTABLE:

                for key in subnet['subnet'].keys():

                    subnet['subnet'][key] = original_subnet[key]

                super(SdnvePluginV2, self).update_subnet(

                    context, id, subnet)

                raise sdnve_exc.SdnveException(

                    msg=(_('Update subnet failed in SDN-VE: %s') % res))

        return updated_subnet

    @_ha

**** CubicPower OpenStack Study ****

    def delete_subnet(self, context, id):

        LOG.debug(_("Delete subnet in progress: %s"), id)

        super(SdnvePluginV2, self).delete_subnet(context, id)

        (res, data) = self.sdnve_client.sdnve_delete('subnet', id)

        if res not in constants.HTTP_ACCEPTABLE:

            LOG.error(_("Delete subnet operation failed in SDN-VE after "

                        "deleting the subnet from DB: %s"), res)

    #

    # Router

    #

    @_ha

**** CubicPower OpenStack Study ****

    def create_router(self, context, router):

        LOG.debug(_("Create router in progress: %r"), router)

        if router['router']['admin_state_up'] is False:

            LOG.warning(_('Ignoring admin_state_up=False for router=%r.  '

                          'Overriding with True'), router)

            router['router']['admin_state_up'] = True

        tenant_id = self._get_tenant_id_for_create(context, router['router'])

        # Create a new Pinnaacles tenant if need be

        sdnve_tenant = self.sdnve_client.sdnve_check_and_create_tenant(

            tenant_id)

        if sdnve_tenant is None:

            raise sdnve_exc.SdnveException(

                msg=_('Create router failed: no SDN-VE tenant.'))

        new_router = super(SdnvePluginV2, self).create_router(context, router)

        # Create Sdnve router

        (res, data) = self.sdnve_client.sdnve_create('router', new_router)

        if res not in constants.HTTP_ACCEPTABLE:

            super(SdnvePluginV2, self).delete_router(context, new_router['id'])

            raise sdnve_exc.SdnveException(

                msg=(_('Create router failed in SDN-VE: %s') % res))

        LOG.debug(_("Router created: %r"), new_router)

        return new_router

    @_ha

**** CubicPower OpenStack Study ****

    def update_router(self, context, id, router):

        LOG.debug(_("Update router in progress: id=%(id)s "

                    "router=%(router)r"),

                  {'id': id, 'router': router})

        session = context.session

        processed_request = {}

        if not router['router'].get('admin_state_up', True):

            raise n_exc.NotImplementedError(_('admin_state_up=False '

                                              'routers are not '

                                              'supported.'))

        with session.begin(subtransactions=True):

            original_router = super(SdnvePluginV2, self).get_router(

                context, id)

            processed_request['router'] = self._process_request(

                router['router'], original_router)

            updated_router = super(SdnvePluginV2, self).update_router(

                context, id, router)

        if processed_request['router']:

            (res, data) = self.sdnve_client.sdnve_update(

                'router', id, processed_request['router'])

            if res not in constants.HTTP_ACCEPTABLE:

                super(SdnvePluginV2, self).update_router(

                    context, id, {'router': original_router})

                raise sdnve_exc.SdnveException(

                    msg=(_('Update router failed in SDN-VE: %s') % res))

        return updated_router

    @_ha

**** CubicPower OpenStack Study ****

    def delete_router(self, context, id):

        LOG.debug(_("Delete router in progress: %s"), id)

        super(SdnvePluginV2, self).delete_router(context, id)

        (res, data) = self.sdnve_client.sdnve_delete('router', id)

        if res not in constants.HTTP_ACCEPTABLE:

            LOG.error(

                _("Delete router operation failed in SDN-VE after "

                  "deleting the router in DB: %s"), res)

    @_ha

**** CubicPower OpenStack Study ****

    def add_router_interface(self, context, router_id, interface_info):

        LOG.debug(_("Add router interface in progress: "

                    "router_id=%(router_id)s "

                    "interface_info=%(interface_info)r"),

                  {'router_id': router_id, 'interface_info': interface_info})

        new_interface = super(SdnvePluginV2, self).add_router_interface(

            context, router_id, interface_info)

        LOG.debug(

            _("SdnvePluginV2.add_router_interface called. Port info: %s"),

            new_interface)

        request_info = interface_info.copy()

        request_info['port_id'] = new_interface['port_id']

        # Add the subnet_id to the request sent to the controller

        if 'subnet_id' not in interface_info:

            request_info['subnet_id'] = new_interface['subnet_id']

        (res, data) = self.sdnve_client.sdnve_update(

            'router', router_id + '/add_router_interface', request_info)

        if res not in constants.HTTP_ACCEPTABLE:

            super(SdnvePluginV2, self).remove_router_interface(

                context, router_id, interface_info)

            raise sdnve_exc.SdnveException(

                msg=(_('Update router-add-interface failed in SDN-VE: %s') %

                     res))

        LOG.debug(_("Added router interface: %r"), new_interface)

        return new_interface

**** CubicPower OpenStack Study ****

    def _add_router_interface_only(self, context, router_id, interface_info):

        LOG.debug(_("Add router interface only called: "

                    "router_id=%(router_id)s "

                    "interface_info=%(interface_info)r"),

                  {'router_id': router_id, 'interface_info': interface_info})

        port_id = interface_info.get('port_id')

        if port_id:

            (res, data) = self.sdnve_client.sdnve_update(

                'router', router_id + '/add_router_interface', interface_info)

            if res not in constants.HTTP_ACCEPTABLE:

                LOG.error(_("SdnvePluginV2._add_router_interface_only: "

                            "failed to add the interface in the roll back."

                            " of a remove_router_interface operation"))

    @_ha

**** CubicPower OpenStack Study ****

    def remove_router_interface(self, context, router_id, interface_info):

        LOG.debug(_("Remove router interface in progress: "

                    "router_id=%(router_id)s "

                    "interface_info=%(interface_info)r"),

                  {'router_id': router_id, 'interface_info': interface_info})

        subnet_id = interface_info.get('subnet_id')

        if not subnet_id:

            portid = interface_info.get('port_id')

            if not portid:

                raise sdnve_exc.BadInputException(msg=_('No port ID'))

            myport = super(SdnvePluginV2, self).get_port(context, portid)

            LOG.debug(_("SdnvePluginV2.remove_router_interface port: %s"),

                      myport)

            myfixed_ips = myport.get('fixed_ips')

            if not myfixed_ips:

                raise sdnve_exc.BadInputException(msg=_('No fixed IP'))

            subnet_id = myfixed_ips[0].get('subnet_id')

            if subnet_id:

                interface_info['subnet_id'] = subnet_id

                LOG.debug(

                    _("SdnvePluginV2.remove_router_interface subnet_id: %s"),

                    subnet_id)

        (res, data) = self.sdnve_client.sdnve_update(

            'router', router_id + '/remove_router_interface', interface_info)

        if res not in constants.HTTP_ACCEPTABLE:

            raise sdnve_exc.SdnveException(

                msg=(_('Update router-remove-interface failed SDN-VE: %s') %

                     res))

        session = context.session

        with session.begin(subtransactions=True):

            try:

                info = super(SdnvePluginV2, self).remove_router_interface(

                    context, router_id, interface_info)

            except Exception:

                with excutils.save_and_reraise_exception():

                    self._add_router_interface_only(context,

                                                    router_id, interface_info)

        return info

    #

    # Floating Ip

    #

    @_ha

**** CubicPower OpenStack Study ****

    def create_floatingip(self, context, floatingip):

        LOG.debug(_("Create floatingip in progress: %r"),

                  floatingip)

        new_floatingip = super(SdnvePluginV2, self).create_floatingip(

            context, floatingip)

        (res, data) = self.sdnve_client.sdnve_create(

            'floatingip', {'floatingip': new_floatingip})

        if res not in constants.HTTP_ACCEPTABLE:

            super(SdnvePluginV2, self).delete_floatingip(

                context, new_floatingip['id'])

            raise sdnve_exc.SdnveException(

                msg=(_('Creating floating ip operation failed '

                       'in SDN-VE controller: %s') % res))

        LOG.debug(_("Created floatingip : %r"), new_floatingip)

        return new_floatingip

    @_ha

**** CubicPower OpenStack Study ****

    def update_floatingip(self, context, id, floatingip):

        LOG.debug(_("Update floatingip in progress: %r"), floatingip)

        session = context.session

        processed_request = {}

        with session.begin(subtransactions=True):

            original_floatingip = super(

                SdnvePluginV2, self).get_floatingip(context, id)

            processed_request['floatingip'] = self._process_request(

                floatingip['floatingip'], original_floatingip)

            updated_floatingip = super(

                SdnvePluginV2, self).update_floatingip(context, id, floatingip)

        if processed_request['floatingip']:

            (res, data) = self.sdnve_client.sdnve_update(

                'floatingip', id,

                {'floatingip': processed_request['floatingip']})

            if res not in constants.HTTP_ACCEPTABLE:

                super(SdnvePluginV2, self).update_floatingip(

                    context, id, {'floatingip': original_floatingip})

                raise sdnve_exc.SdnveException(

                    msg=(_('Update floating ip failed in SDN-VE: %s') % res))

        return updated_floatingip

    @_ha

**** CubicPower OpenStack Study ****

    def delete_floatingip(self, context, id):

        LOG.debug(_("Delete floatingip in progress: %s"), id)

        super(SdnvePluginV2, self).delete_floatingip(context, id)

        (res, data) = self.sdnve_client.sdnve_delete('floatingip', id)

        if res not in constants.HTTP_ACCEPTABLE:

            LOG.error(_("Delete floatingip failed in SDN-VE: %s"), res)