Fix 'no actual-pathname' NetApp API error

Cinder volume logs sometimes show this error

      NetApp API failed. Reason - 13114:
      No actual-pathname for 10.1.0.9:/vol/whatever

with Kilo code and 7-mode DOT system.

The issue is due to our Cinder driver passing the
entire share to the relevant API instead of just
the export path portion of the share.  This only
happens when the NFS image cache is full, and cached
files need to be removed.

Change-Id: I0c40840dac975dd7fd2c62f1f9c0cd3f8c5c1252
Closes-Bug: #1468884
(cherry picked from commit 6a65422c69)
This commit is contained in:
Tom Barron 2015-06-17 14:54:01 -04:00
parent 6c055943e7
commit 4f86ae7f2f
5 changed files with 110 additions and 9 deletions

View File

@ -12,7 +12,6 @@
# License for the specific language governing permissions and limitations
# under the License.
VOLUME = 'f10d1a84-9b7b-427e-8fec-63c48b509a56'
LUN = 'ee6b4cc7-477b-4016-aa0c-7127b4e3af86'
SIZE = '1024'
@ -115,12 +114,16 @@ ISCSI_TARGET_DETAILS_LIST = [
HOSTNAME = 'fake.host.com'
IPV4_ADDRESS = '192.168.14.2'
IPV6_ADDRESS = 'fe80::6e40:8ff:fe8a:130'
SHARE_IP = '192.168.99.24'
EXPORT_PATH = '/fake/export/path'
NFS_SHARE = HOSTNAME + ':' + EXPORT_PATH
NFS_SHARE_IPV4 = IPV4_ADDRESS + ':' + EXPORT_PATH
NFS_SHARE_IPV6 = IPV6_ADDRESS + ':' + EXPORT_PATH
FLEXVOL = 'openstack-flexvol'
RESERVED_PERCENTAGE = 7
TOTAL_BYTES = 4797892092432
AVAILABLE_BYTES = 13479932478
CAPACITY_VALUES = (TOTAL_BYTES, AVAILABLE_BYTES)
FILE_LIST = ['file1', 'file2', 'file3']

View File

@ -12,7 +12,7 @@
# License for the specific language governing permissions and limitations
# under the License.
"""
Mock unit tests for the NetApp 7mode nfs storage driver
Unit tests for the NetApp 7mode NFS storage driver
"""
import mock
@ -40,6 +40,7 @@ class NetApp7modeNfsDriverTestCase(test.TestCase):
self.driver = nfs_7mode.NetApp7modeNfsDriver(**kwargs)
self.driver._mounted_shares = [fake.NFS_SHARE]
self.driver.ssc_vols = True
self.driver.zapi_client = mock.Mock()
def get_config_7mode(self):
config = na_fakes.create_configuration_cmode()
@ -73,3 +74,34 @@ class NetApp7modeNfsDriverTestCase(test.TestCase):
result[0]['reserved_percentage'])
self.assertEqual(total_capacity_gb, result[0]['total_capacity_gb'])
self.assertEqual(free_capacity_gb, result[0]['free_capacity_gb'])
def test_shortlist_del_eligible_files(self):
mock_get_path_for_export = self.mock_object(
self.driver.zapi_client, 'get_actual_path_for_export')
mock_get_path_for_export.return_value = fake.FLEXVOL
mock_get_file_usage = self.mock_object(
self.driver.zapi_client, 'get_file_usage')
mock_get_file_usage.return_value = fake.CAPACITY_VALUES[0]
expected = [(old_file, fake.CAPACITY_VALUES[0]) for old_file
in fake.FILE_LIST]
result = self.driver._shortlist_del_eligible_files(
fake.NFS_SHARE, fake.FILE_LIST)
self.assertEqual(expected, result)
def test_shortlist_del_eligible_files_empty_list(self):
mock_get_export_ip_path = self.mock_object(
self.driver, '_get_export_ip_path')
mock_get_export_ip_path.return_value = ('', '/export_path')
mock_get_path_for_export = self.mock_object(
self.driver.zapi_client, 'get_actual_path_for_export')
mock_get_path_for_export.return_value = fake.FLEXVOL
result = self.driver._shortlist_del_eligible_files(
fake.NFS_SHARE, [])
self.assertEqual([], result)

View File

