import random
import re
import socket
+import unittest
from cinder import context
from cinder import exception
self._fcmappings_list = {}
self._next_cmd_error = {
'lsportip': '',
- 'lsportfc': '',
'lsfabric': '',
'lsiscsiauth': '',
'lsnodecanister': '',
# Handle the special case of lsnode which is a two-word command
# Use the one word version of the command internally
- if arg_list[0] == 'svcinfo' and arg_list[1] == 'lsnode':
- ret = {'cmd': 'lsnodecanister'}
+ if arg_list[0] in ('svcinfo', 'svctask'):
+ if arg_list[1] == 'lsnode':
+ if len(arg_list) > 4: # e.g. svcinfo lsnode -delim ! <node id>
+ ret = {'cmd': 'lsnode', 'node_id': arg_list[-1]}
+ else:
+ ret = {'cmd': 'lsnodecanister'}
+ else:
+ ret = {'cmd': arg_list[1]}
arg_list.pop(0)
else:
ret = {'cmd': arg_list[0]}
def _print_info_cmd(self, rows, delim=' ', nohdr=False, **kwargs):
"""Generic function for printing information."""
if nohdr:
- del rows[0]
+ del rows[0]
for index in range(len(rows)):
rows[index] = delim.join(rows[index])
return self._print_info_cmd(rows=rows, **kwargs)
+ # Print information of every single node of SVC
+ def _cmd_lsnode(self, **kwargs):
+ node_infos = dict()
+ node_infos['1'] = r'''id!1
+name!node1
+port_id!500507680210C744
+port_status!active
+port_speed!8Gb
+port_id!500507680220C744
+port_status!active
+port_speed!8Gb
+'''
+ node_infos['2'] = r'''id!2
+name!node2
+port_id!500507680220C745
+port_status!active
+port_speed!8Gb
+port_id!500507680230C745
+port_status!inactive
+port_speed!N/A
+'''
+ node_id = kwargs.get('node_id', None)
+ stdout = node_infos.get(node_id, '')
+ return stdout, ''
+
# Print mostly made-up stuff in the correct syntax
def _cmd_lsportip(self, **kwargs):
if self._next_cmd_error['lsportip'] == 'ip_no_config':
return self._print_info_cmd(rows=rows, **kwargs)
- def _cmd_lsportfc(self, **kwargs):
- if self._next_cmd_error['lsportfc'] == 'fc_no_config':
- self._next_cmd_error['lsportfc'] = ''
- wwpn1 = ''
- wwpn2 = ''
- else:
- wwpn1 = '123456789ABCDEF0'
- wwpn2 = '123456789ABCDEF1'
-
- rows = [None] * 9
- rows[0] = ['id', 'fc_io_port_id', 'port_id', 'type', 'port_speed',
- 'node_id', 'node_name', 'WWPN', 'nportid', 'status']
- rows[1] = ['0', '1', '1', 'fc', '4Gb', '1', 'node1',
- wwpn1, '012ABC', 'active']
- rows[2] = ['1', '2', '2', 'fc', '4Gb', '1', 'node1',
- wwpn1, '012ABC', 'active']
- rows[3] = ['2', '3', '3', 'fc', 'N/A', '1', 'node1',
- wwpn1, '000000', 'inactive_unconfigured']
- rows[4] = ['3', '4', '4', 'fc', '4Gb', '1', 'node1',
- wwpn1, 'ABCDEF', 'active']
- rows[5] = ['6', '1', '1', 'fc', '4Gb', '2', 'node2',
- wwpn2, '012ABC', 'active']
- rows[6] = ['7', '2', '2', 'fc', '4Gb', '2', 'node2',
- wwpn2, '012ABC', 'active']
- rows[7] = ['8', '3', '3', 'fc', '4Gb', '2', 'node2',
- wwpn2, 'ABC123', 'active']
- rows[8] = ['9', '4', '4', 'fc', '4Gb', '2', 'node2',
- wwpn2, '012ABC', 'active']
-
- if self._next_cmd_error['lsportfc'] == 'header_mismatch':
- rows[0].pop(2)
- self._next_cmd_error['lsportfc'] = ''
- if self._next_cmd_error['lsportfc'] == 'remove_field':
- for row in rows:
- row.pop(7)
- self._next_cmd_error['lsportfc'] = ''
-
- return self._print_info_cmd(rows=rows, **kwargs)
-
def _cmd_lsfabric(self, **kwargs):
host_name = kwargs['host'] if 'host' in kwargs else None
target_wwpn = kwargs['wwpn'] if 'wwpn' in kwargs else None
out, err = self._cmd_lssystem(**kwargs)
elif command == 'lsnodecanister':
out, err = self._cmd_lsnodecanister(**kwargs)
+ elif command == 'lsnode':
+ out, err = self._cmd_lsnode(**kwargs)
elif command == 'lsportip':
out, err = self._cmd_lsportip(**kwargs)
- elif command == 'lsportfc':
- out, err = self._cmd_lsportfc(**kwargs)
elif command == 'lsfabric':
out, err = self._cmd_lsfabric(**kwargs)
elif command == 'mkvdisk':
self.assertRaises(exception.VolumeBackendAPIException,
self.driver.do_setup, None)
self.sim.error_injection('lsnodecanister', 'remove_field')
- self.assertRaises(exception.VolumeBackendAPIException,
- self.driver.do_setup, None)
-
- self.sim.error_injection('lsportip', 'ip_no_config')
- self.sim.error_injection('lsportfc', 'fc_no_config')
self.assertRaises(exception.VolumeBackendAPIException,
self.driver.do_setup, None)
self.sim.error_injection('lsportip', 'header_mismatch')
self.sim.error_injection('lsportip', 'remove_field')
self.assertRaises(exception.VolumeBackendAPIException,
self.driver.do_setup, None)
- self.sim.error_injection('lsportfc', 'header_mismatch')
- self.assertRaises(exception.VolumeBackendAPIException,
- self.driver.do_setup, None)
- self.sim.error_injection('lsportfc', 'remove_field')
- self.assertRaises(exception.VolumeBackendAPIException,
- self.driver.do_setup, None)
# Check with bad parameters
self._set_flag('san_ip', '')
'storwize-svc-sim_volpool')
self.assertAlmostEqual(stats['total_capacity_gb'], 3328.0)
self.assertAlmostEqual(stats['free_capacity_gb'], 3287.5)
+
+
+# The test case does not rely on Openstack runtime,
+# so it should inherit from unittest.TestCase.
+class CLIResponseTestCase(unittest.TestCase):
+ def test_empty(self):
+ self.assertEqual(0, len(storwize_svc.CLIResponse('')))
+ self.assertEqual(0, len(storwize_svc.CLIResponse(('', 'stderr'))))
+
+ def test_header(self):
+ raw = r'''id!name
+1!node1
+2!node2
+'''
+ resp = storwize_svc.CLIResponse(raw, with_header=True)
+ self.assertEqual(2, len(resp))
+ self.assertEqual('1', resp[0]['id'])
+ self.assertEqual('2', resp[1]['id'])
+
+ def test_select(self):
+ raw = r'''id!123
+name!Bill
+name!Bill2
+age!30
+home address!s1
+home address!s2
+
+id! 7
+name!John
+name!John2
+age!40
+home address!s3
+home address!s4
+'''
+ resp = storwize_svc.CLIResponse(raw, with_header=False)
+ self.assertEqual(list(resp.select('home address', 'name',
+ 'home address')),
+ [('s1', 'Bill', 's1'), ('s2', 'Bill2', 's2'),
+ ('s3', 'John', 's3'), ('s4', 'John2', 's4')])
+
+ def test_lsnode_all(self):
+ raw = r'''id!name!UPS_serial_number!WWNN!status
+1!node1!!500507680200C744!online
+2!node2!!500507680200C745!online
+'''
+ resp = storwize_svc.CLIResponse(raw)
+ self.assertEqual(2, len(resp))
+ self.assertEqual('1', resp[0]['id'])
+ self.assertEqual('500507680200C744', resp[0]['WWNN'])
+ self.assertEqual('2', resp[1]['id'])
+ self.assertEqual('500507680200C745', resp[1]['WWNN'])
+
+ def test_lsnode_single(self):
+ raw = r'''id!1
+port_id!500507680210C744
+port_status!active
+port_speed!8Gb
+port_id!500507680240C744
+port_status!inactive
+port_speed!8Gb
+'''
+ resp = storwize_svc.CLIResponse(raw, with_header=False)
+ self.assertEqual(1, len(resp))
+ self.assertEqual('1', resp[0]['id'])
+ self.assertEqual(list(resp.select('port_id', 'port_status')),
+ [('500507680210C744', 'active'),
+ ('500507680240C744', 'inactive')])
+
+if __name__ == '__main__':
+ unittest.main()
for char in invalid_ch_in_host)
def _get_iscsi_ip_addrs(self):
- generator = self._port_conf_generator('lsportip')
+ generator = self._port_conf_generator('svcinfo lsportip')
header = next(generator, None)
if not header:
return
node['ipv6'].append(port_ipv6)
def _get_fc_wwpns(self):
- generator = self._port_conf_generator('lsportfc')
- header = next(generator, None)
- if not header:
- return
-
- for port_data in generator:
- try:
- port_node_id = port_data['node_id']
- wwpn = port_data['WWPN']
- status = port_data['status']
- except KeyError as e:
- self._handle_keyerror('lsportfc', header)
-
- if (port_node_id in self._storage_nodes and
- 'unconfigured' not in status):
- node = self._storage_nodes[port_node_id]
- if len(wwpn) and wwpn not in node['WWPN']:
- node['WWPN'].append(wwpn)
+ for key in self._storage_nodes:
+ node = self._storage_nodes[key]
+ ssh_cmd = 'svcinfo lsnode -delim ! %s' % node['id']
+ raw = self._run_ssh(ssh_cmd)
+ resp = CLIResponse(raw, delim='!', with_header=False)
+ wwpns = set(node['WWPN'])
+ for i, s in resp.select('port_id', 'port_status'):
+ if 'unconfigured' != s:
+ wwpns.add(i)
+ node['WWPN'] = list(wwpns)
+ LOG.info(_('WWPN on node %(node)s: %(wwpn)s')
+ % {'node': node['id'], 'wwpn': node['WWPN']})
def do_setup(self, ctxt):
"""Check that we have all configuration details from the storage."""
self._context = ctxt
# Validate that the pool exists
- ssh_cmd = 'lsmdiskgrp -delim ! -nohdr'
+ 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)
% self.configuration.storwize_svc_volpool_name))
# Check if compression is supported
- ssh_cmd = 'lslicense -delim !'
- out, err = self._run_ssh(ssh_cmd)
- license_lines = out.strip().split('\n')
self._compression_enabled = False
- for license_line in license_lines:
- name, foo, value = license_line.partition('!')
- if ((name == "license_compression_enclosures" and value != '0') or
- (name == "license_compression_capacity" and value != '0')):
- self._compression_enabled = True
+ try:
+ ssh_cmd = 'svcinfo lslicense -delim !'
+ out, err = self._run_ssh(ssh_cmd)
+ license_lines = out.strip().split('\n')
+ for license_line in license_lines:
+ name, foo, value = license_line.partition('!')
+ if name in ('license_compression_enclosures',
+ 'license_compression_capacity') and value != '0':
+ self._compression_enabled = True
+ break
+ except exception.ProcessExecutionError:
+ LOG.exception(_('Failed to get license information.'))
# Get the iSCSI and FC names of the Storwize/SVC nodes
ssh_cmd = 'svcinfo lsnode -delim !'
"""Generate and store a randomly-generated CHAP secret for the host."""
chap_secret = utils.generate_password()
- ssh_cmd = ('chhost -chapsecret "%(chap_secret)s" %(host_name)s'
+ ssh_cmd = ('svctask chhost -chapsecret "%(chap_secret)s" %(host_name)s'
% {'chap_secret': chap_secret, 'host_name': host_name})
out, err = self._run_ssh(ssh_cmd)
# No output should be returned from chhost
LOG.debug(_('enter: _get_chap_secret_for_host: host name %s')
% host_name)
- ssh_cmd = 'lsiscsiauth -delim !'
+ ssh_cmd = 'svcinfo lsiscsiauth -delim !'
out, err = self._run_ssh(ssh_cmd)
if not len(out.strip()):
def _find_host_from_wwpn(self, connector):
for wwpn in connector['wwpns']:
- ssh_cmd = 'lsfabric -wwpn %s -delim !' % wwpn
+ ssh_cmd = 'svcinfo lsfabric -wwpn %s -delim !' % wwpn
out, err = self._run_ssh(ssh_cmd)
if not len(out.strip()):
def _find_host_exhaustive(self, connector, hosts):
for host in hosts:
- ssh_cmd = 'lshost -delim ! %s' % host
+ ssh_cmd = 'svcinfo lshost -delim ! %s' % host
out, err = self._run_ssh(ssh_cmd)
self._assert_ssh_return(len(out.strip()),
'_find_host_exhaustive',
LOG.debug(_('enter: _get_host_from_connector: prefix %s') % prefix)
# Get list of host in the storage
- ssh_cmd = 'lshost -delim !'
+ ssh_cmd = 'svcinfo lshost -delim !'
out, err = self._run_ssh(ssh_cmd)
if not len(out.strip()):
# When creating a host, we need one port
self._driver_assert(len(ports), _('_create_host: No connector ports'))
port1 = ports.pop(0)
- ssh_cmd = ('mkhost -force %(port1)s -name "%(host_name)s"' %
+ ssh_cmd = ('svctask mkhost -force %(port1)s -name "%(host_name)s"' %
{'port1': port1, 'host_name': host_name})
out, err = self._run_ssh(ssh_cmd)
self._assert_ssh_return('successfully created' in out,
# Add any additional ports to the host
for port in ports:
- ssh_cmd = ('addhostport -force %s %s' % (port, host_name))
+ ssh_cmd = ('svctask addhostport -force %s %s' % (port, host_name))
out, err = self._run_ssh(ssh_cmd)
LOG.debug(_('leave: _create_host: host %(host)s - %(host_name)s') %
"""Return the defined storage mappings for a host."""
return_data = {}
- ssh_cmd = 'lshostvdiskmap -delim ! %s' % host_name
+ ssh_cmd = 'svcinfo lshostvdiskmap -delim ! %s' % host_name
out, err = self._run_ssh(ssh_cmd)
mappings = out.strip().split('\n')
# Volume is not mapped to host, create a new LUN
if not mapped_flag:
- ssh_cmd = ('mkvdiskhostmap -host %(host_name)s -scsi '
+ ssh_cmd = ('svctask mkvdiskhostmap -host %(host_name)s -scsi '
'%(result_lun)s %(volume_name)s' %
{'host_name': host_name,
'result_lun': result_lun,
LOG.debug(_('enter: _delete_host: host %s ') % host_name)
- ssh_cmd = 'rmhost %s ' % host_name
+ ssh_cmd = 'svctask rmhost %s ' % host_name
out, err = self._run_ssh(ssh_cmd)
# No output should be returned from rmhost
self._assert_ssh_return(len(out.strip()) == 0,
def _get_conn_fc_wwpns(self, host_name):
wwpns = []
- cmd = 'lsfabric -host %s' % host_name
+ cmd = 'svcinfo lsfabric -host %s' % host_name
generator = self._port_conf_generator(cmd)
header = next(generator, None)
if not header:
# Check if vdisk-host mapping exists, remove if it does
mapping_data = self._get_hostvdisk_mappings(host_name)
if vol_name in mapping_data:
- ssh_cmd = 'rmvdiskhostmap -host %s %s' % (host_name, vol_name)
+ ssh_cmd = 'svctask rmvdiskhostmap -host %s %s' % \
+ (host_name, vol_name)
out, err = self._run_ssh(ssh_cmd)
# Verify CLI behaviour - no output is returned from
# rmvdiskhostmap
parsed/matched to a single vdisk.
"""
- ssh_cmd = 'lsvdisk -bytes -delim ! %s ' % vdisk_name
+ ssh_cmd = 'svcinfo lsvdisk -bytes -delim ! %s ' % vdisk_name
return self._execute_command_and_parse_attributes(ssh_cmd)
def _get_vdisk_fc_mappings(self, vdisk_name):
"""Return FlashCopy mappings that this vdisk is associated with."""
- ssh_cmd = 'lsvdiskfcmappings -nohdr %s' % vdisk_name
+ ssh_cmd = 'svcinfo lsvdiskfcmappings -nohdr %s' % vdisk_name
out, err = self._run_ssh(ssh_cmd)
mapping_ids = []
ssh_cmd_se_opt = ssh_cmd_se_opt + (
' -grainsize %d' % opts['grainsize'])
- ssh_cmd = ('mkvdisk -name %(name)s -mdiskgrp %(mdiskgrp)s '
+ ssh_cmd = ('svctask mkvdisk -name %(name)s -mdiskgrp %(mdiskgrp)s '
'-iogrp 0 -size %(size)s -unit '
'%(unit)s %(easytier)s %(ssh_cmd_se_opt)s'
% {'name': name,
def _make_fc_map(self, source, target, full_copy):
copyflag = '' if full_copy else '-copyrate 0'
- fc_map_cli_cmd = ('mkfcmap -source %(src)s -target %(tgt)s '
+ fc_map_cli_cmd = ('svctask mkfcmap -source %(src)s -target %(tgt)s '
'-autodelete %(copyflag)s' %
{'src': source,
'tgt': target,
def _call_prepare_fc_map(self, fc_map_id, source, target):
try:
- out, err = self._run_ssh('prestartfcmap %s' % fc_map_id)
+ out, err = self._run_ssh('svctask prestartfcmap %s' % fc_map_id)
except exception.ProcessExecutionError as e:
with excutils.save_and_reraise_exception():
LOG.error(_('_prepare_fc_map: Failed to prepare FlashCopy '
def _start_fc_map(self, fc_map_id, source, target):
try:
- out, err = self._run_ssh('startfcmap %s' % fc_map_id)
+ out, err = self._run_ssh('svctask startfcmap %s' % fc_map_id)
except exception.ProcessExecutionError as e:
with excutils.save_and_reraise_exception():
LOG.error(_('_start_fc_map: Failed to start FlashCopy '
LOG.debug(_('enter: _get_flashcopy_mapping_attributes: mapping %s')
% fc_map_id)
- fc_ls_map_cmd = ('lsfcmap -filtervalue id=%s -delim !' % fc_map_id)
+ fc_ls_map_cmd = 'svcinfo lsfcmap -filtervalue id=%s -delim !' % \
+ fc_map_id
out, err = self._run_ssh(fc_ls_map_cmd)
if not len(out.strip()):
return None
if copy_rate == '0':
# Case #2: A vdisk that has snapshots
if source == name:
- ssh_cmd = ('chfcmap -copyrate 50 -autodelete '
- 'on %s' % map_id)
+ ssh_cmd = ('svctask chfcmap -copyrate 50 '
+ '-autodelete on %s' % map_id)
out, err = self._run_ssh(ssh_cmd)
wait_for_copy = True
# Case #3: A snapshot
{'name': name, 'src': source, 'tgt': target})
self._driver_assert(target == name, msg)
if status in ['copying', 'prepared']:
- self._run_ssh('stopfcmap %s' % map_id)
+ self._run_ssh('svctask stopfcmap %s' % map_id)
elif status == 'stopping':
wait_for_copy = True
else:
- self._run_ssh('rmfcmap -force %s' % map_id)
+ self._run_ssh('svctask rmfcmap -force %s' % map_id)
# Case 4: Copy in progress - wait and will autodelete
else:
if status == 'prepared':
- self._run_ssh('stopfcmap %s' % map_id)
- self._run_ssh('rmfcmap -force %s' % map_id)
+ self._run_ssh('svctask stopfcmap %s' % map_id)
+ self._run_ssh('svctask rmfcmap -force %s' % map_id)
elif status == 'idle_or_copied':
# Prepare failed
- self._run_ssh('rmfcmap -force %s' % map_id)
+ self._run_ssh('svctask rmfcmap -force %s' % map_id)
else:
wait_for_copy = True
if wait_for_copy:
mapping_ids = self._get_vdisk_fc_mappings(name)
forceflag = '-force' if force else ''
- ssh_cmd = 'rmvdisk %(frc)s %(name)s' % {'frc': forceflag, 'name': name}
+ cmd_params = {'frc': forceflag, 'name': name}
+ ssh_cmd = 'svctask rmvdisk %(frc)s %(name)s' % cmd_params
out, err = self._run_ssh(ssh_cmd)
# No output should be returned from rmvdisk
self._assert_ssh_return(len(out.strip()) == 0,
pool = self.configuration.storwize_svc_volpool_name
#Get storage system name
- ssh_cmd = 'lssystem -delim !'
+ 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_status: '
backend_name = '%s_%s' % (attributes['name'], pool)
data['volume_backend_name'] = backend_name
- ssh_cmd = 'lsmdiskgrp -bytes -delim ! %s' % pool
+ ssh_cmd = 'svcinfo lsmdiskgrp -bytes -delim ! %s' % pool
attributes = self._execute_command_and_parse_attributes(ssh_cmd)
if not attributes:
LOG.error(_('Could not get pool data from the storage'))
LOG.error(msg)
raise exception.VolumeBackendAPIException(
data=msg)
+
+
+class CLIResponse(object):
+ '''Parse SVC CLI output and generate iterable'''
+
+ def __init__(self, raw, delim='!', with_header=True):
+ super(CLIResponse, self).__init__()
+ self.raw = raw
+ self.delim = delim
+ self.with_header = with_header
+ self.result = self._parse()
+
+ def select(self, *keys):
+ for a in self.result:
+ vs = []
+ for k in keys:
+ v = a.get(k, None)
+ if isinstance(v, basestring):
+ v = [v]
+ if isinstance(v, list):
+ vs.append(v)
+ for item in zip(*vs):
+ yield item
+
+ def __getitem__(self, key):
+ return self.result[key]
+
+ def __iter__(self):
+ for a in self.result:
+ yield a
+
+ def __len__(self):
+ return len(self.result)
+
+ def _parse(self):
+ def get_reader(content, delim):
+ for line in content.lstrip().splitlines():
+ line = line.strip()
+ if line:
+ yield line.split(delim)
+ else:
+ yield []
+
+ if isinstance(self.raw, basestring):
+ stdout, stderr = self.raw, ''
+ else:
+ stdout, stderr = self.raw
+ reader = get_reader(stdout, self.delim)
+ result = []
+
+ if self.with_header:
+ hds = tuple()
+ for row in reader:
+ hds = row
+ break
+ for row in reader:
+ cur = dict()
+ for k, v in zip(hds, row):
+ CLIResponse.append_dict(cur, k, v)
+ result.append(cur)
+ else:
+ cur = dict()
+ for row in reader:
+ if row:
+ CLIResponse.append_dict(cur, row[0], ' '.join(row[1:]))
+ elif cur: # start new section
+ result.append(cur)
+ cur = dict()
+ if cur:
+ result.append(cur)
+ return result
+
+ @staticmethod
+ def append_dict(dict_, key, value):
+ key, value = key.strip(), value.strip()
+ obj = dict_.get(key, None)
+ if obj is None:
+ dict_[key] = value
+ elif isinstance(obj, list):
+ obj.append(value)
+ dict_[key] = obj
+ else:
+ dict_[key] = [obj, value]
+ return dict_