]> review.fuel-infra Code Review - openstack-build/cinder-build.git/commitdiff
Modified 3PAR drives to support 3parclient 2.0.0
authorJim Branen <james.branen@hp.com>
Mon, 19 Aug 2013 23:36:16 +0000 (16:36 -0700)
committerJim Branen <james.branen@hp.com>
Tue, 27 Aug 2013 21:55:34 +0000 (14:55 -0700)
Replaced ssh calls to remove host, show host,
show port, show vlun and create vlun with the
new hp3parclient calls.

Change-Id: I917b4263389331597103d2fe6c9a73994165051b
Fixes: bug #1211994
cinder/tests/test_hp3par.py
cinder/volume/drivers/san/hp/hp_3par_common.py
cinder/volume/drivers/san/hp/hp_3par_fc.py
cinder/volume/drivers/san/hp/hp_3par_iscsi.py
test-requirements.txt

index 6778a326b158663c7659b0edc93e493849c3d3ab..011c471722eb089d1efed08219fa43c84416bfd1 100644 (file)
@@ -42,6 +42,27 @@ CLI_CR = '\r\n'
 
 class FakeHP3ParClient(object):
 
+    PORT_MODE_TARGET = 2
+    PORT_MODE_INITIATOR = 3
+    PORT_MODE_PEER = 4
+
+    PORT_TYPE_HOST = 1
+    PORT_TYPE_DISK = 2
+    PORT_TYPE_FREE = 3
+    PORT_TYPE_RCIP = 6
+    PORT_TYPE_ISCSI = 7
+
+    PORT_PROTO_FC = 1
+    PORT_PROTO_ISCSI = 2
+    PORT_PROTO_IP = 4
+
+    PORT_STATE_READY = 4
+    PORT_STATE_SYNC = 5
+    PORT_STATE_OFFLINE = 10
+
+    HOST_EDIT_ADD = 1
+    HOST_EDIT_REMOVE = 2
+
     api_url = None
     debug = False
 
@@ -290,6 +311,15 @@ class FakeHP3ParClient(object):
                'desc': "VLUN '%s' was not found" % volumeName}
         raise hpexceptions.HTTPNotFound(msg)
 
+    def getHost(self, hostname):
+        return None
+
+    def modifyHost(self, hostname, options):
+        return None
+
+    def getPorts(self):
+        return None
+
 
 class HP3PARBaseDriver():
 
@@ -305,14 +335,25 @@ class HP3PARBaseDriver():
     PROJECT_ID = 'fac88235b9d64685a3530f73e490348f'
     VOLUME_ID_SNAP = '761fc5e5-5191-4ec7-aeba-33e36de44156'
     FAKE_DESC = 'test description name'
-    FAKE_FC_PORTS = ['0987654321234', '123456789000987']
+    FAKE_FC_PORTS = [{'portPos': {'node': 7, 'slot': 1, 'cardPort': 1},
+                      'portWWN': '0987654321234',
+                      'protocol': 1,
+                      'mode': 2,
+                      'linkState': 4},
+                     {'portPos': {'node': 6, 'slot': 1, 'cardPort': 1},
+                      'portWWN': '123456789000987',
+                      'protocol': 1,
+                      'mode': 2,
+                      'linkState': 4}]
     QOS = {'qos:maxIOPS': '1000', 'qos:maxBWS': '50'}
     VVS_NAME = "myvvs"
