[NetApp] Fix svm scoped account

When the NetApp backend starts, it needs to know whether the
`revert_to_snapshot` support exist. So, it was retrieving the
licenses and checking if the `SnapRestore` is included. Using a
scoped account, the backend cannot retrieve that information,
though. So, this patch solves it by sending a fake operation
`revert_to_snapshot` that must fail. Analyzing the given error, it
sets the backend support field.

Closes-Bug: #1882590
Change-Id: Ib71a6cec939288498e48736f129fbfdacaabe9da
This commit is contained in:
Felipe Rodrigues 2020-06-05 15:28:01 +00:00 committed by Felipe Rodrigues
parent bbea70d044
commit 6f58af1ae3
4 changed files with 86 additions and 25 deletions

View File

@ -30,6 +30,7 @@ from oslo_log import log
from oslo_service import loopingcall from oslo_service import loopingcall
from oslo_utils import timeutils from oslo_utils import timeutils
from oslo_utils import units from oslo_utils import units
from oslo_utils import uuidutils
import six import six
from manila.common import constants from manila.common import constants
@ -129,6 +130,7 @@ class NetAppCmodeFileStorageLibrary(object):
self._clients = {} self._clients = {}
self._ssc_stats = {} self._ssc_stats = {}
self._have_cluster_creds = None self._have_cluster_creds = None
self._revert_to_snapshot_support = False
self._cluster_info = {} self._cluster_info = {}
self._app_version = kwargs.get('app_version', 'unknown') self._app_version = kwargs.get('app_version', 'unknown')
@ -145,6 +147,9 @@ class NetAppCmodeFileStorageLibrary(object):
if self._have_cluster_creds is True: if self._have_cluster_creds is True:
self._set_cluster_info() self._set_cluster_info()
self._licenses = self._get_licenses()
self._revert_to_snapshot_support = self._check_snaprestore_license()
# Performance monitoring library # Performance monitoring library
self._perf_library = performance.PerformanceLibrary(self._client) self._perf_library = performance.PerformanceLibrary(self._client)
@ -156,7 +161,6 @@ class NetAppCmodeFileStorageLibrary(object):
@na_utils.trace @na_utils.trace
def check_for_setup_error(self): def check_for_setup_error(self):
self._licenses = self._get_licenses()
self._start_periodic_tasks() self._start_periodic_tasks()
def _get_vserver(self, share_server=None): def _get_vserver(self, share_server=None):
@ -260,10 +264,30 @@ class NetAppCmodeFileStorageLibrary(object):
@na_utils.trace @na_utils.trace
def _check_snaprestore_license(self): def _check_snaprestore_license(self):
"""Check if snaprestore license is enabled.""" """Check if snaprestore license is enabled."""
if not self._licenses: if self._have_cluster_creds:
self._licenses = self._client.get_licenses() return 'snaprestore' in self._licenses
else:
# NOTE: (felipe_rodrigues): workaround to find out whether the
# backend has the license: since without cluster credentials it
# cannot retrieve the ontap licenses, it sends a fake ONTAP
# "snapshot-restore-volume" request which is only available when
# the license exists. By the got error, it checks whether license
# is installed or not.
try:
self._client.restore_snapshot(
"fake_%s" % uuidutils.generate_uuid(dashed=False), "")
except netapp_api.NaApiError as e:
no_license = 'is not licensed'
LOG.debug('Fake restore_snapshot request failed: %s', e)
return not (e.code == netapp_api.EAPIERROR and
no_license in e.message)
return 'snaprestore' in self._licenses # since it passed an empty snapshot, it should never get here
msg = _("Caught an unexpected behavior: the fake restore to "
"snapshot request using 'fake' volume and empty string "
"snapshot as argument has not failed.")
LOG.exception(msg)
raise exception.NetAppException(msg)
@na_utils.trace @na_utils.trace
def _get_aggregate_node(self, aggregate_name): def _get_aggregate_node(self, aggregate_name):
@ -335,8 +359,6 @@ class NetAppCmodeFileStorageLibrary(object):
netapp_flexvol_encryption = self._cluster_info.get( netapp_flexvol_encryption = self._cluster_info.get(
'nve_support', False) 'nve_support', False)
revert_to_snapshot_support = self._check_snaprestore_license()
for aggr_name in sorted(aggregates): for aggr_name in sorted(aggregates):
reserved_percentage = self.configuration.reserved_share_percentage reserved_percentage = self.configuration.reserved_share_percentage
@ -368,7 +390,7 @@ class NetAppCmodeFileStorageLibrary(object):
'thin_provisioning': [True, False], 'thin_provisioning': [True, False],
'snapshot_support': True, 'snapshot_support': True,
'create_share_from_snapshot_support': True, 'create_share_from_snapshot_support': True,
'revert_to_snapshot_support': revert_to_snapshot_support, 'revert_to_snapshot_support': self._revert_to_snapshot_support,
} }
# Add storage service catalog data. # Add storage service catalog data.

