mogan/mogan/db/sqlalchemy/api.py

1027 lines
39 KiB
Python

# Copyright 2016 Huawei Technologies Co.,LTD.
# 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.
"""SQLAlchemy storage backend."""
import threading
from oslo_db import exception as db_exc
from oslo_db.sqlalchemy import enginefacade
from oslo_db.sqlalchemy import utils as sqlalchemyutils
from oslo_log import log as logging
from oslo_utils import strutils
from oslo_utils import timeutils
from oslo_utils import uuidutils
from sqlalchemy import or_
from sqlalchemy.orm.exc import NoResultFound
from sqlalchemy.orm import joinedload
from sqlalchemy.sql.expression import desc
from sqlalchemy.sql import true
from mogan.common import exception
from mogan.common.i18n import _
from mogan.db import api
from mogan.db.sqlalchemy import models
_CONTEXT = threading.local()
LOG = logging.getLogger(__name__)
def get_backend():
"""The backend is this module itself."""
return Connection()
def _session_for_read():
return enginefacade.reader.using(_CONTEXT)
def _session_for_write():
return enginefacade.writer.using(_CONTEXT)
def model_query(context, model, *args, **kwargs):
"""Query helper for simpler session usage.
:param context: Context of the query
:param model: Model to query. Must be a subclass of ModelBase.
:param args: Arguments to query. If None - model is used.
Keyword arguments:
:keyword project_only:
If set to True, then will do query filter with context's project_id.
if set to False or absent, then will not do query filter with context's
project_id.
:type project_only: bool
"""
if kwargs.pop("project_only", False):
kwargs["project_id"] = context.tenant
with _session_for_read() as session:
query = sqlalchemyutils.model_query(
model, session, args, **kwargs)
return query
def add_identity_filter(query, value):
"""Adds an identity filter to a query.
Filters results by ID, if supplied value is a valid integer.
Otherwise attempts to filter results by UUID.
:param query: Initial query to add filter to.
:param value: Value for filtering results by.
:return: Modified query.
"""
if strutils.is_int_like(value):
return query.filter_by(id=value)
elif uuidutils.is_uuid_like(value):
return query.filter_by(uuid=value)
else:
raise exception.InvalidParameterValue(identity=value)
def _dict_with_extra_fields(flavor_query):
"""Takes a server type query and returns it as a dictionary."""
flavor_dict = dict(flavor_query)
# extra specs
extra_specs = {x['key']: x['value']
for x in flavor_query['extra_specs']}
flavor_dict['extra_specs'] = extra_specs
# cpus
cpus = {}
for c in flavor_query['cpus']:
cpus = {'model': c['model'], 'cores': c['cores']}
flavor_dict['cpus'] = cpus
# memory
memory = {}
for m in flavor_query['memory']:
memory = {'type': m['type'], 'size_mb': m['size_mb']}
flavor_dict['memory'] = memory
# nics
nics = []
for n in flavor_query['nics']:
nic = {'type': n['type'], 'speed': n['speed']}
nics.append(nic)
flavor_dict['nics'] = nics
# disks
disks = []
for d in flavor_query['disks']:
disk = {'type': d['type'], 'size_gb': d['size_gb']}
disks.append(disk)
flavor_dict['disks'] = disks
return flavor_dict
class Connection(api.Connection):
"""SqlAlchemy connection."""
def __init__(self):
self.QUOTA_SYNC_FUNCTIONS = {'_sync_servers': self._sync_servers}
pass
def flavor_create(self, context, values):
if not values.get('uuid'):
values['uuid'] = uuidutils.generate_uuid()
if not values.get('description'):
values['description'] = ""
cpus = values.pop('cpus', None)
memory = values.pop('memory', None)
nics = values.pop('nics', [])
disks = values.pop('disks', [])
flavor = models.Flavors()
flavor.update(values)
with _session_for_write() as session:
try:
session.add(flavor)
# add flavor cpus
if cpus:
flavor_cpus = models.FlavorCpus()
cpus['flavor_uuid'] = values['uuid']
flavor_cpus.update(cpus)
session.add(flavor_cpus)
# add flavor memory
if memory:
flavor_mem = models.FlavorMemory()
memory['flavor_uuid'] = values['uuid']
flavor_mem.update(memory)
session.add(flavor_mem)
# add flavor nics
for nic in nics:
flavor_nic = models.FlavorNics()
nic['flavor_uuid'] = values['uuid']
flavor_nic.update(nic)
session.add(flavor_nic)
# add flavor disks
for disk in disks:
flavor_disk = models.FlavorDisks()
disk['flavor_uuid'] = values['uuid']
flavor_disk.update(disk)
session.add(flavor_disk)
session.flush()
except db_exc.DBDuplicateEntry:
raise exception.FlavorAlreadyExists(uuid=values['uuid'])
return _dict_with_extra_fields(flavor)
def flavor_get(self, context, flavor_uuid):
query = model_query(context, models.Flavors).filter_by(
uuid=flavor_uuid).options(joinedload('extra_specs'))
if not context.is_admin:
the_filter = [models.Flavors.is_public == true()]
the_filter.extend([
models.Flavors.projects.has(project_id=context.project_id)
])
query = query.filter(or_(*the_filter))
try:
return _dict_with_extra_fields(query.one())
except NoResultFound:
raise exception.FlavorNotFound(
type_id=flavor_uuid)
def flavor_update(self, context, flavor_id, values):
with _session_for_write():
query = model_query(context, models.Flavors)
query = add_identity_filter(query, flavor_id)
try:
ref = query.with_lockmode('update').one()
except NoResultFound:
raise exception.FlavorNotFound(
type_id=flavor_id)
ref.update(values)
return ref
def flavor_get_all(self, context):
query = model_query(context, models.Flavors)
if not context.is_admin:
the_filter = [models.Flavors.is_public == true()]
the_filter.extend([
models.Flavors.projects.has(project_id=context.project_id)
])
query = query.filter(or_(*the_filter))
return [_dict_with_extra_fields(i) for i in query.all()]
def flavor_destroy(self, context, flavor_uuid):
with _session_for_write():
# First clean up all extra specs related to this type
type_id = _type_get_id_from_type(context, flavor_uuid)
extra_query = model_query(
context,
models.FlavorExtraSpecs).filter_by(
flavor_uuid=type_id)
extra_query.delete()
# Clean up cpus related to this flavor
cpus_query = model_query(
context,
models.FlavorCpus).filter_by(
flavor_uuid=type_id)
cpus_query.delete()
# Clean up memory related to this flavor
memory_query = model_query(
context,
models.FlavorMemory).filter_by(
flavor_uuid=type_id)
memory_query.delete()
# Clean up nics related to this flavor
nics_query = model_query(
context,
models.FlavorNics).filter_by(
flavor_uuid=type_id)
nics_query.delete()
# Clean up disks related to this flavor
disks_query = model_query(
context,
models.FlavorDisks).filter_by(
flavor_uuid=type_id)
disks_query.delete()
# Clean up all access related to this flavor
project_query = model_query(
context,
models.FlavorProjects).filter_by(
flavor_uuid=type_id)
project_query.delete()
# Then delete the type record
query = model_query(context, models.Flavors)
query = add_identity_filter(query, flavor_uuid)
count = query.delete()
if count != 1:
raise exception.FlavorNotFound(
type_id=flavor_uuid)
def server_create(self, context, values):
if not values.get('uuid'):
values['uuid'] = uuidutils.generate_uuid()
server_nics = values.pop('nics', [])
server = models.Server()
server.update(values)
nic_refs = []
for nic in server_nics:
nic_ref = models.ServerNic()
nic_ref.update(nic)
nic_refs.append(nic_ref)
with _session_for_write() as session:
try:
session.add(server)
for nic_r in nic_refs:
session.add(nic_r)
session.flush()
except db_exc.DBDuplicateEntry:
raise exception.ServerAlreadyExists(name=values['name'])
return server
def server_get(self, context, server_id):
query = model_query(
context,
models.Server,
server=True).filter_by(uuid=server_id)
try:
return query.one()
except NoResultFound:
raise exception.ServerNotFound(server=server_id)
def server_get_all(self, context, project_only):
return model_query(context, models.Server,
server=True, project_only=project_only)
def server_destroy(self, context, server_id):
with _session_for_write():
query = model_query(context, models.Server)
query = add_identity_filter(query, server_id)
nics_query = model_query(context, models.ServerNic).filter_by(
server_uuid=server_id)
nics_query.delete()
faults_query = model_query(
context,
models.ServerFault).filter_by(server_uuid=server_id)
faults_query.delete()
count = query.delete()
if count != 1:
raise exception.ServerNotFound(server=server_id)
def server_update(self, context, server_id, values):
if 'uuid' in values:
msg = _("Cannot overwrite UUID for an existing Server.")
raise exception.InvalidParameterValue(err=msg)
try:
return self._do_update_server(context, server_id, values)
except db_exc.DBDuplicateEntry as e:
if 'name' in e.columns:
raise exception.DuplicateName(name=values['name'])
def _do_update_server(self, context, server_id, values):
with _session_for_write():
query = model_query(context, models.Server, server=True)
query = add_identity_filter(query, server_id)
try:
ref = query.with_lockmode('update').one()
except NoResultFound:
raise exception.ServerNotFound(server=server_id)
ref.update(values)
return ref
def compute_node_create(self, context, values):
compute_node = models.ComputeNode()
compute_node.update(values)
with _session_for_write() as session:
try:
session.add(compute_node)
session.flush()
except db_exc.DBDuplicateEntry:
raise exception.ComputeNodeAlreadyExists(
node=values['node_uuid'])
return compute_node
def compute_node_get(self, context, node_uuid):
query = model_query(
context,
models.ComputeNode).filter_by(node_uuid=node_uuid). \
options(joinedload('ports'))
try:
return query.one()
except NoResultFound:
raise exception.ComputeNodeNotFound(node=node_uuid)
def compute_node_get_all(self, context):
return model_query(
context,
models.ComputeNode).options(joinedload('ports'))
def compute_node_get_all_available(self, context):
return model_query(
context,
models.ComputeNode).filter_by(used=False). \
options(joinedload('ports'))
def compute_node_destroy(self, context, node_uuid):
with _session_for_write():
query = model_query(
context,
models.ComputeNode).filter_by(node_uuid=node_uuid)
port_query = model_query(context, models.ComputePort).filter_by(
node_uuid=node_uuid)
port_query.delete()
disk_query = model_query(context, models.ComputeDisk).filter_by(
node_uuid=node_uuid)
disk_query.delete()
count = query.delete()
if count != 1:
raise exception.ComputeNodeNotFound(node=node_uuid)
def compute_node_update(self, context, node_uuid, values):
with _session_for_write():
query = model_query(
context,
models.ComputeNode).filter_by(node_uuid=node_uuid)
try:
ref = query.with_lockmode('update').one()
except NoResultFound:
raise exception.ComputeNodeNotFound(node=node_uuid)
ref.update(values)
return ref
def compute_port_create(self, context, values):
compute_port = models.ComputePort()
compute_port.update(values)
with _session_for_write() as session:
try:
session.add(compute_port)
session.flush()
except db_exc.DBDuplicateEntry:
raise exception.ComputePortAlreadyExists(
port=values['port_uuid'])
return compute_port
def compute_port_get(self, context, port_uuid):
query = model_query(
context,
models.ComputePort).filter_by(port_uuid=port_uuid)
try:
return query.one()
except NoResultFound:
raise exception.ComputePortNotFound(port=port_uuid)
def compute_port_get_all(self, context):
return model_query(context, models.ComputePort)
def compute_port_get_by_node_uuid(self, context, node_uuid):
return model_query(context, models.ComputePort).filter_by(
node_uuid=node_uuid).all()
def compute_port_destroy(self, context, port_uuid):
with _session_for_write():
query = model_query(
context,
models.ComputePort).filter_by(port_uuid=port_uuid)
count = query.delete()
if count != 1:
raise exception.ComputePortNotFound(port=port_uuid)
def compute_port_update(self, context, port_uuid, values):
with _session_for_write():
query = model_query(
context,
models.ComputePort).filter_by(port_uuid=port_uuid)
try:
ref = query.with_lockmode('update').one()
except NoResultFound:
raise exception.ComputePortNotFound(port=port_uuid)
ref.update(values)
return ref
def compute_disk_create(self, context, values):
compute_disk = models.ComputeDisk()
compute_disk.update(values)
with _session_for_write() as session:
try:
session.add(compute_disk)
session.flush()
except db_exc.DBDuplicateEntry:
raise exception.ComputeDiskAlreadyExists(
disk=values['disk_uuid'])
return compute_disk
def compute_disk_get(self, context, disk_uuid):
query = model_query(
context,
models.ComputeDisk).filter_by(disk_uuid=disk_uuid)
try:
return query.one()
except NoResultFound:
raise exception.ComputeDiskNotFound(disk=disk_uuid)
def compute_disk_get_all(self, context):
return model_query(context, models.ComputeDisk)
def compute_disk_get_by_node_uuid(self, context, node_uuid):
return model_query(context, models.ComputeDisk).filter_by(
node_uuid=node_uuid).all()
def compute_disk_destroy(self, context, disk_uuid):
with _session_for_write():
query = model_query(
context,
models.ComputeDisk).filter_by(disk_uuid=disk_uuid)
count = query.delete()
if count != 1:
raise exception.ComputeDiskNotFound(disk=disk_uuid)
def compute_disk_update(self, context, disk_uuid, values):
with _session_for_write():
query = model_query(
context,
models.ComputeDisk).filter_by(disk_uuid=disk_uuid)
try:
ref = query.with_lockmode('update').one()
except NoResultFound:
raise exception.ComputeDiskNotFound(disk=disk_uuid)
ref.update(values)
return ref
def extra_specs_update_or_create(self, context,
flavor_uuid, specs,
max_retries=10):
"""Create or update server type extra specs.
This adds or modifies the key/value pairs specified in the
extra specs dict argument
"""
for attempt in range(max_retries):
with _session_for_write() as session:
try:
spec_refs = model_query(
context, models.FlavorExtraSpecs). \
filter_by(flavor_uuid=flavor_uuid). \
filter(models.FlavorExtraSpecs.key.in_(
specs.keys())).with_lockmode('update').all()
existing_keys = set()
for spec_ref in spec_refs:
key = spec_ref["key"]
existing_keys.add(key)
spec_ref.update({"value": specs[key]})
for key, value in specs.items():
if key in existing_keys:
continue
spec_ref = models.FlavorExtraSpecs()
spec_ref.update(
{"key": key, "value": value,
"flavor_uuid": flavor_uuid})
session.add(spec_ref)
session.flush()
return specs
except db_exc.DBDuplicateEntry:
# a concurrent transaction has been committed,
# try again unless this was the last attempt
if attempt == max_retries - 1:
raise exception.FlavorExtraSpecUpdateCreateFailed(
id=flavor_uuid, retries=max_retries)
def flavor_extra_specs_get(self, context, type_id):
rows = _type_extra_specs_get_query(context, type_id).all()
return {row['key']: row['value'] for row in rows}
def type_extra_specs_delete(self, context, type_id, key):
result = _type_extra_specs_get_query(context, type_id). \
filter(models.FlavorExtraSpecs.key == key). \
delete(synchronize_session=False)
# did not find the extra spec
if result == 0:
raise exception.FlavorExtraSpecsNotFound(
extra_specs_key=key, flavor_id=type_id)
def flavor_access_get(self, context, flavor_id):
return _flavor_access_query(context, flavor_id)
def flavor_access_add(self, context, flavor_id, project_id):
access_ref = models.FlavorProjects()
access_ref.update({"flavor_uuid": flavor_id,
"project_id": project_id})
with _session_for_write() as session:
try:
session.add(access_ref)
session.flush()
except db_exc.DBDuplicateEntry:
raise exception.FlavorAccessExists(flavor_id=flavor_id,
project_id=project_id)
return access_ref
def flavor_access_remove(self, context, flavor_id, project_id):
count = _flavor_access_query(context, flavor_id). \
filter_by(project_id=project_id). \
delete(synchronize_session=False)
if count == 0:
raise exception.FlavorAccessNotFound(flavor_id=flavor_id,
project_id=project_id)
def server_nic_update_or_create(self, context, port_id, values):
with _session_for_write() as session:
query = model_query(context, models.ServerNic).filter_by(
port_id=port_id)
nic = query.first()
if not nic:
nic = models.ServerNic()
values.update(port_id=port_id)
nic.update(values)
session.add(nic)
session.flush()
return nic
def server_nics_get_by_server_uuid(self, context, server_uuid):
return model_query(context, models.ServerNic).filter_by(
server_uuid=server_uuid).all()
def server_fault_create(self, context, values):
"""Create a new ServerFault."""
fault = models.ServerFault()
fault.update(values)
with _session_for_write() as session:
session.add(fault)
session.flush()
return fault
def server_fault_get_by_server_uuids(self, context, server_uuids):
"""Get all server faults for the provided server_uuids."""
if not server_uuids:
return {}
rows = model_query(context, models.ServerFault).\
filter(models.ServerFault.server_uuid.in_(server_uuids)).\
order_by(desc("created_at"), desc("id")).all()
output = {}
for server_uuid in server_uuids:
output[server_uuid] = []
for row in rows:
data = dict(row)
output[row['server_uuid']].append(data)
return output
def quota_get(self, context, project_id, resource_name):
query = model_query(
context,
models.Quota).filter_by(project_id=project_id,
resource_name=resource_name)
try:
return query.one()
except NoResultFound:
raise exception.QuotaNotFound(quota_name=resource_name)
def quota_create(self, context, values):
quota = models.Quota()
quota.update(values)
with _session_for_write() as session:
try:
session.add(quota)
session.flush()
except db_exc.DBDuplicateEntry:
project_id = values['project_id']
raise exception.QuotaAlreadyExists(name=values['name'],
project_id=project_id)
return quota
def quota_get_all(self, context, project_only):
return model_query(context, models.Quota, project_only=project_only)
def quota_destroy(self, context, project_id, resource_name):
with _session_for_write():
query = model_query(context, models.Quota)
query = query.filter_by(project_id=project_id,
resource_name=resource_name)
count = query.delete()
if count != 1:
raise exception.QuotaNotFound(quota_name=resource_name)
def _do_update_quota(self, context, project_id, resource_name, updates):
with _session_for_write():
query = model_query(context, models.Quota)
query = query.filter_by(project_id=project_id,
resource_name=resource_name)
try:
ref = query.with_lockmode('update').one()
except NoResultFound:
raise exception.QuotaNotFound(quota_name=resource_name)
ref.update(updates)
return ref
def quota_update(self, context, project_id, resource_name, updates):
if 'resource_name' in updates or 'project_id' in updates:
msg = _("Cannot overwrite resource_name/project_id for "
"an existing Quota.")
raise exception.InvalidParameterValue(err=msg)
try:
return self._do_update_quota(context, project_id, resource_name,
updates)
except db_exc.DBDuplicateEntry:
pass
def quota_get_all_by_project(self, context, project_id):
return model_query(context, models.Quota, project_id=project_id)
def quota_usage_get_all_by_project(self, context, project_id):
rows = model_query(context, models.QuotaUsage,
project_id=project_id)
result = {'project_id': project_id}
for row in rows:
result[row.resource_name] = dict(in_use=row.in_use,
reserved=row.reserved)
return result
def quota_allocated_get_all_by_project(self, context, project_id):
rows = model_query(context, models.Quota,
project_id=project_id)
result = {'project_id': project_id}
for row in rows:
result[row.resource_name] = row.allocated
return result
def _get_quota_usages(self, context, project_id):
# Broken out for testability
rows = model_query(context, models.QuotaUsage,
project_id=project_id).\
order_by(models.QuotaUsage.id.asc()).\
with_lockmode('update').all()
return {row.resource_name: row for row in rows}
def quota_allocated_update(self, context, project_id, resource, allocated):
with _session_for_write():
quota_ref = self.quota_get(context, project_id, resource)
quota_ref.update({'allocated': allocated})
return quota_ref
def _quota_usage_create(self, context, project_id, resource, in_use,
reserved, until_refresh, session=None):
quota_usage_ref = models.QuotaUsage()
quota_usage_ref.project_id = project_id
quota_usage_ref.resource_name = resource
quota_usage_ref.in_use = in_use
quota_usage_ref.reserved = reserved
quota_usage_ref.until_refresh = until_refresh
try:
session.add(quota_usage_ref)
session.flush()
except db_exc.DBDuplicateEntry:
raise exception.QuotaAlreadyExists(name=resource,
project_id=project_id)
return quota_usage_ref
def _reservation_create(self, context, uuid, usage, project_id, resource,
delta, expire, session=None, allocated_id=None):
usage_id = usage['id'] if usage else None
reservation_ref = models.Reservation()
reservation_ref.uuid = uuid
reservation_ref.usage_id = usage_id
reservation_ref.project_id = project_id
reservation_ref.resource_name = resource
reservation_ref.delta = delta
reservation_ref.expire = expire
reservation_ref.allocated_id = allocated_id
try:
session.add(reservation_ref)
session.flush()
except db_exc.DBDuplicateEntry:
raise exception.ReservationAlreadyExists(name=resource,
project_id=project_id)
return reservation_ref
def _sync_servers(self, context, project_id):
query = model_query(context, models.Server, server=True).\
filter_by(project_id=project_id).all()
return {'servers': len(query) or 0}
def quota_reserve(self, context, resources, quotas, deltas, expire,
until_refresh, max_age, project_id,
is_allocated_reserve=False):
# NOTE(wanghao): Now we still doesn't support contenxt.elevated() yet.
# We can support it later.
elevated = context
with _session_for_write() as session:
if project_id is None:
project_id = context.project_id
# Get the current usages
usages = self._get_quota_usages(context, project_id)
allocated = self.quota_allocated_get_all_by_project(context,
project_id)
allocated.pop('project_id')
# Handle usage refresh
work = set(deltas.keys())
while work:
resource = work.pop()
# Do we need to refresh the usage?
refresh = False
if resource not in usages:
usages[resource] = self._quota_usage_create(
elevated, project_id, resource, 0, 0,
until_refresh or None, session=session)
refresh = True
elif usages[resource].in_use < 0:
refresh = True
elif usages[resource].until_refresh is not None:
usages[resource].until_refresh -= 1
if usages[resource].until_refresh <= 0:
refresh = True
elif max_age and usages[resource].updated_at is not None and (
(usages[resource].updated_at -
timeutils.utcnow()).seconds >= max_age):
refresh = True
# OK, refresh the usage
if refresh:
# Grab the sync routine
sync = self.QUOTA_SYNC_FUNCTIONS[resources[resource].sync]
updates = sync(elevated, project_id)
for res, in_use in updates.items():
# Make sure we have a destination for the usage!
if res not in usages:
usages[res] = self._quota_usage_create(
elevated, project_id, res, 0, 0,
until_refresh or None, session=session)
# Update the usage
usages[res].in_use = in_use
usages[res].until_refresh = until_refresh or None
# Because more than one resource may be refreshed
# by the call to the sync routine, and we don't
# want to double-sync, we make sure all refreshed
# resources are dropped from the work set.
work.discard(res)
# Check for deltas that would go negative
if is_allocated_reserve:
unders = [r for r, delta in deltas.items()
if delta < 0 and delta + allocated.get(r, 0) < 0]
else:
unders = [r for r, delta in deltas.items()
if delta < 0 and delta + usages[r].in_use < 0]
# Now, let's check the quotas
overs = [r for r, delta in deltas.items()
if quotas[r] >= 0 and delta >= 0 and
quotas[r] < delta + usages[r].total + allocated.get(r, 0)]
# Create the reservations
if not overs:
reservations = []
for resource, delta in deltas.items():
usage = usages[resource]
allocated_id = None
if is_allocated_reserve:
try:
quota = self.quota_get(context, project_id,
resource)
except exception.ProjectQuotaNotFound:
# If we were using the default quota, create DB
# entry
quota = self.quota_create(context,
project_id,
resource,
quotas[resource], 0)
# Since there's no reserved/total for allocated, update
# allocated immediately and subtract on rollback
# if needed
self.quota_allocated_update(context, project_id,
resource,
quota.allocated + delta)
allocated_id = quota.id
usage = None
reservation = self._reservation_create(
elevated, uuidutils.generate_uuid(), usage, project_id,
resource, delta, expire, session=session,
allocated_id=allocated_id)
reservations.append(reservation)
# Also update the reserved quantity
if delta > 0 and not is_allocated_reserve:
usages[resource].reserved += delta
if unders:
LOG.warning("Change will make usage less than 0 for the "
"following resources: %s", unders)
if overs:
usages = {k: dict(in_use=v.in_use, reserved=v.reserved,
allocated=allocated.get(k, 0))
for k, v in usages.items()}
raise exception.OverQuota(overs=sorted(overs), quotas=quotas,
usages=usages)
return reservations
def _dict_with_usage_id(self, usages):
return {row.id: row for row in usages.values()}
def reservation_commit(self, context, reservations, project_id):
with _session_for_write():
usages = self._get_quota_usages(context, project_id)
usages = self._dict_with_usage_id(usages)
for reservation in reservations:
# Allocated reservations will have already been bumped
if not reservation.allocated_id:
usage = usages[reservation.usage_id]
if reservation.delta >= 0:
usage.reserved -= reservation.delta
usage.in_use += reservation.delta
query = model_query(context, models.Reservation)
query = query.filter_by(uuid=reservation.uuid)
count = query.delete()
if count != 1:
raise exception.ReservationNotFound(uuid=reservation.uuid)
def reservation_rollback(self, context, reservations, project_id):
with _session_for_write():
usages = self._get_quota_usages(context, project_id)
usages = self._dict_with_usage_id(usages)
for reservation in reservations:
if reservation.allocated_id:
reservation.quota.allocated -= reservation.delta
else:
usage = usages[reservation.usage_id]
if reservation.delta >= 0:
usage.reserved -= reservation.delta
query = model_query(context, models.Reservation)
query = query.filter_by(uuid=reservation.uuid)
count = query.delete()
if count != 1:
raise exception.ReservationNotFound(uuid=reservation.uuid)
def reservation_expire(self, context):
with _session_for_write() as session:
current_time = timeutils.utcnow()
results = model_query(context, models.Reservation).\
filter(models.Reservation.expire < current_time).\
all()
if results:
for reservation in results:
if reservation.delta >= 0:
if reservation.allocated_id:
reservation.quota.allocated -= reservation.delta
reservation.quota.save(session=session)
else:
reservation.usage.reserved -= reservation.delta
reservation.usage.save(session=session)
query = model_query(context, models.Reservation)
query = query.filter_by(uuid=reservation.uuid)
count = query.delete()
if count != 1:
uuid = reservation.uuid
raise exception.ReservationNotFound(uuid=uuid)
def key_pair_create(self, context, values):
key_pair_ref = models.KeyPair()
key_pair_ref.update(values)
with _session_for_write() as session:
try:
session.add(key_pair_ref)
session.flush()
except db_exc.DBDuplicateEntry:
raise exception.KeyPairExists(key_name=values['name'])
return key_pair_ref
def key_pair_destroy(self, context, user_id, name):
result = model_query(context, models.KeyPair).filter_by(
user_id=user_id).filter_by(name=name).delete()
if not result:
raise exception.KeypairNotFound(user_id=user_id, name=name)
def key_pair_get(self, context, user_id, name):
result = model_query(context, models.KeyPair).filter_by(
user_id=user_id).filter_by(name=name).first()
if not result:
raise exception.KeypairNotFound(user_id=user_id, name=name)
return result
def key_pair_get_all_by_user(self, context, user_id):
query = model_query(context, models.KeyPair).filter_by(user_id=user_id)
return query.all()
def key_pair_count_by_user(self, context, user_id):
return model_query(context, models.KeyPair).filter_by(
user_id=user_id).count()
def _type_get_id_from_type_query(context, type_id):
return model_query(context, models.Flavors). \
filter_by(uuid=type_id)
def _type_get_id_from_type(context, type_id):
result = _type_get_id_from_type_query(context, type_id).first()
if not result:
raise exception.FlavorNotFound(type_id=type_id)
return result.uuid
def _type_extra_specs_get_query(context, type_id):
return model_query(context, models.FlavorExtraSpecs). \
filter_by(flavor_uuid=type_id)
def _flavor_access_query(context, flavor_id):
return model_query(context, models.FlavorProjects). \
filter_by(flavor_uuid=flavor_id)