¡@

Home 

OpenStack Study: networkgw_db.py

OpenStack Index

**** CubicPower OpenStack Study ****

# Copyright 2013 VMware, Inc. All rights reserved.

#

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

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

# a copy of the License at

#

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

#

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

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

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

# License for the specific language governing permissions and limitations

# under the License.

import sqlalchemy as sa

from sqlalchemy import orm

from sqlalchemy.orm import exc as sa_orm_exc

from webob import exc as web_exc

from neutron.api.v2 import attributes

from neutron.api.v2 import base

from neutron.common import exceptions

from neutron.db import model_base

from neutron.db import models_v2

from neutron.openstack.common import log as logging

from neutron.openstack.common import uuidutils

from neutron.plugins.vmware.extensions import networkgw

LOG = logging.getLogger(__name__)

DEVICE_OWNER_NET_GW_INTF = 'network:gateway-interface'

NETWORK_ID = 'network_id'

SEGMENTATION_TYPE = 'segmentation_type'

SEGMENTATION_ID = 'segmentation_id'

ALLOWED_CONNECTION_ATTRIBUTES = set((NETWORK_ID,

SEGMENTATION_TYPE,

SEGMENTATION_ID))

# Constants for gateway device operational status

STATUS_UNKNOWN = "UNKNOWN"

STATUS_ERROR = "ERROR"

STATUS_ACTIVE = "ACTIVE"

STATUS_DOWN = "DOWN"

**** CubicPower OpenStack Study ****

class GatewayInUse(exceptions.InUse):

message = _("Network Gateway '%(gateway_id)s' still has active mappings "

"with one or more neutron networks.")

**** CubicPower OpenStack Study ****

class GatewayNotFound(exceptions.NotFound):

message = _("Network Gateway %(gateway_id)s could not be found")

**** CubicPower OpenStack Study ****

class GatewayDeviceInUse(exceptions.InUse):

message = _("Network Gateway Device '%(device_id)s' is still used by "

"one or more network gateways.")

**** CubicPower OpenStack Study ****

class GatewayDeviceNotFound(exceptions.NotFound):

message = _("Network Gateway Device %(device_id)s could not be found.")

**** CubicPower OpenStack Study ****

class NetworkGatewayPortInUse(exceptions.InUse):

message = _("Port '%(port_id)s' is owned by '%(device_owner)s' and "

"therefore cannot be deleted directly via the port API.")

**** CubicPower OpenStack Study ****

class GatewayConnectionInUse(exceptions.InUse):

message = _("The specified mapping '%(mapping)s' is already in use on "

"network gateway '%(gateway_id)s'.")

**** CubicPower OpenStack Study ****

class MultipleGatewayConnections(exceptions.NeutronException):

message = _("Multiple network connections found on '%(gateway_id)s' "

"with provided criteria.")

**** CubicPower OpenStack Study ****

class GatewayConnectionNotFound(exceptions.NotFound):

message = _("The connection %(network_mapping_info)s was not found on the "

"network gateway '%(network_gateway_id)s'")

**** CubicPower OpenStack Study ****

class NetworkGatewayUnchangeable(exceptions.InUse):

message = _("The network gateway %(gateway_id)s "

"cannot be updated or deleted")

# Add exceptions to HTTP Faults mappings

base.FAULT_MAP.update({GatewayInUse: web_exc.HTTPConflict,

NetworkGatewayPortInUse: web_exc.HTTPConflict,

GatewayConnectionInUse: web_exc.HTTPConflict,

GatewayConnectionNotFound: web_exc.HTTPNotFound,

MultipleGatewayConnections: web_exc.HTTPConflict})

**** CubicPower OpenStack Study ****

class NetworkConnection(model_base.BASEV2, models_v2.HasTenant):

"""Defines a connection between a network gateway and a network."""

# We use port_id as the primary key as one can connect a gateway

# to a network in multiple ways (and we cannot use the same port form

# more than a single gateway)

network_gateway_id = sa.Column(sa.String(36),

sa.ForeignKey('networkgateways.id',

ondelete='CASCADE'))

network_id = sa.Column(sa.String(36),

sa.ForeignKey('networks.id', ondelete='CASCADE'))

segmentation_type = sa.Column(

sa.Enum('flat', 'vlan',

name='networkconnections_segmentation_type'))

segmentation_id = sa.Column(sa.Integer)

