Support Cinder FC driver for TOYOU NetStor
* Supported Protocol - iSCSI - FC * Supported Feature - Volume Attach/Detach(FC) - Extend Attached Volume - Volume Manage/Unmanage - Revert to Snapshot - Multi-attach - Thin Provisioning ThirdPartySystems: TOYOU ACS5000 CI Change-Id: Id9bd2f880ea92e9f74ba286a1cb25aea174328c5
This commit is contained in:
parent
de6d54108b
commit
2bdc086783
File diff suppressed because it is too large
Load Diff
@ -19,6 +19,7 @@ It will be called by iSCSI driver
|
||||
"""
|
||||
|
||||
import json
|
||||
import math
|
||||
import random
|
||||
|
||||
from eventlet import greenthread
|
||||
@ -53,7 +54,12 @@ acs5000c_opts = [
|
||||
min=3,
|
||||
max=100,
|
||||
help='When volume copy task is going on,refresh volume '
|
||||
'status interval')
|
||||
'status interval'),
|
||||
cfg.BoolOpt(
|
||||
'acs5000_multiattach',
|
||||
default=False,
|
||||
help='Enable to allow volumes attaching to multiple '
|
||||
'hosts with no limit.'),
|
||||
]
|
||||
CONF = cfg.CONF
|
||||
CONF.register_opts(acs5000c_opts)
|
||||
@ -78,6 +84,7 @@ class Command(object):
|
||||
|
||||
def run_ssh_info(self, ssh_cmd, key=False):
|
||||
"""Run an SSH command and return parsed output."""
|
||||
ssh_cmd.insert(0, 'cinder')
|
||||
out, err = self._run_ssh(ssh_cmd)
|
||||
if len(err):
|
||||
msg = (_('Execute command %(cmd)s failed, '
|
||||
@ -134,35 +141,43 @@ class Command(object):
|
||||
return info['arr']
|
||||
|
||||
def get_system(self):
|
||||
ssh_cmd = ['cinder', 'Storage', 'sshGetSystem']
|
||||
ssh_cmd = ['get_system']
|
||||
return self.run_ssh_info(ssh_cmd)
|
||||
|
||||
def get_ip_connect(self):
|
||||
ssh_cmd = ['cinder',
|
||||
'Storage',
|
||||
'sshGetIpConnect']
|
||||
return self.run_ssh_info(ssh_cmd)
|
||||
def ls_iscsi(self):
|
||||
ssh_cmd = ['ls_iscsi']
|
||||
ports = self.run_ssh_info(ssh_cmd)
|
||||
up_ports = []
|
||||
for port in ports:
|
||||
if 'link' in port and port['link'] != 'Down':
|
||||
up_ports.append(up_ports)
|
||||
return up_ports
|
||||
|
||||
def get_pool_info(self, pool):
|
||||
ssh_cmd = ['cinder',
|
||||
'Storage',
|
||||
'sshGetPoolInfo',
|
||||
'--poolName',
|
||||
def ls_fc(self):
|
||||
ssh_cmd = ['ls_fc']
|
||||
ports = self.run_ssh_info(ssh_cmd)
|
||||
up_ports = []
|
||||
for port in ports:
|
||||
if 'link' in port and port['link'] == 'Up':
|
||||
up_ports.append(port)
|
||||
return up_ports
|
||||
|
||||
def get_pool(self, pool):
|
||||
ssh_cmd = ['get_pool',
|
||||
'--pool',
|
||||
pool]
|
||||
return self.run_ssh_info(ssh_cmd)
|
||||
|
||||
def get_volume(self, volume):
|
||||
ssh_cmd = ['cinder',
|
||||
'Storage',
|
||||
'sshGetVolume']
|
||||
ssh_cmd = ['get_volume']
|
||||
if not volume:
|
||||
return []
|
||||
elif isinstance(volume, str):
|
||||
ssh_cmd.append('--name')
|
||||
ssh_cmd.append('--volume')
|
||||
ssh_cmd.append(volume)
|
||||
elif isinstance(volume, list):
|
||||
for vol in volume:
|
||||
ssh_cmd.append('--name')
|
||||
ssh_cmd.append('--volume')
|
||||
ssh_cmd.append(vol)
|
||||
result = self.run_ssh_info(ssh_cmd)
|
||||
if not result:
|
||||
@ -170,83 +185,63 @@ class Command(object):
|
||||
else:
|
||||
return result
|
||||
|
||||
def ls_ctr_info(self):
|
||||
ssh_cmd = ['cinder', 'Storage', 'sshGetCtrInfo']
|
||||
def ls_controller(self):
|
||||
ssh_cmd = ['ls_controller']
|
||||
ctrs = self.run_ssh_info(ssh_cmd)
|
||||
nodes = {}
|
||||
for node_data in ctrs:
|
||||
nodes[node_data['id']] = {
|
||||
'id': node_data['id'],
|
||||
'id': int(node_data['id']),
|
||||
'name': node_data['name'],
|
||||
'iscsi_name': node_data['iscsi_name'],
|
||||
'WWNN': node_data['WWNN'],
|
||||
'WWPN': [],
|
||||
'status': node_data['status'],
|
||||
'ipv4': [],
|
||||
'ipv6': [],
|
||||
'enabled_protocols': []
|
||||
'status': node_data['status']
|
||||
}
|
||||
return nodes
|
||||
|
||||
def create_volume(self, name, size, pool_name, type='0'):
|
||||
ssh_cmd = ['cinder',
|
||||
'Storage',
|
||||
'sshCreateVolume',
|
||||
'--volumesize',
|
||||
ssh_cmd = ['create_volume',
|
||||
'--size',
|
||||
size,
|
||||
'--volumename',
|
||||
'--volume',
|
||||
name,
|
||||
'--cinderPool',
|
||||
'--pool',
|
||||
pool_name,
|
||||
'--type',
|
||||
type]
|
||||
return self.run_ssh_info(ssh_cmd, key=True)
|
||||
|
||||
def delete_volume(self, volume):
|
||||
ssh_cmd = ['cinder',
|
||||
'Storage',
|
||||
'sshDeleteVolume',
|
||||
'--cinderVolume',
|
||||
ssh_cmd = ['delete_volume',
|
||||
'--volume',
|
||||
volume]
|
||||
return self.run_ssh_info(ssh_cmd)
|
||||
|
||||
def extend_volume(self, volume, size):
|
||||
ssh_cmd = ['cinder',
|
||||
'Storage',
|
||||
'sshCinderExtendVolume',
|
||||
'--cinderVolume',
|
||||
ssh_cmd = ['extend_volume',
|
||||
'--volume',
|
||||
volume,
|
||||
'--extendunit',
|
||||
'gb',
|
||||
'--extendsize',
|
||||
'--size',
|
||||
str(size)]
|
||||
return self.run_ssh_info(ssh_cmd, key=True)
|
||||
|
||||
def create_clone(self, volume_name, clone_name):
|
||||
ssh_cmd = ['cinder',
|
||||
'Storage',
|
||||
'sshMkLocalClone',
|
||||
'--cinderVolume',
|
||||
ssh_cmd = ['create_clone',
|
||||
'--volume',
|
||||
volume_name,
|
||||
'--cloneVolume',
|
||||
'--clone',
|
||||
clone_name]
|
||||
return self.run_ssh_info(ssh_cmd, key=True)
|
||||
|
||||
def start_clone(self, volume_name, snapshot=''):
|
||||
ssh_cmd = ['cinder',
|
||||
'Storage',
|
||||
'sshMkStartLocalClone',
|
||||
'--cinderVolume',
|
||||
ssh_cmd = ['start_clone',
|
||||
'--volume',
|
||||
volume_name,
|
||||
'--snapshot',
|
||||
snapshot]
|
||||
return self.run_ssh_info(ssh_cmd, key=True)
|
||||
|
||||
def delete_clone(self, volume_name, snapshot=''):
|
||||
ssh_cmd = ['cinder',
|
||||
'Storage',
|
||||
'sshRemoveLocalClone',
|
||||
'--name',
|
||||
ssh_cmd = ['delete_clone',
|
||||
'--volume',
|
||||
volume_name,
|
||||
'--snapshot',
|
||||
snapshot]
|
||||
@ -255,10 +250,8 @@ class Command(object):
|
||||
def create_lun_map(self, volume_name, protocol, host):
|
||||
"""Map volume to host."""
|
||||
LOG.debug('enter: create_lun_map volume %s.', volume_name)
|
||||
ssh_cmd = ['cinder',
|
||||
'Storage',
|
||||
'sshMapVoltoHost',
|
||||
'--cinderVolume',
|
||||
ssh_cmd = ['create_lun_map',
|
||||
'--volume',
|
||||
volume_name,
|
||||
'--protocol',
|
||||
protocol]
|
||||
@ -272,26 +265,22 @@ class Command(object):
|
||||
return self.run_ssh_info(ssh_cmd, key=True)
|
||||
|
||||
def delete_lun_map(self, volume_name, protocol, host):
|
||||
ssh_cmd = ['cinder',
|
||||
'Storage',
|
||||
'sshDeleteLunMap',
|
||||
'--cinderVolume',
|
||||
ssh_cmd = ['delete_lun_map',
|
||||
'--volume',
|
||||
volume_name,
|
||||
'--protocol',
|
||||
protocol]
|
||||
if isinstance(host, list):
|
||||
for ht in host:
|
||||
ssh_cmd.append('--cinderHost')
|
||||
ssh_cmd.append('--host')
|
||||
ssh_cmd.append(ht)
|
||||
else:
|
||||
ssh_cmd.append('--cinderHost')
|
||||
ssh_cmd.append('--host')
|
||||
ssh_cmd.append(str(host))
|
||||
return self.run_ssh_info(ssh_cmd, key=True)
|
||||
|
||||
def create_snapshot(self, volume_name, snapshot_name):
|
||||
ssh_cmd = ['cinder',
|
||||
'Storage',
|
||||
'sshCreateSnapshot',
|
||||
ssh_cmd = ['create_snapshot',
|
||||
'--volume',
|
||||
volume_name,
|
||||
'--snapshot',
|
||||
@ -299,19 +288,23 @@ class Command(object):
|
||||
return self.run_ssh_info(ssh_cmd, key=True)
|
||||
|
||||
def delete_snapshot(self, volume_name, snapshot_name):
|
||||
ssh_cmd = ['cinder',
|
||||
'Storage',
|
||||
'sshDeleteSnapshot',
|
||||
ssh_cmd = ['delete_snapshot',
|
||||
'--volume',
|
||||
volume_name,
|
||||
'--snapshot',
|
||||
snapshot_name]
|
||||
return self.run_ssh_info(ssh_cmd, key=True)
|
||||
|
||||
def rollback_snapshot(self, snapshot_name, volume_name=''):
|
||||
ssh_cmd = ['rollback_snapshot',
|
||||
'--snapshot',
|
||||
snapshot_name,
|
||||
'--volume',
|
||||
volume_name]
|
||||
return self.run_ssh_info(ssh_cmd, key=True)
|
||||
|
||||
def set_volume_property(self, name, setting):
|
||||
ssh_cmd = ['cinder',
|
||||
'Storage',
|
||||
'sshSetVolumeProperty',
|
||||
ssh_cmd = ['set_volume',
|
||||
'--volume',
|
||||
name]
|
||||
for key, value in setting.items():
|
||||
@ -340,7 +333,7 @@ class Acs5000CommonDriver(san.SanDriver,
|
||||
self.pools = self.configuration.acs5000_volpool_name
|
||||
self._cmd = Command(self._run_ssh)
|
||||
self.protocol = None
|
||||
self._state = {'storage_nodes': {},
|
||||
self._state = {'controller': {},
|
||||
'enabled_protocols': set(),
|
||||
'system_name': None,
|
||||
'system_id': None,
|
||||
@ -361,27 +354,53 @@ class Acs5000CommonDriver(san.SanDriver,
|
||||
|
||||
self._state.update(self._cmd.get_system())
|
||||
|
||||
self._state['storage_nodes'] = self._cmd.ls_ctr_info()
|
||||
ports = self._cmd.get_ip_connect()
|
||||
self._state['controller'] = self._cmd.ls_controller()
|
||||
if self.protocol == 'FC':
|
||||
ports = self._cmd.ls_fc()
|
||||
else:
|
||||
ports = self._cmd.ls_iscsi()
|
||||
if len(ports) > 0:
|
||||
self._state['enabled_protocols'].add('iSCSI')
|
||||
for node in self._state['storage_nodes'].values():
|
||||
if node['id'] in ports.keys():
|
||||
node['enabled_protocols'].append('iSCSI')
|
||||
for port in ports[node['id']]:
|
||||
node['ipv4'].append(port['ip'])
|
||||
return
|
||||
self._state['enabled_protocols'].add(self.protocol)
|
||||
|
||||
def _validate_pools_exist(self):
|
||||
LOG.debug('_validate_pools_exist. '
|
||||
'pools: %s', ' '.join(self.pools))
|
||||
for pool in self.pools:
|
||||
pool_data = self._cmd.get_pool_info(pool)
|
||||
pool_data = self._cmd.get_pool(pool)
|
||||
if not pool_data:
|
||||
msg = _('Failed getting details for pool %s.') % pool
|
||||
raise exception.InvalidInput(reason=msg)
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def _convert_name(name):
|
||||
if len(name) >= 12:
|
||||
suffix = name[-12:]
|
||||
elif len(name) > 0:
|
||||
suffix = str(name).zfill(12)
|
||||
else:
|
||||
suffix = str(random.randint(0, 999999)).zfill(12)
|
||||
return VOLUME_PREFIX + suffix
|
||||
|
||||
@staticmethod
|
||||
def _check_multi_attached(volume, connector):
|
||||
# In the case of multi-attach, these VMs belong to the same host.
|
||||
# The mapping action only happens once.
|
||||
# If the only mapping relationship is cancelled,
|
||||
# volume on other VMs cannot be read or written.
|
||||
if not connector or 'uuid' not in connector:
|
||||
return 0
|
||||
attached_count = 0
|
||||
uuid = connector['uuid']
|
||||
for ref in volume.volume_attachment:
|
||||
ref_connector = {}
|
||||
if 'connector' in ref and ref.connector:
|
||||
# ref.connector may be None
|
||||
ref_connector = ref.connector
|
||||
if 'uuid' in ref_connector and uuid == ref_connector['uuid']:
|
||||
attached_count += 1
|
||||
return attached_count
|
||||
|
||||
@volume_utils.trace_method
|
||||
def check_for_setup_error(self):
|
||||
"""Ensure that the params are set properly."""
|
||||
@ -391,8 +410,8 @@ class Acs5000CommonDriver(san.SanDriver,
|
||||
if self._state['system_id'] is None:
|
||||
exception_msg = _('Unable to determine system id.')
|
||||
raise exception.VolumeBackendAPIException(data=exception_msg)
|
||||
if len(self._state['storage_nodes']) != 2:
|
||||
msg = _('do_setup: No configured nodes.')
|
||||
if len(self._state['controller']) != 2:
|
||||
msg = _('do_setup: The dual controller status is incorrect.')
|
||||
LOG.error(msg)
|
||||
raise exception.VolumeDriverException(message=msg)
|
||||
if self.protocol not in self._state['enabled_protocols']:
|
||||
@ -468,7 +487,7 @@ class Acs5000CommonDriver(san.SanDriver,
|
||||
|
||||
def create_volume(self, volume):
|
||||
LOG.debug('create_volume, volume %s.', volume['id'])
|
||||
volume_name = VOLUME_PREFIX + volume['id'][-12:]
|
||||
volume_name = self._convert_name(volume.name)
|
||||
pool_name = volume_utils.extract_host(volume['host'], 'pool')
|
||||
ret = self._cmd.create_volume(
|
||||
volume_name,
|
||||
@ -492,16 +511,22 @@ class Acs5000CommonDriver(san.SanDriver,
|
||||
elif ret['key'] == 308:
|
||||
raise exception.VolumeLimitExceeded(allowed=4096,
|
||||
name=volume_name)
|
||||
model_update = None
|
||||
return model_update
|
||||
elif ret['key'] != 0:
|
||||
msg = (_('Failed to create_volume %(vol)s on pool %(pool)s, '
|
||||
'code=%(ret)s, error=%(msg)s.') % {'vol': volume_name,
|
||||
'pool': pool_name,
|
||||
'ret': ret['key'],
|
||||
'msg': ret['msg']})
|
||||
raise exception.VolumeBackendAPIException(data=msg)
|
||||
return None
|
||||
|
||||
def delete_volume(self, volume):
|
||||
volume_name = VOLUME_PREFIX + volume['id'][-12:]
|
||||
volume_name = self._convert_name(volume.name)
|
||||
self._cmd.delete_volume(volume_name)
|
||||
|
||||
def create_snapshot(self, snapshot):
|
||||
volume_name = VOLUME_PREFIX + snapshot['volume_name'][-12:]
|
||||
snapshot_name = VOLUME_PREFIX + snapshot['name'][-12:]
|
||||
volume_name = self._convert_name(snapshot.volume_name)
|
||||
snapshot_name = self._convert_name(snapshot.name)
|
||||
ret = self._cmd.create_snapshot(volume_name, snapshot_name)
|
||||
if ret['key'] == 303:
|
||||
raise exception.VolumeNotFound(volume_id=volume_name)
|
||||
@ -509,18 +534,32 @@ class Acs5000CommonDriver(san.SanDriver,
|
||||
raise exception.SnapshotLimitExceeded(allowed=4096)
|
||||
elif ret['key'] == 504:
|
||||
raise exception.SnapshotLimitExceeded(allowed=64)
|
||||
elif ret['key'] != 0:
|
||||
msg = (_('Failed to create_snapshot %(snap)s on volume %(vol)s '
|
||||
'code=%(ret)s, error=%(msg)s.') % {'vol': volume_name,
|
||||
'snap': snapshot_name,
|
||||
'ret': ret['key'],
|
||||
'msg': ret['msg']})
|
||||
raise exception.VolumeBackendAPIException(data=msg)
|
||||
|
||||
def delete_snapshot(self, snapshot):
|
||||
volume_name = VOLUME_PREFIX + snapshot['volume_name'][-12:]
|
||||
snapshot_name = VOLUME_PREFIX + snapshot['name'][-12:]
|
||||
volume_name = self._convert_name(snapshot.volume_name)
|
||||
snapshot_name = self._convert_name(snapshot.name)
|
||||
ret = self._cmd.delete_snapshot(volume_name, snapshot_name)
|
||||
if ret['key'] == 505:
|
||||
raise exception.SnapshotNotFound(snapshot_id=snapshot['id'])
|
||||
elif ret['key'] != 0:
|
||||
msg = (_('Failed to delete_snapshot %(snap)s on volume %(vol)s '
|
||||
'code=%(ret)s, error=%(msg)s.') % {'vol': volume_name,
|
||||
'snap': snapshot_name,
|
||||
'ret': ret['key'],
|
||||
'msg': ret['msg']})
|
||||
raise exception.VolumeBackendAPIException(data=msg)
|
||||
|
||||
def create_volume_from_snapshot(self, volume, snapshot):
|
||||
snapshot_name = VOLUME_PREFIX + snapshot['name'][-12:]
|
||||
volume_name = VOLUME_PREFIX + volume['id'][-12:]
|
||||
source_volume = VOLUME_PREFIX + snapshot['volume_name'][-12:]
|
||||
snapshot_name = self._convert_name(snapshot.name)
|
||||
volume_name = self._convert_name(volume.name)
|
||||
source_volume = self._convert_name(snapshot.volume_name)
|
||||
pool = volume_utils.extract_host(volume['host'], 'pool')
|
||||
self._cmd.create_volume(volume_name,
|
||||
str(volume['size']),
|
||||
@ -530,9 +569,32 @@ class Acs5000CommonDriver(san.SanDriver,
|
||||
'create_volume_from_snapshot',
|
||||
snapshot_name)
|
||||
|
||||
def snapshot_revert_use_temp_snapshot(self):
|
||||
return False
|
||||
|
||||
@volume_utils.trace
|
||||
def revert_to_snapshot(self, context, volume, snapshot):
|
||||
volume_name = self._convert_name(volume.name)
|
||||
snapshot_name = self._convert_name(snapshot.name)
|
||||
ret = self._cmd.rollback_snapshot(snapshot_name, volume_name)
|
||||
if ret['key'] == 303:
|
||||
raise exception.VolumeNotFound(volume_id=volume_name)
|
||||
elif ret['key'] == 505:
|
||||
raise exception.SnapshotNotFound(snapshot_id=snapshot_name)
|
||||
elif ret['key'] == 506:
|
||||
msg = (_('Snapshot %s is not the latest one.') % snapshot_name)
|
||||
raise exception.InvalidSnapshot(reason=msg)
|
||||
elif ret['key'] != 0:
|
||||
msg = (_('Failed to revert volume %(vol)s to snapshot %(snap)s, '
|
||||
'code=%(ret)s, error=%(msg)s.') % {'vol': volume_name,
|
||||
'snap': snapshot_name,
|
||||
'ret': ret['key'],
|
||||
'msg': ret['msg']})
|
||||
raise exception.VolumeBackendAPIException(data=msg)
|
||||
|
||||
def create_cloned_volume(self, tgt_volume, src_volume):
|
||||
clone_name = VOLUME_PREFIX + tgt_volume['id'][-12:]
|
||||
volume_name = VOLUME_PREFIX + src_volume['id'][-12:]
|
||||
clone_name = self._convert_name(tgt_volume.name)
|
||||
volume_name = self._convert_name(src_volume.name)
|
||||
tgt_pool = volume_utils.extract_host(tgt_volume['host'], 'pool')
|
||||
try:
|
||||
self._cmd.create_volume(clone_name, str(
|
||||
@ -545,7 +607,7 @@ class Acs5000CommonDriver(san.SanDriver,
|
||||
data='create_cloned_volume failed.')
|
||||
|
||||
def extend_volume(self, volume, new_size):
|
||||
volume_name = VOLUME_PREFIX + volume['id'][-12:]
|
||||
volume_name = self._convert_name(volume.name)
|
||||
ret = self._cmd.extend_volume(volume_name, int(new_size))
|
||||
if ret['key'] == 303:
|
||||
raise exception.VolumeNotFound(volume_id=volume_name)
|
||||
@ -562,6 +624,33 @@ class Acs5000CommonDriver(san.SanDriver,
|
||||
break
|
||||
raise exception.VolumeSizeExceedsLimit(size=int(new_size),
|
||||
limit=allow_size)
|
||||
elif ret['key'] != 0:
|
||||
msg = (_('Failed to extend_volume %(vol)s to size %(size)s, '
|
||||
'code=%(ret)s, error=%(msg)s.') % {'vol': volume_name,
|
||||
'size': new_size,
|
||||
'ret': ret['key'],
|
||||
'msg': ret['msg']})
|
||||
raise exception.VolumeBackendAPIException(data=msg)
|
||||
|
||||
def update_migrated_volume(self, ctxt, volume, new_volume,
|
||||
original_volume_status):
|
||||
"""Only for host copy."""
|
||||
existing_name = self._convert_name(new_volume.name)
|
||||
wanted_name = self._convert_name(volume.name)
|
||||
LOG.debug('enter: update_migrated_volume: rename of %(new)s '
|
||||
'to original name %(wanted)s.', {'new': existing_name,
|
||||
'wanted': wanted_name})
|
||||
is_existed = self._cmd.get_volume(wanted_name)
|
||||
if len(is_existed) == 1:
|
||||
LOG.warn('volume name %(wanted)s is existed, The two volumes '
|
||||
'%(wanted)s and %(new)s may be on the same system.',
|
||||
{'new': existing_name,
|
||||
'wanted': wanted_name})
|
||||
return {'_name_id': new_volume['_name_id'] or new_volume['id']}
|
||||
else:
|
||||
self._cmd.set_volume_property(existing_name,
|
||||
{'new_name': wanted_name})
|
||||
return {'_name_id': None}
|
||||
|
||||
def migrate_volume(self, ctxt, volume, host):
|
||||
LOG.debug('enter: migrate_volume id %(id)s, host %(host)s',
|
||||
@ -581,7 +670,7 @@ class Acs5000CommonDriver(san.SanDriver,
|
||||
LOG.info('The target host belongs to the same storage system '
|
||||
'as the current but to a different pool. '
|
||||
'The same storage system will clone volume into the new pool')
|
||||
volume_name = VOLUME_PREFIX + volume['id'][-12:]
|
||||
volume_name = self._convert_name(volume.name)
|
||||
tmp_name = VOLUME_PREFIX + 'tmp'
|
||||
tmp_name += str(random.randint(0, 999999)).zfill(8)
|
||||
self._cmd.create_volume(tmp_name,
|
||||
@ -596,6 +685,58 @@ class Acs5000CommonDriver(san.SanDriver,
|
||||
'new_name': volume_name})
|
||||
return (True, None)
|
||||
|
||||
def _manage_get_volume(self, ref, pool_name=None):
|
||||
if 'source-name' in ref:
|
||||
manage_source = ref['source-name']
|
||||
volumes = self._cmd.get_volume(manage_source)
|
||||
else:
|
||||
reason = _('Reference must contain source-name element '
|
||||
'and only support source-name.')
|
||||
raise exception.ManageExistingInvalidReference(existing_ref=ref,
|
||||
reason=reason)
|
||||
|
||||
if not volumes:
|
||||
reason = (_('No volume by ref %s.')
|
||||
% manage_source)
|
||||
raise exception.ManageExistingInvalidReference(existing_ref=ref,
|
||||
reason=reason)
|
||||
volume = volumes[0]
|
||||
if pool_name and pool_name != volume['poolname']:
|
||||
reason = (_('Volume %(volume)s does not belong to pool name '
|
||||
'%(pool)s.') % {'volume': manage_source,
|
||||
'pool': pool_name})
|
||||
raise exception.ManageExistingInvalidReference(existing_ref=ref,
|
||||
reason=reason)
|
||||
return volume
|
||||
|
||||
@volume_utils.trace_method
|
||||
def manage_existing(self, volume, ref):
|
||||
"""Manages an existing volume."""
|
||||
volume_name = ref.get('source-name')
|
||||
if not volume_name:
|
||||
reason = _('Reference must contain source-name element '
|
||||
'and only support source-name.')
|
||||
raise exception.ManageExistingInvalidReference(existing_ref=ref,
|
||||
reason=reason)
|
||||
new_name = self._convert_name(volume.name)
|
||||
self._cmd.set_volume_property(volume_name, {'type': '2',
|
||||
'new_name': new_name})
|
||||
|
||||
@volume_utils.trace_method
|
||||
def manage_existing_get_size(self, volume, ref):
|
||||
"""Return size of an existing volume for manage_existing."""
|
||||
pool_name = volume_utils.extract_host(volume['host'], 'pool')
|
||||
vol_backend = self._manage_get_volume(ref, pool_name)
|
||||
size = int(vol_backend.get('size_mb', 0))
|
||||
size_gb = int(math.ceil(size / 1024))
|
||||
if (size_gb * 1024) > size:
|
||||
LOG.warn('Volume %(vol)s capacity is %(mb)s MB, '
|
||||
'extend to %(gb)s GB.', {'vol': ref['source-name'],
|
||||
'mb': size,
|
||||
'gb': size_gb})
|
||||
self._cmd.extend_volume(ref['source-name'], size_gb)
|
||||
return size_gb
|
||||
|
||||
def get_volume_stats(self, refresh=False):
|
||||
"""Get volume stats.
|
||||
|
||||
@ -628,15 +769,20 @@ class Acs5000CommonDriver(san.SanDriver,
|
||||
"""Build pool status"""
|
||||
pool_stats = {}
|
||||
try:
|
||||
pool_data = self._cmd.get_pool_info(pool)
|
||||
pool_data = self._cmd.get_pool(pool)
|
||||
if pool_data:
|
||||
total_capacity_gb = float(pool_data['capacity']) / units.Gi
|
||||
free_capacity_gb = float(pool_data['free_capacity']) / units.Gi
|
||||
total_capacity_gb = float(
|
||||
pool_data['capacity']) / units.Gi
|
||||
free_capacity_gb = float(
|
||||
pool_data['free_capacity']) / units.Gi
|
||||
allocated_capacity_gb = float(
|
||||
pool_data['used_capacity']) / units.Gi
|
||||
total_volumes = None
|
||||
if 'total_volumes' in pool_data.keys():
|
||||
total_volumes = int(pool_data['total_volumes'])
|
||||
thin_provisioning = False
|
||||
if 'thin' in pool_data and pool_data['thin'] == 'Enabled':
|
||||
thin_provisioning = True
|
||||
pool_stats = {
|
||||
'pool_name': pool_data['name'],
|
||||
'total_capacity_gb': total_capacity_gb,
|
||||
@ -647,8 +793,8 @@ class Acs5000CommonDriver(san.SanDriver,
|
||||
self.configuration.reserved_percentage,
|
||||
'QoS_support': False,
|
||||
'consistencygroup_support': False,
|
||||
'multiattach': False,
|
||||
'easytier_support': False,
|
||||
'multiattach': self.configuration.acs5000_multiattach,
|
||||
'thin_provisioning_support': thin_provisioning,
|
||||
'total_volumes': total_volumes,
|
||||
'system_id': self._state['system_id']}
|
||||
else:
|
||||
|
165
cinder/volume/drivers/toyou/acs5000/acs5000_fc.py
Normal file
165
cinder/volume/drivers/toyou/acs5000/acs5000_fc.py
Normal file
@ -0,0 +1,165 @@
|
||||
# Copyright 2021 toyou Corp.
|
||||
# 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.
|
||||
|
||||
"""
|
||||
acs5000 FC driver
|
||||
"""
|
||||
|
||||
from oslo_log import log as logging
|
||||
|
||||
from cinder import exception
|
||||
from cinder.i18n import _
|
||||
from cinder import interface
|
||||
from cinder import utils
|
||||
from cinder.volume.drivers.toyou.acs5000 import acs5000_common
|
||||
from cinder.zonemanager import utils as zone_utils
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@interface.volumedriver
|
||||
class Acs5000FCDriver(acs5000_common.Acs5000CommonDriver):
|
||||
"""TOYOU ACS5000 storage FC volume driver.
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
Version history:
|
||||
1.0.0 - Initial driver
|
||||
|
||||
"""
|
||||
|
||||
VENDOR = 'TOYOU'
|
||||
VERSION = '1.0.0'
|
||||
PROTOCOL = 'FC'
|
||||
|
||||
# ThirdPartySystems wiki page
|
||||
CI_WIKI_NAME = 'TOYOU_ACS5000_CI'
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(Acs5000FCDriver, self).__init__(*args, **kwargs)
|
||||
self.protocol = self.PROTOCOL
|
||||
|
||||
@staticmethod
|
||||
def get_driver_options():
|
||||
return acs5000_common.Acs5000CommonDriver.get_driver_options()
|
||||
|
||||
def _get_connected_wwpns(self):
|
||||
fc_ports = self._cmd.ls_fc()
|
||||
connected_wwpns = []
|
||||
for port in fc_ports:
|
||||
if 'wwpn' in port:
|
||||
connected_wwpns.append(port['wwpn'])
|
||||
elif 'WWPN' in port:
|
||||
connected_wwpns.append(port['WWPN'])
|
||||
return connected_wwpns
|
||||
|
||||
def validate_connector(self, connector):
|
||||
"""Check connector for at least one enabled FC protocol."""
|
||||
if 'wwpns' not in connector:
|
||||
LOG.error('The connector does not '
|
||||
'contain the required information.')
|
||||
raise exception.InvalidConnectorException(
|
||||
missing='wwpns')
|
||||
|
||||
@utils.synchronized('acs5000A-host', external=True)
|
||||
def initialize_connection(self, volume, connector):
|
||||
LOG.debug('enter: initialize_connection: volume '
|
||||
'%(vol)s with connector %(conn)s',
|
||||
{'vol': volume.id, 'conn': connector})
|
||||
volume_name = self._convert_name(volume.name)
|
||||
ret = self._cmd.create_lun_map(volume_name,
|
||||
self.protocol,
|
||||
connector['wwpns'])
|
||||
if ret['key'] == 0:
|
||||
if 'lun' in ret['arr']:
|
||||
lun_id = int(ret['arr']['lun'])
|
||||
else:
|
||||
msg = (_('_create_fc_lun: Lun id did not find '
|
||||
'when volume %s create lun map.') % volume['id'])
|
||||
raise exception.VolumeBackendAPIException(data=msg)
|
||||
|
||||
target_wwpns = self._get_connected_wwpns()
|
||||
if len(target_wwpns) == 0:
|
||||
if self._check_multi_attached(volume, connector) < 1:
|
||||
self._cmd.delete_lun_map(volume_name,
|
||||
self.protocol,
|
||||
connector['wwpns'])
|
||||
msg = (_('_create_fc_lun: Did not find '
|
||||
'available fc wwpns when volume %s '
|
||||
'create lun map.') % volume['id'])
|
||||
raise exception.VolumeBackendAPIException(data=msg)
|
||||
|
||||
initiator_target = {}
|
||||
for initiator_wwpn in connector['wwpns']:
|
||||
initiator_target[str(initiator_wwpn)] = target_wwpns
|
||||
properties = {'driver_volume_type': 'fibre_channel',
|
||||
'data': {'target_wwn': target_wwpns,
|
||||
'target_discovered': False,
|
||||
'target_lun': lun_id,
|
||||
'volume_id': volume['id']}}
|
||||
properties['data']['initiator_target_map'] = initiator_target
|
||||
elif ret['key'] == 303:
|
||||
raise exception.VolumeNotFound(volume_id=volume_name)
|
||||
else:
|
||||
msg = (_('failed to map the volume %(vol)s to '
|
||||
'connector %(conn)s.') %
|
||||
{'vol': volume['id'], 'conn': connector})
|
||||
raise exception.VolumeBackendAPIException(data=msg)
|
||||
|
||||
zone_utils.add_fc_zone(properties)
|
||||
LOG.debug('leave: initialize_connection: volume '
|
||||
'%(vol)s with connector %(conn)s',
|
||||
{'vol': volume.id, 'conn': connector})
|
||||
return properties
|
||||
|
||||
@utils.synchronized('acs5000A-host', external=True)
|
||||
def terminate_connection(self, volume, connector, **kwargs):
|
||||
LOG.debug('enter: terminate_connection: volume '
|
||||
'%(vol)s with connector %(conn)s',
|
||||
{'vol': volume.id, 'conn': connector})
|
||||
volume_name = self._convert_name(volume.name)
|
||||
properties = {'driver_volume_type': 'fibre_channel',
|
||||
'data': {}}
|
||||
initiator_wwpns = []
|
||||
target_wwpns = []
|
||||
if connector and 'wwpns' in connector:
|
||||
initiator_wwpns = connector['wwpns']
|
||||
target_wwpns = self._get_connected_wwpns()
|
||||
if len(target_wwpns) == 0:
|
||||
target_wwpns = []
|
||||
LOG.warn('terminate_connection: Did not find '
|
||||
'available fc wwpns when volume %s '
|
||||
'delete lun map.', volume.id)
|
||||
|
||||
initiator_target = {}
|
||||
for i_wwpn in initiator_wwpns:
|
||||
initiator_target[str(i_wwpn)] = target_wwpns
|
||||
properties['data'] = {'initiator_target_map': initiator_target}
|
||||
if self._check_multi_attached(volume, connector) < 2:
|
||||
if not initiator_wwpns:
|
||||
# -1 means all lun maps of this volume
|
||||
initiator_wwpns = -1
|
||||
self._cmd.delete_lun_map(volume_name,
|
||||
self.protocol,
|
||||
initiator_wwpns)
|
||||
else:
|
||||
LOG.warn('volume %s has been mapped to multi VMs, and these VMs '
|
||||
'belong to the same host. The mapping cancellation '
|
||||
'request is aborted.', volume.id)
|
||||
zone_utils.remove_fc_zone(properties)
|
||||
LOG.debug('leave: terminate_connection: volume '
|
||||
'%(vol)s with connector %(conn)s',
|
||||
{'vol': volume.id, 'conn': connector})
|
||||
return properties
|
@ -20,6 +20,7 @@ acs5000 iSCSI driver
|
||||
from oslo_log import log as logging
|
||||
|
||||
from cinder import exception
|
||||
from cinder.i18n import _
|
||||
from cinder import interface
|
||||
from cinder import utils
|
||||
from cinder.volume.drivers.toyou.acs5000 import acs5000_common
|
||||
@ -61,48 +62,63 @@ class Acs5000ISCSIDriver(acs5000_common.Acs5000CommonDriver):
|
||||
raise exception.InvalidConnectorException(
|
||||
missing='initiator')
|
||||
|
||||
@utils.synchronized('Acs5000A-host', external=True)
|
||||
@utils.synchronized('acs5000A-host', external=True)
|
||||
def initialize_connection(self, volume, connector):
|
||||
LOG.debug('initialize_connection: volume %(vol)s with connector '
|
||||
'%(conn)s', {'vol': volume['id'], 'conn': connector})
|
||||
volume_name = acs5000_common.VOLUME_PREFIX + volume['name'][-12:]
|
||||
LOG.debug('enter: initialize_connection: volume '
|
||||
'%(vol)s with connector %(conn)s',
|
||||
{'vol': volume.id, 'conn': connector})
|
||||
volume_name = self._convert_name(volume.name)
|
||||
ret = self._cmd.create_lun_map(volume_name,
|
||||
'WITH_ISCSI',
|
||||
self.protocol,
|
||||
connector['initiator'])
|
||||
if ret['key'] == 0:
|
||||
lun_required = ['iscsi_name', 'portal', 'lun']
|
||||
lun_info = ret['arr']
|
||||
for param in lun_required:
|
||||
if param not in lun_info:
|
||||
msg = (_('initialize_connection: Param %(param)s '
|
||||
'was not returned correctly when volume '
|
||||
'%(vol)s mapping.') % {'param': param,
|
||||
'vol': volume.id})
|
||||
raise exception.VolumeBackendAPIException(data=msg)
|
||||
data = {'target_discovered': False,
|
||||
'target_iqns': lun_info['iscsi_name'],
|
||||
'target_portals': lun_info['portal'],
|
||||
'target_luns': lun_info['lun'],
|
||||
'volume_id': volume.id}
|
||||
LOG.debug('leave: initialize_connection: volume '
|
||||
'%(vol)s with connector %(conn)s',
|
||||
{'vol': volume.id, 'conn': connector})
|
||||
return {'driver_volume_type': 'iscsi', 'data': data}
|
||||
if ret['key'] == 303:
|
||||
raise exception.VolumeNotFound(volume_id=volume_name)
|
||||
elif ret['key'] == 402:
|
||||
raise exception.ISCSITargetAttachFailed(volume_id=volume_name)
|
||||
else:
|
||||
lun_info = ret['arr']
|
||||
properties = {}
|
||||
properties['target_discovered'] = False
|
||||
properties['target_iqns'] = lun_info['iscsi_name']
|
||||
properties['target_portals'] = lun_info['portal']
|
||||
properties['target_luns'] = lun_info['lun']
|
||||
properties['volume_id'] = volume['id']
|
||||
properties['auth_method'] = ''
|
||||
properties['auth_username'] = ''
|
||||
properties['auth_password'] = ''
|
||||
properties['discovery_auth_method'] = ''
|
||||
properties['discovery_auth_username'] = ''
|
||||
properties['discovery_auth_password'] = ''
|
||||
return {'driver_volume_type': 'iscsi', 'data': properties}
|
||||
msg = (_('failed to map the volume %(vol)s to '
|
||||
'connector %(conn)s.') %
|
||||
{'vol': volume['id'], 'conn': connector})
|
||||
raise exception.VolumeBackendAPIException(data=msg)
|
||||
|
||||
@utils.synchronized('Acs5000A-host', external=True)
|
||||
@utils.synchronized('acs5000A-host', external=True)
|
||||
def terminate_connection(self, volume, connector, **kwargs):
|
||||
LOG.debug('terminate_connection: volume %(vol)s with connector '
|
||||
'%(conn)s', {'vol': volume['id'], 'conn': connector})
|
||||
info = {'driver_volume_type': 'iscsi', 'data': {}}
|
||||
name = acs5000_common.VOLUME_PREFIX + volume['name'][-12:]
|
||||
LOG.debug('enter: terminate_connection: volume '
|
||||
'%(vol)s with connector %(conn)s',
|
||||
{'vol': volume.id, 'conn': connector})
|
||||
name = self._convert_name(volume.name)
|
||||
# -1 means all lun maps
|
||||
initiator = '-1'
|
||||
if connector and connector['initiator']:
|
||||
initiator = connector['initiator']
|
||||
self._cmd.delete_lun_map(name,
|
||||
'WITH_ISCSI',
|
||||
initiator)
|
||||
LOG.debug('leave: terminate_connection: volume %(vol)s with '
|
||||
'connector %(conn)s', {'vol': volume['id'],
|
||||
'conn': connector})
|
||||
return info
|
||||
if self._check_multi_attached(volume, connector) < 2:
|
||||
self._cmd.delete_lun_map(name,
|
||||
self.protocol,
|
||||
initiator)
|
||||
else:
|
||||
LOG.warn('volume %s has been mapped to multi VMs, and these VMs '
|
||||
'belong to the same host. The mapping cancellation '
|
||||
'request is aborted.', volume.id)
|
||||
LOG.debug('leave: terminate_connection: volume '
|
||||
'%(vol)s with connector %(conn)s',
|
||||
{'vol': volume.id, 'conn': connector})
|
||||
return {'driver_volume_type': 'iscsi', 'data': {}}
|
||||
|
@ -1,72 +0,0 @@
|
||||
==========================
|
||||
TOYOU ACS5000 iSCSI driver
|
||||
==========================
|
||||
|
||||
TOYOU ACS5000 series volume driver provides OpenStack Compute instances
|
||||
with access to TOYOU ACS5000 series storage systems.
|
||||
|
||||
TOYOU ACS5000 storage can be used with iSCSI connection.
|
||||
|
||||
This documentation explains how to configure and connect the block storage
|
||||
nodes to TOYOU ACS5000 series storage.
|
||||
|
||||
Driver options
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
The following table contains the configuration options supported by the
|
||||
TOYOU ACS5000 iSCSI driver.
|
||||
|
||||
.. config-table::
|
||||
:config-target: TOYOU ACS5000
|
||||
|
||||
cinder.volume.drivers.toyou.acs5000.acs5000_iscsi
|
||||
cinder.volume.drivers.toyou.acs5000.acs5000_common
|
||||
|
||||
Supported operations
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
- Create, list, delete, attach (map), and detach (unmap) volumes.
|
||||
- Create, list and delete volume snapshots.
|
||||
- Create a volume from a snapshot.
|
||||
- Copy an image to a volume.
|
||||
- Copy a volume to an image.
|
||||
- Clone a volume.
|
||||
- Extend a volume.
|
||||
- Migrate a volume.
|
||||
|
||||
Configure TOYOU ACS5000 iSCSI backend
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
This section details the steps required to configure the TOYOU ACS5000
|
||||
storage cinder driver.
|
||||
|
||||
#. In the ``cinder.conf`` configuration file under the ``[DEFAULT]``
|
||||
section, set the enabled_backends parameter.
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[DEFAULT]
|
||||
enabled_backends = ACS5000-1
|
||||
|
||||
|
||||
#. Add a backend group section for the backend group specified
|
||||
in the enabled_backends parameter.
|
||||
|
||||
#. In the newly created backend group section, set the
|
||||
following configuration options:
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[ACS5000-1]
|
||||
# The driver path
|
||||
volume_driver = cinder.volume.drivers.toyou.acs5000.acs5000_iscsi.Acs5000ISCSIDriver
|
||||
# Management IP of TOYOU ACS5000 storage array
|
||||
san_ip = 10.0.0.10
|
||||
# Management username of TOYOU ACS5000 storage array
|
||||
san_login = cliuser
|
||||
# Management password of TOYOU ACS5000 storage array
|
||||
san_password = clipassword
|
||||
# The Pool used to allocated volumes
|
||||
acs5000_volpool_name = pool01
|
||||
# Backend name
|
||||
volume_backend_name = ACS5000
|
@ -0,0 +1,106 @@
|
||||
===========================
|
||||
TOYOU NetStor Cinder driver
|
||||
===========================
|
||||
|
||||
TOYOU NetStor series volume driver provides OpenStack Compute instances
|
||||
with access to TOYOU NetStor series storage systems.
|
||||
|
||||
TOYOU NetStor storage can be used with iSCSI or FC connection.
|
||||
|
||||
This documentation explains how to configure and connect the block storage
|
||||
nodes to TOYOU NetStor series storage.
|
||||
|
||||
Driver options
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
The following table contains the configuration options supported by the
|
||||
TOYOU NetStor iSCSI/FC driver.
|
||||
|
||||
.. config-table::
|
||||
:config-target: TOYOU NetStor
|
||||
|
||||
cinder.volume.drivers.toyou.acs5000.acs5000_common
|
||||
|
||||
Supported operations
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
- Create, list, delete, attach (map), and detach (unmap) volumes.
|
||||
- Create, list and delete volume snapshots.
|
||||
- Create a volume from a snapshot.
|
||||
- Copy an image to a volume.
|
||||
- Copy a volume to an image.
|
||||
- Clone a volume.
|
||||
- Extend a volume.
|
||||
- Migrate a volume.
|
||||
- Manage/Unmanage volume.
|
||||
- Revert to Snapshot.
|
||||
- Multi-attach.
|
||||
- Thin Provisioning.
|
||||
- Extend Attached Volume.
|
||||
|
||||
Configure TOYOU NetStor iSCSI/FC backend
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
This section details the steps required to configure the TOYOU NetStor
|
||||
storage cinder driver.
|
||||
|
||||
#. In the ``cinder.conf`` configuration file under the ``[DEFAULT]``
|
||||
section, set the enabled_backends parameter
|
||||
with the iSCSI or FC back-end group.
|
||||
|
||||
- For Fibre Channel:
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[DEFAULT]
|
||||
enabled_backends = toyou-fc-1
|
||||
|
||||
- For iSCSI:
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[DEFAULT]
|
||||
enabled_backends = toyou-iscsi-1
|
||||
|
||||
|
||||
#. Add a backend group section for the backend group specified
|
||||
in the enabled_backends parameter.
|
||||
|
||||
#. In the newly created backend group section, set the
|
||||
following configuration options:
|
||||
|
||||
- For Fibre Channel:
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[toyou-fc-1]
|
||||
# The TOYOU NetStor driver path
|
||||
volume_driver = cinder.volume.drivers.toyou.acs5000.acs5000_fc.Acs5000FCDriver
|
||||
# Management IP of TOYOU NetStor storage array
|
||||
san_ip = 10.0.0.10
|
||||
# Management username of TOYOU NetStor storage array
|
||||
san_login = cliuser
|
||||
# Management password of TOYOU NetStor storage array
|
||||
san_password = clipassword
|
||||
# The Pool used to allocated volumes
|
||||
acs5000_volpool_name = pool01
|
||||
# Backend name
|
||||
volume_backend_name = toyou-fc
|
||||
|
||||
- For iSCSI:
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[toyou-iscsi-1]
|
||||
# The TOYOU NetStor driver path
|
||||
volume_driver = cinder.volume.drivers.toyou.acs5000.acs5000_iscsi.Acs5000ISCSIDriver
|
||||
# Management IP of TOYOU NetStor storage array
|
||||
san_ip = 10.0.0.10
|
||||
# Management username of TOYOU NetStor storage array
|
||||
san_login = cliuser
|
||||
# Management password of TOYOU NetStor storage array
|
||||
san_password = clipassword
|
||||
# The Pool used to allocated volumes
|
||||
acs5000_volpool_name = pool01
|
||||
# Backend name
|
||||
volume_backend_name = toyou-iscsi
|
@ -183,8 +183,8 @@ title=StorPool Storage Driver (storpool)
|
||||
[driver.synology]
|
||||
title=Synology Storage Driver (iSCSI)
|
||||
|
||||
[driver.toyou]
|
||||
title=TOYOU ACS5000 Storage Driver (iSCSI)
|
||||
[driver.toyou_netstor]
|
||||
title=TOYOU NetStor Storage Driver (iSCSI, FC)
|
||||
|
||||
[driver.vrtsaccess]
|
||||
title=Veritas Access iSCSI Driver (iSCSI)
|
||||
@ -273,7 +273,7 @@ driver.sandstone=complete
|
||||
driver.seagate=complete
|
||||
driver.storpool=complete
|
||||
driver.synology=complete
|
||||
driver.toyou=complete
|
||||
driver.toyou_netstor=complete
|
||||
driver.vrtsaccess=missing
|
||||
driver.vrtscnfs=missing
|
||||
driver.vzstorage=missing
|
||||
@ -343,7 +343,7 @@ driver.sandstone=complete
|
||||
driver.seagate=complete
|
||||
driver.storpool=complete
|
||||
driver.synology=complete
|
||||
driver.toyou=missing
|
||||
driver.toyou_netstor=complete
|
||||
driver.vrtsaccess=complete
|
||||
driver.vrtscnfs=complete
|
||||
driver.vzstorage=complete
|
||||
@ -416,7 +416,7 @@ driver.sandstone=complete
|
||||
driver.seagate=missing
|
||||
driver.storpool=missing
|
||||
driver.synology=missing
|
||||
driver.toyou=missing
|
||||
driver.toyou_netstor=missing
|
||||
driver.vrtsaccess=missing
|
||||
driver.vrtscnfs=missing
|
||||
driver.vzstorage=missing
|
||||
@ -488,7 +488,7 @@ driver.sandstone=complete
|
||||
driver.seagate=missing
|
||||
driver.storpool=complete
|
||||
driver.synology=missing
|
||||
driver.toyou=missing
|
||||
driver.toyou_netstor=missing
|
||||
driver.vrtsaccess=missing
|
||||
driver.vrtscnfs=missing
|
||||
driver.vzstorage=missing
|
||||
@ -561,7 +561,7 @@ driver.sandstone=missing
|
||||
driver.seagate=missing
|
||||
driver.storpool=missing
|
||||
driver.synology=missing
|
||||
driver.toyou=missing
|
||||
driver.toyou_netstor=missing
|
||||
driver.vrtsaccess=missing
|
||||
driver.vrtscnfs=missing
|
||||
driver.vzstorage=missing
|
||||
@ -633,7 +633,7 @@ driver.sandstone=complete
|
||||
driver.seagate=missing
|
||||
driver.storpool=complete
|
||||
driver.synology=missing
|
||||
driver.toyou=missing
|
||||
driver.toyou_netstor=complete
|
||||
driver.vrtsaccess=missing
|
||||
driver.vrtscnfs=missing
|
||||
driver.vzstorage=missing
|
||||
@ -706,7 +706,7 @@ driver.sandstone=missing
|
||||
driver.seagate=missing
|
||||
driver.storpool=complete
|
||||
driver.synology=missing
|
||||
driver.toyou=complete
|
||||
driver.toyou_netstor=complete
|
||||
driver.vrtsaccess=missing
|
||||
driver.vrtscnfs=missing
|
||||
driver.vzstorage=missing
|
||||
@ -779,7 +779,7 @@ driver.sandstone=complete
|
||||
driver.seagate=complete
|
||||
driver.storpool=complete
|
||||
driver.synology=missing
|
||||
driver.toyou=missing
|
||||
driver.toyou_netstor=complete
|
||||
driver.vrtsaccess=missing
|
||||
driver.vrtscnfs=missing
|
||||
driver.vzstorage=missing
|
||||
@ -849,7 +849,7 @@ driver.sandstone=complete
|
||||
driver.seagate=missing
|
||||
driver.storpool=missing
|
||||
driver.synology=missing
|
||||
driver.toyou=missing
|
||||
driver.toyou_netstor=complete
|
||||
driver.vrtsaccess=missing
|
||||
driver.vrtscnfs=missing
|
||||
driver.vzstorage=missing
|
||||
@ -923,7 +923,7 @@ driver.sandstone=complete
|
||||
driver.seagate=missing
|
||||
driver.storpool=missing
|
||||
driver.synology=missing
|
||||
driver.toyou=missing
|
||||
driver.toyou_netstor=missing
|
||||
driver.vrtsaccess=missing
|
||||
driver.vrtscnfs=missing
|
||||
driver.vzstorage=missing
|
||||
|
@ -0,0 +1,4 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
New FC cinder volume driver for TOYOU NetStor Storage.
|
Loading…
Reference in New Issue
Block a user