Add db operation for unified limit
This patch adds the db operation part for unified limit Co-Authored-By: Colleen Murphy<colleen@gazlene.net> Change-Id: Ifb2bb54b35ea0d1573cdb9cdab77dfdeb8f22446 bp: unified-limits
This commit is contained in:
parent
6d0ca2f869
commit
7229381c38
|
@ -328,6 +328,8 @@ class _WontMatch(Exception):
|
|||
won't match any value in the column in the table.
|
||||
|
||||
"""
|
||||
if value is None:
|
||||
return
|
||||
col = col_attr.property.columns[0]
|
||||
if isinstance(col.type, sql.types.Boolean):
|
||||
# The column is a Boolean, we should have already validated input.
|
||||
|
|
|
@ -456,6 +456,19 @@ class PublicIDNotFound(NotFound):
|
|||
message_format = "%(id)s"
|
||||
|
||||
|
||||
class RegisteredLimitNotFound(NotFound):
|
||||
message_format = _("Could not find registered limit for %(id)s.")
|
||||
|
||||
|
||||
class LimitNotFound(NotFound):
|
||||
message_format = _("Could not find limit for %(id)s.")
|
||||
|
||||
|
||||
class NoLimitReference(Forbidden):
|
||||
message_format = _("Unable to create a limit that doesn't have a "
|
||||
"corresponding registered limit")
|
||||
|
||||
|
||||
class DomainConfigNotFound(NotFound):
|
||||
message_format = _('Could not find %(group_or_option)s in domain '
|
||||
'configuration for domain %(domain_id)s.')
|
||||
|
|
|
@ -0,0 +1,162 @@
|
|||
# Copyright 2017 SUSE Linux Gmbh
|
||||
# Copyright 2017 Huawei
|
||||
#
|
||||
# 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.
|
||||
|
||||
import abc
|
||||
|
||||
from oslo_log import log
|
||||
import six
|
||||
|
||||
from keystone import exception
|
||||
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class UnifiedLimitDriverBase(object):
|
||||
|
||||
@abc.abstractmethod
|
||||
def create_registered_limits(self, registered_limits):
|
||||
"""Create new registered limits.
|
||||
|
||||
:param registered_limits: a list of dictionaries representing limits to
|
||||
create.
|
||||
|
||||
:returns: all the registered limits.
|
||||
:raises keystone.exception.Conflict: If a duplicate registered limit
|
||||
exists.
|
||||
|
||||
"""
|
||||
raise exception.NotImplemented() # pragma: no cover
|
||||
|
||||
@abc.abstractmethod
|
||||
def update_registered_limits(self, registered_limits):
|
||||
"""Update existing registered limits.
|
||||
|
||||
:param registered_limits: a list of dictionaries representing limits to
|
||||
update.
|
||||
|
||||
:returns: all the registered limits.
|
||||
:raises keystone.exception.RegisteredLimitNotFound: If registered limit
|
||||
doesn't exist.
|
||||
:raises keystone.exception.Conflict: If update to a duplicate
|
||||
registered limit.
|
||||
|
||||
"""
|
||||
raise exception.NotImplemented() # pragma: no cover
|
||||
|
||||
@abc.abstractmethod
|
||||
def list_registered_limits(self, hints):
|
||||
"""List all registered limits.
|
||||
|
||||
:param hints: contains the list of filters yet to be satisfied.
|
||||
Any filters satisfied here will be removed so that
|
||||
the caller will know if any filters remain.
|
||||
|
||||
:returns: a list of dictionaries or an empty registered limit.
|
||||
|
||||
"""
|
||||
raise exception.NotImplemented() # pragma: no cover
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_registered_limit(self, registered_limit_id):
|
||||
"""Get a registered limit.
|
||||
|
||||
:param registered_limit_id: the registered limit id to get.
|
||||
|
||||
:returns: a dictionary representing a registered limit reference.
|
||||
:raises keystone.exception.RegisteredLimitNotFound: If registered limit
|
||||
doesn't exist.
|
||||
|
||||
"""
|
||||
raise exception.NotImplemented() # pragma: no cover
|
||||
|
||||
@abc.abstractmethod
|
||||
def delete_registered_limit(self, registered_limit_id):
|
||||
"""Delete an existing registered limit.
|
||||
|
||||
:param registered_limit_id: the registered limit id to delete.
|
||||
|
||||
:raises keystone.exception.RegisteredLimitNotFound: If registered limit
|
||||
doesn't exist.
|
||||
|
||||
"""
|
||||
raise exception.NotImplemented() # pragma: no cover
|
||||
|
||||
@abc.abstractmethod
|
||||
def create_limits(self, limits):
|
||||
"""Create new limits.
|
||||
|
||||
:param limits: a list of dictionaries representing limits to create.
|
||||
|
||||
:returns: all the limits.
|
||||
:raises keystone.exception.Conflict: If a duplicate limit exists.
|
||||
:raises keystone.exception.NoLimitReference: If no reference registered
|
||||
limit exists.
|
||||
|
||||
"""
|
||||
raise exception.NotImplemented() # pragma: no cover
|
||||
|
||||
@abc.abstractmethod
|
||||
def update_limits(self, limits):
|
||||
"""Update existing limits.
|
||||
|
||||
:param limits: a list of dictionaries representing limits to update.
|
||||
|
||||
:returns: all the limits.
|
||||
:raises keystone.exception.LimitNotFound: If limit doesn't
|
||||
exist.
|
||||
:raises keystone.exception.Conflict: If update to a duplicate limit.
|
||||
|
||||
"""
|
||||
raise exception.NotImplemented() # pragma: no cover
|
||||
|
||||
@abc.abstractmethod
|
||||
def list_limits(self, hints):
|
||||
"""List all limits.
|
||||
|
||||
:param hints: contains the list of filters yet to be satisfied.
|
||||
Any filters satisfied here will be removed so that
|
||||
the caller will know if any filters remain.
|
||||
|
||||
:returns: a list of dictionaries or an empty list.
|
||||
|
||||
"""
|
||||
raise exception.NotImplemented() # pragma: no cover
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_limit(self, limit_id):
|
||||
"""Get a limit.
|
||||
|
||||
:param limit_id: the limit id to get.
|
||||
|
||||
:returns: a dictionary representing a limit reference.
|
||||
:raises keystone.exception.LimitNotFound: If limit doesn't
|
||||
exist.
|
||||
|
||||
"""
|
||||
raise exception.NotImplemented() # pragma: no cover
|
||||
|
||||
@abc.abstractmethod
|
||||
def delete_limit(self, limit_id):
|
||||
"""Delete an existing limit.
|
||||
|
||||
:param limit_id: the limit id to delete.
|
||||
|
||||
:raises keystone.exception.LimitNotFound: If limit doesn't
|
||||
exist.
|
||||
|
||||
"""
|
||||
raise exception.NotImplemented() # pragma: no cover
|
|
@ -0,0 +1,223 @@
|
|||
# Copyright 2017 SUSE Linux Gmbh
|
||||
# Copyright 2017 Huawei
|
||||
#
|
||||
# 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.
|
||||
|
||||
import copy
|
||||
|
||||
from oslo_db import exception as db_exception
|
||||
import sqlalchemy
|
||||
|
||||
from keystone.common import driver_hints
|
||||
from keystone.common import sql
|
||||
from keystone import exception
|
||||
from keystone.i18n import _
|
||||
from keystone.limit.backends import base
|
||||
|
||||
|
||||
class RegisteredLimitModel(sql.ModelBase, sql.ModelDictMixin):
|
||||
__tablename__ = 'registered_limit'
|
||||
attributes = [
|
||||
'id',
|
||||
'service_id',
|
||||
'region_id',
|
||||
'resource_name',
|
||||
'default_limit'
|
||||
]
|
||||
|
||||
id = sql.Column(sql.String(length=64), primary_key=True)
|
||||
service_id = sql.Column(sql.String(255),
|
||||
sql.ForeignKey('service.id'))
|
||||
region_id = sql.Column(sql.String(64),
|
||||
sql.ForeignKey('region.id'), nullable=True)
|
||||
resource_name = sql.Column(sql.String(255))
|
||||
default_limit = sql.Column(sql.Integer, nullable=False)
|
||||
|
||||
__table_args__ = (
|
||||
sqlalchemy.UniqueConstraint('service_id',
|
||||
'region_id',
|
||||
'resource_name'),)
|
||||
|
||||
|
||||
class LimitModel(sql.ModelBase, sql.ModelDictMixin):
|
||||
__tablename__ = 'limit'
|
||||
attributes = [
|
||||
'id',
|
||||
'project_id',
|
||||
'service_id',
|
||||
'region_id',
|
||||
'resource_name',
|
||||
'resource_limit'
|
||||
]
|
||||
|
||||
id = sql.Column(sql.String(length=64), primary_key=True)
|
||||
project_id = sql.Column(sql.String(64),
|
||||
sql.ForeignKey('project.id'))
|
||||
service_id = sql.Column(sql.String(255))
|
||||
region_id = sql.Column(sql.String(64), nullable=True)
|
||||
resource_name = sql.Column(sql.String(255))
|
||||
resource_limit = sql.Column(sql.Integer, nullable=False)
|
||||
|
||||
__table_args__ = (
|
||||
sqlalchemy.ForeignKeyConstraint(['service_id',
|
||||
'region_id',
|
||||
'resource_name'],
|
||||
['registered_limit.service_id',
|
||||
'registered_limit.region_id',
|
||||
'registered_limit.resource_name']),
|
||||
sqlalchemy.UniqueConstraint('project_id',
|
||||
'service_id',
|
||||
'region_id',
|
||||
'resource_name'),)
|
||||
|
||||
|
||||
class UnifiedLimit(base.UnifiedLimitDriverBase):
|
||||
|
||||
def _check_unified_limit_without_region(self, unified_limit,
|
||||
is_registered_limit=True):
|
||||
hints = driver_hints.Hints()
|
||||
hints.add_filter('service_id', unified_limit['service_id'])
|
||||
hints.add_filter('resource_name', unified_limit['resource_name'])
|
||||
hints.add_filter('region_id', None)
|
||||
if not is_registered_limit:
|
||||
# For limit, we should ensure:
|
||||
# 1. there is no duplicate entry.
|
||||
# 2. there is a registered limit reference.
|
||||
reference_hints = copy.deepcopy(hints)
|
||||
hints.add_filter('project_id', unified_limit['project_id'])
|
||||
with sql.session_for_read() as session:
|
||||
unified_limits = session.query(LimitModel)
|
||||
unified_limits = sql.filter_limit_query(LimitModel,
|
||||
unified_limits,
|
||||
hints)
|
||||
with sql.session_for_read() as session:
|
||||
registered_limits = session.query(RegisteredLimitModel)
|
||||
registered_limits = sql.filter_limit_query(
|
||||
RegisteredLimitModel, registered_limits, reference_hints)
|
||||
if not registered_limits.all():
|
||||
raise exception.NoLimitReference
|
||||
else:
|
||||
# For registered limit, we should just ensure that there is no
|
||||
# duplicate entry.
|
||||
with sql.session_for_read() as session:
|
||||
unified_limits = session.query(RegisteredLimitModel)
|
||||
unified_limits = sql.filter_limit_query(RegisteredLimitModel,
|
||||
unified_limits,
|
||||
hints)
|
||||
if unified_limits.all():
|
||||
msg = _('Duplicate entry')
|
||||
limit_type = 'registered_limit' if is_registered_limit else 'limit'
|
||||
raise exception.Conflict(type=limit_type, details=msg)
|
||||
|
||||
@sql.handle_conflicts(conflict_type='registered_limit')
|
||||
def create_registered_limits(self, registered_limits):
|
||||
with sql.session_for_write() as session:
|
||||
return_ref = []
|
||||
for registered_limit in registered_limits:
|
||||
if registered_limit.get('region_id') is None:
|
||||
self._check_unified_limit_without_region(registered_limit)
|
||||
ref = RegisteredLimitModel.from_dict(registered_limit)
|
||||
session.add(ref)
|
||||
return_ref.append(ref.to_dict())
|
||||
return return_ref
|
||||
|
||||
@sql.handle_conflicts(conflict_type='registered_limit')
|
||||
def update_registered_limits(self, registered_limits):
|
||||
with sql.session_for_write() as session:
|
||||
for registered_limit in registered_limits:
|
||||
ref = self._get_registered_limit(session,
|
||||
registered_limit['id'])
|
||||
old_dict = ref.to_dict()
|
||||
old_dict.update(registered_limit)
|
||||
new_registered_limit = RegisteredLimitModel.from_dict(old_dict)
|
||||
for attr in RegisteredLimitModel.attributes:
|
||||
if attr != 'id':
|
||||
setattr(ref, attr, getattr(new_registered_limit, attr))
|
||||
|
||||
@driver_hints.truncated
|
||||
def list_registered_limits(self, hints):
|
||||
with sql.session_for_read() as session:
|
||||
registered_limits = session.query(RegisteredLimitModel)
|
||||
registered_limits = sql.filter_limit_query(RegisteredLimitModel,
|
||||
registered_limits,
|
||||
hints)
|
||||
return [s.to_dict() for s in registered_limits]
|
||||
|
||||
def _get_registered_limit(self, session, registered_limit_id):
|
||||
ref = session.query(RegisteredLimitModel).get(registered_limit_id)
|
||||
if ref is None:
|
||||
raise exception.RegisteredLimitNotFound(id=registered_limit_id)
|
||||
return ref
|
||||
|
||||
def get_registered_limit(self, registered_limit_id):
|
||||
with sql.session_for_read() as session:
|
||||
return self._get_registered_limit(
|
||||
session, registered_limit_id).to_dict()
|
||||
|
||||
def delete_registered_limit(self, registered_limit_id):
|
||||
with sql.session_for_write() as session:
|
||||
ref = self._get_registered_limit(session,
|
||||
registered_limit_id)
|
||||
session.delete(ref)
|
||||
|
||||
@sql.handle_conflicts(conflict_type='limit')
|
||||
def create_limits(self, limits):
|
||||
try:
|
||||
with sql.session_for_write() as session:
|
||||
return_ref = []
|
||||
for limit in limits:
|
||||
if limit.get('region_id') is None:
|
||||
self._check_unified_limit_without_region(
|
||||
limits, is_registered_limit=False)
|
||||
ref = LimitModel.from_dict(limit)
|
||||
session.add(ref)
|
||||
return_ref.append(ref.to_dict())
|
||||
return return_ref
|
||||
except db_exception.DBReferenceError:
|
||||
raise exception.NoLimitReference()
|
||||
|
||||
@sql.handle_conflicts(conflict_type='limit')
|
||||
def update_limits(self, limits):
|
||||
with sql.session_for_write() as session:
|
||||
for limit in limits:
|
||||
ref = self._get_limit(session, limit['id'])
|
||||
old_dict = ref.to_dict()
|
||||
old_dict['resource_limit'] = limit['resource_limit']
|
||||
new_limit = LimitModel.from_dict(old_dict)
|
||||
ref.resource_limit = new_limit.resource_limit
|
||||
|
||||
@driver_hints.truncated
|
||||
def list_limits(self, hints):
|
||||
with sql.session_for_read() as session:
|
||||
limits = session.query(LimitModel)
|
||||
limits = sql.filter_limit_query(LimitModel,
|
||||
limits,
|
||||
hints)
|
||||
return [s.to_dict() for s in limits]
|
||||
|
||||
def _get_limit(self, session, limit_id):
|
||||
ref = session.query(LimitModel).get(limit_id)
|
||||
if ref is None:
|
||||
raise exception.LimitNotFound(id=limit_id)
|
||||
return ref
|
||||
|
||||
def get_limit(self, limit_id):
|
||||
with sql.session_for_read() as session:
|
||||
return self._get_limit(session,
|
||||
limit_id).to_dict()
|
||||
|
||||
def delete_limit(self, limit_id):
|
||||
with sql.session_for_write() as session:
|
||||
ref = self._get_limit(session,
|
||||
limit_id)
|
||||
session.delete(ref)
|
Loading…
Reference in New Issue