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]))