b03f539e6e
None of the Versioned Objects destroy methods we have update the instance fields, so after doing a destroy on an instance the status, deleted, and deleted_at fields will still be the same as they were before, leaving the instance in an inconsistent state. This is important because our workers decorator for the HA A-A cleanup mechanism relies on the status of the resources being properly updated by the operations. Change-Id: I4f15356931007e2ca725e477327c1ef67c7db487 Closes-Bug: #1604347
127 lines
5.4 KiB
Python
127 lines
5.4 KiB
Python
# 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 mock
|
|
from oslo_utils import timeutils
|
|
import pytz
|
|
|
|
from cinder.db.sqlalchemy import models
|
|
from cinder import exception
|
|
from cinder import objects
|
|
from cinder.tests.unit import fake_constants as fake
|
|
from cinder.tests.unit import objects as test_objects
|
|
|
|
fake_qos = {'consumer': 'front-end',
|
|
'id': fake.OBJECT_ID,
|
|
'name': 'qos_name',
|
|
'specs': {'key1': 'val1', 'key2': 'val2'}}
|
|
|
|
fake_qos_no_id = fake_qos.copy()
|
|
del fake_qos_no_id['id']
|
|
|
|
|
|
class TestQos(test_objects.BaseObjectsTestCase):
|
|
@mock.patch('cinder.db.get_by_id', return_value=fake_qos)
|
|
def test_get_by_id(self, qos_get):
|
|
qos_object = objects.QualityOfServiceSpecs.get_by_id(
|
|
self.context, fake.OBJECT_ID)
|
|
self._compare(self, fake_qos, qos_object)
|
|
qos_get.assert_called_once_with(
|
|
self.context, models.QualityOfServiceSpecs, fake.OBJECT_ID)
|
|
|
|
@mock.patch('cinder.db.qos_specs_create',
|
|
return_value={'name': 'qos_name', 'id': fake.OBJECT_ID})
|
|
def test_create(self, qos_fake_create):
|
|
qos_object = objects.QualityOfServiceSpecs(
|
|
self.context, **fake_qos_no_id)
|
|
qos_object.create()
|
|
self._compare(self, fake_qos, qos_object)
|
|
|
|
# Fail to create a second time
|
|
self.assertRaises(exception.ObjectActionError, qos_object.create)
|
|
|
|
self.assertEqual(1, len(qos_fake_create.mock_calls))
|
|
|
|
@mock.patch('cinder.db.qos_specs_item_delete')
|
|
@mock.patch('cinder.db.qos_specs_update')
|
|
def test_save(self, qos_fake_update, qos_fake_delete):
|
|
qos_dict = fake_qos.copy()
|
|
qos_dict['specs']['key_to_remove1'] = 'val'
|
|
qos_dict['specs']['key_to_remove2'] = 'val'
|
|
qos_object = objects.QualityOfServiceSpecs._from_db_object(
|
|
self.context, objects.QualityOfServiceSpecs(), qos_dict)
|
|
|
|
qos_object.specs['key1'] = 'val1'
|
|
qos_object.save()
|
|
# No values have changed so no updates should be made
|
|
self.assertFalse(qos_fake_update.called)
|
|
|
|
qos_object.consumer = 'back-end'
|
|
qos_object.specs['key1'] = 'val2'
|
|
qos_object.specs['new_key'] = 'val3'
|
|
|
|
del qos_object.specs['key_to_remove1']
|
|
del qos_object.specs['key_to_remove2']
|
|
qos_object.save()
|
|
qos_fake_update.assert_called_once_with(
|
|
self.context, fake.OBJECT_ID,
|
|
{'specs': {'key1': 'val2', 'new_key': 'val3'},
|
|
'consumer': 'back-end'})
|
|
qos_fake_delete.assert_has_calls([
|
|
mock.call(self.context, fake.OBJECT_ID, 'key_to_remove1'),
|
|
mock.call(self.context, fake.OBJECT_ID, 'key_to_remove2')])
|
|
|
|
@mock.patch('oslo_utils.timeutils.utcnow', return_value=timeutils.utcnow())
|
|
@mock.patch('cinder.objects.VolumeTypeList.get_all_types_for_qos',
|
|
return_value=None)
|
|
@mock.patch('cinder.db.sqlalchemy.api.qos_specs_delete')
|
|
def test_destroy_no_vol_types(self, qos_fake_delete, fake_get_vol_types,
|
|
utcnow_mock):
|
|
qos_fake_delete.return_value = {
|
|
'deleted': True,
|
|
'deleted_at': utcnow_mock.return_value}
|
|
qos_object = objects.QualityOfServiceSpecs._from_db_object(
|
|
self.context, objects.QualityOfServiceSpecs(), fake_qos)
|
|
qos_object.destroy()
|
|
|
|
qos_fake_delete.assert_called_once_with(mock.ANY, fake_qos['id'])
|
|
self.assertTrue(qos_object.deleted)
|
|
self.assertEqual(utcnow_mock.return_value.replace(tzinfo=pytz.UTC),
|
|
qos_object.deleted_at)
|
|
|
|
@mock.patch('cinder.db.sqlalchemy.api.qos_specs_delete')
|
|
@mock.patch('cinder.db.qos_specs_disassociate_all')
|
|
@mock.patch('cinder.objects.VolumeTypeList.get_all_types_for_qos')
|
|
def test_destroy_with_vol_types(self, fake_get_vol_types,
|
|
qos_fake_disassociate, qos_fake_delete):
|
|
qos_object = objects.QualityOfServiceSpecs._from_db_object(
|
|
self.context, objects.QualityOfServiceSpecs(), fake_qos)
|
|
fake_get_vol_types.return_value = objects.VolumeTypeList(
|
|
objects=[objects.VolumeType(id=fake.VOLUME_TYPE_ID)])
|
|
self.assertRaises(exception.QoSSpecsInUse, qos_object.destroy)
|
|
|
|
qos_object.destroy(force=True)
|
|
qos_fake_delete.assert_called_once_with(mock.ANY, fake_qos['id'])
|
|
qos_fake_disassociate.assert_called_once_with(
|
|
self.context, fake_qos['id'])
|
|
|
|
@mock.patch('cinder.objects.VolumeTypeList.get_all_types_for_qos',
|
|
return_value=None)
|
|
@mock.patch('cinder.db.get_by_id', return_value=fake_qos)
|
|
def test_get_volume_type(self, fake_get_by_id, fake_get_vol_types):
|
|
qos_object = objects.QualityOfServiceSpecs.get_by_id(
|
|
self.context, fake.OBJECT_ID)
|
|
self.assertFalse(fake_get_vol_types.called)
|
|
# Access lazy-loadable attribute
|
|
qos_object.volume_types
|
|
self.assertTrue(fake_get_vol_types.called)
|