# # 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 []