193 lines
7.6 KiB
Python
193 lines
7.6 KiB
Python
# 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 os
|
|
|
|
from oslo_concurrency import lockutils
|
|
from oslo_log import log as logging
|
|
|
|
from os_brick.i18n import _, _LE
|
|
from os_brick import exception
|
|
from os_brick.initiator.connectors import base
|
|
from os_brick import utils
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
synchronized = lockutils.synchronized_with_prefix('os-brick-')
|
|
|
|
|
|
class HuaweiStorHyperConnector(base.BaseLinuxConnector):
|
|
""""Connector class to attach/detach SDSHypervisor volumes."""
|
|
|
|
attached_success_code = 0
|
|
has_been_attached_code = 50151401
|
|
attach_mnid_done_code = 50151405
|
|
vbs_unnormal_code = 50151209
|
|
not_mount_node_code = 50155007
|
|
iscliexist = True
|
|
|
|
def __init__(self, root_helper, driver=None,
|
|
*args, **kwargs):
|
|
self.cli_path = os.getenv('HUAWEISDSHYPERVISORCLI_PATH')
|
|
if not self.cli_path:
|
|
self.cli_path = '/usr/local/bin/sds/sds_cli'
|
|
LOG.debug("CLI path is not configured, using default %s.",
|
|
self.cli_path)
|
|
if not os.path.isfile(self.cli_path):
|
|
self.iscliexist = False
|
|
LOG.error(_LE('SDS CLI file not found, '
|
|
'HuaweiStorHyperConnector init failed.'))
|
|
super(HuaweiStorHyperConnector, self).__init__(root_helper,
|
|
driver=driver,
|
|
*args, **kwargs)
|
|
|
|
@staticmethod
|
|
def get_connector_properties(root_helper, *args, **kwargs):
|
|
"""The HuaweiStor connector properties."""
|
|
return {}
|
|
|
|
def get_search_path(self):
|
|
# TODO(walter-boring): Where is the location on the filesystem to
|
|
# look for Huawei volumes to show up?
|
|
return None
|
|
|
|
def get_all_available_volumes(self, connection_properties=None):
|
|
# TODO(walter-boring): what to return here for all Huawei volumes ?
|
|
return []
|
|
|
|
def get_volume_paths(self, connection_properties):
|
|
volume_path = None
|
|
try:
|
|
volume_path = self._get_volume_path(connection_properties)
|
|
except Exception:
|
|
msg = _("Couldn't find a volume.")
|
|
LOG.warning(msg)
|
|
raise exception.BrickException(message=msg)
|
|
return [volume_path]
|
|
|
|
def _get_volume_path(self, connection_properties):
|
|
out = self._query_attached_volume(
|
|
connection_properties['volume_id'])
|
|
if not out or int(out['ret_code']) != 0:
|
|
msg = _("Couldn't find attached volume.")
|
|
LOG.error(msg)
|
|
raise exception.BrickException(message=msg)
|
|
return out['dev_addr']
|
|
|
|
@utils.trace
|
|
@synchronized('connect_volume')
|
|
def connect_volume(self, connection_properties):
|
|
"""Connect to a volume.
|
|
|
|
:param connection_properties: The dictionary that describes all
|
|
of the target volume attributes.
|
|
:type connection_properties: dict
|
|
:returns: dict
|
|
"""
|
|
LOG.debug("Connect_volume connection properties: %s.",
|
|
connection_properties)
|
|
out = self._attach_volume(connection_properties['volume_id'])
|
|
if not out or int(out['ret_code']) not in (self.attached_success_code,
|
|
self.has_been_attached_code,
|
|
self.attach_mnid_done_code):
|
|
msg = (_("Attach volume failed, "
|
|
"error code is %s") % out['ret_code'])
|
|
raise exception.BrickException(message=msg)
|
|
|
|
try:
|
|
volume_path = self._get_volume_path(connection_properties)
|
|
except Exception:
|
|
msg = _("query attached volume failed or volume not attached.")
|
|
LOG.error(msg)
|
|
raise exception.BrickException(message=msg)
|
|
|
|
device_info = {'type': 'block',
|
|
'path': volume_path}
|
|
return device_info
|
|
|
|
@utils.trace
|
|
@synchronized('connect_volume')
|
|
def disconnect_volume(self, connection_properties, device_info):
|
|
"""Disconnect a volume from the local host.
|
|
|
|
:param connection_properties: The dictionary that describes all
|
|
of the target volume attributes.
|
|
:type connection_properties: dict
|
|
:param device_info: historical difference, but same as connection_props
|
|
:type device_info: dict
|
|
"""
|
|
LOG.debug("Disconnect_volume: %s.", connection_properties)
|
|
out = self._detach_volume(connection_properties['volume_id'])
|
|
if not out or int(out['ret_code']) not in (self.attached_success_code,
|
|
self.vbs_unnormal_code,
|
|
self.not_mount_node_code):
|
|
msg = (_("Disconnect_volume failed, "
|
|
"error code is %s") % out['ret_code'])
|
|
raise exception.BrickException(message=msg)
|
|
|
|
def is_volume_connected(self, volume_name):
|
|
"""Check if volume already connected to host"""
|
|
LOG.debug('Check if volume %s already connected to a host.',
|
|
volume_name)
|
|
out = self._query_attached_volume(volume_name)
|
|
if out:
|
|
return int(out['ret_code']) == 0
|
|
return False
|
|
|
|
def _attach_volume(self, volume_name):
|
|
return self._cli_cmd('attach', volume_name)
|
|
|
|
def _detach_volume(self, volume_name):
|
|
return self._cli_cmd('detach', volume_name)
|
|
|
|
def _query_attached_volume(self, volume_name):
|
|
return self._cli_cmd('querydev', volume_name)
|
|
|
|
def _cli_cmd(self, method, volume_name):
|
|
LOG.debug("Enter into _cli_cmd.")
|
|
if not self.iscliexist:
|
|
msg = _("SDS command line doesn't exist, "
|
|
"can't execute SDS command.")
|
|
raise exception.BrickException(message=msg)
|
|
if not method or volume_name is None:
|
|
return
|
|
cmd = [self.cli_path, '-c', method, '-v', volume_name]
|
|
out, clilog = self._execute(*cmd, run_as_root=False,
|
|
root_helper=self._root_helper)
|
|
analyse_result = self._analyze_output(out)
|
|
LOG.debug('%(method)s volume returns %(analyse_result)s.',
|
|
{'method': method, 'analyse_result': analyse_result})
|
|
if clilog:
|
|
LOG.error(_LE("SDS CLI output some log: %s."), clilog)
|
|
return analyse_result
|
|
|
|
def _analyze_output(self, out):
|
|
LOG.debug("Enter into _analyze_output.")
|
|
if out:
|
|
analyse_result = {}
|
|
out_temp = out.split('\n')
|
|
for line in out_temp:
|
|
LOG.debug("Line is %s.", line)
|
|
if line.find('=') != -1:
|
|
key, val = line.split('=', 1)
|
|
LOG.debug("%(key)s = %(val)s", {'key': key, 'val': val})
|
|
if key in ['ret_code', 'ret_desc', 'dev_addr']:
|
|
analyse_result[key] = val
|
|
return analyse_result
|
|
else:
|
|
return None
|
|
|
|
def extend_volume(self, connection_properties):
|
|
# TODO(walter-boring): is this possible?
|
|
raise NotImplementedError
|