remove unnecessary neutron_plugin_base_v2.py quota.py
Change-Id: I1b87abb1e9d3afd855b8aa2f25fbfaab17552449
This commit is contained in:
parent
5d67048792
commit
380dcf445d
|
@ -1,352 +0,0 @@
|
||||||
# Copyright 2011 VMware, Inc.
|
|
||||||
# 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.
|
|
||||||
|
|
||||||
"""
|
|
||||||
v2 Neutron Plug-in API specification.
|
|
||||||
|
|
||||||
:class:`NeutronPluginBaseV2` provides the definition of minimum set of
|
|
||||||
methods that needs to be implemented by a v2 Neutron Plug-in.
|
|
||||||
"""
|
|
||||||
|
|
||||||
import abc
|
|
||||||
import six
|
|
||||||
|
|
||||||
|
|
||||||
@six.add_metaclass(abc.ABCMeta)
|
|
||||||
class NeutronPluginBaseV2(object):
|
|
||||||
|
|
||||||
@abc.abstractmethod
|
|
||||||
def create_subnet(self, context, subnet):
|
|
||||||
"""Create a subnet.
|
|
||||||
|
|
||||||
Create a subnet, which represents a range of IP addresses
|
|
||||||
that can be allocated to devices
|
|
||||||
|
|
||||||
:param context: neutron api request context
|
|
||||||
:param subnet: dictionary describing the subnet, with keys
|
|
||||||
as listed in the :obj:`RESOURCE_ATTRIBUTE_MAP` object
|
|
||||||
in :file:`neutron/api/v2/attributes.py`. All keys will
|
|
||||||
be populated.
|
|
||||||
"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
@abc.abstractmethod
|
|
||||||
def update_subnet(self, context, id, subnet):
|
|
||||||
"""Update values of a subnet.
|
|
||||||
|
|
||||||
:param context: neutron api request context
|
|
||||||
:param id: UUID representing the subnet to update.
|
|
||||||
:param subnet: dictionary with keys indicating fields to update.
|
|
||||||
valid keys are those that have a value of True for
|
|
||||||
'allow_put' as listed in the
|
|
||||||
:obj:`RESOURCE_ATTRIBUTE_MAP` object in
|
|
||||||
:file:`neutron/api/v2/attributes.py`.
|
|
||||||
"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
@abc.abstractmethod
|
|
||||||
def get_subnet(self, context, id, fields=None):
|
|
||||||
"""Retrieve a subnet.
|
|
||||||
|
|
||||||
:param context: neutron api request context
|
|
||||||
:param id: UUID representing the subnet to fetch.
|
|
||||||
:param fields: a list of strings that are valid keys in a
|
|
||||||
subnet dictionary as listed in the
|
|
||||||
:obj:`RESOURCE_ATTRIBUTE_MAP` object in
|
|
||||||
:file:`neutron/api/v2/attributes.py`. Only these fields
|
|
||||||
will be returned.
|
|
||||||
"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
@abc.abstractmethod
|
|
||||||
def get_subnets(self, context, filters=None, fields=None,
|
|
||||||
sorts=None, limit=None, marker=None, page_reverse=False):
|
|
||||||
"""Retrieve a list of subnets.
|
|
||||||
|
|
||||||
The contents of the list depends on
|
|
||||||
the identity of the user making the request (as indicated by the
|
|
||||||
context) as well as any filters.
|
|
||||||
|
|
||||||
:param context: neutron api request context
|
|
||||||
:param filters: a dictionary with keys that are valid keys for
|
|
||||||
a subnet as listed in the :obj:`RESOURCE_ATTRIBUTE_MAP`
|
|
||||||
object in :file:`neutron/api/v2/attributes.py`.
|
|
||||||
Values in this dictiontary are an iterable containing
|
|
||||||
values that will be used for an exact match comparison
|
|
||||||
for that value. Each result returned by this
|
|
||||||
function will have matched one of the values for each
|
|
||||||
key in filters.
|
|
||||||
:param fields: a list of strings that are valid keys in a
|
|
||||||
subnet dictionary as listed in the
|
|
||||||
:obj:`RESOURCE_ATTRIBUTE_MAP` object in
|
|
||||||
:file:`neutron/api/v2/attributes.py`. Only these fields
|
|
||||||
will be returned.
|
|
||||||
"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
def get_subnets_count(self, context, filters=None):
|
|
||||||
"""Return the number of subnets.
|
|
||||||
|
|
||||||
The result depends on the identity of
|
|
||||||
the user making the request (as indicated by the context) as well as
|
|
||||||
any filters.
|
|
||||||
|
|
||||||
:param context: neutron api request context
|
|
||||||
:param filters: a dictionary with keys that are valid keys for
|
|
||||||
a network as listed in the
|
|
||||||
:obj:`RESOURCE_ATTRIBUTE_MAP` object in
|
|
||||||
:file:`neutron/api/v2/attributes.py`. Values in this
|
|
||||||
dictiontary are an iterable containing values that
|
|
||||||
will be used for an exact match comparison for that
|
|
||||||
value. Each result returned by this function will
|
|
||||||
have matched one of the values for each key in filters.
|
|
||||||
|
|
||||||
.. note:: this method is optional, as it was not part of the originally
|
|
||||||
defined plugin API.
|
|
||||||
"""
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
@abc.abstractmethod
|
|
||||||
def delete_subnet(self, context, id):
|
|
||||||
"""Delete a subnet.
|
|
||||||
|
|
||||||
:param context: neutron api request context
|
|
||||||
:param id: UUID representing the subnet to delete.
|
|
||||||
"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
@abc.abstractmethod
|
|
||||||
def create_network(self, context, network):
|
|
||||||
"""Create a network.
|
|
||||||
|
|
||||||
Create a network, which represents an L2 network segment which
|
|
||||||
can have a set of subnets and ports associated with it.
|
|
||||||
|
|
||||||
:param context: neutron api request context
|
|
||||||
:param network: dictionary describing the network, with keys
|
|
||||||
as listed in the :obj:`RESOURCE_ATTRIBUTE_MAP` object
|
|
||||||
in :file:`neutron/api/v2/attributes.py`. All keys will
|
|
||||||
be populated.
|
|
||||||
|
|
||||||
"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
@abc.abstractmethod
|
|
||||||
def update_network(self, context, id, network):
|
|
||||||
"""Update values of a network.
|
|
||||||
|
|
||||||
:param context: neutron api request context
|
|
||||||
:param id: UUID representing the network to update.
|
|
||||||
:param network: dictionary with keys indicating fields to update.
|
|
||||||
valid keys are those that have a value of True for
|
|
||||||
'allow_put' as listed in the
|
|
||||||
:obj:`RESOURCE_ATTRIBUTE_MAP` object in
|
|
||||||
:file:`neutron/api/v2/attributes.py`.
|
|
||||||
"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
@abc.abstractmethod
|
|
||||||
def get_network(self, context, id, fields=None):
|
|
||||||
"""Retrieve a network.
|
|
||||||
|
|
||||||
:param context: neutron api request context
|
|
||||||
:param id: UUID representing the network to fetch.
|
|
||||||
:param fields: a list of strings that are valid keys in a
|
|
||||||
network dictionary as listed in the
|
|
||||||
:obj:`RESOURCE_ATTRIBUTE_MAP` object in
|
|
||||||
:file:`neutron/api/v2/attributes.py`. Only these fields
|
|
||||||
will be returned.
|
|
||||||
"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
@abc.abstractmethod
|
|
||||||
def get_networks(self, context, filters=None, fields=None,
|
|
||||||
sorts=None, limit=None, marker=None, page_reverse=False):
|
|
||||||
"""Retrieve a list of networks.
|
|
||||||
|
|
||||||
The contents of the list depends on
|
|
||||||
the identity of the user making the request (as indicated by the
|
|
||||||
context) as well as any filters.
|
|
||||||
|
|
||||||
:param context: neutron api request context
|
|
||||||
:param filters: a dictionary with keys that are valid keys for
|
|
||||||
a network as listed in the
|
|
||||||
:obj:`RESOURCE_ATTRIBUTE_MAP` object in
|
|
||||||
:file:`neutron/api/v2/attributes.py`. Values in this
|
|
||||||
dictiontary are an iterable containing values that will
|
|
||||||
be used for an exact match comparison for that value.
|
|
||||||
Each result returned by this function will have matched
|
|
||||||
one of the values for each key in filters.
|
|
||||||
:param fields: a list of strings that are valid keys in a
|
|
||||||
network dictionary as listed in the
|
|
||||||
:obj:`RESOURCE_ATTRIBUTE_MAP` object in
|
|
||||||
:file:`neutron/api/v2/attributes.py`. Only these fields
|
|
||||||
will be returned.
|
|
||||||
"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
def get_networks_count(self, context, filters=None):
|
|
||||||
"""Return the number of networks.
|
|
||||||
|
|
||||||
The result depends on the identity
|
|
||||||
of the user making the request (as indicated by the context) as well
|
|
||||||
as any filters.
|
|
||||||
|
|
||||||
:param context: neutron api request context
|
|
||||||
:param filters: a dictionary with keys that are valid keys for
|
|
||||||
a network as listed in the
|
|
||||||
:obj:`RESOURCE_ATTRIBUTE_MAP` object
|
|
||||||
in :file:`neutron/api/v2/attributes.py`. Values in
|
|
||||||
this dictiontary are an iterable containing values that
|
|
||||||
will be used for an exact match comparison for that
|
|
||||||
value. Each result returned by this function will have
|
|
||||||
matched one of the values for each key in filters.
|
|
||||||
|
|
||||||
NOTE: this method is optional, as it was not part of the originally
|
|
||||||
defined plugin API.
|
|
||||||
"""
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
@abc.abstractmethod
|
|
||||||
def delete_network(self, context, id):
|
|
||||||
"""Delete a network.
|
|
||||||
|
|
||||||
:param context: neutron api request context
|
|
||||||
:param id: UUID representing the network to delete.
|
|
||||||
"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
@abc.abstractmethod
|
|
||||||
def create_port(self, context, port):
|
|
||||||
"""Create a port.
|
|
||||||
|
|
||||||
Create a port, which is a connection point of a device (e.g., a VM
|
|
||||||
NIC) to attach to a L2 neutron network.
|
|
||||||
|
|
||||||
:param context: neutron api request context
|
|
||||||
:param port: dictionary describing the port, with keys as listed in the
|
|
||||||
:obj:`RESOURCE_ATTRIBUTE_MAP` object in
|
|
||||||
:file:`neutron/api/v2/attributes.py`. All keys will be
|
|
||||||
populated.
|
|
||||||
"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
@abc.abstractmethod
|
|
||||||
def update_port(self, context, id, port):
|
|
||||||
"""Update values of a port.
|
|
||||||
|
|
||||||
:param context: neutron api request context
|
|
||||||
:param id: UUID representing the port to update.
|
|
||||||
:param port: dictionary with keys indicating fields to update.
|
|
||||||
valid keys are those that have a value of True for
|
|
||||||
'allow_put' as listed in the :obj:`RESOURCE_ATTRIBUTE_MAP`
|
|
||||||
object in :file:`neutron/api/v2/attributes.py`.
|
|
||||||
"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
@abc.abstractmethod
|
|
||||||
def get_port(self, context, id, fields=None):
|
|
||||||
"""Retrieve a port.
|
|
||||||
|
|
||||||
:param context: neutron api request context
|
|
||||||
:param id: UUID representing the port to fetch.
|
|
||||||
:param fields: a list of strings that are valid keys in a port
|
|
||||||
dictionary as listed in the
|
|
||||||
:obj:`RESOURCE_ATTRIBUTE_MAP` object in
|
|
||||||
:file:`neutron/api/v2/attributes.py`. Only these fields
|
|
||||||
will be returned.
|
|
||||||
"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
@abc.abstractmethod
|
|
||||||
def get_ports(self, context, filters=None, fields=None,
|
|
||||||
sorts=None, limit=None, marker=None, page_reverse=False):
|
|
||||||
"""Retrieve a list of ports.
|
|
||||||
|
|
||||||
The contents of the list depends on the identity of the user making
|
|
||||||
the request (as indicated by the context) as well as any filters.
|
|
||||||
|
|
||||||
:param context: neutron api request context
|
|
||||||
:param filters: a dictionary with keys that are valid keys for
|
|
||||||
a port as listed in the :obj:`RESOURCE_ATTRIBUTE_MAP`
|
|
||||||
object in :file:`neutron/api/v2/attributes.py`. Values
|
|
||||||
in this dictiontary are an iterable containing values
|
|
||||||
that will be used for an exact match comparison for
|
|
||||||
that value. Each result returned by this function will
|
|
||||||
have matched one of the values for each key in filters.
|
|
||||||
:param fields: a list of strings that are valid keys in a
|
|
||||||
port dictionary as listed in the
|
|
||||||
:obj:`RESOURCE_ATTRIBUTE_MAP` object in
|
|
||||||
:file:`neutron/api/v2/attributes.py`. Only these fields
|
|
||||||
will be returned.
|
|
||||||
"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
def get_ports_count(self, context, filters=None):
|
|
||||||
"""Return the number of ports.
|
|
||||||
|
|
||||||
The result depends on the identity of the user making the request
|
|
||||||
(as indicated by the context) as well as any filters.
|
|
||||||
|
|
||||||
:param context: neutron api request context
|
|
||||||
:param filters: a dictionary with keys that are valid keys for
|
|
||||||
a network as listed in the
|
|
||||||
:obj:`RESOURCE_ATTRIBUTE_MAP` object in
|
|
||||||
:file:`neutron/api/v2/attributes.py`. Values in this
|
|
||||||
dictiontary are an iterable containing values that will
|
|
||||||
be used for an exact match comparison for that value.
|
|
||||||
Each result returned by this function will have matched
|
|
||||||
one of the values for each key in filters.
|
|
||||||
|
|
||||||
.. note:: this method is optional, as it was not part of the originally
|
|
||||||
defined plugin API.
|
|
||||||
"""
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
@abc.abstractmethod
|
|
||||||
def delete_port(self, context, id):
|
|
||||||
"""Delete a port.
|
|
||||||
|
|
||||||
:param context: neutron api request context
|
|
||||||
:param id: UUID representing the port to delete.
|
|
||||||
"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
def start_rpc_listeners(self):
|
|
||||||
"""Start the RPC listeners.
|
|
||||||
|
|
||||||
Most plugins start RPC listeners implicitly on initialization. In
|
|
||||||
order to support multiple process RPC, the plugin needs to expose
|
|
||||||
control over when this is started.
|
|
||||||
|
|
||||||
.. note:: this method is optional, as it was not part of the originally
|
|
||||||
defined plugin API.
|
|
||||||
"""
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
def rpc_workers_supported(self):
|
|
||||||
"""Return whether the plugin supports multiple RPC workers.
|
|
||||||
|
|
||||||
A plugin that supports multiple RPC workers should override the
|
|
||||||
start_rpc_listeners method to ensure that this method returns True and
|
|
||||||
that start_rpc_listeners is called at the appropriate time.
|
|
||||||
Alternately, a plugin can override this method to customize detection
|
|
||||||
of support for multiple rpc workers
|
|
||||||
|
|
||||||
.. note:: this method is optional, as it was not part of the originally
|
|
||||||
defined plugin API.
|
|
||||||
"""
|
|
||||||
return (self.__class__.start_rpc_listeners !=
|
|
||||||
NeutronPluginBaseV2.start_rpc_listeners)
|
|
334
neutron/quota.py
334
neutron/quota.py
|
@ -1,334 +0,0 @@
|
||||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
|
||||||
|
|
||||||
# Copyright 2011 OpenStack Foundation
|
|
||||||
#
|
|
||||||
# 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.
|
|
||||||
|
|
||||||
"""Quotas for instances, volumes, and floating ips."""
|
|
||||||
|
|
||||||
import sys
|
|
||||||
|
|
||||||
from oslo.config import cfg
|
|
||||||
import webob
|
|
||||||
|
|
||||||
from neutron.common import exceptions
|
|
||||||
from neutron.openstack.common import importutils
|
|
||||||
from neutron.openstack.common import log as logging
|
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
|
||||||
QUOTA_DB_MODULE = 'neutron.db.quota_db'
|
|
||||||
QUOTA_DB_DRIVER = 'neutron.db.quota_db.DbQuotaDriver'
|
|
||||||
QUOTA_CONF_DRIVER = 'neutron.quota.ConfDriver'
|
|
||||||
|
|
||||||
quota_opts = [
|
|
||||||
cfg.ListOpt('quota_items',
|
|
||||||
default=['network', 'subnet', 'port'],
|
|
||||||
help=_('Resource name(s) that are supported in quota '
|
|
||||||
'features')),
|
|
||||||
cfg.IntOpt('default_quota',
|
|
||||||
default=-1,
|
|
||||||
help=_('Default number of resource allowed per tenant. '
|
|
||||||
'A negative value means unlimited.')),
|
|
||||||
cfg.IntOpt('quota_network',
|
|
||||||
default=10,
|
|
||||||
help=_('Number of networks allowed per tenant.'
|
|
||||||
'A negative value means unlimited.')),
|
|
||||||
cfg.IntOpt('quota_subnet',
|
|
||||||
default=10,
|
|
||||||
help=_('Number of subnets allowed per tenant, '
|
|
||||||
'A negative value means unlimited.')),
|
|
||||||
cfg.IntOpt('quota_port',
|
|
||||||
default=50,
|
|
||||||
help=_('Number of ports allowed per tenant. '
|
|
||||||
'A negative value means unlimited.')),
|
|
||||||
cfg.StrOpt('quota_driver',
|
|
||||||
default=QUOTA_DB_DRIVER,
|
|
||||||
help=_('Default driver to use for quota checks')),
|
|
||||||
]
|
|
||||||
# Register the configuration options
|
|
||||||
cfg.CONF.register_opts(quota_opts, 'QUOTAS')
|
|
||||||
|
|
||||||
|
|
||||||
class ConfDriver(object):
|
|
||||||
"""Configuration driver.
|
|
||||||
|
|
||||||
Driver to perform necessary checks to enforce quotas and obtain
|
|
||||||
quota information. The default driver utilizes the default values
|
|
||||||
in neutron.conf.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def _get_quotas(self, context, resources, keys):
|
|
||||||
"""Get quotas.
|
|
||||||
|
|
||||||
A helper method which retrieves the quotas for the specific
|
|
||||||
resources identified by keys, and which apply to the current
|
|
||||||
context.
|
|
||||||
|
|
||||||
:param context: The request context, for access checks.
|
|
||||||
:param resources: A dictionary of the registered resources.
|
|
||||||
:param keys: A list of the desired quotas to retrieve.
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Filter resources
|
|
||||||
desired = set(keys)
|
|
||||||
sub_resources = dict((k, v) for k, v in resources.items()
|
|
||||||
if k in desired)
|
|
||||||
|
|
||||||
# Make sure we accounted for all of them...
|
|
||||||
if len(keys) != len(sub_resources):
|
|
||||||
unknown = desired - set(sub_resources.keys())
|
|
||||||
raise exceptions.QuotaResourceUnknown(unknown=sorted(unknown))
|
|
||||||
quotas = {}
|
|
||||||
for resource in sub_resources.values():
|
|
||||||
quotas[resource.name] = resource.default
|
|
||||||
return quotas
|
|
||||||
|
|
||||||
def limit_check(self, context, tenant_id,
|
|
||||||
resources, values):
|
|
||||||
"""Check simple quota limits.
|
|
||||||
|
|
||||||
For limits--those quotas for which there is no usage
|
|
||||||
synchronization function--this method checks that a set of
|
|
||||||
proposed values are permitted by the limit restriction.
|
|
||||||
|
|
||||||
This method will raise a QuotaResourceUnknown exception if a
|
|
||||||
given resource is unknown or if it is not a simple limit
|
|
||||||
resource.
|
|
||||||
|
|
||||||
If any of the proposed values is over the defined quota, an
|
|
||||||
OverQuota exception will be raised with the sorted list of the
|
|
||||||
resources which are too high. Otherwise, the method returns
|
|
||||||
nothing.
|
|
||||||
|
|
||||||
:param context: The request context, for access checks.
|
|
||||||
:param tennant_id: The tenant_id to check quota.
|
|
||||||
:param resources: A dictionary of the registered resources.
|
|
||||||
:param values: A dictionary of the values to check against the
|
|
||||||
quota.
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Ensure no value is less than zero
|
|
||||||
unders = [key for key, val in values.items() if val < 0]
|
|
||||||
if unders:
|
|
||||||
raise exceptions.InvalidQuotaValue(unders=sorted(unders))
|
|
||||||
|
|
||||||
# Get the applicable quotas
|
|
||||||
quotas = self._get_quotas(context, resources, values.keys())
|
|
||||||
|
|
||||||
# Check the quotas and construct a list of the resources that
|
|
||||||
# would be put over limit by the desired values
|
|
||||||
overs = [key for key, val in values.items()
|
|
||||||
if quotas[key] >= 0 and quotas[key] < val]
|
|
||||||
if overs:
|
|
||||||
raise exceptions.OverQuota(overs=sorted(overs), quotas=quotas,
|
|
||||||
usages={})
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_tenant_quotas(context, resources, tenant_id):
|
|
||||||
quotas = {}
|
|
||||||
sub_resources = dict((k, v) for k, v in resources.items())
|
|
||||||
for resource in sub_resources.values():
|
|
||||||
quotas[resource.name] = resource.default
|
|
||||||
return quotas
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_all_quotas(context, resources):
|
|
||||||
return []
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def delete_tenant_quota(context, tenant_id):
|
|
||||||
msg = _('Access to this resource was denied.')
|
|
||||||
raise webob.exc.HTTPForbidden(msg)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def update_quota_limit(context, tenant_id, resource, limit):
|
|
||||||
msg = _('Access to this resource was denied.')
|
|
||||||
raise webob.exc.HTTPForbidden(msg)
|
|
||||||
|
|
||||||
|
|
||||||
class BaseResource(object):
|
|
||||||
"""Describe a single resource for quota checking."""
|
|
||||||
|
|
||||||
def __init__(self, name, flag):
|
|
||||||
"""Initializes a resource.
|
|
||||||
|
|
||||||
:param name: The name of the resource, i.e., "instances".
|
|
||||||
:param flag: The name of the flag or configuration option
|
|
||||||
"""
|
|
||||||
|
|
||||||
self.name = name
|
|
||||||
self.flag = flag
|
|
||||||
|
|
||||||
@property
|
|
||||||
def default(self):
|
|
||||||
"""Return the default value of the quota."""
|
|
||||||
return getattr(cfg.CONF.QUOTAS,
|
|
||||||
self.flag,
|
|
||||||
cfg.CONF.QUOTAS.default_quota)
|
|
||||||
|
|
||||||
|
|
||||||
class CountableResource(BaseResource):
|
|
||||||
"""Describe a resource where the counts are determined by a function."""
|
|
||||||
|
|
||||||
def __init__(self, name, count, flag=None):
|
|
||||||
"""Initializes a CountableResource.
|
|
||||||
|
|
||||||
Countable resources are those resources which directly
|
|
||||||
correspond to objects in the database, i.e., netowk, subnet,
|
|
||||||
etc.,. A CountableResource must be constructed with a counting
|
|
||||||
function, which will be called to determine the current counts
|
|
||||||
of the resource.
|
|
||||||
|
|
||||||
The counting function will be passed the context, along with
|
|
||||||
the extra positional and keyword arguments that are passed to
|
|
||||||
Quota.count(). It should return an integer specifying the
|
|
||||||
count.
|
|
||||||
|
|
||||||
:param name: The name of the resource, i.e., "instances".
|
|
||||||
:param count: A callable which returns the count of the
|
|
||||||
resource. The arguments passed are as described
|
|
||||||
above.
|
|
||||||
:param flag: The name of the flag or configuration option
|
|
||||||
which specifies the default value of the quota
|
|
||||||
for this resource.
|
|
||||||
"""
|
|
||||||
|
|
||||||
super(CountableResource, self).__init__(name, flag=flag)
|
|
||||||
self.count = count
|
|
||||||
|
|
||||||
|
|
||||||
class QuotaEngine(object):
|
|
||||||
"""Represent the set of recognized quotas."""
|
|
||||||
|
|
||||||
def __init__(self, quota_driver_class=None):
|
|
||||||
"""Initialize a Quota object."""
|
|
||||||
|
|
||||||
self._resources = {}
|
|
||||||
self._driver = None
|
|
||||||
self._driver_class = quota_driver_class
|
|
||||||
|
|
||||||
def get_driver(self):
|
|
||||||
if self._driver is None:
|
|
||||||
_driver_class = (self._driver_class or
|
|
||||||
cfg.CONF.QUOTAS.quota_driver)
|
|
||||||
if (_driver_class == QUOTA_DB_DRIVER and
|
|
||||||
QUOTA_DB_MODULE not in sys.modules):
|
|
||||||
# If quotas table is not loaded, force config quota driver.
|
|
||||||
_driver_class = QUOTA_CONF_DRIVER
|
|
||||||
LOG.info(_("ConfDriver is used as quota_driver because the "
|
|
||||||
"loaded plugin does not support 'quotas' table."))
|
|
||||||
if isinstance(_driver_class, basestring):
|
|
||||||
_driver_class = importutils.import_object(_driver_class)
|
|
||||||
self._driver = _driver_class
|
|
||||||
LOG.info(_('Loaded quota_driver: %s.'), _driver_class)
|
|
||||||
return self._driver
|
|
||||||
|
|
||||||
def __contains__(self, resource):
|
|
||||||
return resource in self._resources
|
|
||||||
|
|
||||||
def register_resource(self, resource):
|
|
||||||
"""Register a resource."""
|
|
||||||
if resource.name in self._resources:
|
|
||||||
LOG.warn(_('%s is already registered.'), resource.name)
|
|
||||||
return
|
|
||||||
self._resources[resource.name] = resource
|
|
||||||
|
|
||||||
def register_resource_by_name(self, resourcename):
|
|
||||||
"""Register a resource by name."""
|
|
||||||
resource = CountableResource(resourcename, _count_resource,
|
|
||||||
'quota_' + resourcename)
|
|
||||||
self.register_resource(resource)
|
|
||||||
|
|
||||||
def register_resources(self, resources):
|
|
||||||
"""Register a list of resources."""
|
|
||||||
|
|
||||||
for resource in resources:
|
|
||||||
self.register_resource(resource)
|
|
||||||
|
|
||||||
def count(self, context, resource, *args, **kwargs):
|
|
||||||
"""Count a resource.
|
|
||||||
|
|
||||||
For countable resources, invokes the count() function and
|
|
||||||
returns its result. Arguments following the context and
|
|
||||||
resource are passed directly to the count function declared by
|
|
||||||
the resource.
|
|
||||||
|
|
||||||
:param context: The request context, for access checks.
|
|
||||||
:param resource: The name of the resource, as a string.
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Get the resource
|
|
||||||
res = self._resources.get(resource)
|
|
||||||
if not res or not hasattr(res, 'count'):
|
|
||||||
raise exceptions.QuotaResourceUnknown(unknown=[resource])
|
|
||||||
|
|
||||||
return res.count(context, *args, **kwargs)
|
|
||||||
|
|
||||||
def limit_check(self, context, tenant_id, **values):
|
|
||||||
"""Check simple quota limits.
|
|
||||||
|
|
||||||
For limits--those quotas for which there is no usage
|
|
||||||
synchronization function--this method checks that a set of
|
|
||||||
proposed values are permitted by the limit restriction. The
|
|
||||||
values to check are given as keyword arguments, where the key
|
|
||||||
identifies the specific quota limit to check, and the value is
|
|
||||||
the proposed value.
|
|
||||||
|
|
||||||
This method will raise a QuotaResourceUnknown exception if a
|
|
||||||
given resource is unknown or if it is not a simple limit
|
|
||||||
resource.
|
|
||||||
|
|
||||||
If any of the proposed values is over the defined quota, an
|
|
||||||
OverQuota exception will be raised with the sorted list of the
|
|
||||||
resources which are too high. Otherwise, the method returns
|
|
||||||
nothing.
|
|
||||||
|
|
||||||
:param context: The request context, for access checks.
|
|
||||||
"""
|
|
||||||
|
|
||||||
return self.get_driver().limit_check(context, tenant_id,
|
|
||||||
self._resources, values)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def resources(self):
|
|
||||||
return self._resources
|
|
||||||
|
|
||||||
|
|
||||||
QUOTAS = QuotaEngine()
|
|
||||||
|
|
||||||
|
|
||||||
def _count_resource(context, plugin, resources, tenant_id):
|
|
||||||
count_getter_name = "get_%s_count" % resources
|
|
||||||
|
|
||||||
# Some plugins support a count method for particular resources,
|
|
||||||
# using a DB's optimized counting features. We try to use that one
|
|
||||||
# if present. Otherwise just use regular getter to retrieve all objects
|
|
||||||
# and count in python, allowing older plugins to still be supported
|
|
||||||
try:
|
|
||||||
obj_count_getter = getattr(plugin, count_getter_name)
|
|
||||||
return obj_count_getter(context, filters={'tenant_id': [tenant_id]})
|
|
||||||
except (NotImplementedError, AttributeError):
|
|
||||||
obj_getter = getattr(plugin, "get_%s" % resources)
|
|
||||||
obj_list = obj_getter(context, filters={'tenant_id': [tenant_id]})
|
|
||||||
return len(obj_list) if obj_list else 0
|
|
||||||
|
|
||||||
|
|
||||||
def register_resources_from_config():
|
|
||||||
resources = []
|
|
||||||
for resource_item in cfg.CONF.QUOTAS.quota_items:
|
|
||||||
resources.append(CountableResource(resource_item, _count_resource,
|
|
||||||
'quota_' + resource_item))
|
|
||||||
QUOTAS.register_resources(resources)
|
|
||||||
|
|
||||||
|
|
||||||
register_resources_from_config()
|
|
Loading…
Reference in New Issue