From 7ba41320f3ee36116d114874cb01d589a92f3291 Mon Sep 17 00:00:00 2001 From: Valeriy Ponomaryov Date: Fri, 13 Oct 2017 10:54:05 +0300 Subject: [PATCH] Fix 'project_share_type_quotas' DB table unique constraint It should be possible to use share type quotas in each project/tenant. But we get error trying to set share types quota in second+ project for the same share type. It happens so due to the bug in DB schema, where we don't have 'project_id' in unique constraint. So, add new DB migration that fixes unique constraint and cover it with DB and tempest tests. Change-Id: I1405ced4e12b40904b7227c9f6285e2775005f8a Closes-Bug: #1722707 --- ...ect_share_type_quotas_unique_constraint.py | 45 +++++++++++++++++++ .../alembic/migrations_data_checks.py | 34 ++++++++++++++ .../tests/api/admin/test_quotas.py | 24 ++++++++++ 3 files changed, 103 insertions(+) create mode 100644 manila/db/migrations/alembic/versions/829a09b0ddd4_fix_project_share_type_quotas_unique_constraint.py diff --git a/manila/db/migrations/alembic/versions/829a09b0ddd4_fix_project_share_type_quotas_unique_constraint.py b/manila/db/migrations/alembic/versions/829a09b0ddd4_fix_project_share_type_quotas_unique_constraint.py new file mode 100644 index 0000000000..16482ad924 --- /dev/null +++ b/manila/db/migrations/alembic/versions/829a09b0ddd4_fix_project_share_type_quotas_unique_constraint.py @@ -0,0 +1,45 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +"""Fix 'project_share_type_quotas' unique constraint + +Revision ID: 829a09b0ddd4 +Revises: b516de97bfee +Create Date: 2017-10-12 20:15:51.267488 + +""" + +# revision identifiers, used by Alembic. +revision = '829a09b0ddd4' +down_revision = 'b516de97bfee' + +from alembic import op + +TABLE_NAME = 'project_share_type_quotas' +UNIQUE_CONSTRAINT_NAME = 'uc_quotas_per_share_types' +ST_FK_NAME = 'share_type_id_fk' + + +def upgrade(): + op.drop_constraint(ST_FK_NAME, TABLE_NAME, type_='foreignkey') + op.drop_constraint(UNIQUE_CONSTRAINT_NAME, TABLE_NAME, type_='unique') + op.create_foreign_key( + ST_FK_NAME, TABLE_NAME, 'share_types', ['share_type_id'], ['id']) + op.create_unique_constraint( + UNIQUE_CONSTRAINT_NAME, TABLE_NAME, + ['share_type_id', 'resource', 'deleted', 'project_id']) + + +def downgrade(): + # NOTE(vponomaryov): no need to implement old behaviour as it was bug, and, + # moreover, not compatible with data from upgraded version. + pass diff --git a/manila/tests/db/migrations/alembic/migrations_data_checks.py b/manila/tests/db/migrations/alembic/migrations_data_checks.py index f3215be531..4b62ea0f4f 100644 --- a/manila/tests/db/migrations/alembic/migrations_data_checks.py +++ b/manila/tests/db/migrations/alembic/migrations_data_checks.py @@ -2466,3 +2466,37 @@ class ProjectShareTypesQuotasChecks(BaseMigrationChecks): self.test_case.assertGreater(db_result.rowcount, 0) for row in db_result: self.test_case.assertFalse(hasattr(row, 'share_type_id')) + + +@map_to_migration('829a09b0ddd4') +class FixProjectShareTypesQuotasUniqueConstraintChecks(BaseMigrationChecks): + st_record_id = uuidutils.generate_uuid() + + def setup_upgrade_data(self, engine): + # Create share type + self.st_data = { + 'id': self.st_record_id, + 'name': uuidutils.generate_uuid(), + 'deleted': "False", + } + st_table = utils.load_table('share_types', engine) + engine.execute(st_table.insert(self.st_data)) + + def check_upgrade(self, engine, data): + for project_id in ('x' * 255, 'x'): + # Create share type quota + self.quota_data = { + 'project_id': project_id, + 'resource': 'y' * 255, + 'hard_limit': 987654321, + 'created_at': datetime.datetime(2017, 4, 11, 18, 5, 58), + 'updated_at': None, + 'deleted_at': None, + 'deleted': 0, + 'share_type_id': self.st_record_id, + } + new_table = utils.load_table('project_share_type_quotas', engine) + engine.execute(new_table.insert(self.quota_data)) + + def check_downgrade(self, engine): + pass diff --git a/manila_tempest_tests/tests/api/admin/test_quotas.py b/manila_tempest_tests/tests/api/admin/test_quotas.py index 02d0a62d86..d94582860f 100644 --- a/manila_tempest_tests/tests/api/admin/test_quotas.py +++ b/manila_tempest_tests/tests/api/admin/test_quotas.py @@ -247,6 +247,30 @@ class SharesAdminQuotasUpdateTest(base.BaseSharesAdminTest): for q in ('shares', 'gigabytes', 'snapshots', 'snapshot_gigabytes'): self.assertEqual(int(quotas[q]) - 1, current_quotas[q]) + @tc.attr(base.TAG_POSITIVE, base.TAG_API) + @base.skip_if_microversion_lt("2.39") + def test_update_share_type_quota_in_two_projects(self): + """Regression test for bug/1722707""" + share_type = self._create_share_type() + client1 = self.get_client_with_isolated_creds(client_version='2') + client2 = self.get_client_with_isolated_creds(client_version='2') + + for client in (client1, client2): + # Update quotas + for q in ('shares', 'gigabytes', 'snapshots', + 'snapshot_gigabytes'): + # Set new quota + updated = client.update_quotas( + client.tenant_id, share_type=share_type['id'], **{q: 0}) + self.assertEqual(0, int(updated[q])) + + current_quotas = client.show_quotas( + client.tenant_id, share_type=share_type['id']) + + for q in ('shares', 'gigabytes', 'snapshots', + 'snapshot_gigabytes'): + self.assertEqual(0, int(current_quotas[q])) + @tc.attr(base.TAG_POSITIVE, base.TAG_API) def test_update_tenant_quota_snapshots(self): # get current quotas