# Copyright (c) 2014 ProphetStor, Inc. # All Rights Reserved. # 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 errno import httplib import re import mock from cinder import exception from cinder.openstack.common import units from cinder import test from cinder.volume import configuration as conf from cinder.volume.drivers.prophetstor import dpl_iscsi as DPLDRIVER from cinder.volume.drivers.prophetstor import dplcommon as DPLCOMMON POOLUUID = 'ac33fc6e417440d5a1ef27d7231e1cc4' VOLUMEUUID = 'a000000000000000000000000000001' INITIATOR = 'iqn.2013-08.org.debian:01:aaaaaaaa' DATA_IN_VOLUME = {'id': VOLUMEUUID} DATA_IN_CONNECTOR = {'initiator': INITIATOR} ## dpl.getpool DATA_SERVER_INFO = 0, { 'metadata': {'vendor': 'ProphetStor', 'version': '1.5'}} DATA_POOLINFO = 0, { 'capabilitiesURI': '', 'children': [], 'childrenrange': '', 'completionStatus': 'Complete', 'metadata': {'available_capacity': 4194074624, 'ctime': 1390551362349, 'vendor': 'prophetstor', 'version': '1.5', 'display_description': 'Default Pool', 'display_name': 'default_pool', 'event_uuid': '4f7c4d679a664857afa4d51f282a516a', 'physical_device': {'cache': [], 'data': ['disk_uuid_0', 'disk_uuid_1', 'disk_uuid_2'], 'log': [], 'spare': []}, 'pool_uuid': POOLUUID, 'properties': {'raid_level': 'raid0'}, 'state': 'Online', 'total_capacity': 4194828288, 'zpool_guid': '8173612007304181810'}, 'objectType': 'application/cdmi-container', 'percentComplete': 100} ## dpl.assignvdev DATA_ASSIGNVDEV = 0, { 'children': [], 'childrenrange': '', 'completionStatus': 'Complete', 'domainURI': '', 'exports': {'Network/iSCSI': [ {'logical_unit_name': '', 'logical_unit_number': '101', 'permissions': [INITIATOR], 'portals': ['172.31.1.210:3260'], 'target_identifier': 'iqn.2013-09.com.prophetstor:hypervisor.886423051816' }]}, 'metadata': {'ctime': 0, 'event_uuid': 'c11e90287e9348d0b4889695f1ec4be5', 'type': 'volume'}, 'objectID': '', 'objectName': 'd827e23d403f4f12bb208a6fec208fd8', 'objectType': 'application/cdmi-container', 'parentID': '8daa374670af447e8efea27e16bf84cd', 'parentURI': '/dpl_volume', 'snapshots': [] } DATA_OUTPUT = 0, None DATA_IN_VOLUME = {'id': 'abc123', 'display_name': 'abc123', 'display_description': '', 'size': 1} DATA_IN_VOLUME1 = {'id': 'abc456', 'display_name': 'abc456', 'display_description': '', 'size': 1} DATA_IN_SNAPSHOT = {'id': 'snapshot1', 'volume_id': 'abc123', 'display_name': 'snapshot1', 'display_description': ''} class TestProphetStorDPLVolume(test.TestCase): def _gen_snapshot_url(self, vdevid, snapshotid): snapshot_url = '/%s/%s/%s' % (vdevid, DPLCOMMON.DPL_OBJ_SNAPSHOT, snapshotid) return snapshot_url def setUp(self): super(TestProphetStorDPLVolume, self).setUp() self.dplcmd = DPLCOMMON.DPLVolume('1.1.1.1', 8356, 'admin', 'password') self.DPL_MOCK = mock.MagicMock() self.dplcmd.objCmd = self.DPL_MOCK self.DPL_MOCK.send_cmd.return_value = DATA_OUTPUT def test_getserverinfo(self): self.dplcmd.get_server_info() self.DPL_MOCK.send_cmd.assert_called_once_with( 'GET', '/%s/%s/' % (DPLCOMMON.DPL_VER_V1, DPLCOMMON.DPL_OBJ_SYSTEM), None, [httplib.OK, httplib.ACCEPTED]) def test_createvdev(self): self.dplcmd.create_vdev(DATA_IN_VOLUME['id'], DATA_IN_VOLUME['display_name'], DATA_IN_VOLUME['display_description'], POOLUUID, int(DATA_IN_VOLUME['size']) * units.Gi) metadata = {} metadata['display_name'] = DATA_IN_VOLUME['display_name'] metadata['display_description'] = DATA_IN_VOLUME['display_description'] metadata['pool_uuid'] = POOLUUID metadata['total_capacity'] = int(DATA_IN_VOLUME['size']) * units.Gi metadata['maximum_snapshot'] = 1024 metadata['properties'] = dict(thin_provision=True) params = {} params['metadata'] = metadata self.DPL_MOCK.send_cmd.assert_called_once_with( 'PUT', '/%s/%s/%s/' % (DPLCOMMON.DPL_VER_V1, DPLCOMMON.DPL_OBJ_VOLUME, DATA_IN_VOLUME['id']), params, [httplib.OK, httplib.ACCEPTED, httplib.CREATED]) def test_extendvdev(self): self.dplcmd.extend_vdev(DATA_IN_VOLUME['id'], DATA_IN_VOLUME['display_name'], DATA_IN_VOLUME['display_description'], int(DATA_IN_VOLUME['size']) * units.Gi) metadata = {} metadata['display_name'] = DATA_IN_VOLUME['display_name'] metadata['display_description'] = DATA_IN_VOLUME['display_description'] metadata['total_capacity'] = int(DATA_IN_VOLUME['size']) * units.Gi metadata['maximum_snapshot'] = 1024 params = {} params['metadata'] = metadata self.DPL_MOCK.send_cmd.assert_called_once_with( 'PUT', '/%s/%s/%s/' % (DPLCOMMON.DPL_VER_V1, DPLCOMMON.DPL_OBJ_VOLUME, DATA_IN_VOLUME['id']), params, [httplib.OK, httplib.ACCEPTED, httplib.CREATED]) def test_deletevdev(self): self.dplcmd.delete_vdev(DATA_IN_VOLUME['id'], True) metadata = {} params = {} metadata['force'] = True params['metadata'] = metadata self.DPL_MOCK.send_cmd.assert_called_once_with( 'DELETE', '/%s/%s/%s/' % (DPLCOMMON.DPL_VER_V1, DPLCOMMON.DPL_OBJ_VOLUME, DATA_IN_VOLUME['id']), params, [httplib.OK, httplib.ACCEPTED, httplib.NOT_FOUND, httplib.NO_CONTENT]) def test_createvdevfromsnapshot(self): self.dplcmd.create_vdev_from_snapshot( DATA_IN_VOLUME['id'], DATA_IN_VOLUME['display_name'], DATA_IN_VOLUME['display_description'], DATA_IN_SNAPSHOT['id'], POOLUUID) metadata = {} params = {} metadata['snapshot_operation'] = 'copy' metadata['display_name'] = DATA_IN_VOLUME['display_name'] metadata['display_description'] = DATA_IN_VOLUME['display_description'] metadata['pool_uuid'] = POOLUUID metadata['maximum_snapshot'] = 1024 metadata['properties'] = dict(thin_provision=True) params['metadata'] = metadata params['copy'] = self._gen_snapshot_url(DATA_IN_VOLUME['id'], DATA_IN_SNAPSHOT['id']) self.DPL_MOCK.send_cmd.assert_called_once_with( 'PUT', '/%s/%s/%s/' % (DPLCOMMON.DPL_VER_V1, DPLCOMMON.DPL_OBJ_VOLUME, DATA_IN_VOLUME['id']), params, [httplib.OK, httplib.ACCEPTED, httplib.CREATED]) def test_getpool(self): self.dplcmd.get_pool(POOLUUID) self.DPL_MOCK.send_cmd.assert_called_once_with( 'GET', '/%s/%s/%s/' % (DPLCOMMON.DPL_VER_V1, DPLCOMMON.DPL_OBJ_POOL, POOLUUID), None, [httplib.OK, httplib.ACCEPTED]) def test_clonevdev(self): self.dplcmd.clone_vdev( DATA_IN_VOLUME['id'], DATA_IN_VOLUME1['id'], POOLUUID, DATA_IN_VOLUME['display_name'], DATA_IN_VOLUME['display_description'], int(DATA_IN_VOLUME['size']) * units.Gi ) metadata = {} params = {} metadata["snapshot_operation"] = "clone" metadata["display_name"] = DATA_IN_VOLUME['display_name'] metadata["display_description"] = DATA_IN_VOLUME['display_description'] metadata["pool_uuid"] = POOLUUID metadata["total_capacity"] = int(DATA_IN_VOLUME['size']) * units.Gi metadata['maximum_snapshot'] = 1024 metadata['properties'] = dict(thin_provision=True) params["metadata"] = metadata params["copy"] = DATA_IN_VOLUME['id'] self.DPL_MOCK.send_cmd.assert_called_once_with( 'PUT', '/%s/%s/%s/' % (DPLCOMMON.DPL_VER_V1, DPLCOMMON.DPL_OBJ_VOLUME, DATA_IN_VOLUME1['id']), params, [httplib.OK, httplib.CREATED, httplib.ACCEPTED]) def test_createvdevsnapshot(self): self.dplcmd.create_vdev_snapshot( DATA_IN_VOLUME['id'], DATA_IN_SNAPSHOT['id'], DATA_IN_SNAPSHOT['display_name'], DATA_IN_SNAPSHOT['display_description'] ) metadata = {} params = {} metadata['display_name'] = DATA_IN_SNAPSHOT['display_name'] metadata['display_description'] = \ DATA_IN_SNAPSHOT['display_description'] params['metadata'] = metadata params['snapshot'] = DATA_IN_SNAPSHOT['id'] self.DPL_MOCK.send_cmd.assert_called_once_with( 'PUT', '/%s/%s/%s/' % (DPLCOMMON.DPL_VER_V1, DPLCOMMON.DPL_OBJ_VOLUME, DATA_IN_VOLUME['id']), params, [httplib.OK, httplib.CREATED, httplib.ACCEPTED]) def test_getvdev(self): self.dplcmd.get_vdev(DATA_IN_VOLUME['id']) self.DPL_MOCK.send_cmd.assert_called_once_with( 'GET', '/%s/%s/%s/' % (DPLCOMMON.DPL_VER_V1, DPLCOMMON.DPL_OBJ_VOLUME, DATA_IN_VOLUME['id']), None, [httplib.OK, httplib.ACCEPTED, httplib.NOT_FOUND]) def test_getvdevstatus(self): self.dplcmd.get_vdev_status(DATA_IN_VOLUME['id'], '123456') self.DPL_MOCK.send_cmd.assert_called_once_with( 'GET', '/%s/%s/%s/?event_uuid=%s' % (DPLCOMMON.DPL_VER_V1, DPLCOMMON.DPL_OBJ_VOLUME, DATA_IN_VOLUME['id'], '123456'), None, [httplib.OK, httplib.NOT_FOUND]) def test_getpoolstatus(self): self.dplcmd.get_pool_status(POOLUUID, '123456') self.DPL_MOCK.send_cmd.assert_called_once_with( 'GET', '/%s/%s/%s/?event_uuid=%s' % (DPLCOMMON.DPL_VER_V1, DPLCOMMON.DPL_OBJ_POOL, POOLUUID, '123456'), None, [httplib.OK, httplib.NOT_FOUND]) def test_assignvdev(self): self.dplcmd.assign_vdev( DATA_IN_VOLUME['id'], 'iqn.1993-08.org.debian:01:test1', '', '1.1.1.1:3260', 0 ) params = {} metadata = {} exports = {} metadata['export_operation'] = 'assign' exports['Network/iSCSI'] = {} target_info = {} target_info['logical_unit_number'] = 0 target_info['logical_unit_name'] = '' permissions = [] portals = [] portals.append('1.1.1.1:3260') permissions.append('iqn.1993-08.org.debian:01:test1') target_info['permissions'] = permissions target_info['portals'] = portals exports['Network/iSCSI'] = target_info params['metadata'] = metadata params['exports'] = exports self.DPL_MOCK.send_cmd.assert_called_once_with( 'PUT', '/%s/%s/%s/' % (DPLCOMMON.DPL_VER_V1, DPLCOMMON.DPL_OBJ_VOLUME, DATA_IN_VOLUME['id']), params, [httplib.OK, httplib.ACCEPTED, httplib.CREATED]) def test_unassignvdev(self): self.dplcmd.unassign_vdev(DATA_IN_VOLUME['id'], 'iqn.1993-08.org.debian:01:test1', '') params = {} metadata = {} exports = {} metadata['export_operation'] = 'unassign' params['metadata'] = metadata exports['Network/iSCSI'] = {} exports['Network/iSCSI']['target_identifier'] = '' permissions = [] permissions.append('iqn.1993-08.org.debian:01:test1') exports['Network/iSCSI']['permissions'] = permissions params['exports'] = exports self.DPL_MOCK.send_cmd.assert_called_once_with( 'PUT', '/%s/%s/%s/' % (DPLCOMMON.DPL_VER_V1, DPLCOMMON.DPL_OBJ_VOLUME, DATA_IN_VOLUME['id']), params, [httplib.OK, httplib.ACCEPTED, httplib.NO_CONTENT, httplib.NOT_FOUND]) def test_deletevdevsnapshot(self): self.dplcmd.delete_vdev_snapshot(DATA_IN_VOLUME['id'], DATA_IN_SNAPSHOT['id']) params = {} params['copy'] = self._gen_snapshot_url(DATA_IN_VOLUME['id'], DATA_IN_SNAPSHOT['id']) self.DPL_MOCK.send_cmd.assert_called_once_with( 'DELETE', '/%s/%s/%s/%s/%s/' % (DPLCOMMON.DPL_VER_V1, DPLCOMMON.DPL_OBJ_VOLUME, DATA_IN_VOLUME['id'], DPLCOMMON.DPL_OBJ_SNAPSHOT, DATA_IN_SNAPSHOT['id']), None, [httplib.OK, httplib.ACCEPTED, httplib.NO_CONTENT, httplib.NOT_FOUND]) def test_listvdevsnapshots(self): self.dplcmd.list_vdev_snapshots(DATA_IN_VOLUME['id']) self.DPL_MOCK.send_cmd.assert_called_once_with( 'GET', '/%s/%s/%s/%s/' % (DPLCOMMON.DPL_VER_V1, DPLCOMMON.DPL_OBJ_VOLUME, DATA_IN_VOLUME['id'], DPLCOMMON.DPL_OBJ_SNAPSHOT), None, [httplib.OK]) class TestProphetStorDPLDriver(test.TestCase): def __init__(self, method): super(TestProphetStorDPLDriver, self).__init__(method) def _conver_uuid2hex(self, strID): return strID.replace('-', '') def setUp(self): super(TestProphetStorDPLDriver, self).setUp() self.configuration = mock.Mock(conf.Configuration) self.configuration.san_ip = '1.1.1.1' self.configuration.dpl_port = 8356 self.configuration.san_login = 'admin' self.configuration.san_password = 'password' self.configuration.dpl_pool = POOLUUID self.configuration.iscsi_port = 3260 self.configuration.san_is_local = False self.configuration.san_thin_provision = True self.context = '' self.DPL_MOCK = mock.MagicMock() self.dpldriver = DPLDRIVER.DPLISCSIDriver( configuration=self.configuration) self.dpldriver.dpl = self.DPL_MOCK self.dpldriver.do_setup(self.context) def test_get_volume_stats(self): self.DPL_MOCK.get_pool.return_value = DATA_POOLINFO self.DPL_MOCK.get_server_info.return_value = DATA_SERVER_INFO res = self.dpldriver.get_volume_stats(True) self.assertEqual(res['vendor_name'], 'ProphetStor') self.assertEqual(res['driver_version'], '1.5') self.assertEqual(res['total_capacity_gb'], 3.91) self.assertEqual(res['free_capacity_gb'], 3.91) self.assertEqual(res['reserved_percentage'], 0) self.assertEqual(res['QoS_support'], False) def test_create_volume(self): self.DPL_MOCK.create_vdev.return_value = DATA_OUTPUT self.dpldriver.create_volume(DATA_IN_VOLUME) self.DPL_MOCK\ .create_vdev\ .assert_called_once_with( self._conver_uuid2hex(DATA_IN_VOLUME['id']), DATA_IN_VOLUME['display_name'], DATA_IN_VOLUME['display_description'], self.configuration.dpl_pool, int(DATA_IN_VOLUME['size']) * units.Gi, True) def test_delete_volume(self): self.DPL_MOCK.delete_vdev.return_value = DATA_OUTPUT self.dpldriver.delete_volume(DATA_IN_VOLUME) self.DPL_MOCK\ .delete_vdev\ .assert_called_once_with(self ._conver_uuid2hex(DATA_IN_VOLUME['id'])) def test_create_volume_from_snapshot(self): self.DPL_MOCK.create_vdev_from_snapshot.return_value = DATA_OUTPUT self.dpldriver.create_volume_from_snapshot(DATA_IN_VOLUME, DATA_IN_SNAPSHOT) self.DPL_MOCK\ .create_vdev_from_snapshot\ .assert_called_once_with(self ._conver_uuid2hex(DATA_IN_VOLUME['id']), DATA_IN_VOLUME['display_name'], DATA_IN_VOLUME['display_description'], self ._conver_uuid2hex(DATA_IN_SNAPSHOT['id']), self.configuration.dpl_pool, True) def test_create_cloned_volume(self): self.DPL_MOCK.clone_vdev.return_value = DATA_OUTPUT self.dpldriver.create_cloned_volume(DATA_IN_VOLUME1, DATA_IN_VOLUME) self.DPL_MOCK\ .clone_vdev\ .assert_called_once_with(self ._conver_uuid2hex(DATA_IN_VOLUME['id']), self ._conver_uuid2hex(DATA_IN_VOLUME1['id']), self.configuration.dpl_pool, DATA_IN_VOLUME1['display_name'], DATA_IN_VOLUME1['display_description'], int(DATA_IN_VOLUME1['size']) * units.Gi, True) def test_create_snapshot(self): self.DPL_MOCK.create_vdev_snapshot.return_value = DATA_OUTPUT self.dpldriver.create_snapshot(DATA_IN_SNAPSHOT) self.DPL_MOCK\ .create_vdev_snapshot\ .assert_called_once_with( self._conver_uuid2hex(DATA_IN_SNAPSHOT['volume_id']), self._conver_uuid2hex(DATA_IN_SNAPSHOT['id']), DATA_IN_SNAPSHOT['display_name'], DATA_IN_SNAPSHOT['display_description']) def test_delete_snapshot(self): self.DPL_MOCK.delete_vdev_snapshot.return_value = DATA_OUTPUT self.dpldriver.delete_snapshot(DATA_IN_SNAPSHOT) self.DPL_MOCK\ .delete_vdev_snapshot\ .assert_called_once_with( self._conver_uuid2hex(DATA_IN_SNAPSHOT['volume_id']), self._conver_uuid2hex(DATA_IN_SNAPSHOT['id'])) def test_initialize_connection(self): self.DPL_MOCK.assign_vdev.return_value = DATA_ASSIGNVDEV self.DPL_MOCK.get_vdev.return_value = DATA_ASSIGNVDEV res = self.dpldriver.initialize_connection(DATA_IN_VOLUME, DATA_IN_CONNECTOR) self.assertEqual(res['driver_volume_type'], 'iscsi') self.assertEqual(res['data']['target_lun'], '101') self.assertEqual(res['data']['target_discovered'], True) self.assertEqual(res['data']['target_portal'], '172.31.1.210:3260') self.assertEqual(res['data']['target_iqn'], 'iqn.2013-09.com.' 'prophetstor:hypervisor.' '886423051816') def test_terminate_connection(self): self.DPL_MOCK.unassign_vdev.return_value = DATA_OUTPUT self.dpldriver.terminate_connection(DATA_IN_VOLUME, DATA_IN_CONNECTOR) self.DPL_MOCK\ .unassign_vdev\ .assert_called_once_with( self._conver_uuid2hex(DATA_IN_VOLUME['id']), DATA_IN_CONNECTOR['initiator']) def test_terminate_connection_volume_detached(self): self.DPL_MOCK.unassign_vdev.return_value = errno.ENODATA, None self.dpldriver.terminate_connection(DATA_IN_VOLUME, DATA_IN_CONNECTOR) self.DPL_MOCK\ .unassign_vdev\ .assert_called_once_with( self._conver_uuid2hex(DATA_IN_VOLUME['id']), DATA_IN_CONNECTOR['initiator']) def test_terminate_connection_failed(self): self.DPL_MOCK.unassign_vdev.return_value = errno.EFAULT, None ex = self.assertRaises( exception.VolumeBackendAPIException, self.dpldriver.terminate_connection, volume=DATA_IN_VOLUME, connector=DATA_IN_CONNECTOR) self.assertTrue( re.match(r".*Flexvisor failed", ex.msg)) def test_get_pool_info(self): self.DPL_MOCK.get_pool.return_value = DATA_POOLINFO _, res = self.dpldriver._get_pool_info(POOLUUID) self.assertEqual(res['metadata']['available_capacity'], 4194074624) self.assertEqual(res['metadata']['ctime'], 1390551362349) self.assertEqual(res['metadata']['display_description'], 'Default Pool') self.assertEqual(res['metadata']['display_name'], 'default_pool') self.assertEqual(res['metadata']['event_uuid'], '4f7c4d679a664857afa4d51f282a516a') self.assertEqual(res['metadata']['physical_device'], { 'cache': [], 'data': ['disk_uuid_0', 'disk_uuid_1', 'disk_uuid_2'], 'log': [], 'spare': []}) self.assertEqual(res['metadata']['pool_uuid'], POOLUUID) self.assertEqual(res['metadata']['properties'], { 'raid_level': 'raid0'}) self.assertEqual(res['metadata']['state'], 'Online') self.assertEqual(res['metadata']['total_capacity'], 4194828288) self.assertEqual(res['metadata']['zpool_guid'], '8173612007304181810')