Zadara VPSA: Fix driver force detach operation

Cinder API supports os-force_detach, which once invoked can call the
driver terminate_connection passing a 'None' connector. When receiving
this, the driver should handle this call by terminating every
connection between the specified volume and any host it is mapped to.
This patch addresses this behavior for the Zadara VPSA driver.

Volume detach 'force' flag is an internal flag due to an API change on
our side. It will not have any impact on the standard detach
procedure.

Change-Id: I09aeac3b777427f9694697f579c7fb9716e311e5
This commit is contained in:
Bhaa Shakur 2019-07-02 14:20:24 +03:00
parent 9dd5232ea5
commit d0a196724f
2 changed files with 49 additions and 16 deletions

View File

@ -1,4 +1,4 @@
# Copyright (c) 2016 Zadara Storage, Inc.
# Copyright (c) 2019 Zadara Storage, Inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
@ -632,6 +632,24 @@ class ZadaraVPSADriverTestCase(test.TestCase):
self.assertRaises(exception.BadHTTPResponseStatus,
self.driver.create_volume, volume)
@mock.patch.object(requests, 'request', FakeRequests)
def test_termiante_connection_force_detach(self):
"""Test terminate connection for os-force_detach """
volume = {'name': 'test_volume_01', 'size': 1, 'id': 101}
connector = dict(initiator='test_iqn.1')
self.driver.create_volume(volume)
self.driver.initialize_connection(volume, connector)
# connector is None - force detach - detach all mappings
self.driver.terminate_connection(volume, None)
self.assertRaises(zadara.exception.FailedCmdWithDump,
self.driver.terminate_connection,
volume, connector)
self.driver.delete_volume(volume)
@mock.patch.object(requests, 'request', FakeRequests)
def test_delete_without_detach(self):
"""Test volume deletion without detach."""

View File

@ -1,4 +1,4 @@
# Copyright (c) 2016 Zadara Storage, Inc.
# Copyright (c) 2019 Zadara Storage, Inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
@ -183,7 +183,7 @@ class ZadaraVPSAConnection(object):
'/api/volumes/%s/detach.xml'
% kwargs.get('vpsa_vol'),
{'server_name[]': kwargs.get('vpsa_srv'),
'force': 'NO'}),
'force': 'YES'}),
# Get operations
'list_volumes': ('GET',
@ -435,6 +435,16 @@ class ZadaraVPSAISCSIDriver(driver.ISCSIDriver):
chap_passwd=ctrl.findtext('vpsa-chap-secret'))
return None
def _detach_vpsa_volume(self, vpsa_vol):
"""Detach volume from all attached servers."""
list_servers = self._get_servers_attached_to_volume(vpsa_vol)
for server in list_servers:
# Detach volume from server
vpsa_srv = server.findtext('name')
self.vpsa.send_cmd('detach_volume',
vpsa_srv=vpsa_srv,
vpsa_vol=vpsa_vol)
def _get_server_name(self, initiator):
"""Return VPSA's name for server object with given IQN."""
xml_tree = self.vpsa.send_cmd('list_servers')
@ -472,18 +482,7 @@ class ZadaraVPSAISCSIDriver(driver.ISCSIDriver):
'It might be already deleted', name)
return
# Check attachment info and detach from all
xml_tree = self.vpsa.send_cmd('list_vol_attachments',
vpsa_vol=vpsa_vol)
servers = self._xml_parse_helper(xml_tree, 'servers',
('iqn', None), first=False)
if servers:
for server in servers:
vpsa_srv = server.findtext('name')
if vpsa_srv:
self.vpsa.send_cmd('detach_volume',
vpsa_srv=vpsa_srv,
vpsa_vol=vpsa_vol)
self._detach_vpsa_volume(vpsa_vol)
# Delete volume
self.vpsa.send_cmd('delete_volume', vpsa_vol=vpsa_vol)
@ -684,7 +683,16 @@ class ZadaraVPSAISCSIDriver(driver.ISCSIDriver):
def terminate_connection(self, volume, connector, **kwargs):
"""Detach volume from the initiator."""
# Get server name for IQN
if connector is None:
# Detach volume from all servers
# Get volume name
name = self.configuration.zadara_vol_name_template % volume['name']
vpsa_vol = self._get_vpsa_volume_name(name)
self._detach_vpsa_volume(vpsa_vol)
return
initiator_name = connector['initiator']
vpsa_srv = self._get_server_name(initiator_name)
if not vpsa_srv:
raise ZadaraServerNotFound(name=initiator_name)
@ -711,9 +719,16 @@ class ZadaraVPSAISCSIDriver(driver.ISCSIDriver):
return self._stats
def _get_servers_attached_to_volume(self, vpsa_vol):
"""Return all servers attached to volume."""
xml_tree = self.vpsa.send_cmd('list_vol_attachments',
vpsa_vol=vpsa_vol)
list_servers = self._xml_parse_helper(xml_tree, 'servers',
('iqn', None), first=False)
return list_servers or []
def _update_volume_stats(self):
"""Retrieve stats info from volume group."""
LOG.debug("Updating volume stats")
data = {}
backend_name = self.configuration.safe_get('volume_backend_name')