__table_args__ = (sa.UniqueConstraint(network_gateway_id,

segmentation_type,

segmentation_id),)

# Also, storing port id comes back useful when disconnecting a network

# from a gateway

port_id = sa.Column(sa.String(36),

sa.ForeignKey('ports.id', ondelete='CASCADE'),

primary_key=True)

**** CubicPower OpenStack Study ****

class NetworkGatewayDeviceReference(model_base.BASEV2):

id = sa.Column(sa.String(36), primary_key=True)

network_gateway_id = sa.Column(sa.String(36),

sa.ForeignKey('networkgateways.id',

ondelete='CASCADE'),

primary_key=True)

interface_name = sa.Column(sa.String(64), primary_key=True)

**** CubicPower OpenStack Study ****

class NetworkGatewayDevice(model_base.BASEV2, models_v2.HasId, models_v2.HasTenant):

nsx_id = sa.Column(sa.String(36))

# Optional name for the gateway device

name = sa.Column(sa.String(255))

# Transport connector type. Not using enum as range of

# connector types might vary with backend version

connector_type = sa.Column(sa.String(10))

# Transport connector IP Address

connector_ip = sa.Column(sa.String(64))

# operational status

status = sa.Column(sa.String(16))

**** CubicPower OpenStack Study ****

class NetworkGateway(model_base.BASEV2, models_v2.HasId, models_v2.HasTenant):

"""Defines the data model for a network gateway."""

name = sa.Column(sa.String(255))

# Tenant id is nullable for this resource

tenant_id = sa.Column(sa.String(36))

**** CubicPower OpenStack Study ****

class NetworkGatewayMixin(networkgw.NetworkGatewayPluginBase):

gateway_resource = networkgw.GATEWAY_RESOURCE_NAME

device_resource = networkgw.DEVICE_RESOURCE_NAME

**** CubicPower OpenStack Study ****

    def _get_network_gateway(self, context, gw_id):

        try:

            gw = self._get_by_id(context, NetworkGateway, gw_id)

        except sa_orm_exc.NoResultFound:

            raise GatewayNotFound(gateway_id=gw_id)

        return gw

**** CubicPower OpenStack Study ****

    def _make_gw_connection_dict(self, gw_conn):

        return {'port_id': gw_conn['port_id'],

                'segmentation_type': gw_conn['segmentation_type'],

                'segmentation_id': gw_conn['segmentation_id']}

**** CubicPower OpenStack Study ****

    def _make_network_gateway_dict(self, network_gateway, fields=None):

        device_list = []

        for d in network_gateway['devices']:

            device_list.append({'id': d['id'],

                                'interface_name': d['interface_name']})

        res = {'id': network_gateway['id'],

               'name': network_gateway['name'],

               'default': network_gateway['default'],

               'devices': device_list,

               'tenant_id': network_gateway['tenant_id']}

        # Query gateway connections only if needed

        if (fields and 'ports' in fields) or not fields:

            res['ports'] = [self._make_gw_connection_dict(conn)

                            for conn in network_gateway.network_connections]

        return self._fields(res, fields)

**** CubicPower OpenStack Study ****

    def _set_mapping_info_defaults(self, mapping_info):

        if not mapping_info.get('segmentation_type'):

            mapping_info['segmentation_type'] = 'flat'

        if not mapping_info.get('segmentation_id'):

            mapping_info['segmentation_id'] = 0

**** CubicPower OpenStack Study ****

    def _validate_network_mapping_info(self, network_mapping_info):

        self._set_mapping_info_defaults(network_mapping_info)

        network_id = network_mapping_info.get(NETWORK_ID)

        if not network_id:

            raise exceptions.InvalidInput(

                error_message=_("A network identifier must be specified "

                                "when connecting a network to a network "

                                "gateway. Unable to complete operation"))

        connection_attrs = set(network_mapping_info.keys())

        if not connection_attrs.issubset(ALLOWED_CONNECTION_ATTRIBUTES):

            raise exceptions.InvalidInput(

                error_message=(_("Invalid keys found among the ones provided "

                                 "in request body: %(connection_attrs)s."),

                               connection_attrs))

        seg_type = network_mapping_info.get(SEGMENTATION_TYPE)

        seg_id = network_mapping_info.get(SEGMENTATION_ID)

        if not seg_type and seg_id:

            msg = _("In order to specify a segmentation id the "

                    "segmentation type must be specified as well")

            raise exceptions.InvalidInput(error_message=msg)

        elif seg_type and seg_type.lower() == 'flat' and seg_id:

            msg = _("Cannot specify a segmentation id when "

                    "the segmentation type is flat")

            raise exceptions.InvalidInput(error_message=msg)

        return network_id

