~niedbalski/ubuntu/vivid/neutron/fixes-1447803

« back to all changes in this revision

Viewing changes to neutron/plugins/openvswitch/ovs_db_v2.py

  • Committer: Package Import Robot
  • Author(s): James Page
  • Date: 2014-10-03 18:45:23 UTC
  • mfrom: (1.1.15)
  • Revision ID: package-import@ubuntu.com-20141003184523-4mt6dy1q3j8n30c9
Tags: 1:2014.2~rc1-0ubuntu1
* New upstream release candidate:
  - d/p/*: Refreshed.
  - d/control: Add python-requests-mock to BD's.
  - d/control: Align versioned requirements with upstream.
* Transition linuxbridge and openvswitch plugin users to modular
  layer 2 plugin (LP: #1323729):
  - d/control: Mark removed plugin packages as transitional, depend
    on neutron-plugin-ml2, mark oldlibs/extra.
  - d/neutron-plugin-{linuxbridge,openvswitch}.install: Drop.
  - d/control: Depend on neutron-plugin-ml2 for linuxbridge
    agent package.
  - d/neutron-plugin-linuxbridge-agent.upstart: Use ml2 plugin
    configuration files.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright 2011 VMware, Inc.
2
 
# All Rights Reserved.
3
 
#
4
 
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
5
 
#    not use this file except in compliance with the License. You may obtain
6
 
#    a copy of the License at
7
 
#
8
 
#         http://www.apache.org/licenses/LICENSE-2.0
9
 
#
10
 
#    Unless required by applicable law or agreed to in writing, software
11
 
#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12
 
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13
 
#    License for the specific language governing permissions and limitations
14
 
#    under the License.
15
 
 
16
 
from oslo.db import exception as db_exc
17
 
from six import moves
18
 
from sqlalchemy import func
19
 
from sqlalchemy.orm import exc
20
 
 
21
 
from neutron.common import exceptions as n_exc
22
 
import neutron.db.api as db
23
 
from neutron.db import models_v2
24
 
from neutron.db import securitygroups_db as sg_db
25
 
from neutron.extensions import securitygroup as ext_sg
26
 
from neutron import manager
27
 
from neutron.openstack.common import log as logging
28
 
from neutron.plugins.openvswitch.common import constants
29
 
from neutron.plugins.openvswitch import ovs_models_v2
30
 
 
31
 
LOG = logging.getLogger(__name__)
32
 
 
33
 
 
34
 
def get_network_binding(session, network_id):
35
 
    session = session or db.get_session()
36
 
    try:
37
 
        binding = (session.query(ovs_models_v2.NetworkBinding).
38
 
                   filter_by(network_id=network_id).
39
 
                   one())
40
 
        return binding
41
 
    except exc.NoResultFound:
42
 
        return
43
 
 
44
 
 
45
 
def add_network_binding(session, network_id, network_type,
46
 
                        physical_network, segmentation_id):
47
 
    with session.begin(subtransactions=True):
48
 
        binding = ovs_models_v2.NetworkBinding(network_id, network_type,
49
 
                                               physical_network,
50
 
                                               segmentation_id)
51
 
        session.add(binding)
52
 
    return binding
53
 
 
54
 
 
55
 
def sync_vlan_allocations(network_vlan_ranges):
56
 
    """Synchronize vlan_allocations table with configured VLAN ranges."""
57
 
 
58
 
    session = db.get_session()
59
 
    with session.begin():
60
 
        # get existing allocations for all physical networks
61
 
        allocations = dict()
62
 
        allocs = (session.query(ovs_models_v2.VlanAllocation).
63
 
                  all())
64
 
        for alloc in allocs:
65
 
            if alloc.physical_network not in allocations:
66
 
                allocations[alloc.physical_network] = set()
67
 
            allocations[alloc.physical_network].add(alloc)
68
 
 
69
 
        # process vlan ranges for each configured physical network
70
 
        for physical_network, vlan_ranges in network_vlan_ranges.iteritems():
71
 
            # determine current configured allocatable vlans for this
72
 
            # physical network
73
 
            vlan_ids = set()
74
 
            for vlan_range in vlan_ranges:
75
 
                vlan_ids |= set(moves.xrange(vlan_range[0], vlan_range[1] + 1))
76
 
 
77
 
            # remove from table unallocated vlans not currently allocatable
78
 
            if physical_network in allocations:
79
 
                for alloc in allocations[physical_network]:
80
 
                    try:
81
 
                        # see if vlan is allocatable
82
 
                        vlan_ids.remove(alloc.vlan_id)
83
 
                    except KeyError:
84
 
                        # it's not allocatable, so check if its allocated
85
 
                        if not alloc.allocated:
86
 
                            # it's not, so remove it from table
87
 
                            LOG.debug(_("Removing vlan %(vlan_id)s on "
88
 
                                        "physical network "
89
 
                                        "%(physical_network)s from pool"),
90
 
                                      {'vlan_id': alloc.vlan_id,
91
 
                                       'physical_network': physical_network})
92
 
                            session.delete(alloc)
93
 
                del allocations[physical_network]
94
 
 
95
 
            # add missing allocatable vlans to table
96
 
            for vlan_id in sorted(vlan_ids):
97
 
                alloc = ovs_models_v2.VlanAllocation(physical_network, vlan_id)
98
 
                session.add(alloc)
99
 
 
100
 
        # remove from table unallocated vlans for any unconfigured physical
101
 
        # networks
102
 
        for allocs in allocations.itervalues():
103
 
            for alloc in allocs:
104
 
                if not alloc.allocated:
105
 
                    LOG.debug(_("Removing vlan %(vlan_id)s on physical "
106
 
                                "network %(physical_network)s from pool"),
107
 
                              {'vlan_id': alloc.vlan_id,
108
 
                               'physical_network': alloc.physical_network})
109
 
                    session.delete(alloc)
110
 
 
111
 
 
112
 
def get_vlan_allocation(physical_network, vlan_id):
113
 
    session = db.get_session()
114
 
    try:
115
 
        alloc = (session.query(ovs_models_v2.VlanAllocation).
116
 
                 filter_by(physical_network=physical_network,
117
 
                           vlan_id=vlan_id).
118
 
                 one())
119
 
        return alloc
120
 
    except exc.NoResultFound:
121
 
        return
122
 
 
123
 
 
124
 
def reserve_vlan(session):
125
 
    with session.begin(subtransactions=True):
126
 
        alloc = (session.query(ovs_models_v2.VlanAllocation).
127
 
                 filter_by(allocated=False).
128
 
                 with_lockmode('update').
129
 
                 first())
130
 
        if alloc:
131
 
            LOG.debug(_("Reserving vlan %(vlan_id)s on physical network "
132
 
                        "%(physical_network)s from pool"),
133
 
                      {'vlan_id': alloc.vlan_id,
134
 
                       'physical_network': alloc.physical_network})
135
 
            alloc.allocated = True
136
 
            return (alloc.physical_network, alloc.vlan_id)
137
 
    raise n_exc.NoNetworkAvailable()
138
 
 
139
 
 
140
 
def reserve_specific_vlan(session, physical_network, vlan_id):
141
 
    with session.begin(subtransactions=True):
142
 
        try:
143
 
            alloc = (session.query(ovs_models_v2.VlanAllocation).
144
 
                     filter_by(physical_network=physical_network,
145
 
                               vlan_id=vlan_id).
146
 
                     with_lockmode('update').
147
 
                     one())
148
 
            if alloc.allocated:
149
 
                if vlan_id == constants.FLAT_VLAN_ID:
150
 
                    raise n_exc.FlatNetworkInUse(
151
 
                        physical_network=physical_network)
152
 
                else:
153
 
                    raise n_exc.VlanIdInUse(vlan_id=vlan_id,
154
 
                                            physical_network=physical_network)
155
 
            LOG.debug(_("Reserving specific vlan %(vlan_id)s on physical "
156
 
                        "network %(physical_network)s from pool"),
157
 
                      {'vlan_id': vlan_id,
158
 
                       'physical_network': physical_network})
159
 
            alloc.allocated = True
160
 
        except exc.NoResultFound:
161
 
            LOG.debug(_("Reserving specific vlan %(vlan_id)s on physical "
162
 
                        "network %(physical_network)s outside pool"),
163
 
                      {'vlan_id': vlan_id,
164
 
                       'physical_network': physical_network})
165
 
            alloc = ovs_models_v2.VlanAllocation(physical_network, vlan_id)
166
 
            alloc.allocated = True
167
 
            session.add(alloc)
168
 
 
169
 
 
170
 
def release_vlan(session, physical_network, vlan_id, network_vlan_ranges):
171
 
    with session.begin(subtransactions=True):
172
 
        try:
173
 
            alloc = (session.query(ovs_models_v2.VlanAllocation).
174
 
                     filter_by(physical_network=physical_network,
175
 
                               vlan_id=vlan_id).
176
 
                     with_lockmode('update').
177
 
                     one())
178
 
            alloc.allocated = False
179
 
            inside = False
180
 
            for vlan_range in network_vlan_ranges.get(physical_network, []):
181
 
                if vlan_id >= vlan_range[0] and vlan_id <= vlan_range[1]:
182
 
                    inside = True
183
 
                    break
184
 
            if not inside:
185
 
                session.delete(alloc)
186
 
                LOG.debug(_("Releasing vlan %(vlan_id)s on physical network "
187
 
                            "%(physical_network)s outside pool"),
188
 
                          {'vlan_id': vlan_id,
189
 
                           'physical_network': physical_network})
190
 
            else:
191
 
                LOG.debug(_("Releasing vlan %(vlan_id)s on physical network "
192
 
                            "%(physical_network)s to pool"),
193
 
                          {'vlan_id': vlan_id,
194
 
                           'physical_network': physical_network})
195
 
        except exc.NoResultFound:
196
 
            LOG.warning(_("vlan_id %(vlan_id)s on physical network "
197
 
                          "%(physical_network)s not found"),
198
 
                        {'vlan_id': vlan_id,
199
 
                         'physical_network': physical_network})
200
 
 
201
 
 
202
 
def sync_tunnel_allocations(tunnel_id_ranges):
203
 
    """Synchronize tunnel_allocations table with configured tunnel ranges."""
204
 
 
205
 
    # determine current configured allocatable tunnels
206
 
    tunnel_ids = set()
207
 
    for tunnel_id_range in tunnel_id_ranges:
208
 
        tun_min, tun_max = tunnel_id_range
209
 
        if tun_max + 1 - tun_min > 1000000:
210
 
            LOG.error(_("Skipping unreasonable tunnel ID range "
211
 
                        "%(tun_min)s:%(tun_max)s"),
212
 
                      {'tun_min': tun_min, 'tun_max': tun_max})
213
 
        else:
214
 
            tunnel_ids |= set(moves.xrange(tun_min, tun_max + 1))
215
 
 
216
 
    session = db.get_session()
217
 
    with session.begin():
218
 
        # remove from table unallocated tunnels not currently allocatable
219
 
        allocs = (session.query(ovs_models_v2.TunnelAllocation).
220
 
                  all())
221
 
        for alloc in allocs:
222
 
            try:
223
 
                # see if tunnel is allocatable
224
 
                tunnel_ids.remove(alloc.tunnel_id)
225
 
            except KeyError:
226
 
                # it's not allocatable, so check if its allocated
227
 
                if not alloc.allocated:
228
 
                    # it's not, so remove it from table
229
 
                    LOG.debug(_("Removing tunnel %s from pool"),
230
 
                              alloc.tunnel_id)
231
 
                    session.delete(alloc)
232
 
 
233
 
        # add missing allocatable tunnels to table
234
 
        for tunnel_id in sorted(tunnel_ids):
235
 
            alloc = ovs_models_v2.TunnelAllocation(tunnel_id)
236
 
            session.add(alloc)
237
 
 
238
 
 
239
 
def get_tunnel_allocation(tunnel_id):
240
 
    session = db.get_session()
241
 
    try:
242
 
        alloc = (session.query(ovs_models_v2.TunnelAllocation).
243
 
                 filter_by(tunnel_id=tunnel_id).
244
 
                 with_lockmode('update').
245
 
                 one())
246
 
        return alloc
247
 
    except exc.NoResultFound:
248
 
        return
249
 
 
250
 
 
251
 
def reserve_tunnel(session):
252
 
    with session.begin(subtransactions=True):
253
 
        alloc = (session.query(ovs_models_v2.TunnelAllocation).
254
 
                 filter_by(allocated=False).
255
 
                 with_lockmode('update').
256
 
                 first())
257
 
        if alloc:
258
 
            LOG.debug(_("Reserving tunnel %s from pool"), alloc.tunnel_id)
259
 
            alloc.allocated = True
260
 
            return alloc.tunnel_id
261
 
    raise n_exc.NoNetworkAvailable()
262
 
 
263
 
 
264
 
def reserve_specific_tunnel(session, tunnel_id):
265
 
    with session.begin(subtransactions=True):
266
 
        try:
267
 
            alloc = (session.query(ovs_models_v2.TunnelAllocation).
268
 
                     filter_by(tunnel_id=tunnel_id).
269
 
                     with_lockmode('update').
270
 
                     one())
271
 
            if alloc.allocated:
272
 
                raise n_exc.TunnelIdInUse(tunnel_id=tunnel_id)
273
 
            LOG.debug(_("Reserving specific tunnel %s from pool"), tunnel_id)
274
 
            alloc.allocated = True
275
 
        except exc.NoResultFound:
276
 
            LOG.debug(_("Reserving specific tunnel %s outside pool"),
277
 
                      tunnel_id)
278
 
            alloc = ovs_models_v2.TunnelAllocation(tunnel_id)
279
 
            alloc.allocated = True
280
 
            session.add(alloc)
281
 
 
282
 
 
283
 
def release_tunnel(session, tunnel_id, tunnel_id_ranges):
284
 
    with session.begin(subtransactions=True):
285
 
        try:
286
 
            alloc = (session.query(ovs_models_v2.TunnelAllocation).
287
 
                     filter_by(tunnel_id=tunnel_id).
288
 
                     with_lockmode('update').
289
 
                     one())
290
 
            alloc.allocated = False
291
 
            inside = False
292
 
            for tunnel_id_range in tunnel_id_ranges:
293
 
                if (tunnel_id >= tunnel_id_range[0]
294
 
                    and tunnel_id <= tunnel_id_range[1]):
295
 
                    inside = True
296
 
                    break
297
 
            if not inside:
298
 
                session.delete(alloc)
299
 
                LOG.debug(_("Releasing tunnel %s outside pool"), tunnel_id)
300
 
            else:
301
 
                LOG.debug(_("Releasing tunnel %s to pool"), tunnel_id)
302
 
        except exc.NoResultFound:
303
 
            LOG.warning(_("tunnel_id %s not found"), tunnel_id)
304
 
 
305
 
 
306
 
def get_port(port_id):
307
 
    session = db.get_session()
308
 
    try:
309
 
        port = session.query(models_v2.Port).filter_by(id=port_id).one()
310
 
    except exc.NoResultFound:
311
 
        port = None
312
 
    return port
313
 
 
314
 
 
315
 
def get_port_from_device(port_id):
316
 
    """Get port from database."""
317
 
    LOG.debug(_("get_port_with_securitygroups() called:port_id=%s"), port_id)
318
 
    session = db.get_session()
319
 
    sg_binding_port = sg_db.SecurityGroupPortBinding.port_id
320
 
 
321
 
    query = session.query(models_v2.Port,
322
 
                          sg_db.SecurityGroupPortBinding.security_group_id)
323
 
    query = query.outerjoin(sg_db.SecurityGroupPortBinding,
324
 
                            models_v2.Port.id == sg_binding_port)
325
 
    query = query.filter(models_v2.Port.id == port_id)
326
 
    port_and_sgs = query.all()
327
 
    if not port_and_sgs:
328
 
        return None
329
 
    port = port_and_sgs[0][0]
330
 
    plugin = manager.NeutronManager.get_plugin()
331
 
    port_dict = plugin._make_port_dict(port)
332
 
    port_dict[ext_sg.SECURITYGROUPS] = [
333
 
        sg_id for port_, sg_id in port_and_sgs if sg_id]
334
 
    port_dict['security_group_rules'] = []
335
 
    port_dict['security_group_source_groups'] = []
336
 
    port_dict['fixed_ips'] = [ip['ip_address']
337
 
                              for ip in port['fixed_ips']]
338
 
    return port_dict
339
 
 
340
 
 
341
 
def set_port_status(port_id, status):
342
 
    session = db.get_session()
343
 
    try:
344
 
        port = session.query(models_v2.Port).filter_by(id=port_id).one()
345
 
        port['status'] = status
346
 
        session.merge(port)
347
 
        session.flush()
348
 
    except exc.NoResultFound:
349
 
        raise n_exc.PortNotFound(port_id=port_id)
350
 
 
351
 
 
352
 
def get_tunnel_endpoints():
353
 
    session = db.get_session()
354
 
 
355
 
    tunnels = session.query(ovs_models_v2.TunnelEndpoint)
356
 
    return [{'id': tunnel.id,
357
 
             'ip_address': tunnel.ip_address} for tunnel in tunnels]
358
 
 
359
 
 
360
 
def _generate_tunnel_id(session):
361
 
    max_tunnel_id = session.query(
362
 
        func.max(ovs_models_v2.TunnelEndpoint.id)).scalar() or 0
363
 
    return max_tunnel_id + 1
364
 
 
365
 
 
366
 
def add_tunnel_endpoint(ip, max_retries=10):
367
 
    """Return the endpoint of the given IP address or generate a new one."""
368
 
 
369
 
    # NOTE(rpodolyaka): generation of a new tunnel endpoint must be put into a
370
 
    #                   repeatedly executed transactional block to ensure it
371
 
    #                   doesn't conflict with any other concurrently executed
372
 
    #                   DB transactions in spite of the specified transactions
373
 
    #                   isolation level value
374
 
    for i in moves.xrange(max_retries):
375
 
        LOG.debug(_('Adding a tunnel endpoint for %s'), ip)
376
 
        try:
377
 
            session = db.get_session()
378
 
            with session.begin(subtransactions=True):
379
 
                tunnel = (session.query(ovs_models_v2.TunnelEndpoint).
380
 
                          filter_by(ip_address=ip).with_lockmode('update').
381
 
                          first())
382
 
 
383
 
                if tunnel is None:
384
 
                    tunnel_id = _generate_tunnel_id(session)
385
 
                    tunnel = ovs_models_v2.TunnelEndpoint(ip, tunnel_id)
386
 
                    session.add(tunnel)
387
 
 
388
 
                return tunnel
389
 
        except db_exc.DBDuplicateEntry:
390
 
            # a concurrent transaction has been committed, try again
391
 
            LOG.debug(_('Adding a tunnel endpoint failed due to a concurrent'
392
 
                        'transaction had been committed (%s attempts left)'),
393
 
                      max_retries - (i + 1))
394
 
 
395
 
    raise n_exc.NeutronException(
396
 
        message=_('Unable to generate a new tunnel id'))