]> review.fuel-infra Code Review - openstack-build/cinder-build.git/commitdiff
Remove the HP CLIQ proxy driver
authorWalter A. Boring IV <walter.boring@hpe.com>
Tue, 3 Nov 2015 23:09:50 +0000 (15:09 -0800)
committerWalter A. Boring IV <walter.boring@hpe.com>
Fri, 13 Nov 2015 20:47:47 +0000 (12:47 -0800)
This patch removes the deprecated HP Lefthand
CLIQ/SSH based driver.  It also refactors the proxy
class, which was nothing but a wrapper for the 2
drivers.   The LeftHand driver is now entirely based
off of the REST client.

Implements: blueprint deprecate-hp-lefthand-cliq
DocImpact

Change-Id: I12d8bb7b7dcac8ae40796d9d169825265c634a33

cinder/tests/unit/test_hplefthand.py
cinder/volume/drivers/san/hp/hp_lefthand_cliq_proxy.py [deleted file]
cinder/volume/drivers/san/hp/hp_lefthand_iscsi.py
cinder/volume/drivers/san/hp/hp_lefthand_rest_proxy.py [deleted file]

index acd437a7f19bc21a169442a6196c03117110bce2..31dbcd1c466476d3fdb17001df5a1fefe19967c9 100644 (file)
 
 import mock
 from oslo_utils import units
-import six
 
 from cinder import context
 from cinder import exception
 from cinder import test
 from cinder.tests.unit import fake_hp_lefthand_client as hplefthandclient
 from cinder.volume.drivers.san.hp import hp_lefthand_iscsi
-from cinder.volume.drivers.san.hp import hp_lefthand_rest_proxy
 from cinder.volume import volume_types
 
 hpexceptions = hplefthandclient.hpexceptions
@@ -89,578 +87,10 @@ class HPLeftHandBaseDriver(object):
     driver_startup_call_stack = [
         mock.call.login('foo1', 'bar2'),
         mock.call.getClusterByName('CloudCluster1'),
-        mock.call.getCluster(1),
     ]
 
 
-class TestHPLeftHandCLIQISCSIDriver(HPLeftHandBaseDriver, test.TestCase):
-
-    def _fake_cliq_run(self, verb, cliq_args, check_exit_code=True):
-        """Return fake results for the various methods."""
-
-        def create_volume(cliq_args):
-            """Create volume CLIQ input for test.
-
-            input = "createVolume description="fake description"
-                                  clusterName=Cluster01 volumeName=fakevolume
-                                  thinProvision=0 output=XML size=1GB"
-            """
-            output = """<gauche version="1.0">
-                <response description="Operation succeeded."
-                          name="CliqSuccess" processingTime="181" result="0"/>
-                </gauche>"""
-            self.assertEqual(self.volume_name, cliq_args['volumeName'])
-            self.assertEqual('1', cliq_args['thinProvision'])
-            self.assertEqual('1GB', cliq_args['size'])
-            return output, None
-
-        def delete_volume(cliq_args):
-            """Delete volume CLIQ input for test.
-
-            input = "deleteVolume volumeName=fakevolume prompt=false
-                                  output=XML"
-            """
-            output = """<gauche version="1.0">
-                <response description="Operation succeeded."
-                          name="CliqSuccess" processingTime="164" result="0"/>
-                </gauche>"""
-            self.assertEqual(self.volume_name, cliq_args['volumeName'])
-            self.assertEqual('false', cliq_args['prompt'])
-            return output, None
-
-        def extend_volume(cliq_args):
-            """Extend volume CLIQ input for test.
-
-            input = "modifyVolume description="fake description"
-                                  volumeName=fakevolume
-                                  output=XML size=2GB"
-            """
-            output = """<gauche version="1.0">
-                <response description="Operation succeeded."
-                          name="CliqSuccess" processingTime="181" result="0"/>
-                </gauche>"""
-            self.assertEqual(self.volume_name, cliq_args['volumeName'])
-            self.assertEqual('2GB', cliq_args['size'])
-            return output, None
-
-        def assign_volume(cliq_args):
-            """Assign volume CLIQ input for test.
-
-            input = "assignVolumeToServer volumeName=fakevolume
-                                          serverName=fakehost
-                                          output=XML"
-            """
-            output = """<gauche version="1.0">
-                <response description="Operation succeeded."
-                          name="CliqSuccess" processingTime="174" result="0"/>
-                </gauche>"""
-            self.assertEqual(self.volume_name, cliq_args['volumeName'])
-            self.assertEqual(self.connector['host'],
-                             cliq_args['serverName'])
-            return output, None
-
-        def unassign_volume(cliq_args):
-            """Unassign volume CLIQ input for test.
-
-            input = "unassignVolumeToServer volumeName=fakevolume
-                                            serverName=fakehost output=XML
-            """
-            output = """<gauche version="1.0">
-                <response description="Operation succeeded."
-                          name="CliqSuccess" processingTime="205" result="0"/>
-                </gauche>"""
-            self.assertEqual(self.volume_name, cliq_args['volumeName'])
-            self.assertEqual(self.connector['host'],
-                             cliq_args['serverName'])
-            return output, None
-
-        def create_snapshot(cliq_args):
-            """Create snapshot CLIQ input for test.
-
-            input = "createSnapshot description="fake description"
-                                    snapshotName=fakesnapshot
-                                    volumeName=fakevolume
-                                    output=XML"
-            """
-            output = """<gauche version="1.0">
-                <response description="Operation succeeded."
-                          name="CliqSuccess" processingTime="181" result="0"/>
-                </gauche>"""
-            self.assertEqual(self.snapshot_name, cliq_args['snapshotName'])
-            self.assertEqual(self.volume_name, cliq_args['volumeName'])
-            return output, None
-
-        def delete_snapshot(cliq_args):
-            """Delete shapshot CLIQ input for test.
-
-            input = "deleteSnapshot snapshotName=fakesnapshot prompt=false
-                                    output=XML"
-            """
-            output = """<gauche version="1.0">
-                <response description="Operation succeeded."
-                          name="CliqSuccess" processingTime="164" result="0"/>
-                </gauche>"""
-            self.assertEqual(self.snapshot_name, cliq_args['snapshotName'])
-            self.assertEqual('false', cliq_args['prompt'])
-            return output, None
-
-        def create_volume_from_snapshot(cliq_args):
-            """Create volume from snapshot CLIQ input for test.
-
-            input = "cloneSnapshot description="fake description"
-                                   snapshotName=fakesnapshot
-                                   volumeName=fakevolume
-                                   output=XML"
-            """
-            output = """<gauche version="1.0">
-                <response description="Operation succeeded."
-                          name="CliqSuccess" processingTime="181" result="0"/>
-                </gauche>"""
-            self.assertEqual(self.snapshot_name, cliq_args['snapshotName'])
-            self.assertEqual(self.volume_name, cliq_args['volumeName'])
-            return output, None
-
-        def get_cluster_info(cliq_args):
-            """Get cluster info CLIQ input for test.
-
-            input = "getClusterInfo clusterName=Cluster01 searchDepth=1
-                                    verbose=0 output=XML"
-            """
-            output = """<gauche version="1.0">
-                <response description="Operation succeeded." name="CliqSuccess"
-                          processingTime="1164" result="0">
-                <cluster blockSize="1024" description=""
-                         maxVolumeSizeReplication1="622957690"
-                         maxVolumeSizeReplication2="311480287"
-                         minVolumeSize="262144" name="Cluster01"
-                         pageSize="262144" spaceTotal="633697992"
-                         storageNodeCount="2" unprovisionedSpace="622960574"
-                         useVip="true">
-                <nsm ipAddress="10.0.1.7" name="111-vsa"/>
-                <nsm ipAddress="10.0.1.8" name="112-vsa"/>
-                <vip ipAddress="10.0.1.6" subnetMask="255.255.255.0"/>
-                </cluster></response></gauche>"""
-            return output, None
-
-        def get_volume_info(cliq_args):
-            """Get volume info CLIQ input for test.
-
-            input = "getVolumeInfo volumeName=fakevolume output=XML"
-            """
-            output = """<gauche version="1.0">
-                <response description="Operation succeeded." name="CliqSuccess"
-                          processingTime="87" result="0">
-                <volume autogrowPages="4" availability="online"
-                        blockSize="1024" bytesWritten="0" checkSum="false"
-                        clusterName="Cluster01" created="2011-02-08T19:56:53Z"
-                        deleting="false" description="" groupName="Group01"
-                        initialQuota="536870912" isPrimary="true"
-                iscsiIqn="iqn.2003-10.com.lefthandnetworks:group01:25366:fakev"
-                maxSize="6865387257856" md5="9fa5c8b2cca54b2948a63d833097e1ca"
-                minReplication="1" name="vol-b" parity="0" replication="2"
-                reserveQuota="536870912" scratchQuota="4194304"
-                serialNumber="9fa5c8b2cca54b2948a63d8"
-                size="1073741824" stridePages="32" thinProvision="true">
-                <status description="OK" value="2"/>
-                <permission access="rw" authGroup="api-1"
-                            chapName="chapusername" chapRequired="true"
-                            id="25369" initiatorSecret="" iqn=""
-                            iscsiEnabled="true" loadBalance="true"
-                            targetSecret="supersecret"/>
-                </volume></response></gauche>"""
-            return output, None
-
-        def get_snapshot_info(cliq_args):
-            """Get snapshot info CLIQ input for test.
-
-            input = "getSnapshotInfo snapshotName=fakesnapshot output=XML"
-            """
-            output = """<gauche version="1.0">
-                <response description="Operation succeeded." name="CliqSuccess"
-                          processingTime="87" result="0">
-                <snapshot applicationManaged="false" autogrowPages="32768"
-                    automatic="false" availability="online" bytesWritten="0"
-                    clusterName="CloudCluster1" created="2013-08-26T07:03:44Z"
-                    deleting="false" description="" groupName="CloudGroup1"
-                    id="730" initialQuota="536870912" isPrimary="true"
-                    iscsiIqn="iqn.2003-10.com.lefthandnetworks:cloudgroup1:73"
-                    md5="a64b4f850539c07fb5ce3cee5db1fcce" minReplication="1"
-                    name="snapshot-7849288e-e5e8-42cb-9687-9af5355d674b"
-                    replication="2" reserveQuota="536870912" scheduleId="0"
-                    scratchQuota="4194304" scratchWritten="0"
-                    serialNumber="a64b4f850539c07fb5ce3cee5db1fcce"
-                    size="2147483648" stridePages="32"
-                    volumeSerial="a64b4f850539c07fb5ce3cee5db1fcce">
-               <status description="OK" value="2"/>
-               <permission access="rw"
-                     authGroup="api-34281B815713B78-(trimmed)51ADD4B7030853AA7"
-                     chapName="chapusername" chapRequired="true" id="25369"
-                     initiatorSecret="" iqn="" iscsiEnabled="true"
-                     loadBalance="true" targetSecret="supersecret"/>
-               </snapshot></response></gauche>"""
-            return output, None
-
-        def get_server_info(cliq_args):
-            """Get server info CLIQ input for test.
-
-            input = "getServerInfo serverName=fakeName"
-            """
-            output = """<gauche version="1.0"><response result="0"/>
-                     </gauche>"""
-            return output, None
-
-        def create_server(cliq_args):
-            """Create server CLIQ input for test.
-
-            input = "createServer serverName=fakeName initiator=something"
-            """
-            output = """<gauche version="1.0"><response result="0"/>
-                     </gauche>"""
-            return output, None
-
-        def test_error(cliq_args):
-            output = """<gauche version="1.0">
-                <response description="Volume '134234' not found."
-                name="CliqVolumeNotFound" processingTime="1083"
-                result="8000100c"/>
-                </gauche>"""
-            return output, None
-
-        def test_paramiko_1_13_0(cliq_args):
-
-            # paramiko 1.13.0 now returns unicode
-            output = six.text_type(
-                '<?xml version="1.0" encoding="UTF-8" standalone="no" ?>\n'
-                '<gauche version="1.0">\n\n  <response description="Operation'
-                ' succeeded." name="CliqSuccess" processingTime="423" '
-                'result="0">\n    <cluster adaptiveOptimization="false" '
-                'blockSize="1024" description="" maxVolumeSizeReplication1='
-                '"114594676736" minVolumeSize="262144" name="clusterdemo" '
-                'pageSize="262144" spaceTotal="118889644032" storageNodeCount='
-                '"1" unprovisionedSpace="114594676736" useVip="true">\n'
-                '      <nsm ipAddress="10.10.29.102" name="lefdemo1"/>\n'
-                '      <vip ipAddress="10.10.22.87" subnetMask='
-                '"255.255.224.0"/>\n    </cluster>\n  </response>\n\n'
-                '</gauche>\n    ')
-            return output, None
-
-        def test_paramiko_1_10_0(cliq_args):
-
-            # paramiko 1.10.0 returns python default encoding.
-            output = (
-                '<?xml version="1.0" encoding="UTF-8" standalone="no" ?>\n'
-                '<gauche version="1.0">\n\n  <response description="Operation'
-                ' succeeded." name="CliqSuccess" processingTime="423" '
-                'result="0">\n    <cluster adaptiveOptimization="false" '
-                'blockSize="1024" description="" maxVolumeSizeReplication1='
-                '"114594676736" minVolumeSize="262144" name="clusterdemo" '
-                'pageSize="262144" spaceTotal="118889644032" storageNodeCount='
-                '"1" unprovisionedSpace="114594676736" useVip="true">\n'
-                '      <nsm ipAddress="10.10.29.102" name="lefdemo1"/>\n'
-                '      <vip ipAddress="10.10.22.87" subnetMask='
-                '"255.255.224.0"/>\n    </cluster>\n  </response>\n\n'
-                '</gauche>\n    ')
-            return output, None
-
-        self.assertEqual('XML', cliq_args['output'])
-        try:
-            verbs = {'createVolume': create_volume,
-                     'deleteVolume': delete_volume,
-                     'modifyVolume': extend_volume,
-                     'assignVolumeToServer': assign_volume,
-                     'unassignVolumeToServer': unassign_volume,
-                     'createSnapshot': create_snapshot,
-                     'deleteSnapshot': delete_snapshot,
-                     'cloneSnapshot': create_volume_from_snapshot,
-                     'getClusterInfo': get_cluster_info,
-                     'getVolumeInfo': get_volume_info,
-                     'getSnapshotInfo': get_snapshot_info,
-                     'getServerInfo': get_server_info,
-                     'createServer': create_server,
-                     'testError': test_error,
-                     'testParamiko_1.10.1': test_paramiko_1_10_0,
-                     'testParamiko_1.13.1': test_paramiko_1_13_0}
-        except KeyError:
-            raise NotImplementedError()
-
-        return verbs[verb](cliq_args)
-
-    def setUp(self):
-        super(TestHPLeftHandCLIQISCSIDriver, self).setUp()
-
-        self.properties = {
-            'target_discovered': True,
-            'target_portal': '10.0.1.6:3260',
-            'target_iqn':
-            'iqn.2003-10.com.lefthandnetworks:group01:25366:fakev',
-            'volume_id': self.volume_id}
-
-    def default_mock_conf(self):
-
-        mock_conf = mock.Mock()
-        mock_conf.san_ip = '10.10.10.10'
-        mock_conf.san_login = 'foo'
-        mock_conf.san_password = 'bar'
-        mock_conf.san_ssh_port = 16022
-        mock_conf.san_clustername = 'CloudCluster1'
-        mock_conf.hplefthand_api_url = None
-        return mock_conf
-
-    def setup_driver(self, config=None):
-
-        if config is None:
-            config = self.default_mock_conf()
-
-        self.driver = hp_lefthand_iscsi.HPLeftHandISCSIDriver(
-            configuration=config)
-        self.driver.do_setup(None)
-
-        self.driver.proxy._cliq_run = mock.Mock(
-            side_effect=self._fake_cliq_run)
-        return self.driver.proxy._cliq_run
-
-    def test_create_volume(self):
-
-        # set up driver with default config
-        mock_cliq_run = self.setup_driver()
-
-        volume = {'name': self.volume_name, 'size': 1}
-        model_update = self.driver.create_volume(volume)
-        expected_iqn = "iqn.2003-10.com.lefthandnetworks:group01:25366:fakev 0"
-        expected_location = "10.0.1.6:3260,1 %s" % expected_iqn
-        self.assertEqual(expected_location, model_update['provider_location'])
-
-        expected = [
-            mock.call(
-                'createVolume', {
-                    'clusterName': 'CloudCluster1',
-                    'volumeName': 'fakevolume',
-                    'thinProvision': '1',
-                    'output': 'XML',
-                    'size': '1GB'},
-                True),
-            mock.call(
-                'getVolumeInfo', {
-                    'volumeName': 'fakevolume',
-                    'output': 'XML'},
-                True),
-            mock.call(
-                'getClusterInfo', {
-                    'clusterName': 'Cluster01',
-                    'searchDepth': '1',
-                    'verbose': '0',
-                    'output': 'XML'},
-                True)]
-
-        # validate call chain
-        mock_cliq_run.assert_has_calls(expected)
-
-    def test_delete_volume(self):
-
-        # set up driver with default config
-        mock_cliq_run = self.setup_driver()
-
-        volume = {'name': self.volume_name}
-        self.driver.delete_volume(volume)
-
-        expected = [
-            mock.call(
-                'getVolumeInfo', {
-                    'volumeName': 'fakevolume',
-                    'output': 'XML'},
-                True),
-            mock.call(
-                'deleteVolume', {
-                    'volumeName': 'fakevolume',
-                    'prompt': 'false',
-                    'output': 'XML'},
-                True)]
-
-        # validate call chain
-        mock_cliq_run.assert_has_calls(expected)
-
-    def test_extend_volume(self):
-
-        # set up driver with default config
-        mock_cliq_run = self.setup_driver()
-
-        volume = {'name': self.volume_name}
-        self.driver.extend_volume(volume, 2)
-
-        expected = [
-            mock.call(
-                'modifyVolume', {
-                    'volumeName': 'fakevolume',
-                    'output': 'XML',
-                    'size': '2GB'},
-                True)]
-
-        # validate call chain
-        mock_cliq_run.assert_has_calls(expected)
-
-    def test_initialize_connection(self):
-
-        # set up driver with default config
-        mock_cliq_run = self.setup_driver()
-
-        self.driver.proxy._get_iscsi_properties = mock.Mock(
-            return_value=self.properties)
-        volume = {'name': self.volume_name}
-        result = self.driver.initialize_connection(volume,
-                                                   self.connector)
-        self.assertEqual('iscsi', result['driver_volume_type'])
-        self.assertDictMatch(self.properties, result['data'])
-
-        expected = [
-            mock.call(
-                'getServerInfo', {
-                    'output': 'XML',
-                    'serverName': 'fakehost'},
-                False),
-            mock.call(
-                'assignVolumeToServer', {
-                    'volumeName': 'fakevolume',
-                    'serverName': 'fakehost',
-                    'output': 'XML'},
-                True)]
-
-        # validate call chain
-        mock_cliq_run.assert_has_calls(expected)
-
-    def test_terminate_connection(self):
-
-        # set up driver with default config
-        mock_cliq_run = self.setup_driver()
-
-        volume = {'name': self.volume_name}
-        self.driver.terminate_connection(volume, self.connector)
-
-        expected = [
-            mock.call(
-                'unassignVolumeToServer', {
-                    'volumeName': 'fakevolume',
-                    'serverName': 'fakehost',
-                    'output': 'XML'},
-                True)]
-
-        # validate call chain
-        mock_cliq_run.assert_has_calls(expected)
-
-    def test_create_snapshot(self):
-
-        # set up driver with default config
-        mock_cliq_run = self.setup_driver()
-
-        snapshot = {'name': self.snapshot_name,
-                    'volume_name': self.volume_name}
-        self.driver.create_snapshot(snapshot)
-
-        expected = [
-            mock.call(
-                'createSnapshot', {
-                    'snapshotName': 'fakeshapshot',
-                    'output': 'XML',
-                    'inheritAccess': 1,
-                    'volumeName': 'fakevolume'},
-                True)]
-
-        # validate call chain
-        mock_cliq_run.assert_has_calls(expected)
-
-    def test_delete_snapshot(self):
-
-        # set up driver with default config
-        mock_cliq_run = self.setup_driver()
-
-        snapshot = {'name': self.snapshot_name}
-        self.driver.delete_snapshot(snapshot)
-
-        expected = [
-            mock.call(
-                'getSnapshotInfo', {
-                    'snapshotName': 'fakeshapshot',
-                    'output': 'XML'},
-                True),
-            mock.call(
-                'deleteSnapshot', {
-                    'snapshotName': 'fakeshapshot',
-                    'prompt': 'false',
-                    'output': 'XML'},
-                True)]
-
-        # validate call chain
-        mock_cliq_run.assert_has_calls(expected)
-
-    def test_create_volume_from_snapshot(self):
-
-        # set up driver with default config
-        mock_cliq_run = self.setup_driver()
-
-        volume = {'name': self.volume_name}
-        snapshot = {'name': self.snapshot_name}
-        model_update = self.driver.create_volume_from_snapshot(volume,
-                                                               snapshot)
-        expected_iqn = "iqn.2003-10.com.lefthandnetworks:group01:25366:fakev 0"
-        expected_location = "10.0.1.6:3260,1 %s" % expected_iqn
-        self.assertEqual(expected_location, model_update['provider_location'])
-
-        expected = [
-            mock.call(
-                'cloneSnapshot', {
-                    'snapshotName': 'fakeshapshot',
-                    'output': 'XML',
-                    'volumeName': 'fakevolume'},
-                True),
-            mock.call(
-                'getVolumeInfo', {
-                    'volumeName': 'fakevolume',
-                    'output': 'XML'},
-                True),
-            mock.call(
-                'getClusterInfo', {
-                    'clusterName': 'Cluster01',
-                    'searchDepth': '1',
-                    'verbose': '0',
-                    'output': 'XML'},
-                True)]
-
-        # validate call chain
-        mock_cliq_run.assert_has_calls(expected)
-
-    def test_get_volume_stats(self):
-
-        # set up driver with default config
-        mock_cliq_run = self.setup_driver()
-        volume_stats = self.driver.get_volume_stats(True)
-
-        self.assertEqual('Hewlett-Packard', volume_stats['vendor_name'])
-        self.assertEqual('iSCSI', volume_stats['storage_protocol'])
-
-        expected = [
-            mock.call('getClusterInfo', {
-                'searchDepth': 1,
-                'clusterName': 'CloudCluster1',
-                'output': 'XML'}, True)]
-
-        # validate call chain
-        mock_cliq_run.assert_has_calls(expected)
-
-    def test_cliq_run_xml_paramiko_1_13_0(self):
-
-        # set up driver with default config
-        self.setup_driver()
-        xml = self.driver.proxy._cliq_run_xml('testParamiko_1.13.1', {})
-        self.assertIsNotNone(xml)
-
-    def test_cliq_run_xml_paramiko_1_10_0(self):
-
-        # set up driver with default config
-        self.setup_driver()
-        xml = self.driver.proxy._cliq_run_xml('testParamiko_1.10.1', {})
-        self.assertIsNotNone(xml)
-
-
-class TestHPLeftHandRESTISCSIDriver(HPLeftHandBaseDriver, test.TestCase):
+class TestHPLeftHandISCSIDriver(HPLeftHandBaseDriver, test.TestCase):
 
     CONSIS_GROUP_ID = '3470cc4c-63b3-4c7a-8120-8a0693b45838'
     CGSNAPSHOT_ID = '5351d914-6c90-43e7-9a8e-7e84610927da'
