diff options
Diffstat (limited to 'networking-odl/networking_odl/l3')
-rw-r--r-- | networking-odl/networking_odl/l3/__init__.py | 0 | ||||
-rw-r--r-- | networking-odl/networking_odl/l3/l3_odl.py | 189 | ||||
-rw-r--r-- | networking-odl/networking_odl/l3/l3_odl_v2.py | 206 |
3 files changed, 395 insertions, 0 deletions
diff --git a/networking-odl/networking_odl/l3/__init__.py b/networking-odl/networking_odl/l3/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/networking-odl/networking_odl/l3/__init__.py diff --git a/networking-odl/networking_odl/l3/l3_odl.py b/networking-odl/networking_odl/l3/l3_odl.py new file mode 100644 index 0000000..e06e335 --- /dev/null +++ b/networking-odl/networking_odl/l3/l3_odl.py @@ -0,0 +1,189 @@ +# +# Copyright (C) 2013 Red Hat, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# + +from oslo_config import cfg +from oslo_log import log as logging + +from neutron.api.rpc.agentnotifiers import l3_rpc_agent_api +from neutron.api.rpc.handlers import l3_rpc +from neutron.common import rpc as n_rpc +from neutron.common import topics +from neutron.db import extraroute_db +from neutron.db import l3_agentschedulers_db +from neutron.db import l3_dvr_db +from neutron.db import l3_gwmode_db +from neutron.plugins.common import constants +from neutron_lib import constants as q_const + +from networking_odl.common import client as odl_client +from networking_odl.common import utils as odl_utils + +try: + from neutron.db.db_base_plugin_v2 import common_db_mixin +except ImportError as e: + # the change set ofece8cc2e9aae1610a325d0c206e38da3da9a0a1a + # the Change-Id of I1eac61c258541bca80e14be4b7c75519a014ffae + # db_base_plugin_v2.common_db_mixin was removed + from neutron.db import common_db_mixin + + +cfg.CONF.import_group('ml2_odl', 'networking_odl.common.config') +LOG = logging.getLogger(__name__) +ROUTERS = 'routers' +FLOATINGIPS = 'floatingips' + + +class OpenDaylightL3RouterPlugin( + common_db_mixin.CommonDbMixin, + extraroute_db.ExtraRoute_db_mixin, + l3_dvr_db.L3_NAT_with_dvr_db_mixin, + l3_gwmode_db.L3_NAT_db_mixin, + l3_agentschedulers_db.L3AgentSchedulerDbMixin): + + """Implementation of the OpenDaylight L3 Router Service Plugin. + + This class implements a L3 service plugin that provides + router and floatingip resources and manages associated + request/response. + """ + supported_extension_aliases = ["dvr", "router", "ext-gw-mode", + "extraroute"] + + def __init__(self): + self.setup_rpc() + self.client = odl_client.OpenDaylightRestClient.create_client() + + def setup_rpc(self): + self.topic = topics.L3PLUGIN + self.conn = n_rpc.create_connection() + self.agent_notifiers.update( + {q_const.AGENT_TYPE_L3: l3_rpc_agent_api.L3AgentNotifyAPI()}) + self.endpoints = [l3_rpc.L3RpcCallback()] + self.conn.create_consumer(self.topic, self.endpoints, + fanout=False) + self.conn.consume_in_threads() + + def get_plugin_type(self): + return constants.L3_ROUTER_NAT + + def get_plugin_description(self): + """returns string description of the plugin.""" + return ("L3 Router Service Plugin for basic L3 forwarding" + " using OpenDaylight") + + def filter_update_router_attributes(self, router): + """Filter out router attributes for an update operation.""" + odl_utils.try_del(router, ['id', 'tenant_id', 'status']) + + def create_router(self, context, router): + router_dict = super(OpenDaylightL3RouterPlugin, self).create_router( + context, router) + url = ROUTERS + self.client.sendjson('post', url, {ROUTERS[:-1]: router_dict}) + return router_dict + + def update_router(self, context, id, router): + router_dict = super(OpenDaylightL3RouterPlugin, self).update_router( + context, id, router) + url = ROUTERS + "/" + id + resource = router_dict.copy() + self.filter_update_router_attributes(resource) + self.client.sendjson('put', url, {ROUTERS[:-1]: resource}) + return router_dict + + def delete_router(self, context, id): + super(OpenDaylightL3RouterPlugin, self).delete_router(context, id) + url = ROUTERS + "/" + id + self.client.sendjson('delete', url, None) + + def create_floatingip(self, context, floatingip, + initial_status=q_const.FLOATINGIP_STATUS_ACTIVE): + fip_dict = super(OpenDaylightL3RouterPlugin, self).create_floatingip( + context, floatingip, initial_status) + url = FLOATINGIPS + self.client.sendjson('post', url, {FLOATINGIPS[:-1]: fip_dict}) + return fip_dict + + def update_floatingip(self, context, id, floatingip): + with context.session.begin(subtransactions=True): + fip_dict = super(OpenDaylightL3RouterPlugin, + self).update_floatingip(context, id, floatingip) + # Update status based on association + if fip_dict.get('port_id') is None: + fip_dict['status'] = q_const.FLOATINGIP_STATUS_DOWN + else: + fip_dict['status'] = q_const.FLOATINGIP_STATUS_ACTIVE + self.update_floatingip_status(context, id, fip_dict['status']) + + url = FLOATINGIPS + "/" + id + self.client.sendjson('put', url, {FLOATINGIPS[:-1]: fip_dict}) + return fip_dict + + def delete_floatingip(self, context, id): + super(OpenDaylightL3RouterPlugin, self).delete_floatingip(context, id) + url = FLOATINGIPS + "/" + id + self.client.sendjson('delete', url, None) + + def add_router_interface(self, context, router_id, interface_info): + new_router = super( + OpenDaylightL3RouterPlugin, self).add_router_interface( + context, router_id, interface_info) + url = ROUTERS + "/" + router_id + "/add_router_interface" + router_dict = self._generate_router_dict(router_id, interface_info, + new_router) + self.client.sendjson('put', url, router_dict) + return new_router + + def remove_router_interface(self, context, router_id, interface_info): + new_router = super( + OpenDaylightL3RouterPlugin, self).remove_router_interface( + context, router_id, interface_info) + url = ROUTERS + "/" + router_id + "/remove_router_interface" + router_dict = self._generate_router_dict(router_id, interface_info, + new_router) + self.client.sendjson('put', url, router_dict) + return new_router + + def _generate_router_dict(self, router_id, interface_info, new_router): + # Get network info for the subnet that is being added to the router. + # Check if the interface information is by port-id or subnet-id + add_by_port, add_by_sub = self._validate_interface_info(interface_info) + if add_by_sub: + _port_id = new_router['port_id'] + _subnet_id = interface_info['subnet_id'] + elif add_by_port: + _port_id = interface_info['port_id'] + _subnet_id = new_router['subnet_id'] + + router_dict = {'subnet_id': _subnet_id, + 'port_id': _port_id, + 'id': router_id, + 'tenant_id': new_router['tenant_id']} + + return router_dict + + dvr_deletens_if_no_port_warned = False + + def dvr_deletens_if_no_port(self, context, port_id): + # TODO(yamahata): implement this method or delete this logging + # For now, this is defined to avoid attribute exception + # Since ODL L3 does not create namespaces, this is always going to + # be a noop. When it is confirmed, delete this comment and logging + if not self.dvr_deletens_if_no_port_warned: + LOG.debug('dvr is not suported yet. ' + 'this method needs to be implemented') + self.dvr_deletens_if_no_port_warned = True + return [] diff --git a/networking-odl/networking_odl/l3/l3_odl_v2.py b/networking-odl/networking_odl/l3/l3_odl_v2.py new file mode 100644 index 0000000..2732ea6 --- /dev/null +++ b/networking-odl/networking_odl/l3/l3_odl_v2.py @@ -0,0 +1,206 @@ +# Copyright (c) 2016 OpenStack Foundation +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# + +from oslo_log import log as logging + +from neutron.db import api as db_api +from neutron.db import common_db_mixin +from neutron.db import extraroute_db +from neutron.db import l3_agentschedulers_db +from neutron.db import l3_dvr_db +from neutron.db import l3_gwmode_db +from neutron.plugins.common import constants +from neutron_lib import constants as q_const + +from networking_odl.common import config # noqa +from networking_odl.common import constants as odl_const +from networking_odl.db import db +from networking_odl.journal import journal + +LOG = logging.getLogger(__name__) + + +class OpenDaylightL3RouterPlugin( + common_db_mixin.CommonDbMixin, + extraroute_db.ExtraRoute_db_mixin, + l3_dvr_db.L3_NAT_with_dvr_db_mixin, + l3_gwmode_db.L3_NAT_db_mixin, + l3_agentschedulers_db.L3AgentSchedulerDbMixin): + + """Implementation of the OpenDaylight L3 Router Service Plugin. + + This class implements a L3 service plugin that provides + router and floatingip resources and manages associated + request/response. + """ + supported_extension_aliases = ["dvr", "router", "ext-gw-mode", + "extraroute"] + + def __init__(self): + super(OpenDaylightL3RouterPlugin, self).__init__() + + # TODO(rcurran): Continue investigation into how many journal threads + # to run per neutron controller deployment. + self.journal = journal.OpendaylightJournalThread() + + def get_plugin_type(self): + return constants.L3_ROUTER_NAT + + def get_plugin_description(self): + """Returns string description of the plugin.""" + return ("L3 Router Service Plugin for basic L3 forwarding " + "using OpenDaylight.") + + @journal.call_thread_on_end + def create_router(self, context, router): + session = db_api.get_session() + with session.begin(subtransactions=True): + router_dict = super( + OpenDaylightL3RouterPlugin, self).create_router(context, + router) + db.create_pending_row(context.session, odl_const.ODL_ROUTER, + router_dict['id'], odl_const.ODL_CREATE, + router_dict) + return router_dict + + @journal.call_thread_on_end + def update_router(self, context, router_id, router): + session = db_api.get_session() + with session.begin(subtransactions=True): + router_dict = super( + OpenDaylightL3RouterPlugin, self).update_router( + context, router_id, router) + db.create_pending_row(context.session, odl_const.ODL_ROUTER, + router_id, odl_const.ODL_UPDATE, router_dict) + return router_dict + + @journal.call_thread_on_end + def delete_router(self, context, router_id): + session = db_api.get_session() + router_dict = self.get_router(context, router_id) + dependency_list = [router_dict['gw_port_id']] + with session.begin(subtransactions=True): + super(OpenDaylightL3RouterPlugin, self).delete_router(context, + router_id) + db.create_pending_row(context.session, odl_const.ODL_ROUTER, + router_id, odl_const.ODL_DELETE, + dependency_list) + + @journal.call_thread_on_end + def create_floatingip(self, context, floatingip, + initial_status=q_const.FLOATINGIP_STATUS_ACTIVE): + session = db_api.get_session() + with session.begin(subtransactions=True): + fip_dict = super( + OpenDaylightL3RouterPlugin, self).create_floatingip( + context, floatingip, initial_status) + db.create_pending_row(context.session, odl_const.ODL_FLOATINGIP, + fip_dict['id'], odl_const.ODL_CREATE, + fip_dict) + return fip_dict + + @journal.call_thread_on_end + def update_floatingip(self, context, floatingip_id, floatingip): + session = db_api.get_session() + with session.begin(subtransactions=True): + fip_dict = super( + OpenDaylightL3RouterPlugin, self).update_floatingip( + context, floatingip_id, floatingip) + + # Update status based on association + if fip_dict.get('port_id') is None: + fip_dict['status'] = q_const.FLOATINGIP_STATUS_DOWN + else: + fip_dict['status'] = q_const.FLOATINGIP_STATUS_ACTIVE + self.update_floatingip_status(context, floatingip_id, + fip_dict['status']) + + db.create_pending_row(context.session, odl_const.ODL_FLOATINGIP, + floatingip_id, odl_const.ODL_UPDATE, + fip_dict) + return fip_dict + + @journal.call_thread_on_end + def delete_floatingip(self, context, floatingip_id): + session = db_api.get_session() + floatingip_dict = self.get_floatingip(context, floatingip_id) + dependency_list = [floatingip_dict['router_id']] + dependency_list.append(floatingip_dict['floating_network_id']) + with session.begin(subtransactions=True): + super(OpenDaylightL3RouterPlugin, self).delete_floatingip( + context, floatingip_id) + db.create_pending_row(context.session, odl_const.ODL_FLOATINGIP, + floatingip_id, odl_const.ODL_DELETE, + dependency_list) + + @journal.call_thread_on_end + def add_router_interface(self, context, router_id, interface_info): + session = db_api.get_session() + with session.begin(subtransactions=True): + new_router = super( + OpenDaylightL3RouterPlugin, self).add_router_interface( + context, router_id, interface_info) + router_dict = self._generate_router_dict(router_id, interface_info, + new_router) + db.create_pending_row(context.session, odl_const.ODL_ROUTER_INTF, + odl_const.ODL_UUID_NOT_USED, + odl_const.ODL_ADD, router_dict) + return new_router + + @journal.call_thread_on_end + def remove_router_interface(self, context, router_id, interface_info): + session = db_api.get_session() + with session.begin(subtransactions=True): + new_router = super( + OpenDaylightL3RouterPlugin, self).remove_router_interface( + context, router_id, interface_info) + router_dict = self._generate_router_dict(router_id, interface_info, + new_router) + db.create_pending_row(context.session, odl_const.ODL_ROUTER_INTF, + odl_const.ODL_UUID_NOT_USED, + odl_const.ODL_REMOVE, router_dict) + return new_router + + def _generate_router_dict(self, router_id, interface_info, new_router): + # Get network info for the subnet that is being added to the router. + # Check if the interface information is by port-id or subnet-id. + add_by_port, add_by_sub = self._validate_interface_info(interface_info) + if add_by_sub: + _port_id = new_router['port_id'] + _subnet_id = interface_info['subnet_id'] + elif add_by_port: + _port_id = interface_info['port_id'] + _subnet_id = new_router['subnet_id'] + + router_dict = {'subnet_id': _subnet_id, + 'port_id': _port_id, + 'id': router_id, + 'tenant_id': new_router['tenant_id']} + + return router_dict + + dvr_deletens_if_no_port_warned = False + + def dvr_deletens_if_no_port(self, context, port_id): + # TODO(yamahata): implement this method or delete this logging + # For now, this is defined to avoid attribute exception + # Since ODL L3 does not create namespaces, this is always going to + # be a noop. When it is confirmed, delete this comment and logging + if not self.dvr_deletens_if_no_port_warned: + LOG.debug('dvr is not suported yet. ' + 'this method needs to be implemented') + self.dvr_deletens_if_no_port_warned = True + return [] |