diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 2281920b7288..ea830b5f3275 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -1581,7 +1581,7 @@ def _handle_objects_related_type_conversions(values): values[key] = str(values[key]) datetime_keys = ('created_at', 'deleted_at', 'updated_at', - 'launched_at', 'terminated_at', 'scheduled_at') + 'launched_at', 'terminated_at') convert_objects_related_datetimes(values, *datetime_keys) diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py index 29c7dc2e18e3..91e6ca655932 100644 --- a/nova/db/sqlalchemy/models.py +++ b/nova/db/sqlalchemy/models.py @@ -273,7 +273,9 @@ class Instance(BASE, NovaBase): reservation_id = Column(String(255)) - scheduled_at = Column(DateTime) + # NOTE(sbiswas7): 'scheduled_at' is still in the database + # and can be removed in the future release. + launched_at = Column(DateTime) terminated_at = Column(DateTime) diff --git a/nova/objects/instance.py b/nova/objects/instance.py index e834a23b80b8..0a02ebe89b61 100644 --- a/nova/objects/instance.py +++ b/nova/objects/instance.py @@ -158,6 +158,8 @@ class Instance(base.NovaPersistentObject, base.NovaObject, 'reservation_id': fields.StringField(nullable=True), + # NOTE(sbiswas7): this field is depcrecated, + # will be removed in instance v2.0 'scheduled_at': fields.DateTimeField(nullable=True), 'launched_at': fields.DateTimeField(nullable=True), 'terminated_at': fields.DateTimeField(nullable=True), @@ -457,6 +459,8 @@ class Instance(base.NovaPersistentObject, base.NovaObject, instance.deleted = db_inst['deleted'] == db_inst['id'] elif field == 'cleaned': instance.cleaned = db_inst['cleaned'] == 1 + elif field == 'scheduled_at': + instance.scheduled_at = None else: instance[field] = db_inst[field] @@ -569,6 +573,9 @@ class Instance(base.NovaPersistentObject, base.NovaObject, updates = self.obj_get_changes() expected_attrs = [attr for attr in INSTANCE_DEFAULT_FIELDS if attr in updates] + if 'scheduled_at' in updates: + # NOTE(sbiswas7): 'scheduled_at' is not present in models. + del updates['scheduled_at'] if 'security_groups' in updates: updates['security_groups'] = [x.name for x in updates['security_groups']] @@ -794,6 +801,10 @@ class Instance(base.NovaPersistentObject, base.NovaObject, self._maybe_upgrade_flavor() updates = {} changes = self.obj_what_changed() + if 'scheduled_at' in changes: + # NOTE(sbiswas7): Since 'scheduled_at' is removed from models, + # we need to discard it. + changes.remove('scheduled_at') for field in self.fields: # NOTE(danms): For object fields, we construct and call a @@ -861,7 +872,6 @@ class Instance(base.NovaPersistentObject, base.NovaObject, old_ref, inst_ref = db.instance_update_and_get_original( context, self.uuid, updates, columns_to_join=_expected_cols(expected_attrs)) - self._from_db_object(context, self, inst_ref, expected_attrs=expected_attrs) diff --git a/nova/tests/unit/api/openstack/compute/test_consoles.py b/nova/tests/unit/api/openstack/compute/test_consoles.py index 04a82050e826..57c9aade9749 100644 --- a/nova/tests/unit/api/openstack/compute/test_consoles.py +++ b/nova/tests/unit/api/openstack/compute/test_consoles.py @@ -108,7 +108,6 @@ def stub_instance(id, user_id='fake', project_id='fake', host=None, "user_data": "", "reservation_id": reservation_id, "mac_address": "", - "scheduled_at": timeutils.utcnow(), "launched_at": timeutils.utcnow(), "terminated_at": timeutils.utcnow(), "availability_zone": "", diff --git a/nova/tests/unit/api/openstack/fakes.py b/nova/tests/unit/api/openstack/fakes.py index b5628a558910..a56228cd8d9d 100644 --- a/nova/tests/unit/api/openstack/fakes.py +++ b/nova/tests/unit/api/openstack/fakes.py @@ -543,7 +543,6 @@ def stub_instance(id=1, user_id=None, project_id=None, host=None, "user_data": user_data, "reservation_id": reservation_id, "mac_address": "", - "scheduled_at": timeutils.utcnow(), "launched_at": launched_at, "terminated_at": terminated_at, "availability_zone": availability_zone, diff --git a/nova/tests/unit/compute/test_resource_tracker.py b/nova/tests/unit/compute/test_resource_tracker.py index 297299e8f13b..74a52d6ee26e 100644 --- a/nova/tests/unit/compute/test_resource_tracker.py +++ b/nova/tests/unit/compute/test_resource_tracker.py @@ -329,7 +329,6 @@ class BaseTestCase(test.TestCase): 'display_name': None, 'default_swap_device': None, 'power_state': None, - 'scheduled_at': None, 'access_ip_v6': None, 'access_ip_v4': None, 'key_name': None, diff --git a/nova/tests/unit/db/test_db_api.py b/nova/tests/unit/db/test_db_api.py index d719bdcea908..7024a56c4960 100644 --- a/nova/tests/unit/db/test_db_api.py +++ b/nova/tests/unit/db/test_db_api.py @@ -1938,7 +1938,7 @@ class InstanceTestCase(test.TestCase, ModelsObjectComparatorMixin): 'access_ip_v6': netaddr.IPAddress('::1'), } dt_keys = ('created_at', 'deleted_at', 'updated_at', - 'launched_at', 'terminated_at', 'scheduled_at') + 'launched_at', 'terminated_at') dt = timeutils.utcnow() dt_utc = dt.replace(tzinfo=iso8601.iso8601.Utc()) for key in dt_keys: @@ -1955,7 +1955,7 @@ class InstanceTestCase(test.TestCase, ModelsObjectComparatorMixin): 'access_ip_v6': netaddr.IPAddress('::1'), } dt_keys = ('created_at', 'deleted_at', 'updated_at', - 'launched_at', 'terminated_at', 'scheduled_at') + 'launched_at', 'terminated_at') dt = timeutils.utcnow() dt_utc = dt.replace(tzinfo=iso8601.iso8601.Utc()) for key in dt_keys: diff --git a/nova/tests/unit/db/test_migrations.py b/nova/tests/unit/db/test_migrations.py index 8fc057c20414..e6a45548d277 100644 --- a/nova/tests/unit/db/test_migrations.py +++ b/nova/tests/unit/db/test_migrations.py @@ -766,6 +766,22 @@ class NovaMigrationsCheckers(test_migrations.ModelsMigrationsSync, # the point-of-view of unit tests, since they use SQLite pass + def filter_metadata_diff(self, diff): + # Overriding the parent method to decide on certain attributes + # that maybe present in the DB but not in the models.py + # Define a whitelist of columns that would be removed from the + # DB at a later release. + column_whitelist = {'instances': ['scheduled_at']} + for element in diff: + # Assuming that the diff structure will remain constant + # The 4th element in the diff is always the Column object + for key, values in column_whitelist.iteritems(): + table_name = element[3].table.name + table_column = element[3].name + if key == table_name and table_column in values: + diff.remove(element) + return diff + class TestNovaMigrationsSQLite(NovaMigrationsCheckers, test_base.DbTestCase, diff --git a/nova/tests/unit/objects/test_instance.py b/nova/tests/unit/objects/test_instance.py index c473882ac1b7..9b602d6995d3 100644 --- a/nova/tests/unit/objects/test_instance.py +++ b/nova/tests/unit/objects/test_instance.py @@ -52,7 +52,6 @@ class _TestInstanceObject(object): access_ip_v6='::1') db_inst['uuid'] = '34fd7606-2ed5-42c7-ad46-76240c088801' db_inst['cell_name'] = 'api!child' - db_inst['scheduled_at'] = None db_inst['terminated_at'] = None db_inst['deleted_at'] = None db_inst['created_at'] = None @@ -425,6 +424,17 @@ class _TestInstanceObject(object): inst.save() self.assertTrue(save_mock.called) + @mock.patch('nova.db.instance_update_and_get_original') + @mock.patch.object(objects.Instance, '_from_db_object') + def test_save_skip_scheduled_at(self, mock_fdo, mock_update): + mock_update.return_value = None, None + inst = instance.Instance(context=self.context, id=123) + inst.uuid = 'foo' + inst.scheduled_at = None + inst.save() + self.assertNotIn('scheduled_at', + mock_update.call_args_list[0][0][2]) + @mock.patch('nova.db.instance_update_and_get_original') @mock.patch.object(objects.Instance, '_from_db_object') def test_save_does_not_refresh_pci_devices(self, mock_fdo, mock_update): @@ -823,6 +833,22 @@ class _TestInstanceObject(object): inst.obj_reset_changes() self.assertEqual(set(), inst.obj_what_changed()) + def test_create_skip_scheduled_at(self): + self.mox.StubOutWithMock(db, 'instance_create') + vals = {'host': 'foo-host', + 'memory_mb': 128, + 'system_metadata': {'foo': 'bar'}, + 'extra': {}} + fake_inst = fake_instance.fake_db_instance(**vals) + db.instance_create(self.context, vals).AndReturn(fake_inst) + self.mox.ReplayAll() + inst = instance.Instance(context=self.context, + host='foo-host', memory_mb=128, + scheduled_at=None, + system_metadata={'foo': 'bar'}) + inst.create() + self.assertEqual(inst.host, 'foo-host') + def test_metadata_change_tracking(self): self._test_metadata_change_tracking('metadata') @@ -1412,7 +1438,6 @@ class _TestInstanceListObject(object): db_inst = fake_instance.fake_db_instance(id=2, access_ip_v4='1.2.3.4', access_ip_v6='::1') - db_inst['scheduled_at'] = None db_inst['terminated_at'] = None db_inst['deleted_at'] = None db_inst['created_at'] = None