**** CubicPower OpenStack Study ****

    def _retrieve_gateway_connections(self, context, gateway_id,

                                      mapping_info={}, only_one=False):

        filters = {'network_gateway_id': [gateway_id]}

        for k, v in mapping_info.iteritems():

            if v and k != NETWORK_ID:

                filters[k] = [v]

        query = self._get_collection_query(context,

                                           NetworkConnection,

                                           filters)

        return only_one and query.one() or query.all()

**** CubicPower OpenStack Study ****

    def _unset_default_network_gateways(self, context):

        with context.session.begin(subtransactions=True):

            context.session.query(NetworkGateway).update(

                {NetworkGateway.default: False})

**** CubicPower OpenStack Study ****

    def _set_default_network_gateway(self, context, gw_id):

        with context.session.begin(subtransactions=True):

            gw = (context.session.query(NetworkGateway).

                  filter_by(id=gw_id).one())

            gw['default'] = True

**** CubicPower OpenStack Study ****

    def prevent_network_gateway_port_deletion(self, context, port):

        """Pre-deletion check.

        Ensures a port will not be deleted if is being used by a network

        gateway. In that case an exception will be raised.

        """

        if port['device_owner'] == DEVICE_OWNER_NET_GW_INTF:

            raise NetworkGatewayPortInUse(port_id=port['id'],

                                          device_owner=port['device_owner'])

**** CubicPower OpenStack Study ****

    def create_network_gateway(self, context, network_gateway):

        gw_data = network_gateway[self.gateway_resource]

        tenant_id = self._get_tenant_id_for_create(context, gw_data)

        with context.session.begin(subtransactions=True):

            gw_db = NetworkGateway(

                id=gw_data.get('id', uuidutils.generate_uuid()),

                tenant_id=tenant_id,

                name=gw_data.get('name'))

            # Device list is guaranteed to be a valid list

            # TODO(salv-orlando): Enforce that gateway device identifiers

            # in this list are among the tenant's NSX network gateway devices

            # to avoid risk a tenant 'guessing' other tenant's network devices

            gw_db.devices.extend([NetworkGatewayDeviceReference(**device)

                                  for device in gw_data['devices']])

            context.session.add(gw_db)

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

        return self._make_network_gateway_dict(gw_db)

**** CubicPower OpenStack Study ****

    def update_network_gateway(self, context, id, network_gateway):

        gw_data = network_gateway[self.gateway_resource]

        with context.session.begin(subtransactions=True):

            gw_db = self._get_network_gateway(context, id)

            if gw_db.default:

                raise NetworkGatewayUnchangeable(gateway_id=id)

            # Ensure there is something to update before doing it

            if any([gw_db[k] != gw_data[k] for k in gw_data]):

                gw_db.update(gw_data)

        LOG.debug(_("Updated network gateway with id:%s"), id)

        return self._make_network_gateway_dict(gw_db)

**** CubicPower OpenStack Study ****

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

        gw_db = self._get_network_gateway(context, id)

        return self._make_network_gateway_dict(gw_db, fields)

**** CubicPower OpenStack Study ****

    def delete_network_gateway(self, context, id):

        with context.session.begin(subtransactions=True):

            gw_db = self._get_network_gateway(context, id)

            if gw_db.network_connections:

                raise GatewayInUse(gateway_id=id)

            if gw_db.default:

                raise NetworkGatewayUnchangeable(gateway_id=id)

            context.session.delete(gw_db)

        LOG.debug(_("Network gateway '%s' was destroyed."), id)

**** CubicPower OpenStack Study ****

    def get_network_gateways(self, context, filters=None, fields=None,

                             sorts=None, limit=None, marker=None,

                             page_reverse=False):

        marker_obj = self._get_marker_obj(

            context, 'network_gateway', limit, marker)

        return self._get_collection(context, NetworkGateway,

                                    self._make_network_gateway_dict,

                                    filters=filters, fields=fields,

                                    sorts=sorts, limit=limit,

                                    marker_obj=marker_obj,

                                    page_reverse=page_reverse)

