cinder/cinder/tests/unit/volume/drivers/solidfire/test_solidfire.py

3341 lines
149 KiB
Python

#
# Copyright 2012 OpenStack Foundation
# 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 datetime
import re
import six
from ddt import data
from ddt import ddt
from ddt import file_data
from ddt import unpack
import mock
from oslo_utils import timeutils
from oslo_utils import units
from cinder import context
from cinder import exception
from cinder.objects import fields
from cinder import test
from cinder.tests.unit.api import fakes
from cinder.tests.unit import fake_group_snapshot
from cinder.tests.unit import fake_snapshot
from cinder.tests.unit import fake_volume
from cinder.tests.unit.image import fake as fake_image
from cinder.tests.unit import utils as test_utils
from cinder.volume import configuration as conf
from cinder.volume.drivers import solidfire
from cinder.volume import qos_specs
from cinder.volume import volume_types
class mock_vref(object):
def __init__(self):
self._name_id = None
self.admin_metadata = {}
self.attach_status = 'detached'
self.id = '262b9ce2-a71a-4fbe-830c-c20c5596caea'
self.project_id = '52423d9394ad4c67b3b9034da58cedbc'
self.provider_id = '5 4 6ecebf5d-5521-4ce1-80f3-358ebc1b9cdc'
self.size = 20
def __setitem__(self, item, value):
self.__dict__[item] = value
def __getitem__(self, item):
return self.__dict__[item]
def get(self, item, arg2 = None):
return self.__dict__[item]
f_uuid = ['262b9ce2-a71a-4fbe-830c-c20c5596caea',
'362b9ce2-a71a-4fbe-830c-c20c5596caea']
@ddt
class SolidFireVolumeTestCase(test.TestCase):
EXPECTED_QOS = {'minIOPS': 110, 'burstIOPS': 1530, 'maxIOPS': 1020}
def setUp(self):
self.ctxt = context.get_admin_context()
self.configuration = conf.BackendGroupConfiguration(
[], conf.SHARED_CONF_GROUP)
self.configuration.sf_allow_tenant_qos = True
self.configuration.san_is_local = True
self.configuration.sf_emulate_512 = True
self.configuration.sf_account_prefix = 'cinder'
self.configuration.reserved_percentage = 25
self.configuration.target_helper = None
self.configuration.sf_template_account_name = 'openstack-vtemplate'
self.configuration.sf_allow_template_caching = False
self.configuration.sf_svip = None
self.configuration.sf_volume_prefix = 'UUID-'
self.configuration.sf_enable_vag = False
self.configuration.replication_device = []
self.configuration.max_over_subscription_ratio = 2
super(SolidFireVolumeTestCase, self).setUp()
self.mock_object(solidfire.SolidFireDriver,
'_issue_api_request',
self.fake_issue_api_request)
self.mock_object(solidfire.SolidFireDriver,
'_get_provisioned_capacity_iops',
return_value=(0, 0))
self.expected_qos_results = {'minIOPS': 1000,
'maxIOPS': 10000,
'burstIOPS': 20000}
self.mock_stats_data =\
{'result':
{'clusterCapacity': {'maxProvisionedSpace': 107374182400,
'usedSpace': 1073741824,
'compressionPercent': 100,
'deDuplicationPercent': 100,
'thinProvisioningPercent': 100}}}
vol_updates = {'project_id': 'testprjid',
'name': 'testvol',
'size': 1,
'id': 'a720b3c0-d1f0-11e1-9b23-0800200c9a66',
'volume_type_id': 'fast',
'created_at': timeutils.utcnow(),
'attributes':
{'uuid': '262b9ce2-a71a-4fbe-830c-c20c5596caea'}}
ctx = context.get_admin_context()
self.mock_volume = fake_volume.fake_volume_obj(ctx, **vol_updates)
self.fake_image_meta = {'id': '17c550bb-a411-44c0-9aaf-0d96dd47f501',
'updated_at': datetime.datetime(2013, 9,
28, 15,
27, 36,
325355),
'is_public': True,
'owner': 'testprjid'}
self.fake_image_service = fake_image.FakeImageService()
self.vol = test_utils.create_volume(
self.ctxt, volume_id='b831c4d1-d1f0-11e1-9b23-0800200c9a66')
self.snap = test_utils.create_snapshot(
self.ctxt, volume_id=self.vol.id)
self.fake_sfaccount = {'accountID': 25,
'targetSecret': 'shhhh',
'username': 'prefix-testprjid',
'volumes': [6, 7, 20]}
self.fake_sfvol = {'volumeID': 6,
'name': 'test_volume',
'accountID': 25,
'sliceCount': 1,
'totalSize': 1 * units.Gi,
'enable512e': True,
'access': "readWrite",
'status': "active",
'attributes': {'uuid': f_uuid[0]},
'qos': None,
'iqn': 'super_fake_iqn'}
self.cluster_pairs = (
[{'uniqueID': 'lu9f',
'endpoint': {'passwd': 'admin', 'port': 443,
'url': 'https://192.168.139.102:443',
'svip': '10.10.8.134',
'mvip': '192.168.139.102',
'login': 'admin'},
'name': 'AutoTest2-6AjG-FOR-TEST-ONLY',
'clusterPairID': 33,
'clusterAPIVersion': '9.4',
'uuid': '9c499d4b-8fff-48b4-b875-27601d5d9889',
'svip': '10.10.23.2',
'mvipNodeID': 1,
'repCount': 1,
'encryptionAtRestState': 'disabled',
'attributes': {},
'mvip': '192.168.139.102',
'ensemble': ['10.10.5.130'],
'svipNodeID': 1}])
self.mvip = '192.168.139.102'
self.svip = '10.10.8.134'
self.fake_sfsnap_name = '%s%s' % (self.configuration.sf_volume_prefix,
self.snap.id)
self.fake_sfsnaps = [{'snapshotID': '5',
'name': self.fake_sfsnap_name,
'volumeID': 6}]
def fake_issue_api_request(self, method, params, version='1.0',
endpoint=None):
if method is 'GetClusterCapacity':
data = {}
if version == '1.0':
data = {'result': {'clusterCapacity': {
'maxProvisionedSpace': 107374182400,
'usedSpace': 1073741824,
'compressionPercent': 100,
'deDuplicationPercent': 100,
'thinProvisioningPercent': 100,
'maxUsedSpace': 53687091200}}}
elif version == '8.0':
data = {'result': {'clusterCapacity': {
'usedMetadataSpaceInSnapshots': 16476454912,
'maxUsedMetadataSpace': 432103337164,
'activeBlockSpace': 616690857535,
'uniqueBlocksUsedSpace': 628629229316,
'totalOps': 7092186135,
'peakActiveSessions': 0,
'uniqueBlocks': 519489473,
'maxOverProvisionableSpace': 276546135777280,
'zeroBlocks': 8719571984,
'provisionedSpace': 19938551005184,
'maxUsedSpace': 8402009333760,
'peakIOPS': 0,
'timestamp': '2019-04-24T12:08:22Z',
'currentIOPS': 0,
'usedSpace': 628629229316,
'activeSessions': 0,
'nonZeroBlocks': 1016048624,
'maxProvisionedSpace': 55309227155456,
'usedMetadataSpace': 16476946432,
'averageIOPS': 0,
'snapshotNonZeroBlocks': 1606,
'maxIOPS': 200000,
'clusterRecentIOSize': 0}}}
return data
elif method is 'GetClusterInfo':
results = {
'result':
{'clusterInfo':
{'name': 'fake-cluster',
'mvip': '1.1.1.1',
'svip': '1.1.1.1',
'uniqueID': 'unqid',
'repCount': 2,
'uuid': '53c8be1e-89e2-4f7f-a2e3-7cb84c47e0ec',
'attributes': {}}}}
return results
elif method is 'GetClusterVersionInfo':
return {'id': None, 'result': {'softwareVersionInfo':
{'pendingVersion': '8.2.1.4',
'packageName': '',
'currentVersion': '8.2.1.4',
'nodeID': 0, 'startTime': ''},
'clusterVersion': '8.2.1.4',
'clusterAPIVersion': '8.2'}}
elif method is 'AddAccount' and version == '1.0':
return {'result': {'accountID': 25}, 'id': 1}
elif method is 'GetAccountByName' and version == '1.0':
results = {'result': {'account':
{'accountID': 25,
'username': params['username'],
'status': 'active',
'initiatorSecret': '123456789012',
'targetSecret': '123456789012',
'attributes': {},
'volumes': [6, 7, 20]}},
"id": 1}
return results
elif method is 'CreateVolume' and version == '1.0':
return {'result': {'volumeID': 5}, 'id': 1}
elif method is 'CreateSnapshot' and version == '6.0':
return {'result': {'snapshotID': 5}, 'id': 1}
elif method is 'DeleteVolume' and version == '1.0':
return {'result': {}, 'id': 1}
elif method is 'ModifyVolume' and version == '5.0':
return {'result': {}, 'id': 1}
elif method is 'CloneVolume':
return {'result': {'volumeID': 6}, 'id': 2}
elif method is 'ModifyVolume':
return {'result': {}, 'id': 1}
elif method is 'ListVolumesForAccount' and version == '1.0':
test_name = 'OS-VOLID-a720b3c0-d1f0-11e1-9b23-0800200c9a66'
result = {'result': {
'volumes': [{'volumeID': 5,
'name': test_name,
'accountID': 25,
'sliceCount': 1,
'totalSize': 1 * units.Gi,
'enable512e': True,
'access': "readWrite",
'status': "active",
'attributes': {'uuid': f_uuid[0]},
'qos': None,
'iqn': test_name}]}}
return result
elif method is 'ListActiveVolumes':
test_name = "existing_volume"
result = {'result': {
'volumes': [{'volumeID': 5,
'name': test_name,
'accountID': 8,
'sliceCount': 1,
'totalSize': int(1.75 * units.Gi),
'enable512e': True,
'access': "readWrite",
'status': "active",
'attributes': {},
'qos': None,
'iqn': test_name}]}}
return result
elif method is 'ListVolumes':
test_name = "get_sfvol_by_cinder"
result = {'result': {
'volumes': [{'volumeID': 5,
'name': test_name,
'accountID': 8,
'sliceCount': 1,
'totalSize': int(1.75 * units.Gi),
'enable512e': True,
'access': "readWrite",
'status': "active",
'attributes': {'uuid': f_uuid[0]},
'qos': None,
'iqn': test_name},
{'volumeID': 15,
'name': test_name,
'accountID': 8,
'sliceCount': 1,
'totalSize': int(1.75 * units.Gi),
'enable512e': True,
'access': "readWrite",
'status': "active",
'attributes': {'uuid': f_uuid[1]},
'qos': None,
'iqn': test_name}]}}
if params and params['startVolumeID']:
volumes = result['result']['volumes']
selected_volumes = [v for v in volumes if v.get('volumeID')
!= params['startVolumeID']]
result['result']['volumes'] = selected_volumes
return result
elif method is 'DeleteSnapshot':
return {'result': {}}
elif method is 'GetClusterVersionInfo':
return {'result': {'clusterAPIVersion': '8.0'}}
elif method is 'StartVolumePairing':
return {'result': {'volumePairingKey': 'fake-pairing-key'}}
elif method is 'RollbackToSnapshot':
return {
"id": 1,
"result": {
"checksum": "0x0",
"snapshot": {
"attributes": {},
"checksum": "0x0",
"createTime": "2016-04-04T17:27:32Z",
"enableRemoteReplication": "false",
"expirationReason": "None",
"expirationTime": "null",
"groupID": 0,
"groupSnapshotUUID": f_uuid[0],
"name": "test1-copy",
"snapshotID": 1,
"snapshotUUID": f_uuid[1],
"status": "done",
"totalSize": 5000658944,
"virtualVolumeID": "null",
"volumeID": 1
},
"snapshotID": 1
}
}
elif method is 'ListAccounts':
return {
'result': {
'accounts': [{
'accountID': 5,
'targetSecret': 'shhhh',
'username': 'prefix-testprjid'
}]
}
}
elif method == 'ListSnapshots':
raise exception.VolumeNotFound('test clone unconfigured image')
else:
# Crap, unimplemented API call in Fake
return None
def fake_issue_api_request_fails(self, method,
params, version='1.0',
endpoint=None):
response = {'error': {'code': 000,
'name': 'DummyError',
'message': 'This is a fake error response'},
'id': 1}
msg = ('Error (%s) encountered during '
'SolidFire API call.' % response['error']['name'])
raise solidfire.SolidFireAPIException(message=msg)
def fake_set_qos_by_volume_type(self, type_id, ctxt):
return {'minIOPS': 500,
'maxIOPS': 1000,
'burstIOPS': 1000}
def fake_volume_get(self, key, default=None):
return {'qos': 'fast'}
def fake_update_cluster_status(self):
return
def fake_get_cluster_version_info(self):
return
def fake_get_model_info(self, account, vid, endpoint=None):
return {'fake': 'fake-model'}
@mock.patch.object(solidfire.SolidFireDriver, '_issue_api_request')
def test_create_volume_with_qos_type(self,
_mock_issue_api_request):
_mock_issue_api_request.side_effect = self.fake_issue_api_request
testvol = {'project_id': 'testprjid',
'name': 'testvol',
'size': 1,
'id': 'a720b3c0-d1f0-11e1-9b23-0800200c9a66',
'volume_type_id': 'fast',
'created_at': timeutils.utcnow()}
fake_sfaccounts = [{'accountID': 5,
'targetSecret': 'shhhh',
'username': 'prefix-testprjid'}]
test_type = {'name': 'sf-1',
'qos_specs_id': 'fb0576d7-b4b5-4cad-85dc-ca92e6a497d1',
'deleted': False,
'created_at': '2014-02-06 04:58:11',
'updated_at': None,
'extra_specs': {},
'deleted_at': None,
'id': 'e730e97b-bc7d-4af3-934a-32e59b218e81'}
test_qos_spec = {'id': 'asdfafdasdf',
'specs': {'minIOPS': '1000',
'maxIOPS': '2000',
'burstIOPS': '3000'}}
ctx = context.get_admin_context()
testvol = fake_volume.fake_volume_obj(ctx, **testvol)
def _fake_get_volume_type(ctxt, type_id):
return test_type
def _fake_get_qos_spec(ctxt, spec_id):
return test_qos_spec
def _fake_do_volume_create(account, params):
params['provider_location'] = '1.1.1.1 iqn 0'
return params
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
with mock.patch.object(sfv,
'_get_sfaccounts_for_tenant',
return_value=fake_sfaccounts), \
mock.patch.object(sfv,
'_get_account_create_availability',
return_value=fake_sfaccounts[0]), \
mock.patch.object(sfv,
'_do_volume_create',
side_effect=_fake_do_volume_create), \
mock.patch.object(volume_types,
'get_volume_type',
side_effect=_fake_get_volume_type), \
mock.patch.object(qos_specs,
'get_qos_specs',
side_effect=_fake_get_qos_spec):
self.assertEqual({'burstIOPS': 3000,
'minIOPS': 1000,
'maxIOPS': 2000},
sfv.create_volume(testvol)['qos'])
@mock.patch.object(solidfire.SolidFireDriver, '_issue_api_request')
def test_create_volume(self,
_mock_issue_api_request):
_mock_issue_api_request.side_effect = self.fake_issue_api_request
testvol = {'project_id': 'testprjid',
'name': 'testvol',
'size': 1,
'id': 'a720b3c0-d1f0-11e1-9b23-0800200c9a66',
'volume_type_id': None,
'created_at': timeutils.utcnow()}
fake_sfaccounts = [{'accountID': 5,
'targetSecret': 'shhhh',
'username': 'prefix-testprjid'}]
ctx = context.get_admin_context()
testvol = fake_volume.fake_volume_obj(ctx, **testvol)
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
with mock.patch.object(sfv,
'_get_sfaccounts_for_tenant',
return_value=fake_sfaccounts), \
mock.patch.object(sfv,
'_get_account_create_availability',
return_value=fake_sfaccounts[0]):
model_update = sfv.create_volume(testvol)
self.assertIsNotNone(model_update)
self.assertNotIn('provider_geometry', model_update)
@mock.patch.object(solidfire.SolidFireDriver, '_issue_api_request')
def test_create_volume_non_512e(self,
_mock_issue_api_request):
_mock_issue_api_request.side_effect = self.fake_issue_api_request
testvol = {'project_id': 'testprjid',
'name': 'testvol',
'size': 1,
'id': 'a720b3c0-d1f0-11e1-9b23-0800200c9a66',
'volume_type_id': None,
'created_at': timeutils.utcnow()}
ctx = context.get_admin_context()
testvol = fake_volume.fake_volume_obj(ctx, **testvol)
fake_sfaccounts = [{'accountID': 5,
'targetSecret': 'shhhh',
'username': 'prefix-testprjid'}]
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
with mock.patch.object(sfv,
'_get_sfaccounts_for_tenant',
return_value=fake_sfaccounts), \
mock.patch.object(sfv,
'_issue_api_request',
side_effect=self.fake_issue_api_request), \
mock.patch.object(sfv,
'_get_account_create_availability',
return_value=fake_sfaccounts[0]):
self.configuration.sf_emulate_512 = False
model_update = sfv.create_volume(testvol)
self.configuration.sf_emulate_512 = True
self.assertEqual('4096 4096',
model_update.get('provider_geometry', None))
def test_create_delete_snapshot(self):
ctx = context.get_admin_context()
testvol = fake_volume.fake_volume_obj(ctx)
testsnap_dict = {'project_id': 'testprjid',
'name': testvol.name,
'volume_size': testvol.size,
'id': 'b831c4d1-d1f0-11e1-9b23-0800200c9a66',
'volume_id': testvol.id,
'volume_type_id': None,
'created_at': timeutils.utcnow(),
'provider_id': '8 99 None',
'volume': testvol}
testsnap = fake_snapshot.fake_snapshot_obj(ctx, **testsnap_dict)
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
fake_uuid = 'UUID-b831c4d1-d1f0-11e1-9b23-0800200c9a66'
with mock.patch.object(
solidfire.SolidFireDriver,
'_get_sf_snapshots',
return_value=[{'snapshotID': '5',
'name': fake_uuid,
'volumeID': 5}]), \
mock.patch.object(sfv,
'_get_sfaccounts_for_tenant',
return_value=[{'accountID': 5,
'username':
'prefix-testprjid'}]),\
mock.patch.object(sfv, '_retrieve_replication_settings',
return_value=["Async", {}]),\
mock.patch.object(sfv, '_get_sf_volume',
return_value={'volumeID': 33}):
sfv.create_snapshot(testsnap)
sfv.delete_snapshot(testsnap)
@mock.patch.object(solidfire.SolidFireDriver, '_issue_api_request')
def test_create_clone(self,
_mock_issue_api_request):
_mock_issue_api_request.side_effect = self.fake_issue_api_request
_fake_get_snaps = [{'snapshotID': 5, 'name': 'testvol'}]
_fake_get_volume = (
{'volumeID': 99,
'name': 'UUID-a720b3c0-d1f0-11e1-9b23-0800200c9a66',
'attributes': {}})
updates_vol_a = {'project_id': 'testprjid',
'name': 'testvol',
'size': 1,
'id': 'a720b3c0-d1f0-11e1-9b23-0800200c9a66',
'volume_type_id': None,
'created_at': timeutils.utcnow()}
updates_vol_b = {'project_id': 'testprjid',
'name': 'testvol',
'size': 1,
'id': 'b831c4d1-d1f0-11e1-9b23-0800200c9a66',
'volume_type_id': None,
'created_at': timeutils.utcnow()}
ctx = context.get_admin_context()
testvol = fake_volume.fake_volume_obj(ctx, **updates_vol_a)
testvol_b = fake_volume.fake_volume_obj(ctx, **updates_vol_b)
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
with mock.patch.object(sfv,
'_get_sf_snapshots',
return_value=_fake_get_snaps), \
mock.patch.object(sfv,
'_get_sf_volume',
return_value=_fake_get_volume), \
mock.patch.object(sfv,
'_issue_api_request',
side_effect=self.fake_issue_api_request), \
mock.patch.object(sfv,
'_get_sfaccounts_for_tenant',
return_value=[]), \
mock.patch.object(sfv,
'_get_model_info',
return_value={}):
sfv.create_cloned_volume(testvol_b, testvol)
def test_initialize_connector_with_blocksizes(self):
connector = {'initiator': 'iqn.2012-07.org.fake:01'}
testvol = {'project_id': 'testprjid',
'name': 'testvol',
'size': 1,
'id': 'a720b3c0-d1f0-11e1-9b23-0800200c9a66',
'volume_type_id': None,
'provider_location': '10.10.7.1:3260 iqn.2010-01.com.'
'solidfire:87hg.uuid-2cc06226-cc'
'74-4cb7-bd55-14aed659a0cc.4060 0',
'provider_auth': 'CHAP stack-1-a60e2611875f40199931f2'
'c76370d66b 2FE0CQ8J196R',
'provider_geometry': '4096 4096',
'created_at': timeutils.utcnow(),
}
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
properties = sfv.initialize_connection(testvol, connector)
self.assertEqual('4096', properties['data']['physical_block_size'])
self.assertEqual('4096', properties['data']['logical_block_size'])
self.assertTrue(properties['data']['discard'])
def test_create_volume_fails(self):
# NOTE(JDG) This test just fakes update_cluster_status
# this is inentional for this test
self.mock_object(solidfire.SolidFireDriver,
'_update_cluster_status',
self.fake_update_cluster_status)
self.mock_object(solidfire.SolidFireDriver,
'_issue_api_request',
self.fake_issue_api_request)
testvol = {'project_id': 'testprjid',
'name': 'testvol',
'size': 1,
'id': 'a720b3c0-d1f0-11e1-9b23-0800200c9a66',
'created_at': timeutils.utcnow()}
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
self.mock_object(solidfire.SolidFireDriver,
'_issue_api_request',
self.fake_issue_api_request_fails)
try:
sfv.create_volume(testvol)
self.fail("Should have thrown Error")
except Exception:
pass
def test_create_sfaccount(self):
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
self.mock_object(solidfire.SolidFireDriver,
'_issue_api_request',
self.fake_issue_api_request)
account = sfv._create_sfaccount('some-name')
self.assertIsNotNone(account)
def test_create_sfaccount_fails(self):
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
self.mock_object(solidfire.SolidFireDriver,
'_issue_api_request',
self.fake_issue_api_request_fails)
self.assertRaises(solidfire.SolidFireAPIException,
sfv._create_sfaccount, 'project-id')
def test_get_sfaccounts_for_tenant(self):
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
self.mock_object(solidfire.SolidFireDriver,
'_issue_api_request',
self.fake_issue_api_request)
accounts = sfv._get_sfaccounts_for_tenant('some-name')
self.assertIsNotNone(accounts)
def test_get_sfaccounts_for_tenant_fails(self):
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
self.mock_object(solidfire.SolidFireDriver,
'_issue_api_request',
self.fake_issue_api_request_fails)
self.assertRaises(solidfire.SolidFireAPIException,
sfv._get_sfaccounts_for_tenant, 'some-name')
def test_get_sfaccount_by_name(self):
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
self.mock_object(solidfire.SolidFireDriver,
'_issue_api_request',
self.fake_issue_api_request)
account = sfv._get_sfaccount_by_name('some-name')
self.assertIsNotNone(account)
def test_get_account_create_availability_no_account(self):
fake_sfaccounts = []
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
sfaccount = sfv._get_account_create_availability(fake_sfaccounts)
self.assertIsNone(sfaccount)
def test_get_account_create_availability(self):
fake_sfaccounts = [{'accountID': 29,
'targetSecret': 'shhhh',
'username': 'prefix-testprjid',
'volumes': [6, 7, 20]}]
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
sfaccount = sfv._get_account_create_availability(fake_sfaccounts)
self.assertIsNotNone(sfaccount)
self.assertEqual(sfaccount['accountID'],
fake_sfaccounts[0]['accountID'])
def test_get_account_create_availability_primary_full(self):
fake_sfaccounts = [{'accountID': 30,
'targetSecret': 'shhhh',
'username': 'prefix-testprjid'}]
get_sfaccount_result = {'accountID': 31,
'targetSecret': 'shhhh',
'username': 'prefix-testprjid_'}
get_vol_result = list(range(1, 2001))
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
with mock.patch.object(sfv,
'_get_sfaccounts_for_tenant',
return_value=fake_sfaccounts), \
mock.patch.object(sfv,
'_get_volumes_for_account',
return_value=get_vol_result):
sfaccount = sfv._get_account_create_availability(fake_sfaccounts)
self.assertIsNotNone(sfaccount)
self.assertEqual(sfaccount['username'],
get_sfaccount_result['username'])
def test_get_account_create_availability_both_full(self):
fake_sfaccounts = [{'accountID': 32,
'targetSecret': 'shhhh',
'username': 'prefix-testprjid'},
{'accountID': 33,
'targetSecret': 'shhhh',
'username': 'prefix-testprjid_'}]
get_vol_result = list(range(1, 2001))
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
with mock.patch.object(sfv,
'_get_sfaccounts_for_tenant',
return_value=fake_sfaccounts), \
mock.patch.object(sfv,
'_get_volumes_for_account',
return_value=get_vol_result):
sfaccount = sfv._get_account_create_availability(fake_sfaccounts)
self.assertIsNone(sfaccount)
def test_get_create_account(self):
fake_sfaccounts = [{'accountID': 34,
'targetSecret': 'shhhh',
'username': 'prefix-testprjid'},
{'accountID': 35,
'targetSecret': 'shhhh',
'username': 'prefix-testprjid_'}]
get_vol_result = list(range(1, 2001))
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
with mock.patch.object(sfv,
'_get_sfaccounts_for_tenant',
return_value=fake_sfaccounts), \
mock.patch.object(sfv,
'_get_volumes_for_account',
return_value=get_vol_result):
sfaccount = sfv._get_account_create_availability(fake_sfaccounts)
self.assertRaises(solidfire.SolidFireDriverException,
sfv._get_create_account, sfaccount)
def test_get_sfaccount_by_name_fails(self):
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
self.mock_object(solidfire.SolidFireDriver,
'_issue_api_request',
self.fake_issue_api_request_fails)
self.assertRaises(solidfire.SolidFireAPIException,
sfv._get_sfaccount_by_name, 'some-name')
def test_get_sfvol_by_cinder_vref_no_provider_id(self):
fake_sfaccounts = [{'accountID': 25,
'targetSecret': 'shhhh',
'username': 'prefix-testprjid',
'volumes': [6, 7, 20]}]
self.mock_vref = mock_vref()
vol_result = {'volumeID': 5,
'name': 'test_volume',
'accountID': 25,
'sliceCount': 1,
'totalSize': 1 * units.Gi,
'enable512e': True,
'access': "readWrite",
'status': "active",
'attributes': {'uuid': f_uuid[0]},
'qos': None,
'iqn': 'super_fake_iqn'}
mod_conf = self.configuration
mod_conf.sf_enable_vag = True
sfv = solidfire.SolidFireDriver(configuration=mod_conf)
with mock.patch.object(sfv,
'_get_sfaccounts_for_tenant',
return_value = fake_sfaccounts), \
mock.patch.object(sfv, '_issue_api_request',
side_effect = self.fake_issue_api_request):
self.mock_vref['provider_id'] = None
sfvol = sfv._get_sfvol_by_cinder_vref(self.mock_vref)
self.assertIsNotNone(sfvol)
self.assertEqual(sfvol['attributes']['uuid'],
vol_result['attributes']['uuid'])
self.assertEqual(sfvol['volumeID'], vol_result['volumeID'])
def test_get_sfvol_by_cinder_vref_no_provider_id_nomatch(self):
fake_sfaccounts = [{'accountID': 5,
'targetSecret': 'shhhh',
'username': 'prefix-testprjid',
'volumes': [5, 6, 7, 8]}]
self.mock_vref = mock_vref()
mod_conf = self.configuration
mod_conf.sf_enable_vag = True
sfv = solidfire.SolidFireDriver(configuration=mod_conf)
with mock.patch.object(sfv,
'_get_sfaccounts_for_tenant',
return_value = fake_sfaccounts), \
mock.patch.object(sfv, '_issue_api_request',
side_effect = self.fake_issue_api_request):
self.mock_vref['provider_id'] = None
self.mock_vref['id'] = '142b9c32-a71A-4fbe-830c-c20c5596caea'
sfvol = sfv._get_sfvol_by_cinder_vref(self.mock_vref)
self.assertIsNone(sfvol)
def test_get_sfvol_by_cinder_vref_nomatch(self):
fake_sfaccounts = [{'accountID': 5,
'targetSecret': 'shhhh',
'username': 'prefix-testprjid',
'volumes': [5, 6, 7, 8]}]
self.mock_vref = mock_vref()
mod_conf = self.configuration
mod_conf.sf_enable_vag = True
sfv = solidfire.SolidFireDriver(configuration=mod_conf)
with mock.patch.object(sfv,
'_get_sfaccounts_for_tenant',
return_value = fake_sfaccounts), \
mock.patch.object(sfv, '_issue_api_request',
side_effect = self.fake_issue_api_request):
p_i = '324 8 6ecebf5d-5521-4ce1-80f3-358ebc1b9cdc'
self.mock_vref['provider_id'] = p_i
self.mock_vref['id'] = '142b9c32-a71A-4fbe-830c-c20c5596caea'
sfvol = sfv._get_sfvol_by_cinder_vref(self.mock_vref)
self.assertIsNone(sfvol)
def test_get_sfvol_by_cinder_vref(self):
fake_sfaccounts = [{'accountID': 5,
'targetSecret': 'shhhh',
'username': 'prefix-testprjid',
'volumes': [5, 6, 7, 8]}]
self.mock_vref = mock_vref()
get_vol_result = {'volumeID': 5,
'name': 'test_volume',
'accountID': 25,
'sliceCount': 1,
'totalSize': 1 * units.Gi,
'enable512e': True,
'access': "readWrite",
'status': "active",
'attributes': {'uuid': f_uuid[0]},
'qos': None,
'iqn': 'super_fake_iqn'}
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
with mock.patch.object(sfv, '_get_sfaccounts_for_tenant',
return_value = fake_sfaccounts), \
mock.patch.object(sfv, '_issue_api_request',
side_effect = self.fake_issue_api_request):
sfvol = sfv._get_sfvol_by_cinder_vref(self.mock_vref)
self.assertIsNotNone(sfvol)
self.assertEqual(get_vol_result['volumeID'], sfvol['volumeID'])
def test_delete_volume(self):
vol_id = 'a720b3c0-d1f0-11e1-9b23-0800200c9a66'
testvol = test_utils.create_volume(
self.ctxt,
id=vol_id,
display_name='test_volume',
provider_id='1 5 None',
multiattach=False)
fake_sfaccounts = [{'accountID': 5,
'name': 'testprjid',
'targetSecret': 'shhhh',
'username': 'john-wayne'}]
get_vol_result = {'volumeID': 5,
'name': 'test_volume',
'accountID': 25,
'sliceCount': 1,
'totalSize': 1 * units.Gi,
'enable512e': True,
'access': "readWrite",
'status': "active",
'attributes': {},
'qos': None,
'iqn': 'super_fake_iqn'}
mod_conf = self.configuration
mod_conf.sf_enable_vag = True
sfv = solidfire.SolidFireDriver(configuration=mod_conf)
with mock.patch.object(sfv,
'_get_sfaccounts_for_tenant',
return_value=fake_sfaccounts), \
mock.patch.object(sfv,
'_get_sfvol_by_cinder_vref',
return_value=get_vol_result), \
mock.patch.object(sfv,
'_issue_api_request'), \
mock.patch.object(sfv,
'_remove_volume_from_vags') as rem_vol:
sfv.delete_volume(testvol)
rem_vol.not_called(get_vol_result['volumeID'])
def test_delete_multiattach_volume(self):
vol_id = 'a720b3c0-d1f0-11e1-9b23-0800200c9a66'
testvol = test_utils.create_volume(
self.ctxt,
id=vol_id,
display_name='test_volume',
provider_id='1 5 None',
multiattach=True)
fake_sfaccounts = [{'accountID': 5,
'targetSecret': 'shhhh',
'username': 'prefix-testprjid'}]
get_vol_result = {'volumeID': 5,
'name': 'test_volume',
'accountID': 25,
'sliceCount': 1,
'totalSize': 1 * units.Gi,
'enable512e': True,
'access': "readWrite",
'status': "active",
'attributes': {},
'qos': None,
'iqn': 'super_fake_iqn'}
mod_conf = self.configuration
mod_conf.sf_enable_vag = True
sfv = solidfire.SolidFireDriver(configuration=mod_conf)
with mock.patch.object(sfv,
'_get_sfaccounts_for_tenant',
return_value=fake_sfaccounts), \
mock.patch.object(sfv,
'_get_sfvol_by_cinder_vref',
return_value=get_vol_result), \
mock.patch.object(sfv,
'_issue_api_request'), \
mock.patch.object(sfv,
'_remove_volume_from_vags') as rem_vol:
sfv.delete_volume(testvol)
rem_vol.assert_called_with(get_vol_result['volumeID'])
def test_delete_volume_no_volume_on_backend(self):
fake_sfaccounts = [{'accountID': 5,
'targetSecret': 'shhhh',
'username': 'prefix-testprjid'}]
fake_no_volumes = []
testvol = test_utils.create_volume(self.ctxt)
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
with mock.patch.object(sfv,
'_get_sfaccounts_for_tenant',
return_value=fake_sfaccounts), \
mock.patch.object(sfv,
'_get_volumes_for_account',
return_value=fake_no_volumes):
sfv.delete_volume(testvol)
def test_delete_snapshot_no_snapshot_on_backend(self):
fake_sfaccounts = [{'accountID': 5,
'targetSecret': 'shhhh',
'username': 'prefix-testprjid'}]
fake_no_volumes = []
testvol = test_utils.create_volume(
self.ctxt,
volume_id='b831c4d1-d1f0-11e1-9b23-0800200c9a66')
testsnap = test_utils.create_snapshot(
self.ctxt,
volume_id=testvol.id)
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
with mock.patch.object(sfv,
'_get_sfaccounts_for_tenant',
return_value=fake_sfaccounts), \
mock.patch.object(sfv,
'_get_volumes_for_account',
return_value=fake_no_volumes):
sfv.delete_snapshot(testsnap)
def fake_ext_qos_issue_api_request(self, method, params, version='1.0',
endpoint=None):
EXPECTED_SIZE = 2 << 30 # 2147483648 size + increase
if method == 'ModifyVolume':
response = {'error': {'code': 0,
'name': 'Extend Volume',
'message': 'extend fail, size/scale-iops'},
'id': 1}
if params.get('totalSize', None) != EXPECTED_SIZE:
msg = ('Error (%s) encountered during '
'SolidFire API call.' % response['error']['name'])
raise solidfire.SolidFireAPIException(message=msg)
if params.get('qos', None) != SolidFireVolumeTestCase.EXPECTED_QOS:
msg = ('Error (%s) encountered during '
'SolidFire API call.' % response['error']['name'])
raise solidfire.SolidFireAPIException(message=msg)
return {'result': {}, 'id': 1}
elif method == 'GetAccountByName' and version == '1.0':
results = {'result': {'account':
{'accountID': 25,
'username': params['username'],
'status': 'active',
'initiatorSecret': '123456789012',
'targetSecret': '123456789012',
'attributes': {},
'volumes': [6, 7, 20]}},
"id": 1}
return results
elif method == 'ListVolumesForAccount' and version == '1.0':
test_name = 'OS-VOLID-a720b3c0-d1f0-11e1-9b23-0800200c9a66'
result = {'result': {
'volumes': [{'volumeID': 5,
'name': test_name,
'accountID': 25,
'sliceCount': 1,
'totalSize': 1 * units.Gi,
'enable512e': True,
'access': "readWrite",
'status': "active",
'attributes': {},
'qos': None,
'iqn': test_name}]}}
return result
else:
return None
def test_extend_volume(self):
self.mock_object(solidfire.SolidFireDriver,
'_issue_api_request',
self.fake_issue_api_request)
testvol = {'project_id': 'testprjid',
'name': 'test_volume',
'size': 1,
'id': 'a720b3c0-d1f0-11e1-9b23-0800200c9a66',
'created_at': timeutils.utcnow()}
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
sfv.extend_volume(testvol, 2)
def test_extend_volume_with_scaled_qos(self):
size = 1
self.mock_object(solidfire.SolidFireDriver,
'_issue_api_request',
self.fake_issue_api_request)
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
qos_ref = qos_specs.create(self.ctxt,
'qos-specs-1', {'minIOPS': '100',
'maxIOPS': '1000',
'burstIOPS': '1500',
'scaledIOPS': 'True',
'scaleMin': '10',
'scaleMax': '20',
'scaleBurst': '30'})
type_ref = volume_types.create(self.ctxt, "type1",
{'qos:minIOPS': '1000',
'qos:maxIOPS': '10000',
'qos:burstIOPS': '20000'})
qos_specs.associate_qos_with_type(self.ctxt,
qos_ref['id'],
type_ref['id'])
qos = sfv._set_qos_by_volume_type(self.ctxt, type_ref['id'], size + 1)
self.assertEqual(SolidFireVolumeTestCase.EXPECTED_QOS, qos)
def test_extend_volume_fails_no_volume(self):
self.mock_object(solidfire.SolidFireDriver,
'_issue_api_request',
self.fake_issue_api_request)
testvol = {'project_id': 'testprjid',
'name': 'no-name',
'size': 1,
'id': 'not-found'}
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
self.assertRaises(exception.VolumeNotFound,
sfv.extend_volume,
testvol, 2)
def test_extend_volume_fails_account_lookup(self):
# NOTE(JDG) This test just fakes update_cluster_status
# this is intentional for this test
self.mock_object(solidfire.SolidFireDriver,
'_update_cluster_status',
self.fake_update_cluster_status)
self.mock_object(solidfire.SolidFireDriver,
'_issue_api_request',
self.fake_issue_api_request)
testvol = {'project_id': 'testprjid',
'name': 'no-name',
'size': 1,
'id': 'a720b3c0-d1f0-11e1-9b23-0800200c9a66',
'created_at': timeutils.utcnow()}
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
self.mock_object(solidfire.SolidFireDriver,
'_issue_api_request',
self.fake_issue_api_request_fails)
self.assertRaises(solidfire.SolidFireAPIException,
sfv.extend_volume,
testvol, 2)
@mock.patch.object(solidfire.SolidFireDriver, '_get_sfaccount')
@mock.patch.object(solidfire.SolidFireDriver, '_get_sf_volume')
@mock.patch.object(solidfire.SolidFireDriver, '_retrieve_qos_setting')
@mock.patch.object(solidfire.SolidFireDriver, '_issue_api_request')
@mock.patch.object(solidfire.SolidFireDriver,
'_retrieve_replication_settings')
@mock.patch.object(solidfire.SolidFireDriver, '_create_cluster_reference')
def test_extend_replicated_volume(self, mock_create_cluster_reference,
mock_retrieve_replication_settings,
mock_issue_api_request,
mock_retrieve_qos_setting,
mock_get_sf_volume,
mock_get_sfaccount):
mock_create_cluster_reference.return_value = {
'mvip': self.mvip,
'svip': self.svip}
mock_retrieve_replication_settings.return_value = "Async"
mock_retrieve_qos_setting.return_value = None
self.fake_sfvol['volumePairs'] = [{'remoteVolumeID': 26}]
mock_get_sf_volume.return_value = self.fake_sfvol
mock_get_sfaccount.return_value = self.fake_sfaccount
ctx = context.get_admin_context()
utc_now = timeutils.utcnow().isoformat()
vol_fields = {
'id': f_uuid,
'created_at': utc_now
}
vol = fake_volume.fake_volume_obj(ctx, **vol_fields)
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
sfv.replication_enabled = True
sfv.cluster_pairs = self.cluster_pairs
sfv.active_cluster['mvip'] = self.mvip
sfv.active_cluster['svip'] = self.svip
mock_issue_api_request.reset_mock()
# pylint: disable=assignment-from-no-return
updates = sfv.extend_volume(vol, vol.size + 10)
# pylint: enable=assignment-from-no-return
self.assertIsNone(updates)
modify_params = {
'volumeID': self.fake_sfvol['volumeID'],
'totalSize': int((vol.size + 10) * units.Gi),
'qos': None
}
modify_params2 = modify_params.copy()
modify_params2['volumeID'] = 26
expected_calls = [
mock.call("ModifyVolume", modify_params, version='5.0'),
mock.call("ModifyVolume", modify_params2, version='5.0',
endpoint=self.cluster_pairs[0]['endpoint'])
]
mock_issue_api_request.assert_has_calls(expected_calls)
mock_create_cluster_reference.assert_called()
mock_retrieve_replication_settings.assert_called_with(vol)
mock_retrieve_qos_setting.assert_called_with(vol, vol.size + 10)
mock_get_sf_volume.assert_called_with(
vol.id, {'accountID': self.fake_sfaccount['accountID']})
mock_get_sfaccount.assert_called_with(vol.project_id)
def test_set_by_qos_spec_with_scoping(self):
size = 1
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
qos_ref = qos_specs.create(self.ctxt,
'qos-specs-1', {'qos:minIOPS': '1000',
'qos:maxIOPS': '10000',
'qos:burstIOPS': '20000'})
type_ref = volume_types.create(self.ctxt,
"type1", {"qos:minIOPS": "100",
"qos:burstIOPS": "300",
"qos:maxIOPS": "200"})
qos_specs.associate_qos_with_type(self.ctxt,
qos_ref['id'],
type_ref['id'])
qos = sfv._set_qos_by_volume_type(self.ctxt, type_ref['id'], size)
self.assertEqual(self.expected_qos_results, qos)
def test_set_by_qos_spec(self):
size = 1
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
qos_ref = qos_specs.create(self.ctxt,
'qos-specs-1', {'minIOPS': '1000',
'maxIOPS': '10000',
'burstIOPS': '20000'})
type_ref = volume_types.create(self.ctxt,
"type1", {"qos:minIOPS": "100",
"qos:burstIOPS": "300",
"qos:maxIOPS": "200"})
qos_specs.associate_qos_with_type(self.ctxt,
qos_ref['id'],
type_ref['id'])
qos = sfv._set_qos_by_volume_type(self.ctxt, type_ref['id'], size)
self.assertEqual(self.expected_qos_results, qos)
@file_data("scaled_iops_test_data.json")
@unpack
def test_scaled_qos_spec_by_type(self, argument):
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
size = argument[0].pop('size')
type_ref = volume_types.create(self.ctxt, "type1", argument[0])
qos = sfv._set_qos_by_volume_type(self.ctxt, type_ref['id'], size)
self.assertEqual(argument[1], qos)
@file_data("scaled_iops_invalid_data.json")
@unpack
def test_set_scaled_qos_by_type_invalid(self, inputs):
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
size = inputs[0].pop('size')
type_ref = volume_types.create(self.ctxt, "type1", inputs[0])
self.assertRaises(exception.InvalidQoSSpecs,
sfv._set_qos_by_volume_type,
self.ctxt,
type_ref['id'],
size)
def test_accept_transfer(self):
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
self.mock_object(solidfire.SolidFireDriver,
'_issue_api_request',
self.fake_issue_api_request)
testvol = {'project_id': 'testprjid',
'name': 'test_volume',
'size': 1,
'id': 'a720b3c0-d1f0-11e1-9b23-0800200c9a66',
'created_at': timeutils.utcnow()}
expected = {'provider_auth': 'CHAP cinder-new_project 123456789012'}
self.assertEqual(expected,
sfv.accept_transfer(self.ctxt,
testvol,
'new_user', 'new_project'))
def test_accept_transfer_volume_not_found_raises(self):
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
self.mock_object(solidfire.SolidFireDriver,
'_issue_api_request',
self.fake_issue_api_request)
testvol = {'project_id': 'testprjid',
'name': 'test_volume',
'size': 1,
'id': 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa',
'created_at': timeutils.utcnow()}
self.assertRaises(exception.VolumeNotFound,
sfv.accept_transfer,
self.ctxt,
testvol,
'new_user',
'new_project')
def test_retype(self):
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
self.mock_object(solidfire.SolidFireDriver,
'_issue_api_request',
self.fake_issue_api_request)
type_ref = volume_types.create(self.ctxt,
"type1", {"qos:minIOPS": "500",
"qos:burstIOPS": "2000",
"qos:maxIOPS": "1000"})
diff = {'encryption': {}, 'qos_specs': {},
'extra_specs': {'qos:burstIOPS': ('10000', u'2000'),
'qos:minIOPS': ('1000', u'500'),
'qos:maxIOPS': ('10000', u'1000')}}
host = None
updates = {'project_id': 'testprjid',
'name': 'test_volume',
'size': 1,
'id': 'a720b3c0-d1f0-11e1-9b23-0800200c9a66',
'created_at': timeutils.utcnow()}
ctx = context.get_admin_context()
testvol = fake_volume.fake_volume_obj(ctx, **updates)
migrated, updates = sfv.retype(self.ctxt, testvol, type_ref,
diff, host)
self.assertTrue(migrated)
self.assertEqual({}, updates)
@data(None, 'Success', 'Error', 'target:{}'.format(f_uuid[0]))
@mock.patch.object(solidfire.SolidFireDriver, '_get_sf_volume')
@mock.patch.object(solidfire.SolidFireDriver, '_get_sfaccount')
def test_attach_volume(self, mig_status, mock_get_sfaccount,
mock_get_sf_volume):
mock_get_sfaccount.return_value = self.fake_sfaccount
i_uuid = 'fake_instance_uuid'
ctx = context.get_admin_context()
type_fields = {}
vol_type = fake_volume.fake_volume_type_obj(ctx, **type_fields)
utc_now = timeutils.utcnow().isoformat()
vol_fields = {
'id': f_uuid[0],
'created_at': utc_now,
'volume_type': vol_type,
'volume_type_id': vol_type.id,
'migration_status': mig_status,
}
vol = fake_volume.fake_volume_obj(ctx, **vol_fields)
sf_vol = self.fake_sfvol
mock_get_sf_volume.return_value = sf_vol
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
sfv.attach_volume(ctx, vol, i_uuid, 'fake_host', '/dev/sdf')
self.assertEqual(sf_vol['attributes']['attached_to'],
i_uuid)
mock_get_sfaccount.assert_called()
mock_get_sf_volume.assert_called()
def test_retype_with_qos_spec(self):
test_type = {'name': 'sf-1',
'qos_specs_id': 'fb0576d7-b4b5-4cad-85dc-ca92e6a497d1',
'deleted': False,
'created_at': '2014-02-06 04:58:11',
'updated_at': None,
'extra_specs': {},
'deleted_at': None,
'id': 'e730e97b-bc7d-4af3-934a-32e59b218e81'}
test_qos_spec = {'id': 'asdfafdasdf',
'specs': {'minIOPS': '1000',
'maxIOPS': '2000',
'burstIOPS': '3000'}}
def _fake_get_volume_type(ctxt, type_id):
return test_type
def _fake_get_qos_spec(ctxt, spec_id):
return test_qos_spec
self.mock_object(solidfire.SolidFireDriver,
'_issue_api_request',
self.fake_issue_api_request)
self.mock_object(volume_types, 'get_volume_type',
_fake_get_volume_type)
self.mock_object(qos_specs, 'get_qos_specs',
_fake_get_qos_spec)
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
diff = {'encryption': {}, 'extra_specs': {},
'qos_specs': {'burstIOPS': ('10000', '2000'),
'minIOPS': ('1000', '500'),
'maxIOPS': ('10000', '1000')}}
host = None
updates = {'project_id': 'testprjid',
'name': 'test_volume',
'size': 1,
'id': 'a720b3c0-d1f0-11e1-9b23-0800200c9a66',
'created_at': timeutils.utcnow()}
ctx = context.get_admin_context()
testvol = fake_volume.fake_volume_obj(ctx, **updates)
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
migrated, updates = sfv.retype(self.ctxt, testvol, test_type,
diff, host)
self.assertTrue(migrated)
self.assertEqual({}, updates)
@mock.patch.object(solidfire.SolidFireDriver, '_get_sfaccount')
@mock.patch.object(solidfire.SolidFireDriver, '_get_sf_volume')
@mock.patch.object(solidfire.SolidFireDriver, '_set_rep_by_volume_type')
@mock.patch.object(solidfire.SolidFireDriver,
'_retrieve_replication_settings')
@mock.patch.object(solidfire.SolidFireDriver, '_get_default_volume_params')
@mock.patch.object(solidfire.SolidFireDriver, '_replicate_volume')
@mock.patch.object(solidfire.SolidFireDriver, '_disable_replication')
@mock.patch.object(solidfire.SolidFireDriver, '_set_qos_by_volume_type')
def test_retype_replicated(self,
mock_set_qos_by_volume_type,
mock_disable_replication,
mock_replicate_volume,
mock_get_default_volume_params,
mock_retrieve_replication_settings,
mock_set_rep_by_volume_type,
mock_get_sf_volume,
mock_get_sfaccount):
all_mocks = locals()
mock_get_sf_volume.return_value = None
mock_get_sfaccount.return_value = self.fake_sfaccount
mock_retrieve_replication_settings.return_value = 'Async'
ctx = context.get_admin_context()
type_fields = {'extra_specs': {'replication_enabled': '<is> True'},
'id': fakes.get_fake_uuid()}
src_vol_type = fake_volume.fake_volume_type_obj(ctx, **type_fields)
fake_provider_id = "%s %s %s" % (
self.fake_sfvol['volumeID'],
fakes.FAKE_UUID,
self.cluster_pairs[0]['uuid'])
utc_now = timeutils.utcnow().isoformat()
vol_fields = {
'id': fakes.FAKE_UUID,
'created_at': utc_now,
'volume_type': src_vol_type,
'volume_type_id': src_vol_type.id,
'provider_id': fake_provider_id
}
vol = fake_volume.fake_volume_obj(ctx, **vol_fields)
dst_vol_type = fake_volume.fake_volume_type_obj(ctx)
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
sfv.replication_enabled = True
sfv.cluster_pairs = self.cluster_pairs
sfv.active_cluster['mvip'] = self.mvip
sfv.active_cluster['svip'] = self.svip
self.assertRaises(exception.VolumeNotFound,
sfv.retype, ctx, vol, dst_vol_type, None, None)
mock_get_sfaccount.assert_called_once_with(vol.project_id)
mock_get_sf_volume.assert_called_once_with(
vol.id, {'accountID': self.fake_sfaccount['accountID']})
mock_get_sfaccount.reset_mock()
mock_get_sf_volume.reset_mock()
expected = {"key": "value"}
mock_get_sf_volume.return_value = self.fake_sfvol
mock_replicate_volume.return_value = expected
mock_set_rep_by_volume_type.side_effect = [src_vol_type, dst_vol_type]
retyped, updates = sfv.retype(ctx, vol, dst_vol_type, None, None)
self.assertDictEqual(expected, updates)
mock_get_sfaccount.assert_called_once_with(vol.project_id)
mock_get_sf_volume.assert_called_once_with(
vol.id, {'accountID': self.fake_sfaccount['accountID']})
mock_get_default_volume_params.assert_called()
mock_disable_replication.assert_not_called()
mock_replicate_volume.assert_called_once()
mock_retrieve_replication_settings.assert_called_once()
mock_set_qos_by_volume_type.assert_called_once()
expected = {}
for mk in all_mocks.values():
if isinstance(mk, mock.MagicMock):
mk.reset_mock()
mock_set_rep_by_volume_type.side_effect = [src_vol_type, None]
retyped, updates = sfv.retype(ctx, vol, dst_vol_type, None, None)
self.assertDictEqual(expected, updates)
mock_get_sfaccount.assert_called_once_with(vol.project_id)
mock_get_sf_volume.assert_called_once_with(
vol.id, {'accountID': self.fake_sfaccount['accountID']})
mock_get_default_volume_params.assert_not_called()
mock_disable_replication.assert_called_with(vol)
mock_replicate_volume.assert_not_called()
mock_retrieve_replication_settings.assert_not_called()
mock_set_qos_by_volume_type.assert_called_once()
def test_update_cluster_status(self):
self.mock_object(solidfire.SolidFireDriver,
'_issue_api_request',
self.fake_issue_api_request)
driver_defined_stats = ['volume_backend_name', 'vendor_name',
'driver_version', 'storage_protocol',
'consistencygroup_support',
'consistent_group_snapshot_enabled',
'replication_enabled', 'active_cluster_mvip',
'reserved_percentage', 'QoS_support',
'multiattach', 'total_capacity_gb',
'free_capacity_gb', 'compression_percent',
'deduplicaton_percent',
'thin_provision_percent', 'provisioned_iops',
'current_iops', 'average_iops', 'max_iops',
'peak_iops']
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
sfv._update_cluster_status()
for key in driver_defined_stats:
if sfv.cluster_stats.get(key, None) is None:
msg = 'Key %s should be present at driver stats.' % key
raise exception.CinderException(message=msg)
sfv.configuration.sf_provisioning_calc = 'usedSpace'
sfv._update_cluster_status()
driver_defined_stats += ['thin_provisioning_support',
'provisioned_capacity_gb',
'max_over_subscription_ratio']
for key in driver_defined_stats:
self.assertIn(key, driver_defined_stats)
def test_update_cluster_status_mvip_unreachable(self):
self.mock_object(solidfire.SolidFireDriver,
'_issue_api_request',
self.fake_issue_api_request)
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
with mock.patch.object(sfv,
'_issue_api_request',
side_effect=self.fake_issue_api_request_fails):
sfv._update_cluster_status()
self.assertEqual(0, sfv.cluster_stats['free_capacity_gb'])
self.assertEqual(0, sfv.cluster_stats['total_capacity_gb'])
def test_manage_existing_volume(self):
external_ref = {'name': 'existing volume', 'source-id': 5}
updates = {'project_id': 'testprjid',
'name': 'testvol',
'size': 1,
'id': 'a720b3c0-d1f0-11e1-9b23-0800200c9a66',
'created_at': timeutils.utcnow()}
ctx = context.get_admin_context()
testvol = fake_volume.fake_volume_obj(ctx, **updates)
self.mock_object(solidfire.SolidFireDriver,
'_issue_api_request',
self.fake_issue_api_request)
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
model_update = sfv.manage_existing(testvol, external_ref)
self.assertIsNotNone(model_update)
self.assertNotIn('provider_geometry', model_update)
def test_manage_existing_get_size(self):
external_ref = {'name': 'existing volume', 'source-id': 5}
testvol = {'project_id': 'testprjid',
'name': 'testvol',
'size': 1,
'id': 'a720b3c0-d1f0-11e1-9b23-0800200c9a66',
'created_at': timeutils.utcnow()}
mock_issue_api_request = self.mock_object(solidfire.SolidFireDriver,
'_issue_api_request')
mock_issue_api_request.side_effect = self.fake_issue_api_request
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
size = sfv.manage_existing_get_size(testvol, external_ref)
self.assertEqual(2, size)
@mock.patch.object(solidfire.SolidFireDriver, '_issue_api_request')
@mock.patch.object(solidfire.SolidFireDriver, '_get_create_account')
@mock.patch.object(solidfire.SolidFireDriver, '_get_default_volume_params')
@mock.patch.object(solidfire.SolidFireDriver,
'_retrieve_replication_settings')
@mock.patch.object(solidfire.SolidFireDriver, '_replicate_volume')
@mock.patch.object(solidfire.SolidFireDriver, '_get_model_info')
@mock.patch.object(solidfire.SolidFireDriver, '_update_cluster_status')
@mock.patch.object(solidfire.SolidFireDriver, '_create_cluster_reference')
def test_manage_existing_replicated_fail(
self,
mock_create_cluster_reference,
mock_update_cluster_status,
mock_get_model_info,
mock_replicate_volume,
mock_retrieve_replication_settings,
mock_get_default_volume_params,
mock_get_create_account,
mock_issue_api_request):
mock_retrieve_replication_settings.return_value = 'Async'
mock_get_default_volume_params.return_value = {'totalSize': 50}
mock_get_create_account.return_value = self.fake_sfaccount
mock_replicate_volume.side_effect = solidfire.SolidFireAPIException
ctx = context.get_admin_context()
type_fields = {'extra_specs': {'replication_enabled': '<is> True'},
'id': fakes.get_fake_uuid()}
vol_type = fake_volume.fake_volume_type_obj(ctx, **type_fields)
fake_provider_id = "%s %s %s" % (
self.fake_sfvol['volumeID'],
fakes.FAKE_UUID,
self.cluster_pairs[0]['uuid'])
utc_now = timeutils.utcnow().isoformat()
vol_fields = {
'id': fakes.FAKE_UUID,
'created_at': utc_now,
'volume_type': vol_type,
'volume_type_id': vol_type.id,
'provider_id': fake_provider_id
}
vol = fake_volume.fake_volume_obj(ctx, **vol_fields)
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
sfv.replication_enabled = True
sfv.active_cluster['mvip'] = self.mvip
sfv.active_cluster['svip'] = self.svip
external_ref = {}
self.assertRaises(solidfire.SolidFireAPIException,
sfv.manage_existing, vol, external_ref)
self.fake_sfvol['volumePairs'] = [{'remoteVolumeID': 26}]
mock_issue_api_request.return_value = {
'result': {'volumes': [self.fake_sfvol]}}
external_ref = {'source-id': 6, 'name': 'new-being-managed'}
self.assertRaises(solidfire.SolidFireDriverException,
sfv.manage_existing, vol, external_ref)
mock_get_default_volume_params.return_value = {'totalSize': 50}
self.fake_sfvol['volumePairs'] = []
mock_issue_api_request.return_value = {
'result': {'volumes': [self.fake_sfvol]}}
self.assertRaises(solidfire.SolidFireAPIException,
sfv.manage_existing, vol, external_ref)
modify_attributes = {'uuid': vol.id,
'is_clone': 'False',
'os_imported_at': utc_now + "+00:00",
'old_name': 'new-being-managed'}
modify_params1 = {'volumeID': self.fake_sfvol['volumeID'],
'attributes': modify_attributes}
modify_params2 = {'volumeID': self.fake_sfvol['volumeID'],
'attributes': self.fake_sfvol['attributes']}
calls = [mock.call('ListActiveVolumes',
{'startVolumeID': self.fake_sfvol['volumeID'],
'limit': 1}),
mock.call('ModifyVolume', modify_params1, version='5.0'),
mock.call('ModifyVolume', modify_params2, version='5.0')]
mock_issue_api_request.assert_has_calls(calls)
mock_get_model_info.assert_not_called()
mock_create_cluster_reference.assert_called_once()
mock_update_cluster_status.assert_called_once()
mock_replicate_volume.assert_called()
mock_retrieve_replication_settings.assert_called_with(vol)
mock_get_default_volume_params.assert_called_with(vol)
mock_get_create_account.assert_called_with(vol.project_id)
@mock.patch.object(solidfire.SolidFireDriver, '_get_sfaccount')
@mock.patch.object(solidfire.SolidFireDriver, '_get_sf_volume')
@mock.patch.object(solidfire.SolidFireDriver, '_set_rep_by_volume_type')
@mock.patch.object(solidfire.SolidFireDriver,
'_retrieve_replication_settings')
@mock.patch.object(solidfire.SolidFireDriver, '_get_default_volume_params')
@mock.patch.object(solidfire.SolidFireDriver, '_replicate_volume')
@mock.patch.object(solidfire.SolidFireDriver, '_disable_replication')
@mock.patch.object(solidfire.SolidFireDriver, '_set_qos_by_volume_type')
def test_manage_existing_replicated(
self,
mock_set_qos_by_volume_type,
mock_disable_replication,
mock_replicate_volume,
mock_get_default_volume_params,
mock_retrieve_replication_settings,
mock_set_rep_by_volume_type,
mock_get_sf_volume,
mock_get_sfaccount):
mock_get_sf_volume.return_value = None
mock_get_sfaccount.return_value = self.fake_sfaccount
mock_retrieve_replication_settings.return_value = 'Async'
ctx = context.get_admin_context()
type_fields = {'extra_specs': {'replication_enabled': '<is> True'},
'id': fakes.get_fake_uuid()}
src_vol_type = fake_volume.fake_volume_type_obj(ctx, **type_fields)
fake_provider_id = "%s %s %s" % (
self.fake_sfvol['volumeID'],
fakes.FAKE_UUID,
self.cluster_pairs[0]['uuid'])
utc_now = timeutils.utcnow().isoformat()
vol_fields = {
'id': fakes.FAKE_UUID,
'created_at': utc_now,
'volume_type': src_vol_type,
'volume_type_id': src_vol_type.id,
'provider_id': fake_provider_id
}
vol = fake_volume.fake_volume_obj(ctx, **vol_fields)
dst_vol_type = fake_volume.fake_volume_type_obj(ctx)
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
sfv.replication_enabled = True
sfv.cluster_pairs = self.cluster_pairs
sfv.active_cluster['mvip'] = self.mvip
sfv.active_cluster['svip'] = self.svip
self.assertRaises(exception.VolumeNotFound,
sfv.retype, ctx, vol, dst_vol_type, None, None)
mock_get_sfaccount.assert_called_once_with(vol.project_id)
mock_get_sf_volume.assert_called_once_with(
vol.id, {'accountID': self.fake_sfaccount['accountID']})
mock_get_sfaccount.reset_mock()
mock_get_sf_volume.reset_mock()
expected = {"key": "value"}
mock_get_sf_volume.return_value = self.fake_sfvol
mock_replicate_volume.return_value = expected
mock_set_rep_by_volume_type.side_effect = [src_vol_type, dst_vol_type]
retyped, updates = sfv.retype(ctx, vol, dst_vol_type, None, None)
self.assertDictEqual(expected, updates)
mock_get_sfaccount.assert_called_once_with(vol.project_id)
mock_get_sf_volume.assert_called_once_with(
vol.id, {'accountID': self.fake_sfaccount['accountID']})
mock_get_default_volume_params.assert_called()
mock_disable_replication.assert_not_called()
mock_replicate_volume.assert_called_once()
mock_retrieve_replication_settings.assert_called_once()
mock_set_qos_by_volume_type.assert_called_once()
mock_set_rep_by_volume_type.assert_called()
@mock.patch.object(solidfire.SolidFireDriver, '_issue_api_request')
def test_create_volume_for_migration(self,
_mock_issue_api_request):
_mock_issue_api_request.side_effect = self.fake_issue_api_request
testvol = {'project_id': 'testprjid',
'name': 'testvol',
'size': 1,
'id': 'b830b3c0-d1f0-11e1-9b23-1900200c9a77',
'volume_type_id': None,
'created_at': timeutils.utcnow(),
'migration_status': 'target:'
'a720b3c0-d1f0-11e1-9b23-0800200c9a66'}
ctx = context.get_admin_context()
testvol = fake_volume.fake_volume_obj(ctx, **testvol)
fake_sfaccounts = [{'accountID': 5,
'targetSecret': 'shhhh',
'username': 'prefix-testprjid'}]
def _fake_do_v_create(project_id, params):
cvol = {
'name': 'UUID-a720b3c0-d1f0-11e1-9b23-0800200c9a66',
'attributes': {
'uuid': 'a720b3c0-d1f0-11e1-9b23-0800200c9a66',
'migration_uuid': 'b830b3c0-d1f0-11e1-9b23-1900200c9a77'
}
}
return cvol
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
with mock.patch.object(sfv,
'_get_sfaccounts_for_tenant',
return_value=fake_sfaccounts), \
mock.patch.object(sfv,
'_get_account_create_availability',
return_value=fake_sfaccounts[0]), \
mock.patch.object(sfv,
'_do_volume_create',
side_effect=_fake_do_v_create):
sf_vol_object = sfv.create_volume(testvol)
self.assertEqual('a720b3c0-d1f0-11e1-9b23-0800200c9a66',
sf_vol_object['attributes']['uuid'])
self.assertEqual('b830b3c0-d1f0-11e1-9b23-1900200c9a77',
sf_vol_object['attributes']['migration_uuid'])
self.assertEqual('UUID-a720b3c0-d1f0-11e1-9b23-0800200c9a66',
sf_vol_object['name'])
@mock.patch.object(solidfire.SolidFireDriver, '_issue_api_request')
def test_clone_image_not_configured(self, _mock_issue_api_request):
_mock_issue_api_request.side_effect = self.fake_issue_api_request
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
self.assertEqual((None, False),
sfv.clone_image(self.ctxt,
self.mock_volume,
'fake',
self.fake_image_meta,
'fake'))
def test_init_volume_mappings(self):
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
vid_1 = 'c9125d6d-22ff-4cc3-974d-d4e350df9c91'
vid_2 = '79883868-6933-47a1-a362-edfbf8d55a18'
sid_1 = 'e3caa4fa-485e-45ca-970e-1d3e693a2520'
project_1 = 'e6fb073c-11f0-4f4c-897c-90e7c7c4bcf8'
project_2 = '4ff32607-305c-4a6b-a51a-0dd33124eecf'
vrefs = [{'id': vid_1,
'project_id': project_1,
'provider_id': None},
{'id': vid_2,
'project_id': project_2,
'provider_id': 22}]
snaprefs = [{'id': sid_1,
'project_id': project_1,
'provider_id': None,
'volume_id': vid_1}]
sf_vols = [{'volumeID': 99,
'name': 'UUID-' + vid_1,
'accountID': 100},
{'volumeID': 22,
'name': 'UUID-' + vid_2,
'accountID': 200}]
sf_snaps = [{'snapshotID': 1,
'name': 'UUID-' + sid_1,
'volumeID': 99}]
def _fake_issue_api_req(method, params, version=0):
if 'ListActiveVolumes' in method:
return {'result': {'volumes': sf_vols}}
if 'ListSnapshots'in method:
return {'result': {'snapshots': sf_snaps}}
with mock.patch.object(sfv, '_issue_api_request',
side_effect=_fake_issue_api_req):
volume_updates, snapshot_updates = sfv.update_provider_info(
vrefs, snaprefs)
self.assertEqual('99 100 53c8be1e-89e2-4f7f-a2e3-7cb84c47e0ec',
volume_updates[0]['provider_id'])
self.assertEqual(1, len(volume_updates))
self.assertEqual('1 99 53c8be1e-89e2-4f7f-a2e3-7cb84c47e0ec',
snapshot_updates[0]['provider_id'])
self.assertEqual(1, len(snapshot_updates))
def test_get_sf_volume_missing_attributes(self):
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
test_name = "existing_volume"
fake_response = {'result': {
'volumes': [{'volumeID': 5,
'name': test_name,
'accountID': 8,
'sliceCount': 1,
'totalSize': 1 * units.Gi,
'enable512e': True,
'access': "readWrite",
'status': "active",
'qos': None,
'iqn': test_name}]}}
def _fake_issue_api_req(method, params, version=0, endpoint=None):
return fake_response
with mock.patch.object(
sfv, '_issue_api_request', side_effect=_fake_issue_api_req):
self.assertEqual(5, sfv._get_sf_volume(test_name, 8)['volumeID'])
def test_sf_init_conn_with_vag(self):
# Verify with the _enable_vag conf set that we correctly create a VAG.
mod_conf = self.configuration
mod_conf.sf_enable_vag = True
sfv = solidfire.SolidFireDriver(configuration=mod_conf)
testvol = {'project_id': 'testprjid',
'name': 'testvol',
'size': 1,
'id': 'a720b3c0-d1f0-11e1-9b23-0800200c9a66',
'volume_type_id': None,
'provider_location': '10.10.7.1:3260 iqn.2010-01.com.'
'solidfire:87hg.uuid-2cc06226-cc'
'74-4cb7-bd55-14aed659a0cc.4060 0',
'provider_auth': 'CHAP stack-1-a60e2611875f40199931f2'
'c76370d66b 2FE0CQ8J196R',
'provider_geometry': '4096 4096',
'created_at': timeutils.utcnow(),
'provider_id': "1 1 1"
}
connector = {'initiator': 'iqn.2012-07.org.fake:01'}
provider_id = testvol['provider_id']
vol_id = int(provider_id.split()[0])
vag_id = 1
with mock.patch.object(sfv,
'_safe_create_vag',
return_value=vag_id) as create_vag, \
mock.patch.object(sfv,
'_add_volume_to_vag') as add_vol:
sfv._sf_initialize_connection(testvol, connector)
create_vag.assert_called_with(connector['initiator'],
vol_id)
add_vol.assert_called_with(vol_id,
connector['initiator'],
vag_id)
def test_sf_term_conn_with_vag_rem_vag(self):
# Verify we correctly remove an empty VAG on detach.
mod_conf = self.configuration
mod_conf.sf_enable_vag = True
sfv = solidfire.SolidFireDriver(configuration=mod_conf)
testvol = {'project_id': 'testprjid',
'name': 'testvol',
'size': 1,
'id': 'a720b3c0-d1f0-11e1-9b23-0800200c9a66',
'volume_type_id': None,
'provider_location': '10.10.7.1:3260 iqn.2010-01.com.'
'solidfire:87hg.uuid-2cc06226-cc'
'74-4cb7-bd55-14aed659a0cc.4060 0',
'provider_auth': 'CHAP stack-1-a60e2611875f40199931f2'
'c76370d66b 2FE0CQ8J196R',
'provider_geometry': '4096 4096',
'created_at': timeutils.utcnow(),
'provider_id': "1 1 1",
'multiattach': False
}
connector = {'initiator': 'iqn.2012-07.org.fake:01'}
vag_id = 1
vags = [{'attributes': {},
'deletedVolumes': [],
'initiators': [connector['initiator']],
'name': 'fakeiqn',
'volumeAccessGroupID': vag_id,
'volumes': [1],
'virtualNetworkIDs': []}]
with mock.patch.object(sfv,
'_get_vags_by_name',
return_value=vags), \
mock.patch.object(sfv,
'_remove_vag') as rem_vag:
sfv._sf_terminate_connection(testvol, connector, False)
rem_vag.assert_called_with(vag_id)
def test_sf_term_conn_with_vag_rem_vol(self):
# Verify we correctly remove a the volume from a non-empty VAG.
mod_conf = self.configuration
mod_conf.sf_enable_vag = True
sfv = solidfire.SolidFireDriver(configuration=mod_conf)
testvol = {'project_id': 'testprjid',
'name': 'testvol',
'size': 1,
'id': 'a720b3c0-d1f0-11e1-9b23-0800200c9a66',
'volume_type_id': None,
'provider_location': '10.10.7.1:3260 iqn.2010-01.com.'
'solidfire:87hg.uuid-2cc06226-cc'
'74-4cb7-bd55-14aed659a0cc.4060 0',
'provider_auth': 'CHAP stack-1-a60e2611875f40199931f2'
'c76370d66b 2FE0CQ8J196R',
'provider_geometry': '4096 4096',
'created_at': timeutils.utcnow(),
'provider_id': "1 1 1",
'multiattach': False
}
provider_id = testvol['provider_id']
vol_id = int(provider_id.split()[0])
connector = {'initiator': 'iqn.2012-07.org.fake:01'}
vag_id = 1
vags = [{'attributes': {},
'deletedVolumes': [],
'initiators': [connector['initiator']],
'name': 'fakeiqn',
'volumeAccessGroupID': vag_id,
'volumes': [1, 2],
'virtualNetworkIDs': []}]
with mock.patch.object(sfv,
'_get_vags_by_name',
return_value=vags), \
mock.patch.object(sfv,
'_remove_volume_from_vag') as rem_vag:
sfv._sf_terminate_connection(testvol, connector, False)
rem_vag.assert_called_with(vol_id, vag_id)
def test_sf_term_conn_without_connector(self):
# Verify we correctly force the deletion of a volume.
mod_conf = self.configuration
mod_conf.sf_enable_vag = True
sfv = solidfire.SolidFireDriver(configuration=mod_conf)
testvol = {'project_id': 'testprjid',
'name': 'testvol',
'size': 1,
'id': 'a720b3c0-d1f0-11e1-9b23-0800200c9a66',
'volume_type_id': None,
'provider_location': '10.10.7.1:3260 iqn.2010-01.com.'
'solidfire:87hg.uuid-2cc06226-cc'
'74-4cb7-bd55-14aed659a0cc.4060 0',
'provider_auth': 'CHAP stack-1-a60e2611875f40199931f2'
'c76370d66b 2FE0CQ8J196R',
'provider_geometry': '4096 4096',
'created_at': timeutils.utcnow(),
'provider_id': "1 1 1",
'multiattach': False
}
provider_id = testvol['provider_id']
vol_id = int(provider_id.split()[0])
vag_id = 1
vags = [{'attributes': {},
'deletedVolumes': [],
'initiators': ['iqn.2012-07.org.fake:01'],
'name': 'fakeiqn',
'volumeAccessGroupID': vag_id,
'volumes': [1, 2],
'virtualNetworkIDs': []}]
with mock.patch.object(sfv,
'_get_vags_by_volume',
return_value=vags), \
mock.patch.object(sfv,
'_remove_volume_from_vags') as rem_vags:
sfv._sf_terminate_connection(testvol, None, False)
rem_vags.assert_called_with(vol_id)
def test_safe_create_vag_simple(self):
# Test the sunny day call straight into _create_vag.
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
iqn = 'fake_iqn'
vol_id = 1
with mock.patch.object(sfv,
'_get_vags_by_name',
return_value=[]), \
mock.patch.object(sfv,
'_create_vag') as mock_create_vag:
sfv._safe_create_vag(iqn, vol_id)
mock_create_vag.assert_called_with(iqn, vol_id)
def test_safe_create_vag_matching_vag(self):
# Vag exists, resuse.
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
iqn = 'TESTIQN'
vags = [{'attributes': {},
'deletedVolumes': [],
'initiators': [iqn],
'name': iqn,
'volumeAccessGroupID': 1,
'volumes': [1, 2],
'virtualNetworkIDs': []}]
with mock.patch.object(sfv,
'_get_vags_by_name',
return_value=vags), \
mock.patch.object(sfv,
'_create_vag') as create_vag, \
mock.patch.object(sfv,
'_add_initiator_to_vag') as add_iqn:
vag_id = sfv._safe_create_vag(iqn, None)
self.assertEqual(vag_id, vags[0]['volumeAccessGroupID'])
create_vag.assert_not_called()
add_iqn.assert_not_called()
def test_safe_create_vag_reuse_vag(self):
# Reuse a matching vag.
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
iqn = 'TESTIQN'
vags = [{'attributes': {},
'deletedVolumes': [],
'initiators': [],
'name': iqn,
'volumeAccessGroupID': 1,
'volumes': [1, 2],
'virtualNetworkIDs': []}]
vag_id = vags[0]['volumeAccessGroupID']
with mock.patch.object(sfv,
'_get_vags_by_name',
return_value=vags), \
mock.patch.object(sfv,
'_add_initiator_to_vag',
return_value=vag_id) as add_init:
res_vag_id = sfv._safe_create_vag(iqn, None)
self.assertEqual(res_vag_id, vag_id)
add_init.assert_called_with(iqn, vag_id)
def test_create_vag_iqn_fail(self):
# Attempt to create a VAG with an already in-use initiator.
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
iqn = 'TESTIQN'
vag_id = 1
vol_id = 42
def throw_request(method, params, version):
msg = 'xExceededLimit: {}'.format(params['initiators'][0])
raise solidfire.SolidFireAPIException(message=msg)
with mock.patch.object(sfv,
'_issue_api_request',
side_effect=throw_request), \
mock.patch.object(sfv,
'_safe_create_vag',
return_value=vag_id) as create_vag, \
mock.patch.object(sfv,
'_purge_vags') as purge_vags:
res_vag_id = sfv._create_vag(iqn, vol_id)
self.assertEqual(res_vag_id, vag_id)
create_vag.assert_called_with(iqn, vol_id)
purge_vags.assert_not_called()
def test_create_vag_limit_fail(self):
# Attempt to create a VAG with VAG limit reached.
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
iqn = 'TESTIQN'
vag_id = 1
vol_id = 42
def throw_request(method, params, version):
msg = 'xExceededLimit'
raise solidfire.SolidFireAPIException(message=msg)
with mock.patch.object(sfv,
'_issue_api_request',
side_effect=throw_request), \
mock.patch.object(sfv,
'_safe_create_vag',
return_value=vag_id) as create_vag, \
mock.patch.object(sfv,
'_purge_vags') as purge_vags:
res_vag_id = sfv._create_vag(iqn, vol_id)
self.assertEqual(res_vag_id, vag_id)
create_vag.assert_called_with(iqn, vol_id)
purge_vags.assert_called_with()
def test_add_initiator_duplicate(self):
# Thrown exception should yield vag_id.
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
iqn = 'TESTIQN'
vag_id = 1
def throw_request(method, params, version):
msg = 'xAlreadyInVolumeAccessGroup'
raise solidfire.SolidFireAPIException(message=msg)
with mock.patch.object(sfv,
'_issue_api_request',
side_effect=throw_request):
res_vag_id = sfv._add_initiator_to_vag(iqn, vag_id)
self.assertEqual(vag_id, res_vag_id)
def test_add_initiator_missing_vag(self):
# Thrown exception should result in create_vag call.
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
iqn = 'TESTIQN'
vag_id = 1
def throw_request(method, params, version):
msg = 'xVolumeAccessGroupIDDoesNotExist'
raise solidfire.SolidFireAPIException(message=msg)
with mock.patch.object(sfv,
'_issue_api_request',
side_effect=throw_request), \
mock.patch.object(sfv,
'_safe_create_vag',
return_value=vag_id) as mock_create_vag:
res_vag_id = sfv._add_initiator_to_vag(iqn, vag_id)
self.assertEqual(vag_id, res_vag_id)
mock_create_vag.assert_called_with(iqn)
def test_add_volume_to_vag_duplicate(self):
# Thrown exception should yield vag_id
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
iqn = 'TESTIQN'
vag_id = 1
vol_id = 42
def throw_request(method, params, version):
msg = 'xAlreadyInVolumeAccessGroup'
raise solidfire.SolidFireAPIException(message=msg)
with mock.patch.object(sfv,
'_issue_api_request',
side_effect=throw_request):
res_vag_id = sfv._add_volume_to_vag(vol_id, iqn, vag_id)
self.assertEqual(res_vag_id, vag_id)
def test_add_volume_to_vag_missing_vag(self):
# Thrown exception should yield vag_id
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
iqn = 'TESTIQN'
vag_id = 1
vol_id = 42
def throw_request(method, params, version):
msg = 'xVolumeAccessGroupIDDoesNotExist'
raise solidfire.SolidFireAPIException(message=msg)
with mock.patch.object(sfv,
'_issue_api_request',
side_effect=throw_request), \
mock.patch.object(sfv,
'_safe_create_vag',
return_value=vag_id) as mock_create_vag:
res_vag_id = sfv._add_volume_to_vag(vol_id, iqn, vag_id)
self.assertEqual(res_vag_id, vag_id)
mock_create_vag.assert_called_with(iqn, vol_id)
def test_remove_volume_from_vag_missing_volume(self):
# Volume not in VAG, throws.
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
vag_id = 1
vol_id = 42
def throw_request(method, params, version):
msg = 'xNotInVolumeAccessGroup'
raise solidfire.SolidFireAPIException(message=msg)
with mock.patch.object(sfv,
'_issue_api_request',
side_effect=throw_request):
sfv._remove_volume_from_vag(vol_id, vag_id)
def test_remove_volume_from_vag_missing_vag(self):
# Volume not in VAG, throws.
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
vag_id = 1
vol_id = 42
def throw_request(method, params, version):
msg = 'xVolumeAccessGroupIDDoesNotExist'
raise solidfire.SolidFireAPIException(message=msg)
with mock.patch.object(sfv,
'_issue_api_request',
side_effect=throw_request):
sfv._remove_volume_from_vag(vol_id, vag_id)
def test_remove_volume_from_vag_unknown_exception(self):
# Volume not in VAG, throws.
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
vag_id = 1
vol_id = 42
def throw_request(method, params, version):
msg = 'xUnknownException'
raise solidfire.SolidFireAPIException(message=msg)
with mock.patch.object(sfv,
'_issue_api_request',
side_effect=throw_request):
self.assertRaises(solidfire.SolidFireAPIException,
sfv._remove_volume_from_vag,
vol_id,
vag_id)
def test_remove_volume_from_vags(self):
# Remove volume from several VAGs.
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
vol_id = 42
vags = [{'volumeAccessGroupID': 1,
'volumes': [vol_id]},
{'volumeAccessGroupID': 2,
'volumes': [vol_id, 43]}]
with mock.patch.object(sfv,
'_get_vags_by_volume',
return_value=vags), \
mock.patch.object(sfv,
'_remove_volume_from_vag') as rem_vol:
sfv._remove_volume_from_vags(vol_id)
self.assertEqual(len(vags), rem_vol.call_count)
def test_purge_vags(self):
# Remove subset of VAGs.
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
vags = [{'initiators': [],
'volumeAccessGroupID': 1,
'deletedVolumes': [],
'volumes': [],
'attributes': {'openstack': True}},
{'initiators': [],
'volumeAccessGroupID': 2,
'deletedVolumes': [],
'volumes': [],
'attributes': {'openstack': False}},
{'initiators': [],
'volumeAccessGroupID': 3,
'deletedVolumes': [1],
'volumes': [],
'attributes': {'openstack': True}},
{'initiators': [],
'volumeAccessGroupID': 4,
'deletedVolumes': [],
'volumes': [1],
'attributes': {'openstack': True}},
{'initiators': ['fakeiqn'],
'volumeAccessGroupID': 5,
'deletedVolumes': [],
'volumes': [],
'attributes': {'openstack': True}}]
with mock.patch.object(sfv,
'_base_get_vags',
return_value=vags), \
mock.patch.object(sfv,
'_remove_vag') as rem_vag:
sfv._purge_vags()
# Of the vags provided there is only one that is valid for purge
# based on the limits of no initiators, volumes, deleted volumes,
# and features the openstack attribute.
self.assertEqual(1, rem_vag.call_count)
rem_vag.assert_called_with(1)
def test_sf_create_group_snapshot(self):
# Sunny day group snapshot creation.
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
name = 'great_gsnap_name'
sf_volumes = [{'volumeID': 1}, {'volumeID': 42}]
expected_params = {'name': name,
'volumes': [1, 42]}
fake_result = {'result': 'contrived_test'}
with mock.patch.object(sfv,
'_issue_api_request',
return_value=fake_result) as fake_api:
res = sfv._sf_create_group_snapshot(name, sf_volumes)
self.assertEqual('contrived_test', res)
fake_api.assert_called_with('CreateGroupSnapshot',
expected_params,
version='7.0')
def test_group_snapshot_creator_sunny(self):
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
gsnap_name = 'great_gsnap_name'
prefix = sfv.configuration.sf_volume_prefix
vol_uuids = ['one', 'two', 'three']
active_vols = [{'name': prefix + 'one'},
{'name': prefix + 'two'},
{'name': prefix + 'three'}]
with mock.patch.object(sfv,
'_get_all_active_volumes',
return_value=active_vols),\
mock.patch.object(sfv,
'_sf_create_group_snapshot',
return_value=None) as create:
sfv._group_snapshot_creator(gsnap_name, vol_uuids)
create.assert_called_with(gsnap_name,
active_vols)
def test_group_snapshot_creator_rainy(self):
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
gsnap_name = 'great_gsnap_name'
prefix = sfv.configuration.sf_volume_prefix
vol_uuids = ['one', 'two', 'three']
active_vols = [{'name': prefix + 'one'},
{'name': prefix + 'two'}]
with mock.patch.object(sfv,
'_get_all_active_volumes',
return_value=active_vols):
self.assertRaises(solidfire.SolidFireDriverException,
sfv._group_snapshot_creator,
gsnap_name,
vol_uuids)
def test_create_temp_group_snapshot(self):
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
cg = {'id': 'great_gsnap_name'}
prefix = sfv.configuration.sf_volume_prefix
tmp_name = prefix + cg['id'] + '-tmp'
vols = [{'id': 'one'},
{'id': 'two'},
{'id': 'three'}]
with mock.patch.object(sfv,
'_group_snapshot_creator',
return_value=None) as create:
sfv._create_temp_group_snapshot(cg, vols)
create.assert_called_with(tmp_name, ['one', 'two', 'three'])
def test_list_group_snapshots(self):
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
res = {'result': {'groupSnapshots': 'a_thing'}}
with mock.patch.object(sfv,
'_issue_api_request',
return_value=res):
result = sfv._list_group_snapshots()
self.assertEqual('a_thing', result)
def test_get_group_snapshot_by_name(self):
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
fake_snaps = [{'name': 'a_fantastic_name'}]
with mock.patch.object(sfv,
'_list_group_snapshots',
return_value=fake_snaps):
result = sfv._get_group_snapshot_by_name('a_fantastic_name')
self.assertEqual(fake_snaps[0], result)
def test_delete_group_snapshot(self):
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
gsnap_id = 1
with mock.patch.object(sfv,
'_issue_api_request') as api_req:
sfv._delete_group_snapshot(gsnap_id)
api_req.assert_called_with('DeleteGroupSnapshot',
{'groupSnapshotID': gsnap_id},
version='7.0')
def test_delete_cgsnapshot_by_name(self):
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
fake_gsnap = {'groupSnapshotID': 42}
with mock.patch.object(sfv,
'_get_group_snapshot_by_name',
return_value=fake_gsnap),\
mock.patch.object(sfv,
'_delete_group_snapshot') as del_stuff:
sfv._delete_cgsnapshot_by_name('does not matter')
del_stuff.assert_called_with(fake_gsnap['groupSnapshotID'])
def test_delete_cgsnapshot_by_name_rainy(self):
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
with mock.patch.object(sfv, '_get_group_snapshot_by_name',
return_value=None):
self.assertRaises(solidfire.SolidFireDriverException,
sfv._delete_cgsnapshot_by_name,
'does not matter')
def test_find_linked_snapshot(self):
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
group_snap = {'members': [{'volumeID': 1}, {'volumeID': 2}]}
source_vol = {'volumeID': 1}
with mock.patch.object(sfv,
'_get_sf_volume',
return_value=source_vol) as get_vol:
res = sfv._find_linked_snapshot('fake_uuid', group_snap)
self.assertEqual(source_vol, res)
get_vol.assert_called_with('fake_uuid')
def test_create_consisgroup_from_src_cgsnapshot(self):
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
ctxt = None
group = {}
volumes = [{'id': 'one'}, {'id': 'two'}, {'id': 'three'}]
cgsnapshot = {'id': 'great_uuid'}
snapshots = [{'id': 'snap_id_1', 'volume_id': 'one'},
{'id': 'snap_id_2', 'volume_id': 'two'},
{'id': 'snap_id_3', 'volume_id': 'three'}]
source_cg = None
source_vols = None
group_snap = {}
name = sfv.configuration.sf_volume_prefix + cgsnapshot['id']
kek = (None, None, {})
with mock.patch.object(sfv,
'_get_group_snapshot_by_name',
return_value=group_snap) as get_snap,\
mock.patch.object(sfv,
'_find_linked_snapshot'),\
mock.patch.object(sfv,
'_do_clone_volume',
return_value=kek):
model, vol_models = sfv._create_consistencygroup_from_src(
ctxt, group, volumes,
cgsnapshot, snapshots,
source_cg, source_vols)
get_snap.assert_called_with(name)
self.assertEqual(
{'status': fields.GroupStatus.AVAILABLE}, model)
def test_create_consisgroup_from_src_source_cg(self):
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
ctxt = None
group = {}
volumes = [{'id': 'one', 'source_volid': 'source_one'},
{'id': 'two', 'source_volid': 'source_two'},
{'id': 'three', 'source_volid': 'source_three'}]
cgsnapshot = {'id': 'great_uuid'}
snapshots = None
source_cg = {'id': 'fantastic_cg'}
source_vols = [1, 2, 3]
source_snap = None
group_snap = {}
kek = (None, None, {})
with mock.patch.object(sfv,
'_create_temp_group_snapshot',
return_value=source_cg['id']),\
mock.patch.object(sfv,
'_get_group_snapshot_by_name',
return_value=group_snap) as get_snap,\
mock.patch.object(sfv,
'_find_linked_snapshot',
return_value=source_snap),\
mock.patch.object(sfv,
'_do_clone_volume',
return_value=kek),\
mock.patch.object(sfv,
'_delete_cgsnapshot_by_name'):
model, vol_models = sfv._create_consistencygroup_from_src(
ctxt, group, volumes,
cgsnapshot, snapshots,
source_cg,
source_vols)
get_snap.assert_called_with(source_cg['id'])
self.assertEqual(
{'status': fields.GroupStatus.AVAILABLE}, model)
def test_create_cgsnapshot(self):
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
ctxt = None
cgsnapshot = {'id': 'acceptable_cgsnap_id'}
snapshots = [{'volume_id': 'one'},
{'volume_id': 'two'}]
pfx = sfv.configuration.sf_volume_prefix
active_vols = [{'name': pfx + 'one'},
{'name': pfx + 'two'}]
with mock.patch.object(sfv,
'_get_all_active_volumes',
return_value=active_vols),\
mock.patch.object(sfv,
'_sf_create_group_snapshot') as create_gsnap:
sfv._create_cgsnapshot(ctxt, cgsnapshot, snapshots)
create_gsnap.assert_called_with(pfx + cgsnapshot['id'],
active_vols)
def test_create_cgsnapshot_rainy(self):
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
ctxt = None
cgsnapshot = {'id': 'acceptable_cgsnap_id'}
snapshots = [{'volume_id': 'one'},
{'volume_id': 'two'}]
pfx = sfv.configuration.sf_volume_prefix
active_vols = [{'name': pfx + 'one'}]
with mock.patch.object(sfv,
'_get_all_active_volumes',
return_value=active_vols),\
mock.patch.object(sfv,
'_sf_create_group_snapshot'):
self.assertRaises(solidfire.SolidFireDriverException,
sfv._create_cgsnapshot,
ctxt,
cgsnapshot,
snapshots)
def test_create_vol_from_cgsnap(self):
# cgsnaps on the backend yield numerous identically named snapshots.
# create_volume_from_snapshot now searches for the correct snapshot.
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
source = {'group_snapshot_id': 'typical_cgsnap_id',
'volume_id': 'typical_vol_id',
'id': 'no_id_4_u'}
name = (self.configuration.sf_volume_prefix +
source.get('group_snapshot_id'))
with mock.patch.object(sfv,
'_get_group_snapshot_by_name',
return_value={}) as get,\
mock.patch.object(sfv,
'_create_clone_from_sf_snapshot',
return_value='model'):
result = sfv.create_volume_from_snapshot({}, source)
get.assert_called_once_with(name)
self.assertEqual('model', result)
@mock.patch('cinder.volume.volume_utils.is_group_a_cg_snapshot_type')
def test_create_group_cg(self, group_cg_test):
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
group_cg_test.return_value = True
group = mock.MagicMock()
result = sfv.create_group(self.ctxt, group)
self.assertEqual(result,
{'status': fields.GroupStatus.AVAILABLE})
group_cg_test.assert_called_once_with(group)
@mock.patch('cinder.volume.volume_utils.is_group_a_cg_snapshot_type')
def test_delete_group_snap_cg(self, group_cg_test):
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
group_cg_test.return_value = True
cgsnapshot = fake_group_snapshot.fake_group_snapshot_obj(
mock.MagicMock())
snapshots = fake_snapshot.fake_snapshot_obj(mock.MagicMock())
with mock.patch.object(sfv, '_delete_cgsnapshot',
return_value={}) as _del_mock:
model_update = sfv.delete_group_snapshot(self.ctxt,
cgsnapshot, snapshots)
_del_mock.assert_called_once_with(self.ctxt, cgsnapshot, snapshots)
self.assertEqual({}, model_update)
@mock.patch('cinder.volume.volume_utils.is_group_a_cg_snapshot_type')
def test_delete_group_snap(self, group_cg_test):
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
group_cg_test.return_value = False
cgsnapshot = fake_group_snapshot.fake_group_snapshot_obj(
mock.MagicMock())
snapshots = fake_snapshot.fake_snapshot_obj(mock.MagicMock())
with mock.patch.object(sfv, '_delete_cgsnapshot',
return_value={}) as _del_mock:
self.assertRaises(NotImplementedError, sfv.delete_group_snapshot,
self.ctxt, cgsnapshot, snapshots)
_del_mock.assert_not_called()
@mock.patch('cinder.volume.volume_utils.is_group_a_cg_snapshot_type')
def test_create_group_rainy(self, group_cg_test):
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
group_cg_test.return_value = False
group = mock.MagicMock()
self.assertRaises(NotImplementedError,
sfv.create_group,
self.ctxt, group)
group_cg_test.assert_called_once_with(group)
@mock.patch('cinder.volume.volume_utils.is_group_a_cg_snapshot_type')
def test_create_group_from_src_rainy(self, group_cg_test):
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
group_cg_test.return_value = False
group = mock.MagicMock()
volumes = [mock.MagicMock()]
self.assertRaises(NotImplementedError,
sfv.create_group_from_src,
self.ctxt, group, volumes)
group_cg_test.assert_called_once_with(group)
@mock.patch('cinder.volume.volume_utils.is_group_a_cg_snapshot_type')
def test_create_group_from_src_cg(self, group_cg_test):
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
group_cg_test.return_value = True
group = mock.MagicMock()
volumes = [mock.MagicMock()]
ret = 'things'
with mock.patch.object(sfv,
'_create_consistencygroup_from_src',
return_value=ret):
result = sfv.create_group_from_src(self.ctxt,
group,
volumes)
self.assertEqual(ret, result)
group_cg_test.assert_called_once_with(group)
@mock.patch('cinder.volume.volume_utils.is_group_a_cg_snapshot_type')
def test_create_group_snapshot_rainy(self, group_cg_test):
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
group_cg_test.return_value = False
group_snapshot = mock.MagicMock()
snapshots = [mock.MagicMock()]
self.assertRaises(NotImplementedError,
sfv.create_group_snapshot,
self.ctxt,
group_snapshot,
snapshots)
group_cg_test.assert_called_once_with(group_snapshot)
@mock.patch('cinder.volume.volume_utils.is_group_a_cg_snapshot_type')
def test_create_group_snapshot(self, group_cg_test):
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
group_cg_test.return_value = True
group_snapshot = mock.MagicMock()
snapshots = [mock.MagicMock()]
ret = 'things'
with mock.patch.object(sfv,
'_create_cgsnapshot',
return_value=ret):
result = sfv.create_group_snapshot(self.ctxt,
group_snapshot,
snapshots)
self.assertEqual(ret, result)
group_cg_test.assert_called_once_with(group_snapshot)
@mock.patch('cinder.volume.volume_utils.is_group_a_cg_snapshot_type')
def test_delete_group_rainy(self, group_cg_test):
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
group_cg_test.return_value = False
group = mock.MagicMock()
volumes = [mock.MagicMock()]
self.assertRaises(NotImplementedError,
sfv.delete_group,
self.ctxt,
group,
volumes)
group_cg_test.assert_called_once_with(group)
@mock.patch('cinder.volume.volume_utils.is_group_a_cg_snapshot_type')
def test_delete_group(self, group_cg_test):
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
group_cg_test.return_value = True
group = mock.MagicMock()
volumes = [mock.MagicMock()]
ret = 'things'
with mock.patch.object(sfv,
'_delete_consistencygroup',
return_value=ret):
result = sfv.delete_group(self.ctxt,
group,
volumes)
self.assertEqual(ret, result)
group_cg_test.assert_called_once_with(group)
@mock.patch('cinder.volume.volume_utils.is_group_a_cg_snapshot_type')
def test_update_group_rainy(self, group_cg_test):
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
group_cg_test.return_value = False
group = mock.MagicMock()
self.assertRaises(NotImplementedError,
sfv.update_group,
self.ctxt,
group)
group_cg_test.assert_called_once_with(group)
@mock.patch('cinder.volume.volume_utils.is_group_a_cg_snapshot_type')
def test_update_group(self, group_cg_test):
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
group_cg_test.return_value = True
group = mock.MagicMock()
ret = 'things'
with mock.patch.object(sfv,
'_update_consistencygroup',
return_value=ret):
result = sfv.update_group(self.ctxt,
group)
self.assertEqual(ret, result)
group_cg_test.assert_called_once_with(group)
def test_getattr_failure(self):
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
try:
sfv.foo()
self.fail("Should have thrown Error")
except Exception:
pass
@data('Async', 'Sync', 'SnapshotsOnly')
@mock.patch.object(volume_types, 'get_volume_type')
def test_set_rep_by_volume_type(self, mode, mock_get_volume_type):
mock_get_volume_type.return_value = {
'name': 'sf-1', 'deleted': False,
'created_at': '2014-02-06 04:58:11',
'updated_at': None, 'extra_specs':
{'replication_enabled': '<is> True',
'solidfire:replication_mode': mode},
'deleted_at': None,
'id': '290edb2a-f5ea-11e5-9ce9-5e5517507c66'}
rep_opts = {}
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
sfv.cluster_pairs = self.cluster_pairs
ctxt = None
type_id = '290edb2a-f5ea-11e5-9ce9-5e5517507c66'
rep_opts['rep_type'] = mode
self.assertEqual(rep_opts, sfv._set_rep_by_volume_type(ctxt, type_id))
mock_get_volume_type.assert_called()
def test_replicate_volume(self):
replication_status = fields.ReplicationStatus.ENABLED
fake_vol = {'project_id': 1, 'volumeID': 1, 'size': 1}
params = {'attributes': {}}
rep_info = {'rep_type': 'Async'}
sf_account = {'initiatorSecret': 'shhh', 'targetSecret': 'dont-tell'}
model_update = {'provider_id': '1 2 xxxx'}
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
sfv.cluster_pairs = self.cluster_pairs
with mock.patch.object(sfv,
'_issue_api_request',
self.fake_issue_api_request),\
mock.patch.object(sfv,
'_get_sfaccount_by_name',
return_value={'accountID': 1}),\
mock.patch.object(sfv,
'_do_volume_create',
return_value=model_update):
self.assertEqual({'replication_status': replication_status},
sfv._replicate_volume(fake_vol, params,
sf_account, rep_info))
def test_pythons_try_except(self):
def _fake_retrieve_rep(vol):
raise solidfire.SolidFireAPIException
fake_type = {'extra_specs': {}}
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
with mock.patch.object(sfv,
'_get_create_account',
return_value={'accountID': 5}),\
mock.patch.object(sfv,
'_retrieve_qos_setting',
return_value=None), \
mock.patch.object(sfv,
'_do_volume_create',
return_value={'provider_id': '1 2 xxxx'}),\
mock.patch.object(volume_types,
'get_volume_type',
return_value=fake_type), \
mock.patch.object(sfv,
'_retrieve_replication_settings',
side_effect=_fake_retrieve_rep):
self.assertRaises(solidfire.SolidFireAPIException,
sfv.create_volume,
self.mock_volume)
def test_extract_sf_attributes_from_extra_specs(self):
type_id = '290edb2a-f5ea-11e5-9ce9-5e5517507c66'
fake_type = {'extra_specs': {'SFAttribute:foo': 'bar',
'SFAttribute:biz': 'baz'}}
expected = [{'foo': 'bar'}, {'biz': 'baz'}]
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
with mock.patch.object(volume_types, 'get_volume_type',
return_value=fake_type):
res = sfv._extract_sf_attributes_from_extra_specs(type_id)
six.assertCountEqual(self, expected, res)
def test_build_endpoint_with_kwargs(self):
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
expected_ep = {'passwd': 'nunyabiz',
'port': 888,
'url': 'https://1.2.3.4:888',
'svip': None,
'mvip': '1.2.3.4',
'login': 'JohnWayne'}
ep = sfv._build_endpoint_info(mvip='1.2.3.4', login='JohnWayne',
password='nunyabiz', port=888)
self.assertEqual(expected_ep, ep)
# Make sure we pick up defaults for those not specified
expected_ep = {'passwd': 'nunyabiz',
'url': 'https://1.2.3.4:443',
'svip': None,
'mvip': '1.2.3.4',
'login': 'admin',
'port': 443}
ep = sfv._build_endpoint_info(mvip='1.2.3.4', password='nunyabiz')
self.assertEqual(expected_ep, ep)
def test_generate_random_string(self):
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
a = sfv._generate_random_string(12)
self.assertEqual(len(a), 12)
self.assertIsNotNone(re.match(r'[A-Z0-9]{12}', a), a)
@mock.patch.object(solidfire.SolidFireDriver, '_get_sfaccount')
@mock.patch.object(solidfire.SolidFireDriver, '_get_sf_volume')
@mock.patch.object(solidfire.SolidFireDriver, '_get_sf_snapshots')
@mock.patch.object(solidfire.SolidFireDriver, '_issue_api_request')
def test_revert_to_snapshot_success(self, mock_issue_api_request,
mock_get_sf_snapshots,
mock_get_sf_volume,
mock_get_sfaccount):
mock_issue_api_request.side_effect = self.fake_issue_api_request
mock_get_sfaccount.return_value = self.fake_sfaccount
mock_get_sf_volume.return_value = self.fake_sfvol
mock_get_sf_snapshots.return_value = self.fake_sfsnaps
expected_params = {'accountID': 25,
'volumeID': 6,
'snapshotID': '5',
'saveCurrentState': 'false'}
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
# Success path
sfv.revert_to_snapshot(self.ctxt, self.vol, self.snap)
mock_issue_api_request.assert_called_with(
'RollbackToSnapshot', expected_params, version='6.0')
@mock.patch.object(solidfire.SolidFireDriver, '_get_sfaccount')
@mock.patch.object(solidfire.SolidFireDriver, '_get_sf_volume')
@mock.patch.object(solidfire.SolidFireDriver, '_get_sf_snapshots')
@mock.patch.object(solidfire.SolidFireDriver, '_issue_api_request')
def test_revert_to_snapshot_fail_vol_not_found(
self, mock_issue_api_request, mock_get_sf_snapshots,
mock_get_sf_volume, mock_get_sfaccount):
mock_issue_api_request.side_effect = self.fake_issue_api_request
mock_get_sfaccount.return_value = self.fake_sfaccount
mock_get_sf_volume.return_value = None
mock_get_sf_snapshots.return_value = []
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
# Volume not found
mock_get_sf_volume.return_value = None
self.assertRaises(exception.VolumeNotFound,
sfv.revert_to_snapshot,
self.ctxt, self.vol, self.snap)
@mock.patch.object(solidfire.SolidFireDriver, '_get_sfaccount')
@mock.patch.object(solidfire.SolidFireDriver, '_get_sf_volume')
@mock.patch.object(solidfire.SolidFireDriver, '_get_sf_snapshots')
@mock.patch.object(solidfire.SolidFireDriver, '_issue_api_request')
def test_revert_to_snapshot_fail_snap_not_found(
self, mock_issue_api_request, mock_get_sf_snapshots,
mock_get_sf_volume, mock_get_sfaccount):
mock_issue_api_request.side_effect = self.fake_issue_api_request
mock_get_sfaccount.return_value = self.fake_sfaccount
mock_get_sf_volume.return_value = self.fake_sfvol
mock_get_sf_snapshots.return_value = []
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
# Snapshot not found
mock_get_sf_snapshots.return_value = []
self.assertRaises(exception.VolumeSnapshotNotFound,
sfv.revert_to_snapshot,
self.ctxt, self.vol, self.snap)
@mock.patch.object(solidfire.SolidFireDriver, '_get_create_account')
@mock.patch.object(solidfire.SolidFireDriver, '_set_cluster_pairs')
@mock.patch.object(solidfire.SolidFireDriver, '_snapshot_discovery')
@mock.patch.object(solidfire.SolidFireDriver, '_issue_api_request')
@mock.patch.object(solidfire.SolidFireDriver, '_get_model_info')
@mock.patch.object(solidfire.SolidFireDriver, '_update_attributes')
@mock.patch.object(solidfire.SolidFireDriver, '_update_cluster_status')
@mock.patch.object(solidfire.SolidFireDriver, '_set_cluster_pairs')
@mock.patch.object(solidfire.SolidFireDriver, '_get_default_volume_params')
@mock.patch.object(solidfire.SolidFireDriver,
'_retrieve_replication_settings')
@mock.patch.object(solidfire.SolidFireDriver, '_replicate_volume')
@mock.patch.object(solidfire.SolidFireDriver, '_create_cluster_reference')
def test_do_clone_volume_rep_disabled(self,
mock_create_cluster_reference,
mock_replicate_volume,
mock_retrieve_replication_settings,
mock_get_default_volume_params,
mock_set_cluster_pairs,
mock_update_cluster_status,
mock_update_attributes,
mock_get_model_info,
mock_issue_api_request,
mock_snapshot_discovery,
mock_test_set_cluster_pairs,
mock_get_create_account):
all_mocks = locals()
def reset_mocks():
for mk in all_mocks.values():
if isinstance(mk, mock.MagicMock):
mk.reset_mock()
sf_volume_params = {'volumeID': 1, 'snapshotID': 2, 'newSize': 3}
mock_snapshot_discovery.return_value = (sf_volume_params, True,
self.fake_sfvol)
mock_get_create_account.return_value = self.fake_sfaccount
ctx = context.get_admin_context()
vol_fields = {'updated_at': timeutils.utcnow(),
'created_at': timeutils.utcnow()}
src_vol = fake_volume.fake_volume_obj(ctx)
dst_vol = fake_volume.fake_volume_obj(ctx, **vol_fields)
mock_create_cluster_reference.return_value = {
'mvip': self.mvip,
'svip': self.svip}
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
sfv.replication_enabled = False
reset_mocks()
mock_issue_api_request.return_value = {
'error': {'code': 000, 'name': 'DummyError',
'message': 'This is a fake error response'},
'id': 1}
self.assertRaises(solidfire.SolidFireAPIException,
sfv._do_clone_volume, src_vol.id,
dst_vol, sf_src_snap=self.fake_sfsnaps[0])
clone_vol_params = {
'snapshotID': self.fake_sfsnaps[0]['snapshotID'],
'volumeID': self.fake_sfsnaps[0]['volumeID'],
'newSize': dst_vol.size * units.Gi,
'name': '%(prefix)s%(id)s' % {
'prefix': self.configuration.sf_volume_prefix,
'id': dst_vol.id},
'newAccountID': self.fake_sfaccount['accountID']}
mock_get_create_account.assert_called_with(dst_vol.project_id)
mock_issue_api_request.assert_called_once_with(
'CloneVolume', clone_vol_params, version='6.0')
mock_test_set_cluster_pairs.assert_not_called()
mock_update_attributes.assert_not_called()
mock_get_model_info.assert_not_called()
mock_snapshot_discovery.assert_not_called()
reset_mocks()
mock_issue_api_request.side_effect = self.fake_issue_api_request
mock_get_default_volume_params.return_value = {}
mock_get_model_info.return_value = None
self.assertRaises(solidfire.SolidFireAPIException,
sfv._do_clone_volume, src_vol.id,
dst_vol, sf_src_snap=self.fake_sfsnaps[0])
mock_get_create_account.assert_called_with(dst_vol.project_id)
calls = [mock.call('CloneVolume', clone_vol_params, version='6.0'),
mock.call('ModifyVolume', {'volumeID': 6})]
mock_issue_api_request.assert_has_calls(calls)
mock_test_set_cluster_pairs.assert_not_called()
mock_update_attributes.assert_not_called()
mock_get_model_info.assert_called_once()
mock_snapshot_discovery.assert_not_called()
reset_mocks()
mock_retrieve_replication_settings.return_value = 'Async'
update = {'replication_status': fields.ReplicationStatus.ENABLED}
mock_replicate_volume.side_effect = solidfire.SolidFireDriverException
mock_update_attributes.return_value = {'result': {}, 'id': 1}
mock_get_model_info.return_value = {
'provider_location': '1.1.1.1 iqn 0',
'provider_auth': 'CHAP stack-1-a60e2611875f40199931f2c76370d66b '
'2FE0CQ8J196R',
'provider_id': '%s %s cluster-id-01' % (
self.fake_sfvol['volumeID'],
self.fake_sfaccount['accountID'])
}
data, account, updates = sfv._do_clone_volume(
src_vol.id, dst_vol, sf_src_snap=self.fake_sfsnaps[0])
self.assertEqual({'result': {}, 'id': 1}, data)
self.assertEqual(25, account['accountID'])
self.assertEqual(self.fake_sfvol['volumeID'],
int(updates['provider_id'].split()[0]))
mock_get_create_account.assert_called_with(dst_vol.project_id)
calls = [mock.call('CloneVolume', clone_vol_params, version='6.0'),
mock.call('ModifyVolume', {'volumeID': 6})]
mock_issue_api_request.assert_has_calls(calls)
mock_test_set_cluster_pairs.assert_not_called()
mock_update_attributes.assert_not_called()
mock_get_model_info.assert_called_once()
mock_snapshot_discovery.assert_not_called()
@mock.patch.object(solidfire.SolidFireDriver, '_get_create_account')
@mock.patch.object(solidfire.SolidFireDriver, '_retrieve_qos_setting')
@mock.patch.object(solidfire.SolidFireDriver,
'_extract_sf_attributes_from_extra_specs')
def test_get_default_volume_params(
self, mock_extract_sf_attributes_from_extra_specs,
mock_retrieve_qos_setting, mock_get_create_account):
mock_extract_sf_attributes_from_extra_specs.return_value = [{
'key1': 'value1',
'key2': 'value2'
}]
mock_retrieve_qos_setting.return_value = None
mock_get_create_account.return_value = self.fake_sfaccount
ctx = context.get_admin_context()
type_fields = {'extra_specs': {'replication_enabled': '<is> True'}}
vol_type = fake_volume.fake_volume_type_obj(ctx, **type_fields)
utc_now = timeutils.utcnow().isoformat()
vol_fields = {
'id': fakes.FAKE_UUID,
'created_at': utc_now,
'volume_type': vol_type,
'volume_type_id': vol_type.id
}
vol = fake_volume.fake_volume_obj(ctx, **vol_fields)
vol_name = '%s%s' % (self.configuration.sf_volume_prefix, vol.id)
expected_attr = {
'uuid': vol.id,
'is_clone': False,
'created_at': utc_now + "+00:00",
'cinder-name': vol.get('display_name', ""),
'key1': 'value1',
'key2': 'value2',
}
expected_params = {
'name': vol_name,
'accountID': self.fake_sfaccount['accountID'],
'sliceCount': 1,
'totalSize': int(vol.size * units.Gi),
'enable512e': self.configuration.sf_emulate_512,
'attributes': expected_attr,
'qos': None
}
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
sfv.replication_enabled = True
params = sfv._get_default_volume_params(vol, False)
self.assertDictEqual(expected_params, params)
mock_extract_sf_attributes_from_extra_specs.assert_called()
mock_retrieve_qos_setting.assert_called()
mock_get_create_account.assert_called()
@mock.patch.object(solidfire.SolidFireDriver, '_get_sfvol_by_cinder_vref')
def test_disable_replication_fail(self, mock_get_sfvol_by_cinder_vref):
self.fake_sfvol['volumePairs'] = []
mock_get_sfvol_by_cinder_vref.return_value = self.fake_sfvol
ctx = context.get_admin_context()
utc_now = timeutils.utcnow().isoformat()
vol_fields = {
'id': f_uuid,
'created_at': utc_now
}
vol = fake_volume.fake_volume_obj(ctx, **vol_fields)
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
sfv.replication_enabled = True
sfv.cluster_pairs = self.cluster_pairs
expected = {'replication_status': fields.ReplicationStatus.DISABLED}
updates = sfv._disable_replication(vol)
self.assertDictEqual(expected, updates)
@mock.patch.object(solidfire.SolidFireDriver, '_get_sfvol_by_cinder_vref')
@mock.patch.object(solidfire.SolidFireDriver, '_issue_api_request')
@mock.patch.object(solidfire.SolidFireDriver, '_create_cluster_reference')
def test_disable_replication(self, mock_create_cluster_reference,
mock_issue_api_request,
mock_get_sfvol_by_cinder_vref):
mock_create_cluster_reference.return_value = {
'mvip': self.mvip,
'svip': self.svip}
self.fake_sfvol['volumePairs'] = [{"remoteVolumeID": 26}]
mock_get_sfvol_by_cinder_vref.return_value = self.fake_sfvol
ctx = context.get_admin_context()
utc_now = timeutils.utcnow().isoformat()
vol_fields = {
'id': f_uuid,
'created_at': utc_now
}
vol = fake_volume.fake_volume_obj(ctx, **vol_fields)
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
sfv.replication_enabled = True
sfv.cluster_pairs = self.cluster_pairs
sfv.active_cluster['mvip'] = self.mvip
sfv.active_cluster['svip'] = self.svip
expected = {'replication_status': fields.ReplicationStatus.DISABLED}
mock_issue_api_request.reset_mock()
updates = sfv._disable_replication(vol)
self.assertDictEqual(expected, updates)
expected = [
mock.call("RemoveVolumePair",
{'volumeID': self.fake_sfvol['volumeID']}, '8.0'),
mock.call("RemoveVolumePair", {'volumeID': 26}, '8.0',
endpoint=sfv.cluster_pairs[0]['endpoint']),
mock.call("DeleteVolume", {'volumeID': 26},
endpoint=sfv.cluster_pairs[0]['endpoint']),
mock.call("PurgeDeletedVolume", {'volumeID': 26},
endpoint=sfv.cluster_pairs[0]['endpoint'])
]
mock_issue_api_request.assert_has_calls(expected)
mock_create_cluster_reference.assert_called()
mock_get_sfvol_by_cinder_vref.assert_called()
@mock.patch.object(solidfire.SolidFireDriver, '_issue_api_request')
@mock.patch.object(solidfire.SolidFireDriver, '_create_cluster_reference')
@mock.patch.object(solidfire.SolidFireDriver, '_set_cluster_pairs')
@mock.patch.object(solidfire.SolidFireDriver, '_update_cluster_status')
@mock.patch.object(solidfire.SolidFireDriver, '_get_cluster_info')
@mock.patch.object(solidfire.SolidFireDriver, '_map_sf_volumes')
@mock.patch.object(solidfire.SolidFireDriver, '_failover_volume')
@mock.patch.object(solidfire.SolidFireDriver, '_get_create_account')
@mock.patch.object(solidfire.SolidFireDriver, '_get_remote_info_by_id')
def test_failover_host(self, mock_get_remote_info_by_id,
mock_get_create_account,
mock_failover_volume,
mock_map_sf_volumes,
mock_get_cluster_info,
mock_update_cluster_status,
mock_set_cluster_pairs,
mock_create_cluster_reference,
mock_issue_api_request):
all_mocks = locals()
def reset_mocks():
for mk in all_mocks.values():
if isinstance(mk, mock.MagicMock):
mk.reset_mock()
ctx = context.get_admin_context()
vol_fields = {'updated_at': timeutils.utcnow(),
'created_at': timeutils.utcnow()}
cinder_vols = []
sf_vols = []
for i in range(1, 6):
vol = fake_volume.fake_volume_obj(ctx, **vol_fields)
sf_vol = self.fake_sfvol.copy()
sf_vol['volumeID'] = i
sf_vol['name'] = '%s%s' % (self.configuration.sf_volume_prefix,
vol.id)
sf_vol['access'] = 'replicationTarget'
sf_vol['attributes'] = {'uuid': vol.id}
sf_vol['cinder_id'] = vol.id
sf_vols.append(sf_vol)
cinder_vols.append(vol)
mock_map_sf_volumes.return_value = sf_vols
self.configuration.replication_device = []
reset_mocks()
drv_args = {'active_backend_id': None}
sfv = solidfire.SolidFireDriver(configuration=self.configuration,
**drv_args)
self.assertRaises(exception.UnableToFailOver,
sfv.failover_host, ctx, cinder_vols, 'fake', None)
mock_map_sf_volumes.assert_not_called()
fake_replication_device = {'backend_id': 'fake',
'mvip': '0.0.0.0',
'login': 'fake_login',
'password': 'fake_pwd'}
self.configuration.replication_device = [fake_replication_device]
reset_mocks()
drv_args = {'active_backend_id': None}
sfv = solidfire.SolidFireDriver(configuration=self.configuration,
**drv_args)
self.assertRaises(exception.InvalidReplicationTarget,
sfv.failover_host, ctx, cinder_vols, 'default', None)
mock_map_sf_volumes.assert_not_called()
reset_mocks()
drv_args = {'active_backend_id': None}
sfv = solidfire.SolidFireDriver(configuration=self.configuration,
**drv_args)
self.assertRaises(exception.InvalidReplicationTarget,
sfv.failover_host, ctx, cinder_vols,
secondary_id='not_fake_id', groups=None)
mock_map_sf_volumes.assert_not_called()
mock_create_cluster_reference.return_value = self.cluster_pairs[0]
reset_mocks()
drv_args = {'active_backend_id': 'secondary'}
sfv = solidfire.SolidFireDriver(configuration=self.configuration,
**drv_args)
sfv.cluster_pairs = self.cluster_pairs
sfv.cluster_pairs[0]['backend_id'] = 'fake'
sfv.replication_enabled = True
cluster_id, updates, _ = sfv.failover_host(
ctx, cinder_vols, secondary_id='default', groups=None)
self.assertEqual(5, len(updates))
for update in updates:
self.assertEqual(fields.ReplicationStatus.ENABLED,
update['updates']['replication_status'])
self.assertEqual('', cluster_id)
mock_get_create_account.assert_called()
mock_failover_volume.assert_called()
mock_map_sf_volumes.assert_called()
mock_update_cluster_status.assert_called()
mock_set_cluster_pairs.assert_called()
mock_create_cluster_reference.assert_called()
reset_mocks()
drv_args = {'active_backend_id': None}
sfv = solidfire.SolidFireDriver(configuration=self.configuration,
**drv_args)
sfv.cluster_pairs = self.cluster_pairs
sfv.cluster_pairs[0]['backend_id'] = 'fake'
sfv.replication_enabled = True
cluster_id, updates, _ = sfv.failover_host(
ctx, cinder_vols, secondary_id='fake', groups=None)
self.assertEqual(5, len(updates))
for update in updates:
self.assertEqual(fields.ReplicationStatus.FAILED_OVER,
update['updates']['replication_status'])
self.assertEqual('fake', cluster_id)
mock_get_create_account.assert_called()
mock_failover_volume.assert_called()
mock_map_sf_volumes.assert_called()
mock_update_cluster_status.assert_called()
mock_set_cluster_pairs.assert_called()
mock_create_cluster_reference.assert_called()
@mock.patch.object(solidfire.SolidFireDriver, '_issue_api_request')
@mock.patch.object(solidfire.SolidFireDriver, '_create_cluster_reference')
@mock.patch.object(solidfire.SolidFireDriver, '_update_cluster_status')
def test_failover_volume(self, mock_update_cluster_status,
mock_create_cluster_reference,
mock_issue_api_request):
all_mocks = locals()
def reset_mocks():
for mk in all_mocks.values():
if isinstance(mk, mock.MagicMock):
mk.reset_mock()
mock_issue_api_request.return_value = self.fake_sfaccount
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
sfv.replication_enabled = True
fake_src_sfvol = {'volumeID': 600,
'name': 'test_volume',
'accountID': 25,
'sliceCount': 1,
'totalSize': 1 * units.Gi,
'enable512e': True,
'access': "replicationTarget",
'status': "active",
'attributes': {'uuid': f_uuid[0]},
'qos': None,
'iqn': 'super_fake_iqn'}
expected_src_params = {'volumeID': fake_src_sfvol['volumeID'],
'access': 'replicationTarget'}
expected_tgt_params = {'volumeID': self.fake_sfvol['volumeID'],
'access': 'readWrite'}
sfv._failover_volume(self.fake_sfvol, self.cluster_pairs[0],
fake_src_sfvol)
mock_issue_api_request.assert_has_calls(
[mock.call("ModifyVolume", expected_src_params),
mock.call("ModifyVolume", expected_tgt_params,
endpoint=self.cluster_pairs[0]['endpoint'])]
)
reset_mocks()
sfv._failover_volume(self.fake_sfvol, self.cluster_pairs[0])
mock_issue_api_request.assert_called_with(
"ModifyVolume",
expected_tgt_params,
endpoint=self.cluster_pairs[0]['endpoint']
)