24
25
import sqlalchemy as sa
25
26
from sqlalchemy import orm
26
27
from sqlalchemy.orm import exc
28
from sqlalchemy.sql import expression as expr
27
29
import webob.exc as w_exc
29
31
from quantum.api.v2 import attributes
30
32
from quantum.common import exceptions as q_exc
31
33
from quantum.common import utils
34
from quantum.db import db_base_plugin_v2
32
35
from quantum.db import model_base
33
36
from quantum.db import models_v2
34
37
from quantum.extensions import l3
56
59
name = sa.Column(sa.String(255))
57
60
status = sa.Column(sa.String(16))
58
61
admin_state_up = sa.Column(sa.Boolean)
59
gw_port_id = sa.Column(sa.String(36), sa.ForeignKey('ports.id',
62
gw_port_id = sa.Column(sa.String(36), sa.ForeignKey('ports.id'))
61
63
gw_port = orm.relationship(models_v2.Port)
84
86
class L3_NAT_db_mixin(l3.RouterPluginBase):
85
87
"""Mixin class to add L3/NAT router methods to db_plugin_base_v2"""
89
def _network_model_hook(self, context, original_model, query):
90
query = query.outerjoin(ExternalNetwork,
92
ExternalNetwork.network_id))
95
def _network_filter_hook(self, context, original_model, conditions):
96
if conditions is not None and not hasattr(conditions, '__iter__'):
97
conditions = (conditions, )
98
# Apply the external network filter only in non-admin context
99
if not context.is_admin and hasattr(original_model, 'tenant_id'):
100
conditions = expr.or_(ExternalNetwork.network_id != expr.null(),
104
# TODO(salvatore-orlando): Perform this operation without explicitly
105
# referring to db_base_plugin_v2, as plugins that do not extend from it
106
# might exist in the future
107
db_base_plugin_v2.QuantumDbPluginV2.register_model_query_hook(
111
_network_filter_hook)
87
113
def _get_router(self, context, id):
89
115
router = self._get_by_id(context, Router, id)
162
188
with context.session.begin(subtransactions=True):
163
189
router.update({'gw_port_id': None})
164
190
context.session.add(router)
165
self.delete_port(context, gw_port['id'], l3_port_check=False)
191
self.delete_port(context.elevated(), gw_port['id'],
167
194
if network_id is not None and (gw_port is None or
168
195
gw_port['network_id'] != network_id):
169
196
# Port has no 'tenant-id', as it is hidden from user
170
gw_port = self.create_port(context, {
197
gw_port = self.create_port(context.elevated(), {
172
{'network_id': network_id,
199
{'tenant_id': '', # intentionally not set
200
'network_id': network_id,
173
201
'mac_address': attributes.ATTR_NOT_SPECIFIED,
174
202
'fixed_ips': attributes.ATTR_NOT_SPECIFIED,
175
203
'device_id': router_id,
180
208
if not len(gw_port['fixed_ips']):
181
self.delete_port(context, gw_port['id'], l3_port_check=False)
209
self.delete_port(context.elevated(), gw_port['id'],
182
211
msg = ('No IPs available for external network %s' %
184
213
raise q_exc.BadRequest(resource='router', msg=msg)
196
225
ports = self.get_ports(context, filters=device_filter)
198
227
raise l3.RouterInUse(router_id=id)
199
# NOTE(salvatore-orlando): gw port will be automatically deleted
200
# thanks to cascading on the ORM relationship
230
device_filter = {'device_id': [id],
231
'device_owner': [DEVICE_OWNER_ROUTER_GW]}
232
ports = self.get_ports(context.elevated(), filters=device_filter)
234
self._delete_port(context.elevated(), ports[0]['id'])
201
236
context.session.delete(router)
203
238
def get_router(self, context, id, fields=None):
215
250
rport_qry = context.session.query(models_v2.Port)
216
251
rports = rport_qry.filter_by(
217
252
device_id=router_id,
218
device_owner=DEVICE_OWNER_ROUTER_INTF,
219
network_id=network_id).all()
253
device_owner=DEVICE_OWNER_ROUTER_INTF,).all()
220
254
# its possible these ports on on the same network, but
221
255
# different subnet
256
new_cidr = self._get_subnet(context, subnet_id)['cidr']
257
new_ipnet = netaddr.IPNetwork(new_cidr)
223
259
for ip in p['fixed_ips']:
224
260
if ip['subnet_id'] == subnet_id:
225
261
msg = ("Router already has a port on subnet %s"
227
263
raise q_exc.BadRequest(resource='router', msg=msg)
264
cidr = self._get_subnet(context, ip['subnet_id'])['cidr']
265
ipnet = netaddr.IPNetwork(cidr)
266
match1 = netaddr.all_matching_cidrs(new_ipnet, [cidr])
267
match2 = netaddr.all_matching_cidrs(ipnet, [new_cidr])
269
msg = (("Cidr %s of subnet %s is overlapped "
270
+ "with cidr %s of subnet %s")
271
% (new_cidr, subnet_id, cidr, ip['subnet_id']))
272
raise q_exc.BadRequest(resource='router', msg=msg)
229
273
except exc.NoResultFound:
232
276
def add_router_interface(self, context, router_id, interface_info):
233
# make sure router exists - will raise if not
234
self._get_router(context, router_id)
277
# make sure router exists
278
router = self._get_router(context, router_id)
235
279
if not interface_info:
236
280
msg = "Either subnet_id or port_id must be specified"
237
281
raise q_exc.BadRequest(resource='router', msg=msg)
284
policy.enforce(context,
285
"extension:router:add_router_interface",
286
self._make_router_dict(router))
287
except q_exc.PolicyNotAuthorized:
288
raise l3.RouterNotFound(router_id=router_id)
239
290
if 'port_id' in interface_info:
240
291
if 'subnet_id' in interface_info:
241
292
msg = "cannot specify both subnet-id and port-id"
269
320
'subnet_id': subnet['id']}
270
321
port = self.create_port(context, {
272
{'network_id': subnet['network_id'],
323
{'tenant_id': subnet['tenant_id'],
324
'network_id': subnet['network_id'],
273
325
'fixed_ips': [fixed_ip],
274
326
'mac_address': attributes.ATTR_NOT_SPECIFIED,
275
327
'admin_state_up': True,
282
334
def remove_router_interface(self, context, router_id, interface_info):
283
335
# make sure router exists
284
336
router = self._get_router(context, router_id)
338
policy.enforce(context,
339
"extension:router:remove_router_interface",
340
self._make_router_dict(router))
341
except q_exc.PolicyNotAuthorized:
342
raise l3.RouterNotFound(router_id=router_id)
286
344
if not interface_info:
287
345
msg = "Either subnet_id or port_id must be specified"
390
448
internal_port = self._get_port(context, fip['port_id'])
391
449
if not internal_port['tenant_id'] == fip['tenant_id']:
392
msg = ('Port %s is associated with a different tenant'
393
'and therefore cannot be found to floating IP %s'
394
% (fip['port_id'], fip['id']))
395
raise q_exc.BadRequest(resource='floating', msg=msg)
450
port_id = fip['port_id']
452
floatingip_id = fip['id']
453
msg = _('Port %(port_id)s is associated with a different '
454
'tenant than Floating IP %(floatingip_id)s and '
455
'therefore cannot be bound.')
457
msg = _('Cannnot create floating IP and bind it to '
458
'Port %(port_id)s, since that port is owned by a '
460
raise q_exc.BadRequest(resource='floatingip', msg=msg % locals())
397
462
internal_subnet_id = None
398
463
if 'fixed_ip_address' in fip and fip['fixed_ip_address']:
424
489
# confirm that this router has a floating
425
490
# ip enabled gateway with support for this floating IP network
427
port_qry = context.session.query(models_v2.Port)
492
port_qry = context.elevated().session.query(models_v2.Port)
428
493
ports = port_qry.filter_by(
429
494
network_id=floating_network_id,
430
495
device_id=router_id,
439
504
def _update_fip_assoc(self, context, fip, floatingip_db, external_port):
440
505
port_id = internal_ip_address = router_id = None
506
if (('fixed_ip_address' in fip and fip['fixed_ip_address']) and
507
not ('port_id' in fip and fip['port_id'])):
508
msg = "fixed_ip_address cannot be specified without a port_id"
509
raise q_exc.BadRequest(resource='floatingip', msg=msg)
441
510
if 'port_id' in fip and fip['port_id']:
442
511
port_qry = context.session.query(FloatingIP)
467
536
# This external port is never exposed to the tenant.
468
537
# it is used purely for internal system and admin use when
469
538
# managing floating IPs.
470
external_port = self.create_port(context, {
539
external_port = self.create_port(context.elevated(), {
472
{'network_id': f_net_id,
541
{'tenant_id': '', # tenant intentionally not set
542
'network_id': f_net_id,
473
543
'mac_address': attributes.ATTR_NOT_SPECIFIED,
474
544
'fixed_ips': attributes.ATTR_NOT_SPECIFIED,
475
545
'admin_state_up': True,
480
550
if not external_port['fixed_ips']:
481
551
msg = "Unable to find any IP address on external network"
482
552
# remove the external port
483
self.delete_port(context, external_port['id'], l3_port_check=False)
553
self.delete_port(context.elevated(), external_port['id'],
484
555
raise q_exc.BadRequest(resource='floatingip', msg=msg)
486
557
floating_ip_address = external_port['fixed_ips'][0]['ip_address']
500
571
context.session.add(floatingip_db)
501
572
# TODO(salvatore-orlando): Avoid broad catch
502
573
# Maybe by introducing base class for L3 exceptions
574
except q_exc.BadRequest:
575
LOG.exception("Unable to create Floating ip due to a "
503
578
except Exception:
504
579
LOG.exception("Floating IP association failed")
505
580
# Remove the port created for internal purposes
506
self.delete_port(context, external_port['id'], l3_port_check=False)
581
self.delete_port(context.elevated(), external_port['id'],
509
585
return self._make_floatingip_dict(floatingip_db)
517
593
fip_port_id = floatingip_db['floating_port_id']
518
594
self._update_fip_assoc(context, fip, floatingip_db,
519
self.get_port(context, fip_port_id))
595
self.get_port(context.elevated(),
520
597
return self._make_floatingip_dict(floatingip_db)
522
599
def delete_floatingip(self, context, id):
523
600
floatingip = self._get_floatingip(context, id)
524
601
with context.session.begin(subtransactions=True):
525
602
context.session.delete(floatingip)
526
self.delete_port(context, floatingip['floating_port_id'],
603
self.delete_port(context.elevated(),
604
floatingip['floating_port_id'],
527
605
l3_port_check=False)
529
607
def get_floatingip(self, context, id, fields=None):
586
664
def _extend_network_dict_l3(self, context, network):
587
665
if self._check_l3_view_auth(context, network):
588
network['router:external'] = self._network_is_external(
666
network[l3.EXTERNAL] = self._network_is_external(
589
667
context, network['id'])
591
669
def _process_l3_create(self, context, net_data, net_id):
592
external = net_data.get('router:external')
670
external = net_data.get(l3.EXTERNAL)
593
671
external_set = attributes.is_attr_set(external)
595
673
if not external_set:
604
682
def _process_l3_update(self, context, net_data, net_id):
606
new_value = net_data.get('router:external')
684
new_value = net_data.get(l3.EXTERNAL)
607
685
if not attributes.is_attr_set(new_value):
623
701
context.session.query(models_v2.Port).filter_by(
624
702
device_owner=DEVICE_OWNER_ROUTER_GW,
625
703
network_id=net_id).first()
626
raise ExternalNetworkInUse(net_id=net_id)
704
raise l3.ExternalNetworkInUse(net_id=net_id)
627
705
except exc.NoResultFound: