[NetApp] Fix non-disruptive migration cifs shares
Migrate non-disruptive cifs share from different pools change the export location. When the non-disruptive migration complete process is started a new share and export location is created. As result, Manila finds a conflict between the old export location and the new one. This patch add a condition to skip export location creation when a CIFS migration is in progress, also change the way that the export location is created. Instead of create the export path with share name, the new one is taken from the backend. The fix is only for ZAPI API calls. Change-Id: I1bb888a0b644f0b071816d275d464c4dd27125a7 Co-authored-by: Lucas Oliveira <lucasmoliveira059@gmail.com> Closes-bug: #1920937
This commit is contained in:
parent
f03e84ae62
commit
9ddddeeea3
@ -1698,7 +1698,7 @@ def share_instance_get(context, share_instance_id, session=None,
|
||||
).filter_by(
|
||||
id=share_instance_id,
|
||||
).options(
|
||||
joinedload('export_locations'),
|
||||
joinedload('export_locations').joinedload('_el_metadata_bare'),
|
||||
joinedload('share_type'),
|
||||
).first()
|
||||
if result is None:
|
||||
|
@ -3434,9 +3434,8 @@ class NetAppCmodeClient(client_base.NetAppBaseClient):
|
||||
self.send_request('cg-commit', api_args)
|
||||
|
||||
@na_utils.trace
|
||||
def create_cifs_share(self, share_name):
|
||||
share_path = '/%s' % share_name
|
||||
api_args = {'path': share_path, 'share-name': share_name}
|
||||
def create_cifs_share(self, share_name, path):
|
||||
api_args = {'path': path, 'share-name': share_name}
|
||||
self.send_request('cifs-share-create', api_args)
|
||||
|
||||
@na_utils.trace
|
||||
|
@ -3571,12 +3571,31 @@ class NetAppCmodeFileStorageLibrary(object):
|
||||
}
|
||||
LOG.info(msg, msg_args)
|
||||
|
||||
# NOTE(gouthamr): For nondisruptive migration, current export
|
||||
# NOTE(gouthamr): For NFS nondisruptive migration, current export
|
||||
# policy will not be cleared, the export policy will be renamed to
|
||||
# match the name of the share.
|
||||
export_locations = self._create_export(
|
||||
destination_share, share_server, vserver, vserver_client,
|
||||
clear_current_export_policy=False)
|
||||
# NOTE (caiquemello): For CIFS nondisruptive migration, current CIFS
|
||||
# share cannot be renamed, so keep the previous CIFS share.
|
||||
share_protocol = source_share['share_proto'].lower()
|
||||
if share_protocol != 'cifs':
|
||||
export_locations = self._create_export(
|
||||
destination_share, share_server, vserver, vserver_client,
|
||||
clear_current_export_policy=False)
|
||||
else:
|
||||
export_locations = []
|
||||
for item in source_share['export_locations']:
|
||||
export_locations.append(
|
||||
{
|
||||
'path': item['path'],
|
||||
'is_admin_only': item['is_admin_only'],
|
||||
'metadata': item['el_metadata']
|
||||
}
|
||||
)
|
||||
|
||||
# Sort the export locations to report preferred paths first
|
||||
export_locations = self._sort_export_locations_by_preferred_paths(
|
||||
export_locations)
|
||||
|
||||
src_snaps_dict = {s['id']: s for s in source_snapshots}
|
||||
snapshot_updates = {}
|
||||
|
||||
|
@ -46,12 +46,13 @@ class NetAppCmodeCIFSHelper(base.NetAppBaseHelper):
|
||||
"""
|
||||
|
||||
cifs_exist = self._client.cifs_share_exists(share_name)
|
||||
export_path = self._client.get_volume_junction_path(share_name)
|
||||
if ensure_share_already_exists and not cifs_exist:
|
||||
msg = _("The expected CIFS share %(share_name)s was not found.")
|
||||
msg_args = {'share_name': share_name}
|
||||
raise exception.NetAppException(msg % msg_args)
|
||||
elif not cifs_exist:
|
||||
self._client.create_cifs_share(share_name)
|
||||
self._client.create_cifs_share(share_name, export_path)
|
||||
self._client.remove_cifs_share_access(share_name, 'Everyone')
|
||||
|
||||
# Ensure 'ntfs' security style for RW volume. DP volumes cannot set it.
|
||||
@ -61,8 +62,8 @@ class NetAppCmodeCIFSHelper(base.NetAppBaseHelper):
|
||||
|
||||
# Return a callback that may be used for generating export paths
|
||||
# for this share.
|
||||
return (lambda export_address, share_name=share_name:
|
||||
r'\\%s\%s' % (export_address, share_name))
|
||||
return (lambda export_address, export_path=export_path:
|
||||
r'\\%s%s' % (export_address, export_path.replace('/', '\\')))
|
||||
|
||||
@na_utils.trace
|
||||
def delete_share(self, share, share_name):
|
||||
@ -75,6 +76,7 @@ class NetAppCmodeCIFSHelper(base.NetAppBaseHelper):
|
||||
def update_access(self, share, share_name, rules):
|
||||
"""Replaces the list of access rules known to the backend storage."""
|
||||
|
||||
_, cifs_share_name = self._get_export_location(share)
|
||||
# Ensure rules are valid
|
||||
for rule in rules:
|
||||
self._validate_access_rule(rule)
|
||||
@ -82,13 +84,13 @@ class NetAppCmodeCIFSHelper(base.NetAppBaseHelper):
|
||||
new_rules = {rule['access_to']: rule['access_level'] for rule in rules}
|
||||
|
||||
# Get rules from share
|
||||
existing_rules = self._get_access_rules(share, share_name)
|
||||
existing_rules = self._get_access_rules(share, cifs_share_name)
|
||||
|
||||
# Update rules in an order that will prevent transient disruptions
|
||||
self._handle_added_rules(share_name, existing_rules, new_rules)
|
||||
self._handle_ro_to_rw_rules(share_name, existing_rules, new_rules)
|
||||
self._handle_rw_to_ro_rules(share_name, existing_rules, new_rules)
|
||||
self._handle_deleted_rules(share_name, existing_rules, new_rules)
|
||||
self._handle_added_rules(cifs_share_name, existing_rules, new_rules)
|
||||
self._handle_ro_to_rw_rules(cifs_share_name, existing_rules, new_rules)
|
||||
self._handle_rw_to_ro_rules(cifs_share_name, existing_rules, new_rules)
|
||||
self._handle_deleted_rules(cifs_share_name, existing_rules, new_rules)
|
||||
|
||||
@na_utils.trace
|
||||
def _validate_access_rule(self, rule):
|
||||
@ -170,8 +172,10 @@ class NetAppCmodeCIFSHelper(base.NetAppBaseHelper):
|
||||
@na_utils.trace
|
||||
def get_share_name_for_share(self, share):
|
||||
"""Returns the flexvol name that hosts a share."""
|
||||
_, share_name = self._get_export_location(share)
|
||||
return share_name
|
||||
_, volume_junction_path = self._get_export_location(share)
|
||||
volume = self._client.get_volume_at_junction_path(
|
||||
f"/{volume_junction_path}")
|
||||
return volume.get('name') if volume else None
|
||||
|
||||
@na_utils.trace
|
||||
def _get_export_location(self, share):
|
||||
|
@ -5139,10 +5139,11 @@ class NetAppClientCmodeTestCase(test.TestCase):
|
||||
|
||||
self.mock_object(self.client, 'send_request')
|
||||
|
||||
self.client.create_cifs_share(fake.SHARE_NAME)
|
||||
self.client.create_cifs_share(
|
||||
fake.SHARE_NAME, fake.VOLUME_JUNCTION_PATH)
|
||||
|
||||
cifs_share_create_args = {
|
||||
'path': '/%s' % fake.SHARE_NAME,
|
||||
'path': fake.VOLUME_JUNCTION_PATH,
|
||||
'share-name': fake.SHARE_NAME
|
||||
}
|
||||
|
||||
|
@ -23,8 +23,10 @@ SHARE_ADDRESS_2 = '10.10.10.20'
|
||||
CLIENT_ADDRESS_1 = '20.20.20.10'
|
||||
CLIENT_ADDRESS_2 = '20.20.20.20'
|
||||
|
||||
CIFS_SHARE_PATH = '/%s' % SHARE_NAME
|
||||
CIFS_SHARE_PATH_PARSED = '\\%s' % SHARE_NAME
|
||||
CIFS_SHARE = {
|
||||
'export_location': r'\\%s\%s' % (SHARE_ADDRESS_1, SHARE_NAME),
|
||||
'export_location': r'\\%s%s' % (SHARE_ADDRESS_1, CIFS_SHARE_PATH_PARSED),
|
||||
'id': SHARE_ID
|
||||
}
|
||||
|
||||
|
@ -46,6 +46,8 @@ class NetAppClusteredCIFSHelperTestCase(test.TestCase):
|
||||
def test_create_share(self, replica, cifs_exist):
|
||||
|
||||
self.mock_client.cifs_share_exists.return_value = cifs_exist
|
||||
self.mock_client.get_volume_junction_path.return_value = (
|
||||
fake.CIFS_SHARE_PATH)
|
||||
|
||||
result = self.helper.create_share(
|
||||
fake.CIFS_SHARE, fake.SHARE_NAME,
|
||||
@ -54,8 +56,8 @@ class NetAppClusteredCIFSHelperTestCase(test.TestCase):
|
||||
export_addresses = [fake.SHARE_ADDRESS_1, fake.SHARE_ADDRESS_2]
|
||||
export_paths = [result(address) for address in export_addresses]
|
||||
expected_paths = [
|
||||
r'\\%s\%s' % (fake.SHARE_ADDRESS_1, fake.SHARE_NAME),
|
||||
r'\\%s\%s' % (fake.SHARE_ADDRESS_2, fake.SHARE_NAME),
|
||||
r'\\%s%s' % (fake.SHARE_ADDRESS_1, fake.CIFS_SHARE_PATH_PARSED),
|
||||
r'\\%s%s' % (fake.SHARE_ADDRESS_2, fake.CIFS_SHARE_PATH_PARSED),
|
||||
]
|
||||
self.assertEqual(expected_paths, export_paths)
|
||||
|
||||
@ -66,7 +68,7 @@ class NetAppClusteredCIFSHelperTestCase(test.TestCase):
|
||||
self.mock_client.remove_cifs_share.assert_not_called()
|
||||
else:
|
||||
self.mock_client.create_cifs_share.assert_called_once_with(
|
||||
fake.SHARE_NAME)
|
||||
fake.SHARE_NAME, fake.CIFS_SHARE_PATH)
|
||||
self.mock_client.remove_cifs_share_access.assert_called_once_with(
|
||||
fake.SHARE_NAME, 'Everyone')
|
||||
|
||||
@ -214,9 +216,24 @@ class NetAppClusteredCIFSHelperTestCase(test.TestCase):
|
||||
|
||||
def test_get_share_name_for_share(self):
|
||||
|
||||
self.mock_client.get_volume_at_junction_path.return_value = (
|
||||
fake.VOLUME)
|
||||
|
||||
share_name = self.helper.get_share_name_for_share(fake.CIFS_SHARE)
|
||||
|
||||
self.assertEqual(fake.SHARE_NAME, share_name)
|
||||
self.mock_client.get_volume_at_junction_path.assert_called_once_with(
|
||||
fake.CIFS_SHARE_PATH)
|
||||
|
||||
def test_get_share_name_for_share_not_found(self):
|
||||
|
||||
self.mock_client.get_volume_at_junction_path.return_value = None
|
||||
|
||||
share_name = self.helper.get_share_name_for_share(fake.CIFS_SHARE)
|
||||
|
||||
self.assertIsNone(share_name)
|
||||
self.mock_client.get_volume_at_junction_path.assert_called_once_with(
|
||||
fake.CIFS_SHARE_PATH)
|
||||
|
||||
@ddt.data(
|
||||
{
|
||||
|
@ -0,0 +1,8 @@
|
||||
---
|
||||
fixes:
|
||||
- |
|
||||
Fixed non-disruptive share migration of CIFS shares in the NetApp ONTAP
|
||||
driver using ZAPI API. During the CIFS share migration the creation of a
|
||||
new export path is skipped and the actual export path is taken from the
|
||||
backend. For more details, please refer to
|
||||
`launchpad bug #1920937 <https://bugs.launchpad.net/manila/+bug/1920937>`_.
|
Loading…
x
Reference in New Issue
Block a user