-    FAKE_ISCSI_PORTS = {'1.1.1.2': {'nsp': '8:1:1',
-                                    'iqn': ('iqn.2000-05.com.3pardata:'
-                                            '21810002ac00383d'),
-                                    'ip_port': '3262'}}
-
+    FAKE_ISCSI_PORT = {'portPos': {'node': 8, 'slot': 1, 'cardPort': 1},
+                       'protocol': 2,
+                       'mode': 2,
+                       'IPAddr': '1.1.1.2',
+                       'iSCSIName': ('iqn.2000-05.com.3pardata:'
+                                     '21810002ac00383d'),
+                       'linkState': 4}
     volume = {'name': VOLUME_NAME,
               'id': VOLUME_ID,
               'display_name': 'Foo Volume',
@@ -440,7 +481,9 @@ class HP3PARBaseDriver():
         self.driver.common.client.createVLUN(volume, 19, hostname)
 
     def fake_get_ports(self):
-        return {'FC': self.FAKE_FC_PORTS, 'iSCSI': self.FAKE_ISCSI_PORTS}
+        ports = self.FAKE_FC_PORTS
+        ports.append(self.FAKE_ISCSI_PORT)
+        return {'members': ports}
 
     def fake_get_volume_type(self, type_id):
         return self.volume_type
@@ -725,15 +768,18 @@ class TestHP3PARFCDriver(HP3PARBaseDriver, test.TestCase):
         _run_ssh = self.mox.CreateMock(hpdriver.hpcommon.HP3PARCommon._run_ssh)
         self.stubs.Set(hpdriver.hpcommon.HP3PARCommon, "_run_ssh", _run_ssh)
 
-        show_host_cmd = ['showhost', '-verbose', 'fakehost']
-        _run_ssh(show_host_cmd, False).AndReturn([pack('no hosts listed'), ''])
+        getHost = self.mox.CreateMock(FakeHP3ParClient.getHost)
+        self.stubs.Set(FakeHP3ParClient, "getHost", getHost)
+
+        ex = hpexceptions.HTTPNotFound('Host not found.')
+        getHost('fakehost').AndRaise(ex)
 
         create_host_cmd = (['createhost', '-persona', '1', '-domain',
                             ('OpenStack',), 'fakehost', '123456789012345',
                             '123456789054321'])
         _run_ssh(create_host_cmd, False).AndReturn([CLI_CR, ''])
 
-        _run_ssh(show_host_cmd, False).AndReturn([pack(FC_HOST_RET), ''])
+        getHost('fakehost').AndReturn({'name': self.FAKE_HOST})
         self.mox.ReplayAll()
 
         host = self.driver._create_host(self.volume, self.connector)
@@ -751,8 +797,11 @@ class TestHP3PARFCDriver(HP3PARBaseDriver, test.TestCase):
         _run_ssh = self.mox.CreateMock(hpdriver.hpcommon.HP3PARCommon._run_ssh)
         self.stubs.Set(hpdriver.hpcommon.HP3PARCommon, "_run_ssh", _run_ssh)
 
-        show_host_cmd = ['showhost', '-verbose', 'fakehost']
-        _run_ssh(show_host_cmd, False).AndReturn([pack('no hosts listed'), ''])
+        getHost = self.mox.CreateMock(FakeHP3ParClient.getHost)
+        self.stubs.Set(FakeHP3ParClient, "getHost", getHost)
+
+        not_found_ex = hpexceptions.HTTPNotFound('Host not found.')
+        getHost('fakehost').AndRaise(not_found_ex)
 
         create_host_cmd = (['createhost', '-persona', '1', '-domain',
                             ('OpenStack',), 'fakehost', '123456789012345',
@@ -761,8 +810,7 @@ class TestHP3PARFCDriver(HP3PARBaseDriver, test.TestCase):
                                'already used by host fakehost.foo (19)')
         _run_ssh(create_host_cmd, False).AndReturn([create_host_ret, ''])
 
-        show_3par_cmd = ['showhost', '-verbose', 'fakehost.foo']
-        _run_ssh(show_3par_cmd, False).AndReturn([pack(FC_SHOWHOST_RET), ''])
+        getHost('fakehost.foo').AndReturn({'name': 'fakehost.foo'})
         self.mox.ReplayAll()
 
         host = self.driver._create_host(self.volume, self.connector)
@@ -778,22 +826,30 @@ class TestHP3PARFCDriver(HP3PARBaseDriver, test.TestCase):
                        self.fake_get_cpg)
         self.stubs.Set(hpdriver.hpcommon.HP3PARCommon, "get_domain",
                        self.fake_get_domain)
-        _run_ssh = self.mox.CreateMock(hpdriver.hpcommon.HP3PARCommon._run_ssh)
-        self.stubs.Set(hpdriver.hpcommon.HP3PARCommon, "_run_ssh", _run_ssh)
 
-        show_host_cmd = ['showhost', '-verbose', 'fakehost']
-        _run_ssh(show_host_cmd, False).AndReturn([pack(NO_FC_HOST_RET), ''])
+        getHost = self.mox.CreateMock(FakeHP3ParClient.getHost)
+        self.stubs.Set(FakeHP3ParClient, "getHost", getHost)
 
-        create_host_cmd = ['createhost', '-add', 'fakehost', '123456789012345',
-                           '123456789054321']
-        _run_ssh(create_host_cmd, False).AndReturn([CLI_CR, ''])
+        modifyHost = self.mox.CreateMock(FakeHP3ParClient.modifyHost)
+        self.stubs.Set(FakeHP3ParClient, "modifyHost", modifyHost)
+
+        getHost('fakehost').AndReturn(({'name': self.FAKE_HOST,
+                                        'FCPaths': []}))
+
+        modifyHost('fakehost', {'FCWWNs':
+                                ['123456789012345', '123456789054321'],
+                                'pathOperation': 1})
+
+        getHost('fakehost').AndReturn({'name': self.FAKE_HOST,
+                                       'FCPaths': [{'WWN': '123456789012345'},
+                                                   {'WWN': '123456789054321'}]}
+                                      )
 
-        show_host_cmd = ['showhost', '-verbose', 'fakehost']
-        _run_ssh(show_host_cmd, False).AndReturn([pack(FC_HOST_RET), ''])
         self.mox.ReplayAll()
 
         host = self.driver._create_host(self.volume, self.connector)
         self.assertEqual(host['name'], self.FAKE_HOST)
+        self.assertEqual(len(host['FCPaths']), 2)
 
 
 class TestHP3PARISCSIDriver(HP3PARBaseDriver, test.TestCase):
@@ -920,15 +976,18 @@ class TestHP3PARISCSIDriver(HP3PARBaseDriver, test.TestCase):
         _run_ssh = self.mox.CreateMock(hpdriver.hpcommon.HP3PARCommon._run_ssh)
         self.stubs.Set(hpdriver.hpcommon.HP3PARCommon, "_run_ssh", _run_ssh)
 
-        show_host_cmd = ['showhost', '-verbose', 'fakehost']
-        _run_ssh(show_host_cmd, False).AndReturn([pack('no hosts listed'), ''])
+        getHost = self.mox.CreateMock(FakeHP3ParClient.getHost)
+        self.stubs.Set(FakeHP3ParClient, "getHost", getHost)
+
+        not_found_ex = hpexceptions.HTTPNotFound('Host not found.')
+        getHost('fakehost').AndRaise(not_found_ex)
 
         create_host_cmd = (['createhost', '-iscsi', '-persona', '1', '-domain',
                             ('OpenStack',), 'fakehost',
                             'iqn.1993-08.org.debian:01:222'])
         _run_ssh(create_host_cmd, False).AndReturn([CLI_CR, ''])
 
-        _run_ssh(show_host_cmd, False).AndReturn([pack(ISCSI_HOST_RET), ''])
+        getHost('fakehost').AndReturn({'name': self.FAKE_HOST})
         self.mox.ReplayAll()
 
         host = self.driver._create_host(self.volume, self.connector)
@@ -946,8 +1005,11 @@ class TestHP3PARISCSIDriver(HP3PARBaseDriver, test.TestCase):
         _run_ssh = self.mox.CreateMock(hpdriver.hpcommon.HP3PARCommon._run_ssh)
         self.stubs.Set(hpdriver.hpcommon.HP3PARCommon, "_run_ssh", _run_ssh)
 
-        show_host_cmd = ['showhost', '-verbose', 'fakehost']
-        _run_ssh(show_host_cmd, False).AndReturn([pack('no hosts listed'), ''])
+        getHost = self.mox.CreateMock(FakeHP3ParClient.getHost)
+        self.stubs.Set(FakeHP3ParClient, "getHost", getHost)
+
+        not_found_ex = hpexceptions.HTTPNotFound('Host not found.')
+        getHost('fakehost').AndRaise(not_found_ex)
 
         create_host_cmd = (['createhost', '-iscsi', '-persona', '1', '-domain',
                            ('OpenStack',), 'fakehost',
@@ -955,8 +1017,7 @@ class TestHP3PARISCSIDriver(HP3PARBaseDriver, test.TestCase):
         in_use_ret = pack('\r\nalready used by host fakehost.foo ')
         _run_ssh(create_host_cmd, False).AndReturn([in_use_ret, ''])
 
-        show_3par_cmd = ['showhost', '-verbose', 'fakehost.foo']
-        _run_ssh(show_3par_cmd, False).AndReturn([pack(ISCSI_3PAR_RET), ''])
+        getHost('fakehost.foo').AndReturn({'name': 'fakehost.foo'})
         self.mox.ReplayAll()
 
         host = self.driver._create_host(self.volume, self.connector)
@@ -972,77 +1033,63 @@ class TestHP3PARISCSIDriver(HP3PARBaseDriver, test.TestCase):
                        self.fake_get_cpg)
         self.stubs.Set(hpdriver.hpcommon.HP3PARCommon, "get_domain",
                        self.fake_get_domain)
-        _run_ssh = self.mox.CreateMock(hpdriver.hpcommon.HP3PARCommon._run_ssh)
-        self.stubs.Set(hpdriver.hpcommon.HP3PARCommon, "_run_ssh", _run_ssh)
 
-        show_host_cmd = ['showhost', '-verbose', 'fakehost']
-        _run_ssh(show_host_cmd, False).AndReturn([pack(ISCSI_NO_HOST_RET), ''])
+        getHost = self.mox.CreateMock(FakeHP3ParClient.getHost)
+        self.stubs.Set(FakeHP3ParClient, "getHost", getHost)
 
-        create_host_cmd = ['createhost', '-iscsi', '-add', 'fakehost',
-                           'iqn.1993-08.org.debian:01:222']
-        _run_ssh(create_host_cmd, False).AndReturn([CLI_CR, ''])
-        _run_ssh(show_host_cmd, False).AndReturn([pack(ISCSI_HOST_RET), ''])
+        modifyHost = self.mox.CreateMock(FakeHP3ParClient.modifyHost)
+        self.stubs.Set(FakeHP3ParClient, "modifyHost", modifyHost)
+
+        getHost('fakehost').AndReturn(({'name': self.FAKE_HOST,
+                                        'iSCSIPaths': []}))
+
+        modifyHost('fakehost', {'iSCSINames':
+                                ['iqn.1993-08.org.debian:01:222'],
+                                'pathOperation': 1})
+
+        ret_value = {'name': self.FAKE_HOST,
+                     'iSCSIPaths': [{'name': 'iqn.1993-08.org.debian:01:222'}]
+                     }
+        getHost('fakehost').AndReturn(ret_value)
         self.mox.ReplayAll()
 
         host = self.driver._create_host(self.volume, self.connector)
         self.assertEqual(host['name'], self.FAKE_HOST)
+        self.assertEqual(len(host['iSCSIPaths']), 1)
 
     def test_get_ports(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_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),
-                                                    ''])
+        getPorts = self.mox.CreateMock(FakeHP3ParClient.getPorts)
+        self.stubs.Set(FakeHP3ParClient, "getPorts", getPorts)
 
