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