Quota management for Nova-APIGW and Cinder-APIGW(part2)
Port and modify the quota management code mostly from Cinder, for Cinder has implemented for hierarchy multi-tenancy quota management The code is modified as following: 1. combine the Nova quota resources 2. add AllQuotaEngine and QuotaSetOperation 3. not implemented the volume_type, user based quota management 4. not process is_force 5. update and add test use cases to reflect the new added code and resources The quota management and control in Tricircle is described in the design doc: https://docs.google.com/document/d/18kZZ1snMOCD9IQvUKI5NVDzSASpw-QKj7l2zNqMEd3g/ BP: https://blueprints.launchpad.net/tricircle/+spec/implement-stateless Change-Id: I636d21b5bd7e51949f1431d642dac49321496fbd Signed-off-by: Chaoyi Huang <joehuang@huawei.com>
This commit is contained in:
parent
8dd1b87e26
commit
b350bfe6ba
@ -51,3 +51,5 @@ ns_bridge_subnet_name = 'ns_bridge_subnet_%s' # project_id
|
||||
# for external gateway port: project_id b_router_id None
|
||||
# for floating ip port: project_id None b_internal_port_id
|
||||
ns_bridge_port_name = 'ns_bridge_port_%s_%s_%s'
|
||||
|
||||
MAX_INT = 0x7FFFFFFF
|
||||
|
@ -71,7 +71,8 @@ def get_context_from_neutron_context(context):
|
||||
class ContextBase(oslo_ctx.RequestContext):
|
||||
def __init__(self, auth_token=None, user_id=None, tenant_id=None,
|
||||
is_admin=False, request_id=None, overwrite=True,
|
||||
user_name=None, tenant_name=None, **kwargs):
|
||||
user_name=None, tenant_name=None, quota_class=None,
|
||||
**kwargs):
|
||||
super(ContextBase, self).__init__(
|
||||
auth_token=auth_token,
|
||||
user=user_id or kwargs.get('user', None),
|
||||
@ -87,6 +88,7 @@ class ContextBase(oslo_ctx.RequestContext):
|
||||
overwrite=overwrite)
|
||||
self.user_name = user_name
|
||||
self.tenant_name = tenant_name
|
||||
self.quota_class = quota_class
|
||||
|
||||
def to_dict(self):
|
||||
ctx_dict = super(ContextBase, self).to_dict()
|
||||
@ -94,21 +96,38 @@ class ContextBase(oslo_ctx.RequestContext):
|
||||
'user_name': self.user_name,
|
||||
'tenant_name': self.tenant_name,
|
||||
'tenant_id': self.tenant_id,
|
||||
'project_id': self.project_id
|
||||
'project_id': self.project_id,
|
||||
'quota_class': self.quota_class
|
||||
})
|
||||
return ctx_dict
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, values):
|
||||
return cls(**values)
|
||||
|
||||
@property
|
||||
def project_id(self):
|
||||
return self.tenant
|
||||
|
||||
@project_id.setter
|
||||
def project_id(self, value):
|
||||
self.tenant = value
|
||||
|
||||
@property
|
||||
def tenant_id(self):
|
||||
return self.tenant
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, ctx):
|
||||
return cls(**ctx)
|
||||
@tenant_id.setter
|
||||
def tenant_id(self, value):
|
||||
self.tenant = value
|
||||
|
||||
@property
|
||||
def user_id(self):
|
||||
return self.user
|
||||
|
||||
@user_id.setter
|
||||
def user_id(self, value):
|
||||
self.user = value
|
||||
|
||||
|
||||
class Context(ContextBase):
|
||||
|
@ -178,3 +178,19 @@ class PodNotFound(NotFound):
|
||||
|
||||
def __init__(self, pod_name):
|
||||
super(PodNotFound, self).__init__(pod_name=pod_name)
|
||||
|
||||
|
||||
class ChildQuotaNotZero(TricircleException):
|
||||
message = _("Child projects having non-zero quota")
|
||||
|
||||
|
||||
# parameter validation error
|
||||
class ValidationError(TricircleException):
|
||||
message = _("%(msg)s")
|
||||
code = 400
|
||||
|
||||
|
||||
# parameter validation error
|
||||
class HTTPForbiddenError(TricircleException):
|
||||
message = _("%(msg)s")
|
||||
code = 403
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -13,6 +13,10 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import six
|
||||
|
||||
from tricircle.common.i18n import _
|
||||
|
||||
|
||||
def get_import_path(cls):
|
||||
return cls.__module__ + "." + cls.__name__
|
||||
@ -35,3 +39,45 @@ def validate_required_fields_set(body, fields):
|
||||
if field not in body:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
TRUE_STRINGS = ('1', 't', 'true', 'on', 'y', 'yes')
|
||||
FALSE_STRINGS = ('0', 'f', 'false', 'off', 'n', 'no')
|
||||
|
||||
|
||||
def is_valid_boolstr(val):
|
||||
"""Check if the provided string is a valid bool string or not."""
|
||||
val = str(val).lower()
|
||||
return (val in TRUE_STRINGS) or (val in FALSE_STRINGS)
|
||||
|
||||
|
||||
def bool_from_string(subject, strict=False, default=False):
|
||||
"""Interpret a string as a boolean.
|
||||
|
||||
A case-insensitive match is performed such that strings matching 't',
|
||||
'true', 'on', 'y', 'yes', or '1' are considered True and, when
|
||||
`strict=False`, anything else returns the value specified by 'default'.
|
||||
Useful for JSON-decoded stuff and config file parsing.
|
||||
If `strict=True`, unrecognized values, including None, will raise a
|
||||
ValueError which is useful when parsing values passed in from an API call.
|
||||
Strings yielding False are 'f', 'false', 'off', 'n', 'no', or '0'.
|
||||
"""
|
||||
|
||||
if not isinstance(subject, six.string_types):
|
||||
subject = six.text_type(subject)
|
||||
|
||||
lowered = subject.strip().lower()
|
||||
|
||||
if lowered in TRUE_STRINGS:
|
||||
return True
|
||||
elif lowered in FALSE_STRINGS:
|
||||
return False
|
||||
elif strict:
|
||||
acceptable = ', '.join(
|
||||
"'%s'" % s for s in sorted(TRUE_STRINGS + FALSE_STRINGS))
|
||||
msg = _("Unrecognized value '%(val)s', acceptable values are:"
|
||||
" %(acceptable)s") % {'val': subject,
|
||||
'acceptable': acceptable}
|
||||
raise ValueError(msg)
|
||||
else:
|
||||
return default
|
||||
|
@ -626,9 +626,23 @@ def quota_reserve(context, resources, quotas, deltas, expire,
|
||||
timeutils.utcnow()).seconds >= max_age):
|
||||
refresh = True
|
||||
|
||||
if refresh:
|
||||
# refresh from the bottom pod
|
||||
pass
|
||||
if refresh:
|
||||
# no actural usage refresh here
|
||||
|
||||
# refresh from the bottom pod
|
||||
usages[resource].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(resource)
|
||||
|
||||
# NOTE(Vek): We make the assumption that the sync
|
||||
# routine actually refreshes the
|
||||
# resources that it is the sync routine
|
||||
# for. We don't check, because this is
|
||||
# a best-effort mechanism.
|
||||
|
||||
# Check for deltas that would go negative
|
||||
unders = [r for r, delta in deltas.items()
|
||||
|
2250
tricircle/tests/unit/common/test_quota.py
Normal file
2250
tricircle/tests/unit/common/test_quota.py
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user