-        show_port_i_cmd = ['showport', '-iscsiname']
-        _run_ssh(show_port_i_cmd, False).AndReturn([pack(SHOW_PORT_ISCSI),
-                                                    ''])
+        getPorts().AndReturn(PORTS1_RET)
         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')
+        ports = self.driver.common.get_ports()['members']
+        self.assertTrue(len(ports) == 3)
 
     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),
-                                                    ''])
+        getPorts = self.mox.CreateMock(FakeHP3ParClient.getPorts)
+        self.stubs.Set(FakeHP3ParClient, "getPorts", getPorts)
 
-        show_port_i_cmd = ['showport', '-iscsiname']
-        _run_ssh(show_port_i_cmd, False).AndReturn([pack(SHOW_PORT_ISCSI), ''])
+        getVLUNs = self.mox.CreateMock(FakeHP3ParClient.getVLUNs)
+        self.stubs.Set(FakeHP3ParClient, "getVLUNs", getVLUNs)
 
+        getPorts().AndReturn(PORTS_RET)
+        getVLUNs().AndReturn(VLUNS2_RET)
         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')
@@ -1053,26 +1100,14 @@ class TestHP3PARISCSIDriver(HP3PARBaseDriver, test.TestCase):
 
         #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),
-                                                    ''])
+        getPorts = self.mox.CreateMock(FakeHP3ParClient.getPorts)
+        self.stubs.Set(FakeHP3ParClient, "getPorts", getPorts)
 
-        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), ''])
+        getVLUNs = self.mox.CreateMock(FakeHP3ParClient.getVLUNs)
+        self.stubs.Set(FakeHP3ParClient, "getVLUNs", getVLUNs)
 
+        getPorts().AndReturn(PORTS_RET)
+        getVLUNs().AndReturn(VLUNS1_RET)
         self.mox.ReplayAll()
 
         config = self.setup_configuration()
@@ -1088,18 +1123,10 @@ class TestHP3PARISCSIDriver(HP3PARBaseDriver, test.TestCase):
 
         #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),
-                                                    ''])
+        getPorts = self.mox.CreateMock(FakeHP3ParClient.getPorts)
+        self.stubs.Set(FakeHP3ParClient, "getPorts", getPorts)
 
-        show_port_i_cmd = ['showport', '-iscsiname']
-        _run_ssh(show_port_i_cmd, False).AndReturn([pack(SHOW_PORT_ISCSI), ''])
+        getPorts().AndReturn(PORTS_RET)
 
         config = self.setup_configuration()
         config.hp3par_iscsi_ips = ['10.10.220.250', '10.10.220.251']
@@ -1117,25 +1144,30 @@ class TestHP3PARISCSIDriver(HP3PARBaseDriver, test.TestCase):
 
         #record
         self.clear_mox()
-        _run_ssh = self.mox.CreateMock(hpdriver.hpcommon.HP3PARCommon._run_ssh)
-        self.stubs.Set(hpdriver.hpcommon.HP3PARCommon, "_run_ssh", _run_ssh)
+        getVLUNs = self.mox.CreateMock(FakeHP3ParClient.getVLUNs)
+        self.stubs.Set(FakeHP3ParClient, "getVLUNs", getVLUNs)
 
-        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), ''])
+        getVLUNs().AndReturn(VLUNS3_RET)
+        getVLUNs().AndReturn(VLUNS4_RET)
+        getVLUNs().AndReturn(VLUNS4_RET)
 
         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'])
+        # in use count
+        vluns = self.driver.common.client.getVLUNs()
+        nsp = self.driver._get_least_used_nsp(vluns['members'],
+                                              ['0:2:1', '1:8:1'])
+        self.assertEqual(nsp, '1:8:1')
+
+        # in use count
+        vluns = self.driver.common.client.getVLUNs()
+        nsp = self.driver._get_least_used_nsp(vluns['members'],
+                                              ['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'])
+        # in use count
+        vluns = self.driver.common.client.getVLUNs()
+        nsp = self.driver._get_least_used_nsp(vluns['members'],
+                                              ['1:1:1', '1:2:1'])
         self.assertEqual(nsp, '1:1:1')
 
 
@@ -1144,209 +1176,108 @@ def pack(arg):
     footer = '\r\n\r\n\r\n'
     return header + arg + footer
 
