Merge "Replace "tenant_id" with "project_id" in Quota engine"
This commit is contained in:
commit
d3fa1ac3bf
@ -245,7 +245,7 @@ The process of making a reservation is fairly straightforward:
|
|||||||
on every requested resource, and then retrieving the amount of reserved
|
on every requested resource, and then retrieving the amount of reserved
|
||||||
resources.
|
resources.
|
||||||
* Fetch current quota limits for requested resources, by invoking the
|
* Fetch current quota limits for requested resources, by invoking the
|
||||||
_get_tenant_quotas method.
|
_get_project_quotas method.
|
||||||
* Fetch expired reservations for selected resources. This amount will be
|
* Fetch expired reservations for selected resources. This amount will be
|
||||||
subtracted from resource usage. As in most cases there won't be any
|
subtracted from resource usage. As in most cases there won't be any
|
||||||
expired reservation, this approach actually requires less DB operations than
|
expired reservation, this approach actually requires less DB operations than
|
||||||
|
@ -401,8 +401,10 @@ class NeutronDbPluginV2(db_base_plugin_common.DbBasePluginCommon,
|
|||||||
def create_network_db(self, context, network):
|
def create_network_db(self, context, network):
|
||||||
# single request processing
|
# single request processing
|
||||||
n = network['network']
|
n = network['network']
|
||||||
|
# TODO(ralonsoh): "tenant_id" reference should be removed.
|
||||||
|
project_id = n.get('project_id') or n['tenant_id']
|
||||||
with db_api.CONTEXT_WRITER.using(context):
|
with db_api.CONTEXT_WRITER.using(context):
|
||||||
args = {'tenant_id': n['tenant_id'],
|
args = {'tenant_id': project_id,
|
||||||
'id': n.get('id') or uuidutils.generate_uuid(),
|
'id': n.get('id') or uuidutils.generate_uuid(),
|
||||||
'name': n['name'],
|
'name': n['name'],
|
||||||
'mtu': n.get('mtu', constants.DEFAULT_NETWORK_MTU),
|
'mtu': n.get('mtu', constants.DEFAULT_NETWORK_MTU),
|
||||||
@ -1426,12 +1428,14 @@ class NeutronDbPluginV2(db_base_plugin_common.DbBasePluginCommon,
|
|||||||
p = port['port']
|
p = port['port']
|
||||||
port_id = p.get('id') or uuidutils.generate_uuid()
|
port_id = p.get('id') or uuidutils.generate_uuid()
|
||||||
network_id = p['network_id']
|
network_id = p['network_id']
|
||||||
|
# TODO(ralonsoh): "tenant_id" reference should be removed.
|
||||||
|
project_id = p.get('project_id') or p['tenant_id']
|
||||||
if p.get('device_owner'):
|
if p.get('device_owner'):
|
||||||
self._enforce_device_owner_not_router_intf_or_device_id(
|
self._enforce_device_owner_not_router_intf_or_device_id(
|
||||||
context, p.get('device_owner'), p.get('device_id'),
|
context, p.get('device_owner'), p.get('device_id'),
|
||||||
p['tenant_id'])
|
project_id)
|
||||||
|
|
||||||
port_data = dict(tenant_id=p['tenant_id'],
|
port_data = dict(tenant_id=project_id,
|
||||||
name=p['name'],
|
name=p['name'],
|
||||||
id=port_id,
|
id=port_id,
|
||||||
network_id=network_id,
|
network_id=network_id,
|
||||||
|
@ -31,28 +31,28 @@ def utcnow():
|
|||||||
|
|
||||||
|
|
||||||
class QuotaUsageInfo(collections.namedtuple(
|
class QuotaUsageInfo(collections.namedtuple(
|
||||||
'QuotaUsageInfo', ['resource', 'tenant_id', 'used', 'dirty'])):
|
'QuotaUsageInfo', ['resource', 'project_id', 'used', 'dirty'])):
|
||||||
"""Information about resource quota usage."""
|
"""Information about resource quota usage."""
|
||||||
|
|
||||||
|
|
||||||
class ReservationInfo(collections.namedtuple(
|
class ReservationInfo(collections.namedtuple(
|
||||||
'ReservationInfo', ['reservation_id', 'tenant_id',
|
'ReservationInfo', ['reservation_id', 'project_id',
|
||||||
'expiration', 'deltas'])):
|
'expiration', 'deltas'])):
|
||||||
"""Information about a resource reservation."""
|
"""Information about a resource reservation."""
|
||||||
|
|
||||||
|
|
||||||
@db_api.retry_if_session_inactive()
|
@db_api.retry_if_session_inactive()
|
||||||
def get_quota_usage_by_resource_and_tenant(context, resource, tenant_id):
|
def get_quota_usage_by_resource_and_project(context, resource, project_id):
|
||||||
"""Return usage info for a given resource and tenant.
|
"""Return usage info for a given resource and project.
|
||||||
|
|
||||||
:param context: Request context
|
:param context: Request context
|
||||||
:param resource: Name of the resource
|
:param resource: Name of the resource
|
||||||
:param tenant_id: Tenant identifier
|
:param project_id: Project identifier
|
||||||
:returns: a QuotaUsageInfo instance
|
:returns: a QuotaUsageInfo instance
|
||||||
"""
|
"""
|
||||||
|
|
||||||
result = quota_obj.QuotaUsage.get_object_dirty_protected(
|
result = quota_obj.QuotaUsage.get_object_dirty_protected(
|
||||||
context, resource=resource, project_id=tenant_id)
|
context, resource=resource, project_id=project_id)
|
||||||
if not result:
|
if not result:
|
||||||
return
|
return
|
||||||
return QuotaUsageInfo(result.resource, result.project_id, result.in_use,
|
return QuotaUsageInfo(result.resource, result.project_id, result.in_use,
|
||||||
@ -69,23 +69,22 @@ def get_quota_usage_by_resource(context, resource):
|
|||||||
|
|
||||||
|
|
||||||
@db_api.retry_if_session_inactive()
|
@db_api.retry_if_session_inactive()
|
||||||
def get_quota_usage_by_tenant_id(context, tenant_id):
|
def get_quota_usage_by_project_id(context, project_id):
|
||||||
objs = quota_obj.QuotaUsage.get_objects(context, project_id=tenant_id)
|
objs = quota_obj.QuotaUsage.get_objects(context, project_id=project_id)
|
||||||
return [QuotaUsageInfo(item.resource,
|
return [QuotaUsageInfo(item.resource,
|
||||||
tenant_id,
|
project_id,
|
||||||
item.in_use,
|
item.in_use,
|
||||||
item.dirty) for item in objs]
|
item.dirty) for item in objs]
|
||||||
|
|
||||||
|
|
||||||
@db_api.retry_if_session_inactive()
|
@db_api.retry_if_session_inactive()
|
||||||
def set_quota_usage(context, resource, tenant_id,
|
def set_quota_usage(context, resource, project_id, in_use=None, delta=False):
|
||||||
in_use=None, delta=False):
|
|
||||||
"""Set resource quota usage.
|
"""Set resource quota usage.
|
||||||
|
|
||||||
:param context: instance of neutron context with db session
|
:param context: instance of neutron context with db session
|
||||||
:param resource: name of the resource for which usage is being set
|
:param resource: name of the resource for which usage is being set
|
||||||
:param tenant_id: identifier of the tenant for which quota usage is
|
:param project_id: identifier of the project for which quota usage is
|
||||||
being set
|
being set
|
||||||
:param in_use: integer specifying the new quantity of used resources,
|
:param in_use: integer specifying the new quantity of used resources,
|
||||||
or a delta to apply to current used resource
|
or a delta to apply to current used resource
|
||||||
:param delta: Specifies whether in_use is an absolute number
|
:param delta: Specifies whether in_use is an absolute number
|
||||||
@ -93,11 +92,11 @@ def set_quota_usage(context, resource, tenant_id,
|
|||||||
"""
|
"""
|
||||||
with db_api.CONTEXT_WRITER.using(context):
|
with db_api.CONTEXT_WRITER.using(context):
|
||||||
usage_data = quota_obj.QuotaUsage.get_object(
|
usage_data = quota_obj.QuotaUsage.get_object(
|
||||||
context, resource=resource, project_id=tenant_id)
|
context, resource=resource, project_id=project_id)
|
||||||
if not usage_data:
|
if not usage_data:
|
||||||
# Must create entry
|
# Must create entry
|
||||||
usage_data = quota_obj.QuotaUsage(
|
usage_data = quota_obj.QuotaUsage(
|
||||||
context, resource=resource, project_id=tenant_id)
|
context, resource=resource, project_id=project_id)
|
||||||
usage_data.create()
|
usage_data.create()
|
||||||
# Perform explicit comparison with None as 0 is a valid value
|
# Perform explicit comparison with None as 0 is a valid value
|
||||||
if in_use is not None:
|
if in_use is not None:
|
||||||
@ -113,16 +112,16 @@ def set_quota_usage(context, resource, tenant_id,
|
|||||||
|
|
||||||
@db_api.retry_if_session_inactive()
|
@db_api.retry_if_session_inactive()
|
||||||
@db_api.CONTEXT_WRITER
|
@db_api.CONTEXT_WRITER
|
||||||
def set_quota_usage_dirty(context, resource, tenant_id, dirty=True):
|
def set_quota_usage_dirty(context, resource, project_id, dirty=True):
|
||||||
"""Set quota usage dirty bit for a given resource and tenant.
|
"""Set quota usage dirty bit for a given resource and project.
|
||||||
|
|
||||||
:param resource: a resource for which quota usage if tracked
|
:param resource: a resource for which quota usage if tracked
|
||||||
:param tenant_id: tenant identifier
|
:param project_id: project identifier
|
||||||
:param dirty: the desired value for the dirty bit (defaults to True)
|
:param dirty: the desired value for the dirty bit (defaults to True)
|
||||||
:returns: 1 if the quota usage data were updated, 0 otherwise.
|
:returns: 1 if the quota usage data were updated, 0 otherwise.
|
||||||
"""
|
"""
|
||||||
obj = quota_obj.QuotaUsage.get_object(
|
obj = quota_obj.QuotaUsage.get_object(
|
||||||
context, resource=resource, project_id=tenant_id)
|
context, resource=resource, project_id=project_id)
|
||||||
if obj:
|
if obj:
|
||||||
obj.dirty = dirty
|
obj.dirty = dirty
|
||||||
obj.update()
|
obj.update()
|
||||||
@ -132,16 +131,17 @@ def set_quota_usage_dirty(context, resource, tenant_id, dirty=True):
|
|||||||
|
|
||||||
@db_api.retry_if_session_inactive()
|
@db_api.retry_if_session_inactive()
|
||||||
@db_api.CONTEXT_WRITER
|
@db_api.CONTEXT_WRITER
|
||||||
def set_resources_quota_usage_dirty(context, resources, tenant_id, dirty=True):
|
def set_resources_quota_usage_dirty(context, resources, project_id,
|
||||||
"""Set quota usage dirty bit for a given tenant and multiple resources.
|
dirty=True):
|
||||||
|
"""Set quota usage dirty bit for a given project and multiple resources.
|
||||||
|
|
||||||
:param resources: list of resource for which the dirty bit is going
|
:param resources: list of resource for which the dirty bit is going
|
||||||
to be set
|
to be set
|
||||||
:param tenant_id: tenant identifier
|
:param project_id: project identifier
|
||||||
:param dirty: the desired value for the dirty bit (defaults to True)
|
:param dirty: the desired value for the dirty bit (defaults to True)
|
||||||
:returns: the number of records for which the bit was actually set.
|
:returns: the number of records for which the bit was actually set.
|
||||||
"""
|
"""
|
||||||
filters = {'project_id': tenant_id}
|
filters = {'project_id': project_id}
|
||||||
if resources:
|
if resources:
|
||||||
filters['resource'] = resources
|
filters['resource'] = resources
|
||||||
objs = quota_obj.QuotaUsage.get_objects(context, **filters)
|
objs = quota_obj.QuotaUsage.get_objects(context, **filters)
|
||||||
@ -154,10 +154,10 @@ def set_resources_quota_usage_dirty(context, resources, tenant_id, dirty=True):
|
|||||||
@db_api.retry_if_session_inactive()
|
@db_api.retry_if_session_inactive()
|
||||||
@db_api.CONTEXT_WRITER
|
@db_api.CONTEXT_WRITER
|
||||||
def set_all_quota_usage_dirty(context, resource, dirty=True):
|
def set_all_quota_usage_dirty(context, resource, dirty=True):
|
||||||
"""Set the dirty bit on quota usage for all tenants.
|
"""Set the dirty bit on quota usage for all projects.
|
||||||
|
|
||||||
:param resource: the resource for which the dirty bit should be set
|
:param resource: the resource for which the dirty bit should be set
|
||||||
:returns: the number of tenants for which the dirty bit was
|
:returns: the number of projects for which the dirty bit was
|
||||||
actually updated
|
actually updated
|
||||||
"""
|
"""
|
||||||
# TODO(manjeets) consider squashing this method with
|
# TODO(manjeets) consider squashing this method with
|
||||||
@ -170,7 +170,7 @@ def set_all_quota_usage_dirty(context, resource, dirty=True):
|
|||||||
|
|
||||||
|
|
||||||
@db_api.retry_if_session_inactive()
|
@db_api.retry_if_session_inactive()
|
||||||
def create_reservation(context, tenant_id, deltas, expiration=None):
|
def create_reservation(context, project_id, deltas, expiration=None):
|
||||||
# This method is usually called from within another transaction.
|
# This method is usually called from within another transaction.
|
||||||
# Consider using begin_nested
|
# Consider using begin_nested
|
||||||
expiration = expiration or (utcnow() + datetime.timedelta(0, 120))
|
expiration = expiration or (utcnow() + datetime.timedelta(0, 120))
|
||||||
@ -179,7 +179,7 @@ def create_reservation(context, tenant_id, deltas, expiration=None):
|
|||||||
delta_objs.append(quota_obj.ResourceDelta(
|
delta_objs.append(quota_obj.ResourceDelta(
|
||||||
context, resource=resource, amount=delta))
|
context, resource=resource, amount=delta))
|
||||||
reserv_obj = quota_obj.Reservation(
|
reserv_obj = quota_obj.Reservation(
|
||||||
context, project_id=tenant_id, expiration=expiration,
|
context, project_id=project_id, expiration=expiration,
|
||||||
resource_deltas=delta_objs)
|
resource_deltas=delta_objs)
|
||||||
reserv_obj.create()
|
reserv_obj.create()
|
||||||
return ReservationInfo(reserv_obj['id'],
|
return ReservationInfo(reserv_obj['id'],
|
||||||
@ -223,12 +223,12 @@ def remove_reservation(context, reservation_id, set_dirty=False):
|
|||||||
|
|
||||||
@db_api.retry_if_session_inactive()
|
@db_api.retry_if_session_inactive()
|
||||||
@db_api.CONTEXT_READER
|
@db_api.CONTEXT_READER
|
||||||
def get_reservations_for_resources(context, tenant_id, resources,
|
def get_reservations_for_resources(context, project_id, resources,
|
||||||
expired=False):
|
expired=False):
|
||||||
"""Retrieve total amount of reservations for specified resources.
|
"""Retrieve total amount of reservations for specified resources.
|
||||||
|
|
||||||
:param context: Neutron context with db session
|
:param context: Neutron context with db session
|
||||||
:param tenant_id: Tenant identifier
|
:param project_id: Project identifier
|
||||||
:param resources: Resources for which reserved amounts should be fetched
|
:param resources: Resources for which reserved amounts should be fetched
|
||||||
:param expired: False to fetch active reservations, True to fetch expired
|
:param expired: False to fetch active reservations, True to fetch expired
|
||||||
reservations (defaults to False)
|
reservations (defaults to False)
|
||||||
@ -238,17 +238,17 @@ def get_reservations_for_resources(context, tenant_id, resources,
|
|||||||
# can be mocked easily where as datetime is built in type
|
# can be mocked easily where as datetime is built in type
|
||||||
# mock.path does not allow mocking built in types.
|
# mock.path does not allow mocking built in types.
|
||||||
return quota_obj.Reservation.get_total_reservations_map(
|
return quota_obj.Reservation.get_total_reservations_map(
|
||||||
context, utcnow(), tenant_id, resources, expired)
|
context, utcnow(), project_id, resources, expired)
|
||||||
|
|
||||||
|
|
||||||
@db_api.retry_if_session_inactive()
|
@db_api.retry_if_session_inactive()
|
||||||
@db_api.CONTEXT_WRITER
|
@db_api.CONTEXT_WRITER
|
||||||
def remove_expired_reservations(context, tenant_id=None, timeout=None):
|
def remove_expired_reservations(context, project_id=None, timeout=None):
|
||||||
expiring_time = utcnow()
|
expiring_time = utcnow()
|
||||||
if timeout:
|
if timeout:
|
||||||
expiring_time -= datetime.timedelta(seconds=timeout)
|
expiring_time -= datetime.timedelta(seconds=timeout)
|
||||||
return quota_obj.Reservation.delete_expired(context, expiring_time,
|
return quota_obj.Reservation.delete_expired(context, expiring_time,
|
||||||
tenant_id)
|
project_id)
|
||||||
|
|
||||||
|
|
||||||
class QuotaDriverAPI(object, metaclass=abc.ABCMeta):
|
class QuotaDriverAPI(object, metaclass=abc.ABCMeta):
|
||||||
@ -257,7 +257,7 @@ class QuotaDriverAPI(object, metaclass=abc.ABCMeta):
|
|||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
def get_default_quotas(context, resources, project_id):
|
def get_default_quotas(context, resources, project_id):
|
||||||
"""Given a list of resources, retrieve the default quotas set for
|
"""Given a list of resources, retrieve the default quotas set for
|
||||||
a tenant.
|
a project.
|
||||||
|
|
||||||
:param context: The request context, for access checks.
|
:param context: The request context, for access checks.
|
||||||
:param resources: A dictionary of the registered resource keys.
|
:param resources: A dictionary of the registered resource keys.
|
||||||
@ -267,7 +267,7 @@ class QuotaDriverAPI(object, metaclass=abc.ABCMeta):
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
def get_tenant_quotas(context, resources, project_id):
|
def get_project_quotas(context, resources, project_id):
|
||||||
"""Retrieve the quotas for the given list of resources and project
|
"""Retrieve the quotas for the given list of resources and project
|
||||||
|
|
||||||
:param context: The request context, for access checks.
|
:param context: The request context, for access checks.
|
||||||
@ -278,7 +278,7 @@ class QuotaDriverAPI(object, metaclass=abc.ABCMeta):
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
def get_detailed_tenant_quotas(context, resources, project_id):
|
def get_detailed_project_quotas(context, resources, project_id):
|
||||||
"""Retrieve detailed quotas for the given list of resources and project
|
"""Retrieve detailed quotas for the given list of resources and project
|
||||||
|
|
||||||
:param context: The request context, for access checks.
|
:param context: The request context, for access checks.
|
||||||
@ -291,11 +291,11 @@ class QuotaDriverAPI(object, metaclass=abc.ABCMeta):
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
def delete_tenant_quota(context, project_id):
|
def delete_project_quota(context, project_id):
|
||||||
"""Delete the quota entries for a given project_id.
|
"""Delete the quota entries for a given project_id.
|
||||||
|
|
||||||
After deletion, this tenant will use default quota values in conf.
|
After deletion, this project will use default quota values in conf.
|
||||||
Raise a "not found" error if the quota for the given tenant was
|
Raise a "not found" error if the quota for the given project was
|
||||||
never defined.
|
never defined.
|
||||||
|
|
||||||
:param context: The request context, for access checks.
|
:param context: The request context, for access checks.
|
||||||
@ -382,15 +382,15 @@ class NullQuotaDriver(QuotaDriverAPI):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_tenant_quotas(context, resources, project_id):
|
def get_project_quotas(context, resources, project_id):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_detailed_tenant_quotas(context, resources, project_id):
|
def get_detailed_project_quotas(context, resources, project_id):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def delete_tenant_quota(context, project_id):
|
def delete_project_quota(context, project_id):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
@ -35,139 +35,142 @@ class DbQuotaDriver(quota_api.QuotaDriverAPI):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_default_quotas(context, resources, tenant_id):
|
def get_default_quotas(context, resources, project_id):
|
||||||
"""Given a list of resources, retrieve the default quotas set for
|
"""Given a list of resources, retrieve the default quotas set for
|
||||||
a tenant.
|
a project.
|
||||||
|
|
||||||
:param context: The request context, for access checks.
|
:param context: The request context, for access checks.
|
||||||
:param resources: A dictionary of the registered resource keys.
|
:param resources: A dictionary of the registered resource keys.
|
||||||
:param tenant_id: The ID of the tenant to return default quotas for.
|
:param project_id: The ID of the project to return default quotas for.
|
||||||
:return: dict from resource name to dict of name and limit
|
:return: dict from resource name to dict of name and limit
|
||||||
"""
|
"""
|
||||||
# Currently the tenant_id parameter is unused, since all tenants
|
# Currently the project_id parameter is unused, since all projects
|
||||||
# share the same default values. This may change in the future so
|
# share the same default values. This may change in the future so
|
||||||
# we include tenant-id to remain backwards compatible.
|
# we include project ID to remain backwards compatible.
|
||||||
return dict((key, resource.default)
|
return dict((key, resource.default)
|
||||||
for key, resource in resources.items())
|
for key, resource in resources.items())
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@db_api.retry_if_session_inactive()
|
@db_api.retry_if_session_inactive()
|
||||||
def get_tenant_quotas(context, resources, tenant_id):
|
def get_project_quotas(context, resources, project_id):
|
||||||
"""Given a list of resources, retrieve the quotas for the given
|
"""Given a list of resources, retrieve the quotas for the given
|
||||||
tenant. If no limits are found for the specified tenant, the operation
|
project. If no limits are found for the specified project, the
|
||||||
returns the default limits.
|
operation returns the default limits.
|
||||||
|
|
||||||
:param context: The request context, for access checks.
|
:param context: The request context, for access checks.
|
||||||
:param resources: A dictionary of the registered resource keys.
|
:param resources: A dictionary of the registered resource keys.
|
||||||
:param tenant_id: The ID of the tenant to return quotas for.
|
:param project_id: The ID of the project to return quotas for.
|
||||||
:return: dict from resource name to dict of name and limit
|
:return: dict from resource name to dict of name and limit
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# init with defaults
|
# init with defaults
|
||||||
tenant_quota = dict((key, resource.default)
|
project_quota = dict((key, resource.default)
|
||||||
for key, resource in resources.items())
|
for key, resource in resources.items())
|
||||||
|
|
||||||
# update with tenant specific limits
|
# update with project specific limits
|
||||||
quota_objs = quota_obj.Quota.get_objects(context, project_id=tenant_id)
|
quota_objs = quota_obj.Quota.get_objects(context,
|
||||||
|
project_id=project_id)
|
||||||
for item in quota_objs:
|
for item in quota_objs:
|
||||||
tenant_quota[item['resource']] = item['limit']
|
project_quota[item['resource']] = item['limit']
|
||||||
|
|
||||||
return tenant_quota
|
return project_quota
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@db_api.retry_if_session_inactive()
|
@db_api.retry_if_session_inactive()
|
||||||
def get_detailed_tenant_quotas(context, resources, tenant_id):
|
def get_detailed_project_quotas(context, resources, project_id):
|
||||||
"""Given a list of resources and a sepecific tenant, retrieve
|
"""Given a list of resources and a specific project, retrieve
|
||||||
the detailed quotas (limit, used, reserved).
|
the detailed quotas (limit, used, reserved).
|
||||||
:param context: The request context, for access checks.
|
:param context: The request context, for access checks.
|
||||||
:param resources: A dictionary of the registered resource keys.
|
:param resources: A dictionary of the registered resource keys.
|
||||||
|
:param project_id: The ID of the project to return quotas for.
|
||||||
:return dict: mapping resource name in dict to its corresponding limit
|
:return dict: mapping resource name in dict to its corresponding limit
|
||||||
used and reserved. Reserved currently returns default value of 0
|
used and reserved. Reserved currently returns default value of 0
|
||||||
"""
|
"""
|
||||||
res_reserve_info = quota_api.get_reservations_for_resources(
|
res_reserve_info = quota_api.get_reservations_for_resources(
|
||||||
context, tenant_id, resources.keys())
|
context, project_id, resources.keys())
|
||||||
tenant_quota_ext = {}
|
project_quota_ext = {}
|
||||||
for key, resource in resources.items():
|
for key, resource in resources.items():
|
||||||
if isinstance(resource, res.TrackedResource):
|
if isinstance(resource, res.TrackedResource):
|
||||||
used = resource.count_used(context, tenant_id,
|
used = resource.count_used(context, project_id,
|
||||||
resync_usage=False)
|
resync_usage=False)
|
||||||
else:
|
else:
|
||||||
# NOTE(ihrachys) .count won't use the plugin we pass, but we
|
# NOTE(ihrachys) .count won't use the plugin we pass, but we
|
||||||
# pass it regardless to keep the quota driver API intact
|
# pass it regardless to keep the quota driver API intact
|
||||||
plugins = directory.get_plugins()
|
plugins = directory.get_plugins()
|
||||||
plugin = plugins.get(key, plugins[constants.CORE])
|
plugin = plugins.get(key, plugins[constants.CORE])
|
||||||
used = resource.count(context, plugin, tenant_id)
|
used = resource.count(context, plugin, project_id)
|
||||||
|
|
||||||
tenant_quota_ext[key] = {
|
project_quota_ext[key] = {
|
||||||
'limit': resource.default,
|
'limit': resource.default,
|
||||||
'used': used,
|
'used': used,
|
||||||
'reserved': res_reserve_info.get(key, 0),
|
'reserved': res_reserve_info.get(key, 0),
|
||||||
}
|
}
|
||||||
# update with specific tenant limits
|
# update with specific project limits
|
||||||
quota_objs = quota_obj.Quota.get_objects(context, project_id=tenant_id)
|
quota_objs = quota_obj.Quota.get_objects(context,
|
||||||
|
project_id=project_id)
|
||||||
for item in quota_objs:
|
for item in quota_objs:
|
||||||
tenant_quota_ext[item['resource']]['limit'] = item['limit']
|
project_quota_ext[item['resource']]['limit'] = item['limit']
|
||||||
return tenant_quota_ext
|
return project_quota_ext
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@db_api.retry_if_session_inactive()
|
@db_api.retry_if_session_inactive()
|
||||||
def delete_tenant_quota(context, tenant_id):
|
def delete_project_quota(context, project_id):
|
||||||
"""Delete the quota entries for a given tenant_id.
|
"""Delete the quota entries for a given project_id.
|
||||||
|
|
||||||
After deletion, this tenant will use default quota values in conf.
|
After deletion, this project will use default quota values in conf.
|
||||||
Raise a "not found" error if the quota for the given tenant was
|
Raise a "not found" error if the quota for the given project was
|
||||||
never defined.
|
never defined.
|
||||||
"""
|
"""
|
||||||
if quota_obj.Quota.delete_objects(context, project_id=tenant_id) < 1:
|
if quota_obj.Quota.delete_objects(context, project_id=project_id) < 1:
|
||||||
# No record deleted means the quota was not found
|
# No record deleted means the quota was not found
|
||||||
raise exceptions.TenantQuotaNotFound(tenant_id=tenant_id)
|
raise exceptions.TenantQuotaNotFound(tenant_id=project_id)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@db_api.retry_if_session_inactive()
|
@db_api.retry_if_session_inactive()
|
||||||
def get_all_quotas(context, resources):
|
def get_all_quotas(context, resources):
|
||||||
"""Given a list of resources, retrieve the quotas for the all tenants.
|
"""Given a list of resources, retrieve the quotas for the all projects.
|
||||||
|
|
||||||
:param context: The request context, for access checks.
|
:param context: The request context, for access checks.
|
||||||
:param resources: A dictionary of the registered resource keys.
|
:param resources: A dictionary of the registered resource keys.
|
||||||
:return: quotas list of dict of tenant_id:, resourcekey1:
|
:return: quotas list of dict of project_id:, resourcekey1:
|
||||||
resourcekey2: ...
|
resourcekey2: ...
|
||||||
"""
|
"""
|
||||||
tenant_default = dict((key, resource.default)
|
project_default = dict((key, resource.default)
|
||||||
for key, resource in resources.items())
|
for key, resource in resources.items())
|
||||||
|
|
||||||
all_tenant_quotas = {}
|
all_project_quotas = {}
|
||||||
|
|
||||||
for quota in quota_obj.Quota.get_objects(context):
|
for quota in quota_obj.Quota.get_objects(context):
|
||||||
tenant_id = quota['project_id']
|
project_id = quota['project_id']
|
||||||
|
|
||||||
# avoid setdefault() because only want to copy when actually
|
# avoid setdefault() because only want to copy when actually
|
||||||
# required
|
# required
|
||||||
tenant_quota = all_tenant_quotas.get(tenant_id)
|
project_quota = all_project_quotas.get(project_id)
|
||||||
if tenant_quota is None:
|
if project_quota is None:
|
||||||
tenant_quota = tenant_default.copy()
|
project_quota = project_default.copy()
|
||||||
tenant_quota['tenant_id'] = tenant_id
|
project_quota['project_id'] = project_id
|
||||||
attributes.populate_project_info(tenant_quota)
|
attributes.populate_project_info(project_quota)
|
||||||
all_tenant_quotas[tenant_id] = tenant_quota
|
all_project_quotas[project_id] = project_quota
|
||||||
|
|
||||||
tenant_quota[quota['resource']] = quota['limit']
|
project_quota[quota['resource']] = quota['limit']
|
||||||
|
|
||||||
# Convert values to a list to as caller expect an indexable iterable,
|
# Convert values to a list to as caller expect an indexable iterable,
|
||||||
# where python3's dict_values does not support indexing
|
# where python3's dict_values does not support indexing
|
||||||
return list(all_tenant_quotas.values())
|
return list(all_project_quotas.values())
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@db_api.retry_if_session_inactive()
|
@db_api.retry_if_session_inactive()
|
||||||
def update_quota_limit(context, tenant_id, resource, limit):
|
def update_quota_limit(context, project_id, resource, limit):
|
||||||
tenant_quotas = quota_obj.Quota.get_objects(
|
project_quotas = quota_obj.Quota.get_objects(
|
||||||
context, project_id=tenant_id, resource=resource)
|
context, project_id=project_id, resource=resource)
|
||||||
if tenant_quotas:
|
if project_quotas:
|
||||||
tenant_quotas[0].limit = limit
|
project_quotas[0].limit = limit
|
||||||
tenant_quotas[0].update()
|
project_quotas[0].update()
|
||||||
else:
|
else:
|
||||||
quota_obj.Quota(context, project_id=tenant_id, resource=resource,
|
quota_obj.Quota(context, project_id=project_id, resource=resource,
|
||||||
limit=limit).create()
|
limit=limit).create()
|
||||||
|
|
||||||
def _get_quotas(self, context, tenant_id, resources):
|
def _get_quotas(self, context, project_id, resources):
|
||||||
"""Retrieves the quotas for specific resources.
|
"""Retrieves the quotas for specific resources.
|
||||||
|
|
||||||
A helper method which retrieves the quotas for the specific
|
A helper method which retrieves the quotas for the specific
|
||||||
@ -175,24 +178,23 @@ class DbQuotaDriver(quota_api.QuotaDriverAPI):
|
|||||||
context.
|
context.
|
||||||
|
|
||||||
:param context: The request context, for access checks.
|
:param context: The request context, for access checks.
|
||||||
:param tenant_id: the tenant_id to check quota.
|
:param project_id: the project ID to check quota.
|
||||||
:param resources: A dictionary of the registered resources.
|
:param resources: A dictionary of the registered resources.
|
||||||
"""
|
"""
|
||||||
# Grab and return the quotas (without usages)
|
# Grab and return the quotas (without usages)
|
||||||
quotas = DbQuotaDriver.get_tenant_quotas(
|
quotas = DbQuotaDriver.get_project_quotas(
|
||||||
context, resources, tenant_id)
|
context, resources, project_id)
|
||||||
|
|
||||||
return dict((k, v) for k, v in quotas.items())
|
return dict((k, v) for k, v in quotas.items())
|
||||||
|
|
||||||
def _handle_expired_reservations(self, context, tenant_id):
|
def _handle_expired_reservations(self, context, project_id):
|
||||||
LOG.debug("Deleting expired reservations for tenant:%s", tenant_id)
|
LOG.debug("Deleting expired reservations for project: %s", project_id)
|
||||||
# Delete expired reservations (we don't want them to accrue
|
# Delete expired reservations (we don't want them to accrue
|
||||||
# in the database)
|
# in the database)
|
||||||
quota_api.remove_expired_reservations(
|
quota_api.remove_expired_reservations(context, project_id=project_id)
|
||||||
context, tenant_id=tenant_id)
|
|
||||||
|
|
||||||
@db_api.retry_if_session_inactive()
|
@db_api.retry_if_session_inactive()
|
||||||
def make_reservation(self, context, tenant_id, resources, deltas, plugin):
|
def make_reservation(self, context, project_id, resources, deltas, plugin):
|
||||||
# Lock current reservation table
|
# Lock current reservation table
|
||||||
# NOTE(salv-orlando): This routine uses DB write locks.
|
# NOTE(salv-orlando): This routine uses DB write locks.
|
||||||
# These locks are acquired by the count() method invoked on resources.
|
# These locks are acquired by the count() method invoked on resources.
|
||||||
@ -207,11 +209,11 @@ class DbQuotaDriver(quota_api.QuotaDriverAPI):
|
|||||||
# to a single node will be available.
|
# to a single node will be available.
|
||||||
requested_resources = deltas.keys()
|
requested_resources = deltas.keys()
|
||||||
with db_api.CONTEXT_WRITER.using(context):
|
with db_api.CONTEXT_WRITER.using(context):
|
||||||
# get_tenant_quotes needs in input a dictionary mapping resource
|
# "get_project_quotas" needs in input a dictionary mapping resource
|
||||||
# name to BaseResosurce instances so that the default quota can be
|
# name to BaseResosurce instances so that the default quota can be
|
||||||
# retrieved
|
# retrieved
|
||||||
current_limits = self.get_tenant_quotas(
|
current_limits = self.get_project_quotas(
|
||||||
context, resources, tenant_id)
|
context, resources, project_id)
|
||||||
unlimited_resources = set([resource for (resource, limit) in
|
unlimited_resources = set([resource for (resource, limit) in
|
||||||
current_limits.items() if limit < 0])
|
current_limits.items() if limit < 0])
|
||||||
# Do not even bother counting resources and calculating headroom
|
# Do not even bother counting resources and calculating headroom
|
||||||
@ -230,13 +232,13 @@ class DbQuotaDriver(quota_api.QuotaDriverAPI):
|
|||||||
# instances
|
# instances
|
||||||
current_usages = dict(
|
current_usages = dict(
|
||||||
(resource, resources[resource].count(
|
(resource, resources[resource].count(
|
||||||
context, plugin, tenant_id, resync_usage=False)) for
|
context, plugin, project_id, resync_usage=False)) for
|
||||||
resource in requested_resources)
|
resource in requested_resources)
|
||||||
# Adjust for expired reservations. Apparently it is cheaper than
|
# Adjust for expired reservations. Apparently it is cheaper than
|
||||||
# querying every time for active reservations and counting overall
|
# querying every time for active reservations and counting overall
|
||||||
# quantity of resources reserved
|
# quantity of resources reserved
|
||||||
expired_deltas = quota_api.get_reservations_for_resources(
|
expired_deltas = quota_api.get_reservations_for_resources(
|
||||||
context, tenant_id, requested_resources, expired=True)
|
context, project_id, requested_resources, expired=True)
|
||||||
# Verify that the request can be accepted with current limits
|
# Verify that the request can be accepted with current limits
|
||||||
resources_over_limit = []
|
resources_over_limit = []
|
||||||
for resource in requested_resources:
|
for resource in requested_resources:
|
||||||
@ -254,14 +256,14 @@ class DbQuotaDriver(quota_api.QuotaDriverAPI):
|
|||||||
if res_headroom < deltas[resource]:
|
if res_headroom < deltas[resource]:
|
||||||
resources_over_limit.append(resource)
|
resources_over_limit.append(resource)
|
||||||
if expired_reservations:
|
if expired_reservations:
|
||||||
self._handle_expired_reservations(context, tenant_id)
|
self._handle_expired_reservations(context, project_id)
|
||||||
|
|
||||||
if resources_over_limit:
|
if resources_over_limit:
|
||||||
raise exceptions.OverQuota(overs=sorted(resources_over_limit))
|
raise exceptions.OverQuota(overs=sorted(resources_over_limit))
|
||||||
# Success, store the reservation
|
# Success, store the reservation
|
||||||
# TODO(salv-orlando): Make expiration time configurable
|
# TODO(salv-orlando): Make expiration time configurable
|
||||||
return quota_api.create_reservation(
|
return quota_api.create_reservation(
|
||||||
context, tenant_id, deltas)
|
context, project_id, deltas)
|
||||||
|
|
||||||
def commit_reservation(self, context, reservation_id):
|
def commit_reservation(self, context, reservation_id):
|
||||||
# Do not mark resource usage as dirty. If a reservation is committed,
|
# Do not mark resource usage as dirty. If a reservation is committed,
|
||||||
@ -276,7 +278,7 @@ class DbQuotaDriver(quota_api.QuotaDriverAPI):
|
|||||||
quota_api.remove_reservation(context, reservation_id,
|
quota_api.remove_reservation(context, reservation_id,
|
||||||
set_dirty=True)
|
set_dirty=True)
|
||||||
|
|
||||||
def limit_check(self, context, tenant_id, resources, values):
|
def limit_check(self, context, project_id, resources, values):
|
||||||
"""Check simple quota limits.
|
"""Check simple quota limits.
|
||||||
|
|
||||||
For limits--those quotas for which there is no usage
|
For limits--those quotas for which there is no usage
|
||||||
@ -289,7 +291,7 @@ class DbQuotaDriver(quota_api.QuotaDriverAPI):
|
|||||||
nothing.
|
nothing.
|
||||||
|
|
||||||
:param context: The request context, for access checks.
|
:param context: The request context, for access checks.
|
||||||
:param tenant_id: The tenant_id to check the quota.
|
:param project_id: The project ID to check the quota.
|
||||||
:param resources: A dictionary of the registered resources.
|
:param resources: A dictionary of the registered resources.
|
||||||
:param values: A dictionary of the values to check against the
|
:param values: A dictionary of the values to check against the
|
||||||
quota.
|
quota.
|
||||||
@ -301,7 +303,7 @@ class DbQuotaDriver(quota_api.QuotaDriverAPI):
|
|||||||
raise exceptions.InvalidQuotaValue(unders=sorted(unders))
|
raise exceptions.InvalidQuotaValue(unders=sorted(unders))
|
||||||
|
|
||||||
# Get the applicable quotas
|
# Get the applicable quotas
|
||||||
quotas = self._get_quotas(context, tenant_id, resources)
|
quotas = self._get_quotas(context, project_id, resources)
|
||||||
|
|
||||||
# Check the quotas and construct a list of the resources that
|
# Check the quotas and construct a list of the resources that
|
||||||
# would be put over limit by the desired values
|
# would be put over limit by the desired values
|
||||||
|
@ -45,7 +45,7 @@ class DbQuotaNoLockDriver(quota_driver.DbQuotaDriver):
|
|||||||
resources_over_limit = []
|
resources_over_limit = []
|
||||||
with db_api.CONTEXT_WRITER.using(context):
|
with db_api.CONTEXT_WRITER.using(context):
|
||||||
# Filter out unlimited resources.
|
# Filter out unlimited resources.
|
||||||
limits = self.get_tenant_quotas(context, resources, project_id)
|
limits = self.get_project_quotas(context, resources, project_id)
|
||||||
unlimited_resources = set([resource for (resource, limit) in
|
unlimited_resources = set([resource for (resource, limit) in
|
||||||
limits.items() if limit < 0])
|
limits.items() if limit < 0])
|
||||||
requested_resources = (set(deltas.keys()) - unlimited_resources)
|
requested_resources = (set(deltas.keys()) - unlimited_resources)
|
||||||
@ -54,7 +54,7 @@ class DbQuotaNoLockDriver(quota_driver.DbQuotaDriver):
|
|||||||
# operation is fast and by calling it before making any
|
# operation is fast and by calling it before making any
|
||||||
# reservation, we ensure the freshness of the reservations.
|
# reservation, we ensure the freshness of the reservations.
|
||||||
quota_api.remove_expired_reservations(
|
quota_api.remove_expired_reservations(
|
||||||
context, tenant_id=project_id,
|
context, project_id=project_id,
|
||||||
timeout=quota_api.RESERVATION_EXPIRATION_TIMEOUT)
|
timeout=quota_api.RESERVATION_EXPIRATION_TIMEOUT)
|
||||||
|
|
||||||
# Count the number of (1) used and (2) reserved resources for this
|
# Count the number of (1) used and (2) reserved resources for this
|
||||||
|
@ -924,10 +924,10 @@ class SecurityGroupDbMixin(ext_sg.SecurityGroupPluginBase,
|
|||||||
@registry.receives(resources.NETWORK, [events.BEFORE_CREATE])
|
@registry.receives(resources.NETWORK, [events.BEFORE_CREATE])
|
||||||
def _ensure_default_security_group_handler(self, resource, event, trigger,
|
def _ensure_default_security_group_handler(self, resource, event, trigger,
|
||||||
payload):
|
payload):
|
||||||
if event == events.BEFORE_UPDATE:
|
_state = (payload.states[0] if event == events.BEFORE_UPDATE else
|
||||||
project_id = payload.states[0]['tenant_id']
|
payload.latest_state)
|
||||||
else:
|
# TODO(ralonsoh): "tenant_id" reference should be removed.
|
||||||
project_id = payload.latest_state['tenant_id']
|
project_id = _state.get('project_id') or _state['tenant_id']
|
||||||
if project_id:
|
if project_id:
|
||||||
self._ensure_default_security_group(payload.context, project_id)
|
self._ensure_default_security_group(payload.context, project_id)
|
||||||
|
|
||||||
@ -993,7 +993,8 @@ class SecurityGroupDbMixin(ext_sg.SecurityGroupPluginBase,
|
|||||||
return
|
return
|
||||||
port_sg = port.get(ext_sg.SECURITYGROUPS)
|
port_sg = port.get(ext_sg.SECURITYGROUPS)
|
||||||
if port_sg is None or not validators.is_attr_set(port_sg):
|
if port_sg is None or not validators.is_attr_set(port_sg):
|
||||||
port_project = port.get('tenant_id')
|
# TODO(ralonsoh): "tenant_id" reference should be removed.
|
||||||
|
port_project = port.get('project_id') or port.get('tenant_id')
|
||||||
default_sg = self._ensure_default_security_group(context,
|
default_sg = self._ensure_default_security_group(context,
|
||||||
port_project)
|
port_project)
|
||||||
if default_sg:
|
if default_sg:
|
||||||
|
@ -13,6 +13,8 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
import warnings
|
||||||
|
|
||||||
from neutron_lib.api import converters
|
from neutron_lib.api import converters
|
||||||
from neutron_lib.api import extensions as api_extensions
|
from neutron_lib.api import extensions as api_extensions
|
||||||
from neutron_lib.api import faults
|
from neutron_lib.api import faults
|
||||||
@ -74,20 +76,20 @@ class QuotaSetsController(wsgi.Controller):
|
|||||||
'is_visible': True}
|
'is_visible': True}
|
||||||
self._update_extended_attributes = False
|
self._update_extended_attributes = False
|
||||||
|
|
||||||
def _get_quotas(self, request, tenant_id):
|
def _get_quotas(self, request, project_id):
|
||||||
return self._driver.get_tenant_quotas(
|
return self._driver.get_project_quotas(
|
||||||
request.context,
|
request.context,
|
||||||
resource_registry.get_all_resources(),
|
resource_registry.get_all_resources(),
|
||||||
tenant_id)
|
project_id)
|
||||||
|
|
||||||
def default(self, request, id):
|
def default(self, request, id):
|
||||||
context = request.context
|
context = request.context
|
||||||
if id != context.tenant_id:
|
if id != context.project_id:
|
||||||
validate_policy(context, "get_quota")
|
validate_policy(context, "get_quota")
|
||||||
return {self._resource_name: self._driver.get_default_quotas(
|
return {self._resource_name: self._driver.get_default_quotas(
|
||||||
context=context,
|
context=context,
|
||||||
resources=resource_registry.get_all_resources(),
|
resources=resource_registry.get_all_resources(),
|
||||||
tenant_id=id)}
|
project_id=id)}
|
||||||
|
|
||||||
def create(self, request, body=None):
|
def create(self, request, body=None):
|
||||||
msg = _('POST requests are not supported on this resource.')
|
msg = _('POST requests are not supported on this resource.')
|
||||||
@ -101,20 +103,31 @@ class QuotaSetsController(wsgi.Controller):
|
|||||||
context, resource_registry.get_all_resources())}
|
context, resource_registry.get_all_resources())}
|
||||||
|
|
||||||
def tenant(self, request):
|
def tenant(self, request):
|
||||||
"""Retrieve the tenant info in context."""
|
"""Retrieve the project info in context."""
|
||||||
|
warnings.warn(
|
||||||
|
'"tenant" Quota API method is deprecated, use "project" instead')
|
||||||
|
return self._project(request, 'tenant')
|
||||||
|
|
||||||
|
def project(self, request):
|
||||||
|
"""Retrieve the project info in context."""
|
||||||
|
return self._project(request, 'project')
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _project(request, key):
|
||||||
|
"""Retrieve the project info in context."""
|
||||||
context = request.context
|
context = request.context
|
||||||
if not context.tenant_id:
|
if not context.project_id:
|
||||||
raise exceptions.QuotaMissingTenant()
|
raise exceptions.QuotaMissingTenant()
|
||||||
return {'tenant': {'tenant_id': context.tenant_id}}
|
return {key: {key + '_id': context.project_id}}
|
||||||
|
|
||||||
def show(self, request, id):
|
def show(self, request, id):
|
||||||
if id != request.context.tenant_id:
|
if id != request.context.project_id:
|
||||||
validate_policy(request.context, "get_quota")
|
validate_policy(request.context, "get_quota")
|
||||||
return {self._resource_name: self._get_quotas(request, id)}
|
return {self._resource_name: self._get_quotas(request, id)}
|
||||||
|
|
||||||
def delete(self, request, id):
|
def delete(self, request, id):
|
||||||
validate_policy(request.context, "delete_quota")
|
validate_policy(request.context, "delete_quota")
|
||||||
self._driver.delete_tenant_quota(request.context, id)
|
self._driver.delete_project_quota(request.context, id)
|
||||||
|
|
||||||
def update(self, request, id, body=None):
|
def update(self, request, id, body=None):
|
||||||
validate_policy(request.context, "update_quota")
|
validate_policy(request.context, "update_quota")
|
||||||
@ -152,7 +165,7 @@ class Quotasv2(api_extensions.ExtensionDescriptor):
|
|||||||
def get_description(cls):
|
def get_description(cls):
|
||||||
description = 'Expose functions for quotas management'
|
description = 'Expose functions for quotas management'
|
||||||
if cfg.CONF.QUOTAS.quota_driver == DB_QUOTA_DRIVER:
|
if cfg.CONF.QUOTAS.quota_driver == DB_QUOTA_DRIVER:
|
||||||
description += ' per tenant'
|
description += ' per project'
|
||||||
return description
|
return description
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -169,7 +182,8 @@ class Quotasv2(api_extensions.ExtensionDescriptor):
|
|||||||
Quotasv2.get_alias(),
|
Quotasv2.get_alias(),
|
||||||
controller,
|
controller,
|
||||||
member_actions={DEFAULT_QUOTAS_ACTION: 'GET'},
|
member_actions={DEFAULT_QUOTAS_ACTION: 'GET'},
|
||||||
collection_actions={'tenant': 'GET'})]
|
collection_actions={'tenant': 'GET',
|
||||||
|
'project': 'GET'})]
|
||||||
|
|
||||||
def get_extended_resources(self, version):
|
def get_extended_resources(self, version):
|
||||||
if version == "2.0":
|
if version == "2.0":
|
||||||
|
@ -46,17 +46,17 @@ EXTENDED_ATTRIBUTES_2_0 = {
|
|||||||
|
|
||||||
class DetailQuotaSetsController(quotasv2.QuotaSetsController):
|
class DetailQuotaSetsController(quotasv2.QuotaSetsController):
|
||||||
|
|
||||||
def _get_detailed_quotas(self, request, tenant_id):
|
def _get_detailed_quotas(self, request, project_id):
|
||||||
return self._driver.get_detailed_tenant_quotas(
|
return self._driver.get_detailed_project_quotas(
|
||||||
request.context,
|
request.context,
|
||||||
resource_registry.get_all_resources(), tenant_id)
|
resource_registry.get_all_resources(), project_id)
|
||||||
|
|
||||||
def details(self, request, id):
|
def details(self, request, id):
|
||||||
if id != request.context.project_id:
|
if id != request.context.project_id:
|
||||||
# Check if admin
|
# Check if admin
|
||||||
if not request.context.is_admin:
|
if not request.context.is_admin:
|
||||||
reason = _("Only admin is authorized to access quotas for"
|
reason = _("Only admin is authorized to access quotas for "
|
||||||
" another tenant")
|
"another project")
|
||||||
raise n_exc.AdminRequired(reason=reason)
|
raise n_exc.AdminRequired(reason=reason)
|
||||||
return {self._resource_name:
|
return {self._resource_name:
|
||||||
self._get_detailed_quotas(request, id)}
|
self._get_detailed_quotas(request, id)}
|
||||||
@ -95,7 +95,8 @@ class Quotasv2_detail(api_extensions.ExtensionDescriptor):
|
|||||||
RESOURCE_COLLECTION,
|
RESOURCE_COLLECTION,
|
||||||
controller,
|
controller,
|
||||||
member_actions={'details': 'GET'},
|
member_actions={'details': 'GET'},
|
||||||
collection_actions={'tenant': 'GET'})]
|
collection_actions={'tenant': 'GET',
|
||||||
|
'project': 'GET'})]
|
||||||
|
|
||||||
def get_extended_resources(self, version):
|
def get_extended_resources(self, version):
|
||||||
return EXTENDED_ATTRIBUTES_2_0 if version == "2.0" else {}
|
return EXTENDED_ATTRIBUTES_2_0 if version == "2.0" else {}
|
||||||
|
@ -1051,7 +1051,8 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
|
|||||||
|
|
||||||
def _create_network_db(self, context, network):
|
def _create_network_db(self, context, network):
|
||||||
net_data = network[net_def.RESOURCE_NAME]
|
net_data = network[net_def.RESOURCE_NAME]
|
||||||
tenant_id = net_data['tenant_id']
|
# TODO(ralonsoh): "tenant_id" reference should be removed.
|
||||||
|
tenant_id = net_data.get('project_id') or net_data['tenant_id']
|
||||||
with db_api.CONTEXT_WRITER.using(context):
|
with db_api.CONTEXT_WRITER.using(context):
|
||||||
net_db = self.create_network_db(context, network)
|
net_db = self.create_network_db(context, network)
|
||||||
net_data['id'] = net_db.id
|
net_data['id'] = net_db.id
|
||||||
|
@ -30,7 +30,7 @@ from neutron.db.quota import api as quota_api
|
|||||||
LOG = log.getLogger(__name__)
|
LOG = log.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def _count_resource(context, collection_name, tenant_id):
|
def _count_resource(context, collection_name, project_id):
|
||||||
count_getter_name = "get_%s_count" % collection_name
|
count_getter_name = "get_%s_count" % collection_name
|
||||||
getter_name = "get_%s" % collection_name
|
getter_name = "get_%s" % collection_name
|
||||||
|
|
||||||
@ -45,12 +45,12 @@ def _count_resource(context, collection_name, tenant_id):
|
|||||||
try:
|
try:
|
||||||
obj_count_getter = getattr(plugins[pname], count_getter_name)
|
obj_count_getter = getattr(plugins[pname], count_getter_name)
|
||||||
return obj_count_getter(
|
return obj_count_getter(
|
||||||
context, filters={'tenant_id': [tenant_id]})
|
context, filters={'project_id': [project_id]})
|
||||||
except (NotImplementedError, AttributeError):
|
except (NotImplementedError, AttributeError):
|
||||||
try:
|
try:
|
||||||
obj_getter = getattr(plugins[pname], getter_name)
|
obj_getter = getattr(plugins[pname], getter_name)
|
||||||
obj_list = obj_getter(
|
obj_list = obj_getter(
|
||||||
context, filters={'tenant_id': [tenant_id]})
|
context, filters={'project_id': [project_id]})
|
||||||
return len(obj_list) if obj_list else 0
|
return len(obj_list) if obj_list else 0
|
||||||
except (NotImplementedError, AttributeError):
|
except (NotImplementedError, AttributeError):
|
||||||
pass
|
pass
|
||||||
@ -172,7 +172,7 @@ class TrackedResource(BaseResource):
|
|||||||
usage data are employed when performing quota checks.
|
usage data are employed when performing quota checks.
|
||||||
|
|
||||||
This class operates under the assumption that the model class
|
This class operates under the assumption that the model class
|
||||||
describing the resource has a tenant identifier attribute.
|
describing the resource has a project identifier attribute.
|
||||||
|
|
||||||
:param name: The name of the resource, i.e., "networks".
|
:param name: The name of the resource, i.e., "networks".
|
||||||
:param model_class: The sqlalchemy model class of the resource for
|
:param model_class: The sqlalchemy model class of the resource for
|
||||||
@ -191,11 +191,11 @@ class TrackedResource(BaseResource):
|
|||||||
super(TrackedResource, self).__init__(
|
super(TrackedResource, self).__init__(
|
||||||
name, flag=flag, plural_name=plural_name)
|
name, flag=flag, plural_name=plural_name)
|
||||||
# Register events for addition/removal of records in the model class
|
# Register events for addition/removal of records in the model class
|
||||||
# As tenant_id is immutable for all Neutron objects there is no need
|
# As project_id is immutable for all Neutron objects there is no need
|
||||||
# to register a listener for update events
|
# to register a listener for update events
|
||||||
self._model_class = model_class
|
self._model_class = model_class
|
||||||
self._dirty_tenants = set()
|
self._dirty_projects = set()
|
||||||
self._out_of_sync_tenants = set()
|
self._out_of_sync_projects = set()
|
||||||
# NOTE(ralonsoh): "DbQuotaNoLockDriver" driver does not need to track
|
# NOTE(ralonsoh): "DbQuotaNoLockDriver" driver does not need to track
|
||||||
# the DB events or resync the resource quota usage.
|
# the DB events or resync the resource quota usage.
|
||||||
if cfg.CONF.QUOTAS.quota_driver == quota_conf.QUOTA_DB_DRIVER:
|
if cfg.CONF.QUOTAS.quota_driver == quota_conf.QUOTA_DB_DRIVER:
|
||||||
@ -207,78 +207,78 @@ class TrackedResource(BaseResource):
|
|||||||
def dirty(self):
|
def dirty(self):
|
||||||
if not self._track_resource_events:
|
if not self._track_resource_events:
|
||||||
return
|
return
|
||||||
return self._dirty_tenants
|
return self._dirty_projects
|
||||||
|
|
||||||
def mark_dirty(self, context):
|
def mark_dirty(self, context):
|
||||||
if not self._dirty_tenants or not self._track_resource_events:
|
if not self._dirty_projects or not self._track_resource_events:
|
||||||
return
|
return
|
||||||
with db_api.CONTEXT_WRITER.using(context):
|
with db_api.CONTEXT_WRITER.using(context):
|
||||||
# It is not necessary to protect this operation with a lock.
|
# It is not necessary to protect this operation with a lock.
|
||||||
# Indeed when this method is called the request has been processed
|
# Indeed when this method is called the request has been processed
|
||||||
# and therefore all resources created or deleted.
|
# and therefore all resources created or deleted.
|
||||||
# dirty_tenants will contain all the tenants for which the
|
# dirty_projects will contain all the projects for which the
|
||||||
# resource count is changed. The list might contain also tenants
|
# resource count is changed. The list might contain also projects
|
||||||
# for which resource count was altered in other requests, but this
|
# for which resource count was altered in other requests, but this
|
||||||
# won't be harmful.
|
# won't be harmful.
|
||||||
dirty_tenants_snap = self._dirty_tenants.copy()
|
dirty_projects_snap = self._dirty_projects.copy()
|
||||||
for tenant_id in dirty_tenants_snap:
|
for project_id in dirty_projects_snap:
|
||||||
quota_api.set_quota_usage_dirty(context, self.name, tenant_id)
|
quota_api.set_quota_usage_dirty(context, self.name, project_id)
|
||||||
self._out_of_sync_tenants |= dirty_tenants_snap
|
self._out_of_sync_projects |= dirty_projects_snap
|
||||||
self._dirty_tenants -= dirty_tenants_snap
|
self._dirty_projects -= dirty_projects_snap
|
||||||
|
|
||||||
def _db_event_handler(self, mapper, _conn, target):
|
def _db_event_handler(self, mapper, _conn, target):
|
||||||
try:
|
try:
|
||||||
tenant_id = target['tenant_id']
|
project_id = target['project_id']
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
with excutils.save_and_reraise_exception():
|
with excutils.save_and_reraise_exception():
|
||||||
LOG.error("Model class %s does not have a tenant_id "
|
LOG.error("Model class %s does not have a project_id "
|
||||||
"attribute", target)
|
"attribute", target)
|
||||||
self._dirty_tenants.add(tenant_id)
|
self._dirty_projects.add(project_id)
|
||||||
|
|
||||||
# Retry the operation if a duplicate entry exception is raised. This
|
# Retry the operation if a duplicate entry exception is raised. This
|
||||||
# can happen is two or more workers are trying to create a resource of a
|
# can happen is two or more workers are trying to create a resource of a
|
||||||
# give kind for the same tenant concurrently. Retrying the operation will
|
# give kind for the same project concurrently. Retrying the operation will
|
||||||
# ensure that an UPDATE statement is emitted rather than an INSERT one
|
# ensure that an UPDATE statement is emitted rather than an INSERT one
|
||||||
@db_api.retry_if_session_inactive()
|
@db_api.retry_if_session_inactive()
|
||||||
def _set_quota_usage(self, context, tenant_id, in_use):
|
def _set_quota_usage(self, context, project_id, in_use):
|
||||||
return quota_api.set_quota_usage(
|
return quota_api.set_quota_usage(
|
||||||
context, self.name, tenant_id, in_use=in_use)
|
context, self.name, project_id, in_use=in_use)
|
||||||
|
|
||||||
def _resync(self, context, tenant_id, in_use):
|
def _resync(self, context, project_id, in_use):
|
||||||
# Update quota usage
|
# Update quota usage
|
||||||
usage_info = self._set_quota_usage(context, tenant_id, in_use)
|
usage_info = self._set_quota_usage(context, project_id, in_use)
|
||||||
|
|
||||||
self._dirty_tenants.discard(tenant_id)
|
self._dirty_projects.discard(project_id)
|
||||||
self._out_of_sync_tenants.discard(tenant_id)
|
self._out_of_sync_projects.discard(project_id)
|
||||||
LOG.debug(("Unset dirty status for tenant:%(tenant_id)s on "
|
LOG.debug(("Unset dirty status for project:%(project_id)s on "
|
||||||
"resource:%(resource)s"),
|
"resource:%(resource)s"),
|
||||||
{'tenant_id': tenant_id, 'resource': self.name})
|
{'project_id': project_id, 'resource': self.name})
|
||||||
return usage_info
|
return usage_info
|
||||||
|
|
||||||
def resync(self, context, tenant_id):
|
def resync(self, context, project_id):
|
||||||
if (tenant_id not in self._out_of_sync_tenants or
|
if (project_id not in self._out_of_sync_projects or
|
||||||
not self._track_resource_events):
|
not self._track_resource_events):
|
||||||
return
|
return
|
||||||
LOG.debug(("Synchronizing usage tracker for tenant:%(tenant_id)s on "
|
LOG.debug(("Synchronizing usage tracker for project:%(project_id)s on "
|
||||||
"resource:%(resource)s"),
|
"resource:%(resource)s"),
|
||||||
{'tenant_id': tenant_id, 'resource': self.name})
|
{'project_id': project_id, 'resource': self.name})
|
||||||
in_use = context.session.query(
|
in_use = context.session.query(
|
||||||
self._model_class.tenant_id).filter_by(
|
self._model_class.project_id).filter_by(
|
||||||
tenant_id=tenant_id).count()
|
project_id=project_id).count()
|
||||||
# Update quota usage
|
# Update quota usage
|
||||||
return self._resync(context, tenant_id, in_use)
|
return self._resync(context, project_id, in_use)
|
||||||
|
|
||||||
def count_used(self, context, tenant_id, resync_usage=True):
|
def count_used(self, context, project_id, resync_usage=True):
|
||||||
"""Returns the current usage count for the resource.
|
"""Returns the current usage count for the resource.
|
||||||
|
|
||||||
:param context: The request context.
|
:param context: The request context.
|
||||||
:param tenant_id: The ID of the tenant
|
:param project_id: The ID of the project
|
||||||
:param resync_usage: Default value is set to True. Syncs
|
:param resync_usage: Default value is set to True. Syncs
|
||||||
with in_use usage.
|
with in_use usage.
|
||||||
"""
|
"""
|
||||||
# Load current usage data, setting a row-level lock on the DB
|
# Load current usage data, setting a row-level lock on the DB
|
||||||
usage_info = quota_api.get_quota_usage_by_resource_and_tenant(
|
usage_info = quota_api.get_quota_usage_by_resource_and_project(
|
||||||
context, self.name, tenant_id)
|
context, self.name, project_id)
|
||||||
|
|
||||||
# If dirty or missing, calculate actual resource usage querying
|
# If dirty or missing, calculate actual resource usage querying
|
||||||
# the database and set/create usage info data
|
# the database and set/create usage info data
|
||||||
@ -286,27 +286,28 @@ class TrackedResource(BaseResource):
|
|||||||
# assumption is generally valid, but if the database is tampered with,
|
# assumption is generally valid, but if the database is tampered with,
|
||||||
# or if data migrations do not take care of usage counters, the
|
# or if data migrations do not take care of usage counters, the
|
||||||
# assumption will not hold anymore
|
# assumption will not hold anymore
|
||||||
if (tenant_id in self._dirty_tenants or
|
if (project_id in self._dirty_projects or
|
||||||
not usage_info or usage_info.dirty):
|
not usage_info or usage_info.dirty):
|
||||||
LOG.debug(("Usage tracker for resource:%(resource)s and tenant:"
|
LOG.debug(("Usage tracker for resource:%(resource)s and project:"
|
||||||
"%(tenant_id)s is out of sync, need to count used "
|
"%(project_id)s is out of sync, need to count used "
|
||||||
"quota"), {'resource': self.name,
|
"quota"), {'resource': self.name,
|
||||||
'tenant_id': tenant_id})
|
'project_id': project_id})
|
||||||
in_use = context.session.query(
|
in_use = context.session.query(
|
||||||
self._model_class.tenant_id).filter_by(
|
self._model_class.project_id).filter_by(
|
||||||
tenant_id=tenant_id).count()
|
project_id=project_id).count()
|
||||||
|
|
||||||
# Update quota usage, if requested (by default do not do that, as
|
# Update quota usage, if requested (by default do not do that, as
|
||||||
# typically one counts before adding a record, and that would mark
|
# typically one counts before adding a record, and that would mark
|
||||||
# the usage counter as dirty again)
|
# the usage counter as dirty again)
|
||||||
if resync_usage:
|
if resync_usage:
|
||||||
usage_info = self._resync(context, tenant_id, in_use)
|
usage_info = self._resync(context, project_id, in_use)
|
||||||
else:
|
else:
|
||||||
resource = usage_info.resource if usage_info else self.name
|
resource = usage_info.resource if usage_info else self.name
|
||||||
tenant_id = usage_info.tenant_id if usage_info else tenant_id
|
project_id = (usage_info.project_id if usage_info else
|
||||||
|
project_id)
|
||||||
dirty = usage_info.dirty if usage_info else True
|
dirty = usage_info.dirty if usage_info else True
|
||||||
usage_info = quota_api.QuotaUsageInfo(
|
usage_info = quota_api.QuotaUsageInfo(
|
||||||
resource, tenant_id, in_use, dirty)
|
resource, project_id, in_use, dirty)
|
||||||
|
|
||||||
LOG.debug(("Quota usage for %(resource)s was recalculated. "
|
LOG.debug(("Quota usage for %(resource)s was recalculated. "
|
||||||
"Used quota:%(used)d."),
|
"Used quota:%(used)d."),
|
||||||
@ -314,16 +315,16 @@ class TrackedResource(BaseResource):
|
|||||||
'used': usage_info.used})
|
'used': usage_info.used})
|
||||||
return usage_info.used
|
return usage_info.used
|
||||||
|
|
||||||
def count_reserved(self, context, tenant_id):
|
def count_reserved(self, context, project_id):
|
||||||
"""Return the current reservation count for the resource."""
|
"""Return the current reservation count for the resource."""
|
||||||
# NOTE(princenana) Current implementation of reservations
|
# NOTE(princenana) Current implementation of reservations
|
||||||
# is ephemeral and returns the default value
|
# is ephemeral and returns the default value
|
||||||
reservations = quota_api.get_reservations_for_resources(
|
reservations = quota_api.get_reservations_for_resources(
|
||||||
context, tenant_id, [self.name])
|
context, project_id, [self.name])
|
||||||
reserved = reservations.get(self.name, 0)
|
reserved = reservations.get(self.name, 0)
|
||||||
return reserved
|
return reserved
|
||||||
|
|
||||||
def count(self, context, _plugin, tenant_id, resync_usage=True,
|
def count(self, context, _plugin, project_id, resync_usage=True,
|
||||||
count_db_registers=False):
|
count_db_registers=False):
|
||||||
"""Return the count of the resource.
|
"""Return the count of the resource.
|
||||||
|
|
||||||
@ -332,11 +333,11 @@ class TrackedResource(BaseResource):
|
|||||||
CountableResource instances.
|
CountableResource instances.
|
||||||
"""
|
"""
|
||||||
if count_db_registers:
|
if count_db_registers:
|
||||||
count = self._count_db_registers(context, tenant_id)
|
count = self._count_db_registers(context, project_id)
|
||||||
else:
|
else:
|
||||||
count = self.count_used(context, tenant_id, resync_usage)
|
count = self.count_used(context, project_id, resync_usage)
|
||||||
|
|
||||||
return count + self.count_reserved(context, tenant_id)
|
return count + self.count_reserved(context, project_id)
|
||||||
|
|
||||||
def _count_db_registers(self, context, project_id):
|
def _count_db_registers(self, context, project_id):
|
||||||
"""Return the existing resources (self._model_class) in a project.
|
"""Return the existing resources (self._model_class) in a project.
|
||||||
|
@ -192,7 +192,7 @@ class TestQuotasController(test_functional.PecanFunctionalTest):
|
|||||||
def test_index_admin(self):
|
def test_index_admin(self):
|
||||||
# NOTE(salv-orlando): The quota controller has an hardcoded check for
|
# NOTE(salv-orlando): The quota controller has an hardcoded check for
|
||||||
# admin-ness for this operation, which is supposed to return quotas for
|
# admin-ness for this operation, which is supposed to return quotas for
|
||||||
# all tenants. Such check is "vestigial" from the home-grown WSGI and
|
# all projects. Such check is "vestigial" from the home-grown WSGI and
|
||||||
# shall be removed
|
# shall be removed
|
||||||
response = self.app.get('%s.json' % self.base_url,
|
response = self.app.get('%s.json' % self.base_url,
|
||||||
headers={'X-Project-Id': 'admin',
|
headers={'X-Project-Id': 'admin',
|
||||||
@ -213,7 +213,7 @@ class TestQuotasController(test_functional.PecanFunctionalTest):
|
|||||||
self._verify_default_limits(json_body)
|
self._verify_default_limits(json_body)
|
||||||
|
|
||||||
def test_get(self):
|
def test_get(self):
|
||||||
# It is not ok to access another tenant's limits
|
# It is not ok to access another project's limits
|
||||||
url = '%s/foo.json' % self.base_url
|
url = '%s/foo.json' % self.base_url
|
||||||
response = self.app.get(url, expect_errors=True)
|
response = self.app.get(url, expect_errors=True)
|
||||||
self.assertEqual(403, response.status_int)
|
self.assertEqual(403, response.status_int)
|
||||||
@ -269,7 +269,7 @@ class TestQuotasController(test_functional.PecanFunctionalTest):
|
|||||||
json_body = jsonutils.loads(response.body)
|
json_body = jsonutils.loads(response.body)
|
||||||
found = False
|
found = False
|
||||||
for qs in json_body['quotas']:
|
for qs in json_body['quotas']:
|
||||||
if qs['tenant_id'] == 'foo':
|
if qs['project_id'] == 'foo':
|
||||||
found = True
|
found = True
|
||||||
self.assertTrue(found)
|
self.assertTrue(found)
|
||||||
|
|
||||||
@ -283,7 +283,7 @@ class TestQuotasController(test_functional.PecanFunctionalTest):
|
|||||||
self.assertEqual(200, response.status_int)
|
self.assertEqual(200, response.status_int)
|
||||||
json_body = jsonutils.loads(response.body)
|
json_body = jsonutils.loads(response.body)
|
||||||
for qs in json_body['quotas']:
|
for qs in json_body['quotas']:
|
||||||
self.assertNotEqual('foo', qs['tenant_id'])
|
self.assertNotEqual('foo', qs['project_id'])
|
||||||
|
|
||||||
def test_quotas_get_defaults(self):
|
def test_quotas_get_defaults(self):
|
||||||
response = self.app.get('%s/foo/default.json' % self.base_url,
|
response = self.app.get('%s/foo/default.json' % self.base_url,
|
||||||
@ -294,13 +294,14 @@ class TestQuotasController(test_functional.PecanFunctionalTest):
|
|||||||
json_body = jsonutils.loads(response.body)
|
json_body = jsonutils.loads(response.body)
|
||||||
self._verify_default_limits(json_body)
|
self._verify_default_limits(json_body)
|
||||||
|
|
||||||
def test_get_tenant_info(self):
|
def test_get_project_info(self):
|
||||||
response = self.app.get('%s/tenant.json' % self.base_url,
|
for key in ('project', 'tenant'):
|
||||||
headers={'X-Project-Id': 'admin',
|
response = self.app.get('%s/%s.json' % (self.base_url, key),
|
||||||
'X-Roles': 'admin'})
|
headers={'X-Project-Id': 'admin',
|
||||||
self.assertEqual(200, response.status_int)
|
'X-Roles': 'admin'})
|
||||||
json_body = jsonutils.loads(response.body)
|
self.assertEqual(200, response.status_int)
|
||||||
self.assertEqual('admin', json_body['tenant']['tenant_id'])
|
json_body = jsonutils.loads(response.body)
|
||||||
|
self.assertEqual('admin', json_body[key][key + '_id'])
|
||||||
|
|
||||||
|
|
||||||
class TestResourceController(TestRootController):
|
class TestResourceController(TestRootController):
|
||||||
@ -318,11 +319,11 @@ class TestResourceController(TestRootController):
|
|||||||
def _gen_port(self):
|
def _gen_port(self):
|
||||||
network_id = self.plugin.create_network(context.get_admin_context(), {
|
network_id = self.plugin.create_network(context.get_admin_context(), {
|
||||||
'network':
|
'network':
|
||||||
{'name': 'pecannet', 'tenant_id': 'tenid', 'shared': False,
|
{'name': 'pecannet', 'project_id': 'tenid', 'shared': False,
|
||||||
'admin_state_up': True, 'status': 'ACTIVE'}})['id']
|
'admin_state_up': True, 'status': 'ACTIVE'}})['id']
|
||||||
self.port = self.plugin.create_port(context.get_admin_context(), {
|
self.port = self.plugin.create_port(context.get_admin_context(), {
|
||||||
'port':
|
'port':
|
||||||
{'tenant_id': 'tenid', 'network_id': network_id,
|
{'project_id': 'tenid', 'network_id': network_id,
|
||||||
'fixed_ips': n_const.ATTR_NOT_SPECIFIED,
|
'fixed_ips': n_const.ATTR_NOT_SPECIFIED,
|
||||||
'mac_address': '00:11:22:33:44:55',
|
'mac_address': '00:11:22:33:44:55',
|
||||||
'admin_state_up': True, 'device_id': 'FF',
|
'admin_state_up': True, 'device_id': 'FF',
|
||||||
@ -364,7 +365,7 @@ class TestResourceController(TestRootController):
|
|||||||
self._test_get_collection_with_fields_selector(fields=[])
|
self._test_get_collection_with_fields_selector(fields=[])
|
||||||
|
|
||||||
def test_project_id_in_mandatory_fields(self):
|
def test_project_id_in_mandatory_fields(self):
|
||||||
# ports only specifies that tenant_id is mandatory, but project_id
|
# ports only specifies that project_id is mandatory, but project_id
|
||||||
# should still be passed to the plugin.
|
# should still be passed to the plugin.
|
||||||
mock_get = mock.patch.object(self.plugin, 'get_ports',
|
mock_get = mock.patch.object(self.plugin, 'get_ports',
|
||||||
return_value=[]).start()
|
return_value=[]).start()
|
||||||
@ -384,10 +385,10 @@ class TestResourceController(TestRootController):
|
|||||||
# Explicitly require an attribute which is also 'required_by_policy'.
|
# Explicitly require an attribute which is also 'required_by_policy'.
|
||||||
# The attribute should not be stripped while generating the response
|
# The attribute should not be stripped while generating the response
|
||||||
item_resp = self.app.get(
|
item_resp = self.app.get(
|
||||||
'/v2.0/ports/%s.json?fields=id&fields=tenant_id' % self.port['id'],
|
'/v2.0/ports/%s.json?fields=id&fields=project_id' %
|
||||||
headers={'X-Project-Id': 'tenid'})
|
self.port['id'], headers={'X-Project-Id': 'tenid'})
|
||||||
self.assertEqual(200, item_resp.status_int)
|
self.assertEqual(200, item_resp.status_int)
|
||||||
self._check_item(['id', 'tenant_id'],
|
self._check_item(['id', 'project_id'],
|
||||||
jsonutils.loads(item_resp.body)['port'])
|
jsonutils.loads(item_resp.body)['port'])
|
||||||
|
|
||||||
def test_duped_and_empty_fields_stripped(self):
|
def test_duped_and_empty_fields_stripped(self):
|
||||||
@ -406,7 +407,7 @@ class TestResourceController(TestRootController):
|
|||||||
'/v2.0/ports.json',
|
'/v2.0/ports.json',
|
||||||
params={'port': {'network_id': self.port['network_id'],
|
params={'port': {'network_id': self.port['network_id'],
|
||||||
'admin_state_up': True,
|
'admin_state_up': True,
|
||||||
'tenant_id': 'tenid'}},
|
'project_id': 'tenid'}},
|
||||||
headers={'X-Project-Id': 'tenid'})
|
headers={'X-Project-Id': 'tenid'})
|
||||||
self.assertEqual(response.status_int, 201)
|
self.assertEqual(response.status_int, 201)
|
||||||
|
|
||||||
@ -426,7 +427,7 @@ class TestResourceController(TestRootController):
|
|||||||
'/v2.0/ports.json',
|
'/v2.0/ports.json',
|
||||||
params={'port': {'network_id': self.port['network_id'],
|
params={'port': {'network_id': self.port['network_id'],
|
||||||
'admin_state_up': True,
|
'admin_state_up': True,
|
||||||
'tenant_id': 'tenid'}},
|
'project_id': 'tenid'}},
|
||||||
headers={'X-Project-Id': 'tenid'})
|
headers={'X-Project-Id': 'tenid'})
|
||||||
self.assertEqual(201, response.status_int)
|
self.assertEqual(201, response.status_int)
|
||||||
|
|
||||||
@ -439,7 +440,7 @@ class TestResourceController(TestRootController):
|
|||||||
self.assertEqual(1, len(json_body))
|
self.assertEqual(1, len(json_body))
|
||||||
self.assertIn('port', json_body)
|
self.assertIn('port', json_body)
|
||||||
self.assertEqual('test', json_body['port']['name'])
|
self.assertEqual('test', json_body['port']['name'])
|
||||||
self.assertEqual('tenid', json_body['port']['tenant_id'])
|
self.assertEqual('tenid', json_body['port']['project_id'])
|
||||||
|
|
||||||
def test_delete(self):
|
def test_delete(self):
|
||||||
response = self.app.delete('/v2.0/ports/%s.json' % self.port['id'],
|
response = self.app.delete('/v2.0/ports/%s.json' % self.port['id'],
|
||||||
@ -487,10 +488,10 @@ class TestResourceController(TestRootController):
|
|||||||
'/v2.0/ports.json',
|
'/v2.0/ports.json',
|
||||||
params={'ports': [{'network_id': self.port['network_id'],
|
params={'ports': [{'network_id': self.port['network_id'],
|
||||||
'admin_state_up': True,
|
'admin_state_up': True,
|
||||||
'tenant_id': 'tenid'},
|
'project_id': 'tenid'},
|
||||||
{'network_id': self.port['network_id'],
|
{'network_id': self.port['network_id'],
|
||||||
'admin_state_up': True,
|
'admin_state_up': True,
|
||||||
'tenant_id': 'tenid'}]
|
'project_id': 'tenid'}]
|
||||||
},
|
},
|
||||||
headers={'X-Project-Id': 'tenid'})
|
headers={'X-Project-Id': 'tenid'})
|
||||||
self.assertEqual(201, response.status_int)
|
self.assertEqual(201, response.status_int)
|
||||||
@ -518,11 +519,11 @@ class TestResourceController(TestRootController):
|
|||||||
params={'ports': [{'network_id': self.port['network_id'],
|
params={'ports': [{'network_id': self.port['network_id'],
|
||||||
'admin_state_up': True,
|
'admin_state_up': True,
|
||||||
'security_groups': [sg_id],
|
'security_groups': [sg_id],
|
||||||
'tenant_id': 'tenid'},
|
'project_id': 'tenid'},
|
||||||
{'network_id': self.port['network_id'],
|
{'network_id': self.port['network_id'],
|
||||||
'admin_state_up': True,
|
'admin_state_up': True,
|
||||||
'security_groups': [sg_id],
|
'security_groups': [sg_id],
|
||||||
'tenant_id': 'tenid'}]
|
'project_id': 'tenid'}]
|
||||||
},
|
},
|
||||||
headers={'X-Project-Id': 'tenid'})
|
headers={'X-Project-Id': 'tenid'})
|
||||||
self.assertEqual(201, port_response.status_int)
|
self.assertEqual(201, port_response.status_int)
|
||||||
@ -539,10 +540,10 @@ class TestResourceController(TestRootController):
|
|||||||
'/v2.0/ports.json',
|
'/v2.0/ports.json',
|
||||||
params={'ports': [{'network_id': self.port['network_id'],
|
params={'ports': [{'network_id': self.port['network_id'],
|
||||||
'admin_state_up': True,
|
'admin_state_up': True,
|
||||||
'tenant_id': 'tenid'},
|
'project_id': 'tenid'},
|
||||||
{'network_id': self.port['network_id'],
|
{'network_id': self.port['network_id'],
|
||||||
'admin_state_up': True,
|
'admin_state_up': True,
|
||||||
'tenant_id': 'tenid'}]
|
'project_id': 'tenid'}]
|
||||||
},
|
},
|
||||||
headers={'X-Project-Id': 'tenid'})
|
headers={'X-Project-Id': 'tenid'})
|
||||||
self.assertEqual(response.status_int, 201)
|
self.assertEqual(response.status_int, 201)
|
||||||
@ -556,13 +557,13 @@ class TestResourceController(TestRootController):
|
|||||||
'/v2.0/ports.json',
|
'/v2.0/ports.json',
|
||||||
params={'ports': [{'network_id': self.port['network_id'],
|
params={'ports': [{'network_id': self.port['network_id'],
|
||||||
'admin_state_up': True,
|
'admin_state_up': True,
|
||||||
'tenant_id': 'tenid'},
|
'project_id': 'tenid'},
|
||||||
{'network_id': self.port['network_id'],
|
{'network_id': self.port['network_id'],
|
||||||
'admin_state_up': True,
|
'admin_state_up': True,
|
||||||
'tenant_id': 'tenid'},
|
'project_id': 'tenid'},
|
||||||
{'network_id': 'bad_net_id',
|
{'network_id': 'bad_net_id',
|
||||||
'admin_state_up': True,
|
'admin_state_up': True,
|
||||||
'tenant_id': 'tenid'}]
|
'project_id': 'tenid'}]
|
||||||
},
|
},
|
||||||
headers={'X-Project-Id': 'tenid'},
|
headers={'X-Project-Id': 'tenid'},
|
||||||
expect_errors=True)
|
expect_errors=True)
|
||||||
@ -579,7 +580,7 @@ class TestResourceController(TestRootController):
|
|||||||
'/v2.0/ports.json',
|
'/v2.0/ports.json',
|
||||||
params={'ports': [{'network_id': self.port['network_id'],
|
params={'ports': [{'network_id': self.port['network_id'],
|
||||||
'admin_state_up': True,
|
'admin_state_up': True,
|
||||||
'tenant_id': 'tenid'}]
|
'project_id': 'tenid'}]
|
||||||
},
|
},
|
||||||
headers={'X-Project-Id': 'tenid'})
|
headers={'X-Project-Id': 'tenid'})
|
||||||
self.assertEqual(response.status_int, 201)
|
self.assertEqual(response.status_int, 201)
|
||||||
@ -598,13 +599,15 @@ class TestPaginationAndSorting(test_functional.PecanFunctionalTest):
|
|||||||
self.addCleanup(policy.reset)
|
self.addCleanup(policy.reset)
|
||||||
self.plugin = directory.get_plugin()
|
self.plugin = directory.get_plugin()
|
||||||
self.ctx = context.get_admin_context()
|
self.ctx = context.get_admin_context()
|
||||||
|
self._project_id = 'project_id1'
|
||||||
self._create_networks(self.RESOURCE_COUNT)
|
self._create_networks(self.RESOURCE_COUNT)
|
||||||
self.networks = self._get_collection()['networks']
|
self.networks = self._get_collection()['networks']
|
||||||
|
|
||||||
def _create_networks(self, count=1):
|
def _create_networks(self, count=1):
|
||||||
network_ids = []
|
network_ids = []
|
||||||
for index in range(count):
|
for index in range(count):
|
||||||
network = {'name': 'pecannet-%d' % index, 'tenant_id': 'tenid',
|
network = {'name': 'pecannet-%d' % index,
|
||||||
|
'project_id': self._project_id,
|
||||||
'shared': False, 'admin_state_up': True,
|
'shared': False, 'admin_state_up': True,
|
||||||
'status': 'ACTIVE'}
|
'status': 'ACTIVE'}
|
||||||
network_id = self.plugin.create_network(
|
network_id = self.plugin.create_network(
|
||||||
@ -632,7 +635,8 @@ class TestPaginationAndSorting(test_functional.PecanFunctionalTest):
|
|||||||
url = '/v2.0/%s.json' % collection
|
url = '/v2.0/%s.json' % collection
|
||||||
if query_params:
|
if query_params:
|
||||||
url = '%s?%s' % (url, '&'.join(query_params))
|
url = '%s?%s' % (url, '&'.join(query_params))
|
||||||
list_resp = self.app.get(url, headers={'X-Project-Id': 'tenid'})
|
list_resp = self.app.get(url,
|
||||||
|
headers={'X-Project-Id': self._project_id})
|
||||||
self.assertEqual(200, list_resp.status_int)
|
self.assertEqual(200, list_resp.status_int)
|
||||||
return list_resp.json
|
return list_resp.json
|
||||||
|
|
||||||
@ -735,9 +739,9 @@ class TestRequestProcessing(TestRootController):
|
|||||||
|
|
||||||
def test_context_set_in_request(self):
|
def test_context_set_in_request(self):
|
||||||
self.app.get('/v2.0/ports.json',
|
self.app.get('/v2.0/ports.json',
|
||||||
headers={'X-Project-Id': 'tenant_id'})
|
headers={'X-Project-Id': 'project_id'})
|
||||||
self.assertEqual('tenant_id',
|
self.assertEqual('project_id',
|
||||||
self.captured_context['neutron_context'].tenant_id)
|
self.captured_context['neutron_context'].project_id)
|
||||||
|
|
||||||
def test_core_resource_identified(self):
|
def test_core_resource_identified(self):
|
||||||
self.app.get('/v2.0/ports.json')
|
self.app.get('/v2.0/ports.json')
|
||||||
@ -1169,4 +1173,3 @@ class TestExcludeAttributePolicy(test_functional.PecanFunctionalTest):
|
|||||||
json_body = jsonutils.loads(response.body)
|
json_body = jsonutils.loads(response.body)
|
||||||
self.assertEqual(response.status_int, 200)
|
self.assertEqual(response.status_int, 200)
|
||||||
self.assertEqual('tenid', json_body['network']['project_id'])
|
self.assertEqual('tenid', json_body['network']['project_id'])
|
||||||
self.assertEqual('tenid', json_body['network']['tenant_id'])
|
|
||||||
|
@ -31,26 +31,26 @@ DB_PLUGIN_KLASS = 'neutron.db.db_base_plugin_v2.NeutronDbPluginV2'
|
|||||||
class TestQuotaDbApi(testlib_api.SqlTestCaseLight):
|
class TestQuotaDbApi(testlib_api.SqlTestCaseLight):
|
||||||
|
|
||||||
def _set_context(self):
|
def _set_context(self):
|
||||||
self.tenant_id = 'Higuain'
|
self.project_id = 'Higuain'
|
||||||
self.context = context.Context('Gonzalo', self.tenant_id,
|
self.context = context.Context('Gonzalo', self.project_id,
|
||||||
is_admin=False, is_advsvc=False)
|
is_admin=False, is_advsvc=False)
|
||||||
|
|
||||||
def _create_reservation(self, resource_deltas,
|
def _create_reservation(self, resource_deltas,
|
||||||
tenant_id=None, expiration=None):
|
project_id=None, expiration=None):
|
||||||
tenant_id = tenant_id or self.tenant_id
|
project_id = project_id or self.project_id
|
||||||
return quota_api.create_reservation(
|
return quota_api.create_reservation(
|
||||||
self.context, tenant_id, resource_deltas, expiration)
|
self.context, project_id, resource_deltas, expiration)
|
||||||
|
|
||||||
def _create_quota_usage(self, resource, used, tenant_id=None):
|
def _create_quota_usage(self, resource, used, project_id=None):
|
||||||
tenant_id = tenant_id or self.tenant_id
|
project_id = project_id or self.project_id
|
||||||
return quota_api.set_quota_usage(context.get_admin_context(),
|
return quota_api.set_quota_usage(context.get_admin_context(),
|
||||||
resource, tenant_id, in_use=used)
|
resource, project_id, in_use=used)
|
||||||
|
|
||||||
def _verify_quota_usage(self, usage_info,
|
def _verify_quota_usage(self, usage_info,
|
||||||
expected_resource=None,
|
expected_resource=None,
|
||||||
expected_used=None,
|
expected_used=None,
|
||||||
expected_dirty=None):
|
expected_dirty=None):
|
||||||
self.assertEqual(self.tenant_id, usage_info.tenant_id)
|
self.assertEqual(self.project_id, usage_info.project_id)
|
||||||
if expected_resource:
|
if expected_resource:
|
||||||
self.assertEqual(expected_resource, usage_info.resource)
|
self.assertEqual(expected_resource, usage_info.resource)
|
||||||
if expected_dirty is not None:
|
if expected_dirty is not None:
|
||||||
@ -75,12 +75,12 @@ class TestQuotaDbApi(testlib_api.SqlTestCaseLight):
|
|||||||
self._create_quota_usage('goals', 26)
|
self._create_quota_usage('goals', 26)
|
||||||
# Higuain scores a double
|
# Higuain scores a double
|
||||||
usage_info_1 = quota_api.set_quota_usage(
|
usage_info_1 = quota_api.set_quota_usage(
|
||||||
self.context, 'goals', self.tenant_id,
|
self.context, 'goals', self.project_id,
|
||||||
in_use=28)
|
in_use=28)
|
||||||
self._verify_quota_usage(usage_info_1,
|
self._verify_quota_usage(usage_info_1,
|
||||||
expected_used=28)
|
expected_used=28)
|
||||||
usage_info_2 = quota_api.set_quota_usage(
|
usage_info_2 = quota_api.set_quota_usage(
|
||||||
self.context, 'goals', self.tenant_id,
|
self.context, 'goals', self.project_id,
|
||||||
in_use=24)
|
in_use=24)
|
||||||
self._verify_quota_usage(usage_info_2,
|
self._verify_quota_usage(usage_info_2,
|
||||||
expected_used=24)
|
expected_used=24)
|
||||||
@ -89,7 +89,7 @@ class TestQuotaDbApi(testlib_api.SqlTestCaseLight):
|
|||||||
self._create_quota_usage('goals', 26)
|
self._create_quota_usage('goals', 26)
|
||||||
# Higuain scores a double
|
# Higuain scores a double
|
||||||
usage_info_1 = quota_api.set_quota_usage(
|
usage_info_1 = quota_api.set_quota_usage(
|
||||||
self.context, 'goals', self.tenant_id,
|
self.context, 'goals', self.project_id,
|
||||||
in_use=2, delta=True)
|
in_use=2, delta=True)
|
||||||
self._verify_quota_usage(usage_info_1,
|
self._verify_quota_usage(usage_info_1,
|
||||||
expected_used=28)
|
expected_used=28)
|
||||||
@ -98,35 +98,36 @@ class TestQuotaDbApi(testlib_api.SqlTestCaseLight):
|
|||||||
self._create_quota_usage('goals', 26)
|
self._create_quota_usage('goals', 26)
|
||||||
# Higuain needs a shower after the match
|
# Higuain needs a shower after the match
|
||||||
self.assertEqual(1, quota_api.set_quota_usage_dirty(
|
self.assertEqual(1, quota_api.set_quota_usage_dirty(
|
||||||
self.context, 'goals', self.tenant_id))
|
self.context, 'goals', self.project_id))
|
||||||
usage_info = quota_api.get_quota_usage_by_resource_and_tenant(
|
usage_info = quota_api.get_quota_usage_by_resource_and_project(
|
||||||
self.context, 'goals', self.tenant_id)
|
self.context, 'goals', self.project_id)
|
||||||
self._verify_quota_usage(usage_info,
|
self._verify_quota_usage(usage_info,
|
||||||
expected_dirty=True)
|
expected_dirty=True)
|
||||||
# Higuain is clean now
|
# Higuain is clean now
|
||||||
self.assertEqual(1, quota_api.set_quota_usage_dirty(
|
self.assertEqual(1, quota_api.set_quota_usage_dirty(
|
||||||
self.context, 'goals', self.tenant_id, dirty=False))
|
self.context, 'goals', self.project_id, dirty=False))
|
||||||
usage_info = quota_api.get_quota_usage_by_resource_and_tenant(
|
usage_info = quota_api.get_quota_usage_by_resource_and_project(
|
||||||
self.context, 'goals', self.tenant_id)
|
self.context, 'goals', self.project_id)
|
||||||
self._verify_quota_usage(usage_info,
|
self._verify_quota_usage(usage_info,
|
||||||
expected_dirty=False)
|
expected_dirty=False)
|
||||||
|
|
||||||
def test_set_dirty_non_existing_quota_usage(self):
|
def test_set_dirty_non_existing_quota_usage(self):
|
||||||
self.assertEqual(0, quota_api.set_quota_usage_dirty(
|
self.assertEqual(0, quota_api.set_quota_usage_dirty(
|
||||||
self.context, 'meh', self.tenant_id))
|
self.context, 'meh', self.project_id))
|
||||||
|
|
||||||
def test_set_resources_quota_usage_dirty(self):
|
def test_set_resources_quota_usage_dirty(self):
|
||||||
self._create_quota_usage('goals', 26)
|
self._create_quota_usage('goals', 26)
|
||||||
self._create_quota_usage('assists', 11)
|
self._create_quota_usage('assists', 11)
|
||||||
self._create_quota_usage('bookings', 3)
|
self._create_quota_usage('bookings', 3)
|
||||||
self.assertEqual(2, quota_api.set_resources_quota_usage_dirty(
|
self.assertEqual(2, quota_api.set_resources_quota_usage_dirty(
|
||||||
self.context, ['goals', 'bookings'], self.tenant_id))
|
self.context, ['goals', 'bookings'], self.project_id))
|
||||||
usage_info_goals = quota_api.get_quota_usage_by_resource_and_tenant(
|
usage_info_goals = quota_api.get_quota_usage_by_resource_and_project(
|
||||||
self.context, 'goals', self.tenant_id)
|
self.context, 'goals', self.project_id)
|
||||||
usage_info_assists = quota_api.get_quota_usage_by_resource_and_tenant(
|
usage_info_assists = quota_api.get_quota_usage_by_resource_and_project(
|
||||||
self.context, 'assists', self.tenant_id)
|
self.context, 'assists', self.project_id)
|
||||||
usage_info_bookings = quota_api.get_quota_usage_by_resource_and_tenant(
|
usage_info_bookings = (
|
||||||
self.context, 'bookings', self.tenant_id)
|
quota_api.get_quota_usage_by_resource_and_project(
|
||||||
|
self.context, 'bookings', self.project_id))
|
||||||
self._verify_quota_usage(usage_info_goals, expected_dirty=True)
|
self._verify_quota_usage(usage_info_goals, expected_dirty=True)
|
||||||
self._verify_quota_usage(usage_info_assists, expected_dirty=False)
|
self._verify_quota_usage(usage_info_assists, expected_dirty=False)
|
||||||
self._verify_quota_usage(usage_info_bookings, expected_dirty=True)
|
self._verify_quota_usage(usage_info_bookings, expected_dirty=True)
|
||||||
@ -135,30 +136,31 @@ class TestQuotaDbApi(testlib_api.SqlTestCaseLight):
|
|||||||
self._create_quota_usage('goals', 26)
|
self._create_quota_usage('goals', 26)
|
||||||
self._create_quota_usage('assists', 11)
|
self._create_quota_usage('assists', 11)
|
||||||
self._create_quota_usage('bookings', 3)
|
self._create_quota_usage('bookings', 3)
|
||||||
# Expect all the resources for the tenant to be set dirty
|
# Expect all the resources for the project to be set dirty
|
||||||
self.assertEqual(3, quota_api.set_resources_quota_usage_dirty(
|
self.assertEqual(3, quota_api.set_resources_quota_usage_dirty(
|
||||||
self.context, [], self.tenant_id))
|
self.context, [], self.project_id))
|
||||||
usage_info_goals = quota_api.get_quota_usage_by_resource_and_tenant(
|
usage_info_goals = quota_api.get_quota_usage_by_resource_and_project(
|
||||||
self.context, 'goals', self.tenant_id)
|
self.context, 'goals', self.project_id)
|
||||||
usage_info_assists = quota_api.get_quota_usage_by_resource_and_tenant(
|
usage_info_assists = quota_api.get_quota_usage_by_resource_and_project(
|
||||||
self.context, 'assists', self.tenant_id)
|
self.context, 'assists', self.project_id)
|
||||||
usage_info_bookings = quota_api.get_quota_usage_by_resource_and_tenant(
|
usage_info_bookings = (
|
||||||
self.context, 'bookings', self.tenant_id)
|
quota_api.get_quota_usage_by_resource_and_project(
|
||||||
|
self.context, 'bookings', self.project_id))
|
||||||
self._verify_quota_usage(usage_info_goals, expected_dirty=True)
|
self._verify_quota_usage(usage_info_goals, expected_dirty=True)
|
||||||
self._verify_quota_usage(usage_info_assists, expected_dirty=True)
|
self._verify_quota_usage(usage_info_assists, expected_dirty=True)
|
||||||
self._verify_quota_usage(usage_info_bookings, expected_dirty=True)
|
self._verify_quota_usage(usage_info_bookings, expected_dirty=True)
|
||||||
|
|
||||||
# Higuain is clean now
|
# Higuain is clean now
|
||||||
self.assertEqual(1, quota_api.set_quota_usage_dirty(
|
self.assertEqual(1, quota_api.set_quota_usage_dirty(
|
||||||
self.context, 'goals', self.tenant_id, dirty=False))
|
self.context, 'goals', self.project_id, dirty=False))
|
||||||
usage_info = quota_api.get_quota_usage_by_resource_and_tenant(
|
usage_info = quota_api.get_quota_usage_by_resource_and_project(
|
||||||
self.context, 'goals', self.tenant_id)
|
self.context, 'goals', self.project_id)
|
||||||
self._verify_quota_usage(usage_info,
|
self._verify_quota_usage(usage_info,
|
||||||
expected_dirty=False)
|
expected_dirty=False)
|
||||||
|
|
||||||
def _test_set_all_quota_usage_dirty(self, expected):
|
def _test_set_all_quota_usage_dirty(self, expected):
|
||||||
self._create_quota_usage('goals', 26)
|
self._create_quota_usage('goals', 26)
|
||||||
self._create_quota_usage('goals', 12, tenant_id='Callejon')
|
self._create_quota_usage('goals', 12, project_id='Callejon')
|
||||||
self.assertEqual(expected, quota_api.set_all_quota_usage_dirty(
|
self.assertEqual(expected, quota_api.set_all_quota_usage_dirty(
|
||||||
self.context, 'goals'))
|
self.context, 'goals'))
|
||||||
|
|
||||||
@ -167,13 +169,13 @@ class TestQuotaDbApi(testlib_api.SqlTestCaseLight):
|
|||||||
# admin context we can clean only one
|
# admin context we can clean only one
|
||||||
self._test_set_all_quota_usage_dirty(expected=1)
|
self._test_set_all_quota_usage_dirty(expected=1)
|
||||||
|
|
||||||
def test_get_quota_usage_by_tenant(self):
|
def test_get_quota_usage_by_project(self):
|
||||||
self._create_quota_usage('goals', 26)
|
self._create_quota_usage('goals', 26)
|
||||||
self._create_quota_usage('assists', 11)
|
self._create_quota_usage('assists', 11)
|
||||||
# Create a resource for a different tenant
|
# Create a resource for a different project
|
||||||
self._create_quota_usage('mehs', 99, tenant_id='buffon')
|
self._create_quota_usage('mehs', 99, project_id='buffon')
|
||||||
usage_infos = quota_api.get_quota_usage_by_tenant_id(
|
usage_infos = quota_api.get_quota_usage_by_project_id(
|
||||||
self.context, self.tenant_id)
|
self.context, self.project_id)
|
||||||
|
|
||||||
self.assertEqual(2, len(usage_infos))
|
self.assertEqual(2, len(usage_infos))
|
||||||
resources = [info.resource for info in usage_infos]
|
resources = [info.resource for info in usage_infos]
|
||||||
@ -183,26 +185,26 @@ class TestQuotaDbApi(testlib_api.SqlTestCaseLight):
|
|||||||
def test_get_quota_usage_by_resource(self):
|
def test_get_quota_usage_by_resource(self):
|
||||||
self._create_quota_usage('goals', 26)
|
self._create_quota_usage('goals', 26)
|
||||||
self._create_quota_usage('assists', 11)
|
self._create_quota_usage('assists', 11)
|
||||||
self._create_quota_usage('goals', 12, tenant_id='Callejon')
|
self._create_quota_usage('goals', 12, project_id='Callejon')
|
||||||
usage_infos = quota_api.get_quota_usage_by_resource(
|
usage_infos = quota_api.get_quota_usage_by_resource(
|
||||||
self.context, 'goals')
|
self.context, 'goals')
|
||||||
# Only 1 result expected in tenant context
|
# Only 1 result expected in project context
|
||||||
self.assertEqual(1, len(usage_infos))
|
self.assertEqual(1, len(usage_infos))
|
||||||
self._verify_quota_usage(usage_infos[0],
|
self._verify_quota_usage(usage_infos[0],
|
||||||
expected_resource='goals',
|
expected_resource='goals',
|
||||||
expected_used=26)
|
expected_used=26)
|
||||||
|
|
||||||
def test_get_quota_usage_by_tenant_and_resource(self):
|
def test_get_quota_usage_by_project_and_resource(self):
|
||||||
self._create_quota_usage('goals', 26)
|
self._create_quota_usage('goals', 26)
|
||||||
usage_info = quota_api.get_quota_usage_by_resource_and_tenant(
|
usage_info = quota_api.get_quota_usage_by_resource_and_project(
|
||||||
self.context, 'goals', self.tenant_id)
|
self.context, 'goals', self.project_id)
|
||||||
self._verify_quota_usage(usage_info,
|
self._verify_quota_usage(usage_info,
|
||||||
expected_resource='goals',
|
expected_resource='goals',
|
||||||
expected_used=26)
|
expected_used=26)
|
||||||
|
|
||||||
def test_get_non_existing_quota_usage_returns_none(self):
|
def test_get_non_existing_quota_usage_returns_none(self):
|
||||||
self.assertIsNone(quota_api.get_quota_usage_by_resource_and_tenant(
|
self.assertIsNone(quota_api.get_quota_usage_by_resource_and_project(
|
||||||
self.context, 'goals', self.tenant_id))
|
self.context, 'goals', self.project_id))
|
||||||
|
|
||||||
def _verify_reserved_resources(self, expected, actual):
|
def _verify_reserved_resources(self, expected, actual):
|
||||||
for (resource, delta) in actual.items():
|
for (resource, delta) in actual.items():
|
||||||
@ -214,14 +216,14 @@ class TestQuotaDbApi(testlib_api.SqlTestCaseLight):
|
|||||||
def test_create_reservation(self):
|
def test_create_reservation(self):
|
||||||
resources = {'goals': 2, 'assists': 1}
|
resources = {'goals': 2, 'assists': 1}
|
||||||
resv = self._create_reservation(resources)
|
resv = self._create_reservation(resources)
|
||||||
self.assertEqual(self.tenant_id, resv.tenant_id)
|
self.assertEqual(self.project_id, resv.project_id)
|
||||||
self._verify_reserved_resources(resources, resv.deltas)
|
self._verify_reserved_resources(resources, resv.deltas)
|
||||||
|
|
||||||
def test_create_reservation_with_expiration(self):
|
def test_create_reservation_with_expiration(self):
|
||||||
resources = {'goals': 2, 'assists': 1}
|
resources = {'goals': 2, 'assists': 1}
|
||||||
exp_date = datetime.datetime(2016, 3, 31, 14, 30)
|
exp_date = datetime.datetime(2016, 3, 31, 14, 30)
|
||||||
resv = self._create_reservation(resources, expiration=exp_date)
|
resv = self._create_reservation(resources, expiration=exp_date)
|
||||||
self.assertEqual(self.tenant_id, resv.tenant_id)
|
self.assertEqual(self.project_id, resv.project_id)
|
||||||
self.assertEqual(exp_date, resv.expiration)
|
self.assertEqual(exp_date, resv.expiration)
|
||||||
self._verify_reserved_resources(resources, resv.deltas)
|
self._verify_reserved_resources(resources, resv.deltas)
|
||||||
|
|
||||||
@ -245,7 +247,8 @@ class TestQuotaDbApi(testlib_api.SqlTestCaseLight):
|
|||||||
mock_utcnow.return_value = datetime.datetime(
|
mock_utcnow.return_value = datetime.datetime(
|
||||||
2015, 5, 20, 0, 0)
|
2015, 5, 20, 0, 0)
|
||||||
deltas = quota_api.get_reservations_for_resources(
|
deltas = quota_api.get_reservations_for_resources(
|
||||||
self.context, self.tenant_id, ['goals', 'assists', 'bookings'])
|
self.context, self.project_id,
|
||||||
|
['goals', 'assists', 'bookings'])
|
||||||
self.assertIn('goals', deltas)
|
self.assertIn('goals', deltas)
|
||||||
self.assertEqual(5, deltas['goals'])
|
self.assertEqual(5, deltas['goals'])
|
||||||
self.assertIn('assists', deltas)
|
self.assertIn('assists', deltas)
|
||||||
@ -260,7 +263,7 @@ class TestQuotaDbApi(testlib_api.SqlTestCaseLight):
|
|||||||
2015, 5, 20, 0, 0)
|
2015, 5, 20, 0, 0)
|
||||||
self._get_reservations_for_resource_helper()
|
self._get_reservations_for_resource_helper()
|
||||||
deltas = quota_api.get_reservations_for_resources(
|
deltas = quota_api.get_reservations_for_resources(
|
||||||
self.context, self.tenant_id,
|
self.context, self.project_id,
|
||||||
['goals', 'assists', 'bookings'],
|
['goals', 'assists', 'bookings'],
|
||||||
expired=True)
|
expired=True)
|
||||||
self.assertIn('assists', deltas)
|
self.assertIn('assists', deltas)
|
||||||
@ -271,7 +274,7 @@ class TestQuotaDbApi(testlib_api.SqlTestCaseLight):
|
|||||||
|
|
||||||
def test_get_reservation_for_resources_with_empty_list(self):
|
def test_get_reservation_for_resources_with_empty_list(self):
|
||||||
self.assertIsNone(quota_api.get_reservations_for_resources(
|
self.assertIsNone(quota_api.get_reservations_for_resources(
|
||||||
self.context, self.tenant_id, []))
|
self.context, self.project_id, []))
|
||||||
|
|
||||||
def test_remove_expired_reservations(self):
|
def test_remove_expired_reservations(self):
|
||||||
with mock.patch('neutron.db.quota.api.utcnow') as mock_utcnow:
|
with mock.patch('neutron.db.quota.api.utcnow') as mock_utcnow:
|
||||||
@ -283,13 +286,13 @@ class TestQuotaDbApi(testlib_api.SqlTestCaseLight):
|
|||||||
exp_date_2 = datetime.datetime(2015, 3, 31, 14, 30)
|
exp_date_2 = datetime.datetime(2015, 3, 31, 14, 30)
|
||||||
resv_2 = self._create_reservation(resources, expiration=exp_date_2)
|
resv_2 = self._create_reservation(resources, expiration=exp_date_2)
|
||||||
self.assertEqual(1, quota_api.remove_expired_reservations(
|
self.assertEqual(1, quota_api.remove_expired_reservations(
|
||||||
self.context, self.tenant_id))
|
self.context, self.project_id))
|
||||||
self.assertIsNone(quota_api.get_reservation(
|
self.assertIsNone(quota_api.get_reservation(
|
||||||
self.context, resv_2.reservation_id))
|
self.context, resv_2.reservation_id))
|
||||||
self.assertIsNotNone(quota_api.get_reservation(
|
self.assertIsNotNone(quota_api.get_reservation(
|
||||||
self.context, resv_1.reservation_id))
|
self.context, resv_1.reservation_id))
|
||||||
|
|
||||||
def test_remove_expired_reservations_no_tenant(self):
|
def test_remove_expired_reservations_no_project(self):
|
||||||
with mock.patch('neutron.db.quota.api.utcnow') as mock_utcnow:
|
with mock.patch('neutron.db.quota.api.utcnow') as mock_utcnow:
|
||||||
mock_utcnow.return_value = datetime.datetime(
|
mock_utcnow.return_value = datetime.datetime(
|
||||||
2015, 5, 20, 0, 0)
|
2015, 5, 20, 0, 0)
|
||||||
@ -299,7 +302,7 @@ class TestQuotaDbApi(testlib_api.SqlTestCaseLight):
|
|||||||
exp_date_2 = datetime.datetime(2015, 3, 31, 14, 30)
|
exp_date_2 = datetime.datetime(2015, 3, 31, 14, 30)
|
||||||
resv_2 = self._create_reservation(resources,
|
resv_2 = self._create_reservation(resources,
|
||||||
expiration=exp_date_2,
|
expiration=exp_date_2,
|
||||||
tenant_id='Callejon')
|
project_id='Callejon')
|
||||||
self.assertEqual(2, quota_api.remove_expired_reservations(
|
self.assertEqual(2, quota_api.remove_expired_reservations(
|
||||||
context.get_admin_context()))
|
context.get_admin_context()))
|
||||||
self.assertIsNone(quota_api.get_reservation(
|
self.assertIsNone(quota_api.get_reservation(
|
||||||
@ -311,14 +314,14 @@ class TestQuotaDbApi(testlib_api.SqlTestCaseLight):
|
|||||||
class TestQuotaDbApiAdminContext(TestQuotaDbApi):
|
class TestQuotaDbApiAdminContext(TestQuotaDbApi):
|
||||||
|
|
||||||
def _set_context(self):
|
def _set_context(self):
|
||||||
self.tenant_id = 'Higuain'
|
self.project_id = 'Higuain'
|
||||||
self.context = context.Context('Gonzalo', self.tenant_id,
|
self.context = context.Context('Gonzalo', self.project_id,
|
||||||
is_admin=True, is_advsvc=True)
|
is_admin=True, is_advsvc=True)
|
||||||
|
|
||||||
def test_get_quota_usage_by_resource(self):
|
def test_get_quota_usage_by_resource(self):
|
||||||
self._create_quota_usage('goals', 26)
|
self._create_quota_usage('goals', 26)
|
||||||
self._create_quota_usage('assists', 11)
|
self._create_quota_usage('assists', 11)
|
||||||
self._create_quota_usage('goals', 12, tenant_id='Callejon')
|
self._create_quota_usage('goals', 12, project_id='Callejon')
|
||||||
usage_infos = quota_api.get_quota_usage_by_resource(
|
usage_infos = quota_api.get_quota_usage_by_resource(
|
||||||
self.context, 'goals')
|
self.context, 'goals')
|
||||||
# 2 results expected in admin context
|
# 2 results expected in admin context
|
||||||
|
@ -28,7 +28,7 @@ from neutron.tests.unit import testlib_api
|
|||||||
DB_PLUGIN_KLASS = 'neutron.db.db_base_plugin_v2.NeutronDbPluginV2'
|
DB_PLUGIN_KLASS = 'neutron.db.db_base_plugin_v2.NeutronDbPluginV2'
|
||||||
|
|
||||||
|
|
||||||
def _count_resource(context, resource, tenant_id):
|
def _count_resource(context, resource, project_id):
|
||||||
"""A fake counting function to determine current used counts"""
|
"""A fake counting function to determine current used counts"""
|
||||||
if resource[-1] == 's':
|
if resource[-1] == 's':
|
||||||
resource = resource[:-1]
|
resource = resource[:-1]
|
||||||
@ -97,7 +97,8 @@ class TestDbQuotaDriver(testlib_api.SqlTestCase,
|
|||||||
defaults = {RESOURCE: TestResource(RESOURCE, 4)}
|
defaults = {RESOURCE: TestResource(RESOURCE, 4)}
|
||||||
|
|
||||||
self.plugin.update_quota_limit(self.context, PROJECT, RESOURCE, 2)
|
self.plugin.update_quota_limit(self.context, PROJECT, RESOURCE, 2)
|
||||||
quotas = self.plugin.get_tenant_quotas(self.context, defaults, PROJECT)
|
quotas = self.plugin.get_project_quotas(self.context, defaults,
|
||||||
|
PROJECT)
|
||||||
self.assertEqual(2, quotas[RESOURCE])
|
self.assertEqual(2, quotas[RESOURCE])
|
||||||
|
|
||||||
def test_update_quota_limit(self):
|
def test_update_quota_limit(self):
|
||||||
@ -105,15 +106,17 @@ class TestDbQuotaDriver(testlib_api.SqlTestCase,
|
|||||||
|
|
||||||
self.plugin.update_quota_limit(self.context, PROJECT, RESOURCE, 2)
|
self.plugin.update_quota_limit(self.context, PROJECT, RESOURCE, 2)
|
||||||
self.plugin.update_quota_limit(self.context, PROJECT, RESOURCE, 3)
|
self.plugin.update_quota_limit(self.context, PROJECT, RESOURCE, 3)
|
||||||
quotas = self.plugin.get_tenant_quotas(self.context, defaults, PROJECT)
|
quotas = self.plugin.get_project_quotas(self.context, defaults,
|
||||||
|
PROJECT)
|
||||||
self.assertEqual(3, quotas[RESOURCE])
|
self.assertEqual(3, quotas[RESOURCE])
|
||||||
|
|
||||||
def test_delete_tenant_quota_restores_default_limit(self):
|
def test_delete_project_quota_restores_default_limit(self):
|
||||||
defaults = {RESOURCE: TestResource(RESOURCE, 4)}
|
defaults = {RESOURCE: TestResource(RESOURCE, 4)}
|
||||||
|
|
||||||
self.plugin.update_quota_limit(self.context, PROJECT, RESOURCE, 2)
|
self.plugin.update_quota_limit(self.context, PROJECT, RESOURCE, 2)
|
||||||
self.plugin.delete_tenant_quota(self.context, PROJECT)
|
self.plugin.delete_project_quota(self.context, PROJECT)
|
||||||
quotas = self.plugin.get_tenant_quotas(self.context, defaults, PROJECT)
|
quotas = self.plugin.get_project_quotas(self.context, defaults,
|
||||||
|
PROJECT)
|
||||||
self.assertEqual(4, quotas[RESOURCE])
|
self.assertEqual(4, quotas[RESOURCE])
|
||||||
|
|
||||||
def test_get_default_quotas(self):
|
def test_get_default_quotas(self):
|
||||||
@ -123,20 +126,20 @@ class TestDbQuotaDriver(testlib_api.SqlTestCase,
|
|||||||
quotas = self.plugin.get_default_quotas(user_ctx, defaults, PROJECT)
|
quotas = self.plugin.get_default_quotas(user_ctx, defaults, PROJECT)
|
||||||
self.assertEqual(4, quotas[RESOURCE])
|
self.assertEqual(4, quotas[RESOURCE])
|
||||||
|
|
||||||
def test_get_tenant_quotas(self):
|
def test_get_project_quotas(self):
|
||||||
user_ctx = context.Context(user_id=PROJECT, tenant_id=PROJECT)
|
user_ctx = context.Context(user_id=PROJECT, tenant_id=PROJECT)
|
||||||
self.plugin.update_quota_limit(self.context, PROJECT, RESOURCE, 2)
|
self.plugin.update_quota_limit(self.context, PROJECT, RESOURCE, 2)
|
||||||
quotas = self.plugin.get_tenant_quotas(user_ctx, {}, PROJECT)
|
quotas = self.plugin.get_project_quotas(user_ctx, {}, PROJECT)
|
||||||
self.assertEqual(2, quotas[RESOURCE])
|
self.assertEqual(2, quotas[RESOURCE])
|
||||||
|
|
||||||
def test_get_tenant_quotas_different_tenant(self):
|
def test_get_project_quotas_different_project(self):
|
||||||
user_ctx = context.Context(user_id=PROJECT,
|
user_ctx = context.Context(user_id=PROJECT,
|
||||||
tenant_id='another_project')
|
tenant_id='another_project')
|
||||||
self.plugin.update_quota_limit(self.context, PROJECT, RESOURCE, 2)
|
self.plugin.update_quota_limit(self.context, PROJECT, RESOURCE, 2)
|
||||||
# It is appropriate to use assertFalse here as the expected return
|
# It is appropriate to use assertFalse here as the expected return
|
||||||
# value is an empty dict (the defaults passed in the statement below
|
# value is an empty dict (the defaults passed in the statement below
|
||||||
# after the request context)
|
# after the request context)
|
||||||
self.assertFalse(self.plugin.get_tenant_quotas(user_ctx, {}, PROJECT))
|
self.assertFalse(self.plugin.get_project_quotas(user_ctx, {}, PROJECT))
|
||||||
|
|
||||||
def test_get_all_quotas(self):
|
def test_get_all_quotas(self):
|
||||||
project_1 = 'prj_test_1'
|
project_1 = 'prj_test_1'
|
||||||
@ -151,14 +154,14 @@ class TestDbQuotaDriver(testlib_api.SqlTestCase,
|
|||||||
self.plugin.update_quota_limit(self.context, project_2, resource_2, 9)
|
self.plugin.update_quota_limit(self.context, project_2, resource_2, 9)
|
||||||
quotas = self.plugin.get_all_quotas(self.context, resources)
|
quotas = self.plugin.get_all_quotas(self.context, resources)
|
||||||
|
|
||||||
# Expect two tenants' quotas
|
# Expect two projects' quotas
|
||||||
self.assertEqual(2, len(quotas))
|
self.assertEqual(2, len(quotas))
|
||||||
# But not quotas for the same tenant twice
|
# But not quotas for the same project twice
|
||||||
self.assertNotEqual(quotas[0]['tenant_id'], quotas[1]['tenant_id'])
|
self.assertNotEqual(quotas[0]['project_id'], quotas[1]['project_id'])
|
||||||
|
|
||||||
# Check the expected limits. The quotas can be in any order.
|
# Check the expected limits. The quotas can be in any order.
|
||||||
for quota in quotas:
|
for quota in quotas:
|
||||||
project = quota['tenant_id']
|
project = quota['project_id']
|
||||||
self.assertIn(project, (project_1, project_2))
|
self.assertIn(project, (project_1, project_2))
|
||||||
if project == project_1:
|
if project == project_1:
|
||||||
expected_limit_r1 = 7
|
expected_limit_r1 = 7
|
||||||
@ -208,15 +211,15 @@ class TestDbQuotaDriver(testlib_api.SqlTestCase,
|
|||||||
self.plugin.update_quota_limit(self.context, PROJECT, resource_name, 2)
|
self.plugin.update_quota_limit(self.context, PROJECT, resource_name, 2)
|
||||||
reservation = quota_driver.make_reservation(
|
reservation = quota_driver.make_reservation(
|
||||||
self.context,
|
self.context,
|
||||||
self.context.tenant_id,
|
self.context.project_id,
|
||||||
resources,
|
resources,
|
||||||
deltas,
|
deltas,
|
||||||
self.plugin)
|
self.plugin)
|
||||||
self.assertIn(resource_name, reservation.deltas)
|
self.assertIn(resource_name, reservation.deltas)
|
||||||
self.assertEqual(deltas[resource_name],
|
self.assertEqual(deltas[resource_name],
|
||||||
reservation.deltas[resource_name])
|
reservation.deltas[resource_name])
|
||||||
self.assertEqual(self.context.tenant_id,
|
self.assertEqual(self.context.project_id,
|
||||||
reservation.tenant_id)
|
reservation.project_id)
|
||||||
|
|
||||||
def test_make_reservation_single_resource(self):
|
def test_make_reservation_single_resource(self):
|
||||||
quota_driver = driver.DbQuotaDriver()
|
quota_driver = driver.DbQuotaDriver()
|
||||||
@ -237,7 +240,7 @@ class TestDbQuotaDriver(testlib_api.SqlTestCase,
|
|||||||
self.plugin.update_quota_limit(self.context, PROJECT, ALT_RESOURCE, 2)
|
self.plugin.update_quota_limit(self.context, PROJECT, ALT_RESOURCE, 2)
|
||||||
reservation = quota_driver.make_reservation(
|
reservation = quota_driver.make_reservation(
|
||||||
self.context,
|
self.context,
|
||||||
self.context.tenant_id,
|
self.context.project_id,
|
||||||
resources,
|
resources,
|
||||||
deltas,
|
deltas,
|
||||||
self.plugin)
|
self.plugin)
|
||||||
@ -245,8 +248,8 @@ class TestDbQuotaDriver(testlib_api.SqlTestCase,
|
|||||||
self.assertIn(ALT_RESOURCE, reservation.deltas)
|
self.assertIn(ALT_RESOURCE, reservation.deltas)
|
||||||
self.assertEqual(1, reservation.deltas[RESOURCE])
|
self.assertEqual(1, reservation.deltas[RESOURCE])
|
||||||
self.assertEqual(2, reservation.deltas[ALT_RESOURCE])
|
self.assertEqual(2, reservation.deltas[ALT_RESOURCE])
|
||||||
self.assertEqual(self.context.tenant_id,
|
self.assertEqual(self.context.project_id,
|
||||||
reservation.tenant_id)
|
reservation.project_id)
|
||||||
|
|
||||||
def test_make_reservation_over_quota_fails(self):
|
def test_make_reservation_over_quota_fails(self):
|
||||||
quota_driver = driver.DbQuotaDriver()
|
quota_driver = driver.DbQuotaDriver()
|
||||||
@ -257,12 +260,12 @@ class TestDbQuotaDriver(testlib_api.SqlTestCase,
|
|||||||
self.assertRaises(exceptions.OverQuota,
|
self.assertRaises(exceptions.OverQuota,
|
||||||
quota_driver.make_reservation,
|
quota_driver.make_reservation,
|
||||||
self.context,
|
self.context,
|
||||||
self.context.tenant_id,
|
self.context.project_id,
|
||||||
resources,
|
resources,
|
||||||
deltas,
|
deltas,
|
||||||
self.plugin)
|
self.plugin)
|
||||||
|
|
||||||
def test_get_detailed_tenant_quotas_resource(self):
|
def test_get_detailed_project_quotas_resource(self):
|
||||||
res = {RESOURCE: TestTrackedResource(RESOURCE, test_quota.MehModel)}
|
res = {RESOURCE: TestTrackedResource(RESOURCE, test_quota.MehModel)}
|
||||||
|
|
||||||
self.plugin.update_quota_limit(self.context, PROJECT, RESOURCE, 6)
|
self.plugin.update_quota_limit(self.context, PROJECT, RESOURCE, 6)
|
||||||
@ -270,13 +273,13 @@ class TestDbQuotaDriver(testlib_api.SqlTestCase,
|
|||||||
quota_driver.make_reservation(self.context, PROJECT, res,
|
quota_driver.make_reservation(self.context, PROJECT, res,
|
||||||
{RESOURCE: 1}, self.plugin)
|
{RESOURCE: 1}, self.plugin)
|
||||||
quota_api.set_quota_usage(self.context, RESOURCE, PROJECT, 2)
|
quota_api.set_quota_usage(self.context, RESOURCE, PROJECT, 2)
|
||||||
detailed_quota = self.plugin.get_detailed_tenant_quotas(self.context,
|
detailed_quota = self.plugin.get_detailed_project_quotas(
|
||||||
res, PROJECT)
|
self.context, res, PROJECT)
|
||||||
self.assertEqual(6, detailed_quota[RESOURCE]['limit'])
|
self.assertEqual(6, detailed_quota[RESOURCE]['limit'])
|
||||||
self.assertEqual(2, detailed_quota[RESOURCE]['used'])
|
self.assertEqual(2, detailed_quota[RESOURCE]['used'])
|
||||||
self.assertEqual(1, detailed_quota[RESOURCE]['reserved'])
|
self.assertEqual(1, detailed_quota[RESOURCE]['reserved'])
|
||||||
|
|
||||||
def test_get_detailed_tenant_quotas_multiple_resource(self):
|
def test_get_detailed_project_quotas_multiple_resource(self):
|
||||||
project_1 = 'prj_test_1'
|
project_1 = 'prj_test_1'
|
||||||
resource_1 = 'res_test_1'
|
resource_1 = 'res_test_1'
|
||||||
resource_2 = 'res_test_2'
|
resource_2 = 'res_test_2'
|
||||||
@ -295,9 +298,8 @@ class TestDbQuotaDriver(testlib_api.SqlTestCase,
|
|||||||
|
|
||||||
quota_api.set_quota_usage(self.context, resource_1, project_1, 2)
|
quota_api.set_quota_usage(self.context, resource_1, project_1, 2)
|
||||||
quota_api.set_quota_usage(self.context, resource_2, project_1, 3)
|
quota_api.set_quota_usage(self.context, resource_2, project_1, 3)
|
||||||
detailed_quota = self.plugin.get_detailed_tenant_quotas(self.context,
|
detailed_quota = self.plugin.get_detailed_project_quotas(
|
||||||
resources,
|
self.context, resources, project_1)
|
||||||
project_1)
|
|
||||||
|
|
||||||
self.assertEqual(6, detailed_quota[resource_1]['limit'])
|
self.assertEqual(6, detailed_quota[resource_1]['limit'])
|
||||||
self.assertEqual(1, detailed_quota[resource_1]['reserved'])
|
self.assertEqual(1, detailed_quota[resource_1]['reserved'])
|
||||||
|
@ -73,9 +73,9 @@ class QuotaExtensionTestCase(testlib_api.WebTestCase):
|
|||||||
router.APIRouter()
|
router.APIRouter()
|
||||||
|
|
||||||
def _test_quota_default_values(self, expected_values):
|
def _test_quota_default_values(self, expected_values):
|
||||||
tenant_id = 'tenant_id1'
|
project_id = 'project_id1'
|
||||||
env = {'neutron.context': context.Context('', tenant_id)}
|
env = {'neutron.context': context.Context('', project_id)}
|
||||||
res = self.api.get(_get_path('quotas', id=tenant_id, fmt=self.fmt),
|
res = self.api.get(_get_path('quotas', id=project_id, fmt=self.fmt),
|
||||||
extra_environ=env)
|
extra_environ=env)
|
||||||
quota = self.deserialize(res)
|
quota = self.deserialize(res)
|
||||||
for resource, expected_value in expected_values.items():
|
for resource, expected_value in expected_values.items():
|
||||||
@ -118,10 +118,10 @@ class QuotaExtensionDbTestCase(QuotaExtensionTestCase):
|
|||||||
'extra1': qconf.DEFAULT_QUOTA})
|
'extra1': qconf.DEFAULT_QUOTA})
|
||||||
|
|
||||||
def test_show_default_quotas_with_admin(self):
|
def test_show_default_quotas_with_admin(self):
|
||||||
tenant_id = 'tenant_id1'
|
project_id = 'project_id1'
|
||||||
env = {'neutron.context': context.Context('', tenant_id + '2',
|
env = {'neutron.context': context.Context('', project_id + '2',
|
||||||
is_admin=True)}
|
is_admin=True)}
|
||||||
res = self.api.get(_get_path('quotas', id=tenant_id,
|
res = self.api.get(_get_path('quotas', id=project_id,
|
||||||
action=DEFAULT_QUOTAS_ACTION,
|
action=DEFAULT_QUOTAS_ACTION,
|
||||||
fmt=self.fmt),
|
fmt=self.fmt),
|
||||||
extra_environ=env)
|
extra_environ=env)
|
||||||
@ -134,11 +134,11 @@ class QuotaExtensionDbTestCase(QuotaExtensionTestCase):
|
|||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
qconf.DEFAULT_QUOTA_PORT, quota['quota']['port'])
|
qconf.DEFAULT_QUOTA_PORT, quota['quota']['port'])
|
||||||
|
|
||||||
def test_show_default_quotas_with_owner_tenant(self):
|
def test_show_default_quotas_with_owner_project(self):
|
||||||
tenant_id = 'tenant_id1'
|
project_id = 'project_id1'
|
||||||
env = {'neutron.context': context.Context('', tenant_id,
|
env = {'neutron.context': context.Context('', project_id,
|
||||||
is_admin=False)}
|
is_admin=False)}
|
||||||
res = self.api.get(_get_path('quotas', id=tenant_id,
|
res = self.api.get(_get_path('quotas', id=project_id,
|
||||||
action=DEFAULT_QUOTAS_ACTION,
|
action=DEFAULT_QUOTAS_ACTION,
|
||||||
fmt=self.fmt),
|
fmt=self.fmt),
|
||||||
extra_environ=env)
|
extra_environ=env)
|
||||||
@ -152,20 +152,20 @@ class QuotaExtensionDbTestCase(QuotaExtensionTestCase):
|
|||||||
qconf.DEFAULT_QUOTA_PORT, quota['quota']['port'])
|
qconf.DEFAULT_QUOTA_PORT, quota['quota']['port'])
|
||||||
|
|
||||||
def test_show_default_quotas_without_admin_forbidden_returns_403(self):
|
def test_show_default_quotas_without_admin_forbidden_returns_403(self):
|
||||||
tenant_id = 'tenant_id1'
|
project_id = 'project_id1'
|
||||||
env = {'neutron.context': context.Context('', tenant_id + '2',
|
env = {'neutron.context': context.Context('', project_id + '2',
|
||||||
is_admin=False)}
|
is_admin=False)}
|
||||||
res = self.api.get(_get_path('quotas', id=tenant_id,
|
res = self.api.get(_get_path('quotas', id=project_id,
|
||||||
action=DEFAULT_QUOTAS_ACTION,
|
action=DEFAULT_QUOTAS_ACTION,
|
||||||
fmt=self.fmt),
|
fmt=self.fmt),
|
||||||
extra_environ=env, expect_errors=True)
|
extra_environ=env, expect_errors=True)
|
||||||
self.assertEqual(403, res.status_int)
|
self.assertEqual(403, res.status_int)
|
||||||
|
|
||||||
def test_show_quotas_with_admin(self):
|
def test_show_quotas_with_admin(self):
|
||||||
tenant_id = 'tenant_id1'
|
project_id = 'project_id1'
|
||||||
env = {'neutron.context': context.Context('', tenant_id + '2',
|
env = {'neutron.context': context.Context('', project_id + '2',
|
||||||
is_admin=True)}
|
is_admin=True)}
|
||||||
res = self.api.get(_get_path('quotas', id=tenant_id, fmt=self.fmt),
|
res = self.api.get(_get_path('quotas', id=project_id, fmt=self.fmt),
|
||||||
extra_environ=env)
|
extra_environ=env)
|
||||||
self.assertEqual(200, res.status_int)
|
self.assertEqual(200, res.status_int)
|
||||||
quota = self.deserialize(res)
|
quota = self.deserialize(res)
|
||||||
@ -177,18 +177,18 @@ class QuotaExtensionDbTestCase(QuotaExtensionTestCase):
|
|||||||
qconf.DEFAULT_QUOTA_PORT, quota['quota']['port'])
|
qconf.DEFAULT_QUOTA_PORT, quota['quota']['port'])
|
||||||
|
|
||||||
def test_show_quotas_without_admin_forbidden_returns_403(self):
|
def test_show_quotas_without_admin_forbidden_returns_403(self):
|
||||||
tenant_id = 'tenant_id1'
|
project_id = 'project_id1'
|
||||||
env = {'neutron.context': context.Context('', tenant_id + '2',
|
env = {'neutron.context': context.Context('', project_id + '2',
|
||||||
is_admin=False)}
|
is_admin=False)}
|
||||||
res = self.api.get(_get_path('quotas', id=tenant_id, fmt=self.fmt),
|
res = self.api.get(_get_path('quotas', id=project_id, fmt=self.fmt),
|
||||||
extra_environ=env, expect_errors=True)
|
extra_environ=env, expect_errors=True)
|
||||||
self.assertEqual(403, res.status_int)
|
self.assertEqual(403, res.status_int)
|
||||||
|
|
||||||
def test_show_quotas_with_owner_tenant(self):
|
def test_show_quotas_with_owner_project(self):
|
||||||
tenant_id = 'tenant_id1'
|
project_id = 'project_id1'
|
||||||
env = {'neutron.context': context.Context('', tenant_id,
|
env = {'neutron.context': context.Context('', project_id,
|
||||||
is_admin=False)}
|
is_admin=False)}
|
||||||
res = self.api.get(_get_path('quotas', id=tenant_id, fmt=self.fmt),
|
res = self.api.get(_get_path('quotas', id=project_id, fmt=self.fmt),
|
||||||
extra_environ=env)
|
extra_environ=env)
|
||||||
self.assertEqual(200, res.status_int)
|
self.assertEqual(200, res.status_int)
|
||||||
quota = self.deserialize(res)
|
quota = self.deserialize(res)
|
||||||
@ -200,8 +200,8 @@ class QuotaExtensionDbTestCase(QuotaExtensionTestCase):
|
|||||||
qconf.DEFAULT_QUOTA_PORT, quota['quota']['port'])
|
qconf.DEFAULT_QUOTA_PORT, quota['quota']['port'])
|
||||||
|
|
||||||
def test_list_quotas_with_admin(self):
|
def test_list_quotas_with_admin(self):
|
||||||
tenant_id = 'tenant_id1'
|
project_id = 'project_id1'
|
||||||
env = {'neutron.context': context.Context('', tenant_id,
|
env = {'neutron.context': context.Context('', project_id,
|
||||||
is_admin=True)}
|
is_admin=True)}
|
||||||
res = self.api.get(_get_path('quotas', fmt=self.fmt),
|
res = self.api.get(_get_path('quotas', fmt=self.fmt),
|
||||||
extra_environ=env)
|
extra_environ=env)
|
||||||
@ -210,93 +210,93 @@ class QuotaExtensionDbTestCase(QuotaExtensionTestCase):
|
|||||||
self.assertEqual([], quota['quotas'])
|
self.assertEqual([], quota['quotas'])
|
||||||
|
|
||||||
def test_list_quotas_without_admin_forbidden_returns_403(self):
|
def test_list_quotas_without_admin_forbidden_returns_403(self):
|
||||||
tenant_id = 'tenant_id1'
|
project_id = 'project_id1'
|
||||||
env = {'neutron.context': context.Context('', tenant_id,
|
env = {'neutron.context': context.Context('', project_id,
|
||||||
is_admin=False)}
|
is_admin=False)}
|
||||||
res = self.api.get(_get_path('quotas', fmt=self.fmt),
|
res = self.api.get(_get_path('quotas', fmt=self.fmt),
|
||||||
extra_environ=env, expect_errors=True)
|
extra_environ=env, expect_errors=True)
|
||||||
self.assertEqual(403, res.status_int)
|
self.assertEqual(403, res.status_int)
|
||||||
|
|
||||||
def test_update_quotas_without_admin_forbidden_returns_403(self):
|
def test_update_quotas_without_admin_forbidden_returns_403(self):
|
||||||
tenant_id = 'tenant_id1'
|
project_id = 'project_id1'
|
||||||
env = {'neutron.context': context.Context('', tenant_id,
|
env = {'neutron.context': context.Context('', project_id,
|
||||||
is_admin=False)}
|
is_admin=False)}
|
||||||
quotas = {'quota': {'network': 100}}
|
quotas = {'quota': {'network': 100}}
|
||||||
res = self.api.put(_get_path('quotas', id=tenant_id, fmt=self.fmt),
|
res = self.api.put(_get_path('quotas', id=project_id, fmt=self.fmt),
|
||||||
self.serialize(quotas), extra_environ=env,
|
self.serialize(quotas), extra_environ=env,
|
||||||
expect_errors=True)
|
expect_errors=True)
|
||||||
self.assertEqual(403, res.status_int)
|
self.assertEqual(403, res.status_int)
|
||||||
|
|
||||||
def test_update_quotas_with_non_integer_returns_400(self):
|
def test_update_quotas_with_non_integer_returns_400(self):
|
||||||
tenant_id = 'tenant_id1'
|
project_id = 'project_id1'
|
||||||
env = {'neutron.context': context.Context('', tenant_id,
|
env = {'neutron.context': context.Context('', project_id,
|
||||||
is_admin=True)}
|
is_admin=True)}
|
||||||
quotas = {'quota': {'network': 'abc'}}
|
quotas = {'quota': {'network': 'abc'}}
|
||||||
res = self.api.put(_get_path('quotas', id=tenant_id, fmt=self.fmt),
|
res = self.api.put(_get_path('quotas', id=project_id, fmt=self.fmt),
|
||||||
self.serialize(quotas), extra_environ=env,
|
self.serialize(quotas), extra_environ=env,
|
||||||
expect_errors=True)
|
expect_errors=True)
|
||||||
self.assertEqual(400, res.status_int)
|
self.assertEqual(400, res.status_int)
|
||||||
|
|
||||||
def test_update_quotas_with_negative_integer_returns_400(self):
|
def test_update_quotas_with_negative_integer_returns_400(self):
|
||||||
tenant_id = 'tenant_id1'
|
project_id = 'project_id1'
|
||||||
env = {'neutron.context': context.Context('', tenant_id,
|
env = {'neutron.context': context.Context('', project_id,
|
||||||
is_admin=True)}
|
is_admin=True)}
|
||||||
quotas = {'quota': {'network': -2}}
|
quotas = {'quota': {'network': -2}}
|
||||||
res = self.api.put(_get_path('quotas', id=tenant_id, fmt=self.fmt),
|
res = self.api.put(_get_path('quotas', id=project_id, fmt=self.fmt),
|
||||||
self.serialize(quotas), extra_environ=env,
|
self.serialize(quotas), extra_environ=env,
|
||||||
expect_errors=True)
|
expect_errors=True)
|
||||||
self.assertEqual(400, res.status_int)
|
self.assertEqual(400, res.status_int)
|
||||||
|
|
||||||
def test_update_quotas_with_out_of_range_integer_returns_400(self):
|
def test_update_quotas_with_out_of_range_integer_returns_400(self):
|
||||||
tenant_id = 'tenant_id1'
|
project_id = 'project_id1'
|
||||||
env = {'neutron.context': context.Context('', tenant_id,
|
env = {'neutron.context': context.Context('', project_id,
|
||||||
is_admin=True)}
|
is_admin=True)}
|
||||||
quotas = {'quota': {'network': constants.DB_INTEGER_MAX_VALUE + 1}}
|
quotas = {'quota': {'network': constants.DB_INTEGER_MAX_VALUE + 1}}
|
||||||
res = self.api.put(_get_path('quotas', id=tenant_id, fmt=self.fmt),
|
res = self.api.put(_get_path('quotas', id=project_id, fmt=self.fmt),
|
||||||
self.serialize(quotas), extra_environ=env,
|
self.serialize(quotas), extra_environ=env,
|
||||||
expect_errors=True)
|
expect_errors=True)
|
||||||
self.assertEqual(exc.HTTPBadRequest.code, res.status_int)
|
self.assertEqual(exc.HTTPBadRequest.code, res.status_int)
|
||||||
|
|
||||||
def test_update_quotas_to_unlimited(self):
|
def test_update_quotas_to_unlimited(self):
|
||||||
tenant_id = 'tenant_id1'
|
project_id = 'project_id1'
|
||||||
env = {'neutron.context': context.Context('', tenant_id,
|
env = {'neutron.context': context.Context('', project_id,
|
||||||
is_admin=True)}
|
is_admin=True)}
|
||||||
quotas = {'quota': {'network': -1}}
|
quotas = {'quota': {'network': -1}}
|
||||||
res = self.api.put(_get_path('quotas', id=tenant_id, fmt=self.fmt),
|
res = self.api.put(_get_path('quotas', id=project_id, fmt=self.fmt),
|
||||||
self.serialize(quotas), extra_environ=env,
|
self.serialize(quotas), extra_environ=env,
|
||||||
expect_errors=False)
|
expect_errors=False)
|
||||||
self.assertEqual(200, res.status_int)
|
self.assertEqual(200, res.status_int)
|
||||||
|
|
||||||
def test_update_quotas_exceeding_current_limit(self):
|
def test_update_quotas_exceeding_current_limit(self):
|
||||||
tenant_id = 'tenant_id1'
|
project_id = 'project_id1'
|
||||||
env = {'neutron.context': context.Context('', tenant_id,
|
env = {'neutron.context': context.Context('', project_id,
|
||||||
is_admin=True)}
|
is_admin=True)}
|
||||||
quotas = {'quota': {'network': 120}}
|
quotas = {'quota': {'network': 120}}
|
||||||
res = self.api.put(_get_path('quotas', id=tenant_id, fmt=self.fmt),
|
res = self.api.put(_get_path('quotas', id=project_id, fmt=self.fmt),
|
||||||
self.serialize(quotas), extra_environ=env,
|
self.serialize(quotas), extra_environ=env,
|
||||||
expect_errors=False)
|
expect_errors=False)
|
||||||
self.assertEqual(200, res.status_int)
|
self.assertEqual(200, res.status_int)
|
||||||
|
|
||||||
def test_update_quotas_with_non_support_resource_returns_400(self):
|
def test_update_quotas_with_non_support_resource_returns_400(self):
|
||||||
tenant_id = 'tenant_id1'
|
project_id = 'project_id1'
|
||||||
env = {'neutron.context': context.Context('', tenant_id,
|
env = {'neutron.context': context.Context('', project_id,
|
||||||
is_admin=True)}
|
is_admin=True)}
|
||||||
quotas = {'quota': {'abc': 100}}
|
quotas = {'quota': {'abc': 100}}
|
||||||
res = self.api.put(_get_path('quotas', id=tenant_id, fmt=self.fmt),
|
res = self.api.put(_get_path('quotas', id=project_id, fmt=self.fmt),
|
||||||
self.serialize(quotas), extra_environ=env,
|
self.serialize(quotas), extra_environ=env,
|
||||||
expect_errors=True)
|
expect_errors=True)
|
||||||
self.assertEqual(400, res.status_int)
|
self.assertEqual(400, res.status_int)
|
||||||
|
|
||||||
def test_update_quotas_with_admin(self):
|
def test_update_quotas_with_admin(self):
|
||||||
tenant_id = 'tenant_id1'
|
project_id = 'project_id1'
|
||||||
env = {'neutron.context': context.Context('', tenant_id + '2',
|
env = {'neutron.context': context.Context('', project_id + '2',
|
||||||
is_admin=True)}
|
is_admin=True)}
|
||||||
quotas = {'quota': {'network': 100}}
|
quotas = {'quota': {'network': 100}}
|
||||||
res = self.api.put(_get_path('quotas', id=tenant_id, fmt=self.fmt),
|
res = self.api.put(_get_path('quotas', id=project_id, fmt=self.fmt),
|
||||||
self.serialize(quotas), extra_environ=env)
|
self.serialize(quotas), extra_environ=env)
|
||||||
self.assertEqual(200, res.status_int)
|
self.assertEqual(200, res.status_int)
|
||||||
env2 = {'neutron.context': context.Context('', tenant_id)}
|
env2 = {'neutron.context': context.Context('', project_id)}
|
||||||
res = self.api.get(_get_path('quotas', id=tenant_id, fmt=self.fmt),
|
res = self.api.get(_get_path('quotas', id=project_id, fmt=self.fmt),
|
||||||
extra_environ=env2)
|
extra_environ=env2)
|
||||||
quota = self.deserialize(res)
|
quota = self.deserialize(res)
|
||||||
self.assertEqual(100, quota['quota']['network'])
|
self.assertEqual(100, quota['quota']['network'])
|
||||||
@ -304,44 +304,44 @@ class QuotaExtensionDbTestCase(QuotaExtensionTestCase):
|
|||||||
self.assertEqual(qconf.DEFAULT_QUOTA_PORT, quota['quota']['port'])
|
self.assertEqual(qconf.DEFAULT_QUOTA_PORT, quota['quota']['port'])
|
||||||
|
|
||||||
def test_update_attributes(self):
|
def test_update_attributes(self):
|
||||||
tenant_id = 'tenant_id1'
|
project_id = 'project_id1'
|
||||||
env = {'neutron.context': context.Context('', tenant_id + '2',
|
env = {'neutron.context': context.Context('', project_id + '2',
|
||||||
is_admin=True)}
|
is_admin=True)}
|
||||||
quotas = {'quota': {'extra1': 100}}
|
quotas = {'quota': {'extra1': 100}}
|
||||||
res = self.api.put(_get_path('quotas', id=tenant_id, fmt=self.fmt),
|
res = self.api.put(_get_path('quotas', id=project_id, fmt=self.fmt),
|
||||||
self.serialize(quotas), extra_environ=env)
|
self.serialize(quotas), extra_environ=env)
|
||||||
self.assertEqual(200, res.status_int)
|
self.assertEqual(200, res.status_int)
|
||||||
env2 = {'neutron.context': context.Context('', tenant_id)}
|
env2 = {'neutron.context': context.Context('', project_id)}
|
||||||
res = self.api.get(_get_path('quotas', id=tenant_id, fmt=self.fmt),
|
res = self.api.get(_get_path('quotas', id=project_id, fmt=self.fmt),
|
||||||
extra_environ=env2)
|
extra_environ=env2)
|
||||||
quota = self.deserialize(res)
|
quota = self.deserialize(res)
|
||||||
self.assertEqual(100, quota['quota']['extra1'])
|
self.assertEqual(100, quota['quota']['extra1'])
|
||||||
|
|
||||||
def test_delete_quotas_with_admin(self):
|
def test_delete_quotas_with_admin(self):
|
||||||
tenant_id = 'tenant_id1'
|
project_id = 'project_id1'
|
||||||
env = {'neutron.context': context.Context('', tenant_id + '2',
|
env = {'neutron.context': context.Context('', project_id + '2',
|
||||||
is_admin=True)}
|
is_admin=True)}
|
||||||
# Create a quota to ensure we have something to delete
|
# Create a quota to ensure we have something to delete
|
||||||
quotas = {'quota': {'network': 100}}
|
quotas = {'quota': {'network': 100}}
|
||||||
self.api.put(_get_path('quotas', id=tenant_id, fmt=self.fmt),
|
self.api.put(_get_path('quotas', id=project_id, fmt=self.fmt),
|
||||||
self.serialize(quotas), extra_environ=env)
|
self.serialize(quotas), extra_environ=env)
|
||||||
res = self.api.delete(_get_path('quotas', id=tenant_id, fmt=self.fmt),
|
res = self.api.delete(_get_path('quotas', id=project_id, fmt=self.fmt),
|
||||||
extra_environ=env)
|
extra_environ=env)
|
||||||
self.assertEqual(204, res.status_int)
|
self.assertEqual(204, res.status_int)
|
||||||
|
|
||||||
def test_delete_quotas_without_admin_forbidden_returns_403(self):
|
def test_delete_quotas_without_admin_forbidden_returns_403(self):
|
||||||
tenant_id = 'tenant_id1'
|
project_id = 'project_id1'
|
||||||
env = {'neutron.context': context.Context('', tenant_id,
|
env = {'neutron.context': context.Context('', project_id,
|
||||||
is_admin=False)}
|
is_admin=False)}
|
||||||
res = self.api.delete(_get_path('quotas', id=tenant_id, fmt=self.fmt),
|
res = self.api.delete(_get_path('quotas', id=project_id, fmt=self.fmt),
|
||||||
extra_environ=env, expect_errors=True)
|
extra_environ=env, expect_errors=True)
|
||||||
self.assertEqual(403, res.status_int)
|
self.assertEqual(403, res.status_int)
|
||||||
|
|
||||||
def test_delete_quota_with_unknown_tenant_returns_404(self):
|
def test_delete_quota_with_unknown_project_returns_404(self):
|
||||||
tenant_id = 'idnotexist'
|
project_id = 'idnotexist'
|
||||||
env = {'neutron.context': context.Context('', tenant_id + '2',
|
env = {'neutron.context': context.Context('', project_id + '2',
|
||||||
is_admin=True)}
|
is_admin=True)}
|
||||||
res = self.api.delete(_get_path('quotas', id=tenant_id, fmt=self.fmt),
|
res = self.api.delete(_get_path('quotas', id=project_id, fmt=self.fmt),
|
||||||
extra_environ=env, expect_errors=True)
|
extra_environ=env, expect_errors=True)
|
||||||
self.assertEqual(exc.HTTPNotFound.code, res.status_int)
|
self.assertEqual(exc.HTTPNotFound.code, res.status_int)
|
||||||
|
|
||||||
@ -353,44 +353,48 @@ class QuotaExtensionDbTestCase(QuotaExtensionTestCase):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
def test_quotas_limit_check(self):
|
def test_quotas_limit_check(self):
|
||||||
tenant_id = 'tenant_id1'
|
project_id = 'project_id1'
|
||||||
env = {'neutron.context': context.Context('', tenant_id,
|
env = {'neutron.context': context.Context('', project_id,
|
||||||
is_admin=True)}
|
is_admin=True)}
|
||||||
quotas = {'quota': {'network': 5}}
|
quotas = {'quota': {'network': 5}}
|
||||||
res = self.api.put(_get_path('quotas', id=tenant_id,
|
res = self.api.put(_get_path('quotas', id=project_id,
|
||||||
fmt=self.fmt),
|
fmt=self.fmt),
|
||||||
self.serialize(quotas), extra_environ=env)
|
self.serialize(quotas), extra_environ=env)
|
||||||
self.assertEqual(200, res.status_int)
|
self.assertEqual(200, res.status_int)
|
||||||
quota.QUOTAS.limit_check(context.Context('', tenant_id),
|
quota.QUOTAS.limit_check(context.Context('', project_id),
|
||||||
tenant_id,
|
project_id,
|
||||||
network=4)
|
network=4)
|
||||||
|
|
||||||
def test_quotas_limit_check_with_invalid_quota_value(self):
|
def test_quotas_limit_check_with_invalid_quota_value(self):
|
||||||
tenant_id = 'tenant_id1'
|
project_id = 'project_id1'
|
||||||
with testtools.ExpectedException(exceptions.InvalidQuotaValue):
|
with testtools.ExpectedException(exceptions.InvalidQuotaValue):
|
||||||
quota.QUOTAS.limit_check(context.Context('', tenant_id),
|
quota.QUOTAS.limit_check(context.Context('', project_id),
|
||||||
tenant_id,
|
project_id,
|
||||||
network=-2)
|
network=-2)
|
||||||
|
|
||||||
def test_quotas_limit_check_with_not_registered_resource_fails(self):
|
def test_quotas_limit_check_with_not_registered_resource_fails(self):
|
||||||
tenant_id = 'tenant_id1'
|
project_id = 'project_id1'
|
||||||
self.assertRaises(exceptions.QuotaResourceUnknown,
|
self.assertRaises(exceptions.QuotaResourceUnknown,
|
||||||
quota.QUOTAS.limit_check,
|
quota.QUOTAS.limit_check,
|
||||||
context.get_admin_context(),
|
context.get_admin_context(),
|
||||||
tenant_id,
|
project_id,
|
||||||
foobar=1)
|
foobar=1)
|
||||||
|
|
||||||
def test_quotas_get_tenant_from_request_context(self):
|
def test_quotas_get_project_from_request_context(self):
|
||||||
tenant_id = 'tenant_id1'
|
project_id = 'project_id1'
|
||||||
env = {'neutron.context': context.Context('', tenant_id,
|
env = {'neutron.context': context.Context('', project_id,
|
||||||
is_admin=True)}
|
is_admin=True)}
|
||||||
res = self.api.get(_get_path('quotas/tenant', fmt=self.fmt),
|
# NOTE(ralonsoh): the Quota API keeps "tenant" and "project" methods
|
||||||
extra_environ=env)
|
# for compatibility. "tenant" is already deprecated and will be
|
||||||
self.assertEqual(200, res.status_int)
|
# removed.
|
||||||
quota = self.deserialize(res)
|
for key in ('project', 'tenant'):
|
||||||
self.assertEqual(quota['tenant']['tenant_id'], tenant_id)
|
res = self.api.get(_get_path('quotas/' + key, fmt=self.fmt),
|
||||||
|
extra_environ=env)
|
||||||
|
self.assertEqual(200, res.status_int)
|
||||||
|
quota = self.deserialize(res)
|
||||||
|
self.assertEqual(quota[key][key + '_id'], project_id)
|
||||||
|
|
||||||
def test_quotas_get_tenant_from_empty_request_context_returns_400(self):
|
def test_quotas_get_project_from_empty_request_context_returns_400(self):
|
||||||
env = {'neutron.context': context.Context('', '',
|
env = {'neutron.context': context.Context('', '',
|
||||||
is_admin=True)}
|
is_admin=True)}
|
||||||
res = self.api.get(_get_path('quotas/tenant', fmt=self.fmt),
|
res = self.api.get(_get_path('quotas/tenant', fmt=self.fmt),
|
||||||
@ -398,20 +402,20 @@ class QuotaExtensionDbTestCase(QuotaExtensionTestCase):
|
|||||||
self.assertEqual(400, res.status_int)
|
self.assertEqual(400, res.status_int)
|
||||||
|
|
||||||
def test_make_reservation_resource_unknown_raises(self):
|
def test_make_reservation_resource_unknown_raises(self):
|
||||||
tenant_id = 'tenant_id1'
|
project_id = 'project_id1'
|
||||||
self.assertRaises(exceptions.QuotaResourceUnknown,
|
self.assertRaises(exceptions.QuotaResourceUnknown,
|
||||||
quota.QUOTAS.make_reservation,
|
quota.QUOTAS.make_reservation,
|
||||||
context.get_admin_context(),
|
context.get_admin_context(),
|
||||||
tenant_id,
|
project_id,
|
||||||
{'foobar': 1},
|
{'foobar': 1},
|
||||||
plugin=None)
|
plugin=None)
|
||||||
|
|
||||||
def test_make_reservation_negative_delta_raises(self):
|
def test_make_reservation_negative_delta_raises(self):
|
||||||
tenant_id = 'tenant_id1'
|
project_id = 'project_id1'
|
||||||
self.assertRaises(exceptions.InvalidQuotaValue,
|
self.assertRaises(exceptions.InvalidQuotaValue,
|
||||||
quota.QUOTAS.make_reservation,
|
quota.QUOTAS.make_reservation,
|
||||||
context.get_admin_context(),
|
context.get_admin_context(),
|
||||||
tenant_id,
|
project_id,
|
||||||
{'network': -1},
|
{'network': -1},
|
||||||
plugin=None)
|
plugin=None)
|
||||||
|
|
||||||
@ -441,34 +445,34 @@ class QuotaExtensionCfgTestCase(QuotaExtensionTestCase):
|
|||||||
'extra1': qconf.DEFAULT_QUOTA})
|
'extra1': qconf.DEFAULT_QUOTA})
|
||||||
|
|
||||||
def test_show_quotas_with_admin(self):
|
def test_show_quotas_with_admin(self):
|
||||||
tenant_id = 'tenant_id1'
|
project_id = 'project_id1'
|
||||||
env = {'neutron.context': context.Context('', tenant_id + '2',
|
env = {'neutron.context': context.Context('', project_id + '2',
|
||||||
is_admin=True)}
|
is_admin=True)}
|
||||||
res = self.api.get(_get_path('quotas', id=tenant_id, fmt=self.fmt),
|
res = self.api.get(_get_path('quotas', id=project_id, fmt=self.fmt),
|
||||||
extra_environ=env)
|
extra_environ=env)
|
||||||
self.assertEqual(200, res.status_int)
|
self.assertEqual(200, res.status_int)
|
||||||
|
|
||||||
def test_show_quotas_without_admin_forbidden(self):
|
def test_show_quotas_without_admin_forbidden(self):
|
||||||
tenant_id = 'tenant_id1'
|
project_id = 'project_id1'
|
||||||
env = {'neutron.context': context.Context('', tenant_id + '2',
|
env = {'neutron.context': context.Context('', project_id + '2',
|
||||||
is_admin=False)}
|
is_admin=False)}
|
||||||
res = self.api.get(_get_path('quotas', id=tenant_id, fmt=self.fmt),
|
res = self.api.get(_get_path('quotas', id=project_id, fmt=self.fmt),
|
||||||
extra_environ=env, expect_errors=True)
|
extra_environ=env, expect_errors=True)
|
||||||
self.assertEqual(403, res.status_int)
|
self.assertEqual(403, res.status_int)
|
||||||
|
|
||||||
def test_update_quotas_forbidden(self):
|
def test_update_quotas_forbidden(self):
|
||||||
tenant_id = 'tenant_id1'
|
project_id = 'project_id1'
|
||||||
quotas = {'quota': {'network': 100}}
|
quotas = {'quota': {'network': 100}}
|
||||||
res = self.api.put(_get_path('quotas', id=tenant_id, fmt=self.fmt),
|
res = self.api.put(_get_path('quotas', id=project_id, fmt=self.fmt),
|
||||||
self.serialize(quotas),
|
self.serialize(quotas),
|
||||||
expect_errors=True)
|
expect_errors=True)
|
||||||
self.assertEqual(200, res.status_int)
|
self.assertEqual(200, res.status_int)
|
||||||
|
|
||||||
def test_delete_quotas_forbidden(self):
|
def test_delete_quotas_forbidden(self):
|
||||||
tenant_id = 'tenant_id1'
|
project_id = 'project_id1'
|
||||||
env = {'neutron.context': context.Context('', tenant_id,
|
env = {'neutron.context': context.Context('', project_id,
|
||||||
is_admin=False)}
|
is_admin=False)}
|
||||||
res = self.api.delete(_get_path('quotas', id=tenant_id, fmt=self.fmt),
|
res = self.api.delete(_get_path('quotas', id=project_id, fmt=self.fmt),
|
||||||
extra_environ=env, expect_errors=True)
|
extra_environ=env, expect_errors=True)
|
||||||
self.assertEqual(403, res.status_int)
|
self.assertEqual(403, res.status_int)
|
||||||
|
|
||||||
@ -476,7 +480,7 @@ class QuotaExtensionCfgTestCase(QuotaExtensionTestCase):
|
|||||||
class TestDbQuotaDriver(base.BaseTestCase):
|
class TestDbQuotaDriver(base.BaseTestCase):
|
||||||
"""Test for neutron.db.quota.driver.DbQuotaDriver."""
|
"""Test for neutron.db.quota.driver.DbQuotaDriver."""
|
||||||
|
|
||||||
def test_get_tenant_quotas_arg(self):
|
def test_get_project_quotas_arg(self):
|
||||||
"""Call neutron.db.quota.driver.DbQuotaDriver._get_quotas."""
|
"""Call neutron.db.quota.driver.DbQuotaDriver._get_quotas."""
|
||||||
|
|
||||||
quota_driver = driver.DbQuotaDriver()
|
quota_driver = driver.DbQuotaDriver()
|
||||||
@ -484,20 +488,20 @@ class TestDbQuotaDriver(base.BaseTestCase):
|
|||||||
|
|
||||||
foo_quotas = {'network': 5}
|
foo_quotas = {'network': 5}
|
||||||
default_quotas = {'network': 10}
|
default_quotas = {'network': 10}
|
||||||
target_tenant = 'foo'
|
target_project = 'foo'
|
||||||
|
|
||||||
with mock.patch.object(driver.DbQuotaDriver,
|
with mock.patch.object(driver.DbQuotaDriver,
|
||||||
'get_tenant_quotas',
|
'get_project_quotas',
|
||||||
return_value=foo_quotas) as get_tenant_quotas:
|
return_value=foo_quotas) as get_project_quotas:
|
||||||
|
|
||||||
quotas = quota_driver._get_quotas(ctx,
|
quotas = quota_driver._get_quotas(ctx,
|
||||||
target_tenant,
|
target_project,
|
||||||
default_quotas)
|
default_quotas)
|
||||||
|
|
||||||
self.assertEqual(quotas, foo_quotas)
|
self.assertEqual(quotas, foo_quotas)
|
||||||
get_tenant_quotas.assert_called_once_with(ctx,
|
get_project_quotas.assert_called_once_with(ctx,
|
||||||
default_quotas,
|
default_quotas,
|
||||||
target_tenant)
|
target_project)
|
||||||
|
|
||||||
|
|
||||||
class TestQuotaDriverLoad(base.BaseTestCase):
|
class TestQuotaDriverLoad(base.BaseTestCase):
|
||||||
|
@ -67,9 +67,9 @@ class DetailQuotaExtensionDbTestCase(DetailQuotaExtensionTestCase):
|
|||||||
fmt = 'json'
|
fmt = 'json'
|
||||||
|
|
||||||
def test_show_detail_quotas(self):
|
def test_show_detail_quotas(self):
|
||||||
tenant_id = 'tenant_id1'
|
project_id = 'project_id1'
|
||||||
env = {'neutron.context': context.Context('', tenant_id)}
|
env = {'neutron.context': context.Context('', project_id)}
|
||||||
res = self.api.get(_get_path('quotas', id=tenant_id,
|
res = self.api.get(_get_path('quotas', id=project_id,
|
||||||
fmt=self.fmt,
|
fmt=self.fmt,
|
||||||
endpoint=DEFAULT_QUOTAS_ACTION),
|
endpoint=DEFAULT_QUOTAS_ACTION),
|
||||||
extra_environ=env)
|
extra_environ=env)
|
||||||
@ -95,10 +95,10 @@ class DetailQuotaExtensionDbTestCase(DetailQuotaExtensionTestCase):
|
|||||||
'quota_network', -10, group='QUOTAS')
|
'quota_network', -10, group='QUOTAS')
|
||||||
cfg.CONF.set_override(
|
cfg.CONF.set_override(
|
||||||
'quota_subnet', -50, group='QUOTAS')
|
'quota_subnet', -50, group='QUOTAS')
|
||||||
tenant_id = 'tenant_id1'
|
project_id = 'project_id1'
|
||||||
env = {'neutron.context': context.Context('', tenant_id,
|
env = {'neutron.context': context.Context('', project_id,
|
||||||
is_admin=True)}
|
is_admin=True)}
|
||||||
res = self.api.get(_get_path('quotas', id=tenant_id,
|
res = self.api.get(_get_path('quotas', id=project_id,
|
||||||
fmt=self.fmt,
|
fmt=self.fmt,
|
||||||
endpoint=DEFAULT_QUOTAS_ACTION),
|
endpoint=DEFAULT_QUOTAS_ACTION),
|
||||||
extra_environ=env)
|
extra_environ=env)
|
||||||
@ -118,10 +118,10 @@ class DetailQuotaExtensionDbTestCase(DetailQuotaExtensionTestCase):
|
|||||||
quota['quota']['port']['limit'])
|
quota['quota']['port']['limit'])
|
||||||
|
|
||||||
def test_show_detail_quotas_with_admin(self):
|
def test_show_detail_quotas_with_admin(self):
|
||||||
tenant_id = 'tenant_id1'
|
project_id = 'project_id1'
|
||||||
env = {'neutron.context': context.Context('', tenant_id + '2',
|
env = {'neutron.context': context.Context('', project_id + '2',
|
||||||
is_admin=True)}
|
is_admin=True)}
|
||||||
res = self.api.get(_get_path('quotas', id=tenant_id,
|
res = self.api.get(_get_path('quotas', id=project_id,
|
||||||
fmt=self.fmt,
|
fmt=self.fmt,
|
||||||
endpoint=DEFAULT_QUOTAS_ACTION),
|
endpoint=DEFAULT_QUOTAS_ACTION),
|
||||||
extra_environ=env)
|
extra_environ=env)
|
||||||
@ -141,10 +141,10 @@ class DetailQuotaExtensionDbTestCase(DetailQuotaExtensionTestCase):
|
|||||||
quota['quota']['port']['limit'])
|
quota['quota']['port']['limit'])
|
||||||
|
|
||||||
def test_detail_quotas_without_admin_forbidden_returns_403(self):
|
def test_detail_quotas_without_admin_forbidden_returns_403(self):
|
||||||
tenant_id = 'tenant_id1'
|
project_id = 'project_id1'
|
||||||
env = {'neutron.context': context.Context('', tenant_id + '2',
|
env = {'neutron.context': context.Context('', project_id + '2',
|
||||||
is_admin=False)}
|
is_admin=False)}
|
||||||
res = self.api.get(_get_path('quotas', id=tenant_id,
|
res = self.api.get(_get_path('quotas', id=project_id,
|
||||||
fmt=self.fmt,
|
fmt=self.fmt,
|
||||||
endpoint=DEFAULT_QUOTAS_ACTION),
|
endpoint=DEFAULT_QUOTAS_ACTION),
|
||||||
extra_environ=env, expect_errors=True)
|
extra_environ=env, expect_errors=True)
|
||||||
|
@ -45,7 +45,9 @@ class BaseTestTrackedResources(test_plugin.Ml2PluginV2TestCase,
|
|||||||
test_db_base_plugin_v2.NeutronDbPluginV2TestCase.quota_db_driver = (
|
test_db_base_plugin_v2.NeutronDbPluginV2TestCase.quota_db_driver = (
|
||||||
'neutron.db.quota.driver.DbQuotaDriver')
|
'neutron.db.quota.driver.DbQuotaDriver')
|
||||||
super(BaseTestTrackedResources, self).setUp()
|
super(BaseTestTrackedResources, self).setUp()
|
||||||
self._tenant_id = uuidutils.generate_uuid()
|
self._project_id = uuidutils.generate_uuid()
|
||||||
|
# TODO(ralonsoh): "tenant_id" reference should be removed.
|
||||||
|
self._tenant_id = self._project_id
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _cleanup():
|
def _cleanup():
|
||||||
@ -54,7 +56,7 @@ class BaseTestTrackedResources(test_plugin.Ml2PluginV2TestCase,
|
|||||||
|
|
||||||
def _test_init(self, resource_name):
|
def _test_init(self, resource_name):
|
||||||
quota_db_api.set_quota_usage(
|
quota_db_api.set_quota_usage(
|
||||||
self.ctx, resource_name, self._tenant_id)
|
self.ctx, resource_name, self._project_id)
|
||||||
|
|
||||||
|
|
||||||
class BaseTestEventHandler(object):
|
class BaseTestEventHandler(object):
|
||||||
@ -88,7 +90,7 @@ class BaseTestEventHandler(object):
|
|||||||
if item:
|
if item:
|
||||||
model = self.handler_mock.call_args_list[call_idx][0][-1]
|
model = self.handler_mock.call_args_list[call_idx][0][-1]
|
||||||
self.assertEqual(model['id'], item['id'])
|
self.assertEqual(model['id'], item['id'])
|
||||||
self.assertEqual(model['tenant_id'], item['tenant_id'])
|
self.assertEqual(model['project_id'], item['project_id'])
|
||||||
call_idx = call_idx - 1
|
call_idx = call_idx - 1
|
||||||
|
|
||||||
|
|
||||||
@ -140,7 +142,7 @@ class TestTrackedResourcesEventHandler(BaseTestEventHandler,
|
|||||||
self._test_init('subnetpool')
|
self._test_init('subnetpool')
|
||||||
pool = self._make_subnetpool('json', ['10.0.0.0/8'],
|
pool = self._make_subnetpool('json', ['10.0.0.0/8'],
|
||||||
name='meh',
|
name='meh',
|
||||||
tenant_id=self._tenant_id)['subnetpool']
|
tenant_id=self._project_id)['subnetpool']
|
||||||
self._verify_event_handler_calls(pool)
|
self._verify_event_handler_calls(pool)
|
||||||
self._delete('subnetpools', pool['id'])
|
self._delete('subnetpools', pool['id'])
|
||||||
self._verify_event_handler_calls(pool, expected_call_count=2)
|
self._verify_event_handler_calls(pool, expected_call_count=2)
|
||||||
@ -148,7 +150,8 @@ class TestTrackedResourcesEventHandler(BaseTestEventHandler,
|
|||||||
def test_create_delete_securitygroup_triggers_event(self):
|
def test_create_delete_securitygroup_triggers_event(self):
|
||||||
self._test_init('security_group')
|
self._test_init('security_group')
|
||||||
sec_group = self._make_security_group(
|
sec_group = self._make_security_group(
|
||||||
'json', 'meh', 'meh', tenant_id=self._tenant_id)['security_group']
|
'json', 'meh', 'meh',
|
||||||
|
project_id=self._project_id)['security_group']
|
||||||
# When a security group is created it also creates 2 rules, therefore
|
# When a security group is created it also creates 2 rules, therefore
|
||||||
# there will be three calls and we need to verify the first
|
# there will be three calls and we need to verify the first
|
||||||
self._verify_event_handler_calls([None, None, sec_group],
|
self._verify_event_handler_calls([None, None, sec_group],
|
||||||
@ -161,9 +164,10 @@ class TestTrackedResourcesEventHandler(BaseTestEventHandler,
|
|||||||
def test_create_delete_securitygrouprule_triggers_event(self):
|
def test_create_delete_securitygrouprule_triggers_event(self):
|
||||||
self._test_init('security_group_rule')
|
self._test_init('security_group_rule')
|
||||||
sec_group = self._make_security_group(
|
sec_group = self._make_security_group(
|
||||||
'json', 'meh', 'meh', tenant_id=self._tenant_id)['security_group']
|
'json', 'meh', 'meh',
|
||||||
|
project_id=self._project_id)['security_group']
|
||||||
rule_req = self._build_security_group_rule(
|
rule_req = self._build_security_group_rule(
|
||||||
sec_group['id'], 'ingress', 'TCP', tenant_id=self._tenant_id)
|
sec_group['id'], 'ingress', 'TCP', tenant_id=self._project_id)
|
||||||
sec_group_rule = self._make_security_group_rule(
|
sec_group_rule = self._make_security_group_rule(
|
||||||
'json', rule_req)['security_group_rule']
|
'json', rule_req)['security_group_rule']
|
||||||
# When a security group is created it also creates 2 rules, therefore
|
# When a security group is created it also creates 2 rules, therefore
|
||||||
@ -213,8 +217,8 @@ class TestL3ResourcesEventHandler(BaseTestEventHandler,
|
|||||||
class TestTrackedResources(BaseTestTrackedResources):
|
class TestTrackedResources(BaseTestTrackedResources):
|
||||||
|
|
||||||
def _verify_dirty_bit(self, resource_name, expected_value=True):
|
def _verify_dirty_bit(self, resource_name, expected_value=True):
|
||||||
usage = quota_db_api.get_quota_usage_by_resource_and_tenant(
|
usage = quota_db_api.get_quota_usage_by_resource_and_project(
|
||||||
self.ctx, resource_name, self._tenant_id)
|
self.ctx, resource_name, self._project_id)
|
||||||
self.assertEqual(expected_value, usage.dirty)
|
self.assertEqual(expected_value, usage.dirty)
|
||||||
|
|
||||||
def test_create_delete_network_marks_dirty(self):
|
def test_create_delete_network_marks_dirty(self):
|
||||||
@ -223,14 +227,14 @@ class TestTrackedResources(BaseTestTrackedResources):
|
|||||||
self._verify_dirty_bit('network')
|
self._verify_dirty_bit('network')
|
||||||
# Clear the dirty bit
|
# Clear the dirty bit
|
||||||
quota_db_api.set_quota_usage_dirty(
|
quota_db_api.set_quota_usage_dirty(
|
||||||
self.ctx, 'network', self._tenant_id, dirty=False)
|
self.ctx, 'network', self._project_id, dirty=False)
|
||||||
self._delete('networks', net['id'])
|
self._delete('networks', net['id'])
|
||||||
self._verify_dirty_bit('network')
|
self._verify_dirty_bit('network')
|
||||||
|
|
||||||
def test_list_networks_clears_dirty(self):
|
def test_list_networks_clears_dirty(self):
|
||||||
self._test_init('network')
|
self._test_init('network')
|
||||||
net = self._make_network('json', 'meh', True)['network']
|
net = self._make_network('json', 'meh', True)['network']
|
||||||
self.ctx.tenant_id = net['tenant_id']
|
self.ctx.project_id = net['project_id']
|
||||||
self._list('networks', neutron_context=self.ctx)
|
self._list('networks', neutron_context=self.ctx)
|
||||||
self._verify_dirty_bit('network', expected_value=False)
|
self._verify_dirty_bit('network', expected_value=False)
|
||||||
|
|
||||||
@ -241,7 +245,7 @@ class TestTrackedResources(BaseTestTrackedResources):
|
|||||||
self._verify_dirty_bit('port')
|
self._verify_dirty_bit('port')
|
||||||
# Clear the dirty bit
|
# Clear the dirty bit
|
||||||
quota_db_api.set_quota_usage_dirty(
|
quota_db_api.set_quota_usage_dirty(
|
||||||
self.ctx, 'port', self._tenant_id, dirty=False)
|
self.ctx, 'port', self._project_id, dirty=False)
|
||||||
self._delete('ports', port['id'])
|
self._delete('ports', port['id'])
|
||||||
self._verify_dirty_bit('port')
|
self._verify_dirty_bit('port')
|
||||||
|
|
||||||
@ -249,7 +253,7 @@ class TestTrackedResources(BaseTestTrackedResources):
|
|||||||
self._test_init('port')
|
self._test_init('port')
|
||||||
net = self._make_network('json', 'meh', True)['network']
|
net = self._make_network('json', 'meh', True)['network']
|
||||||
port = self._make_port('json', net['id'])['port']
|
port = self._make_port('json', net['id'])['port']
|
||||||
self.ctx.tenant_id = port['tenant_id']
|
self.ctx.project_id = port['project_id']
|
||||||
self._list('ports', neutron_context=self.ctx)
|
self._list('ports', neutron_context=self.ctx)
|
||||||
self._verify_dirty_bit('port', expected_value=False)
|
self._verify_dirty_bit('port', expected_value=False)
|
||||||
|
|
||||||
@ -261,7 +265,7 @@ class TestTrackedResources(BaseTestTrackedResources):
|
|||||||
self._verify_dirty_bit('subnet')
|
self._verify_dirty_bit('subnet')
|
||||||
# Clear the dirty bit
|
# Clear the dirty bit
|
||||||
quota_db_api.set_quota_usage_dirty(
|
quota_db_api.set_quota_usage_dirty(
|
||||||
self.ctx, 'subnet', self._tenant_id, dirty=False)
|
self.ctx, 'subnet', self._project_id, dirty=False)
|
||||||
self._delete('subnets', subnet['id'])
|
self._delete('subnets', subnet['id'])
|
||||||
self._verify_dirty_bit('subnet')
|
self._verify_dirty_bit('subnet')
|
||||||
|
|
||||||
@ -274,7 +278,7 @@ class TestTrackedResources(BaseTestTrackedResources):
|
|||||||
self._verify_dirty_bit('subnet')
|
self._verify_dirty_bit('subnet')
|
||||||
# Clear the dirty bit
|
# Clear the dirty bit
|
||||||
quota_db_api.set_quota_usage_dirty(
|
quota_db_api.set_quota_usage_dirty(
|
||||||
self.ctx, 'subnet', self._tenant_id, dirty=False)
|
self.ctx, 'subnet', self._project_id, dirty=False)
|
||||||
self._delete('networks', net['network']['id'])
|
self._delete('networks', net['network']['id'])
|
||||||
self._verify_dirty_bit('network')
|
self._verify_dirty_bit('network')
|
||||||
self._verify_dirty_bit('subnet')
|
self._verify_dirty_bit('subnet')
|
||||||
@ -284,7 +288,7 @@ class TestTrackedResources(BaseTestTrackedResources):
|
|||||||
net = self._make_network('json', 'meh', True)
|
net = self._make_network('json', 'meh', True)
|
||||||
subnet = self._make_subnet('json', net, '10.0.0.1',
|
subnet = self._make_subnet('json', net, '10.0.0.1',
|
||||||
'10.0.0.0/24')['subnet']
|
'10.0.0.0/24')['subnet']
|
||||||
self.ctx.tenant_id = subnet['tenant_id']
|
self.ctx.project_id = subnet['project_id']
|
||||||
self._list('subnets', neutron_context=self.ctx)
|
self._list('subnets', neutron_context=self.ctx)
|
||||||
self._verify_dirty_bit('subnet', expected_value=False)
|
self._verify_dirty_bit('subnet', expected_value=False)
|
||||||
|
|
||||||
@ -292,11 +296,11 @@ class TestTrackedResources(BaseTestTrackedResources):
|
|||||||
self._test_init('subnetpool')
|
self._test_init('subnetpool')
|
||||||
pool = self._make_subnetpool('json', ['10.0.0.0/8'],
|
pool = self._make_subnetpool('json', ['10.0.0.0/8'],
|
||||||
name='meh',
|
name='meh',
|
||||||
tenant_id=self._tenant_id)['subnetpool']
|
tenant_id=self._project_id)['subnetpool']
|
||||||
self._verify_dirty_bit('subnetpool')
|
self._verify_dirty_bit('subnetpool')
|
||||||
# Clear the dirty bit
|
# Clear the dirty bit
|
||||||
quota_db_api.set_quota_usage_dirty(
|
quota_db_api.set_quota_usage_dirty(
|
||||||
self.ctx, 'subnetpool', self._tenant_id, dirty=False)
|
self.ctx, 'subnetpool', self._project_id, dirty=False)
|
||||||
self._delete('subnetpools', pool['id'])
|
self._delete('subnetpools', pool['id'])
|
||||||
self._verify_dirty_bit('subnetpool')
|
self._verify_dirty_bit('subnetpool')
|
||||||
|
|
||||||
@ -304,51 +308,51 @@ class TestTrackedResources(BaseTestTrackedResources):
|
|||||||
self._test_init('subnetpool')
|
self._test_init('subnetpool')
|
||||||
pool = self._make_subnetpool('json', ['10.0.0.0/8'],
|
pool = self._make_subnetpool('json', ['10.0.0.0/8'],
|
||||||
name='meh',
|
name='meh',
|
||||||
tenant_id=self._tenant_id)['subnetpool']
|
tenant_id=self._project_id)['subnetpool']
|
||||||
self.ctx.tenant_id = pool['tenant_id']
|
self.ctx.project_id = pool['project_id']
|
||||||
self._list('subnetpools', neutron_context=self.ctx)
|
self._list('subnetpools', neutron_context=self.ctx)
|
||||||
self._verify_dirty_bit('subnetpool', expected_value=False)
|
self._verify_dirty_bit('subnetpool', expected_value=False)
|
||||||
|
|
||||||
def test_create_delete_securitygroup_marks_dirty(self):
|
def test_create_delete_securitygroup_marks_dirty(self):
|
||||||
self._test_init('security_group')
|
self._test_init('security_group')
|
||||||
sec_group = self._make_security_group(
|
sec_group = self._make_security_group(
|
||||||
'json', 'meh', 'meh', tenant_id=self._tenant_id)['security_group']
|
'json', 'meh', 'meh', tenant_id=self._project_id)['security_group']
|
||||||
self._verify_dirty_bit('security_group')
|
self._verify_dirty_bit('security_group')
|
||||||
# Clear the dirty bit
|
# Clear the dirty bit
|
||||||
quota_db_api.set_quota_usage_dirty(
|
quota_db_api.set_quota_usage_dirty(
|
||||||
self.ctx, 'security_group', self._tenant_id, dirty=False)
|
self.ctx, 'security_group', self._project_id, dirty=False)
|
||||||
self._delete('security-groups', sec_group['id'])
|
self._delete('security-groups', sec_group['id'])
|
||||||
self._verify_dirty_bit('security_group')
|
self._verify_dirty_bit('security_group')
|
||||||
|
|
||||||
def test_list_securitygroups_clears_dirty(self):
|
def test_list_securitygroups_clears_dirty(self):
|
||||||
self._test_init('security_group')
|
self._test_init('security_group')
|
||||||
self._make_security_group(
|
self._make_security_group(
|
||||||
'json', 'meh', 'meh', tenant_id=self._tenant_id)['security_group']
|
'json', 'meh', 'meh', tenant_id=self._project_id)['security_group']
|
||||||
self.ctx.tenant_id = self._tenant_id
|
self.ctx.project_id = self._project_id
|
||||||
self._list('security-groups', neutron_context=self.ctx)
|
self._list('security-groups', neutron_context=self.ctx)
|
||||||
self._verify_dirty_bit('security_group', expected_value=False)
|
self._verify_dirty_bit('security_group', expected_value=False)
|
||||||
|
|
||||||
def test_create_delete_securitygrouprule_marks_dirty(self):
|
def test_create_delete_securitygrouprule_marks_dirty(self):
|
||||||
self._test_init('security_group_rule')
|
self._test_init('security_group_rule')
|
||||||
sec_group = self._make_security_group(
|
sec_group = self._make_security_group(
|
||||||
'json', 'meh', 'meh', tenant_id=self._tenant_id)['security_group']
|
'json', 'meh', 'meh', tenant_id=self._project_id)['security_group']
|
||||||
rule_req = self._build_security_group_rule(
|
rule_req = self._build_security_group_rule(
|
||||||
sec_group['id'], 'ingress', 'TCP', tenant_id=self._tenant_id)
|
sec_group['id'], 'ingress', 'TCP', tenant_id=self._project_id)
|
||||||
sec_group_rule = self._make_security_group_rule(
|
sec_group_rule = self._make_security_group_rule(
|
||||||
'json', rule_req)['security_group_rule']
|
'json', rule_req)['security_group_rule']
|
||||||
self._verify_dirty_bit('security_group_rule')
|
self._verify_dirty_bit('security_group_rule')
|
||||||
# Clear the dirty bit
|
# Clear the dirty bit
|
||||||
quota_db_api.set_quota_usage_dirty(
|
quota_db_api.set_quota_usage_dirty(
|
||||||
self.ctx, 'security_group_rule', self._tenant_id, dirty=False)
|
self.ctx, 'security_group_rule', self._project_id, dirty=False)
|
||||||
self._delete('security-group-rules', sec_group_rule['id'])
|
self._delete('security-group-rules', sec_group_rule['id'])
|
||||||
self._verify_dirty_bit('security_group_rule')
|
self._verify_dirty_bit('security_group_rule')
|
||||||
|
|
||||||
def test_list_securitygrouprules_clears_dirty(self):
|
def test_list_securitygrouprules_clears_dirty(self):
|
||||||
self._test_init('security_group_rule')
|
self._test_init('security_group_rule')
|
||||||
self._make_security_group(
|
self._make_security_group(
|
||||||
'json', 'meh', 'meh', tenant_id=self._tenant_id)['security_group']
|
'json', 'meh', 'meh', tenant_id=self._project_id)['security_group']
|
||||||
# As the security group create operation also creates 2 security group
|
# As the security group create operation also creates 2 security group
|
||||||
# rules there is no need to explicitly create any rule
|
# rules there is no need to explicitly create any rule
|
||||||
self.ctx.tenant_id = self._tenant_id
|
self.ctx.project_id = self._project_id
|
||||||
self._list('security-group-rules', neutron_context=self.ctx)
|
self._list('security-group-rules', neutron_context=self.ctx)
|
||||||
self._verify_dirty_bit('security_group_rule', expected_value=False)
|
self._verify_dirty_bit('security_group_rule', expected_value=False)
|
||||||
|
@ -75,22 +75,22 @@ class TestResource(base.DietTestCase):
|
|||||||
|
|
||||||
class TestTrackedResource(testlib_api.SqlTestCase):
|
class TestTrackedResource(testlib_api.SqlTestCase):
|
||||||
|
|
||||||
def _add_data(self, tenant_id=None):
|
def _add_data(self, project_id=None):
|
||||||
session = db_api.get_writer_session()
|
session = db_api.get_writer_session()
|
||||||
with session.begin():
|
with session.begin():
|
||||||
tenant_id = tenant_id or self.tenant_id
|
project_id = project_id or self.project_id
|
||||||
session.add(test_quota.MehModel(
|
session.add(test_quota.MehModel(
|
||||||
meh='meh_%s' % uuidutils.generate_uuid(),
|
meh='meh_%s' % uuidutils.generate_uuid(),
|
||||||
tenant_id=tenant_id))
|
project_id=project_id))
|
||||||
session.add(test_quota.MehModel(
|
session.add(test_quota.MehModel(
|
||||||
meh='meh_%s' % uuidutils.generate_uuid(),
|
meh='meh_%s' % uuidutils.generate_uuid(),
|
||||||
tenant_id=tenant_id))
|
project_id=project_id))
|
||||||
|
|
||||||
def _delete_data(self):
|
def _delete_data(self):
|
||||||
session = db_api.get_writer_session()
|
session = db_api.get_writer_session()
|
||||||
with session.begin():
|
with session.begin():
|
||||||
query = session.query(test_quota.MehModel).filter_by(
|
query = session.query(test_quota.MehModel).filter_by(
|
||||||
tenant_id=self.tenant_id)
|
project_id=self.project_id)
|
||||||
for item in query:
|
for item in query:
|
||||||
session.delete(item)
|
session.delete(item)
|
||||||
|
|
||||||
@ -98,7 +98,7 @@ class TestTrackedResource(testlib_api.SqlTestCase):
|
|||||||
session = db_api.get_writer_session()
|
session = db_api.get_writer_session()
|
||||||
with session.begin():
|
with session.begin():
|
||||||
query = session.query(test_quota.MehModel).filter_by(
|
query = session.query(test_quota.MehModel).filter_by(
|
||||||
tenant_id=self.tenant_id)
|
project_id=self.project_id)
|
||||||
for item in query:
|
for item in query:
|
||||||
item['meh'] = 'meh-%s' % item['meh']
|
item['meh'] = 'meh-%s' % item['meh']
|
||||||
session.add(item)
|
session.add(item)
|
||||||
@ -109,9 +109,9 @@ class TestTrackedResource(testlib_api.SqlTestCase):
|
|||||||
self.setup_coreplugin(DB_PLUGIN_KLASS)
|
self.setup_coreplugin(DB_PLUGIN_KLASS)
|
||||||
self.resource = 'meh'
|
self.resource = 'meh'
|
||||||
self.other_resource = 'othermeh'
|
self.other_resource = 'othermeh'
|
||||||
self.tenant_id = 'meh'
|
self.project_id = 'meh'
|
||||||
self.context = context.Context(
|
self.context = context.Context(
|
||||||
user_id='', tenant_id=self.tenant_id, is_admin=False)
|
user_id='', project_id=self.project_id, is_admin=False)
|
||||||
|
|
||||||
def _create_resource(self):
|
def _create_resource(self):
|
||||||
res = resource.TrackedResource(
|
res = resource.TrackedResource(
|
||||||
@ -133,7 +133,7 @@ class TestTrackedResource(testlib_api.SqlTestCase):
|
|||||||
|
|
||||||
def test_count_first_call_with_dirty_false(self):
|
def test_count_first_call_with_dirty_false(self):
|
||||||
quota_api.set_quota_usage(
|
quota_api.set_quota_usage(
|
||||||
self.context, self.resource, self.tenant_id, in_use=1)
|
self.context, self.resource, self.project_id, in_use=1)
|
||||||
res = self._create_resource()
|
res = self._create_resource()
|
||||||
self._add_data()
|
self._add_data()
|
||||||
# explicitly set dirty flag to False
|
# explicitly set dirty flag to False
|
||||||
@ -141,17 +141,17 @@ class TestTrackedResource(testlib_api.SqlTestCase):
|
|||||||
self.context, self.resource, dirty=False)
|
self.context, self.resource, dirty=False)
|
||||||
# Expect correct count to be returned anyway since the first call to
|
# Expect correct count to be returned anyway since the first call to
|
||||||
# count() always resyncs with the db
|
# count() always resyncs with the db
|
||||||
self.assertEqual(2, res.count(self.context, None, self.tenant_id))
|
self.assertEqual(2, res.count(self.context, None, self.project_id))
|
||||||
|
|
||||||
def test_count_reserved(self):
|
def test_count_reserved(self):
|
||||||
res = self._create_resource()
|
res = self._create_resource()
|
||||||
quota_api.create_reservation(self.context, self.tenant_id,
|
quota_api.create_reservation(self.context, self.project_id,
|
||||||
{res.name: 1})
|
{res.name: 1})
|
||||||
self.assertEqual(1, res.count_reserved(self.context, self.tenant_id))
|
self.assertEqual(1, res.count_reserved(self.context, self.project_id))
|
||||||
|
|
||||||
def test_count_used_first_call_with_dirty_false(self):
|
def test_count_used_first_call_with_dirty_false(self):
|
||||||
quota_api.set_quota_usage(
|
quota_api.set_quota_usage(
|
||||||
self.context, self.resource, self.tenant_id, in_use=1)
|
self.context, self.resource, self.project_id, in_use=1)
|
||||||
res = self._create_resource()
|
res = self._create_resource()
|
||||||
self._add_data()
|
self._add_data()
|
||||||
# explicitly set dirty flag to False
|
# explicitly set dirty flag to False
|
||||||
@ -160,18 +160,18 @@ class TestTrackedResource(testlib_api.SqlTestCase):
|
|||||||
# Expect correct count_used to be returned
|
# Expect correct count_used to be returned
|
||||||
# anyway since the first call to
|
# anyway since the first call to
|
||||||
# count_used() always resyncs with the db
|
# count_used() always resyncs with the db
|
||||||
self.assertEqual(2, res.count_used(self.context, self.tenant_id))
|
self.assertEqual(2, res.count_used(self.context, self.project_id))
|
||||||
|
|
||||||
def _test_count(self):
|
def _test_count(self):
|
||||||
res = self._create_resource()
|
res = self._create_resource()
|
||||||
quota_api.set_quota_usage(
|
quota_api.set_quota_usage(
|
||||||
self.context, res.name, self.tenant_id, in_use=0)
|
self.context, res.name, self.project_id, in_use=0)
|
||||||
self._add_data()
|
self._add_data()
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def test_count_with_dirty_false(self):
|
def test_count_with_dirty_false(self):
|
||||||
res = self._test_count()
|
res = self._test_count()
|
||||||
res.count(self.context, None, self.tenant_id)
|
res.count(self.context, None, self.project_id)
|
||||||
# At this stage count has been invoked, and the dirty flag should be
|
# At this stage count has been invoked, and the dirty flag should be
|
||||||
# false. Another invocation of count should not query the model class
|
# false. Another invocation of count should not query the model class
|
||||||
set_quota = 'neutron.db.quota.api.set_quota_usage'
|
set_quota = 'neutron.db.quota.api.set_quota_usage'
|
||||||
@ -179,11 +179,11 @@ class TestTrackedResource(testlib_api.SqlTestCase):
|
|||||||
self.assertEqual(0, mock_set_quota.call_count)
|
self.assertEqual(0, mock_set_quota.call_count)
|
||||||
self.assertEqual(2, res.count(self.context,
|
self.assertEqual(2, res.count(self.context,
|
||||||
None,
|
None,
|
||||||
self.tenant_id))
|
self.project_id))
|
||||||
|
|
||||||
def test_count_used_with_dirty_false(self):
|
def test_count_used_with_dirty_false(self):
|
||||||
res = self._test_count()
|
res = self._test_count()
|
||||||
res.count_used(self.context, self.tenant_id)
|
res.count_used(self.context, self.project_id)
|
||||||
# At this stage count_used has been invoked,
|
# At this stage count_used has been invoked,
|
||||||
# and the dirty flag should be false. Another invocation
|
# and the dirty flag should be false. Another invocation
|
||||||
# of count_used should not query the model class
|
# of count_used should not query the model class
|
||||||
@ -191,7 +191,7 @@ class TestTrackedResource(testlib_api.SqlTestCase):
|
|||||||
with mock.patch(set_quota) as mock_set_quota:
|
with mock.patch(set_quota) as mock_set_quota:
|
||||||
self.assertEqual(0, mock_set_quota.call_count)
|
self.assertEqual(0, mock_set_quota.call_count)
|
||||||
self.assertEqual(2, res.count_used(self.context,
|
self.assertEqual(2, res.count_used(self.context,
|
||||||
self.tenant_id))
|
self.project_id))
|
||||||
|
|
||||||
def test_count_with_dirty_true_resync(self):
|
def test_count_with_dirty_true_resync(self):
|
||||||
res = self._test_count()
|
res = self._test_count()
|
||||||
@ -199,7 +199,7 @@ class TestTrackedResource(testlib_api.SqlTestCase):
|
|||||||
# set_quota_usage has been invoked with the correct parameters
|
# set_quota_usage has been invoked with the correct parameters
|
||||||
self.assertEqual(2, res.count(self.context,
|
self.assertEqual(2, res.count(self.context,
|
||||||
None,
|
None,
|
||||||
self.tenant_id,
|
self.project_id,
|
||||||
resync_usage=True))
|
resync_usage=True))
|
||||||
|
|
||||||
def test_count_used_with_dirty_true_resync(self):
|
def test_count_used_with_dirty_true_resync(self):
|
||||||
@ -207,7 +207,7 @@ class TestTrackedResource(testlib_api.SqlTestCase):
|
|||||||
# Expect correct count_used to be returned, which also implies
|
# Expect correct count_used to be returned, which also implies
|
||||||
# set_quota_usage has been invoked with the correct parameters
|
# set_quota_usage has been invoked with the correct parameters
|
||||||
self.assertEqual(2, res.count_used(self.context,
|
self.assertEqual(2, res.count_used(self.context,
|
||||||
self.tenant_id,
|
self.project_id,
|
||||||
resync_usage=True))
|
resync_usage=True))
|
||||||
|
|
||||||
def test_count_with_dirty_true_resync_calls_set_quota_usage(self):
|
def test_count_with_dirty_true_resync_calls_set_quota_usage(self):
|
||||||
@ -216,11 +216,11 @@ class TestTrackedResource(testlib_api.SqlTestCase):
|
|||||||
with mock.patch(set_quota_usage) as mock_set_quota_usage:
|
with mock.patch(set_quota_usage) as mock_set_quota_usage:
|
||||||
quota_api.set_quota_usage_dirty(self.context,
|
quota_api.set_quota_usage_dirty(self.context,
|
||||||
self.resource,
|
self.resource,
|
||||||
self.tenant_id)
|
self.project_id)
|
||||||
res.count(self.context, None, self.tenant_id,
|
res.count(self.context, None, self.project_id,
|
||||||
resync_usage=True)
|
resync_usage=True)
|
||||||
mock_set_quota_usage.assert_called_once_with(
|
mock_set_quota_usage.assert_called_once_with(
|
||||||
self.context, self.resource, self.tenant_id, in_use=2)
|
self.context, self.resource, self.project_id, in_use=2)
|
||||||
|
|
||||||
def test_count_used_with_dirty_true_resync_calls_set_quota_usage(self):
|
def test_count_used_with_dirty_true_resync_calls_set_quota_usage(self):
|
||||||
res = self._test_count()
|
res = self._test_count()
|
||||||
@ -228,25 +228,25 @@ class TestTrackedResource(testlib_api.SqlTestCase):
|
|||||||
with mock.patch(set_quota_usage) as mock_set_quota_usage:
|
with mock.patch(set_quota_usage) as mock_set_quota_usage:
|
||||||
quota_api.set_quota_usage_dirty(self.context,
|
quota_api.set_quota_usage_dirty(self.context,
|
||||||
self.resource,
|
self.resource,
|
||||||
self.tenant_id)
|
self.project_id)
|
||||||
res.count_used(self.context, self.tenant_id,
|
res.count_used(self.context, self.project_id,
|
||||||
resync_usage=True)
|
resync_usage=True)
|
||||||
mock_set_quota_usage.assert_called_once_with(
|
mock_set_quota_usage.assert_called_once_with(
|
||||||
self.context, self.resource, self.tenant_id, in_use=2)
|
self.context, self.resource, self.project_id, in_use=2)
|
||||||
|
|
||||||
def test_count_with_dirty_true_no_usage_info(self):
|
def test_count_with_dirty_true_no_usage_info(self):
|
||||||
res = self._create_resource()
|
res = self._create_resource()
|
||||||
self._add_data()
|
self._add_data()
|
||||||
# Invoke count without having usage info in DB - Expect correct
|
# Invoke count without having usage info in DB - Expect correct
|
||||||
# count to be returned
|
# count to be returned
|
||||||
self.assertEqual(2, res.count(self.context, None, self.tenant_id))
|
self.assertEqual(2, res.count(self.context, None, self.project_id))
|
||||||
|
|
||||||
def test_count_used_with_dirty_true_no_usage_info(self):
|
def test_count_used_with_dirty_true_no_usage_info(self):
|
||||||
res = self._create_resource()
|
res = self._create_resource()
|
||||||
self._add_data()
|
self._add_data()
|
||||||
# Invoke count_used without having usage info in DB - Expect correct
|
# Invoke count_used without having usage info in DB - Expect correct
|
||||||
# count_used to be returned
|
# count_used to be returned
|
||||||
self.assertEqual(2, res.count_used(self.context, self.tenant_id))
|
self.assertEqual(2, res.count_used(self.context, self.project_id))
|
||||||
|
|
||||||
def test_count_with_dirty_true_no_usage_info_calls_set_quota_usage(self):
|
def test_count_with_dirty_true_no_usage_info_calls_set_quota_usage(self):
|
||||||
res = self._create_resource()
|
res = self._create_resource()
|
||||||
@ -255,10 +255,10 @@ class TestTrackedResource(testlib_api.SqlTestCase):
|
|||||||
with mock.patch(set_quota_usage) as mock_set_quota_usage:
|
with mock.patch(set_quota_usage) as mock_set_quota_usage:
|
||||||
quota_api.set_quota_usage_dirty(self.context,
|
quota_api.set_quota_usage_dirty(self.context,
|
||||||
self.resource,
|
self.resource,
|
||||||
self.tenant_id)
|
self.project_id)
|
||||||
res.count(self.context, None, self.tenant_id, resync_usage=True)
|
res.count(self.context, None, self.project_id, resync_usage=True)
|
||||||
mock_set_quota_usage.assert_called_once_with(
|
mock_set_quota_usage.assert_called_once_with(
|
||||||
self.context, self.resource, self.tenant_id, in_use=2)
|
self.context, self.resource, self.project_id, in_use=2)
|
||||||
|
|
||||||
def test_count_used_with_dirty_true_no_usage_info_calls_set_quota_usage(
|
def test_count_used_with_dirty_true_no_usage_info_calls_set_quota_usage(
|
||||||
self):
|
self):
|
||||||
@ -268,44 +268,44 @@ class TestTrackedResource(testlib_api.SqlTestCase):
|
|||||||
with mock.patch(set_quota_usage) as mock_set_quota_usage:
|
with mock.patch(set_quota_usage) as mock_set_quota_usage:
|
||||||
quota_api.set_quota_usage_dirty(self.context,
|
quota_api.set_quota_usage_dirty(self.context,
|
||||||
self.resource,
|
self.resource,
|
||||||
self.tenant_id)
|
self.project_id)
|
||||||
res.count_used(self.context, self.tenant_id, resync_usage=True)
|
res.count_used(self.context, self.project_id, resync_usage=True)
|
||||||
mock_set_quota_usage.assert_called_once_with(
|
mock_set_quota_usage.assert_called_once_with(
|
||||||
self.context, self.resource, self.tenant_id, in_use=2)
|
self.context, self.resource, self.project_id, in_use=2)
|
||||||
|
|
||||||
def test_add_delete_data_triggers_event(self):
|
def test_add_delete_data_triggers_event(self):
|
||||||
res = self._create_resource()
|
res = self._create_resource()
|
||||||
other_res = self._create_other_resource()
|
other_res = self._create_other_resource()
|
||||||
# Validate dirty tenants since mock does not work well with SQLAlchemy
|
# Validate dirty projects since mock does not work well with SQLAlchemy
|
||||||
# event handlers.
|
# event handlers.
|
||||||
self._add_data()
|
self._add_data()
|
||||||
self._add_data('someone_else')
|
self._add_data('someone_else')
|
||||||
self.assertEqual(2, len(res._dirty_tenants))
|
self.assertEqual(2, len(res._dirty_projects))
|
||||||
# Also, the dirty flag should not be set for other resources
|
# Also, the dirty flag should not be set for other resources
|
||||||
self.assertEqual(0, len(other_res._dirty_tenants))
|
self.assertEqual(0, len(other_res._dirty_projects))
|
||||||
self.assertIn(self.tenant_id, res._dirty_tenants)
|
self.assertIn(self.project_id, res._dirty_projects)
|
||||||
self.assertIn('someone_else', res._dirty_tenants)
|
self.assertIn('someone_else', res._dirty_projects)
|
||||||
|
|
||||||
def test_delete_data_triggers_event(self):
|
def test_delete_data_triggers_event(self):
|
||||||
res = self._create_resource()
|
res = self._create_resource()
|
||||||
self._add_data()
|
self._add_data()
|
||||||
self._add_data('someone_else')
|
self._add_data('someone_else')
|
||||||
# Artificially clear _dirty_tenants
|
# Artificially clear _dirty_projects
|
||||||
res._dirty_tenants.clear()
|
res._dirty_projects.clear()
|
||||||
self._delete_data()
|
self._delete_data()
|
||||||
# We did not delete "someone_else", so expect only a single dirty
|
# We did not delete "someone_else", so expect only a single dirty
|
||||||
# tenant
|
# project
|
||||||
self.assertEqual(1, len(res._dirty_tenants))
|
self.assertEqual(1, len(res._dirty_projects))
|
||||||
self.assertIn(self.tenant_id, res._dirty_tenants)
|
self.assertIn(self.project_id, res._dirty_projects)
|
||||||
|
|
||||||
def test_update_does_not_trigger_event(self):
|
def test_update_does_not_trigger_event(self):
|
||||||
res = self._create_resource()
|
res = self._create_resource()
|
||||||
self._add_data()
|
self._add_data()
|
||||||
self._add_data('someone_else')
|
self._add_data('someone_else')
|
||||||
# Artificially clear _dirty_tenants
|
# Artificially clear _dirty_projects
|
||||||
res._dirty_tenants.clear()
|
res._dirty_projects.clear()
|
||||||
self._update_data()
|
self._update_data()
|
||||||
self.assertEqual(0, len(res._dirty_tenants))
|
self.assertEqual(0, len(res._dirty_projects))
|
||||||
|
|
||||||
def test_mark_dirty(self):
|
def test_mark_dirty(self):
|
||||||
res = self._create_resource()
|
res = self._create_resource()
|
||||||
@ -316,11 +316,11 @@ class TestTrackedResource(testlib_api.SqlTestCase):
|
|||||||
res.mark_dirty(self.context)
|
res.mark_dirty(self.context)
|
||||||
self.assertEqual(2, mock_set_quota_usage.call_count)
|
self.assertEqual(2, mock_set_quota_usage.call_count)
|
||||||
mock_set_quota_usage.assert_any_call(
|
mock_set_quota_usage.assert_any_call(
|
||||||
self.context, self.resource, self.tenant_id)
|
self.context, self.resource, self.project_id)
|
||||||
mock_set_quota_usage.assert_any_call(
|
mock_set_quota_usage.assert_any_call(
|
||||||
self.context, self.resource, 'someone_else')
|
self.context, self.resource, 'someone_else')
|
||||||
|
|
||||||
def test_mark_dirty_no_dirty_tenant(self):
|
def test_mark_dirty_no_dirty_project(self):
|
||||||
res = self._create_resource()
|
res = self._create_resource()
|
||||||
set_quota_usage = 'neutron.db.quota.api.set_quota_usage_dirty'
|
set_quota_usage = 'neutron.db.quota.api.set_quota_usage_dirty'
|
||||||
with mock.patch(set_quota_usage) as mock_set_quota_usage:
|
with mock.patch(set_quota_usage) as mock_set_quota_usage:
|
||||||
@ -331,14 +331,14 @@ class TestTrackedResource(testlib_api.SqlTestCase):
|
|||||||
res = self._create_resource()
|
res = self._create_resource()
|
||||||
self._add_data()
|
self._add_data()
|
||||||
res.mark_dirty(self.context)
|
res.mark_dirty(self.context)
|
||||||
# self.tenant_id now is out of sync
|
# self.project_id now is out of sync
|
||||||
set_quota_usage = 'neutron.db.quota.api.set_quota_usage'
|
set_quota_usage = 'neutron.db.quota.api.set_quota_usage'
|
||||||
with mock.patch(set_quota_usage) as mock_set_quota_usage:
|
with mock.patch(set_quota_usage) as mock_set_quota_usage:
|
||||||
res.resync(self.context, self.tenant_id)
|
res.resync(self.context, self.project_id)
|
||||||
# and now it should be in sync
|
# and now it should be in sync
|
||||||
self.assertNotIn(self.tenant_id, res._out_of_sync_tenants)
|
self.assertNotIn(self.project_id, res._out_of_sync_projects)
|
||||||
mock_set_quota_usage.assert_called_once_with(
|
mock_set_quota_usage.assert_called_once_with(
|
||||||
self.context, self.resource, self.tenant_id, in_use=2)
|
self.context, self.resource, self.project_id, in_use=2)
|
||||||
|
|
||||||
|
|
||||||
class Test_CountResource(base.BaseTestCase):
|
class Test_CountResource(base.BaseTestCase):
|
||||||
@ -355,15 +355,15 @@ class Test_CountResource(base.BaseTestCase):
|
|||||||
|
|
||||||
context = mock.Mock()
|
context = mock.Mock()
|
||||||
collection_name = 'floatingips'
|
collection_name = 'floatingips'
|
||||||
tenant_id = 'fakeid'
|
project_id = 'fakeid'
|
||||||
self.assertRaises(
|
self.assertRaises(
|
||||||
NotImplementedError,
|
NotImplementedError,
|
||||||
resource._count_resource, context, collection_name, tenant_id)
|
resource._count_resource, context, collection_name, project_id)
|
||||||
|
|
||||||
for plugin in plugins.values():
|
for plugin in plugins.values():
|
||||||
for func in (plugin.get_floatingips_count, plugin.get_floatingips):
|
for func in (plugin.get_floatingips_count, plugin.get_floatingips):
|
||||||
func.assert_called_with(
|
func.assert_called_with(
|
||||||
context, filters={'tenant_id': [tenant_id]})
|
context, filters={'project_id': [project_id]})
|
||||||
|
|
||||||
def test_core_plugin_checked_first(self):
|
def test_core_plugin_checked_first(self):
|
||||||
plugin1 = mock.Mock()
|
plugin1 = mock.Mock()
|
||||||
@ -378,6 +378,6 @@ class Test_CountResource(base.BaseTestCase):
|
|||||||
|
|
||||||
context = mock.Mock()
|
context = mock.Mock()
|
||||||
collection_name = 'floatingips'
|
collection_name = 'floatingips'
|
||||||
tenant_id = 'fakeid'
|
project_id = 'fakeid'
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
10, resource._count_resource(context, collection_name, tenant_id))
|
10, resource._count_resource(context, collection_name, project_id))
|
||||||
|
@ -101,7 +101,7 @@ class TestAuxiliaryFunctions(base.DietTestCase):
|
|||||||
'TrackedResource.resync') as mock_resync:
|
'TrackedResource.resync') as mock_resync:
|
||||||
self.registry.set_tracked_resource('meh', test_quota.MehModel)
|
self.registry.set_tracked_resource('meh', test_quota.MehModel)
|
||||||
self.registry.register_resource_by_name('meh')
|
self.registry.register_resource_by_name('meh')
|
||||||
resource_registry.resync_resource(mock.ANY, 'meh', 'tenant_id')
|
resource_registry.resync_resource(mock.ANY, 'meh', 'project_id')
|
||||||
self.assertEqual(0, mock_resync.call_count)
|
self.assertEqual(0, mock_resync.call_count)
|
||||||
|
|
||||||
def test_resync_tracked_resource(self):
|
def test_resync_tracked_resource(self):
|
||||||
@ -109,14 +109,14 @@ class TestAuxiliaryFunctions(base.DietTestCase):
|
|||||||
'TrackedResource.resync') as mock_resync:
|
'TrackedResource.resync') as mock_resync:
|
||||||
self.registry.set_tracked_resource('meh', test_quota.MehModel)
|
self.registry.set_tracked_resource('meh', test_quota.MehModel)
|
||||||
self.registry.register_resource_by_name('meh')
|
self.registry.register_resource_by_name('meh')
|
||||||
resource_registry.resync_resource(mock.ANY, 'meh', 'tenant_id')
|
resource_registry.resync_resource(mock.ANY, 'meh', 'project_id')
|
||||||
mock_resync.assert_called_once_with(mock.ANY, 'tenant_id')
|
mock_resync.assert_called_once_with(mock.ANY, 'project_id')
|
||||||
|
|
||||||
def test_resync_non_tracked_resource(self):
|
def test_resync_non_tracked_resource(self):
|
||||||
with mock.patch('neutron.quota.resource.'
|
with mock.patch('neutron.quota.resource.'
|
||||||
'TrackedResource.resync') as mock_resync:
|
'TrackedResource.resync') as mock_resync:
|
||||||
self.registry.register_resource_by_name('meh')
|
self.registry.register_resource_by_name('meh')
|
||||||
resource_registry.resync_resource(mock.ANY, 'meh', 'tenant_id')
|
resource_registry.resync_resource(mock.ANY, 'meh', 'project_id')
|
||||||
self.assertEqual(0, mock_resync.call_count)
|
self.assertEqual(0, mock_resync.call_count)
|
||||||
|
|
||||||
def test_set_resources_dirty_invoked_with_tracking_disabled(self):
|
def test_set_resources_dirty_invoked_with_tracking_disabled(self):
|
||||||
@ -132,7 +132,7 @@ class TestAuxiliaryFunctions(base.DietTestCase):
|
|||||||
self.assertEqual(0, mock_mark_dirty.call_count)
|
self.assertEqual(0, mock_mark_dirty.call_count)
|
||||||
|
|
||||||
def test_set_resources_dirty_no_dirty_resource(self):
|
def test_set_resources_dirty_no_dirty_resource(self):
|
||||||
ctx = context.Context('user_id', 'tenant_id',
|
ctx = context.Context('user_id', 'project_id',
|
||||||
is_admin=False, is_advsvc=False)
|
is_admin=False, is_advsvc=False)
|
||||||
with mock.patch('neutron.quota.resource.'
|
with mock.patch('neutron.quota.resource.'
|
||||||
'TrackedResource.mark_dirty') as mock_mark_dirty:
|
'TrackedResource.mark_dirty') as mock_mark_dirty:
|
||||||
@ -140,12 +140,12 @@ class TestAuxiliaryFunctions(base.DietTestCase):
|
|||||||
self.registry.register_resource_by_name('meh')
|
self.registry.register_resource_by_name('meh')
|
||||||
res = self.registry.get_resource('meh')
|
res = self.registry.get_resource('meh')
|
||||||
# This ensures dirty is false
|
# This ensures dirty is false
|
||||||
res._dirty_tenants.clear()
|
res._dirty_projects.clear()
|
||||||
resource_registry.set_resources_dirty(ctx)
|
resource_registry.set_resources_dirty(ctx)
|
||||||
self.assertEqual(0, mock_mark_dirty.call_count)
|
self.assertEqual(0, mock_mark_dirty.call_count)
|
||||||
|
|
||||||
def test_set_resources_dirty_no_tracked_resource(self):
|
def test_set_resources_dirty_no_tracked_resource(self):
|
||||||
ctx = context.Context('user_id', 'tenant_id',
|
ctx = context.Context('user_id', 'project_id',
|
||||||
is_admin=False, is_advsvc=False)
|
is_admin=False, is_advsvc=False)
|
||||||
with mock.patch('neutron.quota.resource.'
|
with mock.patch('neutron.quota.resource.'
|
||||||
'TrackedResource.mark_dirty') as mock_mark_dirty:
|
'TrackedResource.mark_dirty') as mock_mark_dirty:
|
||||||
@ -154,7 +154,7 @@ class TestAuxiliaryFunctions(base.DietTestCase):
|
|||||||
self.assertEqual(0, mock_mark_dirty.call_count)
|
self.assertEqual(0, mock_mark_dirty.call_count)
|
||||||
|
|
||||||
def test_set_resources_dirty(self):
|
def test_set_resources_dirty(self):
|
||||||
ctx = context.Context('user_id', 'tenant_id',
|
ctx = context.Context('user_id', 'project_id',
|
||||||
is_admin=False, is_advsvc=False)
|
is_admin=False, is_advsvc=False)
|
||||||
with mock.patch('neutron.quota.resource.'
|
with mock.patch('neutron.quota.resource.'
|
||||||
'TrackedResource.mark_dirty') as mock_mark_dirty:
|
'TrackedResource.mark_dirty') as mock_mark_dirty:
|
||||||
@ -163,6 +163,6 @@ class TestAuxiliaryFunctions(base.DietTestCase):
|
|||||||
self.registry.resources['meh']._track_resource_events = True
|
self.registry.resources['meh']._track_resource_events = True
|
||||||
res = self.registry.get_resource('meh')
|
res = self.registry.get_resource('meh')
|
||||||
# This ensures dirty is true
|
# This ensures dirty is true
|
||||||
res._dirty_tenants.add('tenant_id')
|
res._dirty_projects.add('project_id')
|
||||||
resource_registry.set_resources_dirty(ctx)
|
resource_registry.set_resources_dirty(ctx)
|
||||||
mock_mark_dirty.assert_called_once_with(ctx)
|
mock_mark_dirty.assert_called_once_with(ctx)
|
||||||
|
Loading…
Reference in New Issue
Block a user