diff --git a/src/coldfront_plugin_cloud/management/commands/validate_allocations.py b/src/coldfront_plugin_cloud/management/commands/validate_allocations.py index f86a135..85c4f45 100644 --- a/src/coldfront_plugin_cloud/management/commands/validate_allocations.py +++ b/src/coldfront_plugin_cloud/management/commands/validate_allocations.py @@ -105,6 +105,8 @@ def handle(self, *args, **options): failed_validation = Command.sync_users(project_id, allocation, allocator, options["apply"]) + obj_key = openstack.QUOTA_KEY_MAPPING['object']['keys'][attributes.QUOTA_OBJECT_GB] + for attr in attributes.ALLOCATION_QUOTA_ATTRIBUTES: if 'OpenStack' in attr.name: key = openstack.QUOTA_KEY_MAPPING_ALL_KEYS.get(attr.name, None) @@ -116,7 +118,15 @@ def handle(self, *args, **options): expected_value = allocation.get_attribute(attr.name) current_value = quota.get(key, None) - if expected_value is None and current_value: + if key == obj_key and expected_value <= 0: + expected_obj_value = 1 + current_value = allocator.object(project_id).head_account().get(obj_key) + if current_value != expected_obj_value: + failed_validation = True + msg = (f'Value for quota for {attr.name} = {current_value} does not match expected' + f' value of {expected_obj_value} on allocation {allocation_str}') + logger.warning(msg) + elif expected_value is None and current_value: msg = (f'Attribute "{attr.name}" expected on allocation {allocation_str} but not set.' f' Current quota is {current_value}.') if options['apply']: @@ -132,9 +142,13 @@ def handle(self, *args, **options): logger.warning(msg) if failed_validation and options['apply']: - allocator.set_quota( - allocation.get_attribute(attributes.ALLOCATION_PROJECT_ID) - ) + try: + allocator.set_quota( + allocation.get_attribute(attributes.ALLOCATION_PROJECT_ID) + ) + except Exception as e: + logger.error(f'setting openstack quota failed: {e}') + continue logger.warning(f'Quota for allocation {allocation_str} was out of date. Reapplied!') # Deal with OpenShift @@ -229,8 +243,10 @@ def handle(self, *args, **options): current_value = round(current_value / suffix["Mi"]) elif "Storage" in attr.name: current_value = round(current_value / suffix["Gi"]) + elif current_value and current_value == "0": + current_value = 0 - if expected_value is None and current_value: + if expected_value is None and current_value is not None: msg = ( f'Attribute "{attr.name}" expected on allocation {allocation_str} but not set.' f" Current quota is {current_value}." @@ -249,7 +265,11 @@ def handle(self, *args, **options): logger.warning(msg) if options["apply"]: - allocator.set_quota(project_id) - logger.warning( - f"Quota for allocation {project_id} was out of date. Reapplied!" - ) + try: + allocator.set_quota(project_id) + logger.warning( + f"Quota for allocation {project_id} was out of date. Reapplied!" + ) + except Exception as e: + logger.error(f'setting openshift quota failed: {e}') + continue diff --git a/src/coldfront_plugin_cloud/openstack.py b/src/coldfront_plugin_cloud/openstack.py index d9ce0cf..db61b4c 100644 --- a/src/coldfront_plugin_cloud/openstack.py +++ b/src/coldfront_plugin_cloud/openstack.py @@ -188,9 +188,12 @@ def _set_object_quota(self, project_id, payload): # Note(knikolla): For consistency with other OpenStack # quotas we're storing this as GB on the attribute and # converting to bytes for Swift. - payload[QUOTA_KEY_MAPPING['object']['keys'][ - attributes.QUOTA_OBJECT_GB] - ] *= GB_IN_BYTES + obj_q_mapping = QUOTA_KEY_MAPPING['object']['keys'][ + attributes.QUOTA_OBJECT_GB + ] + payload[obj_q_mapping] *= GB_IN_BYTES + if payload[obj_q_mapping] <= 0: + payload[obj_q_mapping] = 1 self.object(project_id).post_account(headers=payload) except ksa_exceptions.catalog.EndpointNotFound: logger.debug('No swift available, skipping its quota.') diff --git a/src/coldfront_plugin_cloud/tests/functional/openstack/test_allocation.py b/src/coldfront_plugin_cloud/tests/functional/openstack/test_allocation.py index 7a45c97..2c523e7 100644 --- a/src/coldfront_plugin_cloud/tests/functional/openstack/test_allocation.py +++ b/src/coldfront_plugin_cloud/tests/functional/openstack/test_allocation.py @@ -205,6 +205,40 @@ def test_new_allocation_with_quantity(self): actual_nova_quota = self.compute.quotas.get(openstack_project.id) self.assertEqual(actual_nova_quota.__getattr__('cores'), 200) + # Change allocation attributes for object store quota + current_quota = allocator.get_quota(openstack_project.id) + obj_key = openstack.QUOTA_KEY_MAPPING['object']['keys'][attributes.QUOTA_OBJECT_GB] + if obj_key in current_quota.keys(): + utils.set_attribute_on_allocation(allocation, attributes.QUOTA_OBJECT_GB, 6) + self.assertEqual(allocation.get_attribute(attributes.QUOTA_OBJECT_GB), 6) + tasks.activate_allocation(allocation.pk) + self.assertEqual( + allocation.get_attribute(attributes.QUOTA_OBJECT_GB), + allocator.get_quota(openstack_project.id)[obj_key] + ) + + # setting 0 object quota in coldfront -> 1 byte quota in swift/rgw + utils.set_attribute_on_allocation(allocation, attributes.QUOTA_OBJECT_GB, 0) + self.assertEqual(allocation.get_attribute(attributes.QUOTA_OBJECT_GB), 0) + tasks.activate_allocation(allocation.pk) + obj_quota = allocator.object(project_id).head_account().get(obj_key) + self.assertEqual(int(obj_quota), 1) + + # test validate_allocations works for object quota set to 0 + utils.set_attribute_on_allocation(allocation, attributes.QUOTA_OBJECT_GB, 3) + self.assertEqual(allocation.get_attribute(attributes.QUOTA_OBJECT_GB), 3) + tasks.activate_allocation(allocation.pk) + self.assertEqual( + allocation.get_attribute(attributes.QUOTA_OBJECT_GB), + allocator.get_quota(openstack_project.id)[obj_key] + ) + utils.set_attribute_on_allocation(allocation, attributes.QUOTA_OBJECT_GB, 0) + self.assertEqual(allocation.get_attribute(attributes.QUOTA_OBJECT_GB), 0) + call_command('validate_allocations', apply=True) + obj_quota = allocator.object(project_id).head_account().get(obj_key) + self.assertEqual(int(obj_quota), 1) + + def test_reactivate_allocation(self): user = self.new_user() project = self.new_project(pi=user)