-FC_HOST_RET = (
-    'Id,Name,Persona,-WWN/iSCSI_Name-,Port,IP_addr\r\n'
-    '75,fakehost,Generic,50014380242B8B4C,0:2:1,n/a\r\n'
-    '75,fakehost,Generic,50014380242B8B4E,---,n/a\r\n'
-    '75,fakehost,Generic,1000843497F90711,0:2:1,n/a \r\n'
-    '75,fakehost,Generic,1000843497F90715,1:2:1,n/a\r\n'
-    '\r\n'
-    'Id,Name,-Initiator_CHAP_Name-,-Target_CHAP_Name-\r\n'
-    '75,fakehost,--,--\r\n'
-    '\r\n'
-    '---------- Host fakehost ----------\r\n'
-    'Name       : fakehost\r\n'
-    'Domain     : FAKE_TEST\r\n'
-    'Id         : 75\r\n'
-    'Location   : --\r\n'
-    'IP Address : --\r\n'
-    'OS         : --\r\n'
-    'Model      : --\r\n'
-    'Contact    : --\r\n'
-    'Comment    : --  \r\n\r\n\r\n')
-
-FC_SHOWHOST_RET = (
-    'Id,Name,Persona,-WWN/iSCSI_Name-,Port,IP_addr\r\n'
-    '75,fakehost.foo,Generic,50014380242B8B4C,0:2:1,n/a\r\n'
-    '75,fakehost.foo,Generic,50014380242B8B4E,---,n/a\r\n'
-    '75,fakehost.foo,Generic,1000843497F90711,0:2:1,n/a \r\n'
-    '75,fakehost.foo,Generic,1000843497F90715,1:2:1,n/a\r\n'
-    '\r\n'
-    'Id,Name,-Initiator_CHAP_Name-,-Target_CHAP_Name-\r\n'
-    '75,fakehost.foo,--,--\r\n'
-    '\r\n'
-    '---------- Host fakehost.foo ----------\r\n'
-    'Name       : fakehost.foo\r\n'
-    'Domain     : FAKE_TEST\r\n'
-    'Id         : 75\r\n'
-    'Location   : --\r\n'
-    'IP Address : --\r\n'
-    'OS         : --\r\n'
-    'Model      : --\r\n'
-    'Contact    : --\r\n'
-    'Comment    : --  \r\n\r\n\r\n')
-
-NO_FC_HOST_RET = (
-    'Id,Name,Persona,-WWN/iSCSI_Name-,Port,IP_addr\r\n'
-    '\r\n'
-    'Id,Name,-Initiator_CHAP_Name-,-Target_CHAP_Name-\r\n'
-    '75,fakehost,--,--\r\n'
-    '\r\n'
-    '---------- Host fakehost ----------\r\n'
-    'Name       : fakehost\r\n'
-    'Domain     : FAKE_TEST\r\n'
-    'Id         : 75\r\n'
-    'Location   : --\r\n'
-    'IP Address : --\r\n'
-    'OS         : --\r\n'
-    'Model      : --\r\n'
-    'Contact    : --\r\n'
-    'Comment    : --  \r\n\r\n\r\n')
-
-ISCSI_HOST_RET = (
-    'Id,Name,Persona,-WWN/iSCSI_Name-,Port,IP_addr\r\n'
-    '75,fakehost,Generic,iqn.1993-08.org.debian:01:222,---,10.10.222.12\r\n'
-    '\r\n'
-    'Id,Name,-Initiator_CHAP_Name-,-Target_CHAP_Name-\r\n'
-    '75,fakehost,--,--\r\n'
-    '\r\n'
-    '---------- Host fakehost ----------\r\n'
-    'Name       : fakehost\r\n'
-    'Domain     : FAKE_TEST\r\n'
-    'Id         : 75\r\n'
-    'Location   : --\r\n'
-    'IP Address : --\r\n'
-    'OS         : --\r\n'
-    'Model      : --\r\n'
-    'Contact    : --\r\n'
-    'Comment    : --  \r\n\r\n\r\n')
-
-ISCSI_NO_HOST_RET = (
-    'Id,Name,Persona,-WWN/iSCSI_Name-,Port,IP_addr\r\n'
-    '\r\n'
-    'Id,Name,-Initiator_CHAP_Name-,-Target_CHAP_Name-\r\n'
-    '75,fakehost,--,--\r\n'
-    '\r\n'
-    '---------- Host fakehost ----------\r\n'
-    'Name       : fakehost\r\n'
-    'Domain     : FAKE_TEST\r\n'
-    'Id         : 75\r\n'
-    'Location   : --\r\n'
-    'IP Address : --\r\n'
-    'OS         : --\r\n'
-    'Model      : --\r\n'
-    'Contact    : --\r\n'
-    'Comment    : --  \r\n\r\n\r\n')
-
-ISCSI_PORT_IDS_RET = (
-    'N:S:P,-Node_WWN/IPAddr-,-----------Port_WWN/iSCSI_Name-----------\r\n'
-    '0:2:1,28210002AC00383D,20210002AC00383D\r\n'
-    '0:2:2,2FF70002AC00383D,20220002AC00383D\r\n'
-    '0:2:3,2FF70002AC00383D,20230002AC00383D\r\n'
-    '0:2:4,2FF70002AC00383D,20240002AC00383D\r\n'
-    '0:5:1,2FF70002AC00383D,20510002AC00383D\r\n'
-    '0:5:2,2FF70002AC00383D,20520002AC00383D\r\n'
-    '0:5:3,2FF70002AC00383D,20530002AC00383D\r\n'
-    '0:5:4,2FF70202AC00383D,20540202AC00383D\r\n'
-    '0:6:4,2FF70002AC00383D,20640002AC00383D\r\n'
-    '0:8:1,10.10.120.253,iqn.2000-05.com.3pardata:21810002ac00383d\r\n'
-    '0:8:2,0.0.0.0,iqn.2000-05.com.3pardata:20820002ac00383d\r\n'
-    '1:2:1,29210002AC00383D,21210002AC00383D\r\n'
-    '1:2:2,2FF70002AC00383D,21220002AC00383D\r\n'
-    '-----------------------------------------------------------------\r\n')
-
-VOLUME_STATE_RET = (
-    'Id,Name,Prov,Type,State,-Detailed_State-\r\n'
-    '410,volume-d03338a9-9115-48a3-8dfc-35cdfcdc15a7,snp,vcopy,normal,'
-    'normal\r\n'
-    '-----------------------------------------------------------------\r\n')
-
-PORT_RET = (
-    'N:S:P,Mode,State,----Node_WWN----,-Port_WWN/HW_Addr-,Type,Protocol,'
-    'Label,Partner,FailoverState\r\n'
-    '0:2:1,target,ready,28210002AC00383D,20210002AC00383D,host,FC,'
-    '-,1:2:1,none\r\n'
-    '0:2:2,initiator,loss_sync,2FF70002AC00383D,20220002AC00383D,free,FC,'
-    '-,-,-\r\n'
-    '0:2:3,initiator,loss_sync,2FF70002AC00383D,20230002AC00383D,free,FC,'
-    '-,-,-\r\n'
-    '0:2:4,initiator,loss_sync,2FF70002AC00383D,20240002AC00383D,free,FC,'
-    '-,-,-\r\n'
-    '0:5:1,initiator,loss_sync,2FF70002AC00383D,20510002AC00383D,free,FC,'
-    '-,-,-\r\n'
-    '0:5:2,initiator,loss_sync,2FF70002AC00383D,20520002AC00383D,free,FC,'
-    '-,-,-\r\n'
-    '0:5:3,initiator,loss_sync,2FF70002AC00383D,20530002AC00383D,free,FC,'
-    '-,-,-\r\n'
-    '0:5:4,initiator,ready,2FF70202AC00383D,20540202AC00383D,host,FC,'
-    '-,1:5:4,active\r\n'
-    '0:6:1,initiator,ready,2FF70002AC00383D,20610002AC00383D,disk,FC,'
-    '-,-,-\r\n'
-    '0:6:2,initiator,ready,2FF70002AC00383D,20620002AC00383D,disk,FC,'
-    '-,-,-\r\n')
-
-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,loss_sync,0.0.0.0,0.0.0.0,0.0.0.0,82,1500,n/a,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,loss_sync,0.0.0.0,0.0.0.0,0.0.0.0,182,1500,n/a,0,0.0.0.0,3205\r\n')
-
-ISCSI_3PAR_RET = (
-    'Id,Name,Persona,-WWN/iSCSI_Name-,Port,IP_addr\r\n'
-    '75,fakehost.foo,Generic,iqn.1993-08.org.debian:01:222,---,'
-    '10.10.222.12\r\n'
-    '\r\n'
-    'Id,Name,-Initiator_CHAP_Name-,-Target_CHAP_Name-\r\n'
-    '75,fakehost.foo,--,--\r\n'
-    '\r\n'
-    '---------- Host fakehost.foo ----------\r\n'
-    'Name       : fakehost.foo\r\n'
-    'Domain     : FAKE_TEST\r\n'
-    'Id         : 75\r\n'
-    'Location   : --\r\n'
-    'IP Address : --\r\n'
-    'OS         : --\r\n'
-    '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')
+PORTS_RET = ({'members':
+              [{'portPos': {'node': 1, 'slot': 8, 'cardPort': 2},
+                'protocol': 2,
+                'IPAddr': '10.10.220.252',
+                'linkState': 4,
+                'device': [],
+                'iSCSIName': 'iqn.2000-05.com.3pardata:21820002ac00383d',
+                'mode': 2,
+                'HWAddr': '2C27D75375D2',
+                'type': 8},
+               {'portPos': {'node': 1, 'slot': 8, 'cardPort': 1},
+                'protocol': 2,
+                'IPAddr': '10.10.220.253',
+                'linkState': 4,
+                'device': [],
+                'iSCSIName': 'iqn.2000-05.com.3pardata:21810002ac00383d',
+                'mode': 2,
+                'HWAddr': '2C27D75375D6',
+                'type': 8}]})
+
+PORTS1_RET = ({'members':
+               [{'portPos': {'node': 0, 'slot': 8, 'cardPort': 2},
+                 'protocol': 2,
+                 'IPAddr': '10.10.120.252',
+                 'linkState': 4,
+                 'device': [],
+                 'iSCSIName': 'iqn.2000-05.com.3pardata:21820002ac00383d',
+                 'mode': 2,
+                 'HWAddr': '2C27D75375D2',
+                 'type': 8},
+                {'portPos': {'node': 1, 'slot': 8, 'cardPort': 1},
+                 'protocol': 2,
+                 'IPAddr': '10.10.220.253',
+                 'linkState': 4,
+                 'device': [],
+                 'iSCSIName': 'iqn.2000-05.com.3pardata:21810002ac00383d',
+                 'mode': 2,
+                 'HWAddr': '2C27D75375D6',
+                 'type': 8},
+                {'portWWN': '20210002AC00383D',
+                 'protocol': 1,
+                 'linkState': 4,
+                 'mode': 2,
+                 'device': ['cage2'],
+                 'nodeWWN': '20210002AC00383D',
+                 'type': 2,
+                 'portPos': {'node': 0, 'slot': 6, 'cardPort': 3}}]})
+
+VLUNS1_RET = ({'members':
+               [{'portPos': {'node': 1, 'slot': 8, 'cardPort': 2},
+                 'hostname': 'foo', 'active': True},
+                {'portPos': {'node': 1, 'slot': 8, 'cardPort': 1},
+                 'hostname': 'bar', 'active': True},
+                {'portPos': {'node': 1, 'slot': 8, 'cardPort': 1},
+                 'hostname': 'bar', 'active': True},
+                {'portPos': {'node': 1, 'slot': 8, 'cardPort': 1},
+                 'hostname': 'bar', 'active': True}]})
+
+VLUNS2_RET = ({'members':
+               [{'portPos': {'node': 1, 'slot': 8, 'cardPort': 2},
+                 'hostname': 'bar', 'active': True},
+                {'portPos': {'node': 1, 'slot': 8, 'cardPort': 1},
+                 'hostname': 'fakehost', 'active': True},
+                {'portPos': {'node': 1, 'slot': 8, 'cardPort': 2},
+                 'hostname': 'bar', 'active': True},
+                {'portPos': {'node': 1, 'slot': 8, 'cardPort': 2},
+                 'hostname': 'bar', 'active': True}]})
+
+VLUNS3_RET = ({'members':
+               [{'portPos': {'node': 1, 'slot': 8, 'cardPort': 2},
+                 'active': True},
+                {'portPos': {'node': 1, 'slot': 8, 'cardPort': 1},
+                 'active': True},
+                {'portPos': {'node': 1, 'slot': 8, 'cardPort': 2},
+                 'active': True},
+                {'portPos': {'node': 0, 'slot': 2, 'cardPort': 2},
+                 'active': True},
+                {'portPos': {'node': 0, 'slot': 2, 'cardPort': 1},
+                 'active': True},
+                {'portPos': {'node': 0, 'slot': 2, 'cardPort': 1},
+                 'active': True},
+                {'portPos': {'node': 0, 'slot': 2, 'cardPort': 1},
+                 'active': True},
+                {'portPos': {'node': 0, 'slot': 2, 'cardPort': 1},
+                 'active': True}]})
+
+VLUNS4_RET = ({'members':
+               [{'portPos': {'node': 1, 'slot': 2, 'cardPort': 1},
+                 'active': True},
+                {'portPos': {'node': 1, 'slot': 2, 'cardPort': 1},
+                 'active': True},
+                {'portPos': {'node': 1, 'slot': 2, 'cardPort': 1},
+                 'active': True},
+                {'portPos': {'node': 1, 'slot': 2, 'cardPort': 1},
+                 'active': True},
+                {'portPos': {'node': 0, 'slot': 2, 'cardPort': 1},
+                 'active': True},
+                {'portPos': {'node': 0, 'slot': 2, 'cardPort': 1},
+                 'active': True},
+                {'portPos': {'node': 0, 'slot': 2, 'cardPort': 1},
+                 'active': True},
+                {'portPos': {'node': 0, 'slot': 2, 'cardPort': 1},
+                 'active': True},
+                {'portPos': {'node': 0, 'slot': 2, 'cardPort': 1},
+                 'active': True}]})
index 2b4ba2418fe1e5ef5580085edae2e17dac81e92a..e84aedd5df5b5501f6a2f6b7e07e0bb2ab613b4d 100644 (file)
@@ -48,6 +48,7 @@ import time
 import uuid
 
 from eventlet import greenthread
