From fed19822f89ce7f6542d8ea6a4a8b85cfa6065d6 Mon Sep 17 00:00:00 2001 From: Dan Smith Date: Tue, 13 Nov 2012 10:53:06 -0800 Subject: [PATCH] Keep flavor information in system_metadata This patch copies the instance_type information into an instance's system_metadata on creation. This provides a way for us to recall the attributes used to create the instance even after the type may have been deleted. This adds db migration 153, which generates flavor information in system_metadata for existing instances in the database, as well as brings a test to make sure that works. Change-Id: Iad9d640b44df5f3831de58f9e800bcf42bc0610e --- nova/compute/api.py | 10 ++- .../153_instance_type_in_system_metadata.py | 49 ++++++++++++++ nova/tests/compute/test_compute.py | 22 ++++++ nova/tests/test_migrations.py | 67 +++++++++++++++++++ 4 files changed, 147 insertions(+), 1 deletion(-) create mode 100644 nova/db/sqlalchemy/migrate_repo/versions/153_instance_type_in_system_metadata.py diff --git a/nova/compute/api.py b/nova/compute/api.py index 06ce2e07ef93..ed56cecb502f 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -508,6 +508,13 @@ class API(base.Base): availability_zone, forced_host = self._handle_availability_zone( availability_zone) + system_metadata = {} + instance_type_props = ['id', 'name', 'memory_mb', 'vcpus', + 'root_gb', 'ephemeral_gb', 'flavorid', + 'swap', 'rxtx_factor', 'vcpu_weight'] + for k in instance_type_props: + system_metadata["instance_type_%s" % k] = instance_type[k] + base_options = { 'reservation_id': reservation_id, 'image_ref': image_href, @@ -537,7 +544,8 @@ class API(base.Base): 'access_ip_v6': access_ip_v6, 'availability_zone': availability_zone, 'root_device_name': root_device_name, - 'progress': 0} + 'progress': 0, + 'system_metadata': system_metadata} options_from_image = self._inherit_properties_from_image( image, auto_disk_config) diff --git a/nova/db/sqlalchemy/migrate_repo/versions/153_instance_type_in_system_metadata.py b/nova/db/sqlalchemy/migrate_repo/versions/153_instance_type_in_system_metadata.py new file mode 100644 index 000000000000..20e75a6eb61d --- /dev/null +++ b/nova/db/sqlalchemy/migrate_repo/versions/153_instance_type_in_system_metadata.py @@ -0,0 +1,49 @@ +# Copyright 2013 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. + +from sqlalchemy import MetaData, select, Table + + +def upgrade(migrate_engine): + meta = MetaData() + meta.bind = migrate_engine + instances = Table('instances', meta, autoload=True) + instance_types = Table('instance_types', meta, autoload=True) + sys_meta = Table('instance_system_metadata', meta, autoload=True) + + # Taken from nova/compute/api.py + instance_type_props = ['id', 'name', 'memory_mb', 'vcpus', + 'root_gb', 'ephemeral_gb', 'flavorid', + 'swap', 'rxtx_factor', 'vcpu_weight'] + + select_columns = [instances.c.uuid] + select_columns += [getattr(instance_types.c, name) + for name in instance_type_props] + + q = select(select_columns, from_obj=instances.join( + instance_types, + instances.c.instance_type_id == instance_types.c.id)) + + i = sys_meta.insert() + for values in q.execute(): + for index in range(0, len(instance_type_props)): + i.execute({"key": "instance_type_%s" % instance_type_props[index], + "value": str(values[index + 1]), + "instance_uuid": values[0]}) + + +def downgrade(migration_engine): + # This migration only touches data, and only metadata at that. No need + # to go through and delete old metadata items. + pass diff --git a/nova/tests/compute/test_compute.py b/nova/tests/compute/test_compute.py index dc381d800a38..d51c7c842360 100644 --- a/nova/tests/compute/test_compute.py +++ b/nova/tests/compute/test_compute.py @@ -3780,6 +3780,28 @@ class ComputeAPITestCase(BaseTestCase): finally: db.instance_destroy(self.context, ref[0]['uuid']) + def test_create_saves_type_in_system_metadata(self): + instance_type = instance_types.get_default_instance_type() + (ref, resv_id) = self.compute_api.create( + self.context, + instance_type=instance_type, + image_href=None) + try: + sys_metadata = db.instance_system_metadata_get(self.context, + ref[0]['uuid']) + + instance_type_props = ['name', 'memory_mb', 'vcpus', 'root_gb', + 'ephemeral_gb', 'flavorid', 'swap', + 'rxtx_factor', 'vcpu_weight'] + for key in instance_type_props: + sys_meta_key = "instance_type_%s" % key + self.assertTrue(sys_meta_key in sys_metadata) + self.assertEqual(str(instance_type[key]), + str(sys_metadata[sys_meta_key])) + + finally: + db.instance_destroy(self.context, ref[0]['uuid']) + def test_create_instance_associates_security_groups(self): # Make sure create associates security groups. group = self._create_group() diff --git a/nova/tests/test_migrations.py b/nova/tests/test_migrations.py index 3b67e64399b0..8c18d55118f0 100644 --- a/nova/tests/test_migrations.py +++ b/nova/tests/test_migrations.py @@ -24,6 +24,7 @@ properly both upgrading and downgrading, and that no data loss occurs if possible. """ +import collections import commands import ConfigParser import os @@ -452,3 +453,69 @@ class TestMigrations(test.TestCase): self.assertEqual("", volume.deleted) volume = volumes.select(volumes.c.id == "second").execute().first() self.assertEqual(volume.id, volume.deleted) + + # migration 153, copy flavor information into system_metadata + def _prerun_153(self, engine): + fake_types = [ + dict(id=10, name='type1', memory_mb=128, vcpus=1, + root_gb=10, ephemeral_gb=0, flavorid="1", swap=0, + rxtx_factor=1.0, vcpu_weight=1, disabled=False, + is_public=True), + dict(id=11, name='type2', memory_mb=512, vcpus=1, + root_gb=10, ephemeral_gb=5, flavorid="2", swap=0, + rxtx_factor=1.5, vcpu_weight=2, disabled=False, + is_public=True), + dict(id=12, name='type3', memory_mb=128, vcpus=1, + root_gb=10, ephemeral_gb=0, flavorid="3", swap=0, + rxtx_factor=1.0, vcpu_weight=1, disabled=False, + is_public=False), + dict(id=13, name='type4', memory_mb=128, vcpus=1, + root_gb=10, ephemeral_gb=0, flavorid="4", swap=0, + rxtx_factor=1.0, vcpu_weight=1, disabled=True, + is_public=True), + dict(id=14, name='type5', memory_mb=128, vcpus=1, + root_gb=10, ephemeral_gb=0, flavorid="5", swap=0, + rxtx_factor=1.0, vcpu_weight=1, disabled=True, + is_public=False), + ] + + fake_instances = [ + dict(uuid='m153-uuid1', instance_type_id=10), + dict(uuid='m153-uuid2', instance_type_id=11), + dict(uuid='m153-uuid3', instance_type_id=12), + dict(uuid='m153-uuid4', instance_type_id=13), + # NOTE(danms): no use of type5 + ] + + instances = get_table(engine, 'instances') + instance_types = get_table(engine, 'instance_types') + engine.execute(instance_types.insert(), fake_types) + engine.execute(instances.insert(), fake_instances) + + return fake_types, fake_instances + + def _check_153(self, engine, data): + fake_types, fake_instances = data + # NOTE(danms): Fetch all the tables and data from scratch after change + instances = get_table(engine, 'instances') + instance_types = get_table(engine, 'instance_types') + sys_meta = get_table(engine, 'instance_system_metadata') + + # Collect all system metadata, indexed by instance_uuid + metadata = collections.defaultdict(dict) + for values in sys_meta.select().execute(): + metadata[values['instance_uuid']][values['key']] = values['value'] + + # Taken from nova/compute/api.py + instance_type_props = ['id', 'name', 'memory_mb', 'vcpus', + 'root_gb', 'ephemeral_gb', 'flavorid', + 'swap', 'rxtx_factor', 'vcpu_weight'] + + for instance in fake_instances: + inst_sys_meta = metadata[instance['uuid']] + inst_type = fake_types[instance['instance_type_id'] - 10] + for prop in instance_type_props: + prop_name = 'instance_type_%s' % prop + self.assertIn(prop_name, inst_sys_meta) + self.assertEqual(str(inst_sys_meta[prop_name]), + str(inst_type[prop]))