diff --git a/glance/db/sqlalchemy/migrate_repo/versions/033_add_location_status.py b/glance/db/sqlalchemy/migrate_repo/versions/033_add_location_status.py new file mode 100644 index 0000000000..af8d4b0eac --- /dev/null +++ b/glance/db/sqlalchemy/migrate_repo/versions/033_add_location_status.py @@ -0,0 +1,52 @@ +# Copyright 2014 IBM Corp. +# +# 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. + +import sqlalchemy + +from glance.db.sqlalchemy.migrate_repo import schema + + +def upgrade(migrate_engine): + meta = sqlalchemy.schema.MetaData() + meta.bind = migrate_engine + + images_table = sqlalchemy.Table('images', meta, autoload=True) + image_locations_table = sqlalchemy.Table('image_locations', meta, + autoload=True) + + # Create 'status' column for image_locations table + status = sqlalchemy.Column('status', schema.String(30), + server_default='active', nullable=False) + status.create(image_locations_table) + + # Set 'status' column initial value for image_locations table + mapping = {'active': 'active', 'pending_delete': 'pending_delete', + 'deleted': 'deleted', 'killed': 'deleted'} + for src, dst in mapping.iteritems(): + subq = sqlalchemy.sql.select([images_table.c.id])\ + .where(images_table.c.status == src) + image_locations_table.update(values={'status': dst})\ + .where(image_locations_table.c.image_id.in_(subq))\ + .execute() + + +def downgrade(migrate_engine): + meta = sqlalchemy.schema.MetaData() + meta.bind = migrate_engine + + image_locations_table = sqlalchemy.Table('image_locations', meta, + autoload=True) + + # Remove 'status' column from image_locations table + image_locations_table.columns['status'].drop() diff --git a/glance/db/sqlalchemy/models.py b/glance/db/sqlalchemy/models.py index 1d4823b25f..6dd3ff2633 100644 --- a/glance/db/sqlalchemy/models.py +++ b/glance/db/sqlalchemy/models.py @@ -178,6 +178,7 @@ class ImageLocation(BASE, GlanceBase): image = relationship(Image, backref=backref('locations')) value = Column(Text(), nullable=False) meta_data = Column(JSONEncodedDict(), default={}) + status = Column(String(30), default='active', nullable=False) class ImageMember(BASE, GlanceBase): diff --git a/glance/tests/unit/test_migrations.py b/glance/tests/unit/test_migrations.py index 06fda46c16..65553657b4 100644 --- a/glance/tests/unit/test_migrations.py +++ b/glance/tests/unit/test_migrations.py @@ -1172,3 +1172,56 @@ class TestMigrations(test_utils.BaseTestCase): self.assertIsNone(task_2.input) self.assertIsNone(task_2.result) self.assertIsNone(task_2.message) + + def _pre_upgrade_033(self, engine): + images = get_table(engine, 'images') + image_locations = get_table(engine, 'image_locations') + + now = datetime.datetime.now() + image_id = 'fake_id_028_%d' + url = 'file:///some/place/onthe/fs_%d' + status_list = ['active', 'saving', 'queued', 'killed', + 'pending_delete', 'deleted'] + image_id_list = [] + + for (idx, status) in enumerate(status_list): + temp = dict(deleted=False, + created_at=now, + updated_at=now, + status=status, + is_public=True, + min_disk=0, + min_ram=0, + id=image_id % idx) + images.insert().values(temp).execute() + + temp = dict(deleted=False, + created_at=now, + updated_at=now, + image_id=image_id % idx, + value=url % idx) + image_locations.insert().values(temp).execute() + + image_id_list.append(image_id % idx) + return image_id_list + + def _check_033(self, engine, data): + image_locations = get_table(engine, 'image_locations') + + self.assertIn('status', image_locations.c) + self.assertEqual(image_locations.c['status'].type.length, 30) + + status_list = ['active', 'active', 'active', + 'deleted', 'pending_delete', 'deleted'] + + for (idx, image_id) in enumerate(data): + results = image_locations.select()\ + .where(image_locations.c.image_id == image_id).execute() + r = list(results) + self.assertEqual(len(r), 1) + self.assertTrue('status' in r[0]) + self.assertEqual(r[0]['status'], status_list[idx]) + + def _post_downgrade_033(self, engine): + image_locations = get_table(engine, 'image_locations') + self.assertNotIn('status', image_locations.c)