+import hp3parclient
 from hp3parclient import client
 from hp3parclient import exceptions as hpexceptions
 from oslo.config import cfg
@@ -64,6 +65,8 @@ from cinder.volume import volume_types
 
 LOG = logging.getLogger(__name__)
 
+MIN_CLIENT_VERSION = '2.0.0'
+
 hp3par_opts = [
     cfg.StrOpt('hp3par_api_url',
                default='',
@@ -144,7 +147,17 @@ class HP3PARCommon(object):
                 raise exception.InvalidInput(reason=_('%s is not set') % flag)
 
     def _create_client(self):
-        return client.HP3ParClient(self.config.hp3par_api_url)
+        cl = client.HP3ParClient(self.config.hp3par_api_url)
+        client_version = hp3parclient.version
+
+        if (client_version < MIN_CLIENT_VERSION):
+            ex_msg = (_('Invalid hp3parclient version. Version %s or greater '
+                        'required.') % MIN_CLIENT_VERSION)
+            raise hpexceptions.UnsupportedVersion(ex_msg)
+        else:
+            LOG.debug(('Using hp3parclient %s.') % client_version)
+
+        return cl
 
     def client_login(self):
         try:
@@ -358,15 +371,14 @@ exit
                 LOG.error(_("Error running ssh command: %s") % command)
 
     def _delete_3par_host(self, hostname):
-        self._cli_run(['removehost', hostname])
+        self.client.deleteHost(hostname)
 
     def _create_3par_vlun(self, volume, hostname):
-        out = self._cli_run(['createvlun', volume, 'auto', hostname])
-        if out and len(out) > 1:
-            if "must be in the same domain" in out[0]:
-                err = out[0].strip()
-                err = err + " " + out[1].strip()
-                raise exception.Invalid3PARDomain(err=err)
+        try:
+            self.client.createVLUN(volume, hostname=hostname, auto=True)
+        except hpexceptions.HTTPBadRequest as e:
+            if 'must be in the same domain' in e.get_description():
+                raise exception.Invalid3PARDomain(err=e.get_description())
 
     def _safe_hostname(self, hostname):
         """We have to use a safe hostname length for 3PAR host names."""
@@ -383,142 +395,41 @@ exit
         return hostname[:index]
 
     def _get_3par_host(self, hostname):
-        out = self._cli_run(['showhost', '-verbose', hostname])
-        LOG.debug("OUTPUT = \n%s" % (pprint.pformat(out)))
-        host = {'id': None, 'name': None,
-                'domain': None,
-                'descriptors': {},
-                'iSCSIPaths': [],
-                'FCPaths': []}
-
-        if out:
-            err = out[0]
-            if err == 'no hosts listed':
-                msg = {'code': 'NON_EXISTENT_HOST',
-                       'desc': "HOST '%s' was not found" % hostname}
-                raise hpexceptions.HTTPNotFound(msg)
-
-            # start parsing the lines after the header line
-            for line in out[1:]:
-                if line == '':
-                    break
-                tmp = line.split(',')
-                paths = {}
-
-                LOG.debug("line = %s" % (pprint.pformat(tmp)))
-                host['id'] = tmp[0]
-                host['name'] = tmp[1]
-
-                portPos = tmp[4]
-                LOG.debug("portPos = %s" % (pprint.pformat(portPos)))
-                if portPos == '---':
-                    portPos = None
-                else:
-                    port = portPos.split(':')
-                    portPos = {'node': int(port[0]), 'slot': int(port[1]),
-                               'cardPort': int(port[2])}
-
-                paths['portPos'] = portPos
-
-                # If FC entry
-                if tmp[5] == 'n/a':
-                    paths['wwn'] = tmp[3]
-                    host['FCPaths'].append(paths)
-                # else iSCSI entry
-                else:
-                    paths['name'] = tmp[3]
-                    paths['ipAddr'] = tmp[5]
-                    host['iSCSIPaths'].append(paths)
-
-            # find the offset to the description stuff
-            offset = 0
-            for line in out:
-                if line[:15] == '---------- Host':
-                    break
-                else:
-                    offset += 1
-
-            info = out[offset + 2]
-            tmp = info.split(':')
-            host['domain'] = tmp[1]
-
-            info = out[offset + 4]
-            tmp = info.split(':')
-            host['descriptors']['location'] = tmp[1]
-
-            info = out[offset + 5]
-            tmp = info.split(':')
-            host['descriptors']['ipAddr'] = tmp[1]
-
-            info = out[offset + 6]
-            tmp = info.split(':')
-            host['descriptors']['os'] = tmp[1]
-
-            info = out[offset + 7]
-            tmp = info.split(':')
-            host['descriptors']['model'] = tmp[1]
-
-            info = out[offset + 8]
-            tmp = info.split(':')
-            host['descriptors']['contact'] = tmp[1]
-
-            info = out[offset + 9]
-            tmp = info.split(':')
-            host['descriptors']['comment'] = tmp[1]
-
-        return host
+        return self.client.getHost(hostname)
 
     def get_ports(self):
-        # First get the active FC ports
-        out = self._cli_run(['showport'])
-
-        # strip out header
-        # N:S:P,Mode,State,----Node_WWN----,-Port_WWN/HW_Addr-,Type,
-        # Protocol,Label,Partner,FailoverState
-        out = out[1:len(out) - 2]
-
-        ports = {'FC': [], 'iSCSI': {}}
-        for line in out:
-            tmp = line.split(',')
-
-            if tmp:
-                if tmp[1] == 'target' and tmp[2] == 'ready':
-                    if tmp[6] == 'FC':
-                        ports['FC'].append(tmp[4])
-
-        # now get the active iSCSI ports
-        out = self._cli_run(['showport', '-iscsi'])
-
-        # strip out header
-        # N:S:P,State,IPAddr,Netmask,Gateway,
-        # TPGT,MTU,Rate,DHCP,iSNS_Addr,iSNS_Port
-        out = out[1:len(out) - 2]
-        for line in out:
-            tmp = line.split(',')
-
-            if tmp and len(tmp) > 2:
-                if tmp[1] == 'ready':
-                    ports['iSCSI'][tmp[2]] = {}
-
-        # now get the nsp and iqn
-        result = self._cli_run(['showport', '-iscsiname'])
-        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
+        return self.client.getPorts()
+
+    def get_active_target_ports(self):
+        ports = self.get_ports()
+        target_ports = []
+        for port in ports['members']:
+            if (
+                port['mode'] == self.client.PORT_MODE_TARGET and
+                port['linkState'] == self.client.PORT_STATE_READY
+            ):
+                port['nsp'] = self.build_nsp(port['portPos'])
+                target_ports.append(port)
+
+        return target_ports
+
+    def get_active_fc_target_ports(self):
+        ports = self.get_active_target_ports()
+        fc_ports = []
+        for port in ports:
+            if port['protocol'] == self.client.PORT_PROTO_FC:
+                fc_ports.append(port)
+
+        return fc_ports
+
+    def get_active_iscsi_target_ports(self):
+        ports = self.get_active_target_ports()
+        iscsi_ports = []
+        for port in ports:
+            if port['protocol'] == self.client.PORT_PROTO_ISCSI:
+                iscsi_ports.append(port)
+
+        return iscsi_ports
 
     def get_volume_stats(self, refresh):
         if refresh:
@@ -574,7 +485,15 @@ exit
         volume_name = self._get_3par_vol_name(volume['id'])
         vlun = self.client.getVLUN(volume_name)
         self.client.deleteVLUN(volume_name, vlun['lun'], hostname)
-        self._delete_3par_host(hostname)
+        try:
+            self._delete_3par_host(hostname)
+        except hpexceptions.HTTPConflict as ex:
+            # host will only be removed after all vluns
+            # have been removed
+            if 'has exported VLUN' in ex.get_description():
+                pass
+            else:
+                raise
 
     def _get_volume_type(self, type_id):
         ctxt = context.get_admin_context()
@@ -1027,21 +946,30 @@ exit
         except hpexceptions.HTTPNotFound as ex:
             LOG.error(str(ex))
 
-    def _get_3par_hostname_from_wwn_iqn(self, wwns_iqn):
-        out = self._cli_run(['showhost', '-d'])
-        # wwns_iqn may be a list of strings or a single
-        # string. So, if necessary, create a list to loop.
-        if not isinstance(wwns_iqn, list):
-            wwn_iqn_list = [wwns_iqn]
-        else:
-            wwn_iqn_list = wwns_iqn
-
-        for wwn_iqn in wwn_iqn_list:
-            for showhost in out:
-                if (wwn_iqn.upper() in showhost.upper()):
-                    return showhost.split(',')[1]
-
-    def terminate_connection(self, volume, hostname, wwn_iqn):
+    def _get_3par_hostname_from_wwn_iqn(self, wwns, iqns):
+        if wwns is not None and not isinstance(wwns, list):
+            wwns = [wwns]
+        if iqns is not None and not isinstance(iqns, list):
+            iqns = [iqns]
+
+        out = self.client.getHosts()
+        hosts = out['members']
+        for host in hosts:
+            if 'iSCSIPaths' in host and iqns is not None:
+                iscsi_paths = host['iSCSIPaths']
+                for iscsi in iscsi_paths:
+                    for iqn in iqns:
+                        if iqn == iscsi['name']:
+                            return host['name']
+
+            if 'FCPaths' in host and wwns is not None:
+                fc_paths = host['FCPaths']
+                for fc in fc_paths:
+                    for wwn in wwns:
+                        if wwn == fc['WWN']:
+                            return host['name']
+
+    def terminate_connection(self, volume, hostname, wwn=None, iqn=None):
         """Driver entry point to unattach a volume from an instance."""
         try:
             # does 3par know this host by a different name?
@@ -1052,7 +980,7 @@ exit
         except hpexceptions.HTTPNotFound as e:
             if 'host does not exist' in e.get_description():
                 # use the wwn to see if we can find the hostname
-                hostname = self._get_3par_hostname_from_wwn_iqn(wwn_iqn)
+                hostname = self._get_3par_hostname_from_wwn_iqn(wwniqn)
                 # no 3par host, re-throw
                 if (hostname is None):
                     raise
@@ -1060,13 +988,18 @@ exit
             # not a 'host does not exist' HTTPNotFound exception, re-throw
                 raise
 
-        #try again with name retrieved from 3par
+        # try again with name retrieved from 3par
         self.delete_vlun(volume, hostname)
 
     def parse_create_host_error(self, hostname, out):
         search_str = "already used by host "
         if search_str in out[1]:
-            #host exists, return name used by 3par
+            # host exists, return name used by 3par
             hostname_3par = self.get_next_word(out[1], search_str)
             self.hosts_naming_dict[hostname] = hostname_3par
             return hostname_3par
+
+    def build_nsp(self, portPos):
+        return '%s:%s:%s' % (portPos['node'],
+                             portPos['slot'],
+                             portPos['cardPort'])
index 4be85571894663bd74e3be19730aad5eedf3e8a2..8a1d2d2493ba2e441bbaa9ce0abbe4c1af959709 100644 (file)
@@ -180,13 +180,17 @@ class HP3PARFCDriver(cinder.volume.driver.FibreChannelDriver):
         # now that we have a host, create the VLUN
         vlun = self.common.create_vlun(volume, host)
 
-        ports = self.common.get_ports()
+        fc_ports = self.common.get_active_fc_target_ports()
+        wwns = []
+
+        for port in fc_ports:
+            wwns.append(port['portWWN'])
 
         self.common.client_logout()
         info = {'driver_volume_type': 'fibre_channel',
                 'data': {'target_lun': vlun['lun'],
                          'target_discovered': True,
-                         'target_wwn': ports['FC']}}
+                         'target_wwn': wwns}}
         return info
 
     @utils.synchronized('3par', external=True)
