cinder/cinder/tests/test_fusionio_ioControl.py

843 lines
40 KiB
Python

# Copyright (c) 2014 Fusion-io, Inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import copy
import json
import mock
from oslo_utils import timeutils
from oslo_utils import units
import requests
from cinder import context
from cinder.db.sqlalchemy import models
from cinder import exception
from cinder.openstack.common import log as logging
from cinder import test
from cinder.volume import configuration as conf
from cinder.volume.drivers.fusionio import ioControl
from cinder.volume import qos_specs
from cinder.volume import volume_types
LOG = logging.getLogger(__name__)
basic_net_response = [{"IsManagementPort": True,
"NetworkAddress": "10.10.1.82",
"IsReplicationPort": True, "OperationalState": "up",
"ControllerUID": "FakeControl1_UID",
"IfIndex": 2},
{"IsManagementPort": True,
"NetworkAddress": "10.10.1.83",
"IsReplicationPort": True, "OperationalState": "up",
"ControllerUID": "FakeControl1_UID",
"IfIndex": 3},
{"IsManagementPort": False,
"NetworkAddress": "",
"IsReplicationPort": False, "OperationalState": "down",
"ControllerUID": "FakeControl1_UID",
"IfIndex": 4},
{"IsManagementPort": True,
"NetworkAddress": "10.10.2.88",
"IsReplicationPort": True, "OperationalState": "up",
"ControllerUID": "FakeControl2_UID",
"IfIndex": 2},
{"IsManagementPort": False,
"NetworkAddress": "10.10.2.84",
"IsReplicationPort": False, "OperationalState": "up",
"ControllerUID": "FakeControl2_UID",
"IfIndex": 3},
{"IsManagementPort": False,
"NetworkAddress": "",
"IsReplicationPort": False, "OperationalState": "down",
"ControllerUID": "FakeControl2_UID",
"IfIndex": 4}]
basic_pools_response = [{"TotalMB": 5079, "Name": "PoolOwnerA",
"ExportedVolumeMB": 2049,
"basetype": "StoragePool", "UsedVolumeMB": 3,
"ObjectPath": "", "UsedMetaMB": 4, "UsedMB": 4,
"SizeMB": 68677278, "UsedSnapMB": 0,
"PagingUsedMB": 4,
"CurrentOwnerUUID": "FakeControl1_UID",
"TaskId": "", "PagingTotalMB": 5079, "Ready": True,
"id": "FakePoolA_id",
"Size": 72013345456128},
{"TotalMB": 5079, "Name": "PoolOwnerB",
"ExportedVolumeMB": 2049,
"basetype": "StoragePool", "UsedVolumeMB": 193,
"ObjectPath": "", "UsedMetaMB": 3, "UsedMB": 211,
"SizeMB": 68677278, "UsedSnapMB": 0,
"PagingUsedMB": 211,
"CurrentOwnerUUID": "FakeControl2_UID",
"TaskId": "", "PagingTotalMB": 5079, "Ready": True,
"id": "FakePoolB_id",
"Size": 72013345456128}
]
basic_vol_response = [{"basetype": "Volume", "ObjectPath": "", "TaskId": "",
"id": "FakeBasicVolID",
"Name": "cinderVolumeID",
"IQN": "iqn.2010-11.com.ngs:Volume:FakeBasicVolID",
"Size": 1074266112, "SizeMB": 1024, "HighWaterMark": 0,
"HighWaterMarkMB": 0, "MetadataSize": 262144,
"MetadataSizeMB": 0, "DupedSize": 1074266112,
"DupedSizeMB": 1024, "FaultTolerance": 0,
"PathTolerance": 0,
"AllowedTierMask": 18446744073709551615,
"RequiredTierMask": 0, "NumberOfPagesPerChapter": 0,
"CreateDateTime": 1390837136,
"LayerId": "407115424bb9539c",
"ParentLayerId": "0", "Protocol": "iscsi",
"PoolUUID": "FakePoolB_id",
"PolicyUUID": "00000000-00000000-0000-000000000000",
"CurrentOwnerUUID": "FakeControl2_UID",
"AclGroupList": ["1"], "ReplicaPeerList": [],
"SnapshotRetention": 0}
]
basic_policy_response = [{"id": "00000000-00000000-0000-000000000000",
"Name": "Policy 5", },
{"id": "00000000-00000000-0000-000000000002",
"Name": "Policy 4", },
{"id": "00000000-00000000-0000-000000000004",
"Name": "Policy 3", },
{"id": "00000000-00000000-0000-000000000008",
"Name": "Policy 2", },
{"id": "00000000-00000000-0000-000000000010",
"Name": "Policy 1", },
]
basic_snapshot_response = [{"basetype": "Snapshot", "ObjectPath": "",
"TaskId": "", "id": "407115424bb9539c",
"Name": "cinderSnapshotID",
"VolumeUUID": "FakeBasicVolID",
"PoolUUID": "FakePoolB_id",
"ParentUUID": "0", "Size": 1074266112,
"SizeMB": 1024, "SizeUsed": 0, "SizeUsedMB": 0,
"SizeReclaimable": 0, "SizeReclaimableMB": 0,
"CreateDateTime": 1390952554, "ChildCount": 1,
"IsMounted": False, "IsHostConsistent": False,
"ReplicaInfoList": []}
]
basic_acl_group_response = [{"id": 1,
"GroupName": "Deny Access",
"InitiatorList": [], },
{"id": 2,
"GroupName": "Allow Access",
"InitiatorList": ["iqn*"], },
{"id": 3,
"GroupName": "fake:01", "Description": "",
"InitiatorList": ["fake:01"], },
{"id": 4,
"GroupName": "iqn.1994-05.com.redhat:fake1",
"InitiatorList": ["iqn.1994-05.com.rhel:fake1"],
},
{"id": 5,
"GroupName": "MyGroup", "Description": "",
"InitiatorList": "iqn.1994-05.com.rhel:fake2", }
]
def create_configuration():
configuration = conf.Configuration(None)
configuration.san_ip = "10.123.10.123"
configuration.san_login = "fioTestUser"
configuration.san_password = "fioTestUserPassword"
# we can set targetdelay to 0 for testing
configuration.fusionio_iocontrol_targetdelay = 0
configuration.fusionio_iocontrol_retry = 3
configuration.fusionio_iocontrol_verify_cert = True
return configuration
class FIOFakeResponse(object):
"""Fake response to requests."""
def __init__(self, code=None, text=None):
self.status_code = code
self.text = text
def json(self):
return json.loads(self.text)
def raise_for_status(self):
if self.status_code > 300:
raise requests.exceptions.HTTPError
class FIOioControlConnectionTests(test.TestCase):
VERSION = '1.0.0'
fakeSessionID = '12345678'
def setUp(self):
super(FIOioControlConnectionTests, self).setUp()
self.configuration = create_configuration()
self.ctxt = context.get_admin_context()
return_text = json.dumps(
{"Version": ioControl.FIOconnection.APIVERSION})
get_return = FIOFakeResponse(code=200,
text=return_text)
requests.get = mock.Mock(return_value=get_return)
self.conn = ioControl.FIOconnection(
self.configuration.san_ip,
self.configuration.san_login,
self.configuration.san_password,
self.configuration.fusionio_iocontrol_retry,
(self.configuration.
fusionio_iocontrol_verify_cert),)
def test_conn_init_sucess(self):
expected = [mock.call(url=("https://" +
self.configuration.san_ip +
"/AUTH/Version"),
headers=self.conn.defhdrs,
verify=True)]
requests.get.assert_has_calls(expected)
def test_wrong_version(self):
expected = json.dumps(
{"Version": (ioControl.FIOconnection.APIVERSION + ".1")})
get_return = FIOFakeResponse(code=200,
text=expected)
requests.get = mock.Mock(return_value=get_return)
self.assertRaises(exception.VolumeDriverException,
ioControl.FIOconnection,
self.configuration.san_ip,
self.configuration.san_login,
self.configuration.san_password,
self.configuration.fusionio_iocontrol_retry,
self.configuration.fusionio_iocontrol_verify_cert,)
def test_create_session_sucess(self):
expected_text = json.dumps({"id": self.fakeSessionID})
post_return = FIOFakeResponse(code=201,
text=expected_text)
put_return = FIOFakeResponse(code=201,
text=json.dumps({"Status": 1}))
requests.post = mock.Mock(return_value=post_return)
requests.put = mock.Mock(return_value=put_return)
result = self.conn._create_session()
expectedhdr = copy.deepcopy(self.conn.defhdrs)
expectedhdr["Cookie"] = 'session=' + self.fakeSessionID
assert result == expectedhdr
def test_create_session_auth_fail(self):
expected_text = json.dumps({"id": self.fakeSessionID})
post_return = FIOFakeResponse(code=201,
text=expected_text)
put_return = FIOFakeResponse(code=201,
text=json.dumps({"Status": (-1)}))
requests.post = mock.Mock(return_value=post_return)
requests.put = mock.Mock(return_value=put_return)
requests.delete = mock.Mock()
self.assertRaises(exception.VolumeDriverException,
self.conn._create_session,)
def test_delete_session_sucess(self):
requests.delete = mock.Mock(return_value=True)
hdrs = copy.deepcopy(self.conn.defhdrs)
hdrs["Cookie"] = 'session=' + self.fakeSessionID
self.conn._delete_session(hdrs)
expected = [mock.call(url=("https://" +
self.configuration.san_ip +
"/AUTH/SESSION/" + self.fakeSessionID),
headers=self.conn.defhdrs,
verify=True), ]
requests.delete.assert_has_calls(expected)
def test_put_sucess(self):
put_return = FIOFakeResponse(code=201,
text=json.dumps({"Status": 1}))
requests.put = mock.Mock(return_value=put_return)
expectedhdr = copy.deepcopy(self.conn.defhdrs)
expectedhdr["Cookie"] = 'session=' + self.fakeSessionID
self.conn._create_session = mock.Mock(return_value=expectedhdr)
self.conn._delete_session = mock.Mock()
testurl = '/test/url/'
testcontent = {'testdict': 'testvalue'}
self.conn.put(testurl, testcontent)
self.conn.post(testurl, testcontent)
expected = [mock.call(), ]
self.conn._create_session.assert_has_calls(expected)
expected = [mock.call(expectedhdr), ]
self.conn._delete_session.assert_has_calls(expected)
expected = [mock.call(url=self.conn._complete_uri(testurl),
data=json.dumps(testcontent, sort_keys=True),
headers=expectedhdr, verify=True), ]
requests.put.assert_has_calls(expected)
def test_post_sucess(self):
expected_text = json.dumps({"id": self.fakeSessionID})
post_return = FIOFakeResponse(code=201,
text=expected_text)
requests.post = mock.Mock(return_value=post_return)
expectedhdr = copy.deepcopy(self.conn.defhdrs)
expectedhdr["Cookie"] = 'session=' + self.fakeSessionID
self.conn._create_session = mock.Mock(return_value=expectedhdr)
self.conn._delete_session = mock.Mock()
testurl = '/test/url/'
testcontent = {'testdict': 'testvalue'}
self.conn.post(testurl, testcontent)
expected = [mock.call(), ]
self.conn._create_session.assert_has_calls(expected)
expected = [mock.call(expectedhdr), ]
self.conn._delete_session.assert_has_calls(expected)
expected = [mock.call(url=self.conn._complete_uri(testurl),
data=json.dumps(testcontent, sort_keys=True),
headers=expectedhdr, verify=True), ]
requests.post.assert_has_calls(expected)
def test_delete_sucess(self):
del_return = FIOFakeResponse(code=201, text=json.dumps({}))
requests.delete = mock.Mock(return_value=del_return)
expectedhdr = copy.deepcopy(self.conn.defhdrs)
expectedhdr["Cookie"] = 'session=' + self.fakeSessionID
self.conn._create_session = mock.Mock(return_value=expectedhdr)
self.conn._delete_session = mock.Mock()
testurl = '/test/url/'
self.conn.delete(testurl,)
expected = [mock.call(), ]
self.conn._create_session.assert_has_calls(expected)
expected = [mock.call(expectedhdr), ]
self.conn._delete_session.assert_has_calls(expected)
expected = [mock.call(url=self.conn._complete_uri(testurl),
headers=expectedhdr, verify=True), ]
requests.delete.assert_has_calls(expected)
def test_get_sucess(self):
get_return = FIOFakeResponse(code=200,
text=json.dumps(basic_acl_group_response))
expectedhdr = copy.deepcopy(self.conn.defhdrs)
expectedhdr["Cookie"] = 'session=' + self.fakeSessionID
self.conn._create_session = mock.Mock(return_value=expectedhdr)
self.conn._delete_session = mock.Mock()
requests.get = mock.Mock(return_value=get_return)
testurl = '/test/url/'
result = self.conn.get(testurl,)
expected = [mock.call(), ]
self.conn._create_session.assert_has_calls(expected)
expected = [mock.call(expectedhdr), ]
self.conn._delete_session.assert_has_calls(expected)
expected = [mock.call(url=self.conn._complete_uri(testurl),
headers=expectedhdr, verify=True), ]
requests.get.assert_has_calls(expected)
assert result == basic_acl_group_response
def test_get_bad_json_once(self):
expectedhdr = copy.deepcopy(self.conn.defhdrs)
expectedhdr["Cookie"] = 'session=' + self.fakeSessionID
self.conn._create_session = mock.Mock(return_value=expectedhdr)
self.conn._delete_session = mock.Mock()
expected_text = json.dumps(basic_acl_group_response)
jsonErrEffect = [FIOFakeResponse(code=200,
text='{"badjson":"bad",,}'),
FIOFakeResponse(code=200,
text=expected_text)]
requests.get = mock.Mock(side_effect=jsonErrEffect)
testurl = '/test/url/'
result = self.conn.get(testurl,)
expected = [mock.call(), ]
self.conn._create_session.assert_has_calls(expected)
expected = [mock.call(expectedhdr), ]
self.conn._delete_session.assert_has_calls(expected)
expected = [mock.call(url=self.conn._complete_uri(testurl),
headers=expectedhdr, verify=True), ]
requests.get.assert_has_calls(expected)
assert result == basic_acl_group_response
def test_get_bad_json_retry_expire(self):
get_return = FIOFakeResponse(code=200, text='{"badjson":"bad",,}')
expectedhdr = copy.deepcopy(self.conn.defhdrs)
expectedhdr["Cookie"] = 'session=' + self.fakeSessionID
self.conn._create_session = mock.Mock(return_value=expectedhdr)
self.conn._delete_session = mock.Mock()
requests.get = mock.Mock(return_value=get_return)
testurl = '/test/url/'
self.assertRaises(exception.VolumeDriverException,
self.conn.get, testurl)
expected = [mock.call(), ]
self.conn._create_session.assert_has_calls(expected)
expected = [mock.call(expectedhdr), ]
self.conn._delete_session.assert_has_calls(expected)
expected = [mock.call(url=self.conn._complete_uri(testurl),
headers=expectedhdr, verify=True),
mock.call(url=self.conn._complete_uri(testurl),
headers=expectedhdr, verify=True),
mock.call(url=self.conn._complete_uri(testurl),
headers=expectedhdr, verify=True), ]
requests.get.assert_has_calls(expected)
def test_get_failed_http_response(self):
get_return = FIOFakeResponse(code=404,
text=json.dumps(basic_acl_group_response))
expectedhdr = copy.deepcopy(self.conn.defhdrs)
expectedhdr["Cookie"] = 'session=' + self.fakeSessionID
self.conn._create_session = mock.Mock(return_value=expectedhdr)
self.conn._delete_session = mock.Mock()
requests.get = mock.Mock(return_value=get_return)
testurl = '/test/url/'
self.assertRaises(requests.exceptions.HTTPError,
self.conn.get, testurl)
expected = [mock.call(), ]
self.conn._create_session.assert_has_calls(expected)
expected = [mock.call(expectedhdr), ]
self.conn._delete_session.assert_has_calls(expected)
expected = [mock.call(url=self.conn._complete_uri(testurl),
headers=expectedhdr, verify=True), ]
requests.get.assert_has_calls(expected)
@mock.patch('cinder.volume.drivers.fusionio.ioControl.FIOconnection',
autospec=True)
class FIOioControlTestCases(test.TestCase):
VERSION = '1.0.0'
policyTable = {'Policy 4': '00000000-00000000-0000-000000000002',
'Policy 5': '00000000-00000000-0000-000000000000',
'Policy 2': '00000000-00000000-0000-000000000008',
'Policy 3': '00000000-00000000-0000-000000000004',
'Policy 1': '00000000-00000000-0000-000000000010'}
def setUp(self):
super(FIOioControlTestCases, self).setUp()
self.configuration = create_configuration()
self.ctxt = context.get_admin_context()
self.drv = ioControl.FIOioControlDriver(
configuration=self.configuration)
self.drv.fio_qos_dict = self.policyTable
def test_do_setup_sucess(self, connmock):
# erase policy table, then make sure drv.do_setup builds it
self.drv.fio_qos_dict = {}
instance = connmock.return_value
instance.get.return_value = basic_policy_response
self.drv.do_setup(context="")
self.assertEqual(self.policyTable, self.drv.fio_qos_dict,
"wrong policy table built")
def test_create_volume_simple_success_poolA(self, connmock):
self.drv.conn = connmock.return_value
bPoolResponse = copy.deepcopy(basic_pools_response)
bPoolResponse[1]['ExportedVolumeMB'] = 5009
self.drv.conn.get.return_value = bPoolResponse
testvol = {'project_id': 'testproject',
'name': 'cinderVolumeName',
'size': 1,
'id': 'cinderVolumeID',
'volume_type_id': None,
'created_at': timeutils.utcnow()}
self.drv.create_volume(testvol)
cmd = {"Size": int(testvol['size']) * units.Gi,
"PolicyUUID": '00000000-00000000-0000-000000000000',
"PoolUUID": "FakePoolA_id",
"Name": testvol['id'], }
expected = [mock.call.get('TierStore/Pools/by-id/'),
mock.call.post('TierStore/Volumes/by-id/', cmd)]
self.drv.conn.assert_has_calls(expected)
def test_create_volume_simple_success_poolB(self, connmock):
self.drv.conn = connmock.return_value
bPoolResponse = copy.deepcopy(basic_pools_response)
bPoolResponse[0]['ExportedVolumeMB'] = 5009
self.drv.conn.get.return_value = bPoolResponse
testvol = {'project_id': 'testproject',
'name': 'cinderVolumeName',
'size': 1,
'id': 'cinderVolumeID',
'volume_type_id': None,
'created_at': timeutils.utcnow()}
self.drv.create_volume(testvol)
cmd = {"Size": int(testvol['size']) * units.Gi,
"PolicyUUID": '00000000-00000000-0000-000000000000',
"PoolUUID": "FakePoolB_id",
"Name": testvol['id'], }
expected = [mock.call.get('TierStore/Pools/by-id/'),
mock.call.post('TierStore/Volumes/by-id/', cmd)]
self.drv.conn.assert_has_calls(expected)
def test_delete_volume_sucess(self, connmock):
self.drv.conn = connmock.return_value
testvol = {'project_id': 'testproject',
'name': 'cinderVolumeName',
'size': 1,
'id': 'cinderVolumeID',
'volume_type_id': None,
'created_at': timeutils.utcnow()}
self.drv.conn.get.return_value = basic_vol_response
self.drv.delete_volume(testvol)
expected = [mock.call.get('TierStore/Volumes/by-id/'),
mock.call.delete('TierStore/Volumes/by-id/FakeBasicVolID')]
self.drv.conn.assert_has_calls(expected)
def test_create_snapshot_sucess(self, connmock):
self.drv.conn = connmock.return_value
snapshot = {'volume_id': 'cinderVolumeID',
'id': 'a720b3c0-d1f0-11e1-9b23-1234500cab39', }
self.drv.conn.get.return_value = basic_vol_response
cmd = {"VolumeUUID": "FakeBasicVolID",
"Name": snapshot['id'], }
self.drv.create_snapshot(snapshot)
expected = [mock.call.get('TierStore/Volumes/by-id/'),
mock.call.post('TierStore/Snapshots/by-id/', cmd), ]
self.drv.conn.assert_has_calls(expected)
def test_delete_snapshot_sucess(self, connmock):
self.drv.conn = connmock.return_value
snapshot = {'volume_id': '1dead3c0-d1f0-beef-9b23-1274500cab58',
'id': 'cinderSnapshotID'}
self.drv.conn.get.return_value = basic_snapshot_response
self.drv.delete_snapshot(snapshot)
expected = [mock.call.get('TierStore/Snapshots/by-id/'),
mock.call.delete(
('TierStore/Snapshots/by-id/' +
'407115424bb9539c')), ]
self.drv.conn.assert_has_calls(expected)
def test_create_volume_from_snapshot_simple_sucess(self, connmock):
self.drv.conn = connmock.return_value
testvol = {'project_id': 'testproject',
'name': 'cinderVolumeName',
'size': 1,
'id': 'cinderVolumeID',
'volume_type_id': None,
'created_at': timeutils.utcnow()}
snapshot = {'volume_id': testvol['id'],
'id': 'cinderSnapshotID'}
self.drv.conn.get.return_value = basic_snapshot_response
cmd = {"ParentLayerId": "407115424bb9539c",
"Name": testvol['id'],
"PolicyUUID": '00000000-00000000-0000-000000000000'}
self.drv.create_volume_from_snapshot(testvol, snapshot)
expected = [mock.call.get('TierStore/Snapshots/by-id/'),
mock.call.put(
'TierStore/Snapshots/functions/CloneSnapshot', cmd), ]
self.drv.conn.assert_has_calls(expected)
def test_initialize_connection_no_usable_Networks_fail(self, connmock):
self.drv.conn = connmock.return_value
connector = {'initiator': 'fake:01'}
testvol = {'project_id': 'testproject',
'name': 'cinderVolumeName',
'size': 1,
'id': 'cinderVolumeID',
'volume_type_id': None,
'created_at': timeutils.utcnow(),
'provider_auth': {}}
cmd = {"GroupName": "fake:01",
"InitiatorList": ["fake:01"]}
cmd2 = {"AclGroupList": ["3"], }
netResponse = copy.deepcopy(basic_net_response)
netResponse[4]['OperationalState'] = "down"
get_effect = [basic_vol_response,
basic_acl_group_response,
basic_vol_response,
netResponse, ]
self.drv.conn.get.side_effect = get_effect
self.assertRaises(exception.VolumeDriverException,
self.drv.initialize_connection, testvol,
connector)
expected = [mock.call.get('TierStore/Volumes/by-id/'),
mock.call.post('TierStore/ACLGroup/by-id/', cmd),
mock.call.get('TierStore/ACLGroup/by-id/'),
mock.call.put('TierStore/Volumes/by-id/FakeBasicVolID',
cmd2),
mock.call.get('TierStore/Volumes/by-id/'),
mock.call.get('System/Network/by-id/'), ]
self.drv.conn.assert_has_calls(expected)
def test_initialize_connection_simple_sucess(self, connmock):
self.drv.conn = connmock.return_value
connector = {'initiator': 'fake:01'}
testvol = {'project_id': 'testproject',
'name': 'cinderVolumeName',
'size': 1,
'id': 'cinderVolumeID',
'volume_type_id': None,
'created_at': timeutils.utcnow(),
'provider_auth': {}}
cmd = {"GroupName": "fake:01",
"InitiatorList": ["fake:01"]}
cmd2 = {"AclGroupList": ["3"], }
netResponse = copy.deepcopy(basic_net_response)
netResponse[2]['OperationalState'] = "up"
get_effect = [basic_vol_response,
basic_acl_group_response,
basic_vol_response,
netResponse, ]
self.drv.conn.get.side_effect = get_effect
result = self.drv.initialize_connection(testvol, connector)
expected = {'driver_volume_type': 'iscsi',
'data': {'target_lun': 0,
'target_portal': u'10.10.2.84:3260',
'target_iqn': (
'iqn.2010-11.com.ngs:Volume:FakeBasicVolID'),
'target_discovered': False,
'volume_id': 'cinderVolumeID'}}
self.assertEqual(result, expected, "wrong result from init connection")
expected = [mock.call.get('TierStore/Volumes/by-id/'),
mock.call.post('TierStore/ACLGroup/by-id/', cmd),
mock.call.get('TierStore/ACLGroup/by-id/'),
mock.call.put('TierStore/Volumes/by-id/FakeBasicVolID',
cmd2),
mock.call.get('TierStore/Volumes/by-id/'),
mock.call.get('System/Network/by-id/'), ]
self.drv.conn.assert_has_calls(expected)
def test_terminate_connection_single_delete_sucess(self, connmock):
self.drv.conn = connmock.return_value
connector = {'initiator': 'fake:01'}
testvol = {'project_id': 'testproject',
'name': 'cinderVolumeName',
'size': 1,
'id': 'cinderVolumeID',
'volume_type_id': None,
'created_at': timeutils.utcnow(),
'provider_auth': {}}
cmd = {"AclGroupList": ["1"], }
get_effect = [basic_vol_response,
basic_acl_group_response,
basic_acl_group_response,
basic_vol_response, ]
self.drv.conn.get.side_effect = get_effect
self.drv.terminate_connection(testvol, connector)
expected = [mock.call.get('TierStore/Volumes/by-id/'),
mock.call.get('TierStore/ACLGroup/by-id/'),
mock.call.put('TierStore/Volumes/by-id/FakeBasicVolID',
cmd),
mock.call.get('TierStore/ACLGroup/by-id/'),
mock.call.get('TierStore/Volumes/by-id/'),
mock.call.delete('TierStore/ACLGroup/by-id/3')]
self.drv.conn.assert_has_calls(expected)
def test_terminate_connection_multiple_no_delete(self, connmock):
self.drv.conn = connmock.return_value
connector = {'initiator': 'fake:01'}
testvol = {'project_id': 'testproject',
'name': 'cinderVolumeName',
'size': 1,
'id': 'cinderVolumeID',
'volume_type_id': None,
'created_at': timeutils.utcnow(),
'provider_auth': {}}
cmd = {"AclGroupList": ["1"], }
return2vol = copy.deepcopy(basic_vol_response)
return2vol.append(copy.deepcopy(basic_vol_response[0]))
return2vol[1]['AclGroupList'] = ["3"]
get_effect = [basic_vol_response,
basic_acl_group_response,
basic_acl_group_response,
return2vol, ]
self.drv.conn.get.side_effect = get_effect
self.drv.terminate_connection(testvol, connector)
expected = [mock.call.get('TierStore/Volumes/by-id/'),
mock.call.get('TierStore/ACLGroup/by-id/'),
mock.call.put('TierStore/Volumes/by-id/FakeBasicVolID',
cmd),
mock.call.get('TierStore/ACLGroup/by-id/'),
mock.call.get('TierStore/Volumes/by-id/')]
self.drv.conn.assert_has_calls(expected)
def test_terminate_connection_multiple_delete(self, connmock):
self.drv.conn = connmock.return_value
connector = {'initiator': 'fake:01'}
testvol = {'project_id': 'testproject',
'name': 'cinderVolumeName',
'size': 1,
'id': 'cinderVolumeID',
'volume_type_id': None,
'created_at': timeutils.utcnow(),
'provider_auth': {}}
cmd = {"AclGroupList": ["1"], }
return2vol = copy.deepcopy(basic_vol_response)
return2vol.append(copy.deepcopy(basic_vol_response[0]))
get_effect = [basic_vol_response,
basic_acl_group_response,
basic_acl_group_response,
return2vol, ]
self.drv.conn.get.side_effect = get_effect
self.drv.terminate_connection(testvol, connector)
expected = [mock.call.get('TierStore/Volumes/by-id/'),
mock.call.get('TierStore/ACLGroup/by-id/'),
mock.call.put('TierStore/Volumes/by-id/FakeBasicVolID',
cmd),
mock.call.get('TierStore/ACLGroup/by-id/'),
mock.call.get('TierStore/Volumes/by-id/'),
mock.call.delete('TierStore/ACLGroup/by-id/3')]
self.drv.conn.assert_has_calls(expected)
def test_create_cloned_volume_simple_sucess(self, connmock):
self.drv.conn = connmock.return_value
srcvol = {'id': 'cinderVolumeID'}
dstvol = {'project_id': 'testproject',
'name': 'cinderVolumeName',
'size': 1,
'id': 'cinderVolumeID-dst',
'volume_type_id': None,
'created_at': timeutils.utcnow()}
cmd = {'VolumeUUID': 'FakeBasicVolID',
'Name': 'mockedFakeUUID'}
# also mock _getSnapshotByName because of the random snapshotname.
self.drv._get_snapshot_by_name = mock.MagicMock()
self.drv._get_snapshot_by_name.return_value = \
basic_snapshot_response[0]
cmd2 = {"ParentLayerId": "407115424bb9539c",
"Name": "cinderVolumeID-dst",
"PolicyUUID": "00000000-00000000-0000-000000000000"}
get_effect = [basic_vol_response, ]
self.drv.conn.get.side_effect = get_effect
with mock.patch('cinder.volume.drivers.fusionio.ioControl.uuid',
autospec=True) as uuidmock:
uuidmock.uuid4.return_value = cmd['Name']
self.drv.create_cloned_volume(dstvol, srcvol)
expected = [mock.call.get('TierStore/Volumes/by-id/'),
mock.call.post('TierStore/Snapshots/by-id/', cmd),
mock.call.put(('TierStore/Snapshots/functions/' +
'CloneSnapshot'), cmd2), ]
self.drv.conn.assert_has_calls(expected)
def test_create_cloned_volume_snapfails(self, connmock):
self.drv.conn = connmock.return_value
# this operation is a 2 part process, snap, then clone.
# This tests for the snap failing
srcvol = {'id': 'cinderVolumeID'}
dstvol = {'project_id': 'testproject',
'name': 'cinderVolumeName',
'size': 1,
'id': 'cinderVolumeID-dst',
'volume_type_id': None,
'created_at': timeutils.utcnow()}
cmd = {'VolumeUUID': 'FakeBasicVolID',
'Name': 'mockedFakeUUID'}
get_effect = [basic_vol_response, ]
self.drv.conn.get.side_effect = get_effect
self.drv.conn.post.side_effect = requests.exceptions.HTTPError
with mock.patch('cinder.volume.drivers.fusionio.ioControl.uuid',
autospec=True) as uuidmock:
uuidmock.uuid4.return_value = cmd['Name']
self.assertRaises(requests.exceptions.HTTPError,
self.drv.create_cloned_volume,
dstvol, srcvol)
expected = [mock.call.get('TierStore/Volumes/by-id/'),
mock.call.post('TierStore/Snapshots/by-id/', cmd), ]
self.drv.conn.assert_has_calls(expected)
def test_create_cloned_volume_clonefails(self, connmock):
self.drv.conn = connmock.return_value
srcvol = {'id': 'cinderVolumeID'}
dstvol = {'project_id': 'testproject',
'name': 'cinderVolumeName',
'size': 1,
'id': 'cinderVolumeID-dst',
'volume_type_id': None,
'created_at': timeutils.utcnow()}
get_effect = [basic_vol_response,
basic_snapshot_response[0], ]
self.drv.conn.get.side_effect = get_effect
# also mock _getSnapshotByName because of the random snapshotname.
self.drv._get_snapshot_by_name = mock.MagicMock()
self.drv._get_snapshot_by_name.return_value = \
basic_snapshot_response[0]
cmd = {'VolumeUUID': 'FakeBasicVolID',
'Name': 'mockedFakeUUID'}
cmd2 = {"ParentLayerId": "407115424bb9539c",
"Name": "cinderVolumeID-dst",
"PolicyUUID": "00000000-00000000-0000-000000000000"}
self.drv.conn.put.side_effect = requests.exceptions.HTTPError
with mock.patch('cinder.volume.drivers.fusionio.ioControl.uuid',
autospec=True) as uuidmock:
uuidmock.uuid4.return_value = cmd['Name']
self.assertRaises(requests.exceptions.HTTPError,
self.drv.create_cloned_volume,
dstvol, srcvol)
expected = [mock.call.get('TierStore/Volumes/by-id/'),
mock.call.post('TierStore/Snapshots/by-id/', cmd),
mock.call.put(('TierStore/Snapshots/functions/' +
'CloneSnapshot'), cmd2),
mock.call.delete(('TierStore/Snapshots/by-id/' +
cmd2['ParentLayerId'])), ]
self.drv.conn.assert_has_calls(expected)
def test_get_volume_stats_simple_sucess(self, connmock):
self.drv.conn = connmock.return_value
self.drv.conn.get.side_effect = [basic_pools_response, ]
result = self.drv.get_volume_stats(refresh=True)
self.assertEqual(basic_pools_response[0]['PagingTotalMB'] +
basic_pools_response[1]['PagingTotalMB'],
result['total_capacity_gb'],
"capacity calc wrong")
self.assertEqual(self.VERSION, result['driver_version'],
"Driver/Test version Mismatch")
def test_create_volume_QoS_by_presets(self, connmock):
preset_qos = models.VolumeMetadata(key='fio-qos', value='Policy 2')
testvol = {'project_id': 'testprjid',
'name': 'testvol',
'size': 1,
'id': 'a720b3c0-d1f0-11e1-9b23-0800200c9a66',
'volume_metadata': [preset_qos],
'volume_type_id': None,
'created_at': timeutils.utcnow()}
expected_qos_result = '00000000-00000000-0000-000000000008' # Policy 2
qos = self.drv._set_qos_presets(testvol)
self.assertEqual(qos, expected_qos_result)
def test_create_volume_Qos_by_volumeType_QoSspec(self, connmock):
qos_ref = qos_specs.create(self.ctxt,
'qos-specs-1', {'fio-qos': 'Policy 2'})
type_ref = volume_types.create(self.ctxt,
"type1",
{"volume_backend_name": "fio-ioControl",
"qos:fio-qos": "Policy 4"}
)
qos_specs.associate_qos_with_type(self.ctxt,
qos_ref['id'],
type_ref['id'])
expected_qos_result = '00000000-00000000-0000-000000000008' # Policy 2
qos = self.drv._set_qos_by_volume_type(type_ref['id'])
self.assertEqual(qos, expected_qos_result)
def test_create_volume_Qos_by_volumeType_extraSpec(self, connmock):
type_ref = volume_types.create(self.ctxt,
"type1",
{"volume_backend_name": "fio-ioControl",
"qos:fio-qos": "Policy 4"}
)
expected_qos_result = '00000000-00000000-0000-000000000002' # Policy 4
qos = self.drv._set_qos_by_volume_type(type_ref['id'])
self.assertEqual(qos, expected_qos_result)
def test_extend_volume_simple_success(self, connmock):
self.drv.conn = connmock.return_value
testvol = {'project_id': 'testproject',
'name': 'cinderVolumeName',
'size': 1,
'id': 'cinderVolumeID',
'volume_type_id': None,
'created_at': timeutils.utcnow()}
new_size = 10
cmd = {"Size": int(new_size) * units.Gi}
self.drv.conn.get.side_effect = [basic_vol_response, ]
self.drv.extend_volume(testvol, new_size)
expected = [mock.call.get('TierStore/Volumes/by-id/'),
mock.call.put('TierStore/Volumes/by-id/FakeBasicVolID',
cmd)]
self.drv.conn.assert_has_calls(expected)