neutron/neutron/ipam/drivers/neutrondb_ipam/db_api.py

254 lines
9.5 KiB
Python

# Copyright 2015 OpenStack LLC.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from oslo_db import exception as db_exc
from oslo_log import log
from oslo_utils import uuidutils
from sqlalchemy.orm import exc as orm_exc
from neutron.ipam.drivers.neutrondb_ipam import db_models
from neutron.ipam import exceptions as ipam_exc
LOG = log.getLogger(__name__)
# Database operations for Neutron's DB-backed IPAM driver
class IpamSubnetManager(object):
@classmethod
def load_by_neutron_subnet_id(cls, session, neutron_subnet_id):
return session.query(db_models.IpamSubnet).filter_by(
neutron_subnet_id=neutron_subnet_id).first()
def __init__(self, ipam_subnet_id, neutron_subnet_id):
self._ipam_subnet_id = ipam_subnet_id
self._neutron_subnet_id = neutron_subnet_id
@property
def neutron_id(self):
return self._neutron_subnet_id
def create(self, session):
"""Create database models for an IPAM subnet.
This method creates a subnet resource for the IPAM driver and
associates it with its neutron identifier, if specified.
:param session: database sesssion.
:returns: the idenfier of created IPAM subnet
"""
if not self._ipam_subnet_id:
self._ipam_subnet_id = uuidutils.generate_uuid()
ipam_subnet = db_models.IpamSubnet(
id=self._ipam_subnet_id,
neutron_subnet_id=self._neutron_subnet_id)
session.add(ipam_subnet)
return self._ipam_subnet_id
@classmethod
def delete(cls, session, neutron_subnet_id):
"""Delete IPAM subnet.
IPAM subnet no longer has foreign key to neutron subnet,
so need to perform delete manually
:param session: database sesssion
:param neutron_subnet_id: neutron subnet id associated with ipam subnet
"""
return session.query(db_models.IpamSubnet).filter_by(
neutron_subnet_id=neutron_subnet_id).delete()
def create_pool(self, session, pool_start, pool_end):
"""Create an allocation pool and availability ranges for the subnet.
This method does not perform any validation on parameters; it simply
persist data on the database.
:param pool_start: string expressing the start of the pool
:param pool_end: string expressing the end of the pool
:return: the newly created pool object.
"""
ip_pool = db_models.IpamAllocationPool(
ipam_subnet_id=self._ipam_subnet_id,
first_ip=pool_start,
last_ip=pool_end)
session.add(ip_pool)
ip_range = db_models.IpamAvailabilityRange(
allocation_pool=ip_pool,
first_ip=pool_start,
last_ip=pool_end)
session.add(ip_range)
return ip_pool
def delete_allocation_pools(self, session):
"""Remove all allocation pools for the current subnet.
:param session: database session
"""
session.query(db_models.IpamAllocationPool).filter_by(
ipam_subnet_id=self._ipam_subnet_id).delete()
def list_pools(self, session):
"""Return pools for the current subnet."""
return session.query(
db_models.IpamAllocationPool).filter_by(
ipam_subnet_id=self._ipam_subnet_id)
def _range_query(self, session):
return session.query(
db_models.IpamAvailabilityRange).join(
db_models.IpamAllocationPool).filter_by(
ipam_subnet_id=self._ipam_subnet_id)
def get_first_range(self, session):
"""Return the first availability range for the subnet
:param session: database session
:return: first available range as instance of
neutron.ipam.drivers.neutrondb_ipam.db_models.IpamAvailabilityRange
"""
return self._range_query(session).first()
def list_ranges_by_subnet_id(self, session):
"""Return availability ranges for a given ipam subnet
:param session: database session
:return: list of availability ranges as instances of
neutron.ipam.drivers.neutrondb_ipam.db_models.IpamAvailabilityRange
"""
return self._range_query(session)
def list_ranges_by_allocation_pool(self, session, allocation_pool_id):
"""Return availability ranges for a given pool.
:param session: database session
:param allocation_pool_id: allocation pool identifier
:return: list of availability ranges as instances of
neutron.ipam.drivers.neutrondb_ipam.db_models.IpamAvailabilityRange
"""
return session.query(
db_models.IpamAvailabilityRange).join(
db_models.IpamAllocationPool).filter_by(
id=allocation_pool_id)
def update_range(self, session, db_range, first_ip=None, last_ip=None):
"""Updates db_range to have new first_ip and last_ip.
:param session: database session
:param db_range: IpamAvailabilityRange db object
:param first_ip: first ip address in range
:param last_ip: last ip address in range
:return: count of updated rows
"""
opts = {}
if first_ip:
opts['first_ip'] = str(first_ip)
if last_ip:
opts['last_ip'] = str(last_ip)
if not opts:
raise ipam_exc.IpamAvailabilityRangeNoChanges()
try:
return session.query(
db_models.IpamAvailabilityRange).filter_by(
allocation_pool_id=db_range.allocation_pool_id).filter_by(
first_ip=db_range.first_ip).filter_by(
last_ip=db_range.last_ip).update(opts)
except orm_exc.ObjectDeletedError:
raise db_exc.RetryRequest(ipam_exc.IPAllocationFailed)
def delete_range(self, session, db_range):
"""Return count of deleted ranges
:param session: database session
:param db_range: IpamAvailabilityRange db object
"""
try:
return session.query(
db_models.IpamAvailabilityRange).filter_by(
allocation_pool_id=db_range.allocation_pool_id).filter_by(
first_ip=db_range.first_ip).filter_by(
last_ip=db_range.last_ip).delete()
except orm_exc.ObjectDeletedError:
raise db_exc.RetryRequest(ipam_exc.IPAllocationFailed)
def create_range(self, session, allocation_pool_id,
range_start, range_end):
"""Create an availability range for a given pool.
This method does not perform any validation on parameters; it simply
persist data on the database.
:param session: database session
:param allocation_pool_id: allocation pool identifier
:param range_start: first ip address in the range
:param range_end: last ip address in the range
:return: the newly created availability range as an instance of
neutron.ipam.drivers.neutrondb_ipam.db_models.IpamAvailabilityRange
"""
new_ip_range = db_models.IpamAvailabilityRange(
allocation_pool_id=allocation_pool_id,
first_ip=range_start,
last_ip=range_end)
session.add(new_ip_range)
return new_ip_range
def check_unique_allocation(self, session, ip_address):
"""Validate that the IP address on the subnet is not in use."""
iprequest = session.query(db_models.IpamAllocation).filter_by(
ipam_subnet_id=self._ipam_subnet_id, status='ALLOCATED',
ip_address=ip_address).first()
if iprequest:
return False
return True
def list_allocations(self, session, status='ALLOCATED'):
"""Return current allocations for the subnet.
:param session: database session
:param status: IP allocation status
:returns: a list of IP allocation as instance of
neutron.ipam.drivers.neutrondb_ipam.db_models.IpamAllocation
"""
return session.query(
db_models.IpamAllocation).filter_by(
ipam_subnet_id=self._ipam_subnet_id,
status=status)
def create_allocation(self, session, ip_address,
status='ALLOCATED'):
"""Create an IP allocation entry.
:param session: database session
:param ip_address: the IP address to allocate
:param status: IP allocation status
"""
ip_request = db_models.IpamAllocation(
ip_address=ip_address,
status=status,
ipam_subnet_id=self._ipam_subnet_id)
session.add(ip_request)
def delete_allocation(self, session, ip_address):
"""Remove an IP allocation for this subnet.
:param session: database session
:param ip_address: IP address for which the allocation entry should
be removed.
"""
return session.query(db_models.IpamAllocation).filter_by(
ip_address=ip_address,
ipam_subnet_id=self._ipam_subnet_id).delete(
synchronize_session=False)