Address review comments for MacroSAN driver

- Removing unnecessary os-brick code and refactor/optimize driver
- Solve the modifications made by the reviewer in the 19th and 20th
  patches in the driver of the initial join.
  Link:https://review.opendev.org/#/c/612311/

Closes-Bug: #1837920
Implements: blueprint macrosan-cinder-driver
Change-Id: I0a6b13941936cd9e8521e3fcbe8fb61d3da3c45c
This commit is contained in:
lol 2019-08-02 10:40:26 +08:00
parent 0a0d55d8a9
commit 12096ee1d0
5 changed files with 181 additions and 211 deletions

View File

@ -34,7 +34,6 @@ test_volume = (
UserDict({'name': 'volume-728ec287-bf30-4d2d-98a8-7f1bed3f59ce',
'volume_name': 'test',
'id': '728ec287-bf30-4d2d-98a8-7f1bed3f59ce',
'volume_id': '728ec287-bf30-4d2d-98a8-7f1bed3f59ce',
'provider_auth': None,
'project_id': 'project',
'display_name': 'test',
@ -45,7 +44,7 @@ test_volume = (
'macrosan uuid:0x00b34201-025b0000-46b35ae7-b7deec47'}))
test_volume.size = 10
test_volume.volume_type_id = None
test_volume.volume_type_id = '36674caf-5314-468a-a8cb-baab4f71fe44'
test_volume.volume_attachment = []
test_migrate_volume = {
@ -58,7 +57,7 @@ test_migrate_volume = {
'project_id': 'project',
'display_name': 'test',
'display_description': 'test',
'volume_type_id': None,
'volume_type_id': '36674caf-5314-468a-a8cb-baab4f71fe44',
'_name_id': None,
'host': 'controller@macrosan#MacroSAN',
'provider_location':
@ -73,7 +72,7 @@ test_snap = {'name': 'volume-728ec287-bf30-4d2d-98a8-7f1bed3f59ce',
'project_id': 'project',
'display_name': 'test',
'display_description': 'test volume',
'volume_type_id': None,
'volume_type_id': '36674caf-5314-468a-a8cb-baab4f71fe44',
'provider_location': 'pointid: 1',
'volume_size': 10,
'volume': test_volume}
@ -106,6 +105,17 @@ expected_iscsi_properties = {'target_discovered': False,
'728ec287-bf30-4d2d-98a8-7f1bed3f59ce'
}
expected_iscsi_connection_data = {
'client': 'devstack',
'ports': [{'ip': '192.168.251.1',
'port': 'eth-1:0:0',
'port_name': 'iSCSI-Target-1:0:0',
'target': 'iqn.2010-05.com.macrosan.target:controller'},
{'ip': '192.168.251.2',
'port': 'eth-2:0:0',
'port_name': 'iSCSI-Target-2:0:0',
'target': 'iqn.2010-05.com.macrosan.target:controller'}]}
expected_initr_port_map_tgtexist = {
'21:00:00:24:ff:20:03:ec': [{'port_name': 'FC-Target-1:1:1',
'wwn': '50:0b:34:20:01:00:18:05'},
@ -188,12 +198,10 @@ class FakeMacroSANISCSIDriver(driver.MacroSANISCSIDriver):
def _get_client_name(self, host):
return 'devstack'
@utils.synchronized('MacroSAN-Attach', external=True)
def _attach_volume(self, context, volume, properties, remote=False):
return super(FakeMacroSANISCSIDriver, self)._attach_volume(
context, volume, properties, remote)
@utils.synchronized('MacroSAN-Attach', external=True)
def _detach_volume(self, context, attach_info, volume,
properties, force=False, remote=False,
ignore_errors=True):
@ -383,8 +391,7 @@ class MacroSANISCSIDriverTestCase(test.TestCase):
def setUp(self):
super(MacroSANISCSIDriverTestCase, self).setUp()
self.configuration = mock.Mock(spec=conf.Configuration)
self.configuration.san_ip = \
"172.192.251.1, 172.192.251.2"
self.configuration.san_ip = "172.192.251.1, 172.192.251.2"
self.configuration.san_login = "openstack"
self.configuration.san_password = "passwd"
self.configuration.macrosan_sdas_ipaddrs = None
@ -398,9 +405,8 @@ class MacroSANISCSIDriverTestCase(test.TestCase):
self.configuration.macrosan_snapshot_resource_ratio = 0.3
self.configuration.macrosan_log_timing = True
self.configuration.macrosan_client = \
['devstack; decive1; "eth-1:0:0"; "eth-2:0:0"']
self.configuration.macrosan_client_default = \
"eth-1:0:0;eth-2:0:0"
['devstack; device1; "eth-1:0:0"; "eth-2:0:0"']
self.configuration.macrosan_client_default = "eth-1:0:0;eth-2:0:0"
self.driver = FakeMacroSANISCSIDriver(configuration=self.configuration)
self.driver.do_setup()
@ -465,12 +471,19 @@ class MacroSANISCSIDriverTestCase(test.TestCase):
actual = ret['provider_location']
self.assertEqual(test_volume['provider_location'], actual)
@mock.patch.object(volume_types, 'get_volume_type',
return_value={'qos_specs_id':
'99f3d240-1b20-4b7b-9321-c6b8b86243ff',
'extra_specs': {}})
@mock.patch.object(qos_specs, 'get_qos_specs',
return_value={'specs': {'qos-strategy': 'QoS-1'}})
@mock.patch.object(socket, 'gethostname', return_value='controller')
@mock.patch.object(utils, 'brick_get_connector',
return_value=DummyBrickGetConnector())
@mock.patch.object(volutils, 'copy_volume', return_value=None)
@mock.patch.object(os.path, 'realpath', return_value=None)
def test_create_cloned_volume(self, mock_hostname,
def test_create_cloned_volume(self, mock_volume_types, mock_qos,
mock_hostname,
mock_brick_get_connector,
mock_copy_volume,
mock_os_path):
@ -514,7 +527,9 @@ class MacroSANISCSIDriverTestCase(test.TestCase):
@mock.patch.object(qos_specs, 'get_qos_specs',
return_value={'specs': {'qos-strategy': 'QoS-1'}})
def test_terminate_connection(self, mock_volume_type, mock_qos):
self.driver.terminate_connection(test_volume, test_connector)
ret = self.driver.terminate_connection(test_volume, test_connector)
self.assertEqual({'driver_volume_type': 'iSCSI',
'data': expected_iscsi_connection_data}, ret)
def test_get_raid_list(self):
expected = ["RAID-1"]
@ -564,12 +579,19 @@ class MacroSANISCSIDriverTestCase(test.TestCase):
self.driver.create_volume_from_snapshot,
test_volume, test_snap)
@mock.patch.object(volume_types, 'get_volume_type',
return_value={'qos_specs_id':
'99f3d240-1b20-4b7b-9321-c6b8b86243ff',
'extra_specs': {}})
@mock.patch.object(qos_specs, 'get_qos_specs',
return_value={'specs': {'qos-strategy': 'QoS-1'}})
@mock.patch.object(socket, 'gethostname', return_value='controller')
@mock.patch.object(utils, 'brick_get_connector',
return_value=DummyBrickGetConnector())
@mock.patch.object(volutils, 'copy_volume', return_value=None)
@mock.patch.object(os.path, 'realpath', return_value=None)
def test_create_cloned_volume_fail(self, mock_hostname,
def test_create_cloned_volume_fail(self, mock_volume_types, mock_qos,
mock_hostname,
mock_brick_get_connector,
mock_copy_volume,
mock_os_path):
@ -630,7 +652,7 @@ class MacroSANFCDriverTestCase(test.TestCase):
self.configuration.macrosan_fc_keep_mapped_ports = True
self.configuration.macrosan_host_name = 'devstack'
self.configuration.macrosan_client = \
['devstack; decive1; "eth-1:0:0"; "eth-2:0:0"']
['devstack; device1; "eth-1:0:0"; "eth-2:0:0"']
self.configuration.macrosan_client_default = \
"eth-1:0:0;eth-2:0:0"
self.driver = FakeMacroSANFCDriver(configuration=self.configuration)
@ -647,19 +669,40 @@ class MacroSANFCDriverTestCase(test.TestCase):
test_connector['wwpns'])
self.assertEqual(expected_initr_port_map_tgtexist, ret)
def test_initialize_connection(self):
@mock.patch.object(volume_types, 'get_volume_type',
return_value={'qos_specs_id':
'99f3d240-1b20-4b7b-9321-c6b8b86243ff',
'extra_specs': {}})
@mock.patch.object(qos_specs, 'get_qos_specs',
return_value={'specs': {'qos-strategy': 'QoS-1'}})
def test_initialize_connection(self, mock_volume_types, mock_qos):
ret = self.driver.initialize_connection(test_volume, test_connector)
self.assertEqual(expected_fctgtexist_properties, ret['data'])
def test_terminate_connection(self):
self.driver.terminate_connection(test_volume, test_connector)
@mock.patch.object(volume_types, 'get_volume_type',
return_value={'qos_specs_id':
'99f3d240-1b20-4b7b-9321-c6b8b86243ff',
'extra_specs': {}})
@mock.patch.object(qos_specs, 'get_qos_specs',
return_value={'specs': {'qos-strategy': 'QoS-1'}})
def test_terminate_connection(self, mock_volume_types, mock_qos):
ret = self.driver.terminate_connection(test_volume, test_connector)
self.assertEqual({'driver_volume_type': 'fibre_channel', 'data': {}},
ret)
@mock.patch.object(volume_types, 'get_volume_type',
return_value={'qos_specs_id':
'99f3d240-1b20-4b7b-9321-c6b8b86243ff',
'extra_specs': {}})
@mock.patch.object(qos_specs, 'get_qos_specs',
return_value={'specs': {'qos-strategy': 'QoS-1'}})
@mock.patch.object(socket, 'gethostname', return_value='controller')
@mock.patch.object(utils, 'brick_get_connector',
return_value=DummyBrickGetConnector())
@mock.patch.object(volutils, 'copy_volume', return_value=None)
@mock.patch.object(os.path, 'realpath', return_value=None)
def test_create_volume_from_snapshot(self, mock_hostname,
def test_create_volume_from_snapshot(self, mock_volume_types, mock_qos,
mock_hostname,
mock_brick_get_connector,
mock_copy_volume,
mock_os_path):
@ -667,12 +710,20 @@ class MacroSANFCDriverTestCase(test.TestCase):
actual = ret['provider_location']
self.assertEqual(test_volume['provider_location'], actual)
@mock.patch.object(volume_types, 'get_volume_type',
return_value={
'qos_specs_id':
'99f3d240-1b20-4b7b-9321-c6b8b86243ff',
'extra_specs': {}})
@mock.patch.object(qos_specs, 'get_qos_specs',
return_value={'specs': {'qos-strategy': 'QoS-1'}})
@mock.patch.object(socket, 'gethostname', return_value='controller')
@mock.patch.object(utils, 'brick_get_connector',
return_value=DummyBrickGetConnector())
@mock.patch.object(volutils, 'copy_volume', return_value=None)
@mock.patch.object(os.path, 'realpath', return_value=None)
def test_create_cloned_volume(self, mock_hostname,
def test_create_cloned_volume(self, mock_volume_types, mock_qos,
mock_hostname,
mock_brick_get_connector,
mock_copy_volume,
mock_os_path):
@ -681,12 +732,20 @@ class MacroSANFCDriverTestCase(test.TestCase):
actual = ret['provider_location']
self.assertEqual(test_volume['provider_location'], actual)
@mock.patch.object(volume_types, 'get_volume_type',
return_value={'qos_specs_id':
'99f3d240-1b20-4b7b-9321-c6b8b86243ff',
'extra_specs': {}})
@mock.patch.object(qos_specs, 'get_qos_specs',
return_value={'specs': {'qos-strategy': 'QoS-1'}})
@mock.patch.object(socket, 'gethostname', return_value='controller')
@mock.patch.object(utils, 'brick_get_connector',
return_value=DummyBrickGetConnector())
@mock.patch.object(volutils, 'copy_volume', return_value=None)
@mock.patch.object(os.path, 'realpath', return_value=None)
def test_create_volume_from_snapshot_fail(self, mock_hostname,
def test_create_volume_from_snapshot_fail(self, mock_volume_types,
mock_qos,
mock_hostname,
mock_brick_get_connector,
mock_copy_volume,
mock_os_path):
@ -695,12 +754,19 @@ class MacroSANFCDriverTestCase(test.TestCase):
self.driver.create_volume_from_snapshot,
test_volume, test_snap)
@mock.patch.object(volume_types, 'get_volume_type',
return_value={'qos_specs_id':
'99f3d240-1b20-4b7b-9321-c6b8b86243ff',
'extra_specs': {}})
@mock.patch.object(qos_specs, 'get_qos_specs',
return_value={'specs': {'qos-strategy': 'QoS-1'}})
@mock.patch.object(socket, 'gethostname', return_value='controller')
@mock.patch.object(utils, 'brick_get_connector',
return_value=DummyBrickGetConnector())
@mock.patch.object(volutils, 'copy_volume', return_value=None)
@mock.patch.object(os.path, 'realpath', return_value=None)
def test_create_cloned_volume_fail(self, mock_hostname,
def test_create_cloned_volume_fail(self, mock_volume_types, mock_qos,
mock_hostname,
mock_brick_get_connector,
mock_copy_volume,
mock_os_path):
@ -709,13 +775,25 @@ class MacroSANFCDriverTestCase(test.TestCase):
self.driver.create_cloned_volume,
test_volume, test_volume)
def test_initialize_connection_fail(self):
@mock.patch.object(volume_types, 'get_volume_type',
return_value={'qos_specs_id':
'99f3d240-1b20-4b7b-9321-c6b8b86243ff',
'extra_specs': {}})
@mock.patch.object(qos_specs, 'get_qos_specs',
return_value={'specs': {'qos-strategy': 'QoS-1'}})
def test_initialize_connection_fail(self, mock_volume_types, mock_qos):
self.driver.client.cmd_fail = True
self.assertRaises(exception.VolumeBackendAPIException,
self.driver.initialize_connection,
test_volume, test_connector)
def test_terminate_connection_fail(self):
@mock.patch.object(volume_types, 'get_volume_type',
return_value={'qos_specs_id':
'99f3d240-1b20-4b7b-9321-c6b8b86243ff',
'extra_specs': {}})
@mock.patch.object(qos_specs, 'get_qos_specs',
return_value={'specs': {'qos-strategy': 'QoS-1'}})
def test_terminate_connection_fail(self, mock_volume_types, mock_qos):
self.driver.client.cmd_fail = True
self.assertRaises(exception.VolumeBackendAPIException,
self.driver.terminate_connection,

View File

@ -20,33 +20,25 @@ from oslo_config import cfg
macrosan_opts = [
# sdas login_info
cfg.ListOpt('macrosan_sdas_ipaddrs',
default=None,
help="MacroSAN sdas devices' ip addresses"),
cfg.StrOpt('macrosan_sdas_username',
default=None,
help=""),
help="MacroSAN sdas devices' username"),
cfg.StrOpt('macrosan_sdas_password',
default=None,
help="",
secret=True),
secret=True,
help="MacroSAN sdas devices' password"),
# replication login_info
cfg.ListOpt('macrosan_replication_ipaddrs',
default=None,
help="MacroSAN replication devices' ip addresses"),
cfg.StrOpt('macrosan_replication_username',
default=None,
help=""),
help="MacroSAN replication devices' username"),
cfg.StrOpt('macrosan_replication_password',
default=None,
help="",
secret=True),
secret=True,
help="MacroSAN replication devices' password"),
cfg.ListOpt('macrosan_replication_destination_ports',
default=None,
sample_default="eth-1:0/eth-1:1, eth-2:0/eth-2:1",
help="Slave device"),
# device_features
cfg.StrOpt('macrosan_pool', quotes=True,
default=None,
help='Pool to use for volume creation'),
cfg.IntOpt('macrosan_thin_lun_extent_size',
default=8,
@ -80,21 +72,20 @@ macrosan_opts = [
"item associated with the port is maintained."),
# iscsi connection
cfg.ListOpt('macrosan_client',
default=None,
help="""Macrosan iscsi_clients list.
You can configure multiple clients.
You can configure it in this format:
(host; client_name; sp1_iscsi_port; sp2_iscsi_port),
(host; client_name; sp1_iscsi_port; sp2_iscsi_port)
Important warning, Client_name has the following requirements:
[a-zA-Z0-9.-_:], the maximum number of characters is 31
[a-zA-Z0-9.-_:], the maximum number of characters is 31
E.g:
(controller1; decive1; eth-1:0; eth-2:0),
(controller2; decive2; eth-1:0/eth-1:1; eth-2:0/eth-2:1),
(controller1; device1; eth-1:0; eth-2:0),
(controller2; device2; eth-1:0/eth-1:1; eth-2:0/eth-2:1),
"""),
cfg.StrOpt('macrosan_client_default',
default=None,
help="This is the default connection information for iscsi. "
help="This is the default connection ports' name for iscsi. "
"This default configuration is used "
"when no host related information is obtained.")
"when no host related information is obtained."
"E.g: eth-1:0/eth-1:1; eth-2:0/eth-2:1")
]

View File

@ -109,7 +109,7 @@ class Client(object):
'attr': 'existence',
'name': name
}
return self.send_request('get', '/lun', data=data)
return self.send_request(method='get', url='/lun', data=data)
def snapshot_point_exists(self, lun_name, pointid):
"""Whether the snapshot point exists."""

View File

@ -14,17 +14,13 @@
# under the License.
"""Volume Drivers for MacroSAN SAN."""
import base64
from contextlib import contextmanager
import math
import re
import six
import socket
import time
import uuid
from os_brick.initiator import connector as cn
from os_brick.initiator import linuxfc
from oslo_config import cfg
from oslo_log import log as logging
from oslo_utils import excutils
@ -47,7 +43,7 @@ from cinder.volume import utils as volume_utils
from cinder.volume import volume_types
from cinder.zonemanager import utils as fczm_utils
version = '1.0.0'
version = '1.0.1'
lock_name = 'MacroSAN'
LOG = logging.getLogger(__name__)
@ -63,22 +59,6 @@ def ignored(*exceptions):
pass
def _timing(fn):
def __timing(*vargs, **kv):
start = time.time()
if timing_on:
LOG.info('========== start %s', fn.__name__)
result = fn(*vargs, **kv)
if timing_on:
end = time.time()
LOG.info('========== end %(fname)s, cost: %(cost).2f secs',
{'fname': fn.__name__, 'cost': end - start})
return result
return __timing
def record_request_id(fn):
def _record_request_id(*vargs, **kv):
ctx = context.context.get_current()
@ -93,14 +73,6 @@ def replication_synced(params):
params['replication_mode'] == 'sync')
def b64encode(s):
return base64.b64encode(six.b(s)).decode()
def b64decode(s):
return base64.b64decode(six.b(s)).decode()
class MacroSANBaseDriver(driver.VolumeDriver):
"""Base driver for MacroSAN SAN."""
@ -182,15 +154,6 @@ class MacroSANBaseDriver(driver.VolumeDriver):
self.initialize_iscsi_info()
@staticmethod
def get_driver_options():
"""Return the oslo_config options specific to the driver."""
return config.macrosan_opts
@property
def _self_node_wwns(self):
return []
def _size_str_to_int(self, size_in_g):
if int(size_in_g) == 0:
return 1
@ -254,7 +217,7 @@ class MacroSANBaseDriver(driver.VolumeDriver):
self.replica_login_info))
self.device_uuid = self.client.get_device_uuid()
self._do_setup()
LOG.info('MacroSAN Cinder Driver setup complete.')
LOG.debug('MacroSAN Cinder Driver setup complete.')
def _do_setup(self):
pass
@ -448,14 +411,9 @@ class MacroSANBaseDriver(driver.VolumeDriver):
@synchronized(lock_name)
@record_request_id
@_timing
@utils.trace
def create_volume(self, volume):
"""Create a volume."""
LOG.debug(('========== create volume, name: %(name)s,'
'id: %(volume_id)s, size: %(size)s.'),
{'name': volume['name'], 'volume_id': volume['id'],
'size': volume['size']})
name = volume['name']
size = self._size_str_to_int(volume['size'])
params = self._parse_volume_params(volume)
@ -508,22 +466,21 @@ class MacroSANBaseDriver(driver.VolumeDriver):
@synchronized(lock_name)
@record_request_id
@_timing
@utils.trace
def delete_volume(self, volume):
"""Delete a volume."""
LOG.debug('========== delete volume, id: %s.', volume['id'])
name = self._volume_name(volume)
params = self._parse_volume_params(volume)
self._delete_volume(name, params)
@utils.synchronized('MacroSAN-Attach', external=True)
@utils.synchronized('MacroSAN-Attach-Detach', external=True)
def _attach_volume(self, context, volume, properties, remote=False):
return super(MacroSANBaseDriver, self)._attach_volume(context,
volume,
properties,
remote)
@utils.synchronized('MacroSAN-Attach', external=True)
@utils.synchronized('MacroSAN-Attach-Detach', external=True)
def _detach_volume(self, context, attach_info, volume,
properties, force=False, remote=False,
ignore_errors=True):
@ -568,14 +525,10 @@ class MacroSANBaseDriver(driver.VolumeDriver):
@synchronized(lock_name)
@record_request_id
@_timing
@utils.trace
def create_snapshot(self, snapshot):
"""Create a snapshot."""
volume = snapshot['volume']
LOG.debug(('========== create snapshot, snapshot id: %(snapshot_id)s,'
' volume id: %(volume_id)s, size: %(size)s.'),
{'snapshot_id': snapshot['id'], 'volume_id': volume['id'],
'size': volume['size']})
snapshot_name = self._snapshot_name(snapshot['name'])
volume_name = self._volume_name(volume)
@ -601,7 +554,7 @@ class MacroSANBaseDriver(driver.VolumeDriver):
@synchronized(lock_name)
@record_request_id
@_timing
@utils.trace
def delete_snapshot(self, snapshot):
"""Delete a snapshot."""
volume = snapshot['volume']
@ -612,10 +565,7 @@ class MacroSANBaseDriver(driver.VolumeDriver):
m = re.findall(r'pointid: (\d+)', provider)
if m is None:
return
LOG.debug(('========== delete snapshot, snapshot id: %(snapshot_id)s,'
' pointid: %(point_id)s, volume id: %(volume_id)s.'),
{'snapshot_id': snapshot['id'], 'point_id': m[0],
'volume_id': volume['id']})
snapshot_name = self._snapshot_name(snapshot['id'])
volume_name = self._volume_name(volume)
self._delete_snapshot(snapshot_name, volume_name, m[0])
@ -626,35 +576,6 @@ class MacroSANBaseDriver(driver.VolumeDriver):
def _terminate_connection(self, name, host, wwns):
raise NotImplementedError
def _connect(self, name):
host = socket.gethostname()
conn = self._initialize_connection(name, host,
self._self_node_wwns)
device_scan_attempts = self.configuration.num_volume_device_scan_tries
protocol = conn['driver_volume_type']
connector = utils.brick_get_connector(
protocol,
use_multipath=self.use_multipath,
device_scan_attempts=device_scan_attempts,
conn=conn)
device = None
try:
device = connector.connect_volume(conn['data'])
except Exception:
with excutils.save_and_reraise_exception():
self._terminate_connection(name, host, self._self_node_wwns)
return {'conn': conn, 'device': device, 'connector': connector}
def _disconnect(self, conn, name):
connector = conn['connector']
connector.disconnect_volume(conn['conn']['data'],
conn['device'])
self._terminate_connection(name, socket.gethostname(),
self._self_node_wwns)
def _create_volume_from_snapshot(self, vol_name, vol_size,
vol_params, snp_name, pointid,
snp_vol_name, snp_vol_size):
@ -685,10 +606,9 @@ class MacroSANBaseDriver(driver.VolumeDriver):
@synchronized(lock_name)
@record_request_id
@_timing
@utils.trace
def create_volume_from_snapshot(self, volume, snapshot):
"""Create a volume from a snapshot."""
LOG.debug('========== create volume from snapshot.')
snapshot_volume = snapshot['volume']
provider = snapshot['provider_location']
m = re.findall(r'pointid: (\d+)', provider)
@ -727,10 +647,9 @@ class MacroSANBaseDriver(driver.VolumeDriver):
self._delete_snapshot(snp_name, src_vol_name, pointid)
@record_request_id
@_timing
@utils.trace
def create_cloned_volume(self, volume, src_vref):
"""Create a clone of the specified volume."""
LOG.debug('========== create cloned volume.')
vol_name = volume['id']
src_vol_name = self._volume_name(src_vref)
snapshotid =\
@ -768,13 +687,9 @@ class MacroSANBaseDriver(driver.VolumeDriver):
@synchronized(lock_name)
@record_request_id
@_timing
@utils.trace
def extend_volume(self, volume, new_size):
"""Extend a volume."""
LOG.debug(('========== extend volume, id: %(volume_id)s,'
'size: %(size)s.'),
{'volume_id': volume['id'], 'size': new_size})
name = self._volume_name(volume)
moresize = self._size_str_to_int(new_size - int(volume['size']))
params = self._parse_volume_params(volume)
@ -839,14 +754,12 @@ class MacroSANBaseDriver(driver.VolumeDriver):
self._stats = data
@record_request_id
@utils.trace
def update_migrated_volume(self, ctxt, volume, new_volume,
original_volume_status=None):
"""Return model update for migrated volume."""
original_name = self._volume_name(volume)
cur_name = self._volume_name(new_volume)
LOG.debug(('========== update migrated volume,'
'volume: %(original_name)s, new_volume: %(cur_name)s'),
{'original_name': original_name, 'cur_name': cur_name})
if self.client.lun_exists(original_name):
self.client.backup_lun_name_to_rename_file(cur_name, original_name)
@ -866,7 +779,7 @@ class MacroSANBaseDriver(driver.VolumeDriver):
@synchronized(lock_name)
@record_request_id
@_timing
@utils.trace
def initialize_connection_snapshot(self, snapshot, connector, **kwargs):
volume = snapshot['volume']
provider = snapshot['provider_location']
@ -905,13 +818,13 @@ class MacroSANBaseDriver(driver.VolumeDriver):
@synchronized(lock_name)
@record_request_id
@_timing
@utils.trace
def manage_existing(self, volume, external_ref):
vol_params = self._parse_volume_params(volume)
self._check_volume_params(vol_params)
if vol_params['qos-strategy']:
raise exception.VolumeBackendAPIException(
data=_('Not support to import qos-strategy'))
data=_('Import qos-strategy not supported'))
pool = volume_utils.extract_host(volume.host, 'pool')
name, info, params = self._get_existing_lun_info(external_ref)
@ -995,7 +908,7 @@ class MacroSANBaseDriver(driver.VolumeDriver):
@synchronized(lock_name)
@record_request_id
@_timing
@utils.trace
def manage_existing_snapshot(self, snapshot, existing_ref):
volume = snapshot['volume']
src_name = self._get_existing_snapname(existing_ref).lstrip('_')
@ -1042,7 +955,7 @@ class MacroSANBaseDriver(driver.VolumeDriver):
@synchronized(lock_name)
@record_request_id
@_timing
@utils.trace
def migrate_volume(self, ctxt, volume, host):
if not self.migration_valid(volume, host):
return False, None
@ -1054,8 +967,11 @@ class MacroSANBaseDriver(driver.VolumeDriver):
owner = self.client.get_lun_sp(src_name)
pool = host['capabilities'].get('pool_name', self.pool)
LOG.info('host: %(host)s, backend: %(volume_backend_name)s',
{'host': host,
LOG.info('Migrating volume: %(volume), '
'host: %(host)s, '
'backend: %(volume_backend_name)s',
{'volume': src_name,
'host': host,
'volume_backend_name': self.volume_backend_name})
self._create_volume(name, size, params, owner, pool)
@ -1106,8 +1022,10 @@ class MacroSANISCSIDriver(MacroSANBaseDriver, driver.ISCSIDriver):
.. code-block:: none
1.0.0 - Initial driver
1.0.1 - Adjust some log level and text prompts; Remove some useless
functions; Add Cinder trace decorator. #1837920
"""
VERSION = "1.0.0"
VERSION = "1.0.1"
def __init__(self, *args, **kwargs):
"""Initialize the driver."""
@ -1198,11 +1116,6 @@ class MacroSANISCSIDriver(MacroSANBaseDriver, driver.ISCSIDriver):
return id_list.pop()
@property
def _self_node_wwns(self):
connector = cn.ISCSIConnector(utils.get_root_helper())
return [connector.get_initiator()]
def _initialize_connection(self, name, vol_params, host, wwns):
client_name = self._get_client_name(host)
wwn = wwns[0]
@ -1242,11 +1155,9 @@ class MacroSANISCSIDriver(MacroSANBaseDriver, driver.ISCSIDriver):
@synchronized(lock_name)
@record_request_id
@_timing
@utils.trace
def initialize_connection(self, volume, connector):
"""Allow connection to connector and return connection info."""
LOG.debug('========== initialize_connection connector: %(connector)s',
{'connector': connector})
name = self._volume_name(volume)
params = self._parse_volume_params(volume)
@ -1273,21 +1184,26 @@ class MacroSANISCSIDriver(MacroSANBaseDriver, driver.ISCSIDriver):
if volume_params['sdas']:
self._unmap_itl(self.sdas_client, client_name, wwns, ports, name)
data = dict()
data['ports'] = ports
data['client'] = client_name
return {'driver_volume_type': 'iSCSI', 'data': data}
@synchronized(lock_name)
@record_request_id
@_timing
@utils.trace
def terminate_connection(self, volume, connector, **kwargs):
"""Disallow connection from connector."""
LOG.debug('========== terminate_connection %(connector)s',
{'connector': connector})
name = self._volume_name(volume)
conn = None
if not connector:
self.force_terminate_connection(name, True)
else:
params = self._parse_volume_params(volume)
self._terminate_connection(name, params, connector['host'],
[connector['initiator']])
conn = self._terminate_connection(name, params, connector['host'],
[connector['initiator']])
return conn
def _initialize_connection_snapshot(self, snp_name, connector):
return self._initialize_connection(snp_name, None, connector['host'],
@ -1307,8 +1223,10 @@ class MacroSANFCDriver(MacroSANBaseDriver, driver.FibreChannelDriver):
.. code-block:: none
1.0.0 - Initial driver
1.0.1 - Adjust some log level and text prompts; Remove some useless
functions; Add Cinder trace decorator. #1837920
"""
VERSION = "1.0.0"
VERSION = "1.0.1"
def __init__(self, *args, **kwargs):
"""Initialize the driver."""
@ -1333,11 +1251,6 @@ class MacroSANFCDriver(MacroSANBaseDriver, driver.FibreChannelDriver):
if port['port_name'] == '':
self.sdas_client.create_target(port['port'])
@property
def _self_node_wwns(self):
fc = linuxfc.LinuxFibreChannel(utils.get_root_helper())
return [self._format_wwn_with_colon(wwn) for wwn in fc.get_fc_wwpns()]
def _strip_wwn_colon(self, wwn_str):
return wwn_str.replace(':', '')
@ -1479,8 +1392,8 @@ class MacroSANFCDriver(MacroSANBaseDriver, driver.FibreChannelDriver):
has_port_not_mapped, initr_port_map = (
self._map_initr_tgt(self.client, client_name, wwns))
LOG.info('====================initr_port_map %(initr_port_map)s',
{'initr_port_map': initr_port_map})
LOG.debug('initr_port_map: %(initr_port_map)s',
{'initr_port_map': initr_port_map})
if vol_params and vol_params['sdas']:
sdas_has_port_not_mapped, sdas_initr_port_map = (
@ -1488,9 +1401,8 @@ class MacroSANFCDriver(MacroSANBaseDriver, driver.FibreChannelDriver):
lun_id = self._get_unused_lun_id(self.client, initr_port_map,
self.sdas_client,
sdas_initr_port_map)
LOG.info('%(fr)sdas_initr_port_map %(sdas_initr_port_map)s',
{'fr': '=' * 10,
'sdas_initr_port_map': sdas_initr_port_map})
LOG.debug('sdas_initr_port_map: %(sdas_initr_port_map)s',
{'sdas_initr_port_map': sdas_initr_port_map})
self._map_itl(self.sdas_client, sdas_initr_port_map, name, lun_id)
lun_id = self._map_itl(self.client, initr_port_map, name, lun_id)
@ -1528,11 +1440,9 @@ class MacroSANFCDriver(MacroSANBaseDriver, driver.FibreChannelDriver):
@synchronized(lock_name)
@record_request_id
@_timing
@utils.trace
def initialize_connection(self, volume, connector):
"""Allow connection to connector and return connection info."""
LOG.debug('========== initialize_connection connector: %(connector)s',
{'connector': connector})
name = self._volume_name(volume)
params = self._parse_volume_params(volume)
@ -1593,11 +1503,9 @@ class MacroSANFCDriver(MacroSANBaseDriver, driver.FibreChannelDriver):
@synchronized(lock_name)
@record_request_id
@_timing
@utils.trace
def terminate_connection(self, volume, connector, **kwargs):
"""Disallow connection from connector."""
LOG.debug('========== terminate_connection %(connector)s',
{'connector': connector})
name = self._volume_name(volume)
conn = None

View File

@ -3,8 +3,7 @@ MacroSAN Fibre Channel and iSCSI drivers
==========================================
The ``MacroSANFCDriver`` and ``MacroSANISCSIDriver`` Cinder drivers allow the
MacroSAN Storage arrays to be used for Block Storage in
OpenStack deployments.
MacroSAN Storage arrays to be used for Block Storage in OpenStack deployments.
System requirements
~~~~~~~~~~~~~~~~~~~
@ -13,7 +12,10 @@ To use the MacroSAN drivers, the following are required:
- MacroSAN Storage arrays with:
- iSCSI or FC host interfaces
- Enable RESTful service on the MacroSAN Storage Appliance.
- Enable RESTful service on the MacroSAN Storage Appliance. (The service is
automatically turned on in the device. You can check if
`python /odsp/scripts/devop/devop.py` is available via `ps -aux|grep python`.
)
- Network connectivity between the OpenStack host and the array management
interfaces
@ -28,22 +30,13 @@ the ``/etc/cinder/cinder.conf`` file:
use_multipath_for_image_xfer = True
Add and change the following configuration keys of
the ``/etc/multipath.conf`` file:
When creating a instance from image, install the ``multipath`` tool and add the
following configuration keys in the ``[libvirt]`` configuration group of
the ``/etc/nova/nova.conf`` file:
.. code-block:: ini
blacklist {
devnode "^sda$"
devnode "^(ram|raw|loop|fd|md|dm-|sr|scd|st)[0-9]*"
devnode "^hd[a-z]"
devnode "^nbd*"
}
Need to set user_friendly_names to no in the multipath.conf file.
In addition, you need to delete the getuid_callout parameter in
the centos7 system.
iscsi_use_multipath = True
Supported operations
~~~~~~~~~~~~~~~~~~~~
@ -55,13 +48,13 @@ Supported operations
- Copy a volume to an image.
- Clone a volume.
- Extend a volume.
- Volume Migration (Host assisted).
- Volume Migration (Host Assisted).
- Volume Migration (Storage Assisted).
- Retype a volume.
- Manage and unmanage a volume.
- Manage and unmanage a snapshot.
- Volume Replication
- Thin Provisioning
- Volume Replication.
- Thin Provisioning.
Configuring the array
~~~~~~~~~~~~~~~~~~~~~
@ -109,7 +102,7 @@ Configuring the array
# Name to give this storage back-end.
volume_backend_name = macrosan
#Chose attach/detach volumes in cinder using multipath for volume to image and image to volume transfers.
#Choose attach/detach volumes in cinder using multipath for volume to image and image to volume transfers.
use_multipath_for_image_xfer = True
# IP address of the Storage if attaching directly.
@ -121,7 +114,7 @@ Configuring the array
# Storage user password.
san_password = openstack
#Chose using thin-lun or thick lun.When set san_thin_provision to True,you must set
#Choose using thin-lun or thick lun. When set san_thin_provision to True,you must set
#macrosan_thin_lun_extent_size, macrosan_thin_lun_low_watermark, macrosan_thin_lun_high_watermark.
san_thin_provision = False
@ -130,7 +123,7 @@ Configuring the array
#The default ports used for initializing connection.
#Separate the controller by semicolons (``;``)
#Separate the ports by semicolons (``,``)
#Separate the ports by comma (``,``)
macrosan_client_default = eth-1:0:0, eth-1:0:1; eth-2:0:0, eth-2:0:1
#The switch to force detach volume when deleting
@ -158,7 +151,7 @@ Configuring the array
macrosan_sdas_username = openstack
macrosan_sdas_password = openstack
#The setting of Replication Storage.When you set ip, you must set
#The setting of Replication Storage. When you set ip, you must set
#the macrosan_replication_destination_ports parameter.
macrosan_replication_ipaddrs = 172.17.251.142, 172.17.251.143
macrosan_replication_username = openstack
@ -169,7 +162,7 @@ Configuring the array
#Separate the ports by semicolons (``/``)
macrosan_replication_destination_ports = eth-1:0:0/eth-1:0:1, eth-2:0:0/eth-2:0:1
#Macrosan iscsi_clients list.You can configure multiple clients.Separate the ports by semicolons (``/``)
#Macrosan iscsi_clients list. You can configure multiple clients. Separate the ports by semicolons (``/``)
macrosan_client = (devstack; controller1name; eth-1:0:0/eth-1:0:1; eth-2:0:0/eth-2:0:1), (dev; controller2name; eth-1:0:0/eth-1:0:1; eth-2:0:0/eth-2:0:1)
[cinder-iscsi-b]
@ -342,7 +335,7 @@ of the MacroSAN volume driver.
- iSCSI
* - macrosan_client_default
- ``None``
- This is the default connection information for iscsi.This default configuration is used when no host related information is obtained.
- This is the default connection information for iscsi. This default configuration is used when no host related information is obtained.
- iSCSI
* - zoning_mode
- ``True``
@ -379,7 +372,7 @@ of the MacroSAN volume driver.
- All
* - macrosan_replication_ipaddrs
- ``-``
- The ip of replication Storage.When you set ip, you must set
- The ip of replication Storage. When you set ip, you must set
the macrosan_replication_destination_ports parameter.
- All
* - macrosan_replication_username
@ -408,7 +401,7 @@ of the MacroSAN volume driver.
- All
* - macrosan_client
- ``True``
- Macrosan iscsi_clients list.You can configure multiple clients.
- Macrosan iscsi_clients list. You can configure multiple clients.
You can configure it in this format:
(hostname; client_name; sp1_iscsi_port; sp2_iscsi_port),
E.g: