Add Infortrend Manila Driver
This driver supports NFS and CIFS shares. The following operations are supported: - Create a share. - Delete a share. - Allow share access. - Deny share access. - Manage a share. - Unmanage a share. - Extend a share. - Shrink a share. DocImpact Implements: blueprint infortrend-support-manila-driver Change-Id: Ib1adbd8f7f55805387b126851dbb0ff50cfbcd75
This commit is contained in:
parent
d3b26f3d93
commit
266972ab91
@ -71,6 +71,8 @@ Mapping of share drivers and share features support
|
||||
+----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+--------------------+
|
||||
| INSPUR InStorage | T | \- | T | \- | \- | \- | \- | \- | \- |
|
||||
+----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+--------------------+
|
||||
| Infortrend | T | T | T | T | \- | \- | \- | \- | \- |
|
||||
+----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+--------------------+
|
||||
| LVM | M | \- | M | \- | M | M | \- | O | O |
|
||||
+----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+--------------------+
|
||||
| Quobyte | K | \- | M | M | \- | \- | \- | \- | \- |
|
||||
@ -144,6 +146,8 @@ Mapping of share drivers and share access rules support
|
||||
+----------------------------------------+--------------+--------------+----------------+------------+--------------+--------------+--------------+----------------+------------+------------+
|
||||
| INSPUR InStorage | NFS (T) | \- | CIFS (T) | \- | \- | NFS (T) | \- | CIFS (T) | \- | \- |
|
||||
+----------------------------------------+--------------+--------------+----------------+------------+--------------+--------------+--------------+----------------+------------+------------+
|
||||
| Infortrend | NFS (T) | \- | CIFS (T) | \- | \- | NFS (T) | \- | CIFS (T) | \- | \- |
|
||||
+----------------------------------------+--------------+--------------+----------------+------------+--------------+--------------+--------------+----------------+------------+------------+
|
||||
| Oracle ZFSSA | NFS,CIFS(K) | \- | \- | \- | \- | \- | \- | \- | \- | \- |
|
||||
+----------------------------------------+--------------+--------------+----------------+------------+--------------+--------------+--------------+----------------+------------+------------+
|
||||
| CephFS | NFS (P) | \- | \- | \- | CEPHFS (M) | NFS (P) | \- | \- | \- | CEPHFS (N) |
|
||||
@ -209,6 +213,8 @@ Mapping of share drivers and security services support
|
||||
+----------------------------------------+------------------+-----------------+------------------+
|
||||
| INSPUR InStorage | \- | \- | \- |
|
||||
+----------------------------------------+------------------+-----------------+------------------+
|
||||
| Infortrend | \- | \- | \- |
|
||||
+----------------------------------------+------------------+-----------------+------------------+
|
||||
| Oracle ZFSSA | \- | \- | \- |
|
||||
+----------------------------------------+------------------+-----------------+------------------+
|
||||
| CephFS | \- | \- | \- |
|
||||
@ -264,6 +270,8 @@ More information: :ref:`capabilities_and_extra_specs`
|
||||
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+
|
||||
| INFINIDAT | \- | Q | \- | \- | Q | Q | \- | Q | Q | Q | Q | \- |
|
||||
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+
|
||||
| Infortrend | \- | T | \- | \- | \- | \- | \- | \- | \- | \- | T | \- |
|
||||
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+
|
||||
| LVM | \- | M | \- | \- | \- | M | \- | K | O | O | P | P |
|
||||
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+
|
||||
| Quobyte | \- | K | \- | \- | \- | L | \- | M | \- | \- | P | \- |
|
||||
|
@ -967,3 +967,13 @@ class LockingFailed(ManilaException):
|
||||
# Ganesha library
|
||||
class GaneshaException(ManilaException):
|
||||
message = _("Unknown NFS-Ganesha library exception.")
|
||||
|
||||
|
||||
# Infortrend Storage driver
|
||||
class InfortrendCLIException(ShareBackendException):
|
||||
message = _("Infortrend CLI exception: %(err)s "
|
||||
"Return Code: %(rc)s, Output: %(out)s")
|
||||
|
||||
|
||||
class InfortrendNASException(ShareBackendException):
|
||||
message = _("Infortrend NAS exception: %(err)s")
|
||||
|
@ -72,6 +72,7 @@ import manila.share.drivers.hpe.hpe_3par_driver
|
||||
import manila.share.drivers.huawei.huawei_nas
|
||||
import manila.share.drivers.ibm.gpfs
|
||||
import manila.share.drivers.infinidat.infinibox
|
||||
import manila.share.drivers.infortrend.driver
|
||||
import manila.share.drivers.inspur.as13000.as13000_nas
|
||||
import manila.share.drivers.inspur.instorage.instorage
|
||||
import manila.share.drivers.lvm
|
||||
@ -159,6 +160,7 @@ _global_opt_lists = [
|
||||
manila.share.drivers.infinidat.infinibox.infinidat_auth_opts,
|
||||
manila.share.drivers.infinidat.infinibox.infinidat_connection_opts,
|
||||
manila.share.drivers.infinidat.infinibox.infinidat_general_opts,
|
||||
manila.share.drivers.infortrend.driver.infortrend_nas_opts,
|
||||
manila.share.drivers.inspur.as13000.as13000_nas.inspur_as13000_opts,
|
||||
manila.share.drivers.inspur.instorage.instorage.instorage_opts,
|
||||
manila.share.drivers.maprfs.maprfs_native.maprfs_native_share_opts,
|
||||
|
0
manila/share/drivers/infortrend/__init__.py
Normal file
0
manila/share/drivers/infortrend/__init__.py
Normal file
257
manila/share/drivers/infortrend/driver.py
Normal file
257
manila/share/drivers/infortrend/driver.py
Normal file
@ -0,0 +1,257 @@
|
||||
# Copyright (c) 2019 Infortrend Technology, Inc.
|
||||
# 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.
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log
|
||||
|
||||
from manila import exception
|
||||
from manila.i18n import _
|
||||
from manila.share import driver
|
||||
from manila.share.drivers.infortrend import infortrend_nas
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
infortrend_nas_opts = [
|
||||
cfg.HostAddressOpt('infortrend_nas_ip',
|
||||
required=True,
|
||||
help='Infortrend NAS IP for management.'),
|
||||
cfg.StrOpt('infortrend_nas_user',
|
||||
default='manila',
|
||||
help='User for the Infortrend NAS server.'),
|
||||
cfg.StrOpt('infortrend_nas_password',
|
||||
default=None,
|
||||
secret=True,
|
||||
help='Password for the Infortrend NAS server. '
|
||||
'This is not necessary '
|
||||
'if infortrend_nas_ssh_key is set.'),
|
||||
cfg.StrOpt('infortrend_nas_ssh_key',
|
||||
default=None,
|
||||
help='SSH key for the Infortrend NAS server. '
|
||||
'This is not necessary '
|
||||
'if infortrend_nas_password is set.'),
|
||||
cfg.ListOpt('infortrend_share_pools',
|
||||
required=True,
|
||||
help='Comma separated list of Infortrend NAS pools.'),
|
||||
cfg.ListOpt('infortrend_share_channels',
|
||||
required=True,
|
||||
help='Comma separated list of Infortrend channels.'),
|
||||
cfg.IntOpt('infortrend_ssh_timeout',
|
||||
default=30,
|
||||
help='SSH timeout in seconds.'),
|
||||
]
|
||||
|
||||
CONF = cfg.CONF
|
||||
CONF.register_opts(infortrend_nas_opts)
|
||||
|
||||
|
||||
class InfortrendNASDriver(driver.ShareDriver):
|
||||
|
||||
"""Infortrend Share Driver for GS/GSe Family using NASCLI.
|
||||
|
||||
Version history:
|
||||
1.0.0 - Initial driver
|
||||
"""
|
||||
|
||||
VERSION = "1.0.0"
|
||||
PROTOCOL = "NFS_CIFS"
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(InfortrendNASDriver, self).__init__(False, *args, **kwargs)
|
||||
self.configuration.append_config_values(infortrend_nas_opts)
|
||||
|
||||
nas_ip = self.configuration.safe_get('infortrend_nas_ip')
|
||||
username = self.configuration.safe_get('infortrend_nas_user')
|
||||
password = self.configuration.safe_get('infortrend_nas_password')
|
||||
ssh_key = self.configuration.safe_get('infortrend_nas_ssh_key')
|
||||
timeout = self.configuration.safe_get('infortrend_ssh_timeout')
|
||||
self.backend_name = self.configuration.safe_get('share_backend_name')
|
||||
|
||||
if not (password or ssh_key):
|
||||
msg = _('Either infortrend_nas_password or infortrend_nas_ssh_key '
|
||||
'should be set.')
|
||||
raise exception.InvalidParameterValue(err=msg)
|
||||
|
||||
pool_dict = self._init_pool_dict()
|
||||
channel_dict = self._init_channel_dict()
|
||||
self.ift_nas = infortrend_nas.InfortrendNAS(nas_ip, username, password,
|
||||
ssh_key, timeout,
|
||||
pool_dict, channel_dict)
|
||||
|
||||
def _init_pool_dict(self):
|
||||
pools_names = self.configuration.safe_get('infortrend_share_pools')
|
||||
|
||||
return {el: {} for el in pools_names}
|
||||
|
||||
def _init_channel_dict(self):
|
||||
channels = self.configuration.safe_get('infortrend_share_channels')
|
||||
|
||||
return {el: '' for el in channels}
|
||||
|
||||
def do_setup(self, context):
|
||||
"""Any initialization the share driver does while starting."""
|
||||
LOG.debug('Infortrend NAS do_setup start.')
|
||||
self.ift_nas.do_setup()
|
||||
|
||||
def check_for_setup_error(self):
|
||||
"""Check for setup error."""
|
||||
LOG.debug('Infortrend NAS check_for_setup_error start.')
|
||||
self.ift_nas.check_for_setup_error()
|
||||
|
||||
def _update_share_stats(self):
|
||||
"""Retrieve stats info from share group."""
|
||||
|
||||
LOG.debug('Updating Infortrend backend [%s].', self.backend_name)
|
||||
|
||||
data = dict(
|
||||
share_backend_name=self.backend_name,
|
||||
vendor_name='Infortrend',
|
||||
driver_version=self.VERSION,
|
||||
storage_protocol=self.PROTOCOL,
|
||||
reserved_percentage=self.configuration.reserved_share_percentage,
|
||||
pools=self.ift_nas.update_pools_stats())
|
||||
LOG.debug('Infortrend pools status: %s', data['pools'])
|
||||
|
||||
super(InfortrendNASDriver, self)._update_share_stats(data)
|
||||
|
||||
def update_access(self, context, share, access_rules, add_rules,
|
||||
delete_rules, share_server=None):
|
||||
"""Update access rules for given share.
|
||||
|
||||
:param context: Current context
|
||||
:param share: Share model with share data.
|
||||
:param access_rules: All access rules for given share
|
||||
:param add_rules: Empty List or List of access rules which should be
|
||||
added. access_rules already contains these rules.
|
||||
:param delete_rules: Empty List or List of access rules which should be
|
||||
removed. access_rules doesn't contain these rules.
|
||||
:param share_server: Not used by this driver.
|
||||
|
||||
:returns: None, or a dictionary of ``access_id``, ``access_key`` as
|
||||
key: value pairs for the rules added, where, ``access_id``
|
||||
is the UUID (string) of the access rule, and ``access_key``
|
||||
is the credential (string) of the entity granted access.
|
||||
During recovery after error, the returned dictionary must
|
||||
contain ``access_id``, ``access_key`` for all the rules that
|
||||
the driver is ordered to resync, i.e. rules in the
|
||||
``access_rules`` parameter.
|
||||
"""
|
||||
|
||||
return self.ift_nas.update_access(share, access_rules, add_rules,
|
||||
delete_rules, share_server)
|
||||
|
||||
def create_share(self, context, share, share_server=None):
|
||||
"""Create a share."""
|
||||
|
||||
LOG.debug('Creating share: %s.', share['id'])
|
||||
|
||||
return self.ift_nas.create_share(share, share_server)
|
||||
|
||||
def delete_share(self, context, share, share_server=None):
|
||||
"""Remove a share."""
|
||||
|
||||
LOG.debug('Deleting share: %s.', share['id'])
|
||||
|
||||
return self.ift_nas.delete_share(share, share_server)
|
||||
|
||||
def get_pool(self, share):
|
||||
"""Return pool name where the share resides on.
|
||||
|
||||
:param share: The share hosted by the driver.
|
||||
"""
|
||||
return self.ift_nas.get_pool(share)
|
||||
|
||||
def ensure_share(self, context, share, share_server=None):
|
||||
"""Invoked to ensure that share is exported.
|
||||
|
||||
Driver can use this method to update the list of export locations of
|
||||
the share if it changes. To do that, you should return list with
|
||||
export locations.
|
||||
|
||||
:return None or list with export locations
|
||||
"""
|
||||
return self.ift_nas.ensure_share(share, share_server)
|
||||
|
||||
def manage_existing(self, share, driver_options):
|
||||
"""Brings an existing share under Manila management.
|
||||
|
||||
If the provided share is not valid, then raise a
|
||||
ManageInvalidShare exception, specifying a reason for the failure.
|
||||
|
||||
If the provided share is not in a state that can be managed, such as
|
||||
being replicated on the backend, the driver *MUST* raise
|
||||
ManageInvalidShare exception with an appropriate message.
|
||||
|
||||
The share has a share_type, and the driver can inspect that and
|
||||
compare against the properties of the referenced backend share.
|
||||
If they are incompatible, raise a
|
||||
ManageExistingShareTypeMismatch, specifying a reason for the failure.
|
||||
|
||||
:param share: Share model
|
||||
:param driver_options: Driver-specific options provided by admin.
|
||||
:return: share_update dictionary with required key 'size',
|
||||
which should contain size of the share.
|
||||
"""
|
||||
LOG.debug(
|
||||
'Manage existing for share: %(share)s,', {
|
||||
'share': share['share_id'],
|
||||
})
|
||||
return self.ift_nas.manage_existing(share, driver_options)
|
||||
|
||||
def unmanage(self, share):
|
||||
"""Removes the specified share from Manila management.
|
||||
|
||||
Does not delete the underlying backend share.
|
||||
|
||||
For most drivers, this will not need to do anything. However, some
|
||||
drivers might use this call as an opportunity to clean up any
|
||||
Manila-specific configuration that they have associated with the
|
||||
backend share.
|
||||
|
||||
If provided share cannot be unmanaged, then raise an
|
||||
UnmanageInvalidShare exception, specifying a reason for the failure.
|
||||
|
||||
This method is invoked when the share is being unmanaged with
|
||||
a share type that has ``driver_handles_share_servers``
|
||||
extra-spec set to False.
|
||||
"""
|
||||
LOG.debug(
|
||||
'Unmanage share: %(share)s', {
|
||||
'share': share['share_id'],
|
||||
})
|
||||
return self.ift_nas.unmanage(share)
|
||||
|
||||
def extend_share(self, share, new_size, share_server=None):
|
||||
"""Extends size of existing share.
|
||||
|
||||
:param share: Share model
|
||||
:param new_size: New size of share (new_size > share['size'])
|
||||
:param share_server: Optional -- Share server model
|
||||
"""
|
||||
return self.ift_nas.extend_share(share, new_size, share_server)
|
||||
|
||||
def shrink_share(self, share, new_size, share_server=None):
|
||||
"""Shrinks size of existing share.
|
||||
|
||||
If consumed space on share larger than new_size driver should raise
|
||||
ShareShrinkingPossibleDataLoss exception:
|
||||
raise ShareShrinkingPossibleDataLoss(share_id=share['id'])
|
||||
|
||||
:param share: Share model
|
||||
:param new_size: New size of share (new_size < share['size'])
|
||||
:param share_server: Optional -- Share server model
|
||||
|
||||
:raises ShareShrinkingPossibleDataLoss, NotImplementedError
|
||||
"""
|
||||
return self.ift_nas.shrink_share(share, new_size, share_server)
|
642
manila/share/drivers/infortrend/infortrend_nas.py
Normal file
642
manila/share/drivers/infortrend/infortrend_nas.py
Normal file
@ -0,0 +1,642 @@
|
||||
# Copyright (c) 2019 Infortrend Technology, Inc.
|
||||
# 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.
|
||||
|
||||
import json
|
||||
import re
|
||||
|
||||
from oslo_concurrency import processutils
|
||||
from oslo_log import log
|
||||
from oslo_utils import units
|
||||
|
||||
from manila.common import constants
|
||||
from manila import exception
|
||||
from manila.i18n import _
|
||||
from manila.share import utils as share_utils
|
||||
from manila import utils as manila_utils
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
|
||||
def _bi_to_gi(bi_size):
|
||||
return bi_size / units.Gi
|
||||
|
||||
|
||||
class InfortrendNAS(object):
|
||||
|
||||
_SSH_PORT = 22
|
||||
|
||||
def __init__(self, nas_ip, username, password, ssh_key,
|
||||
timeout, pool_dict, channel_dict):
|
||||
self.nas_ip = nas_ip
|
||||
self.port = self._SSH_PORT
|
||||
self.username = username
|
||||
self.password = password
|
||||
self.ssh_key = ssh_key
|
||||
self.ssh_timeout = timeout
|
||||
self.pool_dict = pool_dict
|
||||
self.channel_dict = channel_dict
|
||||
self.command = ""
|
||||
self.ssh = None
|
||||
self.sshpool = None
|
||||
self.location = 'a@0'
|
||||
|
||||
def _execute(self, command_line):
|
||||
command_line.extend(['-z', self.location])
|
||||
commands = ' '.join(command_line)
|
||||
manila_utils.check_ssh_injection(commands)
|
||||
LOG.debug('Executing: %(command)s', {'command': commands})
|
||||
|
||||
cli_out = self._ssh_execute(commands)
|
||||
|
||||
return self._parser(cli_out)
|
||||
|
||||
def _ssh_execute(self, commands):
|
||||
try:
|
||||
out, err = processutils.ssh_execute(
|
||||
self.ssh, commands,
|
||||
timeout=self.ssh_timeout, check_exit_code=True)
|
||||
except processutils.ProcessExecutionError as pe:
|
||||
rc = pe.exit_code
|
||||
out = pe.stdout
|
||||
out = out.replace('\n', '\\n')
|
||||
msg = _('Error on execute ssh command. '
|
||||
'Exit code: %(rc)d, msg: %(out)s') % {
|
||||
'rc': rc, 'out': out}
|
||||
raise exception.InfortrendNASException(err=msg)
|
||||
|
||||
return out
|
||||
|
||||
def _parser(self, content=None):
|
||||
LOG.debug('parsing data:\n%s', content)
|
||||
content = content.replace("\r", "")
|
||||
content = content.strip()
|
||||
json_string = content.replace("'", "\"")
|
||||
cli_data = json_string.splitlines()[2]
|
||||
if cli_data:
|
||||
try:
|
||||
data_dict = json.loads(cli_data)
|
||||
except Exception:
|
||||
msg = _('Failed to parse data: '
|
||||
'%(cli_data)s to dictionary.') % {
|
||||
'cli_data': cli_data}
|
||||
LOG.error(msg)
|
||||
raise exception.InfortrendNASException(err=msg)
|
||||
|
||||
rc = int(data_dict['cliCode'][0]['Return'], 16)
|
||||
if rc == 0:
|
||||
result = data_dict['data']
|
||||
else:
|
||||
result = data_dict['cliCode'][0]['CLI']
|
||||
else:
|
||||
msg = _('No data is returned from NAS.')
|
||||
LOG.error(msg)
|
||||
raise exception.InfortrendNASException(err=msg)
|
||||
|
||||
if rc != 0:
|
||||
msg = _('NASCLI error, returned: %(result)s.') % {
|
||||
'result': result}
|
||||
LOG.error(msg)
|
||||
raise exception.InfortrendCLIException(
|
||||
err=msg, rc=rc, out=result)
|
||||
|
||||
return rc, result
|
||||
|
||||
def do_setup(self):
|
||||
self._init_connect()
|
||||
self._ensure_service_on('nfs')
|
||||
self._ensure_service_on('cifs')
|
||||
|
||||
def _init_connect(self):
|
||||
if not (self.sshpool and self.ssh):
|
||||
self.sshpool = manila_utils.SSHPool(ip=self.nas_ip,
|
||||
port=self.port,
|
||||
conn_timeout=None,
|
||||
login=self.username,
|
||||
password=self.password,
|
||||
privatekey=self.ssh_key)
|
||||
self.ssh = self.sshpool.create()
|
||||
|
||||
if not self.ssh.get_transport().is_active():
|
||||
self.sshpool = manila_utils.SSHPool(ip=self.nas_ip,
|
||||
port=self.port,
|
||||
conn_timeout=None,
|
||||
login=self.username,
|
||||
password=self.password,
|
||||
privatekey=self.ssh_key)
|
||||
self.ssh = self.sshpool.create()
|
||||
|
||||
LOG.debug('NAScmd [%s@%s] start!', self.username, self.nas_ip)
|
||||
|
||||
def check_for_setup_error(self):
|
||||
self._check_pools_setup()
|
||||
self._check_channels_status()
|
||||
|
||||
def _ensure_service_on(self, proto, slot='A'):
|
||||
command_line = ['service', 'status', proto]
|
||||
rc, service_status = self._execute(command_line)
|
||||
if not service_status[0][slot][proto.upper()]['enabled']:
|
||||
command_line = ['service', 'restart', proto]
|
||||
self._execute(command_line)
|
||||
|
||||
def _check_channels_status(self):
|
||||
channel_list = list(self.channel_dict.keys())
|
||||
command_line = ['ifconfig', 'inet', 'show']
|
||||
rc, channels_status = self._execute(command_line)
|
||||
for channel in channels_status:
|
||||
if 'CH' in channel['datalink']:
|
||||
ch = channel['datalink'].strip('CH')
|
||||
if ch in self.channel_dict.keys():
|
||||
self.channel_dict[ch] = channel['IP']
|
||||
channel_list.remove(ch)
|
||||
if channel['status'] == 'DOWN':
|
||||
LOG.warning('Channel [%(ch)s] status '
|
||||
'is down, please check.', {
|
||||
'ch': ch})
|
||||
if len(channel_list) != 0:
|
||||
msg = _('Channel setting %(channel_list)s is invalid!') % {
|
||||
'channel_list': channel_list}
|
||||
LOG.error(msg)
|
||||
raise exception.InfortrendNASException(message=msg)
|
||||
|
||||
def _check_pools_setup(self):
|
||||
pool_list = list(self.pool_dict.keys())
|
||||
command_line = ['folder', 'status']
|
||||
rc, pool_data = self._execute(command_line)
|
||||
for pool in pool_data:
|
||||
pool_name = self._extract_pool_name(pool)
|
||||
if pool_name in self.pool_dict.keys():
|
||||
pool_list.remove(pool_name)
|
||||
self.pool_dict[pool_name]['id'] = pool['volumeId']
|
||||
self.pool_dict[pool_name]['path'] = pool['directory'] + '/'
|
||||
if len(pool_list) == 0:
|
||||
break
|
||||
|
||||
if len(pool_list) != 0:
|
||||
msg = _('Please create %(pool_list)s pool/s in advance!') % {
|
||||
'pool_list': pool_list}
|
||||
LOG.error(msg)
|
||||
raise exception.InfortrendNASException(message=msg)
|
||||
|
||||
def _extract_pool_name(self, pool_info):
|
||||
return pool_info['directory'].split('/')[1]
|
||||
|
||||
def _extract_lv_name(self, pool_info):
|
||||
return pool_info['path'].split('/')[2]
|
||||
|
||||
def update_pools_stats(self):
|
||||
pools = []
|
||||
command_line = ['folder', 'status']
|
||||
rc, pools_data = self._execute(command_line)
|
||||
|
||||
for pool_info in pools_data:
|
||||
pool_name = self._extract_pool_name(pool_info)
|
||||
|
||||
if pool_name in self.pool_dict.keys():
|
||||
total_space = float(pool_info['size'])
|
||||
pool_quota_used = self._get_pool_quota_used(pool_name)
|
||||
available_space = total_space - pool_quota_used
|
||||
|
||||
total_capacity_gb = round(_bi_to_gi(total_space), 2)
|
||||
free_capacity_gb = round(_bi_to_gi(available_space), 2)
|
||||
|
||||
pool = {
|
||||
'pool_name': pool_name,
|
||||
'total_capacity_gb': total_capacity_gb,
|
||||
'free_capacity_gb': free_capacity_gb,
|
||||
'reserved_percentage': 0,
|
||||
'qos': False,
|
||||
'dedupe': False,
|
||||
'compression': False,
|
||||
'snapshot_support': False,
|
||||
'thin_provisioning': False,
|
||||
'thick_provisioning': True,
|
||||
'replication_type': None,
|
||||
}
|
||||
pools.append(pool)
|
||||
|
||||
return pools
|
||||
|
||||
def _get_pool_quota_used(self, pool_name):
|
||||
pool_quota_used = 0.0
|
||||
pool_data = self._get_share_pool_data(pool_name)
|
||||
folder_name = self._extract_lv_name(pool_data)
|
||||
|
||||
command_line = ['fquota', 'status', pool_data['id'],
|
||||
folder_name, '-t', 'folder']
|
||||
rc, quota_status = self._execute(command_line)
|
||||
|
||||
for share_quota in quota_status:
|
||||
pool_quota_used += int(share_quota['quota'])
|
||||
|
||||
return pool_quota_used
|
||||
|
||||
def _get_share_pool_data(self, pool_name):
|
||||
if not pool_name:
|
||||
msg = _("Pool is not available in the share host.")
|
||||
raise exception.InvalidHost(reason=msg)
|
||||
|
||||
if pool_name in self.pool_dict.keys():
|
||||
return self.pool_dict[pool_name]
|
||||
else:
|
||||
msg = _('Pool [%(pool_name)s] not set in conf.') % {
|
||||
'pool_name': pool_name}
|
||||
LOG.error(msg)
|
||||
raise exception.InfortrendNASException(err=msg)
|
||||
|
||||
def create_share(self, share, share_server=None):
|
||||
pool_name = share_utils.extract_host(share['host'], level='pool')
|
||||
pool_data = self._get_share_pool_data(pool_name)
|
||||
folder_name = self._extract_lv_name(pool_data)
|
||||
share_proto = share['share_proto'].lower()
|
||||
share_name = share['id'].replace('-', '')
|
||||
share_path = pool_data['path'] + share_name
|
||||
|
||||
command_line = ['folder', 'options', pool_data['id'],
|
||||
folder_name, '-c', share_name]
|
||||
self._execute(command_line)
|
||||
|
||||
self._set_share_size(
|
||||
pool_data['id'], pool_name, share_name, share['size'])
|
||||
self._ensure_protocol_on(share_path, share_proto, share_name)
|
||||
|
||||
LOG.info('Create Share [%(share)s] completed.', {
|
||||
'share': share['id']})
|
||||
|
||||
return self._export_location(
|
||||
share_name, share_proto, pool_data['path'])
|
||||
|
||||
def _export_location(self, share_name, share_proto, pool_path=None):
|
||||
location = []
|
||||
location_data = {
|
||||
'pool_path': pool_path,
|
||||
'share_name': share_name,
|
||||
}
|
||||
self._check_channels_status()
|
||||
for ch in sorted(self.channel_dict.keys()):
|
||||
ip = self.channel_dict[ch]
|
||||
if share_proto == 'nfs':
|
||||
location.append(
|
||||
ip + ':%(pool_path)s%(share_name)s' % location_data)
|
||||
elif share_proto == 'cifs':
|
||||
location.append(
|
||||
'\\\\' + ip + '\\%(share_name)s' % location_data)
|
||||
else:
|
||||
msg = _('Unsupported protocol: [%s].') % share_proto
|
||||
raise exception.InvalidInput(msg)
|
||||
|
||||
return location
|
||||
|
||||
def _set_share_size(self, pool_id, pool_name, share_name, share_size):
|
||||
pool_data = self._get_share_pool_data(pool_name)
|
||||
folder_name = self._extract_lv_name(pool_data)
|
||||
command_line = ['fquota', 'create', pool_id, folder_name,
|
||||
share_name, str(share_size) + 'G', '-t', 'folder']
|
||||
self._execute(command_line)
|
||||
|
||||
LOG.debug('Set Share [%(share_name)s] '
|
||||
'Size [%(share_size)s G] completed.', {
|
||||
'share_name': share_name,
|
||||
'share_size': share_size})
|
||||
return
|
||||
|
||||
def _get_share_size(self, pool_id, pool_name, share_name):
|
||||
share_size = None
|
||||
command_line = ['fquota', 'status', pool_id,
|
||||
share_name, '-t', 'folder']
|
||||
rc, quota_status = self._execute(command_line)
|
||||
|
||||
for share_quota in quota_status:
|
||||
if share_quota['name'] == share_name:
|
||||
share_size = round(_bi_to_gi(float(share_quota['quota'])), 2)
|
||||
break
|
||||
|
||||
return share_size
|
||||
|
||||
def delete_share(self, share, share_server=None):
|
||||
pool_name = share_utils.extract_host(share['host'], level='pool')
|
||||
pool_data = self._get_share_pool_data(pool_name)
|
||||
folder_name = self._extract_lv_name(pool_data)
|
||||
share_name = share['id'].replace('-', '')
|
||||
|
||||
if self._check_share_exist(pool_name, share_name):
|
||||
command_line = ['folder', 'options', pool_data['id'],
|
||||
folder_name, '-d', share_name]
|
||||
self._execute(command_line)
|
||||
else:
|
||||
LOG.warning('Share [%(share_name)s] is already deleted.', {
|
||||
'share_name': share_name})
|
||||
|
||||
LOG.info('Delete Share [%(share)s] completed.', {
|
||||
'share': share['id']})
|
||||
|
||||
def _check_share_exist(self, pool_name, share_name):
|
||||
path = self.pool_dict[pool_name]['path']
|
||||
command_line = ['pagelist', 'folder', path]
|
||||
rc, subfolders = self._execute(command_line)
|
||||
return any(subfolder['name'] == share_name for subfolder in subfolders)
|
||||
|
||||
def update_access(self, share, access_rules, add_rules,
|
||||
delete_rules, share_server=None):
|
||||
self._evict_unauthorized_clients(share, access_rules, share_server)
|
||||
access_dict = {}
|
||||
for access in access_rules:
|
||||
try:
|
||||
self._allow_access(share, access, share_server)
|
||||
except (exception.InfortrendNASException) as e:
|
||||
msg = _('Failed to allow access to client %(access)s, '
|
||||
'reason %(e)s.') % {
|
||||
'access': access['access_to'], 'e': e}
|
||||
LOG.error(msg)
|
||||
access_dict[access['id']] = 'error'
|
||||
|
||||
return access_dict
|
||||
|
||||
def _evict_unauthorized_clients(self, share, access_rules,
|
||||
share_server=None):
|
||||
pool_name = share_utils.extract_host(share['host'], level='pool')
|
||||
pool_data = self._get_share_pool_data(pool_name)
|
||||
share_proto = share['share_proto'].lower()
|
||||
share_name = share['id'].replace('-', '')
|
||||
share_path = pool_data['path'] + share_name
|
||||
|
||||
access_list = []
|
||||
for access in access_rules:
|
||||
access_list.append(access['access_to'])
|
||||
|
||||
if share_proto == 'nfs':
|
||||
host_ip_list = []
|
||||
command_line = ['share', 'status', '-f', share_path]
|
||||
rc, nfs_status = self._execute(command_line)
|
||||
host_list = nfs_status[0]['nfs_detail']['hostList']
|
||||
for host in host_list:
|
||||
if host['host'] != '*':
|
||||
host_ip_list.append(host['host'])
|
||||
for ip in host_ip_list:
|
||||
if ip not in access_list:
|
||||
command_line = ['share', 'options', share_path,
|
||||
'nfs', '-c', ip]
|
||||
try:
|
||||
self._execute(command_line)
|
||||
except exception.InfortrendNASException:
|
||||
msg = _("Failed to remove share access rule %s") % (ip)
|
||||
LOG.exception(msg)
|
||||
pass
|
||||
|
||||
elif share_proto == 'cifs':
|
||||
host_user_list = []
|
||||
command_line = ['acl', 'get', share_path]
|
||||
rc, cifs_status = self._execute(command_line)
|
||||
for cifs_rule in cifs_status:
|
||||
if cifs_rule['name']:
|
||||
host_user_list.append(cifs_rule['name'])
|
||||
for user in host_user_list:
|
||||
if user not in access_list:
|
||||
command_line = ['acl', 'delete', share_path, '-u', user]
|
||||
try:
|
||||
self._execute(command_line)
|
||||
except exception.InfortrendNASException:
|
||||
msg = _("Failed to remove share access rule %s") % (
|
||||
user)
|
||||
LOG.exception(msg)
|
||||
pass
|
||||
|
||||
def _allow_access(self, share, access, share_server=None):
|
||||
pool_name = share_utils.extract_host(share['host'], level='pool')
|
||||
pool_data = self._get_share_pool_data(pool_name)
|
||||
share_name = share['id'].replace('-', '')
|
||||
share_path = pool_data['path'] + share_name
|
||||
share_proto = share['share_proto'].lower()
|
||||
access_type = access['access_type']
|
||||
access_level = access['access_level'] or constants.ACCESS_LEVEL_RW
|
||||
access_to = access['access_to']
|
||||
ACCESS_LEVEL_MAP = {access_level: access_level}
|
||||
msg = self._check_access_legal(share_proto, access_type)
|
||||
if msg:
|
||||
raise exception.InvalidShareAccess(reason=msg)
|
||||
|
||||
if share_proto == 'nfs':
|
||||
command_line = ['share', 'options', share_path, 'nfs',
|
||||
'-h', access_to, '-p', access_level]
|
||||
self._execute(command_line)
|
||||
|
||||
elif share_proto == 'cifs':
|
||||
if not self._check_user_exist(access_to):
|
||||
msg = _('Please create user [%(user)s] in advance.') % {
|
||||
'user': access_to}
|
||||
LOG.error(msg)
|
||||
raise exception.InfortrendNASException(err=msg)
|
||||
|
||||
if access_level == constants.ACCESS_LEVEL_RW:
|
||||
cifs_access = 'f'
|
||||
elif access_level == constants.ACCESS_LEVEL_RO:
|
||||
cifs_access = 'r'
|
||||
try:
|
||||
access_level = ACCESS_LEVEL_MAP[access_level]
|
||||
except KeyError:
|
||||
msg = _('Unsupported access_level: [%s].') % access_level
|
||||
raise exception.InvalidInput(msg)
|
||||
|
||||
command_line = ['acl', 'set', share_path,
|
||||
'-u', access_to, '-a', cifs_access]
|
||||
self._execute(command_line)
|
||||
|
||||
LOG.info('Share [%(share)s] access to [%(access_to)s] '
|
||||
'level [%(level)s] protocol [%(share_proto)s] completed.', {
|
||||
'share': share['id'],
|
||||
'access_to': access_to,
|
||||
'level': access_level,
|
||||
'share_proto': share_proto})
|
||||
|
||||
def _ensure_protocol_on(self, share_path, share_proto, cifs_name):
|
||||
if not self._check_proto_enabled(share_path, share_proto):
|
||||
command_line = ['share', share_path, share_proto, 'on']
|
||||
if share_proto == 'cifs':
|
||||
command_line.extend(['-n', cifs_name])
|
||||
self._execute(command_line)
|
||||
|
||||
def _check_proto_enabled(self, share_path, share_proto):
|
||||
command_line = ['share', 'status', '-f', share_path]
|
||||
rc, share_status = self._execute(command_line)
|
||||
if share_status:
|
||||
check_enabled = share_status[0][share_proto]
|
||||
if check_enabled:
|
||||
return True
|
||||
return False
|
||||
|
||||
def _check_user_exist(self, user_name):
|
||||
command_line = ['useradmin', 'user', 'list']
|
||||
rc, user_list = self._execute(command_line)
|
||||
for user in user_list:
|
||||
if user['Name'] == user_name:
|
||||
return True
|
||||
return False
|
||||
|
||||
def _check_access_legal(self, share_proto, access_type):
|
||||
msg = None
|
||||
if share_proto == 'cifs' and access_type != 'user':
|
||||
msg = _('Infortrend CIFS share only supports USER access type.')
|
||||
elif share_proto == 'nfs' and access_type != 'ip':
|
||||
msg = _('Infortrend NFS share only supports IP access type.')
|
||||
elif share_proto not in ('nfs', 'cifs'):
|
||||
msg = _('Unsupported share protocol [%s].') % share_proto
|
||||
return msg
|
||||
|
||||
def get_pool(self, share):
|
||||
pool_name = share_utils.extract_host(share['host'], level='pool')
|
||||
if not pool_name:
|
||||
share_name = share['id'].replace('-', '')
|
||||
for pool in self.pool_dict.keys():
|
||||
if self._check_share_exist(pool, share_name):
|
||||
pool_name = pool
|
||||
break
|
||||
return pool_name
|
||||
|
||||
def ensure_share(self, share, share_server=None):
|
||||
share_proto = share['share_proto'].lower()
|
||||
pool_name = share_utils.extract_host(share['host'], level='pool')
|
||||
pool_data = self._get_share_pool_data(pool_name)
|
||||
share_name = share['id'].replace('-', '')
|
||||
return self._export_location(
|
||||
share_name, share_proto, pool_data['path'])
|
||||
|
||||
def extend_share(self, share, new_size, share_server=None):
|
||||
pool_name = share_utils.extract_host(share['host'], level='pool')
|
||||
pool_data = self._get_share_pool_data(pool_name)
|
||||
share_name = share['id'].replace('-', '')
|
||||
self._set_share_size(pool_data['id'], pool_name, share_name, new_size)
|
||||
|
||||
LOG.info('Successfully Extend Share [%(share)s] '
|
||||
'to size [%(new_size)s G].', {
|
||||
'share': share['id'],
|
||||
'new_size': new_size})
|
||||
|
||||
def shrink_share(self, share, new_size, share_server=None):
|
||||
pool_name = share_utils.extract_host(share['host'], level='pool')
|
||||
pool_data = self._get_share_pool_data(pool_name)
|
||||
share_name = share['id'].replace('-', '')
|
||||
folder_name = self._extract_lv_name(pool_data)
|
||||
|
||||
command_line = ['fquota', 'status', pool_data['id'],
|
||||
folder_name, '-t', 'folder']
|
||||
rc, quota_status = self._execute(command_line)
|
||||
|
||||
for share_quota in quota_status:
|
||||
if share_quota['name'] == share_name:
|
||||
used_space = round(_bi_to_gi(float(share_quota['used'])), 2)
|
||||
|
||||
if new_size < used_space:
|
||||
raise exception.ShareShrinkingPossibleDataLoss(
|
||||
share_id=share['id'])
|
||||
|
||||
self._set_share_size(pool_data['id'], pool_name, share_name, new_size)
|
||||
|
||||
LOG.info('Successfully Shrink Share [%(share)s] '
|
||||
'to size [%(new_size)s G].', {
|
||||
'share': share['id'],
|
||||
'new_size': new_size})
|
||||
|
||||
def manage_existing(self, share, driver_options):
|
||||
share_proto = share['share_proto'].lower()
|
||||
pool_name = share_utils.extract_host(share['host'], level='pool')
|
||||
pool_data = self._get_share_pool_data(pool_name)
|
||||
volume_name = self._extract_lv_name(pool_data)
|
||||
input_location = share['export_locations'][0]['path']
|
||||
share_name = share['id'].replace('-', '')
|
||||
|
||||
ch_ip, folder_name = self._parse_location(input_location, share_proto)
|
||||
|
||||
if not self._check_channel_ip(ch_ip):
|
||||
msg = _('Export location ip: [%(ch_ip)s] '
|
||||
'is incorrect, please use data port ip.') % {
|
||||
'ch_ip': ch_ip}
|
||||
LOG.error(msg)
|
||||
raise exception.InfortrendNASException(err=msg)
|
||||
|
||||
if not self._check_share_exist(pool_name, folder_name):
|
||||
msg = _('Can not find folder [%(folder_name)s] '
|
||||
'in pool [%(pool_name)s].') % {
|
||||
'folder_name': folder_name,
|
||||
'pool_name': pool_name}
|
||||
LOG.error(msg)
|
||||
raise exception.InfortrendNASException(err=msg)
|
||||
|
||||
share_path = pool_data['path'] + folder_name
|
||||
self._ensure_protocol_on(share_path, share_proto, share_name)
|
||||
share_size = self._get_share_size(
|
||||
pool_data['id'], pool_name, folder_name)
|
||||
|
||||
if not share_size:
|
||||
msg = _('Folder [%(folder_name)s] has no size limitation, '
|
||||
'please set it first for Openstack management.') % {
|
||||
'folder_name': folder_name}
|
||||
LOG.error(msg)
|
||||
raise exception.InfortrendNASException(err=msg)
|
||||
|
||||
# rename folder name
|
||||
command_line = ['folder', 'options', pool_data['id'], volume_name,
|
||||
'-k', folder_name, share_name]
|
||||
self._execute(command_line)
|
||||
|
||||
location = self._export_location(
|
||||
share_name, share_proto, pool_data['path'])
|
||||
|
||||
LOG.info('Successfully Manage Infortrend Share [%(folder_name)s], '
|
||||
'Size: [%(size)s G], Protocol: [%(share_proto)s], '
|
||||
'new name: [%(share_name)s].', {
|
||||
'folder_name': folder_name,
|
||||
'size': share_size,
|
||||
'share_proto': share_proto,
|
||||
'share_name': share_name})
|
||||
|
||||
return {'size': share_size, 'export_locations': location}
|
||||
|
||||
def _parse_location(self, input_location, share_proto):
|
||||
ip = None
|
||||
folder_name = None
|
||||
pattern_ip = '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}'
|
||||
if share_proto == 'nfs':
|
||||
pattern_folder = '[^\/]+$'
|
||||
ip = "".join(re.findall(pattern_ip, input_location))
|
||||
folder_name = "".join(re.findall(pattern_folder, input_location))
|
||||
|
||||
elif share_proto == 'cifs':
|
||||
pattern_folder = '[^\\\]+$'
|
||||
ip = "".join(re.findall(pattern_ip, input_location))
|
||||
folder_name = "".join(re.findall(pattern_folder, input_location))
|
||||
|
||||
if not (ip and folder_name):
|
||||
msg = _('Export location error, please check '
|
||||
'ip: [%(ip)s], folder_name: [%(folder_name)s].') % {
|
||||
'ip': ip,
|
||||
'folder_name': folder_name}
|
||||
LOG.error(msg)
|
||||
raise exception.InfortrendNASException(err=msg)
|
||||
|
||||
return ip, folder_name
|
||||
|
||||
def _check_channel_ip(self, channel_ip):
|
||||
return any(ip == channel_ip for ip in self.channel_dict.values())
|
||||
|
||||
def unmanage(self, share):
|
||||
pool_name = share_utils.extract_host(share['host'], level='pool')
|
||||
share_name = share['id'].replace('-', '')
|
||||
|
||||
if not self._check_share_exist(pool_name, share_name):
|
||||
LOG.warning('Share [%(share_name)s] does not exist.', {
|
||||
'share_name': share_name})
|
||||
return
|
||||
|
||||
LOG.info('Successfully Unmanaged Share [%(share)s].', {
|
||||
'share': share['id']})
|
@ -65,6 +65,10 @@ def set_defaults(conf):
|
||||
_safe_set_of_opts(conf, 'instorage_nas_password', 'password')
|
||||
_safe_set_of_opts(conf, 'instorage_nas_pools', 'pool0')
|
||||
|
||||
_safe_set_of_opts(conf, 'infortrend_nas_ip', '172.27.1.1')
|
||||
_safe_set_of_opts(conf, 'infortrend_share_pools', 'share-pool-01')
|
||||
_safe_set_of_opts(conf, 'infortrend_share_channels', '0,1')
|
||||
|
||||
_safe_set_of_opts(conf, 'qnap_management_url', 'http://1.2.3.4:8080')
|
||||
_safe_set_of_opts(conf, 'qnap_share_ip', '1.2.3.4')
|
||||
_safe_set_of_opts(conf, 'qnap_nas_login', 'admin')
|
||||
|
0
manila/tests/share/drivers/infortrend/__init__.py
Normal file
0
manila/tests/share/drivers/infortrend/__init__.py
Normal file
@ -0,0 +1,408 @@
|
||||
# Copyright (c) 2019 Infortrend Technology, Inc.
|
||||
# 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.
|
||||
|
||||
|
||||
class InfortrendManilaTestData(object):
|
||||
|
||||
fake_share_id = ['4d6984fd-8572-4467-964f-24936a8c4ea2', # NFS
|
||||
'a7b933e6-bb77-4823-a86f-f2c3ab41a8a5'] # CIFS
|
||||
|
||||
fake_id = ['iftt8862-2226-0126-7610-chengweichou',
|
||||
'987c8763-3333-4444-5555-666666666666']
|
||||
|
||||
fake_share_nfs = {
|
||||
'share_id': fake_share_id[0],
|
||||
'availability_zone': 'nova',
|
||||
'terminated_at': 'datetime.datetime(2017, 5, 8, 8, 27, 25)',
|
||||
'availability_zone_id': 'fd32d76d-b5a8-4c5c-93d7-8f09fc2a8ad3',
|
||||
'updated_at': 'datetime.datetime(2017, 5, 8, 8, 27, 25)',
|
||||
'share_network_id': None,
|
||||
'export_locations': [],
|
||||
'share_server_id': None,
|
||||
'snapshot_id': None,
|
||||
'deleted_at': None,
|
||||
'id': '5a0aa06e-1c57-4996-be46-b81e360e8866',
|
||||
'size': 30,
|
||||
'replica_state': None,
|
||||
'user_id': '4944594433f0405588928a4212964658',
|
||||
'export_location': '172.27.112.223:/share-pool-01/LV-1/' +
|
||||
fake_share_id[0],
|
||||
'display_description': None,
|
||||
'consistency_group_id': None,
|
||||
'project_id': '0e63326c50a246ac81fa1a0c8e003d5b',
|
||||
'launched_at': 'datetime.datetime(2017, 5, 8, 8, 23, 33)',
|
||||
'scheduled_at': 'datetime.datetime(2017, 5, 8, 8, 23, 29)',
|
||||
'status': 'deleting',
|
||||
'share_type_id': '23d8c637-0192-47fa-b921-958f22ed772f',
|
||||
'deleted': 'False',
|
||||
'host': 'compute@ift-manila#share-pool-01',
|
||||
'access_rules_status': 'active',
|
||||
'display_name': 'nfs-01',
|
||||
'name': 'share-5a0aa06e-1c57-4996-be46-b81e360e8866',
|
||||
'created_at': 'datetime.datetime(2017, 5, 8, 8, 23, 29)',
|
||||
'share_proto': 'NFS',
|
||||
'is_public': False,
|
||||
'source_cgsnapshot_member_id': None
|
||||
}
|
||||
|
||||
fake_share_cifs = {
|
||||
'share_id': fake_share_id[1],
|
||||
'availability_zone': 'nova',
|
||||
'terminated_at': None,
|
||||
'availability_zone_id': 'fd32d76d-b5a8-4c5c-93d7-8f09fc2a8ad3',
|
||||
'updated_at': 'datetime.datetime(2017, 5, 9, 2, 28, 35)',
|
||||
'share_network_id': None,
|
||||
'export_locations': [],
|
||||
'share_server_id': None,
|
||||
'snapshot_id': None,
|
||||
'deleted_at': None,
|
||||
'id': 'aac4fe64-7a9c-472a-b156-9adbb50b4d29',
|
||||
'size': 50,
|
||||
'replica_state': None,
|
||||
'user_id': '4944594433f0405588928a4212964658',
|
||||
'export_location': None,
|
||||
'display_description': None,
|
||||
'consistency_group_id': None,
|
||||
'project_id': '0e63326c50a246ac81fa1a0c8e003d5b',
|
||||
'launched_at': None,
|
||||
'scheduled_at': 'datetime.datetime(2017, 5, 9, 2, 28, 35)',
|
||||
'status': 'creating',
|
||||
'share_type_id': '23d8c637-0192-47fa-b921-958f22ed772f',
|
||||
'deleted': 'False',
|
||||
'host': 'compute@ift-manila#share-pool-01',
|
||||
'access_rules_status': 'active',
|
||||
'display_name': 'cifs-01',
|
||||
'name': 'share-aac4fe64-7a9c-472a-b156-9adbb50b4d29',
|
||||
'created_at': 'datetime.datetime(2017, 5, 9, 2, 28, 35)',
|
||||
'share_proto': 'CIFS',
|
||||
'is_public': False,
|
||||
'source_cgsnapshot_member_id': None
|
||||
}
|
||||
|
||||
fake_share_cifs_no_host = {
|
||||
'share_id': fake_share_id[1],
|
||||
'availability_zone': 'nova',
|
||||
'terminated_at': None,
|
||||
'availability_zone_id': 'fd32d76d-b5a8-4c5c-93d7-8f09fc2a8ad3',
|
||||
'updated_at': 'datetime.datetime(2017, 5, 9, 2, 28, 35)',
|
||||
'share_network_id': None,
|
||||
'export_locations': [],
|
||||
'share_server_id': None,
|
||||
'snapshot_id': None,
|
||||
'deleted_at': None,
|
||||
'id': 'aac4fe64-7a9c-472a-b156-9adbb50b4d29',
|
||||
'size': 50,
|
||||
'replica_state': None,
|
||||
'user_id': '4944594433f0405588928a4212964658',
|
||||
'export_location': None,
|
||||
'display_description': None,
|
||||
'consistency_group_id': None,
|
||||
'project_id': '0e63326c50a246ac81fa1a0c8e003d5b',
|
||||
'launched_at': None,
|
||||
'scheduled_at': 'datetime.datetime(2017, 5, 9, 2, 28, 35)',
|
||||
'status': 'creating',
|
||||
'share_type_id': '23d8c637-0192-47fa-b921-958f22ed772f',
|
||||
'deleted': 'False',
|
||||
'host': '',
|
||||
'access_rules_status': 'active',
|
||||
'display_name': 'cifs-01',
|
||||
'name': 'share-aac4fe64-7a9c-472a-b156-9adbb50b4d29',
|
||||
'created_at': 'datetime.datetime(2017, 5, 9, 2, 28, 35)',
|
||||
'share_proto': 'CIFS',
|
||||
'is_public': False,
|
||||
'source_cgsnapshot_member_id': None
|
||||
}
|
||||
|
||||
fake_non_exist_share = {
|
||||
'share_id': fake_id[0],
|
||||
'availability_zone': 'nova',
|
||||
'terminated_at': 'datetime.datetime(2017, 5, 8, 8, 27, 25)',
|
||||
'availability_zone_id': 'fd32d76d-b5a8-4c5c-93d7-8f09fc2a8ad3',
|
||||
'updated_at': 'datetime.datetime(2017, 5, 8, 8, 27, 25)',
|
||||
'share_network_id': None,
|
||||
'export_locations': [],
|
||||
'share_server_id': None,
|
||||
'snapshot_id': None,
|
||||
'deleted_at': None,
|
||||
'id': fake_id[1],
|
||||
'size': 30,
|
||||
'replica_state': None,
|
||||
'user_id': '4944594433f0405588928a4212964658',
|
||||
'export_location': '172.27.112.223:/share-pool-01/LV-1/' +
|
||||
fake_id[0],
|
||||
'display_description': None,
|
||||
'consistency_group_id': None,
|
||||
'project_id': '0e63326c50a246ac81fa1a0c8e003d5b',
|
||||
'launched_at': 'datetime.datetime(2017, 5, 8, 8, 23, 33)',
|
||||
'scheduled_at': 'datetime.datetime(2017, 5, 8, 8, 23, 29)',
|
||||
'status': 'available',
|
||||
'share_type_id': '23d8c637-0192-47fa-b921-958f22ed772f',
|
||||
'deleted': 'False',
|
||||
'host': 'compute@ift-manila#share-pool-01',
|
||||
'access_rules_status': 'active',
|
||||
'display_name': 'nfs-01',
|
||||
'name': 'share-5a0aa06e-1c57-4996-be46-b81e360e8866',
|
||||
'created_at': 'datetime.datetime(2017, 5, 8, 8, 23, 29)',
|
||||
'share_proto': 'NFS',
|
||||
'is_public': False,
|
||||
'source_cgsnapshot_member_id': None
|
||||
}
|
||||
|
||||
fake_access_rules_nfs = [{
|
||||
'share_id': fake_share_id[0],
|
||||
'deleted': 'False',
|
||||
'created_at': 'datetime.datetime(2017, 5, 9, 8, 41, 21)',
|
||||
'updated_at': None,
|
||||
'access_type': 'ip',
|
||||
'access_to': '172.27.1.1',
|
||||
'access_level': 'rw',
|
||||
'instance_mappings': [],
|
||||
'deleted_at': None,
|
||||
'id': 'fa60b50f-1428-44a2-9931-7e31f0c5b033'}, {
|
||||
'share_id': fake_share_id[0],
|
||||
'deleted': 'False',
|
||||
'created_at': 'datetime.datetime(2017, 5, 9, 8, 45, 37)',
|
||||
'updated_at': None,
|
||||
'access_type': 'ip',
|
||||
'access_to': '172.27.1.2',
|
||||
'access_level': 'rw',
|
||||
'instance_mappings': [],
|
||||
'deleted_at': None,
|
||||
'id': '9bcdd5e6-11c7-4f8f-939c-84fa2f3334bc'
|
||||
}]
|
||||
|
||||
fake_rule_ip_1 = [{
|
||||
'share_id': fake_share_id[0],
|
||||
'deleted': 'False',
|
||||
'created_at': 'datetime.datetime(2017, 5, 9, 8, 41, 21)',
|
||||
'updated_at': None,
|
||||
'access_type': 'ip',
|
||||
'access_to': '172.27.1.1',
|
||||
'access_level': 'rw',
|
||||
'instance_mappings': [],
|
||||
'deleted_at': None,
|
||||
'id': 'fa60b50f-1428-44a2-9931-7e31f0c5b033'
|
||||
}]
|
||||
|
||||
fake_rule_ip_2 = [{
|
||||
'share_id': fake_share_id[0],
|
||||
'deleted': 'False',
|
||||
'created_at': 'datetime.datetime(2017, 5, 9, 8, 45, 37)',
|
||||
'updated_at': None,
|
||||
'access_type': 'ip',
|
||||
'access_to': '172.27.1.2',
|
||||
'access_level': 'rw',
|
||||
'instance_mappings': [],
|
||||
'deleted_at': None,
|
||||
'id': '9bcdd5e6-11c7-4f8f-939c-84fa2f3334bc'
|
||||
}]
|
||||
|
||||
fake_access_rules_cifs = [{
|
||||
'share_id': fake_share_id[1],
|
||||
'deleted': 'False',
|
||||
'created_at': 'datetime.datetime(2017, 5, 9, 9, 39, 18)',
|
||||
'updated_at': None,
|
||||
'access_type': 'user',
|
||||
'access_to': 'user02',
|
||||
'access_level': 'ro',
|
||||
'instance_mappings': [],
|
||||
'deleted_at': None,
|
||||
'id': '6e8bc969-51c9-4bbb-8e8b-020dc5fec81e'}, {
|
||||
'share_id': fake_share_id[1],
|
||||
'deleted': 'False',
|
||||
'created_at': 'datetime.datetime(2017, 5, 9, 9, 38, 59)',
|
||||
'updated_at': None,
|
||||
'access_type': 'user',
|
||||
'access_to': 'user01',
|
||||
'access_level': 'rw',
|
||||
'instance_mappings': [],
|
||||
'deleted_at': None,
|
||||
'id': '0cd9926d-fac4-4122-a523-538e98752e78'
|
||||
}]
|
||||
|
||||
fake_rule_user01 = [{
|
||||
'share_id': fake_share_id[1],
|
||||
'deleted': 'False',
|
||||
'created_at': 'datetime.datetime(2017, 5, 9, 9, 38, 59)',
|
||||
'updated_at': None,
|
||||
'access_type': 'user',
|
||||
'access_to': 'user01',
|
||||
'access_level': 'rw',
|
||||
'instance_mappings': [],
|
||||
'deleted_at': None,
|
||||
'id': '0cd9926d-fac4-4122-a523-538e98752e78'
|
||||
}]
|
||||
|
||||
fake_rule_user02 = [{
|
||||
'share_id': fake_share_id[1],
|
||||
'deleted': 'False',
|
||||
'created_at': 'datetime.datetime(2017, 5, 9, 9, 39, 18)',
|
||||
'updated_at': None,
|
||||
'access_type': 'user',
|
||||
'access_to': 'user02',
|
||||
'access_level': 'ro',
|
||||
'instance_mappings': [],
|
||||
'deleted_at': None,
|
||||
'id': '6e8bc969-51c9-4bbb-8e8b-020dc5fec81e'
|
||||
}]
|
||||
|
||||
fake_rule_user03 = [{
|
||||
'share_id': fake_id[0],
|
||||
'deleted': 'False',
|
||||
'created_at': 'datetime.datetime(2017, 5, 9, 9, 39, 18)',
|
||||
'updated_at': None,
|
||||
'access_type': 'user',
|
||||
'access_to': 'user03',
|
||||
'access_level': 'rw',
|
||||
'instance_mappings': [],
|
||||
'deleted_at': None,
|
||||
'id': fake_id[1]
|
||||
}]
|
||||
|
||||
fake_share_for_manage_nfs = {
|
||||
'share_id': '419ab73c-c0fc-4e73-b56a-70756e0b6d27',
|
||||
'availability_zone': None,
|
||||
'terminated_at': None,
|
||||
'availability_zone_id': None,
|
||||
'updated_at': None,
|
||||
'share_network_id': None,
|
||||
'export_locations': [{
|
||||
'uuid': '0ebd59e4-e65e-4fda-9457-320375efd0be',
|
||||
'deleted': 0,
|
||||
'created_at': 'datetime.datetime(2017, 5, 10, 10, 0, 3)',
|
||||
'updated_at': 'datetime.datetime(2017, 5, 10, 10, 0, 3)',
|
||||
'is_admin_only': False,
|
||||
'share_instance_id': 'd3cfe195-85cf-41e6-be4f-a96f7e7db192',
|
||||
'path': '172.27.112.223:/share-pool-01/LV-1/test-folder',
|
||||
'el_metadata': {},
|
||||
'deleted_at': None,
|
||||
'id': 83
|
||||
}],
|
||||
'share_server_id': None,
|
||||
'snapshot_id': None,
|
||||
'deleted_at': None,
|
||||
'id': '615ac1ed-e808-40b5-8d7b-87018c6f66eb',
|
||||
'size': None,
|
||||
'replica_state': None,
|
||||
'user_id': '4944594433f0405588928a4212964658',
|
||||
'export_location': '172.27.112.223:/share-pool-01/LV-1/test-folder',
|
||||
'display_description': '',
|
||||
'consistency_group_id': None,
|
||||
'project_id': '0e63326c50a246ac81fa1a0c8e003d5b',
|
||||
'launched_at': None,
|
||||
'scheduled_at': 'datetime.datetime(2017, 5, 10, 9, 22, 5)',
|
||||
'status': 'manage_starting',
|
||||
'share_type_id': '23d8c637-0192-47fa-b921-958f22ed772f',
|
||||
'deleted': 'False',
|
||||
'host': 'compute@ift-manila#share-pool-01',
|
||||
'access_rules_status': 'active',
|
||||
'display_name': 'test-manage',
|
||||
'name': 'share-615ac1ed-e808-40b5-8d7b-87018c6f66eb',
|
||||
'created_at': 'datetime.datetime(2017, 5, 10, 9, 22, 5)',
|
||||
'share_proto': 'NFS',
|
||||
'is_public': False,
|
||||
'source_cgsnapshot_member_id': None
|
||||
}
|
||||
|
||||
def _get_fake_share_for_manage(self, location=''):
|
||||
return {
|
||||
'share_id': '419ab73c-c0fc-4e73-b56a-70756e0b6d27',
|
||||
'availability_zone': None,
|
||||
'terminated_at': None,
|
||||
'availability_zone_id': None,
|
||||
'updated_at': None,
|
||||
'share_network_id': None,
|
||||
'export_locations': [{
|
||||
'uuid': '0ebd59e4-e65e-4fda-9457-320375efd0be',
|
||||
'deleted': 0,
|
||||
'created_at': 'datetime.datetime(2017, 5, 10, 10, 0, 3)',
|
||||
'updated_at': 'datetime.datetime(2017, 5, 10, 10, 0, 3)',
|
||||
'is_admin_only': False,
|
||||
'share_instance_id': 'd3cfe195-85cf-41e6-be4f-a96f7e7db192',
|
||||
'path': location,
|
||||
'el_metadata': {},
|
||||
'deleted_at': None,
|
||||
'id': 83
|
||||
}],
|
||||
'share_server_id': None,
|
||||
'snapshot_id': None,
|
||||
'deleted_at': None,
|
||||
'id': '615ac1ed-e808-40b5-8d7b-87018c6f66eb',
|
||||
'size': None,
|
||||
'replica_state': None,
|
||||
'user_id': '4944594433f0405588928a4212964658',
|
||||
'export_location': location,
|
||||
'display_description': '',
|
||||
'consistency_group_id': None,
|
||||
'project_id': '0e63326c50a246ac81fa1a0c8e003d5b',
|
||||
'launched_at': None,
|
||||
'scheduled_at': 'datetime.datetime(2017, 5, 10, 9, 22, 5)',
|
||||
'status': 'manage_starting',
|
||||
'share_type_id': '23d8c637-0192-47fa-b921-958f22ed772f',
|
||||
'deleted': 'False',
|
||||
'host': 'compute@ift-manila#share-pool-01',
|
||||
'access_rules_status': 'active',
|
||||
'display_name': 'test-manage',
|
||||
'name': 'share-615ac1ed-e808-40b5-8d7b-87018c6f66eb',
|
||||
'created_at': 'datetime.datetime(2017, 5, 10, 9, 22, 5)',
|
||||
'share_proto': 'NFS',
|
||||
'is_public': False,
|
||||
'source_cgsnapshot_member_id': None
|
||||
}
|
||||
|
||||
fake_share_for_manage_cifs = {
|
||||
'share_id': '3a1222d3-c981-490a-9390-4d560ced68eb',
|
||||
'availability_zone': None,
|
||||
'terminated_at': None,
|
||||
'availability_zone_id': None,
|
||||
'updated_at': None,
|
||||
'share_network_id': None,
|
||||
'export_locations': [{
|
||||
'uuid': '0ebd59e4-e65e-4fda-9457-320375efd0de',
|
||||
'deleted': 0,
|
||||
'created_at': 'datetime.datetime(2017, 5, 11, 10, 10, 3)',
|
||||
'updated_at': 'datetime.datetime(2017, 5, 11, 10, 10, 3)',
|
||||
'is_admin_only': False,
|
||||
'share_instance_id': 'd3cfe195-85cf-41e6-be4f-a96f7e7db192',
|
||||
'path': '\\\\172.27.113.209\\test-folder-02',
|
||||
'el_metadata': {},
|
||||
'deleted_at': None,
|
||||
'id': 87
|
||||
}],
|
||||
'share_server_id': None,
|
||||
'snapshot_id': None,
|
||||
'deleted_at': None,
|
||||
'id': 'd156baf7-5422-4c9b-8c78-ee7943d000ec',
|
||||
'size': None,
|
||||
'replica_state': None,
|
||||
'user_id': '4944594433f0405588928a4212964658',
|
||||
'export_location': '\\\\172.27.113.209\\test-folder-02',
|
||||
'display_description': '',
|
||||
'consistency_group_id': None,
|
||||
'project_id': '0e63326c50a246ac81fa1a0c8e003d5b',
|
||||
'launched_at': None,
|
||||
'scheduled_at': 'datetime.datetime(2017, 5, 11, 3, 7, 59)',
|
||||
'status': 'manage_starting',
|
||||
'share_type_id': '23d8c637-0192-47fa-b921-958f22ed772f',
|
||||
'deleted': 'False',
|
||||
'host': 'compute@ift-manila#share-pool-01',
|
||||
'access_rules_status': 'active',
|
||||
'display_name': 'test-manage-02',
|
||||
'name': 'share-d156baf7-5422-4c9b-8c78-ee7943d000ec',
|
||||
'created_at': 'datetime.datetime(2017, 5, 11, 3, 7, 59)',
|
||||
'share_proto': 'CIFS',
|
||||
'is_public': False,
|
||||
'source_cgsnapshot_member_id': None
|
||||
}
|
@ -0,0 +1,416 @@
|
||||
# Copyright (c) 2019 Infortrend Technology, Inc.
|
||||
# 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.
|
||||
|
||||
|
||||
class InfortrendNASTestData(object):
|
||||
|
||||
fake_share_id = ['5a0aa06e-1c57-4996-be46-b81e360e8866', # NFS
|
||||
'aac4fe64-7a9c-472a-b156-9adbb50b4d29'] # CIFS
|
||||
|
||||
fake_share_name = [fake_share_id[0].replace('-', ''),
|
||||
fake_share_id[1].replace('-', '')]
|
||||
|
||||
fake_channel_ip = ['172.27.112.223', '172.27.113.209']
|
||||
|
||||
fake_service_status_data = ('(64175, 1234, 272, 0)\n\n'
|
||||
'{"cliCode": '
|
||||
'[{"Return": "0x0000", "CLI": "Successful"}], '
|
||||
'"returnCode": [], '
|
||||
'"data": '
|
||||
'[{"A": '
|
||||
'{"NFS": '
|
||||
'{"displayName": "NFS", '
|
||||
'"state_time": "2017-05-04 14:19:53", '
|
||||
'"enabled": true, '
|
||||
'"cpu_rate": "0.0", '
|
||||
'"mem_rate": "0.0", '
|
||||
'"state": "exited", '
|
||||
'"type": "share"}}}]}\n\n')
|
||||
|
||||
fake_folder_status_data = ('(64175, 1234, 1017, 0)\n\n'
|
||||
'{"cliCode": '
|
||||
'[{"Return": "0x0000", "CLI": "Successful"}], '
|
||||
'"returnCode": [], '
|
||||
'"data": '
|
||||
'[{"utility": "1.00", '
|
||||
'"used": "33886208", '
|
||||
'"subshare": true, '
|
||||
'"share": false, '
|
||||
'"worm": "", '
|
||||
'"free": "321931374592", '
|
||||
'"fsType": "xfs", '
|
||||
'"owner": "A", '
|
||||
'"readOnly": false, '
|
||||
'"modifyTime": "2017-04-27 16:16", '
|
||||
'"directory": "/share-pool-01/LV-1", '
|
||||
'"volumeId": "6541BAFB2E6C57B6", '
|
||||
'"mounted": true, '
|
||||
'"size": "321965260800"}, '
|
||||
'{"utility": "1.00", '
|
||||
'"used": "33779712", '
|
||||
'"subshare": false, '
|
||||
'"share": false, '
|
||||
'"worm": "", '
|
||||
'"free": "107287973888", '
|
||||
'"fsType": "xfs", '
|
||||
'"owner": "A", '
|
||||
'"readOnly": false, '
|
||||
'"modifyTime": "2017-04-27 15:45", '
|
||||
'"directory": "/share-pool-02/LV-1", '
|
||||
'"volumeId": "147A8FB67DA39914", '
|
||||
'"mounted": true, '
|
||||
'"size": "107321753600"}]}\n\n')
|
||||
|
||||
fake_nfs_status_off = [{
|
||||
'A': {
|
||||
'NFS': {
|
||||
'displayName': 'NFS',
|
||||
'state_time': '2017-05-04 14:19:53',
|
||||
'enabled': False,
|
||||
'cpu_rate': '0.0',
|
||||
'mem_rate': '0.0',
|
||||
'state': 'exited',
|
||||
'type': 'share',
|
||||
}
|
||||
}
|
||||
}]
|
||||
|
||||
fake_folder_status = [{
|
||||
'utility': '1.00',
|
||||
'used': '33886208',
|
||||
'subshare': True,
|
||||
'share': False,
|
||||
'worm': '',
|
||||
'free': '321931374592',
|
||||
'fsType': 'xfs',
|
||||
'owner': 'A',
|
||||
'readOnly': False,
|
||||
'modifyTime': '2017-04-27 16:16',
|
||||
'directory': '/share-pool-01/LV-1',
|
||||
'volumeId': '6541BAFB2E6C57B6',
|
||||
'mounted': True,
|
||||
'size': '321965260800'}, {
|
||||
'utility': '1.00',
|
||||
'used': '33779712',
|
||||
'subshare': False,
|
||||
'share': False,
|
||||
'worm': '',
|
||||
'free': '107287973888',
|
||||
'fsType': 'xfs',
|
||||
'owner': 'A',
|
||||
'readOnly': False,
|
||||
'modifyTime': '2017-04-27 15:45',
|
||||
'directory': '/share-pool-02/LV-1',
|
||||
'volumeId': '147A8FB67DA39914',
|
||||
'mounted': True,
|
||||
'size': '107321753600',
|
||||
}]
|
||||
|
||||
def fake_get_channel_status(self, ch1_status='UP'):
|
||||
return [{
|
||||
'datalink': 'mgmt0',
|
||||
'status': 'UP',
|
||||
'typeConfig': 'DHCP',
|
||||
'IP': '172.27.112.125',
|
||||
'MAC': '00:d0:23:00:15:a6',
|
||||
'netmask': '255.255.240.0',
|
||||
'type': 'dhcp',
|
||||
'gateway': '172.27.127.254'}, {
|
||||
'datalink': 'CH0',
|
||||
'status': 'UP',
|
||||
'typeConfig': 'DHCP',
|
||||
'IP': self.fake_channel_ip[0],
|
||||
'MAC': '00:d0:23:80:15:a6',
|
||||
'netmask': '255.255.240.0',
|
||||
'type': 'dhcp',
|
||||
'gateway': '172.27.127.254'}, {
|
||||
'datalink': 'CH1',
|
||||
'status': ch1_status,
|
||||
'typeConfig': 'DHCP',
|
||||
'IP': self.fake_channel_ip[1],
|
||||
'MAC': '00:d0:23:40:15:a6',
|
||||
'netmask': '255.255.240.0',
|
||||
'type': 'dhcp',
|
||||
'gateway': '172.27.127.254'}, {
|
||||
'datalink': 'CH2',
|
||||
'status': 'DOWN',
|
||||
'typeConfig': 'DHCP',
|
||||
'IP': '',
|
||||
'MAC': '00:d0:23:c0:15:a6',
|
||||
'netmask': '',
|
||||
'type': '',
|
||||
'gateway': ''}, {
|
||||
'datalink': 'CH3',
|
||||
'status': 'DOWN',
|
||||
'typeConfig': 'DHCP',
|
||||
'IP': '',
|
||||
'MAC': '00:d0:23:20:15:a6',
|
||||
'netmask': '',
|
||||
'type': '',
|
||||
'gateway': '',
|
||||
}]
|
||||
|
||||
fake_fquota_status = [{
|
||||
'quota': '21474836480',
|
||||
'used': '0',
|
||||
'name': 'test-folder',
|
||||
'type': 'subfolder',
|
||||
'id': '537178178'}, {
|
||||
'quota': '32212254720',
|
||||
'used': '0',
|
||||
'name': fake_share_name[0],
|
||||
'type': 'subfolder',
|
||||
'id': '805306752'}, {
|
||||
'quota': '53687091200',
|
||||
'used': '21474836480',
|
||||
'name': fake_share_name[1],
|
||||
'type': 'subfolder',
|
||||
'id': '69'}, {
|
||||
'quota': '94091997184',
|
||||
'used': '0',
|
||||
'type': 'subfolder',
|
||||
'id': '70',
|
||||
"name": 'test-folder-02'
|
||||
}]
|
||||
|
||||
fake_fquota_status_with_no_settings = []
|
||||
|
||||
def fake_get_share_status_nfs(self, status=False):
|
||||
fake_share_status_nfs = [{
|
||||
'ftp': False,
|
||||
'cifs': False,
|
||||
'oss': False,
|
||||
'sftp': False,
|
||||
'nfs': status,
|
||||
'directory': '/LV-1/share-pool-01/' + self.fake_share_name[0],
|
||||
'exist': True,
|
||||
'afp': False,
|
||||
'webdav': False
|
||||
}]
|
||||
if status:
|
||||
fake_share_status_nfs[0]['nfs_detail'] = {
|
||||
'hostList': [{
|
||||
'uid': '65534',
|
||||
'insecure': 'insecure',
|
||||
'squash': 'all',
|
||||
'access': 'ro',
|
||||
'host': '*',
|
||||
'gid': '65534',
|
||||
'mode': 'async',
|
||||
'no_subtree_check': 'no_subtree_check',
|
||||
}]
|
||||
}
|
||||
return fake_share_status_nfs
|
||||
|
||||
def fake_get_share_status_cifs(self, status=False):
|
||||
fake_share_status_cifs = [{
|
||||
'ftp': False,
|
||||
'cifs': status,
|
||||
'oss': False,
|
||||
'sftp': False,
|
||||
'nfs': False,
|
||||
'directory': '/share-pool-01/LV-1/' + self.fake_share_name[1],
|
||||
'exist': True,
|
||||
'afp': False,
|
||||
'webdav': False
|
||||
}]
|
||||
if status:
|
||||
fake_share_status_cifs[0]['cifs_detail'] = {
|
||||
'available': True,
|
||||
'encrypt': False,
|
||||
'description': '',
|
||||
'sharename': 'cifs-01',
|
||||
'failover': '',
|
||||
'AIO': True,
|
||||
'priv': 'None',
|
||||
'recycle_bin': False,
|
||||
'ABE': True,
|
||||
}
|
||||
return fake_share_status_cifs
|
||||
|
||||
fake_subfolder_data = [{
|
||||
'size': '6',
|
||||
'index': '34',
|
||||
'description': '',
|
||||
'encryption': '',
|
||||
'isEnd': False,
|
||||
'share': False,
|
||||
'volumeId': '6541BAFB2E6C57B6',
|
||||
'quota': '',
|
||||
'modifyTime': '2017-04-06 11:35',
|
||||
'owner': 'A',
|
||||
'path': '/share-pool-01/LV-1/UserHome',
|
||||
'subshare': True,
|
||||
'type': 'subfolder',
|
||||
'empty': False,
|
||||
'name': 'UserHome'}, {
|
||||
'size': '6',
|
||||
'index': '39',
|
||||
'description': '',
|
||||
'encryption': '',
|
||||
'isEnd': False,
|
||||
'share': False,
|
||||
'volumeId': '6541BAFB2E6C57B6',
|
||||
'quota': '21474836480',
|
||||
'modifyTime': '2017-04-27 15:44',
|
||||
'owner': 'A',
|
||||
'path': '/share-pool-01/LV-1/test-folder',
|
||||
'subshare': False,
|
||||
'type': 'subfolder',
|
||||
'empty': True,
|
||||
'name': 'test-folder'}, {
|
||||
'size': '6',
|
||||
'index': '45',
|
||||
'description': '',
|
||||
'encryption': '',
|
||||
'isEnd': False,
|
||||
'share': True,
|
||||
'volumeId': '6541BAFB2E6C57B6',
|
||||
'quota': '32212254720',
|
||||
'modifyTime': '2017-04-27 16:15',
|
||||
'owner': 'A',
|
||||
'path': '/share-pool-01/LV-1/' + fake_share_name[0],
|
||||
'subshare': False,
|
||||
'type': 'subfolder',
|
||||
'empty': True,
|
||||
'name': fake_share_name[0]}, {
|
||||
'size': '6',
|
||||
'index': '512',
|
||||
'description': '',
|
||||
'encryption': '',
|
||||
'isEnd': True,
|
||||
'share': True,
|
||||
'volumeId': '6541BAFB2E6C57B6',
|
||||
'quota': '53687091200',
|
||||
'modifyTime': '2017-04-27 16:16',
|
||||
'owner': 'A',
|
||||
'path': '/share-pool-01/LV-1/' + fake_share_name[1],
|
||||
'subshare': False,
|
||||
'type': 'subfolder',
|
||||
'empty': True,
|
||||
'name': fake_share_name[1]}, {
|
||||
'size': '6',
|
||||
'index': '777',
|
||||
'description': '',
|
||||
'encryption': '',
|
||||
'isEnd': False,
|
||||
'share': False,
|
||||
'volumeId': '6541BAFB2E6C57B6',
|
||||
'quota': '94091997184',
|
||||
'modifyTime': '2017-04-28 15:44',
|
||||
'owner': 'A',
|
||||
'path': '/share-pool-01/LV-1/test-folder-02',
|
||||
'subshare': False,
|
||||
'type': 'subfolder',
|
||||
'empty': True,
|
||||
'name': 'test-folder-02'
|
||||
}]
|
||||
|
||||
fake_cifs_user_list = [{
|
||||
'Superuser': 'No',
|
||||
'Group': 'users',
|
||||
'Description': '',
|
||||
'Quota': 'none',
|
||||
'PWD Expiry Date': '2291-01-19',
|
||||
'Home Directory': '/share-pool-01/LV-1/UserHome/user01',
|
||||
'UID': '100001',
|
||||
'Type': 'Local',
|
||||
'Name': 'user01'}, {
|
||||
'Superuser': 'No',
|
||||
'Group': 'users',
|
||||
'Description': '',
|
||||
'Quota': 'none',
|
||||
'PWD Expiry Date': '2017-08-07',
|
||||
'Home Directory': '/share-pool-01/LV-1/UserHome/user02',
|
||||
'UID': '100002',
|
||||
'Type': 'Local',
|
||||
'Name': 'user02'
|
||||
}]
|
||||
|
||||
fake_share_status_nfs_with_rules = [{
|
||||
'ftp': False,
|
||||
'cifs': False,
|
||||
'oss': False,
|
||||
'sftp': False,
|
||||
'nfs': True,
|
||||
'directory': '/share-pool-01/LV-1/' + fake_share_name[0],
|
||||
'exist': True,
|
||||
'nfs_detail': {
|
||||
'hostList': [{
|
||||
'uid': '65534',
|
||||
'insecure': 'insecure',
|
||||
'squash': 'all',
|
||||
'access': 'ro',
|
||||
'host': '*',
|
||||
'gid': '65534',
|
||||
'mode': 'async',
|
||||
'no_subtree_check':
|
||||
'no_subtree_check'}, {
|
||||
'uid': '65534',
|
||||
'insecure': 'insecure',
|
||||
'squash': 'all',
|
||||
'access': 'rw',
|
||||
'host': '172.27.1.1',
|
||||
'gid': '65534',
|
||||
'mode': 'async',
|
||||
'no_subtree_check': 'no_subtree_check'}, {
|
||||
'uid': '65534',
|
||||
'insecure': 'insecure',
|
||||
'squash': 'all',
|
||||
'access': 'rw',
|
||||
'host': '172.27.1.2',
|
||||
'gid': '65534',
|
||||
'mode': 'async',
|
||||
'no_subtree_check': 'no_subtree_check'}]
|
||||
},
|
||||
'afp': False,
|
||||
'webdav': False,
|
||||
}]
|
||||
|
||||
fake_share_status_cifs_with_rules = [
|
||||
{
|
||||
'permission': {
|
||||
'Read': True,
|
||||
'Write': True,
|
||||
'Execute': True},
|
||||
'type': 'user',
|
||||
'id': '100001',
|
||||
'name': 'user01'
|
||||
}, {
|
||||
'permission': {
|
||||
'Read': True,
|
||||
'Write': False,
|
||||
'Execute': True},
|
||||
'type': 'user',
|
||||
'id': '100002',
|
||||
'name': 'user02'
|
||||
}, {
|
||||
'permission': {
|
||||
'Read': True,
|
||||
'Write': False,
|
||||
'Execute': True},
|
||||
'type': 'group@',
|
||||
'id': '100',
|
||||
'name': 'users'
|
||||
}, {
|
||||
'permission': {
|
||||
'Read': True,
|
||||
'Write': False,
|
||||
'Execute': True},
|
||||
'type': 'other@',
|
||||
'id': '',
|
||||
'name': ''
|
||||
}
|
||||
]
|
573
manila/tests/share/drivers/infortrend/test_infortrend_nas.py
Normal file
573
manila/tests/share/drivers/infortrend/test_infortrend_nas.py
Normal file
@ -0,0 +1,573 @@
|
||||
# Copyright (c) 2019 Infortrend Technology, Inc.
|
||||
# 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.
|
||||
|
||||
import ddt
|
||||
import mock
|
||||
|
||||
from oslo_config import cfg
|
||||
|
||||
from manila import context
|
||||
from manila import exception
|
||||
from manila.share import configuration
|
||||
from manila.share.drivers.infortrend import driver
|
||||
from manila.share.drivers.infortrend import infortrend_nas
|
||||
from manila import test
|
||||
from manila.tests.share.drivers.infortrend import fake_infortrend_manila_data
|
||||
from manila.tests.share.drivers.infortrend import fake_infortrend_nas_data
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
SUCCEED = (0, [])
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
class InfortrendNASDriverTestCase(test.TestCase):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(InfortrendNASDriverTestCase, self).__init__(*args, **kwargs)
|
||||
self._ctxt = context.get_admin_context()
|
||||
self.nas_data = fake_infortrend_nas_data.InfortrendNASTestData()
|
||||
self.m_data = fake_infortrend_manila_data.InfortrendManilaTestData()
|
||||
|
||||
def setUp(self):
|
||||
CONF.set_default('driver_handles_share_servers', False)
|
||||
CONF.set_default('infortrend_nas_ip', '172.27.1.1')
|
||||
CONF.set_default('infortrend_nas_user', 'fake_user')
|
||||
CONF.set_default('infortrend_nas_password', 'fake_password')
|
||||
CONF.set_default('infortrend_nas_ssh_key', 'fake_sshkey')
|
||||
CONF.set_default('infortrend_share_pools', 'share-pool-01')
|
||||
CONF.set_default('infortrend_share_channels', '0,1')
|
||||
self.fake_conf = configuration.Configuration(None)
|
||||
super(InfortrendNASDriverTestCase, self).setUp()
|
||||
|
||||
def _get_driver(self, fake_conf, init_dict=False):
|
||||
self._driver = driver.InfortrendNASDriver(
|
||||
configuration=fake_conf)
|
||||
self._iftnas = self._driver.ift_nas
|
||||
self.pool_id = ['6541BAFB2E6C57B6']
|
||||
self.pool_path = ['/share-pool-01/LV-1/']
|
||||
|
||||
if init_dict:
|
||||
self._iftnas.pool_dict = {
|
||||
'share-pool-01': {
|
||||
'id': self.pool_id[0],
|
||||
'path': self.pool_path[0],
|
||||
}
|
||||
}
|
||||
self._iftnas.channel_dict = {
|
||||
'0': self.nas_data.fake_channel_ip[0],
|
||||
'1': self.nas_data.fake_channel_ip[1],
|
||||
}
|
||||
|
||||
def test_no_login_ssh_key_and_pass(self):
|
||||
self.fake_conf.set_default('infortrend_nas_password', None)
|
||||
self.fake_conf.set_default('infortrend_nas_ssh_key', None)
|
||||
|
||||
self.assertRaises(
|
||||
exception.InvalidParameterValue,
|
||||
self._get_driver,
|
||||
self.fake_conf)
|
||||
|
||||
def test_parser_with_service_status(self):
|
||||
self._get_driver(self.fake_conf)
|
||||
expect_service_status = [{
|
||||
'A': {
|
||||
'NFS': {
|
||||
'displayName': 'NFS',
|
||||
'state_time': '2017-05-04 14:19:53',
|
||||
'enabled': True,
|
||||
'cpu_rate': '0.0',
|
||||
'mem_rate': '0.0',
|
||||
'state': 'exited',
|
||||
'type': 'share',
|
||||
}
|
||||
}
|
||||
}]
|
||||
|
||||
rc, service_status = self._iftnas._parser(
|
||||
self.nas_data.fake_service_status_data)
|
||||
|
||||
self.assertEqual(0, rc)
|
||||
self.assertDictListMatch(expect_service_status, service_status)
|
||||
|
||||
def test_parser_with_folder_status(self):
|
||||
self._get_driver(self.fake_conf)
|
||||
expect_folder_status = [{
|
||||
'utility': '1.00',
|
||||
'used': '33886208',
|
||||
'subshare': True,
|
||||
'share': False,
|
||||
'worm': '',
|
||||
'free': '321931374592',
|
||||
'fsType': 'xfs',
|
||||
'owner': 'A',
|
||||
'readOnly': False,
|
||||
'modifyTime': '2017-04-27 16:16',
|
||||
'directory': self.pool_path[0][:-1],
|
||||
'volumeId': self.pool_id[0],
|
||||
'mounted': True,
|
||||
'size': '321965260800'}, {
|
||||
'utility': '1.00',
|
||||
'used': '33779712',
|
||||
'subshare': False,
|
||||
'share': False,
|
||||
'worm': '',
|
||||
'free': '107287973888',
|
||||
'fsType': 'xfs',
|
||||
'owner': 'A',
|
||||
'readOnly': False,
|
||||
'modifyTime': '2017-04-27 15:45',
|
||||
'directory': '/share-pool-02/LV-1',
|
||||
'volumeId': '147A8FB67DA39914',
|
||||
'mounted': True,
|
||||
'size': '107321753600'
|
||||
}]
|
||||
|
||||
rc, folder_status = self._iftnas._parser(
|
||||
self.nas_data.fake_folder_status_data)
|
||||
|
||||
self.assertEqual(0, rc)
|
||||
self.assertDictListMatch(expect_folder_status, folder_status)
|
||||
|
||||
def test_ensure_service_on(self):
|
||||
self._get_driver(self.fake_conf)
|
||||
mock_execute = mock.Mock(
|
||||
side_effect=[(0, self.nas_data.fake_nfs_status_off), SUCCEED])
|
||||
self._iftnas._execute = mock_execute
|
||||
|
||||
self._iftnas._ensure_service_on('nfs')
|
||||
|
||||
mock_execute.assert_called_with(['service', 'restart', 'nfs'])
|
||||
|
||||
def test_check_channels_status(self):
|
||||
self._get_driver(self.fake_conf)
|
||||
expect_channel_dict = {
|
||||
'0': self.nas_data.fake_channel_ip[0],
|
||||
'1': self.nas_data.fake_channel_ip[1],
|
||||
}
|
||||
|
||||
self._iftnas._execute = mock.Mock(
|
||||
return_value=(0, self.nas_data.fake_get_channel_status()))
|
||||
|
||||
self._iftnas._check_channels_status()
|
||||
|
||||
self.assertDictMatch(expect_channel_dict, self._iftnas.channel_dict)
|
||||
|
||||
@mock.patch.object(infortrend_nas.LOG, 'warning')
|
||||
def test_channel_status_down(self, log_warning):
|
||||
self._get_driver(self.fake_conf)
|
||||
self._iftnas._execute = mock.Mock(
|
||||
return_value=(0, self.nas_data.fake_get_channel_status('DOWN')))
|
||||
|
||||
self._iftnas._check_channels_status()
|
||||
|
||||
self.assertEqual(1, log_warning.call_count)
|
||||
|
||||
@mock.patch.object(infortrend_nas.LOG, 'error')
|
||||
def test_invalid_channel(self, log_error):
|
||||
self.fake_conf.set_default('infortrend_share_channels', '0, 6')
|
||||
self._get_driver(self.fake_conf)
|
||||
self._iftnas._execute = mock.Mock(
|
||||
return_value=(0, self.nas_data.fake_get_channel_status()))
|
||||
|
||||
self.assertRaises(
|
||||
exception.InfortrendNASException,
|
||||
self._iftnas._check_channels_status)
|
||||
|
||||
def test_check_pools_setup(self):
|
||||
self._get_driver(self.fake_conf)
|
||||
expect_pool_dict = {
|
||||
'share-pool-01': {
|
||||
'id': self.pool_id[0],
|
||||
'path': self.pool_path[0],
|
||||
}
|
||||
}
|
||||
self._iftnas._execute = mock.Mock(
|
||||
return_value=(0, self.nas_data.fake_folder_status))
|
||||
|
||||
self._iftnas._check_pools_setup()
|
||||
|
||||
self.assertDictMatch(expect_pool_dict, self._iftnas.pool_dict)
|
||||
|
||||
def test_unknow_pools_setup(self):
|
||||
self.fake_conf.set_default(
|
||||
'infortrend_share_pools', 'chengwei, share-pool-01')
|
||||
self._get_driver(self.fake_conf)
|
||||
self._iftnas._execute = mock.Mock(
|
||||
return_value=(0, self.nas_data.fake_folder_status))
|
||||
|
||||
self.assertRaises(
|
||||
exception.InfortrendNASException,
|
||||
self._iftnas._check_pools_setup)
|
||||
|
||||
@mock.patch.object(infortrend_nas.InfortrendNAS, '_execute')
|
||||
def test_get_pool_quota_used(self, mock_execute):
|
||||
self._get_driver(self.fake_conf, True)
|
||||
mock_execute.return_value = (0, self.nas_data.fake_fquota_status)
|
||||
|
||||
pool_quota = self._iftnas._get_pool_quota_used('share-pool-01')
|
||||
|
||||
mock_execute.assert_called_with(
|
||||
['fquota', 'status', self.pool_id[0],
|
||||
'LV-1', '-t', 'folder'])
|
||||
self.assertEqual(201466179584, pool_quota)
|
||||
|
||||
@mock.patch.object(infortrend_nas.InfortrendNAS, '_execute')
|
||||
def test_create_share_nfs(self, mock_execute):
|
||||
self._get_driver(self.fake_conf, True)
|
||||
fake_share_id = self.m_data.fake_share_nfs['id']
|
||||
fake_share_name = fake_share_id.replace('-', '')
|
||||
expect_locations = [
|
||||
self.nas_data.fake_channel_ip[0] +
|
||||
':/share-pool-01/LV-1/' + fake_share_name,
|
||||
self.nas_data.fake_channel_ip[1] +
|
||||
':/share-pool-01/LV-1/' + fake_share_name,
|
||||
]
|
||||
mock_execute.side_effect = [
|
||||
SUCCEED, # create folder
|
||||
SUCCEED, # set size
|
||||
(0, self.nas_data.fake_get_share_status_nfs()), # check proto
|
||||
SUCCEED, # enable proto
|
||||
(0, self.nas_data.fake_get_channel_status()) # update channel
|
||||
]
|
||||
|
||||
locations = self._driver.create_share(
|
||||
self._ctxt, self.m_data.fake_share_nfs)
|
||||
|
||||
self.assertEqual(expect_locations, locations)
|
||||
mock_execute.assert_any_call(
|
||||
['share', self.pool_path[0] + fake_share_name, 'nfs', 'on'])
|
||||
|
||||
@mock.patch.object(infortrend_nas.InfortrendNAS, '_execute')
|
||||
def test_create_share_cifs(self, mock_execute):
|
||||
self._get_driver(self.fake_conf, True)
|
||||
fake_share_id = self.m_data.fake_share_cifs['id']
|
||||
fake_share_name = fake_share_id.replace('-', '')
|
||||
expect_locations = [
|
||||
'\\\\' + self.nas_data.fake_channel_ip[0] +
|
||||
'\\' + fake_share_name,
|
||||
'\\\\' + self.nas_data.fake_channel_ip[1] +
|
||||
'\\' + fake_share_name,
|
||||
]
|
||||
mock_execute.side_effect = [
|
||||
SUCCEED, # create folder
|
||||
SUCCEED, # set size
|
||||
(0, self.nas_data.fake_get_share_status_cifs()), # check proto
|
||||
SUCCEED, # enable proto
|
||||
(0, self.nas_data.fake_get_channel_status()) # update channel
|
||||
]
|
||||
|
||||
locations = self._driver.create_share(
|
||||
self._ctxt, self.m_data.fake_share_cifs)
|
||||
|
||||
self.assertEqual(expect_locations, locations)
|
||||
mock_execute.assert_any_call(
|
||||
['share', self.pool_path[0] + fake_share_name,
|
||||
'cifs', 'on', '-n', fake_share_name])
|
||||
|
||||
@mock.patch.object(infortrend_nas.InfortrendNAS, '_execute')
|
||||
def test_delete_share_nfs(self, mock_execute):
|
||||
self._get_driver(self.fake_conf, True)
|
||||
fake_share_id = self.m_data.fake_share_nfs['id']
|
||||
fake_share_name = fake_share_id.replace('-', '')
|
||||
mock_execute.side_effect = [
|
||||
(0, self.nas_data.fake_subfolder_data), # pagelist folder
|
||||
SUCCEED, # delete folder
|
||||
]
|
||||
|
||||
self._driver.delete_share(
|
||||
self._ctxt, self.m_data.fake_share_nfs)
|
||||
|
||||
mock_execute.assert_any_call(
|
||||
['folder', 'options', self.pool_id[0],
|
||||
'LV-1', '-d', fake_share_name])
|
||||
|
||||
@mock.patch.object(infortrend_nas.InfortrendNAS, '_execute')
|
||||
def test_delete_share_cifs(self, mock_execute):
|
||||
self._get_driver(self.fake_conf, True)
|
||||
fake_share_id = self.m_data.fake_share_cifs['id']
|
||||
fake_share_name = fake_share_id.replace('-', '')
|
||||
mock_execute.side_effect = [
|
||||
(0, self.nas_data.fake_subfolder_data), # pagelist folder
|
||||
SUCCEED, # delete folder
|
||||
]
|
||||
|
||||
self._driver.delete_share(
|
||||
self._ctxt, self.m_data.fake_share_cifs)
|
||||
|
||||
mock_execute.assert_any_call(
|
||||
['folder', 'options', self.pool_id[0],
|
||||
'LV-1', '-d', fake_share_name])
|
||||
|
||||
@mock.patch.object(infortrend_nas.LOG, 'warning')
|
||||
@mock.patch.object(infortrend_nas.InfortrendNAS, '_execute')
|
||||
def test_delete_non_exist_share(self, mock_execute, log_warning):
|
||||
self._get_driver(self.fake_conf, True)
|
||||
mock_execute.side_effect = [
|
||||
(0, self.nas_data.fake_subfolder_data), # pagelist folder
|
||||
]
|
||||
|
||||
self._driver.delete_share(
|
||||
self._ctxt, self.m_data.fake_non_exist_share)
|
||||
|
||||
self.assertEqual(1, log_warning.call_count)
|
||||
|
||||
def test_get_pool(self):
|
||||
self._get_driver(self.fake_conf, True)
|
||||
pool = self._driver.get_pool(self.m_data.fake_share_nfs)
|
||||
|
||||
self.assertEqual('share-pool-01', pool)
|
||||
|
||||
def test_get_pool_without_host(self):
|
||||
self._get_driver(self.fake_conf, True)
|
||||
self._iftnas._execute = mock.Mock(
|
||||
return_value=(0, self.nas_data.fake_subfolder_data))
|
||||
|
||||
pool = self._driver.get_pool(self.m_data.fake_share_cifs_no_host)
|
||||
|
||||
self.assertEqual('share-pool-01', pool)
|
||||
|
||||
def test_ensure_share_nfs(self):
|
||||
self._get_driver(self.fake_conf, True)
|
||||
share_id = self.m_data.fake_share_nfs['id']
|
||||
share_name = share_id.replace('-', '')
|
||||
share_path = self.pool_path[0] + share_name
|
||||
expect_locations = [
|
||||
self.nas_data.fake_channel_ip[0] + ':' + share_path,
|
||||
self.nas_data.fake_channel_ip[1] + ':' + share_path,
|
||||
]
|
||||
self._iftnas._execute = mock.Mock(
|
||||
return_value=(0, self.nas_data.fake_get_channel_status()))
|
||||
|
||||
locations = self._driver.ensure_share(
|
||||
self._ctxt, self.m_data.fake_share_nfs)
|
||||
|
||||
self.assertEqual(expect_locations, locations)
|
||||
|
||||
def test_ensure_share_cifs(self):
|
||||
self._get_driver(self.fake_conf, True)
|
||||
share_id = self.m_data.fake_share_cifs['id']
|
||||
share_name = share_id.replace('-', '')
|
||||
expect_locations = [
|
||||
'\\\\' + self.nas_data.fake_channel_ip[0] +
|
||||
'\\' + share_name,
|
||||
'\\\\' + self.nas_data.fake_channel_ip[1] +
|
||||
'\\' + share_name,
|
||||
]
|
||||
self._iftnas._execute = mock.Mock(
|
||||
return_value=(0, self.nas_data.fake_get_channel_status()))
|
||||
|
||||
locations = self._driver.ensure_share(
|
||||
self._ctxt, self.m_data.fake_share_cifs)
|
||||
|
||||
self.assertEqual(expect_locations, locations)
|
||||
|
||||
def test_extend_share(self):
|
||||
self._get_driver(self.fake_conf, True)
|
||||
share_id = self.m_data.fake_share_nfs['id']
|
||||
share_name = share_id.replace('-', '')
|
||||
self._iftnas._execute = mock.Mock(return_value=SUCCEED)
|
||||
|
||||
self._driver.extend_share(self.m_data.fake_share_nfs, 100)
|
||||
|
||||
self._iftnas._execute.assert_called_once_with(
|
||||
['fquota', 'create', self.pool_id[0], 'LV-1',
|
||||
share_name, '100G', '-t', 'folder'])
|
||||
|
||||
@mock.patch.object(infortrend_nas.InfortrendNAS, '_execute')
|
||||
def test_shrink_share(self, mock_execute):
|
||||
self._get_driver(self.fake_conf, True)
|
||||
share_id = self.m_data.fake_share_nfs['id']
|
||||
share_name = share_id.replace('-', '')
|
||||
mock_execute.side_effect = [
|
||||
(0, self.nas_data.fake_fquota_status), # check used
|
||||
SUCCEED,
|
||||
]
|
||||
|
||||
self._driver.shrink_share(self.m_data.fake_share_nfs, 10)
|
||||
|
||||
mock_execute.assert_has_calls([
|
||||
mock.call(['fquota', 'status', self.pool_id[0],
|
||||
'LV-1', '-t', 'folder']),
|
||||
mock.call(['fquota', 'create', self.pool_id[0],
|
||||
'LV-1', share_name, '10G', '-t', 'folder'])])
|
||||
|
||||
@mock.patch.object(infortrend_nas.InfortrendNAS, '_execute')
|
||||
def test_shrink_share_smaller_than_used_size(self, mock_execute):
|
||||
self._get_driver(self.fake_conf, True)
|
||||
mock_execute.side_effect = [
|
||||
(0, self.nas_data.fake_fquota_status), # check used
|
||||
]
|
||||
|
||||
self.assertRaises(
|
||||
exception.ShareShrinkingPossibleDataLoss,
|
||||
self._driver.shrink_share,
|
||||
self.m_data.fake_share_cifs,
|
||||
10)
|
||||
|
||||
def test_get_share_size(self):
|
||||
self._get_driver(self.fake_conf, True)
|
||||
self._iftnas._execute = mock.Mock(
|
||||
return_value=(0, self.nas_data.fake_fquota_status))
|
||||
|
||||
size = self._iftnas._get_share_size('', '', 'test-folder-02')
|
||||
|
||||
self.assertEqual(87.63, size)
|
||||
|
||||
@mock.patch.object(infortrend_nas.InfortrendNAS, '_execute')
|
||||
def test_manage_existing_nfs(self, mock_execute):
|
||||
self._get_driver(self.fake_conf, True)
|
||||
share_id = self.m_data.fake_share_for_manage_nfs['id']
|
||||
share_name = share_id.replace('-', '')
|
||||
origin_share_path = self.pool_path[0] + 'test-folder'
|
||||
export_share_path = self.pool_path[0] + share_name
|
||||
expect_result = {
|
||||
'size': 20.0,
|
||||
'export_locations': [
|
||||
self.nas_data.fake_channel_ip[0] + ':' + export_share_path,
|
||||
self.nas_data.fake_channel_ip[1] + ':' + export_share_path,
|
||||
]
|
||||
}
|
||||
mock_execute.side_effect = [
|
||||
(0, self.nas_data.fake_subfolder_data), # pagelist folder
|
||||
(0, self.nas_data.fake_get_share_status_nfs()), # check proto
|
||||
SUCCEED, # enable nfs
|
||||
(0, self.nas_data.fake_fquota_status), # get share size
|
||||
SUCCEED, # rename share
|
||||
(0, self.nas_data.fake_get_channel_status()) # update channel
|
||||
]
|
||||
|
||||
result = self._driver.manage_existing(
|
||||
self.m_data.fake_share_for_manage_nfs,
|
||||
{}
|
||||
)
|
||||
|
||||
self.assertEqual(expect_result, result)
|
||||
mock_execute.assert_has_calls([
|
||||
mock.call(['pagelist', 'folder', self.pool_path[0]]),
|
||||
mock.call(['share', 'status', '-f', origin_share_path]),
|
||||
mock.call(['share', origin_share_path, 'nfs', 'on']),
|
||||
mock.call(['fquota', 'status', self.pool_id[0],
|
||||
origin_share_path.split('/')[3], '-t', 'folder']),
|
||||
mock.call(['folder', 'options', self.pool_id[0],
|
||||
'LV-1', '-k', 'test-folder', share_name]),
|
||||
mock.call(['ifconfig', 'inet', 'show']),
|
||||
])
|
||||
|
||||
@mock.patch.object(infortrend_nas.InfortrendNAS, '_execute')
|
||||
def test_manage_existing_cifs(self, mock_execute):
|
||||
self._get_driver(self.fake_conf, True)
|
||||
share_id = self.m_data.fake_share_for_manage_cifs['id']
|
||||
share_name = share_id.replace('-', '')
|
||||
origin_share_path = self.pool_path[0] + 'test-folder-02'
|
||||
expect_result = {
|
||||
'size': 87.63,
|
||||
'export_locations': [
|
||||
'\\\\' + self.nas_data.fake_channel_ip[0] + '\\' + share_name,
|
||||
'\\\\' + self.nas_data.fake_channel_ip[1] + '\\' + share_name,
|
||||
]
|
||||
}
|
||||
mock_execute.side_effect = [
|
||||
(0, self.nas_data.fake_subfolder_data), # pagelist folder
|
||||
(0, self.nas_data.fake_get_share_status_cifs()), # check proto
|
||||
SUCCEED, # enable cifs
|
||||
(0, self.nas_data.fake_fquota_status), # get share size
|
||||
SUCCEED, # rename share
|
||||
(0, self.nas_data.fake_get_channel_status()) # update channel
|
||||
]
|
||||
|
||||
result = self._driver.manage_existing(
|
||||
self.m_data.fake_share_for_manage_cifs,
|
||||
{}
|
||||
)
|
||||
|
||||
self.assertEqual(expect_result, result)
|
||||
mock_execute.assert_has_calls([
|
||||
mock.call(['pagelist', 'folder', self.pool_path[0]]),
|
||||
mock.call(['share', 'status', '-f', origin_share_path]),
|
||||
mock.call(['share', origin_share_path, 'cifs', 'on',
|
||||
'-n', share_name]),
|
||||
mock.call(['fquota', 'status', self.pool_id[0],
|
||||
origin_share_path.split('/')[3], '-t', 'folder']),
|
||||
mock.call(['folder', 'options', self.pool_id[0],
|
||||
'LV-1', '-k', 'test-folder-02', share_name]),
|
||||
mock.call(['ifconfig', 'inet', 'show']),
|
||||
])
|
||||
|
||||
def test_manage_existing_with_no_location(self):
|
||||
self._get_driver(self.fake_conf, True)
|
||||
fake_share = self.m_data._get_fake_share_for_manage('')
|
||||
|
||||
self.assertRaises(
|
||||
exception.InfortrendNASException,
|
||||
self._driver.manage_existing,
|
||||
fake_share, {})
|
||||
|
||||
@ddt.data('172.27.1.1:/share-pool-01/LV-1/test-folder',
|
||||
'172.27.112.223:/share-pool-01/LV-1/some-folder')
|
||||
def test_manage_existing_wrong_ip_or_name(self, fake_share_path):
|
||||
self._get_driver(self.fake_conf, True)
|
||||
fake_share = self.m_data._get_fake_share_for_manage(fake_share_path)
|
||||
self._iftnas._execute = mock.Mock(
|
||||
return_value=(0, self.nas_data.fake_subfolder_data))
|
||||
|
||||
self.assertRaises(
|
||||
exception.InfortrendNASException,
|
||||
self._driver.manage_existing,
|
||||
fake_share, {})
|
||||
|
||||
@mock.patch.object(infortrend_nas.InfortrendNAS, '_execute')
|
||||
def test_manage_existing_with_no_size_setting(self, mock_execute):
|
||||
self._get_driver(self.fake_conf, True)
|
||||
mock_execute.side_effect = [
|
||||
(0, self.nas_data.fake_subfolder_data), # pagelist folder
|
||||
(0, self.nas_data.fake_get_share_status_nfs()), # check proto
|
||||
SUCCEED, # enable nfs
|
||||
(0, self.nas_data.fake_fquota_status_with_no_settings),
|
||||
]
|
||||
|
||||
self.assertRaises(
|
||||
exception.InfortrendNASException,
|
||||
self._driver.manage_existing,
|
||||
self.m_data.fake_share_for_manage_nfs,
|
||||
{})
|
||||
|
||||
@ddt.data('NFS', 'CIFS')
|
||||
@mock.patch.object(infortrend_nas.InfortrendNAS, '_execute')
|
||||
def test_unmanage(self, protocol, mock_execute):
|
||||
share_to_unmanage = (self.m_data.fake_share_nfs
|
||||
if protocol == 'NFS' else
|
||||
self.m_data.fake_share_cifs)
|
||||
self._get_driver(self.fake_conf, True)
|
||||
mock_execute.side_effect = [
|
||||
(0, self.nas_data.fake_subfolder_data), # pagelist folder
|
||||
]
|
||||
|
||||
self._driver.unmanage(share_to_unmanage)
|
||||
|
||||
mock_execute.assert_called_once_with(
|
||||
['pagelist', 'folder', self.pool_path[0]],
|
||||
)
|
||||
|
||||
@mock.patch.object(infortrend_nas.LOG, 'warning')
|
||||
def test_unmanage_share_not_exist(self, log_warning):
|
||||
self._get_driver(self.fake_conf, True)
|
||||
self._iftnas._execute = mock.Mock(
|
||||
return_value=(0, self.nas_data.fake_subfolder_data))
|
||||
|
||||
self._driver.unmanage(
|
||||
self.m_data.fake_share_for_manage_nfs,
|
||||
)
|
||||
|
||||
self.assertEqual(1, log_warning.call_count)
|
@ -0,0 +1,5 @@
|
||||
---
|
||||
prelude: >
|
||||
Added Manila share driver for Infortrend storage systems.
|
||||
features:
|
||||
- The new Infortrend driver supports GS/GSe Family storage systems.
|
Loading…
Reference in New Issue
Block a user