1889 lines
77 KiB
Python
1889 lines
77 KiB
Python
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
|
|
|
# Copyright 2012, 2013 IBM Corp
|
|
# Copyright 2012 OpenStack LLC.
|
|
# All Rights Reserved.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
# not use this file except in compliance with the License. You may obtain
|
|
# a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# 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.
|
|
"""
|
|
|
|
import random
|
|
import re
|
|
import socket
|
|
|
|
from cinder import context
|
|
from cinder import exception
|
|
from cinder.openstack.common import excutils
|
|
from cinder.openstack.common import log as logging
|
|
from cinder import test
|
|
from cinder import utils
|
|
from cinder.volume import configuration as conf
|
|
from cinder.volume.drivers import storwize_svc
|
|
from cinder.volume import volume_types
|
|
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
|
|
class StorwizeSVCFakeDB:
|
|
def __init__(self):
|
|
self.volume = None
|
|
|
|
def volume_get(self, context, vol_id):
|
|
return self.volume
|
|
|
|
def volume_set(self, vol):
|
|
self.volume = vol
|
|
|
|
|
|
class StorwizeSVCManagementSimulator:
|
|
def __init__(self, pool_name):
|
|
self._flags = {'storwize_svc_volpool_name': pool_name}
|
|
self._volumes_list = {}
|
|
self._hosts_list = {}
|
|
self._mappings_list = {}
|
|
self._fcmappings_list = {}
|
|
self._next_cmd_error = {
|
|
'lsportip': '',
|
|
'lsportfc': '',
|
|
'lsfabric': '',
|
|
'lsiscsiauth': '',
|
|
'lsnodecanister': '',
|
|
'mkvdisk': '',
|
|
'lsvdisk': '',
|
|
'lsfcmap': '',
|
|
'prestartfcmap': '',
|
|
'startfcmap': '',
|
|
'rmfcmap': '',
|
|
'lslicense': '',
|
|
}
|
|
self._errors = {
|
|
'CMMVC5701E': ('', 'CMMVC5701E No object ID was specified.'),
|
|
'CMMVC6035E': ('', 'CMMVC6035E The action failed as the '
|
|
'object already exists.'),
|
|
'CMMVC5753E': ('', 'CMMVC5753E The specified object does not '
|
|
'exist or is not a suitable candidate.'),
|
|
'CMMVC5707E': ('', 'CMMVC5707E Required parameters are missing.'),
|
|
'CMMVC6581E': ('', 'CMMVC6581E The command has failed because '
|
|
'the maximum number of allowed iSCSI '
|
|
'qualified names (IQNs) has been reached, '
|
|
'or the IQN is already assigned or is not '
|
|
'valid.'),
|
|
'CMMVC5754E': ('', 'CMMVC5754E The specified object does not '
|
|
'exist, or the name supplied does not meet '
|
|
'the naming rules.'),
|
|
'CMMVC6071E': ('', 'CMMVC6071E The VDisk-to-host mapping was '
|
|
'not created because the VDisk is already '
|
|
'mapped to a host.'),
|
|
'CMMVC5879E': ('', 'CMMVC5879E The VDisk-to-host mapping was '
|
|
'not created because a VDisk is already '
|
|
'mapped to this host with this SCSI LUN.'),
|
|
'CMMVC5840E': ('', 'CMMVC5840E The virtual disk (VDisk) was '
|
|
'not deleted because it is mapped to a '
|
|
'host or because it is part of a FlashCopy '
|
|
'or Remote Copy mapping, or is involved in '
|
|
'an image mode migrate.'),
|
|
'CMMVC6527E': ('', 'CMMVC6527E The name that you have entered '
|
|
'is not valid. The name can contain letters, '
|
|
'numbers, spaces, periods, dashes, and '
|
|
'underscores. The name must begin with a '
|
|
'letter or an underscore. The name must not '
|
|
'begin or end with a space.'),
|
|
'CMMVC5871E': ('', 'CMMVC5871E The action failed because one or '
|
|
'more of the configured port names is in a '
|
|
'mapping.'),
|
|
'CMMVC5924E': ('', 'CMMVC5924E The FlashCopy mapping was not '
|
|
'created because the source and target '
|
|
'virtual disks (VDisks) are different sizes.'),
|
|
'CMMVC6303E': ('', 'CMMVC6303E The create failed because the '
|
|
'source and target VDisks are the same.'),
|
|
'CMMVC7050E': ('', 'CMMVC7050E The command failed because at '
|
|
'least one node in the I/O group does not '
|
|
'support compressed VDisks.'),
|
|
# Catch-all for invalid state transitions:
|
|
'CMMVC5903E': ('', 'CMMVC5903E The FlashCopy mapping was not '
|
|
'changed because the mapping or consistency '
|
|
'group is another state.'),
|
|
}
|
|
self._transitions = {'begin': {'make': 'idle_or_copied'},
|
|
'idle_or_copied': {'prepare': 'preparing',
|
|
'delete': 'end',
|
|
'delete_force': 'end'},
|
|
'preparing': {'flush_failed': 'stopped',
|
|
'wait': 'prepared'},
|
|
'end': None,
|
|
'stopped': {'prepare': 'preparing',
|
|
'delete_force': 'end'},
|
|
'prepared': {'stop': 'stopped',
|
|
'start': 'copying'},
|
|
'copying': {'wait': 'idle_or_copied',
|
|
'stop': 'stopping'},
|
|
# Assume the worst case where stopping->stopped
|
|
# rather than stopping idle_or_copied
|
|
'stopping': {'wait': 'stopped'},
|
|
}
|
|
|
|
def _state_transition(self, function, fcmap):
|
|
if (function == 'wait' and
|
|
'wait' not in self._transitions[fcmap['status']]):
|
|
return ('', '')
|
|
|
|
if fcmap['status'] == 'copying' and function == 'wait':
|
|
if fcmap['copyrate'] != '0':
|
|
if fcmap['progress'] == '0':
|
|
fcmap['progress'] = '50'
|
|
else:
|
|
fcmap['progress'] = '100'
|
|
fcmap['status'] = 'idle_or_copied'
|
|
return ('', '')
|
|
else:
|
|
try:
|
|
curr_state = fcmap['status']
|
|
fcmap['status'] = self._transitions[curr_state][function]
|
|
return ('', '')
|
|
except Exception:
|
|
return self._errors['CMMVC5903E']
|
|
|
|
# Find an unused ID
|
|
def _find_unused_id(self, d):
|
|
ids = []
|
|
for k, v in d.iteritems():
|
|
ids.append(int(v['id']))
|
|
ids.sort()
|
|
for index, n in enumerate(ids):
|
|
if n > index:
|
|
return str(index)
|
|
return str(len(ids))
|
|
|
|
# Check if name is valid
|
|
def _is_invalid_name(self, name):
|
|
if re.match("^[a-zA-Z_][\w ._-]*$", name):
|
|
return False
|
|
return True
|
|
|
|
# Convert argument string to dictionary
|
|
def _cmd_to_dict(self, cmd):
|
|
arg_list = cmd.split()
|
|
no_param_args = [
|
|
'autodelete',
|
|
'autoexpand',
|
|
'bytes',
|
|
'compressed',
|
|
'force',
|
|
'nohdr',
|
|
]
|
|
one_param_args = [
|
|
'chapsecret',
|
|
'cleanrate',
|
|
'copyrate',
|
|
'delim',
|
|
'filtervalue',
|
|
'grainsize',
|
|
'hbawwpn',
|
|
'host',
|
|
'iogrp',
|
|
'iscsiname',
|
|
'mdiskgrp',
|
|
'name',
|
|
'rsize',
|
|
'scsi',
|
|
'size',
|
|
'source',
|
|
'target',
|
|
'unit',
|
|
'easytier',
|
|
'warning',
|
|
'wwpn',
|
|
]
|
|
|
|
# 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'}
|
|
arg_list.pop(0)
|
|
else:
|
|
ret = {'cmd': arg_list[0]}
|
|
|
|
skip = False
|
|
for i in range(1, len(arg_list)):
|
|
if skip:
|
|
skip = False
|
|
continue
|
|
if arg_list[i][0] == '-':
|
|
if arg_list[i][1:] in no_param_args:
|
|
ret[arg_list[i][1:]] = True
|
|
elif arg_list[i][1:] in one_param_args:
|
|
ret[arg_list[i][1:]] = arg_list[i + 1]
|
|
skip = True
|
|
else:
|
|
raise exception.InvalidInput(
|
|
reason=_('unrecognized argument %s') % arg_list[i])
|
|
else:
|
|
ret['obj'] = arg_list[i]
|
|
return ret
|
|
|
|
def _print_info_cmd(self, rows, delim=' ', nohdr=False, **kwargs):
|
|
"""Generic function for printing information."""
|
|
if nohdr:
|
|
del rows[0]
|
|
|
|
for index in range(len(rows)):
|
|
rows[index] = delim.join(rows[index])
|
|
return ('%s' % '\n'.join(rows), '')
|
|
|
|
def _print_info_obj_cmd(self, header, row, delim=' ', nohdr=False):
|
|
"""Generic function for printing information for a specific object."""
|
|
objrows = []
|
|
for idx, val in enumerate(header):
|
|
objrows.append([val, row[idx]])
|
|
|
|
if nohdr:
|
|
for index in range(len(objrows)):
|
|
objrows[index] = ' '.join(objrows[index][1:])
|
|
for index in range(len(objrows)):
|
|
objrows[index] = delim.join(objrows[index])
|
|
return ('%s' % '\n'.join(objrows), '')
|
|
|
|
def _convert_bytes_units(self, bytestr):
|
|
num = int(bytestr)
|
|
unit_array = ['B', 'KB', 'MB', 'GB', 'TB', 'PB']
|
|
unit_index = 0
|
|
|
|
while num > 1024:
|
|
num = num / 1024
|
|
unit_index += 1
|
|
|
|
return '%d%s' % (num, unit_array[unit_index])
|
|
|
|
def _convert_units_bytes(self, num, unit):
|
|
unit_array = ['B', 'KB', 'MB', 'GB', 'TB', 'PB']
|
|
unit_index = 0
|
|
|
|
while unit.lower() != unit_array[unit_index].lower():
|
|
num = num * 1024
|
|
unit_index += 1
|
|
|
|
return str(num)
|
|
|
|
def _cmd_lslicense(self, **kwargs):
|
|
rows = [None] * 3
|
|
rows[0] = ['used_compression_capacity', '0.08']
|
|
rows[1] = ['license_compression_capacity', '0']
|
|
if self._next_cmd_error['lslicense'] == 'no_compression':
|
|
self._next_cmd_error['lslicense'] = ''
|
|
rows[2] = ['license_compression_enclosures', '0']
|
|
else:
|
|
rows[2] = ['license_compression_enclosures', '1']
|
|
return self._print_info_cmd(rows=rows, **kwargs)
|
|
|
|
# Print mostly made-up stuff in the correct syntax
|
|
def _cmd_lssystem(self, **kwargs):
|
|
rows = [None] * 2
|
|
rows[0] = ['id', '0123456789ABCDEF']
|
|
rows[1] = ['name', 'storwize-svc-sim']
|
|
return self._print_info_cmd(rows=rows, **kwargs)
|
|
|
|
# Print mostly made-up stuff in the correct syntax, assume -bytes passed
|
|
def _cmd_lsmdiskgrp(self, **kwargs):
|
|
rows = [None] * 3
|
|
rows[0] = ['id', 'name', 'status', 'mdisk_count',
|
|
'vdisk_count', 'capacity', 'extent_size',
|
|
'free_capacity', 'virtual_capacity', 'used_capacity',
|
|
'real_capacity', 'overallocation', 'warning',
|
|
'easy_tier', 'easy_tier_status']
|
|
rows[1] = ['1', self._flags['storwize_svc_volpool_name'], 'online',
|
|
'1', str(len(self._volumes_list)), '3573412790272',
|
|
'256', '3529926246400', '1693247906775', '277841182',
|
|
'38203734097', '47', '80', 'auto', 'inactive']
|
|
rows[2] = ['2', 'volpool2', 'online',
|
|
'1', '0', '3573412790272', '256',
|
|
'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':
|
|
row = rows[2]
|
|
else:
|
|
return self._errors['CMMVC5754E']
|
|
|
|
objrows = []
|
|
for idx, val in enumerate(rows[0]):
|
|
objrows.append([val, row[idx]])
|
|
|
|
if 'nohdr' in kwargs:
|
|
for index in range(len(objrows)):
|
|
objrows[index] = ' '.join(objrows[index][1:])
|
|
|
|
if 'delim' in kwargs:
|
|
for index in range(len(objrows)):
|
|
objrows[index] = kwargs['delim'].join(objrows[index])
|
|
|
|
return ('%s' % '\n'.join(objrows), '')
|
|
|
|
# Print mostly made-up stuff in the correct syntax
|
|
def _cmd_lsnodecanister(self, **kwargs):
|
|
rows = [None] * 3
|
|
rows[0] = ['id', 'name', 'UPS_serial_number', 'WWNN', 'status',
|
|
'IO_group_id', 'IO_group_name', 'config_node',
|
|
'UPS_unique_id', 'hardware', 'iscsi_name', 'iscsi_alias',
|
|
'panel_name', 'enclosure_id', 'canister_id',
|
|
'enclosure_serial_number']
|
|
rows[1] = ['1', 'node1', '', '123456789ABCDEF0', 'online', '0',
|
|
'io_grp0',
|
|
'yes', '123456789ABCDEF0', '100',
|
|
'iqn.1982-01.com.ibm:1234.sim.node1', '', '01-1', '1', '1',
|
|
'0123ABC']
|
|
rows[2] = ['2', 'node2', '', '123456789ABCDEF1', 'online', '0',
|
|
'io_grp0',
|
|
'no', '123456789ABCDEF1', '100',
|
|
'iqn.1982-01.com.ibm:1234.sim.node2', '', '01-2', '1', '2',
|
|
'0123ABC']
|
|
|
|
if self._next_cmd_error['lsnodecanister'] == 'header_mismatch':
|
|
rows[0].pop(2)
|
|
self._next_cmd_error['lsnodecanister'] = ''
|
|
if self._next_cmd_error['lsnodecanister'] == 'remove_field':
|
|
for row in rows:
|
|
row.pop(0)
|
|
self._next_cmd_error['lsnodecanister'] = ''
|
|
|
|
return self._print_info_cmd(rows=rows, **kwargs)
|
|
|
|
# Print mostly made-up stuff in the correct syntax
|
|
def _cmd_lsportip(self, **kwargs):
|
|
if self._next_cmd_error['lsportip'] == 'ip_no_config':
|
|
self._next_cmd_error['lsportip'] = ''
|
|
ip_addr1 = ''
|
|
ip_addr2 = ''
|
|
gw = ''
|
|
else:
|
|
ip_addr1 = '1.234.56.78'
|
|
ip_addr2 = '1.234.56.79'
|
|
gw = '1.234.56.1'
|
|
|
|
rows = [None] * 17
|
|
rows[0] = ['id', 'node_id', 'node_name', 'IP_address', 'mask',
|
|
'gateway', 'IP_address_6', 'prefix_6', 'gateway_6', 'MAC',
|
|
'duplex', 'state', 'speed', 'failover']
|
|
rows[1] = ['1', '1', 'node1', ip_addr1, '255.255.255.0',
|
|
gw, '', '', '', '01:23:45:67:89:00', 'Full',
|
|
'online', '1Gb/s', 'no']
|
|
rows[2] = ['1', '1', 'node1', '', '', '', '', '', '',
|
|
'01:23:45:67:89:00', 'Full', 'online', '1Gb/s', 'yes']
|
|
rows[3] = ['2', '1', 'node1', '', '', '', '', '', '',
|
|
'01:23:45:67:89:01', 'Full', 'unconfigured', '1Gb/s', 'no']
|
|
rows[4] = ['2', '1', 'node1', '', '', '', '', '', '',
|
|
'01:23:45:67:89:01', 'Full', 'unconfigured', '1Gb/s', 'yes']
|
|
rows[5] = ['3', '1', 'node1', '', '', '', '', '', '', '', '',
|
|
'unconfigured', '', 'no']
|
|
rows[6] = ['3', '1', 'node1', '', '', '', '', '', '', '', '',
|
|
'unconfigured', '', 'yes']
|
|
rows[7] = ['4', '1', 'node1', '', '', '', '', '', '', '', '',
|
|
'unconfigured', '', 'no']
|
|
rows[8] = ['4', '1', 'node1', '', '', '', '', '', '', '', '',
|
|
'unconfigured', '', 'yes']
|
|
rows[9] = ['1', '2', 'node2', ip_addr2, '255.255.255.0',
|
|
gw, '', '', '', '01:23:45:67:89:02', 'Full',
|
|
'online', '1Gb/s', 'no']
|
|
rows[10] = ['1', '2', 'node2', '', '', '', '', '', '',
|
|
'01:23:45:67:89:02', 'Full', 'online', '1Gb/s', 'yes']
|
|
rows[11] = ['2', '2', 'node2', '', '', '', '', '', '',
|
|
'01:23:45:67:89:03', 'Full', 'unconfigured', '1Gb/s', 'no']
|
|
rows[12] = ['2', '2', 'node2', '', '', '', '', '', '',
|
|
'01:23:45:67:89:03', 'Full', 'unconfigured', '1Gb/s',
|
|
'yes']
|
|
rows[13] = ['3', '2', 'node2', '', '', '', '', '', '', '', '',
|
|
'unconfigured', '', 'no']
|
|
rows[14] = ['3', '2', 'node2', '', '', '', '', '', '', '', '',
|
|
'unconfigured', '', 'yes']
|
|
rows[15] = ['4', '2', 'node2', '', '', '', '', '', '', '', '',
|
|
'unconfigured', '', 'no']
|
|
rows[16] = ['4', '2', 'node2', '', '', '', '', '', '', '', '',
|
|
'unconfigured', '', 'yes']
|
|
|
|
if self._next_cmd_error['lsportip'] == 'header_mismatch':
|
|
rows[0].pop(2)
|
|
self._next_cmd_error['lsportip'] = ''
|
|
if self._next_cmd_error['lsportip'] == 'remove_field':
|
|
for row in rows:
|
|
row.pop(1)
|
|
self._next_cmd_error['lsportip'] = ''
|
|
|
|
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
|
|
host_infos = []
|
|
|
|
for hk, hv in self._hosts_list.iteritems():
|
|
if not host_name or hv['host_name'] == host_name:
|
|
for mk, mv in self._mappings_list.iteritems():
|
|
if mv['host'] == hv['host_name']:
|
|
if not target_wwpn or target_wwpn in hv['wwpns']:
|
|
host_infos.append(hv)
|
|
break
|
|
|
|
if not len(host_infos):
|
|
return ('', '')
|
|
|
|
rows = []
|
|
rows.append(['remote_wwpn', 'remote_nportid', 'id', 'node_name',
|
|
'local_wwpn', 'local_port', 'local_nportid', 'state',
|
|
'name', 'cluster_name', 'type'])
|
|
for host_info in host_infos:
|
|
for wwpn in host_info['wwpns']:
|
|
rows.append([wwpn, '123456', host_info['id'], 'nodeN',
|
|
'AABBCCDDEEFF0011', '1', '0123ABC', 'active',
|
|
host_info['host_name'], '', 'host'])
|
|
|
|
if self._next_cmd_error['lsfabric'] == 'header_mismatch':
|
|
rows[0].pop(0)
|
|
self._next_cmd_error['lsfabric'] = ''
|
|
if self._next_cmd_error['lsfabric'] == 'remove_field':
|
|
for row in rows:
|
|
row.pop(0)
|
|
self._next_cmd_error['lsfabric'] = ''
|
|
return self._print_info_cmd(rows=rows, **kwargs)
|
|
|
|
# Create a vdisk
|
|
def _cmd_mkvdisk(self, **kwargs):
|
|
# We only save the id/uid, name, and size - all else will be made up
|
|
volume_info = {}
|
|
volume_info['id'] = self._find_unused_id(self._volumes_list)
|
|
volume_info['uid'] = ('ABCDEF' * 3) + ('0' * 14) + volume_info['id']
|
|
|
|
if 'name' in kwargs:
|
|
volume_info['name'] = kwargs['name'].strip('\'\'')
|
|
else:
|
|
volume_info['name'] = 'vdisk' + volume_info['id']
|
|
|
|
# Assume size and unit are given, store it in bytes
|
|
capacity = int(kwargs['size'])
|
|
unit = kwargs['unit']
|
|
volume_info['capacity'] = self._convert_units_bytes(capacity, unit)
|
|
|
|
if 'easytier' in kwargs:
|
|
if kwargs['easytier'] == 'on':
|
|
volume_info['easy_tier'] = 'on'
|
|
else:
|
|
volume_info['easy_tier'] = 'off'
|
|
|
|
if 'rsize' in kwargs:
|
|
# Fake numbers
|
|
volume_info['used_capacity'] = '786432'
|
|
volume_info['real_capacity'] = '21474816'
|
|
volume_info['free_capacity'] = '38219264'
|
|
if 'warning' in kwargs:
|
|
volume_info['warning'] = kwargs['warning'].rstrip('%')
|
|
else:
|
|
volume_info['warning'] = '80'
|
|
if 'autoexpand' in kwargs:
|
|
volume_info['autoexpand'] = 'on'
|
|
else:
|
|
volume_info['autoexpand'] = 'off'
|
|
if 'grainsize' in kwargs:
|
|
volume_info['grainsize'] = kwargs['grainsize']
|
|
else:
|
|
volume_info['grainsize'] = '32'
|
|
if 'compressed' in kwargs:
|
|
volume_info['compressed_copy'] = 'yes'
|
|
else:
|
|
volume_info['compressed_copy'] = 'no'
|
|
else:
|
|
volume_info['used_capacity'] = volume_info['capacity']
|
|
volume_info['real_capacity'] = volume_info['capacity']
|
|
volume_info['free_capacity'] = '0'
|
|
volume_info['warning'] = ''
|
|
volume_info['autoexpand'] = ''
|
|
volume_info['grainsize'] = ''
|
|
volume_info['compressed_copy'] = 'no'
|
|
|
|
if volume_info['name'] in self._volumes_list:
|
|
return self._errors['CMMVC6035E']
|
|
else:
|
|
self._volumes_list[volume_info['name']] = volume_info
|
|
return ('Virtual Disk, id [%s], successfully created' %
|
|
(volume_info['id']), '')
|
|
|
|
# Delete a vdisk
|
|
def _cmd_rmvdisk(self, **kwargs):
|
|
force = True if 'force' in kwargs else False
|
|
|
|
if 'obj' not in kwargs:
|
|
return self._errors['CMMVC5701E']
|
|
vol_name = kwargs['obj'].strip('\'\'')
|
|
|
|
if not vol_name in self._volumes_list:
|
|
return self._errors['CMMVC5753E']
|
|
|
|
if not force:
|
|
for k, mapping in self._mappings_list.iteritems():
|
|
if mapping['vol'] == vol_name:
|
|
return self._errors['CMMVC5840E']
|
|
for k, fcmap in self._fcmappings_list.iteritems():
|
|
if ((fcmap['source'] == vol_name) or
|
|
(fcmap['target'] == vol_name)):
|
|
return self._errors['CMMVC5840E']
|
|
|
|
del self._volumes_list[vol_name]
|
|
return ('', '')
|
|
|
|
def _get_fcmap_info(self, vol_name):
|
|
ret_vals = {
|
|
'fc_id': '',
|
|
'fc_name': '',
|
|
'fc_map_count': '0',
|
|
}
|
|
for k, fcmap in self._fcmappings_list.iteritems():
|
|
if ((fcmap['source'] == vol_name) or
|
|
(fcmap['target'] == vol_name)):
|
|
ret_vals['fc_id'] = fcmap['id']
|
|
ret_vals['fc_name'] = fcmap['name']
|
|
ret_vals['fc_map_count'] = '1'
|
|
return ret_vals
|
|
|
|
# List information about vdisks
|
|
def _cmd_lsvdisk(self, **kwargs):
|
|
rows = []
|
|
rows.append(['id', 'name', 'IO_group_id', 'IO_group_name',
|
|
'status', 'mdisk_grp_id', 'mdisk_grp_name',
|
|
'capacity', 'type', 'FC_id', 'FC_name', 'RC_id',
|
|
'RC_name', 'vdisk_UID', 'fc_map_count', 'copy_count',
|
|
'fast_write_state', 'se_copy_count', 'RC_change'])
|
|
|
|
for k, vol in self._volumes_list.iteritems():
|
|
if (('filtervalue' not in kwargs) or
|
|
(kwargs['filtervalue'] == 'name=' + vol['name'])):
|
|
fcmap_info = self._get_fcmap_info(vol['name'])
|
|
|
|
if 'bytes' in kwargs:
|
|
cap = self._convert_bytes_units(vol['capacity'])
|
|
else:
|
|
cap = vol['capacity']
|
|
rows.append([str(vol['id']), vol['name'], '0', 'io_grp0',
|
|
'online', '0',
|
|
self._flags['storwize_svc_volpool_name'],
|
|
cap, 'striped',
|
|
fcmap_info['fc_id'], fcmap_info['fc_name'],
|
|
'', '', vol['uid'],
|
|
fcmap_info['fc_map_count'], '1', 'empty',
|
|
'1', 'no'])
|
|
|
|
if 'obj' not in kwargs:
|
|
return self._print_info_cmd(rows=rows, **kwargs)
|
|
else:
|
|
if kwargs['obj'] not in self._volumes_list:
|
|
return self._errors['CMMVC5754E']
|
|
vol = self._volumes_list[kwargs['obj']]
|
|
fcmap_info = self._get_fcmap_info(vol['name'])
|
|
cap = vol['capacity']
|
|
cap_u = vol['used_capacity']
|
|
cap_r = vol['real_capacity']
|
|
cap_f = vol['free_capacity']
|
|
if 'bytes' not in kwargs:
|
|
for item in [cap, cap_u, cap_r, cap_f]:
|
|
item = self._convert_bytes_units(item)
|
|
rows = []
|
|
|
|
rows.append(['id', str(vol['id'])])
|
|
rows.append(['name', vol['name']])
|
|
rows.append(['IO_group_id', '0'])
|
|
rows.append(['IO_group_name', 'io_grp0'])
|
|
rows.append(['status', 'online'])
|
|
rows.append(['mdisk_grp_id', '0'])
|
|
rows.append([
|
|
'mdisk_grp_name',
|
|
self._flags['storwize_svc_volpool_name']])
|
|
rows.append(['capacity', cap])
|
|
rows.append(['type', 'striped'])
|
|
rows.append(['formatted', 'no'])
|
|
rows.append(['mdisk_id', ''])
|
|
rows.append(['mdisk_name', ''])
|
|
rows.append(['FC_id', fcmap_info['fc_id']])
|
|
rows.append(['FC_name', fcmap_info['fc_name']])
|
|
rows.append(['RC_id', ''])
|
|
rows.append(['RC_name', ''])
|
|
rows.append(['vdisk_UID', vol['uid']])
|
|
rows.append(['throttling', '0'])
|
|
|
|
if self._next_cmd_error['lsvdisk'] == 'blank_pref_node':
|
|
rows.append(['preferred_node_id', ''])
|
|
self._next_cmd_error['lsvdisk'] = ''
|
|
elif self._next_cmd_error['lsvdisk'] == 'no_pref_node':
|
|
self._next_cmd_error['lsvdisk'] = ''
|
|
else:
|
|
rows.append(['preferred_node_id', '1'])
|
|
rows.append(['fast_write_state', 'empty'])
|
|
rows.append(['cache', 'readwrite'])
|
|
rows.append(['udid', ''])
|
|
rows.append(['fc_map_count', fcmap_info['fc_map_count']])
|
|
rows.append(['sync_rate', '50'])
|
|
rows.append(['copy_count', '1'])
|
|
rows.append(['se_copy_count', '0'])
|
|
rows.append(['mirror_write_priority', 'latency'])
|
|
rows.append(['RC_change', 'no'])
|
|
rows.append(['used_capacity', cap_u])
|
|
rows.append(['real_capacity', cap_r])
|
|
rows.append(['free_capacity', cap_f])
|
|
rows.append(['autoexpand', vol['autoexpand']])
|
|
rows.append(['warning', vol['warning']])
|
|
rows.append(['grainsize', vol['grainsize']])
|
|
rows.append(['easy_tier', vol['easy_tier']])
|
|
rows.append(['compressed_copy', vol['compressed_copy']])
|
|
|
|
if 'nohdr' in kwargs:
|
|
for index in range(len(rows)):
|
|
rows[index] = ' '.join(rows[index][1:])
|
|
|
|
if 'delim' in kwargs:
|
|
for index in range(len(rows)):
|
|
rows[index] = kwargs['delim'].join(rows[index])
|
|
|
|
return ('%s' % '\n'.join(rows), '')
|
|
|
|
def _add_port_to_host(self, host_info, **kwargs):
|
|
if 'iscsiname' in kwargs:
|
|
added_key = 'iscsi_names'
|
|
added_val = kwargs['iscsiname'].strip('\'\"')
|
|
elif 'hbawwpn' in kwargs:
|
|
added_key = 'wwpns'
|
|
added_val = kwargs['hbawwpn'].strip('\'\"')
|
|
else:
|
|
return self._errors['CMMVC5707E']
|
|
|
|
host_info[added_key].append(added_val)
|
|
|
|
for k, v in self._hosts_list.iteritems():
|
|
if v['id'] == host_info['id']:
|
|
continue
|
|
for port in v[added_key]:
|
|
if port == added_val:
|
|
return self._errors['CMMVC6581E']
|
|
return ('', '')
|
|
|
|
# Make a host
|
|
def _cmd_mkhost(self, **kwargs):
|
|
host_info = {}
|
|
host_info['id'] = self._find_unused_id(self._hosts_list)
|
|
|
|
if 'name' in kwargs:
|
|
host_name = kwargs['name'].strip('\'\"')
|
|
else:
|
|
host_name = 'host' + str(host_info['id'])
|
|
|
|
if self._is_invalid_name(host_name):
|
|
return self._errors['CMMVC6527E']
|
|
|
|
if host_name in self._hosts_list:
|
|
return self._errors['CMMVC6035E']
|
|
|
|
host_info['host_name'] = host_name
|
|
host_info['iscsi_names'] = []
|
|
host_info['wwpns'] = []
|
|
|
|
out, err = self._add_port_to_host(host_info, **kwargs)
|
|
if not len(err):
|
|
self._hosts_list[host_name] = host_info
|
|
return ('Host, id [%s], successfully created' %
|
|
(host_info['id']), '')
|
|
else:
|
|
return (out, err)
|
|
|
|
# Add ports to an existing host
|
|
def _cmd_addhostport(self, **kwargs):
|
|
if 'obj' not in kwargs:
|
|
return self._errors['CMMVC5701E']
|
|
host_name = kwargs['obj'].strip('\'\'')
|
|
|
|
if host_name not in self._hosts_list:
|
|
return self._errors['CMMVC5753E']
|
|
|
|
host_info = self._hosts_list[host_name]
|
|
return self._add_port_to_host(host_info, **kwargs)
|
|
|
|
# Change host properties
|
|
def _cmd_chhost(self, **kwargs):
|
|
if 'chapsecret' not in kwargs:
|
|
return self._errors['CMMVC5707E']
|
|
secret = kwargs['obj'].strip('\'\'')
|
|
|
|
if 'obj' not in kwargs:
|
|
return self._errors['CMMVC5701E']
|
|
host_name = kwargs['obj'].strip('\'\'')
|
|
|
|
if host_name not in self._hosts_list:
|
|
return self._errors['CMMVC5753E']
|
|
|
|
self._hosts_list[host_name]['chapsecret'] = secret
|
|
return ('', '')
|
|
|
|
# Remove a host
|
|
def _cmd_rmhost(self, **kwargs):
|
|
if 'obj' not in kwargs:
|
|
return self._errors['CMMVC5701E']
|
|
|
|
host_name = kwargs['obj'].strip('\'\'')
|
|
if host_name not in self._hosts_list:
|
|
return self._errors['CMMVC5753E']
|
|
|
|
for k, v in self._mappings_list.iteritems():
|
|
if (v['host'] == host_name):
|
|
return self._errors['CMMVC5871E']
|
|
|
|
del self._hosts_list[host_name]
|
|
return ('', '')
|
|
|
|
# List information about hosts
|
|
def _cmd_lshost(self, **kwargs):
|
|
if 'obj' not in kwargs:
|
|
rows = []
|
|
rows.append(['id', 'name', 'port_count', 'iogrp_count', 'status'])
|
|
|
|
found = False
|
|
for k, host in self._hosts_list.iteritems():
|
|
filterstr = 'name=' + host['host_name']
|
|
if (('filtervalue' not in kwargs) or
|
|
(kwargs['filtervalue'] == filterstr)):
|
|
rows.append([host['id'], host['host_name'], '1', '4',
|
|
'offline'])
|
|
found = True
|
|
if found:
|
|
return self._print_info_cmd(rows=rows, **kwargs)
|
|
else:
|
|
return ('', '')
|
|
else:
|
|
if kwargs['obj'] not in self._hosts_list:
|
|
return self._errors['CMMVC5754E']
|
|
host = self._hosts_list[kwargs['obj']]
|
|
rows = []
|
|
rows.append(['id', host['id']])
|
|
rows.append(['name', host['host_name']])
|
|
rows.append(['port_count', '1'])
|
|
rows.append(['type', 'generic'])
|
|
rows.append(['mask', '1111'])
|
|
rows.append(['iogrp_count', '4'])
|
|
rows.append(['status', 'online'])
|
|
for port in host['iscsi_names']:
|
|
rows.append(['iscsi_name', port])
|
|
rows.append(['node_logged_in_count', '0'])
|
|
rows.append(['state', 'offline'])
|
|
for port in host['wwpns']:
|
|
rows.append(['WWPN', port])
|
|
rows.append(['node_logged_in_count', '0'])
|
|
rows.append(['state', 'active'])
|
|
|
|
if 'nohdr' in kwargs:
|
|
for index in range(len(rows)):
|
|
rows[index] = ' '.join(rows[index][1:])
|
|
|
|
if 'delim' in kwargs:
|
|
for index in range(len(rows)):
|
|
rows[index] = kwargs['delim'].join(rows[index])
|
|
|
|
return ('%s' % '\n'.join(rows), '')
|
|
|
|
# List iSCSI authorization information about hosts
|
|
def _cmd_lsiscsiauth(self, **kwargs):
|
|
if self._next_cmd_error['lsiscsiauth'] == 'no_info':
|
|
self._next_cmd_error['lsiscsiauth'] = ''
|
|
return ('', '')
|
|
rows = []
|
|
rows.append(['type', 'id', 'name', 'iscsi_auth_method',
|
|
'iscsi_chap_secret'])
|
|
|
|
for k, host in self._hosts_list.iteritems():
|
|
method = 'none'
|
|
secret = ''
|
|
if 'chapsecret' in host:
|
|
method = 'chap'
|
|
secret = host['chapsecret']
|
|
rows.append(['host', host['id'], host['host_name'], method,
|
|
secret])
|
|
return self._print_info_cmd(rows=rows, **kwargs)
|
|
|
|
# Create a vdisk-host mapping
|
|
def _cmd_mkvdiskhostmap(self, **kwargs):
|
|
mapping_info = {}
|
|
mapping_info['id'] = self._find_unused_id(self._mappings_list)
|
|
|
|
if 'host' not in kwargs:
|
|
return self._errors['CMMVC5707E']
|
|
mapping_info['host'] = kwargs['host'].strip('\'\'')
|
|
|
|
if 'scsi' not in kwargs:
|
|
return self._errors['CMMVC5707E']
|
|
mapping_info['lun'] = kwargs['scsi'].strip('\'\'')
|
|
|
|
if 'obj' not in kwargs:
|
|
return self._errors['CMMVC5707E']
|
|
mapping_info['vol'] = kwargs['obj'].strip('\'\'')
|
|
|
|
if not mapping_info['vol'] in self._volumes_list:
|
|
return self._errors['CMMVC5753E']
|
|
|
|
if not mapping_info['host'] in self._hosts_list:
|
|
return self._errors['CMMVC5754E']
|
|
|
|
if mapping_info['vol'] in self._mappings_list:
|
|
return self._errors['CMMVC6071E']
|
|
|
|
for k, v in self._mappings_list.iteritems():
|
|
if ((v['host'] == mapping_info['host']) and
|
|
(v['lun'] == mapping_info['lun'])):
|
|
return self._errors['CMMVC5879E']
|
|
|
|
self._mappings_list[mapping_info['vol']] = mapping_info
|
|
return ('Virtual Disk to Host map, id [%s], successfully created'
|
|
% (mapping_info['id']), '')
|
|
|
|
# Delete a vdisk-host mapping
|
|
def _cmd_rmvdiskhostmap(self, **kwargs):
|
|
if 'host' not in kwargs:
|
|
return self._errors['CMMVC5707E']
|
|
host = kwargs['host'].strip('\'\'')
|
|
|
|
if 'obj' not in kwargs:
|
|
return self._errors['CMMVC5701E']
|
|
vol = kwargs['obj'].strip('\'\'')
|
|
|
|
if not vol in self._mappings_list:
|
|
return self._errors['CMMVC5753E']
|
|
|
|
if self._mappings_list[vol]['host'] != host:
|
|
return self._errors['CMMVC5753E']
|
|
|
|
del self._mappings_list[vol]
|
|
return ('', '')
|
|
|
|
# List information about vdisk-host mappings
|
|
def _cmd_lshostvdiskmap(self, **kwargs):
|
|
index = 1
|
|
no_hdr = 0
|
|
delimeter = ''
|
|
host_name = kwargs['obj']
|
|
|
|
if host_name not in self._hosts_list:
|
|
return self._errors['CMMVC5754E']
|
|
|
|
rows = []
|
|
rows.append(['id', 'name', 'SCSI_id', 'vdisk_id', 'vdisk_name',
|
|
'vdisk_UID'])
|
|
|
|
for k, mapping in self._mappings_list.iteritems():
|
|
if (host_name == '') or (mapping['host'] == host_name):
|
|
volume = self._volumes_list[mapping['vol']]
|
|
rows.append([mapping['id'], mapping['host'],
|
|
mapping['lun'], volume['id'],
|
|
volume['name'], volume['uid']])
|
|
|
|
return self._print_info_cmd(rows=rows, **kwargs)
|
|
|
|
# Create a FlashCopy mapping
|
|
def _cmd_mkfcmap(self, **kwargs):
|
|
source = ''
|
|
target = ''
|
|
copyrate = kwargs['copyrate'] if 'copyrate' in kwargs else '50'
|
|
|
|
if 'source' not in kwargs:
|
|
return self._errors['CMMVC5707E']
|
|
source = kwargs['source'].strip('\'\'')
|
|
if not source in self._volumes_list:
|
|
return self._errors['CMMVC5754E']
|
|
|
|
if 'target' not in kwargs:
|
|
return self._errors['CMMVC5707E']
|
|
target = kwargs['target'].strip('\'\'')
|
|
if not target in self._volumes_list:
|
|
return self._errors['CMMVC5754E']
|
|
|
|
if source == target:
|
|
return self._errors['CMMVC6303E']
|
|
|
|
if (self._volumes_list[source]['capacity'] !=
|
|
self._volumes_list[target]['capacity']):
|
|
return self._errors['CMMVC5924E']
|
|
|
|
fcmap_info = {}
|
|
fcmap_info['source'] = source
|
|
fcmap_info['target'] = target
|
|
fcmap_info['id'] = self._find_unused_id(self._fcmappings_list)
|
|
fcmap_info['name'] = 'fcmap' + fcmap_info['id']
|
|
fcmap_info['copyrate'] = copyrate
|
|
fcmap_info['progress'] = '0'
|
|
fcmap_info['autodelete'] = True if 'autodelete' in kwargs else False
|
|
fcmap_info['status'] = 'idle_or_copied'
|
|
self._fcmappings_list[fcmap_info['id']] = fcmap_info
|
|
|
|
return('FlashCopy Mapping, id [' + fcmap_info['id'] +
|
|
'], successfully created', '')
|
|
|
|
def _cmd_gen_prestartfcmap(self, **kwargs):
|
|
if 'obj' not in kwargs:
|
|
return self._errors['CMMVC5701E']
|
|
id_num = kwargs['obj']
|
|
|
|
if self._next_cmd_error['prestartfcmap'] == 'bad_id':
|
|
id_num = -1
|
|
self._next_cmd_error['prestartfcmap'] = ''
|
|
|
|
try:
|
|
fcmap = self._fcmappings_list[id_num]
|
|
except KeyError:
|
|
return self._errors['CMMVC5753E']
|
|
|
|
return self._state_transition('prepare', fcmap)
|
|
|
|
def _cmd_gen_startfcmap(self, **kwargs):
|
|
if 'obj' not in kwargs:
|
|
return self._errors['CMMVC5701E']
|
|
id_num = kwargs['obj']
|
|
|
|
if self._next_cmd_error['startfcmap'] == 'bad_id':
|
|
id_num = -1
|
|
self._next_cmd_error['startfcmap'] = ''
|
|
|
|
try:
|
|
fcmap = self._fcmappings_list[id_num]
|
|
except KeyError:
|
|
return self._errors['CMMVC5753E']
|
|
|
|
return self._state_transition('start', fcmap)
|
|
|
|
def _cmd_stopfcmap(self, **kwargs):
|
|
if 'obj' not in kwargs:
|
|
return self._errors['CMMVC5701E']
|
|
id_num = kwargs['obj']
|
|
|
|
try:
|
|
fcmap = self._fcmappings_list[id_num]
|
|
except KeyError:
|
|
return self._errors['CMMVC5753E']
|
|
|
|
return self._state_transition('stop', fcmap)
|
|
|
|
def _cmd_rmfcmap(self, **kwargs):
|
|
if 'obj' not in kwargs:
|
|
return self._errors['CMMVC5701E']
|
|
id_num = kwargs['obj']
|
|
force = True if 'force' in kwargs else False
|
|
|
|
if self._next_cmd_error['rmfcmap'] == 'bad_id':
|
|
id_num = -1
|
|
self._next_cmd_error['rmfcmap'] = ''
|
|
|
|
try:
|
|
fcmap = self._fcmappings_list[id_num]
|
|
except KeyError:
|
|
return self._errors['CMMVC5753E']
|
|
|
|
function = 'delete_force' if force else 'delete'
|
|
ret = self._state_transition(function, fcmap)
|
|
if fcmap['status'] == 'end':
|
|
del self._fcmappings_list[id_num]
|
|
return ret
|
|
|
|
def _cmd_lsvdiskfcmappings(self, **kwargs):
|
|
if 'obj' not in kwargs:
|
|
return self._errors['CMMVC5707E']
|
|
vdisk = kwargs['obj']
|
|
rows = []
|
|
rows.append(['id', 'name'])
|
|
for k, v in self._fcmappings_list.iteritems():
|
|
if v['source'] == vdisk or v['target'] == vdisk:
|
|
rows.append([v['id'], v['name']])
|
|
return self._print_info_cmd(rows=rows, **kwargs)
|
|
|
|
def _cmd_chfcmap(self, **kwargs):
|
|
if 'obj' not in kwargs:
|
|
return self._errors['CMMVC5707E']
|
|
id_num = kwargs['obj']
|
|
|
|
try:
|
|
fcmap = self._fcmappings_list[id_num]
|
|
except KeyError:
|
|
return self._errors['CMMVC5753E']
|
|
|
|
for key in ['name', 'copyrate', 'autodelete']:
|
|
if key in kwargs:
|
|
fcmap[key] = kwargs[key]
|
|
return ('', '')
|
|
|
|
def _cmd_lsfcmap(self, **kwargs):
|
|
rows = []
|
|
rows.append(['id', 'name', 'source_vdisk_id', 'source_vdisk_name',
|
|
'target_vdisk_id', 'target_vdisk_name', 'group_id',
|
|
'group_name', 'status', 'progress', 'copy_rate',
|
|
'clean_progress', 'incremental', 'partner_FC_id',
|
|
'partner_FC_name', 'restoring', 'start_time',
|
|
'rc_controlled'])
|
|
|
|
# Assume we always get a filtervalue argument
|
|
filter_key = kwargs['filtervalue'].split('=')[0]
|
|
filter_value = kwargs['filtervalue'].split('=')[1]
|
|
to_delete = []
|
|
for k, v in self._fcmappings_list.iteritems():
|
|
if str(v[filter_key]) == filter_value:
|
|
source = self._volumes_list[v['source']]
|
|
target = self._volumes_list[v['target']]
|
|
self._state_transition('wait', v)
|
|
|
|
if self._next_cmd_error['lsfcmap'] == 'speed_up':
|
|
self._next_cmd_error['lsfcmap'] = ''
|
|
curr_state = v['status']
|
|
while self._state_transition('wait', v) == ("", ""):
|
|
if curr_state == v['status']:
|
|
break
|
|
curr_state = v['status']
|
|
|
|
if ((v['status'] == 'idle_or_copied' and v['autodelete'] and
|
|
v['progress'] == '100') or (v['status'] == 'end')):
|
|
to_delete.append(k)
|
|
else:
|
|
rows.append([v['id'], v['name'], source['id'],
|
|
source['name'], target['id'], target['name'],
|
|
'', '', v['status'], v['progress'],
|
|
v['copyrate'], '100', 'off', '', '', 'no', '',
|
|
'no'])
|
|
|
|
for d in to_delete:
|
|
del self._fcmappings_list[k]
|
|
|
|
return self._print_info_cmd(rows=rows, **kwargs)
|
|
|
|
# Add host to list
|
|
def _add_host_to_list(self, connector):
|
|
host_info = {}
|
|
host_info['id'] = self._find_unused_id(self._hosts_list)
|
|
host_info['host_name'] = connector['host']
|
|
host_info['iscsi_names'] = []
|
|
host_info['wwpns'] = []
|
|
if 'initiator' in connector:
|
|
host_info['iscsi_names'].append(connector['initiator'])
|
|
if 'wwpns' in connector:
|
|
host_info['wwpns'] = host_info['wwpns'] + connector['wwpns']
|
|
self._hosts_list[connector['host']] = host_info
|
|
|
|
# The main function to run commands on the management simulator
|
|
def execute_command(self, cmd, check_exit_code=True):
|
|
try:
|
|
kwargs = self._cmd_to_dict(cmd)
|
|
except IndexError:
|
|
return self._errors['CMMVC5707E']
|
|
|
|
command = kwargs['cmd']
|
|
del kwargs['cmd']
|
|
arg_list = cmd.split()
|
|
|
|
if command == 'lsmdiskgrp':
|
|
out, err = self._cmd_lsmdiskgrp(**kwargs)
|
|
elif command == 'lslicense':
|
|
out, err = self._cmd_lslicense(**kwargs)
|
|
elif command == 'lssystem':
|
|
out, err = self._cmd_lssystem(**kwargs)
|
|
elif command == 'lsnodecanister':
|
|
out, err = self._cmd_lsnodecanister(**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':
|
|
out, err = self._cmd_mkvdisk(**kwargs)
|
|
elif command == 'rmvdisk':
|
|
out, err = self._cmd_rmvdisk(**kwargs)
|
|
elif command == 'lsvdisk':
|
|
out, err = self._cmd_lsvdisk(**kwargs)
|
|
elif command == 'mkhost':
|
|
out, err = self._cmd_mkhost(**kwargs)
|
|
elif command == 'addhostport':
|
|
out, err = self._cmd_addhostport(**kwargs)
|
|
elif command == 'chhost':
|
|
out, err = self._cmd_chhost(**kwargs)
|
|
elif command == 'rmhost':
|
|
out, err = self._cmd_rmhost(**kwargs)
|
|
elif command == 'lshost':
|
|
out, err = self._cmd_lshost(**kwargs)
|
|
elif command == 'lsiscsiauth':
|
|
out, err = self._cmd_lsiscsiauth(**kwargs)
|
|
elif command == 'mkvdiskhostmap':
|
|
out, err = self._cmd_mkvdiskhostmap(**kwargs)
|
|
elif command == 'rmvdiskhostmap':
|
|
out, err = self._cmd_rmvdiskhostmap(**kwargs)
|
|
elif command == 'lshostvdiskmap':
|
|
out, err = self._cmd_lshostvdiskmap(**kwargs)
|
|
elif command == 'mkfcmap':
|
|
out, err = self._cmd_mkfcmap(**kwargs)
|
|
elif command == 'prestartfcmap':
|
|
out, err = self._cmd_gen_prestartfcmap(**kwargs)
|
|
elif command == 'startfcmap':
|
|
out, err = self._cmd_gen_startfcmap(**kwargs)
|
|
elif command == 'stopfcmap':
|
|
out, err = self._cmd_stopfcmap(**kwargs)
|
|
elif command == 'rmfcmap':
|
|
out, err = self._cmd_rmfcmap(**kwargs)
|
|
elif command == 'chfcmap':
|
|
out, err = self._cmd_chfcmap(**kwargs)
|
|
elif command == 'lsfcmap':
|
|
out, err = self._cmd_lsfcmap(**kwargs)
|
|
elif command == 'lsvdiskfcmappings':
|
|
out, err = self._cmd_lsvdiskfcmappings(**kwargs)
|
|
else:
|
|
out, err = ('', 'ERROR: Unsupported command')
|
|
|
|
if (check_exit_code) and (len(err) != 0):
|
|
raise exception.ProcessExecutionError(exit_code=1,
|
|
stdout=out,
|
|
stderr=err,
|
|
cmd=' '.join(cmd))
|
|
|
|
return (out, err)
|
|
|
|
# After calling this function, the next call to the specified command will
|
|
# result in in the error specified
|
|
def error_injection(self, cmd, error):
|
|
self._next_cmd_error[cmd] = error
|
|
|
|
|
|
class StorwizeSVCFakeDriver(storwize_svc.StorwizeSVCDriver):
|
|
def __init__(self, *args, **kwargs):
|
|
super(StorwizeSVCFakeDriver, self).__init__(*args, **kwargs)
|
|
|
|
def set_fake_storage(self, fake):
|
|
self.fake_storage = fake
|
|
|
|
def _run_ssh(self, cmd, check_exit_code=True):
|
|
try:
|
|
LOG.debug(_('Run CLI command: %s') % cmd)
|
|
ret = self.fake_storage.execute_command(cmd, check_exit_code)
|
|
(stdout, stderr) = ret
|
|
LOG.debug(_('CLI output:\n stdout: %(stdout)s\n stderr: '
|
|
'%(stderr)s') % {'stdout': stdout, 'stderr': stderr})
|
|
|
|
except exception.ProcessExecutionError as e:
|
|
with excutils.save_and_reraise_exception():
|
|
LOG.debug(_('CLI Exception output:\n stdout: %(out)s\n '
|
|
'stderr: %(err)s') % {'out': e.stdout,
|
|
'err': e.stderr})
|
|
|
|
return ret
|
|
|
|
|
|
class StorwizeSVCFakeSock:
|
|
def settimeout(self, time):
|
|
return
|
|
|
|
|
|
class StorwizeSVCDriverTestCase(test.TestCase):
|
|
def setUp(self):
|
|
super(StorwizeSVCDriverTestCase, self).setUp()
|
|
self.USESIM = True
|
|
if self.USESIM:
|
|
self.driver = StorwizeSVCFakeDriver(
|
|
configuration=conf.Configuration(None))
|
|
self._def_flags = {'san_ip': 'hostname',
|
|
'san_login': 'user',
|
|
'san_password': 'pass',
|
|
'storwize_svc_flashcopy_timeout': 20,
|
|
# Test ignore capitalization
|
|
'storwize_svc_connection_protocol': 'iScSi',
|
|
'storwize_svc_multipath_enabled': False}
|
|
self._host_name = 'storwize-svc-test'
|
|
self._host_ip = '1.234.56.78'
|
|
self._host_wwpns = [
|
|
str(random.randint(0, 9999999999999999)).zfill(16),
|
|
str(random.randint(0, 9999999999999999)).zfill(16)]
|
|
self._iscsi_name = ('test.initiator.%s' %
|
|
str(random.randint(10000, 99999)))
|
|
self.sim = StorwizeSVCManagementSimulator('volpool')
|
|
|
|
self.driver.set_fake_storage(self.sim)
|
|
else:
|
|
self.driver = storwize_svc.StorwizeSVCDriver(
|
|
configuration=conf.Configuration(None))
|
|
self._def_flags = {'san_ip': '1.111.11.11',
|
|
'san_login': 'user',
|
|
'san_password': 'password',
|
|
'storwize_svc_volpool_name': 'openstack',
|
|
# Test ignore capitalization
|
|
'storwize_svc_connection_protocol': 'iScSi',
|
|
'storwize_svc_multipath_enabled': False,
|
|
'ssh_conn_timeout': 0}
|
|
self._host_name = socket.gethostname()
|
|
self._host_ip = socket.gethostbyname(self._host_name)
|
|
|
|
self._host_wwpns = []
|
|
out, err = utils.execute('systool', '-c', 'fc_host', '-v',
|
|
run_as_root=True)
|
|
lines = out.split('\n')
|
|
for line in lines:
|
|
val = line.split('=')
|
|
if (len(val) == 2 and
|
|
val[0].strip().replace(" ", "") == 'port_name'):
|
|
self._host_wwpns.append(val[1].strip()[3:-1])
|
|
self.assertNotEqual(len(self._host_wwpns), 0)
|
|
|
|
lines = utils.read_file_as_root('/etc/iscsi/initiatorname.iscsi')
|
|
for l in lines.split('\n'):
|
|
if l.startswith('InitiatorName='):
|
|
self._iscsi_name = l[l.index('=') + 1:].strip()
|
|
|
|
self._reset_flags()
|
|
self.driver.db = StorwizeSVCFakeDB()
|
|
self.driver.do_setup(None)
|
|
self.driver.check_for_setup_error()
|
|
self.stubs.Set(storwize_svc.time, 'sleep', lambda s: None)
|
|
|
|
def _set_flag(self, flag, value):
|
|
group = self.driver.configuration.config_group
|
|
self.driver.configuration.set_override(flag, value, group)
|
|
|
|
def _reset_flags(self):
|
|
self.driver.configuration.local_conf.reset()
|
|
for k, v in self._def_flags.iteritems():
|
|
self._set_flag(k, v)
|
|
|
|
def _assert_vol_exists(self, name, exists):
|
|
is_vol_defined = self.driver._is_vdisk_defined(name)
|
|
self.assertEqual(is_vol_defined, exists)
|
|
|
|
def test_storwize_svc_connectivity(self):
|
|
# Make sure we detect if the pool doesn't exist
|
|
no_exist_pool = 'i-dont-exist-%s' % random.randint(10000, 99999)
|
|
self._set_flag('storwize_svc_volpool_name', no_exist_pool)
|
|
self.assertRaises(exception.InvalidInput,
|
|
self.driver.do_setup, None)
|
|
self._reset_flags()
|
|
|
|
# Check the case where the user didn't configure IP addresses
|
|
# as well as receiving unexpected results from the storage
|
|
if self.USESIM:
|
|
self.sim.error_injection('lsnodecanister', 'header_mismatch')
|
|
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.assertRaises(exception.VolumeBackendAPIException,
|
|
self.driver.do_setup, None)
|
|
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', '')
|
|
self.assertRaises(exception.InvalidInput,
|
|
self.driver.check_for_setup_error)
|
|
self._reset_flags()
|
|
|
|
self._set_flag('san_password', None)
|
|
self._set_flag('san_private_key', None)
|
|
self.assertRaises(exception.InvalidInput,
|
|
self.driver.check_for_setup_error)
|
|
self._reset_flags()
|
|
|
|
self._set_flag('storwize_svc_vol_rsize', 101)
|
|
self.assertRaises(exception.InvalidInput,
|
|
self.driver.check_for_setup_error)
|
|
self._reset_flags()
|
|
|
|
self._set_flag('storwize_svc_vol_warning', 101)
|
|
self.assertRaises(exception.InvalidInput,
|
|
self.driver.check_for_setup_error)
|
|
self._reset_flags()
|
|
|
|
self._set_flag('storwize_svc_vol_grainsize', 42)
|
|
self.assertRaises(exception.InvalidInput,
|
|
self.driver.check_for_setup_error)
|
|
self._reset_flags()
|
|
|
|
self._set_flag('storwize_svc_flashcopy_timeout', 601)
|
|
self.assertRaises(exception.InvalidInput,
|
|
self.driver.check_for_setup_error)
|
|
self._reset_flags()
|
|
|
|
self._set_flag('storwize_svc_vol_compression', True)
|
|
self._set_flag('storwize_svc_vol_rsize', -1)
|
|
self.assertRaises(exception.InvalidInput,
|
|
self.driver.check_for_setup_error)
|
|
self._reset_flags()
|
|
|
|
self._set_flag('storwize_svc_connection_protocol', 'foo')
|
|
self.assertRaises(exception.InvalidInput,
|
|
self.driver.check_for_setup_error)
|
|
self._reset_flags()
|
|
|
|
self._set_flag('storwize_svc_connection_protocol', 'iSCSI')
|
|
self._set_flag('storwize_svc_multipath_enabled', True)
|
|
self.assertRaises(exception.InvalidInput,
|
|
self.driver.check_for_setup_error)
|
|
self._reset_flags()
|
|
|
|
if self.USESIM:
|
|
self.sim.error_injection('lslicense', 'no_compression')
|
|
self._set_flag('storwize_svc_vol_compression', True)
|
|
self.driver.do_setup(None)
|
|
self.assertRaises(exception.InvalidInput,
|
|
self.driver.check_for_setup_error)
|
|
self._reset_flags()
|
|
|
|
# Finally, check with good parameters
|
|
self.driver.do_setup(None)
|
|
|
|
def _generate_vol_info(self, vol_name, vol_id):
|
|
rand_id = str(random.randint(10000, 99999))
|
|
if vol_name:
|
|
return {'name': 'snap_volume%s' % rand_id,
|
|
'volume_name': vol_name,
|
|
'id': rand_id,
|
|
'volume_id': vol_id,
|
|
'volume_size': 10}
|
|
else:
|
|
return {'name': 'test_volume%s' % rand_id,
|
|
'size': 10,
|
|
'id': '%s' % rand_id,
|
|
'volume_type_id': None}
|
|
|
|
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']
|
|
self.driver.create_volume(volume)
|
|
|
|
attrs = self.driver._get_vdisk_attributes(volume['name'])
|
|
self.driver.delete_volume(volume)
|
|
volume_types.destroy(ctxt, type_ref['id'])
|
|
return attrs
|
|
|
|
def _fail_prepare_fc_map(self, fc_map_id, source, target):
|
|
raise exception.ProcessExecutionError(exit_code=1,
|
|
stdout='',
|
|
stderr='unit-test-fail',
|
|
cmd='prestartfcmap id')
|
|
|
|
def test_storwize_svc_snapshots(self):
|
|
vol1 = self._generate_vol_info(None, None)
|
|
self.driver.create_volume(vol1)
|
|
self.driver.db.volume_set(vol1)
|
|
snap1 = self._generate_vol_info(vol1['name'], vol1['id'])
|
|
|
|
# Test timeout and volume cleanup
|
|
self._set_flag('storwize_svc_flashcopy_timeout', 1)
|
|
self.assertRaises(exception.InvalidSnapshot,
|
|
self.driver.create_snapshot, snap1)
|
|
self._assert_vol_exists(snap1['name'], False)
|
|
self._reset_flags()
|
|
|
|
# Test prestartfcmap, startfcmap, and rmfcmap failing
|
|
orig = self.driver._call_prepare_fc_map
|
|
self.driver._call_prepare_fc_map = self._fail_prepare_fc_map
|
|
self.assertRaises(exception.ProcessExecutionError,
|
|
self.driver.create_snapshot, snap1)
|
|
self.driver._call_prepare_fc_map = orig
|
|
|
|
if self.USESIM:
|
|
self.sim.error_injection('lsfcmap', 'speed_up')
|
|
self.sim.error_injection('startfcmap', 'bad_id')
|
|
self.assertRaises(exception.ProcessExecutionError,
|
|
self.driver.create_snapshot, snap1)
|
|
self._assert_vol_exists(snap1['name'], False)
|
|
self.sim.error_injection('prestartfcmap', 'bad_id')
|
|
self.assertRaises(exception.ProcessExecutionError,
|
|
self.driver.create_snapshot, snap1)
|
|
self._assert_vol_exists(snap1['name'], False)
|
|
|
|
# Test successful snapshot
|
|
self.driver.create_snapshot(snap1)
|
|
self._assert_vol_exists(snap1['name'], True)
|
|
|
|
# Try to create a snapshot from an non-existing volume - should fail
|
|
snap_novol = self._generate_vol_info('undefined-vol', '12345')
|
|
self.assertRaises(exception.VolumeNotFound,
|
|
self.driver.create_snapshot,
|
|
snap_novol)
|
|
|
|
# We support deleting a volume that has snapshots, so delete the volume
|
|
# first
|
|
self.driver.delete_volume(vol1)
|
|
self.driver.delete_snapshot(snap1)
|
|
|
|
def test_storwize_svc_create_volfromsnap_clone(self):
|
|
vol1 = self._generate_vol_info(None, None)
|
|
self.driver.create_volume(vol1)
|
|
self.driver.db.volume_set(vol1)
|
|
snap1 = self._generate_vol_info(vol1['name'], vol1['id'])
|
|
self.driver.create_snapshot(snap1)
|
|
vol2 = self._generate_vol_info(None, None)
|
|
vol3 = self._generate_vol_info(None, None)
|
|
|
|
# Try to create a volume from a non-existing snapshot
|
|
snap_novol = self._generate_vol_info('undefined-vol', '12345')
|
|
vol_novol = self._generate_vol_info(None, None)
|
|
self.assertRaises(exception.SnapshotNotFound,
|
|
self.driver.create_volume_from_snapshot,
|
|
vol_novol,
|
|
snap_novol)
|
|
|
|
# Fail the snapshot
|
|
orig = self.driver._call_prepare_fc_map
|
|
self.driver._call_prepare_fc_map = self._fail_prepare_fc_map
|
|
self.assertRaises(exception.ProcessExecutionError,
|
|
self.driver.create_volume_from_snapshot,
|
|
vol2, snap1)
|
|
self.driver._call_prepare_fc_map = orig
|
|
self._assert_vol_exists(vol2['name'], False)
|
|
|
|
# Try to create where source size != target size
|
|
vol2['size'] += 1
|
|
self.assertRaises(exception.VolumeBackendAPIException,
|
|
self.driver.create_volume_from_snapshot,
|
|
vol2, snap1)
|
|
self._assert_vol_exists(vol2['name'], False)
|
|
vol2['size'] -= 1
|
|
|
|
# Succeed
|
|
if self.USESIM:
|
|
self.sim.error_injection('lsfcmap', 'speed_up')
|
|
self.driver.create_volume_from_snapshot(vol2, snap1)
|
|
self._assert_vol_exists(vol2['name'], True)
|
|
|
|
# Try to clone where source size != target size
|
|
vol3['size'] += 1
|
|
self.assertRaises(exception.VolumeBackendAPIException,
|
|
self.driver.create_cloned_volume,
|
|
vol3, vol2)
|
|
self._assert_vol_exists(vol3['name'], False)
|
|
vol3['size'] -= 1
|
|
|
|
if self.USESIM:
|
|
self.sim.error_injection('lsfcmap', 'speed_up')
|
|
self.driver.create_cloned_volume(vol3, vol2)
|
|
self._assert_vol_exists(vol3['name'], True)
|
|
|
|
# Delete in the 'opposite' order to make sure it works
|
|
self.driver.delete_volume(vol3)
|
|
self._assert_vol_exists(vol3['name'], False)
|
|
self.driver.delete_volume(vol2)
|
|
self._assert_vol_exists(vol2['name'], False)
|
|
self.driver.delete_snapshot(snap1)
|
|
self._assert_vol_exists(snap1['name'], False)
|
|
self.driver.delete_volume(vol1)
|
|
self._assert_vol_exists(vol1['name'], False)
|
|
|
|
def test_storwize_svc_volumes(self):
|
|
# Create a first volume
|
|
volume = self._generate_vol_info(None, None)
|
|
self.driver.create_volume(volume)
|
|
|
|
self.driver.ensure_export(None, volume)
|
|
|
|
# Do nothing
|
|
self.driver.create_export(None, volume)
|
|
self.driver.remove_export(None, volume)
|
|
|
|
# Make sure volume attributes are as they should be
|
|
attributes = self.driver._get_vdisk_attributes(volume['name'])
|
|
attr_size = float(attributes['capacity']) / (1024 ** 3) # bytes to GB
|
|
self.assertEqual(attr_size, float(volume['size']))
|
|
pool = self.driver.configuration.local_conf.storwize_svc_volpool_name
|
|
self.assertEqual(attributes['mdisk_grp_name'], pool)
|
|
|
|
# Try to create the volume again (should fail)
|
|
self.assertRaises(exception.ProcessExecutionError,
|
|
self.driver.create_volume,
|
|
volume)
|
|
|
|
# Try to delete a volume that doesn't exist (should not fail)
|
|
vol_no_exist = {'name': 'i_dont_exist'}
|
|
self.driver.delete_volume(vol_no_exist)
|
|
# Ensure export for volume that doesn't exist (should not fail)
|
|
self.driver.ensure_export(None, vol_no_exist)
|
|
|
|
# Delete the volume
|
|
self.driver.delete_volume(volume)
|
|
|
|
def test_storwize_svc_volume_params(self):
|
|
# Option test matrix
|
|
# Option Value Covered by test #
|
|
# rsize -1 1
|
|
# rsize 2 2,3
|
|
# warning 0 2
|
|
# warning 80 3
|
|
# autoexpand True 2
|
|
# autoexpand False 3
|
|
# grainsize 32 2
|
|
# grainsize 256 3
|
|
# compression True 4
|
|
# compression False 2,3
|
|
# easytier True 1,3
|
|
# easytier False 2
|
|
|
|
opts_list = []
|
|
chck_list = []
|
|
opts_list.append({'rsize': -1, 'easytier': True})
|
|
chck_list.append({'free_capacity': '0', 'easy_tier': 'on'})
|
|
opts_list.append({'rsize': 2, 'compression': False, 'warning': 0,
|
|
'autoexpand': True, 'grainsize': 32,
|
|
'easytier': False})
|
|
chck_list.append({'-free_capacity': '0', 'compressed_copy': 'no',
|
|
'warning': '0', 'autoexpand': 'on',
|
|
'grainsize': '32', 'easy_tier': 'off'})
|
|
opts_list.append({'rsize': 2, 'compression': False, 'warning': 80,
|
|
'autoexpand': False, 'grainsize': 256,
|
|
'easytier': True})
|
|
chck_list.append({'-free_capacity': '0', 'compressed_copy': 'no',
|
|
'warning': '80', 'autoexpand': 'off',
|
|
'grainsize': '256', 'easy_tier': 'on'})
|
|
opts_list.append({'rsize': 2, 'compression': True})
|
|
chck_list.append({'-free_capacity': '0',
|
|
'compressed_copy': 'yes'})
|
|
|
|
for idx in range(len(opts_list)):
|
|
attrs = self._create_test_vol(opts_list[idx])
|
|
for k, v in chck_list[idx].iteritems():
|
|
try:
|
|
if k[0] == '-':
|
|
k = k[1:]
|
|
self.assertNotEqual(attrs[k], v)
|
|
else:
|
|
self.assertEqual(attrs[k], v)
|
|
except exception.ProcessExecutionError as e:
|
|
if 'CMMVC7050E' not in e.stderr:
|
|
raise e
|
|
|
|
def test_storwize_svc_unicode_host_and_volume_names(self):
|
|
# We'll check with iSCSI only - nothing protocol-dependednt here
|
|
self._set_flag('storwize_svc_connection_protocol', 'iSCSI')
|
|
self.driver.do_setup(None)
|
|
|
|
rand_id = random.randint(10000, 99999)
|
|
volume1 = {'name': u'unicode1_volume%s' % rand_id,
|
|
'size': 2,
|
|
'id': 1,
|
|
'volume_type_id': None}
|
|
self.driver.create_volume(volume1)
|
|
self._assert_vol_exists(volume1['name'], True)
|
|
|
|
self.assertRaises(exception.NoValidHost,
|
|
self.driver._connector_to_hostname_prefix,
|
|
{'host': 12345})
|
|
|
|
# Add a a host first to make life interesting (this host and
|
|
# conn['host'] should be translated to the same prefix, and the
|
|
# initiator should differentiate
|
|
tmpconn1 = {'initiator': u'unicode:initiator1.%s' % rand_id,
|
|
'ip': '10.10.10.10',
|
|
'host': u'unicode.foo}.bar{.baz-%s' % rand_id}
|
|
self.driver._create_host(tmpconn1)
|
|
|
|
# Add a host with a different prefix
|
|
tmpconn2 = {'initiator': u'unicode:initiator2.%s' % rand_id,
|
|
'ip': '10.10.10.11',
|
|
'host': u'unicode.hello.world-%s' % rand_id}
|
|
self.driver._create_host(tmpconn2)
|
|
|
|
conn = {'initiator': u'unicode:initiator3.%s' % rand_id,
|
|
'ip': '10.10.10.12',
|
|
'host': u'unicode.foo}.bar}.baz-%s' % rand_id}
|
|
self.driver.initialize_connection(volume1, conn)
|
|
host_name = self.driver._get_host_from_connector(conn)
|
|
self.assertNotEqual(host_name, None)
|
|
self.driver.terminate_connection(volume1, conn)
|
|
host_name = self.driver._get_host_from_connector(conn)
|
|
self.assertEqual(host_name, None)
|
|
self.driver.delete_volume(volume1)
|
|
|
|
# Clean up temporary hosts
|
|
for tmpconn in [tmpconn1, tmpconn2]:
|
|
host_name = self.driver._get_host_from_connector(tmpconn)
|
|
self.assertNotEqual(host_name, None)
|
|
self.driver._delete_host(host_name)
|
|
|
|
def test_storwize_svc_host_maps(self):
|
|
# Create two volumes to be used in mappings
|
|
|
|
ctxt = context.get_admin_context()
|
|
volume1 = self._generate_vol_info(None, None)
|
|
self.driver.create_volume(volume1)
|
|
volume2 = self._generate_vol_info(None, None)
|
|
self.driver.create_volume(volume2)
|
|
|
|
# Create volume types that we created
|
|
types = {}
|
|
for protocol in ['FC', 'iSCSI']:
|
|
opts = {'storage_protocol': '<in> ' + protocol}
|
|
types[protocol] = volume_types.create(ctxt, protocol, opts)
|
|
|
|
conn = {'initiator': self._iscsi_name,
|
|
'ip': self._host_ip,
|
|
'host': self._host_name,
|
|
'wwpns': self._host_wwpns}
|
|
|
|
for protocol in ['FC', 'iSCSI']:
|
|
volume1['volume_type_id'] = types[protocol]['id']
|
|
volume2['volume_type_id'] = types[protocol]['id']
|
|
|
|
# Check case where no hosts exist
|
|
if self.USESIM:
|
|
ret = self.driver._get_host_from_connector(conn)
|
|
self.assertEqual(ret, None)
|
|
|
|
# Make sure that the volumes have been created
|
|
self._assert_vol_exists(volume1['name'], True)
|
|
self._assert_vol_exists(volume2['name'], True)
|
|
|
|
# Initialize connection from the first volume to a host
|
|
self.driver.initialize_connection(volume1, conn)
|
|
|
|
# Initialize again, should notice it and do nothing
|
|
self.driver.initialize_connection(volume1, conn)
|
|
|
|
# Try to delete the 1st volume (should fail because it is mapped)
|
|
self.assertRaises(exception.ProcessExecutionError,
|
|
self.driver.delete_volume,
|
|
volume1)
|
|
|
|
# Check bad output from lsfabric for the 2nd volume
|
|
if protocol == 'FC' and self.USESIM:
|
|
for error in ['remove_field', 'header_mismatch']:
|
|
self.sim.error_injection('lsfabric', error)
|
|
self.assertRaises(exception.VolumeBackendAPIException,
|
|
self.driver.initialize_connection,
|
|
volume2, conn)
|
|
|
|
self.driver.terminate_connection(volume1, conn)
|
|
if self.USESIM:
|
|
host_name = self.driver._get_host_from_connector(conn)
|
|
self.assertEqual(host_name, None)
|
|
|
|
# Check cases with no auth set for host
|
|
if self.USESIM:
|
|
for case in ['no_info', 'no_auth_set']:
|
|
conn_na = {'initiator': 'test:init:%s' %
|
|
random.randint(10000, 99999),
|
|
'ip': '11.11.11.11',
|
|
'host': 'host-%s' % case}
|
|
self.sim._add_host_to_list(conn_na)
|
|
volume1['volume_type_id'] = types['iSCSI']['id']
|
|
if case == 'no_info':
|
|
self.sim.error_injection('lsiscsiauth', 'no_info')
|
|
self.driver.initialize_connection(volume1, conn_na)
|
|
ret = self.driver._get_chap_secret_for_host(conn_na['host'])
|
|
self.assertNotEqual(ret, None)
|
|
self.driver.terminate_connection(volume1, conn_na)
|
|
|
|
# Test no preferred node
|
|
if self.USESIM:
|
|
self.sim.error_injection('lsvdisk', 'no_pref_node')
|
|
self.assertRaises(exception.VolumeBackendAPIException,
|
|
self.driver.initialize_connection,
|
|
volume1, conn)
|
|
|
|
# Initialize connection from the second volume to the host with no
|
|
# preferred node set if in simulation mode, otherwise, just
|
|
# another initialize connection.
|
|
if self.USESIM:
|
|
self.sim.error_injection('lsvdisk', 'blank_pref_node')
|
|
self.driver.initialize_connection(volume2, conn)
|
|
|
|
# Try to remove connection from host that doesn't exist (should fail)
|
|
conn_no_exist = conn.copy()
|
|
conn_no_exist['initiator'] = 'i_dont_exist'
|
|
conn_no_exist['wwpns'] = ['0000000000000000']
|
|
self.assertRaises(exception.VolumeBackendAPIException,
|
|
self.driver.terminate_connection,
|
|
volume1,
|
|
conn_no_exist)
|
|
|
|
# Try to remove connection from volume that isn't mapped (should print
|
|
# message but NOT fail)
|
|
vol_no_exist = {'name': 'i_dont_exist'}
|
|
self.driver.terminate_connection(vol_no_exist, conn)
|
|
|
|
# Remove the mapping from the 1st volume and delete it
|
|
self.driver.terminate_connection(volume1, conn)
|
|
self.driver.delete_volume(volume1)
|
|
self._assert_vol_exists(volume1['name'], False)
|
|
|
|
# Make sure our host still exists
|
|
host_name = self.driver._get_host_from_connector(conn)
|
|
self.assertNotEqual(host_name, None)
|
|
|
|
# Remove the mapping from the 2nd volume and delete it. The host should
|
|
# be automatically removed because there are no more mappings.
|
|
self.driver.terminate_connection(volume2, conn)
|
|
self.driver.delete_volume(volume2)
|
|
self._assert_vol_exists(volume2['name'], False)
|
|
|
|
# Delete volume types that we created
|
|
for protocol in ['FC', 'iSCSI']:
|
|
volume_types.destroy(ctxt, types[protocol]['id'])
|
|
|
|
# Check if our host still exists (it should not)
|
|
ret = self.driver._get_host_from_connector(conn)
|
|
self.assertEqual(ret, None)
|
|
|
|
def test_storwize_svc_delete_volume_snapshots(self):
|
|
# Create a volume with two snapshots
|
|
master = self._generate_vol_info(None, None)
|
|
self.driver.create_volume(master)
|
|
self.driver.db.volume_set(master)
|
|
|
|
# Fail creating a snapshot - will force delete the snapshot
|
|
if self.USESIM and False:
|
|
snap = self._generate_vol_info(master['name'], master['id'])
|
|
self.sim.error_injection('startfcmap', 'bad_id')
|
|
self.assertRaises(exception.ProcessExecutionError,
|
|
self.driver.create_snapshot, snap)
|
|
self._assert_vol_exists(snap['name'], False)
|
|
|
|
# Delete a snapshot
|
|
snap = self._generate_vol_info(master['name'], master['id'])
|
|
self.driver.create_snapshot(snap)
|
|
self._assert_vol_exists(snap['name'], True)
|
|
self.driver.delete_snapshot(snap)
|
|
self._assert_vol_exists(snap['name'], False)
|
|
|
|
# Delete a volume with snapshots (regular)
|
|
snap = self._generate_vol_info(master['name'], master['id'])
|
|
self.driver.create_snapshot(snap)
|
|
self._assert_vol_exists(snap['name'], True)
|
|
self.driver.delete_volume(master)
|
|
self._assert_vol_exists(master['name'], False)
|
|
|
|
# Fail create volume from snapshot - will force delete the volume
|
|
if self.USESIM:
|
|
volfs = self._generate_vol_info(None, None)
|
|
self.sim.error_injection('startfcmap', 'bad_id')
|
|
self.sim.error_injection('lsfcmap', 'speed_up')
|
|
self.assertRaises(exception.ProcessExecutionError,
|
|
self.driver.create_volume_from_snapshot,
|
|
volfs, snap)
|
|
self._assert_vol_exists(volfs['name'], False)
|
|
|
|
# Create volume from snapshot and delete it
|
|
volfs = self._generate_vol_info(None, None)
|
|
if self.USESIM:
|
|
self.sim.error_injection('lsfcmap', 'speed_up')
|
|
self.driver.create_volume_from_snapshot(volfs, snap)
|
|
self._assert_vol_exists(volfs['name'], True)
|
|
self.driver.delete_volume(volfs)
|
|
self._assert_vol_exists(volfs['name'], False)
|
|
|
|
# Create volume from snapshot and delete the snapshot
|
|
volfs = self._generate_vol_info(None, None)
|
|
if self.USESIM:
|
|
self.sim.error_injection('lsfcmap', 'speed_up')
|
|
self.driver.create_volume_from_snapshot(volfs, snap)
|
|
self.driver.delete_snapshot(snap)
|
|
self._assert_vol_exists(snap['name'], False)
|
|
|
|
# Fail create clone - will force delete the target volume
|
|
if self.USESIM:
|
|
clone = self._generate_vol_info(None, None)
|
|
self.sim.error_injection('startfcmap', 'bad_id')
|
|
self.sim.error_injection('lsfcmap', 'speed_up')
|
|
self.assertRaises(exception.ProcessExecutionError,
|
|
self.driver.create_cloned_volume,
|
|
clone, volfs)
|
|
self._assert_vol_exists(clone['name'], False)
|
|
|
|
# Create the clone, delete the source and target
|
|
clone = self._generate_vol_info(None, None)
|
|
if self.USESIM:
|
|
self.sim.error_injection('lsfcmap', 'speed_up')
|
|
self.driver.create_cloned_volume(clone, volfs)
|
|
self._assert_vol_exists(clone['name'], True)
|
|
self.driver.delete_volume(volfs)
|
|
self._assert_vol_exists(volfs['name'], False)
|
|
self.driver.delete_volume(clone)
|
|
self._assert_vol_exists(clone['name'], False)
|
|
|
|
# Note defined in python 2.6, so define here...
|
|
def assertLessEqual(self, a, b, msg=None):
|
|
if not a <= b:
|
|
self.fail('%s not less than or equal to %s' % (repr(a), repr(b)))
|
|
|
|
def test_storwize_svc_get_volume_stats(self):
|
|
stats = self.driver.get_volume_stats()
|
|
self.assertLessEqual(stats['free_capacity_gb'],
|
|
stats['total_capacity_gb'])
|
|
if self.USESIM:
|
|
self.assertEqual(stats['volume_backend_name'],
|
|
'storwize-svc-sim_volpool')
|
|
self.assertAlmostEqual(stats['total_capacity_gb'], 3328.0)
|
|
self.assertAlmostEqual(stats['free_capacity_gb'], 3287.5)
|