-# (c) Copyright 2014 Hewlett-Packard Development Company, L.P.
+# (c) Copyright 2014-2015 Hewlett Packard Enterprise Development LP
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
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.tests.unit import fake_hpe_lefthand_client as hpelefthandclient
+from cinder.volume.drivers.hpe import hpe_lefthand_iscsi
from cinder.volume import volume_types
-hpexceptions = hplefthandclient.hpexceptions
+hpeexceptions = hpelefthandclient.hpeexceptions
GOODNESS_FUNCTION = \
"capabilities.capacity_utilization < 0.6? 100 : 25"
"capabilities.total_volumes < 400 && capabilities.capacity_utilization"
-class HPLeftHandBaseDriver(object):
+class HPELeftHandBaseDriver(object):
cluster_id = 1
volume_type = {'name': 'gold',
'deleted': False,
'updated_at': None,
- 'extra_specs': {'hplh:provisioning': 'thin',
- 'hplh:ao': 'true',
- 'hplh:data_pl': 'r-0'},
+ 'extra_specs': {'hpelh:provisioning': 'thin',
+ 'hpelh:ao': 'true',
+ 'hpelh:data_pl': 'r-0'},
'deleted_at': None,
'id': 'gold'}
+ old_volume_type = {'name': 'gold',
+ 'deleted': False,
+ 'updated_at': None,
+ 'extra_specs': {'hplh:provisioning': 'thin',
+ 'hplh:ao': 'true',
+ 'hplh:data_pl': 'r-0'},
+ 'deleted_at': None,
+ 'id': 'gold'}
connector = {
'ip': '10.0.0.2',
]
-class TestHPLeftHandISCSIDriver(HPLeftHandBaseDriver, test.TestCase):
+class TestHPELeftHandISCSIDriver(HPELeftHandBaseDriver, test.TestCase):
CONSIS_GROUP_ID = '3470cc4c-63b3-4c7a-8120-8a0693b45838'
CGSNAPSHOT_ID = '5351d914-6c90-43e7-9a8e-7e84610927da'
def default_mock_conf(self):
mock_conf = mock.Mock()
- mock_conf.hplefthand_api_url = 'http://fake.foo:8080/lhos'
- mock_conf.hplefthand_username = 'foo1'
- mock_conf.hplefthand_password = 'bar2'
- mock_conf.hplefthand_iscsi_chap_enabled = False
- mock_conf.hplefthand_debug = False
- mock_conf.hplefthand_clustername = "CloudCluster1"
+ mock_conf.hpelefthand_api_url = 'http://fake.foo:8080/lhos'
+ mock_conf.hpelefthand_username = 'foo1'
+ mock_conf.hpelefthand_password = 'bar2'
+ mock_conf.hpelefthand_iscsi_chap_enabled = False
+ mock_conf.hpelefthand_debug = False
+ mock_conf.hpelefthand_clustername = "CloudCluster1"
mock_conf.goodness_function = GOODNESS_FUNCTION
mock_conf.filter_function = FILTER_FUNCTION
mock_conf.reserved_percentage = 25
return mock_conf
- @mock.patch('hplefthandclient.client.HPLeftHandClient', spec=True)
+ @mock.patch('hpelefthandclient.client.HPELeftHandClient', spec=True)
def setup_driver(self, _mock_client, config=None):
if config is None:
config = self.default_mock_conf()
'spaceTotal': units.Gi * 500,
'spaceAvailable': units.Gi * 250}
db = mock.Mock()
- self.driver = hp_lefthand_iscsi.HPLeftHandISCSIDriver(
+ self.driver = hpe_lefthand_iscsi.HPELeftHandISCSIDriver(
configuration=config, db=db)
self.driver.do_setup(None)
- self.cluster_name = config.hplefthand_clustername
+ self.cluster_name = config.hpelefthand_clustername
return _mock_client.return_value
- @mock.patch('hplefthandclient.version', "1.0.0")
+ @mock.patch('hpelefthandclient.version', "1.0.0")
def test_unsupported_client_version(self):
self.assertRaises(exception.InvalidInput,
self.setup_driver)
- @mock.patch('hplefthandclient.version', "3.0.0")
+ @mock.patch('hpelefthandclient.version', "3.0.0")
def test_supported_client_version(self):
self.setup_driver()
mock_client.createVolume.return_value = {
'iscsiIqn': self.connector['initiator']}
- with mock.patch.object(hp_lefthand_iscsi.HPLeftHandISCSIDriver,
+ with mock.patch.object(hpe_lefthand_iscsi.HPELeftHandISCSIDriver,
'_create_client') as mock_do_setup:
mock_do_setup.return_value = mock_client
# mock HTTPServerError
mock_client.createVolume.side_effect =\
- hpexceptions.HTTPServerError()
+ hpeexceptions.HTTPServerError()
# ensure the raised exception is a cinder exception
self.assertRaises(exception.VolumeBackendAPIException,
self.driver.create_volume, self.volume)
@mock.patch.object(
volume_types,
'get_volume_type',
- return_value={'extra_specs': {'hplh:provisioning': 'full'}})
+ return_value={'extra_specs': {'hpelh:provisioning': 'full'}})
def test_create_volume_with_es(self, _mock_volume_type):
# setup drive with default configuration
'iscsiIqn': self.connector['initiator']}
mock_client.getVolumes.return_value = {'total': 1, 'members': []}
- with mock.patch.object(hp_lefthand_iscsi.HPLeftHandISCSIDriver,
+ with mock.patch.object(hpe_lefthand_iscsi.HPELeftHandISCSIDriver,
'_create_client') as mock_do_setup:
mock_do_setup.return_value = mock_client
mock_client.assert_has_calls(expected)
+ @mock.patch.object(
+ volume_types,
+ 'get_volume_type',
+ return_value={'extra_specs': (HPELeftHandBaseDriver.
+ old_volume_type['extra_specs'])})
+ def test_create_volume_old_volume_type(self, _mock_volume_type):
+
+ # setup drive with default configuration
+ # and return the mock HTTP LeftHand client
+ mock_client = self.setup_driver()
+
+ mock_client.getVolumes.return_value = {'total': 1, 'members': []}
+
+ # mock return value of createVolume
+ mock_client.createVolume.return_value = {
+ 'iscsiIqn': self.connector['initiator']}
+
+ with mock.patch.object(hpe_lefthand_iscsi.HPELeftHandISCSIDriver,
+ '_create_client') as mock_do_setup:
+ mock_do_setup.return_value = mock_client
+
+ # execute driver
+ volume_info = self.driver.create_volume(self.volume)
+
+ self.assertEqual('10.0.1.6:3260,1 iqn.1993-08.org.debian:01:222 0',
+ volume_info['provider_location'])
+
+ expected = self.driver_startup_call_stack + [
+ mock.call.createVolume(
+ 'fakevolume',
+ 1,
+ units.Gi,
+ {'isThinProvisioned': True,
+ 'clusterName': 'CloudCluster1'}),
+ mock.call.logout()]
+
+ mock_client.assert_has_calls(expected)
+
+ # mock HTTPServerError
+ mock_client.createVolume.side_effect =\
+ hpeexceptions.HTTPServerError()
+ # ensure the raised exception is a cinder exception
+ self.assertRaises(exception.VolumeBackendAPIException,
+ self.driver.create_volume, self.volume)
+
def test_delete_volume(self):
# setup drive with default configuration
mock_client.getVolumeByName.return_value = {'id': self.volume_id}
mock_client.getVolumes.return_value = {'total': 1, 'members': []}
- with mock.patch.object(hp_lefthand_iscsi.HPLeftHandISCSIDriver,
+ with mock.patch.object(hpe_lefthand_iscsi.HPELeftHandISCSIDriver,
'_create_client') as mock_do_setup:
mock_do_setup.return_value = mock_client
# mock HTTPNotFound (volume not found)
mock_client.getVolumeByName.side_effect =\
- hpexceptions.HTTPNotFound()
+ hpeexceptions.HTTPNotFound()
# no exception should escape method
self.driver.delete_volume(self.volume)
# mock HTTPConflict
- mock_client.deleteVolume.side_effect = hpexceptions.HTTPConflict()
+ mock_client.deleteVolume.side_effect = hpeexceptions.HTTPConflict()
# ensure the raised exception is a cinder exception
self.assertRaises(exception.VolumeBackendAPIException,
self.driver.delete_volume, self.volume_id)
mock_client.getVolumeByName.return_value = {'id': self.volume_id}
mock_client.getVolumes.return_value = {'total': 1, 'members': []}
- with mock.patch.object(hp_lefthand_iscsi.HPLeftHandISCSIDriver,
+ with mock.patch.object(hpe_lefthand_iscsi.HPELeftHandISCSIDriver,
'_create_client') as mock_do_setup:
mock_do_setup.return_value = mock_client
# mock HTTPServerError (array failure)
mock_client.modifyVolume.side_effect =\
- hpexceptions.HTTPServerError()
+ hpeexceptions.HTTPServerError()
# ensure the raised exception is a cinder exception
self.assertRaises(exception.VolumeBackendAPIException,
self.driver.extend_volume, self.volume, 2)
mock_client = self.setup_driver()
# mock return value of getVolumeByName
- mock_client.getServerByName.side_effect = hpexceptions.HTTPNotFound()
+ mock_client.getServerByName.side_effect = hpeexceptions.HTTPNotFound()
mock_client.createServer.return_value = {'id': self.server_id}
mock_client.getVolumeByName.return_value = {
'id': self.volume_id,
}
mock_client.getVolumes.return_value = {'total': 1, 'members': []}
- with mock.patch.object(hp_lefthand_iscsi.HPLeftHandISCSIDriver,
+ with mock.patch.object(hpe_lefthand_iscsi.HPELeftHandISCSIDriver,
'_create_client') as mock_do_setup:
mock_do_setup.return_value = mock_client
# mock HTTPServerError (array failure)
mock_client.createServer.side_effect =\
- hpexceptions.HTTPServerError()
+ hpeexceptions.HTTPServerError()
# ensure the raised exception is a cinder exception
self.assertRaises(
exception.VolumeBackendAPIException,
mock_client = self.setup_driver()
# mock return value of getVolumeByName
- mock_client.getServerByName.side_effect = hpexceptions.HTTPNotFound()
+ mock_client.getServerByName.side_effect = hpeexceptions.HTTPNotFound()
mock_client.createServer.return_value = {'id': self.server_id}
mock_client.getVolumeByName.return_value = {
'id': self.volume_id,
}
mock_client.getVolumes.return_value = {'total': 1, 'members': []}
- with mock.patch.object(hp_lefthand_iscsi.HPLeftHandISCSIDriver,
+ with mock.patch.object(hpe_lefthand_iscsi.HPELeftHandISCSIDriver,
'_create_client') as mock_do_setup:
mock_do_setup.return_value = mock_client
mock_client = self.setup_driver()
# mock return value of getVolumeByName
- mock_client.getServerByName.side_effect = hpexceptions.HTTPNotFound()
+ mock_client.getServerByName.side_effect = hpeexceptions.HTTPNotFound()
mock_client.createServer.return_value = {
'id': self.server_id,
'chapAuthenticationRequired': True,
}
mock_client.getVolumes.return_value = {'total': 1, 'members': []}
- with mock.patch.object(hp_lefthand_iscsi.HPLeftHandISCSIDriver,
+ with mock.patch.object(hpe_lefthand_iscsi.HPELeftHandISCSIDriver,
'_create_client') as mock_do_setup:
mock_do_setup.return_value = mock_client
mock_client.findServerVolumes.return_value = [{'id': self.volume_id}]
mock_client.getVolumes.return_value = {'total': 1, 'members': []}
- with mock.patch.object(hp_lefthand_iscsi.HPLeftHandISCSIDriver,
+ with mock.patch.object(hpe_lefthand_iscsi.HPELeftHandISCSIDriver,
'_create_client') as mock_do_setup:
mock_do_setup.return_value = mock_client
mock_client.assert_has_calls(expected)
mock_client.getVolumeByName.side_effect = (
- hpexceptions.HTTPNotFound())
+ hpeexceptions.HTTPNotFound())
# ensure the raised exception is a cinder exception
self.assertRaises(
exception.VolumeBackendAPIException,
{'id': 99999}]
mock_client.getVolumes.return_value = {'total': 1, 'members': []}
- with mock.patch.object(hp_lefthand_iscsi.HPLeftHandISCSIDriver,
+ with mock.patch.object(hpe_lefthand_iscsi.HPELeftHandISCSIDriver,
'_create_client') as mock_do_setup:
mock_do_setup.return_value = mock_client
self.assertFalse(mock_client.deleteServer.called)
mock_client.getVolumeByName.side_effect = (
- hpexceptions.HTTPNotFound())
+ hpeexceptions.HTTPNotFound())
# ensure the raised exception is a cinder exception
self.assertRaises(
exception.VolumeBackendAPIException,
mock_client.getVolumeByName.return_value = {'id': self.volume_id}
mock_client.getVolumes.return_value = {'total': 1, 'members': []}
- with mock.patch.object(hp_lefthand_iscsi.HPLeftHandISCSIDriver,
+ with mock.patch.object(hpe_lefthand_iscsi.HPELeftHandISCSIDriver,
'_create_client') as mock_do_setup:
mock_do_setup.return_value = mock_client
# mock HTTPServerError (array failure)
mock_client.getVolumeByName.side_effect =\
- hpexceptions.HTTPNotFound()
+ hpeexceptions.HTTPNotFound()
# ensure the raised exception is a cinder exception
self.assertRaises(
exception.VolumeBackendAPIException,
mock_client.getSnapshotByName.return_value = {'id': self.snapshot_id}
mock_client.getVolumes.return_value = {'total': 1, 'members': []}
- with mock.patch.object(hp_lefthand_iscsi.HPLeftHandISCSIDriver,
+ with mock.patch.object(hpe_lefthand_iscsi.HPELeftHandISCSIDriver,
'_create_client') as mock_do_setup:
mock_do_setup.return_value = mock_client
mock_client.assert_has_calls(expected)
mock_client.getSnapshotByName.side_effect =\
- hpexceptions.HTTPNotFound()
+ hpeexceptions.HTTPNotFound()
# no exception is thrown, just error msg is logged
self.driver.delete_snapshot(self.snapshot)
# mock HTTPServerError (array failure)
- ex = hpexceptions.HTTPServerError({'message': 'Some message.'})
+ ex = hpeexceptions.HTTPServerError({'message': 'Some message.'})
mock_client.getSnapshotByName.side_effect = ex
# ensure the raised exception is a cinder exception
self.assertRaises(
self.snapshot)
# mock HTTPServerError because the snap is in use
- ex = hpexceptions.HTTPServerError({
+ ex = hpeexceptions.HTTPServerError({
'message':
'Hey, dude cannot be deleted because it is a clone point'
' duh.'})
'iscsiIqn': self.connector['initiator']}
mock_client.getVolumes.return_value = {'total': 1, 'members': []}
- with mock.patch.object(hp_lefthand_iscsi.HPLeftHandISCSIDriver,
+ with mock.patch.object(hpe_lefthand_iscsi.HPELeftHandISCSIDriver,
'_create_client') as mock_do_setup:
mock_do_setup.return_value = mock_client
'iscsiIqn': self.connector['initiator']}
mock_client.getVolumes.return_value = {'total': 1, 'members': []}
- with mock.patch.object(hp_lefthand_iscsi.HPLeftHandISCSIDriver,
+ with mock.patch.object(hpe_lefthand_iscsi.HPELeftHandISCSIDriver,
'_create_client') as mock_do_setup:
mock_do_setup.return_value = mock_client
'extra_specs': {
'foo:bar': 'fake',
'bar:foo': 1234,
- 'hplh:provisioning': 'full'}}
+ 'hpelh:provisioning': 'full'}}
volume_with_vt = self.volume
volume_with_vt['volume_type_id'] = self.volume_type_id
volume_with_vt)
extra_specs = self.driver._get_lh_extra_specs(
volume_extra_specs,
- hp_lefthand_iscsi.extra_specs_key_map.keys())
+ hpe_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
_mock_get_volume_type.return_value = {
'extra_specs': {
- # r-07 is an invalid value for hplh:ao
- 'hplh:data_pl': 'r-07',
- 'hplh:ao': 'true'}}
+ # r-07 is an invalid value for hpelh:ao
+ 'hpelh:data_pl': 'r-07',
+ 'hpelh:ao': 'true'}}
# get the extra specs of interest from this volume's volume type
volume_extra_specs = self.driver._get_volume_extra_specs(
volume_with_vt)
extra_specs = self.driver._get_lh_extra_specs(
volume_extra_specs,
- hp_lefthand_iscsi.extra_specs_key_map.keys())
+ hpe_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._map_extra_specs(extra_specs)
- # {'hplh:ao': 'true'} should map to
+ # {'hpelh:ao': 'true'} should map to
# {'isAdaptiveOptimizationEnabled': True}
- # without hplh:data_pl since r-07 is an invalid value
+ # without hpelh:data_pl since r-07 is an invalid value
self.assertDictMatch({'isAdaptiveOptimizationEnabled': True}, optional)
def test_retype_with_no_LH_extra_specs(self):
volume['host'] = host
new_type = volume_types.get_volume_type(ctxt, new_type_ref['id'])
- with mock.patch.object(hp_lefthand_iscsi.HPLeftHandISCSIDriver,
+ with mock.patch.object(hpe_lefthand_iscsi.HPELeftHandISCSIDriver,
'_create_client') as mock_do_setup:
mock_do_setup.return_value = mock_client
ctxt = context.get_admin_context()
host = {'host': self.serverName}
- key_specs_old = {'hplh:provisioning': 'thin'}
- key_specs_new = {'hplh:provisioning': 'full', 'hplh:ao': 'true'}
+ key_specs_old = {'hpelh:provisioning': 'thin'}
+ key_specs_new = {'hpelh:provisioning': 'full', 'hpelh:ao': 'true'}
old_type_ref = volume_types.create(ctxt, 'old', key_specs_old)
new_type_ref = volume_types.create(ctxt, 'new', key_specs_new)
volume['host'] = host
new_type = volume_types.get_volume_type(ctxt, new_type_ref['id'])
- with mock.patch.object(hp_lefthand_iscsi.HPLeftHandISCSIDriver,
+ with mock.patch.object(hpe_lefthand_iscsi.HPELeftHandISCSIDriver,
'_create_client') as mock_do_setup:
mock_do_setup.return_value = mock_client
ctxt = context.get_admin_context()
host = {'host': self.serverName}
- key_specs_old = {'hplh:provisioning': 'full', 'foo': 'bar'}
- key_specs_new = {'hplh:provisioning': 'thin', 'foo': 'foobar'}
+ key_specs_old = {'hpelh:provisioning': 'full', 'foo': 'bar'}
+ key_specs_new = {'hpelh:provisioning': 'thin', 'foo': 'foobar'}
old_type_ref = volume_types.create(ctxt, 'old', key_specs_old)
new_type_ref = volume_types.create(ctxt, 'new', key_specs_new)
volume['host'] = host
new_type = volume_types.get_volume_type(ctxt, new_type_ref['id'])
- with mock.patch.object(hp_lefthand_iscsi.HPLeftHandISCSIDriver,
+ with mock.patch.object(hpe_lefthand_iscsi.HPELeftHandISCSIDriver,
'_create_client') as mock_do_setup:
mock_do_setup.return_value = mock_client
ctxt = context.get_admin_context()
host = {'host': self.serverName}
- key_specs_old = {'hplh:provisioning': 'full', 'hplh:ao': 'true'}
- key_specs_new = {'hplh:provisioning': 'full', 'hplh:ao': 'false'}
+ key_specs_old = {'hpelh:provisioning': 'full', 'hpelh:ao': 'true'}
+ key_specs_new = {'hpelh:provisioning': 'full', 'hpelh:ao': 'false'}
old_type_ref = volume_types.create(ctxt, 'old', key_specs_old)
new_type_ref = volume_types.create(ctxt, 'new', key_specs_new)
volume['host'] = host
new_type = volume_types.get_volume_type(ctxt, new_type_ref['id'])
- with mock.patch.object(hp_lefthand_iscsi.HPLeftHandISCSIDriver,
+ with mock.patch.object(hpe_lefthand_iscsi.HPELeftHandISCSIDriver,
'_create_client') as mock_do_setup:
mock_do_setup.return_value = mock_client
host = {'host': self.serverName, 'capabilities': {}}
- with mock.patch.object(hp_lefthand_iscsi.HPLeftHandISCSIDriver,
+ with mock.patch.object(hpe_lefthand_iscsi.HPELeftHandISCSIDriver,
'_create_client') as mock_do_setup:
mock_do_setup.return_value = mock_client
'host': self.serverName,
'capabilities': {'location_info': location}}
- with mock.patch.object(hp_lefthand_iscsi.HPLeftHandISCSIDriver,
+ with mock.patch.object(hpe_lefthand_iscsi.HPELeftHandISCSIDriver,
'_create_client') as mock_do_setup:
mock_do_setup.return_value = mock_client
'host': self.serverName,
'capabilities': {'location_info': location}}
- with mock.patch.object(hp_lefthand_iscsi.HPLeftHandISCSIDriver,
+ with mock.patch.object(hpe_lefthand_iscsi.HPELeftHandISCSIDriver,
'_create_client') as mock_do_setup:
mock_do_setup.return_value = mock_client
'host': self.serverName,
'capabilities': {'location_info': location}}
- with mock.patch.object(hp_lefthand_iscsi.HPLeftHandISCSIDriver,
+ with mock.patch.object(hpe_lefthand_iscsi.HPELeftHandISCSIDriver,
'_create_client') as mock_do_setup:
mock_do_setup.return_value = mock_client
'_name_id': clone_id,
'provider_location': provider_location}
original_volume_status = 'available'
- with mock.patch.object(hp_lefthand_iscsi.HPLeftHandISCSIDriver,
+ with mock.patch.object(hpe_lefthand_iscsi.HPELeftHandISCSIDriver,
'_create_client') as mock_do_setup:
mock_do_setup.return_value = mock_client
actual_update = self.driver.update_migrated_volume(
'provider_location': provider_location}
original_volume_status = 'in-use'
- with mock.patch.object(hp_lefthand_iscsi.HPLeftHandISCSIDriver,
+ with mock.patch.object(hpe_lefthand_iscsi.HPELeftHandISCSIDriver,
'_create_client') as mock_do_setup:
mock_do_setup.return_value = mock_client
actual_update = self.driver.update_migrated_volume(
self.assertEqual(expected_update, actual_update)
@mock.patch.object(volume_types, 'get_volume_type',
- return_value={'extra_specs': {'hplh:ao': 'true'}})
+ return_value={'extra_specs': {'hpelh:ao': 'true'}})
def test_create_volume_with_ao_true(self, _mock_volume_type):
# setup drive with default configuration
'iscsiIqn': self.connector['initiator']}
mock_client.getVolumes.return_value = {'total': 1, 'members': []}
- with mock.patch.object(hp_lefthand_iscsi.HPLeftHandISCSIDriver,
+ with mock.patch.object(hpe_lefthand_iscsi.HPELeftHandISCSIDriver,
'_create_client') as mock_do_setup:
mock_do_setup.return_value = mock_client
mock_client.assert_has_calls(expected)
@mock.patch.object(volume_types, 'get_volume_type',
- return_value={'extra_specs': {'hplh:ao': 'false'}})
+ return_value={'extra_specs': {'hpelh:ao': 'false'}})
def test_create_volume_with_ao_false(self, _mock_volume_type):
# setup drive with default configuration
'iscsiIqn': self.connector['initiator']}
mock_client.getVolumes.return_value = {'total': 1, 'members': []}
- with mock.patch.object(hp_lefthand_iscsi.HPLeftHandISCSIDriver,
+ with mock.patch.object(hpe_lefthand_iscsi.HPELeftHandISCSIDriver,
'_create_client') as mock_do_setup:
mock_do_setup.return_value = mock_client
'volume_type_id': None,
'id': '12345'}
- with mock.patch.object(hp_lefthand_iscsi.HPLeftHandISCSIDriver,
+ with mock.patch.object(hpe_lefthand_iscsi.HPELeftHandISCSIDriver,
'_create_client') as mock_do_setup:
mock_do_setup.return_value = mock_client
mock_client.getVolumeByName.return_value = {'id': self.volume_id}
'name': 'gold',
'id': 'gold-id',
'extra_specs': {
- 'hplh:provisioning': 'thin',
- 'hplh:ao': 'true',
- 'hplh:data_pl': 'r-0',
+ 'hpelh:provisioning': 'thin',
+ 'hpelh:ao': 'true',
+ 'hpelh:data_pl': 'r-0',
'volume_type': self.volume_type}}
self.driver.api_version = "1.1"
'volume_type_id': 'bcfa9fa4-54a0-4340-a3d8-bfcf19aea65e',
'id': '12345'}
- with mock.patch.object(hp_lefthand_iscsi.HPLeftHandISCSIDriver,
+ with mock.patch.object(hpe_lefthand_iscsi.HPELeftHandISCSIDriver,
'_create_client') as mock_do_setup:
mock_do_setup.return_value = mock_client
mock_client.getVolumeByName.return_value = {'id': self.volume_id}
'name': 'gold',
'id': 'gold-id',
'extra_specs': {
- 'hplh:provisioning': 'thin',
- 'hplh:ao': 'true',
- 'hplh:data_pl': 'r-0',
+ 'hpelh:provisioning': 'thin',
+ 'hpelh:ao': 'true',
+ 'hpelh:data_pl': 'r-0',
'volume_type': self.volume_type}}
self.driver.retype = mock.Mock(
'volume_type_id': 'bcfa9fa4-54a0-4340-a3d8-bfcf19aea65e',
'id': '12345'}
- with mock.patch.object(hp_lefthand_iscsi.HPLeftHandISCSIDriver,
+ with mock.patch.object(hpe_lefthand_iscsi.HPELeftHandISCSIDriver,
'_create_client') as mock_do_setup:
mock_do_setup.return_value = mock_client
mock_client.getVolumeByName.return_value = {'id': self.volume_id}
'volume_type_id': 'bcfa9fa4-54a0-4340-a3d8-bfcf19aea65e',
'id': '12345'}
- with mock.patch.object(hp_lefthand_iscsi.HPLeftHandISCSIDriver,
+ with mock.patch.object(hpe_lefthand_iscsi.HPELeftHandISCSIDriver,
'_create_client') as mock_do_setup:
mock_do_setup.return_value = mock_client
mock_client.getVolumeByName.return_value = {'id': self.volume_id}
self.driver.api_version = "1.1"
- with mock.patch.object(hp_lefthand_iscsi.HPLeftHandISCSIDriver,
+ with mock.patch.object(hpe_lefthand_iscsi.HPELeftHandISCSIDriver,
'_create_client') as mock_do_setup:
mock_do_setup.return_value = mock_client
mock_client.getVolumes.return_value = {
self.driver.api_version = "1.1"
- with mock.patch.object(hp_lefthand_iscsi.HPLeftHandISCSIDriver,
+ with mock.patch.object(hpe_lefthand_iscsi.HPELeftHandISCSIDriver,
'_create_client') as mock_do_setup:
mock_do_setup.return_value = mock_client
def test_manage_existing_get_size_invalid_input(self):
mock_client = self.setup_driver()
mock_client.getVolumeByName.side_effect = (
- hpexceptions.HTTPNotFound('fake'))
+ hpeexceptions.HTTPNotFound('fake'))
self.driver.api_version = "1.1"
- with mock.patch.object(hp_lefthand_iscsi.HPLeftHandISCSIDriver,
+ with mock.patch.object(hpe_lefthand_iscsi.HPELeftHandISCSIDriver,
'_create_client') as mock_do_setup:
mock_do_setup.return_value = mock_client
mock_client.getVolumes.return_value = {
self.driver.api_version = "1.1"
- with mock.patch.object(hp_lefthand_iscsi.HPLeftHandISCSIDriver,
+ with mock.patch.object(hpe_lefthand_iscsi.HPELeftHandISCSIDriver,
'_create_client') as mock_do_setup:
mock_do_setup.return_value = mock_client
self.driver.unmanage(self.volume)
}]
}
- with mock.patch.object(hp_lefthand_iscsi.HPLeftHandISCSIDriver,
+ with mock.patch.object(hpe_lefthand_iscsi.HPELeftHandISCSIDriver,
'_create_client') as mock_do_setup:
mock_do_setup.return_value = mock_client
mock_client.assert_has_calls(expected)
- @mock.patch('hplefthandclient.version', "1.0.6")
def test_create_consistencygroup(self):
class fake_consitencygroup_object(object):
volume_type_id = '371c64d5-b92a-488c-bc14-1e63cef40e08'
# set up driver with default config
mock_client = self.setup_driver()
- with mock.patch.object(hp_lefthand_iscsi.HPLeftHandISCSIDriver,
+ with mock.patch.object(hpe_lefthand_iscsi.HPELeftHandISCSIDriver,
'_create_client') as mock_do_setup:
mock_do_setup.return_value = mock_client
self.assertEqual('available', cg['status'])
- @mock.patch('hplefthandclient.version', "1.0.6")
def test_delete_consistencygroup(self):
class fake_consitencygroup_object(object):
volume_type_id = '371c64d5-b92a-488c-bc14-1e63cef40e08'
expected_volumes = [mock_volume]
self.driver.db.volume_get_all_by_group.return_value = expected_volumes
- with mock.patch.object(hp_lefthand_iscsi.HPLeftHandISCSIDriver,
+ with mock.patch.object(hpe_lefthand_iscsi.HPELeftHandISCSIDriver,
'_create_client') as mock_do_setup:
mock_do_setup.return_value = mock_client
cg, vols = self.driver.delete_consistencygroup(ctxt, group, [])
self.assertEqual('deleting', cg['status'])
- @mock.patch('hplefthandclient.version', "1.0.6")
def test_update_consistencygroup_add_vol_delete_cg(self):
class fake_consitencygroup_object(object):
volume_type_id = '371c64d5-b92a-488c-bc14-1e63cef40e08'
mock_client.createVolume.return_value = {
'iscsiIqn': self.connector['initiator']}
- with mock.patch.object(hp_lefthand_iscsi.HPLeftHandISCSIDriver,
+ with mock.patch.object(hpe_lefthand_iscsi.HPELeftHandISCSIDriver,
'_create_client') as mock_do_setup:
mock_do_setup.return_value = mock_client
cg, vols = self.driver.delete_consistencygroup(ctxt, group, [])
self.assertEqual('deleting', cg['status'])
- @mock.patch('hplefthandclient.version', "1.0.6")
def test_update_consistencygroup_remove_vol_delete_cg(self):
class fake_consitencygroup_object(object):
volume_type_id = '371c64d5-b92a-488c-bc14-1e63cef40e08'
mock_client.createVolume.return_value = {
'iscsiIqn': self.connector['initiator']}
- with mock.patch.object(hp_lefthand_iscsi.HPLeftHandISCSIDriver,
+ with mock.patch.object(hpe_lefthand_iscsi.HPELeftHandISCSIDriver,
'_create_client') as mock_do_setup:
mock_do_setup.return_value = mock_client
self.assertEqual('deleting', cg['status'])
@mock.patch('cinder.objects.snapshot.SnapshotList.get_all_for_cgsnapshot')
- @mock.patch('hplefthandclient.version', "1.0.6")
def test_create_cgsnapshot(self, mock_snap_list):
class fake_consitencygroup_object(object):
volume_type_id = '371c64d5-b92a-488c-bc14-1e63cef40e08'
expected_snaps = [mock_snap]
mock_snap_list.return_value = expected_snaps
- with mock.patch.object(hp_lefthand_iscsi.HPLeftHandISCSIDriver,
+ with mock.patch.object(hpe_lefthand_iscsi.HPELeftHandISCSIDriver,
'_create_client') as mock_do_setup:
mock_do_setup.return_value = mock_client
self.assertEqual('available', cgsnap['status'])
@mock.patch('cinder.objects.snapshot.SnapshotList.get_all_for_cgsnapshot')
- @mock.patch('hplefthandclient.version', "1.0.6")
def test_delete_cgsnapshot(self, mock_snap_list):
class fake_consitencygroup_object(object):
volume_type_id = '371c64d5-b92a-488c-bc14-1e63cef40e08'
expected_snaps = [mock_snap]
mock_snap_list.return_value = expected_snaps
- with mock.patch.object(hp_lefthand_iscsi.HPLeftHandISCSIDriver,
+ with mock.patch.object(hpe_lefthand_iscsi.HPELeftHandISCSIDriver,
'_create_client') as mock_do_setup:
mock_do_setup.return_value = mock_client
-# (c) Copyright 2014-2015 Hewlett-Packard Development Company, L.P.
+# (c) Copyright 2014-2015 Hewlett Packard Enterprise Development LP
# 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.
#
-"""HP LeftHand SAN ISCSI REST Proxy."""
+"""HPE LeftHand SAN ISCSI REST Proxy.
+
+Volume driver for HPE LeftHand Storage array.
+This driver requires 11.5 or greater firmware on the LeftHand array, using
+the 2.0 or greater version of the hpelefthandclient.
+
+You will need to install the python hpelefthandclient module.
+sudo pip install python-lefthandclient
+
+Set the following in the cinder.conf file to enable the
+LeftHand iSCSI REST Driver along with the required flags:
+
+volume_driver=cinder.volume.drivers.hpe.hpe_lefthand_iscsi.
+ HPELeftHandISCSIDriver
+
+It also requires the setting of hpelefthand_api_url, hpelefthand_username,
+hpelefthand_password for credentials to talk to the REST service on the
+LeftHand array.
+
+"""
from oslo_config import cfg
from oslo_log import log as logging
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',
+hpelefthandclient = importutils.try_import("hpelefthandclient")
+if hpelefthandclient:
+ from hpelefthandclient import client as hpe_lh_client
+ from hpelefthandclient import exceptions as hpeexceptions
+
+hpelefthand_opts = [
+ cfg.StrOpt('hpelefthand_api_url',
+ default=None,
+ help="HPE LeftHand WSAPI Server Url like "
+ "https://<LeftHand ip>:8081/lhos",
+ deprecated_name='hplefthand_api_url'),
+ cfg.StrOpt('hpelefthand_username',
+ default=None,
+ help="HPE LeftHand Super user username",
+ deprecated_name='hplefthand_username'),
+ cfg.StrOpt('hpelefthand_password',
+ default=None,
+ help="HPE LeftHand Super user password",
+ secret=True,
+ deprecated_name='hplefthand_password'),
+ cfg.StrOpt('hpelefthand_clustername',
+ default=None,
+ help="HPE LeftHand cluster name",
+ deprecated_name='hplefthand_clustername'),
+ cfg.BoolOpt('hpelefthand_iscsi_chap_enabled',
default=False,
help='Configure CHAP authentication for iSCSI connections '
- '(Default: Disabled)'),
- cfg.BoolOpt('hplefthand_debug',
+ '(Default: Disabled)',
+ deprecated_name='hplefthand_iscsi_chap_enabled'),
+ cfg.BoolOpt('hpelefthand_debug',
default=False,
- help="Enable HTTP debugging to LeftHand"),
+ help="Enable HTTP debugging to LeftHand",
+ deprecated_name='hplefthand_debug'),
]
CONF = cfg.CONF
-CONF.register_opts(hplefthand_opts)
+CONF.register_opts(hpelefthand_opts)
MIN_API_VERSION = "1.1"
-MIN_CLIENT_VERSION = '1.0.4'
-MIN_CG_CLIENT_VERSION = "1.0.6"
+MIN_CLIENT_VERSION = '2.0.0'
# map the extra spec key to the REST client option key
extra_specs_key_map = {
+ 'hpelh:provisioning': 'isThinProvisioned',
+ 'hpelh:ao': 'isAdaptiveOptimizationEnabled',
+ 'hpelh:data_pl': 'dataProtectionLevel',
'hplh:provisioning': 'isThinProvisioned',
'hplh:ao': 'isAdaptiveOptimizationEnabled',
'hplh:data_pl': 'dataProtectionLevel',
}
-class HPLeftHandISCSIDriver(driver.ISCSIDriver):
- """Executes REST commands relating to HP/LeftHand SAN ISCSI volumes.
+class HPELeftHandISCSIDriver(driver.ISCSIDriver):
+ """Executes REST commands relating to HPE/LeftHand SAN ISCSI volumes.
Version history:
1.0.0 - Initial REST iSCSI proxy
1.0.12 - Adds consistency group support
1.0.13 - Added update_migrated_volume #1493546
1.0.14 - Removed the old CLIQ based driver
+ 2.0.0 - Rebranded HP to HPE
"""
- VERSION = "1.0.14"
+ VERSION = "2.0.0"
device_stats = {}
def __init__(self, *args, **kwargs):
- super(HPLeftHandISCSIDriver, 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"))
+ super(HPELeftHandISCSIDriver, self).__init__(*args, **kwargs)
+ self.configuration.append_config_values(hpelefthand_opts)
+ if not self.configuration.hpelefthand_api_url:
+ raise exception.NotFound(_("HPELeftHand url not found"))
# blank is the only invalid character for cluster names
# so we need to use it as a separator
def _login(self):
client = self._create_client()
try:
- if self.configuration.hplefthand_debug:
+ if self.configuration.hpelefthand_debug:
client.debug_rest(True)
client.login(
- self.configuration.hplefthand_username,
- self.configuration.hplefthand_password)
+ self.configuration.hpelefthand_username,
+ self.configuration.hpelefthand_password)
cluster_info = client.getClusterByName(
- self.configuration.hplefthand_clustername)
+ self.configuration.hpelefthand_clustername)
self.cluster_id = cluster_info['id']
virtual_ips = cluster_info['virtualIPAddresses']
self.cluster_vip = virtual_ips[0]['ipV4Address']
return client
- except hpexceptions.HTTPNotFound:
+ except hpeexceptions.HTTPNotFound:
raise exception.DriverNotInitialized(
_('LeftHand cluster not found'))
except Exception as ex:
client.logout()
def _create_client(self):
- return hp_lh_client.HPLeftHandClient(
- self.configuration.hplefthand_api_url)
+ return hpe_lh_client.HPELeftHandClient(
+ self.configuration.hpelefthand_api_url)
def do_setup(self, context):
"""Set up LeftHand client."""
- if hplefthandclient.version < MIN_CLIENT_VERSION:
- ex_msg = (_("Invalid hplefthandclient version found ("
+ if hpelefthandclient.version < MIN_CLIENT_VERSION:
+ ex_msg = (_("Invalid hpelefthandclient version found ("
"%(found)s). Version %(minimum)s or greater "
- "required.")
- % {'found': hplefthandclient.version,
+ "required. Run 'pip install --upgrade "
+ "python-lefthandclient' to upgrade the "
+ "hpelefthandclient.")
+ % {'found': hpelefthandclient.version,
'minimum': MIN_CLIENT_VERSION})
LOG.error(ex_msg)
raise exception.InvalidInput(reason=ex_msg)
try:
self.api_version = client.getApiVersion()
- LOG.info(_LI("HPLeftHand API version %s"), self.api_version)
+ LOG.info(_LI("HPELeftHand API version %s"), self.api_version)
if self.api_version < MIN_API_VERSION:
- LOG.warning(_LW("HPLeftHand API is version %(current)s. "
+ LOG.warning(_LW("HPELeftHand API is version %(current)s. "
"A minimum version of %(min)s is needed for "
"manage/unmanage support."),
{'current': self.api_version,
self._logout(client)
def get_version_string(self):
- return (_('REST %(proxy_ver)s hplefthandclient %(rest_ver)s') % {
+ return (_('REST %(proxy_ver)s hpelefthandclient %(rest_ver)s') % {
'proxy_ver': self.VERSION,
- 'rest_ver': hplefthandclient.get_version_string()})
+ 'rest_ver': hpelefthandclient.get_version_string()})
def create_volume(self, volume):
"""Creates a volume."""
if optional.get('isAdaptiveOptimizationEnabled'):
del optional['isAdaptiveOptimizationEnabled']
- clusterName = self.configuration.hplefthand_clustername
+ clusterName = self.configuration.hpelefthand_clustername
optional['clusterName'] = clusterName
volume_info = client.createVolume(
try:
volume_info = client.getVolumeByName(volume['name'])
client.deleteVolume(volume_info['id'])
- except hpexceptions.HTTPNotFound:
+ except hpeexceptions.HTTPNotFound:
LOG.error(_LE("Volume did not exist. It will not be deleted"))
except Exception as ex:
raise exception.VolumeBackendAPIException(ex)
snap_name = snap_name_base + "-" + six.text_type(i)
snap_info = client.getSnapshotByName(snap_name)
client.deleteSnapshot(snap_info['id'])
- except hpexceptions.HTTPNotFound:
+ except hpeexceptions.HTTPNotFound:
LOG.error(_LE("Snapshot did not exist. It will not be "
"deleted."))
- except hpexceptions.HTTPServerError as ex:
+ except hpeexceptions.HTTPServerError as ex:
in_use_msg = ('cannot be deleted because it is a clone '
'point')
if in_use_msg in ex.get_description():
try:
snap_info = client.getSnapshotByName(snapshot['name'])
client.deleteSnapshot(snap_info['id'])
- except hpexceptions.HTTPNotFound:
+ except hpeexceptions.HTTPNotFound:
LOG.error(_LE("Snapshot did not exist. It will not be deleted"))
- except hpexceptions.HTTPServerError as ex:
+ except hpeexceptions.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'])
data['reserved_percentage'] = (
self.configuration.safe_get('reserved_percentage'))
data['storage_protocol'] = 'iSCSI'
- data['vendor_name'] = 'Hewlett-Packard'
+ data['vendor_name'] = 'Hewlett Packard Enterprise'
data['location_info'] = (self.DRIVER_LOCATION % {
- 'cluster': self.configuration.hplefthand_clustername,
+ 'cluster': self.configuration.hpelefthand_clustername,
'vip': self.cluster_vip})
data['thin_provisioning_support'] = True
data['thick_provisioning_support'] = True
total_volumes = 0
provisioned_size = 0
volumes = client.getVolumes(
- cluster=self.configuration.hplefthand_clustername,
+ cluster=self.configuration.hpelefthand_clustername,
fields=['members[id]', 'members[clusterName]', 'members[size]'])
if volumes:
total_volumes = volumes['total']
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
+ data['consistencygroup_support'] = True
self.device_stats = data
"""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
+ used from that host. HPE VSA requires a volume to be assigned
to a server.
"""
client = self._login()
extra_specs_of_interest = {}
for key, value in extra_specs.items():
if key in valid_keys:
+ prefix = key.split(":")
+ if prefix[0] == "hplh":
+ LOG.warning(_LW("The 'hplh' prefix is deprecated. Use "
+ "'hpelh' instead."))
extra_specs_of_interest[key] = value
return extra_specs_of_interest
def _create_server(self, connector, client):
server_info = None
- chap_enabled = self.configuration.hplefthand_iscsi_chap_enabled
+ chap_enabled = self.configuration.hpelefthand_iscsi_chap_enabled
try:
server_info = client.getServerByName(connector['host'])
chap_secret = server_info['chapTargetSecret']
LOG.warning(_LW('CHAP is enabled, but server secret not '
'configured on server %s'), connector['host'])
return server_info
- except hpexceptions.HTTPNotFound:
+ except hpeexceptions.HTTPNotFound:
# server does not exist, so create one
pass
if len(options) > 0:
client.modifyVolume(volume_info['id'], options)
return True
- except hpexceptions.HTTPNotFound:
+ except hpeexceptions.HTTPNotFound:
raise exception.VolumeNotFound(volume_id=volume['id'])
except Exception as ex:
LOG.warning(_LW("%s"), ex)
'cluster=%(cluster)s', {
'id': volume['id'],
'host': host,
- 'cluster': self.configuration.hplefthand_clustername})
+ 'cluster': self.configuration.hpelefthand_clustername})
false_ret = (False, None)
if 'location_info' not in host['capabilities']:
"management group."), volume['name'])
return false_ret
- except hpexceptions.HTTPNotFound:
+ except hpeexceptions.HTTPNotFound:
LOG.info(_LI("Cannot provide backend assisted migration for "
"volume: %s because cluster exists in different "
"management group."), volume['name'])
options = {'clusterName': cluster}
client.modifyVolume(volume_info['id'], options)
- except hpexceptions.HTTPNotFound:
+ except hpeexceptions.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:
+ except hpeexceptions.HTTPServerError as ex:
LOG.error(_LE("Exception: %s"), ex)
return false_ret
finally:
client = self._login()
try:
volume_info = client.getVolumeByName(target_vol_name)
- except hpexceptions.HTTPNotFound:
+ except hpeexceptions.HTTPNotFound:
err = (_("Virtual volume '%s' doesn't exist on array.") %
target_vol_name)
LOG.error(err)
client = self._login()
try:
volume_info = client.getVolumeByName(target_vol_name)
- except hpexceptions.HTTPNotFound:
+ except hpeexceptions.HTTPNotFound:
err = (_("Virtual volume '%s' doesn't exist on array.") %
target_vol_name)
LOG.error(err)
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. '
+ ex_msg = (_('Invalid HPELeftHand API version found: %(found)s. '
'Version %(minimum)s or greater required for '
'manage/unmanage support.')
% {'found': self.api_version,