def __init__(self):
if Curr_test[0] == 'T':
self.simu = HuaweiTCLIResSimulator()
+ elif Curr_test[0] == 'Dorado5100':
+ self.simu = HuaweiDorado5100CLIResSimulator()
+ else:
+ self.simu = HuaweiDorado2100G2CLIResSimulator()
def resize_pty(self, width=80, height=24):
pass
return out
+class HuaweiDorado5100CLIResSimulator(HuaweiTCLIResSimulator):
+ def cli_showsys(self, params):
+ out = """/>showsys
+=============================================================
+ System Information
+-------------------------------------------------------------
+ System Name | SN_Dorado5100
+ Device Type | Oceanstor Dorado5100
+ Current System Mode | Double Controllers Normal
+ Mirroring Link Status | Link Up
+ Location |
+ Time | 2013-01-01 01:01:01
+ Product Version | V100R001C00
+=============================================================
+"""
+ return out
+
+ def cli_showlun(self, params):
+ if '-lun' not in params:
+ if LUN_INFO['ID'] is None:
+ out = 'command operates successfully, but no information.'
+ elif CLONED_LUN_INFO['ID'] is None:
+ out = """/>showlun
+===========================================================================
+ LUN Information
+---------------------------------------------------------------------------
+ ID RAIDgroup ID Status Controller Visible Capacity(MB) LUN Name..\
+ Strip Unit Size(KB) Lun Type
+---------------------------------------------------------------------------
+ %s %s Normal %s %s %s 64 THICK
+===========================================================================
+""" % (LUN_INFO['ID'], LUN_INFO['RAID Group ID'],
+ LUN_INFO['Owner Controller'], str(int(LUN_INFO['Size']) * 1024),
+ LUN_INFO['Name'])
+ else:
+ out = """/>showlun
+===========================================================================
+ LUN Information
+---------------------------------------------------------------------------
+ ID RAIDgroup ID Status Controller Visible Capacity(MB) LUN Name \
+ Strip Unit Size(KB) Lun Type
+---------------------------------------------------------------------------
+ %s %s Normal %s %s %s 64 THICK
+ %s %s Norma %s %s %s 64 THICK
+===========================================================================
+""" % (LUN_INFO['ID'], LUN_INFO['RAID Group ID'], LUN_INFO['Owner Controller'],
+ str(int(LUN_INFO['Size']) * 1024), LUN_INFO['Name'],
+ CLONED_LUN_INFO['ID'], CLONED_LUN_INFO['RAID Group ID'],
+ CLONED_LUN_INFO['Owner Controller'],
+ str(int(CLONED_LUN_INFO['Size']) * 1024),
+ CLONED_LUN_INFO['Name'])
+ elif params[params.index('-lun') + 1] in VOLUME_SNAP_ID.values():
+ out = """/>showlun
+================================================
+ LUN Information
+------------------------------------------------
+ ID | %s
+ Name | %s
+ LUN WWN | --
+ Visible Capacity | %s
+ RAID GROUP ID | %s
+ Owning Controller | %s
+ Workong Controller | %s
+ Lun Type | %s
+ SnapShot ID | %s
+ LunCopy ID | %s
+================================================
+""" % ((LUN_INFO['ID'], LUN_INFO['Name'], LUN_INFO['Visible Capacity'],
+ LUN_INFO['RAID Group ID'], LUN_INFO['Owner Controller'],
+ LUN_INFO['Worker Controller'], LUN_INFO['Lun Type'],
+ LUN_INFO['SnapShot ID'], LUN_INFO['LunCopy ID'])
+ if params[params.index('-lun')] == VOLUME_SNAP_ID['vol'] else
+ (CLONED_LUN_INFO['ID'], CLONED_LUN_INFO['Name'],
+ CLONED_LUN_INFO['Visible Capacity'], CLONED_LUN_INFO['RAID Group ID'],
+ CLONED_LUN_INFO['Owner Controller'],
+ CLONED_LUN_INFO['Worker Controller'],
+ CLONED_LUN_INFO['Lun Type'], CLONED_LUN_INFO['SnapShot ID'],
+ CLONED_LUN_INFO['LunCopy ID']))
+ else:
+ out = 'ERROR: The object does not exist.'
+ return out
+
+
+class HuaweiDorado2100G2CLIResSimulator(HuaweiTCLIResSimulator):
+ def cli_showsys(self, params):
+ out = """/>showsys
+==========================================================================
+ System Information
+--------------------------------------------------------------------------
+ System Name | SN_Dorado2100_G2
+ Device Type | Oceanstor Dorado2100 G2
+ Current System Mode | Double Controllers Normal
+ Mirroring Link Status | Link Up
+ Location |
+ Time | 2013-01-01 01:01:01
+ Product Version | V100R001C00
+===========================================================================
+"""
+ return out
+
+ def cli_createlun(self, params):
+ lun_type = ('THIN' if params[params.index('-type') + 1] == '2' else
+ 'THICK')
+ if LUN_INFO['ID'] is None:
+ LUN_INFO['Name'] = self._name_translate(FAKE_VOLUME['name'])
+ LUN_INFO['ID'] = VOLUME_SNAP_ID['vol']
+ LUN_INFO['Size'] = FAKE_VOLUME['size']
+ LUN_INFO['Lun Type'] = lun_type
+ LUN_INFO['Owner Controller'] = 'A'
+ LUN_INFO['Worker Controller'] = 'A'
+ LUN_INFO['RAID Group ID'] = POOL_SETTING['ID']
+ FAKE_VOLUME['provider_location'] = LUN_INFO['ID']
+ else:
+ CLONED_LUN_INFO['Name'] = \
+ self._name_translate(FAKE_CLONED_VOLUME['name'])
+ CLONED_LUN_INFO['ID'] = VOLUME_SNAP_ID['vol_copy']
+ CLONED_LUN_INFO['Size'] = FAKE_CLONED_VOLUME['size']
+ CLONED_LUN_INFO['Lun Type'] = lun_type
+ CLONED_LUN_INFO['Owner Controller'] = 'A'
+ CLONED_LUN_INFO['Worker Controller'] = 'A'
+ CLONED_LUN_INFO['RAID Group ID'] = POOL_SETTING['ID']
+ CLONED_LUN_INFO['provider_location'] = CLONED_LUN_INFO['ID']
+ FAKE_CLONED_VOLUME['provider_location'] = CLONED_LUN_INFO['ID']
+ out = 'command operates successfully'
+ return out
+
+ def cli_showlun(self, params):
+ if '-lun' not in params:
+ if LUN_INFO['ID'] is None:
+ out = 'command operates successfully, but no information.'
+ elif CLONED_LUN_INFO['ID'] is None:
+ out = """/>showlun
+===========================================================================
+ LUN Information
+---------------------------------------------------------------------------
+ ID Status Controller Visible Capacity(MB) LUN Name Lun Type
+---------------------------------------------------------------------------
+ %s Normal %s %s %s THICK
+===========================================================================
+""" % (LUN_INFO['ID'], LUN_INFO['Owner Controller'],
+ str(int(LUN_INFO['Size']) * 1024), LUN_INFO['Name'])
+ else:
+ out = """/>showlun
+===========================================================================
+ LUN Information
+---------------------------------------------------------------------------
+ ID Status Controller Visible Capacity(MB) LUN Name Lun Type
+---------------------------------------------------------------------------
+ %s Normal %s %s %s THICK
+ %s Normal %s %s %s THICK
+===========================================================================
+""" % (LUN_INFO['ID'], LUN_INFO['Owner Controller'],
+ str(int(LUN_INFO['Size']) * 1024), LUN_INFO['Name'],
+ CLONED_LUN_INFO['ID'], CLONED_LUN_INFO['Owner Controller'],
+ str(int(CLONED_LUN_INFO['Size']) * 1024), CLONED_LUN_INFO['Name'])
+
+ elif params[params.index('-lun') + 1] in VOLUME_SNAP_ID.values():
+ out = """/>showlun
+================================================
+ LUN Information
+------------------------------------------------
+ ID | %s
+ Name | %s
+ LUN WWN | --
+ Visible Capacity | %s
+ RAID GROUP ID | %s
+ Owning Controller | %s
+ Workong Controller | %s
+ Lun Type | %s
+ SnapShot ID | %s
+ LunCopy ID | %s
+================================================
+""" % ((LUN_INFO['ID'], LUN_INFO['Name'], LUN_INFO['Visible Capacity'],
+ LUN_INFO['RAID Group ID'], LUN_INFO['Owner Controller'],
+ LUN_INFO['Worker Controller'], LUN_INFO['Lun Type'],
+ LUN_INFO['SnapShot ID'], LUN_INFO['LunCopy ID'])
+ if params[params.index('-lun')] == VOLUME_SNAP_ID['vol'] else
+ (CLONED_LUN_INFO['ID'], CLONED_LUN_INFO['Name'],
+ CLONED_LUN_INFO['Visible Capacity'], CLONED_LUN_INFO['RAID Group ID'],
+ CLONED_LUN_INFO['Owner Controller'],
+ CLONED_LUN_INFO['Worker Controller'],
+ CLONED_LUN_INFO['Lun Type'], CLONED_LUN_INFO['SnapShot ID'],
+ CLONED_LUN_INFO['LunCopy ID']))
+
+ else:
+ out = 'ERROR: The object does not exist.'
+
+ return out
+
+
class HuaweiTISCSIDriverTestCase(test.TestCase):
def __init__(self, *args, **kwargs):
super(HuaweiTISCSIDriverTestCase, self).__init__(*args, **kwargs)
self.assertEqual(stats['storage_protocol'], 'iSCSI')
+class HuaweiDorado5100ISCSIDriverTestCase(HuaweiTISCSIDriverTestCase):
+ def __init__(self, *args, **kwargs):
+ super(HuaweiDorado5100ISCSIDriverTestCase, self).__init__(*args,
+ **kwargs)
+
+ def setUp(self):
+ super(HuaweiDorado5100ISCSIDriverTestCase, self).setUp()
+
+ def _init_driver(self):
+ Curr_test[0] = 'Dorado5100'
+ modify_conf(self.fake_conf_file, 'Storage/Product', 'Dorado')
+ self.driver = HuaweiVolumeDriver(configuration=self.configuration)
+ self.driver.do_setup(None)
+
+ def test_create_delete_cloned_volume(self):
+ self.assertRaises(exception.VolumeBackendAPIException,
+ self.driver.create_cloned_volume,
+ FAKE_CLONED_VOLUME, FAKE_VOLUME)
+
+ def test_create_delete_snapshot_volume(self):
+ self.assertRaises(exception.VolumeBackendAPIException,
+ self.driver.create_volume_from_snapshot,
+ FAKE_CLONED_VOLUME, FAKE_SNAPSHOT)
+
+ def test_volume_type(self):
+ pass
+
+
+class HuaweiDorado2100G2ISCSIDriverTestCase(HuaweiTISCSIDriverTestCase):
+ def __init__(self, *args, **kwargs):
+ super(HuaweiDorado2100G2ISCSIDriverTestCase, self).__init__(*args,
+ **kwargs)
+
+ def setUp(self):
+ super(HuaweiDorado2100G2ISCSIDriverTestCase, self).setUp()
+
+ def _init_driver(self):
+ Curr_test[0] = 'Dorado2100G2'
+ modify_conf(self.fake_conf_file, 'Storage/Product', 'Dorado')
+ self.driver = HuaweiVolumeDriver(configuration=self.configuration)
+ self.driver.do_setup(None)
+
+ def test_conf_invalid(self):
+ pass
+
+ def test_create_delete_cloned_volume(self):
+ self.assertRaises(exception.VolumeBackendAPIException,
+ self.driver.create_cloned_volume,
+ FAKE_CLONED_VOLUME, FAKE_VOLUME)
+
+ def test_create_delete_snapshot(self):
+ self.assertRaises(exception.VolumeBackendAPIException,
+ self.driver.create_snapshot, FAKE_SNAPSHOT)
+
+ def test_create_delete_snapshot_volume(self):
+ self.assertRaises(exception.VolumeBackendAPIException,
+ self.driver.create_volume_from_snapshot,
+ FAKE_CLONED_VOLUME, FAKE_SNAPSHOT)
+
+ def test_initialize_connection(self):
+ self.driver.create_volume(FAKE_VOLUME)
+ ret = self.driver.initialize_connection(FAKE_VOLUME, FAKE_CONNECTOR)
+ iscsi_propers = ret['data']
+ self.assertEquals(iscsi_propers['target_iqn'],
+ INITIATOR_SETTING['TargetIQN-form'])
+ self.assertEquals(iscsi_propers['target_portal'],
+ INITIATOR_SETTING['Initiator TargetIP'] + ':3260')
+ self.assertEqual(MAP_INFO["DEV LUN ID"], LUN_INFO['ID'])
+ self.assertEqual(MAP_INFO["INI Port Info"],
+ FAKE_CONNECTOR['initiator'])
+ self.driver.terminate_connection(FAKE_VOLUME, FAKE_CONNECTOR)
+ self.driver.delete_volume(FAKE_VOLUME)
+ self.assertEqual(LUN_INFO['ID'], None)
+
+
class SSHMethodTestCase(test.TestCase):
def __init__(self, *args, **kwargs):
super(SSHMethodTestCase, self).__init__(*args, **kwargs)
pool = out.split('\r\n')[6:-2]
return [line.split() for line in pool]
+
+
+class DoradoCommon(TseriesCommon):
+ """Common class for Huawei Dorado2100 G2 and Dorado5100 storage arrays.
+
+ Dorados share a lot of common codes with T series storage systems,
+ so this class inherited from class TseriesCommon and just rewrite some
+ methods.
+
+ """
+
+ def __init__(self, configuration=None):
+ TseriesCommon.__init__(self, configuration)
+ self.device_type = None
+
+ def do_setup(self, context):
+ """Check config file."""
+ LOG.debug(_('do_setup.'))
+
+ self._check_conf_file()
+ self.lun_distribution = self._get_lun_ctr_info()
+
+ def _check_conf_file(self):
+ """Check the config file, make sure the key elements are set."""
+ root = parse_xml_file(self.xml_conf)
+ # Check login infomation
+ IP1 = root.findtext('Storage/ControllerIP0')
+ IP2 = root.findtext('Storage/ControllerIP1')
+ username = root.findtext('Storage/UserName')
+ pwd = root.findtext('Storage/UserPassword')
+ if (not IP1 and not IP2) or (not username) or (not pwd):
+ err_msg = (_('Config file invalid. Controler IP, UserName, '
+ 'UserPassword must be specified.'))
+ LOG.error(err_msg)
+ raise exception.InvalidInput(reason=err_msg)
+
+ # Check storage pool
+ # No need for Dorado2100 G2
+ self.login_info = self._get_login_info()
+ self.device_type = self._get_device_type()
+ if self.device_type == 'Dorado5100':
+ pool_node = root.findall('LUN/StoragePool')
+ if not pool_node:
+ err_msg = (_('_check_conf_file: Config file invalid. '
+ 'StoragePool must be specified.'))
+ LOG.error(err_msg)
+ raise exception.InvalidInput(reason=err_msg)
+
+ def _get_device_type(self):
+ """Run CLI command to get system type."""
+ cli_cmd = 'showsys'
+ out = self._execute_cli(cli_cmd)
+
+ self._assert_cli_out(re.search('System Information', out),
+ '_get_device_type',
+ 'Failed to get system information',
+ cli_cmd, out)
+
+ for line in out.split('\r\n')[4:-2]:
+ if re.search('Device Type', line):
+ if re.search('Dorado2100 G2$', line):
+ return 'Dorado2100 G2'
+ elif re.search('Dorado5100$', line):
+ return 'Dorado5100'
+ else:
+ LOG.error(_('_get_device_type: The drivers only support'
+ 'Dorado5100 and Dorado 2100 G2 now.'))
+ raise exception.InvalidResults()
+
+ def _get_lun_ctr_info(self):
+ luns = self._get_all_luns_info()
+ ctr_info = [0, 0]
+ (c, n) = ((2, 4) if self.device_type == 'Dorado2100 G2' else (3, 5))
+ for lun in luns:
+ if lun[n].startswith('OpenStack'):
+ if lun[c] == 'A':
+ ctr_info[0] += 1
+ else:
+ ctr_info[1] += 1
+ return ctr_info
+
+ def _create_volume(self, name, size, params):
+ """Create a new volume with the given name and size."""
+ cli_cmd = ('createlun -n %(name)s -lunsize %(size)s '
+ '-wrtype %(wrtype)s '
+ % {'name': name,
+ 'size': size,
+ 'wrtype': params['WriteType']})
+
+ # If write type is "write through", no need to set mirror switch.
+ if params['WriteType'] != '2':
+ cli_cmd = cli_cmd + ('-mirrorsw %(mirrorsw)s '
+ % {'mirrorsw': params['MirrorSwitch']})
+
+ ctr = self._calculate_lun_ctr()
+ # Dorado5100 does not support thin LUN.
+ if self.device_type == 'Dorado5100':
+ cli_cmd = cli_cmd + ('-rg %(raidgroup)s -susize %(susize)s '
+ '-c %(ctr)s'
+ % {'raidgroup': params['StoragePool'],
+ 'susize': params['StripUnitSize'],
+ 'ctr': ctr})
+ else:
+ if params['LUNType'] == 'Thin':
+ # Not allowed to specify ctr for thin LUN.
+ ctr_str = ''
+ luntype_str = '-type 2'
+ else:
+ ctr_str = ' -c %s' % ctr
+ luntype_str = '-type 3'
+
+ cli_cmd = cli_cmd + luntype_str + ctr_str
+
+ out = self._execute_cli(cli_cmd)
+
+ self._assert_cli_operate_out('_create_volume',
+ 'Failed to create volume %s' % name,
+ cli_cmd, out)
+
+ self._update_lun_distribution(ctr)
+
+ return self._get_lun_id(name)
+
+ def _get_lun_id(self, name):
+ luns = self._get_all_luns_info()
+ if luns:
+ n_index = (4 if 'Dorado2100 G2' == self.device_type else 5)
+ for lun in luns:
+ if lun[n_index] == name:
+ return lun[0]
+ return None
+
+ def create_volume_from_snapshot(self, volume, snapshot):
+ err_msg = (_('create_volume_from_snapshot: %(device)s does '
+ 'not support create volume from snapshot.')
+ % {'device': self.device_type})
+ LOG.error(err_msg)
+ raise exception.VolumeBackendAPIException(data=err_msg)
+
+ def create_cloned_volume(self, volume, src_vref):
+ err_msg = (_('create_cloned_volume: %(device)s does '
+ 'not support clone volume.')
+ % {'device': self.device_type})
+ LOG.error(err_msg)
+ raise exception.VolumeBackendAPIException(data=err_msg)
+
+ def create_snapshot(self, snapshot):
+ if self.device_type == 'Dorado2100 G2':
+ err_msg = (_('create_snapshot: %(device)s does not support '
+ 'snapshot.') % {'device': self.device_type})
+ LOG.error(err_msg)
+ raise exception.VolumeBackendAPIException(data=err_msg)
+ else:
+ return TseriesCommon.create_snapshot(self, snapshot)
+
+ def delete_snapshot(self, snapshot):
+ if self.device_type == 'Dorado2100 G2':
+ return
+ else:
+ TseriesCommon.delete_snapshot(self, snapshot)
+
+ def _get_lun_params(self):
+ params_conf = self._parse_conf_lun_params()
+ # Select a pool with maximum capacity.
+ if self.device_type == 'Dorado5100':
+ pools_dev = self._get_dev_pool_info('Thick')
+ params_conf['StoragePool'] = \
+ self._get_maximum_capacity_pool_id(params_conf['StoragePool'],
+ pools_dev, 'Thick')
+ return params_conf
+
+ def _parse_conf_lun_params(self):
+ """Get parameters from config file for creating LUN."""
+ # Default LUN parameters.
+ conf_params = {'LUNType': 'Thin',
+ 'StripUnitSize': '64',
+ 'WriteType': '1',
+ 'MirrorSwitch': '1'}
+
+ root = parse_xml_file(self.xml_conf)
+
+ luntype = root.findtext('LUN/LUNType')
+ if luntype:
+ if luntype.strip() in ['Thick', 'Thin']:
+ conf_params['LUNType'] = luntype.strip()
+ else:
+ err_msg = (_('LUNType must be "Thin" or "Thick". '
+ 'LUNType:%(type)s') % {'type': luntype})
+ raise exception.InvalidInput(reason=err_msg)
+
+ # Here we do not judge whether the parameters are set correct.
+ # CLI will return error responses if the parameters are invalid.
+ stripunitsize = root.findtext('LUN/StripUnitSize')
+ if stripunitsize:
+ conf_params['StripUnitSize'] = stripunitsize.strip()
+ writetype = root.findtext('LUN/WriteType')
+ if writetype:
+ conf_params['WriteType'] = writetype.strip()
+ mirrorswitch = root.findtext('LUN/MirrorSwitch')
+ if mirrorswitch:
+ conf_params['MirrorSwitch'] = mirrorswitch.strip()
+
+ # No need to set StoragePool for Dorado2100 G2.
+ if self.device_type == 'Dorado2100 G2':
+ return conf_params
+
+ pools_conf = root.findall('LUN/StoragePool')
+ conf_params['StoragePool'] = []
+ for pool in pools_conf:
+ conf_params['StoragePool'].append(pool.attrib['Name'].strip())
+
+ return conf_params
+
+ def _get_free_capacity(self):
+ """Get total free capacity of pools."""
+ self._update_login_info()
+ lun_type = ('Thin' if self.device_type == 'Dorado2100 G2' else 'Thick')
+ pools_dev = self._get_dev_pool_info(lun_type)
+ total_free_capacity = 0.0
+ for pool_dev in pools_dev:
+ if self.device_type == 'Dorado2100 G2':
+ total_free_capacity += float(pool_dev[2])
+ continue
+ else:
+ params_conf = self._parse_conf_lun_params()
+ pools_conf = params_conf['StoragePool']
+ for pool_conf in pools_conf:
+ if pool_dev[5] == pool_conf:
+ total_free_capacity += float(pool_dev[3])
+ break
+
+ return total_free_capacity / 1024