@@ -670,15 +100,6 @@ class TestHPLeftHandRESTISCSIDriver(HPLeftHandBaseDriver, test.TestCase):
                   'id': CGSNAPSHOT_ID,
                   'readOnly': False}
 
-    driver_startup_call_stack = [
-        mock.call.login('foo1', 'bar2'),
-        mock.call.getClusterByName('CloudCluster1'),
-        mock.call.getCluster(1),
-        mock.call.getVolumes(
-            cluster='CloudCluster1',
-            fields=['members[id]', 'members[clusterName]', 'members[size]']),
-    ]
-
     def default_mock_conf(self):
 
         mock_conf = mock.Mock()
@@ -703,7 +124,6 @@ class TestHPLeftHandRESTISCSIDriver(HPLeftHandBaseDriver, test.TestCase):
 
     @mock.patch('hplefthandclient.client.HPLeftHandClient', spec=True)
     def setup_driver(self, _mock_client, config=None):
-
         if config is None:
             config = self.default_mock_conf()
 
@@ -742,7 +162,7 @@ class TestHPLeftHandRESTISCSIDriver(HPLeftHandBaseDriver, test.TestCase):
         mock_client.createVolume.return_value = {
             'iscsiIqn': self.connector['initiator']}
 
-        with mock.patch.object(hp_lefthand_rest_proxy.HPLeftHandRESTProxy,
+        with mock.patch.object(hp_lefthand_iscsi.HPLeftHandISCSIDriver,
                                '_create_client') as mock_do_setup:
             mock_do_setup.return_value = mock_client
 
@@ -788,7 +208,7 @@ class TestHPLeftHandRESTISCSIDriver(HPLeftHandBaseDriver, test.TestCase):
             'iscsiIqn': self.connector['initiator']}
         mock_client.getVolumes.return_value = {'total': 1, 'members': []}
 
