execute=execute,
device_scan_attempts=device_scan_attempts,
*args, **kwargs)
+ elif protocol == "HUAWEISDSHYPERVISOR":
+ return HuaweiStorHyperConnector(root_helper=root_helper,
+ driver=driver,
+ execute=execute,
+ device_scan_attempts=
+ device_scan_attempts,
+ *args, **kwargs)
else:
msg = (_("Invalid InitiatorConnector protocol "
"specified %(protocol)s") %
def disconnect_volume(self, connection_properties, device_info):
"""Disconnect a volume from the local host."""
pass
+
+
+class HuaweiStorHyperConnector(InitiatorConnector):
+ """"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, execute=putils.execute,
+ *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,
+ execute=execute,
+ *args, **kwargs)
+
+ @synchronized('connect_volume')
+ def connect_volume(self, connection_properties):
+ """Connect to a volume."""
+ 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(msg=msg)
+ out = self._query_attached_volume(
+ connection_properties['volume_id'])
+ if not out or int(out['ret_code']) != 0:
+ msg = _("query attached volume failed or volume not attached.")
+ raise exception.BrickException(msg=msg)
+
+ device_info = {'type': 'block',
+ 'path': out['dev_addr']}
+ return device_info
+
+ @synchronized('connect_volume')
+ def disconnect_volume(self, connection_properties, device_info):
+ """Disconnect a volume from the local host."""
+ 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(msg=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, "
+ "cann't execute SDS command.")
+ raise exception.BrickException(msg=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 + " = " + val)
+ if key in ['ret_code', 'ret_desc', 'dev_addr']:
+ analyse_result[key] = val
+ return analyse_result
+ else:
+ return None
import os.path
import string
+import tempfile
import time
from oslo.concurrency import processutils as putils
cprops = {}
self.assertRaises(ValueError,
self.connector.connect_volume, cprops)
+
+
+class HuaweiStorHyperConnectorTestCase(ConnectorTestCase):
+ """Test cases for StorHyper initiator class."""
+
+ attached = False
+
+ def setUp(self):
+ super(HuaweiStorHyperConnectorTestCase, self).setUp()
+ self.fake_sdscli_file = tempfile.mktemp()
+ self.addCleanup(os.remove, self.fake_sdscli_file)
+ newefile = open(self.fake_sdscli_file, 'w')
+ newefile.write('test')
+ newefile.close()
+
+ self.connector = connector.HuaweiStorHyperConnector(
+ None, execute=self.fake_execute)
+ self.connector.cli_path = self.fake_sdscli_file
+ self.connector.iscliexist = True
+
+ self.connector_fail = connector.HuaweiStorHyperConnector(
+ None, execute=self.fake_execute_fail)
+ self.connector_fail.cli_path = self.fake_sdscli_file
+ self.connector_fail.iscliexist = True
+
+ self.connector_nocli = connector.HuaweiStorHyperConnector(
+ None, execute=self.fake_execute_fail)
+ self.connector_nocli.cli_path = self.fake_sdscli_file
+ self.connector_nocli.iscliexist = False
+
+ self.connection_properties = {
+ 'access_mode': 'rw',
+ 'qos_specs': None,
+ 'volume_id': 'volume-b2911673-863c-4380-a5f2-e1729eecfe3f'
+ }
+
+ self.device_info = {'type': 'block',
+ 'path': '/dev/vdxxx'}
+ HuaweiStorHyperConnectorTestCase.attached = False
+
+ def fake_execute(self, *cmd, **kwargs):
+ method = cmd[2]
+ self.cmds.append(string.join(cmd))
+ if 'attach' == method:
+ HuaweiStorHyperConnectorTestCase.attached = True
+ return 'ret_code=0', None
+ if 'querydev' == method:
+ if HuaweiStorHyperConnectorTestCase.attached:
+ return 'ret_code=0\ndev_addr=/dev/vdxxx', None
+ else:
+ return 'ret_code=1\ndev_addr=/dev/vdxxx', None
+ if 'detach' == method:
+ HuaweiStorHyperConnectorTestCase.attached = False
+ return 'ret_code=0', None
+
+ def fake_execute_fail(self, *cmd, **kwargs):
+ method = cmd[2]
+ self.cmds.append(string.join(cmd))
+ if 'attach' == method:
+ HuaweiStorHyperConnectorTestCase.attached = False
+ return 'ret_code=330151401', None
+ if 'querydev' == method:
+ if HuaweiStorHyperConnectorTestCase.attached:
+ return 'ret_code=0\ndev_addr=/dev/vdxxx', None
+ else:
+ return 'ret_code=1\ndev_addr=/dev/vdxxx', None
+ if 'detach' == method:
+ HuaweiStorHyperConnectorTestCase.attached = True
+ return 'ret_code=330155007', None
+
+ def test_connect_volume(self):
+ """Test the basic connect volume case."""
+
+ retval = self.connector.connect_volume(self.connection_properties)
+ self.assertEqual(self.device_info, retval)
+
+ expected_commands = [self.fake_sdscli_file + ' -c attach'
+ ' -v volume-b2911673-863c-4380-a5f2-e1729eecfe3f',
+ self.fake_sdscli_file + ' -c querydev'
+ ' -v volume-b2911673-863c-4380-a5f2-e1729eecfe3f']
+ LOG.debug("self.cmds = %s." % self.cmds)
+ LOG.debug("expected = %s." % expected_commands)
+
+ self.assertEqual(expected_commands, self.cmds)
+
+ def test_disconnect_volume(self):
+ """Test the basic disconnect volume case."""
+ self.connector.connect_volume(self.connection_properties)
+ self.assertEqual(True, HuaweiStorHyperConnectorTestCase.attached)
+ self.connector.disconnect_volume(self.connection_properties,
+ self.device_info)
+ self.assertEqual(False, HuaweiStorHyperConnectorTestCase.attached)
+
+ expected_commands = [self.fake_sdscli_file + ' -c attach'
+ ' -v volume-b2911673-863c-4380-a5f2-e1729eecfe3f',
+ self.fake_sdscli_file + ' -c querydev'
+ ' -v volume-b2911673-863c-4380-a5f2-e1729eecfe3f',
+ self.fake_sdscli_file + ' -c detach'
+ ' -v volume-b2911673-863c-4380-a5f2-e1729eecfe3f']
+
+ LOG.debug("self.cmds = %s." % self.cmds)
+ LOG.debug("expected = %s." % expected_commands)
+
+ self.assertEqual(expected_commands, self.cmds)
+
+ def test_is_volume_connected(self):
+ """Test if volume connected to host case."""
+ self.connector.connect_volume(self.connection_properties)
+ self.assertEqual(True, HuaweiStorHyperConnectorTestCase.attached)
+ is_connected = self.connector.is_volume_connected(
+ 'volume-b2911673-863c-4380-a5f2-e1729eecfe3f')
+ self.assertEqual(HuaweiStorHyperConnectorTestCase.attached,
+ is_connected)
+ self.connector.disconnect_volume(self.connection_properties,
+ self.device_info)
+ self.assertEqual(False, HuaweiStorHyperConnectorTestCase.attached)
+ is_connected = self.connector.is_volume_connected(
+ 'volume-b2911673-863c-4380-a5f2-e1729eecfe3f')
+ self.assertEqual(HuaweiStorHyperConnectorTestCase.attached,
+ is_connected)
+
+ expected_commands = [self.fake_sdscli_file + ' -c attach'
+ ' -v volume-b2911673-863c-4380-a5f2-e1729eecfe3f',
+ self.fake_sdscli_file + ' -c querydev'
+ ' -v volume-b2911673-863c-4380-a5f2-e1729eecfe3f',
+ self.fake_sdscli_file + ' -c querydev'
+ ' -v volume-b2911673-863c-4380-a5f2-e1729eecfe3f',
+ self.fake_sdscli_file + ' -c detach'
+ ' -v volume-b2911673-863c-4380-a5f2-e1729eecfe3f',
+ self.fake_sdscli_file + ' -c querydev'
+ ' -v volume-b2911673-863c-4380-a5f2-e1729eecfe3f']
+
+ LOG.debug("self.cmds = %s." % self.cmds)
+ LOG.debug("expected = %s." % expected_commands)
+
+ self.assertEqual(expected_commands, self.cmds)
+
+ def test__analyze_output(self):
+ cliout = 'ret_code=0\ndev_addr=/dev/vdxxx\nret_desc="success"'
+ analyze_result = {'dev_addr': '/dev/vdxxx',
+ 'ret_desc': '"success"',
+ 'ret_code': '0'}
+ result = self.connector._analyze_output(cliout)
+ self.assertEqual(analyze_result, result)
+
+ def test_connect_volume_fail(self):
+ """Test the fail connect volume case."""
+ self.assertRaises(exception.BrickException,
+ self.connector_fail.connect_volume,
+ self.connection_properties)
+ expected_commands = [self.fake_sdscli_file + ' -c attach'
+ ' -v volume-b2911673-863c-4380-a5f2-e1729eecfe3f']
+ LOG.debug("self.cmds = %s." % self.cmds)
+ LOG.debug("expected = %s." % expected_commands)
+ self.assertEqual(expected_commands, self.cmds)
+
+ def test_disconnect_volume_fail(self):
+ """Test the fail disconnect volume case."""
+ self.connector.connect_volume(self.connection_properties)
+ self.assertEqual(True, HuaweiStorHyperConnectorTestCase.attached)
+ self.assertRaises(exception.BrickException,
+ self.connector_fail.disconnect_volume,
+ self.connection_properties,
+ self.device_info)
+
+ expected_commands = [self.fake_sdscli_file + ' -c attach'
+ ' -v volume-b2911673-863c-4380-a5f2-e1729eecfe3f',
+ self.fake_sdscli_file + ' -c querydev'
+ ' -v volume-b2911673-863c-4380-a5f2-e1729eecfe3f',
+ self.fake_sdscli_file + ' -c detach'
+ ' -v volume-b2911673-863c-4380-a5f2-e1729eecfe3f']
+
+ LOG.debug("self.cmds = %s." % self.cmds)
+ LOG.debug("expected = %s." % expected_commands)
+
+ self.assertEqual(expected_commands, self.cmds)
+
+ def test_connect_volume_nocli(self):
+ """Test the fail connect volume case."""
+ self.assertRaises(exception.BrickException,
+ self.connector_nocli.connect_volume,
+ self.connection_properties)
+
+ def test_disconnect_volume_nocli(self):
+ """Test the fail disconnect volume case."""
+ self.connector.connect_volume(self.connection_properties)
+ self.assertEqual(True, HuaweiStorHyperConnectorTestCase.attached)
+ self.assertRaises(exception.BrickException,
+ self.connector_nocli.disconnect_volume,
+ self.connection_properties,
+ self.device_info)
+ expected_commands = [self.fake_sdscli_file + ' -c attach'
+ ' -v volume-b2911673-863c-4380-a5f2-e1729eecfe3f',
+ self.fake_sdscli_file + ' -c querydev'
+ ' -v volume-b2911673-863c-4380-a5f2-e1729eecfe3f']
+
+ LOG.debug("self.cmds = %s." % self.cmds)
+ LOG.debug("expected = %s." % expected_commands)
\ No newline at end of file