1264 lines
48 KiB
Python
1264 lines
48 KiB
Python
# Copyright 2014 IBM Corp.
|
|
# Copyright 2012 OpenStack Foundation
|
|
# 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.
|
|
#
|
|
|
|
"""Tests for the IBM FlashSystem volume driver."""
|
|
|
|
import mock
|
|
from oslo_concurrency import processutils
|
|
from oslo_utils import units
|
|
import six
|
|
|
|
import random
|
|
import re
|
|
|
|
from cinder import context
|
|
from cinder import exception
|
|
from cinder import test
|
|
from cinder import utils
|
|
from cinder.volume import configuration as conf
|
|
from cinder.volume.drivers.ibm import flashsystem_fc
|
|
from cinder.volume import utils as volume_utils
|
|
from cinder.volume import volume_types
|
|
|
|
|
|
class FlashSystemManagementSimulator(object):
|
|
def __init__(self):
|
|
# Default protocol is FC
|
|
self._protocol = 'FC'
|
|
self._volumes_list = {}
|
|
self._hosts_list = {}
|
|
self._mappings_list = {}
|
|
self._next_cmd_error = {
|
|
'lsnode': '',
|
|
'lssystem': '',
|
|
'lsmdiskgrp': ''
|
|
}
|
|
self._errors = {
|
|
# CMMVC50000 is a fake error which indicates that command has not
|
|
# got expected results. This error represents kinds of CLI errors.
|
|
'CMMVC50000': ('', 'CMMVC50000 The command can not be executed '
|
|
'successfully.')
|
|
}
|
|
|
|
@staticmethod
|
|
def _find_unused_id(d):
|
|
ids = []
|
|
for v in d.values():
|
|
ids.append(int(v['id']))
|
|
ids.sort()
|
|
for index, n in enumerate(ids):
|
|
if n > index:
|
|
return six.text_type(index)
|
|
return six.text_type(len(ids))
|
|
|
|
@staticmethod
|
|
def _is_invalid_name(name):
|
|
if re.match(r'^[a-zA-Z_][\w ._-]*$', name):
|
|
return False
|
|
return True
|
|
|
|
@staticmethod
|
|
def _cmd_to_dict(arg_list):
|
|
no_param_args = [
|
|
'bytes',
|
|
'force'
|
|
]
|
|
one_param_args = [
|
|
'delim',
|
|
'hbawwpn',
|
|
'host',
|
|
'iogrp',
|
|
'iscsiname',
|
|
'mdiskgrp',
|
|
'name',
|
|
'scsi',
|
|
'size',
|
|
'unit'
|
|
]
|
|
|
|
# All commands should begin with svcinfo or svctask
|
|
if arg_list[0] not in ('svcinfo', 'svctask') or len(arg_list) < 2:
|
|
raise exception.InvalidInput(reason=six.text_type(arg_list))
|
|
ret = {'cmd': arg_list[1]}
|
|
arg_list.pop(0)
|
|
|
|
skip = False
|
|
for i in range(1, len(arg_list)):
|
|
if skip:
|
|
skip = False
|
|
continue
|
|
if arg_list[i][0] == '-':
|
|
param = arg_list[i][1:]
|
|
if param in no_param_args:
|
|
ret[param] = True
|
|
elif param in one_param_args:
|
|
ret[param] = 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
|
|
|
|
@staticmethod
|
|
def _print_cmd_info(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), '')
|
|
|
|
@staticmethod
|
|
def _convert_units_bytes(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 six.text_type(num)
|
|
|
|
def _cmd_lshost(self, **kwargs):
|
|
"""lshost command.
|
|
|
|
svcinfo lshost -delim !
|
|
svcinfo lshost -delim ! <host>
|
|
"""
|
|
if 'obj' not in kwargs:
|
|
rows = []
|
|
rows.append(['id', 'name', 'port_count', 'iogrp_count', 'status'])
|
|
for host in self._hosts_list.values():
|
|
rows.append([host['id'], host['host_name'], '1', '1',
|
|
'degraded'])
|
|
if len(rows) > 1:
|
|
return self._print_cmd_info(rows=rows, **kwargs)
|
|
else:
|
|
return ('', '')
|
|
else:
|
|
host_name = kwargs['obj'].strip('\'\"')
|
|
if host_name not in self._hosts_list:
|
|
return self._errors['CMMVC50000']
|
|
host = self._hosts_list[host_name]
|
|
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', '1'])
|
|
rows.append(['status', 'degraded'])
|
|
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 'delim' in kwargs:
|
|
for index in range(len(rows)):
|
|
rows[index] = kwargs['delim'].join(rows[index])
|
|
|
|
return ('%s' % '\n'.join(rows), '')
|
|
|
|
def _cmd_lshostvdiskmap(self, **kwargs):
|
|
"""svcinfo lshostvdiskmap -delim ! <host_name>"""
|
|
|
|
if 'obj' not in kwargs:
|
|
return self._errors['CMMVC50000']
|
|
|
|
host_name = kwargs['obj'].strip('\'\"')
|
|
if host_name not in self._hosts_list:
|
|
return self._errors['CMMVC50000']
|
|
|
|
rows = []
|
|
rows.append(['id', 'name', 'SCSI_id', 'vdisk_id', 'vdisk_name',
|
|
'vdisk_UID'])
|
|
|
|
for mapping in self._mappings_list.values():
|
|
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['vdisk_UID']])
|
|
|
|
return self._print_cmd_info(rows=rows, **kwargs)
|
|
|
|
def _cmd_lsmdiskgrp(self, **kwargs):
|
|
"""svcinfo lsmdiskgrp -gui -bytes -delim ! <pool>"""
|
|
|
|
status = 'online'
|
|
if self._next_cmd_error['lsmdiskgrp'] == 'error':
|
|
self._next_cmd_error['lsmdiskgrp'] = ''
|
|
return self._errors['CMMVC50000']
|
|
|
|
if self._next_cmd_error['lsmdiskgrp'] == 'status=offline':
|
|
self._next_cmd_error['lsmdiskgrp'] = ''
|
|
status = 'offline'
|
|
|
|
rows = [None] * 2
|
|
rows[0] = ['id', 'status', 'mdisk_count', 'vdisk_count', 'capacity',
|
|
'free_capacity', 'virtual_capacity', 'used_capacity',
|
|
'real_capacity', 'encrypted', 'type', 'encrypt']
|
|
rows[1] = ['0', status, '1', '0', '3573412790272',
|
|
'3529432325160', '1693247906775', '277841182',
|
|
'38203734097', 'no', 'parent', 'no']
|
|
|
|
if kwargs['obj'] == 'mdiskgrp0':
|
|
row = rows[1]
|
|
else:
|
|
return self._errors['CMMVC50000']
|
|
|
|
objrows = []
|
|
for idx, val in enumerate(rows[0]):
|
|
objrows.append([val, row[idx]])
|
|
|
|
if 'delim' in kwargs:
|
|
for index in range(len(objrows)):
|
|
objrows[index] = kwargs['delim'].join(objrows[index])
|
|
|
|
return ('%s' % '\n'.join(objrows), '')
|
|
|
|
def _cmd_lsnode(self, **kwargs):
|
|
"""lsnode command.
|
|
|
|
svcinfo lsnode -delim !
|
|
svcinfo lsnode -delim ! <node>
|
|
"""
|
|
|
|
if self._protocol == 'FC' or self._protocol == 'both':
|
|
port_status = 'active'
|
|
else:
|
|
port_status = 'unconfigured'
|
|
|
|
rows1 = [None] * 7
|
|
rows1[0] = ['name', 'node1']
|
|
rows1[1] = ['port_id', '000000000000001']
|
|
rows1[2] = ['port_status', port_status]
|
|
rows1[3] = ['port_speed', '8Gb']
|
|
rows1[4] = ['port_id', '000000000000001']
|
|
rows1[5] = ['port_status', port_status]
|
|
rows1[6] = ['port_speed', '8Gb']
|
|
|
|
rows2 = [None] * 7
|
|
rows2[0] = ['name', 'node2']
|
|
rows2[1] = ['port_id', '000000000000002']
|
|
rows2[2] = ['port_status', port_status]
|
|
rows2[3] = ['port_speed', '8Gb']
|
|
rows2[4] = ['port_id', '000000000000002']
|
|
rows2[5] = ['port_status', port_status]
|
|
rows2[6] = ['port_speed', 'N/A']
|
|
|
|
rows3 = [None] * 3
|
|
rows3[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']
|
|
rows3[1] = ['1', 'node1', '', '0123456789ABCDEF', 'online', '0',
|
|
'io_grp0', 'yes', '', 'TR1', 'naa.0123456789ABCDEF', '',
|
|
'01-1', '1', '1', 'H441028']
|
|
rows3[2] = ['2', 'node2', '', '0123456789ABCDEF', 'online', '0',
|
|
'io_grp0', 'no', '', 'TR1', 'naa.0123456789ABCDEF', '',
|
|
'01-2', '1', '2', 'H441028']
|
|
|
|
if self._next_cmd_error['lsnode'] == 'error':
|
|
self._next_cmd_error['lsnode'] = ''
|
|
return self._errors['CMMVC50000']
|
|
|
|
rows = None
|
|
if 'obj' not in kwargs:
|
|
rows = rows3
|
|
elif kwargs['obj'] == '1':
|
|
rows = rows1
|
|
elif kwargs['obj'] == '2':
|
|
rows = rows2
|
|
else:
|
|
return self._errors['CMMVC50000']
|
|
|
|
if self._next_cmd_error['lsnode'] == 'header_mismatch':
|
|
rows[0].pop(2)
|
|
self._next_cmd_error['lsnode'] = ''
|
|
|
|
return self._print_cmd_info(rows=rows, delim=kwargs.get('delim', None))
|
|
|
|
def _cmd_lssystem(self, **kwargs):
|
|
"""svcinfo lssystem -delim !"""
|
|
|
|
open_access_enabled = 'off'
|
|
|
|
if self._next_cmd_error['lssystem'] == 'error':
|
|
self._next_cmd_error['lssystem'] = ''
|
|
return self._errors['CMMVC50000']
|
|
|
|
if self._next_cmd_error['lssystem'] == 'open_access_enabled=on':
|
|
self._next_cmd_error['lssystem'] = ''
|
|
open_access_enabled = 'on'
|
|
|
|
rows = [None] * 3
|
|
rows[0] = ['id', '0123456789ABCDEF']
|
|
rows[1] = ['name', 'flashsystem_1.2.3.4']
|
|
rows[2] = ['open_access_enabled', open_access_enabled]
|
|
|
|
return self._print_cmd_info(rows=rows, **kwargs)
|
|
|
|
def _cmd_lsportfc(self, **kwargs):
|
|
"""svcinfo lsportfc"""
|
|
|
|
if self._protocol == 'FC' or self._protocol == 'both':
|
|
status = 'active'
|
|
else:
|
|
status = 'unconfigured'
|
|
|
|
rows = [None] * 3
|
|
rows[0] = ['id', 'canister_id', 'adapter_id', 'port_id', 'type',
|
|
'port_speed', 'node_id', 'node_name', 'WWPN',
|
|
'nportid', 'status', 'attachment', 'topology']
|
|
rows[1] = ['0', '1', '1', '1', 'fc',
|
|
'8Gb', '1', 'node_1', 'AABBCCDDEEFF0011',
|
|
'000000', status, 'host', 'al']
|
|
rows[2] = ['1', '1', '1', '1', 'fc',
|
|
'8Gb', '1', 'node_1', 'AABBCCDDEEFF0010',
|
|
'000000', status, 'host', 'al']
|
|
return self._print_cmd_info(rows=rows, **kwargs)
|
|
|
|
def _cmd_lsportip(self, **kwargs):
|
|
"""svcinfo lsportip"""
|
|
|
|
if self._protocol == 'iSCSI' or self._protocol == 'both':
|
|
IP_address1 = '192.168.1.10'
|
|
IP_address2 = '192.168.1.11'
|
|
state = 'online'
|
|
speed = '8G'
|
|
else:
|
|
IP_address1 = ''
|
|
IP_address2 = ''
|
|
state = ''
|
|
speed = ''
|
|
|
|
rows = [None] * 3
|
|
rows[0] = ['id', 'node_id', 'node_name', 'canister_id', 'adapter_id',
|
|
'port_id', 'IP_address', 'mask', 'gateway', 'IP_address_6',
|
|
'prefix_6', 'gateway_6', 'MAC', 'duplex', 'state', 'speed',
|
|
'failover', 'link_state', 'host', 'host_6', 'vlan',
|
|
'vlan_6', 'adapter_location', 'adapter_port_id']
|
|
rows[1] = ['1', '1', 'node1', '0', '0',
|
|
'0', IP_address1, '', '', '',
|
|
'0', '', '11:22:33:44:55:AA', '', state, speed,
|
|
'no', 'active', '', '', '', '', '0', '0']
|
|
rows[2] = ['2', '2', 'node2', '0', '0',
|
|
'0', IP_address2, '', '', '',
|
|
'0', '', '11:22:33:44:55:BB', '', state, speed,
|
|
'no', 'active', '', '', '', '', '0', '0']
|
|
|
|
return self._print_cmd_info(rows=rows, **kwargs)
|
|
|
|
def _cmd_lsvdisk(self, **kwargs):
|
|
"""cmd: svcinfo lsvdisk -gui -bytes -delim ! <vdisk_name>"""
|
|
|
|
if 'obj' not in kwargs or (
|
|
'delim' not in kwargs) or (
|
|
'bytes' not in kwargs):
|
|
return self._errors['CMMVC50000']
|
|
|
|
if kwargs['obj'] not in self._volumes_list:
|
|
return self._errors['CMMVC50000']
|
|
|
|
vol = self._volumes_list[kwargs['obj']]
|
|
|
|
rows = []
|
|
rows.append(['id', vol['id']])
|
|
rows.append(['name', vol['name']])
|
|
rows.append(['status', vol['status']])
|
|
rows.append(['capacity', vol['capacity']])
|
|
rows.append(['vdisk_UID', vol['vdisk_UID']])
|
|
rows.append(['udid', ''])
|
|
rows.append(['open_access_scsi_id', '1'])
|
|
rows.append(['parent_mdisk_grp_id', '0'])
|
|
rows.append(['parent_mdisk_grp_name', 'mdiskgrp0'])
|
|
|
|
for index in range(len(rows)):
|
|
rows[index] = kwargs['delim'].join(rows[index])
|
|
return ('%s' % '\n'.join(rows), '')
|
|
|
|
def _cmd_lsvdiskhostmap(self, **kwargs):
|
|
"""svcinfo lsvdiskhostmap -delim ! <vdisk_name>"""
|
|
|
|
if 'obj' not in kwargs or (
|
|
'delim' not in kwargs):
|
|
return self._errors['CMMVC50000']
|
|
|
|
vdisk_name = kwargs['obj']
|
|
if vdisk_name not in self._volumes_list:
|
|
return self._errors['CMMVC50000']
|
|
|
|
rows = []
|
|
rows.append(['id', 'name', 'SCSI_id', 'host_id', 'host_name',
|
|
'vdisk_UID', 'IO_group_id', 'IO_group_name'])
|
|
|
|
mappings_found = 0
|
|
for mapping in self._mappings_list.values():
|
|
if (mapping['vol'] == vdisk_name):
|
|
mappings_found += 1
|
|
volume = self._volumes_list[mapping['vol']]
|
|
host = self._hosts_list[mapping['host']]
|
|
rows.append([volume['id'], volume['name'], '1', host['id'],
|
|
host['host_name'], volume['vdisk_UID'],
|
|
'0', 'mdiskgrp0'])
|
|
|
|
if mappings_found:
|
|
return self._print_cmd_info(rows=rows, **kwargs)
|
|
else:
|
|
return ('', '')
|
|
|
|
def _cmd_expandvdisksize(self, **kwargs):
|
|
"""svctask expandvdisksize -size <size> -unit gb <vdisk_name>"""
|
|
|
|
if 'obj' not in kwargs:
|
|
return self._errors['CMMVC50000']
|
|
vol_name = kwargs['obj'].strip('\'\"')
|
|
|
|
if 'size' not in kwargs:
|
|
return self._errors['CMMVC50000']
|
|
size = int(kwargs['size'])
|
|
|
|
if vol_name not in self._volumes_list:
|
|
return self._errors['CMMVC50000']
|
|
|
|
curr_size = int(self._volumes_list[vol_name]['capacity'])
|
|
addition = size * units.Gi
|
|
self._volumes_list[vol_name]['capacity'] = six.text_type(
|
|
curr_size + addition)
|
|
return ('', '')
|
|
|
|
def _cmd_mkvdisk(self, **kwargs):
|
|
"""mkvdisk command.
|
|
|
|
svctask mkvdisk -name <name> -mdiskgrp <mdiskgrp> -iogrp <iogrp>
|
|
-size <size> -unit <unit>
|
|
"""
|
|
|
|
if 'name' not in kwargs or (
|
|
'size' not in kwargs) or (
|
|
'unit' not in kwargs):
|
|
return self._errors['CMMVC50000']
|
|
|
|
vdisk_info = {}
|
|
vdisk_info['id'] = self._find_unused_id(self._volumes_list)
|
|
vdisk_info['name'] = kwargs['name'].strip('\'\"')
|
|
vdisk_info['status'] = 'online'
|
|
vdisk_info['capacity'] = self._convert_units_bytes(
|
|
int(kwargs['size']), kwargs['unit'])
|
|
vdisk_info['vdisk_UID'] = ('60050760') + ('0' * 14) + vdisk_info['id']
|
|
|
|
if vdisk_info['name'] in self._volumes_list:
|
|
return self._errors['CMMVC50000']
|
|
else:
|
|
self._volumes_list[vdisk_info['name']] = vdisk_info
|
|
return ('Virtual Disk, id [%s], successfully created' %
|
|
(vdisk_info['id']), '')
|
|
|
|
def _cmd_rmvdisk(self, **kwargs):
|
|
"""svctask rmvdisk -force <vdisk_name>"""
|
|
|
|
if 'obj' not in kwargs:
|
|
return self._errors['CMMVC50000']
|
|
|
|
vdisk_name = kwargs['obj'].strip('\'\"')
|
|
|
|
if vdisk_name not in self._volumes_list:
|
|
return self._errors['CMMVC50000']
|
|
|
|
del self._volumes_list[vdisk_name]
|
|
return ('', '')
|
|
|
|
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['CMMVC50000']
|
|
|
|
host_info[added_key].append(added_val)
|
|
|
|
for v in self._hosts_list.values():
|
|
if v['id'] == host_info['id']:
|
|
continue
|
|
for port in v[added_key]:
|
|
if port == added_val:
|
|
return self._errors['CMMVC50000']
|
|
return ('', '')
|
|
|
|
def _cmd_mkhost(self, **kwargs):
|
|
"""mkhost command.
|
|
|
|
svctask mkhost -force -hbawwpn <wwpn> -name <host_name>
|
|
svctask mkhost -force -iscsiname <initiator> -name <host_name>
|
|
"""
|
|
|
|
if 'name' not in kwargs:
|
|
return self._errors['CMMVC50000']
|
|
|
|
host_name = kwargs['name'].strip('\'\"')
|
|
if self._is_invalid_name(host_name):
|
|
return self._errors['CMMVC50000']
|
|
if host_name in self._hosts_list:
|
|
return self._errors['CMMVC50000']
|
|
|
|
host_info = {}
|
|
host_info['id'] = self._find_unused_id(self._hosts_list)
|
|
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)
|
|
|
|
def _cmd_addhostport(self, **kwargs):
|
|
"""addhostport command.
|
|
|
|
svctask addhostport -force -hbawwpn <wwpn> <host>
|
|
svctask addhostport -force -iscsiname <initiator> <host>
|
|
"""
|
|
|
|
if 'obj' not in kwargs:
|
|
return self._errors['CMMVC50000']
|
|
host_name = kwargs['obj'].strip('\'\"')
|
|
|
|
if host_name not in self._hosts_list:
|
|
return self._errors['CMMVC50000']
|
|
|
|
host_info = self._hosts_list[host_name]
|
|
return self._add_port_to_host(host_info, **kwargs)
|
|
|
|
def _cmd_rmhost(self, **kwargs):
|
|
"""svctask rmhost <host>"""
|
|
|
|
if 'obj' not in kwargs:
|
|
return self._errors['CMMVC50000']
|
|
|
|
host_name = kwargs['obj'].strip('\'\"')
|
|
if host_name not in self._hosts_list:
|
|
return self._errors['CMMVC50000']
|
|
|
|
for v in self._mappings_list.values():
|
|
if (v['host'] == host_name):
|
|
return self._errors['CMMVC50000']
|
|
|
|
del self._hosts_list[host_name]
|
|
return ('', '')
|
|
|
|
def _cmd_mkvdiskhostmap(self, **kwargs):
|
|
"""svctask mkvdiskhostmap -host <host> -scsi <lun> <vdisk_name>"""
|
|
|
|
mapping_info = {}
|
|
mapping_info['id'] = self._find_unused_id(self._mappings_list)
|
|
|
|
if 'host' not in kwargs or (
|
|
'scsi' not in kwargs) or (
|
|
'obj' not in kwargs):
|
|
return self._errors['CMMVC50000']
|
|
mapping_info['host'] = kwargs['host'].strip('\'\"')
|
|
mapping_info['lun'] = kwargs['scsi'].strip('\'\"')
|
|
mapping_info['vol'] = kwargs['obj'].strip('\'\"')
|
|
|
|
if mapping_info['vol'] not in self._volumes_list:
|
|
return self._errors['CMMVC50000']
|
|
|
|
if mapping_info['host'] not in self._hosts_list:
|
|
return self._errors['CMMVC50000']
|
|
|
|
if mapping_info['vol'] in self._mappings_list:
|
|
return self._errors['CMMVC50000']
|
|
|
|
for v in self._mappings_list.values():
|
|
if ((v['host'] == mapping_info['host']) and
|
|
(v['lun'] == mapping_info['lun'])):
|
|
return self._errors['CMMVC50000']
|
|
|
|
for v in self._mappings_list.values():
|
|
if (v['lun'] == mapping_info['lun']) and ('force' not in kwargs):
|
|
return self._errors['CMMVC50000']
|
|
|
|
self._mappings_list[mapping_info['id']] = mapping_info
|
|
return ('Virtual Disk to Host map, id [%s], successfully created'
|
|
% (mapping_info['id']), '')
|
|
|
|
def _cmd_rmvdiskhostmap(self, **kwargs):
|
|
"""svctask rmvdiskhostmap -host <host> <vdisk_name>"""
|
|
|
|
if 'host' not in kwargs or 'obj' not in kwargs:
|
|
return self._errors['CMMVC50000']
|
|
host = kwargs['host'].strip('\'\"')
|
|
vdisk = kwargs['obj'].strip('\'\"')
|
|
|
|
mapping_ids = []
|
|
for v in self._mappings_list.values():
|
|
if v['vol'] == vdisk:
|
|
mapping_ids.append(v['id'])
|
|
if not mapping_ids:
|
|
return self._errors['CMMVC50000']
|
|
|
|
this_mapping = None
|
|
for mapping_id in mapping_ids:
|
|
if self._mappings_list[mapping_id]['host'] == host:
|
|
this_mapping = mapping_id
|
|
if this_mapping is None:
|
|
return self._errors['CMMVC50000']
|
|
|
|
del self._mappings_list[this_mapping]
|
|
return ('', '')
|
|
|
|
def set_protocol(self, protocol):
|
|
self._protocol = protocol
|
|
|
|
def execute_command(self, cmd, check_exit_code=True):
|
|
try:
|
|
kwargs = self._cmd_to_dict(cmd)
|
|
except exception.InvalidInput:
|
|
return self._errors['CMMVC50000']
|
|
|
|
command = kwargs['cmd']
|
|
del kwargs['cmd']
|
|
func = getattr(self, '_cmd_' + command)
|
|
out, err = func(**kwargs)
|
|
|
|
if (check_exit_code) and (len(err) != 0):
|
|
raise processutils.ProcessExecutionError(exit_code=1,
|
|
stdout=out,
|
|
stderr=err,
|
|
cmd=command)
|
|
return (out, err)
|
|
|
|
def error_injection(self, cmd, error):
|
|
self._next_cmd_error[cmd] = error
|
|
|
|
|
|
class FlashSystemFakeDriver(flashsystem_fc.FlashSystemFCDriver):
|
|
def __init__(self, *args, **kwargs):
|
|
super(FlashSystemFakeDriver, self).__init__(*args, **kwargs)
|
|
|
|
def set_fake_storage(self, fake):
|
|
self.fake_storage = fake
|
|
|
|
def _ssh(self, cmd, check_exit_code=True):
|
|
utils.check_ssh_injection(cmd)
|
|
ret = self.fake_storage.execute_command(cmd, check_exit_code)
|
|
return ret
|
|
|
|
|
|
class FlashSystemDriverTestCase(test.TestCase):
|
|
|
|
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.items():
|
|
self._set_flag(k, v)
|
|
|
|
def _generate_vol_info(self,
|
|
vol_name,
|
|
vol_size=10,
|
|
vol_status='available'):
|
|
rand_id = six.text_type(random.randint(10000, 99999))
|
|
if not vol_name:
|
|
vol_name = 'test_volume%s' % rand_id
|
|
|
|
return {'name': vol_name,
|
|
'size': vol_size,
|
|
'id': '%s' % rand_id,
|
|
'volume_type_id': None,
|
|
'status': vol_status,
|
|
'mdisk_grp_name': 'mdiskgrp0'}
|
|
|
|
def _generate_snap_info(self,
|
|
vol_name,
|
|
vol_id,
|
|
vol_size,
|
|
vol_status,
|
|
snap_status='available'):
|
|
rand_id = six.text_type(random.randint(10000, 99999))
|
|
return {'name': 'test_snap_%s' % rand_id,
|
|
'id': rand_id,
|
|
'volume': {'name': vol_name,
|
|
'id': vol_id,
|
|
'size': vol_size,
|
|
'status': vol_status},
|
|
'volume_size': vol_size,
|
|
'status': snap_status,
|
|
'mdisk_grp_name': 'mdiskgrp0'}
|
|
|
|
def setUp(self):
|
|
super(FlashSystemDriverTestCase, self).setUp()
|
|
|
|
self._def_flags = {'san_ip': 'hostname',
|
|
'san_login': 'username',
|
|
'san_password': 'password',
|
|
'flashsystem_connection_protocol': 'FC',
|
|
'flashsystem_multihostmap_enabled': True}
|
|
|
|
self.connector = {
|
|
'host': 'flashsystem',
|
|
'wwnns': ['0123456789abcdef', '0123456789abcdeg'],
|
|
'wwpns': ['abcd000000000001', 'abcd000000000002'],
|
|
'initiator': 'iqn.123456'}
|
|
|
|
self.sim = FlashSystemManagementSimulator()
|
|
self.driver = FlashSystemFakeDriver(
|
|
configuration=conf.Configuration(None))
|
|
self.driver.set_fake_storage(self.sim)
|
|
|
|
self._reset_flags()
|
|
self.ctxt = context.get_admin_context()
|
|
self.driver.do_setup(None)
|
|
self.driver.check_for_setup_error()
|
|
|
|
self.sleeppatch = mock.patch('eventlet.greenthread.sleep')
|
|
self.sleeppatch.start()
|
|
|
|
def tearDown(self):
|
|
self.sleeppatch.stop()
|
|
super(FlashSystemDriverTestCase, self).tearDown()
|
|
|
|
def test_flashsystem_do_setup(self):
|
|
# case 1: cmd lssystem encounters error
|
|
self.sim.error_injection('lssystem', 'error')
|
|
self.assertRaises(exception.VolumeBackendAPIException,
|
|
self.driver.do_setup, None)
|
|
|
|
# case 2: open_access_enabled is not off
|
|
self.sim.error_injection('lssystem', 'open_access_enabled=on')
|
|
self.assertRaises(exception.VolumeBackendAPIException,
|
|
self.driver.do_setup, None)
|
|
|
|
# case 3: cmd lsmdiskgrp encounters error
|
|
self.sim.error_injection('lsmdiskgrp', 'error')
|
|
self.assertRaises(exception.InvalidInput,
|
|
self.driver.do_setup, None)
|
|
|
|
# case 4: status is not online
|
|
self.sim.error_injection('lsmdiskgrp', 'status=offline')
|
|
self.assertRaises(exception.InvalidInput,
|
|
self.driver.do_setup, None)
|
|
|
|
# case 5: cmd lsnode encounters error
|
|
self.sim.error_injection('lsnode', 'error')
|
|
self.assertRaises(processutils.ProcessExecutionError,
|
|
self.driver.do_setup, None)
|
|
|
|
# case 6: cmd lsnode header does not match
|
|
self.sim.error_injection('lsnode', 'header_mismatch')
|
|
self.assertRaises(exception.VolumeBackendAPIException,
|
|
self.driver.do_setup, None)
|
|
|
|
# case 7: set as FC
|
|
self.sim.set_protocol('FC')
|
|
self.driver.do_setup(None)
|
|
self.assertEqual('FC', self.driver._protocol)
|
|
|
|
# case 8: no configured nodes available
|
|
self.sim.set_protocol('unknown')
|
|
self.assertRaises(exception.VolumeBackendAPIException,
|
|
self.driver.do_setup, None)
|
|
|
|
# clear environment
|
|
self.sim.set_protocol('FC')
|
|
self.driver.do_setup(None)
|
|
|
|
def test_flashsystem_check_for_setup_error(self):
|
|
self._set_flag('san_ip', '')
|
|
self.assertRaises(exception.InvalidInput,
|
|
self.driver.check_for_setup_error)
|
|
self._reset_flags()
|
|
|
|
self._set_flag('san_ssh_port', '')
|
|
self.assertRaises(exception.InvalidInput,
|
|
self.driver.check_for_setup_error)
|
|
self._reset_flags()
|
|
|
|
self._set_flag('san_login', '')
|
|
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('flashsystem_connection_protocol', 'foo')
|
|
self.assertRaises(exception.InvalidInput,
|
|
self.driver.check_for_setup_error)
|
|
self._reset_flags()
|
|
|
|
# clear environment
|
|
self.driver.do_setup(None)
|
|
|
|
def test_flashsystem_validate_connector(self):
|
|
conn_neither = {'host': 'host'}
|
|
conn_iscsi = {'host': 'host', 'initiator': 'foo'}
|
|
conn_fc = {'host': 'host', 'wwpns': 'bar'}
|
|
conn_both = {'host': 'host', 'initiator': 'foo', 'wwpns': 'bar'}
|
|
|
|
protocol = self.driver._protocol
|
|
|
|
# case 1: when protocol is FC
|
|
self.driver._protocol = 'FC'
|
|
self.driver.validate_connector(conn_fc)
|
|
self.driver.validate_connector(conn_both)
|
|
self.assertRaises(exception.InvalidConnectorException,
|
|
self.driver.validate_connector, conn_iscsi)
|
|
self.assertRaises(exception.InvalidConnectorException,
|
|
self.driver.validate_connector, conn_neither)
|
|
|
|
# clear environment
|
|
self.driver._protocol = protocol
|
|
|
|
def test_flashsystem_volumes(self):
|
|
# case 1: create volume
|
|
vol = self._generate_vol_info(None)
|
|
self.driver.create_volume(vol)
|
|
|
|
# Check whether volume is created successfully
|
|
attributes = self.driver._get_vdisk_attributes(vol['name'])
|
|
attr_size = float(attributes['capacity']) / units.Gi
|
|
self.assertEqual(float(vol['size']), attr_size)
|
|
|
|
# case 2: create volume with empty returning value
|
|
with mock.patch.object(FlashSystemFakeDriver,
|
|
'_ssh') as mock_ssh:
|
|
mock_ssh.return_value = ("", "")
|
|
vol1 = self._generate_vol_info(None)
|
|
self.assertRaises(exception.VolumeBackendAPIException,
|
|
self.driver.create_volume, vol1)
|
|
|
|
# case 3: create volume with error returning value
|
|
with mock.patch.object(FlashSystemFakeDriver,
|
|
'_ssh') as mock_ssh:
|
|
mock_ssh.return_value = ("CMMVC6070E",
|
|
"An invalid or duplicated "
|
|
"parameter has been detected.")
|
|
vol2 = self._generate_vol_info(None)
|
|
self.assertRaises(exception.VolumeBackendAPIException,
|
|
self.driver.create_volume, vol2)
|
|
|
|
# case 4: delete volume
|
|
self.driver.delete_volume(vol)
|
|
|
|
# case 5: delete volume that doesn't exist (expected not fail)
|
|
vol_no_exist = self._generate_vol_info(None)
|
|
self.driver.delete_volume(vol_no_exist)
|
|
|
|
def test_flashsystem_extend_volume(self):
|
|
vol = self._generate_vol_info(None)
|
|
self.driver.create_volume(vol)
|
|
self.driver.extend_volume(vol, '200')
|
|
attrs = self.driver._get_vdisk_attributes(vol['name'])
|
|
vol_size = int(attrs['capacity']) / units.Gi
|
|
self.assertAlmostEqual(vol_size, 200)
|
|
|
|
# clear environment
|
|
self.driver.delete_volume(vol)
|
|
|
|
def test_flashsystem_connection(self):
|
|
# case 1: initialize_connection/terminate_connection for good path
|
|
vol1 = self._generate_vol_info(None)
|
|
self.driver.create_volume(vol1)
|
|
self.driver.initialize_connection(vol1, self.connector)
|
|
self.driver.terminate_connection(vol1, self.connector)
|
|
|
|
# case 2: when volume is not existed
|
|
vol2 = self._generate_vol_info(None)
|
|
self.assertRaises(exception.VolumeBackendAPIException,
|
|
self.driver.initialize_connection,
|
|
vol2, self.connector)
|
|
|
|
# case 3: _get_vdisk_map_properties raises exception
|
|
with mock.patch.object(flashsystem_fc.FlashSystemFCDriver,
|
|
'_get_vdisk_map_properties') as get_properties:
|
|
get_properties.side_effect = exception.VolumeBackendAPIException
|
|
self.assertRaises(exception.VolumeBackendAPIException,
|
|
self.driver.initialize_connection,
|
|
vol1, self.connector)
|
|
|
|
# case 4: terminate_connection with no host
|
|
with mock.patch.object(flashsystem_fc.FlashSystemFCDriver,
|
|
'_get_hostvdisk_mappings') as mock_host:
|
|
mock_host.return_value = {}
|
|
vol3 = self._generate_vol_info(None)
|
|
self.driver.create_volume(vol3)
|
|
self.driver.initialize_connection(vol3, self.connector)
|
|
return_value = self.driver.terminate_connection(vol3,
|
|
self.connector)
|
|
self.assertNotEqual({}, return_value['data'])
|
|
|
|
# case 5: terminate_connection with host
|
|
vol4 = self._generate_vol_info(None)
|
|
self.driver.create_volume(vol4)
|
|
self.driver.initialize_connection(vol4, self.connector)
|
|
vol5 = self._generate_vol_info(None)
|
|
self.driver.create_volume(vol5)
|
|
self.driver.initialize_connection(vol5, self.connector)
|
|
return_value = self.driver.terminate_connection(vol4,
|
|
self.connector)
|
|
self.assertEqual({}, return_value['data'])
|
|
|
|
# clear environment
|
|
self.driver.delete_volume(vol1)
|
|
self.driver.delete_volume(vol2)
|
|
self.driver.delete_volume(vol3)
|
|
self.driver.delete_volume(vol4)
|
|
self.driver.delete_volume(vol5)
|
|
|
|
@mock.patch.object(flashsystem_fc.FlashSystemFCDriver,
|
|
'_create_and_copy_vdisk_data')
|
|
def test_flashsystem_create_snapshot(self, _create_and_copy_vdisk_data):
|
|
# case 1: good path
|
|
vol1 = self._generate_vol_info(None)
|
|
snap1 = self._generate_snap_info(vol1['name'],
|
|
vol1['id'],
|
|
vol1['size'],
|
|
vol1['status'])
|
|
self.driver.create_snapshot(snap1)
|
|
|
|
# case 2: when volume status is error
|
|
vol2 = self._generate_vol_info(None, vol_status='error')
|
|
snap2 = self._generate_snap_info(vol2['name'],
|
|
vol2['id'],
|
|
vol2['size'],
|
|
vol2['status'])
|
|
self.assertRaises(exception.InvalidVolume,
|
|
self.driver.create_snapshot, snap2)
|
|
|
|
@mock.patch.object(flashsystem_fc.FlashSystemFCDriver,
|
|
'_delete_vdisk')
|
|
def test_flashsystem_delete_snapshot(self, _delete_vdisk):
|
|
vol1 = self._generate_vol_info(None)
|
|
snap1 = self._generate_snap_info(vol1['name'],
|
|
vol1['id'],
|
|
vol1['size'],
|
|
vol1['status'])
|
|
self.driver.delete_snapshot(snap1)
|
|
|
|
@mock.patch.object(flashsystem_fc.FlashSystemFCDriver,
|
|
'_create_and_copy_vdisk_data')
|
|
def test_flashsystem_create_volume_from_snapshot(
|
|
self, _create_and_copy_vdisk_data):
|
|
# case 1: good path
|
|
vol = self._generate_vol_info(None)
|
|
snap = self._generate_snap_info(vol['name'],
|
|
vol['id'],
|
|
vol['size'],
|
|
vol['status'])
|
|
self.driver.create_volume_from_snapshot(vol, snap)
|
|
|
|
# case 2: when size does not match
|
|
vol = self._generate_vol_info(None, vol_size=100)
|
|
snap = self._generate_snap_info(vol['name'],
|
|
vol['id'],
|
|
200,
|
|
vol['status'])
|
|
self.assertRaises(exception.VolumeDriverException,
|
|
self.driver.create_volume_from_snapshot,
|
|
vol, snap)
|
|
|
|
# case 3: when snapshot status is not available
|
|
vol = self._generate_vol_info(None)
|
|
snap = self._generate_snap_info(vol['name'],
|
|
vol['id'],
|
|
vol['size'],
|
|
vol['status'],
|
|
snap_status='error')
|
|
self.assertRaises(exception.InvalidSnapshot,
|
|
self.driver.create_volume_from_snapshot,
|
|
vol, snap)
|
|
|
|
@mock.patch.object(flashsystem_fc.FlashSystemFCDriver,
|
|
'_create_and_copy_vdisk_data')
|
|
def test_flashsystem_create_cloned_volume(
|
|
self, _create_and_copy_vdisk_data):
|
|
# case 1: good path
|
|
vol1 = self._generate_vol_info(None)
|
|
vol2 = self._generate_vol_info(None)
|
|
self.driver.create_cloned_volume(vol2, vol1)
|
|
|
|
# case 2: when size does not match
|
|
vol1 = self._generate_vol_info(None, vol_size=10)
|
|
vol2 = self._generate_vol_info(None, vol_size=20)
|
|
self.assertRaises(exception.VolumeDriverException,
|
|
self.driver.create_cloned_volume,
|
|
vol2, vol1)
|
|
|
|
def test_flashsystem_get_volume_stats(self):
|
|
# case 1: good path
|
|
self._set_flag('reserved_percentage', 25)
|
|
pool = 'mdiskgrp0'
|
|
backend_name = 'flashsystem_1.2.3.4' + '_' + pool
|
|
|
|
stats = self.driver.get_volume_stats()
|
|
|
|
self.assertEqual(25, stats['reserved_percentage'])
|
|
self.assertEqual('IBM', stats['vendor_name'])
|
|
self.assertEqual('FC', stats['storage_protocol'])
|
|
self.assertEqual(backend_name, stats['volume_backend_name'])
|
|
|
|
self._reset_flags()
|
|
|
|
# case 2: when lsmdiskgrp returns error
|
|
self.sim.error_injection('lsmdiskgrp', 'error')
|
|
self.assertRaises(exception.VolumeBackendAPIException,
|
|
self.driver.get_volume_stats, refresh=True)
|
|
|
|
@mock.patch.object(flashsystem_fc.FlashSystemFCDriver,
|
|
'_copy_vdisk_data')
|
|
def test_flashsystem_create_and_copy_vdisk_data(self, _copy_vdisk_data):
|
|
# case 1: when volume does not exist
|
|
vol1 = self._generate_vol_info(None)
|
|
vol2 = self._generate_vol_info(None)
|
|
self.assertRaises(exception.VolumeBackendAPIException,
|
|
self.driver._create_and_copy_vdisk_data,
|
|
vol1['name'], vol1['id'], vol2['name'], vol2['id'])
|
|
|
|
# case 2: good path
|
|
self.driver.create_volume(vol1)
|
|
self.driver._create_and_copy_vdisk_data(
|
|
vol1['name'], vol1['id'], vol2['name'], vol2['id'])
|
|
self.driver.delete_volume(vol1)
|
|
self.driver.delete_volume(vol2)
|
|
|
|
# case 3: _copy_vdisk_data raises exception
|
|
self.driver.create_volume(vol1)
|
|
_copy_vdisk_data.side_effect = exception.VolumeBackendAPIException
|
|
self.assertRaises(
|
|
exception.VolumeBackendAPIException,
|
|
self.driver._create_and_copy_vdisk_data,
|
|
vol1['name'], vol1['id'], vol2['name'], vol2['id'])
|
|
self.assertEqual(set(), self.driver._vdisk_copy_in_progress)
|
|
|
|
# clear environment
|
|
self.driver.delete_volume(vol1)
|
|
self.driver.delete_volume(vol2)
|
|
|
|
@mock.patch.object(volume_utils, 'copy_volume')
|
|
@mock.patch.object(flashsystem_fc.FlashSystemFCDriver, '_scan_device')
|
|
@mock.patch.object(flashsystem_fc.FlashSystemFCDriver, '_remove_device')
|
|
@mock.patch.object(utils, 'brick_get_connector_properties')
|
|
def test_flashsystem_copy_vdisk_data(self,
|
|
_connector,
|
|
_remove_device,
|
|
_scan_device,
|
|
copy_volume):
|
|
|
|
connector = _connector.return_value = self.connector
|
|
vol1 = self._generate_vol_info(None)
|
|
vol2 = self._generate_vol_info(None)
|
|
self.driver.create_volume(vol1)
|
|
self.driver.create_volume(vol2)
|
|
|
|
# case 1: no mapped before copy
|
|
self.driver._copy_vdisk_data(
|
|
vol1['name'], vol1['id'], vol2['name'], vol2['id'])
|
|
(v1_mapped, lun) = self.driver._is_vdisk_map(vol1['name'], connector)
|
|
(v2_mapped, lun) = self.driver._is_vdisk_map(vol2['name'], connector)
|
|
self.assertFalse(v1_mapped)
|
|
self.assertFalse(v2_mapped)
|
|
|
|
# case 2: mapped before copy
|
|
self.driver.initialize_connection(vol1, connector)
|
|
self.driver.initialize_connection(vol2, connector)
|
|
self.driver._copy_vdisk_data(
|
|
vol1['name'], vol1['id'], vol2['name'], vol2['id'])
|
|
(v1_mapped, lun) = self.driver._is_vdisk_map(vol1['name'], connector)
|
|
(v2_mapped, lun) = self.driver._is_vdisk_map(vol2['name'], connector)
|
|
self.assertTrue(v1_mapped)
|
|
self.assertTrue(v2_mapped)
|
|
self.driver.terminate_connection(vol1, connector)
|
|
self.driver.terminate_connection(vol2, connector)
|
|
|
|
# case 3: no mapped before copy, raise exception when scan
|
|
_scan_device.side_effect = exception.VolumeBackendAPIException
|
|
self.assertRaises(
|
|
exception.VolumeBackendAPIException,
|
|
self.driver._copy_vdisk_data,
|
|
vol1['name'], vol1['id'], vol2['name'], vol2['id'])
|
|
(v1_mapped, lun) = self.driver._is_vdisk_map(vol1['name'], connector)
|
|
(v2_mapped, lun) = self.driver._is_vdisk_map(vol2['name'], connector)
|
|
self.assertFalse(v1_mapped)
|
|
self.assertFalse(v2_mapped)
|
|
|
|
# case 4: no mapped before copy, raise exception when copy
|
|
copy_volume.side_effect = exception.VolumeBackendAPIException
|
|
self.assertRaises(
|
|
exception.VolumeBackendAPIException,
|
|
self.driver._copy_vdisk_data,
|
|
vol1['name'], vol1['id'], vol2['name'], vol2['id'])
|
|
(v1_mapped, lun) = self.driver._is_vdisk_map(vol1['name'], connector)
|
|
(v2_mapped, lun) = self.driver._is_vdisk_map(vol2['name'], connector)
|
|
self.assertFalse(v1_mapped)
|
|
self.assertFalse(v2_mapped)
|
|
|
|
# clear environment
|
|
self.driver.delete_volume(vol1)
|
|
self.driver.delete_volume(vol2)
|
|
|
|
def test_flashsystem_connector_to_hostname_prefix(self):
|
|
# Invalid characters will be translated to '-'
|
|
|
|
# case 1: host name is unicode with invalid characters
|
|
conn = {'host': u'unicode.test}.abc{.abc'}
|
|
self.assertEqual(u'unicode.test-.abc-.abc',
|
|
self.driver._connector_to_hostname_prefix(conn))
|
|
|
|
# case 2: host name is string with invalid characters
|
|
conn = {'host': 'string.test}.abc{.abc'}
|
|
self.assertEqual('string.test-.abc-.abc',
|
|
self.driver._connector_to_hostname_prefix(conn))
|
|
|
|
# case 3: host name is neither unicode nor string
|
|
conn = {'host': 12345}
|
|
self.assertRaises(exception.NoValidHost,
|
|
self.driver._connector_to_hostname_prefix,
|
|
conn)
|
|
|
|
# case 4: host name started with number will be translated
|
|
conn = {'host': '192.168.1.1'}
|
|
self.assertEqual('_192.168.1.1',
|
|
self.driver._connector_to_hostname_prefix(conn))
|
|
|
|
def test_flashsystem_create_host(self):
|
|
# case 1: create host
|
|
conn = {
|
|
'host': 'flashsystem',
|
|
'wwnns': ['0123456789abcdef', '0123456789abcdeg'],
|
|
'wwpns': ['abcd000000000001', 'abcd000000000002'],
|
|
'initiator': 'iqn.123456'}
|
|
host = self.driver._create_host(conn)
|
|
|
|
# case 2: create host that already exists
|
|
self.assertRaises(processutils.ProcessExecutionError,
|
|
self.driver._create_host,
|
|
conn)
|
|
|
|
# case 3: delete host
|
|
self.driver._delete_host(host)
|
|
|
|
# case 4: create host with empty ports
|
|
conn = {'host': 'flashsystem', 'wwpns': []}
|
|
self.assertRaises(exception.VolumeBackendAPIException,
|
|
self.driver._create_host,
|
|
conn)
|
|
|
|
def test_flashsystem_find_host_exhaustive(self):
|
|
# case 1: create host and find it
|
|
conn1 = {
|
|
'host': 'flashsystem-01',
|
|
'wwnns': ['1111111111abcdef', '1111111111abcdeg'],
|
|
'wwpns': ['1111111111000001', '1111111111000002'],
|
|
'initiator': 'iqn.111111'}
|
|
conn2 = {
|
|
'host': 'flashsystem-02',
|
|
'wwnns': ['2222222222abcdef', '2222222222abcdeg'],
|
|
'wwpns': ['2222222222000001', '2222222222000002'],
|
|
'initiator': 'iqn.222222'}
|
|
conn3 = {
|
|
'host': 'flashsystem-03',
|
|
'wwnns': ['3333333333abcdef', '3333333333abcdeg'],
|
|
'wwpns': ['3333333333000001', '3333333333000002'],
|
|
'initiator': 'iqn.333333'}
|
|
host1 = self.driver._create_host(conn1)
|
|
host2 = self.driver._create_host(conn2)
|
|
self.assertEqual(
|
|
host2,
|
|
self.driver._find_host_exhaustive(conn2, [host1, host2]))
|
|
self.assertIsNone(self.driver._find_host_exhaustive(conn3,
|
|
[host1, host2]))
|
|
|
|
# clear environment
|
|
self.driver._delete_host(host1)
|
|
self.driver._delete_host(host2)
|
|
|
|
def test_flashsystem_get_vdisk_params(self):
|
|
# case 1: use default params
|
|
self.driver._get_vdisk_params(None)
|
|
|
|
# case 2: use extra params from type
|
|
opts1 = {'storage_protocol': 'FC'}
|
|
opts2 = {'capabilities:storage_protocol': 'FC'}
|
|
opts3 = {'storage_protocol': 'iSCSI'}
|
|
type1 = volume_types.create(self.ctxt, 'opts1', opts1)
|
|
type2 = volume_types.create(self.ctxt, 'opts2', opts2)
|
|
type3 = volume_types.create(self.ctxt, 'opts3', opts3)
|
|
self.assertEqual(
|
|
'FC',
|
|
self.driver._get_vdisk_params(type1['id'])['protocol'])
|
|
self.assertEqual(
|
|
'FC',
|
|
self.driver._get_vdisk_params(type2['id'])['protocol'])
|
|
self.assertRaises(exception.InvalidInput,
|
|
self.driver._get_vdisk_params,
|
|
type3['id'])
|
|
|
|
# clear environment
|
|
volume_types.destroy(self.ctxt, type1['id'])
|
|
volume_types.destroy(self.ctxt, type2['id'])
|
|
|
|
def test_flashsystem_map_vdisk_to_host(self):
|
|
# case 1: no host found
|
|
vol1 = self._generate_vol_info(None)
|
|
self.driver.create_volume(vol1)
|
|
self.assertEqual(
|
|
# lun id shoud begin with 1
|
|
1,
|
|
self.driver._map_vdisk_to_host(vol1['name'], self.connector))
|
|
|
|
# case 2: host already exists
|
|
vol2 = self._generate_vol_info(None)
|
|
self.driver.create_volume(vol2)
|
|
self.assertEqual(
|
|
# lun id shoud be sequential
|
|
2,
|
|
self.driver._map_vdisk_to_host(vol2['name'], self.connector))
|
|
|
|
# case 3: test if already mapped
|
|
self.assertEqual(
|
|
1,
|
|
self.driver._map_vdisk_to_host(vol1['name'], self.connector))
|
|
|
|
# clean environment
|
|
self.driver._unmap_vdisk_from_host(vol1['name'], self.connector)
|
|
self.driver._unmap_vdisk_from_host(vol2['name'], self.connector)
|
|
self.driver.delete_volume(vol1)
|
|
self.driver.delete_volume(vol2)
|
|
|
|
# case 4: If there is no vdisk mapped to host, host should be removed
|
|
self.assertIsNone(self.driver._get_host_from_connector(self.connector))
|