**** CubicPower OpenStack Study ****

    def connect_network(self, context, network_gateway_id,

                        network_mapping_info):

        network_id = self._validate_network_mapping_info(network_mapping_info)

        LOG.debug(_("Connecting network '%(network_id)s' to gateway "

                    "'%(network_gateway_id)s'"),

                  {'network_id': network_id,

                   'network_gateway_id': network_gateway_id})

        with context.session.begin(subtransactions=True):

            gw_db = self._get_network_gateway(context, network_gateway_id)

            tenant_id = self._get_tenant_id_for_create(context, gw_db)

            # TODO(salvatore-orlando): Leverage unique constraint instead

            # of performing another query!

            if self._retrieve_gateway_connections(context,

                                                  network_gateway_id,

                                                  network_mapping_info):

                raise GatewayConnectionInUse(mapping=network_mapping_info,

                                             gateway_id=network_gateway_id)

            # TODO(salvatore-orlando): Creating a port will give it an IP,

            # but we actually do not need any. Instead of wasting an IP we

            # should have a way to say a port shall not be associated with

            # any subnet

            try:

                # We pass the segmentation type and id too - the plugin

                # might find them useful as the network connection object

                # does not exist yet.

                # NOTE: they're not extended attributes, rather extra data

                # passed in the port structure to the plugin

                # TODO(salvatore-orlando): Verify optimal solution for

                # ownership of the gateway port

                port = self.create_port(context, {

                    'port':

                    {'tenant_id': tenant_id,

                     'network_id': network_id,

                     'mac_address': attributes.ATTR_NOT_SPECIFIED,

                     'admin_state_up': True,

                     'fixed_ips': [],

                     'device_id': network_gateway_id,

                     'device_owner': DEVICE_OWNER_NET_GW_INTF,

                     'name': '',

                     'gw:segmentation_type':

                     network_mapping_info.get('segmentation_type'),

                     'gw:segmentation_id':

                     network_mapping_info.get('segmentation_id')}})

            except exceptions.NetworkNotFound:

                err_msg = (_("Requested network '%(network_id)s' not found."

                             "Unable to create network connection on "

                             "gateway '%(network_gateway_id)s") %

                           {'network_id': network_id,

                            'network_gateway_id': network_gateway_id})

                LOG.error(err_msg)

                raise exceptions.InvalidInput(error_message=err_msg)

            port_id = port['id']

            LOG.debug(_("Gateway port for '%(network_gateway_id)s' "

                        "created on network '%(network_id)s':%(port_id)s"),

                      {'network_gateway_id': network_gateway_id,

                       'network_id': network_id,

                       'port_id': port_id})

            # Create NetworkConnection record

            network_mapping_info['port_id'] = port_id

            network_mapping_info['tenant_id'] = tenant_id

            gw_db.network_connections.append(

                NetworkConnection(**network_mapping_info))

            port_id = port['id']

            # now deallocate and recycle ip from the port

            for fixed_ip in port.get('fixed_ips', []):

                self._delete_ip_allocation(context, network_id,

                                           fixed_ip['subnet_id'],

                                           fixed_ip['ip_address'])

            LOG.debug(_("Ensured no Ip addresses are configured on port %s"),

                      port_id)

            return {'connection_info':

                    {'network_gateway_id': network_gateway_id,

                     'network_id': network_id,

                     'port_id': port_id}}

**** CubicPower OpenStack Study ****

    def disconnect_network(self, context, network_gateway_id,

                           network_mapping_info):

        network_id = self._validate_network_mapping_info(network_mapping_info)

        LOG.debug(_("Disconnecting network '%(network_id)s' from gateway "

                    "'%(network_gateway_id)s'"),

                  {'network_id': network_id,

                   'network_gateway_id': network_gateway_id})

        with context.session.begin(subtransactions=True):

            # Uniquely identify connection, otherwise raise

            try:

                net_connection = self._retrieve_gateway_connections(

                    context, network_gateway_id,

                    network_mapping_info, only_one=True)

            except sa_orm_exc.NoResultFound:

                raise GatewayConnectionNotFound(

                    network_mapping_info=network_mapping_info,

                    network_gateway_id=network_gateway_id)

            except sa_orm_exc.MultipleResultsFound:

                raise MultipleGatewayConnections(

                    gateway_id=network_gateway_id)

            # Remove gateway port from network

            # FIXME(salvatore-orlando): Ensure state of port in NVP is

            # consistent with outcome of transaction

            self.delete_port(context, net_connection['port_id'],

                             nw_gw_port_check=False)

            # Remove NetworkConnection record

            context.session.delete(net_connection)

