From: Avishay Traeger Date: Wed, 10 Jul 2013 10:59:51 +0000 (+0300) Subject: Add optimized volume migration to Storwize/SVC X-Git-Url: https://review.fuel-infra.org/gitweb?a=commitdiff_plain;h=1cbf6c3c00c03c5acd86bcc6dddfc9822b19120f;p=openstack-build%2Fcinder-build.git Add optimized volume migration to Storwize/SVC Add an optimized migration path for when the source and destination are managed by the same storage. If the source and destination pools have the same extent_size, it will use the optimal migratevdisk command. Otherwise, it will use addvdiskcopy and rmvdiskcopy, which must be watched until the copies are sync'ed. Change-Id: Ic6069422f5ac7f963f4b1268c0d632a038a8474f --- diff --git a/cinder/tests/test_storwize_svc.py b/cinder/tests/test_storwize_svc.py index b2a4949e5..2bc058b53 100644 --- a/cinder/tests/test_storwize_svc.py +++ b/cinder/tests/test_storwize_svc.py @@ -1,5 +1,3 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - # Copyright 2013 IBM Corp. # Copyright 2012 OpenStack LLC. # All Rights Reserved. @@ -62,6 +60,7 @@ class StorwizeSVCManagementSimulator: self._hosts_list = {} self._mappings_list = {} self._fcmappings_list = {} + self._other_pools = {'openstack2': {}, 'openstack3': {}} self._next_cmd_error = { 'lsportip': '', 'lsfabric': '', @@ -118,6 +117,13 @@ class StorwizeSVCManagementSimulator: 'CMMVC7050E': ('', 'CMMVC7050E The command failed because at ' 'least one node in the I/O group does not ' 'support compressed VDisks.'), + 'CMMVC6430E': ('', 'CMMVC6430E The command failed because the ' + 'target and source managed disk groups must ' + 'be different.'), + 'CMMVC6353E': ('', 'CMMVC6353E The command failed because the ' + 'copy specified does not exist.'), + 'CMMVC6446E': ('', 'The command failed because the managed disk ' + 'groups have different extent sizes.'), # Catch-all for invalid state transitions: 'CMMVC5903E': ('', 'CMMVC5903E The FlashCopy mapping was not ' 'changed because the mapping or consistency ' @@ -192,8 +198,10 @@ class StorwizeSVCManagementSimulator: one_param_args = [ 'chapsecret', 'cleanrate', + 'copy', 'copyrate', 'delim', + 'easytier', 'filtervalue', 'grainsize', 'hbawwpn', @@ -208,7 +216,7 @@ class StorwizeSVCManagementSimulator: 'source', 'target', 'unit', - 'easytier', + 'vdisk', 'warning', 'wwpn', ] @@ -308,7 +316,7 @@ class StorwizeSVCManagementSimulator: # Print mostly made-up stuff in the correct syntax, assume -bytes passed def _cmd_lsmdiskgrp(self, **kwargs): - rows = [None] * 3 + rows = [None] * 4 rows[0] = ['id', 'name', 'status', 'mdisk_count', 'vdisk_count', 'capacity', 'extent_size', 'free_capacity', 'virtual_capacity', 'used_capacity', @@ -318,17 +326,23 @@ class StorwizeSVCManagementSimulator: '1', str(len(self._volumes_list)), '3573412790272', '256', '3529926246400', '1693247906775', '277841182', '38203734097', '47', '80', 'auto', 'inactive'] - rows[2] = ['2', 'volpool2', 'online', + rows[2] = ['2', 'openstack2', 'online', '1', '0', '3573412790272', '256', '3529432325160', '1693247906775', '277841182', '38203734097', '47', '80', 'auto', 'inactive'] + rows[3] = ['3', 'openstack3', 'online', + '1', '0', '3573412790272', '128', + '3529432325160', '1693247906775', '277841182', + '38203734097', '47', '80', 'auto', 'inactive'] if 'obj' not in kwargs: return self._print_info_cmd(rows=rows, **kwargs) else: if kwargs['obj'] == self._flags['storwize_svc_volpool_name']: row = rows[1] - elif kwargs['obj'] == 'volpool2': + elif kwargs['obj'] == 'openstack2': row = rows[2] + elif kwargs['obj'] == 'openstack3': + row = rows[3] else: return self._errors['CMMVC5754E'] @@ -552,6 +566,16 @@ port_speed!N/A volume_info['grainsize'] = '' volume_info['compressed_copy'] = 'no' + vol_cp = {'id': '0', + 'status': 'online', + 'sync': 'yes', + 'primary': 'yes', + 'mdisk_grp_id': '1', + 'mdisk_grp_name': self._flags['storwize_svc_volpool_name'], + 'easy_tier': volume_info['easy_tier'], + 'compressed_copy': volume_info['compressed_copy']} + volume_info['copies'] = {'0': vol_cp} + if volume_info['name'] in self._volumes_list: return self._errors['CMMVC6035E'] else: @@ -1144,7 +1168,135 @@ port_speed!N/A return self._print_info_cmd(rows=rows, **kwargs) - # Add host to list + def _cmd_migratevdisk(self, **kwargs): + if 'mdiskgrp' not in kwargs or 'vdisk' not in kwargs: + return self._errors['CMMVC5707E'] + mdiskgrp = kwargs['mdiskgrp'].strip('\'\'') + vdisk = kwargs['vdisk'].strip('\'\'') + + if vdisk in self._volumes_list: + curr_mdiskgrp = self._volumes_list + else: + for pool in self._other_pools: + if vdisk in pool: + curr_mdiskgrp = pool + break + else: + return self._errors['CMMVC5754E'] + + if mdiskgrp == self._flags['storwize_svc_volpool_name']: + tgt_mdiskgrp = self._volumes_list + elif mdiskgrp == 'openstack2': + tgt_mdiskgrp = self._other_pools['openstack2'] + elif mdiskgrp == 'openstack3': + tgt_mdiskgrp = self._other_pools['openstack3'] + else: + return self._errors['CMMVC5754E'] + + if curr_mdiskgrp == tgt_mdiskgrp: + return self._errors['CMMVC6430E'] + + vol = curr_mdiskgrp[vdisk] + tgt_mdiskgrp[vdisk] = vol + del curr_mdiskgrp[vdisk] + return ('', '') + + def _cmd_addvdiskcopy(self, **kwargs): + if 'obj' not in kwargs: + return self._errors['CMMVC5701E'] + vol_name = kwargs['obj'].strip('\'\'') + if vol_name not in self._volumes_list: + return self._errors['CMMVC5753E'] + vol = self._volumes_list[vol_name] + if 'mdiskgrp' not in kwargs: + return self._errors['CMMVC5707E'] + mdiskgrp = kwargs['mdiskgrp'].strip('\'\'') + + copy_info = {} + copy_info['id'] = self._find_unused_id(vol['copies']) + copy_info['status'] = 'online' + copy_info['sync'] = 'no' + copy_info['primary'] = 'no' + copy_info['mdisk_grp_name'] = mdiskgrp + if mdiskgrp == self._flags['storwize_svc_volpool_name']: + copy_info['mdisk_grp_id'] = '1' + elif mdiskgrp == 'openstack2': + copy_info['mdisk_grp_id'] = '2' + elif mdiskgrp == 'openstack3': + copy_info['mdisk_grp_id'] = '3' + if 'easytier' in kwargs: + if kwargs['easytier'] == 'on': + copy_info['easy_tier'] = 'on' + else: + copy_info['easy_tier'] = 'off' + if 'rsize' in kwargs: + if 'compressed' in kwargs: + copy_info['compressed_copy'] = 'yes' + else: + copy_info['compressed_copy'] = 'no' + vol['copies'][copy_info['id']] = copy_info + return ('Vdisk [%(vid)s] copy [%(cid)s] successfully created' % + {'vid': vol['id'], 'cid': copy_info['id']}, '') + + def _cmd_lsvdiskcopy(self, **kwargs): + if 'obj' not in kwargs: + return self._errors['CMMVC5804E'] + name = kwargs['obj'] + vol = self._volumes_list[name] + rows = [] + rows.append(['vdisk_id', 'vdisk_name', 'copy_id', 'status', 'sync', + 'primary', 'mdisk_grp_id', 'mdisk_grp_name', 'capacity', + 'type', 'se_copy', 'easy_tier', 'easy_tier_status', + 'compressed_copy']) + for k, copy in vol['copies'].iteritems(): + rows.append([vol['id'], vol['name'], copy['id'], + copy['status'], copy['sync'], copy['primary'], + copy['mdisk_grp_id'], copy['mdisk_grp_name'], + vol['capacity'], 'striped', 'yes', copy['easy_tier'], + 'inactive', copy['compressed_copy']]) + if 'copy' not in kwargs: + return self._print_info_cmd(rows=rows, **kwargs) + else: + copy_id = kwargs['copy'].strip('\'\'') + if copy_id not in vol['copies']: + return self._errors['CMMVC6353E'] + copy = vol['copies'][copy_id] + rows = [] + rows.append(['vdisk_id', vol['id']]) + rows.append(['vdisk_name', vol['name']]) + rows.append(['capacity', vol['capacity']]) + rows.append(['copy_id', copy['id']]) + rows.append(['status', copy['status']]) + rows.append(['sync', copy['sync']]) + copy['sync'] = 'yes' + rows.append(['primary', copy['primary']]) + rows.append(['mdisk_grp_id', copy['mdisk_grp_id']]) + rows.append(['mdisk_grp_name', copy['mdisk_grp_name']]) + rows.append(['easy_tier', copy['easy_tier']]) + rows.append(['easy_tier_status', 'inactive']) + rows.append(['compressed_copy', copy['compressed_copy']]) + + if 'delim' in kwargs: + for index in range(len(rows)): + rows[index] = kwargs['delim'].join(rows[index]) + + return ('%s' % '\n'.join(rows), '') + + def _cmd_rmvdiskcopy(self, **kwargs): + if 'obj' not in kwargs: + return self._errors['CMMVC5701E'] + vol_name = kwargs['obj'].strip('\'\'') + if 'copy' not in kwargs: + return self._errors['CMMVC5707E'] + copy_id = kwargs['copy'].strip('\'\'') + if vol_name not in self._volumes_list: + return self._errors['CMMVC5753E'] + vol = self._volumes_list[vol_name] + if copy_id not in vol['copies']: + return self._errors['CMMVC6353E'] + del vol['copies'][copy_id] + return ('', '') + def _add_host_to_list(self, connector): host_info = {} host_info['id'] = self._find_unused_id(self._hosts_list) @@ -1225,6 +1377,14 @@ port_speed!N/A out, err = self._cmd_lsfcmap(**kwargs) elif command == 'lsvdiskfcmappings': out, err = self._cmd_lsvdiskfcmappings(**kwargs) + elif command == 'migratevdisk': + out, err = self._cmd_migratevdisk(**kwargs) + elif command == 'addvdiskcopy': + out, err = self._cmd_addvdiskcopy(**kwargs) + elif command == 'lsvdiskcopy': + out, err = self._cmd_lsvdiskcopy(**kwargs) + elif command == 'rmvdiskcopy': + out, err = self._cmd_rmvdiskcopy(**kwargs) else: out, err = ('', 'ERROR: Unsupported command') @@ -1281,6 +1441,7 @@ class StorwizeSVCDriverTestCase(test.TestCase): self._def_flags = {'san_ip': 'hostname', 'san_login': 'user', 'san_password': 'pass', + 'storwize_svc_volpool_name': 'openstack', 'storwize_svc_flashcopy_timeout': 20, # Test ignore capitalization 'storwize_svc_connection_protocol': 'iScSi', @@ -1292,7 +1453,7 @@ class StorwizeSVCDriverTestCase(test.TestCase): 'host': 'storwize-svc-test', 'wwpns': wwpns, 'initiator': initiator} - self.sim = StorwizeSVCManagementSimulator('volpool') + self.sim = StorwizeSVCManagementSimulator('openstack') self.driver.set_fake_storage(self.sim) else: @@ -1969,9 +2130,10 @@ class StorwizeSVCDriverTestCase(test.TestCase): self.assertLessEqual(stats['free_capacity_gb'], stats['total_capacity_gb']) self.assertEquals(stats['reserved_percentage'], 25) + pool = self.driver.configuration.local_conf.storwize_svc_volpool_name if self.USESIM: - self.assertEqual(stats['volume_backend_name'], - 'storwize-svc-sim_volpool') + expected = 'storwize-svc-sim_' + pool + self.assertEqual(stats['volume_backend_name'], expected) self.assertAlmostEqual(stats['total_capacity_gb'], 3328.0) self.assertAlmostEqual(stats['free_capacity_gb'], 3287.5) @@ -1993,6 +2155,52 @@ class StorwizeSVCDriverTestCase(test.TestCase): self.driver.delete_snapshot(snap) self.driver.delete_volume(volume) + def _check_loc_info(self, capabilities, expected): + host = {'host': 'foo', 'capabilities': capabilities} + vol = {'name': 'test', 'id': 1, 'size': 1} + ctxt = context.get_admin_context() + moved, model_update = self.driver.migrate_volume(ctxt, vol, host) + self.assertEqual(moved, expected['moved']) + self.assertEqual(model_update, expected['model_update']) + + def test_storwize_svc_migrate_bad_loc_info(self): + self._check_loc_info({}, {'moved': False, 'model_update': None}) + cap = {'location_info': 'foo'} + self._check_loc_info(cap, {'moved': False, 'model_update': None}) + cap = {'location_info': 'FooDriver:foo:bar'} + self._check_loc_info(cap, {'moved': False, 'model_update': None}) + cap = {'location_info': 'StorwizeSVCDriver:foo:bar'} + self._check_loc_info(cap, {'moved': False, 'model_update': None}) + + def test_storwize_svc_migrate_same_extent_size(self): + def _copy_info_exc(self, name): + raise Exception('should not be called') + + self.stubs.Set(self.driver, '_get_vdisk_copy_info', _copy_info_exc) + self.driver.do_setup(None) + loc = 'StorwizeSVCDriver:' + self.driver._system_id + ':openstack2' + cap = {'location_info': loc, 'extent_size': '256'} + host = {'host': 'foo', 'capabilities': cap} + ctxt = context.get_admin_context() + volume = self._generate_vol_info(None, None) + volume['volume_type_id'] = None + self.driver.create_volume(volume) + self.driver.migrate_volume(ctxt, volume, host) + self.driver.delete_volume(volume) + + def test_storwize_svc_migrate_diff_extent_size(self): + self.driver.do_setup(None) + loc = 'StorwizeSVCDriver:' + self.driver._system_id + ':openstack3' + cap = {'location_info': loc, 'extent_size': '128'} + host = {'host': 'foo', 'capabilities': cap} + ctxt = context.get_admin_context() + volume = self._generate_vol_info(None, None) + volume['volume_type_id'] = None + self.driver.create_volume(volume) + self.assertNotEquals(cap['extent_size'], self.driver._extent_size) + self.driver.migrate_volume(ctxt, volume, host) + self.driver.delete_volume(volume) + class CLIResponseTestCase(test.TestCase): def test_empty(self): diff --git a/cinder/volume/drivers/storwize_svc.py b/cinder/volume/drivers/storwize_svc.py index a6350796e..374da38aa 100644 --- a/cinder/volume/drivers/storwize_svc.py +++ b/cinder/volume/drivers/storwize_svc.py @@ -131,6 +131,9 @@ class StorwizeSVCDriver(san.SanDriver): self._compression_enabled = False self._available_iogrps = [] self._context = None + self._system_name = None + self._system_id = None + self._extent_size = None # Build cleanup translation tables for host names invalid_ch_in_host = '' @@ -188,16 +191,25 @@ class StorwizeSVCDriver(san.SanDriver): LOG.debug(_('enter: do_setup')) self._context = ctxt + # Get storage system name and id + ssh_cmd = ['svcinfo', 'lssystem', '-delim', '!'] + attributes = self._execute_command_and_parse_attributes(ssh_cmd) + if not attributes or not attributes['name']: + msg = (_('do_setup: Could not get system name')) + LOG.error(msg) + raise exception.VolumeBackendAPIException(data=msg) + self._system_name = attributes['name'] + self._system_id = attributes['id'] + # Validate that the pool exists - ssh_cmd = ['svcinfo', 'lsmdiskgrp', '-delim', '!', '-nohdr'] - out, err = self._run_ssh(ssh_cmd) - self._assert_ssh_return(len(out.strip()), 'do_setup', - ssh_cmd, out, err) - search_text = '!%s!' % self.configuration.storwize_svc_volpool_name - if search_text not in out: - raise exception.InvalidInput( - reason=(_('pool %s doesn\'t exist') - % self.configuration.storwize_svc_volpool_name)) + pool = self.configuration.storwize_svc_volpool_name + ssh_cmd = ['svcinfo', 'lsmdiskgrp', '-bytes', '-delim', '!', pool] + attributes = self._execute_command_and_parse_attributes(ssh_cmd) + if not attributes: + msg = (_('do_setup: Pool %s does not exist') % pool) + LOG.error(msg) + raise exception.InvalidInput(reason=msg) + self._extent_size = attributes['extent_size'] # Check if compression is supported self._compression_enabled = False @@ -325,6 +337,17 @@ class StorwizeSVCDriver(san.SanDriver): """Ensure that the flags are set properly.""" LOG.debug(_('enter: check_for_setup_error')) + # Check that we have the system ID information + if self._system_name is None: + exception_msg = (_('Unable to determine system name')) + raise exception.VolumeBackendAPIException(data=exception_msg) + if self._system_id is None: + exception_msg = (_('Unable to determine system id')) + raise exception.VolumeBackendAPIException(data=exception_msg) + if self._extent_size is None: + exception_msg = (_('Unable to determine pool extent size')) + raise exception.VolumeBackendAPIException(data=exception_msg) + required_flags = ['san_ip', 'san_ssh_port', 'san_login', 'storwize_svc_volpool_name'] for flag in required_flags: @@ -961,27 +984,12 @@ class StorwizeSVCDriver(san.SanDriver): LOG.debug(_('enter: _create_vdisk: vdisk %s ') % name) model_update = None - easytier = 'on' if opts['easytier'] else 'off' - - # Set space-efficient options - if opts['rsize'] == -1: - ssh_cmd_se_opt = [] - else: - ssh_cmd_se_opt = ['-rsize', '%s%%' % str(opts['rsize']), - '-autoexpand', '-warning', - '%s%%' % str(opts['warning'])] - if not opts['autoexpand']: - ssh_cmd_se_opt.remove('-autoexpand') - - if opts['compression']: - ssh_cmd_se_opt.append('-compressed') - else: - ssh_cmd_se_opt.extend(['-grainsize', str(opts['grainsize'])]) + params = self._get_vdisk_create_params(opts) ssh_cmd = ['svctask', 'mkvdisk', '-name', name, '-mdiskgrp', self.configuration.storwize_svc_volpool_name, '-iogrp', str(opts['iogrp']), '-size', size, '-unit', - units, '-easytier', easytier] + ssh_cmd_se_opt + units] + params out, err = self._run_ssh(ssh_cmd) self._assert_ssh_return(len(out.strip()), '_create_vdisk', ssh_cmd, out, err) @@ -1383,6 +1391,98 @@ class StorwizeSVCDriver(san.SanDriver): ssh_cmd, out, err) LOG.debug(_('leave: extend_volume: volume %s') % volume['id']) + def migrate_volume(self, ctxt, volume, host): + """Migrate direclty if source and dest are managed by same storage. + + The method uses the migratevdisk method, which returns almost + immediately, if the source and target pools have the same extent_size. + Otherwise, it uses addvdiskcopy and rmvdiskcopy, which require waiting + for the copy operation to complete. + + :param ctxt: Context + :param volume: A dictionary describing the volume to migrate + :param host: A dictionary describing the host to migrate to, where + host['host'] is its name, and host['capabilities'] is a + dictionary of its reported capabilities. + """ + LOG.debug(_('enter: migrate_volume: id=%(id)s, host=%(host)s') % + {'id': volume['id'], 'host': host['host']}) + + false_ret = (False, None) + if 'location_info' not in host['capabilities']: + return false_ret + info = host['capabilities']['location_info'] + try: + (dest_type, dest_id, dest_pool) = info.split(':') + except ValueError: + return false_ret + if (dest_type != 'StorwizeSVCDriver' or dest_id != self._system_id): + return false_ret + + if 'extent_size' not in host['capabilities']: + return false_ret + if host['capabilities']['extent_size'] == self._extent_size: + # If source and dest pools have the same extent size, migratevdisk + ssh_cmd = ['svctask', 'migratevdisk', '-mdiskgrp', dest_pool, + '-vdisk', volume['name']] + out, err = self._run_ssh(ssh_cmd) + # No output should be returned from migratevdisk + self._assert_ssh_return(len(out.strip()) == 0, 'migrate_volume', + ssh_cmd, out, err) + else: + # If source and dest pool extent size differ, add/delete vdisk copy + copy_info = self._get_vdisk_copy_info(volume['name']) + copies = list(copy_info.keys()) + self._driver_assert(len(copies) == 1, + _('migrate_volume started with more than one ' + 'vdisk copy')) + orig_copy_id = copies[0] + + opts = self._get_vdisk_params(volume['volume_type_id']) + params = self._get_vdisk_create_params(opts) + ssh_cmd = (['svctask', 'addvdiskcopy'] + params + ['-mdiskgrp', + dest_pool, volume['name']]) + out, err = self._run_ssh(ssh_cmd) + self._assert_ssh_return(len(out.strip()), 'migrate_volume', + ssh_cmd, out, err) + + # Ensure that the output is as expected + match_obj = re.search('Vdisk \[([0-9]+)\] copy \[([0-9]+)\] ' + 'successfully created', out) + # Make sure we got a "successfully created" message with copy id + self._driver_assert( + match_obj is not None, + _('migrate_volume %(name)s - did not find ' + 'success message in CLI output.\n ' + 'stdout: %(out)s\n stderr: %(err)s') + % {'name': volume['name'], 'out': str(out), 'err': str(err)}) + + copy_id = match_obj.group(2) + sync = False + while not sync: + ssh_cmd = ['svcinfo', 'lsvdiskcopy', '-delim', '!', '-copy', + copy_id, volume['name']] + attrs = self._execute_command_and_parse_attributes(ssh_cmd) + if not attrs: + msg = (_('migrate_volume: Could not get vdisk copy data')) + LOG.error(msg) + raise exception.VolumeBackendAPIException(data=msg) + if attrs['sync'] == 'yes': + sync = True + else: + time.sleep(5) + + ssh_cmd = ['svctask', 'rmvdiskcopy', '-copy', orig_copy_id, + volume['name']] + out, err = self._run_ssh(ssh_cmd) + # No output should be returned from rmvdiskcopy + self._assert_ssh_return(len(out.strip()) == 0, 'migrate_volume', + ssh_cmd, out, err) + + LOG.debug(_('leave: migrate_volume: id=%(id)s, host=%(host)s') % + {'id': volume['id'], 'host': host['host']}) + return (True, None) + """=====================================================================""" """ MISC/HELPERS """ """=====================================================================""" @@ -1414,18 +1514,9 @@ class StorwizeSVCDriver(san.SanDriver): data['QoS_support'] = False pool = self.configuration.storwize_svc_volpool_name - #Get storage system name - ssh_cmd = ['svcinfo', 'lssystem', '-delim', '!'] - attributes = self._execute_command_and_parse_attributes(ssh_cmd) - if not attributes or not attributes['name']: - exception_message = (_('_update_volume_stats: ' - 'Could not get system name.')) - LOG.error(exception_message) - raise exception.VolumeBackendAPIException(data=exception_message) - backend_name = self.configuration.safe_get('volume_backend_name') if not backend_name: - backend_name = '%s_%s' % (attributes['name'], pool) + backend_name = '%s_%s' % (self._system_name, pool) data['volume_backend_name'] = backend_name ssh_cmd = ['svcinfo', 'lsmdiskgrp', '-bytes', '-delim', '!', pool] @@ -1442,6 +1533,10 @@ class StorwizeSVCDriver(san.SanDriver): (1024 ** 3)) data['easytier_support'] = attributes['easy_tier'] in ['on', 'auto'] data['compression_support'] = self._compression_enabled + data['extent_size'] = self._extent_size + data['location_info'] = ('StorwizeSVCDriver:%(sys_id)s:%(pool)s' % + {'sys_id': self._system_id, + 'pool': pool}) self._stats = data @@ -1466,6 +1561,49 @@ class StorwizeSVCDriver(san.SanDriver): ssh_cmd, out, err) yield port_data + def _get_vdisk_copy_info(self, vdisk): + ssh_cmd = ['svcinfo', 'lsvdiskcopy', '-delim', '!', vdisk] + out, err = self._run_ssh(ssh_cmd) + + self._assert_ssh_return(len(out.strip()), '_get_vdisk_copy_info', + ssh_cmd, out, err) + copy_lines = out.strip().split('\n') + self._assert_ssh_return(len(copy_lines), '_get_vdisk_copy_info', + ssh_cmd, out, err) + + header = copy_lines.pop(0) + ret = {} + for copy_line in copy_lines: + try: + copy_data = self._get_hdr_dic(header, copy_line, '!') + except exception.VolumeBackendAPIException: + with excutils.save_and_reraise_exception(): + self._log_cli_output_error('_get_vdisk_copy_info', + ssh_cmd, out, err) + ret[copy_data['copy_id']] = copy_data + return ret + + def _get_vdisk_create_params(self, opts): + easytier = 'on' if opts['easytier'] else 'off' + + # Set space-efficient options + if opts['rsize'] == -1: + params = [] + else: + params = ['-rsize', '%s%%' % str(opts['rsize']), + '-autoexpand', '-warning', + '%s%%' % str(opts['warning'])] + if not opts['autoexpand']: + params.remove('-autoexpand') + + if opts['compression']: + params.append('-compressed') + else: + params.extend(['-grainsize', str(opts['grainsize'])]) + + params.extend(['-easytier', easytier]) + return params + def _check_vdisk_opts(self, opts): # Check that rsize is either -1 or between 0 and 100 if not (opts['rsize'] >= -1 and opts['rsize'] <= 100):