1 # vim: tabstop=4 shiftwidth=4 softtabstop=4
2 # Copyright 2011 Nicira Networks, Inc.
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
9 # http://www.apache.org/licenses/LICENSE-2.0
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
16 # @author: Aaron Rosen, Nicira Networks, Inc.
17 # @author: Bob Kukura, Red Hat, Inc.
19 from sqlalchemy import func
20 from sqlalchemy.orm import exc
22 from neutron.common import exceptions as q_exc
23 import neutron.db.api as db
24 from neutron.db import models_v2
25 from neutron.db import securitygroups_db as sg_db
26 from neutron.extensions import securitygroup as ext_sg
27 from neutron import manager
28 from neutron.openstack.common.db import exception as db_exc
29 from neutron.openstack.common import log as logging
30 from neutron.plugins.openvswitch.common import constants
31 from neutron.plugins.openvswitch import ovs_models_v2
33 LOG = logging.getLogger(__name__)
40 def get_network_binding(session, network_id):
41 session = session or db.get_session()
43 binding = (session.query(ovs_models_v2.NetworkBinding).
44 filter_by(network_id=network_id).
47 except exc.NoResultFound:
51 def 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,
59 def get_port_forwarding(session, port_id):
60 session = session or db.get_session()
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:
68 def clear_port_forwarding(session, port_id):
69 with session.begin(subtransactions=True):
71 # Get rid of old port bindings
72 forward = (session.query(ovs_models_v2.PortForwarding).
73 filter_by(port_id=port_id).one())
75 session.delete(forward)
76 except exc.NoResultFound:
79 def add_port_forwarding(session, port_id, forward_ports):
80 with session.begin(subtransactions=True):
81 forward = ovs_models_v2.PortForwarding(port_id, forward_ports)
84 def sync_vlan_allocations(network_vlan_ranges):
85 """Synchronize vlan_allocations table with configured VLAN ranges."""
87 session = db.get_session()
89 # get existing allocations for all physical networks
91 allocs = (session.query(ovs_models_v2.VlanAllocation).
94 if alloc.physical_network not in allocations:
95 allocations[alloc.physical_network] = set()
96 allocations[alloc.physical_network].add(alloc)
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
103 for vlan_range in vlan_ranges:
104 vlan_ids |= set(xrange(vlan_range[0], vlan_range[1] + 1))
106 # remove from table unallocated vlans not currently allocatable
107 if physical_network in allocations:
108 for alloc in allocations[physical_network]:
110 # see if vlan is allocatable
111 vlan_ids.remove(alloc.vlan_id)
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 "
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]
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)
129 # remove from table unallocated vlans for any unconfigured physical
131 for allocs in allocations.itervalues():
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)
141 def get_vlan_allocation(physical_network, vlan_id):
142 session = db.get_session()
144 alloc = (session.query(ovs_models_v2.VlanAllocation).
145 filter_by(physical_network=physical_network,
149 except exc.NoResultFound:
153 def 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').
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()
169 def reserve_specific_vlan(session, physical_network, vlan_id):
170 with session.begin(subtransactions=True):
172 alloc = (session.query(ovs_models_v2.VlanAllocation).
173 filter_by(physical_network=physical_network,
175 with_lockmode('update').
178 if vlan_id == constants.FLAT_VLAN_ID:
179 raise q_exc.FlatNetworkInUse(
180 physical_network=physical_network)
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"),
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"),
193 'physical_network': physical_network})
194 alloc = ovs_models_v2.VlanAllocation(physical_network, vlan_id)
195 alloc.allocated = True
199 def release_vlan(session, physical_network, vlan_id, network_vlan_ranges):
200 with session.begin(subtransactions=True):
202 alloc = (session.query(ovs_models_v2.VlanAllocation).
203 filter_by(physical_network=physical_network,
205 with_lockmode('update').
207 alloc.allocated = 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]:
214 session.delete(alloc)
215 LOG.debug(_("Releasing vlan %(vlan_id)s on physical network "
216 "%(physical_network)s outside pool"),
218 'physical_network': physical_network})
220 LOG.debug(_("Releasing vlan %(vlan_id)s on physical network "
221 "%(physical_network)s to pool"),
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"),
228 'physical_network': physical_network})
231 def sync_tunnel_allocations(tunnel_id_ranges):
232 """Synchronize tunnel_allocations table with configured tunnel ranges."""
234 # determine current configured allocatable tunnels
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})
243 tunnel_ids |= set(xrange(tun_min, tun_max + 1))
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).
252 # see if tunnel is allocatable
253 tunnel_ids.remove(alloc.tunnel_id)
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"),
260 session.delete(alloc)
262 # add missing allocatable tunnels to table
263 for tunnel_id in sorted(tunnel_ids):
264 alloc = ovs_models_v2.TunnelAllocation(tunnel_id)
268 def get_tunnel_allocation(tunnel_id):
269 session = db.get_session()
271 alloc = (session.query(ovs_models_v2.TunnelAllocation).
272 filter_by(tunnel_id=tunnel_id).
273 with_lockmode('update').
276 except exc.NoResultFound:
280 def 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').
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()
293 def reserve_specific_tunnel(session, tunnel_id):
294 with session.begin(subtransactions=True):
296 alloc = (session.query(ovs_models_v2.TunnelAllocation).
297 filter_by(tunnel_id=tunnel_id).
298 with_lockmode('update').
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"),
307 alloc = ovs_models_v2.TunnelAllocation(tunnel_id)
308 alloc.allocated = True
312 def release_tunnel(session, tunnel_id, tunnel_id_ranges):
313 with session.begin(subtransactions=True):
315 alloc = (session.query(ovs_models_v2.TunnelAllocation).
316 filter_by(tunnel_id=tunnel_id).
317 with_lockmode('update').
319 alloc.allocated = 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]):
327 session.delete(alloc)
328 LOG.debug(_("Releasing tunnel %s outside pool"), tunnel_id)
330 LOG.debug(_("Releasing tunnel %s to pool"), tunnel_id)
331 except exc.NoResultFound:
332 LOG.warning(_("tunnel_id %s not found"), tunnel_id)
335 def get_port(port_id):
336 session = db.get_session()
338 port = session.query(models_v2.Port).filter_by(id=port_id).one()
339 except exc.NoResultFound:
344 def 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
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()
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']]
370 def set_port_status(port_id, status):
371 session = db.get_session()
373 port = session.query(models_v2.Port).filter_by(id=port_id).one()
374 port['status'] = status
377 except exc.NoResultFound:
378 raise q_exc.PortNotFound(port_id=port_id)
381 def get_tunnel_endpoints():
382 session = db.get_session()
384 tunnels = session.query(ovs_models_v2.TunnelEndpoint)
385 return [{'id': tunnel.id,
386 'ip_address': tunnel.ip_address} for tunnel in tunnels]
389 def _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
395 def add_tunnel_endpoint(ip, max_retries=10):
396 """Return the endpoint of the given IP address or generate a new one."""
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)
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').
413 tunnel_id = _generate_tunnel_id(session)
414 tunnel = ovs_models_v2.TunnelEndpoint(ip, tunnel_id)
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))
424 raise q_exc.NeutronException(
425 message=_('Unable to generate a new tunnel id'))