Merge "Adds multiple iSCSI port support to 3PAR"

This commit is contained in:
Jenkins
2013-07-17 00:13:55 +00:00
committed by Gerrit Code Review
4 changed files with 424 additions and 134 deletions

View File

@@ -305,7 +305,10 @@ class HP3PARBaseDriver():
VOLUME_ID_SNAP = '761fc5e5-5191-4ec7-aeba-33e36de44156'
FAKE_DESC = 'test description name'
FAKE_FC_PORTS = ['0987654321234', '123456789000987']
FAKE_ISCSI_PORTS = ['10.10.10.10', '10.10.10.11']
FAKE_ISCSI_PORTS = {'1.1.1.2': {'nsp': '8:1:1',
'iqn': ('iqn.2000-05.com.3pardata:'
'21810002ac00383d'),
'ip_port': '3262'}}
volume = {'name': VOLUME_NAME,
'id': VOLUME_ID,
@@ -333,6 +336,43 @@ class HP3PARBaseDriver():
'wwnns': ["223456789012345", "223456789054321"],
'host': 'fakehost'}
def setup_configuration(self):
configuration = mox.MockObject(conf.Configuration)
configuration.hp3par_debug = False
configuration.hp3par_username = 'testUser'
configuration.hp3par_password = 'testPassword'
configuration.hp3par_api_url = 'https://1.1.1.1/api/v1'
configuration.hp3par_domain = HP3PAR_DOMAIN
configuration.hp3par_cpg = HP3PAR_CPG
configuration.hp3par_cpg_snap = HP3PAR_CPG_SNAP
configuration.iscsi_ip_address = '1.1.1.2'
configuration.iscsi_port = '1234'
configuration.san_ip = '2.2.2.2'
configuration.san_login = 'test'
configuration.san_password = 'test'
configuration.hp3par_snapshot_expiration = ""
configuration.hp3par_snapshot_retention = ""
configuration.hp3par_iscsi_ips = []
return configuration
def setup_fakes(self):
self.stubs.Set(hpdriver.hpcommon.HP3PARCommon, "_create_client",
self.fake_create_client)
self.stubs.Set(hpdriver.hpcommon.HP3PARCommon, "_get_3par_host",
self.fake_get_3par_host)
self.stubs.Set(hpdriver.hpcommon.HP3PARCommon, "_delete_3par_host",
self.fake_delete_3par_host)
self.stubs.Set(hpdriver.hpcommon.HP3PARCommon, "_create_3par_vlun",
self.fake_create_3par_vlun)
self.stubs.Set(hpdriver.hpcommon.HP3PARCommon, "get_ports",
self.fake_get_ports)
self.stubs.Set(hpfcdriver.hpcommon.HP3PARCommon, "get_domain",
self.fake_get_domain)
def clear_mox(self):
self.mox.ResetAll()
self.stubs.UnsetAll()
def fake_create_client(self):
return FakeHP3ParClient(self.driver.configuration.hp3par_api_url)
@@ -430,47 +470,26 @@ class TestHP3PARFCDriver(HP3PARBaseDriver, test.TestCase):
def setUp(self):
self.tempdir = tempfile.mkdtemp()
super(TestHP3PARFCDriver, self).setUp()
self.setup_driver(self.setup_configuration())
self.setup_fakes()
configuration = mox.MockObject(conf.Configuration)
configuration.hp3par_debug = False
configuration.hp3par_username = 'testUser'
configuration.hp3par_password = 'testPassword'
configuration.hp3par_api_url = 'https://1.1.1.1/api/v1'
configuration.hp3par_cpg = HP3PAR_CPG
configuration.hp3par_cpg_snap = HP3PAR_CPG_SNAP
configuration.iscsi_ip_address = '1.1.1.2'
configuration.iscsi_port = '1234'
configuration.san_ip = '2.2.2.2'
configuration.san_login = 'test'
configuration.san_password = 'test'
configuration.hp3par_snapshot_expiration = ""
configuration.hp3par_snapshot_retention = ""
self.stubs.Set(hpfcdriver.hpcommon.HP3PARCommon, "_create_client",
self.fake_create_client)
def setup_fakes(self):
super(TestHP3PARFCDriver, self).setup_fakes()
self.stubs.Set(hpfcdriver.HP3PARFCDriver,
"_create_3par_fibrechan_host",
self.fake_create_3par_fibrechan_host)
self.stubs.Set(hpfcdriver.hpcommon.HP3PARCommon, "_get_3par_host",
self.fake_get_3par_host)
self.stubs.Set(hpfcdriver.hpcommon.HP3PARCommon, "_delete_3par_host",
self.fake_delete_3par_host)
self.stubs.Set(hpdriver.hpcommon.HP3PARCommon, "_create_3par_vlun",
self.fake_create_3par_vlun)
self.stubs.Set(hpdriver.hpcommon.HP3PARCommon, "get_ports",
self.fake_get_ports)
self.stubs.Set(hpfcdriver.hpcommon.HP3PARCommon, "get_domain",
self.fake_get_domain)
self.configuration = configuration
self.driver = hpfcdriver.HP3PARFCDriver(configuration=configuration)
self.driver.do_setup(None)
def tearDown(self):
shutil.rmtree(self.tempdir)
super(TestHP3PARFCDriver, self).tearDown()
def setup_driver(self, configuration):
self.driver = hpfcdriver.HP3PARFCDriver(configuration=configuration)
self.stubs.Set(hpdriver.hpcommon.HP3PARCommon, "_create_client",
self.fake_create_client)
self.driver.do_setup(None)
def fake_create_3par_fibrechan_host(self, hostname, wwn,
domain, persona_id):
host = {'FCPaths': [{'driverVersion': None,
@@ -577,7 +596,7 @@ class TestHP3PARFCDriver(HP3PARBaseDriver, test.TestCase):
self.flags(lock_path=self.tempdir)
#record
self.stubs.UnsetAll()
self.clear_mox()
self.stubs.Set(hpfcdriver.hpcommon.HP3PARCommon, "get_domain",
self.fake_get_domain)
_run_ssh = self.mox.CreateMock(hpdriver.hpcommon.HP3PARCommon._run_ssh)
@@ -600,7 +619,7 @@ class TestHP3PARFCDriver(HP3PARBaseDriver, test.TestCase):
self.flags(lock_path=self.tempdir)
#record
self.stubs.UnsetAll()
self.clear_mox()
self.stubs.Set(hpdriver.hpcommon.HP3PARCommon, "get_domain",
self.fake_get_domain)
_run_ssh = self.mox.CreateMock(hpdriver.hpcommon.HP3PARCommon._run_ssh)
@@ -627,7 +646,7 @@ class TestHP3PARFCDriver(HP3PARBaseDriver, test.TestCase):
self.flags(lock_path=self.tempdir)
#record
self.stubs.UnsetAll()
self.clear_mox()
self.stubs.Set(hpdriver.hpcommon.HP3PARCommon, "get_domain",
self.fake_get_domain)
_run_ssh = self.mox.CreateMock(hpdriver.hpcommon.HP3PARCommon._run_ssh)
@@ -657,49 +676,19 @@ class TestHP3PARISCSIDriver(HP3PARBaseDriver, test.TestCase):
def setUp(self):
self.tempdir = tempfile.mkdtemp()
super(TestHP3PARISCSIDriver, self).setUp()
self.setup_driver(self.setup_configuration())
self.setup_fakes()
configuration = mox.MockObject(conf.Configuration)
configuration.hp3par_debug = False
configuration.hp3par_username = 'testUser'
configuration.hp3par_password = 'testPassword'
configuration.hp3par_api_url = 'https://1.1.1.1/api/v1'
configuration.hp3par_cpg = HP3PAR_CPG
configuration.hp3par_cpg_snap = HP3PAR_CPG_SNAP
configuration.iscsi_ip_address = '1.1.1.2'
configuration.iscsi_port = '1234'
configuration.san_ip = '2.2.2.2'
configuration.san_login = 'test'
configuration.san_password = 'test'
configuration.hp3par_snapshot_expiration = ""
configuration.hp3par_snapshot_retention = ""
def setup_fakes(self):
super(TestHP3PARISCSIDriver, self).setup_fakes()
self.stubs.Set(hpdriver.hpcommon.HP3PARCommon, "_create_client",
self.fake_create_client)
self.stubs.Set(hpdriver.HP3PARISCSIDriver,
"_iscsi_discover_target_iqn",
self.fake_iscsi_discover_target_iqn)
self.stubs.Set(hpdriver.HP3PARISCSIDriver, "_create_3par_iscsi_host",
self.fake_create_3par_iscsi_host)
self.stubs.Set(hpdriver.HP3PARISCSIDriver,
"_iscsi_discover_target_iqn",
self.fake_iscsi_discover_target_iqn)
self.stubs.Set(hpdriver.hpcommon.HP3PARCommon, "_get_3par_host",
self.fake_get_3par_host)
self.stubs.Set(hpdriver.hpcommon.HP3PARCommon, "_delete_3par_host",
self.fake_delete_3par_host)
self.stubs.Set(hpdriver.hpcommon.HP3PARCommon, "_create_3par_vlun",
self.fake_create_3par_vlun)
self.stubs.Set(hpdriver.hpcommon.HP3PARCommon, "get_domain",
self.fake_get_domain)
self.driver = hpdriver.HP3PARISCSIDriver(configuration=configuration)
self.driver.do_setup(None)
target_iqn = 'iqn.2000-05.com.3pardata:21810002ac00383d'
#target_iqn = 'iqn.2000-05.com.3pardata:21810002ac00383d'
self.properties = {'data':
{'target_discovered': True,
'target_iqn': target_iqn,
'target_iqn': self.TARGET_IQN,
'target_lun': 186,
'target_portal': '1.1.1.2:1234'},
'driver_volume_type': 'iscsi'}
@@ -709,8 +698,17 @@ class TestHP3PARISCSIDriver(HP3PARBaseDriver, test.TestCase):
self._hosts = {}
super(TestHP3PARISCSIDriver, self).tearDown()
def fake_iscsi_discover_target_iqn(self, ip_address):
return self.TARGET_IQN
def setup_driver(self, configuration, set_up_fakes=True):
self.driver = hpdriver.HP3PARISCSIDriver(configuration=configuration)
self.stubs.Set(hpdriver.hpcommon.HP3PARCommon, "_create_client",
self.fake_create_client)
if set_up_fakes:
self.stubs.Set(hpdriver.hpcommon.HP3PARCommon, "get_ports",
self.fake_get_ports)
self.driver.do_setup(None)
def fake_create_3par_iscsi_host(self, hostname, iscsi_iqn,
domain, persona_id):
@@ -812,7 +810,7 @@ class TestHP3PARISCSIDriver(HP3PARBaseDriver, test.TestCase):
self.flags(lock_path=self.tempdir)
#record
self.stubs.UnsetAll()
self.clear_mox()
self.stubs.Set(hpdriver.hpcommon.HP3PARCommon, "get_domain",
self.fake_get_domain)
_run_ssh = self.mox.CreateMock(hpdriver.hpcommon.HP3PARCommon._run_ssh)
@@ -836,7 +834,7 @@ class TestHP3PARISCSIDriver(HP3PARBaseDriver, test.TestCase):
self.flags(lock_path=self.tempdir)
#record
self.stubs.UnsetAll()
self.clear_mox()
self.stubs.Set(hpdriver.hpcommon.HP3PARCommon, "get_domain",
self.fake_get_domain)
_run_ssh = self.mox.CreateMock(hpdriver.hpcommon.HP3PARCommon._run_ssh)
@@ -863,7 +861,7 @@ class TestHP3PARISCSIDriver(HP3PARBaseDriver, test.TestCase):
self.flags(lock_path=self.tempdir)
#record
self.stubs.UnsetAll()
self.clear_mox()
self.stubs.Set(hpdriver.hpcommon.HP3PARCommon, "get_domain",
self.fake_get_domain)
_run_ssh = self.mox.CreateMock(hpdriver.hpcommon.HP3PARCommon._run_ssh)
@@ -881,27 +879,11 @@ class TestHP3PARISCSIDriver(HP3PARBaseDriver, test.TestCase):
host = self.driver._create_host(self.volume, self.connector)
self.assertEqual(host['name'], self.FAKE_HOST)
def test_iscsi_discover_target_iqn(self):
self.flags(lock_path=self.tempdir)
#record
self.stubs.UnsetAll()
_run_ssh = self.mox.CreateMock(hpdriver.hpcommon.HP3PARCommon._run_ssh)
self.stubs.Set(hpdriver.hpcommon.HP3PARCommon, "_run_ssh", _run_ssh)
show_port_cmd = 'showport -ids'
_run_ssh(show_port_cmd, False).AndReturn([pack(ISCSI_PORT_IDS_RET),
''])
self.mox.ReplayAll()
iqn = self.driver._iscsi_discover_target_iqn('10.10.120.253')
self.assertEqual(iqn, self.TARGET_IQN)
def test_get_volume_state(self):
self.flags(lock_path=self.tempdir)
#record
self.stubs.UnsetAll()
self.clear_mox()
_run_ssh = self.mox.CreateMock(hpdriver.hpcommon.HP3PARCommon._run_ssh)
self.stubs.Set(hpdriver.hpcommon.HP3PARCommon, "_run_ssh", _run_ssh)
@@ -917,7 +899,7 @@ class TestHP3PARISCSIDriver(HP3PARBaseDriver, test.TestCase):
self.flags(lock_path=self.tempdir)
#record
self.stubs.UnsetAll()
self.clear_mox()
_run_ssh = self.mox.CreateMock(hpdriver.hpcommon.HP3PARCommon._run_ssh)
self.stubs.Set(hpdriver.hpcommon.HP3PARCommon, "_run_ssh", _run_ssh)
@@ -925,11 +907,144 @@ class TestHP3PARISCSIDriver(HP3PARBaseDriver, test.TestCase):
_run_ssh(show_port_cmd, False).AndReturn([pack(PORT_RET), ''])
show_port_i_cmd = 'showport -iscsi'
_run_ssh(show_port_i_cmd, False).AndReturn([pack(ISCSI_PORT_RET), ''])
_run_ssh(show_port_i_cmd, False).AndReturn([pack(READY_ISCSI_PORT_RET),
''])
show_port_i_cmd = 'showport -iscsiname'
_run_ssh(show_port_i_cmd, False).AndReturn([pack(SHOW_PORT_ISCSI),
''])
self.mox.ReplayAll()
ports = self.driver.common.get_ports()
self.assertEqual(ports['FC'][0], '20210002AC00383D')
self.assertEqual(ports['iSCSI']['10.10.120.252']['nsp'], '0:8:2')
def test_get_iscsi_ip_active(self):
self.flags(lock_path=self.tempdir)
#record set up
self.clear_mox()
_run_ssh = self.mox.CreateMock(hpdriver.hpcommon.HP3PARCommon._run_ssh)
self.stubs.Set(hpdriver.hpcommon.HP3PARCommon, "_run_ssh", _run_ssh)
show_port_cmd = 'showport'
_run_ssh(show_port_cmd, False).AndReturn([pack(PORT_RET), ''])
show_port_i_cmd = 'showport -iscsi'
_run_ssh(show_port_i_cmd, False).AndReturn([pack(READY_ISCSI_PORT_RET),
''])
show_port_i_cmd = 'showport -iscsiname'
_run_ssh(show_port_i_cmd, False).AndReturn([pack(SHOW_PORT_ISCSI), ''])
self.mox.ReplayAll()
config = self.setup_configuration()
config.hp3par_iscsi_ips = ['10.10.220.253', '10.10.220.252']
self.setup_driver(config, set_up_fakes=False)
#record
self.clear_mox()
_run_ssh = self.mox.CreateMock(hpdriver.hpcommon.HP3PARCommon._run_ssh)
self.stubs.Set(hpdriver.hpcommon.HP3PARCommon, "_run_ssh", _run_ssh)
show_vlun_cmd = 'showvlun -a -host fakehost'
_run_ssh(show_vlun_cmd, False).AndReturn([pack(SHOW_VLUN), ''])
self.mox.ReplayAll()
ip = self.driver._get_iscsi_ip('fakehost')
self.assertEqual(ip, '10.10.220.253')
def test_get_iscsi_ip(self):
self.flags(lock_path=self.tempdir)
#record driver set up
self.clear_mox()
_run_ssh = self.mox.CreateMock(hpdriver.hpcommon.HP3PARCommon._run_ssh)
self.stubs.Set(hpdriver.hpcommon.HP3PARCommon, "_run_ssh", _run_ssh)
show_port_cmd = 'showport'
_run_ssh(show_port_cmd, False).AndReturn([pack(PORT_RET), ''])
show_port_i_cmd = 'showport -iscsi'
_run_ssh(show_port_i_cmd, False).AndReturn([pack(READY_ISCSI_PORT_RET),
''])
show_port_i_cmd = 'showport -iscsiname'
_run_ssh(show_port_i_cmd, False).AndReturn([pack(SHOW_PORT_ISCSI), ''])
#record
show_vlun_cmd = 'showvlun -a -host fakehost'
show_vlun_ret = 'no vluns listed\r\n'
_run_ssh(show_vlun_cmd, False).AndReturn([pack(show_vlun_ret), ''])
show_vlun_cmd = 'showvlun -a -showcols Port'
_run_ssh(show_vlun_cmd, False).AndReturn([pack(SHOW_VLUN_NONE), ''])
self.mox.ReplayAll()
config = self.setup_configuration()
config.iscsi_ip_address = '10.10.10.10'
config.hp3par_iscsi_ips = ['10.10.220.253', '10.10.220.252']
self.setup_driver(config, set_up_fakes=False)
ip = self.driver._get_iscsi_ip('fakehost')
self.assertEqual(ip, '10.10.220.252')
def test_invalid_iscsi_ip(self):
self.flags(lock_path=self.tempdir)
#record driver set up
self.clear_mox()
_run_ssh = self.mox.CreateMock(hpdriver.hpcommon.HP3PARCommon._run_ssh)
self.stubs.Set(hpdriver.hpcommon.HP3PARCommon, "_run_ssh", _run_ssh)
show_port_cmd = 'showport'
_run_ssh(show_port_cmd, False).AndReturn([pack(PORT_RET), ''])
show_port_i_cmd = 'showport -iscsi'
_run_ssh(show_port_i_cmd, False).AndReturn([pack(READY_ISCSI_PORT_RET),
''])
show_port_i_cmd = 'showport -iscsiname'
_run_ssh(show_port_i_cmd, False).AndReturn([pack(SHOW_PORT_ISCSI), ''])
config = self.setup_configuration()
config.hp3par_iscsi_ips = ['10.10.220.250', '10.10.220.251']
config.iscsi_ip_address = '10.10.10.10'
self.mox.ReplayAll()
# no valid ip addr should be configured.
self.assertRaises(exception.InvalidInput,
self.setup_driver,
config,
set_up_fakes=False)
def test_get_least_used_nsp(self):
self.flags(lock_path=self.tempdir)
#record
self.clear_mox()
_run_ssh = self.mox.CreateMock(hpdriver.hpcommon.HP3PARCommon._run_ssh)
self.stubs.Set(hpdriver.hpcommon.HP3PARCommon, "_run_ssh", _run_ssh)
show_vlun_cmd = 'showvlun -a -showcols Port'
_run_ssh(show_vlun_cmd, False).AndReturn([pack(SHOW_VLUN_NONE), ''])
_run_ssh(show_vlun_cmd, False).AndReturn([pack(SHOW_VLUN_NONE), ''])
_run_ssh(show_vlun_cmd, False).AndReturn([pack(SHOW_VLUN_NONE), ''])
self.mox.ReplayAll()
# in use count 11 12
nsp = self.driver._get_least_used_nsp(['0:2:1', '1:8:1'])
self.assertEqual(nsp, '0:2:1')
# in use count 11 10
nsp = self.driver._get_least_used_nsp(['0:2:1', '1:2:1'])
self.assertEqual(nsp, '1:2:1')
# in use count 0 10
nsp = self.driver._get_least_used_nsp(['1:1:1', '1:2:1'])
self.assertEqual(nsp, '1:1:1')
def pack(arg):
@@ -1106,3 +1221,40 @@ ISCSI_3PAR_RET = (
'Model : --\r\n'
'Contact : --\r\n'
'Comment : -- \r\n\r\n\r\n')
SHOW_PORT_ISCSI = (
'N:S:P,IPAddr,---------------iSCSI_Name----------------\r\n'
'0:8:1,1.1.1.2,iqn.2000-05.com.3pardata:21810002ac00383d\r\n'
'0:8:2,10.10.120.252,iqn.2000-05.com.3pardata:20820002ac00383d\r\n'
'1:8:1,10.10.220.253,iqn.2000-05.com.3pardata:21810002ac00383d\r\n'
'1:8:2,10.10.220.252,iqn.2000-05.com.3pardata:21820002ac00383d\r\n'
'-------------------------------------------------------------\r\n')
SHOW_VLUN = (
'Lun,VVName,HostName,---------Host_WWN/iSCSI_Name----------,Port,Type,'
'Status,ID\r\n'
'0,a,fakehost,iqn.1993-08.org.debian:01:3a779e4abc22,1:8:1,matched set,'
'active,0\r\n'
'------------------------------------------------------------------------'
'--------------\r\n')
SHOW_VLUN_NONE = (
'Port\r\n0:2:1\r\n0:2:1\r\n1:8:1\r\n1:8:1\r\n1:8:1\r\n1:2:1\r\n'
'1:2:1\r\n1:2:1\r\n1:2:1\r\n1:2:1\r\n1:2:1\r\n1:8:1\r\n1:8:1\r\n1:8:1\r\n'
'1:8:1\r\n1:8:1\r\n1:8:1\r\n0:2:1\r\n0:2:1\r\n0:2:1\r\n0:2:1\r\n0:2:1\r\n'
'0:2:1\r\n0:2:1\r\n1:8:1\r\n1:8:1\r\n0:2:1\r\n0:2:1\r\n1:2:1\r\n1:2:1\r\n'
'1:2:1\r\n1:2:1\r\n1:8:1\r\n-----')
READY_ISCSI_PORT_RET = (
'N:S:P,State,IPAddr,Netmask,Gateway,TPGT,MTU,Rate,DHCP,iSNS_Addr,'
'iSNS_Port\r\n'
'0:8:1,ready,10.10.120.253,255.255.224.0,0.0.0.0,81,1500,10Gbps,'
'0,0.0.0.0,3205\r\n'
'0:8:2,ready,10.10.120.252,255.255.224.0,0.0.0.0,82,1500,10Gbps,0,'
'0.0.0.0,3205\r\n'
'1:8:1,ready,10.10.220.253,255.255.224.0,0.0.0.0,181,1500,10Gbps,'
'0,0.0.0.0,3205\r\n'
'1:8:2,ready,10.10.220.252,255.255.224.0,0.0.0.0,182,1500,10Gbps,0,'
'0.0.0.0,3205\r\n'
'-------------------------------------------------------------------'
'----------------------\r\n')

View File

@@ -96,7 +96,10 @@ hp3par_opts = [
" and is deleted. This must be larger than expiration"),
cfg.BoolOpt('hp3par_debug',
default=False,
help="Enable HTTP debugging to 3PAR")
help="Enable HTTP debugging to 3PAR"),
cfg.ListOpt('hp3par_iscsi_ips',
default=[],
help="List of target iSCSI addresses to use.")
]
@@ -458,7 +461,7 @@ exit
# Protocol,Label,Partner,FailoverState
out = out[1:len(out) - 2]
ports = {'FC': [], 'iSCSI': []}
ports = {'FC': [], 'iSCSI': {}}
for line in out:
tmp = line.split(',')
@@ -477,9 +480,26 @@ exit
for line in out:
tmp = line.split(',')
if tmp:
if tmp and len(tmp) > 2:
if tmp[1] == 'ready':
ports['iSCSI'].append(tmp[2])
ports['iSCSI'][tmp[2]] = {}
# now get the nsp and iqn
result = self._cli_run('showport -iscsiname', None)
if result:
# first line is header
# nsp, ip,iqn
result = result[1:]
for line in result:
info = line.split(",")
if info and len(info) > 2:
if info[1] in ports['iSCSI']:
nsp = info[0]
ip_addr = info[1]
iqn = info[2]
ports['iSCSI'][ip_addr] = {'nsp': nsp,
'iqn': iqn
}
LOG.debug("PORTS = %s" % pprint.pformat(ports))
return ports

View File

@@ -30,6 +30,8 @@ Set the following in the cinder.conf file to enable the
volume_driver=cinder.volume.drivers.san.hp.hp_3par_iscsi.HP3PARISCSIDriver
"""
import sys
from hp3parclient import exceptions as hpexceptions
from cinder import exception
@@ -41,6 +43,7 @@ from cinder.volume.drivers.san import san
VERSION = 1.0
LOG = logging.getLogger(__name__)
DEFAULT_ISCSI_PORT = 3260
class HP3PARISCSIDriver(cinder.volume.driver.ISCSIDriver):
@@ -62,8 +65,7 @@ class HP3PARISCSIDriver(cinder.volume.driver.ISCSIDriver):
def _check_flags(self):
"""Sanity check to ensure we have required options set."""
required_flags = ['hp3par_api_url', 'hp3par_username',
'hp3par_password', 'iscsi_ip_address',
'iscsi_port', 'san_ip', 'san_login',
'hp3par_password', 'san_ip', 'san_login',
'san_password']
self.common.check_flags(self.configuration, required_flags)
@@ -80,10 +82,66 @@ class HP3PARISCSIDriver(cinder.volume.driver.ISCSIDriver):
def do_setup(self, context):
self.common = self._init_common()
self._check_flags()
self.common.do_setup(context)
# make sure ssh works.
self._iscsi_discover_target_iqn(self.configuration.iscsi_ip_address)
# map iscsi_ip-> ip_port
# -> iqn
# -> nsp
self.iscsi_ips = {}
temp_iscsi_ip = {}
# use the 3PAR ip_addr list for iSCSI configuration
if len(self.configuration.hp3par_iscsi_ips) > 0:
# add port values to ip_addr, if necessary
for ip_addr in self.configuration.hp3par_iscsi_ips:
ip = ip_addr.split(':')
if len(ip) == 1:
temp_iscsi_ip[ip_addr] = {'ip_port': DEFAULT_ISCSI_PORT}
elif len(ip) == 2:
temp_iscsi_ip[ip[0]] = {'ip_port': ip[1]}
else:
msg = _("Invalid IP address format '%s'") % ip_addr
LOG.warn(msg)
# add the single value iscsi_ip_address option to the IP dictionary.
# This way we can see if it's a valid iSCSI IP. If it's not valid,
# we won't use it and won't bother to report it, see below
if (self.configuration.iscsi_ip_address not in temp_iscsi_ip):
ip = self.configuration.iscsi_ip_address
ip_port = self.configuration.iscsi_port
temp_iscsi_ip[ip] = {'ip_port': ip_port}
# get all the valid iSCSI ports from 3PAR
# when found, add the valid iSCSI ip, ip port, iqn and nsp
# to the iSCSI IP dictionary
# ...this will also make sure ssh works.
iscsi_ports = self.common.get_ports()['iSCSI']
for (ip, iscsi_info) in iscsi_ports.iteritems():
if ip in temp_iscsi_ip:
ip_port = temp_iscsi_ip[ip]['ip_port']
self.iscsi_ips[ip] = {'ip_port': ip_port,
'nsp': iscsi_info['nsp'],
'iqn': iscsi_info['iqn']
}
del temp_iscsi_ip[ip]
# if the single value iscsi_ip_address option is still in the
# temp dictionary it's because it defaults to $my_ip which doesn't
# make sense in this context. So, if present, remove it and move on.
if (self.configuration.iscsi_ip_address in temp_iscsi_ip):
del temp_iscsi_ip[self.configuration.iscsi_ip_address]
# lets see if there are invalid iSCSI IPs left in the temp dict
if len(temp_iscsi_ip) > 0:
msg = _("Found invalid iSCSI IP address(s) in configuration "
"option(s) hp3par_iscsi_ips or iscsi_ip_address '%s.'") % \
(", ".join(temp_iscsi_ip))
LOG.warn(msg)
if not len(self.iscsi_ips) > 0:
msg = _('At least one valid iSCSI IP address must be set.')
raise exception.InvalidInput(reason=(msg))
self.common.do_setup(context)
def check_for_setup_error(self):
"""Returns an error if prerequisites aren't met."""
@@ -95,10 +153,7 @@ class HP3PARISCSIDriver(cinder.volume.driver.ISCSIDriver):
metadata = self.common.create_volume(volume)
self.common.client_logout()
return {'provider_location': "%s:%s" %
(self.configuration.iscsi_ip_address,
self.configuration.iscsi_port),
'metadata': metadata}
return {'metadata': metadata}
@utils.synchronized('3par', external=True)
def create_cloned_volume(self, volume, src_vref):
@@ -107,10 +162,7 @@ class HP3PARISCSIDriver(cinder.volume.driver.ISCSIDriver):
new_vol = self.common.create_cloned_volume(volume, src_vref)
self.common.client_logout()
return {'provider_location': "%s:%s" %
(self.configuration.iscsi_ip_address,
self.configuration.iscsi_port),
'metadata': new_vol}
return {'metadata': new_vol}
@utils.synchronized('3par', external=True)
def delete_volume(self, volume):
@@ -168,9 +220,6 @@ class HP3PARISCSIDriver(cinder.volume.driver.ISCSIDriver):
* create vlun on the 3par
"""
self.common.client_login()
# get the target_iqn on the 3par interface.
target_iqn = self._iscsi_discover_target_iqn(
self.configuration.iscsi_ip_address)
# we have to make sure we have a host
host = self._create_host(volume, connector)
@@ -179,11 +228,14 @@ class HP3PARISCSIDriver(cinder.volume.driver.ISCSIDriver):
vlun = self.common.create_vlun(volume, host)
self.common.client_logout()
iscsi_ip = self._get_iscsi_ip(host['name'])
iscsi_ip_port = self.iscsi_ips[iscsi_ip]['ip_port']
iscsi_target_iqn = self.iscsi_ips[iscsi_ip]['iqn']
info = {'driver_volume_type': 'iscsi',
'data': {'target_portal': "%s:%s" %
(self.configuration.iscsi_ip_address,
self.configuration.iscsi_port),
'target_iqn': target_iqn,
(iscsi_ip, iscsi_ip_port),
'target_iqn': iscsi_target_iqn,
'target_lun': vlun['lun'],
'target_discovered': True
}
@@ -199,21 +251,6 @@ class HP3PARISCSIDriver(cinder.volume.driver.ISCSIDriver):
connector['initiator'])
self.common.client_logout()
def _iscsi_discover_target_iqn(self, remote_ip):
result = self.common._cli_run('showport -ids', None)
iqn = None
if result:
# first line is header
result = result[1:]
for line in result:
info = line.split(",")
if info and len(info) > 2:
if info[1] == remote_ip:
iqn = info[2]
return iqn
def _create_3par_iscsi_host(self, hostname, iscsi_iqn, domain, persona_id):
"""Create a 3PAR host.
@@ -268,3 +305,81 @@ class HP3PARISCSIDriver(cinder.volume.driver.ISCSIDriver):
@utils.synchronized('3par', external=True)
def remove_export(self, context, volume):
pass
def _get_iscsi_ip(self, hostname):
"""Get an iSCSI IP address to use.
Steps to determine which IP address to use.
* If only one IP address, return it
* If there is an active vlun, return the IP associated with it
* Return IP with fewest active vluns
"""
if len(self.iscsi_ips) == 1:
return self.iscsi_ips.keys()[0]
# if we currently have an active port, use it
nsp = self._get_active_nsp(hostname)
if nsp is None:
# no active vlun, find least busy port
nsp = self._get_least_used_nsp(self._get_iscsi_nsps())
if nsp is None:
msg = _("Least busy iSCSI port not found, "
"using first iSCSI port in list.")
LOG.warn(msg)
return self.iscsi_ips.keys()[0]
return self._get_ip_using_nsp(nsp)
def _get_iscsi_nsps(self):
"""Return the list of candidate nsps."""
nsps = []
for value in self.iscsi_ips.values():
nsps.append(value['nsp'])
return nsps
def _get_ip_using_nsp(self, nsp):
"""Return IP assiciated with given nsp."""
for (key, value) in self.iscsi_ips.items():
if value['nsp'] == nsp:
return key
def _get_active_nsp(self, hostname):
"""Return the active nsp, if one exists, for the given host."""
result = self.common._cli_run('showvlun -a -host %s' % hostname, None)
if result:
# first line is header
result = result[1:]
for line in result:
info = line.split(",")
if info and len(info) > 4:
return info[4]
def _get_least_used_nsp(self, nspss):
""""Return the nsp that has the fewest active vluns."""
# return only the nsp (node:server:port)
result = self.common._cli_run('showvlun -a -showcols Port', None)
# count the number of nsps (there is 1 for each active vlun)
nsp_counts = {}
for nsp in nspss:
# initialize counts to zero
nsp_counts[nsp] = 0
current_least_used_nsp = None
if result:
# first line is header
result = result[1:]
for line in result:
nsp = line.strip()
if nsp in nsp_counts:
nsp_counts[nsp] = nsp_counts[nsp] + 1
# identify key (nsp) of least used nsp
current_smallest_count = sys.maxint
for (nsp, count) in nsp_counts.iteritems():
if count < current_smallest_count:
current_least_used_nsp = nsp
current_smallest_count = count
return current_least_used_nsp

View File

@@ -1176,6 +1176,9 @@
# Enable HTTP debugging to 3PAR (boolean value)
#hp3par_debug=false
#List of target iSCSI addresses to use (list value)
#hp3par_iscsi_ips=
#
# Options defined in cinder.volume.drivers.san.san