View File

@ -118,8 +118,16 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
self.mock_object( self.mock_object(
self.library._client, 'check_for_cluster_credentials', self.library._client, 'check_for_cluster_credentials',
mock.Mock(return_value=True)) mock.Mock(return_value=True))
self.mock_object(
self.library, '_check_snaprestore_license',
mock.Mock(return_value=True))
self.mock_object(
self.library,
'_get_licenses',
mock.Mock(return_value=fake.LICENSES))
self.library.do_setup(self.context) self.library.do_setup(self.context)
self.assertEqual(fake.LICENSES, self.library._licenses)
mock_get_api_client.assert_called_once_with() mock_get_api_client.assert_called_once_with()
(self.library._client.check_for_cluster_credentials. (self.library._client.check_for_cluster_credentials.
assert_called_once_with()) assert_called_once_with())
@ -127,6 +135,9 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
self.mock_object(self.library._client, self.mock_object(self.library._client,
'check_for_cluster_credentials', 'check_for_cluster_credentials',
mock.Mock(return_value=True)) mock.Mock(return_value=True))
self.mock_object(
self.library, '_check_snaprestore_license',
mock.Mock(return_value=True))
mock_set_cluster_info = self.mock_object( mock_set_cluster_info = self.mock_object(
self.library, '_set_cluster_info') self.library, '_set_cluster_info')
self.library.do_setup(self.context) self.library.do_setup(self.context)
@ -138,17 +149,10 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
fake.CLUSTER_NODES) fake.CLUSTER_NODES)
def test_check_for_setup_error(self): def test_check_for_setup_error(self):
self.library._licenses = []
self.mock_object(self.library,
'_get_licenses',
mock.Mock(return_value=['fake_license']))
mock_start_periodic_tasks = self.mock_object(self.library, mock_start_periodic_tasks = self.mock_object(self.library,
'_start_periodic_tasks') '_start_periodic_tasks')
self.library.check_for_setup_error() self.library.check_for_setup_error()
self.assertEqual(['fake_license'], self.library._licenses)
mock_start_periodic_tasks.assert_called_once_with() mock_start_periodic_tasks.assert_called_once_with()
def test_get_vserver(self): def test_get_vserver(self):
@ -333,7 +337,8 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
assert_called_once_with(fake.AGGREGATES)) assert_called_once_with(fake.AGGREGATES))
self.assertDictEqual(fake.AGGREGATE_CAPACITIES, result) self.assertDictEqual(fake.AGGREGATE_CAPACITIES, result)
def test_check_snaprestore_license_notfound(self): def test_check_snaprestore_license_admin_notfound(self):
self.library._have_cluster_creds = True
licenses = list(fake.LICENSES) licenses = list(fake.LICENSES)
licenses.remove('snaprestore') licenses.remove('snaprestore')
self.mock_object(self.client, self.mock_object(self.client,
@ -342,13 +347,44 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
result = self.library._check_snaprestore_license() result = self.library._check_snaprestore_license()
self.assertIs(False, result) self.assertIs(False, result)
def test_check_snaprestore_license_found(self): def test_check_snaprestore_license_admin_found(self):
self.mock_object(self.client, self.library._have_cluster_creds = True
'get_licenses', self.library._licenses = fake.LICENSES
mock.Mock(return_value=fake.LICENSES))
result = self.library._check_snaprestore_license() result = self.library._check_snaprestore_license()
self.assertIs(True, result) self.assertIs(True, result)
def test_check_snaprestore_license_svm_scoped_notfound(self):
self.library._have_cluster_creds = False
self.mock_object(self.library._client,
'restore_snapshot',
mock.Mock(side_effect=netapp_api.NaApiError(
code=netapp_api.EAPIERROR,
message=fake.NO_SNAPRESTORE_LICENSE)))
result = self.library._check_snaprestore_license()
self.assertIs(False, result)
def test_check_snaprestore_license_svm_scoped_found(self):
self.library._have_cluster_creds = False
self.mock_object(self.library._client,
'restore_snapshot',
mock.Mock(side_effect=netapp_api.NaApiError(
code=netapp_api.EAPIERROR,
message='Other error')))
result = self.library._check_snaprestore_license()
self.assertIs(True, result)
def test_check_snaprestore_license_svm_scoped_found_exception(self):
self.mock_object(lib_base.LOG, 'exception')
self.library._have_cluster_creds = False
self.mock_object(self.library._client,
'restore_snapshot',
mock.Mock(return_value=None))
self.assertRaises(
exception.NetAppException,
self.library._check_snaprestore_license)
lib_base.LOG.exception.assert_called_once()
def test_get_aggregate_node_cluster_creds(self): def test_get_aggregate_node_cluster_creds(self):
self.library._have_cluster_creds = True self.library._have_cluster_creds = True
@ -449,13 +485,11 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
self.library, '_get_aggregate_space', self.library, '_get_aggregate_space',
mock.Mock(return_value=fake.AGGREGATE_CAPACITIES)) mock.Mock(return_value=fake.AGGREGATE_CAPACITIES))
self.library._have_cluster_creds = True self.library._have_cluster_creds = True
self.library._revert_to_snapshot_support = True
self.library._cluster_info = fake.CLUSTER_INFO self.library._cluster_info = fake.CLUSTER_INFO
self.library._ssc_stats = fake.SSC_INFO self.library._ssc_stats = fake.SSC_INFO
self.library._perf_library.get_node_utilization_for_pool = ( self.library._perf_library.get_node_utilization_for_pool = (
mock.Mock(side_effect=[30.0, 42.0])) mock.Mock(side_effect=[30.0, 42.0]))
self.mock_object(self.library,
'_check_snaprestore_license',
mock.Mock(return_value=True))
result = self.library._get_pools(filter_function='filter', result = self.library._get_pools(filter_function='filter',
goodness_function='goodness') goodness_function='goodness')
@ -468,13 +502,11 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
self.library, '_get_aggregate_space', self.library, '_get_aggregate_space',
mock.Mock(return_value=fake.AGGREGATE_CAPACITIES_VSERVER_CREDS)) mock.Mock(return_value=fake.AGGREGATE_CAPACITIES_VSERVER_CREDS))
self.library._have_cluster_creds = False self.library._have_cluster_creds = False
self.library._revert_to_snapshot_support = True
self.library._cluster_info = fake.CLUSTER_INFO self.library._cluster_info = fake.CLUSTER_INFO
self.library._ssc_stats = fake.SSC_INFO_VSERVER_CREDS self.library._ssc_stats = fake.SSC_INFO_VSERVER_CREDS
self.library._perf_library.get_node_utilization_for_pool = ( self.library._perf_library.get_node_utilization_for_pool = (
mock.Mock(side_effect=[50.0, 50.0])) mock.Mock(side_effect=[50.0, 50.0]))
self.mock_object(self.library,
'_check_snaprestore_license',
mock.Mock(return_value=True))
result = self.library._get_pools() result = self.library._get_pools()

View File

@ -18,6 +18,7 @@ import copy
from manila.common import constants from manila.common import constants
import manila.tests.share.drivers.netapp.fakes as na_fakes import manila.tests.share.drivers.netapp.fakes as na_fakes
NO_SNAPRESTORE_LICENSE = '"SnapRestore" is not licensed in the cluster.'
CLUSTER_NAME = 'fake_cluster' CLUSTER_NAME = 'fake_cluster'
CLUSTER_NAME_2 = 'fake_cluster_2' CLUSTER_NAME_2 = 'fake_cluster_2'
BACKEND_NAME = 'fake_backend_name' BACKEND_NAME = 'fake_backend_name'

View File

@ -0,0 +1,6 @@
---
fixes:
- |
Fixed `bug #1882590 <https://bugs.launchpad.net/manila/+bug/1882590>`_
that caused an error on starting a NetApp backend when using the SVM
scoped account.