Merge "OpenFlow distributed router support in NEC plugin"
This commit is contained in:
commit
42cfd92556
@ -41,3 +41,9 @@ firewall_driver = neutron.agent.linux.iptables_firewall.OVSHybridIptablesFirewal
|
||||
|
||||
# Certificate file
|
||||
# cert_file =
|
||||
|
||||
[provider]
|
||||
# Default router provider to use.
|
||||
# default_router_provider = l3-agent
|
||||
# List of enabled router providers.
|
||||
# router_providers = l3-agent,openflow
|
||||
|
@ -91,6 +91,8 @@ class FloatingIP(model_base.BASEV2, models_v2.HasId, models_v2.HasTenant):
|
||||
class L3_NAT_db_mixin(l3.RouterPluginBase):
|
||||
"""Mixin class to add L3/NAT router methods to db_plugin_base_v2."""
|
||||
|
||||
l3_rpc_notifier = l3_rpc_agent_api.L3AgentNotify
|
||||
|
||||
def _network_model_hook(self, context, original_model, query):
|
||||
query = query.outerjoin(ExternalNetwork,
|
||||
(original_model.id ==
|
||||
@ -186,7 +188,7 @@ class L3_NAT_db_mixin(l3.RouterPluginBase):
|
||||
# Ensure we actually have something to update
|
||||
if r.keys():
|
||||
router_db.update(r)
|
||||
l3_rpc_agent_api.L3AgentNotify.routers_updated(
|
||||
self.l3_rpc_notifier.routers_updated(
|
||||
context, [router_db['id']])
|
||||
return self._make_router_dict(router_db)
|
||||
|
||||
@ -278,7 +280,7 @@ class L3_NAT_db_mixin(l3.RouterPluginBase):
|
||||
self._delete_port(context.elevated(), ports[0]['id'])
|
||||
|
||||
context.session.delete(router)
|
||||
l3_rpc_agent_api.L3AgentNotify.router_deleted(context, id)
|
||||
self.l3_rpc_notifier.router_deleted(context, id)
|
||||
|
||||
def get_router(self, context, id, fields=None):
|
||||
router = self._get_router(context, id)
|
||||
@ -385,7 +387,7 @@ class L3_NAT_db_mixin(l3.RouterPluginBase):
|
||||
'device_owner': DEVICE_OWNER_ROUTER_INTF,
|
||||
'name': ''}})
|
||||
|
||||
l3_rpc_agent_api.L3AgentNotify.routers_updated(
|
||||
self.l3_rpc_notifier.routers_updated(
|
||||
context, [router_id], 'add_router_interface')
|
||||
info = {'id': router_id,
|
||||
'tenant_id': subnet['tenant_id'],
|
||||
@ -457,7 +459,7 @@ class L3_NAT_db_mixin(l3.RouterPluginBase):
|
||||
if not found:
|
||||
raise l3.RouterInterfaceNotFoundForSubnet(router_id=router_id,
|
||||
subnet_id=subnet_id)
|
||||
l3_rpc_agent_api.L3AgentNotify.routers_updated(
|
||||
self.l3_rpc_notifier.routers_updated(
|
||||
context, [router_id], 'remove_router_interface')
|
||||
info = {'id': router_id,
|
||||
'tenant_id': subnet['tenant_id'],
|
||||
@ -670,7 +672,7 @@ class L3_NAT_db_mixin(l3.RouterPluginBase):
|
||||
|
||||
router_id = floatingip_db['router_id']
|
||||
if router_id:
|
||||
l3_rpc_agent_api.L3AgentNotify.routers_updated(
|
||||
self.l3_rpc_notifier.routers_updated(
|
||||
context, [router_id],
|
||||
'create_floatingip')
|
||||
return self._make_floatingip_dict(floatingip_db)
|
||||
@ -693,8 +695,8 @@ class L3_NAT_db_mixin(l3.RouterPluginBase):
|
||||
if router_id and router_id != before_router_id:
|
||||
router_ids.append(router_id)
|
||||
if router_ids:
|
||||
l3_rpc_agent_api.L3AgentNotify.routers_updated(context, router_ids,
|
||||
'update_floatingip')
|
||||
self.l3_rpc_notifier.routers_updated(
|
||||
context, router_ids, 'update_floatingip')
|
||||
return self._make_floatingip_dict(floatingip_db)
|
||||
|
||||
def delete_floatingip(self, context, id):
|
||||
@ -706,7 +708,7 @@ class L3_NAT_db_mixin(l3.RouterPluginBase):
|
||||
floatingip['floating_port_id'],
|
||||
l3_port_check=False)
|
||||
if router_id:
|
||||
l3_rpc_agent_api.L3AgentNotify.routers_updated(
|
||||
self.l3_rpc_notifier.routers_updated(
|
||||
context, [router_id],
|
||||
'delete_floatingip')
|
||||
|
||||
@ -777,7 +779,7 @@ class L3_NAT_db_mixin(l3.RouterPluginBase):
|
||||
raise Exception(_('Multiple floating IPs found for port %s')
|
||||
% port_id)
|
||||
if router_id:
|
||||
l3_rpc_agent_api.L3AgentNotify.routers_updated(
|
||||
self.l3_rpc_notifier.routers_updated(
|
||||
context, [router_id])
|
||||
|
||||
def _network_is_external(self, context, net_id):
|
||||
|
@ -0,0 +1,68 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
#
|
||||
# Copyright 2013 OpenStack Foundation
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
|
||||
"""NEC OpenFlow Router
|
||||
|
||||
Revision ID: 66a59a7f516
|
||||
Revises: 32a65f71af51
|
||||
Create Date: 2013-09-03 22:16:31.446031
|
||||
|
||||
"""
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '66a59a7f516'
|
||||
down_revision = '32a65f71af51'
|
||||
|
||||
# Change to ['*'] if this migration applies to all plugins
|
||||
|
||||
migration_for_plugins = [
|
||||
'neutron.plugins.nec.nec_plugin.NECPluginV2'
|
||||
]
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
from neutron.db import migration
|
||||
|
||||
|
||||
def upgrade(active_plugins=None, options=None):
|
||||
if not migration.should_run(active_plugins, migration_for_plugins):
|
||||
return
|
||||
|
||||
op.create_table(
|
||||
'ofcroutermappings',
|
||||
sa.Column('ofc_id', sa.String(length=255), nullable=False),
|
||||
sa.Column('quantum_id', sa.String(length=36), nullable=False),
|
||||
sa.PrimaryKeyConstraint('quantum_id'),
|
||||
sa.UniqueConstraint('ofc_id'),
|
||||
)
|
||||
op.create_table(
|
||||
'routerproviders',
|
||||
sa.Column('provider', sa.String(length=255), nullable=True),
|
||||
sa.Column('router_id', sa.String(length=36), nullable=False),
|
||||
sa.ForeignKeyConstraint(['router_id'], ['routers.id'],
|
||||
ondelete='CASCADE'),
|
||||
sa.PrimaryKeyConstraint('router_id'),
|
||||
)
|
||||
|
||||
|
||||
def downgrade(active_plugins=None, options=None):
|
||||
if not migration.should_run(active_plugins, migration_for_plugins):
|
||||
return
|
||||
|
||||
op.drop_table('routerproviders')
|
||||
op.drop_table('ofcroutermappings')
|
@ -19,6 +19,7 @@ from oslo.config import cfg
|
||||
|
||||
from neutron.agent.common import config
|
||||
from neutron.openstack.common import rpc # noqa
|
||||
from neutron.plugins.nec.common import constants as nconst
|
||||
|
||||
|
||||
ovs_opts = [
|
||||
@ -49,10 +50,20 @@ ofc_opts = [
|
||||
help=_("Certificate file")),
|
||||
]
|
||||
|
||||
provider_opts = [
|
||||
cfg.StrOpt('default_router_provider',
|
||||
default=nconst.DEFAULT_ROUTER_PROVIDER,
|
||||
help=_('Default router provider to use.')),
|
||||
cfg.ListOpt('router_providers',
|
||||
default=nconst.DEFAULT_ROUTER_PROVIDERS,
|
||||
help=_('List of enabled router providers.'))
|
||||
]
|
||||
|
||||
|
||||
cfg.CONF.register_opts(ovs_opts, "OVS")
|
||||
cfg.CONF.register_opts(agent_opts, "AGENT")
|
||||
cfg.CONF.register_opts(ofc_opts, "OFC")
|
||||
cfg.CONF.register_opts(provider_opts, "PROVIDER")
|
||||
config.register_agent_state_opts_helper(cfg.CONF)
|
||||
config.register_root_helper(cfg.CONF)
|
||||
|
||||
@ -61,3 +72,4 @@ CONF = cfg.CONF
|
||||
OVS = cfg.CONF.OVS
|
||||
AGENT = cfg.CONF.AGENT
|
||||
OFC = cfg.CONF.OFC
|
||||
PROVIDER = cfg.CONF.PROVIDER
|
||||
|
24
neutron/plugins/nec/common/constants.py
Normal file
24
neutron/plugins/nec/common/constants.py
Normal file
@ -0,0 +1,24 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
#
|
||||
# Copyright 2013 NEC Corporation. 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.
|
||||
|
||||
ROUTER_PROVIDER_L3AGENT = 'l3-agent'
|
||||
ROUTER_PROVIDER_OPENFLOW = 'openflow'
|
||||
|
||||
DEFAULT_ROUTER_PROVIDERS = [ROUTER_PROVIDER_L3AGENT, ROUTER_PROVIDER_OPENFLOW]
|
||||
DEFAULT_ROUTER_PROVIDER = ROUTER_PROVIDER_L3AGENT
|
||||
|
||||
ROUTER_STATUS_ACTIVE = 'ACTIVE'
|
||||
ROUTER_STATUS_ERROR = 'ERROR'
|
@ -21,6 +21,12 @@ from neutron.common import exceptions as qexc
|
||||
class OFCException(qexc.NeutronException):
|
||||
message = _("An OFC exception has occurred: %(reason)s")
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super(OFCException, self).__init__(**kwargs)
|
||||
self.status = kwargs.get('status')
|
||||
self.err_msg = kwargs.get('err_msg')
|
||||
self.err_code = kwargs.get('err_code')
|
||||
|
||||
|
||||
class NECDBException(qexc.NeutronException):
|
||||
message = _("An exception occurred in NECPluginV2 DB: %(reason)s")
|
||||
@ -44,3 +50,22 @@ class ProfilePortInfoInvalidDataPathId(qexc.InvalidInput):
|
||||
class ProfilePortInfoInvalidPortNo(qexc.InvalidInput):
|
||||
message = _('Invalid input for operation: '
|
||||
'portinfo:port_no should be [0:65535]')
|
||||
|
||||
|
||||
class RouterExternalGatewayNotSupported(qexc.BadRequest):
|
||||
message = _("Router (provider=%(provider)s) does not support "
|
||||
"an external network")
|
||||
|
||||
|
||||
class ProviderNotFound(qexc.NotFound):
|
||||
message = _("Provider %(provider)s could not be found")
|
||||
|
||||
|
||||
class RouterOverLimit(qexc.Conflict):
|
||||
message = _("Cannot create more routers with provider=%(provider)s")
|
||||
|
||||
|
||||
class RouterProviderMismatch(qexc.Conflict):
|
||||
message = _("Provider of Router %(router_id)s is %(provider)s. "
|
||||
"This operation is supported only for router provider "
|
||||
"%(expected_provider)s.")
|
||||
|
@ -46,12 +46,26 @@ class OFCClient(object):
|
||||
self.cert_file = cert_file
|
||||
self.connection = None
|
||||
|
||||
def get_connection_type(self):
|
||||
"""Returns the proper connection type."""
|
||||
def get_connection(self):
|
||||
"""Returns the proper connection."""
|
||||
if self.use_ssl:
|
||||
return httplib.HTTPSConnection
|
||||
connection_type = httplib.HTTPSConnection
|
||||
else:
|
||||
return httplib.HTTPConnection
|
||||
connection_type = httplib.HTTPConnection
|
||||
|
||||
# Open connection and send request, handling SSL certs
|
||||
certs = {'key_file': self.key_file, 'cert_file': self.cert_file}
|
||||
certs = dict((x, certs[x]) for x in certs if certs[x] is not None)
|
||||
if self.use_ssl and len(certs):
|
||||
conn = connection_type(self.host, self.port, **certs)
|
||||
else:
|
||||
conn = connection_type(self.host, self.port)
|
||||
return conn
|
||||
|
||||
def _format_error_message(self, status, detail):
|
||||
detail = ' ' + detail if detail else ''
|
||||
return (_("Operation on OFC failed: %(status)s%(msg)s") %
|
||||
{'status': status, 'msg': detail})
|
||||
|
||||
def do_request(self, method, action, body=None):
|
||||
LOG.debug(_("Client request: %(host)s:%(port)s "
|
||||
@ -61,32 +75,40 @@ class OFCClient(object):
|
||||
if type(body) is dict:
|
||||
body = json.dumps(body)
|
||||
try:
|
||||
connection_type = self.get_connection_type()
|
||||
conn = self.get_connection()
|
||||
headers = {"Content-Type": "application/json"}
|
||||
# Open connection and send request, handling SSL certs
|
||||
certs = {'key_file': self.key_file, 'cert_file': self.cert_file}
|
||||
certs = dict((x, certs[x]) for x in certs if certs[x] is not None)
|
||||
if self.use_ssl and len(certs):
|
||||
conn = connection_type(self.host, self.port, **certs)
|
||||
else:
|
||||
conn = connection_type(self.host, self.port)
|
||||
conn.request(method, action, body, headers)
|
||||
res = conn.getresponse()
|
||||
data = res.read()
|
||||
LOG.debug(_("OFC returns [%(status)s:%(data)s]"),
|
||||
{'status': res.status,
|
||||
'data': data})
|
||||
|
||||
# Try to decode JSON data if possible.
|
||||
try:
|
||||
data = json.loads(data)
|
||||
except (ValueError, TypeError):
|
||||
pass
|
||||
|
||||
if res.status in (httplib.OK,
|
||||
httplib.CREATED,
|
||||
httplib.ACCEPTED,
|
||||
httplib.NO_CONTENT):
|
||||
if data and len(data) > 1:
|
||||
return json.loads(data)
|
||||
return data
|
||||
else:
|
||||
reason = _("An operation on OFC is failed.")
|
||||
raise nexc.OFCException(reason=reason)
|
||||
LOG.warning(_("Operation on OFC failed: "
|
||||
"status=%(status), detail=%(detail)"),
|
||||
{'status': res.status, 'detail': data})
|
||||
params = {'reason': _("Operation on OFC failed"),
|
||||
'status': res.status}
|
||||
if isinstance(data, dict):
|
||||
params['err_code'] = data.get('err_code')
|
||||
params['err_msg'] = data.get('err_msg')
|
||||
else:
|
||||
params['err_msg'] = data
|
||||
raise nexc.OFCException(**params)
|
||||
except (socket.error, IOError) as e:
|
||||
reason = _("Failed to connect OFC : %s") % str(e)
|
||||
reason = _("Failed to connect OFC : %s") % e
|
||||
LOG.error(reason)
|
||||
raise nexc.OFCException(reason=reason)
|
||||
|
||||
|
@ -36,6 +36,7 @@ OFP_VLAN_NONE = 0xffff
|
||||
resource_map = {'ofc_tenant': nmodels.OFCTenantMapping,
|
||||
'ofc_network': nmodels.OFCNetworkMapping,
|
||||
'ofc_port': nmodels.OFCPortMapping,
|
||||
'ofc_router': nmodels.OFCRouterMapping,
|
||||
'ofc_packet_filter': nmodels.OFCFilterMapping}
|
||||
|
||||
old_resource_map = {'ofc_tenant': nmodels.OFCTenant,
|
||||
@ -48,7 +49,9 @@ old_resource_map = {'ofc_tenant': nmodels.OFCTenant,
|
||||
|
||||
def _get_resource_model(resource, old_style):
|
||||
if old_style:
|
||||
return old_resource_map[resource]
|
||||
# NOTE: Some new resources are not defined in old_resource_map.
|
||||
# In such case None is returned.
|
||||
return old_resource_map.get(resource)
|
||||
else:
|
||||
return resource_map[resource]
|
||||
|
||||
@ -62,8 +65,10 @@ def clear_db(base=model_base.BASEV2):
|
||||
|
||||
|
||||
def get_ofc_item(session, resource, neutron_id, old_style=False):
|
||||
model = _get_resource_model(resource, old_style)
|
||||
if not model:
|
||||
return None
|
||||
try:
|
||||
model = _get_resource_model(resource, old_style)
|
||||
return session.query(model).filter_by(quantum_id=neutron_id).one()
|
||||
except sa.orm.exc.NoResultFound:
|
||||
return None
|
||||
|
@ -47,6 +47,10 @@ class OFCPortMapping(model_base.BASEV2, NeutronId, OFCId):
|
||||
"""Represents a Port on OpenFlow Network/Controller."""
|
||||
|
||||
|
||||
class OFCRouterMapping(model_base.BASEV2, NeutronId, OFCId):
|
||||
"""Represents a router on OpenFlow Network/Controller."""
|
||||
|
||||
|
||||
class OFCFilterMapping(model_base.BASEV2, NeutronId, OFCId):
|
||||
"""Represents a Filter on OpenFlow Network/Controller."""
|
||||
|
||||
|
92
neutron/plugins/nec/db/router.py
Normal file
92
neutron/plugins/nec/db/router.py
Normal file
@ -0,0 +1,92 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2013 NEC Corporation. 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_exc
|
||||
|
||||
from neutron.db import l3_db
|
||||
from neutron.db import models_v2
|
||||
from neutron.openstack.common import log as logging
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class RouterProvider(models_v2.model_base.BASEV2):
|
||||
"""Represents a binding of router_id to provider."""
|
||||
provider = sa.Column(sa.String(255))
|
||||
router_id = sa.Column(sa.String(36),
|
||||
sa.ForeignKey('routers.id', ondelete="CASCADE"),
|
||||
primary_key=True)
|
||||
|
||||
router = orm.relationship(l3_db.Router, uselist=False,
|
||||
backref=orm.backref('provider', uselist=False,
|
||||
lazy='joined',
|
||||
cascade='delete'))
|
||||
|
||||
|
||||
def _get_router_providers_query(query, provider=None, router_ids=None):
|
||||
if provider:
|
||||
query = query.filter_by(provider=provider)
|
||||
if router_ids:
|
||||
column = RouterProvider.router_id
|
||||
query = query.filter(column.in_(router_ids))
|
||||
return query
|
||||
|
||||
|
||||
def get_router_providers(session, provider=None, router_ids=None):
|
||||
"""Retrieve a list of a pair of router ID and its provider."""
|
||||
query = session.query(RouterProvider)
|
||||
query = _get_router_providers_query(query, provider, router_ids)
|
||||
return [{'provider': router.provider, 'router_id': router.router_id}
|
||||
for router in query]
|
||||
|
||||
|
||||
def get_routers_by_provider(session, provider, router_ids=None):
|
||||
"""Retrieve a list of router IDs with the given provider."""
|
||||
query = session.query(RouterProvider.router_id)
|
||||
query = _get_router_providers_query(query, provider, router_ids)
|
||||
return [router[0] for router in query]
|
||||
|
||||
|
||||
def get_router_count_by_provider(session, provider, tenant_id=None):
|
||||
"""Return the number of routers with the given provider."""
|
||||
query = session.query(RouterProvider).filter_by(provider=provider)
|
||||
if tenant_id:
|
||||
query = (query.join('router').
|
||||
filter(l3_db.Router.tenant_id == tenant_id))
|
||||
return query.count()
|
||||
|
||||
|
||||
def get_provider_by_router(session, router_id):
|
||||
"""Retrieve a provider of the given router."""
|
||||
try:
|
||||
binding = (session.query(RouterProvider).
|
||||
filter_by(router_id=router_id).
|
||||
one())
|
||||
except sa_exc.NoResultFound:
|
||||
return None
|
||||
return binding.provider
|
||||
|
||||
|
||||
def add_router_provider_binding(session, provider, router_id):
|
||||
"""Add a router provider association."""
|
||||
LOG.debug(_("Add provider binding "
|
||||
"(router=%(router_id)s, provider=%(provider)s)"),
|
||||
{'router_id': router_id, 'provider': provider})
|
||||
binding = RouterProvider(provider=provider, router_id=router_id)
|
||||
session.add(binding)
|
||||
return binding
|
@ -28,7 +28,9 @@ DRIVER_LIST = {
|
||||
'trema_mac': DRIVER_PATH % "trema.TremaMACBaseDriver",
|
||||
'pfc': DRIVER_PATH % "pfc.PFCV4Driver",
|
||||
'pfc_v3': DRIVER_PATH % "pfc.PFCV3Driver",
|
||||
'pfc_v4': DRIVER_PATH % "pfc.PFCV4Driver"}
|
||||
'pfc_v4': DRIVER_PATH % "pfc.PFCV4Driver",
|
||||
'pfc_v5': DRIVER_PATH % "pfc.PFCV5Driver",
|
||||
}
|
||||
|
||||
|
||||
def get_driver(driver_name):
|
||||
|
@ -33,6 +33,8 @@ class PFCDriverBase(ofc_driver_base.OFCDriverBase):
|
||||
The class implements the API for PFC V4.0 or later.
|
||||
"""
|
||||
|
||||
router_supported = False
|
||||
|
||||
def __init__(self, conf_ofc):
|
||||
self.client = ofc_client.OFCClient(host=conf_ofc.host,
|
||||
port=conf_ofc.port,
|
||||
@ -73,6 +75,10 @@ class PFCDriverBase(ofc_driver_base.OFCDriverBase):
|
||||
"""
|
||||
return self._generate_pfc_str(desc)[:127]
|
||||
|
||||
def _extract_ofc_network_id(self, ofc_network_id):
|
||||
# ofc_network_id : /tenants/<tenant-id>/networks/<network-id>
|
||||
return ofc_network_id.split('/')[4]
|
||||
|
||||
def create_tenant(self, description, tenant_id=None):
|
||||
ofc_tenant_id = self._generate_pfc_id(tenant_id)
|
||||
body = {'id': ofc_tenant_id}
|
||||
@ -136,6 +142,66 @@ class PFCDriverBase(ofc_driver_base.OFCDriverBase):
|
||||
return '%(network)s/ports/%(port)s' % params
|
||||
|
||||
|
||||
class PFCRouterDriverMixin(object):
|
||||
|
||||
router_supported = True
|
||||
router_nat_supported = False
|
||||
|
||||
def create_router(self, ofc_tenant_id, router_id, description):
|
||||
path = '%s/routers' % ofc_tenant_id
|
||||
res = self.client.post(path, body=None)
|
||||
ofc_router_id = res['id']
|
||||
return path + '/' + ofc_router_id
|
||||
|
||||
def delete_router(self, ofc_router_id):
|
||||
return self.client.delete(ofc_router_id)
|
||||
|
||||
def add_router_interface(self, ofc_router_id, ofc_net_id,
|
||||
ip_address=None, mac_address=None):
|
||||
# ip_address : <ip_address>/<netmask> (e.g., 10.0.0.0/24)
|
||||
path = '%s/interfaces' % ofc_router_id
|
||||
body = {'net_id': self._extract_ofc_network_id(ofc_net_id)}
|
||||
if ip_address:
|
||||
body['ip_address'] = ip_address
|
||||
if mac_address:
|
||||
body['mac_address'] = mac_address
|
||||
res = self.client.post(path, body=body)
|
||||
return path + '/' + res['id']
|
||||
|
||||
def update_router_interface(self, ofc_router_inf_id,
|
||||
ip_address=None, mac_address=None):
|
||||
# ip_address : <ip_address>/<netmask> (e.g., 10.0.0.0/24)
|
||||
if not ip_address and not mac_address:
|
||||
return
|
||||
body = {}
|
||||
if ip_address:
|
||||
body['ip_address'] = ip_address
|
||||
if mac_address:
|
||||
body['mac_address'] = mac_address
|
||||
return self.client.put(ofc_router_inf_id, body=body)
|
||||
|
||||
def delete_router_interface(self, ofc_router_inf_id):
|
||||
return self.client.delete(ofc_router_inf_id)
|
||||
|
||||
def list_router_routes(self, ofc_router_id):
|
||||
path = '%s/routes' % ofc_router_id
|
||||
ret = self.client.get(path)
|
||||
# Prepend ofc_router_id to route_id
|
||||
for r in ret['routes']:
|
||||
r['id'] = ofc_router_id + '/routes/' + r['id']
|
||||
return ret['routes']
|
||||
|
||||
def add_router_route(self, ofc_router_id, destination, nexthop):
|
||||
path = '%s/routes' % ofc_router_id
|
||||
body = {'destination': destination,
|
||||
'nexthop': nexthop}
|
||||
ret = self.client.post(path, body=body)
|
||||
return path + '/' + ret['id']
|
||||
|
||||
def delete_router_route(self, ofc_router_route_id):
|
||||
return self.client.delete(ofc_router_route_id)
|
||||
|
||||
|
||||
class PFCV3Driver(PFCDriverBase):
|
||||
|
||||
def create_tenant(self, description, tenant_id):
|
||||
@ -148,3 +214,7 @@ class PFCV3Driver(PFCDriverBase):
|
||||
|
||||
class PFCV4Driver(PFCDriverBase):
|
||||
pass
|
||||
|
||||
|
||||
class PFCV5Driver(PFCRouterDriverMixin, PFCDriverBase):
|
||||
pass
|
||||
|
@ -27,6 +27,8 @@ class TremaDriverBase(ofc_driver_base.OFCDriverBase):
|
||||
networks_path = "/networks"
|
||||
network_path = "/networks/%s"
|
||||
|
||||
router_supported = False
|
||||
|
||||
def __init__(self, conf_ofc):
|
||||
# Trema sliceable REST API does not support HTTPS
|
||||
self.client = ofc_client.OFCClient(host=conf_ofc.host,
|
||||
@ -74,7 +76,7 @@ class TremaDriverBase(ofc_driver_base.OFCDriverBase):
|
||||
return self.network_path % ofc_network_id
|
||||
|
||||
|
||||
class TremaFilterDriver(object):
|
||||
class TremaFilterDriverMixin(object):
|
||||
"""Trema (Sliceable Switch) PacketFilter Driver Mixin."""
|
||||
filters_path = "/filters"
|
||||
filter_path = "/filters/%s"
|
||||
@ -173,7 +175,7 @@ class TremaFilterDriver(object):
|
||||
return self.filter_path % ofc_filter_id
|
||||
|
||||
|
||||
class TremaPortBaseDriver(TremaDriverBase, TremaFilterDriver):
|
||||
class TremaPortBaseDriver(TremaDriverBase, TremaFilterDriverMixin):
|
||||
"""Trema (Sliceable Switch) Driver for port base binding.
|
||||
|
||||
TremaPortBaseDriver uses port base binding.
|
||||
@ -211,7 +213,7 @@ class TremaPortBaseDriver(TremaDriverBase, TremaFilterDriver):
|
||||
'port': ofc_port_id}
|
||||
|
||||
|
||||
class TremaPortMACBaseDriver(TremaDriverBase, TremaFilterDriver):
|
||||
class TremaPortMACBaseDriver(TremaDriverBase, TremaFilterDriverMixin):
|
||||
"""Trema (Sliceable Switch) Driver for port-mac base binding.
|
||||
|
||||
TremaPortBaseDriver uses port-mac base binding.
|
||||
|
61
neutron/plugins/nec/extensions/router_provider.py
Normal file
61
neutron/plugins/nec/extensions/router_provider.py
Normal file
@ -0,0 +1,61 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
#
|
||||
# Copyright 2013 NEC Corporation. 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.api import extensions
|
||||
from neutron.api.v2 import attributes
|
||||
from neutron.openstack.common import log as logging
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
ROUTER_PROVIDER = 'provider'
|
||||
|
||||
ROUTER_PROVIDER_ATTRIBUTE = {
|
||||
'routers': {ROUTER_PROVIDER:
|
||||
{'allow_post': True,
|
||||
'allow_put': False,
|
||||
'is_visible': True,
|
||||
'default': attributes.ATTR_NOT_SPECIFIED}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class Router_provider(extensions.ExtensionDescriptor):
|
||||
@classmethod
|
||||
def get_name(cls):
|
||||
return "Router Provider"
|
||||
|
||||
@classmethod
|
||||
def get_alias(cls):
|
||||
return "router_provider"
|
||||
|
||||
@classmethod
|
||||
def get_description(cls):
|
||||
return "Router Provider Support"
|
||||
|
||||
@classmethod
|
||||
def get_namespace(cls):
|
||||
return "http://docs.openstack.org/ext/router_provider/api/v1.0"
|
||||
|
||||
@classmethod
|
||||
def get_updated(cls):
|
||||
return "2013-08-20T10:00:00-00:00"
|
||||
|
||||
def get_extended_resources(self, version):
|
||||
if version == "2.0":
|
||||
return ROUTER_PROVIDER_ATTRIBUTE
|
||||
else:
|
||||
return {}
|
@ -18,7 +18,6 @@
|
||||
|
||||
from neutron.agent import securitygroups_rpc as sg_rpc
|
||||
from neutron.api.rpc.agentnotifiers import dhcp_rpc_agent_api
|
||||
from neutron.api.rpc.agentnotifiers import l3_rpc_agent_api
|
||||
from neutron.api.v2 import attributes as attrs
|
||||
from neutron.common import constants as const
|
||||
from neutron.common import exceptions as q_exc
|
||||
@ -28,8 +27,6 @@ from neutron.db import agents_db
|
||||
from neutron.db import agentschedulers_db
|
||||
from neutron.db import db_base_plugin_v2
|
||||
from neutron.db import dhcp_rpc_base
|
||||
from neutron.db import extraroute_db
|
||||
from neutron.db import l3_gwmode_db
|
||||
from neutron.db import l3_rpc_base
|
||||
from neutron.db import portbindings_base
|
||||
from neutron.db import portbindings_db
|
||||
@ -44,6 +41,8 @@ from neutron.openstack.common import uuidutils
|
||||
from neutron.plugins.nec.common import config
|
||||
from neutron.plugins.nec.common import exceptions as nexc
|
||||
from neutron.plugins.nec.db import api as ndb
|
||||
from neutron.plugins.nec.db import router as rdb
|
||||
from neutron.plugins.nec import nec_router
|
||||
from neutron.plugins.nec import ofc_manager
|
||||
from neutron.plugins.nec import packet_filter
|
||||
|
||||
@ -51,11 +50,10 @@ LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class NECPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
|
||||
extraroute_db.ExtraRoute_db_mixin,
|
||||
l3_gwmode_db.L3_NAT_db_mixin,
|
||||
nec_router.RouterMixin,
|
||||
sg_db_rpc.SecurityGroupServerRpcMixin,
|
||||
agentschedulers_db.L3AgentSchedulerDbMixin,
|
||||
agentschedulers_db.DhcpAgentSchedulerDbMixin,
|
||||
nec_router.L3AgentSchedulerDbMixin,
|
||||
packet_filter.PacketFilterMixin,
|
||||
portbindings_db.PortBindingMixin):
|
||||
"""NECPluginV2 controls an OpenFlow Controller.
|
||||
@ -70,12 +68,18 @@ class NECPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
|
||||
The port binding extension enables an external application relay
|
||||
information to and from the plugin.
|
||||
"""
|
||||
_supported_extension_aliases = ["router", "ext-gw-mode", "quotas",
|
||||
"binding", "security-group",
|
||||
"extraroute", "agent",
|
||||
"l3_agent_scheduler",
|
||||
_supported_extension_aliases = ["agent",
|
||||
"binding",
|
||||
"dhcp_agent_scheduler",
|
||||
"packet-filter"]
|
||||
"ext-gw-mode",
|
||||
"extraroute",
|
||||
"l3_agent_scheduler",
|
||||
"packet-filter",
|
||||
"quotas",
|
||||
"router",
|
||||
"router_provider",
|
||||
"security-group",
|
||||
]
|
||||
|
||||
@property
|
||||
def supported_extension_aliases(self):
|
||||
@ -99,6 +103,7 @@ class NECPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
|
||||
'neutron/plugins/nec/extensions')
|
||||
|
||||
self.setup_rpc()
|
||||
self.l3_rpc_notifier = nec_router.L3AgentNotifyAPI()
|
||||
|
||||
self.network_scheduler = importutils.import_object(
|
||||
config.CONF.network_scheduler_driver
|
||||
@ -107,6 +112,20 @@ class NECPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
|
||||
config.CONF.router_scheduler_driver
|
||||
)
|
||||
|
||||
nec_router.load_driver(self, self.ofc)
|
||||
self.port_handlers = {
|
||||
'create': {
|
||||
const.DEVICE_OWNER_ROUTER_GW: self.create_router_port,
|
||||
const.DEVICE_OWNER_ROUTER_INTF: self.create_router_port,
|
||||
'default': self.activate_port_if_ready,
|
||||
},
|
||||
'delete': {
|
||||
const.DEVICE_OWNER_ROUTER_GW: self.delete_router_port,
|
||||
const.DEVICE_OWNER_ROUTER_INTF: self.delete_router_port,
|
||||
'default': self.deactivate_port,
|
||||
}
|
||||
}
|
||||
|
||||
def setup_rpc(self):
|
||||
self.topic = topics.PLUGIN
|
||||
self.conn = rpc.create_connection(new=True)
|
||||
@ -115,13 +134,14 @@ class NECPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
|
||||
dhcp_rpc_agent_api.DhcpAgentNotifyAPI()
|
||||
)
|
||||
self.agent_notifiers[const.AGENT_TYPE_L3] = (
|
||||
l3_rpc_agent_api.L3AgentNotify
|
||||
nec_router.L3AgentNotifyAPI()
|
||||
)
|
||||
|
||||
# NOTE: callback_sg is referred to from the sg unit test.
|
||||
self.callback_sg = SecurityGroupServerRpcCallback()
|
||||
callbacks = [NECPluginV2RPCCallbacks(self),
|
||||
DhcpRpcCallback(), L3RpcCallback(),
|
||||
DhcpRpcCallback(),
|
||||
L3RpcCallback(),
|
||||
self.callback_sg,
|
||||
agents_db.AgentExtRpcCallback()]
|
||||
self.dispatcher = q_rpc.PluginRpcDispatcher(callbacks)
|
||||
@ -131,10 +151,35 @@ class NECPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
|
||||
|
||||
def _update_resource_status(self, context, resource, id, status):
|
||||
"""Update status of specified resource."""
|
||||
request = {}
|
||||
request[resource] = dict(status=status)
|
||||
obj_updater = getattr(super(NECPluginV2, self), "update_%s" % resource)
|
||||
obj_updater(context, id, request)
|
||||
request = {'status': status}
|
||||
obj_getter = getattr(self, '_get_%s' % resource)
|
||||
with context.session.begin(subtransactions=True):
|
||||
obj_db = obj_getter(context, id)
|
||||
obj_db.update(request)
|
||||
|
||||
def _check_ofc_tenant_in_use(self, context, tenant_id):
|
||||
"""Check if the specified tenant is used."""
|
||||
# All networks are created on OFC
|
||||
filters = {'tenant_id': [tenant_id]}
|
||||
if self.get_networks_count(context, filters=filters):
|
||||
return True
|
||||
if rdb.get_router_count_by_provider(context.session,
|
||||
nec_router.PROVIDER_OPENFLOW,
|
||||
tenant_id):
|
||||
return True
|
||||
return False
|
||||
|
||||
def _cleanup_ofc_tenant(self, context, tenant_id):
|
||||
if not self._check_ofc_tenant_in_use(context, tenant_id):
|
||||
try:
|
||||
if self.ofc.exists_ofc_tenant(context, tenant_id):
|
||||
self.ofc.delete_ofc_tenant(context, tenant_id)
|
||||
else:
|
||||
LOG.debug(_('_cleanup_ofc_tenant: No OFC tenant for %s'),
|
||||
tenant_id)
|
||||
except (nexc.OFCException, nexc.OFCConsistencyBroken) as exc:
|
||||
reason = _("delete_ofc_tenant() failed due to %s") % exc
|
||||
LOG.warn(reason)
|
||||
|
||||
def activate_port_if_ready(self, context, port, network=None):
|
||||
"""Activate port by creating port on OFC if ready.
|
||||
@ -315,7 +360,6 @@ class NECPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
|
||||
self.delete_packet_filter(context, pf['id'])
|
||||
|
||||
try:
|
||||
# 'net' parameter is required to lookup old OFC mapping
|
||||
self.ofc.delete_ofc_network(context, id, net)
|
||||
except (nexc.OFCException, nexc.OFCConsistencyBroken) as exc:
|
||||
reason = _("delete_network() failed due to %s") % exc
|
||||
@ -326,15 +370,7 @@ class NECPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
|
||||
|
||||
super(NECPluginV2, self).delete_network(context, id)
|
||||
|
||||
# delete unnessary ofc_tenant
|
||||
filters = dict(tenant_id=[tenant_id])
|
||||
nets = super(NECPluginV2, self).get_networks(context, filters=filters)
|
||||
if not nets:
|
||||
try:
|
||||
self.ofc.delete_ofc_tenant(context, tenant_id)
|
||||
except (nexc.OFCException, nexc.OFCConsistencyBroken) as exc:
|
||||
reason = _("delete_ofc_tenant() failed due to %s") % exc
|
||||
LOG.warn(reason)
|
||||
self._cleanup_ofc_tenant(context, tenant_id)
|
||||
|
||||
def _get_base_binding_dict(self):
|
||||
binding = {
|
||||
@ -449,6 +485,14 @@ class NECPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
|
||||
context, port_data, port)
|
||||
return portinfo_changed
|
||||
|
||||
def _get_port_handler(self, operation, device_owner):
|
||||
handlers = self.port_handlers[operation]
|
||||
handler = handlers.get(device_owner)
|
||||
if handler:
|
||||
return handler
|
||||
else:
|
||||
return handlers['default']
|
||||
|
||||
def create_port(self, context, port):
|
||||
"""Create a new port entry on DB, then try to activate it."""
|
||||
LOG.debug(_("NECPluginV2.create_port() called, port=%s ."), port)
|
||||
@ -465,7 +509,8 @@ class NECPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
|
||||
context, port, sgids)
|
||||
self.notify_security_groups_member_updated(context, port)
|
||||
|
||||
return self.activate_port_if_ready(context, port)
|
||||
handler = self._get_port_handler('create', port['device_owner'])
|
||||
return handler(context, port)
|
||||
|
||||
def _update_ofc_port_if_required(self, context, old_port, new_port,
|
||||
portinfo_changed):
|
||||
@ -539,7 +584,9 @@ class NECPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
|
||||
# Thus we need to call self.get_port() instead of super().get_port()
|
||||
port = self.get_port(context, id)
|
||||
|
||||
port = self.deactivate_port(context, port)
|
||||
handler = self._get_port_handler('delete', port['device_owner'])
|
||||
port = handler(context, port)
|
||||
# port = self.deactivate_port(context, port)
|
||||
if port['status'] == const.PORT_STATUS_ERROR:
|
||||
reason = _("Failed to delete port=%s from OFC.") % id
|
||||
raise nexc.OFCException(reason=reason)
|
||||
|
356
neutron/plugins/nec/nec_router.py
Normal file
356
neutron/plugins/nec/nec_router.py
Normal file
@ -0,0 +1,356 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
#
|
||||
# Copyright 2013 NEC Corporation. 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: Akihiro Motoki
|
||||
|
||||
from neutron.api.rpc.agentnotifiers import l3_rpc_agent_api
|
||||
from neutron.api.v2 import attributes as attr
|
||||
from neutron.common import exceptions as q_exc
|
||||
from neutron.db import agentschedulers_db
|
||||
from neutron.db import db_base_plugin_v2
|
||||
from neutron.db import extraroute_db
|
||||
from neutron.db import l3_db
|
||||
from neutron.db import l3_gwmode_db
|
||||
from neutron.db import models_v2
|
||||
from neutron.extensions import l3
|
||||
from neutron.openstack.common import importutils
|
||||
from neutron.openstack.common import log as logging
|
||||
from neutron.plugins.nec.common import config
|
||||
from neutron.plugins.nec.common import constants as nconst
|
||||
from neutron.plugins.nec.common import exceptions as nexc
|
||||
from neutron.plugins.nec.db import router as rdb
|
||||
from neutron.plugins.nec.extensions import router_provider as ext_provider
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
PROVIDER_L3AGENT = nconst.ROUTER_PROVIDER_L3AGENT
|
||||
PROVIDER_OPENFLOW = nconst.ROUTER_PROVIDER_OPENFLOW
|
||||
|
||||
ROUTER_DRIVER_PATH = 'neutron.plugins.nec.router_drivers.'
|
||||
ROUTER_DRIVER_MAP = {
|
||||
PROVIDER_L3AGENT: ROUTER_DRIVER_PATH + 'RouterL3AgentDriver',
|
||||
PROVIDER_OPENFLOW: ROUTER_DRIVER_PATH + 'RouterOpenFlowDriver'
|
||||
}
|
||||
|
||||
ROUTER_DRIVERS = {}
|
||||
|
||||
STATUS_ACTIVE = nconst.ROUTER_STATUS_ACTIVE
|
||||
STATUS_ERROR = nconst.ROUTER_STATUS_ERROR
|
||||
|
||||
|
||||
class RouterMixin(extraroute_db.ExtraRoute_db_mixin,
|
||||
l3_gwmode_db.L3_NAT_db_mixin):
|
||||
|
||||
def create_router(self, context, router):
|
||||
"""Create a new router entry on DB, and create it on OFC."""
|
||||
LOG.debug(_("RouterMixin.create_router() called, "
|
||||
"router=%s ."), router)
|
||||
tenant_id = self._get_tenant_id_for_create(context, router['router'])
|
||||
|
||||
provider = get_provider_with_default(
|
||||
router['router'].get(ext_provider.ROUTER_PROVIDER))
|
||||
driver = get_driver_by_provider(provider)
|
||||
|
||||
with context.session.begin(subtransactions=True):
|
||||
new_router = super(RouterMixin, self).create_router(context,
|
||||
router)
|
||||
new_router['gw_port'] = self._get_gw_port_detail(
|
||||
context, driver, new_router['gw_port_id'])
|
||||
rdb.add_router_provider_binding(context.session,
|
||||
provider, str(new_router['id']))
|
||||
self._extend_router_dict_provider(new_router, provider)
|
||||
|
||||
# create router on the network controller
|
||||
try:
|
||||
return driver.create_router(context, tenant_id, new_router)
|
||||
except nexc.RouterOverLimit:
|
||||
super(RouterMixin, self).delete_router(context, new_router['id'])
|
||||
raise
|
||||
|
||||
def update_router(self, context, router_id, router):
|
||||
LOG.debug(_("RouterMixin.update_router() called, "
|
||||
"id=%(id)s, router=%(router)s ."),
|
||||
{'id': router_id, 'router': router})
|
||||
|
||||
with context.session.begin(subtransactions=True):
|
||||
old_rtr = super(RouterMixin, self).get_router(context, router_id)
|
||||
provider = old_rtr[ext_provider.ROUTER_PROVIDER]
|
||||
driver = get_driver_by_provider(provider)
|
||||
old_rtr['gw_port'] = self._get_gw_port_detail(
|
||||
context, driver, old_rtr['gw_port_id'])
|
||||
new_rtr = super(RouterMixin, self).update_router(
|
||||
context, router_id, router)
|
||||
new_rtr['gw_port'] = self._get_gw_port_detail(
|
||||
context, driver, new_rtr['gw_port_id'])
|
||||
driver.update_router(context, router_id, old_rtr, new_rtr)
|
||||
return new_rtr
|
||||
|
||||
def delete_router(self, context, router_id):
|
||||
LOG.debug(_("RouterMixin.delete_router() called, id=%s."), router_id)
|
||||
|
||||
router = super(RouterMixin, self).get_router(context, router_id)
|
||||
tenant_id = router['tenant_id']
|
||||
# Since l3_db.delete_router() has no interaction with the plugin layer,
|
||||
# we need to check if the router can be deleted first.
|
||||
self._check_router_in_use(context, router_id)
|
||||
driver = self._get_router_driver_by_id(context, router_id)
|
||||
# If gw_port exists, remove it.
|
||||
gw_port = self._get_gw_port(context, router_id)
|
||||
if gw_port:
|
||||
driver.delete_interface(context, router_id, gw_port)
|
||||
driver.delete_router(context, router_id, router)
|
||||
|
||||
super(RouterMixin, self).delete_router(context, router_id)
|
||||
|
||||
self._cleanup_ofc_tenant(context, tenant_id)
|
||||
|
||||
def add_router_interface(self, context, router_id, interface_info):
|
||||
LOG.debug(_("RouterMixin.add_router_interface() called, "
|
||||
"id=%(id)s, interface=%(interface)s."),
|
||||
{'id': router_id, 'interface': interface_info})
|
||||
return super(RouterMixin, self).add_router_interface(
|
||||
context, router_id, interface_info)
|
||||
|
||||
def remove_router_interface(self, context, router_id, interface_info):
|
||||
LOG.debug(_("RouterMixin.remove_router_interface() called, "
|
||||
"id=%(id)s, interface=%(interface)s."),
|
||||
{'id': router_id, 'interface': interface_info})
|
||||
return super(RouterMixin, self).remove_router_interface(
|
||||
context, router_id, interface_info)
|
||||
|
||||
def create_router_port(self, context, port):
|
||||
# This method is called from plugin.create_port()
|
||||
router_id = port['device_id']
|
||||
driver = self._get_router_driver_by_id(context, router_id)
|
||||
port = driver.add_interface(context, router_id, port)
|
||||
return port
|
||||
|
||||
def delete_router_port(self, context, port):
|
||||
# This method is called from plugin.delete_port()
|
||||
router_id = port['device_id']
|
||||
driver = self._get_router_driver_by_id(context, router_id)
|
||||
return driver.delete_interface(context, router_id, port)
|
||||
|
||||
def _get_gw_port_detail(self, context, driver, gw_port_id):
|
||||
if not gw_port_id or not driver.need_gw_info:
|
||||
return
|
||||
ctx_elevated = context.elevated()
|
||||
gw_port = self._get_port(ctx_elevated, gw_port_id)
|
||||
# At this moment gw_port has been created, so it is guaranteed
|
||||
# that fixed_ip is assigned for the gw_port.
|
||||
ext_subnet_id = gw_port['fixed_ips'][0]['subnet_id']
|
||||
ext_subnet = self._get_subnet(ctx_elevated, ext_subnet_id)
|
||||
gw_info = {'network_id': gw_port['network_id'],
|
||||
'ip_address': gw_port['fixed_ips'][0]['ip_address'],
|
||||
'mac_address': gw_port['mac_address'],
|
||||
'cidr': ext_subnet['cidr'],
|
||||
'gateway_ip': ext_subnet['gateway_ip']}
|
||||
return gw_info
|
||||
|
||||
def _get_gw_port(self, context, router_id):
|
||||
device_filter = {'device_id': [router_id],
|
||||
'device_owner': [l3_db.DEVICE_OWNER_ROUTER_GW]}
|
||||
ports = self.get_ports(context.elevated(), filters=device_filter)
|
||||
if ports:
|
||||
return ports[0]
|
||||
|
||||
def _check_router_in_use(self, context, router_id):
|
||||
with context.session.begin(subtransactions=True):
|
||||
# Ensure that the router is not used
|
||||
router_filter = {'router_id': [router_id]}
|
||||
fips = self.get_floatingips_count(context.elevated(),
|
||||
filters=router_filter)
|
||||
if fips:
|
||||
raise l3.RouterInUse(router_id=router_id)
|
||||
|
||||
device_filter = {'device_id': [router_id],
|
||||
'device_owner': [l3_db.DEVICE_OWNER_ROUTER_INTF]}
|
||||
ports = self.get_ports_count(context.elevated(),
|
||||
filters=device_filter)
|
||||
if ports:
|
||||
raise l3.RouterInUse(router_id=router_id)
|
||||
|
||||
def _get_router_for_floatingip(self, context, internal_port,
|
||||
internal_subnet_id,
|
||||
external_network_id):
|
||||
"""Get a router for a requested floating IP.
|
||||
|
||||
OpenFlow vrouter does not support NAT, so we need to exclude them
|
||||
from candidate routers for floating IP association.
|
||||
This method is called in l3_db.get_assoc_data().
|
||||
"""
|
||||
subnet_db = self._get_subnet(context, internal_subnet_id)
|
||||
if not subnet_db['gateway_ip']:
|
||||
msg = (_('Cannot add floating IP to port on subnet %s '
|
||||
'which has no gateway_ip') % internal_subnet_id)
|
||||
raise q_exc.BadRequest(resource='floatingip', msg=msg)
|
||||
|
||||
# find router interface ports on this network
|
||||
router_intf_qry = context.session.query(models_v2.Port)
|
||||
router_intf_ports = router_intf_qry.filter_by(
|
||||
network_id=internal_port['network_id'],
|
||||
device_owner=l3_db.DEVICE_OWNER_ROUTER_INTF)
|
||||
|
||||
for intf_p in router_intf_ports:
|
||||
if intf_p['fixed_ips'][0]['subnet_id'] == internal_subnet_id:
|
||||
router_id = intf_p['device_id']
|
||||
router_gw_qry = context.session.query(models_v2.Port)
|
||||
has_gw_port = router_gw_qry.filter_by(
|
||||
network_id=external_network_id,
|
||||
device_id=router_id,
|
||||
device_owner=l3_db.DEVICE_OWNER_ROUTER_GW).count()
|
||||
driver = self._get_router_driver_by_id(context, router_id)
|
||||
if (has_gw_port and driver.floating_ip_support()):
|
||||
return router_id
|
||||
|
||||
raise l3.ExternalGatewayForFloatingIPNotFound(
|
||||
subnet_id=internal_subnet_id,
|
||||
external_network_id=external_network_id,
|
||||
port_id=internal_port['id'])
|
||||
|
||||
def _get_sync_routers(self, context, router_ids=None, active=None):
|
||||
"""Query routers and their gw ports for l3 agent.
|
||||
|
||||
The difference from the superclass in l3_db is that this method
|
||||
only lists routers hosted on l3-agents.
|
||||
"""
|
||||
router_list = super(RouterMixin, self)._get_sync_routers(
|
||||
context, router_ids, active)
|
||||
if router_list:
|
||||
_router_ids = [r['id'] for r in router_list]
|
||||
agent_routers = rdb.get_routers_by_provider(
|
||||
context.session, 'l3-agent',
|
||||
router_ids=_router_ids)
|
||||
router_list = [r for r in router_list
|
||||
if r['id'] in agent_routers]
|
||||
return router_list
|
||||
|
||||
def _get_router_driver_by_id(self, context, router_id):
|
||||
provider = self._get_provider_by_router_id(context, router_id)
|
||||
return get_driver_by_provider(provider)
|
||||
|
||||
def _get_provider_by_router_id(self, context, router_id):
|
||||
return rdb.get_provider_by_router(context.session, router_id)
|
||||
|
||||
def _extend_router_dict_provider(self, router_res, provider):
|
||||
router_res[ext_provider.ROUTER_PROVIDER] = provider
|
||||
|
||||
def extend_router_dict_provider(self, router_res, router_db):
|
||||
# NOTE: router_db.provider is None just after creating a router,
|
||||
# so we need to skip setting router_provider here.
|
||||
if not router_db.provider:
|
||||
return
|
||||
self._extend_router_dict_provider(router_res,
|
||||
router_db.provider['provider'])
|
||||
|
||||
db_base_plugin_v2.NeutronDbPluginV2.register_dict_extend_funcs(
|
||||
l3.ROUTERS, [extend_router_dict_provider])
|
||||
|
||||
|
||||
class L3AgentSchedulerDbMixin(agentschedulers_db.L3AgentSchedulerDbMixin):
|
||||
|
||||
def auto_schedule_routers(self, context, host, router_ids):
|
||||
router_ids = rdb.get_routers_by_provider(
|
||||
context.session, nconst.ROUTER_PROVIDER_L3AGENT, router_ids)
|
||||
# If no l3-agent hosted router, there is no need to schedule.
|
||||
if not router_ids:
|
||||
return
|
||||
return super(L3AgentSchedulerDbMixin, self).auto_schedule_routers(
|
||||
context, host, router_ids)
|
||||
|
||||
def schedule_router(self, context, router):
|
||||
if (self._get_provider_by_router_id(context, router) ==
|
||||
nconst.ROUTER_PROVIDER_L3AGENT):
|
||||
return super(L3AgentSchedulerDbMixin, self).schedule_router(
|
||||
context, router)
|
||||
|
||||
def add_router_to_l3_agent(self, context, id, router_id):
|
||||
provider = self._get_provider_by_router_id(context, router_id)
|
||||
if provider != nconst.ROUTER_PROVIDER_L3AGENT:
|
||||
raise nexc.RouterProviderMismatch(
|
||||
router_id=router_id, provider=provider,
|
||||
expected_provider=nconst.ROUTER_PROVIDER_L3AGENT)
|
||||
return super(L3AgentSchedulerDbMixin, self).add_router_to_l3_agent(
|
||||
context, id, router_id)
|
||||
|
||||
|
||||
class L3AgentNotifyAPI(l3_rpc_agent_api.L3AgentNotifyAPI):
|
||||
|
||||
def _notification(self, context, method, router_ids, operation, data):
|
||||
"""Notify all the agents that are hosting the routers.
|
||||
|
||||
_notification() is called in L3 db plugin for all routers regardless
|
||||
the routers are hosted on l3 agents or not. When the routers are
|
||||
not hosted on l3 agents, there is no need to notify.
|
||||
This method filters routers not hosted by l3 agents.
|
||||
"""
|
||||
router_ids = rdb.get_routers_by_provider(
|
||||
context.session, nconst.ROUTER_PROVIDER_L3AGENT, router_ids)
|
||||
super(L3AgentNotifyAPI, self)._notification(
|
||||
context, method, router_ids, operation, data)
|
||||
|
||||
|
||||
def load_driver(plugin, ofc_manager):
|
||||
|
||||
if (PROVIDER_OPENFLOW in ROUTER_DRIVER_MAP and
|
||||
not ofc_manager.driver.router_supported):
|
||||
LOG.warning(
|
||||
_('OFC does not support router with provider=%(provider)s, '
|
||||
'so removed it from supported provider '
|
||||
'(new router driver map=%(driver_map)s)'),
|
||||
{'provider': PROVIDER_OPENFLOW,
|
||||
'driver_map': ROUTER_DRIVER_MAP})
|
||||
del ROUTER_DRIVER_MAP[PROVIDER_OPENFLOW]
|
||||
|
||||
if config.PROVIDER.default_router_provider not in ROUTER_DRIVER_MAP:
|
||||
LOG.error(_('default_router_provider %(default)s is supported! '
|
||||
'Please specify one of %(supported)s'),
|
||||
{'default': config.PROVIDER.default_router_provider,
|
||||
'supported': ROUTER_DRIVER_MAP.keys()})
|
||||
raise SystemExit(1)
|
||||
|
||||
enabled_providers = (set(config.PROVIDER.router_providers +
|
||||
[config.PROVIDER.default_router_provider]) &
|
||||
set(ROUTER_DRIVER_MAP.keys()))
|
||||
|
||||
for driver in enabled_providers:
|
||||
driver_klass = importutils.import_class(ROUTER_DRIVER_MAP[driver])
|
||||
ROUTER_DRIVERS[driver] = driver_klass(plugin, ofc_manager)
|
||||
|
||||
LOG.info(_('Enabled router drivers: %s'), ROUTER_DRIVERS.keys())
|
||||
|
||||
if not ROUTER_DRIVERS:
|
||||
LOG.error(_('No router provider is enabled. neutron-server terminated!'
|
||||
' (supported=%(supported)s, configured=%(config)s)'),
|
||||
{'supported': ROUTER_DRIVER_MAP.keys(),
|
||||
'config': config.PROVIDER.router_providers})
|
||||
raise SystemExit(1)
|
||||
|
||||
|
||||
def get_provider_with_default(provider):
|
||||
if not attr.is_attr_set(provider):
|
||||
provider = config.PROVIDER.default_router_provider
|
||||
elif provider not in ROUTER_DRIVERS:
|
||||
raise nexc.ProviderNotFound(provider=provider)
|
||||
return provider
|
||||
|
||||
|
||||
def get_driver_by_provider(provider):
|
||||
if provider is None:
|
||||
provider = config.PROVIDER.default_router_provider
|
||||
elif provider not in ROUTER_DRIVERS:
|
||||
raise nexc.ProviderNotFound(provider=provider)
|
||||
return ROUTER_DRIVERS[provider]
|
@ -16,12 +16,19 @@
|
||||
# @author: Ryota MIBU
|
||||
# @author: Akihiro MOTOKI
|
||||
|
||||
import netaddr
|
||||
|
||||
from neutron.common import utils
|
||||
from neutron.openstack.common import log as logging
|
||||
from neutron.plugins.nec.common import config
|
||||
from neutron.plugins.nec.common import exceptions as nexc
|
||||
from neutron.plugins.nec.db import api as ndb
|
||||
from neutron.plugins.nec import drivers
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class OFCManager(object):
|
||||
"""This class manages an OpenFlow Controller and map resources.
|
||||
|
||||
@ -50,6 +57,10 @@ class OFCManager(object):
|
||||
def _del_ofc_item(self, context, resource, neutron_id):
|
||||
ndb.del_ofc_item_lookup_both(context.session, resource, neutron_id)
|
||||
|
||||
def ensure_ofc_tenant(self, context, tenant_id):
|
||||
if not self.exists_ofc_tenant(context, tenant_id):
|
||||
self.create_ofc_tenant(context, tenant_id)
|
||||
|
||||
def create_ofc_tenant(self, context, tenant_id):
|
||||
desc = "ID=%s at OpenStack." % tenant_id
|
||||
ofc_tenant_id = self.driver.create_tenant(desc, tenant_id)
|
||||
@ -134,3 +145,60 @@ class OFCManager(object):
|
||||
|
||||
self.driver.delete_filter(ofc_pf_id)
|
||||
self._del_ofc_item(context, "ofc_packet_filter", filter_id)
|
||||
|
||||
def create_ofc_router(self, context, tenant_id, router_id, name=None):
|
||||
ofc_tenant_id = self._get_ofc_id(context, "ofc_tenant", tenant_id)
|
||||
ofc_tenant_id = self.driver.convert_ofc_tenant_id(
|
||||
context, ofc_tenant_id)
|
||||
|
||||
desc = "ID=%s Name=%s at Neutron." % (router_id, name)
|
||||
ofc_router_id = self.driver.create_router(ofc_tenant_id, router_id,
|
||||
desc)
|
||||
self._add_ofc_item(context, "ofc_router", router_id, ofc_router_id)
|
||||
|
||||
def exists_ofc_router(self, context, router_id):
|
||||
return self._exists_ofc_item(context, "ofc_router", router_id)
|
||||
|
||||
def delete_ofc_router(self, context, router_id, router):
|
||||
ofc_router_id = self._get_ofc_id(context, "ofc_router", router_id)
|
||||
self.driver.delete_router(ofc_router_id)
|
||||
self._del_ofc_item(context, "ofc_router", router_id)
|
||||
|
||||
def add_ofc_router_interface(self, context, router_id, port_id, port):
|
||||
# port must have the following fields:
|
||||
# network_id, cidr, ip_address, mac_address
|
||||
ofc_router_id = self._get_ofc_id(context, "ofc_router", router_id)
|
||||
ofc_net_id = self._get_ofc_id(context, "ofc_network",
|
||||
port['network_id'])
|
||||
ip_address = '%s/%s' % (port['ip_address'],
|
||||
netaddr.IPNetwork(port['cidr']).prefixlen)
|
||||
mac_address = port['mac_address']
|
||||
ofc_inf_id = self.driver.add_router_interface(
|
||||
ofc_router_id, ofc_net_id, ip_address, mac_address)
|
||||
# Use port mapping table to maintain an interface of OFC router
|
||||
self._add_ofc_item(context, "ofc_port", port_id, ofc_inf_id)
|
||||
|
||||
def delete_ofc_router_interface(self, context, router_id, port_id):
|
||||
# Use port mapping table to maintain an interface of OFC router
|
||||
ofc_inf_id = self._get_ofc_id(context, "ofc_port", port_id)
|
||||
self.driver.delete_router_interface(ofc_inf_id)
|
||||
self._del_ofc_item(context, "ofc_port", port_id)
|
||||
|
||||
def update_ofc_router_route(self, context, router_id, new_routes):
|
||||
ofc_router_id = self._get_ofc_id(context, "ofc_router", router_id)
|
||||
ofc_routes = self.driver.list_router_routes(ofc_router_id)
|
||||
route_dict = {}
|
||||
cur_routes = []
|
||||
for r in ofc_routes:
|
||||
key = ','.join((r['destination'], r['nexthop']))
|
||||
route_dict[key] = r['id']
|
||||
del r['id']
|
||||
cur_routes.append(r)
|
||||
added, removed = utils.diff_list_of_dict(cur_routes, new_routes)
|
||||
for r in removed:
|
||||
key = ','.join((r['destination'], r['nexthop']))
|
||||
route_id = route_dict[key]
|
||||
self.driver.delete_router_route(route_id)
|
||||
for r in added:
|
||||
self.driver.add_router_route(ofc_router_id, r['destination'],
|
||||
r['nexthop'])
|
||||
|
221
neutron/plugins/nec/router_drivers.py
Normal file
221
neutron/plugins/nec/router_drivers.py
Normal file
@ -0,0 +1,221 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
#
|
||||
# Copyright 2013 NEC Corporation. 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: Akihiro Motoki
|
||||
|
||||
import abc
|
||||
import httplib
|
||||
|
||||
from neutron.common import log as call_log
|
||||
from neutron.common import utils
|
||||
from neutron.openstack.common import excutils
|
||||
from neutron.openstack.common import log as logging
|
||||
from neutron.plugins.nec.common import constants as nconst
|
||||
from neutron.plugins.nec.common import exceptions as nexc
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
PROVIDER_OPENFLOW = nconst.ROUTER_PROVIDER_OPENFLOW
|
||||
|
||||
|
||||
class RouterDriverBase(object):
|
||||
|
||||
__metaclass__ = abc.ABCMeta
|
||||
|
||||
def __init__(self, plugin, ofc_manager):
|
||||
self.plugin = plugin
|
||||
self.ofc = ofc_manager
|
||||
|
||||
def floating_ip_support(self):
|
||||
return True
|
||||
|
||||
@abc.abstractmethod
|
||||
def create_router(self, context, tenant_id, router):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def update_router(self, context, router_id, old_router, new_router):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def delete_router(self, context, router_id, router):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def add_interface(self, context, router_id, port):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def delete_interface(self, context, router_id, port):
|
||||
pass
|
||||
|
||||
|
||||
class RouterL3AgentDriver(RouterDriverBase):
|
||||
|
||||
need_gw_info = False
|
||||
|
||||
@call_log.log
|
||||
def create_router(self, context, tenant_id, router):
|
||||
return router
|
||||
|
||||
@call_log.log
|
||||
def update_router(self, context, router_id, old_router, new_router):
|
||||
return new_router
|
||||
|
||||
@call_log.log
|
||||
def delete_router(self, context, router_id, router):
|
||||
pass
|
||||
|
||||
@call_log.log
|
||||
def add_interface(self, context, router_id, port):
|
||||
return self.plugin.activate_port_if_ready(context, port)
|
||||
|
||||
@call_log.log
|
||||
def delete_interface(self, context, router_id, port):
|
||||
return self.plugin.deactivate_port(context, port)
|
||||
|
||||
|
||||
class RouterOpenFlowDriver(RouterDriverBase):
|
||||
|
||||
need_gw_info = True
|
||||
|
||||
def floating_ip_support(self):
|
||||
return self.ofc.driver.router_nat_supported
|
||||
|
||||
def _process_gw_port(self, gw_info, routes):
|
||||
if gw_info and gw_info['gateway_ip']:
|
||||
routes.append({'destination': '0.0.0.0/0',
|
||||
'nexthop': gw_info['gateway_ip']})
|
||||
|
||||
@call_log.log
|
||||
def create_router(self, context, tenant_id, router):
|
||||
try:
|
||||
router_id = router['id']
|
||||
added_routes = []
|
||||
self.ofc.ensure_ofc_tenant(context, tenant_id)
|
||||
self.ofc.create_ofc_router(context, tenant_id, router_id,
|
||||
router['name'])
|
||||
self._process_gw_port(router['gw_port'], added_routes)
|
||||
if added_routes:
|
||||
self.ofc.update_ofc_router_route(context, router_id,
|
||||
added_routes, [])
|
||||
new_status = nconst.ROUTER_STATUS_ACTIVE
|
||||
self.plugin._update_resource_status(context, "router",
|
||||
router['id'],
|
||||
new_status)
|
||||
router['status'] = new_status
|
||||
return router
|
||||
except (nexc.OFCException, nexc.OFCConsistencyBroken) as exc:
|
||||
with excutils.save_and_reraise_exception():
|
||||
if (isinstance(exc, nexc.OFCException) and
|
||||
exc.status == httplib.CONFLICT):
|
||||
raise nexc.RouterOverLimit(provider=PROVIDER_OPENFLOW)
|
||||
reason = _("create_router() failed due to %s") % exc
|
||||
LOG.error(reason)
|
||||
new_status = nconst.ROUTER_STATUS_ERROR
|
||||
self._update_resource_status(context, "router",
|
||||
router['id'],
|
||||
new_status)
|
||||
|
||||
@call_log.log
|
||||
def update_router(self, context, router_id, old_router, new_router):
|
||||
old_routes = old_router['routes'][:]
|
||||
new_routes = new_router['routes'][:]
|
||||
self._process_gw_port(old_router['gw_port'], old_routes)
|
||||
self._process_gw_port(new_router['gw_port'], new_routes)
|
||||
added, removed = utils.diff_list_of_dict(old_routes, new_routes)
|
||||
if added or removed:
|
||||
try:
|
||||
# NOTE(amotoki): PFC supports one-by-one route update at now.
|
||||
# It means there may be a case where some route is updated but
|
||||
# some not. To allow the next call of failures to sync routes
|
||||
# with Neutron side, we pass the whole new routes here.
|
||||
# PFC should support atomic route update in the future.
|
||||
self.ofc.update_ofc_router_route(context, router_id,
|
||||
new_routes)
|
||||
new_status = nconst.ROUTER_STATUS_ACTIVE
|
||||
self.plugin._update_resource_status(
|
||||
context, "router", router_id, new_status)
|
||||
new_router['status'] = new_status
|
||||
except (nexc.OFCException, nexc.OFCConsistencyBroken) as exc:
|
||||
with excutils.save_and_reraise_exception():
|
||||
reason = _("_update_ofc_routes() failed due to %s") % exc
|
||||
LOG.error(reason)
|
||||
new_status = nconst.ROUTER_STATUS_ERROR
|
||||
self.plugin._update_resource_status(
|
||||
context, "router", router_id, new_status)
|
||||
return new_router
|
||||
|
||||
@call_log.log
|
||||
def delete_router(self, context, router_id, router):
|
||||
try:
|
||||
self.ofc.delete_ofc_router(context, router_id, router)
|
||||
except (nexc.OFCException, nexc.OFCConsistencyBroken) as exc:
|
||||
with excutils.save_and_reraise_exception():
|
||||
LOG.error(_("delete_router() failed due to %s"), exc)
|
||||
self.plugin._update_resource_status(
|
||||
context, "router", router_id, nconst.ROUTER_STATUS_ERROR)
|
||||
|
||||
@call_log.log
|
||||
def add_interface(self, context, router_id, port):
|
||||
port_id = port['id']
|
||||
# port['fixed_ips'] may be empty if ext_net has no subnet.
|
||||
# Such port is invalid for a router port and we don't create a port
|
||||
# on OFC. The port is removed in l3_db._create_router_gw_port.
|
||||
if not port['fixed_ips']:
|
||||
msg = _('RouterOpenFlowDriver.add_interface(): the requested port '
|
||||
'has no subnet. add_interface() is skipped. '
|
||||
'router_id=%(id)s, port=%(port)s)')
|
||||
LOG.warning(msg, {'id': router_id, 'port': port})
|
||||
return port
|
||||
fixed_ip = port['fixed_ips'][0]
|
||||
subnet = self.plugin._get_subnet(context, fixed_ip['subnet_id'])
|
||||
port_info = {'network_id': port['network_id'],
|
||||
'ip_address': fixed_ip['ip_address'],
|
||||
'cidr': subnet['cidr'],
|
||||
'mac_address': port['mac_address']}
|
||||
try:
|
||||
self.ofc.add_ofc_router_interface(context, router_id,
|
||||
port_id, port_info)
|
||||
new_status = nconst.ROUTER_STATUS_ACTIVE
|
||||
self.plugin._update_resource_status(
|
||||
context, "port", port_id, new_status)
|
||||
return port
|
||||
except (nexc.OFCException, nexc.OFCConsistencyBroken) as exc:
|
||||
with excutils.save_and_reraise_exception():
|
||||
reason = _("add_router_interface() failed due to %s") % exc
|
||||
LOG.error(reason)
|
||||
new_status = nconst.ROUTER_STATUS_ERROR
|
||||
self.plugin._update_resource_status(
|
||||
context, "port", port_id, new_status)
|
||||
|
||||
@call_log.log
|
||||
def delete_interface(self, context, router_id, port):
|
||||
port_id = port['id']
|
||||
try:
|
||||
self.ofc.delete_ofc_router_interface(context, router_id, port_id)
|
||||
new_status = nconst.ROUTER_STATUS_ACTIVE
|
||||
self.plugin._update_resource_status(context, "port", port_id,
|
||||
new_status)
|
||||
port['status'] = new_status
|
||||
return port
|
||||
except (nexc.OFCException, nexc.OFCConsistencyBroken) as exc:
|
||||
with excutils.save_and_reraise_exception():
|
||||
reason = _("delete_router_interface() failed due to %s") % exc
|
||||
LOG.error(reason)
|
||||
new_status = nconst.ROUTER_STATUS_ERROR
|
||||
self.plugin._update_resource_status(context, "port", port_id,
|
||||
new_status)
|
@ -15,34 +15,129 @@
|
||||
# under the License.
|
||||
# @author: Ryota MIBU
|
||||
|
||||
import netaddr
|
||||
|
||||
from neutron.common import log as call_log
|
||||
from neutron.openstack.common import log as logging
|
||||
from neutron.openstack.common import uuidutils
|
||||
from neutron.plugins.nec.common import exceptions as nexc
|
||||
from neutron.plugins.nec import ofc_driver_base
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
MAX_NUM_OPENFLOW_ROUTER = 2
|
||||
|
||||
|
||||
class StubOFCDriver(ofc_driver_base.OFCDriverBase):
|
||||
"""Stub OFC driver for testing.
|
||||
|
||||
This driver can be used not only for unit tests but also for real testing
|
||||
as a logging driver. It stores the created resources on OFC and returns
|
||||
them in get methods().
|
||||
|
||||
If autocheck is enabled, it checks whether the specified resource exists
|
||||
in OFC and raises an exception if it is different from expected status.
|
||||
"""
|
||||
|
||||
def __init__(self, conf):
|
||||
pass
|
||||
self.autocheck = False
|
||||
self.reset_all()
|
||||
|
||||
def reset_all(self):
|
||||
self.ofc_tenant_dict = {}
|
||||
self.ofc_network_dict = {}
|
||||
self.ofc_port_dict = {}
|
||||
self.ofc_filter_dict = {}
|
||||
self.ofc_router_dict = {}
|
||||
self.ofc_router_inf_dict = {}
|
||||
self.ofc_router_route_dict = {}
|
||||
|
||||
def enable_autocheck(self):
|
||||
self.autocheck = True
|
||||
|
||||
def disable_autocheck(self):
|
||||
self.autocheck = False
|
||||
|
||||
@call_log.log
|
||||
def create_tenant(self, description, tenant_id=None):
|
||||
return "ofc-" + tenant_id[:-4]
|
||||
ofc_id = "ofc-" + tenant_id[:-4]
|
||||
if self.autocheck:
|
||||
if ofc_id in self.ofc_tenant_dict:
|
||||
raise Exception(_('(create_tenant) OFC tenant %s '
|
||||
'already exists') % ofc_id)
|
||||
self.ofc_tenant_dict[ofc_id] = {'tenant_id': tenant_id,
|
||||
'description': description}
|
||||
return ofc_id
|
||||
|
||||
@call_log.log
|
||||
def delete_tenant(self, ofc_tenant_id):
|
||||
pass
|
||||
if ofc_tenant_id in self.ofc_tenant_dict:
|
||||
del self.ofc_tenant_dict[ofc_tenant_id]
|
||||
else:
|
||||
if self.autocheck:
|
||||
raise Exception(_('(delete_tenant) OFC tenant %s not found')
|
||||
% ofc_tenant_id)
|
||||
LOG.debug(_('delete_tenant: SUCCEED'))
|
||||
|
||||
@call_log.log
|
||||
def create_network(self, ofc_tenant_id, description, network_id=None):
|
||||
return "ofc-" + network_id[:-4]
|
||||
ofc_id = "ofc-" + network_id[:-4]
|
||||
if self.autocheck:
|
||||
if ofc_tenant_id not in self.ofc_tenant_dict:
|
||||
raise Exception(_('(create_network) OFC tenant %s not found')
|
||||
% ofc_tenant_id)
|
||||
if ofc_id in self.ofc_network_dict:
|
||||
raise Exception(_('(create_network) OFC network %s '
|
||||
'already exists') % ofc_id)
|
||||
self.ofc_network_dict[ofc_id] = {'tenant_id': ofc_tenant_id,
|
||||
'network_id': network_id,
|
||||
'description': description}
|
||||
return ofc_id
|
||||
|
||||
@call_log.log
|
||||
def update_network(self, ofc_network_id, description):
|
||||
pass
|
||||
if self.autocheck:
|
||||
if ofc_network_id not in self.ofc_network_dict:
|
||||
raise Exception(_('(update_network) OFC network %s not found')
|
||||
% ofc_network_id)
|
||||
data = {'description': description}
|
||||
self.ofc_network_dict[ofc_network_id].update(data)
|
||||
LOG.debug(_('update_network: SUCCEED'))
|
||||
|
||||
@call_log.log
|
||||
def delete_network(self, ofc_network_id):
|
||||
pass
|
||||
if ofc_network_id in self.ofc_network_dict:
|
||||
del self.ofc_network_dict[ofc_network_id]
|
||||
else:
|
||||
if self.autocheck:
|
||||
raise Exception(_('(delete_network) OFC network %s not found')
|
||||
% ofc_network_id)
|
||||
LOG.debug(_('delete_network: SUCCEED'))
|
||||
|
||||
@call_log.log
|
||||
def create_port(self, ofc_network_id, info, port_id=None):
|
||||
return "ofc-" + port_id[:-4]
|
||||
ofc_id = "ofc-" + port_id[:-4]
|
||||
if self.autocheck:
|
||||
if ofc_network_id not in self.ofc_network_dict:
|
||||
raise Exception(_('(create_port) OFC network %s not found')
|
||||
% ofc_network_id)
|
||||
if ofc_id in self.ofc_port_dict:
|
||||
raise Exception(_('(create_port) OFC port %s already exists')
|
||||
% ofc_id)
|
||||
self.ofc_port_dict[ofc_id] = {'network_id': ofc_network_id,
|
||||
'port_id': port_id}
|
||||
return ofc_id
|
||||
|
||||
@call_log.log
|
||||
def delete_port(self, ofc_port_id):
|
||||
pass
|
||||
if ofc_port_id in self.ofc_port_dict:
|
||||
del self.ofc_port_dict[ofc_port_id]
|
||||
else:
|
||||
if self.autocheck:
|
||||
raise Exception(_('(delete_port) OFC port %s not found')
|
||||
% ofc_port_id)
|
||||
LOG.debug(_('delete_port: SUCCEED'))
|
||||
|
||||
@classmethod
|
||||
def filter_supported(cls):
|
||||
@ -66,3 +161,131 @@ class StubOFCDriver(ofc_driver_base.OFCDriverBase):
|
||||
|
||||
def convert_ofc_filter_id(self, context, ofc_filter_id):
|
||||
return ofc_filter_id
|
||||
|
||||
router_supported = True
|
||||
router_nat_supported = True
|
||||
|
||||
@call_log.log
|
||||
def create_router(self, ofc_tenant_id, router_id, description):
|
||||
ofc_id = "ofc-" + router_id[:-4]
|
||||
if self.autocheck:
|
||||
if ofc_tenant_id not in self.ofc_tenant_dict:
|
||||
raise Exception(_('(create_router) OFC tenant %s not found')
|
||||
% ofc_tenant_id)
|
||||
if ofc_id in self.ofc_router_dict:
|
||||
raise Exception(_('(create_router) OFC router %s '
|
||||
'already exists') % ofc_id)
|
||||
if len(self.ofc_router_dict) >= MAX_NUM_OPENFLOW_ROUTER:
|
||||
params = {'reason': _("Operation on OFC is failed"),
|
||||
'status': 409}
|
||||
raise nexc.OFCException(**params)
|
||||
self.ofc_router_dict[ofc_id] = {'tenant_id': ofc_tenant_id,
|
||||
'router_id': router_id,
|
||||
'description': description}
|
||||
return ofc_id
|
||||
|
||||
@call_log.log
|
||||
def delete_router(self, ofc_router_id):
|
||||
if ofc_router_id in self.ofc_router_dict:
|
||||
del self.ofc_router_dict[ofc_router_id]
|
||||
else:
|
||||
if self.autocheck:
|
||||
raise Exception(_('(delete_router) OFC router %s not found')
|
||||
% ofc_router_id)
|
||||
LOG.debug(_('delete_router: SUCCEED'))
|
||||
|
||||
@call_log.log
|
||||
def add_router_interface(self, ofc_router_id, ofc_net_id,
|
||||
ip_address=None, mac_address=None):
|
||||
if_id = "ofc-" + uuidutils.generate_uuid()[:-4]
|
||||
# IP address should have a format of a.b.c.d/N
|
||||
if ip_address != str(netaddr.IPNetwork(ip_address)):
|
||||
raise Exception(_('(add_router_interface) '
|
||||
'ip_address %s is not a valid format (a.b.c.d/N).')
|
||||
% ip_address)
|
||||
if self.autocheck:
|
||||
if ofc_router_id not in self.ofc_router_dict:
|
||||
raise Exception(_('(add_router_interface) '
|
||||
'OFC router %s not found') % ofc_router_id)
|
||||
if ofc_net_id not in self.ofc_network_dict:
|
||||
raise Exception(_('(add_router_interface) '
|
||||
'OFC network %s not found') % ofc_net_id)
|
||||
# Check duplicate destination
|
||||
self.ofc_router_inf_dict[if_id] = {'router_id': ofc_router_id,
|
||||
'network_id': ofc_net_id,
|
||||
'ip_address': ip_address,
|
||||
'mac_address': mac_address}
|
||||
LOG.debug(_('add_router_interface: SUCCEED (if_id=%s)'), if_id)
|
||||
return if_id
|
||||
|
||||
@call_log.log
|
||||
def update_router_interface(self, ofc_router_inf_id,
|
||||
ip_address=None, mac_address=None):
|
||||
if ofc_router_inf_id not in self.ofc_router_inf_dict:
|
||||
if self.autocheck:
|
||||
raise Exception(_('(delete_router_interface) '
|
||||
'OFC router interface %s not found')
|
||||
% ofc_router_inf_id)
|
||||
self.ofc_router_inf_dict[ofc_router_inf_id] = {}
|
||||
inf = self.ofc_router_inf_dict[ofc_router_inf_id]
|
||||
if ip_address:
|
||||
inf.update({'ip_address': ip_address})
|
||||
if mac_address:
|
||||
inf.update({'mac_address': mac_address})
|
||||
LOG.debug(_('update_router_route: SUCCEED'))
|
||||
|
||||
@call_log.log
|
||||
def delete_router_interface(self, ofc_router_inf_id):
|
||||
if ofc_router_inf_id in self.ofc_router_inf_dict:
|
||||
del self.ofc_router_inf_dict[ofc_router_inf_id]
|
||||
else:
|
||||
if self.autocheck:
|
||||
raise Exception(_('(delete_router_interface) '
|
||||
'OFC router interface %s not found')
|
||||
% ofc_router_inf_id)
|
||||
LOG.debug(_('delete_router_interface: SUCCEED'))
|
||||
|
||||
@call_log.log
|
||||
def add_router_route(self, ofc_router_id, destination, nexthop):
|
||||
route_id = "ofc-" + uuidutils.generate_uuid()[:-4]
|
||||
# IP address format check
|
||||
netaddr.IPNetwork(destination)
|
||||
netaddr.IPAddress(nexthop)
|
||||
if self.autocheck:
|
||||
if ofc_router_id not in self.ofc_router_dict:
|
||||
raise Exception(_('(add_router_route) OFC router %s not found')
|
||||
% ofc_router_id)
|
||||
# Check duplicate destination
|
||||
if destination in [route['destination'] for route in
|
||||
self.ofc_router_route_dict.values()]:
|
||||
raise Exception(_('(add_router_route) '
|
||||
'route to "%s" already exists') % destination)
|
||||
self.ofc_router_route_dict[route_id] = {'router_id': ofc_router_id,
|
||||
'destination': destination,
|
||||
'nexthop': nexthop}
|
||||
LOG.debug(_('add_router_route: SUCCEED (route_id=%s)'), route_id)
|
||||
return route_id
|
||||
|
||||
@call_log.log
|
||||
def delete_router_route(self, ofc_router_route_id):
|
||||
if ofc_router_route_id in self.ofc_router_route_dict:
|
||||
del self.ofc_router_route_dict[ofc_router_route_id]
|
||||
else:
|
||||
if self.autocheck:
|
||||
raise Exception(_('(delete_router_route) OFC router route %s '
|
||||
'not found') % ofc_router_route_id)
|
||||
LOG.debug(_('delete_router_route: SUCCEED'))
|
||||
|
||||
@call_log.log
|
||||
def list_router_routes(self, ofc_router_id):
|
||||
if self.autocheck:
|
||||
if ofc_router_id not in self.ofc_router_dict:
|
||||
raise Exception(_('(delete_router) OFC router %s not found')
|
||||
% ofc_router_id)
|
||||
routes = [{'id': k,
|
||||
'destination': v['destination'],
|
||||
'nexthop': v['nexthop']}
|
||||
for k, v in self.ofc_router_route_dict.items()
|
||||
if v['router_id'] == ofc_router_id]
|
||||
LOG.debug(_('list_router_routes: routes=%s'), routes)
|
||||
return routes
|
||||
|
@ -15,41 +15,104 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import mox
|
||||
import contextlib
|
||||
|
||||
from neutron.plugins.nec.common import ofc_client
|
||||
from neutron.common import constants
|
||||
from neutron.db import l3_rpc_base
|
||||
from neutron.tests.unit.nec import test_nec_plugin
|
||||
from neutron.tests.unit.openvswitch import test_agent_scheduler
|
||||
|
||||
L3_HOSTA = test_agent_scheduler.L3_HOSTA
|
||||
L3_HOSTB = test_agent_scheduler.L3_HOSTB
|
||||
|
||||
|
||||
class NecAgentSchedulerTestCase(
|
||||
test_agent_scheduler.OvsAgentSchedulerTestCase):
|
||||
test_agent_scheduler.OvsAgentSchedulerTestCase,
|
||||
test_nec_plugin.NecPluginV2TestCaseBase):
|
||||
|
||||
plugin_str = test_nec_plugin.PLUGIN_NAME
|
||||
|
||||
def setUp(self):
|
||||
self.setup_nec_plugin_base()
|
||||
super(NecAgentSchedulerTestCase, self).setUp()
|
||||
self.mox = mox.Mox()
|
||||
self.mox.StubOutWithMock(ofc_client.OFCClient, 'do_request')
|
||||
self.addCleanup(self.mox.UnsetStubs)
|
||||
|
||||
|
||||
class NecDhcpAgentNotifierTestCase(
|
||||
test_agent_scheduler.OvsDhcpAgentNotifierTestCase):
|
||||
test_agent_scheduler.OvsDhcpAgentNotifierTestCase,
|
||||
test_nec_plugin.NecPluginV2TestCaseBase):
|
||||
|
||||
plugin_str = test_nec_plugin.PLUGIN_NAME
|
||||
|
||||
def setUp(self):
|
||||
# OvsDhcpAgentNotifierTestCase uses stop() for each mock.
|
||||
self.setup_nec_plugin_base(use_stop_each=True)
|
||||
super(NecDhcpAgentNotifierTestCase, self).setUp()
|
||||
self.mox = mox.Mox()
|
||||
self.mox.StubOutWithMock(ofc_client.OFCClient, 'do_request')
|
||||
self.addCleanup(self.mox.UnsetStubs)
|
||||
|
||||
|
||||
class NecL3AgentNotifierTestCase(
|
||||
test_agent_scheduler.OvsL3AgentNotifierTestCase):
|
||||
test_agent_scheduler.OvsL3AgentNotifierTestCase,
|
||||
test_nec_plugin.NecPluginV2TestCaseBase):
|
||||
|
||||
plugin_str = test_nec_plugin.PLUGIN_NAME
|
||||
|
||||
def setUp(self):
|
||||
# OvsDhcpAgentNotifierTestCase uses stop() for each mock.
|
||||
self.setup_nec_plugin_base(use_stop_each=True)
|
||||
super(NecL3AgentNotifierTestCase, self).setUp()
|
||||
self.mox = mox.Mox()
|
||||
self.mox.StubOutWithMock(ofc_client.OFCClient, 'do_request')
|
||||
self.addCleanup(self.mox.UnsetStubs)
|
||||
|
||||
|
||||
class NecL3AgentSchedulerWithOpenFlowRouter(
|
||||
test_agent_scheduler.OvsAgentSchedulerTestCaseBase,
|
||||
test_nec_plugin.NecPluginV2TestCaseBase):
|
||||
|
||||
plugin_str = test_nec_plugin.PLUGIN_NAME
|
||||
|
||||
def setUp(self):
|
||||
self.setup_nec_plugin_base()
|
||||
super(NecL3AgentSchedulerWithOpenFlowRouter, self).setUp()
|
||||
|
||||
def test_router_auto_schedule_with_l3agent_and_openflow(self):
|
||||
with contextlib.nested(
|
||||
self.router(),
|
||||
self.router(arg_list=('provider',),
|
||||
provider='openflow'
|
||||
)) as (r1, r2):
|
||||
l3_rpc = l3_rpc_base.L3RpcCallbackMixin()
|
||||
self._register_agent_states()
|
||||
ret_a = l3_rpc.sync_routers(self.adminContext, host=L3_HOSTA)
|
||||
ret_b = l3_rpc.sync_routers(self.adminContext, host=L3_HOSTB)
|
||||
l3_agents = self._list_l3_agents_hosting_router(
|
||||
r1['router']['id'])
|
||||
self.assertEqual(1, len(ret_a))
|
||||
self.assertFalse(len(ret_b))
|
||||
self.assertIn(r1['router']['id'], [r['id'] for r in ret_a])
|
||||
self.assertNotIn(r2['router']['id'], [r['id'] for r in ret_a])
|
||||
self.assertEqual(1, len(l3_agents['agents']))
|
||||
self.assertEqual(L3_HOSTA, l3_agents['agents'][0]['host'])
|
||||
|
||||
def test_router_auto_schedule_only_with_openflow_router(self):
|
||||
with contextlib.nested(
|
||||
self.router(arg_list=('provider',), provider='openflow'),
|
||||
self.router(arg_list=('provider',), provider='openflow')
|
||||
) as (r1, r2):
|
||||
l3_rpc = l3_rpc_base.L3RpcCallbackMixin()
|
||||
self._register_agent_states()
|
||||
ret_a = l3_rpc.sync_routers(self.adminContext, host=L3_HOSTA)
|
||||
l3_agents_1 = self._list_l3_agents_hosting_router(
|
||||
r1['router']['id'])
|
||||
l3_agents_2 = self._list_l3_agents_hosting_router(
|
||||
r2['router']['id'])
|
||||
self.assertFalse(len(ret_a))
|
||||
self.assertNotIn(r1['router']['id'], [r['id'] for r in ret_a])
|
||||
self.assertNotIn(r2['router']['id'], [r['id'] for r in ret_a])
|
||||
self.assertFalse(len(l3_agents_1['agents']))
|
||||
self.assertFalse(len(l3_agents_2['agents']))
|
||||
|
||||
def test_add_router_to_l3_agent_for_openflow_router(self):
|
||||
with self.router(arg_list=('provider',), provider='openflow') as r1:
|
||||
self._register_agent_states()
|
||||
hosta_id = self._get_agent_id(constants.AGENT_TYPE_L3,
|
||||
L3_HOSTA)
|
||||
self._add_router_to_l3_agent(hosta_id,
|
||||
r1['router']['id'],
|
||||
expected_code=409)
|
||||
|
@ -33,6 +33,8 @@ from neutron.tests.unit import test_db_plugin as test_plugin
|
||||
|
||||
|
||||
PLUGIN_NAME = 'neutron.plugins.nec.nec_plugin.NECPluginV2'
|
||||
OFC_MANAGER = 'neutron.plugins.nec.nec_plugin.ofc_manager.OFCManager'
|
||||
NOTIFIER = 'neutron.plugins.nec.nec_plugin.NECPluginV2AgentNotifierApi'
|
||||
NEC_PLUGIN_INI = """
|
||||
[DEFAULT]
|
||||
api_extensions_path = neutron/plugins/nec/extensions
|
||||
@ -42,9 +44,7 @@ enable_packet_filter = False
|
||||
"""
|
||||
|
||||
|
||||
class NecPluginV2TestCase(test_plugin.NeutronDbPluginV2TestCase):
|
||||
|
||||
_plugin_name = PLUGIN_NAME
|
||||
class NecPluginV2TestCaseBase(object):
|
||||
_nec_ini = NEC_PLUGIN_INI
|
||||
|
||||
def _set_nec_ini(self):
|
||||
@ -64,6 +64,34 @@ class NecPluginV2TestCase(test_plugin.NeutronDbPluginV2TestCase):
|
||||
os.remove(self.nec_ini_file)
|
||||
self.nec_ini_file = None
|
||||
|
||||
def patch_remote_calls(self, use_stop=False):
|
||||
self.plugin_notifier_p = mock.patch(NOTIFIER)
|
||||
self.ofc_manager_p = mock.patch(OFC_MANAGER)
|
||||
self.plugin_notifier_p.start()
|
||||
self.ofc_manager_p.start()
|
||||
# When using mock.patch.stopall, we need to ensure
|
||||
# stop is not used anywhere in a single test.
|
||||
# In Neutron several tests use stop for each patched object,
|
||||
# so we need to take care of both cases.
|
||||
if use_stop:
|
||||
self.addCleanup(self.plugin_notifier_p.stop)
|
||||
self.addCleanup(self.ofc_manager_p.stop)
|
||||
|
||||
def setup_nec_plugin_base(self, use_stop_all=True,
|
||||
use_stop_each=False):
|
||||
# If use_stop_each is set, use_stop_all cannot be set.
|
||||
if use_stop_all and not use_stop_each:
|
||||
self.addCleanup(mock.patch.stopall)
|
||||
self._set_nec_ini()
|
||||
self.addCleanup(self._clean_nec_ini)
|
||||
self.patch_remote_calls(use_stop_each)
|
||||
|
||||
|
||||
class NecPluginV2TestCase(NecPluginV2TestCaseBase,
|
||||
test_plugin.NeutronDbPluginV2TestCase):
|
||||
|
||||
_plugin_name = PLUGIN_NAME
|
||||
|
||||
def rpcapi_update_ports(self, agent_id='nec-q-agent.fake',
|
||||
datapath_id="0xabc", added=[], removed=[]):
|
||||
kwargs = {'topic': topics.AGENT,
|
||||
@ -348,6 +376,7 @@ class TestNecPluginOfcManager(NecPluginV2TestCase):
|
||||
mock.call.create_ofc_network(ctx, self._tenant_id, net['id'],
|
||||
net['name']),
|
||||
mock.call.delete_ofc_network(ctx, net['id'], mock.ANY),
|
||||
mock.call.exists_ofc_tenant(ctx, self._tenant_id),
|
||||
mock.call.delete_ofc_tenant(ctx, self._tenant_id)
|
||||
]
|
||||
self.ofc.assert_has_calls(expected)
|
||||
@ -365,6 +394,7 @@ class TestNecPluginOfcManager(NecPluginV2TestCase):
|
||||
mock.call.create_ofc_network(ctx, self._tenant_id, net['id'],
|
||||
net['name']),
|
||||
mock.call.delete_ofc_network(ctx, net['id'], mock.ANY),
|
||||
mock.call.exists_ofc_tenant(ctx, self._tenant_id),
|
||||
mock.call.delete_ofc_tenant(ctx, self._tenant_id)
|
||||
]
|
||||
self.ofc.assert_has_calls(expected)
|
||||
@ -389,6 +419,7 @@ class TestNecPluginOfcManager(NecPluginV2TestCase):
|
||||
nets[1]['name']),
|
||||
mock.call.delete_ofc_network(ctx, nets[1]['id'], mock.ANY),
|
||||
mock.call.delete_ofc_network(ctx, nets[0]['id'], mock.ANY),
|
||||
mock.call.exists_ofc_tenant(ctx, self._tenant_id),
|
||||
mock.call.delete_ofc_tenant(ctx, self._tenant_id)
|
||||
]
|
||||
self.ofc.assert_has_calls(expected)
|
||||
@ -451,6 +482,7 @@ class TestNecPluginOfcManager(NecPluginV2TestCase):
|
||||
mock.call.create_ofc_network(ctx, self._tenant_id, net['id'],
|
||||
net['name']),
|
||||
mock.call.delete_ofc_network(ctx, net['id'], mock.ANY),
|
||||
mock.call.exists_ofc_tenant(ctx, self._tenant_id),
|
||||
mock.call.delete_ofc_tenant(ctx, self._tenant_id)
|
||||
]
|
||||
self.ofc.assert_has_calls(expected)
|
||||
@ -478,6 +510,7 @@ class TestNecPluginOfcManager(NecPluginV2TestCase):
|
||||
|
||||
mock.call.exists_ofc_port(ctx, p1['id']),
|
||||
mock.call.delete_ofc_network(ctx, net['id'], mock.ANY),
|
||||
mock.call.exists_ofc_tenant(ctx, self._tenant_id),
|
||||
mock.call.delete_ofc_tenant(ctx, self._tenant_id)
|
||||
]
|
||||
self.ofc.assert_has_calls(expected)
|
||||
@ -520,6 +553,7 @@ class TestNecPluginOfcManager(NecPluginV2TestCase):
|
||||
mock.call.exists_ofc_port(ctx, p1['id']),
|
||||
mock.call.delete_ofc_port(ctx, p1['id'], mock.ANY),
|
||||
mock.call.delete_ofc_network(ctx, net['id'], mock.ANY),
|
||||
mock.call.exists_ofc_tenant(ctx, self._tenant_id),
|
||||
mock.call.delete_ofc_tenant(ctx, self._tenant_id)
|
||||
]
|
||||
self.ofc.assert_has_calls(expected)
|
||||
@ -550,6 +584,7 @@ class TestNecPluginOfcManager(NecPluginV2TestCase):
|
||||
mock.call.exists_ofc_port(ctx, p['id']),
|
||||
mock.call.delete_ofc_port(ctx, p['id'], mock.ANY),
|
||||
mock.call.delete_ofc_network(ctx, net['id'], mock.ANY),
|
||||
mock.call.exists_ofc_tenant(ctx, self._tenant_id),
|
||||
mock.call.delete_ofc_tenant(ctx, self._tenant_id)
|
||||
]
|
||||
self.ofc.assert_has_calls(expected)
|
||||
@ -686,6 +721,7 @@ class TestNecPluginOfcManager(NecPluginV2TestCase):
|
||||
|
||||
mock.call.exists_ofc_port(ctx, p1['id']),
|
||||
mock.call.delete_ofc_network(ctx, net['id'], mock.ANY),
|
||||
mock.call.exists_ofc_tenant(ctx, self._tenant_id),
|
||||
mock.call.delete_ofc_tenant(ctx, self._tenant_id)
|
||||
]
|
||||
self.ofc.assert_has_calls(expected)
|
||||
|
117
neutron/tests/unit/nec/test_ofc_client.py
Normal file
117
neutron/tests/unit/nec/test_ofc_client.py
Normal file
@ -0,0 +1,117 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
#
|
||||
# Copyright 2013 NEC Corporation. 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: Akihiro Motoki
|
||||
|
||||
import json
|
||||
import socket
|
||||
|
||||
import mock
|
||||
|
||||
from neutron.plugins.nec.common import exceptions as nexc
|
||||
from neutron.plugins.nec.common import ofc_client
|
||||
from neutron.tests import base
|
||||
|
||||
|
||||
class OFCClientTest(base.BaseTestCase):
|
||||
|
||||
def _test_do_request(self, status, resbody, data, exctype=None,
|
||||
exc_checks=None):
|
||||
res = mock.Mock()
|
||||
res.status = status
|
||||
res.read.return_value = resbody
|
||||
|
||||
conn = mock.Mock()
|
||||
conn.getresponse.return_value = res
|
||||
|
||||
with mock.patch.object(ofc_client.OFCClient, 'get_connection',
|
||||
return_value=conn):
|
||||
client = ofc_client.OFCClient()
|
||||
|
||||
if exctype:
|
||||
e = self.assertRaises(exctype, client.do_request,
|
||||
'GET', '/somewhere', body={})
|
||||
self.assertEqual(data, str(e))
|
||||
if exc_checks:
|
||||
for k, v in exc_checks.items():
|
||||
self.assertEqual(v, getattr(e, k))
|
||||
else:
|
||||
response = client.do_request('GET', '/somewhere', body={})
|
||||
self.assertEqual(response, data)
|
||||
|
||||
headers = {"Content-Type": "application/json"}
|
||||
expected = [
|
||||
mock.call.request('GET', '/somewhere', '{}', headers),
|
||||
mock.call.getresponse(),
|
||||
]
|
||||
conn.assert_has_calls(expected)
|
||||
|
||||
def test_do_request_200_json_value(self):
|
||||
self._test_do_request(200, json.dumps([1, 2, 3]), [1, 2, 3])
|
||||
|
||||
def test_do_request_200_string(self):
|
||||
self._test_do_request(200, 'abcdef', 'abcdef')
|
||||
|
||||
def test_do_request_200_no_body(self):
|
||||
self._test_do_request(200, None, None)
|
||||
|
||||
def test_do_request_other_success_codes(self):
|
||||
for status in [201, 202, 204]:
|
||||
self._test_do_request(status, None, None)
|
||||
|
||||
def test_do_request_error_no_body(self):
|
||||
errmsg = _("An OFC exception has occurred: Operation on OFC failed")
|
||||
exc_checks = {'status': 400, 'err_code': None, 'err_msg': None}
|
||||
self._test_do_request(400, None, errmsg, nexc.OFCException, exc_checks)
|
||||
|
||||
def test_do_request_error_string_body(self):
|
||||
resbody = 'This is an error.'
|
||||
errmsg = _("An OFC exception has occurred: Operation on OFC failed")
|
||||
exc_checks = {'status': 400, 'err_code': None,
|
||||
'err_msg': 'This is an error.'}
|
||||
self._test_do_request(400, resbody, errmsg, nexc.OFCException,
|
||||
exc_checks)
|
||||
|
||||
def test_do_request_error_json_body(self):
|
||||
resbody = json.dumps({'err_code': 40022,
|
||||
'err_msg': 'This is an error.'})
|
||||
errmsg = _("An OFC exception has occurred: Operation on OFC failed")
|
||||
exc_checks = {'status': 400, 'err_code': 40022,
|
||||
'err_msg': 'This is an error.'}
|
||||
self._test_do_request(400, resbody, errmsg, nexc.OFCException,
|
||||
exc_checks)
|
||||
|
||||
def test_do_request_socket_error(self):
|
||||
conn = mock.Mock()
|
||||
conn.request.side_effect = socket.error
|
||||
|
||||
data = _("An OFC exception has occurred: Failed to connect OFC : ")
|
||||
|
||||
with mock.patch.object(ofc_client.OFCClient, 'get_connection',
|
||||
return_value=conn):
|
||||
client = ofc_client.OFCClient()
|
||||
|
||||
e = self.assertRaises(nexc.OFCException, client.do_request,
|
||||
'GET', '/somewhere', body={})
|
||||
self.assertEqual(data, str(e))
|
||||
for k in ['status', 'err_code', 'err_msg']:
|
||||
self.assertIsNone(getattr(e, k))
|
||||
|
||||
headers = {"Content-Type": "application/json"}
|
||||
expected = [
|
||||
mock.call.request('GET', '/somewhere', '{}', headers),
|
||||
]
|
||||
conn.assert_has_calls(expected)
|
@ -49,6 +49,8 @@ class OFCManagerTestBase(base.BaseTestCase):
|
||||
ndb.initialize()
|
||||
self.addCleanup(ndb.clear_db)
|
||||
self.ofc = ofc_manager.OFCManager()
|
||||
# NOTE: enable_autocheck() is a feature of StubOFCDriver
|
||||
self.ofc.driver.enable_autocheck()
|
||||
self.ctx = context.get_admin_context()
|
||||
self.addCleanup(mock.patch.stopall)
|
||||
|
||||
@ -160,6 +162,8 @@ class OFCManagerTest(OFCManagerTestBase):
|
||||
self.assertFalse(ndb.get_ofc_item(self.ctx.session, 'ofc_port', p))
|
||||
get_portinfo.assert_called_once_with(mock.ANY, p)
|
||||
|
||||
|
||||
class OFCManagerFilterTest(OFCManagerTestBase):
|
||||
def testj_create_ofc_packet_filter(self):
|
||||
"""test create ofc_filter."""
|
||||
t, n, p, f, none = self.get_random_params()
|
||||
@ -198,8 +202,92 @@ class OFCManagerTest(OFCManagerTestBase):
|
||||
'ofc_packet_filter', f))
|
||||
|
||||
|
||||
class OFCManagerRouterTest(OFCManagerTestBase):
|
||||
def get_random_params(self):
|
||||
tenant = uuidutils.generate_uuid()
|
||||
router = uuidutils.generate_uuid()
|
||||
network = uuidutils.generate_uuid()
|
||||
return (tenant, router, network)
|
||||
|
||||
def test_create_ofc_router(self):
|
||||
"""test create ofc_router"""
|
||||
t, r, _n = self.get_random_params()
|
||||
self.ofc.create_ofc_tenant(self.ctx, t)
|
||||
self.assertFalse(ndb.get_ofc_item(self.ctx.session, 'ofc_router', r))
|
||||
self.ofc.create_ofc_router(self.ctx, t, r, 'test router')
|
||||
self.assertTrue(ndb.get_ofc_item(self.ctx.session, 'ofc_router', r))
|
||||
router = ndb.get_ofc_item(self.ctx.session, 'ofc_router', r)
|
||||
self.assertEqual(router.ofc_id, "ofc-" + r[:-4])
|
||||
|
||||
def test_exists_ofc_router(self):
|
||||
"""test exists_ofc_router"""
|
||||
t, r, _n = self.get_random_params()
|
||||
self.ofc.create_ofc_tenant(self.ctx, t)
|
||||
self.assertFalse(self.ofc.exists_ofc_router(self.ctx, r))
|
||||
self.ofc.create_ofc_router(self.ctx, t, r)
|
||||
self.assertTrue(self.ofc.exists_ofc_router(self.ctx, r))
|
||||
|
||||
def test_delete_ofc_router(self):
|
||||
"""test delete ofc_router"""
|
||||
t, r, _n = self.get_random_params()
|
||||
self.ofc.create_ofc_tenant(self.ctx, t)
|
||||
self.ofc.create_ofc_router(self.ctx, t, r)
|
||||
self.assertTrue(ndb.get_ofc_item(self.ctx.session, 'ofc_router', r))
|
||||
self.ofc.delete_ofc_router(self.ctx, r, {'tenant_id': t})
|
||||
self.assertFalse(ndb.get_ofc_item(self.ctx.session, 'ofc_network', r))
|
||||
|
||||
def test_router_interface(self):
|
||||
t, r, n = self.get_random_params()
|
||||
self.ofc.create_ofc_tenant(self.ctx, t)
|
||||
self.ofc.create_ofc_router(self.ctx, t, r)
|
||||
self.ofc.create_ofc_network(self.ctx, t, n)
|
||||
self.assertTrue(ndb.get_ofc_item(self.ctx.session, 'ofc_router', r))
|
||||
self.assertTrue(ndb.get_ofc_item(self.ctx.session, 'ofc_network', n))
|
||||
|
||||
p = {'id': uuidutils.generate_uuid(),
|
||||
'network_id': n, 'ip_address': '10.1.1.1', 'cidr': '10.1.0.0/20',
|
||||
'mac_address': '11:22:33:44:55:66'}
|
||||
self.ofc.add_ofc_router_interface(self.ctx, r, p['id'], p)
|
||||
self.assertTrue(ndb.get_ofc_item(self.ctx.session,
|
||||
'ofc_port', p['id']))
|
||||
self.ofc.delete_ofc_router_interface(self.ctx, r, p['id'])
|
||||
self.assertFalse(ndb.get_ofc_item(self.ctx.session,
|
||||
'ofc_port', p['id']))
|
||||
self.ofc.delete_ofc_router(self.ctx, r, {'tenant_id': t})
|
||||
self.assertFalse(ndb.get_ofc_item(self.ctx.session, 'ofc_network', r))
|
||||
|
||||
def test_router_route(self):
|
||||
t, r, _n = self.get_random_params()
|
||||
self.ofc.create_ofc_tenant(self.ctx, t)
|
||||
self.ofc.create_ofc_router(self.ctx, t, r)
|
||||
self.assertTrue(ndb.get_ofc_item(self.ctx.session, 'ofc_router', r))
|
||||
|
||||
routes = [{'destination': '2.2.2.0/24', 'nexthop': '1.1.1.10'}]
|
||||
self.ofc.update_ofc_router_route(self.ctx, r, routes)
|
||||
self.assertEqual(len(self.ofc.driver.ofc_router_route_dict), 1)
|
||||
|
||||
routes = [{'destination': '3.3.3.0/24', 'nexthop': '1.1.1.11'},
|
||||
{'destination': '4.4.4.0/24', 'nexthop': '1.1.1.11'}]
|
||||
self.ofc.update_ofc_router_route(self.ctx, r, routes)
|
||||
self.assertEqual(len(self.ofc.driver.ofc_router_route_dict), 2)
|
||||
|
||||
routes = [{'destination': '2.2.2.0/24', 'nexthop': '1.1.1.10'}]
|
||||
self.ofc.update_ofc_router_route(self.ctx, r, routes)
|
||||
self.assertEqual(len(self.ofc.driver.ofc_router_route_dict), 1)
|
||||
|
||||
routes = []
|
||||
self.ofc.update_ofc_router_route(self.ctx, r, routes)
|
||||
self.assertEqual(len(self.ofc.driver.ofc_router_route_dict), 0)
|
||||
|
||||
|
||||
class OFCManagerTestWithOldMapping(OFCManagerTestBase):
|
||||
|
||||
def setUp(self):
|
||||
super(OFCManagerTestWithOldMapping, self).setUp()
|
||||
# NOTE(amotoki): In OldMapping tests, DB entries are directly modified
|
||||
# to create a case where the old mapping tables are used intentionally.
|
||||
self.ofc.driver.disable_autocheck()
|
||||
|
||||
def test_exists_ofc_tenant(self):
|
||||
t, n, p, f, none = self.get_random_params()
|
||||
ofc_t, ofc_n, ofc_p, ofc_f, ofc_none = self.get_random_params()
|
||||
|
@ -19,6 +19,7 @@ import random
|
||||
import string
|
||||
|
||||
import mox
|
||||
import netaddr
|
||||
|
||||
from neutron import context
|
||||
from neutron.openstack.common import uuidutils
|
||||
@ -202,6 +203,167 @@ class PFCV4DriverTest(PFCDriverTestBase):
|
||||
driver = 'pfc_v4'
|
||||
|
||||
|
||||
class PFCV5DriverTest(PFCDriverTestBase):
|
||||
driver = 'pfc_v5'
|
||||
|
||||
def test_create_router(self):
|
||||
t = uuidutils.generate_uuid()
|
||||
r = uuidutils.generate_uuid()
|
||||
description = 'dummy_router_desc'
|
||||
|
||||
tenant_path = "/tenants/%s" % _ofc(t)
|
||||
post_path = "%s/routers" % tenant_path
|
||||
router = {'id': _ofc(r)}
|
||||
ofc.OFCClient.do_request("POST", post_path,
|
||||
body=None).AndReturn(router)
|
||||
self.mox.ReplayAll()
|
||||
|
||||
ret = self.driver.create_router(tenant_path, description, r)
|
||||
self.mox.VerifyAll()
|
||||
router_path = "/tenants/%s/routers/%s" % (_ofc(t), _ofc(r))
|
||||
self.assertEqual(ret, router_path)
|
||||
|
||||
def test_delete_router(self):
|
||||
t = uuidutils.generate_uuid()
|
||||
r = uuidutils.generate_uuid()
|
||||
|
||||
router_path = "/tenants/%s/routers/%s" % (_ofc(t), _ofc(r))
|
||||
ofc.OFCClient.do_request("DELETE", router_path)
|
||||
self.mox.ReplayAll()
|
||||
|
||||
self.driver.delete_router(router_path)
|
||||
self.mox.VerifyAll()
|
||||
|
||||
def test_add_router_interface(self):
|
||||
t = uuidutils.generate_uuid()
|
||||
r = uuidutils.generate_uuid()
|
||||
n = uuidutils.generate_uuid()
|
||||
p = uuidutils.generate_uuid()
|
||||
|
||||
router_path = "/tenants/%s/routers/%s" % (_ofc(t), _ofc(r))
|
||||
infs_path = router_path + "/interfaces"
|
||||
net_path = "/tenants/%s/networks/%s" % (_ofc(t), _ofc(n))
|
||||
ip_address = '10.1.1.1/24'
|
||||
mac_address = '11:22:33:44:55:66'
|
||||
body = {'net_id': _ofc(n),
|
||||
'ip_address': ip_address,
|
||||
'mac_address': mac_address}
|
||||
inf = {'id': _ofc(p)}
|
||||
ofc.OFCClient.do_request("POST", infs_path,
|
||||
body=body).AndReturn(inf)
|
||||
self.mox.ReplayAll()
|
||||
|
||||
ret = self.driver.add_router_interface(router_path, net_path,
|
||||
ip_address, mac_address)
|
||||
self.mox.VerifyAll()
|
||||
inf_path = "%s/interfaces/%s" % (router_path, _ofc(p))
|
||||
self.assertEqual(ret, inf_path)
|
||||
|
||||
def test_update_router_interface(self):
|
||||
t = uuidutils.generate_uuid()
|
||||
r = uuidutils.generate_uuid()
|
||||
p = uuidutils.generate_uuid()
|
||||
|
||||
router_path = "/tenants/%s/routers/%s" % (_ofc(t), _ofc(r))
|
||||
inf_path = "%s/interfaces/%s" % (router_path, _ofc(p))
|
||||
ip_address = '10.1.1.1/24'
|
||||
mac_address = '11:22:33:44:55:66'
|
||||
|
||||
body = {'ip_address': ip_address,
|
||||
'mac_address': mac_address}
|
||||
ofc.OFCClient.do_request("PUT", inf_path, body=body)
|
||||
|
||||
body = {'ip_address': ip_address}
|
||||
ofc.OFCClient.do_request("PUT", inf_path, body=body)
|
||||
|
||||
body = {'mac_address': mac_address}
|
||||
ofc.OFCClient.do_request("PUT", inf_path, body=body)
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
||||
self.driver.update_router_interface(inf_path, ip_address, mac_address)
|
||||
self.driver.update_router_interface(inf_path, ip_address=ip_address)
|
||||
self.driver.update_router_interface(inf_path, mac_address=mac_address)
|
||||
self.mox.VerifyAll()
|
||||
|
||||
def test_delete_router_interface(self):
|
||||
t = uuidutils.generate_uuid()
|
||||
r = uuidutils.generate_uuid()
|
||||
p = uuidutils.generate_uuid()
|
||||
|
||||
router_path = "/tenants/%s/routers/%s" % (_ofc(t), _ofc(r))
|
||||
inf_path = "%s/interfaces/%s" % (router_path, _ofc(p))
|
||||
ofc.OFCClient.do_request("DELETE", inf_path)
|
||||
self.mox.ReplayAll()
|
||||
|
||||
self.driver.delete_router_interface(inf_path)
|
||||
self.mox.VerifyAll()
|
||||
|
||||
def _get_route_id(self, dest, nexthop):
|
||||
dest = netaddr.IPNetwork(dest)
|
||||
return '-'.join([str(dest.network), nexthop, str(dest.netmask)])
|
||||
|
||||
def test_add_router_route(self):
|
||||
t = uuidutils.generate_uuid()
|
||||
r = uuidutils.generate_uuid()
|
||||
|
||||
router_path = "/tenants/%s/routers/%s" % (_ofc(t), _ofc(r))
|
||||
routes_path = router_path + "/routes"
|
||||
dest = '10.1.1.0/24'
|
||||
nexthop = '192.168.100.10'
|
||||
body = {'destination': dest, 'nexthop': nexthop}
|
||||
route_id = self._get_route_id(dest, nexthop)
|
||||
ofc.OFCClient.do_request("POST", routes_path,
|
||||
body=body).AndReturn({'id': route_id})
|
||||
self.mox.ReplayAll()
|
||||
|
||||
ret = self.driver.add_router_route(router_path, '10.1.1.0/24',
|
||||
'192.168.100.10')
|
||||
self.mox.VerifyAll()
|
||||
route_path = routes_path + '/' + route_id
|
||||
self.assertEqual(ret, route_path)
|
||||
|
||||
def test_delete_router_route(self):
|
||||
t = uuidutils.generate_uuid()
|
||||
r = uuidutils.generate_uuid()
|
||||
|
||||
router_path = "/tenants/%s/routers/%s" % (_ofc(t), _ofc(r))
|
||||
routes_path = router_path + "/routes"
|
||||
|
||||
route_id = self._get_route_id('10.1.1.0/24', '192.168.100.10')
|
||||
route_path = routes_path + '/' + route_id
|
||||
ofc.OFCClient.do_request("DELETE", route_path)
|
||||
self.mox.ReplayAll()
|
||||
|
||||
self.driver.delete_router_route(route_path)
|
||||
self.mox.VerifyAll()
|
||||
|
||||
def test_list_router_routes(self):
|
||||
t = uuidutils.generate_uuid()
|
||||
r = uuidutils.generate_uuid()
|
||||
|
||||
router_path = "/tenants/%s/routers/%s" % (_ofc(t), _ofc(r))
|
||||
routes_path = router_path + "/routes"
|
||||
|
||||
routes = [('10.1.1.0/24', '192.168.100.10'),
|
||||
('10.2.2.0/20', '192.168.100.20')]
|
||||
data = {'routes': [{'id': self._get_route_id(route[0], route[1]),
|
||||
'destination': route[0], 'nexthop': route[1]}
|
||||
for route in routes]}
|
||||
ofc.OFCClient.do_request("GET", routes_path).AndReturn(data)
|
||||
self.mox.ReplayAll()
|
||||
|
||||
ret = self.driver.list_router_routes(router_path)
|
||||
self.mox.VerifyAll()
|
||||
|
||||
expected = [{'id': (routes_path + "/" +
|
||||
self._get_route_id(route[0], route[1])),
|
||||
'destination': route[0], 'nexthop': route[1]}
|
||||
for route in routes]
|
||||
self.assertEqual(len(routes), len(ret))
|
||||
self.assertEqual(data['routes'], expected)
|
||||
|
||||
|
||||
class PFCDriverStringTest(base.BaseTestCase):
|
||||
|
||||
driver = 'neutron.plugins.nec.drivers.pfc.PFCDriverBase'
|
||||
|
46
neutron/tests/unit/nec/test_router.py
Normal file
46
neutron/tests/unit/nec/test_router.py
Normal file
@ -0,0 +1,46 @@
|
||||
# Copyright (c) 2013 OpenStack Foundation.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
# implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import mock
|
||||
|
||||
from neutron import manager
|
||||
from neutron.plugins.nec.common import config
|
||||
from neutron.tests.unit.nec import test_nec_plugin
|
||||
from neutron.tests.unit import test_extension_extraroute as test_ext_route
|
||||
|
||||
|
||||
class NecRouterL3AgentTestCase(test_ext_route.ExtraRouteDBTestCase):
|
||||
|
||||
_plugin_name = test_nec_plugin.PLUGIN_NAME
|
||||
|
||||
def setUp(self):
|
||||
self.addCleanup(mock.patch.stopall)
|
||||
mock.patch(test_nec_plugin.OFC_MANAGER).start()
|
||||
super(NecRouterL3AgentTestCase, self).setUp(self._plugin_name)
|
||||
|
||||
plugin = manager.NeutronManager.get_plugin()
|
||||
plugin.network_scheduler = None
|
||||
plugin.router_scheduler = None
|
||||
|
||||
def test_floatingip_with_invalid_create_port(self):
|
||||
self._test_floatingip_with_invalid_create_port(self._plugin_name)
|
||||
|
||||
|
||||
class NecRouterOpenFlowTestCase(NecRouterL3AgentTestCase):
|
||||
|
||||
def setUp(self):
|
||||
config.CONF.set_override('default_router_provider',
|
||||
'openflow', 'PROVIDER')
|
||||
super(NecRouterOpenFlowTestCase, self).setUp()
|
@ -189,10 +189,10 @@ class AgentSchedulerTestMixIn(object):
|
||||
return agent_data['id']
|
||||
|
||||
|
||||
class OvsAgentSchedulerTestCase(test_l3_plugin.L3NatTestCaseMixin,
|
||||
test_agent_ext_plugin.AgentDBTestMixIn,
|
||||
AgentSchedulerTestMixIn,
|
||||
test_plugin.NeutronDbPluginV2TestCase):
|
||||
class OvsAgentSchedulerTestCaseBase(test_l3_plugin.L3NatTestCaseMixin,
|
||||
test_agent_ext_plugin.AgentDBTestMixIn,
|
||||
AgentSchedulerTestMixIn,
|
||||
test_plugin.NeutronDbPluginV2TestCase):
|
||||
fmt = 'json'
|
||||
plugin_str = ('neutron.plugins.openvswitch.'
|
||||
'ovs_neutron_plugin.OVSNeutronPluginV2')
|
||||
@ -202,7 +202,7 @@ class OvsAgentSchedulerTestCase(test_l3_plugin.L3NatTestCaseMixin,
|
||||
self.saved_attr_map = {}
|
||||
for resource, attrs in attributes.RESOURCE_ATTRIBUTE_MAP.iteritems():
|
||||
self.saved_attr_map[resource] = attrs.copy()
|
||||
super(OvsAgentSchedulerTestCase, self).setUp(self.plugin_str)
|
||||
super(OvsAgentSchedulerTestCaseBase, self).setUp(self.plugin_str)
|
||||
ext_mgr = extensions.PluginAwareExtensionManager.get_instance()
|
||||
self.ext_api = test_extensions.setup_extensions_middleware(ext_mgr)
|
||||
self.adminContext = context.get_admin_context()
|
||||
@ -219,6 +219,9 @@ class OvsAgentSchedulerTestCase(test_l3_plugin.L3NatTestCaseMixin,
|
||||
# Restore the original RESOURCE_ATTRIBUTE_MAP
|
||||
attributes.RESOURCE_ATTRIBUTE_MAP = self.saved_attr_map
|
||||
|
||||
|
||||
class OvsAgentSchedulerTestCase(OvsAgentSchedulerTestCaseBase):
|
||||
|
||||
def test_report_states(self):
|
||||
self._register_agent_states()
|
||||
agents = self._list_agents()
|
||||
@ -957,12 +960,7 @@ class OvsAgentSchedulerTestCase(test_l3_plugin.L3NatTestCaseMixin,
|
||||
admin_context=False)
|
||||
|
||||
|
||||
class OvsDhcpAgentNotifierTestCase(test_l3_plugin.L3NatTestCaseMixin,
|
||||
test_agent_ext_plugin.AgentDBTestMixIn,
|
||||
AgentSchedulerTestMixIn,
|
||||
test_plugin.NeutronDbPluginV2TestCase):
|
||||
plugin_str = ('neutron.plugins.openvswitch.'
|
||||
'ovs_neutron_plugin.OVSNeutronPluginV2')
|
||||
class OvsDhcpAgentNotifierTestCase(OvsAgentSchedulerTestCaseBase):
|
||||
|
||||
def setUp(self):
|
||||
self.dhcp_notifier = dhcp_rpc_agent_api.DhcpAgentNotifyAPI()
|
||||
@ -971,27 +969,8 @@ class OvsDhcpAgentNotifierTestCase(test_l3_plugin.L3NatTestCaseMixin,
|
||||
'DhcpAgentNotifyAPI')
|
||||
self.dhcp_notifier_cls = self.dhcp_notifier_cls_p.start()
|
||||
self.dhcp_notifier_cls.return_value = self.dhcp_notifier
|
||||
# Save the global RESOURCE_ATTRIBUTE_MAP
|
||||
self.saved_attr_map = {}
|
||||
for resource, attrs in attributes.RESOURCE_ATTRIBUTE_MAP.iteritems():
|
||||
self.saved_attr_map[resource] = attrs.copy()
|
||||
super(OvsDhcpAgentNotifierTestCase, self).setUp(self.plugin_str)
|
||||
ext_mgr = extensions.PluginAwareExtensionManager.get_instance()
|
||||
self.ext_api = test_extensions.setup_extensions_middleware(ext_mgr)
|
||||
self.adminContext = context.get_admin_context()
|
||||
# Add the resources to the global attribute map
|
||||
# This is done here as the setup process won't
|
||||
# initialize the main API router which extends
|
||||
# the global attribute map
|
||||
attributes.RESOURCE_ATTRIBUTE_MAP.update(
|
||||
agent.RESOURCE_ATTRIBUTE_MAP)
|
||||
self.agentscheduler_dbMinxin = manager.NeutronManager.get_plugin()
|
||||
super(OvsDhcpAgentNotifierTestCase, self).setUp()
|
||||
self.addCleanup(self.dhcp_notifier_cls_p.stop)
|
||||
self.addCleanup(self.restore_attribute_map)
|
||||
|
||||
def restore_attribute_map(self):
|
||||
# Restore the original RESOURCE_ATTRIBUTE_MAP
|
||||
attributes.RESOURCE_ATTRIBUTE_MAP = self.saved_attr_map
|
||||
|
||||
def test_network_add_to_dhcp_agent_notification(self):
|
||||
with mock.patch.object(self.dhcp_notifier, 'cast') as mock_dhcp:
|
||||
@ -1093,12 +1072,7 @@ class OvsDhcpAgentNotifierTestCase(test_l3_plugin.L3NatTestCaseMixin,
|
||||
self.assertIn(expected, mock_dhcp.call_args_list)
|
||||
|
||||
|
||||
class OvsL3AgentNotifierTestCase(test_l3_plugin.L3NatTestCaseMixin,
|
||||
test_agent_ext_plugin.AgentDBTestMixIn,
|
||||
AgentSchedulerTestMixIn,
|
||||
test_plugin.NeutronDbPluginV2TestCase):
|
||||
plugin_str = ('neutron.plugins.openvswitch.'
|
||||
'ovs_neutron_plugin.OVSNeutronPluginV2')
|
||||
class OvsL3AgentNotifierTestCase(OvsAgentSchedulerTestCaseBase):
|
||||
|
||||
def setUp(self):
|
||||
self.dhcp_notifier_cls_p = mock.patch(
|
||||
@ -1107,27 +1081,8 @@ class OvsL3AgentNotifierTestCase(test_l3_plugin.L3NatTestCaseMixin,
|
||||
self.dhcp_notifier = mock.Mock(name='dhcp_notifier')
|
||||
self.dhcp_notifier_cls = self.dhcp_notifier_cls_p.start()
|
||||
self.dhcp_notifier_cls.return_value = self.dhcp_notifier
|
||||
# Save the global RESOURCE_ATTRIBUTE_MAP
|
||||
self.saved_attr_map = {}
|
||||
for resource, attrs in attributes.RESOURCE_ATTRIBUTE_MAP.iteritems():
|
||||
self.saved_attr_map[resource] = attrs.copy()
|
||||
super(OvsL3AgentNotifierTestCase, self).setUp(self.plugin_str)
|
||||
ext_mgr = extensions.PluginAwareExtensionManager.get_instance()
|
||||
self.ext_api = test_extensions.setup_extensions_middleware(ext_mgr)
|
||||
self.adminContext = context.get_admin_context()
|
||||
# Add the resources to the global attribute map
|
||||
# This is done here as the setup process won't
|
||||
# initialize the main API router which extends
|
||||
# the global attribute map
|
||||
attributes.RESOURCE_ATTRIBUTE_MAP.update(
|
||||
agent.RESOURCE_ATTRIBUTE_MAP)
|
||||
self.agentscheduler_dbMinxin = manager.NeutronManager.get_plugin()
|
||||
super(OvsL3AgentNotifierTestCase, self).setUp()
|
||||
self.addCleanup(self.dhcp_notifier_cls_p.stop)
|
||||
self.addCleanup(self.restore_attribute_map)
|
||||
|
||||
def restore_attribute_map(self):
|
||||
# Restore the original RESOURCE_ATTRIBUTE_MAP
|
||||
attributes.RESOURCE_ATTRIBUTE_MAP = self.saved_attr_map
|
||||
|
||||
def test_router_add_to_l3_agent_notification(self):
|
||||
plugin = manager.NeutronManager.get_plugin()
|
||||
|
@ -59,10 +59,11 @@ class TestExtraRoutePlugin(test_l3.TestL3NatPlugin,
|
||||
|
||||
class ExtraRouteDBTestCase(test_l3.L3NatDBTestCase):
|
||||
|
||||
def setUp(self):
|
||||
test_config['plugin_name_v2'] = (
|
||||
'neutron.tests.unit.'
|
||||
'test_extension_extraroute.TestExtraRoutePlugin')
|
||||
def setUp(self, plugin=None):
|
||||
if not plugin:
|
||||
plugin = ('neutron.tests.unit.test_extension_extraroute.'
|
||||
'TestExtraRoutePlugin')
|
||||
test_config['plugin_name_v2'] = plugin
|
||||
# for these tests we need to enable overlapping ips
|
||||
cfg.CONF.set_default('allow_overlapping_ips', True)
|
||||
cfg.CONF.set_default('max_routes', 3)
|
||||
|
@ -28,7 +28,6 @@ from webob import exc
|
||||
import webtest
|
||||
|
||||
from neutron.api import extensions
|
||||
from neutron.api.rpc.agentnotifiers import l3_rpc_agent_api
|
||||
from neutron.api.v2 import attributes
|
||||
from neutron.common import config
|
||||
from neutron.common import constants as l3_constants
|
||||
@ -321,13 +320,15 @@ class L3NatTestCaseMixin(object):
|
||||
return router_req.get_response(self.ext_api)
|
||||
|
||||
def _make_router(self, fmt, tenant_id, name=None, admin_state_up=None,
|
||||
external_gateway_info=None, set_context=False):
|
||||
arg_list = (external_gateway_info and
|
||||
('external_gateway_info', ) or None)
|
||||
external_gateway_info=None, set_context=False,
|
||||
arg_list=None, **kwargs):
|
||||
if external_gateway_info:
|
||||
arg_list = ('external_gateway_info', ) + (arg_list or ())
|
||||
res = self._create_router(fmt, tenant_id, name,
|
||||
admin_state_up, set_context,
|
||||
arg_list=arg_list,
|
||||
external_gateway_info=external_gateway_info)
|
||||
external_gateway_info=external_gateway_info,
|
||||
**kwargs)
|
||||
return self.deserialize(fmt, res)
|
||||
|
||||
def _add_external_gateway_to_router(self, router_id, network_id,
|
||||
@ -367,10 +368,11 @@ class L3NatTestCaseMixin(object):
|
||||
@contextlib.contextmanager
|
||||
def router(self, name='router1', admin_state_up=True,
|
||||
fmt=None, tenant_id=_uuid(),
|
||||
external_gateway_info=None, set_context=False):
|
||||
external_gateway_info=None, set_context=False,
|
||||
**kwargs):
|
||||
router = self._make_router(fmt or self.fmt, tenant_id, name,
|
||||
admin_state_up, external_gateway_info,
|
||||
set_context)
|
||||
set_context, **kwargs)
|
||||
try:
|
||||
yield router
|
||||
finally:
|
||||
@ -1673,18 +1675,19 @@ class L3AgentDbTestCase(L3NatTestCaseBase):
|
||||
def _test_notify_op_agent(self, target_func, *args):
|
||||
l3_rpc_agent_api_str = (
|
||||
'neutron.api.rpc.agentnotifiers.l3_rpc_agent_api.L3AgentNotifyAPI')
|
||||
oldNotify = l3_rpc_agent_api.L3AgentNotify
|
||||
plugin = NeutronManager.get_plugin()
|
||||
oldNotify = plugin.l3_rpc_notifier
|
||||
try:
|
||||
with mock.patch(l3_rpc_agent_api_str) as notifyApi:
|
||||
l3_rpc_agent_api.L3AgentNotify = notifyApi
|
||||
plugin.l3_rpc_notifier = notifyApi
|
||||
kargs = [item for item in args]
|
||||
kargs.append(notifyApi)
|
||||
target_func(*kargs)
|
||||
except Exception:
|
||||
l3_rpc_agent_api.L3AgentNotify = oldNotify
|
||||
plugin.l3_rpc_notifier = oldNotify
|
||||
raise
|
||||
else:
|
||||
l3_rpc_agent_api.L3AgentNotify = oldNotify
|
||||
plugin.l3_rpc_notifier = oldNotify
|
||||
|
||||
def _test_router_gateway_op_agent(self, notifyApi):
|
||||
with self.router() as r:
|
||||
|
Loading…
Reference in New Issue
Block a user