**** CubicPower OpenStack Study ****

    def _make_gateway_device_dict(self, gateway_device, fields=None,

                                  include_nsx_id=False):

        res = {'id': gateway_device['id'],

               'name': gateway_device['name'],

               'status': gateway_device['status'],

               'connector_type': gateway_device['connector_type'],

               'connector_ip': gateway_device['connector_ip'],

               'tenant_id': gateway_device['tenant_id']}

        if include_nsx_id:

            # Return the NSX mapping as well. This attribute will not be

            # returned in the API response anyway. Ensure it will not be

            # filtered out in field selection.

            if fields:

                fields.append('nsx_id')

            res['nsx_id'] = gateway_device['nsx_id']

        return self._fields(res, fields)

**** CubicPower OpenStack Study ****

    def _get_gateway_device(self, context, device_id):

        try:

            return self._get_by_id(context, NetworkGatewayDevice, device_id)

        except sa_orm_exc.NoResultFound:

            raise GatewayDeviceNotFound(device_id=device_id)

**** CubicPower OpenStack Study ****

    def _is_device_in_use(self, context, device_id):

        query = self._get_collection_query(

            context, NetworkGatewayDeviceReference, {'id': [device_id]})

        return query.first()

**** CubicPower OpenStack Study ****

    def get_gateway_device(self, context, device_id, fields=None,

                           include_nsx_id=False):

        return self._make_gateway_device_dict(

            self._get_gateway_device(context, device_id),

            fields, include_nsx_id)

**** CubicPower OpenStack Study ****

    def get_gateway_devices(self, context, filters=None, fields=None,

                            sorts=None, limit=None, marker=None,

                            page_reverse=False, include_nsx_id=False):

        marker_obj = self._get_marker_obj(

            context, 'gateway_device', limit, marker)

        query = self._get_collection_query(context,

                                           NetworkGatewayDevice,

                                           filters=filters,

                                           fields=fields,

                                           sorts=sorts,

                                           limit=limit,

                                           marker_obj=marker_obj,

                                           page_reverse=page_reverse)

        return [self._make_gateway_device_dict(row, fields, include_nsx_id)

                for row in query]

**** CubicPower OpenStack Study ****

    def create_gateway_device(self, context, gateway_device,

                              initial_status=STATUS_UNKNOWN):

        device_data = gateway_device[self.device_resource]

        tenant_id = self._get_tenant_id_for_create(context, device_data)

        with context.session.begin(subtransactions=True):

            device_db = NetworkGatewayDevice(

                id=device_data.get('id', uuidutils.generate_uuid()),

                tenant_id=tenant_id,

                name=device_data.get('name'),

                connector_type=device_data['connector_type'],

                connector_ip=device_data['connector_ip'],

                status=initial_status)

            context.session.add(device_db)

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

        return self._make_gateway_device_dict(device_db)

**** CubicPower OpenStack Study ****

    def update_gateway_device(self, context, gateway_device_id,

                              gateway_device, include_nsx_id=False):

        device_data = gateway_device[self.device_resource]

        with context.session.begin(subtransactions=True):

            device_db = self._get_gateway_device(context, gateway_device_id)

            # Ensure there is something to update before doing it

            if any([device_db[k] != device_data[k] for k in device_data]):

                device_db.update(device_data)

        LOG.debug(_("Updated network gateway device: %s"),

                  gateway_device_id)

        return self._make_gateway_device_dict(

            device_db, include_nsx_id=include_nsx_id)

**** CubicPower OpenStack Study ****

    def delete_gateway_device(self, context, device_id):

        with context.session.begin(subtransactions=True):

            # A gateway device should not be deleted

            # if it is used in any network gateway service

            if self._is_device_in_use(context, device_id):

                raise GatewayDeviceInUse(device_id=device_id)

            device_db = self._get_gateway_device(context, device_id)

            context.session.delete(device_db)

        LOG.debug(_("Deleted network gateway device: %s."), device_id)