@ -13,13 +13,14 @@
# License for the specific language governing permissions and limitations
# under the License.
"""
Mock unit tests for the NetApp nfs storage driver
Unit tests for the NetApp NFS storage driver
"""
import mock
from oslo_utils import units
from cinder.brick.remotefs import remotefs as remotefs_brick
from cinder import exception
from cinder import test
from cinder.tests.volume.drivers.netapp.dataontap import fakes as fake
from cinder import utils
@ -43,6 +44,7 @@ class NetAppNfsDriverTestCase(test.TestCase):
with mock.patch.object(remotefs_brick, 'RemoteFsClient',
return_value=mock.Mock()):
self.driver = nfs_base.NetAppNfsDriver(**kwargs)
self.driver.db = mock.Mock()
@mock.patch.object(nfs.NfsDriver, 'do_setup')
@mock.patch.object(na_utils, 'check_flags')
@ -98,3 +100,63 @@ class NetAppNfsDriverTestCase(test.TestCase):
self.assertEqual(expected, result)
get_capacity.assert_has_calls([
mock.call(fake.EXPORT_PATH)])
def test_get_export_ip_path_volume_id_provided(self):
mock_get_host_ip = self.mock_object(self.driver, '_get_host_ip')
mock_get_host_ip.return_value = fake.IPV4_ADDRESS
mock_get_export_path = self.mock_object(
self.driver, '_get_export_path')
mock_get_export_path.return_value = fake.EXPORT_PATH
expected = (fake.IPV4_ADDRESS, fake.EXPORT_PATH)
result = self.driver._get_export_ip_path(fake.VOLUME)
self.assertEqual(expected, result)
def test_get_export_ip_path_share_provided(self):
expected = (fake.HOSTNAME, fake.EXPORT_PATH)
result = self.driver._get_export_ip_path(share=fake.NFS_SHARE)
self.assertEqual(expected, result)
def test_get_export_ip_path_volume_id_and_share_provided(self):
mock_get_host_ip = self.mock_object(self.driver, '_get_host_ip')
mock_get_host_ip.return_value = fake.IPV4_ADDRESS
mock_get_export_path = self.mock_object(
self.driver, '_get_export_path')
mock_get_export_path.return_value = fake.EXPORT_PATH
expected = (fake.IPV4_ADDRESS, fake.EXPORT_PATH)
result = self.driver._get_export_ip_path(
fake.VOLUME, fake.NFS_SHARE)
self.assertEqual(expected, result)
def test_get_export_ip_path_no_args(self):
self.assertRaises(exception.InvalidInput,
self.driver._get_export_ip_path)
def test_get_host_ip(self):
mock_get_provider_location = self.mock_object(
self.driver, '_get_provider_location')
mock_get_provider_location.return_value = fake.NFS_SHARE_IPV4
expected = fake.IPV4_ADDRESS
result = self.driver._get_host_ip(fake.VOLUME)
self.assertEqual(expected, result)
def test_get_export_path(self):
mock_get_provider_location = self.mock_object(
self.driver, '_get_provider_location')
mock_get_provider_location.return_value = fake.NFS_SHARE
expected = fake.EXPORT_PATH
result = self.driver._get_export_path(fake.VOLUME)
self.assertEqual(expected, result)

View File

@ -21,6 +21,8 @@
Volume driver for NetApp NFS storage.
"""
import os
from oslo_log import log as logging
import six
@ -152,11 +154,13 @@ class NetApp7modeNfsDriver(nfs_base.NetAppNfsDriver):
def _shortlist_del_eligible_files(self, share, old_files):
"""Prepares list of eligible files to be deleted from cache."""
file_list = []
exp_volume = self.zapi_client.get_actual_path_for_export(share)
for file in old_files:
path = '/vol/%s/%s' % (exp_volume, file)
(_, export_path) = self._get_export_ip_path(share=share)
exported_volume = self.zapi_client.get_actual_path_for_export(
export_path)
for old_file in old_files:
path = os.path.join(exported_volume, old_file)
u_bytes = self.zapi_client.get_file_usage(path)
file_list.append((file, u_bytes))
file_list.append((old_file, u_bytes))
LOG.debug('Shortlisted files eligible for deletion: %s', file_list)
return file_list

View File

@ -149,11 +149,11 @@ class NetAppNfsDriver(nfs.NfsDriver):
def _get_host_ip(self, volume_id):
"""Returns IP address for the given volume."""
return self._get_provider_location(volume_id).split(':')[0]
return self._get_provider_location(volume_id).rsplit(':')[0]
def _get_export_path(self, volume_id):
"""Returns NFS export path for the given volume."""
return self._get_provider_location(volume_id).split(':')[1]
return self._get_provider_location(volume_id).rsplit(':')[1]
def _volume_not_present(self, nfs_mount, volume_name):
"""Check if volume exists."""