# 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 testtools from openstack.block_storage.v3 import volume import openstack.cloud from openstack.cloud import meta from openstack.compute.v2 import volume_attachment from openstack.tests import fakes from openstack.tests.unit import base class TestVolume(base.TestCase): def _compare_volumes(self, exp, real): self.assertDictEqual( volume.Volume(**exp).to_dict(computed=False), real.to_dict(computed=False), ) def _compare_volume_attachments(self, exp, real): self.assertDictEqual( volume_attachment.VolumeAttachment(**exp).to_dict(computed=False), real.to_dict(computed=False), ) def test_attach_volume(self): server = dict(id='server001') vol = { 'id': 'volume001', 'status': 'available', 'name': '', 'attachments': [], } volume = meta.obj_to_munch(fakes.FakeVolume(**vol)) rattach = { 'server_id': server['id'], 'device': 'device001', 'volumeId': volume['id'], 'id': 'attachmentId', } self.register_uris( [ self.get_nova_discovery_mock_dict(), dict( method='POST', uri=self.get_mock_url( 'compute', 'public', append=[ 'servers', server['id'], 'os-volume_attachments', ], ), json={'volumeAttachment': rattach}, validate=dict( json={'volumeAttachment': {'volumeId': vol['id']}} ), ), ] ) ret = self.cloud.attach_volume(server, volume, wait=False) self._compare_volume_attachments(rattach, ret) self.assert_calls() def test_attach_volume_exception(self): server = dict(id='server001') vol = { 'id': 'volume001', 'status': 'available', 'name': '', 'attachments': [], } volume = meta.obj_to_munch(fakes.FakeVolume(**vol)) self.register_uris( [ self.get_nova_discovery_mock_dict(), dict( method='POST', uri=self.get_mock_url( 'compute', 'public', append=[ 'servers', server['id'], 'os-volume_attachments', ], ), status_code=404, validate=dict( json={'volumeAttachment': {'volumeId': vol['id']}} ), ), ] ) with testtools.ExpectedException( openstack.cloud.OpenStackCloudURINotFound ): self.cloud.attach_volume(server, volume, wait=False) self.assert_calls() def test_attach_volume_wait(self): server = dict(id='server001') vol = { 'id': 'volume001', 'status': 'available', 'name': '', 'attachments': [], } volume = meta.obj_to_munch(fakes.FakeVolume(**vol)) vol['attachments'] = [ {'server_id': server['id'], 'device': 'device001'} ] vol['status'] = 'in-use' attached_volume = meta.obj_to_munch(fakes.FakeVolume(**vol)) rattach = { 'server_id': server['id'], 'device': 'device001', 'volumeId': volume['id'], 'id': 'attachmentId', } self.register_uris( [ self.get_nova_discovery_mock_dict(), dict( method='POST', uri=self.get_mock_url( 'compute', 'public', append=[ 'servers', server['id'], 'os-volume_attachments', ], ), json={'volumeAttachment': rattach}, validate=dict( json={'volumeAttachment': {'volumeId': vol['id']}} ), ), self.get_cinder_discovery_mock_dict(), dict( method='GET', uri=self.get_mock_url( 'volumev3', 'public', append=['volumes', vol['id']] ), json={'volume': volume}, ), dict( method='GET', uri=self.get_mock_url( 'volumev3', 'public', append=['volumes', vol['id']] ), json={'volume': attached_volume}, ), ] ) # defaults to wait=True ret = self.cloud.attach_volume(server, volume) self._compare_volume_attachments(rattach, ret) self.assert_calls() def test_attach_volume_wait_error(self): server = dict(id='server001') vol = { 'id': 'volume001', 'status': 'available', 'name': '', 'attachments': [], } volume = meta.obj_to_munch(fakes.FakeVolume(**vol)) vol['status'] = 'error' errored_volume = meta.obj_to_munch(fakes.FakeVolume(**vol)) rattach = { 'server_id': server['id'], 'device': 'device001', 'volumeId': volume['id'], 'id': 'attachmentId', } self.register_uris( [ self.get_nova_discovery_mock_dict(), dict( method='POST', uri=self.get_mock_url( 'compute', 'public', append=[ 'servers', server['id'], 'os-volume_attachments', ], ), json={'volumeAttachment': rattach}, validate=dict( json={'volumeAttachment': {'volumeId': vol['id']}} ), ), self.get_cinder_discovery_mock_dict(), dict( method='GET', uri=self.get_mock_url( 'volumev3', 'public', append=['volumes', volume['id']] ), json={'volume': errored_volume}, ), dict( method='GET', uri=self.get_mock_url( 'volumev3', 'public', append=['volumes', volume['id']] ), json={'volume': errored_volume}, ), ] ) with testtools.ExpectedException(openstack.exceptions.ResourceFailure): self.cloud.attach_volume(server, volume) self.assert_calls() def test_attach_volume_not_available(self): server = dict(id='server001') volume = dict(id='volume001', status='error', attachments=[]) with testtools.ExpectedException( openstack.cloud.OpenStackCloudException, "Volume %s is not available. Status is '%s'" % (volume['id'], volume['status']), ): self.cloud.attach_volume(server, volume) self.assertEqual(0, len(self.adapter.request_history)) def test_attach_volume_already_attached(self): device_id = 'device001' server = dict(id='server001') volume = dict( id='volume001', attachments=[{'server_id': 'server001', 'device': device_id}], ) with testtools.ExpectedException( openstack.cloud.OpenStackCloudException, "Volume %s already attached to server %s on device %s" % (volume['id'], server['id'], device_id), ): self.cloud.attach_volume(server, volume) self.assertEqual(0, len(self.adapter.request_history)) def test_detach_volume(self): server = dict(id='server001') volume = dict( id='volume001', attachments=[{'server_id': 'server001', 'device': 'device001'}], ) self.register_uris( [ self.get_nova_discovery_mock_dict(), dict( method='GET', uri=self.get_mock_url( 'compute', 'public', append=['servers', server['id']] ), json={'server': server}, ), dict( method='DELETE', uri=self.get_mock_url( 'compute', 'public', append=[ 'servers', server['id'], 'os-volume_attachments', volume['id'], ], ), ), ] ) self.cloud.detach_volume(server, volume, wait=False) self.assert_calls() def test_detach_volume_exception(self): server = dict(id='server001') volume = dict( id='volume001', attachments=[{'server_id': 'server001', 'device': 'device001'}], ) self.register_uris( [ self.get_nova_discovery_mock_dict(), dict( method='GET', uri=self.get_mock_url( 'compute', 'public', append=['servers', server['id']] ), json={'server': server}, ), dict( method='DELETE', uri=self.get_mock_url( 'compute', 'public', append=[ 'servers', server['id'], 'os-volume_attachments', volume['id'], ], ), status_code=404, ), ] ) with testtools.ExpectedException( openstack.cloud.OpenStackCloudURINotFound ): self.cloud.detach_volume(server, volume, wait=False) self.assert_calls() def test_detach_volume_wait(self): server = dict(id='server001') attachments = [{'server_id': 'server001', 'device': 'device001'}] vol = { 'id': 'volume001', 'status': 'attached', 'name': '', 'attachments': attachments, } volume = meta.obj_to_munch(fakes.FakeVolume(**vol)) vol['status'] = 'available' vol['attachments'] = [] avail_volume = meta.obj_to_munch(fakes.FakeVolume(**vol)) self.register_uris( [ self.get_nova_discovery_mock_dict(), dict( method='GET', uri=self.get_mock_url( 'compute', 'public', append=['servers', server['id']] ), json={'server': server}, ), dict( method='DELETE', uri=self.get_mock_url( 'compute', 'public', append=[ 'servers', server['id'], 'os-volume_attachments', volume.id, ], ), ), self.get_cinder_discovery_mock_dict(), dict( method='GET', uri=self.get_mock_url( 'volumev3', 'public', append=['volumes', 'detail'] ), json={'volumes': [avail_volume]}, ), ] ) self.cloud.detach_volume(server, volume) self.assert_calls() def test_detach_volume_wait_error(self): server = dict(id='server001') attachments = [{'server_id': 'server001', 'device': 'device001'}] vol = { 'id': 'volume001', 'status': 'attached', 'name': '', 'attachments': attachments, } volume = meta.obj_to_munch(fakes.FakeVolume(**vol)) vol['status'] = 'error' vol['attachments'] = [] errored_volume = meta.obj_to_munch(fakes.FakeVolume(**vol)) self.register_uris( [ self.get_nova_discovery_mock_dict(), dict( method='GET', uri=self.get_mock_url( 'compute', 'public', append=['servers', server['id']] ), json={'server': server}, ), dict( method='DELETE', uri=self.get_mock_url( 'compute', 'public', append=[ 'servers', server['id'], 'os-volume_attachments', volume.id, ], ), ), self.get_cinder_discovery_mock_dict(), dict( method='GET', uri=self.get_mock_url( 'volumev3', 'public', append=['volumes', 'detail'] ), json={'volumes': [errored_volume]}, ), dict( method='GET', uri=self.get_mock_url( 'volumev3', 'public', append=['volumes', errored_volume['id']], ), json={'volume': errored_volume}, ), ] ) with testtools.ExpectedException(openstack.exceptions.ResourceFailure): self.cloud.detach_volume(server, volume) self.assert_calls() def test_delete_volume_deletes(self): vol = { 'id': 'volume001', 'status': 'attached', 'name': '', 'attachments': [], } volume = meta.obj_to_munch(fakes.FakeVolume(**vol)) self.register_uris( [ self.get_cinder_discovery_mock_dict(), dict( method='GET', uri=self.get_mock_url( 'volumev3', 'public', append=['volumes', volume.id] ), json={'volumes': [volume]}, ), dict( method='DELETE', uri=self.get_mock_url( 'volumev3', 'public', append=['volumes', volume.id] ), ), dict( method='GET', uri=self.get_mock_url( 'volumev3', 'public', append=['volumes', volume.id] ), status_code=404, ), ] ) self.assertTrue(self.cloud.delete_volume(volume['id'])) self.assert_calls() def test_delete_volume_gone_away(self): vol = { 'id': 'volume001', 'status': 'attached', 'name': '', 'attachments': [], } volume = meta.obj_to_munch(fakes.FakeVolume(**vol)) self.register_uris( [ self.get_cinder_discovery_mock_dict(), dict( method='GET', uri=self.get_mock_url( 'volumev3', 'public', append=['volumes', volume.id] ), json=volume, ), dict( method='DELETE', uri=self.get_mock_url( 'volumev3', 'public', append=['volumes', volume.id] ), status_code=404, ), dict( method='GET', uri=self.get_mock_url( 'volumev3', 'public', append=['volumes', volume.id] ), status_code=404, ), ] ) self.assertTrue(self.cloud.delete_volume(volume['id'])) self.assert_calls() def test_delete_volume_force(self): vol = { 'id': 'volume001', 'status': 'attached', 'name': '', 'attachments': [], } volume = meta.obj_to_munch(fakes.FakeVolume(**vol)) self.register_uris( [ self.get_cinder_discovery_mock_dict(), dict( method='GET', uri=self.get_mock_url( 'volumev3', 'public', append=['volumes', volume['id']] ), json={'volumes': [volume]}, ), dict( method='POST', uri=self.get_mock_url( 'volumev3', 'public', append=['volumes', volume.id, 'action'], ), validate=dict(json={'os-force_delete': {}}), ), dict( method='GET', uri=self.get_mock_url( 'volumev3', 'public', append=['volumes', volume['id']] ), status_code=404, ), ] ) self.assertTrue(self.cloud.delete_volume(volume['id'], force=True)) self.assert_calls() def test_set_volume_bootable(self): vol = { 'id': 'volume001', 'status': 'attached', 'name': '', 'attachments': [], } volume = meta.obj_to_munch(fakes.FakeVolume(**vol)) self.register_uris( [ self.get_cinder_discovery_mock_dict(), dict( method='GET', uri=self.get_mock_url( 'volumev3', 'public', append=['volumes', 'detail'] ), json={'volumes': [volume]}, ), dict( method='POST', uri=self.get_mock_url( 'volumev3', 'public', append=['volumes', volume.id, 'action'], ), json={'os-set_bootable': {'bootable': True}}, ), ] ) self.cloud.set_volume_bootable(volume['id']) self.assert_calls() def test_set_volume_bootable_false(self): vol = { 'id': 'volume001', 'status': 'attached', 'name': '', 'attachments': [], } volume = meta.obj_to_munch(fakes.FakeVolume(**vol)) self.register_uris( [ self.get_cinder_discovery_mock_dict(), dict( method='GET', uri=self.get_mock_url( 'volumev3', 'public', append=['volumes', 'detail'] ), json={'volumes': [volume]}, ), dict( method='POST', uri=self.get_mock_url( 'volumev3', 'public', append=['volumes', volume.id, 'action'], ), json={'os-set_bootable': {'bootable': False}}, ), ] ) self.cloud.set_volume_bootable(volume['id']) self.assert_calls() def test_get_volume_by_id(self): vol1 = meta.obj_to_munch(fakes.FakeVolume('01', 'available', 'vol1')) self.register_uris( [ self.get_cinder_discovery_mock_dict(), dict( method='GET', uri=self.get_mock_url( 'volumev3', 'public', append=['volumes', '01'] ), json={'volume': vol1}, ), ] ) self._compare_volumes(vol1, self.cloud.get_volume_by_id('01')) self.assert_calls() def test_create_volume(self): vol1 = meta.obj_to_munch(fakes.FakeVolume('01', 'available', 'vol1')) self.register_uris( [ self.get_cinder_discovery_mock_dict(), dict( method='POST', uri=self.get_mock_url( 'volumev3', 'public', append=['volumes'] ), json={'volume': vol1}, validate=dict( json={ 'volume': { 'size': 50, 'name': 'vol1', } } ), ), ] ) self.cloud.create_volume(50, name='vol1') self.assert_calls() def test_create_bootable_volume(self): vol1 = meta.obj_to_munch(fakes.FakeVolume('01', 'available', 'vol1')) self.register_uris( [ self.get_cinder_discovery_mock_dict(), dict( method='POST', uri=self.get_mock_url( 'volumev3', 'public', append=['volumes'] ), json={'volume': vol1}, validate=dict( json={ 'volume': { 'size': 50, 'name': 'vol1', } } ), ), dict( method='POST', uri=self.get_mock_url( 'volumev3', 'public', append=['volumes', '01', 'action'], ), validate=dict( json={'os-set_bootable': {'bootable': True}} ), ), ] ) self.cloud.create_volume(50, name='vol1', bootable=True) self.assert_calls()