VMware: initial NSXv developments
Add the NSXv code to the repo. Co-Authored-By: Kobi Samoray <ksamoray@vmware.com> Change-Id: Iefc76e0d6bfab8136bd2e3300a8b3d4a3fdb3a46
This commit is contained in:
parent
3a96a43c53
commit
9254b0aeda
@ -2,7 +2,8 @@
|
||||
# of appearance. Changing the order has an impact on the overall integration
|
||||
# process, which may cause wedges in the gate later.
|
||||
|
||||
-e git://git.openstack.org/openstack/neutron.git#egg=neutron
|
||||
# Temporary, till https://review.openstack.org/#/c/143949/ is merged
|
||||
-e git://github.com/gkotton/neutron.git#egg=neutron
|
||||
|
||||
hacking>=0.9.2,<0.10
|
||||
|
||||
|
@ -23,7 +23,7 @@ from oslo.config import cfg
|
||||
|
||||
from neutron.i18n import _LE, _LI, _LW
|
||||
from neutron.openstack.common import log as logging
|
||||
from neutron.plugins.vmware import api_client
|
||||
from vmware_nsx.neutron.plugins.vmware import api_client
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
@ -19,11 +19,11 @@ import httplib
|
||||
|
||||
from neutron.i18n import _LE
|
||||
from neutron.openstack.common import log as logging
|
||||
from neutron.plugins.vmware.api_client import base
|
||||
from neutron.plugins.vmware.api_client import eventlet_client
|
||||
from neutron.plugins.vmware.api_client import eventlet_request
|
||||
from neutron.plugins.vmware.api_client import exception
|
||||
from neutron.plugins.vmware.api_client import version
|
||||
from vmware_nsx.neutron.plugins.vmware.api_client import base
|
||||
from vmware_nsx.neutron.plugins.vmware.api_client import eventlet_client
|
||||
from vmware_nsx.neutron.plugins.vmware.api_client import eventlet_request
|
||||
from vmware_nsx.neutron.plugins.vmware.api_client import version
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
@ -22,8 +22,8 @@ eventlet.monkey_patch()
|
||||
|
||||
from neutron.i18n import _LE
|
||||
from neutron.openstack.common import log as logging
|
||||
from neutron.plugins.vmware.api_client import base
|
||||
from neutron.plugins.vmware.api_client import eventlet_request
|
||||
from vmware_nsx.neutron.plugins.vmware.api_client import base
|
||||
from vmware_nsx.neutron.plugins.vmware.api_client import eventlet_request
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
@ -22,7 +22,7 @@ from oslo.serialization import jsonutils
|
||||
|
||||
from neutron.i18n import _LI, _LW
|
||||
from neutron.openstack.common import log as logging
|
||||
from neutron.plugins.vmware.api_client import request
|
||||
from vmware_nsx.neutron.plugins.vmware.api_client import request
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
USER_AGENT = "Neutron eventlet client/2.0"
|
||||
|
@ -27,7 +27,7 @@ import six.moves.urllib.parse as urlparse
|
||||
|
||||
from neutron.i18n import _LI, _LW
|
||||
from neutron.openstack.common import log as logging
|
||||
from neutron.plugins.vmware import api_client
|
||||
from vmware_nsx.neutron.plugins.vmware import api_client
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
@ -20,9 +20,9 @@ import sys
|
||||
from oslo.config import cfg
|
||||
|
||||
from neutron.common import config
|
||||
from neutron.plugins.vmware.common import config as nsx_config # noqa
|
||||
from neutron.plugins.vmware.common import nsx_utils
|
||||
from neutron.plugins.vmware import nsxlib
|
||||
from vmware_nsx.neutron.plugins.vmware.common import config as nsx_config # noqa
|
||||
from vmware_nsx.neutron.plugins.vmware.common import nsx_utils
|
||||
from vmware_nsx.neutron.plugins.vmware import nsxlib
|
||||
|
||||
config.setup_logging()
|
||||
|
||||
|
@ -12,10 +12,15 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import logging
|
||||
|
||||
from oslo.config import cfg
|
||||
|
||||
from neutron.i18n import _LW
|
||||
from neutron.plugins.vmware.common import exceptions as nsx_exc
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class AgentModes:
|
||||
AGENT = 'agent'
|
||||
@ -153,40 +158,87 @@ cluster_opts = [
|
||||
]
|
||||
|
||||
DEFAULT_STATUS_CHECK_INTERVAL = 2000
|
||||
DEFAULT_MINIMUM_POOLED_EDGES = 1
|
||||
DEFAULT_MAXIMUM_POOLED_EDGES = 3
|
||||
DEFAULT_MAXIMUM_TUNNELS_PER_VNIC = 20
|
||||
|
||||
vcns_opts = [
|
||||
nsxv_opts = [
|
||||
cfg.StrOpt('user',
|
||||
default='admin',
|
||||
deprecated_group="vcns",
|
||||
help=_('User name for vsm')),
|
||||
cfg.StrOpt('password',
|
||||
default='default',
|
||||
deprecated_group="vcns",
|
||||
secret=True,
|
||||
help=_('Password for vsm')),
|
||||
cfg.StrOpt('manager_uri',
|
||||
deprecated_group="vcns",
|
||||
help=_('uri for vsm')),
|
||||
cfg.ListOpt('cluster_moid',
|
||||
default=[],
|
||||
help=_('Parameter listing the IDs of the clusters '
|
||||
'which are used by OpenStack.')),
|
||||
cfg.StrOpt('datacenter_moid',
|
||||
deprecated_group="vcns",
|
||||
help=_('Optional parameter identifying the ID of datacenter '
|
||||
'to deploy NSX Edges')),
|
||||
cfg.StrOpt('deployment_container_id',
|
||||
deprecated_group="vcns",
|
||||
help=_('Optional parameter identifying the ID of datastore to '
|
||||
'deploy NSX Edges')),
|
||||
cfg.StrOpt('resource_pool_id',
|
||||
deprecated_group="vcns",
|
||||
help=_('Optional parameter identifying the ID of resource to '
|
||||
'deploy NSX Edges')),
|
||||
cfg.StrOpt('datastore_id',
|
||||
deprecated_group="vcns",
|
||||
help=_('Optional parameter identifying the ID of datastore to '
|
||||
'deploy NSX Edges')),
|
||||
cfg.StrOpt('external_network',
|
||||
deprecated_group="vcns",
|
||||
help=_('Network ID for physical network connectivity')),
|
||||
cfg.IntOpt('task_status_check_interval',
|
||||
default=DEFAULT_STATUS_CHECK_INTERVAL,
|
||||
help=_("Task status check interval"))
|
||||
deprecated_group="vcns",
|
||||
help=_("Task status check interval")),
|
||||
cfg.StrOpt('vdn_scope_id',
|
||||
help=_('Network scope ID for VXLAN virtual wires')),
|
||||
cfg.StrOpt('dvs_id',
|
||||
help=_('DVS ID for VLANs')),
|
||||
cfg.IntOpt('maximum_tunnels_per_vnic',
|
||||
default=DEFAULT_MAXIMUM_TUNNELS_PER_VNIC,
|
||||
help=_('Maximum number of sub interfaces supported '
|
||||
'per vnic in edge. The value should be in 1-110.')),
|
||||
cfg.ListOpt('backup_edge_pool',
|
||||
default=['service:large:4:10',
|
||||
'service:compact:4:10',
|
||||
'vdr:large:4:10'],
|
||||
help=_('Defines edge pool using the format: '
|
||||
'<edge_type>:[edge_size]:<min_edges>:<max_edges>.'
|
||||
'edge_type: service,vdr. '
|
||||
'edge_size: compact, large, xlarge, quadlarge '
|
||||
'and default is large.')),
|
||||
cfg.IntOpt('retries',
|
||||
default=10,
|
||||
help=_('Maximum number of API retries on endpoint.')),
|
||||
cfg.StrOpt('mgt_net_moid',
|
||||
help=_('Network ID for management network connectivity')),
|
||||
cfg.ListOpt('mgt_net_proxy_ips',
|
||||
help=_('Management network IP address for metadata proxy')),
|
||||
cfg.StrOpt('mgt_net_proxy_netmask',
|
||||
help=_('Management network netmask for metadata proxy')),
|
||||
cfg.ListOpt('nova_metadata_ips',
|
||||
help=_('IP addresses used by Nova metadata service')),
|
||||
cfg.IntOpt('nova_metadata_port',
|
||||
default=8775,
|
||||
help=_("TCP Port used by Nova metadata server."))
|
||||
]
|
||||
|
||||
# Register the configuration options
|
||||
cfg.CONF.register_opts(connection_opts)
|
||||
cfg.CONF.register_opts(cluster_opts)
|
||||
cfg.CONF.register_opts(vcns_opts, group="vcns")
|
||||
cfg.CONF.register_opts(nsxv_opts, group="nsxv")
|
||||
cfg.CONF.register_opts(base_opts, group="NSX")
|
||||
cfg.CONF.register_opts(sync_opts, group="NSX_SYNC")
|
||||
|
||||
@ -197,3 +249,15 @@ def validate_config_options():
|
||||
error = (_("Invalid replication_mode: %s") %
|
||||
cfg.CONF.NSX.replication_mode)
|
||||
raise nsx_exc.NsxPluginException(err_msg=error)
|
||||
|
||||
|
||||
def validate_nsxv_config_options():
|
||||
if (cfg.CONF.nsxv.manager_uri is None or
|
||||
cfg.CONF.nsxv.user is None or
|
||||
cfg.CONF.nsxv.password is None):
|
||||
error = _("manager_uri, user and passwork be configured!")
|
||||
raise nsx_exc.NsxPluginException(err_msg=error)
|
||||
if cfg.CONF.nsxv.dvs_id is None:
|
||||
LOG.warning(_LW("dvs_id must be configured to support VLAN's!"))
|
||||
if cfg.CONF.nsxv.vdn_scope_id is None:
|
||||
LOG.warning(_LW("vdn_scope_id must be configured to support VXLAN's!"))
|
||||
|
@ -76,20 +76,6 @@ class ServiceOverQuota(n_exc.Conflict):
|
||||
message = _("Quota exceeded for Vcns resource: %(overs)s: %(err_msg)s")
|
||||
|
||||
|
||||
class RouterInUseByLBService(n_exc.InUse):
|
||||
message = _("Router %(router_id)s is in use by Loadbalancer Service "
|
||||
"%(vip_id)s")
|
||||
|
||||
|
||||
class RouterInUseByFWService(n_exc.InUse):
|
||||
message = _("Router %(router_id)s is in use by firewall Service "
|
||||
"%(firewall_id)s")
|
||||
|
||||
|
||||
class VcnsDriverException(NsxPluginException):
|
||||
message = _("Error happened in NSX VCNS Driver: %(err_msg)s")
|
||||
|
||||
|
||||
class ServiceClusterUnavailable(NsxPluginException):
|
||||
message = _("Service cluster: '%(cluster_id)s' is unavailable. Please, "
|
||||
"check NSX setup and/or configuration")
|
||||
|
@ -19,16 +19,16 @@ from neutron.extensions import multiprovidernet as mpnet
|
||||
from neutron.extensions import providernet as pnet
|
||||
from neutron.i18n import _LW
|
||||
from neutron.openstack.common import log
|
||||
from neutron.plugins.vmware.api_client import client
|
||||
from neutron.plugins.vmware.api_client import exception as api_exc
|
||||
from neutron.plugins.vmware.common import utils as vmw_utils
|
||||
from neutron.plugins.vmware.dbexts import db as nsx_db
|
||||
from neutron.plugins.vmware.dbexts import networkgw_db
|
||||
from neutron.plugins.vmware import nsx_cluster
|
||||
from neutron.plugins.vmware.nsxlib import l2gateway as l2gwlib
|
||||
from neutron.plugins.vmware.nsxlib import router as routerlib
|
||||
from neutron.plugins.vmware.nsxlib import secgroup as secgrouplib
|
||||
from neutron.plugins.vmware.nsxlib import switch as switchlib
|
||||
from vmware_nsx.neutron.plugins.vmware.api_client import client
|
||||
from vmware_nsx.neutron.plugins.vmware.common import utils as vmw_utils
|
||||
from vmware_nsx.neutron.plugins.vmware.dbexts import db as nsx_db
|
||||
from vmware_nsx.neutron.plugins.vmware import nsx_cluster
|
||||
from vmware_nsx.neutron.plugins.vmware.nsxlib import l2gateway as l2gwlib
|
||||
from vmware_nsx.neutron.plugins.vmware.nsxlib import router as routerlib
|
||||
from vmware_nsx.neutron.plugins.vmware.nsxlib import secgroup as secgrouplib
|
||||
from vmware_nsx.neutron.plugins.vmware.nsxlib import switch as switchlib
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
|
28
vmware_nsx/neutron/plugins/vmware/common/nsxv_constants.py
Normal file
28
vmware_nsx/neutron/plugins/vmware/common/nsxv_constants.py
Normal file
@ -0,0 +1,28 @@
|
||||
# Copyright 2014 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.
|
||||
|
||||
# Edge size
|
||||
COMPACT = 'compact'
|
||||
LARGE = 'large'
|
||||
XLARGE = 'xlarge'
|
||||
QUADLARGE = 'quadlarge'
|
||||
|
||||
|
||||
# Edge type
|
||||
SERVICE_EDGE = 'service'
|
||||
VDR_EDGE = 'vdr'
|
||||
|
||||
# Internal element purpose
|
||||
INTER_EDGE_PURPOSE = 'inter_edge_net'
|
@ -14,7 +14,7 @@
|
||||
# under the License.
|
||||
|
||||
from neutron.openstack.common import log
|
||||
from neutron.plugins.vmware.common import nsx_utils
|
||||
from vmware_nsx.neutron.plugins.vmware.common import nsx_utils
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
# Protocol number look up for supported protocols
|
||||
|
@ -30,10 +30,10 @@ from neutron.openstack.common import log
|
||||
from neutron.openstack.common import loopingcall
|
||||
from neutron.plugins.vmware.api_client import exception as api_exc
|
||||
from neutron.plugins.vmware.common import exceptions as nsx_exc
|
||||
from neutron.plugins.vmware.common import nsx_utils
|
||||
from neutron.plugins.vmware import nsxlib
|
||||
from neutron.plugins.vmware.nsxlib import router as routerlib
|
||||
from neutron.plugins.vmware.nsxlib import switch as switchlib
|
||||
from vmware_nsx.neutron.plugins.vmware.common import nsx_utils
|
||||
from vmware_nsx.neutron.plugins.vmware import nsxlib
|
||||
from vmware_nsx.neutron.plugins.vmware.nsxlib import router as routerlib
|
||||
from vmware_nsx.neutron.plugins.vmware.nsxlib import switch as switchlib
|
||||
|
||||
# Maximum page size for a single request
|
||||
# NOTE(salv-orlando): This might become a version-dependent map should the
|
||||
|
@ -36,6 +36,15 @@ class NetworkTypes:
|
||||
BRIDGE = 'bridge'
|
||||
|
||||
|
||||
# Allowed network types for the NSX-v Plugin
|
||||
class NsxVNetworkTypes:
|
||||
"""Allowed provider network types for the NSX-v Plugin."""
|
||||
FLAT = 'flat'
|
||||
VLAN = 'vlan'
|
||||
VXLAN = 'vxlan'
|
||||
PORTGROUP = 'portgroup'
|
||||
|
||||
|
||||
def get_tags(**kwargs):
|
||||
tags = ([dict(tag=value, scope=key)
|
||||
for key, value in kwargs.iteritems()])
|
||||
|
@ -1,5 +1,6 @@
|
||||
# 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
|
||||
@ -13,15 +14,16 @@
|
||||
# under the License.
|
||||
#
|
||||
|
||||
from neutron.db import l3_dvr_db
|
||||
from vmware_nsx.neutron.plugins.vmware.extensions import servicerouter
|
||||
from vmware_nsx.neutron.plugins.vmware.dbexts import nsxrouter
|
||||
from vmware_nsx.neutron.plugins.vmware.extensions import (
|
||||
distributedrouter as dist_rtr)
|
||||
|
||||
|
||||
class ServiceRouter_mixin(l3_dvr_db.L3_NAT_with_dvr_db_mixin):
|
||||
"""Mixin class to enable service router support."""
|
||||
class DistributedRouter_mixin(nsxrouter.NsxRouterMixin):
|
||||
"""Mixin class to enable distributed router support."""
|
||||
|
||||
extra_attributes = (
|
||||
l3_dvr_db.L3_NAT_with_dvr_db_mixin.extra_attributes + [{
|
||||
'name': servicerouter.SERVICE_ROUTER,
|
||||
nsx_attributes = (
|
||||
nsxrouter.NsxRouterMixin.nsx_attributes + [{
|
||||
'name': dist_rtr.DISTRIBUTED,
|
||||
'default': False
|
||||
}])
|
@ -1,132 +0,0 @@
|
||||
# Copyright 2014 VMware, Inc.
|
||||
#
|
||||
# All Rights Reserved
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
|
||||
from oslo.db import exception as d_exc
|
||||
from sqlalchemy import Column
|
||||
from sqlalchemy import ForeignKey
|
||||
from sqlalchemy import orm
|
||||
from sqlalchemy import String
|
||||
|
||||
from neutron.db import models_v2
|
||||
from neutron.openstack.common import log as logging
|
||||
from neutron.plugins.vmware.common import exceptions as p_exc
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class LsnPort(models_v2.model_base.BASEV2):
|
||||
|
||||
__tablename__ = 'lsn_port'
|
||||
|
||||
lsn_port_id = Column(String(36), primary_key=True)
|
||||
|
||||
lsn_id = Column(String(36), ForeignKey('lsn.lsn_id', ondelete="CASCADE"),
|
||||
nullable=False)
|
||||
sub_id = Column(String(36), nullable=False, unique=True)
|
||||
mac_addr = Column(String(32), nullable=False, unique=True)
|
||||
|
||||
def __init__(self, lsn_port_id, subnet_id, mac_address, lsn_id):
|
||||
self.lsn_port_id = lsn_port_id
|
||||
self.lsn_id = lsn_id
|
||||
self.sub_id = subnet_id
|
||||
self.mac_addr = mac_address
|
||||
|
||||
|
||||
class Lsn(models_v2.model_base.BASEV2):
|
||||
__tablename__ = 'lsn'
|
||||
|
||||
lsn_id = Column(String(36), primary_key=True)
|
||||
net_id = Column(String(36), nullable=False)
|
||||
|
||||
def __init__(self, net_id, lsn_id):
|
||||
self.net_id = net_id
|
||||
self.lsn_id = lsn_id
|
||||
|
||||
|
||||
def lsn_add(context, network_id, lsn_id):
|
||||
"""Add Logical Service Node information to persistent datastore."""
|
||||
with context.session.begin(subtransactions=True):
|
||||
lsn = Lsn(network_id, lsn_id)
|
||||
context.session.add(lsn)
|
||||
|
||||
|
||||
def lsn_remove(context, lsn_id):
|
||||
"""Remove Logical Service Node information from datastore given its id."""
|
||||
with context.session.begin(subtransactions=True):
|
||||
context.session.query(Lsn).filter_by(lsn_id=lsn_id).delete()
|
||||
|
||||
|
||||
def lsn_remove_for_network(context, network_id):
|
||||
"""Remove information about the Logical Service Node given its network."""
|
||||
with context.session.begin(subtransactions=True):
|
||||
context.session.query(Lsn).filter_by(net_id=network_id).delete()
|
||||
|
||||
|
||||
def lsn_get_for_network(context, network_id, raise_on_err=True):
|
||||
"""Retrieve LSN information given its network id."""
|
||||
query = context.session.query(Lsn)
|
||||
try:
|
||||
return query.filter_by(net_id=network_id).one()
|
||||
except (orm.exc.NoResultFound, d_exc.DBError):
|
||||
msg = _('Unable to find Logical Service Node for network %s')
|
||||
if raise_on_err:
|
||||
LOG.error(msg, network_id)
|
||||
raise p_exc.LsnNotFound(entity='network',
|
||||
entity_id=network_id)
|
||||
else:
|
||||
LOG.warn(msg, network_id)
|
||||
|
||||
|
||||
def lsn_port_add_for_lsn(context, lsn_port_id, subnet_id, mac, lsn_id):
|
||||
"""Add Logical Service Node Port information to persistent datastore."""
|
||||
with context.session.begin(subtransactions=True):
|
||||
lsn_port = LsnPort(lsn_port_id, subnet_id, mac, lsn_id)
|
||||
context.session.add(lsn_port)
|
||||
|
||||
|
||||
def lsn_port_get_for_subnet(context, subnet_id, raise_on_err=True):
|
||||
"""Return Logical Service Node Port information given its subnet id."""
|
||||
with context.session.begin(subtransactions=True):
|
||||
try:
|
||||
return (context.session.query(LsnPort).
|
||||
filter_by(sub_id=subnet_id).one())
|
||||
except (orm.exc.NoResultFound, d_exc.DBError):
|
||||
if raise_on_err:
|
||||
raise p_exc.LsnPortNotFound(lsn_id=None,
|
||||
entity='subnet',
|
||||
entity_id=subnet_id)
|
||||
|
||||
|
||||
def lsn_port_get_for_mac(context, mac_address, raise_on_err=True):
|
||||
"""Return Logical Service Node Port information given its mac address."""
|
||||
with context.session.begin(subtransactions=True):
|
||||
try:
|
||||
return (context.session.query(LsnPort).
|
||||
filter_by(mac_addr=mac_address).one())
|
||||
except (orm.exc.NoResultFound, d_exc.DBError):
|
||||
if raise_on_err:
|
||||
raise p_exc.LsnPortNotFound(lsn_id=None,
|
||||
entity='mac',
|
||||
entity_id=mac_address)
|
||||
|
||||
|
||||
def lsn_port_remove(context, lsn_port_id):
|
||||
"""Remove Logical Service Node port from the given Logical Service Node."""
|
||||
with context.session.begin(subtransactions=True):
|
||||
(context.session.query(LsnPort).
|
||||
filter_by(lsn_port_id=lsn_port_id).delete())
|
@ -1,78 +0,0 @@
|
||||
# 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
|
||||
|
||||
from neutron.api.v2 import attributes
|
||||
from neutron.db import db_base_plugin_v2
|
||||
from neutron.db import model_base
|
||||
from neutron.db import models_v2
|
||||
from neutron.openstack.common import log as logging
|
||||
from neutron.plugins.vmware.extensions import maclearning as mac
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class MacLearningState(model_base.BASEV2):
|
||||
|
||||
port_id = sa.Column(sa.String(36),
|
||||
sa.ForeignKey('ports.id', ondelete="CASCADE"),
|
||||
primary_key=True)
|
||||
mac_learning_enabled = sa.Column(sa.Boolean(), nullable=False)
|
||||
|
||||
# Add a relationship to the Port model using the backref attribute.
|
||||
# This will instruct SQLAlchemy to eagerly load this association.
|
||||
port = orm.relationship(
|
||||
models_v2.Port,
|
||||
backref=orm.backref("mac_learning_state", lazy='joined',
|
||||
uselist=False, cascade='delete'))
|
||||
|
||||
|
||||
class MacLearningDbMixin(object):
|
||||
"""Mixin class for mac learning."""
|
||||
|
||||
def _make_mac_learning_state_dict(self, port, fields=None):
|
||||
res = {'port_id': port['port_id'],
|
||||
mac.MAC_LEARNING: port[mac.MAC_LEARNING]}
|
||||
return self._fields(res, fields)
|
||||
|
||||
def _extend_port_mac_learning_state(self, port_res, port_db):
|
||||
state = port_db.mac_learning_state
|
||||
if state and state.mac_learning_enabled:
|
||||
port_res[mac.MAC_LEARNING] = state.mac_learning_enabled
|
||||
|
||||
# Register dict extend functions for ports
|
||||
db_base_plugin_v2.NeutronDbPluginV2.register_dict_extend_funcs(
|
||||
attributes.PORTS, ['_extend_port_mac_learning_state'])
|
||||
|
||||
def _update_mac_learning_state(self, context, port_id, enabled):
|
||||
try:
|
||||
query = self._model_query(context, MacLearningState)
|
||||
state = query.filter(MacLearningState.port_id == port_id).one()
|
||||
state.update({mac.MAC_LEARNING: enabled})
|
||||
except exc.NoResultFound:
|
||||
self._create_mac_learning_state(context,
|
||||
{'id': port_id,
|
||||
mac.MAC_LEARNING: enabled})
|
||||
|
||||
def _create_mac_learning_state(self, context, port):
|
||||
with context.session.begin(subtransactions=True):
|
||||
enabled = port[mac.MAC_LEARNING]
|
||||
state = MacLearningState(port_id=port['id'],
|
||||
mac_learning_enabled=enabled)
|
||||
context.session.add(state)
|
||||
return self._make_mac_learning_state_dict(state)
|
@ -1,117 +0,0 @@
|
||||
# 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.
|
||||
|
||||
|
||||
from sqlalchemy import Column, Enum, ForeignKey, Integer, String
|
||||
|
||||
from neutron.db import model_base
|
||||
|
||||
|
||||
class TzNetworkBinding(model_base.BASEV2):
|
||||
"""Represents a binding of a virtual network with a transport zone.
|
||||
|
||||
This model class associates a Neutron network with a transport zone;
|
||||
optionally a vlan ID might be used if the binding type is 'bridge'
|
||||
"""
|
||||
__tablename__ = 'tz_network_bindings'
|
||||
|
||||
# TODO(arosen) - it might be worth while refactoring the how this data
|
||||
# is stored later so every column does not need to be a primary key.
|
||||
network_id = Column(String(36),
|
||||
ForeignKey('networks.id', ondelete="CASCADE"),
|
||||
primary_key=True)
|
||||
# 'flat', 'vlan', stt' or 'gre'
|
||||
binding_type = Column(Enum('flat', 'vlan', 'stt', 'gre', 'l3_ext',
|
||||
name='tz_network_bindings_binding_type'),
|
||||
nullable=False, primary_key=True)
|
||||
phy_uuid = Column(String(36), primary_key=True, default='')
|
||||
vlan_id = Column(Integer, primary_key=True, autoincrement=False, default=0)
|
||||
|
||||
def __init__(self, network_id, binding_type, phy_uuid, vlan_id):
|
||||
self.network_id = network_id
|
||||
self.binding_type = binding_type
|
||||
self.phy_uuid = phy_uuid
|
||||
self.vlan_id = vlan_id
|
||||
|
||||
def __repr__(self):
|
||||
return "<NetworkBinding(%s,%s,%s,%s)>" % (self.network_id,
|
||||
self.binding_type,
|
||||
self.phy_uuid,
|
||||
self.vlan_id)
|
||||
|
||||
|
||||
class NeutronNsxNetworkMapping(model_base.BASEV2):
|
||||
"""Maps neutron network identifiers to NSX identifiers.
|
||||
|
||||
Because of chained logical switches more than one mapping might exist
|
||||
for a single Neutron network.
|
||||
"""
|
||||
__tablename__ = 'neutron_nsx_network_mappings'
|
||||
neutron_id = Column(String(36),
|
||||
ForeignKey('networks.id', ondelete='CASCADE'),
|
||||
primary_key=True)
|
||||
nsx_id = Column(String(36), primary_key=True)
|
||||
|
||||
|
||||
class NeutronNsxSecurityGroupMapping(model_base.BASEV2):
|
||||
"""Backend mappings for Neutron Security Group identifiers.
|
||||
|
||||
This class maps a neutron security group identifier to the corresponding
|
||||
NSX security profile identifier.
|
||||
"""
|
||||
|
||||
__tablename__ = 'neutron_nsx_security_group_mappings'
|
||||
neutron_id = Column(String(36),
|
||||
ForeignKey('securitygroups.id', ondelete="CASCADE"),
|
||||
primary_key=True)
|
||||
nsx_id = Column(String(36), primary_key=True)
|
||||
|
||||
|
||||
class NeutronNsxPortMapping(model_base.BASEV2):
|
||||
"""Represents the mapping between neutron and nsx port uuids."""
|
||||
|
||||
__tablename__ = 'neutron_nsx_port_mappings'
|
||||
neutron_id = Column(String(36),
|
||||
ForeignKey('ports.id', ondelete="CASCADE"),
|
||||
primary_key=True)
|
||||
nsx_switch_id = Column(String(36))
|
||||
nsx_port_id = Column(String(36), nullable=False)
|
||||
|
||||
def __init__(self, neutron_id, nsx_switch_id, nsx_port_id):
|
||||
self.neutron_id = neutron_id
|
||||
self.nsx_switch_id = nsx_switch_id
|
||||
self.nsx_port_id = nsx_port_id
|
||||
|
||||
|
||||
class NeutronNsxRouterMapping(model_base.BASEV2):
|
||||
"""Maps neutron router identifiers to NSX identifiers."""
|
||||
__tablename__ = 'neutron_nsx_router_mappings'
|
||||
neutron_id = Column(String(36),
|
||||
ForeignKey('routers.id', ondelete='CASCADE'),
|
||||
primary_key=True)
|
||||
nsx_id = Column(String(36))
|
||||
|
||||
|
||||
class MultiProviderNetworks(model_base.BASEV2):
|
||||
"""Networks provisioned through multiprovider extension."""
|
||||
|
||||
__tablename__ = 'multi_provider_networks'
|
||||
network_id = Column(String(36),
|
||||
ForeignKey('networks.id', ondelete="CASCADE"),
|
||||
primary_key=True)
|
||||
|
||||
def __init__(self, network_id):
|
||||
self.network_id = network_id
|
@ -1,521 +0,0 @@
|
||||
# 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 neutron.api.v2 import attributes
|
||||
from neutron.common import exceptions
|
||||
from neutron.common import utils
|
||||
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"
|
||||
|
||||
|
||||
class GatewayInUse(exceptions.InUse):
|
||||
message = _("Network Gateway '%(gateway_id)s' still has active mappings "
|
||||
"with one or more neutron networks.")
|
||||
|
||||
|
||||
class GatewayNotFound(exceptions.NotFound):
|
||||
message = _("Network Gateway %(gateway_id)s could not be found")
|
||||
|
||||
|
||||
class GatewayDeviceInUse(exceptions.InUse):
|
||||
message = _("Network Gateway Device '%(device_id)s' is still used by "
|
||||
"one or more network gateways.")
|
||||
|
||||
|
||||
class GatewayDeviceNotFound(exceptions.NotFound):
|
||||
message = _("Network Gateway Device %(device_id)s could not be found.")
|
||||
|
||||
|
||||
class GatewayDevicesNotFound(exceptions.NotFound):
|
||||
message = _("One or more Network Gateway Devices could not be found: "
|
||||
"%(device_ids)s.")
|
||||
|
||||
|
||||
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.")
|
||||
|
||||
|
||||
class GatewayConnectionInUse(exceptions.InUse):
|
||||
message = _("The specified mapping '%(mapping)s' is already in use on "
|
||||
"network gateway '%(gateway_id)s'.")
|
||||
|
||||
|
||||
class MultipleGatewayConnections(exceptions.Conflict):
|
||||
message = _("Multiple network connections found on '%(gateway_id)s' "
|
||||
"with provided criteria.")
|
||||
|
||||
|
||||
class GatewayConnectionNotFound(exceptions.NotFound):
|
||||
message = _("The connection %(network_mapping_info)s was not found on the "
|
||||
"network gateway '%(network_gateway_id)s'")
|
||||
|
||||
|
||||
class NetworkGatewayUnchangeable(exceptions.InUse):
|
||||
message = _("The network gateway %(gateway_id)s "
|
||||
"cannot be updated or deleted")
|
||||
|
||||
|
||||
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)
|
||||
|
||||
|
||||
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)
|
||||
|
||||
|
||||
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))
|
||||
|
||||
|
||||
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))
|
||||
default = sa.Column(sa.Boolean())
|
||||
devices = orm.relationship(NetworkGatewayDeviceReference,
|
||||
backref='networkgateways',
|
||||
cascade='all,delete')
|
||||
network_connections = orm.relationship(NetworkConnection, lazy='joined')
|
||||
|
||||
|
||||
class NetworkGatewayMixin(networkgw.NetworkGatewayPluginBase):
|
||||
|
||||
gateway_resource = networkgw.GATEWAY_RESOURCE_NAME
|
||||
device_resource = networkgw.DEVICE_RESOURCE_NAME
|
||||
|
||||
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
|
||||
|
||||
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']}
|
||||
|
||||
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 not fields or 'ports' in fields:
|
||||
res['ports'] = [self._make_gw_connection_dict(conn)
|
||||
for conn in network_gateway.network_connections]
|
||||
return self._fields(res, fields)
|
||||
|
||||
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
|
||||
|
||||
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)
|
||||
# The NSX plugin accepts 0 as a valid vlan tag
|
||||
seg_id_valid = seg_id == 0 or utils.is_valid_vlan_tag(seg_id)
|
||||
if 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)
|
||||
elif (seg_type.lower() == 'vlan' and not seg_id_valid):
|
||||
msg = _("Invalid segmentation id (%d) for "
|
||||
"vlan segmentation type") % seg_id
|
||||
raise exceptions.InvalidInput(error_message=msg)
|
||||
return network_id
|
||||
|
||||
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 query.one() if only_one else query.all()
|
||||
|
||||
def _unset_default_network_gateways(self, context):
|
||||
with context.session.begin(subtransactions=True):
|
||||
context.session.query(NetworkGateway).update(
|
||||
{NetworkGateway.default: False})
|
||||
|
||||
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
|
||||
|
||||
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'])
|
||||
|
||||
def _validate_device_list(self, context, tenant_id, gateway_data):
|
||||
device_query = self._query_gateway_devices(
|
||||
context, filters={'id': [device['id']
|
||||
for device in gateway_data['devices']]})
|
||||
retrieved_device_ids = set()
|
||||
for device in device_query:
|
||||
retrieved_device_ids.add(device['id'])
|
||||
if device['tenant_id'] != tenant_id:
|
||||
raise GatewayDeviceNotFound(device_id=device['id'])
|
||||
missing_device_ids = (
|
||||
set(device['id'] for device in gateway_data['devices']) -
|
||||
retrieved_device_ids)
|
||||
if missing_device_ids:
|
||||
raise GatewayDevicesNotFound(
|
||||
device_ids=",".join(missing_device_ids))
|
||||
|
||||
def create_network_gateway(self, context, network_gateway,
|
||||
validate_device_list=True):
|
||||
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, but some devices
|
||||
# might still either not exist or belong to a different tenant
|
||||
if validate_device_list:
|
||||
self._validate_device_list(context, tenant_id, gw_data)
|
||||
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)
|
||||
|
||||
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)
|
||||
|
||||
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)
|
||||
|
||||
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)
|
||||
|
||||
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)
|
||||
|
||||
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}}
|
||||
|
||||
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 NSX 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)
|
||||
|
||||
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)
|
||||
|
||||
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)
|
||||
|
||||
def _is_device_in_use(self, context, device_id):
|
||||
query = self._get_collection_query(
|
||||
context, NetworkGatewayDeviceReference, {'id': [device_id]})
|
||||
return query.first()
|
||||
|
||||
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)
|
||||
|
||||
def _query_gateway_devices(self, context,
|
||||
filters=None, sorts=None,
|
||||
limit=None, marker=None,
|
||||
page_reverse=None):
|
||||
marker_obj = self._get_marker_obj(
|
||||
context, 'gateway_device', limit, marker)
|
||||
return self._get_collection_query(context,
|
||||
NetworkGatewayDevice,
|
||||
filters=filters,
|
||||
sorts=sorts,
|
||||
limit=limit,
|
||||
marker_obj=marker_obj,
|
||||
page_reverse=page_reverse)
|
||||
|
||||
def get_gateway_devices(self, context, filters=None, fields=None,
|
||||
sorts=None, limit=None, marker=None,
|
||||
page_reverse=False, include_nsx_id=False):
|
||||
query = self._query_gateway_devices(context, filters, sorts, limit,
|
||||
marker, page_reverse)
|
||||
return [self._make_gateway_device_dict(row, fields, include_nsx_id)
|
||||
for row in query]
|
||||
|
||||
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)
|
||||
|
||||
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)
|
||||
|
||||
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)
|
66
vmware_nsx/neutron/plugins/vmware/dbexts/nsxrouter.py
Normal file
66
vmware_nsx/neutron/plugins/vmware/dbexts/nsxrouter.py
Normal file
@ -0,0 +1,66 @@
|
||||
# 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.
|
||||
#
|
||||
|
||||
from neutron.db import db_base_plugin_v2
|
||||
from neutron.extensions import l3
|
||||
from neutron.openstack.common import log as logging
|
||||
from neutron.plugins.vmware.dbexts import nsxv_models
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class NsxRouterMixin(object):
|
||||
"""Mixin class to enable nsx router support."""
|
||||
|
||||
nsx_attributes = []
|
||||
|
||||
def _extend_nsx_router_dict(self, router_res, router_db):
|
||||
nsx_attrs = router_db['nsx_attributes']
|
||||
# Return False if nsx attributes are not definied for this
|
||||
# neutron router
|
||||
for attr in self.nsx_attributes:
|
||||
name = attr['name']
|
||||
default = attr['default']
|
||||
router_res[name] = (
|
||||
nsx_attrs and nsx_attrs[name] or default)
|
||||
|
||||
def _process_nsx_router_create(
|
||||
self, context, router_db, router_req):
|
||||
if not router_db['nsx_attributes']:
|
||||
kwargs = {}
|
||||
for attr in self.nsx_attributes:
|
||||
name = attr['name']
|
||||
default = attr['default']
|
||||
kwargs[name] = router_req.get(name, default)
|
||||
nsx_attributes = nsxv_models.NsxvRouterExtAttributes(
|
||||
router_id=router_db['id'], **kwargs)
|
||||
context.session.add(nsx_attributes)
|
||||
router_db['nsx_attributes'] = nsx_attributes
|
||||
else:
|
||||
# The situation where the record already exists will
|
||||
# be likely once the NSXRouterExtAttributes model
|
||||
# will allow for defining several attributes pertaining
|
||||
# to different extensions
|
||||
for attr in self.nsx_attributes:
|
||||
name = attr['name']
|
||||
default = attr['default']
|
||||
router_db['nsx_attributes'][name] = router_req.get(
|
||||
name, default)
|
||||
LOG.debug("Nsx router extension successfully processed "
|
||||
"for router:%s", router_db['id'])
|
||||
|
||||
# Register dict extend functions for ports
|
||||
db_base_plugin_v2.NeutronDbPluginV2.register_dict_extend_funcs(
|
||||
l3.ROUTERS, ['_extend_nsx_router_dict'])
|
435
vmware_nsx/neutron/plugins/vmware/dbexts/nsxv_db.py
Normal file
435
vmware_nsx/neutron/plugins/vmware/dbexts/nsxv_db.py
Normal file
@ -0,0 +1,435 @@
|
||||
# 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.
|
||||
|
||||
from oslo.db import exception as db_exc
|
||||
from oslo.utils import excutils
|
||||
from sqlalchemy.orm import exc
|
||||