1
# Copyright 2013 NEC Corporation. All rights reserved.
3
# Licensed under the Apache License, Version 2.0 (the "License"); you may
4
# not use this file except in compliance with the License. You may obtain
5
# a copy of the License at
7
# http://www.apache.org/licenses/LICENSE-2.0
9
# Unless required by applicable law or agreed to in writing, software
10
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12
# License for the specific language governing permissions and limitations
15
from oslo.utils import excutils
16
from oslo.utils import importutils
18
from neutron.api.rpc.agentnotifiers import l3_rpc_agent_api
19
from neutron.api.v2 import attributes as attr
20
from neutron.common import exceptions as n_exc
21
from neutron.db import db_base_plugin_v2
22
from neutron.db import extraroute_db
23
from neutron.db import l3_agentschedulers_db
24
from neutron.db import l3_db
25
from neutron.db import l3_gwmode_db
26
from neutron.db import models_v2
27
from neutron.extensions import l3
28
from neutron.i18n import _LE, _LI, _LW
29
from neutron.openstack.common import log as logging
30
from neutron.plugins.nec.common import config
31
from neutron.plugins.nec.common import constants as nconst
32
from neutron.plugins.nec.common import exceptions as nexc
33
from neutron.plugins.nec.db import router as rdb
34
from neutron.plugins.nec.extensions import router_provider as ext_provider
36
LOG = logging.getLogger(__name__)
38
PROVIDER_L3AGENT = nconst.ROUTER_PROVIDER_L3AGENT
39
PROVIDER_OPENFLOW = nconst.ROUTER_PROVIDER_OPENFLOW
41
ROUTER_DRIVER_PATH = 'neutron.plugins.nec.router_drivers.'
43
PROVIDER_L3AGENT: ROUTER_DRIVER_PATH + 'RouterL3AgentDriver',
44
PROVIDER_OPENFLOW: ROUTER_DRIVER_PATH + 'RouterOpenFlowDriver'
49
STATUS_ACTIVE = nconst.ROUTER_STATUS_ACTIVE
50
STATUS_ERROR = nconst.ROUTER_STATUS_ERROR
53
class RouterMixin(extraroute_db.ExtraRoute_db_mixin,
54
l3_gwmode_db.L3_NAT_db_mixin):
56
def create_router(self, context, router):
57
"""Create a new router entry on DB, and create it on OFC."""
58
LOG.debug("RouterMixin.create_router() called, "
59
"router=%s .", router)
60
tenant_id = self._get_tenant_id_for_create(context, router['router'])
62
provider = get_provider_with_default(
63
router['router'].get(ext_provider.ROUTER_PROVIDER))
64
driver = get_driver_by_provider(provider)
66
with context.session.begin(subtransactions=True):
67
new_router = super(RouterMixin, self).create_router(context,
69
new_router['gw_port'] = self._get_gw_port_detail(
70
context, driver, new_router['gw_port_id'])
71
rdb.add_router_provider_binding(context.session,
72
provider, str(new_router['id']))
73
self._extend_router_dict_provider(new_router, provider)
75
# create router on the network controller
77
return driver.create_router(context, tenant_id, new_router)
78
except nexc.RouterOverLimit:
79
with excutils.save_and_reraise_exception():
80
super(RouterMixin, self).delete_router(context,
83
def update_router(self, context, router_id, router):
84
LOG.debug("RouterMixin.update_router() called, "
85
"id=%(id)s, router=%(router)s .",
86
{'id': router_id, 'router': router})
88
with context.session.begin(subtransactions=True):
89
old_rtr = super(RouterMixin, self).get_router(context, router_id)
90
provider = old_rtr[ext_provider.ROUTER_PROVIDER]
91
driver = get_driver_by_provider(provider)
92
old_rtr['gw_port'] = self._get_gw_port_detail(
93
context, driver, old_rtr['gw_port_id'])
94
new_rtr = super(RouterMixin, self).update_router(
95
context, router_id, router)
96
new_rtr['gw_port'] = self._get_gw_port_detail(
97
context, driver, new_rtr['gw_port_id'])
98
driver.update_router(context, router_id, old_rtr, new_rtr)
101
def delete_router(self, context, router_id):
102
LOG.debug("RouterMixin.delete_router() called, id=%s.", router_id)
104
router = super(RouterMixin, self).get_router(context, router_id)
105
tenant_id = router['tenant_id']
106
# Since l3_db.delete_router() has no interaction with the plugin layer,
107
# we need to check if the router can be deleted first.
108
self._check_router_in_use(context, router_id)
109
driver = self._get_router_driver_by_id(context, router_id)
110
# If gw_port exists, remove it.
111
gw_port = self._get_gw_port(context, router_id)
113
driver.delete_interface(context, router_id, gw_port)
114
driver.delete_router(context, router_id, router)
116
super(RouterMixin, self).delete_router(context, router_id)
118
self._cleanup_ofc_tenant(context, tenant_id)
120
def add_router_interface(self, context, router_id, interface_info):
121
LOG.debug("RouterMixin.add_router_interface() called, "
122
"id=%(id)s, interface=%(interface)s.",
123
{'id': router_id, 'interface': interface_info})
124
return super(RouterMixin, self).add_router_interface(
125
context, router_id, interface_info)
127
def remove_router_interface(self, context, router_id, interface_info):
128
LOG.debug("RouterMixin.remove_router_interface() called, "
129
"id=%(id)s, interface=%(interface)s.",
130
{'id': router_id, 'interface': interface_info})
131
return super(RouterMixin, self).remove_router_interface(
132
context, router_id, interface_info)
134
def create_router_port(self, context, port):
135
# This method is called from plugin.create_port()
136
router_id = port['device_id']
137
driver = self._get_router_driver_by_id(context, router_id)
138
port = driver.add_interface(context, router_id, port)
141
def delete_router_port(self, context, port):
142
# This method is called from plugin.delete_port()
143
router_id = port['device_id']
144
driver = self._get_router_driver_by_id(context, router_id)
145
return driver.delete_interface(context, router_id, port)
147
def _get_gw_port_detail(self, context, driver, gw_port_id):
148
if not gw_port_id or not driver.need_gw_info:
150
ctx_elevated = context.elevated()
151
gw_port = self._get_port(ctx_elevated, gw_port_id)
152
# At this moment gw_port has been created, so it is guaranteed
153
# that fixed_ip is assigned for the gw_port.
154
ext_subnet_id = gw_port['fixed_ips'][0]['subnet_id']
155
ext_subnet = self._get_subnet(ctx_elevated, ext_subnet_id)
156
gw_info = {'network_id': gw_port['network_id'],
157
'ip_address': gw_port['fixed_ips'][0]['ip_address'],
158
'mac_address': gw_port['mac_address'],
159
'cidr': ext_subnet['cidr'],
160
'gateway_ip': ext_subnet['gateway_ip']}
163
def _get_gw_port(self, context, router_id):
164
device_filter = {'device_id': [router_id],
165
'device_owner': [l3_db.DEVICE_OWNER_ROUTER_GW]}
166
ports = self.get_ports(context.elevated(), filters=device_filter)
170
def _check_router_in_use(self, context, router_id):
171
with context.session.begin(subtransactions=True):
172
# Ensure that the router is not used
173
router_filter = {'router_id': [router_id]}
174
fips = self.get_floatingips_count(context.elevated(),
175
filters=router_filter)
177
raise l3.RouterInUse(router_id=router_id)
179
device_filter = {'device_id': [router_id],
180
'device_owner': [l3_db.DEVICE_OWNER_ROUTER_INTF]}
181
ports = self.get_ports_count(context.elevated(),
182
filters=device_filter)
184
raise l3.RouterInUse(router_id=router_id)
186
def _get_router_for_floatingip(self, context, internal_port,
188
external_network_id):
189
"""Get a router for a requested floating IP.
191
OpenFlow vrouter does not support NAT, so we need to exclude them
192
from candidate routers for floating IP association.
193
This method is called in l3_db.get_assoc_data().
195
subnet_db = self._get_subnet(context, internal_subnet_id)
196
if not subnet_db['gateway_ip']:
197
msg = (_('Cannot add floating IP to port on subnet %s '
198
'which has no gateway_ip') % internal_subnet_id)
199
raise n_exc.BadRequest(resource='floatingip', msg=msg)
201
# find router interface ports on this network
202
router_intf_qry = context.session.query(models_v2.Port)
203
router_intf_ports = router_intf_qry.filter_by(
204
network_id=internal_port['network_id'],
205
device_owner=l3_db.DEVICE_OWNER_ROUTER_INTF)
207
for intf_p in router_intf_ports:
208
if intf_p['fixed_ips'][0]['subnet_id'] == internal_subnet_id:
209
router_id = intf_p['device_id']
210
router_gw_qry = context.session.query(models_v2.Port)
211
has_gw_port = router_gw_qry.filter_by(
212
network_id=external_network_id,
214
device_owner=l3_db.DEVICE_OWNER_ROUTER_GW).count()
215
driver = self._get_router_driver_by_id(context, router_id)
216
if (has_gw_port and driver.floating_ip_support()):
219
raise l3.ExternalGatewayForFloatingIPNotFound(
220
subnet_id=internal_subnet_id,
221
external_network_id=external_network_id,
222
port_id=internal_port['id'])
224
def _get_sync_routers(self, context, router_ids=None, active=None):
225
"""Query routers and their gw ports for l3 agent.
227
The difference from the superclass in l3_db is that this method
228
only lists routers hosted on l3-agents.
230
router_list = super(RouterMixin, self)._get_sync_routers(
231
context, router_ids, active)
233
_router_ids = [r['id'] for r in router_list]
234
agent_routers = rdb.get_routers_by_provider(
235
context.session, 'l3-agent',
236
router_ids=_router_ids)
237
router_list = [r for r in router_list
238
if r['id'] in agent_routers]
241
def _get_router_driver_by_id(self, context, router_id):
242
provider = self._get_provider_by_router_id(context, router_id)
243
return get_driver_by_provider(provider)
245
def _get_provider_by_router_id(self, context, router_id):
246
return rdb.get_provider_by_router(context.session, router_id)
248
def _extend_router_dict_provider(self, router_res, provider):
249
router_res[ext_provider.ROUTER_PROVIDER] = provider
251
def extend_router_dict_provider(self, router_res, router_db):
252
# NOTE: router_db.provider is None just after creating a router,
253
# so we need to skip setting router_provider here.
254
if not router_db.provider:
256
self._extend_router_dict_provider(router_res,
257
router_db.provider['provider'])
259
db_base_plugin_v2.NeutronDbPluginV2.register_dict_extend_funcs(
260
l3.ROUTERS, [extend_router_dict_provider])
263
class L3AgentSchedulerDbMixin(l3_agentschedulers_db.L3AgentSchedulerDbMixin):
265
def auto_schedule_routers(self, context, host, router_ids):
266
router_ids = rdb.get_routers_by_provider(
267
context.session, nconst.ROUTER_PROVIDER_L3AGENT, router_ids)
268
# If no l3-agent hosted router, there is no need to schedule.
271
return super(L3AgentSchedulerDbMixin, self).auto_schedule_routers(
272
context, host, router_ids)
274
def schedule_router(self, context, router, candidates=None):
275
if (self._get_provider_by_router_id(context, router) ==
276
nconst.ROUTER_PROVIDER_L3AGENT):
277
return super(L3AgentSchedulerDbMixin, self).schedule_router(
278
context, router, candidates=candidates)
280
def add_router_to_l3_agent(self, context, id, router_id):
281
provider = self._get_provider_by_router_id(context, router_id)
282
if provider != nconst.ROUTER_PROVIDER_L3AGENT:
283
raise nexc.RouterProviderMismatch(
284
router_id=router_id, provider=provider,
285
expected_provider=nconst.ROUTER_PROVIDER_L3AGENT)
286
return super(L3AgentSchedulerDbMixin, self).add_router_to_l3_agent(
287
context, id, router_id)
290
class L3AgentNotifyAPI(l3_rpc_agent_api.L3AgentNotifyAPI):
292
def _notification(self, context, method, router_ids, operation,
294
"""Notify all the agents that are hosting the routers.
296
_notification() is called in L3 db plugin for all routers regardless
297
the routers are hosted on l3 agents or not. When the routers are
298
not hosted on l3 agents, there is no need to notify.
299
This method filters routers not hosted by l3 agents.
301
router_ids = rdb.get_routers_by_provider(
302
context.session, nconst.ROUTER_PROVIDER_L3AGENT, router_ids)
303
super(L3AgentNotifyAPI, self)._notification(
304
context, method, router_ids, operation, shuffle_agents)
307
def load_driver(plugin, ofc_manager):
309
if (PROVIDER_OPENFLOW in ROUTER_DRIVER_MAP and
310
not ofc_manager.driver.router_supported):
312
_LW('OFC does not support router with provider=%(provider)s, '
313
'so removed it from supported provider '
314
'(new router driver map=%(driver_map)s)'),
315
{'provider': PROVIDER_OPENFLOW,
316
'driver_map': ROUTER_DRIVER_MAP})
317
del ROUTER_DRIVER_MAP[PROVIDER_OPENFLOW]
319
if config.PROVIDER.default_router_provider not in ROUTER_DRIVER_MAP:
320
LOG.error(_LE('default_router_provider %(default)s is supported! '
321
'Please specify one of %(supported)s'),
322
{'default': config.PROVIDER.default_router_provider,
323
'supported': ROUTER_DRIVER_MAP.keys()})
326
enabled_providers = (set(config.PROVIDER.router_providers +
327
[config.PROVIDER.default_router_provider]) &
328
set(ROUTER_DRIVER_MAP.keys()))
330
for driver in enabled_providers:
331
driver_klass = importutils.import_class(ROUTER_DRIVER_MAP[driver])
332
ROUTER_DRIVERS[driver] = driver_klass(plugin, ofc_manager)
334
LOG.info(_LI('Enabled router drivers: %s'), ROUTER_DRIVERS.keys())
336
if not ROUTER_DRIVERS:
337
LOG.error(_LE('No router provider is enabled. neutron-server '
338
'terminated! (supported=%(supported)s, '
339
'configured=%(config)s)'),
340
{'supported': ROUTER_DRIVER_MAP.keys(),
341
'config': config.PROVIDER.router_providers})
345
def get_provider_with_default(provider):
346
if not attr.is_attr_set(provider):
347
provider = config.PROVIDER.default_router_provider
348
elif provider not in ROUTER_DRIVERS:
349
raise nexc.ProviderNotFound(provider=provider)
353
def get_driver_by_provider(provider):
355
provider = config.PROVIDER.default_router_provider
356
elif provider not in ROUTER_DRIVERS:
357
raise nexc.ProviderNotFound(provider=provider)
358
return ROUTER_DRIVERS[provider]