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:
Chaoyi Huang 2016-02-02 11:06:31 +08:00
parent 8dd1b87e26
commit b350bfe6ba
7 changed files with 3749 additions and 14 deletions

View File

@ -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

View File

@ -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):

View File

@ -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

View File

@ -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

View File

@ -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()

File diff suppressed because it is too large Load Diff