1
# Copyright 2011 VMware, Inc.
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
8
# http://www.apache.org/licenses/LICENSE-2.0
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
16
from oslo.db import exception as db_exc
18
from sqlalchemy import func
19
from sqlalchemy.orm import exc
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
31
LOG = logging.getLogger(__name__)
34
def get_network_binding(session, network_id):
35
session = session or db.get_session()
37
binding = (session.query(ovs_models_v2.NetworkBinding).
38
filter_by(network_id=network_id).
41
except exc.NoResultFound:
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,
55
def sync_vlan_allocations(network_vlan_ranges):
56
"""Synchronize vlan_allocations table with configured VLAN ranges."""
58
session = db.get_session()
60
# get existing allocations for all physical networks
62
allocs = (session.query(ovs_models_v2.VlanAllocation).
65
if alloc.physical_network not in allocations:
66
allocations[alloc.physical_network] = set()
67
allocations[alloc.physical_network].add(alloc)
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
74
for vlan_range in vlan_ranges:
75
vlan_ids |= set(moves.xrange(vlan_range[0], vlan_range[1] + 1))
77
# remove from table unallocated vlans not currently allocatable
78
if physical_network in allocations:
79
for alloc in allocations[physical_network]:
81
# see if vlan is allocatable
82
vlan_ids.remove(alloc.vlan_id)
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 "
89
"%(physical_network)s from pool"),
90
{'vlan_id': alloc.vlan_id,
91
'physical_network': physical_network})
93
del allocations[physical_network]
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)
100
# remove from table unallocated vlans for any unconfigured physical
102
for allocs in allocations.itervalues():
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)
112
def get_vlan_allocation(physical_network, vlan_id):
113
session = db.get_session()
115
alloc = (session.query(ovs_models_v2.VlanAllocation).
116
filter_by(physical_network=physical_network,
120
except exc.NoResultFound:
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').
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()
140
def reserve_specific_vlan(session, physical_network, vlan_id):
141
with session.begin(subtransactions=True):
143
alloc = (session.query(ovs_models_v2.VlanAllocation).
144
filter_by(physical_network=physical_network,
146
with_lockmode('update').
149
if vlan_id == constants.FLAT_VLAN_ID:
150
raise n_exc.FlatNetworkInUse(
151
physical_network=physical_network)
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"),
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"),
164
'physical_network': physical_network})
165
alloc = ovs_models_v2.VlanAllocation(physical_network, vlan_id)
166
alloc.allocated = True
170
def release_vlan(session, physical_network, vlan_id, network_vlan_ranges):
171
with session.begin(subtransactions=True):
173
alloc = (session.query(ovs_models_v2.VlanAllocation).
174
filter_by(physical_network=physical_network,
176
with_lockmode('update').
178
alloc.allocated = 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]:
185
session.delete(alloc)
186
LOG.debug(_("Releasing vlan %(vlan_id)s on physical network "
187
"%(physical_network)s outside pool"),
189
'physical_network': physical_network})
191
LOG.debug(_("Releasing vlan %(vlan_id)s on physical network "
192
"%(physical_network)s to pool"),
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"),
199
'physical_network': physical_network})
202
def sync_tunnel_allocations(tunnel_id_ranges):
203
"""Synchronize tunnel_allocations table with configured tunnel ranges."""
205
# determine current configured allocatable tunnels
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})
214
tunnel_ids |= set(moves.xrange(tun_min, tun_max + 1))
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).
223
# see if tunnel is allocatable
224
tunnel_ids.remove(alloc.tunnel_id)
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"),
231
session.delete(alloc)
233
# add missing allocatable tunnels to table
234
for tunnel_id in sorted(tunnel_ids):
235
alloc = ovs_models_v2.TunnelAllocation(tunnel_id)
239
def get_tunnel_allocation(tunnel_id):
240
session = db.get_session()
242
alloc = (session.query(ovs_models_v2.TunnelAllocation).
243
filter_by(tunnel_id=tunnel_id).
244
with_lockmode('update').
247
except exc.NoResultFound:
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').
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()
264
def reserve_specific_tunnel(session, tunnel_id):
265
with session.begin(subtransactions=True):
267
alloc = (session.query(ovs_models_v2.TunnelAllocation).
268
filter_by(tunnel_id=tunnel_id).
269
with_lockmode('update').
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"),
278
alloc = ovs_models_v2.TunnelAllocation(tunnel_id)
279
alloc.allocated = True
283
def release_tunnel(session, tunnel_id, tunnel_id_ranges):
284
with session.begin(subtransactions=True):
286
alloc = (session.query(ovs_models_v2.TunnelAllocation).
287
filter_by(tunnel_id=tunnel_id).
288
with_lockmode('update').
290
alloc.allocated = 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]):
298
session.delete(alloc)
299
LOG.debug(_("Releasing tunnel %s outside pool"), tunnel_id)
301
LOG.debug(_("Releasing tunnel %s to pool"), tunnel_id)
302
except exc.NoResultFound:
303
LOG.warning(_("tunnel_id %s not found"), tunnel_id)
306
def get_port(port_id):
307
session = db.get_session()
309
port = session.query(models_v2.Port).filter_by(id=port_id).one()
310
except exc.NoResultFound:
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
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()
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']]
341
def set_port_status(port_id, status):
342
session = db.get_session()
344
port = session.query(models_v2.Port).filter_by(id=port_id).one()
345
port['status'] = status
348
except exc.NoResultFound:
349
raise n_exc.PortNotFound(port_id=port_id)
352
def get_tunnel_endpoints():
353
session = db.get_session()
355
tunnels = session.query(ovs_models_v2.TunnelEndpoint)
356
return [{'id': tunnel.id,
357
'ip_address': tunnel.ip_address} for tunnel in tunnels]
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
366
def add_tunnel_endpoint(ip, max_retries=10):
367
"""Return the endpoint of the given IP address or generate a new one."""
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)
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').
384
tunnel_id = _generate_tunnel_id(session)
385
tunnel = ovs_models_v2.TunnelEndpoint(ip, tunnel_id)
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))
395
raise n_exc.NeutronException(
396
message=_('Unable to generate a new tunnel id'))