Unity: add multiattach support
Add the ability to attach a volume to multiple servers simultaneously. Implements: blueprint unity-multiattach-support Change-Id: Ib7222bb3cd13505f9a8789f8cc85685e4ae9cce4
This commit is contained in:
parent
b52d26a361
commit
dffff08a20
@ -482,7 +482,8 @@ class CommonAdapterTest(test.TestCase):
|
||||
|
||||
def test_terminate_connection_volume(self):
|
||||
def f():
|
||||
volume = MockOSResource(provider_location='id^lun_43', id='id_43')
|
||||
volume = MockOSResource(provider_location='id^lun_43', id='id_43',
|
||||
volume_attachment=None)
|
||||
connector = {'host': 'host1'}
|
||||
self.adapter.terminate_connection(volume, connector)
|
||||
|
||||
@ -490,7 +491,8 @@ class CommonAdapterTest(test.TestCase):
|
||||
|
||||
def test_terminate_connection_force_detach(self):
|
||||
def f():
|
||||
volume = MockOSResource(provider_location='id^lun_44', id='id_44')
|
||||
volume = MockOSResource(provider_location='id^lun_44', id='id_44',
|
||||
volume_attachment=None)
|
||||
self.adapter.terminate_connection(volume, None)
|
||||
|
||||
self.assertRaises(ex.DetachAllIsCalled, f)
|
||||
@ -498,7 +500,8 @@ class CommonAdapterTest(test.TestCase):
|
||||
def test_terminate_connection_snapshot(self):
|
||||
def f():
|
||||
connector = {'host': 'host1'}
|
||||
snap = MockOSResource(name='snap_0', id='snap_0')
|
||||
snap = MockOSResource(name='snap_0', id='snap_0',
|
||||
volume_attachment=None)
|
||||
self.adapter.terminate_connection_snapshot(snap, connector)
|
||||
|
||||
self.assertRaises(ex.DetachIsCalled, f)
|
||||
@ -508,11 +511,27 @@ class CommonAdapterTest(test.TestCase):
|
||||
|
||||
def f():
|
||||
connector = {'host': 'empty-host'}
|
||||
vol = MockOSResource(provider_location='id^lun_45', id='id_45')
|
||||
vol = MockOSResource(provider_location='id^lun_45', id='id_45',
|
||||
volume_attachment=None)
|
||||
self.adapter.terminate_connection(vol, connector)
|
||||
|
||||
self.assertRaises(ex.HostDeleteIsCalled, f)
|
||||
|
||||
def test_terminate_connection_multiattached_volume(self):
|
||||
def f():
|
||||
connector = {'host': 'host1'}
|
||||
attachments = [MockOSResource(id='id-1',
|
||||
attach_status='attached',
|
||||
attached_host='host1'),
|
||||
MockOSResource(id='id-2',
|
||||
attach_status='attached',
|
||||
attached_host='host1')]
|
||||
vol = MockOSResource(provider_location='id^lun_45', id='id_45',
|
||||
volume_attachment=attachments)
|
||||
self.adapter.terminate_connection(vol, connector)
|
||||
|
||||
self.assertIsNone(f())
|
||||
|
||||
def test_manage_existing_by_name(self):
|
||||
ref = {'source-id': 12}
|
||||
volume = MockOSResource(name='lun1')
|
||||
@ -844,7 +863,8 @@ class FCAdapterTest(test.TestCase):
|
||||
|
||||
def test_terminate_connection_auto_zone_enabled(self):
|
||||
connector = {'host': 'host1', 'wwpns': 'abcdefg'}
|
||||
volume = MockOSResource(provider_location='id^lun_41', id='id_41')
|
||||
volume = MockOSResource(provider_location='id^lun_41', id='id_41',
|
||||
volume_attachment=None)
|
||||
ret = self.adapter.terminate_connection(volume, connector)
|
||||
self.assertEqual('fibre_channel', ret['driver_volume_type'])
|
||||
data = ret['data']
|
||||
@ -857,7 +877,8 @@ class FCAdapterTest(test.TestCase):
|
||||
|
||||
def test_terminate_connection_auto_zone_enabled_none_host_luns(self):
|
||||
connector = {'host': 'host-no-host_luns', 'wwpns': 'abcdefg'}
|
||||
volume = MockOSResource(provider_location='id^lun_41', id='id_41')
|
||||
volume = MockOSResource(provider_location='id^lun_41', id='id_41',
|
||||
volume_attachment=None)
|
||||
ret = self.adapter.terminate_connection(volume, connector)
|
||||
self.assertEqual('fibre_channel', ret['driver_volume_type'])
|
||||
data = ret['data']
|
||||
@ -871,7 +892,8 @@ class FCAdapterTest(test.TestCase):
|
||||
def test_terminate_connection_remove_empty_host_return_data(self):
|
||||
self.adapter.remove_empty_host = True
|
||||
connector = {'host': 'empty-host-return-data', 'wwpns': 'abcdefg'}
|
||||
volume = MockOSResource(provider_location='id^lun_41', id='id_41')
|
||||
volume = MockOSResource(provider_location='id^lun_41', id='id_41',
|
||||
volume_attachment=None)
|
||||
ret = self.adapter.terminate_connection(volume, connector)
|
||||
self.assertEqual('fibre_channel', ret['driver_volume_type'])
|
||||
data = ret['data']
|
||||
|
@ -348,12 +348,14 @@ class CommonAdapter(object):
|
||||
# No target info for iSCSI driver
|
||||
return []
|
||||
|
||||
def _detach_and_delete_host(self, host_name, lun_or_snap):
|
||||
def _detach_and_delete_host(self, host_name, lun_or_snap,
|
||||
is_multiattach_to_host=False):
|
||||
@utils.lock_if(self.to_lock_host, '{lock_name}')
|
||||
def _lock_helper(lock_name):
|
||||
# Only get the host from cache here
|
||||
host = self.client.create_host_wo_lock(host_name)
|
||||
self.client.detach(host, lun_or_snap)
|
||||
if not is_multiattach_to_host:
|
||||
self.client.detach(host, lun_or_snap)
|
||||
host.update() # need update to get the latest `host_luns`
|
||||
targets = self.filter_targets_by_host(host)
|
||||
if self.remove_empty_host and not host.host_luns:
|
||||
@ -368,14 +370,16 @@ class CommonAdapter(object):
|
||||
# No return data from terminate_connection for iSCSI driver
|
||||
return {}
|
||||
|
||||
def _terminate_connection(self, lun_or_snap, connector):
|
||||
def _terminate_connection(self, lun_or_snap, connector,
|
||||
is_multiattach_to_host=False):
|
||||
is_force_detach = connector is None
|
||||
data = {}
|
||||
if is_force_detach:
|
||||
self.client.detach_all(lun_or_snap)
|
||||
else:
|
||||
targets = self._detach_and_delete_host(connector['host'],
|
||||
lun_or_snap)
|
||||
targets = self._detach_and_delete_host(
|
||||
connector['host'], lun_or_snap,
|
||||
is_multiattach_to_host=is_multiattach_to_host)
|
||||
data = self.get_terminate_connection_info(connector, targets)
|
||||
return {
|
||||
'driver_volume_type': self.driver_volume_type,
|
||||
@ -385,7 +389,14 @@ class CommonAdapter(object):
|
||||
@cinder_utils.trace
|
||||
def terminate_connection(self, volume, connector):
|
||||
lun = self.client.get_lun(lun_id=self.get_lun_id(volume))
|
||||
return self._terminate_connection(lun, connector)
|
||||
# None `connector` indicates force detach, then detach all even the
|
||||
# volume is multi-attached.
|
||||
multiattach_flag = (connector is not None and
|
||||
utils.is_multiattach_to_host(
|
||||
volume.volume_attachment,
|
||||
connector['host']))
|
||||
return self._terminate_connection(
|
||||
lun, connector, is_multiattach_to_host=multiattach_flag)
|
||||
|
||||
def get_connector_uids(self, connector):
|
||||
return None
|
||||
@ -443,7 +454,9 @@ class CommonAdapter(object):
|
||||
'thin_provisioning_support': True,
|
||||
'thick_provisioning_support': True,
|
||||
'max_over_subscription_ratio': (
|
||||
self.max_over_subscription_ratio)}
|
||||
self.max_over_subscription_ratio),
|
||||
'multiattach': True
|
||||
}
|
||||
|
||||
def get_lun_id(self, volume):
|
||||
"""Retrieves id of the volume's backing LUN.
|
||||
|
@ -26,6 +26,7 @@ import six
|
||||
from cinder import coordination
|
||||
from cinder import exception
|
||||
from cinder.i18n import _
|
||||
from cinder.objects import fields
|
||||
from cinder.volume import utils as vol_utils
|
||||
from cinder.volume import volume_types
|
||||
from cinder.zonemanager import utils as zm_utils
|
||||
@ -304,3 +305,18 @@ def lock_if(condition, lock_name):
|
||||
return coordination.synchronized(lock_name)
|
||||
else:
|
||||
return functools.partial
|
||||
|
||||
|
||||
def is_multiattach_to_host(volume_attachment, host_name):
|
||||
# When multiattach is enabled, a volume could be attached to two or more
|
||||
# instances which are hosted on one nova host.
|
||||
# Because unity cannot recognize the volume is attached to two or more
|
||||
# instances, we should keep the volume attached to the nova host until
|
||||
# the volume is detached from the last instance.
|
||||
if not volume_attachment:
|
||||
return False
|
||||
|
||||
attachment = [a for a in volume_attachment
|
||||
if a.attach_status == fields.VolumeAttachStatus.ATTACHED and
|
||||
a.attached_host == host_name]
|
||||
return len(attachment) > 1
|
||||
|
@ -34,6 +34,7 @@ Supported operations
|
||||
- Efficient non-disruptive volume backup.
|
||||
- Revert a volume to a snapshot.
|
||||
- Create thick volumes.
|
||||
- Attach a volume to multiple servers simultaneously (multiattach).
|
||||
|
||||
Driver configuration
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
@ -0,0 +1,7 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
Dell EMC Unity: Implements `bp unity-multiattach-support
|
||||
<https://blueprints.launchpad.net/cinder/+spec/unity-multiattach-support>`__
|
||||
to support attaching a volume to multiple servers simultaneously.
|
||||
|
Loading…
Reference in New Issue
Block a user