Add cinder volume drivers for NEC Storage M series
MStorageISCSIDriver is an iSCSI driver. MStorageFCDriver is a Fibre Channel driver. These drivers communicate with storage via SSH. Implements: blueprint nec-volume-driver Change-Id: Ieb16f69abb7d5fefd2dc4a51515701caaa651d1b
This commit is contained in:
parent
86322d318e
commit
cb668cb656
0
cinder/tests/unit/volume/drivers/nec/__init__.py
Normal file
0
cinder/tests/unit/volume/drivers/nec/__init__.py
Normal file
435
cinder/tests/unit/volume/drivers/nec/cli_test.py
Normal file
435
cinder/tests/unit/volume/drivers/nec/cli_test.py
Normal file
@ -0,0 +1,435 @@
|
||||
#
|
||||
# Copyright (c) 2016 NEC Corporation.
|
||||
# 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.
|
||||
|
||||
|
||||
class MStorageISMCLI(object):
|
||||
def __init__(self, properties):
|
||||
super(MStorageISMCLI, self).__init__()
|
||||
|
||||
self._properties = properties
|
||||
|
||||
def view_all(self, conf_ismview_path=None, delete_ismview=True,
|
||||
cmd_lock=True):
|
||||
out = '''<REQUEST>
|
||||
<CMD_REQUEST cmd_name="/opt/iSMCliGateway/impl/query/iSMquery"
|
||||
arg="-cinder -xml -all "
|
||||
version="Version 9.4.001">
|
||||
<CHAPTER name="Disk Array">
|
||||
<OBJECT name="Disk Array">
|
||||
<SECTION name="Disk Array Detail Information">
|
||||
<UNIT name="Product ID">M310</UNIT>
|
||||
</SECTION>
|
||||
</OBJECT>
|
||||
</CHAPTER>
|
||||
<CHAPTER name="Logical Disk">
|
||||
<OBJECT name="Logical Disk">
|
||||
<SECTION name="LD Detail Information">
|
||||
<UNIT name="LDN(h)">0000</UNIT>
|
||||
<UNIT name="OS Type">LX</UNIT>
|
||||
<UNIT name="LD Name">287RbQoP7VdwR1WsPC2fZT</UNIT>
|
||||
<UNIT name="LD Capacity">1073741824</UNIT>
|
||||
<UNIT name="Pool No.(h)">0000</UNIT>
|
||||
<UNIT name="Purpose">---</UNIT>
|
||||
<UNIT name="RPL Attribute">MV</UNIT>
|
||||
</SECTION>
|
||||
</OBJECT>
|
||||
<OBJECT name="Logical Disk">
|
||||
<SECTION name="LD Detail Information">
|
||||
<UNIT name="LDN(h)">0001</UNIT>
|
||||
<UNIT name="OS Type"> </UNIT>
|
||||
<UNIT name="LD Name">backup_SDV0001</UNIT>
|
||||
<UNIT name="LD Capacity">5368709120</UNIT>
|
||||
<UNIT name="Pool No.(h)">0001</UNIT>
|
||||
<UNIT name="Purpose">(invalid attribute)</UNIT>
|
||||
<UNIT name="RPL Attribute">IV</UNIT>
|
||||
</SECTION>
|
||||
</OBJECT>
|
||||
<OBJECT name="Logical Disk">
|
||||
<SECTION name="LD Detail Information">
|
||||
<UNIT name="LDN(h)">0003</UNIT>
|
||||
<UNIT name="OS Type">LX</UNIT>
|
||||
<UNIT name="LD Name">31HxzqBiAFTUxxOlcVn3EA_back</UNIT>
|
||||
<UNIT name="LD Capacity">1073741824</UNIT>
|
||||
<UNIT name="Pool No.(h)">0001</UNIT>
|
||||
<UNIT name="Purpose">---</UNIT>
|
||||
<UNIT name="RPL Attribute">RV</UNIT>
|
||||
</SECTION>
|
||||
</OBJECT>
|
||||
<OBJECT name="Logical Disk">
|
||||
<SECTION name="LD Detail Information">
|
||||
<UNIT name="LDN(h)">0004</UNIT>
|
||||
<UNIT name="OS Type">LX</UNIT>
|
||||
<UNIT name="LD Name">287RbQoP7VdwR1WsPC2fZT_back</UNIT>
|
||||
<UNIT name="LD Capacity">1073741824</UNIT>
|
||||
<UNIT name="Pool No.(h)">0000</UNIT>
|
||||
<UNIT name="Purpose">---</UNIT>
|
||||
<UNIT name="RPL Attribute">RV</UNIT>
|
||||
</SECTION>
|
||||
</OBJECT>
|
||||
<OBJECT name="Logical Disk">
|
||||
<SECTION name="LD Detail Information">
|
||||
<UNIT name="LDN(h)">0005</UNIT>
|
||||
<UNIT name="OS Type">LX</UNIT>
|
||||
<UNIT name="LD Name">20000009910200140005</UNIT>
|
||||
<UNIT name="LD Capacity">10737418240</UNIT>
|
||||
<UNIT name="Pool No.(h)">0000</UNIT>
|
||||
<UNIT name="Purpose">---</UNIT>
|
||||
<UNIT name="RPL Attribute">RV</UNIT>
|
||||
</SECTION>
|
||||
</OBJECT>
|
||||
<OBJECT name="Logical Disk">
|
||||
<SECTION name="LD Detail Information">
|
||||
<UNIT name="LDN(h)">0006</UNIT>
|
||||
<UNIT name="OS Type"> </UNIT>
|
||||
<UNIT name="LD Name">20000009910200140006</UNIT>
|
||||
<UNIT name="LD Capacity">10737418240</UNIT>
|
||||
<UNIT name="Pool No.(h)">0000</UNIT>
|
||||
<UNIT name="Purpose">---</UNIT>
|
||||
<UNIT name="RPL Attribute">IV</UNIT>
|
||||
</SECTION>
|
||||
</OBJECT>
|
||||
<OBJECT name="Logical Disk">
|
||||
<SECTION name="LD Detail Information">
|
||||
<UNIT name="LDN(h)">0007</UNIT>
|
||||
<UNIT name="OS Type"> </UNIT>
|
||||
<UNIT name="LD Name">20000009910200140007</UNIT>
|
||||
<UNIT name="LD Capacity">10737418240</UNIT>
|
||||
<UNIT name="Pool No.(h)">0000</UNIT>
|
||||
<UNIT name="Purpose">---</UNIT>
|
||||
<UNIT name="RPL Attribute">IV</UNIT>
|
||||
</SECTION>
|
||||
</OBJECT>
|
||||
<OBJECT name="Logical Disk">
|
||||
<SECTION name="LD Detail Information">
|
||||
<UNIT name="LDN(h)">0008</UNIT>
|
||||
<UNIT name="OS Type"> </UNIT>
|
||||
<UNIT name="LD Name">20000009910200140008</UNIT>
|
||||
<UNIT name="LD Capacity">10737418240</UNIT>
|
||||
<UNIT name="Pool No.(h)">0000</UNIT>
|
||||
<UNIT name="Purpose">---</UNIT>
|
||||
<UNIT name="RPL Attribute">IV</UNIT>
|
||||
</SECTION>
|
||||
</OBJECT>
|
||||
<OBJECT name="Logical Disk">
|
||||
<SECTION name="LD Detail Information">
|
||||
<UNIT name="LDN(h)">0009</UNIT>
|
||||
<UNIT name="OS Type"> </UNIT>
|
||||
<UNIT name="LD Name">20000009910200140009</UNIT>
|
||||
<UNIT name="LD Capacity">10737418240</UNIT>
|
||||
<UNIT name="Pool No.(h)">0000</UNIT>
|
||||
<UNIT name="Purpose">---</UNIT>
|
||||
<UNIT name="RPL Attribute">IV</UNIT>
|
||||
</SECTION>
|
||||
</OBJECT>
|
||||
<OBJECT name="Logical Disk">
|
||||
<SECTION name="LD Detail Information">
|
||||
<UNIT name="LDN(h)">000a</UNIT>
|
||||
<UNIT name="OS Type"> </UNIT>
|
||||
<UNIT name="LD Name">2000000991020012000A</UNIT>
|
||||
<UNIT name="LD Capacity">6442450944</UNIT>
|
||||
<UNIT name="Pool No.(h)">0000</UNIT>
|
||||
<UNIT name="Purpose">---</UNIT>
|
||||
<UNIT name="RPL Attribute">IV</UNIT>
|
||||
</SECTION>
|
||||
</OBJECT>
|
||||
<OBJECT name="Logical Disk">
|
||||
<SECTION name="LD Detail Information">
|
||||
<UNIT name="LDN(h)">000b</UNIT>
|
||||
<UNIT name="OS Type"> </UNIT>
|
||||
<UNIT name="LD Name">2000000991020012000B</UNIT>
|
||||
<UNIT name="LD Capacity">6442450944</UNIT>
|
||||
<UNIT name="Pool No.(h)">0000</UNIT>
|
||||
<UNIT name="Purpose">---</UNIT>
|
||||
<UNIT name="RPL Attribute">IV</UNIT>
|
||||
</SECTION>
|
||||
</OBJECT>
|
||||
<OBJECT name="Logical Disk">
|
||||
<SECTION name="LD Detail Information">
|
||||
<UNIT name="LDN(h)">000c</UNIT>
|
||||
<UNIT name="OS Type"> </UNIT>
|
||||
<UNIT name="LD Name">2000000991020012000C</UNIT>
|
||||
<UNIT name="LD Capacity">6442450944</UNIT>
|
||||
<UNIT name="Pool No.(h)">0000</UNIT>
|
||||
<UNIT name="Purpose">---</UNIT>
|
||||
<UNIT name="RPL Attribute">IV</UNIT>
|
||||
</SECTION>
|
||||
</OBJECT>
|
||||
<OBJECT name="Logical Disk">
|
||||
<SECTION name="LD Detail Information">
|
||||
<UNIT name="LDN(h)">000d</UNIT>
|
||||
<UNIT name="OS Type">LX</UNIT>
|
||||
<UNIT name="LD Name">yEUHrXa5AHMjOZZLb93eP</UNIT>
|
||||
<UNIT name="LD Capacity">6442450944</UNIT>
|
||||
<UNIT name="Pool No.(h)">0001</UNIT>
|
||||
<UNIT name="Purpose">---</UNIT>
|
||||
<UNIT name="RPL Attribute">IV</UNIT>
|
||||
</SECTION>
|
||||
</OBJECT>
|
||||
<OBJECT name="Logical Disk">
|
||||
<SECTION name="LD Detail Information">
|
||||
<UNIT name="LDN(h)">000e</UNIT>
|
||||
<UNIT name="OS Type">LX</UNIT>
|
||||
<UNIT name="LD Name">4T7JpyqI3UuPlKeT9D3VQF</UNIT>
|
||||
<UNIT name="LD Capacity">6442450944</UNIT>
|
||||
<UNIT name="Pool No.(h)">0001</UNIT>
|
||||
<UNIT name="Purpose">RPL</UNIT>
|
||||
<UNIT name="RPL Attribute">IV</UNIT>
|
||||
</SECTION>
|
||||
</OBJECT>
|
||||
<OBJECT name="Logical Disk">
|
||||
<SECTION name="LD Detail Information">
|
||||
<UNIT name="LDN(h)">0fff</UNIT>
|
||||
<UNIT name="OS Type"> </UNIT>
|
||||
<UNIT name="LD Name">Pool0000_SYV0FFF</UNIT>
|
||||
<UNIT name="LD Capacity">8589934592</UNIT>
|
||||
<UNIT name="Pool No.(h)">0000</UNIT>
|
||||
<UNIT name="Purpose">(invalid attribute)</UNIT>
|
||||
<UNIT name="RPL Attribute">---</UNIT>
|
||||
</SECTION>
|
||||
</OBJECT>
|
||||
</CHAPTER>
|
||||
<CHAPTER name="Pool">
|
||||
<OBJECT name="Pool">
|
||||
<SECTION name="Pool Detail Information">
|
||||
<UNIT name="Pool No.(h)">0000</UNIT>
|
||||
<UNIT name="Pool Capacity">281320357888</UNIT>
|
||||
<UNIT name="Used Pool Capacity">84020297728</UNIT>
|
||||
<UNIT name="Free Pool Capacity">197300060160</UNIT>
|
||||
</SECTION>
|
||||
</OBJECT>
|
||||
<OBJECT name="Pool">
|
||||
<SECTION name="Pool Detail Information">
|
||||
<UNIT name="Pool No.(h)">0001</UNIT>
|
||||
<UNIT name="Pool Capacity">89657442304</UNIT>
|
||||
<UNIT name="Used Pool Capacity">6710886400</UNIT>
|
||||
<UNIT name="Free Pool Capacity">82946555904</UNIT>
|
||||
</SECTION>
|
||||
</OBJECT>
|
||||
<OBJECT name="Pool">
|
||||
<SECTION name="Pool Detail Information">
|
||||
<UNIT name="Pool No.(h)">0002</UNIT>
|
||||
<UNIT name="Pool Capacity">1950988894208</UNIT>
|
||||
<UNIT name="Used Pool Capacity">18446744073441116160</UNIT>
|
||||
<UNIT name="Free Pool Capacity">1951257329664</UNIT>
|
||||
</SECTION>
|
||||
</OBJECT>
|
||||
<OBJECT name="Pool">
|
||||
<SECTION name="Pool Detail Information">
|
||||
<UNIT name="Pool No.(h)">0003</UNIT>
|
||||
<UNIT name="Pool Capacity">1950988894208</UNIT>
|
||||
<UNIT name="Used Pool Capacity">18446744073441116160</UNIT>
|
||||
<UNIT name="Free Pool Capacity">1951257329664</UNIT>
|
||||
</SECTION>
|
||||
</OBJECT>
|
||||
</CHAPTER>
|
||||
<CHAPTER name="Controller">
|
||||
<OBJECT name="Host Port">
|
||||
<SECTION name="Host Director/Host Port Information">
|
||||
<UNIT name="Port No.(h)">00-00</UNIT>
|
||||
<UNIT name="WWPN">2100000991020012</UNIT>
|
||||
</SECTION>
|
||||
</OBJECT>
|
||||
<OBJECT name="Host Port">
|
||||
<SECTION name="Host Director/Host Port Information">
|
||||
<UNIT name="Port No.(h)">00-01</UNIT>
|
||||
<UNIT name="WWPN">2200000991020012</UNIT>
|
||||
</SECTION>
|
||||
</OBJECT>
|
||||
<OBJECT name="Host Port">
|
||||
<SECTION name="Host Director/Host Port Information">
|
||||
<UNIT name="Port No.(h)">00-02</UNIT>
|
||||
<UNIT name="IP Address">192.168.1.90</UNIT>
|
||||
<UNIT name="Link Status">Link Down</UNIT>
|
||||
</SECTION>
|
||||
</OBJECT>
|
||||
<OBJECT name="Host Port">
|
||||
<SECTION name="Host Director/Host Port Information">
|
||||
<UNIT name="Port No.(h)">00-03</UNIT>
|
||||
<UNIT name="IP Address">192.168.1.91</UNIT>
|
||||
<UNIT name="Link Status">Link Down</UNIT>
|
||||
</SECTION>
|
||||
</OBJECT>
|
||||
<OBJECT name="Host Port">
|
||||
<SECTION name="Host Director/Host Port Information">
|
||||
<UNIT name="Port No.(h)">01-00</UNIT>
|
||||
<UNIT name="WWPN">2900000991020012</UNIT>
|
||||
</SECTION>
|
||||
</OBJECT>
|
||||
<OBJECT name="Host Port">
|
||||
<SECTION name="Host Director/Host Port Information">
|
||||
<UNIT name="Port No.(h)">01-01</UNIT>
|
||||
<UNIT name="WWPN">2A00000991020012</UNIT>
|
||||
</SECTION>
|
||||
</OBJECT>
|
||||
<OBJECT name="Host Port">
|
||||
<SECTION name="Host Director/Host Port Information">
|
||||
<UNIT name="Port No.(h)">01-02</UNIT>
|
||||
<UNIT name="IP Address">192.168.2.92</UNIT>
|
||||
<UNIT name="Link Status">Link Down</UNIT>
|
||||
</SECTION>
|
||||
</OBJECT>
|
||||
<OBJECT name="Host Port">
|
||||
<SECTION name="Host Director/Host Port Information">
|
||||
<UNIT name="Port No.(h)">01-03</UNIT>
|
||||
<UNIT name="IP Address">192.168.2.93</UNIT>
|
||||
<UNIT name="Link Status">Link Up</UNIT>
|
||||
</SECTION>
|
||||
</OBJECT>
|
||||
</CHAPTER>
|
||||
<CHAPTER name="Access Control">
|
||||
<OBJECT name="LD Set(FC)">
|
||||
<SECTION name="LD Set(FC) Information">
|
||||
<UNIT name="Platform">LX</UNIT>
|
||||
<UNIT name="LD Set Name">OpenStack1</UNIT>
|
||||
</SECTION>
|
||||
<SECTION name="Path List">
|
||||
<UNIT name="Path">1000-0090-FAA0-786B</UNIT>
|
||||
</SECTION>
|
||||
<SECTION name="Path List">
|
||||
<UNIT name="Path">1000-0090-FAA0-786A</UNIT>
|
||||
</SECTION>
|
||||
</OBJECT>
|
||||
<OBJECT name="LD Set(FC)">
|
||||
<SECTION name="LD Set(FC) Information">
|
||||
<UNIT name="Platform">WN</UNIT>
|
||||
<UNIT name="LD Set Name">TNES120250</UNIT>
|
||||
</SECTION>
|
||||
<SECTION name="Path List">
|
||||
<UNIT name="Path">1000-0090-FA76-9605</UNIT>
|
||||
</SECTION>
|
||||
<SECTION name="Path List">
|
||||
<UNIT name="Path">1000-0090-FA76-9604</UNIT>
|
||||
</SECTION>
|
||||
</OBJECT>
|
||||
<OBJECT name="LD Set(FC)">
|
||||
<SECTION name="LD Set(FC) Information">
|
||||
<UNIT name="Platform">WN</UNIT>
|
||||
<UNIT name="LD Set Name">TNES140098</UNIT>
|
||||
</SECTION>
|
||||
<SECTION name="Path List">
|
||||
<UNIT name="Path">1000-0090-FA53-302C</UNIT>
|
||||
</SECTION>
|
||||
<SECTION name="Path List">
|
||||
<UNIT name="Path">1000-0090-FA53-302D</UNIT>
|
||||
</SECTION>
|
||||
<SECTION name="LUN/LD List">
|
||||
<UNIT name="LUN(h)">0000</UNIT>
|
||||
<UNIT name="LDN(h)">0005</UNIT>
|
||||
</SECTION>
|
||||
<SECTION name="LUN/LD List">
|
||||
<UNIT name="LUN(h)">0001</UNIT>
|
||||
<UNIT name="LDN(h)">0006</UNIT>
|
||||
</SECTION>
|
||||
</OBJECT>
|
||||
<OBJECT name="LD Set(iSCSI)">
|
||||
<SECTION name="LD Set(iSCSI) Information">
|
||||
<UNIT name="Platform">LX</UNIT>
|
||||
<UNIT name="LD Set Name">OpenStack0</UNIT>
|
||||
<UNIT name="Target Mode">Multi-Target</UNIT>
|
||||
</SECTION>
|
||||
<SECTION name="Portal">
|
||||
<UNIT name="Portal">192.168.1.90:3260</UNIT>
|
||||
</SECTION>
|
||||
<SECTION name="Portal">
|
||||
<UNIT name="Portal">192.168.1.91:3260</UNIT>
|
||||
</SECTION>
|
||||
<SECTION name="Portal">
|
||||
<UNIT name="Portal">192.168.2.92:3260</UNIT>
|
||||
</SECTION>
|
||||
<SECTION name="Portal">
|
||||
<UNIT name="Portal">192.168.2.93:3260</UNIT>
|
||||
</SECTION>
|
||||
<SECTION name="Initiator List">
|
||||
<UNIT name="Initiator List">iqn.1994-05.com.redhat:d1d8e8f23255</UNIT>
|
||||
</SECTION>
|
||||
<SECTION name="Target Information For Multi-Target Mode">
|
||||
<UNIT name="Target Name">iqn.2001-03.target0000</UNIT>
|
||||
<UNIT name="LUN(h)">0000</UNIT>
|
||||
<UNIT name="LDN(h)">0000</UNIT>
|
||||
</SECTION>
|
||||
</OBJECT>
|
||||
</CHAPTER>
|
||||
<RETURN_MSG>Command Completed Successfully!!</RETURN_MSG>
|
||||
<RETURN_CODE>0</RETURN_CODE>
|
||||
</CMD_REQUEST>
|
||||
</REQUEST>
|
||||
'''
|
||||
return out
|
||||
|
||||
def ldbind(self, name, pool, ldn, size):
|
||||
return True, 0
|
||||
|
||||
def unbind(self, name):
|
||||
pass
|
||||
|
||||
def expand(self, ldn, capacity):
|
||||
pass
|
||||
|
||||
def addldsetld(self, ldset, ldname, lun=None):
|
||||
pass
|
||||
|
||||
def delldsetld(self, ldset, ldname):
|
||||
if ldname == "LX:287RbQoP7VdwR1WsPC2fZT":
|
||||
return False, 'iSM31064'
|
||||
else:
|
||||
return True, None
|
||||
|
||||
def changeldname(self, ldn, new_name, old_name=None):
|
||||
pass
|
||||
|
||||
def setpair(self, mvname, rvname):
|
||||
pass
|
||||
|
||||
def unpair(self, mvname, rvname, flag):
|
||||
pass
|
||||
|
||||
def replicate(self, mvname, rvname, flag):
|
||||
pass
|
||||
|
||||
def separate(self, mvname, rvname, flag):
|
||||
pass
|
||||
|
||||
def query_MV_RV_status(self, ldname, rpltype):
|
||||
return 'separated'
|
||||
|
||||
def query_MV_RV_name(self, ldname, rpltype):
|
||||
pass
|
||||
|
||||
def query_MV_RV_diff(self, ldname, rpltype):
|
||||
pass
|
||||
|
||||
def backup_restore(self, volume_properties, unpairWait, canPairing=True):
|
||||
pass
|
||||
|
||||
def check_ld_existed_rplstatus(self, lds, ldname, snapshot, flag):
|
||||
return {'ld_capacity': 10}
|
||||
|
||||
def get_pair_lds(self, ldname, lds):
|
||||
return {'0004': {'ld_capacity': 1, 'pool_num': 0,
|
||||
'ldname': 'LX:287RbQoP7VdwR1WsPC2fZT_back',
|
||||
'ldn': 4, 'RPL Attribute': 'RV', 'Purpose': '---'}}
|
||||
|
||||
def snapshot_create(self, bvname, svname, poolnumber):
|
||||
pass
|
||||
|
||||
def snapshot_delete(self, bvname, svname):
|
||||
pass
|
||||
|
||||
def query_BV_SV_status(self, bvname, svname):
|
||||
return 'snap/active'
|
||||
|
||||
def set_io_limit(self, ldname, specs, force_delete=True):
|
||||
pass
|
643
cinder/tests/unit/volume/drivers/nec/test_volume.py
Normal file
643
cinder/tests/unit/volume/drivers/nec/test_volume.py
Normal file
@ -0,0 +1,643 @@
|
||||
#
|
||||
# Copyright (c) 2016 NEC Corporation.
|
||||
# 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.
|
||||
|
||||
import ddt
|
||||
import mock
|
||||
import unittest
|
||||
|
||||
from cinder import exception
|
||||
from cinder.tests.unit.volume.drivers.nec import volume_common_test
|
||||
from cinder.volume.drivers.nec import volume_helper
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
class VolumeIDConvertTest(volume_helper.MStorageDriver, unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self._common = volume_common_test.MStorageVolCommDummy(1, 2, 3)
|
||||
self.do_setup(None)
|
||||
self.vol = {}
|
||||
self._properties = self._common.get_conf_properties()
|
||||
self._cli = self._properties['cli']
|
||||
self.xml = self._cli.view_all(self._properties['ismview_path'])
|
||||
(self.pools,
|
||||
self.lds,
|
||||
self.ldsets,
|
||||
self.used_ldns,
|
||||
self.hostports,
|
||||
self.max_ld_count) = self._common.configs(self.xml)
|
||||
|
||||
def tearDown(self):
|
||||
pass
|
||||
|
||||
@ddt.data(("AAAAAAAA", "LX:37mA82"), ("BBBBBBBB", "LX:3R9ZwR"))
|
||||
@ddt.unpack
|
||||
def test_volumeid_should_change_62scale(self, volid, ldname):
|
||||
self.vol['id'] = volid
|
||||
actual = self._convert_id2name(self.vol)
|
||||
self.assertEqual(ldname, actual,
|
||||
"ID:%(volid)s should be change to %(ldname)s" %
|
||||
{'volid': volid, 'ldname': ldname})
|
||||
|
||||
@ddt.data(("AAAAAAAA", "LX:37mA82_back"), ("BBBBBBBB", "LX:3R9ZwR_back"))
|
||||
@ddt.unpack
|
||||
def test_snap_volumeid_should_change_62scale_andpostfix(self,
|
||||
volid,
|
||||
ldname):
|
||||
self.vol['id'] = volid
|
||||
actual = self._convert_id2snapname(self.vol)
|
||||
self.assertEqual(ldname, actual,
|
||||
"ID:%(volid)s should be change to %(ldname)s" %
|
||||
{'volid': volid, 'ldname': ldname})
|
||||
|
||||
@ddt.data(("AAAAAAAA", "LX:37mA82_m"), ("BBBBBBBB", "LX:3R9ZwR_m"))
|
||||
@ddt.unpack
|
||||
def test_ddrsnap_volumeid_should_change_62scale_and_m(self,
|
||||
volid,
|
||||
ldname):
|
||||
self.vol['id'] = volid
|
||||
actual = self._convert_id2migratename(self.vol)
|
||||
self.assertEqual(ldname, actual,
|
||||
"ID:%(volid)s should be change to %(ldname)s" %
|
||||
{'volid': volid, 'ldname': ldname})
|
||||
|
||||
@ddt.data(("AAAAAAAA", "LX:3R9ZwR", "target:BBBBBBBB"))
|
||||
@ddt.unpack
|
||||
def test_migrate_volumeid_should_change_62scale_andpostfix(self,
|
||||
volid,
|
||||
ldname,
|
||||
status):
|
||||
self.vol['id'] = volid
|
||||
self.vol['migration_status'] = status
|
||||
actual = self._convert_id2name_in_migrate(self.vol)
|
||||
self.assertEqual(ldname, actual,
|
||||
"ID:%(volid)s/%(status)s should be "
|
||||
"change to %(ldname)s" %
|
||||
{'volid': volid,
|
||||
'status': status,
|
||||
'ldname': ldname})
|
||||
|
||||
@ddt.data(("AAAAAAAA", "LX:37mA82", "deleting:BBBBBBBB"),
|
||||
("AAAAAAAA", "LX:37mA82", ""),
|
||||
("AAAAAAAA", "LX:37mA82", "success"))
|
||||
@ddt.unpack
|
||||
def test_NOTmigrate_volumeid_should_change_62scale(self,
|
||||
volid,
|
||||
ldname,
|
||||
status):
|
||||
self.vol['id'] = volid
|
||||
self.vol['migration_status'] = status
|
||||
actual = self._convert_id2name_in_migrate(self.vol)
|
||||
self.assertEqual(ldname, actual,
|
||||
"ID:%(volid)s/%(status)s should be "
|
||||
"change to %(ldname)s" %
|
||||
{'volid': volid,
|
||||
'status': status,
|
||||
'ldname': ldname})
|
||||
|
||||
|
||||
class NominatePoolLDTest(volume_helper.MStorageDriver, unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self._common = volume_common_test.MStorageVolCommDummy(1, 2, 3)
|
||||
self.do_setup(None)
|
||||
self.vol = {}
|
||||
self._properties = self._common.get_conf_properties()
|
||||
self._cli = self._properties['cli']
|
||||
self.xml = self._cli.view_all(self._properties['ismview_path'])
|
||||
(self.pools,
|
||||
self.lds,
|
||||
self.ldsets,
|
||||
self.used_ldns,
|
||||
self.hostports,
|
||||
self.max_ld_count) = self._common.configs(self.xml)
|
||||
self._numofld_per_pool = 1024
|
||||
|
||||
def tearDown(self):
|
||||
pass
|
||||
|
||||
def test_getxml(self):
|
||||
self.assertIsNotNone(self.xml, "iSMview xml should not be None")
|
||||
|
||||
def test_selectldn_for_normalvolume(self):
|
||||
ldn = self._select_ldnumber(self.used_ldns, self.max_ld_count)
|
||||
self.assertEqual(2, ldn, "selected ldn should be XXX")
|
||||
|
||||
def test_selectpool_for_normalvolume(self):
|
||||
self.vol['size'] = 10
|
||||
pool = self._select_leastused_poolnumber(self.vol,
|
||||
self.pools,
|
||||
self.xml)
|
||||
self.assertEqual(1, pool, "selected pool should be 1")
|
||||
# config:pool_pools=[1]
|
||||
self.vol['size'] = 999999999999
|
||||
with self.assertRaisesRegexp(exception.VolumeBackendAPIException,
|
||||
'No available pools found.'):
|
||||
pool = self._select_leastused_poolnumber(self.vol,
|
||||
self.pools,
|
||||
self.xml)
|
||||
|
||||
def test_selectpool_for_migratevolume(self):
|
||||
self.vol['id'] = "46045673-41e7-44a7-9333-02f07feab04b"
|
||||
self.vol['size'] = 10
|
||||
self.vol['pool_num'] = 0
|
||||
pool = self._select_migrate_poolnumber(self.vol,
|
||||
self.pools,
|
||||
self.xml,
|
||||
[1])
|
||||
self.assertEqual(1, pool, "selected pool should be 1")
|
||||
self.vol['id'] = "1febb976-86d0-42ed-9bc0-4aa3e158f27d"
|
||||
self.vol['size'] = 10
|
||||
self.vol['pool_num'] = 1
|
||||
pool = self._select_migrate_poolnumber(self.vol,
|
||||
self.pools,
|
||||
self.xml,
|
||||
[1])
|
||||
self.assertEqual(-1, pool, "selected pool is the same pool(return -1)")
|
||||
self.vol['size'] = 999999999999
|
||||
with self.assertRaisesRegexp(exception.VolumeBackendAPIException,
|
||||
'No available pools found.'):
|
||||
pool = self._select_migrate_poolnumber(self.vol,
|
||||
self.pools,
|
||||
self.xml,
|
||||
[1])
|
||||
|
||||
def test_selectpool_for_snapvolume(self):
|
||||
self.vol['size'] = 10
|
||||
savePool1 = self.pools[1]['free']
|
||||
self.pools[1]['free'] = 0
|
||||
pool = self._select_dsv_poolnumber(self.vol, self.pools)
|
||||
self.assertEqual(2, pool, "selected pool should be 2")
|
||||
# config:pool_backup_pools=[2]
|
||||
self.pools[1]['free'] = savePool1
|
||||
|
||||
if len(self.pools[0]['ld_list']) is 1024:
|
||||
savePool2 = self.pools[2]['free']
|
||||
savePool3 = self.pools[3]['free']
|
||||
self.pools[2]['free'] = 0
|
||||
self.pools[3]['free'] = 0
|
||||
with self.assertRaisesRegexp(exception.VolumeBackendAPIException,
|
||||
'No available pools found.'):
|
||||
pool = self._select_dsv_poolnumber(self.vol, self.pools)
|
||||
self.pools[2]['free'] = savePool2
|
||||
self.pools[3]['free'] = savePool3
|
||||
|
||||
self.vol['size'] = 999999999999
|
||||
pool = self._select_dsv_poolnumber(self.vol, self.pools)
|
||||
self.assertEqual(2, pool, "selected pool should be 2")
|
||||
# config:pool_backup_pools=[2]
|
||||
|
||||
def test_selectpool_for_ddrvolume(self):
|
||||
self.vol['size'] = 10
|
||||
pool = self._select_ddr_poolnumber(self.vol,
|
||||
self.pools,
|
||||
self.xml,
|
||||
10)
|
||||
self.assertEqual(2, pool, "selected pool should be 2")
|
||||
# config:pool_backup_pools=[2]
|
||||
|
||||
savePool2 = self.pools[2]['free']
|
||||
savePool3 = self.pools[3]['free']
|
||||
self.pools[2]['free'] = 0
|
||||
self.pools[3]['free'] = 0
|
||||
with self.assertRaisesRegexp(exception.VolumeBackendAPIException,
|
||||
'No available pools found.'):
|
||||
pool = self._select_ddr_poolnumber(self.vol,
|
||||
self.pools,
|
||||
self.xml,
|
||||
10)
|
||||
self.pools[2]['free'] = savePool2
|
||||
self.pools[3]['free'] = savePool3
|
||||
|
||||
self.vol['size'] = 999999999999
|
||||
with self.assertRaisesRegexp(exception.VolumeBackendAPIException,
|
||||
'No available pools found.'):
|
||||
pool = self._select_ddr_poolnumber(self.vol,
|
||||
self.pools,
|
||||
self.xml,
|
||||
999999999999)
|
||||
|
||||
def test_selectpool_for_volddrvolume(self):
|
||||
self.vol['size'] = 10
|
||||
pool = self._select_volddr_poolnumber(self.vol,
|
||||
self.pools,
|
||||
self.xml,
|
||||
10)
|
||||
self.assertEqual(1, pool, "selected pool should be 1")
|
||||
# config:pool_backup_pools=[2]
|
||||
|
||||
savePool0 = self.pools[0]['free']
|
||||
savePool1 = self.pools[1]['free']
|
||||
self.pools[0]['free'] = 0
|
||||
self.pools[1]['free'] = 0
|
||||
with self.assertRaisesRegexp(exception.VolumeBackendAPIException,
|
||||
'No available pools found.'):
|
||||
pool = self._select_volddr_poolnumber(self.vol,
|
||||
self.pools,
|
||||
self.xml,
|
||||
10)
|
||||
self.pools[0]['free'] = savePool0
|
||||
self.pools[1]['free'] = savePool1
|
||||
|
||||
self.vol['size'] = 999999999999
|
||||
with self.assertRaisesRegexp(exception.VolumeBackendAPIException,
|
||||
'No available pools found.'):
|
||||
pool = self._select_volddr_poolnumber(self.vol,
|
||||
self.pools,
|
||||
self.xml,
|
||||
999999999999)
|
||||
|
||||
|
||||
class VolumeCreateTest(volume_helper.MStorageDriver, unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self._common = volume_common_test.MStorageVolCommDummy(1, 2, 3)
|
||||
self.do_setup(None)
|
||||
self.vol = {}
|
||||
self._properties = self._common.get_conf_properties()
|
||||
self._cli = self._properties['cli']
|
||||
self.xml = self._cli.view_all(self._properties['ismview_path'])
|
||||
(self.pools,
|
||||
self.lds,
|
||||
self.ldsets,
|
||||
self.used_ldns,
|
||||
self.hostports,
|
||||
self.max_ld_count) = self._common.configs(self.xml)
|
||||
|
||||
def tearDown(self):
|
||||
pass
|
||||
|
||||
def test_validate_migrate_volume(self):
|
||||
self.vol['id'] = "46045673-41e7-44a7-9333-02f07feab04b"
|
||||
self.vol['size'] = 10
|
||||
self.vol['status'] = 'available'
|
||||
self._validate_migrate_volume(self.vol, self.xml)
|
||||
|
||||
self.vol['id'] = "46045673-41e7-44a7-9333-02f07feab04b"
|
||||
self.vol['size'] = 10
|
||||
self.vol['status'] = 'creating'
|
||||
with self.assertRaisesRegexp(exception.VolumeBackendAPIException,
|
||||
'Specified Logical Disk'
|
||||
' LX:287RbQoP7VdwR1WsPC2fZT'
|
||||
' is not available.'):
|
||||
self._validate_migrate_volume(self.vol, self.xml)
|
||||
|
||||
self.vol['id'] = "AAAAAAAA"
|
||||
self.vol['size'] = 10
|
||||
self.vol['status'] = 'available'
|
||||
with self.assertRaisesRegexp(exception.NotFound,
|
||||
'Logical Disk `LX:37mA82`'
|
||||
' does not exist.'):
|
||||
self._validate_migrate_volume(self.vol, self.xml)
|
||||
|
||||
def test_extend_volume(self):
|
||||
mv = self.lds["LX:287RbQoP7VdwR1WsPC2fZT"] # MV-LDN:0 RV-LDN:4
|
||||
rvs = self._can_extend_capacity(10, self.pools, self.lds, mv)
|
||||
self.assertEqual("LX:287RbQoP7VdwR1WsPC2fZT_back", rvs[4]['ldname'])
|
||||
with self.assertRaisesRegexp(exception.VolumeBackendAPIException,
|
||||
'Not enough pool capacity.'
|
||||
' pool_number=0,'
|
||||
' size_increase=1073741822926258176'):
|
||||
self._can_extend_capacity(1000000000,
|
||||
self.pools,
|
||||
self.lds, mv)
|
||||
|
||||
self.vol['id'] = "46045673-41e7-44a7-9333-02f07feab04b" # MV
|
||||
self.vol['size'] = 1
|
||||
self.vol['status'] = 'available'
|
||||
self.extend_volume(self.vol, 10)
|
||||
|
||||
self.vol['id'] = "00046058-d38e-7f60-67b7-59ed65e54225" # RV
|
||||
self.vol['size'] = 1
|
||||
self.vol['status'] = 'available'
|
||||
with self.assertRaisesRegexp(exception.VolumeBackendAPIException,
|
||||
'RPL Attribute Error.'
|
||||
' RPL Attribute = RV'):
|
||||
self.extend_volume(self.vol, 10)
|
||||
|
||||
|
||||
class BindLDTest(volume_helper.MStorageDriver, unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self._common = volume_common_test.MStorageVolCommDummy(1, 2, 3)
|
||||
self.do_setup(None)
|
||||
self.vol = {}
|
||||
self._properties = self._common.get_conf_properties()
|
||||
self._cli = self._properties['cli']
|
||||
self.xml = self._cli.view_all(self._properties['ismview_path'])
|
||||
(self.pools,
|
||||
self.lds,
|
||||
self.ldsets,
|
||||
self.used_ldns,
|
||||
self.hostports,
|
||||
self.max_ld_count) = self._common.configs(self.xml)
|
||||
self.src = {}
|
||||
mock_bindld = mock.Mock()
|
||||
self._bind_ld = mock_bindld
|
||||
self._bind_ld.return_value = (0, 0, 0)
|
||||
|
||||
def test_bindld_CreateVolume(self):
|
||||
self.vol['id'] = "AAAAAAAA"
|
||||
self.vol['size'] = 1
|
||||
self.vol['migration_status'] = "success"
|
||||
self.create_volume(self.vol)
|
||||
self._bind_ld.assert_called_once_with(
|
||||
self.vol, self.vol['size'], None,
|
||||
self._convert_id2name_in_migrate,
|
||||
self._select_leastused_poolnumber)
|
||||
|
||||
def test_bindld_CreateCloneVolume(self):
|
||||
self.vol['id'] = "AAAAAAAA"
|
||||
self.vol['size'] = 1
|
||||
self.vol['migration_status'] = "success"
|
||||
self.src['id'] = "46045673-41e7-44a7-9333-02f07feab04b"
|
||||
self.src['size'] = 1
|
||||
self.create_cloned_volume(self.vol, self.src)
|
||||
self._bind_ld.assert_called_once_with(
|
||||
self.vol, self.vol['size'], None,
|
||||
self._convert_id2name,
|
||||
self._select_leastused_poolnumber)
|
||||
|
||||
|
||||
class BindLDTest_iSCSISnap(volume_helper.MStorageDriver,
|
||||
unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self._common = volume_common_test.MStorageVolCommDummy(1, 2, 3)
|
||||
self.do_setup(None)
|
||||
self.vol = {}
|
||||
self._properties = self._common.get_conf_properties()
|
||||
self._cli = self._properties['cli']
|
||||
self.xml = self._cli.view_all(self._properties['ismview_path'])
|
||||
(self.pools,
|
||||
self.lds,
|
||||
self.ldsets,
|
||||
self.used_ldns,
|
||||
self.hostports,
|
||||
self.max_ld_count) = self._common.configs(self.xml)
|
||||
self.snap = {}
|
||||
mock_bindld = mock.Mock()
|
||||
self._bind_ld = mock_bindld
|
||||
self._bind_ld.return_value = (0, 0, 0)
|
||||
|
||||
def test_bindld_CreateSnapshot(self):
|
||||
self.snap['id'] = "AAAAAAAA"
|
||||
self.snap['volume_id'] = "1febb976-86d0-42ed-9bc0-4aa3e158f27d"
|
||||
self.snap['size'] = 10
|
||||
self.create_snapshot(self.snap)
|
||||
self._bind_ld.assert_called_once_with(
|
||||
self.snap, 10, None,
|
||||
self._convert_id2snapname,
|
||||
self._select_ddr_poolnumber, 10)
|
||||
|
||||
def test_bindld_CreateFromSnapshot(self):
|
||||
self.vol['id'] = "AAAAAAAA"
|
||||
self.vol['size'] = 1
|
||||
self.vol['migration_status'] = "success"
|
||||
self.snap['id'] = "63410c76-2f12-4473-873d-74a63dfcd3e2"
|
||||
self.snap['volume_id'] = "92dbc7f4-dbc3-4a87-aef4-d5a2ada3a9af"
|
||||
self.create_volume_from_snapshot(self.vol, self.snap)
|
||||
self._bind_ld.assert_called_once_with(
|
||||
self.vol, 10, None,
|
||||
self._convert_id2name,
|
||||
self._select_volddr_poolnumber, 10)
|
||||
|
||||
|
||||
class BindLDTest_Snap(volume_helper.MStorageDSVDriver, unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self._common = volume_common_test.MStorageVolCommDummy(1, 2, 3)
|
||||
self.do_setup(None)
|
||||
self.vol = {}
|
||||
self._properties = self._common.get_conf_properties()
|
||||
self._cli = self._properties['cli']
|
||||
self.xml = self._cli.view_all(self._properties['ismview_path'])
|
||||
(self.pools,
|
||||
self.lds,
|
||||
self.ldsets,
|
||||
self.used_ldns,
|
||||
self.hostports,
|
||||
self.max_ld_count) = self._common.configs(self.xml)
|
||||
self.snap = {}
|
||||
mock_bindld = mock.Mock()
|
||||
self._bind_ld = mock_bindld
|
||||
self._bind_ld.return_value = (0, 0, 0)
|
||||
|
||||
def test_bindld_CreateFromSnapshot(self):
|
||||
self.vol['id'] = "AAAAAAAA"
|
||||
self.vol['size'] = 1
|
||||
self.vol['migration_status'] = "success"
|
||||
self.snap['id'] = "63410c76-2f12-4473-873d-74a63dfcd3e2"
|
||||
self.snap['volume_id'] = "1febb976-86d0-42ed-9bc0-4aa3e158f27d"
|
||||
self.create_volume_from_snapshot(self.vol, self.snap)
|
||||
self._bind_ld.assert_called_once_with(
|
||||
self.vol, 1, None,
|
||||
self._convert_id2name,
|
||||
self._select_volddr_poolnumber, 1)
|
||||
|
||||
|
||||
class ExportTest(volume_helper.MStorageDriver, unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self._common = volume_common_test.MStorageVolCommDummy(1, 2, 3)
|
||||
self.do_setup(None)
|
||||
self.vol = {}
|
||||
self._properties = self._common.get_conf_properties()
|
||||
self._cli = self._properties['cli']
|
||||
self.xml = self._cli.view_all(self._properties['ismview_path'])
|
||||
(self.pools,
|
||||
self.lds,
|
||||
self.ldsets,
|
||||
self.used_ldns,
|
||||
self.hostports,
|
||||
self.max_ld_count) = self._common.configs(self.xml)
|
||||
mock_getldset = mock.Mock()
|
||||
self._common.get_ldset = mock_getldset
|
||||
self._common.get_ldset.return_value = self.ldsets["LX:OpenStack0"]
|
||||
|
||||
def tearDown(self):
|
||||
pass
|
||||
|
||||
def test_iscsi_portal(self):
|
||||
self.vol['id'] = "46045673-41e7-44a7-9333-02f07feab04b"
|
||||
self.vol['size'] = 10
|
||||
self.vol['status'] = None
|
||||
self.vol['migration_status'] = None
|
||||
connector = {'initiator': "iqn.1994-05.com.redhat:d1d8e8f23255"}
|
||||
self.iscsi_do_export(None, self.vol, connector)
|
||||
|
||||
def test_fc_do_export(self):
|
||||
self.vol['id'] = "46045673-41e7-44a7-9333-02f07feab04b"
|
||||
self.vol['size'] = 10
|
||||
self.vol['status'] = None
|
||||
self.vol['migration_status'] = None
|
||||
connector = {'wwpns': ["1000-0090-FAA0-723A", "1000-0090-FAA0-723B"]}
|
||||
self.fc_do_export(None, self.vol, connector)
|
||||
|
||||
def test_remove_export(self):
|
||||
self.vol['id'] = "46045673-41e7-44a7-9333-02f07feab04b"
|
||||
self.vol['size'] = 10
|
||||
self.vol['status'] = 'uploading'
|
||||
self.vol['attach_status'] = 'attached'
|
||||
self.vol['migration_status'] = None
|
||||
context = mock.Mock()
|
||||
ret = self.remove_export(context, self.vol)
|
||||
self.assertIsNone(ret)
|
||||
|
||||
self.vol['attach_status'] = None
|
||||
|
||||
self.vol['status'] = 'downloading'
|
||||
with self.assertRaisesRegexp(exception.VolumeBackendAPIException,
|
||||
'Failed to unregister Logical Disk from'
|
||||
' Logical Disk Set \(iSM31064\)'):
|
||||
self.remove_export(context, self.vol)
|
||||
|
||||
self.vol['status'] = None
|
||||
migstat = 'target:1febb976-86d0-42ed-9bc0-4aa3e158f27d'
|
||||
self.vol['migration_status'] = migstat
|
||||
ret = self.remove_export(context, self.vol)
|
||||
self.assertIsNone(ret)
|
||||
|
||||
def test_iscsi_initialize_connection(self):
|
||||
self.vol['id'] = "46045673-41e7-44a7-9333-02f07feab04b"
|
||||
loc = "127.0.0.1:3260:1 iqn.2010-10.org.openstack:volume-00000001 88"
|
||||
self.vol['provider_location'] = loc
|
||||
connector = {'initiator': "iqn.1994-05.com.redhat:d1d8e8f23255",
|
||||
'multipath': True}
|
||||
info = self._iscsi_initialize_connection(self.vol, connector)
|
||||
self.assertEqual('iscsi', info['driver_volume_type'])
|
||||
self.assertEqual('iqn.2010-10.org.openstack:volume-00000001',
|
||||
info['data']['target_iqn'])
|
||||
self.assertEqual('127.0.0.1:3260', info['data']['target_portal'])
|
||||
self.assertEqual(88, info['data']['target_lun'])
|
||||
self.assertEqual('iqn.2010-10.org.openstack:volume-00000001',
|
||||
info['data']['target_iqns'][0])
|
||||
self.assertEqual('127.0.0.1:3260', info['data']['target_portals'][0])
|
||||
self.assertEqual(88, info['data']['target_luns'][0])
|
||||
|
||||
def test_fc_initialize_connection(self):
|
||||
self.vol['id'] = "46045673-41e7-44a7-9333-02f07feab04b"
|
||||
self.vol['migration_status'] = None
|
||||
connector = {'wwpns': ["1000-0090-FAA0-723A", "1000-0090-FAA0-723B"]}
|
||||
info = self._fc_initialize_connection(self.vol, connector)
|
||||
self.assertEqual('fibre_channel', info['driver_volume_type'])
|
||||
self.assertEqual('2100000991020012', info['data']['target_wwn'][0])
|
||||
self.assertEqual('2200000991020012', info['data']['target_wwn'][1])
|
||||
self.assertEqual('2900000991020012', info['data']['target_wwn'][2])
|
||||
self.assertEqual('2A00000991020012', info['data']['target_wwn'][3])
|
||||
self.assertEqual(
|
||||
'2100000991020012',
|
||||
info['data']['initiator_target_map']['1000-0090-FAA0-723A'][0])
|
||||
self.assertEqual(
|
||||
'2100000991020012',
|
||||
info['data']['initiator_target_map']['1000-0090-FAA0-723B'][0])
|
||||
self.assertEqual(
|
||||
'2200000991020012',
|
||||
info['data']['initiator_target_map']['1000-0090-FAA0-723A'][1])
|
||||
self.assertEqual(
|
||||
'2200000991020012',
|
||||
info['data']['initiator_target_map']['1000-0090-FAA0-723B'][1])
|
||||
self.assertEqual(
|
||||
'2900000991020012',
|
||||
info['data']['initiator_target_map']['1000-0090-FAA0-723A'][2])
|
||||
self.assertEqual(
|
||||
'2900000991020012',
|
||||
info['data']['initiator_target_map']['1000-0090-FAA0-723B'][2])
|
||||
self.assertEqual(
|
||||
'2A00000991020012',
|
||||
info['data']['initiator_target_map']['1000-0090-FAA0-723A'][3])
|
||||
self.assertEqual(
|
||||
'2A00000991020012',
|
||||
info['data']['initiator_target_map']['1000-0090-FAA0-723B'][3])
|
||||
|
||||
def test_fc_terminate_connection(self):
|
||||
self.vol['id'] = "46045673-41e7-44a7-9333-02f07feab04b"
|
||||
connector = {'wwpns': ["1000-0090-FAA0-723A", "1000-0090-FAA0-723B"]}
|
||||
info = self._fc_terminate_connection(self.vol, connector)
|
||||
self.assertEqual('fibre_channel', info['driver_volume_type'])
|
||||
self.assertEqual('2100000991020012', info['data']['target_wwn'][0])
|
||||
self.assertEqual('2200000991020012', info['data']['target_wwn'][1])
|
||||
self.assertEqual('2900000991020012', info['data']['target_wwn'][2])
|
||||
self.assertEqual('2A00000991020012', info['data']['target_wwn'][3])
|
||||
self.assertEqual(
|
||||
'2100000991020012',
|
||||
info['data']['initiator_target_map']['1000-0090-FAA0-723A'][0])
|
||||
self.assertEqual(
|
||||
'2100000991020012',
|
||||
info['data']['initiator_target_map']['1000-0090-FAA0-723B'][0])
|
||||
self.assertEqual(
|
||||
'2200000991020012',
|
||||
info['data']['initiator_target_map']['1000-0090-FAA0-723A'][1])
|
||||
self.assertEqual(
|
||||
'2200000991020012',
|
||||
info['data']['initiator_target_map']['1000-0090-FAA0-723B'][1])
|
||||
self.assertEqual(
|
||||
'2900000991020012',
|
||||
info['data']['initiator_target_map']['1000-0090-FAA0-723A'][2])
|
||||
self.assertEqual(
|
||||
'2900000991020012',
|
||||
info['data']['initiator_target_map']['1000-0090-FAA0-723B'][2])
|
||||
self.assertEqual(
|
||||
'2A00000991020012',
|
||||
info['data']['initiator_target_map']['1000-0090-FAA0-723A'][3])
|
||||
self.assertEqual(
|
||||
'2A00000991020012',
|
||||
info['data']['initiator_target_map']['1000-0090-FAA0-723B'][3])
|
||||
|
||||
|
||||
class DeleteDSVVolume_test(volume_helper.MStorageDSVDriver,
|
||||
unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self._common = volume_common_test.MStorageVolCommDummy(1, 2, 3)
|
||||
self.do_setup(None)
|
||||
self.vol = {}
|
||||
self._properties = self._common.get_conf_properties()
|
||||
self._cli = self._properties['cli']
|
||||
self.xml = self._cli.view_all(self._properties['ismview_path'])
|
||||
(self.pools,
|
||||
self.lds,
|
||||
self.ldsets,
|
||||
self.used_ldns,
|
||||
self.hostports,
|
||||
self.max_ld_count) = self._common.configs(self.xml)
|
||||
|
||||
def patch_query_MV_RV_status(self, ldname, rpltype):
|
||||
return 'replicated'
|
||||
|
||||
@mock.patch('cinder.tests.unit.volume.drivers.nec.cli_test.'
|
||||
'MStorageISMCLI.query_MV_RV_status', patch_query_MV_RV_status)
|
||||
def test_delete_volume(self):
|
||||
# MV not separated
|
||||
self.vol['id'] = "46045673-41e7-44a7-9333-02f07feab04b"
|
||||
with self.assertRaisesRegexp(exception.VolumeBackendAPIException,
|
||||
'Specified Logical Disk'
|
||||
' LX:287RbQoP7VdwR1WsPC2fZT'
|
||||
' has been copied.'):
|
||||
self.delete_volume(self.vol)
|
||||
# RV not separated
|
||||
self.vol['id'] = "00046058-d38e-7f60-67b7-59ed65e54225"
|
||||
with self.assertRaisesRegexp(exception.VolumeBackendAPIException,
|
||||
'Specified Logical Disk'
|
||||
' LX:20000009910200140005'
|
||||
' has been copied.'):
|
||||
self.delete_volume(self.vol)
|
||||
|
||||
def test_delete_snapshot(self):
|
||||
self.vol['id'] = "63410c76-2f12-4473-873d-74a63dfcd3e2"
|
||||
self.vol['volume_id'] = "1febb976-86d0-42ed-9bc0-4aa3e158f27d"
|
||||
ret = self.delete_snapshot(self.vol)
|
||||
self.assertIsNone(ret)
|
299
cinder/tests/unit/volume/drivers/nec/volume_common_test.py
Normal file
299
cinder/tests/unit/volume/drivers/nec/volume_common_test.py
Normal file
@ -0,0 +1,299 @@
|
||||
#
|
||||
# Copyright (c) 2016 NEC Corporation.
|
||||
# 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.
|
||||
|
||||
from lxml import etree
|
||||
|
||||
from oslo_utils import units
|
||||
|
||||
from cinder.tests.unit.volume.drivers.nec import cli_test
|
||||
from cinder.volume.drivers.nec import volume_common
|
||||
|
||||
|
||||
class MStorageVolCommDummy(object):
|
||||
def __init__(self, configuration, host, driver_name):
|
||||
super(MStorageVolCommDummy, self).__init__()
|
||||
self._properties = self.get_conf_properties()
|
||||
self._context = None
|
||||
|
||||
def set_context(self, context):
|
||||
self._context = context
|
||||
|
||||
def get_conf(self, host):
|
||||
return self.get_conf_properties()
|
||||
|
||||
def get_conf_properties(self, conf=None):
|
||||
conf = {
|
||||
'cli': None,
|
||||
'cli_fip': '10.64.169.250',
|
||||
'cli_user': 'sysadmin',
|
||||
'cli_password': 'sys123',
|
||||
'cli_privkey': 'sys123',
|
||||
'pool_pools': [0, 1],
|
||||
'pool_backup_pools': [2, 3],
|
||||
'pool_actual_free_capacity': 50000000000,
|
||||
'ldset_name': 'LX:OpenStack0',
|
||||
'ldset_controller_node_name': 'LX:node0',
|
||||
'ld_name_format': 'LX:%s',
|
||||
'ld_backupname_format': 'LX:%s_back',
|
||||
'ld_backend_max_count': 1024,
|
||||
'thread_timeout': 5,
|
||||
'ismview_dir': 'view',
|
||||
'ismview_alloptimize': '',
|
||||
'ssh_pool_port_number': 22,
|
||||
'diskarray_name': 'node0',
|
||||
'queryconfig_view': '',
|
||||
'ismview_path': None,
|
||||
'driver_name': 'MStorageISCSIDriver',
|
||||
'config_group': '',
|
||||
'configuration': '',
|
||||
'vendor_name': 'nec',
|
||||
'products': '',
|
||||
'backend_name': '',
|
||||
'portal_number': 2
|
||||
}
|
||||
conf['cli'] = cli_test.MStorageISMCLI(conf)
|
||||
return conf
|
||||
|
||||
@staticmethod
|
||||
def get_ldname(volid, volformat):
|
||||
return volume_common.MStorageVolumeCommon.get_ldname(volid, volformat)
|
||||
|
||||
def get_diskarray_max_ld_count(self):
|
||||
return 8192
|
||||
|
||||
def get_pool_config(self, xml, root):
|
||||
pools = {}
|
||||
for xmlobj in root.xpath('./'
|
||||
'CMD_REQUEST/'
|
||||
'CHAPTER[@name="Pool"]/'
|
||||
'OBJECT[@name="Pool"]'):
|
||||
section = xmlobj.find('./SECTION[@name="Pool Detail Information"]')
|
||||
unit = section.find('./UNIT[@name="Pool No.(h)"]')
|
||||
pool_num = int(unit.text, 16)
|
||||
unit = section.find('UNIT[@name="Pool Capacity"]')
|
||||
total = int(unit.text, 10)
|
||||
unit = section.find('UNIT[@name="Free Pool Capacity"]')
|
||||
free = int(unit.text, 10)
|
||||
if self._properties['pool_actual_free_capacity']:
|
||||
unit = section.find('UNIT[@name="Used Pool Capacity"]')
|
||||
used = int(unit.text, 10)
|
||||
for section in xmlobj.xpath('./SECTION[@name='
|
||||
'"Virtual Capacity Pool '
|
||||
'Information"]'):
|
||||
unit = section.find('UNIT[@name="Actual Capacity"]')
|
||||
total = int(unit.text, 10)
|
||||
free = total - used
|
||||
pool = {'pool_num': pool_num,
|
||||
'total': total,
|
||||
'free': free,
|
||||
'ld_list': []}
|
||||
pools[pool_num] = pool
|
||||
return pools
|
||||
|
||||
def get_ld_config(self, xml, root, pools):
|
||||
lds = {}
|
||||
used_ldns = []
|
||||
for section in root.xpath('./'
|
||||
'CMD_REQUEST/'
|
||||
'CHAPTER[@name="Logical Disk"]/'
|
||||
'OBJECT[@name="Logical Disk"]/'
|
||||
'SECTION[@name="LD Detail Information"]'):
|
||||
unit = section.find('./UNIT[@name="LDN(h)"]')
|
||||
ldn = int(unit.text, 16)
|
||||
unit = section.find('./UNIT[@name="OS Type"]')
|
||||
ostype = unit.text if unit.text is not None else ''
|
||||
unit = section.find('./UNIT[@name="LD Name"]')
|
||||
ldname = ostype + ':' + unit.text
|
||||
unit = section.find('./UNIT[@name="Pool No.(h)"]')
|
||||
pool_num = int(unit.text, 16)
|
||||
|
||||
unit = section.find('./UNIT[@name="LD Capacity"]')
|
||||
|
||||
# byte capacity transform GB capacity.
|
||||
ld_capacity = int(unit.text, 10) // units.Gi
|
||||
|
||||
unit = section.find('./UNIT[@name="RPL Attribute"]')
|
||||
rplatr = unit.text
|
||||
|
||||
unit = section.find('./UNIT[@name="Purpose"]')
|
||||
purpose = unit.text
|
||||
|
||||
ld = {'ldname': ldname,
|
||||
'ldn': ldn,
|
||||
'pool_num': pool_num,
|
||||
'ld_capacity': ld_capacity,
|
||||
'RPL Attribute': rplatr,
|
||||
'Purpose': purpose}
|
||||
pools[pool_num]['ld_list'].append(ld)
|
||||
lds[ldname] = ld
|
||||
used_ldns.append(ldn)
|
||||
return lds, used_ldns
|
||||
|
||||
def get_iscsi_ldset_config(self, xml, root):
|
||||
ldsets = {}
|
||||
for xmlobj in root.xpath('./'
|
||||
'CMD_REQUEST/'
|
||||
'CHAPTER[@name="Access Control"]/'
|
||||
'OBJECT[@name="LD Set(iSCSI)"]'):
|
||||
ldsetlds = {}
|
||||
portals = []
|
||||
for unit in xmlobj.xpath('./SECTION[@name="Portal"]/'
|
||||
'UNIT[@name="Portal"]'):
|
||||
if not unit.text.startswith('0.0.0.0:'):
|
||||
portals.append(unit.text)
|
||||
section = xmlobj.find('./SECTION[@name="LD Set(iSCSI)'
|
||||
' Information"]')
|
||||
if section is None:
|
||||
return ldsets
|
||||
unit = section.find('./UNIT[@name="Platform"]')
|
||||
platform = unit.text
|
||||
unit = section.find('./UNIT[@name="LD Set Name"]')
|
||||
ldsetname = platform + ':' + unit.text
|
||||
unit = section.find('./UNIT[@name="Target Mode"]')
|
||||
tmode = unit.text
|
||||
if tmode == 'Normal':
|
||||
unit = section.find('./UNIT[@name="Target Name"]')
|
||||
iqn = unit.text
|
||||
for section in xmlobj.xpath('./SECTION[@name="LUN/LD List"]'):
|
||||
unit = section.find('./UNIT[@name="LDN(h)"]')
|
||||
ldn = int(unit.text, 16)
|
||||
unit = section.find('./UNIT[@name="LUN(h)"]')
|
||||
lun = int(unit.text, 16)
|
||||
ld = {'ldn': ldn,
|
||||
'lun': lun,
|
||||
'iqn': iqn}
|
||||
ldsetlds[ldn] = ld
|
||||
elif tmode == 'Multi-Target':
|
||||
for section in xmlobj.xpath('./SECTION[@name='
|
||||
'"Target Information For '
|
||||
'Multi-Target Mode"]'):
|
||||
unit = section.find('./UNIT[@name="Target Name"]')
|
||||
iqn = unit.text
|
||||
unit = section.find('./UNIT[@name="LDN(h)"]')
|
||||
if unit.text.startswith('-'):
|
||||
continue
|
||||
ldn = int(unit.text, 16)
|
||||
unit = section.find('./UNIT[@name="LUN(h)"]')
|
||||
if unit.text.startswith('-'):
|
||||
continue
|
||||
lun = int(unit.text, 16)
|
||||
ld = {'ldn': ldn,
|
||||
'lun': lun,
|
||||
'iqn': iqn}
|
||||
ldsetlds[ldn] = ld
|
||||
ldset = {'ldsetname': ldsetname,
|
||||
'protocol': 'iSCSI',
|
||||
'portal_list': portals,
|
||||
'lds': ldsetlds}
|
||||
ldsets[ldsetname] = ldset
|
||||
return ldsets
|
||||
|
||||
def get_fc_ldset_config(self, xml, root):
|
||||
ldsets = {}
|
||||
for xmlobj in root.xpath('./'
|
||||
'CMD_REQUEST/'
|
||||
'CHAPTER[@name="Access Control"]/'
|
||||
'OBJECT[@name="LD Set(FC)"]'):
|
||||
ldsetlds = {}
|
||||
section = xmlobj.find('./SECTION[@name="LD Set(FC)'
|
||||
' Information"]')
|
||||
if section is None:
|
||||
return ldsets
|
||||
unit = section.find('./UNIT[@name="Platform"]')
|
||||
platform = unit.text
|
||||
unit = section.find('./UNIT[@name="LD Set Name"]')
|
||||
ldsetname = platform + ':' + unit.text
|
||||
wwpns = []
|
||||
ports = []
|
||||
for section in xmlobj.xpath('./SECTION[@name="Path List"]'):
|
||||
unit = section.find('./UNIT[@name="Path"]')
|
||||
if unit.text.find('(') != -1:
|
||||
ports.append(unit.text)
|
||||
else:
|
||||
wwpns.append(unit.text)
|
||||
for section in xmlobj.xpath('./SECTION[@name="LUN/LD List"]'):
|
||||
unit = section.find('./UNIT[@name="LDN(h)"]')
|
||||
ldn = int(unit.text, 16)
|
||||
unit = section.find('./UNIT[@name="LUN(h)"]')
|
||||
lun = int(unit.text, 16)
|
||||
ld = {'ldn': ldn,
|
||||
'lun': lun}
|
||||
ldsetlds[ldn] = ld
|
||||
ldset = {'ldsetname': ldsetname,
|
||||
'lds': ldsetlds,
|
||||
'protocol': 'FC',
|
||||
'wwpn': wwpns,
|
||||
'port': ports}
|
||||
ldsets[ldsetname] = ldset
|
||||
return ldsets
|
||||
|
||||
def get_hostport_config(self, xml, root):
|
||||
hostports = {}
|
||||
for section in root.xpath('./'
|
||||
'CMD_REQUEST/'
|
||||
'CHAPTER[@name="Controller"]/'
|
||||
'OBJECT[@name="Host Port"]/'
|
||||
'SECTION[@name="Host Director'
|
||||
'/Host Port Information"]'):
|
||||
unit = section.find('./UNIT[@name="Port No.(h)"]')
|
||||
units = unit.text.split('-')
|
||||
director = int(units[0], 16)
|
||||
port = int(units[1], 16)
|
||||
unit = section.find('./UNIT[@name="IP Address"]')
|
||||
if unit is not None:
|
||||
ip = unit.text
|
||||
protocol = 'iSCSI'
|
||||
wwpn = None
|
||||
else:
|
||||
ip = '0.0.0.0'
|
||||
protocol = 'FC'
|
||||
unit = section.find('./UNIT[@name="WWPN"]')
|
||||
wwpn = unit.text
|
||||
|
||||
# Port Link Status check Start.
|
||||
unit = section.find('./UNIT[@name="Link Status"]')
|
||||
hostport = {
|
||||
'director': director,
|
||||
'port': port,
|
||||
'ip': ip,
|
||||
'protocol': protocol,
|
||||
'wwpn': wwpn
|
||||
}
|
||||
if director not in hostports:
|
||||
hostports[director] = []
|
||||
hostports[director].append(hostport)
|
||||
return hostports
|
||||
|
||||
def configs(self, xml):
|
||||
root = etree.fromstring(xml)
|
||||
pools = self.get_pool_config(xml, root)
|
||||
lds, used_ldns = self.get_ld_config(xml, root, pools)
|
||||
iscsi_ldsets = self.get_iscsi_ldset_config(xml, root)
|
||||
fc_ldsets = self.get_fc_ldset_config(xml, root)
|
||||
hostports = self.get_hostport_config(xml, root)
|
||||
diskarray_max_ld_count = self.get_diskarray_max_ld_count()
|
||||
|
||||
ldsets = {}
|
||||
ldsets.update(iscsi_ldsets)
|
||||
ldsets.update(fc_ldsets)
|
||||
|
||||
return pools, lds, ldsets, used_ldns, hostports, diskarray_max_ld_count
|
||||
|
||||
def get_volume_type_qos_specs(self, volume):
|
||||
return {}
|
||||
|
||||
def check_io_parameter(self, specs):
|
||||
pass
|
0
cinder/volume/drivers/nec/__init__.py
Normal file
0
cinder/volume/drivers/nec/__init__.py
Normal file
781
cinder/volume/drivers/nec/cli.py
Normal file
781
cinder/volume/drivers/nec/cli.py
Normal file
@ -0,0 +1,781 @@
|
||||
#
|
||||
# Copyright (c) 2016 NEC Corporation.
|
||||
# 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.
|
||||
|
||||
import os
|
||||
import re
|
||||
import select
|
||||
import time
|
||||
import traceback
|
||||
|
||||
from oslo_log import log as logging
|
||||
from oslo_utils import excutils
|
||||
from oslo_utils import units
|
||||
|
||||
from cinder import coordination
|
||||
from cinder import exception
|
||||
from cinder.i18n import _
|
||||
from cinder import ssh_utils
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
retry_msgids = ['iSM31005', 'iSM31015', 'iSM42408', 'iSM42412']
|
||||
|
||||
|
||||
class MStorageISMCLI(object):
|
||||
"""SSH client."""
|
||||
|
||||
def __init__(self, properties):
|
||||
super(MStorageISMCLI, self).__init__()
|
||||
|
||||
self._sshpool = None
|
||||
self._properties = properties
|
||||
|
||||
def _execute(self, command, expected_status=[0], raise_exec=True):
|
||||
return self._sync_execute(command, self._properties['diskarray_name'],
|
||||
expected_status, raise_exec)
|
||||
|
||||
@coordination.synchronized('mstorage_ismcli_execute_{diskarray_name}')
|
||||
def _sync_execute(self, command, diskarray_name,
|
||||
expected_status=[0], raise_exec=True):
|
||||
retry_flag = True
|
||||
retry_count = 0
|
||||
while retry_flag is True:
|
||||
try:
|
||||
out, err, status = self._cli_execute(command, expected_status,
|
||||
False)
|
||||
if status != 0:
|
||||
errflg = 0
|
||||
errnum = out + err
|
||||
LOG.debug('ismcli failed (errnum=%s).', errnum)
|
||||
for retry_msgid in retry_msgids:
|
||||
if errnum.find(retry_msgid) >= 0:
|
||||
LOG.debug('`%(command)s` failed. '
|
||||
'%(name)s %(errnum)s '
|
||||
'retry_count=%(retry_count)d',
|
||||
{'command': command,
|
||||
'name': __name__,
|
||||
'errnum': errnum,
|
||||
'retry_count': retry_count})
|
||||
errflg = 1
|
||||
break
|
||||
if errflg == 1:
|
||||
retry_count += 1
|
||||
if retry_count >= 60:
|
||||
msg = (_('Timeout `%(command)s`.'
|
||||
' status=%(status)d, '
|
||||
'out="%(out)s", '
|
||||
'err="%(err)s".') %
|
||||
{'command': command,
|
||||
'status': status,
|
||||
'out': out,
|
||||
'err': err})
|
||||
raise exception.APITimeout(msg)
|
||||
time.sleep(5)
|
||||
continue
|
||||
else:
|
||||
if raise_exec is True:
|
||||
msg = _('Command `%s` failed.') % command
|
||||
raise exception.VolumeBackendAPIException(data=msg)
|
||||
except EOFError:
|
||||
with excutils.save_and_reraise_exception() as ctxt:
|
||||
LOG.debug('EOFError has occurred. '
|
||||
'%(name)s retry_count=%(retry_count)d',
|
||||
{'name': __name__,
|
||||
'retry_count': retry_count})
|
||||
retry_count += 1
|
||||
if retry_count < 60:
|
||||
ctxt.reraise = False
|
||||
time.sleep(5)
|
||||
continue
|
||||
retry_flag = False
|
||||
|
||||
return out, err, status
|
||||
|
||||
def _execute_nolock(self, command, expected_status=[0], raise_exec=True):
|
||||
retry_flag = True
|
||||
retry_count = 0
|
||||
while retry_flag is True:
|
||||
try:
|
||||
out, err, status = self._cli_execute(command, expected_status,
|
||||
raise_exec)
|
||||
except EOFError:
|
||||
with excutils.save_and_reraise_exception() as ctxt:
|
||||
LOG.debug('EOFError has occurred. '
|
||||
'%(name)s retry_count=%(retry_count)d',
|
||||
{'name': __name__,
|
||||
'retry_count': retry_count})
|
||||
retry_count += 1
|
||||
if retry_count < 60:
|
||||
ctxt.reraise = False
|
||||
time.sleep(5)
|
||||
continue
|
||||
retry_flag = False
|
||||
return out, err, status
|
||||
|
||||
def _cli_execute(self, command, expected_status=[0], raise_exec=True):
|
||||
if not self._sshpool:
|
||||
LOG.debug('ssh_utils.SSHPool execute.')
|
||||
self._sshpool = ssh_utils.SSHPool(
|
||||
self._properties['cli_fip'],
|
||||
self._properties['ssh_pool_port_number'],
|
||||
self._properties['ssh_conn_timeout'],
|
||||
self._properties['cli_user'],
|
||||
self._properties['cli_password'],
|
||||
privatekey=self._properties['cli_privkey'])
|
||||
|
||||
with self._sshpool.item() as ssh:
|
||||
LOG.debug('`%s` executing...', command)
|
||||
stdin, stdout, stderr = ssh.exec_command(command)
|
||||
stdin.close()
|
||||
channel = stdout.channel
|
||||
_out, _err = [], []
|
||||
while 1:
|
||||
select.select([channel], [], [])
|
||||
if channel.recv_ready():
|
||||
_out.append(channel.recv(4096))
|
||||
continue
|
||||
if channel.recv_stderr_ready():
|
||||
_err.append(channel.recv_stderr(4096))
|
||||
continue
|
||||
if channel.exit_status_ready():
|
||||
status = channel.recv_exit_status()
|
||||
break
|
||||
LOG.debug('`%(command)s` done. status=%(status)d.',
|
||||
{'command': command, 'status': status})
|
||||
out, err = ''.join(_out), ''.join(_err)
|
||||
if expected_status is not None and status not in expected_status:
|
||||
LOG.debug('`%(command)s` failed. status=%(status)d, '
|
||||
'out="%(out)s", err="%(err)s".',
|
||||
{'command': command, 'status': status,
|
||||
'out': out, 'err': err})
|
||||
if raise_exec is True:
|
||||
msg = _('Command `%s` failed.') % command
|
||||
raise exception.VolumeBackendAPIException(data=msg)
|
||||
return out, err, status
|
||||
|
||||
def view_all(self, conf_ismview_path=None, delete_ismview=True,
|
||||
cmd_lock=True):
|
||||
if self._properties['queryconfig_view'] is True:
|
||||
command = 'clioutmsg xml; iSMview'
|
||||
if self._properties['ismview_alloptimize'] is True:
|
||||
command += ' --alloptimize'
|
||||
else:
|
||||
command += ' -all'
|
||||
else:
|
||||
command = 'iSMquery -cinder -xml -all'
|
||||
if cmd_lock is True:
|
||||
out, err, status = self._execute(command)
|
||||
else:
|
||||
out, err, status = self._execute_nolock(command)
|
||||
|
||||
exstats = re.compile("(.*)ExitStatus(.*)\n")
|
||||
tmpout = exstats.sub('', out)
|
||||
out = tmpout
|
||||
if conf_ismview_path is not None:
|
||||
if delete_ismview:
|
||||
if os.path.exists(conf_ismview_path):
|
||||
os.remove(conf_ismview_path)
|
||||
LOG.debug('Remove clioutmsg xml to %s.',
|
||||
conf_ismview_path)
|
||||
else:
|
||||
with open(conf_ismview_path, 'w+') as f:
|
||||
f.write(out)
|
||||
LOG.debug('Wrote clioutmsg xml to %s.',
|
||||
conf_ismview_path)
|
||||
return out
|
||||
|
||||
def get_poolnumber_and_ldnumber(self, pools, used_ldns, max_ld_count):
|
||||
selected_pool = -1
|
||||
min_ldn = 0
|
||||
for pool in pools:
|
||||
nld = len(pool['ld_list'])
|
||||
if selected_pool == -1 or min_ldn > nld:
|
||||
selected_pool = pool['pool_num']
|
||||
min_ldn = nld
|
||||
for ldn in range(0, max_ld_count + 1):
|
||||
if ldn not in used_ldns:
|
||||
break
|
||||
if ldn > max_ld_count - 1:
|
||||
msg = _('All Logical Disk numbers are used.')
|
||||
LOG.error(msg)
|
||||
raise exception.VolumeBackendAPIException(data=msg)
|
||||
|
||||
return selected_pool, ldn
|
||||
|
||||
def ldbind(self, name, pool, ldn, size):
|
||||
"""Bind an LD and attach a nickname to it."""
|
||||
errnum = ""
|
||||
cmd = ('iSMcfg ldbind -poolnumber %(poolnumber)d -ldn %(ldn)d '
|
||||
'-capacity %(capacity)d -immediate'
|
||||
% {'poolnumber': pool, 'ldn': ldn,
|
||||
'capacity': size})
|
||||
out, err, status = self._execute(cmd, [0], False)
|
||||
errnum = err
|
||||
if status != 0:
|
||||
return False, errnum
|
||||
|
||||
cmd = ('iSMcfg nickname -ldn %(ldn)d -newname %(newname)s '
|
||||
'-immediate'
|
||||
% {'ldn': ldn, 'newname': name})
|
||||
self._execute(cmd)
|
||||
return True, errnum
|
||||
|
||||
def unbind(self, name):
|
||||
"""Unbind an LD."""
|
||||
cmd = 'iSMcfg ldunbind -ldname %s' % name
|
||||
self._execute(cmd)
|
||||
|
||||
def expand(self, ldn, capacity):
|
||||
"""Expand a LD."""
|
||||
cmd = ('iSMcfg ldexpand -ldn %(ldn)d -capacity %(capacity)d '
|
||||
'-unit gb'
|
||||
% {'ldn': ldn, 'capacity': capacity})
|
||||
self._execute(cmd)
|
||||
|
||||
def addldsetld(self, ldset, ldname, lun=None):
|
||||
"""Add an LD to specified LD Set."""
|
||||
if lun is None:
|
||||
cmd = ('iSMcfg addldsetld -ldset %(ldset)s '
|
||||
'-ldname %(ldname)s'
|
||||
% {'ldset': ldset, 'ldname': ldname})
|
||||
self._execute(cmd)
|
||||
else:
|
||||
cmd = ('iSMcfg addldsetld -ldset %(ldset)s -ldname %(ldname)s '
|
||||
'-lun %(lun)d'
|
||||
% {'ldset': ldset, 'ldname': ldname,
|
||||
'lun': lun})
|
||||
self._execute(cmd)
|
||||
|
||||
def delldsetld(self, ldset, ldname):
|
||||
"""Delete an LD from specified LD Set."""
|
||||
rtn = True
|
||||
errnum = ""
|
||||
cmd = ('iSMcfg delldsetld -ldset %(ldset)s '
|
||||
'-ldname %(ldname)s'
|
||||
% {'ldset': ldset,
|
||||
'ldname': ldname})
|
||||
out, err, status = self._execute(cmd, [0], False)
|
||||
errnum = err
|
||||
if status != 0:
|
||||
rtn = False
|
||||
return rtn, errnum
|
||||
|
||||
def changeldname(self, ldn, new_name, old_name=None):
|
||||
"""Rename nickname of LD."""
|
||||
if old_name is None:
|
||||
cmd = ('iSMcfg nickname -ldn %(ldn)d -newname %(newname)s '
|
||||
'-immediate'
|
||||
% {'ldn': ldn, 'newname': new_name})
|
||||
self._execute(cmd)
|
||||
else:
|
||||
cmd = ('iSMcfg nickname -ldname %(ldname)s '
|
||||
'-newname %(newname)s'
|
||||
% {'ldname': old_name,
|
||||
'newname': new_name})
|
||||
self._execute(cmd)
|
||||
|
||||
def setpair(self, mvname, rvname):
|
||||
"""Set pair."""
|
||||
cmd = ('iSMrc_pair -pair -mv %(mv)s -mvflg ld '
|
||||
'-rv %(rv)s -rvflg ld'
|
||||
% {'mv': mvname, 'rv': rvname})
|
||||
self._execute(cmd)
|
||||
|
||||
LOG.debug('Pair command completed. MV = %(mv)s RV = %(rv)s.',
|
||||
{'mv': mvname, 'rv': rvname})
|
||||
|
||||
def unpair(self, mvname, rvname, flag):
|
||||
"""Unset pair."""
|
||||
if flag == 'normal':
|
||||
cmd = ('iSMrc_pair -unpair -mv %(mv)s -mvflg ld '
|
||||
'-rv %(rv)s -rvflg ld'
|
||||
% {'mv': mvname, 'rv': rvname})
|
||||
self._execute(cmd)
|
||||
elif flag == 'force':
|
||||
cmd = ('iSMrc_pair -unpair -mv %(mv)s -mvflg ld '
|
||||
'-rv %(rv)s -rvflg ld -force all'
|
||||
% {'mv': mvname, 'rv': rvname})
|
||||
self._execute(cmd)
|
||||
else:
|
||||
LOG.debug('unpair flag ERROR. flag = %s', flag)
|
||||
|
||||
LOG.debug('Unpair command completed. MV = %(mv)s, RV = %(rv)s.',
|
||||
{'mv': mvname, 'rv': rvname})
|
||||
|
||||
def replicate(self, mvname, rvname, flag):
|
||||
if flag == 'full':
|
||||
cmd = ('iSMrc_replicate -mv %(mv)s -mvflg ld '
|
||||
'-rv %(rv)s -rvflg ld -nowait -cprange full '
|
||||
'-cpmode bg'
|
||||
% {'mv': mvname, 'rv': rvname})
|
||||
self._execute(cmd)
|
||||
else:
|
||||
cmd = ('iSMrc_replicate -mv %(mv)s -mvflg ld '
|
||||
'-rv %(rv)s -rvflg ld -nowait -cpmode bg'
|
||||
% {'mv': mvname, 'rv': rvname})
|
||||
self._execute(cmd)
|
||||
|
||||
LOG.debug('Replicate command completed. MV = %(mv)s RV = %(rv)s.',
|
||||
{'mv': mvname, 'rv': rvname})
|
||||
|
||||
def separate(self, mvname, rvname, flag):
|
||||
"""Separate for backup."""
|
||||
if flag == 'backup':
|
||||
cmd = ('iSMrc_separate -mv %(mv)s -mvflg ld '
|
||||
'-rv %(rv)s -rvflg ld '
|
||||
'-rvacc ro -rvuse complete -nowait'
|
||||
% {'mv': mvname, 'rv': rvname})
|
||||
self._execute(cmd)
|
||||
elif flag == 'restore' or flag == 'clone':
|
||||
cmd = ('iSMrc_separate -mv %(mv)s -mvflg ld '
|
||||
'-rv %(rv)s -rvflg ld '
|
||||
'-rvacc rw -rvuse immediate -nowait'
|
||||
% {'mv': mvname, 'rv': rvname})
|
||||
self._execute(cmd)
|
||||
elif flag == 'esv_restore' or flag == 'migrate':
|
||||
cmd = ('iSMrc_separate -mv %(mv)s -mvflg ld '
|
||||
'-rv %(rv)s -rvflg ld '
|
||||
'-rvacc rw -rvuse complete -nowait'
|
||||
% {'mv': mvname, 'rv': rvname})
|
||||
self._execute(cmd)
|
||||
else:
|
||||
LOG.debug('separate flag ERROR. flag = %s', flag)
|
||||
|
||||
LOG.debug('Separate command completed. MV = %(mv)s RV = %(rv)s.',
|
||||
{'mv': mvname, 'rv': rvname})
|
||||
|
||||
def query_MV_RV_status(self, ldname, rpltype):
|
||||
if rpltype == 'MV':
|
||||
cmd = ('iSMrc_query -mv %s -mvflg ld | '
|
||||
'while builtin read line;'
|
||||
'do if [[ "$line" =~ "Sync State" ]]; '
|
||||
'then builtin echo ${line:10};fi;'
|
||||
'done' % ldname)
|
||||
out, err, status = self._execute(cmd)
|
||||
elif rpltype == 'RV':
|
||||
cmd = ('iSMrc_query -rv %s -rvflg ld | '
|
||||
'while builtin read line;'
|
||||
'do if [[ "$line" =~ "Sync State" ]]; '
|
||||
'then builtin echo ${line:10};fi;'
|
||||
'done' % ldname)
|
||||
out, err, status = self._execute(cmd)
|
||||
else:
|
||||
LOG.debug('rpltype flag ERROR. rpltype = %s', rpltype)
|
||||
|
||||
query_status = out.strip()
|
||||
return query_status
|
||||
|
||||
def query_MV_RV_name(self, ldname, rpltype):
|
||||
if rpltype == 'MV':
|
||||
cmd = ('iSMrc_query -mv %s -mvflg ld | '
|
||||
'while builtin read line;'
|
||||
'do if [[ "$line" =~ "LD Name" ]]; '
|
||||
'then builtin echo ${line:7};fi;'
|
||||
'done' % ldname)
|
||||
out, err, status = self._execute(cmd)
|
||||
out = out.replace(ldname, "")
|
||||
elif rpltype == 'RV':
|
||||
cmd = ('iSMrc_query -rv %s -rvflg ld | '
|
||||
'while builtin read line;'
|
||||
'do if [[ "$line" =~ "LD Name" ]]; '
|
||||
'then builtin echo ${line:7};fi;'
|
||||
'done' % ldname)
|
||||
out, err, status = self._execute(cmd)
|
||||
out = out.replace(ldname, "")
|
||||
else:
|
||||
LOG.debug('rpltype flag ERROR. rpltype = %s', rpltype)
|
||||
|
||||
query_name = out.strip()
|
||||
return query_name
|
||||
|
||||
def query_MV_RV_diff(self, ldname, rpltype):
|
||||
if rpltype == 'MV':
|
||||
cmd = ('iSMrc_query -mv %s -mvflg ld | '
|
||||
'while builtin read line;'
|
||||
'do if [[ "$line" =~ "Separate Diff" ]]; '
|
||||
'then builtin echo ${line:13};fi;'
|
||||
'done' % ldname)
|
||||
out, err, status = self._execute(cmd)
|
||||
elif rpltype == 'RV':
|
||||
cmd = ('iSMrc_query -rv %s -rvflg ld | '
|
||||
'while builtin read line;'
|
||||
'do if [[ "$line" =~ "Separate Diff" ]]; '
|
||||
'then builtin echo ${line:13};fi;'
|
||||
'done' % ldname)
|
||||
out, err, status = self._execute(cmd)
|
||||
else:
|
||||
LOG.debug('rpltype flag ERROR. rpltype = %s', rpltype)
|
||||
|
||||
query_status = out.strip()
|
||||
return query_status
|
||||
|
||||
def backup_restore(self, volume_properties, unpairWait, canPairing=True):
|
||||
|
||||
# Setting Pair.
|
||||
flag = 'full'
|
||||
if canPairing is True:
|
||||
self.setpair(volume_properties['mvname'][3:],
|
||||
volume_properties['rvname'][3:])
|
||||
else:
|
||||
rv_diff = self.query_MV_RV_diff(volume_properties['rvname'][3:],
|
||||
'RV')
|
||||
rv_diff = int(rv_diff.replace('KB', ''), 10) // units.Ki
|
||||
if rv_diff != volume_properties['capacity']:
|
||||
flag = None
|
||||
|
||||
# Replicate.
|
||||
self.replicate(volume_properties['mvname'][3:],
|
||||
volume_properties['rvname'][3:], flag)
|
||||
|
||||
# Separate.
|
||||
self.separate(volume_properties['mvname'][3:],
|
||||
volume_properties['rvname'][3:],
|
||||
volume_properties['flag'])
|
||||
|
||||
unpairProc = unpairWait(volume_properties, self)
|
||||
unpairProc.run()
|
||||
|
||||
def check_ld_existed_rplstatus(self, lds, ldname, snapshot, flag):
|
||||
|
||||
if ldname not in lds:
|
||||
if flag == 'backup':
|
||||
LOG.debug('Volume Id not found. '
|
||||
'LD name = %(name)s volume_id = %(id)s.',
|
||||
{'name': ldname, 'id': snapshot['volume_id']})
|
||||
raise exception.NotFound(_('Logical Disk does not exist.'))
|
||||
elif flag == 'restore':
|
||||
LOG.debug('Snapshot Id not found. '
|
||||
'LD name = %(name)s snapshot_id = %(id)s.',
|
||||
{'name': ldname, 'id': snapshot['id']})
|
||||
raise exception.NotFound(_('Logical Disk does not exist.'))
|
||||
elif flag == 'delete':
|
||||
LOG.debug('LD `%(name)s` already unbound? '
|
||||
'snapshot_id = %(id)s.',
|
||||
{'name': ldname, 'id': snapshot['id']})
|
||||
return None
|
||||
else:
|
||||
LOG.debug('check_ld_existed_rplstatus flag error flag = %s.',
|
||||
flag)
|
||||
raise exception.NotFound(_('Logical Disk does not exist.'))
|
||||
|
||||
ld = lds[ldname]
|
||||
|
||||
if ld['RPL Attribute'] == 'IV':
|
||||
pass
|
||||
elif ld['RPL Attribute'] == 'MV':
|
||||
query_status = self.query_MV_RV_status(ldname[3:], 'MV')
|
||||
LOG.debug('query_status : %s.', query_status)
|
||||
if(query_status == 'separated'):
|
||||
# unpair.
|
||||
rvname = self.query_MV_RV_name(ldname[3:], 'MV')
|
||||
self.unpair(ldname[3:], rvname, 'force')
|
||||
else:
|
||||
msg = _('Specified Logical Disk %s has been copied.') % ldname
|
||||
LOG.error(msg)
|
||||
raise exception.VolumeBackendAPIException(data=msg)
|
||||
|
||||
elif ld['RPL Attribute'] == 'RV':
|
||||
query_status = self.query_MV_RV_status(ldname[3:], 'RV')
|
||||
if query_status == 'separated':
|
||||
# unpair.
|
||||
mvname = self.query_MV_RV_name(ldname[3:], 'RV')
|
||||
self.unpair(mvname, ldname[3:], 'force')
|
||||
else:
|
||||
msg = _('Specified Logical Disk %s has been copied.') % ldname
|
||||
LOG.error(msg)
|
||||
raise exception.VolumeBackendAPIException(data=msg)
|
||||
|
||||
return ld
|
||||
|
||||
def get_pair_lds(self, ldname, lds):
|
||||
query_status = self.query_MV_RV_name(ldname[3:], 'MV')
|
||||
query_status = query_status.split('\n')
|
||||
query_status = [query for query in query_status if query != '']
|
||||
LOG.debug('query_status=%s.', query_status)
|
||||
|
||||
pair_lds = {}
|
||||
for rvname in query_status:
|
||||
rvname = self._properties['ld_backupname_format'] % rvname
|
||||
if rvname not in lds:
|
||||
LOG.debug('LD `%s` is RDR pair?', rvname)
|
||||
else:
|
||||
ld = lds[rvname]
|
||||
ldn = ld['ldn']
|
||||
pair_lds[ldn] = ld
|
||||
|
||||
LOG.debug('pair_lds=%s.', pair_lds)
|
||||
return pair_lds
|
||||
|
||||
def snapshot_create(self, bvname, svname, poolnumber):
|
||||
"""Snapshot create."""
|
||||
cmd = ('iSMcfg generationadd -bvname %(bvname)s '
|
||||
'-poolnumber %(poolnumber)d -count 1 '
|
||||
'-svname %(svname)s'
|
||||
% {'bvname': bvname,
|
||||
'poolnumber': poolnumber,
|
||||
'svname': svname})
|
||||
self._execute(cmd)
|
||||
|
||||
cmd = ('iSMsc_create -bv %(bv)s -bvflg ld -sv %(sv)s '
|
||||
'-svflg ld'
|
||||
% {'bv': bvname[3:], 'sv': svname})
|
||||
self._execute(cmd)
|
||||
|
||||
def snapshot_delete(self, bvname, svname):
|
||||
"""Snapshot delete."""
|
||||
query_status = self.query_BV_SV_status(bvname[3:], svname)
|
||||
if query_status == 'snap/active':
|
||||
cmd = ('iSMsc_delete -bv %(bv)s -bvflg ld -sv %(sv)s '
|
||||
'-svflg ld'
|
||||
% {'bv': bvname[3:], 'sv': svname})
|
||||
self._execute(cmd)
|
||||
|
||||
while True:
|
||||
query_status = self.query_BV_SV_status(bvname[3:], svname)
|
||||
if query_status == 'snap/deleting':
|
||||
LOG.debug('Sleep 1 seconds Start')
|
||||
time.sleep(1)
|
||||
else:
|
||||
break
|
||||
else:
|
||||
LOG.debug('The snapshot data does not exist,'
|
||||
' because already forced deletion.'
|
||||
' bvname=%(bvname)s, svname=%(svname)s',
|
||||
{'bvname': bvname, 'svname': svname})
|
||||
|
||||
cmd = 'iSMcfg generationdel -bvname %s -count 1' % bvname
|
||||
self._execute(cmd)
|
||||
|
||||
def query_BV_SV_status(self, bvname, svname):
|
||||
cmd = ('iSMsc_query -bv %(bv)s -bvflg ld -sv %(sv)s -svflg ld '
|
||||
'-summary | '
|
||||
'while builtin read line;do '
|
||||
'if [[ "$line" =~ "%(line)s" ]]; '
|
||||
'then builtin echo "$line";fi;done'
|
||||
% {'bv': bvname, 'sv': svname, 'line': svname})
|
||||
out, err, status = self._execute(cmd)
|
||||
|
||||
query_status = out[34:48].strip()
|
||||
LOG.debug('snap/state:%s.', query_status)
|
||||
return query_status
|
||||
|
||||
def set_io_limit(self, ldname, specs, force_delete=True):
|
||||
if specs['upperlimit'] is not None:
|
||||
upper = int(specs['upperlimit'], 10)
|
||||
else:
|
||||
upper = None
|
||||
|
||||
if specs['lowerlimit'] is not None:
|
||||
lower = int(specs['lowerlimit'], 10)
|
||||
else:
|
||||
lower = None
|
||||
|
||||
report = specs['upperreport']
|
||||
if upper is None and lower is None and report is None:
|
||||
return
|
||||
cmd = 'iSMioc setlimit -ldname %s' % ldname
|
||||
if upper is not None:
|
||||
cmd += ' -upperlimit %d' % upper
|
||||
if lower is not None:
|
||||
cmd += ' -lowerlimit %d' % lower
|
||||
if report is not None:
|
||||
cmd += ' -upperreport %s' % report
|
||||
try:
|
||||
self._execute(cmd)
|
||||
except Exception:
|
||||
with excutils.save_and_reraise_exception():
|
||||
if force_delete:
|
||||
self.unbind(ldname)
|
||||
|
||||
|
||||
class UnpairWait(object):
|
||||
error_updates = {'status': 'error',
|
||||
'progress': '100%',
|
||||
'migration_status': None}
|
||||
|
||||
def __init__(self, volume_properties, cli):
|
||||
super(UnpairWait, self).__init__()
|
||||
self._volume_properties = volume_properties
|
||||
self._mvname = volume_properties['mvname'][3:]
|
||||
self._rvname = volume_properties['rvname'][3:]
|
||||
self._mvID = volume_properties['mvid']
|
||||
self._rvID = volume_properties['rvid']
|
||||
self._flag = volume_properties['flag']
|
||||
self._context = volume_properties['context']
|
||||
self._cli = cli
|
||||
self._local_conf = self._cli._properties
|
||||
|
||||
def _wait(self, unpair=True):
|
||||
timeout = self._local_conf['thread_timeout'] * 24
|
||||
start_time = time.time()
|
||||
while True:
|
||||
cur_time = time.time()
|
||||
if (cur_time - start_time) > timeout:
|
||||
raise exception.APITimeout(_('UnpairWait wait timeout.'))
|
||||
|
||||
LOG.debug('Sleep 60 seconds Start')
|
||||
time.sleep(60)
|
||||
|
||||
query_status = self._cli.query_MV_RV_status(self._rvname, 'RV')
|
||||
if query_status == 'separated':
|
||||
if unpair is True:
|
||||
self._cli.unpair(self._mvname, self._rvname, 'normal')
|
||||
break
|
||||
elif query_status == 'sep/exec':
|
||||
continue
|
||||
else:
|
||||
LOG.debug('iSMrc_query command result abnormal.'
|
||||
'Query status = %(status)s, RV = %(rv)s.',
|
||||
{'status': query_status, 'rv': self._rvname})
|
||||
break
|
||||
|
||||
def run(self):
|
||||
try:
|
||||
self._execute()
|
||||
except Exception:
|
||||
with excutils.save_and_reraise_exception():
|
||||
LOG.debug('UnpairWait Unexpected error. '
|
||||
'exception=%(exception)s, MV = %(mv)s, RV = %(rv)s.',
|
||||
{'exception': traceback.format_exc(),
|
||||
'mv': self._mvname, 'rv': self._rvname})
|
||||
|
||||
def _execute(self):
|
||||
pass
|
||||
|
||||
|
||||
class UnpairWaitForBackup(UnpairWait):
|
||||
def __init__(self, volume_properties, cli):
|
||||
super(UnpairWaitForBackup, self).__init__(volume_properties, cli)
|
||||
|
||||
def _execute(self):
|
||||
LOG.debug('UnpairWaitForBackup start.')
|
||||
|
||||
self._wait(True)
|
||||
|
||||
|
||||
class UnpairWaitForRestore(UnpairWait):
|
||||
def __init__(self, volume_properties, cli):
|
||||
super(UnpairWaitForRestore, self).__init__(volume_properties, cli)
|
||||
|
||||
self._rvldn = None
|
||||
if ('rvldn' in volume_properties and
|
||||
volume_properties['rvldn'] is not None):
|
||||
self._rvldn = volume_properties['rvldn']
|
||||
|
||||
self._rvcapacity = None
|
||||
if ('rvcapacity' in volume_properties and
|
||||
volume_properties['rvcapacity'] is not None):
|
||||
self._rvcapacity = volume_properties['rvcapacity']
|
||||
|
||||
def _execute(self):
|
||||
LOG.debug('UnpairWaitForRestore start.')
|
||||
|
||||
self._wait(True)
|
||||
|
||||
if self._rvcapacity is not None:
|
||||
try:
|
||||
self._cli.expand(self._rvldn, self._rvcapacity)
|
||||
except exception.CinderException:
|
||||
with excutils.save_and_reraise_exception():
|
||||
LOG.debug('UnpairWaitForDDRRestore expand error. '
|
||||
'exception=%(exception)s, '
|
||||
'MV = %(mv)s, RV = %(rv)s.',
|
||||
{'exception': traceback.format_exc(),
|
||||
'mv': self._mvname, 'rv': self._rvname})
|
||||
|
||||
|
||||
class UnpairWaitForClone(UnpairWait):
|
||||
def __init__(self, volume_properties, cli):
|
||||
super(UnpairWaitForClone, self).__init__(volume_properties, cli)
|
||||
|
||||
self._rvldn = None
|
||||
if ('rvldn' in volume_properties and
|
||||
volume_properties['rvldn'] is not None):
|
||||
self._rvldn = volume_properties['rvldn']
|
||||
|
||||
self._rvcapacity = None
|
||||
if ('rvcapacity' in volume_properties and
|
||||
volume_properties['rvcapacity'] is not None):
|
||||
self._rvcapacity = volume_properties['rvcapacity']
|
||||
|
||||
def _execute(self):
|
||||
LOG.debug('UnpairWaitForClone start.')
|
||||
|
||||
self._wait(True)
|
||||
|
||||
if self._rvcapacity is not None:
|
||||
try:
|
||||
self._cli.expand(self._rvldn, self._rvcapacity)
|
||||
except exception.CinderException:
|
||||
with excutils.save_and_reraise_exception():
|
||||
LOG.debug('UnpairWaitForClone expand error. '
|
||||
'exception=%(exception)s, '
|
||||
'MV = %(mv)s, RV = %(rv)s.',
|
||||
{'exception': traceback.format_exc(),
|
||||
'mv': self._mvname, 'rv': self._rvname})
|
||||
|
||||
|
||||
class UnpairWaitForMigrate(UnpairWait):
|
||||
def __init__(self, volume_properties, cli):
|
||||
super(UnpairWaitForMigrate, self).__init__(volume_properties, cli)
|
||||
|
||||
def _execute(self):
|
||||
LOG.debug('UnpairWaitForMigrate start.')
|
||||
|
||||
self._wait(True)
|
||||
|
||||
self._cli.unbind(self._volume_properties['mvname'])
|
||||
self._cli.changeldname(None, self._volume_properties['mvname'],
|
||||
self._volume_properties['rvname'])
|
||||
|
||||
|
||||
class UnpairWaitForDDRBackup(UnpairWaitForBackup):
|
||||
def __init__(self, volume_properties, cli):
|
||||
super(UnpairWaitForDDRBackup, self).__init__(volume_properties, cli)
|
||||
|
||||
def _execute(self):
|
||||
LOG.debug('UnpairWaitForDDRBackup start.')
|
||||
|
||||
self._wait(False)
|
||||
|
||||
|
||||
class UnpairWaitForDDRRestore(UnpairWaitForRestore):
|
||||
def __init__(self, volume_properties, cli):
|
||||
super(UnpairWaitForDDRRestore, self).__init__(volume_properties, cli)
|
||||
|
||||
self._prev_mvname = None
|
||||
if ('prev_mvname' in volume_properties and
|
||||
volume_properties['prev_mvname'] is not None):
|
||||
self._prev_mvname = volume_properties['prev_mvname'][3:]
|
||||
|
||||
def _execute(self):
|
||||
LOG.debug('UnpairWaitForDDRRestore start.')
|
||||
|
||||
self._wait(True)
|
||||
|
||||
if self._rvcapacity is not None:
|
||||
try:
|
||||
self._cli.expand(self._rvldn, self._rvcapacity)
|
||||
except exception.CinderException:
|
||||
with excutils.save_and_reraise_exception():
|
||||
LOG.debug('UnpairWaitForDDRRestore expand error. '
|
||||
'exception=%(exception)s, '
|
||||
'MV = %(mv)s, RV = %(rv)s.',
|
||||
{'exception': traceback.format_exc(),
|
||||
'mv': self._mvname, 'rv': self._rvname})
|
||||
|
||||
if self._prev_mvname is not None:
|
||||
self._cli.setpair(self._prev_mvname, self._mvname)
|
27
cinder/volume/drivers/nec/product.xml
Normal file
27
cinder/volume/drivers/nec/product.xml
Normal file
@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ProductInfo>
|
||||
<VendorName>NEC</VendorName>
|
||||
<ProductMap>
|
||||
<Product Name="iStorage M700">8192</Product>
|
||||
<Product Name="iStorage A5000">8192</Product>
|
||||
<Product Name="iStorage M500">4096</Product>
|
||||
<Product Name="iStorage M300">4096</Product>
|
||||
<Product Name="iStorage M10e">1024</Product>
|
||||
<Product Name="iStorage M100">1024</Product>
|
||||
<Product Name="M700 Disk Array">8192</Product>
|
||||
<Product Name="A5000 Disk Array">8192</Product>
|
||||
<Product Name="M300 Disk Array">4096</Product>
|
||||
<Product Name="M500 Disk Array">4096</Product>
|
||||
<Product Name="M10e Disk Array">1024</Product>
|
||||
<Product Name="M100 Disk Array">1024</Product>
|
||||
<Product Name="M5000">8192</Product>
|
||||
<Product Name="M710">8192</Product>
|
||||
<Product Name="M710F">8192</Product>
|
||||
<Product Name="A3000/100">4096</Product>
|
||||
<Product Name="M310">4096</Product>
|
||||
<Product Name="M310F">4096</Product>
|
||||
<Product Name="M510">4096</Product>
|
||||
<Product Name="M11e">1024</Product>
|
||||
<Product Name="M110">1024</Product>
|
||||
</ProductMap>
|
||||
</ProductInfo>
|
76
cinder/volume/drivers/nec/volume.py
Normal file
76
cinder/volume/drivers/nec/volume.py
Normal file
@ -0,0 +1,76 @@
|
||||
#
|
||||
# Copyright (c) 2016 NEC Corporation.
|
||||
# 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.
|
||||
|
||||
"""Drivers for M-Series Storage."""
|
||||
|
||||
from cinder import interface
|
||||
from cinder.volume import driver
|
||||
from cinder.volume.drivers.nec import volume_helper
|
||||
from cinder.zonemanager import utils as fczm_utils
|
||||
|
||||
|
||||
@interface.volumedriver
|
||||
class MStorageISCSIDriver(volume_helper.MStorageDSVDriver,
|
||||
driver.ISCSIDriver):
|
||||
"""M-Series Storage Snapshot iSCSI Driver."""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(MStorageISCSIDriver, self).__init__(*args, **kwargs)
|
||||
self._set_config(self.configuration, self.host,
|
||||
self.__class__.__name__)
|
||||
|
||||
def create_export(self, context, volume, connector):
|
||||
return self.iscsi_do_export(context, volume, connector)
|
||||
|
||||
def ensure_export(self, context, volume):
|
||||
pass
|
||||
|
||||
def get_volume_stats(self, refresh=False):
|
||||
return self.iscsi_get_volume_stats(refresh)
|
||||
|
||||
def initialize_connection(self, volume, connector):
|
||||
return self.iscsi_initialize_connection(volume, connector)
|
||||
|
||||
def terminate_connection(self, volume, connector, **kwargs):
|
||||
return self.iscsi_terminate_connection(volume, connector)
|
||||
|
||||
|
||||
@interface.volumedriver
|
||||
class MStorageFCDriver(volume_helper.MStorageDSVDriver,
|
||||
driver.FibreChannelDriver):
|
||||
"""M-Series Storage Snapshot FC Driver."""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(MStorageFCDriver, self).__init__(*args, **kwargs)
|
||||
self._set_config(self.configuration, self.host,
|
||||
self.__class__.__name__)
|
||||
|
||||
def create_export(self, context, volume, connector):
|
||||
return self.fc_do_export(context, volume, connector)
|
||||
|
||||
def ensure_export(self, context, volume):
|
||||
pass
|
||||
|
||||
def get_volume_stats(self, refresh=False):
|
||||
return self.fc_get_volume_stats(refresh)
|
||||
|
||||
@fczm_utils.AddFCZone
|
||||
def initialize_connection(self, volume, connector):
|
||||
return self.fc_initialize_connection(volume, connector)
|
||||
|
||||
@fczm_utils.RemoveFCZone
|
||||
def terminate_connection(self, volume, connector, **kwargs):
|
||||
return self.fc_terminate_connection(volume, connector)
|
922
cinder/volume/drivers/nec/volume_common.py
Normal file
922
cinder/volume/drivers/nec/volume_common.py
Normal file
@ -0,0 +1,922 @@
|
||||
#
|
||||
# Copyright (c) 2016 NEC Corporation.
|
||||
# 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.
|
||||
|
||||
import errno
|
||||
from lxml import etree
|
||||
import os
|
||||
import re
|
||||
import six
|
||||
import traceback
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
from oslo_utils import excutils
|
||||
from oslo_utils import units
|
||||
|
||||
from cinder import context
|
||||
from cinder import exception
|
||||
from cinder.i18n import _
|
||||
from cinder.volume.drivers.nec import cli
|
||||
from cinder.volume.drivers.san import san
|
||||
from cinder.volume import qos_specs
|
||||
from cinder.volume import volume_types
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
FLAGS = cfg.CONF
|
||||
|
||||
mstorage_opts = [
|
||||
cfg.IPOpt('nec_ismcli_fip',
|
||||
default=None,
|
||||
help='FIP address of M-Series Storage iSMCLI.'),
|
||||
cfg.StrOpt('nec_ismcli_user',
|
||||
default='',
|
||||
help='User name for M-Series Storage iSMCLI.'),
|
||||
cfg.StrOpt('nec_ismcli_password',
|
||||
secret=True,
|
||||
default='',
|
||||
help='Password for M-Series Storage iSMCLI.'),
|
||||
cfg.StrOpt('nec_ismcli_privkey',
|
||||
default='',
|
||||
help='Filename of RSA private key for '
|
||||
'M-Series Storage iSMCLI.'),
|
||||
cfg.StrOpt('nec_ldset',
|
||||
default='',
|
||||
help='M-Series Storage LD Set name for Compute Node.'),
|
||||
cfg.StrOpt('nec_ldname_format',
|
||||
default='LX:%s',
|
||||
help='M-Series Storage LD name format for volumes.'),
|
||||
cfg.StrOpt('nec_backup_ldname_format',
|
||||
default='LX:%s',
|
||||
help='M-Series Storage LD name format for snapshots.'),
|
||||
cfg.StrOpt('nec_diskarray_name',
|
||||
default='',
|
||||
help='Diskarray name of M-Series Storage.'),
|
||||
cfg.StrOpt('nec_ismview_dir',
|
||||
default='/tmp/nec/cinder',
|
||||
help='Output path of iSMview file.'),
|
||||
cfg.StrOpt('nec_ldset_for_controller_node',
|
||||
default='',
|
||||
help='M-Series Storage LD Set name for Controller Node.'),
|
||||
cfg.IntOpt('nec_ssh_pool_port_number',
|
||||
default=22,
|
||||
help='Port number of ssh pool.'),
|
||||
cfg.IntOpt('nec_unpairthread_timeout',
|
||||
default=3600,
|
||||
help='Timeout value of Unpairthread.'),
|
||||
cfg.IntOpt('nec_backend_max_ld_count',
|
||||
default=1024,
|
||||
help='Maximum number of managing sessions.'),
|
||||
cfg.BoolOpt('nec_actual_free_capacity',
|
||||
default=False,
|
||||
help='Return actual free capacity.'),
|
||||
cfg.BoolOpt('nec_ismview_alloptimize',
|
||||
default=False,
|
||||
help='Use legacy iSMCLI command with optimization.'),
|
||||
cfg.ListOpt('nec_pools',
|
||||
default=[],
|
||||
help='M-Series Storage pool numbers list to be used.'),
|
||||
cfg.ListOpt('nec_backup_pools',
|
||||
default=[],
|
||||
help='M-Series Storage backup pool number to be used.'),
|
||||
cfg.BoolOpt('nec_queryconfig_view',
|
||||
default=False,
|
||||
help='Use legacy iSMCLI command.'),
|
||||
cfg.IntOpt('nec_iscsi_portals_per_cont',
|
||||
default=1,
|
||||
help='Number of iSCSI portals.'),
|
||||
]
|
||||
|
||||
FLAGS.register_opts(mstorage_opts)
|
||||
|
||||
|
||||
def convert_to_name(uuid):
|
||||
alnum = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
|
||||
num = int(uuid.replace(("-"), ""), 16)
|
||||
|
||||
convertname = ""
|
||||
while num != 0:
|
||||
convertname = alnum[num % len(alnum)] + convertname
|
||||
num = num - num % len(alnum)
|
||||
num = num // len(alnum)
|
||||
return convertname
|
||||
|
||||
|
||||
def convert_to_id(value62):
|
||||
alnum = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
|
||||
length = len(value62)
|
||||
|
||||
weight = 0
|
||||
value = 0
|
||||
index = 0
|
||||
for i in reversed(range(0, length)):
|
||||
num = alnum.find(value62[i])
|
||||
if index != 0:
|
||||
value += int(weight * (num))
|
||||
else:
|
||||
value = num
|
||||
index += 1
|
||||
weight = 62 ** index
|
||||
|
||||
value = '%032x' % value
|
||||
|
||||
uuid = value[0:8]
|
||||
uuid += '-'
|
||||
uuid += value[8:12]
|
||||
uuid += '-'
|
||||
uuid += value[12:16]
|
||||
uuid += '-'
|
||||
uuid += value[16:20]
|
||||
uuid += '-'
|
||||
uuid += value[20:]
|
||||
|
||||
return uuid
|
||||
|
||||
|
||||
class MStorageVolumeCommon(object):
|
||||
"""M-Series Storage volume common class."""
|
||||
|
||||
def __init__(self, configuration, host, driver_name):
|
||||
super(MStorageVolumeCommon, self).__init__()
|
||||
|
||||
self._host = host
|
||||
self._driver_name = driver_name
|
||||
|
||||
self._configuration = configuration
|
||||
self._configuration.append_config_values(mstorage_opts)
|
||||
self._configuration.append_config_values(san.san_opts)
|
||||
self._config_group = self._configuration.config_group
|
||||
|
||||
if self._config_group:
|
||||
FLAGS.register_opts(mstorage_opts, group=self._config_group)
|
||||
self._local_conf = FLAGS._get(self._config_group)
|
||||
else:
|
||||
FLAGS.register_opts(mstorage_opts)
|
||||
self._local_conf = FLAGS
|
||||
|
||||
self._check_flags()
|
||||
self._properties = self._set_properties()
|
||||
self._cli = self._properties['cli']
|
||||
|
||||
def set_context(self, context):
|
||||
self._context = context
|
||||
|
||||
def _check_flags(self):
|
||||
for flag in ['nec_ismcli_fip', 'nec_ismcli_user']:
|
||||
if getattr(self._local_conf, flag, '') == '':
|
||||
raise exception.ParameterNotFound(param=flag)
|
||||
if (getattr(self._local_conf, 'nec_ismcli_password', '') == '' and
|
||||
getattr(self._local_conf, 'nec_ismcli_privkey', '') == ''):
|
||||
msg = _('nec_ismcli_password nor nec_ismcli_privkey')
|
||||
raise exception.ParameterNotFound(param=msg)
|
||||
|
||||
def _create_ismview_dir(self,
|
||||
ismview_dir,
|
||||
diskarray_name,
|
||||
driver_name,
|
||||
host):
|
||||
"""Create ismview directory."""
|
||||
filename = diskarray_name
|
||||
if filename == '':
|
||||
filename = driver_name + '_' + host
|
||||
|
||||
ismview_path = os.path.join(ismview_dir, filename)
|
||||
LOG.debug('ismview_path=%s.', ismview_path)
|
||||
try:
|
||||
if os.path.exists(ismview_path):
|
||||
os.remove(ismview_path)
|
||||
except OSError as e:
|
||||
with excutils.save_and_reraise_exception() as ctxt:
|
||||
if e.errno == errno.ENOENT:
|
||||
ctxt.reraise = False
|
||||
|
||||
try:
|
||||
os.makedirs(ismview_dir)
|
||||
except OSError as e:
|
||||
with excutils.save_and_reraise_exception() as ctxt:
|
||||
if e.errno == errno.EEXIST:
|
||||
ctxt.reraise = False
|
||||
|
||||
return ismview_path
|
||||
|
||||
def get_conf(self, host):
|
||||
"""Get another host group configurations."""
|
||||
hostname = host['host']
|
||||
hostname = hostname[:hostname.rindex('#')]
|
||||
if '@' in hostname:
|
||||
group = hostname.split('@')[1]
|
||||
FLAGS.register_opts(mstorage_opts, group=group)
|
||||
conf = FLAGS._get(group)
|
||||
else:
|
||||
FLAGS.register_opts(mstorage_opts)
|
||||
conf = FLAGS
|
||||
return conf
|
||||
|
||||
def get_conf_properties(self, conf=None):
|
||||
if conf is None:
|
||||
return self._properties
|
||||
pool_pools = []
|
||||
for pool in getattr(conf, 'nec_pools', []):
|
||||
if pool.endswith('h'):
|
||||
pool_pools.append(int(pool[:-1], 16))
|
||||
else:
|
||||
pool_pools.append(int(pool, 10))
|
||||
pool_backup_pools = []
|
||||
for pool in getattr(conf, 'nec_backup_pools', []):
|
||||
if pool.endswith('h'):
|
||||
pool_backup_pools.append(int(pool[:-1], 16))
|
||||
else:
|
||||
pool_backup_pools.append(int(pool, 10))
|
||||
ldset_name = getattr(conf, 'nec_ldset', '')
|
||||
ldset_controller_node_name = getattr(conf,
|
||||
'nec_ldset_for_controller_node',
|
||||
'')
|
||||
|
||||
return {
|
||||
'cli_fip': conf.nec_ismcli_fip,
|
||||
'cli_user': conf.nec_ismcli_user,
|
||||
'cli_password': conf.nec_ismcli_password,
|
||||
'cli_privkey': conf.nec_ismcli_privkey,
|
||||
'pool_pools': pool_pools,
|
||||
'pool_backup_pools': pool_backup_pools,
|
||||
'pool_actual_free_capacity': conf.nec_actual_free_capacity,
|
||||
'ldset_name': ldset_name,
|
||||
'ldset_controller_node_name': ldset_controller_node_name,
|
||||
'ld_name_format': conf.nec_ldname_format,
|
||||
'ld_backupname_format': conf.nec_backup_ldname_format,
|
||||
'ld_backend_max_count': conf.nec_backend_max_ld_count,
|
||||
'thread_timeout': conf.nec_unpairthread_timeout,
|
||||
'ismview_dir': conf.nec_ismview_dir,
|
||||
'ismview_alloptimize': conf.nec_ismview_alloptimize,
|
||||
'ssh_conn_timeout': conf.ssh_conn_timeout,
|
||||
'ssh_pool_port_number': conf.nec_ssh_pool_port_number,
|
||||
'diskarray_name': conf.nec_diskarray_name,
|
||||
'queryconfig_view': conf.nec_queryconfig_view,
|
||||
'portal_number': conf.nec_iscsi_portals_per_cont,
|
||||
'reserved_percentage': conf.reserved_percentage
|
||||
}
|
||||
|
||||
def _set_properties(self):
|
||||
conf_properties = self.get_conf_properties(self._local_conf)
|
||||
|
||||
ismview_path = self._create_ismview_dir(
|
||||
self._local_conf.nec_ismview_dir,
|
||||
self._local_conf.nec_diskarray_name,
|
||||
self._driver_name,
|
||||
self._host)
|
||||
|
||||
vendor_name, _product_dict = self.get_oem_parameter()
|
||||
|
||||
backend_name = self._configuration.safe_get('volume_backend_name')
|
||||
|
||||
conf_properties['ismview_path'] = ismview_path
|
||||
conf_properties['driver_name'] = self._driver_name
|
||||
conf_properties['config_group'] = self._config_group
|
||||
conf_properties['configuration'] = self._configuration
|
||||
conf_properties['vendor_name'] = vendor_name
|
||||
conf_properties['products'] = _product_dict
|
||||
conf_properties['backend_name'] = backend_name
|
||||
conf_properties['cli'] = cli.MStorageISMCLI(conf_properties)
|
||||
|
||||
return conf_properties
|
||||
|
||||
def get_oem_parameter(self):
|
||||
product = os.path.join(os.path.dirname(__file__), 'product.xml')
|
||||
try:
|
||||
with open(product, 'r') as f:
|
||||
xml = f.read()
|
||||
root = etree.fromstring(xml)
|
||||
vendor_name = root.xpath('./VendorName')[0].text
|
||||
|
||||
product_dict = {}
|
||||
product_map = root.xpath('./ProductMap/Product')
|
||||
for s in product_map:
|
||||
product_dict[s.attrib['Name']] = int(s.text, 10)
|
||||
|
||||
return vendor_name, product_dict
|
||||
except OSError as e:
|
||||
with excutils.save_and_reraise_exception() as ctxt:
|
||||
if e.errno == errno.ENOENT:
|
||||
ctxt.reraise = False
|
||||
raise exception.NotFound(_('%s not found.') % product)
|
||||
|
||||
@staticmethod
|
||||
def get_ldname(volid, volformat):
|
||||
alnum = ('0123456789'
|
||||
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz')
|
||||
ldname = ""
|
||||
num = int(volid.replace(("-"), ""), 16)
|
||||
while num != 0:
|
||||
ldname = alnum[num % len(alnum)] + ldname
|
||||
num = num - num % len(alnum)
|
||||
num = num // len(alnum)
|
||||
|
||||
return volformat % ldname
|
||||
|
||||
def get_ldset(self, ldsets, metadata=None):
|
||||
ldset = None
|
||||
if metadata is not None and 'ldset' in metadata:
|
||||
ldset_meta = metadata['ldset']
|
||||
LOG.debug('ldset(metadata)=%s.', ldset_meta)
|
||||
for tldset in six.itervalues(ldsets):
|
||||
if tldset['ldsetname'] == ldset_meta:
|
||||
ldset = ldsets[ldset_meta]
|
||||
LOG.debug('ldset information(metadata specified)=%s.',
|
||||
ldset)
|
||||
break
|
||||
if ldset is None:
|
||||
msg = _('Logical Disk Set could not be found.')
|
||||
LOG.error(msg)
|
||||
raise exception.NotFound(msg)
|
||||
elif self._properties['ldset_name'] == '':
|
||||
nldset = len(ldsets)
|
||||
if nldset == 0:
|
||||
msg = _('Logical Disk Set could not be found.')
|
||||
raise exception.NotFound(msg)
|
||||
else:
|
||||
ldset = None
|
||||
else:
|
||||
if self._properties['ldset_name'] not in ldsets:
|
||||
msg = (_('Logical Disk Set `%s` could not be found.') %
|
||||
self._properties['ldset_name'])
|
||||
raise exception.NotFound(msg)
|
||||
ldset = ldsets[self._properties['ldset_name']]
|
||||
return ldset
|
||||
|
||||
def get_pool_capacity(self, pools, ldsets):
|
||||
pools = [pool for (pn, pool) in six.iteritems(pools)
|
||||
if len(self._properties['pool_pools']) == 0 or
|
||||
pn in self._properties['pool_pools']]
|
||||
|
||||
free_capacity_gb = 0
|
||||
total_capacity_gb = 0
|
||||
for pool in pools:
|
||||
# Convert to GB.
|
||||
tmp_total = int(pool['total'] // units.Gi)
|
||||
tmp_free = int(pool['free'] // units.Gi)
|
||||
|
||||
if free_capacity_gb < tmp_free:
|
||||
total_capacity_gb = tmp_total
|
||||
free_capacity_gb = tmp_free
|
||||
|
||||
return {'total_capacity_gb': total_capacity_gb,
|
||||
'free_capacity_gb': free_capacity_gb}
|
||||
|
||||
def set_backend_max_ld_count(self, xml, root):
|
||||
section = root.xpath('./CMD_REQUEST')[0]
|
||||
version = section.get('version').replace('Version ', '')[0:3]
|
||||
version = float(version)
|
||||
if version < 9.1:
|
||||
if 512 < self._properties['ld_backend_max_count']:
|
||||
self._properties['ld_backend_max_count'] = 512
|
||||
else:
|
||||
if 1024 < self._properties['ld_backend_max_count']:
|
||||
self._properties['ld_backend_max_count'] = 1024
|
||||
|
||||
def get_diskarray_max_ld_count(self, xml, root):
|
||||
max_ld_count = 0
|
||||
for section in root.xpath(
|
||||
'./'
|
||||
'CMD_REQUEST/'
|
||||
'CHAPTER[@name="Disk Array"]/'
|
||||
'OBJECT[@name="Disk Array"]/'
|
||||
'SECTION[@name="Disk Array Detail Information"]'):
|
||||
unit = section.find('./UNIT[@name="Product ID"]')
|
||||
if unit is None:
|
||||
msg = (_('UNIT[@name="Product ID"] not found. '
|
||||
'line=%(line)d out="%(out)s"') %
|
||||
{'line': section.sourceline, 'out': xml})
|
||||
LOG.error(msg)
|
||||
raise exception.VolumeBackendAPIException(data=msg)
|
||||
else:
|
||||
product_id = unit.text
|
||||
if product_id in self._properties['products']:
|
||||
max_ld_count = self._properties['products'][product_id]
|
||||
else:
|
||||
max_ld_count = 8192
|
||||
LOG.debug('UNIT[@name="Product ID"] unknown id. '
|
||||
'productId=%s', product_id)
|
||||
LOG.debug('UNIT[@name="Product ID"] max_ld_count=%d.',
|
||||
max_ld_count)
|
||||
return max_ld_count
|
||||
|
||||
def get_pool_config(self, xml, root):
|
||||
pools = {}
|
||||
for xmlobj in root.xpath('./'
|
||||
'CMD_REQUEST/'
|
||||
'CHAPTER[@name="Pool"]/'
|
||||
'OBJECT[@name="Pool"]'):
|
||||
section = xmlobj.find('./SECTION[@name="Pool Detail Information"]')
|
||||
if section is None:
|
||||
msg = (_('SECTION[@name="Pool Detail Information"] '
|
||||
'not found. line=%(line)d out="%(out)s"') %
|
||||
{'line': xmlobj.sourceline, 'out': xml})
|
||||
LOG.error(msg)
|
||||
raise exception.VolumeBackendAPIException(data=msg)
|
||||
unit = section.find('./UNIT[@name="Pool No.(h)"]')
|
||||
if unit is None:
|
||||
msg = (_('UNIT[@name="Pool No.(h)"] not found. '
|
||||
'line=%(line)d out="%(out)s"') %
|
||||
{'line': section.sourceline, 'out': xml})
|
||||
LOG.error(msg)
|
||||
raise exception.VolumeBackendAPIException(data=msg)
|
||||
pool_num = int(unit.text, 16)
|
||||
unit = section.find('UNIT[@name="Pool Capacity"]')
|
||||
if unit is None:
|
||||
msg = (_('UNIT[@name="Pool Capacity"] not found. '
|
||||
'line=%(line)d out="%(out)s"') %
|
||||
{'line': section.sourceline, 'out': xml})
|
||||
LOG.error(msg)
|
||||
raise exception.VolumeBackendAPIException(data=msg)
|
||||
total = int(unit.text, 10)
|
||||
unit = section.find('UNIT[@name="Free Pool Capacity"]')
|
||||
if unit is None:
|
||||
msg = (_('UNIT[@name="Free Pool Capacity"] not found. '
|
||||
'line=%(line)d out="%(out)s"') %
|
||||
{'line': section.sourceline, 'out': xml})
|
||||
LOG.error(msg)
|
||||
raise exception.VolumeBackendAPIException(data=msg)
|
||||
free = int(unit.text, 10)
|
||||
if self._properties['pool_actual_free_capacity']:
|
||||
unit = section.find('UNIT[@name="Used Pool Capacity"]')
|
||||
if unit is None:
|
||||
msg = (_('UNIT[@name="Used Pool Capacity"] not found. '
|
||||
'line=%(line)d out="%(out)s"') %
|
||||
{'line': section.sourceline, 'out': xml})
|
||||
LOG.error(msg)
|
||||
raise exception.VolumeBackendAPIException(data=msg)
|
||||
used = int(unit.text, 10)
|
||||
for section in xmlobj.xpath('./SECTION[@name='
|
||||
'"Virtual Capacity Pool '
|
||||
'Information"]'):
|
||||
unit = section.find('UNIT[@name="Actual Capacity"]')
|
||||
if unit is None:
|
||||
msg = (_('UNIT[@name="Actual Capacity"] not found. '
|
||||
'line=%(line)d out="%(out)s"') %
|
||||
{'line': section.sourceline, 'out': xml})
|
||||
LOG.error(msg)
|
||||
raise exception.VolumeBackendAPIException(data=msg)
|
||||
total = int(unit.text, 10)
|
||||
free = total - used
|
||||
pool = {'pool_num': pool_num,
|
||||
'total': total,
|
||||
'free': free,
|
||||
'ld_list': []}
|
||||
pools[pool_num] = pool
|
||||
return pools
|
||||
|
||||
def get_ld_config(self, xml, root, pools):
|
||||
lds = {}
|
||||
used_ldns = []
|
||||
for section in root.xpath('./'
|
||||
'CMD_REQUEST/'
|
||||
'CHAPTER[@name="Logical Disk"]/'
|
||||
'OBJECT[@name="Logical Disk"]/'
|
||||
'SECTION[@name="LD Detail Information"]'):
|
||||
unit = section.find('./UNIT[@name="LDN(h)"]')
|
||||
if unit is None:
|
||||
msg = (_('UNIT[@name="LDN(h)"] not found. '
|
||||
'line=%(line)d out="%(out)s"') %
|
||||
{'line': section.sourceline, 'out': xml})
|
||||
LOG.error(msg)
|
||||
raise exception.VolumeBackendAPIException(data=msg)
|
||||
ldn = int(unit.text, 16)
|
||||
unit = section.find('./UNIT[@name="OS Type"]')
|
||||
if unit is None:
|
||||
msg = (_('UNIT[@name="OS Type"] not found. '
|
||||
'line=%(line)d out="%(out)s"') %
|
||||
{'line': section.sourceline, 'out': xml})
|
||||
LOG.error(msg)
|
||||
raise exception.VolumeBackendAPIException(data=msg)
|
||||
ostype = unit.text if unit.text is not None else ''
|
||||
unit = section.find('./UNIT[@name="LD Name"]')
|
||||
if unit is None:
|
||||
msg = (_('UNIT[@name="LD Name"] not found. '
|
||||
'line=%(line)d out="%(out)s"') %
|
||||
{'line': section.sourceline, 'out': xml})
|
||||
LOG.error(msg)
|
||||
raise exception.VolumeBackendAPIException(data=msg)
|
||||
ldname = ostype + ':' + unit.text
|
||||
unit = section.find('./UNIT[@name="Pool No.(h)"]')
|
||||
if unit is None:
|
||||
msg = (_('UNIT[@name="Pool No.(h)"] not found. '
|
||||
'line=%(line)d out="%(out)s"') %
|
||||
{'line': section.sourceline, 'out': xml})
|
||||
LOG.error(msg)
|
||||
raise exception.VolumeBackendAPIException(data=msg)
|
||||
pool_num = int(unit.text, 16)
|
||||
|
||||
unit = section.find('./UNIT[@name="LD Capacity"]')
|
||||
if unit is None:
|
||||
msg = (_('UNIT[@name="LD Capacity"] not found. '
|
||||
'line=%(line)d out="%(out)s"') %
|
||||
{'line': section.sourceline, 'out': xml})
|
||||
LOG.error(msg)
|
||||
raise exception.VolumeBackendAPIException(data=msg)
|
||||
|
||||
# byte capacity transform GB capacity.
|
||||
ld_capacity = int(unit.text, 10) // units.Gi
|
||||
|
||||
unit = section.find('./UNIT[@name="RPL Attribute"]')
|
||||
if unit is None:
|
||||
msg = (_('UNIT[@name="RPL Attribute"] not found. '
|
||||
'line=%(line)d out="%(out)s"') %
|
||||
{'line': section.sourceline, 'out': xml})
|
||||
LOG.error(msg)
|
||||
raise exception.VolumeBackendAPIException(data=msg)
|
||||
rplatr = unit.text
|
||||
|
||||
unit = section.find('./UNIT[@name="Purpose"]')
|
||||
if unit is None:
|
||||
msg = (_('UNIT[@name="Purpose"] not found. '
|
||||
'line=%(line)d out="%(out)s"') %
|
||||
{'line': section.sourceline, 'out': xml})
|
||||
LOG.error(msg)
|
||||
raise exception.VolumeBackendAPIException(data=msg)
|
||||
purpose = unit.text
|
||||
|
||||
ld = {'ldname': ldname,
|
||||
'ldn': ldn,
|
||||
'pool_num': pool_num,
|
||||
'ld_capacity': ld_capacity,
|
||||
'RPL Attribute': rplatr,
|
||||
'Purpose': purpose}
|
||||
pools[pool_num]['ld_list'].append(ld)
|
||||
lds[ldname] = ld
|
||||
used_ldns.append(ldn)
|
||||
return lds, used_ldns
|
||||
|
||||
def get_iscsi_ldset_config(self, xml, root):
|
||||
ldsets = {}
|
||||
for xmlobj in root.xpath('./'
|
||||
'CMD_REQUEST/'
|
||||
'CHAPTER[@name="Access Control"]/'
|
||||
'OBJECT[@name="LD Set(iSCSI)"]'):
|
||||
ldsetlds = {}
|
||||
portals = []
|
||||
initiators = []
|
||||
for unit in xmlobj.xpath('./SECTION[@name="Portal"]/'
|
||||
'UNIT[@name="Portal"]'):
|
||||
if not unit.text.startswith('0.0.0.0:'):
|
||||
portals.append(unit.text)
|
||||
|
||||
for unit in xmlobj.xpath('./SECTION[@name="Initiator List"]/'
|
||||
'UNIT[@name="Initiator List"]'):
|
||||
initiators.append(unit.text)
|
||||
|
||||
section = xmlobj.find('./SECTION[@name="LD Set(iSCSI)'
|
||||
' Information"]')
|
||||
if section is None:
|
||||
return ldsets
|
||||
unit = section.find('./UNIT[@name="Platform"]')
|
||||
if unit is None:
|
||||
msg = (_('UNIT[@name="Platform"] not found. '
|
||||
'line=%(line)d out="%(out)s"') %
|
||||
{'line': section.sourceline, 'out': xml})
|
||||
LOG.error(msg)
|
||||
raise exception.VolumeBackendAPIException(data=msg)
|
||||
platform = unit.text
|
||||
unit = section.find('./UNIT[@name="LD Set Name"]')
|
||||
if unit is None:
|
||||
msg = (_('UNIT[@name="LD Set Name"] not found. '
|
||||
'line=%(line)d out="%(out)s"') %
|
||||
{'line': section.sourceline, 'out': xml})
|
||||
LOG.error(msg)
|
||||
raise exception.VolumeBackendAPIException(data=msg)
|
||||
ldsetname = platform + ':' + unit.text
|
||||
unit = section.find('./UNIT[@name="Target Mode"]')
|
||||
if unit is None:
|
||||
msg = (_('UNIT[@name="Target Mode"] not found. '
|
||||
'line=%(line)d out="%(out)s"') %
|
||||
{'line': section.sourceline, 'out': xml})
|
||||
LOG.error(msg)
|
||||
raise exception.VolumeBackendAPIException(data=msg)
|
||||
tmode = unit.text
|
||||
if tmode == 'Normal':
|
||||
unit = section.find('./UNIT[@name="Target Name"]')
|
||||
if unit is None:
|
||||
msg = (_('UNIT[@name="Target Name"] not found. '
|
||||
'line=%(line)d out="%(out)s"') %
|
||||
{'line': section.sourceline, 'out': xml})
|
||||
LOG.error(msg)
|
||||
raise exception.VolumeBackendAPIException(data=msg)
|
||||
iqn = unit.text
|
||||
for section in xmlobj.xpath('./SECTION[@name="LUN/LD List"]'):
|
||||
unit = section.find('./UNIT[@name="LDN(h)"]')
|
||||
if unit is None:
|
||||
msg = (_('UNIT[@name="LDN(h)"] not found. '
|
||||
'line=%(line)d out="%(out)s"') %
|
||||
{'line': section.sourceline, 'out': xml})
|
||||
LOG.error(msg)
|
||||
raise exception.VolumeBackendAPIException(data=msg)
|
||||
ldn = int(unit.text, 16)
|
||||
unit = section.find('./UNIT[@name="LUN(h)"]')
|
||||
if unit is None:
|
||||
msg = (_('UNIT[@name="LUN(h)"] not found. '
|
||||
'line=%(line)d out="%(out)s"') %
|
||||
{'line': section.sourceline, 'out': xml})
|
||||
LOG.error(msg)
|
||||
raise exception.VolumeBackendAPIException(data=msg)
|
||||
lun = int(unit.text, 16)
|
||||
ld = {'ldn': ldn,
|
||||
'lun': lun,
|
||||
'iqn': iqn}
|
||||
ldsetlds[ldn] = ld
|
||||
elif tmode == 'Multi-Target':
|
||||
for section in xmlobj.xpath('./SECTION[@name='
|
||||
'"Target Information For '
|
||||
'Multi-Target Mode"]'):
|
||||
unit = section.find('./UNIT[@name="Target Name"]')
|
||||
if unit is None:
|
||||
msg = (_('UNIT[@name="Target Name"] not found. '
|
||||
'line=%(line)d out="%(out)s"') %
|
||||
{'line': section.sourceline, 'out': xml})
|
||||
LOG.error(msg)
|
||||
raise exception.VolumeBackendAPIException(data=msg)
|
||||
iqn = unit.text
|
||||
unit = section.find('./UNIT[@name="LDN(h)"]')
|
||||
if unit is None:
|
||||
msg = (_('UNIT[@name="LDN(h)"] not found. '
|
||||
'line=%(line)d out="%(out)s"') %
|
||||
{'line': section.sourceline, 'out': xml})
|
||||
LOG.error(msg)
|
||||
raise exception.VolumeBackendAPIException(data=msg)
|
||||
if unit.text.startswith('-'):
|
||||
continue
|
||||
ldn = int(unit.text, 16)
|
||||
unit = section.find('./UNIT[@name="LUN(h)"]')
|
||||
if unit is None:
|
||||
msg = (_('UNIT[@name="LUN(h)"] not found. '
|
||||
'line=%(line)d out="%(out)s"') %
|
||||
{'line': section.sourceline, 'out': xml})
|
||||
LOG.error(msg)
|
||||
raise exception.VolumeBackendAPIException(data=msg)
|
||||
if unit.text.startswith('-'):
|
||||
continue
|
||||
lun = int(unit.text, 16)
|
||||
ld = {'ldn': ldn,
|
||||
'lun': lun,
|
||||
'iqn': iqn}
|
||||
ldsetlds[ldn] = ld
|
||||
else:
|
||||
LOG.debug('`%(mode)s` Unknown Target Mode. '
|
||||
'line=%(line)d out="%(out)s"',
|
||||
{'mode': tmode, 'line': unit.sourceline, 'out': xml})
|
||||
ldset = {'ldsetname': ldsetname,
|
||||
'protocol': 'iSCSI',
|
||||
'portal_list': portals,
|
||||
'lds': ldsetlds,
|
||||
'initiator_list': initiators}
|
||||
ldsets[ldsetname] = ldset
|
||||
return ldsets
|
||||
|
||||
def get_fc_ldset_config(self, xml, root):
|
||||
ldsets = {}
|
||||
for xmlobj in root.xpath('./'
|
||||
'CMD_REQUEST/'
|
||||
'CHAPTER[@name="Access Control"]/'
|
||||
'OBJECT[@name="LD Set(FC)"]'):
|
||||
ldsetlds = {}
|
||||
section = xmlobj.find('./SECTION[@name="LD Set(FC)'
|
||||
' Information"]')
|
||||
if section is None:
|
||||
return ldsets
|
||||
unit = section.find('./UNIT[@name="Platform"]')
|
||||
if unit is None:
|
||||
msg = (_('UNIT[@name="Platform"] not found. '
|
||||
'line=%(line)d out="%(out)s"') %
|
||||
{'line': section.sourceline, 'out': xml})
|
||||
LOG.error(msg)
|
||||
raise exception.VolumeBackendAPIException(data=msg)
|
||||
platform = unit.text
|
||||
unit = section.find('./UNIT[@name="LD Set Name"]')
|
||||
if unit is None:
|
||||
msg = (_('UNIT[@name="LD Set Name"] not found. '
|
||||
'line=%(line)d out="%(out)s"') %
|
||||
{'line': section.sourceline, 'out': xml})
|
||||
LOG.error(msg)
|
||||
raise exception.VolumeBackendAPIException(data=msg)
|
||||
ldsetname = platform + ':' + unit.text
|
||||
wwpns = []
|
||||
ports = []
|
||||
for section in xmlobj.xpath('./SECTION[@name="Path List"]'):
|
||||
unit = section.find('./UNIT[@name="Path"]')
|
||||
if unit is None:
|
||||
msg = (_('UNIT[@name="Path"] not found. '
|
||||
'line=%(line)d out="%(out)s"') %
|
||||
{'line': section.sourceline, 'out': xml})
|
||||
LOG.error(msg)
|
||||
raise exception.VolumeBackendAPIException(data=msg)
|
||||
if unit.text.find('(') != -1:
|
||||
ports.append(unit.text)
|
||||
else:
|
||||
wwpns.append(unit.text)
|
||||
for section in xmlobj.xpath('./SECTION[@name="LUN/LD List"]'):
|
||||
unit = section.find('./UNIT[@name="LDN(h)"]')
|
||||
if unit is None:
|
||||
msg = (_('UNIT[@name="LDN(h)"] not found. '
|
||||
'line=%(line)d out="%(out)s"') %
|
||||
{'line': section.sourceline, 'out': xml})
|
||||
LOG.error(msg)
|
||||
raise exception.VolumeBackendAPIException(data=msg)
|
||||
ldn = int(unit.text, 16)
|
||||
unit = section.find('./UNIT[@name="LUN(h)"]')
|
||||
if unit is None:
|
||||
msg = (_('UNIT[@name="LUN(h)"] not found. '
|
||||
'line=%(line)d out="%(out)s"') %
|
||||
{'line': section.sourceline, 'out': xml})
|
||||
LOG.error(msg)
|
||||
raise exception.VolumeBackendAPIException(data=msg)
|
||||
lun = int(unit.text, 16)
|
||||
ld = {'ldn': ldn,
|
||||
'lun': lun}
|
||||
ldsetlds[ldn] = ld
|
||||
ldset = {'ldsetname': ldsetname,
|
||||
'lds': ldsetlds,
|
||||
'protocol': 'FC',
|
||||
'wwpn': wwpns,
|
||||
'port': ports}
|
||||
ldsets[ldsetname] = ldset
|
||||
return ldsets
|
||||
|
||||
def get_hostport_config(self, xml, root):
|
||||
hostports = {}
|
||||
for section in root.xpath('./'
|
||||
'CMD_REQUEST/'
|
||||
'CHAPTER[@name="Controller"]/'
|
||||
'OBJECT[@name="Host Port"]/'
|
||||
'SECTION[@name="Host Director'
|
||||
'/Host Port Information"]'):
|
||||
unit = section.find('./UNIT[@name="Port No.(h)"]')
|
||||
if unit is None:
|
||||
msg = (_('UNIT[@name="Port No.(h)"] not found. '
|
||||
'line=%(line)d out="%(out)s"') %
|
||||
{'line': section.sourceline, 'out': xml})
|
||||
LOG.error(msg)
|
||||
raise exception.VolumeBackendAPIException(data=msg)
|
||||
units = unit.text.split('-')
|
||||
director = int(units[0], 16)
|
||||
port = int(units[1], 16)
|
||||
unit = section.find('./UNIT[@name="IP Address"]')
|
||||
if unit is None:
|
||||
unit = section.find('./UNIT[@name="WWPN"]')
|
||||
if unit is None:
|
||||
msg = (_('UNIT[@name="WWPN"] not found. '
|
||||
'line=%(line)d out="%(out)s"') %
|
||||
{'line': section.sourceline, 'out': xml})
|
||||
LOG.error(msg)
|
||||
raise exception.VolumeBackendAPIException(data=msg)
|
||||
wwpn = unit.text
|
||||
hostport = {
|
||||
'director': director,
|
||||
'port': port,
|
||||
'wwpn': wwpn,
|
||||
'protocol': 'FC',
|
||||
}
|
||||
else:
|
||||
ip = unit.text
|
||||
if ip == '0.0.0.0':
|
||||
continue
|
||||
|
||||
# Port Link Status check Start.
|
||||
unit = section.find('./UNIT[@name="Link Status"]')
|
||||
if unit is None:
|
||||
msg = (_('UNIT[@name="Link Status"] not found. '
|
||||
'line=%(line)d out="%(out)s"') %
|
||||
{'line': section.sourceline, 'out': xml})
|
||||
LOG.error(msg)
|
||||
raise exception.VolumeBackendAPIException(data=msg)
|
||||
linkstatus = unit.text
|
||||
if linkstatus == 'Link Down':
|
||||
continue
|
||||
|
||||
hostport = {
|
||||
'director': director,
|
||||
'port': port,
|
||||
'ip': ip,
|
||||
'protocol': 'iSCSI',
|
||||
}
|
||||
if director not in hostports:
|
||||
hostports[director] = []
|
||||
hostports[director].append(hostport)
|
||||
return hostports
|
||||
|
||||
def configs(self, xml):
|
||||
root = etree.fromstring(xml)
|
||||
pools = self.get_pool_config(xml, root)
|
||||
lds, used_ldns = self.get_ld_config(xml, root, pools)
|
||||
iscsi_ldsets = self.get_iscsi_ldset_config(xml, root)
|
||||
fc_ldsets = self.get_fc_ldset_config(xml, root)
|
||||
hostports = self.get_hostport_config(xml, root)
|
||||
diskarray_max_ld_count = self.get_diskarray_max_ld_count(xml, root)
|
||||
|
||||
self.set_backend_max_ld_count(xml, root)
|
||||
|
||||
ldsets = {}
|
||||
ldsets.update(iscsi_ldsets)
|
||||
ldsets.update(fc_ldsets)
|
||||
|
||||
return pools, lds, ldsets, used_ldns, hostports, diskarray_max_ld_count
|
||||
|
||||
def get_xml(self):
|
||||
ismview_path = self._properties['ismview_path']
|
||||
if os.path.exists(ismview_path) and os.path.isfile(ismview_path):
|
||||
with open(ismview_path, 'r') as f:
|
||||
xml = f.read()
|
||||
LOG.debug('loaded from %s.', ismview_path)
|
||||
else:
|
||||
xml = self._cli.view_all(ismview_path, False, False)
|
||||
return xml
|
||||
|
||||
def parse_xml(self):
|
||||
try:
|
||||
xml = self.get_xml()
|
||||
return self.configs(xml)
|
||||
except Exception:
|
||||
LOG.debug('parse_xml Unexpected error. exception=%s',
|
||||
traceback.format_exc())
|
||||
xml = self._cli.view_all(self._properties['ismview_path'], False)
|
||||
return self.configs(xml)
|
||||
|
||||
def get_volume_type_qos_specs(self, volume):
|
||||
specs = {}
|
||||
|
||||
ctxt = context.get_admin_context()
|
||||
type_id = volume['volume_type_id']
|
||||
if type_id is not None:
|
||||
volume_type = volume_types.get_volume_type(ctxt, type_id)
|
||||
|
||||
qos_specs_id = volume_type.get('qos_specs_id')
|
||||
if qos_specs_id is not None:
|
||||
specs = qos_specs.get_qos_specs(ctxt, qos_specs_id)['specs']
|
||||
|
||||
LOG.debug('get_volume_type_qos_specs '
|
||||
'volume_type=%(volume_type)s, '
|
||||
'qos_specs_id=%(qos_spec_id)s '
|
||||
'specs=%(specs)s',
|
||||
{'volume_type': volume_type,
|
||||
'qos_spec_id': qos_specs_id,
|
||||
'specs': specs})
|
||||
return specs
|
||||
|
||||
def check_io_parameter(self, specs):
|
||||
if ('upperlimit' not in specs and
|
||||
'lowerlimit' not in specs and
|
||||
'upperreport' not in specs):
|
||||
specs['upperlimit'] = None
|
||||
specs['lowerlimit'] = None
|
||||
specs['upperreport'] = None
|
||||
LOG.debug('qos parameter not found.')
|
||||
else:
|
||||
if ('upperlimit' in specs) and (specs['upperlimit'] is not None):
|
||||
if self.validates_number(specs['upperlimit']) is True:
|
||||
upper_limit = int(specs['upperlimit'], 10)
|
||||
if ((upper_limit != 0) and
|
||||
((upper_limit < 10) or (upper_limit > 1000000))):
|
||||
raise exception.InvalidConfigurationValue(
|
||||
value=upper_limit, option='upperlimit')
|
||||
else:
|
||||
raise exception.InvalidConfigurationValue(
|
||||
value=specs['upperlimit'], option='upperlimit')
|
||||
else:
|
||||
specs['upperlimit'] = None
|
||||
|
||||
if ('lowerlimit' in specs) and (specs['lowerlimit'] is not None):
|
||||
if self.validates_number(specs['lowerlimit']) is True:
|
||||
lower_limit = int(specs['lowerlimit'], 10)
|
||||
if (lower_limit != 0 and (lower_limit < 10 or
|
||||
lower_limit > 1000000)):
|
||||
raise exception.InvalidConfigurationValue(
|
||||
value=lower_limit, option='lowerlimit')
|
||||
else:
|
||||
raise exception.InvalidConfigurationValue(
|
||||
value=specs['lowerlimit'], option='lowerlimit')
|
||||
else:
|
||||
specs['lowerlimit'] = None
|
||||
|
||||
if 'upperreport' in specs:
|
||||
if specs['upperreport'] not in ['on', 'off']:
|
||||
LOG.debug('Illegal arguments. '
|
||||
'upperreport is not on or off.'
|
||||
'upperreport=%s' % specs['upperreport'])
|
||||
specs['upperreport'] = None
|
||||
else:
|
||||
specs['upperreport'] = None
|
||||
|
||||
def validates_number(self, value):
|
||||
return re.match(r'^(?![-+]0+$)[-+]?([1-9][0-9]*)?[0-9](\.[0-9]+)?$',
|
||||
'%s' % value) and True or False
|
1699
cinder/volume/drivers/nec/volume_helper.py
Normal file
1699
cinder/volume/drivers/nec/volume_helper.py
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,3 @@
|
||||
---
|
||||
features:
|
||||
- Added backend drivers for NEC Storage.(FC/iSCSI)
|
Loading…
x
Reference in New Issue
Block a user