@@ -195,7 +199,7 @@ class HP3PARFCDriver(cinder.volume.driver.FibreChannelDriver):
         self.common.client_login()
         hostname = self.common._safe_hostname(connector['host'])
         self.common.terminate_connection(volume, hostname,
-                                         connector['wwpns'])
+                                         wwn=connector['wwpns'])
         self.common.client_logout()
 
     def _create_3par_fibrechan_host(self, hostname, wwns, domain, persona_id):
@@ -219,13 +223,11 @@ class HP3PARFCDriver(cinder.volume.driver.FibreChannelDriver):
 
         return hostname
 
-    def _modify_3par_fibrechan_host(self, hostname, wwns):
-        # when using -add, you can not send the persona or domain options
-        command = ['createhost', '-add', hostname]
-        for wwn in wwns:
-            command.append(wwn)
+    def _modify_3par_fibrechan_host(self, hostname, wwn):
+        mod_request = {'pathOperation': self.common.client.HOST_EDIT_ADD,
+                       'FCWWNs': wwn}
 
-        out = self.common._cli_run(command)
+        self.common.client.modifyHost(hostname, mod_request)
 
     def _create_host(self, volume, connector):
         """Creates or modifies existing 3PAR host."""
@@ -235,7 +237,7 @@ class HP3PARFCDriver(cinder.volume.driver.FibreChannelDriver):
         domain = self.common.get_domain(cpg)
         try:
             host = self.common._get_3par_host(hostname)
-            if not host['FCPaths']:
+            if 'FCPaths' not in host or len(host['FCPaths']) < 1:
                 self._modify_3par_fibrechan_host(hostname, connector['wwpns'])
                 host = self.common._get_3par_host(hostname)
         except hpexceptions.HTTPNotFound as ex:
index db3dad6fa2c6a7bb2c3c439c68cef7caa97b088d..feb2723af395a983f961ec91fcdfe44844715e39 100644 (file)
@@ -87,6 +87,7 @@ class HP3PARISCSIDriver(cinder.volume.driver.ISCSIDriver):
     def do_setup(self, context):
         self.common = self._init_common()
         self._check_flags()
+        self.common.do_setup(context)
 
         # map iscsi_ip-> ip_port
         #             -> iqn
@@ -118,14 +119,15 @@ class HP3PARISCSIDriver(cinder.volume.driver.ISCSIDriver):
         # 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():
+        iscsi_ports = self.common.get_active_iscsi_target_ports()
+
+        for port in iscsi_ports:
+            ip = port['IPAddr']
             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']
+                                      'nsp': port['nsp'],
+                                      'iqn': port['iSCSIName']
                                       }
                 del temp_iscsi_ip[ip]
 
@@ -146,8 +148,6 @@ class HP3PARISCSIDriver(cinder.volume.driver.ISCSIDriver):
             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."""
         self._check_flags()
@@ -233,9 +233,10 @@ class HP3PARISCSIDriver(cinder.volume.driver.ISCSIDriver):
         # now that we have a host, create the VLUN
         vlun = self.common.create_vlun(volume, host)
 
+        iscsi_ip = self._get_iscsi_ip(host['name'])
+
         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',
@@ -254,7 +255,7 @@ class HP3PARISCSIDriver(cinder.volume.driver.ISCSIDriver):
         self.common.client_login()
         hostname = self.common._safe_hostname(connector['host'])
         self.common.terminate_connection(volume, hostname,
-                                         connector['initiator'])
+                                         iqn=connector['initiator'])
         self.common.client_logout()
 
     def _create_3par_iscsi_host(self, hostname, iscsi_iqn, domain, persona_id):
@@ -276,9 +277,10 @@ class HP3PARISCSIDriver(cinder.volume.driver.ISCSIDriver):
         return hostname
 
     def _modify_3par_iscsi_host(self, hostname, iscsi_iqn):
-        # when using -add, you can not send the persona or domain options
-        command = ['createhost', '-iscsi', '-add', hostname, iscsi_iqn]
-        self.common._cli_run(command)
+        mod_request = {'pathOperation': self.common.client.HOST_EDIT_ADD,
+                       'iSCSINames': [iscsi_iqn]}
+
+        self.common.client.modifyHost(hostname, mod_request)
 
     def _create_host(self, volume, connector):
         """Creates or modifies existing 3PAR host."""
@@ -289,7 +291,7 @@ class HP3PARISCSIDriver(cinder.volume.driver.ISCSIDriver):
         domain = self.common.get_domain(cpg)
         try:
             host = self.common._get_3par_host(hostname)
-            if not host['iSCSIPaths']:
+            if 'iSCSIPaths' not in host or len(host['iSCSIPaths']) < 1:
                 self._modify_3par_iscsi_host(hostname, connector['initiator'])
                 host = self.common._get_3par_host(hostname)
         except hpexceptions.HTTPNotFound:
@@ -327,19 +329,27 @@ class HP3PARISCSIDriver(cinder.volume.driver.ISCSIDriver):
         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]
+        vluns = self.common.client.getVLUNs()
+        # see if there is already a path to the
+        # host, if so use it
+        for vlun in vluns['members']:
+            if vlun['active'] == 'true':
+                if vlun['hostname'] == hostname:
+                    # this host already has a path, so use it
+                    nsp = self.common.build_nsp(vlun['portPos'])
+                    return self._get_ip_using_nsp(nsp)
+
+        # no current path find least used port
+        least_used_nsp = self._get_least_used_nsp(vluns['members'],
+                                                  self._get_iscsi_nsps())
+
+        if least_used_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)
+        return self._get_ip_using_nsp(least_used_nsp)
 
     def _get_iscsi_nsps(self):
         """Return the list of candidate nsps."""
@@ -354,43 +364,29 @@ class HP3PARISCSIDriver(cinder.volume.driver.ISCSIDriver):
             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', hostname])
-        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):
+    def _get_least_used_nsp(self, vluns, 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'])
-
-        # count the number of nsps (there is 1 for each active vlun)
+        # count the number of nsps
         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()
+
+        for vlun in vluns:
+            if vlun['active']:
+                nsp = self.common.build_nsp(vlun['portPos'])
                 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
+        # 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
 
index 7a7ff9c7ad427a8266e6b1c84525e4f86e0cafb3..3d8e9bffc00bf4d20bf1ecb98be143d4fe9fde30 100644 (file)
@@ -3,7 +3,7 @@ hacking>=0.5.6,<0.8
 coverage>=3.6
 discover
 fixtures>=0.3.14
-hp3parclient>=1.0.0
+hp3parclient>=2.0,<3.0
 mock>=1.0
 mox>=0.5.3
 MySQL-python