~ubuntu-cloud-archive/ubuntu/precise/quantum/folsom

« back to all changes in this revision

Viewing changes to quantum/db/l3_db.py

  • Committer: Package Import Robot
  • Author(s): Chuck Short, Adam Gandelman, Chuck Short
  • Date: 2012-09-12 13:41:20 UTC
  • mfrom: (2.1.12)
  • Revision ID: package-import@ubuntu.com-20120912134120-sheqxy7c2q5x7m34
Tags: 2012.2~rc1-0ubuntu1
[ Adam Gandelman ]
* debain/*.postrm: Fix argument-less calls to update-rc.d, redirect
  to /dev/null.  (LP: #1047560)
* debian/quantum-server.upstart: Invoke start-stop-daemon properly.
  (LP: #1047404)
* debain/*.postrm, *.upstart: Ensure files are named for corresponding
  agent package, not plugin package.
* debian/control:
  - Group agents with plugins.
  - Fix some copy/paste mistakes.
  - Set dependencies between agents and corresponding plugins.
  - Recommend quantum-plugin-openvswitch for quantum-server.
  - Require the same version of quantum-common and python-quantum.
  - Add quantum-netns-cleanup utility to quantum-common.
* debian/patches/fix-quantum-configuration.patch: Use correct database
  for linuxbridge plugin, use OVS plugin by default, call quantum-rootwrap
  correctly. (LP: #1048668)
* Fix all use of /usr/sbin, things should go in /usr/bin.
* Remove dhcp and l3 plugins, they are not actually plugins.
* Rename packages quantum-plugin-{l3, dhcp}-agent to
  quantum-{l3, dhcp}-agent.
* debain/quantum-*-agent.upstart: Specify config files as a
  parameter to --config-file, specify log files for all.
* debian/*.logrotate: Add logrotate configs for server and agents.
* Install quantum_sudoers with quantum-common, not quantum-server.
* Install rootwrap filters only with the packages that require them.
* debian/*-agent.upstart: Specify --config-file=/etc/quantum/quantum.conf
  in addition to plugin-specific config.  Specify log files for all agents.
* Allow group 'adm' read access to /var/log/quantum.
* debian/quantum-server.postinst: Drop, all has been moved to quantum-common.
* Add packaging for quantum-plugin-nec.

[ Chuck Short ]
* New usptream release.

Show diffs side-by-side

added added

removed removed

Lines of Context:
21
21
 
22
22
import logging
23
23
 
 
24
import netaddr
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
28
30
 
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',
60
 
                                                        ondelete="CASCADE"))
 
62
    gw_port_id = sa.Column(sa.String(36), sa.ForeignKey('ports.id'))
61
63
    gw_port = orm.relationship(models_v2.Port)
62
64
 
63
65
 
84
86
class L3_NAT_db_mixin(l3.RouterPluginBase):
85
87
    """Mixin class to add L3/NAT router methods to db_plugin_base_v2"""
86
88
 
 
89
    def _network_model_hook(self, context, original_model, query):
 
90
        query = query.outerjoin(ExternalNetwork,
 
91
                                (original_model.id ==
 
92
                                 ExternalNetwork.network_id))
 
93
        return query
 
94
 
 
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(),
 
101
                                  *conditions)
 
102
        return conditions
 
103
 
 
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(
 
108
        models_v2.Network,
 
109
        "external_net",
 
110
        _network_model_hook,
 
111
        _network_filter_hook)
 
112
 
87
113
    def _get_router(self, context, id):
88
114
        try:
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'],
 
192
                             l3_port_check=False)
166
193
 
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(), {
171
198
                'port':
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,
178
206
                 'name': ''}})
179
207
 
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'],
 
210
                                 l3_port_check=False)
182
211
                msg = ('No IPs available for external network %s' %
183
212
                       network_id)
184
213
                raise q_exc.BadRequest(resource='router', msg=msg)
196
225
            ports = self.get_ports(context, filters=device_filter)
197
226
            if ports:
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
 
228
 
 
229
            # delete any gw port
 
230
            device_filter = {'device_id': [id],
 
231
                             'device_owner': [DEVICE_OWNER_ROUTER_GW]}
 
232
            ports = self.get_ports(context.elevated(), filters=device_filter)
 
233
            if ports:
 
234
                self._delete_port(context.elevated(), ports[0]['id'])
 
235
 
201
236
            context.session.delete(router)
202
237
 
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)
222
258
            for p in rports:
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"
226
262
                               % subnet_id)
227
263
                        raise q_exc.BadRequest(resource='router', msg=msg)
228
 
 
 
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])
 
268
                    if match1 or match2:
 
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:
230
274
            pass
231
275
 
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)
238
282
 
 
283
        try:
 
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)
 
289
 
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, {
271
322
                'port':
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)
 
337
        try:
 
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)
285
343
 
286
344
        if not interface_info:
287
345
            msg = "Either subnet_id or port_id must be specified"
389
447
        """
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']
 
451
            if 'id' in fip:
 
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.')
 
456
            else:
 
457
                msg = _('Cannnot create floating IP and bind it to '
 
458
                        'Port %(port_id)s, since that port is owned by a '
 
459
                        'different tenant.')
 
460
            raise q_exc.BadRequest(resource='floatingip', msg=msg % locals())
396
461
 
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
426
491
        try:
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,
438
503
 
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)
443
512
            try:
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(), {
471
540
            'port':
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'],
 
554
                             l3_port_check=False)
484
555
            raise q_exc.BadRequest(resource='floatingip', msg=msg)
485
556
 
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 "
 
576
                          "malformed request")
 
577
            raise
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'],
 
582
                             l3_port_check=False)
507
583
            raise
508
584
 
509
585
        return self._make_floatingip_dict(floatingip_db)
516
592
            fip['id'] = id
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(),
 
596
                                                 fip_port_id))
520
597
        return self._make_floatingip_dict(floatingip_db)
521
598
 
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)
528
606
 
529
607
    def get_floatingip(self, context, id, fields=None):
585
663
 
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'])
590
668
 
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)
594
672
 
595
673
        if not external_set:
603
681
 
604
682
    def _process_l3_update(self, context, net_data, net_id):
605
683
 
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):
608
686
            return
609
687
 
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:
628
706
                pass  # expected
629
707