Merge "New HP LeftHand array iSCSI driver"
This commit is contained in:
commit
c7976f116b
@ -1,360 +0,0 @@
|
||||
# Copyright 2012 OpenStack Foundation
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import mox
|
||||
|
||||
from cinder import exception
|
||||
from cinder.openstack.common import log as logging
|
||||
from cinder import test
|
||||
from cinder.volume import configuration as conf
|
||||
from cinder.volume.drivers.san.hp_lefthand import HpSanISCSIDriver
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class HpSanISCSITestCase(test.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(HpSanISCSITestCase, self).setUp()
|
||||
self.stubs.Set(HpSanISCSIDriver, "_cliq_run",
|
||||
self._fake_cliq_run)
|
||||
self.stubs.Set(HpSanISCSIDriver, "_get_iscsi_properties",
|
||||
self._fake_get_iscsi_properties)
|
||||
configuration = mox.MockObject(conf.Configuration)
|
||||
configuration.san_is_local = False
|
||||
configuration.san_ip = "10.0.0.1"
|
||||
configuration.san_login = "foo"
|
||||
configuration.san_password = "bar"
|
||||
configuration.san_ssh_port = 16022
|
||||
configuration.san_clustername = "CloudCluster1"
|
||||
configuration.san_thin_provision = True
|
||||
configuration.append_config_values(mox.IgnoreArg())
|
||||
|
||||
self.driver = HpSanISCSIDriver(configuration=configuration)
|
||||
self.volume_name = "fakevolume"
|
||||
self.snapshot_name = "fakeshapshot"
|
||||
self.connector = {'ip': '10.0.0.2',
|
||||
'initiator': 'iqn.1993-08.org.debian:01:222',
|
||||
'host': 'fakehost'}
|
||||
self.properties = {
|
||||
'target_discoverd': True,
|
||||
'target_portal': '10.0.1.6:3260',
|
||||
'target_iqn':
|
||||
'iqn.2003-10.com.lefthandnetworks:group01:25366:fakev',
|
||||
'volume_id': 1}
|
||||
|
||||
def tearDown(self):
|
||||
super(HpSanISCSITestCase, self).tearDown()
|
||||
|
||||
def _fake_get_iscsi_properties(self, volume):
|
||||
return self.properties
|
||||
|
||||
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(cliq_args['volumeName'], self.volume_name)
|
||||
self.assertEqual(cliq_args['thinProvision'], '1')
|
||||
self.assertEqual(cliq_args['size'], '1GB')
|
||||
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(cliq_args['volumeName'], self.volume_name)
|
||||
self.assertEqual(cliq_args['prompt'], 'false')
|
||||
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(cliq_args['volumeName'], self.volume_name)
|
||||
self.assertEqual(cliq_args['size'], '2GB')
|
||||
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(cliq_args['volumeName'], self.volume_name)
|
||||
self.assertEqual(cliq_args['serverName'], self.connector['host'])
|
||||
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(cliq_args['volumeName'], self.volume_name)
|
||||
self.assertEqual(cliq_args['serverName'], self.connector['host'])
|
||||
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(cliq_args['snapshotName'], self.snapshot_name)
|
||||
self.assertEqual(cliq_args['volumeName'], self.volume_name)
|
||||
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(cliq_args['snapshotName'], self.snapshot_name)
|
||||
self.assertEqual(cliq_args['prompt'], 'false')
|
||||
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(cliq_args['snapshotName'], self.snapshot_name)
|
||||
self.assertEqual(cliq_args['volumeName'], self.volume_name)
|
||||
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
|
||||
|
||||
self.assertEqual(cliq_args['output'], 'XML')
|
||||
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}
|
||||
except KeyError:
|
||||
raise NotImplementedError()
|
||||
|
||||
return verbs[verb](cliq_args)
|
||||
|
||||
def test_create_volume(self):
|
||||
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(model_update['provider_location'], expected_location)
|
||||
|
||||
def test_delete_volume(self):
|
||||
volume = {'name': self.volume_name}
|
||||
self.driver.delete_volume(volume)
|
||||
|
||||
def test_extend_volume(self):
|
||||
volume = {'name': self.volume_name}
|
||||
self.driver.extend_volume(volume, 2)
|
||||
|
||||
def test_initialize_connection(self):
|
||||
volume = {'name': self.volume_name}
|
||||
result = self.driver.initialize_connection(volume, self.connector)
|
||||
self.assertEqual(result['driver_volume_type'], 'iscsi')
|
||||
self.assertDictMatch(result['data'], self.properties)
|
||||
|
||||
def test_terminate_connection(self):
|
||||
volume = {'name': self.volume_name}
|
||||
self.driver.terminate_connection(volume, self.connector)
|
||||
|
||||
def test_create_snapshot(self):
|
||||
snapshot = {'name': self.snapshot_name,
|
||||
'volume_name': self.volume_name}
|
||||
self.driver.create_snapshot(snapshot)
|
||||
|
||||
def test_delete_snapshot(self):
|
||||
snapshot = {'name': self.snapshot_name}
|
||||
self.driver.delete_snapshot(snapshot)
|
||||
|
||||
def test_create_volume_from_snapshot(self):
|
||||
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(model_update['provider_location'], expected_location)
|
||||
|
||||
def test_cliq_error(self):
|
||||
try:
|
||||
self.driver._cliq_run_xml("testError", {})
|
||||
except exception.VolumeBackendAPIException:
|
||||
pass
|
@ -28,7 +28,6 @@ SHEEPDOG_MODULE = "cinder.volume.drivers.sheepdog.SheepdogDriver"
|
||||
NEXENTA_MODULE = "cinder.volume.drivers.nexenta.iscsi.NexentaISCSIDriver"
|
||||
SAN_MODULE = "cinder.volume.drivers.san.san.SanISCSIDriver"
|
||||
SOLARIS_MODULE = "cinder.volume.drivers.san.solaris.SolarisISCSIDriver"
|
||||
LEFTHAND_MODULE = "cinder.volume.drivers.san.hp_lefthand.HpSanISCSIDriver"
|
||||
NFS_MODULE = "cinder.volume.drivers.nfs.NfsDriver"
|
||||
SOLIDFIRE_MODULE = "cinder.volume.drivers.solidfire.SolidFireDriver"
|
||||
STORWIZE_MODULE = "cinder.volume.drivers.ibm.storwize_svc.StorwizeSVCDriver"
|
||||
@ -36,6 +35,8 @@ WINDOWS_MODULE = "cinder.volume.drivers.windows.windows.WindowsDriver"
|
||||
XIV_DS8K_MODULE = "cinder.volume.drivers.xiv_ds8k.XIVDS8KDriver"
|
||||
ZADARA_MODULE = "cinder.volume.drivers.zadara.ZadaraVPSAISCSIDriver"
|
||||
NETAPP_MODULE = "cinder.volume.drivers.netapp.common.Deprecated"
|
||||
LEFTHAND_REST_MODULE = ("cinder.volume.drivers.san.hp.hp_lefthand_iscsi."
|
||||
"HPLeftHandISCSIDriver")
|
||||
|
||||
|
||||
class VolumeDriverCompatibility(test.TestCase):
|
||||
@ -103,14 +104,6 @@ class VolumeDriverCompatibility(test.TestCase):
|
||||
self._load_driver(SOLARIS_MODULE)
|
||||
self.assertEqual(self._driver_module_name(), SOLARIS_MODULE)
|
||||
|
||||
def test_hp_lefthand_old(self):
|
||||
self._load_driver('cinder.volume.san.HpSanISCSIDriver')
|
||||
self.assertEqual(self._driver_module_name(), LEFTHAND_MODULE)
|
||||
|
||||
def test_hp_lefthand_new(self):
|
||||
self._load_driver(LEFTHAND_MODULE)
|
||||
self.assertEqual(self._driver_module_name(), LEFTHAND_MODULE)
|
||||
|
||||
def test_nfs_old(self):
|
||||
self._load_driver('cinder.volume.nfs.NfsDriver')
|
||||
self.assertEqual(self._driver_module_name(), NFS_MODULE)
|
||||
@ -198,3 +191,12 @@ class VolumeDriverCompatibility(test.TestCase):
|
||||
self._load_driver(
|
||||
'cinder.volume.drivers.netapp.nfs.NetAppCmodeNfsDriver')
|
||||
self.assertEqual(self._driver_module_name(), NETAPP_MODULE)
|
||||
|
||||
def test_hp_lefthand_rest_old(self):
|
||||
self._load_driver(
|
||||
'cinder.volume.drivers.san.hp_lefthand.HpSanISCSIDriver')
|
||||
self.assertEqual(self._driver_module_name(), LEFTHAND_REST_MODULE)
|
||||
|
||||
def test_hp_lefthand_rest_new(self):
|
||||
self._load_driver(LEFTHAND_REST_MODULE)
|
||||
self.assertEqual(self._driver_module_name(), LEFTHAND_REST_MODULE)
|
||||
|
1014
cinder/tests/test_hplefthand.py
Normal file
1014
cinder/tests/test_hplefthand.py
Normal file
File diff suppressed because it is too large
Load Diff
@ -22,6 +22,5 @@
|
||||
"""
|
||||
|
||||
# Adding imports for backwards compatibility in loading volume_driver.
|
||||
from hp_lefthand import HpSanISCSIDriver # noqa
|
||||
from san import SanISCSIDriver # noqa
|
||||
from solaris import SolarisISCSIDriver # noqa
|
||||
|
59
cinder/volume/drivers/san/hp_lefthand.py → cinder/volume/drivers/san/hp/hp_lefthand_cliq_proxy.py
59
cinder/volume/drivers/san/hp_lefthand.py → cinder/volume/drivers/san/hp/hp_lefthand_cliq_proxy.py
@ -1,4 +1,5 @@
|
||||
# Copyright 2012 OpenStack Foundation
|
||||
# (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
|
||||
@ -11,8 +12,9 @@
|
||||
# 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.
|
||||
HP LeftHand SAN ISCSI Driver.
|
||||
|
||||
The driver communicates to the backend aka Cliq via SSH to perform all the
|
||||
operations on the SAN.
|
||||
@ -22,14 +24,15 @@ from lxml import etree
|
||||
from cinder import exception
|
||||
from cinder.openstack.common import log as logging
|
||||
from cinder.openstack.common import processutils
|
||||
from cinder import units
|
||||
from cinder.volume.drivers.san.san import SanISCSIDriver
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class HpSanISCSIDriver(SanISCSIDriver):
|
||||
"""Executes commands relating to HP/Lefthand SAN ISCSI volumes.
|
||||
class HPLeftHandCLIQProxy(SanISCSIDriver):
|
||||
"""Executes commands relating to HP/LeftHand SAN ISCSI volumes.
|
||||
|
||||
We use the CLIQ interface, over SSH.
|
||||
|
||||
@ -53,8 +56,6 @@ class HpSanISCSIDriver(SanISCSIDriver):
|
||||
|
||||
:getClusterInfo: (to discover the iSCSI target IP address)
|
||||
|
||||
:assignVolumeChap: (exports it with CHAP security)
|
||||
|
||||
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
|
||||
@ -67,16 +68,26 @@ class HpSanISCSIDriver(SanISCSIDriver):
|
||||
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.
|
||||
"""
|
||||
|
||||
VERSION = "1.1.0"
|
||||
VERSION = "1.2.0"
|
||||
|
||||
device_stats = {}
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(HpSanISCSIDriver, self).__init__(*args, **kwargs)
|
||||
super(HPLeftHandCLIQProxy, self).__init__(*args, **kwargs)
|
||||
self.cluster_vip = None
|
||||
|
||||
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]
|
||||
@ -301,7 +312,7 @@ class HpSanISCSIDriver(SanISCSIDriver):
|
||||
try:
|
||||
volume_info = self._cliq_get_volume_info(volume['name'])
|
||||
except processutils.ProcessExecutionError:
|
||||
LOG.error_("Volume did not exist. It will not be deleted")
|
||||
LOG.error(_("Volume did not exist. It will not be deleted"))
|
||||
return
|
||||
self._cliq_run_xml("deleteVolume", cliq_args)
|
||||
|
||||
@ -313,9 +324,16 @@ class HpSanISCSIDriver(SanISCSIDriver):
|
||||
try:
|
||||
volume_info = self._cliq_get_snapshot_info(snapshot['name'])
|
||||
except processutils.ProcessExecutionError:
|
||||
LOG.error_("Snapshot did not exist. It will not be deleted")
|
||||
LOG.error(_("Snapshot did not exist. It will not be deleted"))
|
||||
return
|
||||
self._cliq_run_xml("deleteSnapshot", cliq_args)
|
||||
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(str(ex))
|
||||
|
||||
raise exception.VolumeBackendAPIException(str(ex))
|
||||
|
||||
def local_path(self, volume):
|
||||
msg = _("local_path not supported")
|
||||
@ -349,10 +367,10 @@ class HpSanISCSIDriver(SanISCSIDriver):
|
||||
cliq_args['serverName'] = connector['host']
|
||||
self._cliq_run_xml("assignVolumeToServer", cliq_args)
|
||||
|
||||
iscsi_properties = self._get_iscsi_properties(volume)
|
||||
iscsi_data = self._get_iscsi_properties(volume)
|
||||
return {
|
||||
'driver_volume_type': 'iscsi',
|
||||
'data': iscsi_properties
|
||||
'data': iscsi_data
|
||||
}
|
||||
|
||||
def _create_server(self, connector):
|
||||
@ -405,7 +423,6 @@ class HpSanISCSIDriver(SanISCSIDriver):
|
||||
data = {}
|
||||
backend_name = self.configuration.safe_get('volume_backend_name')
|
||||
data['volume_backend_name'] = backend_name or self.__class__.__name__
|
||||
data['driver_version'] = self.VERSION
|
||||
data['reserved_percentage'] = 0
|
||||
data['storage_protocol'] = 'iSCSI'
|
||||
data['vendor_name'] = 'Hewlett-Packard'
|
||||
@ -414,8 +431,20 @@ class HpSanISCSIDriver(SanISCSIDriver):
|
||||
cluster_node = result_xml.find("response/cluster")
|
||||
total_capacity = cluster_node.attrib.get("spaceTotal")
|
||||
free_capacity = cluster_node.attrib.get("unprovisionedSpace")
|
||||
GB = 1073741824
|
||||
GB = units.GiB
|
||||
|
||||
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):
|
||||
pass
|
||||
|
||||
def ensure_export(self, context, volume):
|
||||
pass
|
||||
|
||||
def remove_export(self, context, volume):
|
||||
pass
|
137
cinder/volume/drivers/san/hp/hp_lefthand_iscsi.py
Normal file
137
cinder/volume/drivers/san/hp/hp_lefthand_iscsi.py
Normal file
@ -0,0 +1,137 @@
|
||||
# (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.
|
||||
#
|
||||
"""
|
||||
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.
|
||||
"""
|
||||
from cinder import exception
|
||||
from cinder.openstack.common import log as logging
|
||||
from cinder import utils
|
||||
from cinder.volume.driver import VolumeDriver
|
||||
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
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class HPLeftHandISCSIDriver(VolumeDriver):
|
||||
"""Executes commands relating to HP/LeftHand SAN ISCSI volumes.
|
||||
|
||||
Version history:
|
||||
1.0.0 - Initial driver
|
||||
"""
|
||||
|
||||
VERSION = "1.0.0"
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(HPLeftHandISCSIDriver, self).__init__(*args, **kwargs)
|
||||
self.proxy = self._create_proxy(*args, **kwargs)
|
||||
|
||||
def _create_proxy(self, *args, **kwargs):
|
||||
try:
|
||||
proxy = rest_proxy.HPLeftHandRESTProxy(*args, **kwargs)
|
||||
except exception.NotFound:
|
||||
proxy = cliq_proxy.HPLeftHandCLIQProxy(*args, **kwargs)
|
||||
|
||||
return proxy
|
||||
|
||||
@utils.synchronized('lefthand', external=True)
|
||||
def check_for_setup_error(self):
|
||||
self.proxy.check_for_setup_error()
|
||||
|
||||
@utils.synchronized('lefthand', external=True)
|
||||
def do_setup(self, context):
|
||||
self.proxy.do_setup(context)
|
||||
|
||||
LOG.info(_("HPLeftHand driver %(driver_ver)s, proxy %(proxy_ver)s") % {
|
||||
"driver_ver": self.VERSION,
|
||||
"proxy_ver": self.proxy.get_version_string()})
|
||||
|
||||
@utils.synchronized('lefthand', external=True)
|
||||
def create_volume(self, volume):
|
||||
"""Creates a volume."""
|
||||
return self.proxy.create_volume(volume)
|
||||
|
||||
@utils.synchronized('lefthand', external=True)
|
||||
def extend_volume(self, volume, new_size):
|
||||
"""Extend the size of an existing volume."""
|
||||
self.proxy.extend_volume(volume, new_size)
|
||||
|
||||
@utils.synchronized('lefthand', external=True)
|
||||
def create_volume_from_snapshot(self, volume, snapshot):
|
||||
"""Creates a volume from a snapshot."""
|
||||
return self.proxy.create_volume_from_snapshot(volume, snapshot)
|
||||
|
||||
@utils.synchronized('lefthand', external=True)
|
||||
def create_snapshot(self, snapshot):
|
||||
"""Creates a snapshot."""
|
||||
self.proxy.create_snapshot(snapshot)
|
||||
|
||||
@utils.synchronized('lefthand', external=True)
|
||||
def delete_volume(self, volume):
|
||||
"""Deletes a volume."""
|
||||
self.proxy.delete_volume(volume)
|
||||
|
||||
@utils.synchronized('lefthand', external=True)
|
||||
def delete_snapshot(self, snapshot):
|
||||
"""Deletes a snapshot."""
|
||||
self.proxy.delete_snapshot(snapshot)
|
||||
|
||||
@utils.synchronized('lefthand', external=True)
|
||||
def initialize_connection(self, volume, connector):
|
||||
"""Assigns the volume to a server."""
|
||||
return self.proxy.initialize_connection(volume, connector)
|
||||
|
||||
@utils.synchronized('lefthand', external=True)
|
||||
def terminate_connection(self, volume, connector, **kwargs):
|
||||
"""Unassign the volume from the host."""
|
||||
self.proxy.terminate_connection(volume, connector)
|
||||
|
||||
@utils.synchronized('lefthand', external=True)
|
||||
def get_volume_stats(self, refresh):
|
||||
data = self.proxy.get_volume_stats(refresh)
|
||||
data['driver_version'] = self.VERSION
|
||||
return data
|
||||
|
||||
@utils.synchronized('lefthand', external=True)
|
||||
def create_cloned_volume(self, volume, src_vref):
|
||||
return self.proxy.create_cloned_volume(volume, src_vref)
|
||||
|
||||
@utils.synchronized('lefthand', external=True)
|
||||
def create_export(self, context, volume):
|
||||
return self.proxy.create_export(context, volume)
|
||||
|
||||
@utils.synchronized('lefthand', external=True)
|
||||
def ensure_export(self, context, volume):
|
||||
return self.proxy.ensure_export(context, volume)
|
||||
|
||||
@utils.synchronized('lefthand', external=True)
|
||||
def remove_export(self, context, volume):
|
||||
return self.proxy.remove_export(context, volume)
|
363
cinder/volume/drivers/san/hp/hp_lefthand_rest_proxy.py
Normal file
363
cinder/volume/drivers/san/hp/hp_lefthand_rest_proxy.py
Normal file
@ -0,0 +1,363 @@
|
||||
# (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 REST Proxy."""
|
||||
|
||||
from cinder import context
|
||||
from cinder import exception
|
||||
from cinder.openstack.common import log as logging
|
||||
from cinder import units
|
||||
from cinder import utils
|
||||
from cinder.volume.driver import ISCSIDriver
|
||||
from cinder.volume import volume_types
|
||||
from oslo.config import cfg
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
try:
|
||||
import hplefthandclient
|
||||
from hplefthandclient import client
|
||||
from hplefthandclient import exceptions as hpexceptions
|
||||
except ImportError:
|
||||
LOG.error(_('Module hplefthandclient not installed.'))
|
||||
|
||||
hplefthand_opts = [
|
||||
cfg.StrOpt('hplefthand_api_url',
|
||||
default=None,
|
||||
help="HP LeftHand WSAPI Server Url like "
|
||||
"https://<LeftHand ip>:8081/lhos"),
|
||||
cfg.StrOpt('hplefthand_username',
|
||||
default=None,
|
||||
help="HP LeftHand Super user username"),
|
||||
cfg.StrOpt('hplefthand_password',
|
||||
default=None,
|
||||
help="HP LeftHand Super user password",
|
||||
secret=True),
|
||||
cfg.StrOpt('hplefthand_clustername',
|
||||
default=None,
|
||||
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)
|
||||
|
||||
|
||||
# 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(ISCSIDriver):
|
||||
"""Executes REST commands relating to HP/LeftHand SAN ISCSI volumes.
|
||||
|
||||
Version history:
|
||||
1.0.0 - Initial REST iSCSI proxy
|
||||
"""
|
||||
|
||||
VERSION = "1.0.0"
|
||||
|
||||
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"))
|
||||
|
||||
def do_setup(self, context):
|
||||
"""Set up LeftHand client."""
|
||||
try:
|
||||
self.client = client.HPLeftHandClient(
|
||||
self.configuration.hplefthand_api_url)
|
||||
self.client.login(
|
||||
self.configuration.hplefthand_username,
|
||||
self.configuration.hplefthand_password)
|
||||
|
||||
if self.configuration.hplefthand_debug:
|
||||
self.client.debug_rest(True)
|
||||
|
||||
cluster_info = self.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()
|
||||
except hpexceptions.HTTPNotFound:
|
||||
raise exception.DriverNotInitialized(
|
||||
_('LeftHand cluster not found'))
|
||||
except Exception as ex:
|
||||
raise exception.DriverNotInitialized(str(ex))
|
||||
|
||||
def check_for_setup_error(self):
|
||||
pass
|
||||
|
||||
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."""
|
||||
try:
|
||||
# get the extra specs of interest from this volume's volume type
|
||||
extra_specs = self._get_extra_specs(
|
||||
volume,
|
||||
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
|
||||
|
||||
clusterName = self.configuration.hplefthand_clustername
|
||||
optional['clusterName'] = clusterName
|
||||
|
||||
volume_info = self.client.createVolume(
|
||||
volume['name'], self.cluster_id,
|
||||
volume['size'] * units.GiB,
|
||||
optional)
|
||||
|
||||
return self._update_provider(volume_info)
|
||||
except Exception as ex:
|
||||
raise exception.VolumeBackendAPIException(str(ex))
|
||||
|
||||
def delete_volume(self, volume):
|
||||
"""Deletes a volume."""
|
||||
try:
|
||||
volume_info = self.client.getVolumeByName(volume['name'])
|
||||
self.client.deleteVolume(volume_info['id'])
|
||||
except hpexceptions.HTTPNotFound:
|
||||
LOG.error(_("Volume did not exist. It will not be deleted"))
|
||||
except Exception as ex:
|
||||
raise exception.VolumeBackendAPIException(str(ex))
|
||||
|
||||
def extend_volume(self, volume, new_size):
|
||||
"""Extend the size of an existing volume."""
|
||||
try:
|
||||
volume_info = self.client.getVolumeByName(volume['name'])
|
||||
|
||||
# convert GB to bytes
|
||||
options = {'size': int(new_size) * units.GiB}
|
||||
self.client.modifyVolume(volume_info['id'], options)
|
||||
except Exception as ex:
|
||||
raise exception.VolumeBackendAPIException(str(ex))
|
||||
|
||||
def create_snapshot(self, snapshot):
|
||||
"""Creates a snapshot."""
|
||||
try:
|
||||
volume_info = self.client.getVolumeByName(snapshot['volume_name'])
|
||||
|
||||
option = {'inheritAccess': True}
|
||||
self.client.createSnapshot(snapshot['name'],
|
||||
volume_info['id'],
|
||||
option)
|
||||
except Exception as ex:
|
||||
raise exception.VolumeBackendAPIException(str(ex))
|
||||
|
||||
def delete_snapshot(self, snapshot):
|
||||
"""Deletes a snapshot."""
|
||||
try:
|
||||
snap_info = self.client.getSnapshotByName(snapshot['name'])
|
||||
self.client.deleteSnapshot(snap_info['id'])
|
||||
except hpexceptions.HTTPNotFound:
|
||||
LOG.error(_("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(str(ex))
|
||||
|
||||
raise exception.VolumeBackendAPIException(str(ex))
|
||||
|
||||
except Exception as ex:
|
||||
raise exception.VolumeBackendAPIException(str(ex))
|
||||
|
||||
def get_volume_stats(self, refresh):
|
||||
"""Gets volume stats."""
|
||||
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'
|
||||
|
||||
cluster_info = self.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.GiB
|
||||
data['free_capacity_gb'] = int(free_capacity) / units.GiB
|
||||
|
||||
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.
|
||||
"""
|
||||
try:
|
||||
server_info = self._create_server(connector)
|
||||
volume_info = self.client.getVolumeByName(volume['name'])
|
||||
self.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(str(ex))
|
||||
|
||||
def terminate_connection(self, volume, connector, **kwargs):
|
||||
"""Unassign the volume from the host."""
|
||||
try:
|
||||
volume_info = self.client.getVolumeByName(volume['name'])
|
||||
server_info = self.client.getServerByName(connector['host'])
|
||||
self.client.removeServerAccess(
|
||||
volume_info['id'],
|
||||
server_info['id'])
|
||||
except Exception as ex:
|
||||
raise exception.VolumeBackendAPIException(str(ex))
|
||||
|
||||
def create_volume_from_snapshot(self, volume, snapshot):
|
||||
"""Creates a volume from a snapshot."""
|
||||
try:
|
||||
snap_info = self.client.getSnapshotByName(snapshot['name'])
|
||||
volume_info = self.client.cloneSnapshot(
|
||||
volume['name'],
|
||||
snap_info['id'])
|
||||
return self._update_provider(volume_info)
|
||||
except Exception as ex:
|
||||
raise exception.VolumeBackendAPIException(str(ex))
|
||||
|
||||
def create_cloned_volume(self, volume, src_vref):
|
||||
try:
|
||||
volume_info = self.client.getVolumeByName(src_vref['name'])
|
||||
self.client.cloneVolume(volume['name'], volume_info['id'])
|
||||
except Exception as ex:
|
||||
raise exception.VolumeBackendAPIException(str(ex))
|
||||
|
||||
def _get_extra_specs(self, volume, valid_keys):
|
||||
"""Get extra specs of interest (valid_keys) from volume type."""
|
||||
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)
|
||||
specs = volume_type.get('extra_specs')
|
||||
for key, value in specs.iteritems():
|
||||
if key in valid_keys:
|
||||
extra_specs[key] = value
|
||||
return extra_specs
|
||||
|
||||
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.iteritems():
|
||||
# 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(_("'%(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):
|
||||
server_info = None
|
||||
chap_enabled = self.configuration.hplefthand_iscsi_chap_enabled
|
||||
try:
|
||||
server_info = self.client.getServerByName(connector['host'])
|
||||
chap_secret = server_info['chapTargetSecret']
|
||||
if not chap_enabled and chap_secret:
|
||||
LOG.warning(_('CHAP secret exists for host %s but CHAP is '
|
||||
'disabled') % connector['host'])
|
||||
if chap_enabled and chap_secret is None:
|
||||
LOG.warning(_('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 = self.client.createServer(connector['host'],
|
||||
connector['initiator'],
|
||||
optional)
|
||||
return server_info
|
||||
|
||||
def create_export(self, context, volume):
|
||||
pass
|
||||
|
||||
def ensure_export(self, context, volume):
|
||||
pass
|
||||
|
||||
def remove_export(self, context, volume):
|
||||
pass
|
@ -94,8 +94,6 @@ MAPPING = {
|
||||
'cinder.volume.drivers.san.san.SanISCSIDriver',
|
||||
'cinder.volume.san.SolarisISCSIDriver':
|
||||
'cinder.volume.drivers.san.solaris.SolarisISCSIDriver',
|
||||
'cinder.volume.san.HpSanISCSIDriver':
|
||||
'cinder.volume.drivers.san.hp_lefthand.HpSanISCSIDriver',
|
||||
'cinder.volume.nfs.NfsDriver':
|
||||
'cinder.volume.drivers.nfs.NfsDriver',
|
||||
'cinder.volume.solidfire.SolidFire':
|
||||
@ -133,7 +131,9 @@ MAPPING = {
|
||||
'cinder.volume.drivers.netapp.nfs.NetAppCmodeNfsDriver':
|
||||
'cinder.volume.drivers.netapp.common.Deprecated',
|
||||
'cinder.volume.drivers.huawei.HuaweiISCSIDriver':
|
||||
'cinder.volume.drivers.huawei.HuaweiVolumeDriver'}
|
||||
'cinder.volume.drivers.huawei.HuaweiVolumeDriver',
|
||||
'cinder.volume.drivers.san.hp_lefthand.HpSanISCSIDriver':
|
||||
'cinder.volume.drivers.san.hp.hp_lefthand_iscsi.HPLeftHandISCSIDriver'}
|
||||
|
||||
|
||||
def locked_volume_operation(f):
|
||||
|
@ -1482,6 +1482,31 @@
|
||||
#hp3par_iscsi_ips=
|
||||
|
||||
|
||||
#
|
||||
# Options defined in cinder.volume.drivers.san.hp.hp_lefthand_rest_proxy
|
||||
#
|
||||
|
||||
# HP LeftHand WSAPI Server Url like https://<LeftHand
|
||||
# ip>:8081/lhos (string value)
|
||||
#hplefthand_api_url=<None>
|
||||
|
||||
# HP LeftHand Super user username (string value)
|
||||
#hplefthand_username=<None>
|
||||
|
||||
# HP LeftHand Super user password (string value)
|
||||
#hplefthand_password=<None>
|
||||
|
||||
# HP LeftHand cluster name (string value)
|
||||
#hplefthand_clustername=<None>
|
||||
|
||||
# Configure CHAP authentication for iSCSI connections
|
||||
# (Default: Disabled) (boolean value)
|
||||
#hplefthand_iscsi_chap_enabled=false
|
||||
|
||||
# Enable HTTP debugging to LeftHand (boolean value)
|
||||
#hplefthand_debug=false
|
||||
|
||||
|
||||
#
|
||||
# Options defined in cinder.volume.drivers.san.san
|
||||
#
|
||||
|
@ -4,6 +4,7 @@ coverage>=3.6
|
||||
discover
|
||||
fixtures>=0.3.14
|
||||
hp3parclient>=2.0,<3.0
|
||||
hplefthandclient>=1.0.0,<2.0.0
|
||||
mock>=1.0
|
||||
mox>=0.5.3
|
||||
MySQL-python
|
||||
|
Loading…
x
Reference in New Issue
Block a user