# 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
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 ==
# 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)
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)
'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'],
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'],
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)
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):
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')
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):
--- /dev/null
+# 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')
from neutron.agent.common import config
from neutron.openstack.common import rpc # noqa
+from neutron.plugins.nec.common import constants as nconst
ovs_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)
OVS = cfg.CONF.OVS
AGENT = cfg.CONF.AGENT
OFC = cfg.CONF.OFC
+PROVIDER = cfg.CONF.PROVIDER
--- /dev/null
+# 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'
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")
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.")
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 "
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)
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,
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]
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
"""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."""
--- /dev/null
+# 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
'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):
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,
"""
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}
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):
class PFCV4Driver(PFCDriverBase):
pass
+
+
+class PFCV5Driver(PFCRouterDriverMixin, PFCDriverBase):
+ pass
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,
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"
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.
'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.
--- /dev/null
+# 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 {}
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
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
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
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.
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):
'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
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)
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)
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.
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
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 = {
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)
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):
# 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)
--- /dev/null
+# 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]
# @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.
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)
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'])
--- /dev/null
+# 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)
# 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):
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
# 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)
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
"""
-class NecPluginV2TestCase(test_plugin.NeutronDbPluginV2TestCase):
-
- _plugin_name = PLUGIN_NAME
+class NecPluginV2TestCaseBase(object):
_nec_ini = NEC_PLUGIN_INI
def _set_nec_ini(self):
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,
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)
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)
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)
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)
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)
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)
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)
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)
--- /dev/null
+# 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)
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)
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()
'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()
import string
import mox
+import netaddr
from neutron import context
from neutron.openstack.common import uuidutils
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'
--- /dev/null
+# 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()
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')
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()
# 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()
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()
'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:
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(
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()
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)
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
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,
@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:
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: