"""
Tests for HUAWEI volume driver.
"""
+import mox
+import os
+import shutil
+import tempfile
+from xml.dom.minidom import Document
from xml.etree import ElementTree as ET
from cinder import exception
LOG = logging.getLogger(__name__)
-FakeXML = """<?xml version="1.0" encoding="UTF-8" ?>
-<config>
- <Storage>
- <ControllerIP0>10.10.10.1</ControllerIP0>
- <ControllerIP1>10.10.10.2</ControllerIP1>
- <UserName>admin</UserName>
- <UserPassword>123456</UserPassword>
- </Storage>
- <LUN>
- <LUNType>Thick</LUNType>
- <StripUnitSize>64</StripUnitSize>
- <WriteType>1</WriteType>
- <MirrorSwitch>1</MirrorSwitch>
- <Prefetch Type="3" Value="0"/>
- <StoragePool Name="RAID_001"/>
- <StoragePool Name="RAID_002"/>
- </LUN>
- <iSCSI>
- <DefaultTargetIP>192.168.100.1</DefaultTargetIP>
- <Initiator Name="iqn.1993-08.debian:01:ec2bff7ac3a3"
- TargetIP="192.168.100.2"/>
- </iSCSI>
-</config>"""
-
LUNInfo = {'ID': None,
'Name': None,
'Size': None,
def __init__(self, *args, **kwargs):
super(HuaweiVolumeTestCase, self).__init__(*args, **kwargs)
- self.driver = FakeHuaweiStorage(configuration=conf.Configuration(None))
+
+ self.tmp_dir = tempfile.mkdtemp()
+ self.fake_conf_file = self.tmp_dir + '/cinder_huawei_conf.xml'
+ self._create_fake_conf_file()
+ configuration = mox.MockObject(conf.Configuration)
+ configuration.cinder_huawei_conf_file = self.fake_conf_file
+ configuration.append_config_values(mox.IgnoreArg())
+ self.driver = FakeHuaweiStorage(configuration=configuration)
+
self.driver.do_setup({})
- self.driver._test_flg = 'check_for_fail'
- self._test_check_for_setup_errors()
def setUp(self):
super(HuaweiVolumeTestCase, self).setUp()
+ self.driver._test_flg = 'check_for_fail'
+ self._test_check_for_setup_errors()
def test_create_export_failed(self):
self.assertRaises(exception.VolumeBackendAPIException,
self._test_delete_snapshot()
self._test_delete_volume()
+ def cleanup(self):
+ if os.path.exists(self.fake_conf_file):
+ os.remove(self.fake_conf_file)
+ shutil.rmtree(self.tmp_dir)
+
+ def _create_fake_conf_file(self):
+ doc = Document()
+
+ config = doc.createElement('config')
+ doc.appendChild(config)
+
+ storage = doc.createElement('Storage')
+ config.appendChild(storage)
+ controllerip0 = doc.createElement('ControllerIP0')
+ controllerip0_text = doc.createTextNode('10.10.10.1')
+ controllerip0.appendChild(controllerip0_text)
+ storage.appendChild(controllerip0)
+ controllerip1 = doc.createElement('ControllerIP1')
+ controllerip1_text = doc.createTextNode('10.10.10.2')
+ controllerip1.appendChild(controllerip1_text)
+ storage.appendChild(controllerip1)
+ username = doc.createElement('UserName')
+ username_text = doc.createTextNode('admin')
+ username.appendChild(username_text)
+ storage.appendChild(username)
+ userpassword = doc.createElement('UserPassword')
+ userpassword_text = doc.createTextNode('123456')
+ userpassword.appendChild(userpassword_text)
+ storage.appendChild(userpassword)
+
+ lun = doc.createElement('LUN')
+ config.appendChild(lun)
+ storagepool = doc.createElement('StoragePool')
+ storagepool.setAttribute('Name', 'RAID_001')
+ lun.appendChild(storagepool)
+ storagepool = doc.createElement('StoragePool')
+ storagepool.setAttribute('Name', 'RAID_002')
+ lun.appendChild(storagepool)
+
+ iscsi = doc.createElement('iSCSI')
+ config.appendChild(iscsi)
+ defaulttargetip = doc.createElement('DefaultTargetIP')
+ defaulttargetip_text = doc.createTextNode('192.168.100.1')
+ defaulttargetip.appendChild(defaulttargetip_text)
+ iscsi.appendChild(defaulttargetip)
+ initiator = doc.createElement('Initiator')
+ initiator.setAttribute('Name', 'iqn.1993-08.debian:01:ec2bff7ac3a3')
+ initiator.setAttribute('TargetIP', '192.168.100.2')
+ iscsi.appendChild(initiator)
+
+ file = open(self.fake_conf_file, 'w')
+ file.write(doc.toprettyxml(indent=''))
+ file.close()
+
def _test_check_for_setup_errors(self):
self.driver.check_for_setup_error()
============================================================================
"""
+ elif cmd == 'chglun':
+ out = 'command operates successfully'
+
out = out.replace('\n', '\r\n')
return out
- def _read_xml(self):
- try:
- root = ET.fromstring(FakeXML)
- except Exception as err:
- LOG.debug(_('_read_xml:ERROR:%s') % err)
- raise exception.VolumeBackendAPIException(data=err)
- return root
+ def _get_lun_controller(self, lunid):
+ pass
"""
Volume driver for HUAWEI T series and Dorado storage systems.
"""
+import base64
import os
import paramiko
import re
raise exception.VolumeBackendAPIException(data=err_msg)
target_ip = iscsi_conf['DefaultTargetIP']
- target_iqn = self._get_tgt_iqn(target_ip)
+ (target_iqn, controller) = self._get_tgt_iqn(target_ip)
if not target_iqn:
err_msg = (_('initialize_connection:Failed to find target iSCSI'
'iqn. Target IP:%(ip)s')
self._map_lun(lun_id, host_id, new_hostlun_id)
hostlun_id = self._get_hostlunid(host_id, lun_id)
+ # Change lun ownning controller for better performance.
+ if self._get_lun_controller(lun_id) != controller:
+ self._change_lun_controller(lun_id, controller)
+
# Return iSCSI properties.
properties = {}
properties['target_discovered'] = False
def _get_login_info(self):
"""Get login IP, username and password from config file."""
logininfo = {}
- root = self._read_xml()
try:
+ filename = self.configuration.cinder_huawei_conf_file
+ tree = ET.parse(filename)
+ root = tree.getroot()
logininfo['ControllerIP0'] = root.findtext('Storage/ControllerIP0')
logininfo['ControllerIP1'] = root.findtext('Storage/ControllerIP1')
- logininfo['UserName'] = root.findtext('Storage/UserName')
- logininfo['UserPassword'] = root.findtext('Storage/UserPassword')
+
+ need_encode = False
+ for key in ['UserName', 'UserPassword']:
+ node = root.find('Storage/%s' % key)
+ node_text = node.text
+ if node_text.find('!$$$') == 0:
+ logininfo[key] = base64.b64decode(node_text[4:])
+ else:
+ logininfo[key] = node_text
+ node.text = '!$$$' + base64.b64encode(node_text)
+ need_encode = True
+ if need_encode:
+ try:
+ tree.write(filename, 'UTF-8')
+ except Exception as err:
+ LOG.error(_('Write login informationto xml error. %s')
+ % err)
except Exception as err:
LOG.error(_('_get_login_info error. %s') % err)
out = self._execute_cli(cli_cmd)
en = out.split('\r\n')
if len(en) < 4:
- return None
+ return (None, None)
index = en[4].find('iqn')
iqn_prefix = en[4][index:]
LOG.debug(_('_get_tgt_iqn:iSCSI target iqn is:%s') % iqn)
- return iqn
+ return (iqn, iscsiip_info['ctrid'])
else:
- return None
+ return (None, None)
def _get_iscsi_ip_info(self, iscsiip):
"""Get iSCSI IP infomation of storage device."""
"""Get map infomation of the given host.
This method return a map information list. Every item in the list
- is a dictionary. The dictionarie includes three keys: mapid,
+ is a dictionary. The dictionary includes three keys: mapid,
devlunid, hostlunid. These items are sorted by hostlunid value
from small to large.
"""
return r[1]
return None
+ def _get_lun_controller(self, lun_id):
+ cli_cmd = ('showlun -lun %s' % lun_id)
+ out = self._execute_cli(cli_cmd)
+ en = out.split('\r\n')
+ if len(en) <= 4:
+ return None
+
+ if "Dorado2100 G2" == self.device_type['type']:
+ return en[10].split()[3]
+ else:
+ return en[12].split()[3]
+
+ def _change_lun_controller(self, lun_id, controller):
+ cli_cmd = ('chglun -lun %s -c %s' % (lun_id, controller))
+ out = self._execute_cli(cli_cmd)
+ if not re.search('command operates successfully', out):
+ err_msg = (_('_change_lun_controller:Failed to change lun owning'
+ 'controller. lun id:%(lunid)s. '
+ 'new controller:%(controller)s. '
+ 'out:%(out)s')
+ % {'lunid': lun_id,
+ 'controller': controller,
+ 'out': out})
+ LOG.error(err_msg)
+ raise exception.VolumeBackendAPIException(data=err_msg)
+
def _is_resource_pool_enough(self):
"""Check whether resource pools' valid size is more than 1G."""
cli_cmd = ('showrespool')