# License for the specific language governing permissions and limitations
# under the License.
#
-# Authors:
-# Ronen Kat <ronenkat@il.ibm.com>
-# Avishay Traeger <avishay@il.ibm.com>
-
"""
Tests for the IBM Storwize family and SVC volume driver.
"""
'CMMVC5903E': ('', 'CMMVC5903E The FlashCopy mapping was not '
'changed because the mapping or consistency '
'group is another state.'),
+ 'CMMVC5709E': ('', 'CMMVC5709E [-%(VALUE)s] is not a supported '
+ 'parameter.'),
}
self._transitions = {'begin': {'make': 'idle_or_copied'},
'idle_or_copied': {'prepare': 'preparing',
def _cmd_to_dict(self, arg_list):
no_param_args = [
'autodelete',
- 'autoexpand',
'bytes',
'compressed',
'force',
'warning',
'wwpn',
]
+ no_or_one_param_args = [
+ 'autoexpand',
+ ]
# Handle the special case of lsnode which is a two-word command
# Use the one word version of the command internally
elif arg_list[i][1:] in one_param_args:
ret[arg_list[i][1:]] = arg_list[i + 1]
skip = True
+ elif arg_list[i][1:] in no_or_one_param_args:
+ if i == (len(arg_list) - 1) or arg_list[i + 1][0] == '-':
+ ret[arg_list[i][1:]] = True
+ else:
+ ret[arg_list[i][1:]] = arg_list[i + 1]
+ skip = True
else:
raise exception.InvalidInput(
reason=_('unrecognized argument %s') % arg_list[i])
# Print mostly made-up stuff in the correct syntax
def _cmd_lssystem(self, **kwargs):
- rows = [None] * 2
+ rows = [None] * 3
rows[0] = ['id', '0123456789ABCDEF']
rows[1] = ['name', 'storwize-svc-sim']
+ rows[2] = ['code_level', '7.2.0.0 (build 87.0.1311291000)']
return self._print_info_cmd(rows=rows, **kwargs)
# Print mostly made-up stuff in the correct syntax, assume -bytes passed
rows.append(['IO_group_name', vol['IO_group_name']])
rows.append(['status', 'online'])
rows.append(['mdisk_grp_id', '0'])
- rows.append([
- 'mdisk_grp_name',
- self._flags['storwize_svc_volpool_name']])
+ if 'mdisk_grp_name' in vol:
+ mdisk_grp_name = vol['mdisk_grp_name']
+ else:
+ mdisk_grp_name = self._flags['storwize_svc_volpool_name']
+ rows.append(['mdisk_grp_name', mdisk_grp_name])
rows.append(['capacity', cap])
rows.append(['type', 'striped'])
rows.append(['formatted', 'no'])
if copy_id not in vol['copies']:
return self._errors['CMMVC6353E']
del vol['copies'][copy_id]
+
+ copy_info = vol['copies'].values()[0]
+ for key in copy_info:
+ vol[key] = copy_info[key]
+ del vol['copies']
+ return ('', '')
+
+ def _cmd_chvdisk(self, **kwargs):
+ if 'obj' not in kwargs:
+ return self._errors['CMMVC5701E']
+ vol_name = kwargs['obj'].strip('\'\'')
+ vol = self._volumes_list[vol_name]
+ kwargs.pop('obj')
+
+ params = ['name', 'warning', 'udid', 'autoexpand', 'easytier']
+ for key, value in kwargs.iteritems():
+ if key == 'easytier':
+ vol['easy_tier'] = value
+ continue
+ if key == 'warning':
+ vol['warning'] = value.rstrip('%')
+ continue
+ if key in params:
+ vol[key] = value
+ else:
+ err = self._errors['CMMVC5709E'][1] % {'VALUE': key}
+ return ('', err)
+ return ('', '')
+
+ def _cmd_movevdisk(self, **kwargs):
+ if 'obj' not in kwargs:
+ return self._errors['CMMVC5701E']
+ vol_name = kwargs['obj'].strip('\'\'')
+ vol = self._volumes_list[vol_name]
+
+ if 'iogrp' not in kwargs:
+ return self._errors['CMMVC5707E']
+
+ iogrp = kwargs['iogrp']
+ if iogrp.isdigit():
+ vol['IO_group_id'] = iogrp
+ vol['IO_group_name'] = 'io_grp%s' % iogrp
+ else:
+ vol['IO_group_id'] = iogrp[6:]
+ vol['IO_group_name'] = iogrp
return ('', '')
def _add_host_to_list(self, connector):
out, err = self._cmd_lsvdiskcopy(**kwargs)
elif command == 'rmvdiskcopy':
out, err = self._cmd_rmvdiskcopy(**kwargs)
+ elif command == 'chvdisk':
+ out, err = self._cmd_chvdisk(**kwargs)
+ elif command == 'movevdisk':
+ out, err = self._cmd_movevdisk(**kwargs)
else:
out, err = ('', 'ERROR: Unsupported command')
'volume_name': vol_name,
'id': rand_id,
'volume_id': vol_id,
- 'volume_size': 10}
+ 'volume_size': 10,
+ 'mdisk_grp_name': 'openstack'}
else:
return {'name': 'test_volume%s' % rand_id,
'size': 10,
'id': '%s' % rand_id,
- 'volume_type_id': None}
+ 'volume_type_id': None,
+ 'mdisk_grp_name': 'openstack'}
def _create_test_vol(self, opts):
ctxt = context.get_admin_context()
type_ref = volume_types.create(ctxt, 'testtype', opts)
volume = self._generate_vol_info(None, None)
- volume['volume_type_id'] = type_ref['id']
+ type_id = type_ref['id']
+ type_ref = volume_types.get_volume_type(ctxt, type_id)
+ volume['volume_type_id'] = type_id
+ volume['volume_type'] = type_ref
self.driver.create_volume(volume)
attrs = self.driver._get_vdisk_attributes(volume['name'])
self.driver.create_volume(volume)
self.assertNotEqual(cap['extent_size'], self.driver._extent_size)
self.driver.migrate_volume(ctxt, volume, host)
+ attrs = self.driver._get_vdisk_attributes(volume['name'])
+ self.assertEqual('openstack3', attrs['mdisk_grp_name'], 'migrate '
+ 'with diff extent size failed')
self.driver.delete_volume(volume)
+ def test_storwize_svc_retype_no_copy(self):
+ self.driver.do_setup(None)
+ loc = 'StorwizeSVCDriver:' + self.driver._system_id + ':openstack'
+ cap = {'location_info': loc, 'extent_size': '128'}
+ self.driver._stats = {'location_info': loc}
+ host = {'host': 'foo', 'capabilities': cap}
+ ctxt = context.get_admin_context()
+
+ key_specs_old = {'easytier': False, 'warning': 2, 'autoexpand': True}
+ key_specs_new = {'easytier': True, 'warning': 5, 'autoexpand': False}
+ old_type_ref = volume_types.create(ctxt, 'old', key_specs_old)
+ new_type_ref = volume_types.create(ctxt, 'new', key_specs_new)
+
+ diff, equel = volume_types.volume_types_diff(ctxt, old_type_ref['id'],
+ new_type_ref['id'])
+
+ volume = self._generate_vol_info(None, None)
+ old_type = volume_types.get_volume_type(ctxt, old_type_ref['id'])
+ volume['volume_type'] = old_type
+ volume['host'] = host
+ new_type = volume_types.get_volume_type(ctxt, new_type_ref['id'])
+
+ self.driver.create_volume(volume)
+ self.driver.retype(ctxt, volume, new_type, diff, host)
+ attrs = self.driver._get_vdisk_attributes(volume['name'])
+ self.assertEqual('on', attrs['easy_tier'], 'Volume retype failed')
+ self.assertEqual('5', attrs['warning'], 'Volume retype failed')
+ self.assertEqual('off', attrs['autoexpand'], 'Volume retype failed')
+ self.driver.delete_volume(volume)
+
+ def test_storwize_svc_retype_only_change_iogrp(self):
+ self.driver.do_setup(None)
+ loc = 'StorwizeSVCDriver:' + self.driver._system_id + ':openstack'
+ cap = {'location_info': loc, 'extent_size': '128'}
+ self.driver._stats = {'location_info': loc}
+ host = {'host': 'foo', 'capabilities': cap}
+ ctxt = context.get_admin_context()
+
+ key_specs_old = {'iogrp': 0}
+ key_specs_new = {'iogrp': 1}
+ old_type_ref = volume_types.create(ctxt, 'old', key_specs_old)
+ new_type_ref = volume_types.create(ctxt, 'new', key_specs_new)
+
+ diff, equal = volume_types.volume_types_diff(ctxt, old_type_ref['id'],
+ new_type_ref['id'])
+
+ volume = self._generate_vol_info(None, None)
+ old_type = volume_types.get_volume_type(ctxt, old_type_ref['id'])
+ volume['volume_type'] = old_type
+ volume['host'] = host
+ new_type = volume_types.get_volume_type(ctxt, new_type_ref['id'])
+
+ self.driver.create_volume(volume)
+ self.driver.retype(ctxt, volume, new_type, diff, host)
+ attrs = self.driver._get_vdisk_attributes(volume['name'])
+ self.assertEqual('1', attrs['IO_group_id'], 'Volume retype '
+ 'failed')
+ self.driver.delete_volume(volume)
+
+ def test_storwize_svc_retype_need_copy(self):
+ self.driver.do_setup(None)
+ loc = 'StorwizeSVCDriver:' + self.driver._system_id + ':openstack'
+ cap = {'location_info': loc, 'extent_size': '128'}
+ self.driver._stats = {'location_info': loc}
+ host = {'host': 'foo', 'capabilities': cap}
+ ctxt = context.get_admin_context()
+
+ key_specs_old = {'compression': True}
+ key_specs_new = {'compression': False}
+ old_type_ref = volume_types.create(ctxt, 'old', key_specs_old)
+ new_type_ref = volume_types.create(ctxt, 'new', key_specs_new)
+
+ diff, equal = volume_types.volume_types_diff(ctxt, old_type_ref['id'],
+ new_type_ref['id'])
+
+ volume = self._generate_vol_info(None, None)
+ old_type = volume_types.get_volume_type(ctxt, old_type_ref['id'])
+ volume['volume_type'] = old_type
+ volume['host'] = host
+ new_type = volume_types.get_volume_type(ctxt, new_type_ref['id'])
+
+ self.driver.create_volume(volume)
+ self.driver.retype(ctxt, volume, new_type, diff, host)
+ attrs = self.driver._get_vdisk_attributes(volume['name'])
+ self.assertEqual('no', attrs['compressed_copy'], 'Volume retype '
+ 'failed')
+ self.driver.delete_volume(volume)
+
+ def test_set_storage_code_level_success(self):
+ code_level = '7.2.0.0 (build 87.0.1311291000)'
+ res = self.driver._get_code_level(code_level)
+ self.assertEqual((7, 2, 0, 0), res, 'Get code level error')
+
class CLIResponseTestCase(test.TestCase):
def test_empty(self):
# License for the specific language governing permissions and limitations
# under the License.
#
-# Authors:
-# Ronen Kat <ronenkat@il.ibm.com>
-# Avishay Traeger <avishay@il.ibm.com>
-
"""
Volume driver for IBM Storwize family and SVC storage systems.
1.0 - Initial driver
1.1 - FC support, create_cloned_volume, volume type support,
get_volume_stats, minor bug fixes
-
+ 1.2.0 - Added retype
"""
"""====================================================================="""
""" SETUP """
"""====================================================================="""
- VERSION = "1.1.0"
+ VERSION = "1.2.0"
def __init__(self, *args, **kwargs):
super(StorwizeSVCDriver, self).__init__(*args, **kwargs)
self._system_name = None
self._system_id = None
self._extent_size = None
+ self._code_level = None
# Build cleanup translation tables for host names
invalid_ch_in_host = ''
LOG.info(_('WWPN on node %(node)s: %(wwpn)s')
% {'node': node['id'], 'wwpn': node['WWPN']})
+ def _get_code_level(self, level):
+ match_obj = re.search('([0-9].){3}[0-9]', level)
+ self._driver_assert(match_obj is not None, _('Get code level failed'))
+ code_level = match_obj.group().split('.')
+ return tuple(map(int, code_level))
+
def do_setup(self, ctxt):
"""Check that we have all configuration details from the storage."""
raise exception.VolumeBackendAPIException(data=msg)
self._system_name = attributes['name']
self._system_id = attributes['id']
+ self._code_level = self._get_code_level(attributes['code_level'])
# Validate that the pool exists
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)
+ attributes = self._get_pool_attrs(pool)
self._extent_size = attributes['extent_size']
# Check if compression is supported
'initiator' in connector and
attr_val == connector['initiator']):
return host
- elif (attr_name == 'WWPN' and
- 'wwpns' in connector and
+ elif (attr_name == 'WWPN' and 'wwpns' in connector and
attr_val.lower() in
map(str.lower, map(str, connector['wwpns']))):
- return host
+ return host
return None
def _get_host_from_connector(self, connector):
preferred_node = volume_attributes['preferred_node_id']
IO_group = volume_attributes['IO_group_id']
except KeyError as e:
- LOG.error(_('Did not find expected column name in '
- 'lsvdisk: %s') % str(e))
- exception_msg = (_('initialize_connection: Missing volume '
- 'attribute for volume %s') % volume_name)
- raise exception.VolumeBackendAPIException(data=exception_msg)
+ LOG.error(_('Did not find expected column name in '
+ 'lsvdisk: %s') % str(e))
+ exception_msg = (_('initialize_connection: Missing volume '
+ 'attribute for volume %s') % volume_name)
+ raise exception.VolumeBackendAPIException(data=exception_msg)
try:
# Get preferred node and other nodes in I/O group
mapping_ids = [line.split()[0] for line in lines]
return mapping_ids
- def _get_vdisk_params(self, type_id):
+ def _get_vdisk_params(self, type_id, volume_type=None):
opts = self._build_default_opts()
- if type_id:
+ if volume_type is None and type_id is not None:
ctxt = context.get_admin_context()
volume_type = volume_types.get_volume_type(ctxt, type_id)
- specs = volume_type.get('extra_specs')
+ if volume_type:
+ specs = dict(volume_type).get('extra_specs')
for k, value in specs.iteritems():
# Get the scope, if using scope format
key_split = k.split(':')
del words[0]
value = words[0]
- # Anything keys that the driver should look at should have the
+ # Any keys that the driver should look at should have the
# 'drivers' scope.
if scope and scope != "drivers":
continue
ssh_cmd, out, err)
LOG.debug(_('leave: extend_volume: volume %s') % volume['id'])
+ def _add_vdisk_copy(self, volume, dest_pool, opts):
+ """Create a vdisk copy for the given volume."""
+ 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()), '_add_vdisk_copy',
+ 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,
+ _('_add_vdisk_copy %(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)
+ return copy_id
+
+ def _get_vdisk_copy_attrs(self, volume, copy_id):
+ ssh_cmd = ['svcinfo', 'lsvdiskcopy', '-delim', '!', '-copy',
+ copy_id, volume['name']]
+ attrs = self._execute_command_and_parse_attributes(ssh_cmd)
+ if not attrs:
+ msg = (_('_get_vdisk_copy_attrs: Could not get vdisk '
+ 'copy data'))
+ LOG.error(msg)
+ raise exception.VolumeBackendAPIException(data=msg)
+ return attrs
+
+ def _wait_for_vdisk_copy_sync(self, volume, copy_id):
+ sync = False
+ while not sync:
+ attrs = self._get_vdisk_copy_attrs(volume, copy_id)
+ if attrs['sync'] == 'yes':
+ sync = True
+ else:
+ time.sleep(5)
+
+ def _remove_vdisk_copy(self, volume, copy_id):
+ ssh_cmd = ['svctask', 'rmvdiskcopy', '-copy', 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, '_remove_vdisk_copy',
+ ssh_cmd, out, err)
+
+ def _migrate_volume_vdiskcopy(self, volume, dest_pool, volume_type):
+ """Migrate a volume using addvdiskcopy and rmvdiskcopy.
+
+ This will add a vdisk copy with the given volume type in the given
+ pool, wait until it syncs, and delete the original copy.
+ """
+ this_pool = self.configuration.storwize_svc_volpool_name
+ orig_copy_id = self._find_vdisk_copy_id(this_pool, volume['name'])
+ self._driver_assert(orig_copy_id is not None,
+ _('migrate_volume started without a vdisk '
+ 'copy in the expected pool.'))
+
+ if volume_type is None:
+ opts = self._get_vdisk_params(None)
+ else:
+ opts = self._get_vdisk_params(volume_type['id'],
+ volume_type=volume_type)
+ new_copy_id = self._add_vdisk_copy(volume, dest_pool, opts)
+ self._wait_for_vdisk_copy_sync(volume, new_copy_id)
+ self._remove_vdisk_copy(volume, orig_copy_id)
+
+ def _same_host(self, host):
+ dest_location = host['capabilities'].get('location_info')
+ if self._stats['location_info'] == dest_location:
+ return True
+ return False
+
+ def _can_migrate_to_host(self, host):
+ if 'location_info' not in host['capabilities']:
+ return None
+ info = host['capabilities']['location_info']
+ try:
+ (dest_type, dest_id, dest_pool) = info.split(':')
+ except ValueError:
+ return None
+ if (dest_type != 'StorwizeSVCDriver' or dest_id != self._system_id):
+ return None
+ return dest_pool
+
+ @staticmethod
+ def _convert_vdisk_parameters(opts):
+ if 'iogrp' in opts:
+ opts['iogrp'] = str(opts['iogrp'])
+ if 'warning' in opts:
+ opts['warning'] = '%s%%' % str(opts['warning'])
+ if 'easytier' in opts:
+ opts['easytier'] = 'on' if opts['easytier'] else 'off'
+ if 'autoexpand' in opts:
+ opts['autoexpand'] = 'on' if opts['autoexpand'] else 'off'
+ return opts
+
def migrate_volume(self, ctxt, volume, host):
"""Migrate directly if source and dest are managed by same storage.
{'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):
+ dest_pool = self._can_migrate_to_host(host)
+ if dest_pool is None:
return false_ret
if 'extent_size' not in host['capabilities']:
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)
+ ctxt = context.get_admin_context()
+ if volume['volume_type_id'] is not None:
+ volume_type_id = volume['volume_type_id']
+ vol_type = volume_types.get_volume_type(ctxt, volume_type_id)
+ else:
+ vol_type = None
+ self._migrate_volume_vdiskcopy(volume, dest_pool, vol_type)
LOG.debug(_('leave: migrate_volume: id=%(id)s, host=%(host)s') %
{'id': volume['id'], 'host': host['host']})
return (True, None)
+ def retype(self, ctxt, volume, new_type, diff, host):
+ """Convert the volume to be of the new type.
+
+ Returns a boolean indicating whether the retype occurred.
+
+ :param ctxt: Context
+ :param volume: A dictionary describing the volume to migrate
+ :param new_type: A dictionary describing the volume type to convert to
+ :param diff: A dictionary with the difference between the two types
+ :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: retype: id=%(id)s, new_type=%(new_type)s,'
+ 'diff=%(diff)s, host=%(host)s') % {'id': volume['id'],
+ 'new_type': new_type,
+ 'diff': diff,
+ 'host': host})
+
+ ignore_keys = ['protocol', 'multipath']
+ no_copy_keys = ['warning', 'autoexpand', 'easytier', 'iogrp']
+ copy_keys = ['rsize', 'grainsize', 'compression']
+ all_keys = ignore_keys + no_copy_keys + copy_keys
+ old_opts = self._get_vdisk_params(volume['volume_type_id'])
+ new_opts = self._get_vdisk_params(new_type['id'],
+ volume_type=new_type)
+
+ vdisk_changes = []
+ need_copy = False
+ for key in all_keys:
+ if old_opts[key] != new_opts[key]:
+ if key in copy_keys:
+ need_copy = True
+ break
+ elif key in no_copy_keys:
+ vdisk_changes.append(key)
+
+ if not self._same_host(host):
+ need_copy = True
+
+ if need_copy:
+ dest_pool = self._can_migrate_to_host(host)
+ if dest_pool is None:
+ return False
+
+ self._migrate_volume_vdiskcopy(volume, dest_pool, new_type)
+ else:
+ new_opts = self._convert_vdisk_parameters(new_opts)
+ if 'iogrp' in vdisk_changes:
+ vdisk_changes.remove('iogrp')
+ if self._code_level < (6, 4, 0, 0):
+ LOG.debug(_('Ignore change IO group as storage code level '
+ 'is %(code_level)s, below then '
+ '6.4.0.0') % {'code_level': self._code_level})
+ else:
+ ssh_cmd = (['svctask', 'movevdisk', '-iogrp',
+ new_opts['iogrp']] + [volume['name']])
+ out, err = self._run_ssh(ssh_cmd)
+ self._assert_ssh_return(len(out.strip()) == 0,
+ 'movevdisk', ssh_cmd, out, err)
+ params = []
+ for key in vdisk_changes:
+ params.extend(['-' + key, new_opts[key]])
+ if params:
+ ssh_cmd = (['svctask', 'chvdisk'] + params + [volume['name']])
+ out, err = self._run_ssh(ssh_cmd)
+ self._assert_ssh_return(len(out.strip()) == 0,
+ 'retype', ssh_cmd, out, err)
+
+ LOG.debug(_('exit: retype: ild=%(id)s, new_type=%(new_type)s,'
+ 'diff=%(diff)s, host=%(host)s') % {'id': volume['id'],
+ 'new_type': new_type,
+ 'diff': diff,
+ 'host': host['host']})
+ return True
+
+ def report_volume_migrate_status(self):
+ pass
+
"""====================================================================="""
""" MISC/HELPERS """
"""====================================================================="""
self._stats = data
+ def _get_pool_attrs(self, pool):
+ ssh_cmd = ['svcinfo', 'lsmdiskgrp', '-bytes', '-delim', '!', pool]
+ attributes = self._execute_command_and_parse_attributes(ssh_cmd)
+ if not attributes:
+ msg = (_('_get_pool_attrs: Pool %s does not exist') % pool)
+ LOG.error(msg)
+ raise exception.InvalidInput(reason=msg)
+ return attributes
+
+ def _find_vdisk_copy_id(self, pool, name):
+ copies_info = self._get_vdisk_copy_info(name)
+ for cid, cinfo in copies_info.iteritems():
+ if cinfo['mdisk_grp_name'] == pool:
+ return cid
+ return None
+
def _port_conf_generator(self, cmd):
ssh_cmd = cmd + ['-delim', '!']
out, err = self._run_ssh(ssh_cmd)
ret[copy_data['copy_id']] = copy_data
return ret
- def _get_vdisk_create_params(self, opts):
+ @staticmethod
+ def _get_vdisk_create_params(opts):
easytier = 'on' if opts['easytier'] else 'off'
# Set space-efficient options