manila/manila/share/drivers/huawei/v3/connection.py

428 lines
16 KiB
Python

# Copyright (c) 2015 Huawei Technologies Co., Ltd.
# 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 time
from oslo_log import log
from oslo_utils import units
from manila.common import constants as common_constants
from manila import exception
from manila.i18n import _, _LI, _LW
from manila.share.drivers.huawei import base as driver
from manila.share.drivers.huawei import constants
from manila.share.drivers.huawei.v3 import helper
LOG = log.getLogger(__name__)
class V3StorageConnection(driver.HuaweiBase):
"""Helper class for Huawei OceanStor V3 storage system."""
def __init__(self, configuration):
super(V3StorageConnection, self).__init__(configuration)
def connect(self):
"""Try to connect to V3 server."""
if self.configuration:
self.helper = helper.RestHelper(self.configuration)
else:
raise exception.InvalidInput(_("Huawei configuration missing."))
self.helper.login()
def create_share(self, share, share_server=None):
"""Create a share."""
share_name = share['name']
share_proto = share['share_proto']
fs_id = None
# We sleep here to ensure the newly created filesystem can be read.
wait_interval = self._get_wait_interval()
timeout = self._get_timeout()
try:
fs_id = self.allocate_container(share)
fs = self.helper._get_fs_info_by_id(fs_id)
end_time = time.time() + timeout
while not (self.check_fs_status(fs['HEALTHSTATUS'],
fs['RUNNINGSTATUS'])
or time.time() > end_time):
time.sleep(wait_interval)
fs = self.helper._get_fs_info_by_id(fs_id)
if not self.check_fs_status(fs['HEALTHSTATUS'],
fs['RUNNINGSTATUS']):
raise exception.InvalidShare(
reason=(_('Invalid status of filesystem: %(health)s '
'%(running)s.')
% {'health': fs['HEALTHSTATUS'],
'running': fs['RUNNINGSTATUS']}))
except Exception as err:
if fs_id is not None:
self.helper._delete_fs(fs_id)
message = (_('Failed to create share %(name)s.'
'Reason: %(err)s.')
% {'name': share_name,
'err': err})
raise exception.InvalidShare(reason=message)
try:
self.helper._create_share(share_name, fs_id, share_proto)
except Exception as err:
if fs_id is not None:
self.helper._delete_fs(fs_id)
raise exception.InvalidShare(
reason=(_('Failed to create share %(name)s.'
'Reason: %(err)s.')
% {'name': share_name,
'err': err}))
location = self._get_location_path(share_name, share_proto)
return location
def extend_share(self, share, new_size, share_server):
share_proto = share['share_proto']
share_name = share['name']
# The unit is the sectors.
size = new_size * units.Mi * 2
share_type = self.helper._get_share_type(share_proto)
share = self.helper._get_share_by_name(share_name, share_type)
if not share:
err_msg = (_("Can not get share ID by share %s.")
% share_name)
LOG.error(err_msg)
raise exception.InvalidShareAccess(reason=err_msg)
fsid = share['FSID']
self.helper._extend_share(fsid, size)
def check_fs_status(self, health_status, running_status):
if (health_status == constants.STATUS_FS_HEALTH
and running_status == constants.STATUS_FS_RUNNING):
return True
else:
return False
def create_snapshot(self, snapshot, share_server=None):
"""Create a snapshot."""
snap_name = snapshot['id']
share_proto = snapshot['share_proto']
share_name = self.helper._get_share_name_by_id(snapshot['share_id'])
share_type = self.helper._get_share_type(share_proto)
share = self.helper._get_share_by_name(share_name, share_type)
if not share:
err_msg = _('Can not create snapshot,'
' because share_id is not provided.')
LOG.error(err_msg)
raise exception.InvalidInput(reason=err_msg)
sharefsid = share['FSID']
snapshot_name = "share_snapshot_" + snap_name
snap_id = self.helper._create_snapshot(sharefsid,
snapshot_name)
LOG.info(_LI('Creating snapshot id %s.'), snap_id)
def delete_snapshot(self, snapshot, share_server=None):
"""Delete a snapshot."""
LOG.debug("Delete a snapshot.")
snap_name = snapshot['id']
share_name = self.helper._get_share_name_by_id(snapshot['share_id'])
sharefsid = self.helper._get_fsid_by_name(share_name)
if sharefsid is None:
LOG.warning(_LW('Delete snapshot share id %s fs has been '
'deleted.'), snap_name)
return
snapshot_id = self.helper._get_snapshot_id(sharefsid, snap_name)
snapshot_flag = self.helper._check_snapshot_id_exist(snapshot_id)
if snapshot_flag:
self.helper._delete_snapshot(snapshot_id)
else:
LOG.warning(_LW("Can not find snapshot %s in array."), snap_name)
def update_share_stats(self, stats_dict):
"""Retrieve status info from share group."""
capacity = self._get_capacity()
stats_dict["pools"] = []
pool = {}
pool.update(dict(
pool_name=capacity['name'],
total_capacity_gb=capacity['TOTALCAPACITY'],
free_capacity_gb=capacity['CAPACITY'],
QoS_support=False,
reserved_percentage=0,
))
stats_dict["pools"].append(pool)
def delete_share(self, share, share_server=None):
"""Delete share."""
share_name = share['name']
share_proto = self.helper._get_share_type(share['share_proto'])
share = self.helper._get_share_by_name(share_name, share_proto)
if not share:
LOG.warning(_LW('The share was not found. Share name:%s'),
share_name)
fsid = self.helper._get_fsid_by_name(share_name)
if fsid:
self.helper._delete_fs(fsid)
return
LOG.warning(_LW('The filesystem was not found.'))
return
share_id = share['ID']
share_fs_id = share['FSID']
if share_id:
self.helper._delete_share_by_id(share_id, share_proto)
if share_fs_id:
self.helper._delete_fs(share_fs_id)
return share
def get_network_allocations_number(self):
"""Get number of network interfaces to be created."""
return constants.IP_ALLOCATIONS
def _get_capacity(self):
"""Get free capacity and total capacity of the pools."""
poolinfo = self.helper._find_pool_info()
if poolinfo:
total = int(poolinfo['TOTALCAPACITY']) / units.Mi / 2
free = int(poolinfo['CAPACITY']) / units.Mi / 2
poolinfo['TOTALCAPACITY'] = total
poolinfo['CAPACITY'] = free
return poolinfo
def _init_filesys_para(self, share):
"""Init basic filesystem parameters."""
name = share['name']
size = share['size'] * units.Mi * 2
poolinfo = self.helper._find_pool_info()
fileparam = {
"NAME": name.replace("-", "_"),
"DESCRIPTION": "",
"ALLOCTYPE": 1,
"CAPACITY": size,
"PARENTID": poolinfo['ID'],
"INITIALALLOCCAPACITY": units.Ki * 20,
"PARENTTYPE": 216,
"SNAPSHOTRESERVEPER": 20,
"INITIALDISTRIBUTEPOLICY": 0,
"ISSHOWSNAPDIR": True,
"RECYCLESWITCH": 0,
"RECYCLEHOLDTIME": 15,
"RECYCLETHRESHOLD": 0,
"RECYCLEAUTOCLEANSWITCH": 0,
"ENABLEDEDUP": False,
"ENABLECOMPRESSION": False,
}
root = self.helper._read_xml()
fstype = root.findtext('Filesystem/AllocType')
if fstype:
fstype = fstype.strip()
if fstype == 'Thin':
fileparam['ALLOCTYPE'] = 1
elif fstype == 'Thick':
fileparam['ALLOCTYPE'] = 0
else:
err_msg = (_(
'Config file is wrong. Filesystem type must be "Thin"'
' or "Thick". AllocType:%(fetchtype)s') %
{'fetchtype': fstype})
LOG.error(err_msg)
raise exception.InvalidShare(reason=err_msg)
return fileparam
def deny_access(self, share, access, share_server=None):
"""Deny access to share."""
share_proto = share['share_proto']
share_name = share['name']
share_type = self.helper._get_share_type(share_proto)
share_client_type = self.helper._get_share_client_type(share_proto)
access_type = access['access_type']
if share_proto == 'NFS' and access_type != 'ip':
LOG.warning(_LW('Only IP access type is allowed for NFS shares.'))
return
elif share_proto == 'CIFS' and access_type != 'user':
LOG.warning(_LW('Only USER access type is allowed for'
' CIFS shares.'))
return
access_to = access['access_to']
share = self.helper._get_share_by_name(share_name, share_type)
if not share:
LOG.warning(_LW('Can not get share. share_name: %s'), share_name)
return
access_id = self.helper._get_access_from_share(share['ID'], access_to,
share_client_type)
if not access_id:
LOG.warning(_LW('Can not get access id from share. '
'share_name: %s'), share_name)
return
self.helper._remove_access_from_share(access_id, share_client_type)
def allow_access(self, share, access, share_server=None):
"""Allow access to the share."""
share_proto = share['share_proto']
share_name = share['name']
share_type = self.helper._get_share_type(share_proto)
access_type = access['access_type']
access_level = access['access_level']
if access_level not in common_constants.ACCESS_LEVELS:
raise exception.InvalidShareAccess(
reason=(_('Unsupported level of access was provided - %s') %
access_level))
if share_proto == 'NFS':
if access_type == 'ip':
if access_level == common_constants.ACCESS_LEVEL_RW:
access_level = constants.ACCESS_NFS_RW
else:
access_level = constants.ACCESS_NFS_RO
else:
message = _('Only IP access type is allowed for NFS shares.')
raise exception.InvalidShareAccess(reason=message)
elif share_proto == 'CIFS':
if access_type == 'user':
if access_level == common_constants.ACCESS_LEVEL_RW:
access_level = constants.ACCESS_CIFS_RW
else:
access_level = constants.ACCESS_CIFS_RO
else:
message = _('Only USER access type is allowed'
' for CIFS shares.')
raise exception.InvalidShareAccess(reason=message)
share = self.helper._get_share_by_name(share_name, share_type)
if not share:
err_msg = (_("Can not get share ID by share %s.")
% share_name)
LOG.error(err_msg)
raise exception.InvalidShareAccess(reason=err_msg)
share_id = share['ID']
access_to = access['access_to']
self.helper._allow_access_rest(share_id, access_to,
share_proto, access_level)
def allocate_container(self, share):
"""Creates filesystem associated to share by name."""
fileParam = self._init_filesys_para(share)
fsid = self.helper._create_filesystem(fileParam)
return fsid
def _get_location_path(self, share_name, share_proto):
root = self.helper._read_xml()
target_ip = root.findtext('Storage/LogicalPortIP').strip()
location = None
if share_proto == 'NFS':
location = '%s:/%s' % (target_ip,
share_name.replace("-", "_"))
elif share_proto == 'CIFS':
location = '\\\\%s\\%s' % (target_ip,
share_name.replace("-", "_"))
else:
raise exception.InvalidShareAccess(
reason=(_('Invalid NAS protocol supplied: %s.')
% share_proto))
return location
def _get_wait_interval(self):
"""Get wait interval from huawei conf file."""
root = self.helper._read_xml()
wait_interval = root.findtext('Filesystem/WaitInterval')
if wait_interval:
return int(wait_interval)
else:
LOG.info(_LI(
"Wait interval is not configured in huawei "
"conf file. Use default: %(default_wait_interval)d."),
{"default_wait_interval": constants.DEFAULT_WAIT_INTERVAL})
return constants.DEFAULT_WAIT_INTERVAL
def _get_timeout(self):
"""Get timeout from huawei conf file."""
root = self.helper._read_xml()
timeout = root.findtext('Filesystem/Timeout')
if timeout:
return int(timeout)
else:
LOG.info(_LI(
"Timeout is not configured in huawei conf file. "
"Use default: %(default_timeout)d."),
{"default_timeout": constants.DEFAULT_TIMEOUT})
return constants.DEFAULT_TIMEOUT
def check_conf_file(self):
"""Check the config file, make sure the essential items are set."""
root = self.helper._read_xml()
resturl = root.findtext('Storage/RestURL')
username = root.findtext('Storage/UserName')
pwd = root.findtext('Storage/UserPassword')
product = root.findtext('Storage/Product')
pool_node = root.findtext('Filesystem/StoragePool')
if product != "V3":
err_msg = (_(
'_check_conf_file: Config file invalid. '
'Product must be set to V3.'))
LOG.error(err_msg)
raise exception.InvalidInput(err_msg)
if not (resturl and username and pwd):
err_msg = (_(
'_check_conf_file: Config file invalid. RestURL,'
' UserName and UserPassword must be set.'))
LOG.error(err_msg)
raise exception.InvalidInput(err_msg)
if not pool_node:
err_msg = (_(
'_check_conf_file: Config file invalid. '
'StoragePool must be set.'))
LOG.error(err_msg)
raise exception.InvalidInput(err_msg)
def check_service(self):
running_status = self.helper._get_cifs_service_status()
if running_status != constants.STATUS_SERVICE_RUNNING:
self.helper._start_cifs_service_status()
service = self.helper._get_nfs_service_status()
if ((service['RUNNINGSTATUS'] != constants.STATUS_SERVICE_RUNNING) or
(service['SUPPORTV3'] == 'false') or
(service['SUPPORTV4'] == 'false')):
self.helper._start_nfs_service_status()