-        with mock.patch.object(hp_lefthand_rest_proxy.HPLeftHandRESTProxy,
+        with mock.patch.object(hp_lefthand_iscsi.HPLeftHandISCSIDriver,
                                '_create_client') as mock_do_setup:
             mock_do_setup.return_value = mock_client
 
@@ -819,7 +239,7 @@ class TestHPLeftHandRESTISCSIDriver(HPLeftHandBaseDriver, test.TestCase):
         mock_client.getVolumeByName.return_value = {'id': self.volume_id}
         mock_client.getVolumes.return_value = {'total': 1, 'members': []}
 
-        with mock.patch.object(hp_lefthand_rest_proxy.HPLeftHandRESTProxy,
+        with mock.patch.object(hp_lefthand_iscsi.HPLeftHandISCSIDriver,
                                '_create_client') as mock_do_setup:
             mock_do_setup.return_value = mock_client
 
@@ -855,7 +275,7 @@ class TestHPLeftHandRESTISCSIDriver(HPLeftHandBaseDriver, test.TestCase):
         mock_client.getVolumeByName.return_value = {'id': self.volume_id}
         mock_client.getVolumes.return_value = {'total': 1, 'members': []}
 
-        with mock.patch.object(hp_lefthand_rest_proxy.HPLeftHandRESTProxy,
+        with mock.patch.object(hp_lefthand_iscsi.HPLeftHandISCSIDriver,
                                '_create_client') as mock_do_setup:
             mock_do_setup.return_value = mock_client
 
@@ -892,7 +312,7 @@ class TestHPLeftHandRESTISCSIDriver(HPLeftHandBaseDriver, test.TestCase):
         }
         mock_client.getVolumes.return_value = {'total': 1, 'members': []}
 
-        with mock.patch.object(hp_lefthand_rest_proxy.HPLeftHandRESTProxy,
+        with mock.patch.object(hp_lefthand_iscsi.HPLeftHandISCSIDriver,
                                '_create_client') as mock_do_setup:
             mock_do_setup.return_value = mock_client
 
@@ -945,7 +365,7 @@ class TestHPLeftHandRESTISCSIDriver(HPLeftHandBaseDriver, test.TestCase):
         }
         mock_client.getVolumes.return_value = {'total': 1, 'members': []}
 
-        with mock.patch.object(hp_lefthand_rest_proxy.HPLeftHandRESTProxy,
+        with mock.patch.object(hp_lefthand_iscsi.HPLeftHandISCSIDriver,
                                '_create_client') as mock_do_setup:
             mock_do_setup.return_value = mock_client
 
@@ -992,7 +412,7 @@ class TestHPLeftHandRESTISCSIDriver(HPLeftHandBaseDriver, test.TestCase):
         }
         mock_client.getVolumes.return_value = {'total': 1, 'members': []}
 
-        with mock.patch.object(hp_lefthand_rest_proxy.HPLeftHandRESTProxy,
+        with mock.patch.object(hp_lefthand_iscsi.HPLeftHandISCSIDriver,
                                '_create_client') as mock_do_setup:
             mock_do_setup.return_value = mock_client
 
@@ -1035,7 +455,7 @@ class TestHPLeftHandRESTISCSIDriver(HPLeftHandBaseDriver, test.TestCase):
         mock_client.findServerVolumes.return_value = [{'id': self.volume_id}]
         mock_client.getVolumes.return_value = {'total': 1, 'members': []}
 
-        with mock.patch.object(hp_lefthand_rest_proxy.HPLeftHandRESTProxy,
+        with mock.patch.object(hp_lefthand_iscsi.HPLeftHandISCSIDriver,
                                '_create_client') as mock_do_setup:
             mock_do_setup.return_value = mock_client
 
@@ -1076,7 +496,7 @@ class TestHPLeftHandRESTISCSIDriver(HPLeftHandBaseDriver, test.TestCase):
             {'id': 99999}]
         mock_client.getVolumes.return_value = {'total': 1, 'members': []}
 
-        with mock.patch.object(hp_lefthand_rest_proxy.HPLeftHandRESTProxy,
+        with mock.patch.object(hp_lefthand_iscsi.HPLeftHandISCSIDriver,
                                '_create_client') as mock_do_setup:
             mock_do_setup.return_value = mock_client
 
@@ -1111,7 +531,7 @@ class TestHPLeftHandRESTISCSIDriver(HPLeftHandBaseDriver, test.TestCase):
         mock_client.getVolumeByName.return_value = {'id': self.volume_id}
         mock_client.getVolumes.return_value = {'total': 1, 'members': []}
 
-        with mock.patch.object(hp_lefthand_rest_proxy.HPLeftHandRESTProxy,
+        with mock.patch.object(hp_lefthand_iscsi.HPLeftHandISCSIDriver,
                                '_create_client') as mock_do_setup:
             mock_do_setup.return_value = mock_client
 
@@ -1147,7 +567,7 @@ class TestHPLeftHandRESTISCSIDriver(HPLeftHandBaseDriver, test.TestCase):
         mock_client.getSnapshotByName.return_value = {'id': self.snapshot_id}
         mock_client.getVolumes.return_value = {'total': 1, 'members': []}
 
-        with mock.patch.object(hp_lefthand_rest_proxy.HPLeftHandRESTProxy,
+        with mock.patch.object(hp_lefthand_iscsi.HPLeftHandISCSIDriver,
                                '_create_client') as mock_do_setup:
             mock_do_setup.return_value = mock_client
 
@@ -1199,7 +619,7 @@ class TestHPLeftHandRESTISCSIDriver(HPLeftHandBaseDriver, test.TestCase):
             'iscsiIqn': self.connector['initiator']}
         mock_client.getVolumes.return_value = {'total': 1, 'members': []}
 
-        with mock.patch.object(hp_lefthand_rest_proxy.HPLeftHandRESTProxy,
+        with mock.patch.object(hp_lefthand_iscsi.HPLeftHandISCSIDriver,
                                '_create_client') as mock_do_setup:
             mock_do_setup.return_value = mock_client
 
@@ -1231,7 +651,7 @@ class TestHPLeftHandRESTISCSIDriver(HPLeftHandBaseDriver, test.TestCase):
             'iscsiIqn': self.connector['initiator']}
         mock_client.getVolumes.return_value = {'total': 1, 'members': []}
 
-        with mock.patch.object(hp_lefthand_rest_proxy.HPLeftHandRESTProxy,
+        with mock.patch.object(hp_lefthand_iscsi.HPLeftHandISCSIDriver,
                                '_create_client') as mock_do_setup:
             mock_do_setup.return_value = mock_client
 
@@ -1270,15 +690,15 @@ class TestHPLeftHandRESTISCSIDriver(HPLeftHandBaseDriver, test.TestCase):
         volume_with_vt['volume_type_id'] = self.volume_type_id
 
         # get the extra specs of interest from this volume's volume type
-        volume_extra_specs = self.driver.proxy._get_volume_extra_specs(
+        volume_extra_specs = self.driver._get_volume_extra_specs(
             volume_with_vt)
-        extra_specs = self.driver.proxy._get_lh_extra_specs(
+        extra_specs = self.driver._get_lh_extra_specs(
             volume_extra_specs,
-            hp_lefthand_rest_proxy.extra_specs_key_map.keys())
+            hp_lefthand_iscsi.extra_specs_key_map.keys())
 
         # map the extra specs key/value pairs to key/value pairs
         # used as optional configuration values by the LeftHand backend
-        optional = self.driver.proxy._map_extra_specs(extra_specs)
+        optional = self.driver._map_extra_specs(extra_specs)
 
         self.assertDictMatch({'isThinProvisioned': False}, optional)
 
@@ -1298,15 +718,15 @@ class TestHPLeftHandRESTISCSIDriver(HPLeftHandBaseDriver, test.TestCase):
                 'hplh:ao': 'true'}}
 
         # get the extra specs of interest from this volume's volume type
-        volume_extra_specs = self.driver.proxy._get_volume_extra_specs(
+        volume_extra_specs = self.driver._get_volume_extra_specs(
             volume_with_vt)
-        extra_specs = self.driver.proxy._get_lh_extra_specs(
+        extra_specs = self.driver._get_lh_extra_specs(
             volume_extra_specs,
-            hp_lefthand_rest_proxy.extra_specs_key_map.keys())
+            hp_lefthand_iscsi.extra_specs_key_map.keys())
 
         # map the extra specs key/value pairs to key/value pairs
         # used as optional configuration values by the LeftHand backend
-        optional = self.driver.proxy._map_extra_specs(extra_specs)
+        optional = self.driver._map_extra_specs(extra_specs)
 
         # {'hplh:ao': 'true'} should map to
         # {'isAdaptiveOptimizationEnabled': True}
@@ -1336,7 +756,7 @@ class TestHPLeftHandRESTISCSIDriver(HPLeftHandBaseDriver, test.TestCase):
         volume['host'] = host
         new_type = volume_types.get_volume_type(ctxt, new_type_ref['id'])
 
-        with mock.patch.object(hp_lefthand_rest_proxy.HPLeftHandRESTProxy,
+        with mock.patch.object(hp_lefthand_iscsi.HPLeftHandISCSIDriver,
                                '_create_client') as mock_do_setup:
             mock_do_setup.return_value = mock_client
 
@@ -1373,7 +793,7 @@ class TestHPLeftHandRESTISCSIDriver(HPLeftHandBaseDriver, test.TestCase):
         volume['host'] = host
         new_type = volume_types.get_volume_type(ctxt, new_type_ref['id'])
 
-        with mock.patch.object(hp_lefthand_rest_proxy.HPLeftHandRESTProxy,
+        with mock.patch.object(hp_lefthand_iscsi.HPLeftHandISCSIDriver,
                                '_create_client') as mock_do_setup:
             mock_do_setup.return_value = mock_client
 
@@ -1414,7 +834,7 @@ class TestHPLeftHandRESTISCSIDriver(HPLeftHandBaseDriver, test.TestCase):
         volume['host'] = host
         new_type = volume_types.get_volume_type(ctxt, new_type_ref['id'])
 
-        with mock.patch.object(hp_lefthand_rest_proxy.HPLeftHandRESTProxy,
+        with mock.patch.object(hp_lefthand_iscsi.HPLeftHandISCSIDriver,
                                '_create_client') as mock_do_setup:
             mock_do_setup.return_value = mock_client
 
@@ -1452,7 +872,7 @@ class TestHPLeftHandRESTISCSIDriver(HPLeftHandBaseDriver, test.TestCase):
         volume['host'] = host
         new_type = volume_types.get_volume_type(ctxt, new_type_ref['id'])
 
-        with mock.patch.object(hp_lefthand_rest_proxy.HPLeftHandRESTProxy,
+        with mock.patch.object(hp_lefthand_iscsi.HPLeftHandISCSIDriver,
                                '_create_client') as mock_do_setup:
             mock_do_setup.return_value = mock_client
 
@@ -1475,7 +895,7 @@ class TestHPLeftHandRESTISCSIDriver(HPLeftHandBaseDriver, test.TestCase):
 
         host = {'host': self.serverName, 'capabilities': {}}
 
-        with mock.patch.object(hp_lefthand_rest_proxy.HPLeftHandRESTProxy,
+        with mock.patch.object(hp_lefthand_iscsi.HPLeftHandISCSIDriver,
                                '_create_client') as mock_do_setup:
             mock_do_setup.return_value = mock_client
 
@@ -1501,7 +921,7 @@ class TestHPLeftHandRESTISCSIDriver(HPLeftHandBaseDriver, test.TestCase):
         mock_client.getVolumeByName.return_value = {'id': self.volume_id}
         mock_client.getVolumes.return_value = {'total': 1, 'members': []}
 
-        location = (self.driver.proxy.DRIVER_LOCATION % {
+        location = (self.driver.DRIVER_LOCATION % {
             'cluster': 'New_CloudCluster',
             'vip': '10.10.10.111'})
 
@@ -1509,7 +929,7 @@ class TestHPLeftHandRESTISCSIDriver(HPLeftHandBaseDriver, test.TestCase):
             'host': self.serverName,
             'capabilities': {'location_info': location}}
 
-        with mock.patch.object(hp_lefthand_rest_proxy.HPLeftHandRESTProxy,
+        with mock.patch.object(hp_lefthand_iscsi.HPLeftHandISCSIDriver,
                                '_create_client') as mock_do_setup:
             mock_do_setup.return_value = mock_client
 
@@ -1545,7 +965,7 @@ class TestHPLeftHandRESTISCSIDriver(HPLeftHandBaseDriver, test.TestCase):
             'resource': None}}
         mock_client.getVolumes.return_value = {'total': 1, 'members': []}
 
-        location = (self.driver.proxy.DRIVER_LOCATION % {
+        location = (self.driver.DRIVER_LOCATION % {
             'cluster': 'New_CloudCluster',
             'vip': '10.10.10.111'})
 
@@ -1553,7 +973,7 @@ class TestHPLeftHandRESTISCSIDriver(HPLeftHandBaseDriver, test.TestCase):
             'host': self.serverName,
             'capabilities': {'location_info': location}}
 
-        with mock.patch.object(hp_lefthand_rest_proxy.HPLeftHandRESTProxy,
+        with mock.patch.object(hp_lefthand_iscsi.HPLeftHandISCSIDriver,
                                '_create_client') as mock_do_setup:
             mock_do_setup.return_value = mock_client
 
@@ -1596,7 +1016,7 @@ class TestHPLeftHandRESTISCSIDriver(HPLeftHandBaseDriver, test.TestCase):
             'resource': 'snapfoo'}}
         mock_client.getVolumes.return_value = {'total': 1, 'members': []}
 
-        location = (self.driver.proxy.DRIVER_LOCATION % {
+        location = (self.driver.DRIVER_LOCATION % {
             'cluster': 'New_CloudCluster',
             'vip': '10.10.10.111'})
 
@@ -1604,7 +1024,7 @@ class TestHPLeftHandRESTISCSIDriver(HPLeftHandBaseDriver, test.TestCase):
             'host': self.serverName,
             'capabilities': {'location_info': location}}
 
-        with mock.patch.object(hp_lefthand_rest_proxy.HPLeftHandRESTProxy,
+        with mock.patch.object(hp_lefthand_iscsi.HPLeftHandISCSIDriver,
                                '_create_client') as mock_do_setup:
             mock_do_setup.return_value = mock_client
 
@@ -1639,7 +1059,7 @@ class TestHPLeftHandRESTISCSIDriver(HPLeftHandBaseDriver, test.TestCase):
                            '_name_id': clone_id,
                            'provider_location': provider_location}
         original_volume_status = 'available'
-        with mock.patch.object(hp_lefthand_rest_proxy.HPLeftHandRESTProxy,
+        with mock.patch.object(hp_lefthand_iscsi.HPLeftHandISCSIDriver,
                                '_create_client') as mock_do_setup:
             mock_do_setup.return_value = mock_client
             actual_update = self.driver.update_migrated_volume(
@@ -1661,7 +1081,7 @@ class TestHPLeftHandRESTISCSIDriver(HPLeftHandBaseDriver, test.TestCase):
                            'provider_location': provider_location}
         original_volume_status = 'in-use'
 
-        with mock.patch.object(hp_lefthand_rest_proxy.HPLeftHandRESTProxy,
+        with mock.patch.object(hp_lefthand_iscsi.HPLeftHandISCSIDriver,
                                '_create_client') as mock_do_setup:
             mock_do_setup.return_value = mock_client
             actual_update = self.driver.update_migrated_volume(
@@ -1688,7 +1108,7 @@ class TestHPLeftHandRESTISCSIDriver(HPLeftHandBaseDriver, test.TestCase):
             'iscsiIqn': self.connector['initiator']}
         mock_client.getVolumes.return_value = {'total': 1, 'members': []}
 
-        with mock.patch.object(hp_lefthand_rest_proxy.HPLeftHandRESTProxy,
+        with mock.patch.object(hp_lefthand_iscsi.HPLeftHandISCSIDriver,
                                '_create_client') as mock_do_setup:
             mock_do_setup.return_value = mock_client
 
@@ -1726,7 +1146,7 @@ class TestHPLeftHandRESTISCSIDriver(HPLeftHandBaseDriver, test.TestCase):
             'iscsiIqn': self.connector['initiator']}
         mock_client.getVolumes.return_value = {'total': 1, 'members': []}
 
-        with mock.patch.object(hp_lefthand_rest_proxy.HPLeftHandRESTProxy,
+        with mock.patch.object(hp_lefthand_iscsi.HPLeftHandISCSIDriver,
                                '_create_client') as mock_do_setup:
             mock_do_setup.return_value = mock_client
 
@@ -1749,31 +1169,31 @@ class TestHPLeftHandRESTISCSIDriver(HPLeftHandBaseDriver, test.TestCase):
 
             mock_client.assert_has_calls(expected)
 
-    def test__get_existing_volume_ref_name(self):
+    def test_get_existing_volume_ref_name(self):
         self.setup_driver()
 
         existing_ref = {'source-name': self.volume_name}
-        result = self.driver.proxy._get_existing_volume_ref_name(
+        result = self.driver._get_existing_volume_ref_name(
             existing_ref)
         self.assertEqual(self.volume_name, result)
 
         existing_ref = {'bad-key': 'foo'}
         self.assertRaises(
             exception.ManageExistingInvalidReference,
-            self.driver.proxy._get_existing_volume_ref_name,
+            self.driver._get_existing_volume_ref_name,
             existing_ref)
 
     def test_manage_existing(self):
         mock_client = self.setup_driver()
 
-        self.driver.proxy.api_version = "1.1"
+        self.driver.api_version = "1.1"
 
         volume = {'display_name': 'Foo Volume',
                   'volume_type': None,
                   'volume_type_id': None,
                   'id': '12345'}
 
-        with mock.patch.object(hp_lefthand_rest_proxy.HPLeftHandRESTProxy,
+        with mock.patch.object(hp_lefthand_iscsi.HPLeftHandISCSIDriver,
                                '_create_client') as mock_do_setup:
             mock_do_setup.return_value = mock_client
             mock_client.getVolumeByName.return_value = {'id': self.volume_id}
@@ -1816,7 +1236,7 @@ class TestHPLeftHandRESTISCSIDriver(HPLeftHandBaseDriver, test.TestCase):
                 'hplh:data_pl': 'r-0',
                 'volume_type': self.volume_type}}
 
-        self.driver.proxy.api_version = "1.1"
+        self.driver.api_version = "1.1"
 
         volume = {'display_name': 'Foo Volume',
                   'host': 'stack@lefthand#lefthand',
@@ -1824,7 +1244,7 @@ class TestHPLeftHandRESTISCSIDriver(HPLeftHandBaseDriver, test.TestCase):
                   'volume_type_id': 'bcfa9fa4-54a0-4340-a3d8-bfcf19aea65e',
                   'id': '12345'}
 
-        with mock.patch.object(hp_lefthand_rest_proxy.HPLeftHandRESTProxy,
+        with mock.patch.object(hp_lefthand_iscsi.HPLeftHandISCSIDriver,
                                '_create_client') as mock_do_setup:
             mock_do_setup.return_value = mock_client
             mock_client.getVolumeByName.return_value = {'id': self.volume_id}
@@ -1867,10 +1287,10 @@ class TestHPLeftHandRESTISCSIDriver(HPLeftHandBaseDriver, test.TestCase):
                 'hplh:data_pl': 'r-0',
                 'volume_type': self.volume_type}}
 
-        self.driver.proxy.retype = mock.Mock(
+        self.driver.retype = mock.Mock(
             side_effect=exception.VolumeNotFound(volume_id="fake"))
 
-        self.driver.proxy.api_version = "1.1"
+        self.driver.api_version = "1.1"
 
         volume = {'display_name': 'Foo Volume',
                   'host': 'stack@lefthand#lefthand',
@@ -1878,7 +1298,7 @@ class TestHPLeftHandRESTISCSIDriver(HPLeftHandBaseDriver, test.TestCase):
                   'volume_type_id': 'bcfa9fa4-54a0-4340-a3d8-bfcf19aea65e',
                   'id': '12345'}
 
-        with mock.patch.object(hp_lefthand_rest_proxy.HPLeftHandRESTProxy,
+        with mock.patch.object(hp_lefthand_iscsi.HPLeftHandISCSIDriver,
                                '_create_client') as mock_do_setup:
             mock_do_setup.return_value = mock_client
             mock_client.getVolumeByName.return_value = {'id': self.volume_id}
@@ -1915,14 +1335,14 @@ class TestHPLeftHandRESTISCSIDriver(HPLeftHandBaseDriver, test.TestCase):
     def test_manage_existing_volume_type_exception(self):
         mock_client = self.setup_driver()
 
-        self.driver.proxy.api_version = "1.1"
+        self.driver.api_version = "1.1"
 
         volume = {'display_name': 'Foo Volume',
                   'volume_type': 'gold',
                   'volume_type_id': 'bcfa9fa4-54a0-4340-a3d8-bfcf19aea65e',
                   'id': '12345'}
 
-        with mock.patch.object(hp_lefthand_rest_proxy.HPLeftHandRESTProxy,
+        with mock.patch.object(hp_lefthand_iscsi.HPLeftHandISCSIDriver,
                                '_create_client') as mock_do_setup:
             mock_do_setup.return_value = mock_client
             mock_client.getVolumeByName.return_value = {'id': self.volume_id}
@@ -1952,9 +1372,9 @@ class TestHPLeftHandRESTISCSIDriver(HPLeftHandBaseDriver, test.TestCase):
         mock_client = self.setup_driver()
         mock_client.getVolumeByName.return_value = {'size': 2147483648}
 
-        self.driver.proxy.api_version = "1.1"
+        self.driver.api_version = "1.1"
 
-        with mock.patch.object(hp_lefthand_rest_proxy.HPLeftHandRESTProxy,
+        with mock.patch.object(hp_lefthand_iscsi.HPLeftHandISCSIDriver,
                                '_create_client') as mock_do_setup:
             mock_do_setup.return_value = mock_client
             mock_client.getVolumes.return_value = {
@@ -1985,9 +1405,9 @@ class TestHPLeftHandRESTISCSIDriver(HPLeftHandBaseDriver, test.TestCase):
         mock_client = self.setup_driver()
         mock_client.getVolumeByName.return_value = {'size': 2147483648}
 
-        self.driver.proxy.api_version = "1.1"
+        self.driver.api_version = "1.1"
 
-        with mock.patch.object(hp_lefthand_rest_proxy.HPLeftHandRESTProxy,
+        with mock.patch.object(hp_lefthand_iscsi.HPLeftHandISCSIDriver,
                                '_create_client') as mock_do_setup:
             mock_do_setup.return_value = mock_client
 
@@ -2015,9 +1435,9 @@ class TestHPLeftHandRESTISCSIDriver(HPLeftHandBaseDriver, test.TestCase):
         mock_client.getVolumeByName.side_effect = (
             hpexceptions.HTTPNotFound('fake'))
 
-        self.driver.proxy.api_version = "1.1"
+        self.driver.api_version = "1.1"
 
-        with mock.patch.object(hp_lefthand_rest_proxy.HPLeftHandRESTProxy,
+        with mock.patch.object(hp_lefthand_iscsi.HPLeftHandISCSIDriver,
                                '_create_client') as mock_do_setup:
             mock_do_setup.return_value = mock_client
             mock_client.getVolumes.return_value = {
@@ -2059,9 +1479,9 @@ class TestHPLeftHandRESTISCSIDriver(HPLeftHandBaseDriver, test.TestCase):
             }]
         }
 
-        self.driver.proxy.api_version = "1.1"
+        self.driver.api_version = "1.1"
 
-        with mock.patch.object(hp_lefthand_rest_proxy.HPLeftHandRESTProxy,
+        with mock.patch.object(hp_lefthand_iscsi.HPLeftHandISCSIDriver,
                                '_create_client') as mock_do_setup:
             mock_do_setup.return_value = mock_client
             self.driver.unmanage(self.volume)
@@ -2080,12 +1500,12 @@ class TestHPLeftHandRESTISCSIDriver(HPLeftHandBaseDriver, test.TestCase):
 
     def test_api_version(self):
         self.setup_driver()
-        self.driver.proxy.api_version = "1.1"
-        self.driver.proxy._check_api_version()
+        self.driver.api_version = "1.1"
+        self.driver._check_api_version()
 
-        self.driver.proxy.api_version = "1.0"
+        self.driver.api_version = "1.0"
         self.assertRaises(exception.InvalidInput,
-                          self.driver.proxy._check_api_version)
+                          self.driver._check_api_version)
 
     def test_get_volume_stats(self):
 
@@ -2103,7 +1523,7 @@ class TestHPLeftHandRESTISCSIDriver(HPLeftHandBaseDriver, test.TestCase):
             }]
         }
 
-        with mock.patch.object(hp_lefthand_rest_proxy.HPLeftHandRESTProxy,
+        with mock.patch.object(hp_lefthand_iscsi.HPLeftHandISCSIDriver,
                                '_create_client') as mock_do_setup:
             mock_do_setup.return_value = mock_client
 
@@ -2148,7 +1568,7 @@ class TestHPLeftHandRESTISCSIDriver(HPLeftHandBaseDriver, test.TestCase):
         # set up driver with default config
         mock_client = self.setup_driver()
 
-        with mock.patch.object(hp_lefthand_rest_proxy.HPLeftHandRESTProxy,
+        with mock.patch.object(hp_lefthand_iscsi.HPLeftHandISCSIDriver,
                                '_create_client') as mock_do_setup:
             mock_do_setup.return_value = mock_client
 
@@ -2175,7 +1595,7 @@ class TestHPLeftHandRESTISCSIDriver(HPLeftHandBaseDriver, test.TestCase):
         expected_volumes = [mock_volume]
         self.driver.db.volume_get_all_by_group.return_value = expected_volumes
 
-        with mock.patch.object(hp_lefthand_rest_proxy.HPLeftHandRESTProxy,
+        with mock.patch.object(hp_lefthand_iscsi.HPLeftHandISCSIDriver,
                                '_create_client') as mock_do_setup:
             mock_do_setup.return_value = mock_client
 
@@ -2213,7 +1633,7 @@ class TestHPLeftHandRESTISCSIDriver(HPLeftHandBaseDriver, test.TestCase):
         mock_client.createVolume.return_value = {
             'iscsiIqn': self.connector['initiator']}
 
-        with mock.patch.object(hp_lefthand_rest_proxy.HPLeftHandRESTProxy,
+        with mock.patch.object(hp_lefthand_iscsi.HPLeftHandISCSIDriver,
                                '_create_client') as mock_do_setup:
             mock_do_setup.return_value = mock_client
 
@@ -2255,7 +1675,7 @@ class TestHPLeftHandRESTISCSIDriver(HPLeftHandBaseDriver, test.TestCase):
         mock_client.createVolume.return_value = {
             'iscsiIqn': self.connector['initiator']}
 
-        with mock.patch.object(hp_lefthand_rest_proxy.HPLeftHandRESTProxy,
+        with mock.patch.object(hp_lefthand_iscsi.HPLeftHandISCSIDriver,
                                '_create_client') as mock_do_setup:
             mock_do_setup.return_value = mock_client
 
@@ -2300,7 +1720,7 @@ class TestHPLeftHandRESTISCSIDriver(HPLeftHandBaseDriver, test.TestCase):
         expected_snaps = [mock_snap]
         mock_snap_list.return_value = expected_snaps
 
-        with mock.patch.object(hp_lefthand_rest_proxy.HPLeftHandRESTProxy,
+        with mock.patch.object(hp_lefthand_iscsi.HPLeftHandISCSIDriver,
                                '_create_client') as mock_do_setup:
             mock_do_setup.return_value = mock_client
 
@@ -2341,7 +1761,7 @@ class TestHPLeftHandRESTISCSIDriver(HPLeftHandBaseDriver, test.TestCase):
         expected_snaps = [mock_snap]
         mock_snap_list.return_value = expected_snaps
 
-        with mock.patch.object(hp_lefthand_rest_proxy.HPLeftHandRESTProxy,
+        with mock.patch.object(hp_lefthand_iscsi.HPLeftHandISCSIDriver,
                                '_create_client') as mock_do_setup:
             mock_do_setup.return_value = mock_client
 
diff --git a/cinder/volume/drivers/san/hp/hp_lefthand_cliq_proxy.py b/cinder/volume/drivers/san/hp/hp_lefthand_cliq_proxy.py
deleted file mode 100644 (file)
index 2561ad1..0000000
+++ /dev/null
@@ -1,491 +0,0 @@
-#    (c) Copyright 2014 Hewlett-Packard Development Company, L.P.
-#    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.
-#
-"""
-HP LeftHand SAN ISCSI Driver.
-
-The driver communicates to the backend aka Cliq via SSH to perform all the
-operations on the SAN.
-"""
-
-from lxml import etree
-from oslo_concurrency import processutils
-from oslo_log import log as logging
-from oslo_utils import units
-
-from cinder import exception
-from cinder.i18n import _, _LE, _LW
-from cinder.volume.drivers.san import san
-
-
-LOG = logging.getLogger(__name__)
-
-
-class HPLeftHandCLIQProxy(san.SanISCSIDriver):
-    """Executes commands relating to HP/LeftHand SAN ISCSI volumes.
-
-    We use the CLIQ interface, over SSH.
-
-    Rough overview of CLIQ commands used:
-
-    :createVolume:    (creates the volume)
-
-    :deleteVolume:    (deletes the volume)
-
-    :modifyVolume:    (extends the volume)
-
-    :createSnapshot:    (creates the snapshot)
-
-    :deleteSnapshot:    (deletes the snapshot)
-
-    :cloneSnapshot:    (creates the volume from a snapshot)
-
-    :getVolumeInfo:    (to discover the IQN etc)
-
-    :getSnapshotInfo:    (to discover the IQN etc)
-
-    :getClusterInfo:    (to discover the iSCSI target IP address)
-
-    The 'trick' here is that the HP SAN enforces security by default, so
-    normally a volume mount would need both to configure the SAN in the volume
-    layer and do the mount on the compute layer.  Multi-layer operations are
-    not catered for at the moment in the cinder architecture, so instead we
-    share the volume using CHAP at volume creation time.  Then the mount need
-    only use those CHAP credentials, so can take place exclusively in the
-    compute layer.
-
-    Version history:
-        1.0.0 - Initial driver
-        1.1.0 - Added create/delete snapshot, extend volume, create volume
-                from snapshot support.
-        1.2.0 - Ported into the new HP LeftHand driver.
-        1.2.1 - Fixed bug #1279897, HP LeftHand CLIQ proxy may return incorrect
-                capacity values.
-        1.2.2 - Fixed driver with Paramiko 1.13.0, bug #1298608.
-        1.2.3 - Added update_migrated_volume #1493546
-    """
-
-    VERSION = "1.2.3"
-
-    device_stats = {}
-
-    def __init__(self, *args, **kwargs):
-        super(HPLeftHandCLIQProxy, self).__init__(*args, **kwargs)
-        self.cluster_vip = None
-        LOG.warning(_LW('The HPLeftHandISCSIDriver CLIQ driver has been '
-                        'DEPRECATED as of the 2015.2 release. This driver '
-                        'will be removed in the 2016.1 release. Please use '
-                        'the HPLeftHandISCSIDriver REST based driver '
-                        'instead.'))
-
-    def do_setup(self, context):
-        pass
-
-    def check_for_setup_error(self):
-        pass
-
-    def get_version_string(self):
-        return (_('CLIQ %(proxy_ver)s') % {'proxy_ver': self.VERSION})
-
-    def _cliq_run(self, verb, cliq_args, check_exit_code=True):
-        """Runs a CLIQ command over SSH, without doing any result parsing."""
-        cmd_list = [verb]
-        for k, v in cliq_args.items():
-            cmd_list.append("%s=%s" % (k, v))
-
-        return self._run_ssh(cmd_list, check_exit_code)
-
-    def _cliq_run_xml(self, verb, cliq_args, check_cliq_result=True):
-        """Runs a CLIQ command over SSH, parsing and checking the output."""
-        cliq_args['output'] = 'XML'
-        (out, _err) = self._cliq_run(verb, cliq_args, check_cliq_result)
-
-        LOG.debug("CLIQ command returned %s", out)
-
-        result_xml = etree.fromstring(out.encode('utf8'))
-        if check_cliq_result:
-            response_node = result_xml.find("response")
-            if response_node is None:
-                msg = (_("Malformed response to CLIQ command "
-                         "%(verb)s %(cliq_args)s. Result=%(out)s") %
-                       {'verb': verb, 'cliq_args': cliq_args, 'out': out})
-                raise exception.VolumeBackendAPIException(data=msg)
-
-            result_code = response_node.attrib.get("result")
-
-            if result_code != "0":
-                msg = (_("Error running CLIQ command %(verb)s %(cliq_args)s. "
-                         " Result=%(out)s") %
-                       {'verb': verb, 'cliq_args': cliq_args, 'out': out})
-                raise exception.VolumeBackendAPIException(data=msg)
-
-        return result_xml
-
-    def _cliq_get_cluster_info(self, cluster_name):
-        """Queries for info about the cluster (including IP)."""
-        cliq_args = {}
-        cliq_args['clusterName'] = cluster_name
-        cliq_args['searchDepth'] = '1'
-        cliq_args['verbose'] = '0'
-
-        result_xml = self._cliq_run_xml("getClusterInfo", cliq_args)
-
-        return result_xml
-
-    def _cliq_get_cluster_vip(self, cluster_name):
-        """Gets the IP on which a cluster shares iSCSI volumes."""
-        cluster_xml = self._cliq_get_cluster_info(cluster_name)
-
-        vips = []
-        for vip in cluster_xml.findall("response/cluster/vip"):
-            vips.append(vip.attrib.get('ipAddress'))
-
-        if len(vips) == 1:
-            return vips[0]
-
-        _xml = etree.tostring(cluster_xml)
-        msg = (_("Unexpected number of virtual ips for cluster "
-                 " %(cluster_name)s. Result=%(_xml)s") %
-               {'cluster_name': cluster_name, '_xml': _xml})
-        raise exception.VolumeBackendAPIException(data=msg)
-
-    def _cliq_get_volume_info(self, volume_name):
-        """Gets the volume info, including IQN."""
-        cliq_args = {}
-        cliq_args['volumeName'] = volume_name
-        result_xml = self._cliq_run_xml("getVolumeInfo", cliq_args)
-
-        # Result looks like this:
-        # <gauche version="1.0">
-        #  <response description="Operation succeeded." name="CliqSuccess"
-        #            processingTime="87" result="0">
-        #    <volume autogrowPages="4" availability="online" blockSize="1024"
-        #       bytesWritten="0" checkSum="false" clusterName="Cluster01"
-        #       created="2011-02-08T19:56:53Z" deleting="false" description=""
-        #       groupName="Group01" initialQuota="536870912" isPrimary="true"
-        #       iscsiIqn="iqn.2003-10.com.lefthandnetworks:group01:25366:vol-b"
-        #       maxSize="6865387257856" md5="9fa5c8b2cca54b2948a63d833097e1ca"
-        #       minReplication="1" name="vol-b" parity="0" replication="2"
-        #       reserveQuota="536870912" scratchQuota="4194304"
-        #       serialNumber="9fa5c8b2cca54b2948a63d833097e1ca0000000000006316"
-        #       size="1073741824" stridePages="32" thinProvision="true">
-        #      <status description="OK" value="2"/>
-        #      <permission access="rw"
-        #            authGroup="api-34281B815713B78-(trimmed)51ADD4B7030853AA7"
-        #            chapName="chapusername" chapRequired="true" id="25369"
-        #            initiatorSecret="" iqn="" iscsiEnabled="true"
-        #            loadBalance="true" targetSecret="supersecret"/>
-        #    </volume>
-        #  </response>
-        # </gauche>
-
-        # Flatten the nodes into a dictionary; use prefixes to avoid collisions
-        volume_attributes = {}
-
-        volume_node = result_xml.find("response/volume")
-        for k, v in volume_node.attrib.items():
-            volume_attributes["volume." + k] = v
-
-        status_node = volume_node.find("status")
-        if status_node is not None:
-            for k, v in status_node.attrib.items():
-                volume_attributes["status." + k] = v
-
-        # We only consider the first permission node
-        permission_node = volume_node.find("permission")
-        if permission_node is not None:
-            for k, v in status_node.attrib.items():
-                volume_attributes["permission." + k] = v
-
-        LOG.debug("Volume info: %(volume_name)s => %(volume_attributes)s",
-                  {'volume_name': volume_name,
-                   'volume_attributes': volume_attributes})
-        return volume_attributes
-
-    def _cliq_get_snapshot_info(self, snapshot_name):
-        """Gets the snapshot info, including IQN."""
-        cliq_args = {}
-        cliq_args['snapshotName'] = snapshot_name
-        result_xml = self._cliq_run_xml("getSnapshotInfo", cliq_args)
-
-        # Result looks like this:
-        # <gauche version="1.0">
-        #  <response description="Operation succeeded." name="CliqSuccess"
-        #            processingTime="87" result="0">
-        #    <snapshot applicationManaged="false" autogrowPages="32768"
-        #       automatic="false" availability="online" bytesWritten="0"
-        #       clusterName="CloudCluster1" created="2013-08-26T07:03:44Z"
-        #       deleting="false" description="" groupName="CloudMgmtGroup1"
-        #       id="730" initialQuota="536870912" isPrimary="true"
-        #       iscsiIqn="iqn.2003-10.com.lefthandnetworks:cloudmgmtgroup1:73"
-        #       md5="a64b4f850539c07fb5ce3cee5db1fcce" minReplication="1"
-        #       name="snapshot-7849288e-e5e8-42cb-9687-9af5355d674b"
-        #       replication="2" reserveQuota="536870912" scheduleId="0"
-        #       scratchQuota="4194304" scratchWritten="0"
-        #       serialNumber="a64b4f850539c07fb5ce3cee5db1fcce00000000000002da"
-        #       size="2147483648" stridePages="32"
-        #       volumeSerial="a64b4f850539c07fb5ce3cee5db1fcce00000000000002d">
-        #      <status description="OK" value="2"/>
-        #      <permission access="rw"
-        #            authGroup="api-34281B815713B78-(trimmed)51ADD4B7030853AA7"
-        #            chapName="chapusername" chapRequired="true" id="25369"
-        #            initiatorSecret="" iqn="" iscsiEnabled="true"
-        #            loadBalance="true" targetSecret="supersecret"/>
-        #    </snapshot>
-        #  </response>
-        # </gauche>
-
-        # Flatten the nodes into a dictionary; use prefixes to avoid collisions
-        snapshot_attributes = {}
-
-        snapshot_node = result_xml.find("response/snapshot")
-        for k, v in snapshot_node.attrib.items():
-            snapshot_attributes["snapshot." + k] = v
-
-        status_node = snapshot_node.find("status")
-        if status_node is not None:
-            for k, v in status_node.attrib.items():
-                snapshot_attributes["status." + k] = v
-
-        # We only consider the first permission node
-        permission_node = snapshot_node.find("permission")
-        if permission_node is not None:
-            for k, v in status_node.attrib.items():
-                snapshot_attributes["permission." + k] = v
-
-        LOG.debug("Snapshot info: %(name)s => %(attributes)s",
-                  {'name': snapshot_name, 'attributes': snapshot_attributes})
-        return snapshot_attributes
-
-    def create_volume(self, volume):
-        """Creates a volume."""
-        cliq_args = {}
-        cliq_args['clusterName'] = self.configuration.san_clustername
-
-        if self.configuration.san_thin_provision:
-            cliq_args['thinProvision'] = '1'
-        else:
-            cliq_args['thinProvision'] = '0'
-
-        cliq_args['volumeName'] = volume['name']
-        cliq_args['size'] = '%sGB' % volume['size']
-
-        self._cliq_run_xml("createVolume", cliq_args)
-
-        return self._get_model_update(volume['name'])
-
-    def extend_volume(self, volume, new_size):
-        """Extend the size of an existing volume."""
-        cliq_args = {}
-        cliq_args['volumeName'] = volume['name']
-        cliq_args['size'] = '%sGB' % new_size
-
-        self._cliq_run_xml("modifyVolume", cliq_args)
-
-    def create_volume_from_snapshot(self, volume, snapshot):
-        """Creates a volume from a snapshot."""
-        cliq_args = {}
-        cliq_args['snapshotName'] = snapshot['name']
-        cliq_args['volumeName'] = volume['name']
-
-        self._cliq_run_xml("cloneSnapshot", cliq_args)
-
-        return self._get_model_update(volume['name'])
-
-    def create_snapshot(self, snapshot):
-        """Creates a snapshot."""
-        cliq_args = {}
-        cliq_args['snapshotName'] = snapshot['name']
-        cliq_args['volumeName'] = snapshot['volume_name']
-        cliq_args['inheritAccess'] = 1
-        self._cliq_run_xml("createSnapshot", cliq_args)
-
-    def delete_volume(self, volume):
-        """Deletes a volume."""
-        cliq_args = {}
-        cliq_args['volumeName'] = volume['name']
-        cliq_args['prompt'] = 'false'  # Don't confirm
-        try:
-            self._cliq_get_volume_info(volume['name'])
-        except processutils.ProcessExecutionError:
-            LOG.error(_LE("Volume did not exist. It will not be deleted"))
-            return
-        self._cliq_run_xml("deleteVolume", cliq_args)
-
-    def delete_snapshot(self, snapshot):
-        """Deletes a snapshot."""
-        cliq_args = {}
-        cliq_args['snapshotName'] = snapshot['name']
-        cliq_args['prompt'] = 'false'  # Don't confirm
-        try:
-            self._cliq_get_snapshot_info(snapshot['name'])
-        except processutils.ProcessExecutionError:
-            LOG.error(_LE("Snapshot did not exist. It will not be deleted"))
-            return
-        try:
-            self._cliq_run_xml("deleteSnapshot", cliq_args)
-        except Exception as ex:
-            in_use_msg = 'cannot be deleted because it is a clone point'
-            if in_use_msg in ex.message:
-                raise exception.SnapshotIsBusy(ex)
-
-            raise exception.VolumeBackendAPIException(ex)
-
-    def local_path(self, volume):
-        msg = _("local_path not supported")
-        raise exception.VolumeBackendAPIException(data=msg)
-
-    def initialize_connection(self, volume, connector):
-        """Assigns the volume to a server.
-
-        Assign any created volume to a compute node/host so that it can be
-        used from that host. HP VSA requires a volume to be assigned
-        to a server.
-
-        This driver returns a driver_volume_type of 'iscsi'.
-        The format of the driver data is defined in _get_iscsi_properties.
-        Example return value:
-
-            {
-                'driver_volume_type': 'iscsi'
-                'data': {
-                    'target_discovered': True,
-                    'target_iqn': 'iqn.2010-10.org.openstack:volume-00000001',
-                    'target_protal': '127.0.0.1:3260',
-                    'volume_id': 1,
-                }
-            }
-
-        """
-        self._create_server(connector)
-        cliq_args = {}
-        cliq_args['volumeName'] = volume['name']
-        cliq_args['serverName'] = connector['host']
-        self._cliq_run_xml("assignVolumeToServer", cliq_args)
-
-        iscsi_data = self._get_iscsi_properties(volume)
-        return {
-            'driver_volume_type': 'iscsi',
-            'data': iscsi_data
-        }
-
-    def _create_server(self, connector):
-        cliq_args = {}
-        cliq_args['serverName'] = connector['host']
-        out = self._cliq_run_xml("getServerInfo", cliq_args, False)
-        response = out.find("response")
-        result = response.attrib.get("result")
-        if result != '0':
-            cliq_args = {}
-            cliq_args['serverName'] = connector['host']
-            cliq_args['initiator'] = connector['initiator']
-            self._cliq_run_xml("createServer", cliq_args)
-
-    def _get_model_update(self, volume_name):
-        volume_info = self._cliq_get_volume_info(volume_name)
-        cluster_name = volume_info['volume.clusterName']
-        iscsi_iqn = volume_info['volume.iscsiIqn']
-
-        # TODO(justinsb): Is this always 1? Does it matter?
-        cluster_interface = '1'
-
-        if not self.cluster_vip:
-            self.cluster_vip = self._cliq_get_cluster_vip(cluster_name)
-        iscsi_portal = self.cluster_vip + ":3260," + cluster_interface
-
-        model_update = {}
-
-        # NOTE(jdg): LH volumes always at lun 0 ?
-        model_update['provider_location'] = ("%s %s %s" %
-                                             (iscsi_portal,
-                                              iscsi_iqn,
-                                              0))
-        return model_update
-
-    def terminate_connection(self, volume, connector, **kwargs):
-        """Unassign the volume from the host."""
-        cliq_args = {}
-        cliq_args['volumeName'] = volume['name']
-        cliq_args['serverName'] = connector['host']
-        self._cliq_run_xml("unassignVolumeToServer", cliq_args)
-
-    def get_volume_stats(self, refresh=False):
-        if refresh:
-            self._update_backend_status()
-
-        return self.device_stats
-
-    def _update_backend_status(self):
-        data = {}
-        backend_name = self.configuration.safe_get('volume_backend_name')
-        data['volume_backend_name'] = backend_name or self.__class__.__name__
-        data['reserved_percentage'] = 0
-        data['storage_protocol'] = 'iSCSI'
-        data['vendor_name'] = 'Hewlett-Packard'
-
-        result_xml = self._cliq_run_xml(
-            "getClusterInfo", {
-                'searchDepth': 1,
-                'clusterName': self.configuration.san_clustername})
-        cluster_node = result_xml.find("response/cluster")
-        total_capacity = cluster_node.attrib.get("spaceTotal")
-        free_capacity = cluster_node.attrib.get("unprovisionedSpace")
-        GB = units.Gi
-
-        data['total_capacity_gb'] = int(total_capacity) / GB
-        data['free_capacity_gb'] = int(free_capacity) / GB
-        self.device_stats = data
-
-    def create_cloned_volume(self, volume, src_vref):
-        raise NotImplementedError()
-
-    def create_export(self, context, volume, connector):
-        pass
-
-    def ensure_export(self, context, volume):
-        pass
-
-    def remove_export(self, context, volume):
-        pass
-
-    def retype(self, context, volume, new_type, diff, host):
-        """Convert the volume to be of the new type.
-
-        Returns a boolean indicating whether the retype occurred.
-
-        :param ctxt: Context
-        :param volume: A dictionary describing the volume to migrate
-        :param new_type: A dictionary describing the volume type to convert to
-        :param diff: A dictionary with the difference between the two types
-        """
-        return False
-
-    def migrate_volume(self, ctxt, volume, host):
-        """Migrate the volume to the specified host.
-
-        Returns a boolean indicating whether the migration occurred, as well as
-        model_update.
-
-        :param ctxt: Context
-        :param volume: A dictionary describing the volume to migrate
-        :param host: A dictionary describing the host to migrate to, where
-                     host['host'] is its name, and host['capabilities'] is a
-                     dictionary of its reported capabilities.
-        """
-        return (False, None)
-
-    def update_migrated_volume(self, context, volume, new_volume,
-                               original_volume_status):
-        raise NotImplementedError()
index 01ed065be3ccf8af01b0d15a623ce7ac9dc67308..fe6b6ba977922a470c6ad2a19ff002ef75c54fb5 100644 (file)
@@ -1,4 +1,4 @@
-#    (c) Copyright 2014 Hewlett-Packard Development Company, L.P.
+#    (c) Copyright 2014-2015 Hewlett-Packard Development Company, L.P.
 #    All Rights Reserved.
 #
 #    Licensed under the Apache License, Version 2.0 (the "License"); you may
 #    License for the specific language governing permissions and limitations
 #    under the License.
 #
-"""
-Volume driver for HP LeftHand Storage array.
-This driver requires 11.5 or greater firmware on the LeftHand array, using
-the 1.0 or greater version of the hplefthandclient.
-
-You will need to install the python hplefthandclient.
-sudo pip install hplefthandclient
-
-Set the following in the cinder.conf file to enable the
-LeftHand Channel Driver along with the required flags:
-
-volume_driver=cinder.volume.drivers.san.hp.hp_lefthand_iscsi.
-    HPLeftHandISCSIDriver
-
-It also requires the setting of hplefthand_api_url, hplefthand_username,
-hplefthand_password for credentials to talk to the REST service on the
-LeftHand array.
-"""
+"""HP LeftHand SAN ISCSI REST Proxy."""
 
+from oslo_config import cfg
 from oslo_log import log as logging
+from oslo_utils import excutils
+from oslo_utils import importutils
+from oslo_utils import units
 
+from cinder import context
 from cinder import exception
-from cinder.i18n import _, _LI
+from cinder.i18n import _, _LE, _LI, _LW
+from cinder import objects
 from cinder.volume import driver
-from cinder.volume.drivers.san.hp import hp_lefthand_cliq_proxy as cliq_proxy
-from cinder.volume.drivers.san.hp import hp_lefthand_rest_proxy as rest_proxy
+from cinder.volume import utils
+from cinder.volume import volume_types
+
+import six
+
+import math
+import re
 
 LOG = logging.getLogger(__name__)
 
+hplefthandclient = importutils.try_import("hplefthandclient")
+if hplefthandclient:
+    from hplefthandclient import client as hp_lh_client
+    from hplefthandclient import exceptions as hpexceptions
+
+hplefthand_opts = [
+    cfg.StrOpt('hplefthand_api_url',
+               help="HP LeftHand WSAPI Server Url like "
+                    "https://<LeftHand ip>:8081/lhos"),
+    cfg.StrOpt('hplefthand_username',
+               help="HP LeftHand Super user username"),
+    cfg.StrOpt('hplefthand_password',
+               help="HP LeftHand Super user password",
+               secret=True),
+    cfg.StrOpt('hplefthand_clustername',
+               help="HP LeftHand cluster name"),
+    cfg.BoolOpt('hplefthand_iscsi_chap_enabled',
+                default=False,
+                help='Configure CHAP authentication for iSCSI connections '
+                '(Default: Disabled)'),
+    cfg.BoolOpt('hplefthand_debug',
+                default=False,
+                help="Enable HTTP debugging to LeftHand"),
+
+]
+
+CONF = cfg.CONF
+CONF.register_opts(hplefthand_opts)
+
+MIN_API_VERSION = "1.1"
 MIN_CLIENT_VERSION = '1.0.4'
+MIN_CG_CLIENT_VERSION = "1.0.6"
 
+# map the extra spec key to the REST client option key
+extra_specs_key_map = {
+    'hplh:provisioning': 'isThinProvisioned',
+    'hplh:ao': 'isAdaptiveOptimizationEnabled',
+    'hplh:data_pl': 'dataProtectionLevel',
+}
 
-class HPLeftHandISCSIDriver(driver.TransferVD,
-                            driver.ManageableVD,
-                            driver.ExtendVD,
-                            driver.SnapshotVD,
-                            driver.MigrateVD,
-                            driver.BaseVD,
-                            driver.ConsistencyGroupVD):
-    """Executes commands relating to HP/LeftHand SAN ISCSI volumes.
+# map the extra spec value to the REST client option value
+extra_specs_value_map = {
+    'isThinProvisioned': {'thin': True, 'full': False},
+    'isAdaptiveOptimizationEnabled': {'true': True, 'false': False},
+    'dataProtectionLevel': {
+        'r-0': 0, 'r-5': 1, 'r-10-2': 2, 'r-10-3': 3, 'r-10-4': 4, 'r-6': 5}
+}
+
+
+class HPLeftHandISCSIDriver(driver.ISCSIDriver):
+    """Executes REST commands relating to HP/LeftHand SAN ISCSI volumes.
 
     Version history:
-        1.0.0 - Initial driver
+        1.0.0 - Initial REST iSCSI proxy
         1.0.1 - Added support for retype
         1.0.2 - Added support for volume migrate
-        1.0.3 - Fix for no handler for logger during tests
-        1.0.4 - Removing locks bug #1395953
-        1.0.5 - Adding support for manage/unmanage.
-        1.0.6 - Updated minimum client version. bug #1432757
-        1.0.7 - Update driver to use ABC metaclasses
-        1.0.8 - Adds consistency group support
-        1.0.9 - Added update_migrated_volume #1493546
+        1.0.3 - Fixed bug #1285829, HP LeftHand backend assisted migration
+                should check for snapshots
+        1.0.4 - Fixed bug #1285925, LeftHand AO volume create performance
+                improvement
+        1.0.5 - Fixed bug #1311350, Live-migration of an instance when
+                attached to a volume was causing an error.
+        1.0.6 - Removing locks bug #1395953
+        1.0.7 - Fixed bug #1353137, Server was not removed from the HP
+                Lefthand backend after the last volume was detached.
+        1.0.8 - Fixed bug #1418201, A cloned volume fails to attach.
+        1.0.9 - Adding support for manage/unmanage.
+        1.0.10 - Add stats for goodness_function and filter_function
+        1.0.11 - Add over subscription support
+        1.0.12 - Adds consistency group support
+        1.0.13 - Added update_migrated_volume #1493546
+        1.0.14 - Removed the old CLIQ based driver
     """
 
-    VERSION = "1.0.9"
+    VERSION = "1.0.14"
+
+    device_stats = {}
 
     def __init__(self, *args, **kwargs):
         super(HPLeftHandISCSIDriver, self).__init__(*args, **kwargs)
-        self.proxy = None
-        self.args = args
-        self.kwargs = kwargs
+        self.configuration.append_config_values(hplefthand_opts)
+        if not self.configuration.hplefthand_api_url:
+            raise exception.NotFound(_("HPLeftHand url not found"))
 
-    def _create_proxy(self, *args, **kwargs):
+        # blank is the only invalid character for cluster names
+        # so we need to use it as a separator
+        self.DRIVER_LOCATION = self.__class__.__name__ + ' %(cluster)s %(vip)s'
+        self.db = kwargs.get('db')
+
+    def _login(self):
+        client = self._create_client()
         try:
-            proxy = rest_proxy.HPLeftHandRESTProxy(*args, **kwargs)
-        except exception.NotFound:
-            proxy = cliq_proxy.HPLeftHandCLIQProxy(*args, **kwargs)
+            if self.configuration.hplefthand_debug:
+                client.debug_rest(True)
 
-        return proxy
+            client.login(
+                self.configuration.hplefthand_username,
+                self.configuration.hplefthand_password)
 
-    def check_for_setup_error(self):
-        self.proxy.check_for_setup_error()
+            cluster_info = client.getClusterByName(
+                self.configuration.hplefthand_clustername)
+            self.cluster_id = cluster_info['id']
+            virtual_ips = cluster_info['virtualIPAddresses']
+            self.cluster_vip = virtual_ips[0]['ipV4Address']
+
+            return client
+        except hpexceptions.HTTPNotFound:
+            raise exception.DriverNotInitialized(
+                _('LeftHand cluster not found'))
+        except Exception as ex:
+            raise exception.DriverNotInitialized(ex)
+
+    def _logout(self, client):
+        client.logout()
+
+    def _create_client(self):
+        return hp_lh_client.HPLeftHandClient(
+            self.configuration.hplefthand_api_url)
 
     def do_setup(self, context):
-        self.proxy = self._create_proxy(*self.args, **self.kwargs)
+        """Set up LeftHand client."""
+        if hplefthandclient.version < MIN_CLIENT_VERSION:
+            ex_msg = (_("Invalid hplefthandclient version found ("
+                        "%(found)s). Version %(minimum)s or greater "
+                        "required.")
+                      % {'found': hplefthandclient.version,
+                         'minimum': MIN_CLIENT_VERSION})
+            LOG.error(ex_msg)
+            raise exception.InvalidInput(reason=ex_msg)
+
+    def check_for_setup_error(self):
+        """Checks for incorrect LeftHand API being used on backend."""
+        client = self._login()
+        try:
+            self.api_version = client.getApiVersion()
 
-        LOG.info(_LI("HPLeftHand driver %(driver_ver)s, "
-                     "proxy %(proxy_ver)s"), {
-            "driver_ver": self.VERSION,
-            "proxy_ver": self.proxy.get_version_string()})
+            LOG.info(_LI("HPLeftHand API version %s"), self.api_version)
 
-        if isinstance(self.proxy, cliq_proxy.HPLeftHandCLIQProxy):
-            self.proxy.do_setup(context)
-        else:
-            # Check minimum client version for REST proxy
-            client_version = rest_proxy.hplefthandclient.version
-
-            if client_version < MIN_CLIENT_VERSION:
-                ex_msg = (_("Invalid hplefthandclient version found ("
-                            "%(found)s). Version %(minimum)s or greater "
-                            "required.")
-                          % {'found': client_version,
-                             'minimum': MIN_CLIENT_VERSION})
-                LOG.error(ex_msg)
-                raise exception.InvalidInput(reason=ex_msg)
+            if self.api_version < MIN_API_VERSION:
+                LOG.warning(_LW("HPLeftHand API is version %(current)s. "
+                                "A minimum version of %(min)s is needed for "
+                                "manage/unmanage support."),
+                            {'current': self.api_version,
+                             'min': MIN_API_VERSION})
+        finally:
+            self._logout(client)
+
+    def get_version_string(self):
+        return (_('REST %(proxy_ver)s hplefthandclient %(rest_ver)s') % {
+            'proxy_ver': self.VERSION,
+            'rest_ver': hplefthandclient.get_version_string()})
 
     def create_volume(self, volume):
         """Creates a volume."""
-        return self.proxy.create_volume(volume)
+        client = self._login()
+        try:
+            # get the extra specs of interest from this volume's volume type
+            volume_extra_specs = self._get_volume_extra_specs(volume)
+            extra_specs = self._get_lh_extra_specs(
+                volume_extra_specs,
+                extra_specs_key_map.keys())
+
+            # map the extra specs key/value pairs to key/value pairs
+            # used as optional configuration values by the LeftHand backend
+            optional = self._map_extra_specs(extra_specs)
+
+            # if provisioning is not set, default to thin
+            if 'isThinProvisioned' not in optional:
+                optional['isThinProvisioned'] = True
+
+            # AdaptiveOptimization defaults to 'true' if you don't specify the
+            # value on a create, and that is the most efficient way to create
+            # a volume. If you pass in 'false' or 'true' for AO, it will result
+            # in an update operation following the create operation to set this
+            # value, so it is best to not specify the value and let it default
+            # to 'true'.
+            if optional.get('isAdaptiveOptimizationEnabled'):
+                del optional['isAdaptiveOptimizationEnabled']
+
+            clusterName = self.configuration.hplefthand_clustername
+            optional['clusterName'] = clusterName
+
+            volume_info = client.createVolume(
+                volume['name'], self.cluster_id,
+                volume['size'] * units.Gi,
+                optional)
+
+            return self._update_provider(volume_info)
+        except Exception as ex:
+            raise exception.VolumeBackendAPIException(data=ex)
+        finally:
+            self._logout(client)
+
+    def delete_volume(self, volume):
+        """Deletes a volume."""
+        client = self._login()
+        try:
+            volume_info = client.getVolumeByName(volume['name'])
+            client.deleteVolume(volume_info['id'])
+        except hpexceptions.HTTPNotFound:
+            LOG.error(_LE("Volume did not exist. It will not be deleted"))
+        except Exception as ex:
+            raise exception.VolumeBackendAPIException(ex)
+        finally:
+            self._logout(client)
 
     def extend_volume(self, volume, new_size):
         """Extend the size of an existing volume."""
-        self.proxy.extend_volume(volume, new_size)
+        client = self._login()
+        try:
+            volume_info = client.getVolumeByName(volume['name'])
+
+            # convert GB to bytes
+            options = {'size': int(new_size) * units.Gi}
+            client.modifyVolume(volume_info['id'], options)
+        except Exception as ex:
+            raise exception.VolumeBackendAPIException(ex)
+        finally:
+            self._logout(client)
 
     def create_consistencygroup(self, context, group):
-        """Creates a consistency group."""
-        return self.proxy.create_consistencygroup(context, group)
+        """Creates a consistencygroup."""
+        model_update = {'status': 'available'}
+        return model_update
 
     def create_consistencygroup_from_src(self, context, group, volumes,
                                          cgsnapshot=None, snapshots=None,
                                          source_cg=None, source_vols=None):
         """Creates a consistency group from a source"""
-        return self.proxy.create_consistencygroup_from_src(
-            context, group, volumes, cgsnapshot, snapshots, source_cg,
-            source_vols)
+        msg = _("Creating a consistency group from a source is not "
+                "currently supported.")
+        LOG.error(msg)
+        raise NotImplementedError(msg)
 
     def delete_consistencygroup(self, context, group, volumes):
         """Deletes a consistency group."""
-        return self.proxy.delete_consistencygroup(context, group)
+        # TODO(aorourke): Can't eliminate the DB calls here due to CG API.
+        # Will fix in M release
+        volumes = self.db.volume_get_all_by_group(context, group.id)
+        for volume in volumes:
+            self.delete_volume(volume)
+            volume.status = 'deleted'
+
+        model_update = {'status': group.status}
+
+        return model_update, volumes
 
     def update_consistencygroup(self, context, group,
                                 add_volumes=None, remove_volumes=None):
-        """Updates a consistency group."""
-        return self.proxy.update_consistencygroup(context, group, add_volumes,
-                                                  remove_volumes)
+        """Updates a consistency group.
+
+        Because the backend has no concept of volume grouping, cinder will
+        maintain all volume/consistency group relationships. Because of this
+        functionality, there is no need to make any client calls; instead
+        simply returning out of this function allows cinder to properly
+        add/remove volumes from the consistency group.
+        """
+        return None, None, None
 
     def create_cgsnapshot(self, context, cgsnapshot, snapshots):
         """Creates a consistency group snapshot."""
-        return self.proxy.create_cgsnapshot(context, cgsnapshot)
+        client = self._login()
+        try:
+            # TODO(aorourke): Can't eliminate the DB calls here due to CG API.
+            # Will fix in M release
+            snapshots = objects.SnapshotList().get_all_for_cgsnapshot(
+                context, cgsnapshot['id'])
+
+            snap_set = []
+            snapshot_base_name = "snapshot-" + cgsnapshot['id']
+            for i, snapshot in enumerate(snapshots):
+                volume = snapshot.volume
+                volume_name = volume['name']
+                try:
+                    volume_info = client.getVolumeByName(volume_name)
+                except Exception as ex:
+                    error = six.text_type(ex)
+                    LOG.error(_LE("Could not find volume with name %(name)s. "
+                                  "Error: %(error)s"),
+                              {'name': volume_name,
+                               'error': error})
+                    raise exception.VolumeBackendAPIException(data=error)
+
+                volume_id = volume_info['id']
+                snapshot_name = snapshot_base_name + "-" + six.text_type(i)
+                snap_set_member = {'volumeName': volume_name,
+                                   'volumeId': volume_id,
+                                   'snapshotName': snapshot_name}
+                snap_set.append(snap_set_member)
+                snapshot.status = 'available'
+
+            source_volume_id = snap_set[0]['volumeId']
+            optional = {'inheritAccess': True}
+            description = cgsnapshot.get('description', None)
+            if description:
+                optional['description'] = description
+
+            try:
+                client.createSnapshotSet(source_volume_id, snap_set, optional)
+            except Exception as ex:
+                error = six.text_type(ex)
+                LOG.error(_LE("Could not create snapshot set. Error: '%s'"),
+                          error)
+                raise exception.VolumeBackendAPIException(
+                    data=error)
+
+        except Exception as ex:
+            raise exception.VolumeBackendAPIException(data=six.text_type(ex))
+        finally:
+            self._logout(client)
+
+        model_update = {'status': 'available'}
+
+        return model_update, snapshots
 
     def delete_cgsnapshot(self, context, cgsnapshot, snapshots):
         """Deletes a consistency group snapshot."""
-        return self.proxy.delete_cgsnapshot(context, cgsnapshot)
 
-    def create_volume_from_snapshot(self, volume, snapshot):
-        """Creates a volume from a snapshot."""
-        return self.proxy.create_volume_from_snapshot(volume, snapshot)
+        client = self._login()
+        try:
+            snap_name_base = "snapshot-" + cgsnapshot['id']
+
+            # TODO(aorourke): Can't eliminate the DB calls here due to CG API.
+            # Will fix in M release
+            snapshots = objects.SnapshotList().get_all_for_cgsnapshot(
+                context, cgsnapshot['id'])
+
+            for i, snapshot in enumerate(snapshots):
+                try:
+                    snap_name = snap_name_base + "-" + six.text_type(i)
+                    snap_info = client.getSnapshotByName(snap_name)
+                    client.deleteSnapshot(snap_info['id'])
+                except hpexceptions.HTTPNotFound:
+                    LOG.error(_LE("Snapshot did not exist. It will not be "
+                              "deleted."))
+                except hpexceptions.HTTPServerError as ex:
+                    in_use_msg = ('cannot be deleted because it is a clone '
+                                  'point')
+                    if in_use_msg in ex.get_description():
+                        raise exception.SnapshotIsBusy(snapshot_name=snap_name)
+
+                    raise exception.VolumeBackendAPIException(
+                        data=six.text_type(ex))
+
+        except Exception as ex:
+            raise exception.VolumeBackendAPIException(
+                data=six.text_type(ex))
+        finally:
+            self._logout(client)
+
+        model_update = {'status': cgsnapshot['status']}
+
+        return model_update, snapshots
 
     def create_snapshot(self, snapshot):
         """Creates a snapshot."""
-        self.proxy.create_snapshot(snapshot)
+        client = self._login()
+        try:
+            volume_info = client.getVolumeByName(snapshot['volume_name'])
 
-    def delete_volume(self, volume):
-        """Deletes a volume."""
-        self.proxy.delete_volume(volume)
+            option = {'inheritAccess': True}
+            client.createSnapshot(snapshot['name'],
+                                  volume_info['id'],
+                                  option)
+        except Exception as ex:
+            raise exception.VolumeBackendAPIException(ex)
+        finally:
+            self._logout(client)
 
     def delete_snapshot(self, snapshot):
         """Deletes a snapshot."""
-        self.proxy.delete_snapshot(snapshot)
+        client = self._login()
+        try:
+            snap_info = client.getSnapshotByName(snapshot['name'])
+            client.deleteSnapshot(snap_info['id'])
+        except hpexceptions.HTTPNotFound:
+            LOG.error(_LE("Snapshot did not exist. It will not be deleted"))
+        except hpexceptions.HTTPServerError as ex:
+            in_use_msg = 'cannot be deleted because it is a clone point'
+            if in_use_msg in ex.get_description():
+                raise exception.SnapshotIsBusy(snapshot_name=snapshot['name'])
+
+            raise exception.VolumeBackendAPIException(ex)
+
+        except Exception as ex:
+            raise exception.VolumeBackendAPIException(ex)
+        finally:
+            self._logout(client)
+
+    def get_volume_stats(self, refresh=False):
+        """Gets volume stats."""
+        client = self._login()
+        try:
+            if refresh:
+                self._update_backend_status(client)
+
+            return self.device_stats
+        finally:
+            self._logout(client)
+
+    def _update_backend_status(self, client):
+        data = {}
+        backend_name = self.configuration.safe_get('volume_backend_name')
+        data['driver_version'] = self.VERSION
+        data['volume_backend_name'] = backend_name or self.__class__.__name__
+        data['reserved_percentage'] = (
+            self.configuration.safe_get('reserved_percentage'))
+        data['storage_protocol'] = 'iSCSI'
+        data['vendor_name'] = 'Hewlett-Packard'
+        data['location_info'] = (self.DRIVER_LOCATION % {
+            'cluster': self.configuration.hplefthand_clustername,
+            'vip': self.cluster_vip})
+        data['thin_provisioning_support'] = True
+        data['thick_provisioning_support'] = True
+        data['max_over_subscription_ratio'] = (
+            self.configuration.safe_get('max_over_subscription_ratio'))
+
+        cluster_info = client.getCluster(self.cluster_id)
+
+        total_capacity = cluster_info['spaceTotal']
+        free_capacity = cluster_info['spaceAvailable']
+
+        # convert to GB
+        data['total_capacity_gb'] = int(total_capacity) / units.Gi
+        data['free_capacity_gb'] = int(free_capacity) / units.Gi
+
+        # Collect some stats
+        capacity_utilization = (
+            (float(total_capacity - free_capacity) /
+             float(total_capacity)) * 100)
+        # Don't have a better way to get the total number volumes
+        # so try to limit the size of data for now. Once new lefthand API is
+        # available, replace this call.
+        total_volumes = 0
+        provisioned_size = 0
+        volumes = client.getVolumes(
+            cluster=self.configuration.hplefthand_clustername,
+            fields=['members[id]', 'members[clusterName]', 'members[size]'])
+        if volumes:
+            total_volumes = volumes['total']
+            provisioned_size = sum(
+                members['size'] for members in volumes['members'])
+        data['provisioned_capacity_gb'] = int(provisioned_size) / units.Gi
+        data['capacity_utilization'] = capacity_utilization
+        data['total_volumes'] = total_volumes
+        data['filter_function'] = self.get_filter_function()
+        data['goodness_function'] = self.get_goodness_function()
+        if hplefthandclient.version >= MIN_CG_CLIENT_VERSION:
+            data['consistencygroup_support'] = True
+
+        self.device_stats = data
 
     def initialize_connection(self, volume, connector):
-        """Assigns the volume to a server."""
-        return self.proxy.initialize_connection(volume, connector)
+        """Assigns the volume to a server.
+
+        Assign any created volume to a compute node/host so that it can be
+        used from that host. HP VSA requires a volume to be assigned
+        to a server.
+        """
+        client = self._login()
+        try:
+            server_info = self._create_server(connector, client)
+            volume_info = client.getVolumeByName(volume['name'])
+
+            access_already_enabled = False
+            if volume_info['iscsiSessions'] is not None:
+                # Extract the server id for each session to check if the
+                # new server already has access permissions enabled.
+                for session in volume_info['iscsiSessions']:
+                    server_id = int(session['server']['uri'].split('/')[3])
+                    if server_id == server_info['id']:
+                        access_already_enabled = True
+                        break
+
+            if not access_already_enabled:
+                client.addServerAccess(
+                    volume_info['id'],
+                    server_info['id'])
+
+            iscsi_properties = self._get_iscsi_properties(volume)
+
+            if ('chapAuthenticationRequired' in server_info and
+                    server_info['chapAuthenticationRequired']):
+                iscsi_properties['auth_method'] = 'CHAP'
+                iscsi_properties['auth_username'] = connector['initiator']
+                iscsi_properties['auth_password'] = (
+                    server_info['chapTargetSecret'])
+
+            return {'driver_volume_type': 'iscsi', 'data': iscsi_properties}
+        except Exception as ex:
+            raise exception.VolumeBackendAPIException(ex)
+        finally:
+            self._logout(client)
 
     def terminate_connection(self, volume, connector, **kwargs):
         """Unassign the volume from the host."""
-        self.proxy.terminate_connection(volume, connector)
+        client = self._login()
+        try:
+            volume_info = client.getVolumeByName(volume['name'])
+            server_info = client.getServerByName(connector['host'])
+            volume_list = client.findServerVolumes(server_info['name'])
 
-    def get_volume_stats(self, refresh=False):
-        data = self.proxy.get_volume_stats(refresh)
-        data['driver_version'] = self.VERSION
-        return data
+            removeServer = True
+            for entry in volume_list:
+                if entry['id'] != volume_info['id']:
+                    removeServer = False
+                    break
+
+            client.removeServerAccess(
+                volume_info['id'],
+                server_info['id'])
+
+            if removeServer:
+                client.deleteServer(server_info['id'])
+        except Exception as ex:
+            raise exception.VolumeBackendAPIException(ex)
+        finally:
+            self._logout(client)
+
+    def create_volume_from_snapshot(self, volume, snapshot):
+        """Creates a volume from a snapshot."""
+        client = self._login()
+        try:
+            snap_info = client.getSnapshotByName(snapshot['name'])
+            volume_info = client.cloneSnapshot(
+                volume['name'],
+                snap_info['id'])
+            return self._update_provider(volume_info)
+        except Exception as ex:
+            raise exception.VolumeBackendAPIException(ex)
+        finally:
+            self._logout(client)
 
     def create_cloned_volume(self, volume, src_vref):
-        return self.proxy.create_cloned_volume(volume, src_vref)
+        client = self._login()
+        try:
+            volume_info = client.getVolumeByName(src_vref['name'])
+            clone_info = client.cloneVolume(volume['name'], volume_info['id'])
+            return self._update_provider(clone_info)
+        except Exception as ex:
+            raise exception.VolumeBackendAPIException(ex)
+        finally:
+            self._logout(client)
+
+    def _get_volume_extra_specs(self, volume):
+        """Get extra specs from a volume."""
+        extra_specs = {}
+        type_id = volume.get('volume_type_id', None)
+        if type_id is not None:
+            ctxt = context.get_admin_context()
+            volume_type = volume_types.get_volume_type(ctxt, type_id)
+            extra_specs = volume_type.get('extra_specs')
+        return extra_specs
+
+    def _get_lh_extra_specs(self, extra_specs, valid_keys):
+        """Get LeftHand extra_specs (valid_keys only)."""
+        extra_specs_of_interest = {}
+        for key, value in extra_specs.items():
+            if key in valid_keys:
+                extra_specs_of_interest[key] = value
+        return extra_specs_of_interest
+
+    def _map_extra_specs(self, extra_specs):
+        """Map the extra spec key/values to LeftHand key/values."""
+        client_options = {}
+        for key, value in extra_specs.items():
+            # map extra spec key to lh client option key
+            client_key = extra_specs_key_map[key]
+            # map extra spect value to lh client option value
+            try:
+                value_map = extra_specs_value_map[client_key]
+                # an invalid value will throw KeyError
+                client_value = value_map[value]
+                client_options[client_key] = client_value
+            except KeyError:
+                LOG.error(_LE("'%(value)s' is an invalid value "
+                              "for extra spec '%(key)s'"),
+                          {'value': value, 'key': key})
+        return client_options
+
+    def _update_provider(self, volume_info):
+        # TODO(justinsb): Is this always 1? Does it matter?
+        cluster_interface = '1'
+        iscsi_portal = self.cluster_vip + ":3260," + cluster_interface
+
+        return {'provider_location': (
+            "%s %s %s" % (iscsi_portal, volume_info['iscsiIqn'], 0))}
+
+    def _create_server(self, connector, client):
+        server_info = None
+        chap_enabled = self.configuration.hplefthand_iscsi_chap_enabled
+        try:
+            server_info = client.getServerByName(connector['host'])
+            chap_secret = server_info['chapTargetSecret']
+            if not chap_enabled and chap_secret:
+                LOG.warning(_LW('CHAP secret exists for host %s but CHAP is '
+                                'disabled'), connector['host'])
+            if chap_enabled and chap_secret is None:
+                LOG.warning(_LW('CHAP is enabled, but server secret not '
+                                'configured on server %s'), connector['host'])
+            return server_info
+        except hpexceptions.HTTPNotFound:
+            # server does not exist, so create one
+            pass
+
+        optional = None
+        if chap_enabled:
+            chap_secret = utils.generate_password()
+            optional = {'chapName': connector['initiator'],
+                        'chapTargetSecret': chap_secret,
+                        'chapAuthenticationRequired': True
+                        }
+
+        server_info = client.createServer(connector['host'],
+                                          connector['initiator'],
+                                          optional)
+        return server_info
 
     def create_export(self, context, volume, connector):
-        return self.proxy.create_export(context, volume, connector)
+        pass
 
     def ensure_export(self, context, volume):
-        return self.proxy.ensure_export(context, volume)
+        pass
 
     def remove_export(self, context, volume):
-        return self.proxy.remove_export(context, volume)
+        pass
 
-    def retype(self, context, volume, new_type, diff, host):
-        """Convert the volume to be of the new type."""
-        return self.proxy.retype(context, volume, new_type, diff, host)
+    def retype(self, ctxt, volume, new_type, diff, host):
+        """Convert the volume to be of the new type.
+
+        Returns a boolean indicating whether the retype occurred.
+
+        :param ctxt: Context
+        :param volume: A dictionary describing the volume to retype
+        :param new_type: A dictionary describing the volume type to convert to
+        :param diff: A dictionary with the difference between the two types
+        :param host: A dictionary describing the host, where
+                     host['host'] is its name, and host['capabilities'] is a
+                     dictionary of its reported capabilities.
+        """
+        LOG.debug('enter: retype: id=%(id)s, new_type=%(new_type)s,'
+                  'diff=%(diff)s, host=%(host)s', {'id': volume['id'],
+                                                   'new_type': new_type,
+                                                   'diff': diff,
+                                                   'host': host})
+        client = self._login()
+        try:
+            volume_info = client.getVolumeByName(volume['name'])
+
+            # pick out the LH extra specs
+            new_extra_specs = dict(new_type).get('extra_specs')
+            lh_extra_specs = self._get_lh_extra_specs(
+                new_extra_specs,
+                extra_specs_key_map.keys())
+
+            LOG.debug('LH specs=%(specs)s', {'specs': lh_extra_specs})
+
+            # only set the ones that have changed
+            changed_extra_specs = {}
+            for key, value in lh_extra_specs.items():
+                (old, new) = diff['extra_specs'][key]
+                if old != new:
+                    changed_extra_specs[key] = value
+
+            # map extra specs to LeftHand options
+            options = self._map_extra_specs(changed_extra_specs)
+            if len(options) > 0:
+                client.modifyVolume(volume_info['id'], options)
+            return True
+        except hpexceptions.HTTPNotFound:
+            raise exception.VolumeNotFound(volume_id=volume['id'])
+        except Exception as ex:
+            LOG.warning(_LW("%s"), ex)
+        finally:
+            self._logout(client)
+
+        return False
 
     def migrate_volume(self, ctxt, volume, host):
-        """Migrate directly if source and dest are managed by same storage."""
-        return self.proxy.migrate_volume(ctxt, volume, host)
+        """Migrate the volume to the specified host.
+
+        Backend assisted volume migration will occur if and only if;
+
+        1. Same LeftHand backend
+        2. Volume cannot be attached
+        3. Volumes with snapshots cannot be migrated
+        4. Source and Destination clusters must be in the same management group
+
+        Volume re-type is not supported.
+
+        Returns a boolean indicating whether the migration occurred, as well as
+        model_update.
+
+        :param ctxt: Context
+        :param volume: A dictionary describing the volume to migrate
+        :param host: A dictionary describing the host to migrate to, where
+                     host['host'] is its name, and host['capabilities'] is a
+                     dictionary of its reported capabilities.
+        """
+        LOG.debug('enter: migrate_volume: id=%(id)s, host=%(host)s, '
+                  'cluster=%(cluster)s', {
+                      'id': volume['id'],
+                      'host': host,
+                      'cluster': self.configuration.hplefthand_clustername})
+
+        false_ret = (False, None)
+        if 'location_info' not in host['capabilities']:
+            return false_ret
+
+        host_location = host['capabilities']['location_info']
+        (driver, cluster, vip) = host_location.split(' ')
+        client = self._login()
+        try:
+            # get the cluster info, if it exists and compare
+            cluster_info = client.getClusterByName(cluster)
+            LOG.debug('Cluster info: %s', cluster_info)
+            virtual_ips = cluster_info['virtualIPAddresses']
+
+            if driver != self.__class__.__name__:
+                LOG.info(_LI("Cannot provide backend assisted migration for "
+                             "volume: %s because volume is from a different "
+                             "backend."), volume['name'])
+                return false_ret
+            if vip != virtual_ips[0]['ipV4Address']:
+                LOG.info(_LI("Cannot provide backend assisted migration for "
+                             "volume: %s because cluster exists in different "
+                             "management group."), volume['name'])
+                return false_ret
+
+        except hpexceptions.HTTPNotFound:
+            LOG.info(_LI("Cannot provide backend assisted migration for "
+                         "volume: %s because cluster exists in different "
+                         "management group."), volume['name'])
+            return false_ret
+        finally:
+            self._logout(client)
+
+        client = self._login()
+        try:
+            volume_info = client.getVolumeByName(volume['name'])
+            LOG.debug('Volume info: %s', volume_info)
+
+            # can't migrate if server is attached
+            if volume_info['iscsiSessions'] is not None:
+                LOG.info(_LI("Cannot provide backend assisted migration "
+                             "for volume: %s because the volume has been "
+                             "exported."), volume['name'])
+                return false_ret
+
+            # can't migrate if volume has snapshots
+            snap_info = client.getVolume(
+                volume_info['id'],
+                'fields=snapshots,snapshots[resource[members[name]]]')
+            LOG.debug('Snapshot info: %s', snap_info)
+            if snap_info['snapshots']['resource'] is not None:
+                LOG.info(_LI("Cannot provide backend assisted migration "
+                             "for volume: %s because the volume has "
+                             "snapshots."), volume['name'])
+                return false_ret
+
+            options = {'clusterName': cluster}
+            client.modifyVolume(volume_info['id'], options)
+        except hpexceptions.HTTPNotFound:
+            LOG.info(_LI("Cannot provide backend assisted migration for "
+                         "volume: %s because volume does not exist in this "
+                         "management group."), volume['name'])
+            return false_ret
+        except hpexceptions.HTTPServerError as ex:
+            LOG.error(_LE("Exception: %s"), ex)
+            return false_ret
+        finally:
+            self._logout(client)
+
+        return (True, None)
 
     def update_migrated_volume(self, context, volume, new_volume,
                                original_volume_status):
-        return self.proxy.update_migrated_volume(context, volume, new_volume,
-                                                 original_volume_status)
+        """Rename the new (temp) volume to it's original name.
+
+
+        This method tries to rename the new volume to it's original
+        name after the migration has completed.
+
+        """
+        LOG.debug("Update volume name for %(id)s.", {'id': new_volume['id']})
+        name_id = None
+        provider_location = None
+        if original_volume_status == 'available':
+            # volume isn't attached and can be updated
+            original_name = CONF.volume_name_template % volume['id']
+            current_name = CONF.volume_name_template % new_volume['id']
+            client = self._login()
+            try:
+                volume_info = client.getVolumeByName(current_name)
+                volumeMods = {'name': original_name}
+                client.modifyVolume(volume_info['id'], volumeMods)
+                LOG.info(_LI("Volume name changed from %(tmp)s to %(orig)s."),
+                         {'tmp': current_name, 'orig': original_name})
+            except Exception as e:
+                LOG.error(_LE("Changing the volume name from %(tmp)s to "
+                              "%(orig)s failed because %(reason)s."),
+                          {'tmp': current_name, 'orig': original_name,
+                           'reason': e})
+                name_id = new_volume['_name_id'] or new_volume['id']
+                provider_location = new_volume['provider_location']
+            finally:
+                self._logout(client)
+        else:
+            # the backend can't change the name.
+            name_id = new_volume['_name_id'] or new_volume['id']
+            provider_location = new_volume['provider_location']
+
+        return {'_name_id': name_id, 'provider_location': provider_location}
 
     def manage_existing(self, volume, existing_ref):
-        return self.proxy.manage_existing(volume, existing_ref)
+        """Manage an existing LeftHand volume.
+
+        existing_ref is a dictionary of the form:
+        {'source-name': <name of the virtual volume>}
+        """
+        # Check API Version
+        self._check_api_version()
+
+        target_vol_name = self._get_existing_volume_ref_name(existing_ref)
+
+        # Check for the existence of the virtual volume.
+        client = self._login()
+        try:
+            volume_info = client.getVolumeByName(target_vol_name)
+        except hpexceptions.HTTPNotFound:
+            err = (_("Virtual volume '%s' doesn't exist on array.") %
+                   target_vol_name)
+            LOG.error(err)
+            raise exception.InvalidInput(reason=err)
+        finally:
+            self._logout(client)
+
+        # Generate the new volume information based on the new ID.
+        new_vol_name = 'volume-' + volume['id']
+
+        volume_type = None
+        if volume['volume_type_id']:
+            try:
+                volume_type = self._get_volume_type(volume['volume_type_id'])
+            except Exception:
+                reason = (_("Volume type ID '%s' is invalid.") %
+                          volume['volume_type_id'])
+                raise exception.ManageExistingVolumeTypeMismatch(reason=reason)
+
+        new_vals = {"name": new_vol_name}
+
+        client = self._login()
+        try:
+            # Update the existing volume with the new name.
+            client.modifyVolume(volume_info['id'], new_vals)
+        finally:
+            self._logout(client)
+
+        LOG.info(_LI("Virtual volume '%(ref)s' renamed to '%(new)s'."),
+                 {'ref': existing_ref['source-name'], 'new': new_vol_name})
+
+        display_name = None
+        if volume['display_name']:
+            display_name = volume['display_name']
+
+        if volume_type:
+            LOG.info(_LI("Virtual volume %(disp)s '%(new)s' is "
+                         "being retyped."),
+                     {'disp': display_name, 'new': new_vol_name})
+
+            try:
+                self.retype(None,
+                            volume,
+                            volume_type,
+                            volume_type['extra_specs'],
+                            volume['host'])
+                LOG.info(_LI("Virtual volume %(disp)s successfully retyped to "
+                             "%(new_type)s."),
+                         {'disp': display_name,
+                          'new_type': volume_type.get('name')})
+            except Exception:
+                with excutils.save_and_reraise_exception():
+                    LOG.warning(_LW("Failed to manage virtual volume %(disp)s "
+                                    "due to error during retype."),
+                                {'disp': display_name})
+                    # Try to undo the rename and clear the new comment.
+                    client = self._login()
+                    try:
+                        client.modifyVolume(
+                            volume_info['id'],
+                            {'name': target_vol_name})
+                    finally:
+                        self._logout(client)
+
+        updates = {'display_name': display_name}
+
+        LOG.info(_LI("Virtual volume %(disp)s '%(new)s' is "
+                     "now being managed."),
+                 {'disp': display_name, 'new': new_vol_name})
+
+        # Return display name to update the name displayed in the GUI and
+        # any model updates from retype.
+        return updates
 
     def manage_existing_get_size(self, volume, existing_ref):
-        return self.proxy.manage_existing_get_size(volume, existing_ref)
+        """Return size of volume to be managed by manage_existing.
+
+        existing_ref is a dictionary of the form:
+        {'source-name': <name of the virtual volume>}
+        """
+        # Check API version.
+        self._check_api_version()
+
+        target_vol_name = self._get_existing_volume_ref_name(existing_ref)
+
+        # Make sure the reference is not in use.
+        if re.match('volume-*|snapshot-*', target_vol_name):
+            reason = _("Reference must be the volume name of an unmanaged "
+                       "virtual volume.")
+            raise exception.ManageExistingInvalidReference(
+                existing_ref=target_vol_name,
+                reason=reason)
+
+        # Check for the existence of the virtual volume.
+        client = self._login()
+        try:
+            volume_info = client.getVolumeByName(target_vol_name)
+        except hpexceptions.HTTPNotFound:
+            err = (_("Virtual volume '%s' doesn't exist on array.") %
+                   target_vol_name)
+            LOG.error(err)
+            raise exception.InvalidInput(reason=err)
+        finally:
+            self._logout(client)
+
+        return int(math.ceil(float(volume_info['size']) / units.Gi))
 
     def unmanage(self, volume):
-        return self.proxy.unmanage(volume)
+        """Removes the specified volume from Cinder management."""
+        # Check API version.
+        self._check_api_version()
+
+        # Rename the volume's name to unm-* format so that it can be
+        # easily found later.
+        client = self._login()
+        try:
+            volume_info = client.getVolumeByName(volume['name'])
+            new_vol_name = 'unm-' + six.text_type(volume['id'])
+            options = {'name': new_vol_name}
+            client.modifyVolume(volume_info['id'], options)
+        finally:
+            self._logout(client)
+
+        LOG.info(_LI("Virtual volume %(disp)s '%(vol)s' is no longer managed. "
+                     "Volume renamed to '%(new)s'."),
+                 {'disp': volume['display_name'],
+                  'vol': volume['name'],
+                  'new': new_vol_name})
+
+    def _get_existing_volume_ref_name(self, existing_ref):
+        """Returns the volume name of an existing reference.
+
+        Checks if an existing volume reference has a source-name element.
+        If source-name is not present an error will be thrown.
+        """
+        if 'source-name' not in existing_ref:
+            reason = _("Reference must contain source-name.")
+            raise exception.ManageExistingInvalidReference(
+                existing_ref=existing_ref,
+                reason=reason)
+
+        return existing_ref['source-name']
+
+    def _check_api_version(self):
+        """Checks that the API version is correct."""
+        if (self.api_version < MIN_API_VERSION):
+            ex_msg = (_('Invalid HPLeftHand API version found: %(found)s. '
+                        'Version %(minimum)s or greater required for '
+                        'manage/unmanage support.')
+                      % {'found': self.api_version,
+                         'minimum': MIN_API_VERSION})
+            LOG.error(ex_msg)
+            raise exception.InvalidInput(reason=ex_msg)
+
+    def _get_volume_type(self, type_id):
+        ctxt = context.get_admin_context()
+        return volume_types.get_volume_type(ctxt, type_id)
diff --git a/cinder/volume/drivers/san/hp/hp_lefthand_rest_proxy.py b/cinder/volume/drivers/san/hp/hp_lefthand_rest_proxy.py
deleted file mode 100644 (file)
index 9063eae..0000000
+++ /dev/null
@@ -1,1016 +0,0 @@
-#    (c) Copyright 2014-2015 Hewlett-Packard Development Company, L.P.
-#    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.
-#
-"""HP LeftHand SAN ISCSI REST Proxy."""
-
-from oslo_config import cfg
-from oslo_log import log as logging
-from oslo_utils import excutils
-from oslo_utils import importutils
-from oslo_utils import units
-
-from cinder import context
-from cinder import exception
-from cinder.i18n import _, _LE, _LI, _LW
-from cinder import objects
-from cinder.volume import driver
-from cinder.volume import utils
-from cinder.volume import volume_types
-
-import six
-
-import math
-import re
-
-LOG = logging.getLogger(__name__)
-
-hplefthandclient = importutils.try_import("hplefthandclient")
-if hplefthandclient:
-    from hplefthandclient import client as hp_lh_client
-    from hplefthandclient import exceptions as hpexceptions
-
-hplefthand_opts = [
-    cfg.StrOpt('hplefthand_api_url',
-               help="HP LeftHand WSAPI Server Url like "
-                    "https://<LeftHand ip>:8081/lhos"),
-    cfg.StrOpt('hplefthand_username',
-               help="HP LeftHand Super user username"),
-    cfg.StrOpt('hplefthand_password',
-               help="HP LeftHand Super user password",
-               secret=True),
-    cfg.StrOpt('hplefthand_clustername',
-               help="HP LeftHand cluster name"),
-    cfg.BoolOpt('hplefthand_iscsi_chap_enabled',
-                default=False,
-                help='Configure CHAP authentication for iSCSI connections '
-                '(Default: Disabled)'),
-    cfg.BoolOpt('hplefthand_debug',
-                default=False,
-                help="Enable HTTP debugging to LeftHand"),
-
-]
-
-CONF = cfg.CONF
-CONF.register_opts(hplefthand_opts)
-
-MIN_API_VERSION = "1.1"
-MIN_CG_CLIENT_VERSION = "1.0.6"
-
-# map the extra spec key to the REST client option key
-extra_specs_key_map = {
-    'hplh:provisioning': 'isThinProvisioned',
-    'hplh:ao': 'isAdaptiveOptimizationEnabled',
-    'hplh:data_pl': 'dataProtectionLevel',
-}
-
-# map the extra spec value to the REST client option value
-extra_specs_value_map = {
-    'isThinProvisioned': {'thin': True, 'full': False},
-    'isAdaptiveOptimizationEnabled': {'true': True, 'false': False},
-    'dataProtectionLevel': {
-        'r-0': 0, 'r-5': 1, 'r-10-2': 2, 'r-10-3': 3, 'r-10-4': 4, 'r-6': 5}
-}
-
-
-class HPLeftHandRESTProxy(driver.ISCSIDriver):
-    """Executes REST commands relating to HP/LeftHand SAN ISCSI volumes.
-
-    Version history:
-        1.0.0 - Initial REST iSCSI proxy
-        1.0.1 - Added support for retype
-        1.0.2 - Added support for volume migrate
-        1.0.3 - Fixed bug #1285829, HP LeftHand backend assisted migration
-                should check for snapshots
-        1.0.4 - Fixed bug #1285925, LeftHand AO volume create performance
-                improvement
-        1.0.5 - Fixed bug #1311350, Live-migration of an instance when
-                attached to a volume was causing an error.
-        1.0.6 - Removing locks bug #1395953
-        1.0.7 - Fixed bug #1353137, Server was not removed from the HP
-                Lefthand backend after the last volume was detached.
-        1.0.8 - Fixed bug #1418201, A cloned volume fails to attach.
-        1.0.9 - Adding support for manage/unmanage.
-        1.0.10 - Add stats for goodness_function and filter_function
-        1.0.11 - Add over subscription support
-        1.0.12 - Adds consistency group support
-        1.0.13 - Added update_migrated_volume #1493546
-    """
-
-    VERSION = "1.0.13"
-
-    device_stats = {}
-
-    def __init__(self, *args, **kwargs):
-        super(HPLeftHandRESTProxy, self).__init__(*args, **kwargs)
-        self.configuration.append_config_values(hplefthand_opts)
-        if not self.configuration.hplefthand_api_url:
-            raise exception.NotFound(_("HPLeftHand url not found"))
-
-        # blank is the only invalid character for cluster names
-        # so we need to use it as a separator
-        self.DRIVER_LOCATION = self.__class__.__name__ + ' %(cluster)s %(vip)s'
-        self.db = kwargs.get('db')
-
-    def _login(self):
-        client = self.do_setup(None)
-        return client
-
-    def _logout(self, client):
-        client.logout()
-
-    def _create_client(self):
-        return hp_lh_client.HPLeftHandClient(
-            self.configuration.hplefthand_api_url)
-
-    def do_setup(self, context):
-        """Set up LeftHand client."""
-        try:
-            client = self._create_client()
-            client.login(
-                self.configuration.hplefthand_username,
-                self.configuration.hplefthand_password)
-
-            if self.configuration.hplefthand_debug:
-                client.debug_rest(True)
-
-            cluster_info = client.getClusterByName(
-                self.configuration.hplefthand_clustername)
-            self.cluster_id = cluster_info['id']
-            virtual_ips = cluster_info['virtualIPAddresses']
-            self.cluster_vip = virtual_ips[0]['ipV4Address']
-            self._update_backend_status(client)
-
-            return client
-        except hpexceptions.HTTPNotFound:
-            raise exception.DriverNotInitialized(
-                _('LeftHand cluster not found'))
-        except Exception as ex:
-            raise exception.DriverNotInitialized(ex)
-
-    def check_for_setup_error(self):
-        """Checks for incorrect LeftHand API being used on backend."""
-        client = self._login()
-        try:
-            self.api_version = client.getApiVersion()
-
-            LOG.info(_LI("HPLeftHand API version %s"), self.api_version)
-
-            if self.api_version < MIN_API_VERSION:
-                LOG.warning(_LW("HPLeftHand API is version %(current)s. "
-                                "A minimum version of %(min)s is needed for "
-                                "manage/unmanage support."),
-                            {'current': self.api_version,
-                             'min': MIN_API_VERSION})
-        finally:
-            self._logout(client)
-
-    def get_version_string(self):
-        return (_('REST %(proxy_ver)s hplefthandclient %(rest_ver)s') % {
-            'proxy_ver': self.VERSION,
-            'rest_ver': hplefthandclient.get_version_string()})
-
-    def create_volume(self, volume):
-        """Creates a volume."""
-        client = self._login()
-        try:
-            # get the extra specs of interest from this volume's volume type
-            volume_extra_specs = self._get_volume_extra_specs(volume)
-            extra_specs = self._get_lh_extra_specs(
-                volume_extra_specs,
-                extra_specs_key_map.keys())
-
-            # map the extra specs key/value pairs to key/value pairs
-            # used as optional configuration values by the LeftHand backend
-            optional = self._map_extra_specs(extra_specs)
-
-            # if provisioning is not set, default to thin
-            if 'isThinProvisioned' not in optional:
-                optional['isThinProvisioned'] = True
-
-            # AdaptiveOptimization defaults to 'true' if you don't specify the
-            # value on a create, and that is the most efficient way to create
-            # a volume. If you pass in 'false' or 'true' for AO, it will result
-            # in an update operation following the create operation to set this
-            # value, so it is best to not specify the value and let it default
-            # to 'true'.
-            if optional.get('isAdaptiveOptimizationEnabled'):
-                del optional['isAdaptiveOptimizationEnabled']
-
-            clusterName = self.configuration.hplefthand_clustername
-            optional['clusterName'] = clusterName
-
-            volume_info = client.createVolume(
-                volume['name'], self.cluster_id,
-                volume['size'] * units.Gi,
-                optional)
-
-            return self._update_provider(volume_info)
-        except Exception as ex:
-            raise exception.VolumeBackendAPIException(ex)
-        finally:
-            self._logout(client)
-
-    def delete_volume(self, volume):
-        """Deletes a volume."""
-        client = self._login()
-        try:
-            volume_info = client.getVolumeByName(volume['name'])
-            client.deleteVolume(volume_info['id'])
-        except hpexceptions.HTTPNotFound:
-            LOG.error(_LE("Volume did not exist. It will not be deleted"))
-        except Exception as ex:
-            raise exception.VolumeBackendAPIException(ex)
-        finally:
-            self._logout(client)
-
-    def extend_volume(self, volume, new_size):
-        """Extend the size of an existing volume."""
-        client = self._login()
-        try:
-            volume_info = client.getVolumeByName(volume['name'])
-
-            # convert GB to bytes
-            options = {'size': int(new_size) * units.Gi}
-            client.modifyVolume(volume_info['id'], options)
-        except Exception as ex:
-            raise exception.VolumeBackendAPIException(ex)
-        finally:
-            self._logout(client)
-
-    def create_consistencygroup(self, context, group):
-        """Creates a consistencygroup."""
-        model_update = {'status': 'available'}
-        return model_update
-
-    def create_consistencygroup_from_src(self, context, group, volumes,
-                                         cgsnapshot=None, snapshots=None,
-                                         source_cg=None, source_vols=None):
-        """Creates a consistency group from a source"""
-        LOG.error(_LE("Creating a consistency group from a source is not "
-                      "currently supported."))
-        raise NotImplementedError()
-
-    def delete_consistencygroup(self, context, group):
-        """Deletes a consistency group."""
-        # TODO(aorourke): Can't eliminate the DB calls here due to CG API.
-        # Will fix in M release
-        volumes = self.db.volume_get_all_by_group(context, group.id)
-        for volume in volumes:
-            self.delete_volume(volume)
-            volume.status = 'deleted'
-
-        model_update = {'status': group.status}
-
-        return model_update, volumes
-
-    def update_consistencygroup(self, context, group,
-                                add_volumes=None, remove_volumes=None):
-        """Updates a consistency group.
-
-        Because the backend has no concept of volume grouping, cinder will
-        maintain all volume/consistency group relationships. Because of this
-        functionality, there is no need to make any client calls; instead
-        simply returning out of this function allows cinder to properly
-        add/remove volumes from the consistency group.
-        """
-        return None, None, None
-
-    def create_cgsnapshot(self, context, cgsnapshot):
-        """Creates a consistency group snapshot."""
-        client = self._login()
-        try:
-            # TODO(aorourke): Can't eliminate the DB calls here due to CG API.
-            # Will fix in M release
-            snapshots = objects.SnapshotList().get_all_for_cgsnapshot(
-                context, cgsnapshot['id'])
-
-            snap_set = []
-            snapshot_base_name = "snapshot-" + cgsnapshot['id']
-            for i, snapshot in enumerate(snapshots):
-                volume = snapshot.volume
-                volume_name = volume['name']
-                try:
-                    volume_info = client.getVolumeByName(volume_name)
-                except Exception as ex:
-                    error = six.text_type(ex)
-                    LOG.error(_LE("Could not find volume with name %(name)s. "
-                                  "Error: %(error)s"),
-                              {'name': volume_name,
-                               'error': error})
-                    raise exception.VolumeBackendAPIException(data=error)
-
-                volume_id = volume_info['id']
-                snapshot_name = snapshot_base_name + "-" + six.text_type(i)
-                snap_set_member = {'volumeName': volume_name,
-                                   'volumeId': volume_id,
-                                   'snapshotName': snapshot_name}
-                snap_set.append(snap_set_member)
-                snapshot.status = 'available'
-
-            source_volume_id = snap_set[0]['volumeId']
-            optional = {'inheritAccess': True}
-            description = cgsnapshot.get('description', None)
-            if description:
-                optional['description'] = description
-
-            try:
-                client.createSnapshotSet(source_volume_id, snap_set, optional)
-            except Exception as ex:
-                error = six.text_type(ex)
-                LOG.error(_LE("Could not create snapshot set. Error: '%s'"),
-                          error)
-                raise exception.VolumeBackendAPIException(
-                    data=error)
-
-        except Exception as ex:
-            raise exception.VolumeBackendAPIException(data=six.text_type(ex))
-        finally:
-            self._logout(client)
-
-        model_update = {'status': 'available'}
-
-        return model_update, snapshots
-
-    def delete_cgsnapshot(self, context, cgsnapshot):
-        """Deletes a consistency group snapshot."""
-
-        client = self._login()
-        try:
-            snap_name_base = "snapshot-" + cgsnapshot['id']
-
-            # TODO(aorourke): Can't eliminate the DB calls here due to CG API.
-            # Will fix in M release
-            snapshots = objects.SnapshotList().get_all_for_cgsnapshot(
-                context, cgsnapshot['id'])
-
-            for i, snapshot in enumerate(snapshots):
-                try:
-                    snap_name = snap_name_base + "-" + six.text_type(i)
-                    snap_info = client.getSnapshotByName(snap_name)
-                    client.deleteSnapshot(snap_info['id'])
-                except hpexceptions.HTTPNotFound:
-                    LOG.error(_LE("Snapshot did not exist. It will not be "
-                              "deleted."))
-                except hpexceptions.HTTPServerError as ex:
-                    in_use_msg = ('cannot be deleted because it is a clone '
-                                  'point')
-                    if in_use_msg in ex.get_description():
-                        raise exception.SnapshotIsBusy(snapshot_name=snap_name)
-
-                    raise exception.VolumeBackendAPIException(
-                        data=six.text_type(ex))
-
-        except Exception as ex:
-            raise exception.VolumeBackendAPIException(
-                data=six.text_type(ex))
-        finally:
-            self._logout(client)
-
-        model_update = {'status': cgsnapshot['status']}
-
-        return model_update, snapshots
-
-    def create_snapshot(self, snapshot):
-        """Creates a snapshot."""
-        client = self._login()
-        try:
-            volume_info = client.getVolumeByName(snapshot['volume_name'])
-
-            option = {'inheritAccess': True}
-            client.createSnapshot(snapshot['name'],
-                                  volume_info['id'],
-                                  option)
-        except Exception as ex:
-            raise exception.VolumeBackendAPIException(ex)
-        finally:
-            self._logout(client)
-
-    def delete_snapshot(self, snapshot):
-        """Deletes a snapshot."""
-        client = self._login()
-        try:
-            snap_info = client.getSnapshotByName(snapshot['name'])
-            client.deleteSnapshot(snap_info['id'])
-        except hpexceptions.HTTPNotFound:
-            LOG.error(_LE("Snapshot did not exist. It will not be deleted"))
-        except hpexceptions.HTTPServerError as ex:
-            in_use_msg = 'cannot be deleted because it is a clone point'
-            if in_use_msg in ex.get_description():
-                raise exception.SnapshotIsBusy(ex)
-
-            raise exception.VolumeBackendAPIException(ex)
-
-        except Exception as ex:
-            raise exception.VolumeBackendAPIException(ex)
-        finally:
-            self._logout(client)
-
-    def get_volume_stats(self, refresh=False):
-        """Gets volume stats."""
-        client = self._login()
-        try:
-            if refresh:
-                self._update_backend_status(client)
-
-            return self.device_stats
-        finally:
-            self._logout(client)
-
-    def _update_backend_status(self, client):
-        data = {}
-        backend_name = self.configuration.safe_get('volume_backend_name')
-        data['volume_backend_name'] = backend_name or self.__class__.__name__
-        data['reserved_percentage'] = (
-            self.configuration.safe_get('reserved_percentage'))
-        data['storage_protocol'] = 'iSCSI'
-        data['vendor_name'] = 'Hewlett-Packard'
-        data['location_info'] = (self.DRIVER_LOCATION % {
-            'cluster': self.configuration.hplefthand_clustername,
-            'vip': self.cluster_vip})
-        data['thin_provisioning_support'] = True
-        data['thick_provisioning_support'] = True
-        data['max_over_subscription_ratio'] = (
-            self.configuration.safe_get('max_over_subscription_ratio'))
-
-        cluster_info = client.getCluster(self.cluster_id)
-
-        total_capacity = cluster_info['spaceTotal']
-        free_capacity = cluster_info['spaceAvailable']
-
-        # convert to GB
-        data['total_capacity_gb'] = int(total_capacity) / units.Gi
-        data['free_capacity_gb'] = int(free_capacity) / units.Gi
-
-        # Collect some stats
-        capacity_utilization = (
-            (float(total_capacity - free_capacity) /
-             float(total_capacity)) * 100)
-        # Don't have a better way to get the total number volumes
-        # so try to limit the size of data for now. Once new lefthand API is
-        # available, replace this call.
-        total_volumes = 0
-        provisioned_size = 0
-        volumes = client.getVolumes(
-            cluster=self.configuration.hplefthand_clustername,
-            fields=['members[id]', 'members[clusterName]', 'members[size]'])
-        if volumes:
-            total_volumes = volumes['total']
-            provisioned_size = sum(
-                members['size'] for members in volumes['members'])
-        data['provisioned_capacity_gb'] = int(provisioned_size) / units.Gi
-        data['capacity_utilization'] = capacity_utilization
-        data['total_volumes'] = total_volumes
-        data['filter_function'] = self.get_filter_function()
-        data['goodness_function'] = self.get_goodness_function()
-        if hplefthandclient.version >= MIN_CG_CLIENT_VERSION:
-            data['consistencygroup_support'] = True
-
-        self.device_stats = data
-
-    def initialize_connection(self, volume, connector):
-        """Assigns the volume to a server.
-
-        Assign any created volume to a compute node/host so that it can be
-        used from that host. HP VSA requires a volume to be assigned
-        to a server.
-        """
-        client = self._login()
-        try:
-            server_info = self._create_server(connector, client)
-            volume_info = client.getVolumeByName(volume['name'])
-
-            access_already_enabled = False
-            if volume_info['iscsiSessions'] is not None:
-                # Extract the server id for each session to check if the
-                # new server already has access permissions enabled.
-                for session in volume_info['iscsiSessions']:
-                    server_id = int(session['server']['uri'].split('/')[3])
-                    if server_id == server_info['id']:
-                        access_already_enabled = True
-                        break
-
-            if not access_already_enabled:
-                client.addServerAccess(
-                    volume_info['id'],
-                    server_info['id'])
-
-            iscsi_properties = self._get_iscsi_properties(volume)
-
-            if ('chapAuthenticationRequired' in server_info and
-                    server_info['chapAuthenticationRequired']):
-                iscsi_properties['auth_method'] = 'CHAP'
-                iscsi_properties['auth_username'] = connector['initiator']
-                iscsi_properties['auth_password'] = (
-                    server_info['chapTargetSecret'])
-
-            return {'driver_volume_type': 'iscsi', 'data': iscsi_properties}
-        except Exception as ex:
-            raise exception.VolumeBackendAPIException(ex)
-        finally:
-            self._logout(client)
-
-    def terminate_connection(self, volume, connector, **kwargs):
-        """Unassign the volume from the host."""
-        client = self._login()
-        try:
-            volume_info = client.getVolumeByName(volume['name'])
-            server_info = client.getServerByName(connector['host'])
-            volume_list = client.findServerVolumes(server_info['name'])
-
-            removeServer = True
-            for entry in volume_list:
-                if entry['id'] != volume_info['id']:
-                    removeServer = False
-                    break
-
-            client.removeServerAccess(
-                volume_info['id'],
-                server_info['id'])
-
-            if removeServer:
-                client.deleteServer(server_info['id'])
-        except Exception as ex:
-            raise exception.VolumeBackendAPIException(ex)
-        finally:
-            self._logout(client)
-
-    def create_volume_from_snapshot(self, volume, snapshot):
-        """Creates a volume from a snapshot."""
-        client = self._login()
-        try:
-            snap_info = client.getSnapshotByName(snapshot['name'])
-            volume_info = client.cloneSnapshot(
-                volume['name'],
-                snap_info['id'])
-            return self._update_provider(volume_info)
-        except Exception as ex:
-            raise exception.VolumeBackendAPIException(ex)
-        finally:
-            self._logout(client)
-
-    def create_cloned_volume(self, volume, src_vref):
-        client = self._login()
-        try:
-            volume_info = client.getVolumeByName(src_vref['name'])
-            clone_info = client.cloneVolume(volume['name'], volume_info['id'])
-            return self._update_provider(clone_info)
-        except Exception as ex:
-            raise exception.VolumeBackendAPIException(ex)
-        finally:
-            self._logout(client)
-
-    def _get_volume_extra_specs(self, volume):
-        """Get extra specs from a volume."""
-        extra_specs = {}
-        type_id = volume.get('volume_type_id', None)
-        if type_id is not None:
-            ctxt = context.get_admin_context()
-            volume_type = volume_types.get_volume_type(ctxt, type_id)
-            extra_specs = volume_type.get('extra_specs')
-        return extra_specs
-
-    def _get_lh_extra_specs(self, extra_specs, valid_keys):
-        """Get LeftHand extra_specs (valid_keys only)."""
-        extra_specs_of_interest = {}
-        for key, value in extra_specs.items():
-            if key in valid_keys:
-                extra_specs_of_interest[key] = value
-        return extra_specs_of_interest
-
-    def _map_extra_specs(self, extra_specs):
-        """Map the extra spec key/values to LeftHand key/values."""
-        client_options = {}
-        for key, value in extra_specs.items():
-            # map extra spec key to lh client option key
-            client_key = extra_specs_key_map[key]
-            # map extra spect value to lh client option value
-            try:
-                value_map = extra_specs_value_map[client_key]
-                # an invalid value will throw KeyError
-                client_value = value_map[value]
-                client_options[client_key] = client_value
-            except KeyError:
-                LOG.error(_LE("'%(value)s' is an invalid value "
-                              "for extra spec '%(key)s'"),
-                          {'value': value, 'key': key})
-        return client_options
-
-    def _update_provider(self, volume_info):
-        # TODO(justinsb): Is this always 1? Does it matter?
-        cluster_interface = '1'
-        iscsi_portal = self.cluster_vip + ":3260," + cluster_interface
-
-        return {'provider_location': (
-            "%s %s %s" % (iscsi_portal, volume_info['iscsiIqn'], 0))}
-
-    def _create_server(self, connector, client):
-        server_info = None
-        chap_enabled = self.configuration.hplefthand_iscsi_chap_enabled
-        try:
-            server_info = client.getServerByName(connector['host'])
-            chap_secret = server_info['chapTargetSecret']
-            if not chap_enabled and chap_secret:
-                LOG.warning(_LW('CHAP secret exists for host %s but CHAP is '
-                                'disabled'), connector['host'])
-            if chap_enabled and chap_secret is None:
-                LOG.warning(_LW('CHAP is enabled, but server secret not '
-                                'configured on server %s'), connector['host'])
-            return server_info
-        except hpexceptions.HTTPNotFound:
-            # server does not exist, so create one
-            pass
-
-        optional = None
-        if chap_enabled:
-            chap_secret = utils.generate_password()
-            optional = {'chapName': connector['initiator'],
-                        'chapTargetSecret': chap_secret,
-                        'chapAuthenticationRequired': True
-                        }
-
-        server_info = client.createServer(connector['host'],
-                                          connector['initiator'],
-                                          optional)
-        return server_info
-
-    def create_export(self, context, volume, connector):
-        pass
-
-    def ensure_export(self, context, volume):
-        pass
-
-    def remove_export(self, context, volume):
-        pass
-
-    def retype(self, ctxt, volume, new_type, diff, host):
-        """Convert the volume to be of the new type.
-
-        Returns a boolean indicating whether the retype occurred.
-
-        :param ctxt: Context
-        :param volume: A dictionary describing the volume to retype
-        :param new_type: A dictionary describing the volume type to convert to
-        :param diff: A dictionary with the difference between the two types
-        :param host: A dictionary describing the host, where
-                     host['host'] is its name, and host['capabilities'] is a
-                     dictionary of its reported capabilities.
-        """
-        LOG.debug('enter: retype: id=%(id)s, new_type=%(new_type)s,'
-                  'diff=%(diff)s, host=%(host)s', {'id': volume['id'],
-                                                   'new_type': new_type,
-                                                   'diff': diff,
-                                                   'host': host})
-        client = self._login()
-        try:
-            volume_info = client.getVolumeByName(volume['name'])
-
-            # pick out the LH extra specs
-            new_extra_specs = dict(new_type).get('extra_specs')
-            lh_extra_specs = self._get_lh_extra_specs(
-                new_extra_specs,
-                extra_specs_key_map.keys())
-
-            LOG.debug('LH specs=%(specs)s', {'specs': lh_extra_specs})
-
-            # only set the ones that have changed
-            changed_extra_specs = {}
-            for key, value in lh_extra_specs.items():
-                (old, new) = diff['extra_specs'][key]
-                if old != new:
-                    changed_extra_specs[key] = value
-
-            # map extra specs to LeftHand options
-            options = self._map_extra_specs(changed_extra_specs)
-            if len(options) > 0:
-                client.modifyVolume(volume_info['id'], options)
-            return True
-        except hpexceptions.HTTPNotFound:
-            raise exception.VolumeNotFound(volume_id=volume['id'])
-        except Exception as ex:
-            LOG.warning(_LW("%s"), ex)
-        finally:
-            self._logout(client)
-
-        return False
-
-    def migrate_volume(self, ctxt, volume, host):
-        """Migrate the volume to the specified host.
-
-        Backend assisted volume migration will occur if and only if;
-
-        1. Same LeftHand backend
-        2. Volume cannot be attached
-        3. Volumes with snapshots cannot be migrated
-        4. Source and Destination clusters must be in the same management group
-
-        Volume re-type is not supported.
-
-        Returns a boolean indicating whether the migration occurred, as well as
-        model_update.
-
-        :param ctxt: Context
-        :param volume: A dictionary describing the volume to migrate
-        :param host: A dictionary describing the host to migrate to, where
-                     host['host'] is its name, and host['capabilities'] is a
-                     dictionary of its reported capabilities.
-        """
-        LOG.debug('enter: migrate_volume: id=%(id)s, host=%(host)s, '
-                  'cluster=%(cluster)s', {
-                      'id': volume['id'],
-                      'host': host,
-                      'cluster': self.configuration.hplefthand_clustername})
-
-        false_ret = (False, None)
-        if 'location_info' not in host['capabilities']:
-            return false_ret
-
-        host_location = host['capabilities']['location_info']
-        (driver, cluster, vip) = host_location.split(' ')
-        client = self._login()
-        try:
-            # get the cluster info, if it exists and compare
-            cluster_info = client.getClusterByName(cluster)
-            LOG.debug('Cluster info: %s', cluster_info)
-            virtual_ips = cluster_info['virtualIPAddresses']
-
-            if driver != self.__class__.__name__:
-                LOG.info(_LI("Cannot provide backend assisted migration for "
-                             "volume: %s because volume is from a different "
-                             "backend."), volume['name'])
-                return false_ret
-            if vip != virtual_ips[0]['ipV4Address']:
-                LOG.info(_LI("Cannot provide backend assisted migration for "
-                             "volume: %s because cluster exists in different "
-                             "management group."), volume['name'])
-                return false_ret
-
-        except hpexceptions.HTTPNotFound:
-            LOG.info(_LI("Cannot provide backend assisted migration for "
-                         "volume: %s because cluster exists in different "
-                         "management group."), volume['name'])
-            return false_ret
-        finally:
-            self._logout(client)
-
-        client = self._login()
-        try:
-            volume_info = client.getVolumeByName(volume['name'])
-            LOG.debug('Volume info: %s', volume_info)
-
-            # can't migrate if server is attached
-            if volume_info['iscsiSessions'] is not None:
-                LOG.info(_LI("Cannot provide backend assisted migration "
-                             "for volume: %s because the volume has been "
-                             "exported."), volume['name'])
-                return false_ret
-
-            # can't migrate if volume has snapshots
-            snap_info = client.getVolume(
-                volume_info['id'],
-                'fields=snapshots,snapshots[resource[members[name]]]')
-            LOG.debug('Snapshot info: %s', snap_info)
-            if snap_info['snapshots']['resource'] is not None:
-                LOG.info(_LI("Cannot provide backend assisted migration "
-                             "for volume: %s because the volume has "
-                             "snapshots."), volume['name'])
-                return false_ret
-
-            options = {'clusterName': cluster}
-            client.modifyVolume(volume_info['id'], options)
-        except hpexceptions.HTTPNotFound:
-            LOG.info(_LI("Cannot provide backend assisted migration for "
-                         "volume: %s because volume does not exist in this "
-                         "management group."), volume['name'])
-            return false_ret
-        except hpexceptions.HTTPServerError as ex:
-            LOG.error(_LE("Exception: %s"), ex)
-            return false_ret
-        finally:
-            self._logout(client)
-
-        return (True, None)
-
-    def update_migrated_volume(self, context, volume, new_volume,
-                               original_volume_status):
-        """Rename the new (temp) volume to it's original name.
-
-
-        This method tries to rename the new volume to it's original
-        name after the migration has completed.
-
-        """
-        LOG.debug("Update volume name for %(id)s.", {'id': new_volume['id']})
-        name_id = None
-        provider_location = None
-        if original_volume_status == 'available':
-            # volume isn't attached and can be updated
-            original_name = CONF.volume_name_template % volume['id']
-            current_name = CONF.volume_name_template % new_volume['id']
-            client = self._login()
-            try:
-                volume_info = client.getVolumeByName(current_name)
-                volumeMods = {'name': original_name}
-                client.modifyVolume(volume_info['id'], volumeMods)
-                LOG.info(_LI("Volume name changed from %(tmp)s to %(orig)s."),
-                         {'tmp': current_name, 'orig': original_name})
-            except Exception as e:
-                LOG.error(_LE("Changing the volume name from %(tmp)s to "
-                              "%(orig)s failed because %(reason)s."),
-                          {'tmp': current_name, 'orig': original_name,
-                           'reason': e})
-                name_id = new_volume['_name_id'] or new_volume['id']
-                provider_location = new_volume['provider_location']
-            finally:
-                self._logout(client)
-        else:
-            # the backend can't change the name.
-            name_id = new_volume['_name_id'] or new_volume['id']
-            provider_location = new_volume['provider_location']
-
-        return {'_name_id': name_id, 'provider_location': provider_location}
-
-    def manage_existing(self, volume, existing_ref):
-        """Manage an existing LeftHand volume.
-
-        existing_ref is a dictionary of the form:
-        {'source-name': <name of the virtual volume>}
-        """
-        # Check API Version
-        self._check_api_version()
-
-        target_vol_name = self._get_existing_volume_ref_name(existing_ref)
-
-        # Check for the existence of the virtual volume.
-        client = self._login()
-        try:
-            volume_info = client.getVolumeByName(target_vol_name)
-        except hpexceptions.HTTPNotFound:
-            err = (_("Virtual volume '%s' doesn't exist on array.") %
-                   target_vol_name)
-            LOG.error(err)
-            raise exception.InvalidInput(reason=err)
-        finally:
-            self._logout(client)
-
-        # Generate the new volume information based on the new ID.
-        new_vol_name = 'volume-' + volume['id']
-
-        volume_type = None
-        if volume['volume_type_id']:
-            try:
-                volume_type = self._get_volume_type(volume['volume_type_id'])
-            except Exception:
-                reason = (_("Volume type ID '%s' is invalid.") %
-                          volume['volume_type_id'])
-                raise exception.ManageExistingVolumeTypeMismatch(reason=reason)
-
-        new_vals = {"name": new_vol_name}
-
-        client = self._login()
-        try:
-            # Update the existing volume with the new name.
-            client.modifyVolume(volume_info['id'], new_vals)
-        finally:
-            self._logout(client)
-
-        LOG.info(_LI("Virtual volume '%(ref)s' renamed to '%(new)s'."),
-                 {'ref': existing_ref['source-name'], 'new': new_vol_name})
-
-        display_name = None
-        if volume['display_name']:
-            display_name = volume['display_name']
-
-        if volume_type:
-            LOG.info(_LI("Virtual volume %(disp)s '%(new)s' is "
-                         "being retyped."),
-                     {'disp': display_name, 'new': new_vol_name})
-
-            try:
-                self.retype(None,
-                            volume,
-                            volume_type,
-                            volume_type['extra_specs'],
-                            volume['host'])
-                LOG.info(_LI("Virtual volume %(disp)s successfully retyped to "
-                             "%(new_type)s."),
-                         {'disp': display_name,
-                          'new_type': volume_type.get('name')})
-            except Exception:
-                with excutils.save_and_reraise_exception():
-                    LOG.warning(_LW("Failed to manage virtual volume %(disp)s "
-                                    "due to error during retype."),
-                                {'disp': display_name})
-                    # Try to undo the rename and clear the new comment.
-                    client = self._login()
-                    try:
-                        client.modifyVolume(
-                            volume_info['id'],
-                            {'name': target_vol_name})
-                    finally:
-                        self._logout(client)
-
-        updates = {'display_name': display_name}
-
-        LOG.info(_LI("Virtual volume %(disp)s '%(new)s' is "
-                     "now being managed."),
-                 {'disp': display_name, 'new': new_vol_name})
-
-        # Return display name to update the name displayed in the GUI and
-        # any model updates from retype.
-        return updates
-
-    def manage_existing_get_size(self, volume, existing_ref):
-        """Return size of volume to be managed by manage_existing.
-
-        existing_ref is a dictionary of the form:
-        {'source-name': <name of the virtual volume>}
-        """
-        # Check API version.
-        self._check_api_version()
-
-        target_vol_name = self._get_existing_volume_ref_name(existing_ref)
-
-        # Make sure the reference is not in use.
-        if re.match('volume-*|snapshot-*', target_vol_name):
-            reason = _("Reference must be the volume name of an unmanaged "
-                       "virtual volume.")
-            raise exception.ManageExistingInvalidReference(
-                existing_ref=target_vol_name,
-                reason=reason)
-
-        # Check for the existence of the virtual volume.
-        client = self._login()
-        try:
-            volume_info = client.getVolumeByName(target_vol_name)
-        except hpexceptions.HTTPNotFound:
-            err = (_("Virtual volume '%s' doesn't exist on array.") %
-                   target_vol_name)
-            LOG.error(err)
-            raise exception.InvalidInput(reason=err)
-        finally:
-            self._logout(client)
-
-        return int(math.ceil(float(volume_info['size']) / units.Gi))
-
-    def unmanage(self, volume):
-        """Removes the specified volume from Cinder management."""
-        # Check API version.
-        self._check_api_version()
-
-        # Rename the volume's name to unm-* format so that it can be
-        # easily found later.
-        client = self._login()
-        try:
-            volume_info = client.getVolumeByName(volume['name'])
-            new_vol_name = 'unm-' + six.text_type(volume['id'])
-            options = {'name': new_vol_name}
-            client.modifyVolume(volume_info['id'], options)
-        finally:
-            self._logout(client)
-
-        LOG.info(_LI("Virtual volume %(disp)s '%(vol)s' is no longer managed. "
-                     "Volume renamed to '%(new)s'."),
-                 {'disp': volume['display_name'],
-                  'vol': volume['name'],
-                  'new': new_vol_name})
-
-    def _get_existing_volume_ref_name(self, existing_ref):
-        """Returns the volume name of an existing reference.
-
-        Checks if an existing volume reference has a source-name element.
-        If source-name is not present an error will be thrown.
-        """
-        if 'source-name' not in existing_ref:
-            reason = _("Reference must contain source-name.")
-            raise exception.ManageExistingInvalidReference(
-                existing_ref=existing_ref,
-                reason=reason)
-
-        return existing_ref['source-name']
-
-    def _check_api_version(self):
-        """Checks that the API version is correct."""
-        if (self.api_version < MIN_API_VERSION):
-            ex_msg = (_('Invalid HPLeftHand API version found: %(found)s. '
-                        'Version %(minimum)s or greater required for '
-                        'manage/unmanage support.')
-                      % {'found': self.api_version,
-                         'minimum': MIN_API_VERSION})
-            LOG.error(ex_msg)
-            raise exception.InvalidInput(reason=ex_msg)
-
-    def _get_volume_type(self, type_id):
-        ctxt = context.get_admin_context()
-        return volume_types.get_volume_type(ctxt, type_id)