From 3983ca1cd54855c537d6bf196f4cbdc7762970a5 Mon Sep 17 00:00:00 2001 From: Mike Perez Date: Thu, 19 Mar 2015 12:29:30 -0700 Subject: [PATCH] Removing FusionIO driver for no reported CI CI deadlines were set and pushed since last year. An email about this requirement and the deadline of March 19th 2015 has been sent to each individual driver maintainer, as well as the mailing list [1]. This driver is being removed because the maintainer has chosen not to respond to the CI deadline email and report a CI to ensure their driver integration is successful. Therfore, we can not validate the driver is working in Cinder today in a continuous way. DocImpact [1] - http://lists.openstack.org/pipermail/openstack-dev/2015-January/054614.html Change-Id: Ic5881eec84e3069f80543bb20b0132ddb20d90ad --- cinder/tests/test_fusionio_ioControl.py | 842 -------------------- cinder/volume/drivers/fusionio/__init__.py | 0 cinder/volume/drivers/fusionio/ioControl.py | 557 ------------- 3 files changed, 1399 deletions(-) delete mode 100644 cinder/tests/test_fusionio_ioControl.py delete mode 100644 cinder/volume/drivers/fusionio/__init__.py delete mode 100644 cinder/volume/drivers/fusionio/ioControl.py diff --git a/cinder/tests/test_fusionio_ioControl.py b/cinder/tests/test_fusionio_ioControl.py deleted file mode 100644 index e761d71f6..000000000 --- a/cinder/tests/test_fusionio_ioControl.py +++ /dev/null @@ -1,842 +0,0 @@ -# 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_log import log as logging -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 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) diff --git a/cinder/volume/drivers/fusionio/__init__.py b/cinder/volume/drivers/fusionio/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/cinder/volume/drivers/fusionio/ioControl.py b/cinder/volume/drivers/fusionio/ioControl.py deleted file mode 100644 index 23e69c782..000000000 --- a/cinder/volume/drivers/fusionio/ioControl.py +++ /dev/null @@ -1,557 +0,0 @@ -# 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. - -""" -Fusion-io Driver for the ioControl Hybrid storage subsystem -""" - -import copy -import hashlib -import json -import random -import uuid - -from oslo_config import cfg -from oslo_log import log as logging -from oslo_utils import units -import requests - -from cinder import exception -from cinder.i18n import _, _LW -from cinder.openstack.common import loopingcall -from cinder.volume.drivers.san import san -from cinder.volume import qos_specs -from cinder.volume import volume_types - -LOG = logging.getLogger(__name__) - -fusionio_iocontrol_opts = [ - cfg.IntOpt('fusionio_iocontrol_targetdelay', - default=5, - help='amount of time wait for iSCSI target to come online'), - cfg.IntOpt('fusionio_iocontrol_retry', - default=3, - help='number of retries for GET operations'), - cfg.BoolOpt('fusionio_iocontrol_verify_cert', - default=True, - help='verify the array certificate on each transaction'), ] - -CONF = cfg.CONF -CONF.register_opts(fusionio_iocontrol_opts) - - -class FIOconnection(object): - """Connection class for connection to ioControl array.""" - - APIVERSION = '1.1' - - def _complete_uri(self, suburi=None, ver='1', loc='en'): - uri = "https://" + self.array_addr + "/API/" - if ver is not None: - uri = uri + ver + "/" - if loc is not None: - uri = uri + loc + "/" - if suburi is not None: - uri = uri + suburi - return uri - - def __init__(self, array_addr, array_login, array_passwd, retry, verify): - self.client = "client=openstack" - self.defhdrs = {"User-Agent": "OpenStack-agent", - "Content-Type": "application/json"} - self.array_addr = array_addr - self.array_login = array_login - self.hashpass = hashlib.md5() - self.hashpass.update(array_passwd) - self.login_content = ("username=" + array_login + "&hash=" + - self.hashpass.hexdigest()) - self.retry = retry - self.verify = verify - # check the version of the API on the array. We only support 1.1 - # for now. - resp = requests.get(url=("https://" + array_addr + "/AUTH/Version"), - headers=self.defhdrs, verify=self.verify) - resp.raise_for_status() - dictresp = resp.json() - if dictresp["Version"] != self.APIVERSION: - msg = _("FIO ioControl API version not supported") - raise exception.VolumeDriverException(message=msg) - LOG.debug('FIO Connection initialized to %s' % array_addr) - - def _create_session(self): - # get the session id - res = requests.post(url=("https://" + self.array_addr + - "/AUTH/SESSION"), - data=self.client, - headers=self.defhdrs, - verify=self.verify) - res.raise_for_status() - result = res.json() - session_key = result["id"] - hdrs = copy.deepcopy(self.defhdrs) - hdrs["Cookie"] = "session=" + session_key - # Authenticate the session - res = requests.put(url=("https://" + self.array_addr + - "/AUTH/SESSION/" + session_key), - data=self.login_content, - headers=self.defhdrs, - verify=self.verify) - try: - res.raise_for_status() - except requests.exceptions: - self._delete_session(hdrs) - raise - result = res.json() - if result["Status"] != 1: - # Authentication error delete the session ID - self._delete_session(hdrs) - msg = (_('FIO ioControl Authentication Error: %s') % (result)) - raise exception.VolumeDriverException(message=msg) - return hdrs - - def _delete_session(self, hdrs): - session = hdrs["Cookie"].split('=')[1] - requests.delete(url=("https://" + self.array_addr + - "/AUTH/SESSION/" + session), - headers=self.defhdrs, - verify=self.verify) - - def get(self, suburl): - session_hdrs = self._create_session() - trynum = 0 - try: - while (trynum < self.retry): - trynum += 1 - res = requests.get(url=self._complete_uri(suburl), - headers=session_hdrs, - verify=self.verify) - res.raise_for_status() - # work around a bug whereby bad json is returned by the array - try: - jres = res.json() - break - except Exception: - if (trynum == self.retry): - # this shouldn't happen, but check for it - msg = (_('FIO ioControl persistent json Error.')) - raise exception.VolumeDriverException(message=msg) - pass - finally: - # deal with the bad result here - self._delete_session(session_hdrs) - return jres - - def put(self, suburl, content=None): - session_hdrs = self._create_session() - try: - result = requests.put(url=self._complete_uri(suburl), - data=json.dumps(content, - sort_keys=True), - headers=session_hdrs, - verify=self.verify) - result.raise_for_status() - finally: - self._delete_session(session_hdrs) - return - - def post(self, suburl, content=None): - session_hdrs = self._create_session() - try: - result = requests.post(url=self._complete_uri(suburl), - data=json.dumps(content, - sort_keys=True), - headers=session_hdrs, - verify=self.verify) - result.raise_for_status() - finally: - self._delete_session(session_hdrs) - return - - def delete(self, suburl,): - session_hdrs = self._create_session() - try: - result = requests.delete(url=self._complete_uri(suburl), - headers=session_hdrs, - verify=self.verify) - result.raise_for_status() - finally: - self._delete_session(session_hdrs) - return - - -class FIOioControlDriver(san.SanISCSIDriver): - """Fusion-io ioControl iSCSI volume driver.""" - - VERSION = '1.0.0' - - def __init__(self, *args, **kwargs): - super(FIOioControlDriver, self).__init__(*args, **kwargs) - LOG.debug('FIO __init__ w/ %s' % kwargs) - self.configuration.append_config_values(fusionio_iocontrol_opts) - self.fio_qos_dict = {} - - def _get_volume_by_name(self, name): - result = self.conn.get("TierStore/Volumes/by-id/") - vol = [x for x in result - if x['Name'] == name] - if len(vol) == 1: - return vol[0] - elif len(vol) == 0: - raise exception.VolumeNotFound(name) - else: - msg = (_("FIO _get_volume_by_name Error: %(name)s, %(len)s") % - {'name': name, - 'len': len(vol)}) - raise exception.VolumeDriverException(msg) - - def _get_acl_by_name(self, name): - result = self.conn.get("TierStore/ACLGroup/by-id/") - acl = [x for x in result - if x['GroupName'] == name] - if len(acl) == 1: - return acl[0] - elif len(acl) == 0: - return [] - else: - msg = (_("FIO _get_acl_by_name Error: %(name)s, %(len)s") % - {'name': name, - 'len': len(acl), }) - raise exception.VolumeDriverException(message=msg) - - def _get_snapshot_by_name(self, name): - result = self.conn.get("TierStore/Snapshots/by-id/") - snap = [x for x in result - if x['Name'] == name] - if len(snap) == 1: - return snap[0] - elif len(snap) == 0: - raise exception.SnapshotNotFound(name) - else: - msg = (_("FIO _get_snapshot_by_name Error: %(name)s, %(len)s") % - {'name': name, - 'len': len(snap), }) - raise exception.VolumeDriverException(message=msg) - - def _set_qos_presets(self, volume): - valid_presets = self.fio_qos_dict.keys() - - presets = [i.value for i in volume.get('volume_metadata') - if i.key == 'fio-qos' and i.value in valid_presets] - if len(presets) > 0: - if len(presets) > 1: - LOG.warning(_LW('More than one valid preset was ' - 'detected, using %s') % presets[0]) - return self.fio_qos_dict[presets[0]] - - def _set_qos_by_volume_type(self, type_id): - valid_presets = self.fio_qos_dict.keys() - volume_type = volume_types.get_volume_type(ctxt=None, - id=type_id) - qos_specs_id = volume_type.get('qos_specs_id') - specs = volume_type.get('extra_specs') - if qos_specs_id is not None: - kvs = qos_specs.get_qos_specs(ctxt=None, - id=qos_specs_id)['specs'] - else: - kvs = specs - for key, value in kvs.iteritems(): - if ':' in key: - fields = key.split(':') - key = fields[1] - if 'fio-qos' in key: - if value in valid_presets: - return self.fio_qos_dict[value] - - def do_setup(self, context): - LOG.debug('FIO do_setup() called') - required_flags = ['san_ip', - 'san_login', - 'san_password', ] - for flag in required_flags: - if not getattr(self.configuration, flag, None): - raise exception.InvalidInput(reason=_('%s is not set') % flag) - if not (self.configuration.san_ip and - self.configuration.san_login and - self.configuration.san_password): - raise exception.InvalidInput( - reason=_('All of ' - 'san_ip ' - 'san_login ' - 'san_password ' - 'must be set')) - self.conn = FIOconnection(self.configuration.san_ip, - self.configuration.san_login, - self.configuration.san_password, - self.configuration.fusionio_iocontrol_retry, - (self.configuration. - fusionio_iocontrol_verify_cert)) - result = self.conn.get("TierStore/Policies/by-id/") - for x in result: - self.fio_qos_dict[x['Name']] = x['id'] - - def check_for_setup_error(self): - pass - - def create_volume(self, volume): - LOG.debug('FIO create_volume() called: %s' % (volume['id'])) - # Roughly we pick the less full pool. - # Someday change the default the policy to be configurable - qos = self.fio_qos_dict['Policy 5'] - result = self.conn.get("TierStore/Pools/by-id/") - poola = result[0]['PagingTotalMB'] - result[0]['ExportedVolumeMB'] - poolb = result[1]['PagingTotalMB'] - result[1]['ExportedVolumeMB'] - if poola >= poolb: - pool = result[0]['id'] - else: - pool = result[1]['id'] - if volume.get('volume_metadata')is not None: - qos = self._set_qos_presets(volume) - - type_id = volume['volume_type_id'] - if type_id is not None: - qos = self._set_qos_by_volume_type(type_id) - - cmd = {"Size": int(volume['size']) * units.Gi, - "PolicyUUID": qos, - "PoolUUID": pool, - "Name": volume['id'], } - self.conn.post("TierStore/Volumes/by-id/", cmd) - LOG.debug(('FIO create_vol(%(id)s) on %(pool)s vals %(poola)s ' - '%(poolb)s') % - {'id': volume['id'], - 'pool': pool, - 'poola': poola, - 'poolb': poolb}) - - def delete_volume(self, volume): - LOG.debug('FIO delete_volume() volID %s' % (volume['id'])) - vol = self._get_volume_by_name(volume['id']) - self.conn.delete("TierStore/Volumes/by-id/" + vol['id']) - - def ensure_export(self, context, volume): - pass - - def create_export(self, context, volume): - pass - - def remove_export(self, context, volume): - pass - - def initialize_connection(self, volume, connector): - LOG.debug('FIO init_connection() w/ %(id)s and %(conn)s' % - {'id': volume['id'], - 'conn': connector['initiator']}) - # setup the access group each initiator will go in a unique access - # group. - # TODO(ebalduf) implement w/ CHAP - volumedata = self._get_volume_by_name(volume['id']) - cmd = {"GroupName": connector['initiator'], - "InitiatorList": [connector['initiator']]} - self.conn.post("TierStore/ACLGroup/by-id/", cmd) - - acl = self._get_acl_by_name(connector['initiator']) - if acl is not []: - cmd = {"AclGroupList": [str(acl['id'])], } - self.conn.put("TierStore/Volumes/by-id/" + volumedata['id'], cmd) - else: - # this should never happen, but check for it in case - msg = _('FIO: ACL does not exist!') - raise exception.VolumeDriverException(message=msg) - # handle the fact that the Application of the ACL to the volume - # is asynchronous. In the future we'll add a call back to the API - - def _wait_routine(): - # unfortunately, the array API at this time doesn't have any - # way to poll. In the future we will add that ability and - # this routine is where were will poll for ready. - if self._looping_count == 0: - self._looping_count += 1 - else: - raise loopingcall.LoopingCallDone() - - # time.sleep(self.configuration.fusionio_iocontrol_targetdelay) - self._looping_count = 0 - timer = loopingcall.FixedIntervalLoopingCall(_wait_routine) - timer.start( - interval=self.configuration.fusionio_iocontrol_targetdelay).wait() - volumedata = self._get_volume_by_name(volume['id']) - - properties = {} - properties['target_discovered'] = False - properties['target_iqn'] = volumedata['IQN'] - properties['target_lun'] = 0 - properties['volume_id'] = volume['id'] - - result = self.conn.get("System/Network/by-id/") - - # probably way too complicated, but pick a random network interface - # on the controller this LUN is owned by - networksinfo = [x for x in result - if x['OperationalState'] == 'up' - if x['IsManagementPort'] is not True - if x['IsReplicationPort'] is not True - if x['ControllerUID'] == - volumedata['CurrentOwnerUUID']] - LOG.debug('NetworkInfo %s' % (networksinfo)) - if len(networksinfo): - ipaddr = (networksinfo[random.randint(0, len(networksinfo) - 1)] - ['NetworkAddress']) - else: - msg = _('No usable Networks found: %s') % (result) - raise exception.VolumeDriverException(message=msg) - properties['target_portal'] = unicode('%s:%s' % (ipaddr, '3260')) - - auth = volume['provider_auth'] - if auth: - (auth_method, auth_username, auth_secret) = auth.split() - - properties['auth_method'] = auth_method - properties['auth_username'] = auth_username - properties['auth_password'] = auth_secret - - LOG.debug('Result from initialize connection: %s' % properties) - return { - 'driver_volume_type': 'iscsi', - 'data': properties, - } - - def create_snapshot(self, snapshot): - LOG.debug(('FIO create_snapshot() vol ID: %(volID)s snapID ' - '%(snapID)s') % - {'volID': snapshot['volume_id'], - 'snapID': snapshot['id']}) - vol = self._get_volume_by_name(snapshot['volume_id']) - cmd = {"VolumeUUID": vol['id'], - "Name": snapshot['id'], } - self.conn.post("TierStore/Snapshots/by-id/", cmd) - - def delete_snapshot(self, snapshot): - LOG.debug('FIO delete_snapshot() SnapID: %s' % (snapshot['id'])) - snap = self._get_snapshot_by_name(snapshot['id']) - self.conn.delete("TierStore/Snapshots/by-id/" + snap['id']) - - def create_volume_from_snapshot(self, volume, snapshot): - LOG.debug('FIO create_volume_from_snapshot() w/ %s' % - volume['id']) - - qos = self.fio_qos_dict['Policy 5'] - if volume.get('volume_metadata')is not None: - qos = self._set_qos_presets(volume) - - type_id = volume['volume_type_id'] - if type_id is not None: - qos = self._set_qos_by_volume_type(type_id) - snap = self._get_snapshot_by_name(snapshot['id']) - cmd = {"ParentLayerId": snap['id'], - "Name": volume['id'], - "PolicyUUID": qos} - self.conn.put("TierStore/Snapshots/functions/CloneSnapshot", cmd) - - def _delete_acl_by_name(self, name): - aclname = self._get_acl_by_name(name) - if aclname is []: - return - result = self.conn.get("TierStore/Volumes/by-id/") - inuse = False - for vol in result: - for acl in vol['AclGroupList']: - if int(acl) == aclname['id']: - inuse = True - break - if inuse: - break - if not inuse: - result = self.conn.delete("TierStore/ACLGroup/by-id/" + - str(aclname['id'])) - - def terminate_connection(self, volume, connector, **kwargs): - LOG.debug('FIO terminate_connection() w/ %(id)s %(conn)s ' % - {'id': volume['id'], - 'conn': connector['initiator']}) - vol = self._get_volume_by_name(volume['id']) - acl = self._get_acl_by_name("Deny Access") - if acl is []: - msg = _('FIO: ACL does not exist!') - raise exception.VolumeDriverException(message=msg) - cmd = {"AclGroupList": [str(acl['id'])], } - self.conn.put("TierStore/Volumes/by-id/" + vol['id'], cmd) - self._delete_acl_by_name(connector['initiator']) - - def create_cloned_volume(self, volume, src_vref): - LOG.debug('FIO create_cloned_volume() w/ %(id)s %(src)s' % - {'id': volume['id'], - 'src': src_vref}) - qos = self.fio_qos_dict['Policy 5'] - # take a snapshot of the volume (use random UUID for name) - snapshotname = str(uuid.uuid4()) - vol = self._get_volume_by_name(src_vref['id']) - cmd = {"VolumeUUID": vol['id'], - "Name": snapshotname, } - self.conn.post("TierStore/Snapshots/by-id/", cmd) - - # create a volume from the snapshot with the new name. - # Rollback = Delete the snapshot if needed. - if volume.get('volume_metadata')is not None: - qos = self._set_qos_presets(volume) - - type_id = volume['volume_type_id'] - if type_id is not None: - qos = self._set_qos_by_volume_type(type_id) - - snap = self._get_snapshot_by_name(snapshotname) - cmd = {"ParentLayerId": snap['id'], - "Name": volume['id'], - "PolicyUUID": qos, } - try: - # watch for any issues here, and if there are, clean up the - # snapshot and re-raise - self.conn.put("TierStore/Snapshots/functions/CloneSnapshot", cmd) - except Exception: - snap = self._get_snapshot_by_name(snapshotname) - self.conn.delete("TierStore/Snapshots/by-id/" + snap['id']) - raise - - def get_volume_stats(self, refresh=False): - """Retrieve status info from volume group.""" - LOG.debug("FIO Updating volume status") - if refresh: - result = self.conn.get("TierStore/Pools/by-id/") - data = {} - backend_name = self.configuration.safe_get('volume_backend_name') - data["volume_backend_name"] = (backend_name - or self.__class__.__name__) - data["vendor_name"] = 'Fusion-io Inc' - data["driver_version"] = self.VERSION - data["storage_protocol"] = 'iSCSI' - data['total_capacity_gb'] = (result[0]['PagingTotalMB'] + - result[1]['PagingTotalMB']) - data['free_capacity_gb'] = (max((result[0]['PagingTotalMB'] - - result[0]['ExportedVolumeMB']), - (result[1]['PagingTotalMB'] - - result[1]['ExportedVolumeMB']))) - data['reserved_percentage'] = 10 - data['QoS_support'] = True - self._stats = data - - LOG.debug('Result from status: %s' % data) - return self._stats - - def extend_volume(self, volume, new_size): - LOG.debug("FIO extend_volume %(id)s to %(size)s" % - {'id': volume['id'], - 'size': new_size}) - cmd = {"Size": int(new_size) * units.Gi} - vol = self._get_volume_by_name(volume['id']) - self.conn.put("TierStore/Volumes/by-id/" + vol['id'], cmd) -- 2.45.2