diff --git a/manila/share/api.py b/manila/share/api.py index 423f042f77..bea2845707 100644 --- a/manila/share/api.py +++ b/manila/share/api.py @@ -1143,7 +1143,7 @@ class API(base.Base): statuses = (constants.STATUS_AVAILABLE, constants.STATUS_ERROR, constants.STATUS_INACTIVE) if not (force or share_instance['status'] in statuses): - msg = _("Share instance status must be one of %(statuses)s") % { + msg = _("Share instance status must be one of %(statuses)s") % { "statuses": statuses} raise exception.InvalidShareInstance(reason=msg) diff --git a/manila/share/drivers/netapp/dataontap/client/client_cmode.py b/manila/share/drivers/netapp/dataontap/client/client_cmode.py index 6b1f4c3a5f..70ee191a6d 100644 --- a/manila/share/drivers/netapp/dataontap/client/client_cmode.py +++ b/manila/share/drivers/netapp/dataontap/client/client_cmode.py @@ -83,6 +83,7 @@ class NetAppCmodeClient(client_base.NetAppBaseClient): self.features.add_feature('CLUSTER_PEER_POLICY', supported=ontapi_1_30) self.features.add_feature('ADVANCED_DISK_PARTITIONING', supported=ontapi_1_30) + self.features.add_feature('KERBEROS_VSERVER', supported=ontapi_1_30) self.features.add_feature('FLEXVOL_ENCRYPTION', supported=ontapi_1_110) self.features.add_feature('SVM_DR', supported=ontapi_1_140) self.features.add_feature('ADAPTIVE_QOS', supported=ontapi_1_140) @@ -452,7 +453,7 @@ class NetAppCmodeClient(client_base.NetAppBaseClient): def _terminate_vserver_services(self, vserver_name, vserver_client, security_services): for service in security_services: - if service['type'] == 'active_directory': + if service['type'].lower() == 'active_directory': api_args = { 'admin-password': service['password'], 'admin-username': service['user'], @@ -465,6 +466,8 @@ class NetAppCmodeClient(client_base.NetAppBaseClient): 'Vserver %s.', vserver_name) else: vserver_client.send_request('cifs-server-delete') + elif service['type'].lower() == 'kerberos': + vserver_client.disable_kerberos(service) @na_utils.trace def is_nve_supported(self): @@ -1435,7 +1438,7 @@ class NetAppCmodeClient(client_base.NetAppBaseClient): vserver_name) elif security_service['type'].lower() == 'kerberos': - self.create_kerberos_realm(security_service) + vserver_client.create_kerberos_realm(security_service) vserver_client.configure_kerberos(security_service, vserver_name) @@ -1549,12 +1552,16 @@ class NetAppCmodeClient(client_base.NetAppBaseClient): def create_kerberos_realm(self, security_service): """Creates Kerberos realm on cluster.""" + if not self.features.KERBEROS_VSERVER: + msg = _('Kerberos realms owned by Vserver are supported on ONTAP ' + '8.3 or later.') + raise exception.NetAppException(msg) + api_args = { 'admin-server-ip': security_service['server'], 'admin-server-port': '749', 'clock-skew': '5', 'comment': '', - 'config-name': security_service['id'], 'kdc-ip': security_service['server'], 'kdc-port': '88', 'kdc-vendor': 'other', @@ -1575,6 +1582,11 @@ class NetAppCmodeClient(client_base.NetAppBaseClient): def configure_kerberos(self, security_service, vserver_name): """Configures Kerberos for NFS on Vserver.""" + if not self.features.KERBEROS_VSERVER: + msg = _('Kerberos realms owned by Vserver are supported on ONTAP ' + '8.3 or later.') + raise exception.NetAppException(msg) + self.configure_dns(security_service) spn = self._get_kerberos_service_principal_name( security_service, vserver_name) @@ -1590,8 +1602,9 @@ class NetAppCmodeClient(client_base.NetAppBaseClient): 'admin-user-name': security_service['user'], 'interface-name': lif_name, 'is-kerberos-enabled': 'true', - 'service-principal-name': spn, + 'service-principal-name': spn } + self.send_request('kerberos-config-modify', api_args) @na_utils.trace @@ -1601,6 +1614,69 @@ class NetAppCmodeClient(client_base.NetAppBaseClient): security_service['domain'] + '@' + security_service['domain'].upper()) + @na_utils.trace + def disable_kerberos(self, security_service): + """Disable Kerberos in all Vserver LIFs.""" + + lifs = self.list_network_interfaces() + # NOTE(dviroel): If the Vserver has no LIFs, there are no Kerberos + # to be disabled. + for lif_name in lifs: + api_args = { + 'admin-password': security_service['password'], + 'admin-user-name': security_service['user'], + 'interface-name': lif_name, + 'is-kerberos-enabled': 'false', + } + try: + self.send_request('kerberos-config-modify', api_args) + except netapp_api.NaApiError as e: + disabled_msg = "Kerberos is already disabled" + if (e.code == netapp_api.EAPIERROR and + disabled_msg in e.message): + # NOTE(dviroel): do not raise an error for 'Kerberos is + # already disabled in this LIF'. + continue + msg = _("Failed to disable Kerberos: %s.") + raise exception.NetAppException(msg % e.message) + + @na_utils.trace + def is_kerberos_enabled(self): + """Check if Kerberos in enabled in all LIFs.""" + + if not self.features.KERBEROS_VSERVER: + msg = _('Kerberos realms owned by Vserver are supported on ONTAP ' + '8.3 or later.') + raise exception.NetAppException(msg) + + lifs = self.list_network_interfaces() + if not lifs: + LOG.debug("There are no LIFs configured for this Vserver. " + "Kerberos is disabled.") + return False + + # NOTE(dviroel): All LIFs must have kerberos enabled + for lif in lifs: + api_args = { + 'interface-name': lif, + 'desired-attributes': { + 'kerberos-config-info': { + 'is-kerberos-enabled': None, + } + } + } + result = self.send_request('kerberos-config-get', api_args) + + attributes = result.get_child_by_name('attributes') + kerberos_info = attributes.get_child_by_name( + 'kerberos-config-info') + kerberos_enabled = kerberos_info.get_child_content( + 'is-kerberos-enabled') + if kerberos_enabled == 'false': + return False + + return True + @na_utils.trace def configure_dns(self, security_service): api_args = { @@ -2922,51 +2998,58 @@ class NetAppCmodeClient(client_base.NetAppBaseClient): self.send_request('cifs-share-delete', {'share-name': share_name}) @na_utils.trace - def add_nfs_export_rule(self, policy_name, client_match, readonly): + def add_nfs_export_rule(self, policy_name, client_match, readonly, + auth_methods): rule_indices = self._get_nfs_export_rule_indices(policy_name, client_match) if not rule_indices: - self._add_nfs_export_rule(policy_name, client_match, readonly) + self._add_nfs_export_rule(policy_name, client_match, readonly, + auth_methods) else: # Update first rule and delete the rest self._update_nfs_export_rule( - policy_name, client_match, readonly, rule_indices.pop(0)) + policy_name, client_match, readonly, rule_indices.pop(0), + auth_methods) self._remove_nfs_export_rules(policy_name, rule_indices) @na_utils.trace - def _add_nfs_export_rule(self, policy_name, client_match, readonly): + def _add_nfs_export_rule(self, policy_name, client_match, readonly, + auth_methods): api_args = { 'policy-name': policy_name, 'client-match': client_match, - 'ro-rule': { - 'security-flavor': 'sys', - }, - 'rw-rule': { - 'security-flavor': 'sys' if not readonly else 'never', - }, - 'super-user-security': { - 'security-flavor': 'sys', - }, + 'ro-rule': [], + 'rw-rule': [], + 'super-user-security': [], } + for am in auth_methods: + api_args['ro-rule'].append({'security-flavor': am}) + api_args['rw-rule'].append({'security-flavor': am}) + api_args['super-user-security'].append({'security-flavor': am}) + if readonly: + # readonly, overwrite with auth method 'never' + api_args['rw-rule'] = [{'security-flavor': 'never'}] + self.send_request('export-rule-create', api_args) @na_utils.trace def _update_nfs_export_rule(self, policy_name, client_match, readonly, - rule_index): + rule_index, auth_methods): api_args = { 'policy-name': policy_name, 'rule-index': rule_index, 'client-match': client_match, - 'ro-rule': { - 'security-flavor': 'sys' - }, - 'rw-rule': { - 'security-flavor': 'sys' if not readonly else 'never' - }, - 'super-user-security': { - 'security-flavor': 'sys' - }, + 'ro-rule': [], + 'rw-rule': [], + 'super-user-security': [], } + for am in auth_methods: + api_args['ro-rule'].append({'security-flavor': am}) + api_args['rw-rule'].append({'security-flavor': am}) + api_args['super-user-security'].append({'security-flavor': am}) + if readonly: + api_args['rw-rule'] = [{'security-flavor': 'never'}] + self.send_request('export-rule-modify', api_args) @na_utils.trace diff --git a/manila/share/drivers/netapp/dataontap/protocols/nfs_cmode.py b/manila/share/drivers/netapp/dataontap/protocols/nfs_cmode.py index d81388d07f..ac920ac933 100644 --- a/manila/share/drivers/netapp/dataontap/protocols/nfs_cmode.py +++ b/manila/share/drivers/netapp/dataontap/protocols/nfs_cmode.py @@ -92,11 +92,14 @@ class NetAppCmodeNFSHelper(base.NetAppBaseHelper): # Create new export policy self._client.create_nfs_export_policy(temp_new_export_policy_name) + # Get authentication methods, based on Vserver configuration + auth_methods = self._get_auth_methods() + # Add new rules to new policy for address in addresses: self._client.add_nfs_export_rule( temp_new_export_policy_name, address, - self._is_readonly(new_rules[address])) + self._is_readonly(new_rules[address]), auth_methods) # Rename policy currently in force LOG.info('Renaming NFS export policy for share %(share)s to ' @@ -187,6 +190,19 @@ class NetAppCmodeNFSHelper(base.NetAppBaseHelper): self._client.rename_nfs_export_policy(actual_export_policy, expected_export_policy) + @na_utils.trace + def _get_auth_methods(self): + """Returns authentication methods for export policy rules. + + This method returns the authentication methods to be configure in an + export policy rule, based on security services configuration set in + the current Vserver. If Kerberos is enabled in vServer LIFs, the auth + methods will be configure to support 'krb5', 'krb5i' and 'krb5p'. The + default authentication method is 'sys' (AUTH_SYS). + """ + kerberos_enabled = self._client.is_kerberos_enabled() + return ['krb5', 'krb5i', 'krb5p'] if kerberos_enabled else ['sys'] + @na_utils.trace def cleanup_demoted_replica(self, share, share_name): return diff --git a/manila/tests/share/drivers/netapp/dataontap/client/fakes.py b/manila/tests/share/drivers/netapp/dataontap/client/fakes.py index c35a42914e..cceb7f7339 100644 --- a/manila/tests/share/drivers/netapp/dataontap/client/fakes.py +++ b/manila/tests/share/drivers/netapp/dataontap/client/fakes.py @@ -2713,6 +2713,20 @@ SNAPMIRROR_POLICY_GET_ITER_RESPONSE = etree.XML(""" 'vserver_name': VSERVER_NAME, }) +KERBEROS_CONFIG_GET_RESPONSE = etree.XML(""" + + + + %(lif_name)s + true + %(vserver_name)s + + + """ % { + 'lif_name': LIF_NAME, + 'vserver_name': VSERVER_NAME, +}) + FAKE_VOL_XML = """ open123 online diff --git a/manila/tests/share/drivers/netapp/dataontap/client/test_client_cmode.py b/manila/tests/share/drivers/netapp/dataontap/client/test_client_cmode.py index 63a4b4fc1d..cedf7d0d61 100644 --- a/manila/tests/share/drivers/netapp/dataontap/client/test_client_cmode.py +++ b/manila/tests/share/drivers/netapp/dataontap/client/test_client_cmode.py @@ -2374,7 +2374,7 @@ class NetAppClientCmodeTestCase(test.TestCase): def test_setup_security_services_kerberos(self): self.mock_object(self.client, 'send_request') - self.mock_object(self.client, 'create_kerberos_realm') + self.mock_object(self.vserver_client, 'create_kerberos_realm') self.mock_object(self.vserver_client, 'configure_kerberos') self.client.setup_security_services([fake.KERBEROS_SECURITY_SERVICE], @@ -2394,7 +2394,7 @@ class NetAppClientCmodeTestCase(test.TestCase): } self.client.send_request.assert_has_calls([ mock.call('vserver-modify', vserver_modify_args)]) - self.client.create_kerberos_realm.assert_has_calls([ + self.vserver_client.create_kerberos_realm.assert_has_calls([ mock.call(fake.KERBEROS_SECURITY_SERVICE)]) self.vserver_client.configure_kerberos.assert_has_calls([ mock.call(fake.KERBEROS_SECURITY_SERVICE, fake.VSERVER_NAME)]) @@ -2574,7 +2574,7 @@ class NetAppClientCmodeTestCase(test.TestCase): fake.VSERVER_NAME) def test_create_kerberos_realm(self): - + self.client.features.add_feature('KERBEROS_VSERVER') self.mock_object(self.client, 'send_request') self.client.create_kerberos_realm(fake.KERBEROS_SECURITY_SERVICE) @@ -2584,7 +2584,6 @@ class NetAppClientCmodeTestCase(test.TestCase): 'admin-server-port': '749', 'clock-skew': '5', 'comment': '', - 'config-name': fake.KERBEROS_SECURITY_SERVICE['id'], 'kdc-ip': fake.KERBEROS_SECURITY_SERVICE['server'], 'kdc-port': '88', 'kdc-vendor': 'other', @@ -2597,7 +2596,7 @@ class NetAppClientCmodeTestCase(test.TestCase): mock.call('kerberos-realm-create', kerberos_realm_create_args)]) def test_create_kerberos_realm_already_present(self): - + self.client.features.add_feature('KERBEROS_VSERVER') self.mock_object(self.client, 'send_request', self._mock_api_error(code=netapp_api.EDUPLICATEENTRY)) @@ -2609,7 +2608,6 @@ class NetAppClientCmodeTestCase(test.TestCase): 'admin-server-port': '749', 'clock-skew': '5', 'comment': '', - 'config-name': fake.KERBEROS_SECURITY_SERVICE['id'], 'kdc-ip': fake.KERBEROS_SECURITY_SERVICE['server'], 'kdc-port': '88', 'kdc-vendor': 'other', @@ -2623,7 +2621,7 @@ class NetAppClientCmodeTestCase(test.TestCase): self.assertEqual(1, client_cmode.LOG.debug.call_count) def test_create_kerberos_realm_api_error(self): - + self.client.features.add_feature('KERBEROS_VSERVER') self.mock_object(self.client, 'send_request', self._mock_api_error()) self.assertRaises(exception.NetAppException, @@ -2631,7 +2629,7 @@ class NetAppClientCmodeTestCase(test.TestCase): fake.KERBEROS_SECURITY_SERVICE) def test_configure_kerberos(self): - + self.client.features.add_feature('KERBEROS_VSERVER') self.mock_object(self.client, 'send_request') self.mock_object(self.client, 'configure_dns') self.mock_object(self.client, @@ -2668,7 +2666,7 @@ class NetAppClientCmodeTestCase(test.TestCase): kerberos_config_modify_args2)]) def test_configure_kerberos_no_network_interfaces(self): - + self.client.features.add_feature('KERBEROS_VSERVER') self.mock_object(self.client, 'send_request') self.mock_object(self.client, 'configure_dns') self.mock_object(self.client, @@ -2683,6 +2681,82 @@ class NetAppClientCmodeTestCase(test.TestCase): self.client.configure_dns.assert_called_with( fake.KERBEROS_SECURITY_SERVICE) + def test_disable_kerberos(self): + self.mock_object(self.client, 'send_request') + self.mock_object(self.client, + 'list_network_interfaces', + mock.Mock(return_value=['lif1', 'lif2'])) + + self.client.disable_kerberos(fake.KERBEROS_SECURITY_SERVICE) + + kerberos_config_modify_args1 = { + 'admin-password': fake.KERBEROS_SECURITY_SERVICE['password'], + 'admin-user-name': fake.KERBEROS_SECURITY_SERVICE['user'], + 'interface-name': 'lif1', + 'is-kerberos-enabled': 'false', + } + kerberos_config_modify_args2 = { + 'admin-password': fake.KERBEROS_SECURITY_SERVICE['password'], + 'admin-user-name': fake.KERBEROS_SECURITY_SERVICE['user'], + 'interface-name': 'lif2', + 'is-kerberos-enabled': 'false', + } + + self.client.send_request.assert_has_calls([ + mock.call('kerberos-config-modify', + kerberos_config_modify_args1), + mock.call('kerberos-config-modify', + kerberos_config_modify_args2)]) + self.client.list_network_interfaces.assert_called_once() + + def test_disable_kerberos_already_disabled(self): + self.mock_object(self.client, 'send_request', + self._mock_api_error( + code=netapp_api.EAPIERROR, + message='Kerberos is already disabled')) + self.mock_object(self.client, + 'list_network_interfaces', + mock.Mock(return_value=['lif1'])) + + self.client.disable_kerberos(fake.KERBEROS_SECURITY_SERVICE) + + kerberos_config_modify_args = { + 'admin-password': fake.KERBEROS_SECURITY_SERVICE['password'], + 'admin-user-name': fake.KERBEROS_SECURITY_SERVICE['user'], + 'interface-name': 'lif1', + 'is-kerberos-enabled': 'false', + } + + self.client.send_request.assert_called_once_with( + 'kerberos-config-modify', kerberos_config_modify_args) + self.client.list_network_interfaces.assert_called_once() + + def test_is_kerberos_enabled(self): + self.client.features.add_feature('KERBEROS_VSERVER') + api_response = netapp_api.NaElement( + fake.KERBEROS_CONFIG_GET_RESPONSE) + self.mock_object(self.client, 'send_request', + mock.Mock(return_value=api_response)) + self.mock_object(self.client, + 'list_network_interfaces', + mock.Mock(return_value=['lif1'])) + + result = self.client.is_kerberos_enabled() + + kerberos_config_get_args = { + 'interface-name': 'lif1', + 'desired-attributes': { + 'kerberos-config-info': { + 'is-kerberos-enabled': None, + } + } + } + + self.assertTrue(result) + self.client.send_request.assert_called_once_with( + 'kerberos-config-get', kerberos_config_get_args) + self.client.list_network_interfaces.assert_called_once() + def test_get_kerberos_service_principal_name(self): spn = self.client._get_kerberos_service_principal_name( @@ -4796,15 +4870,17 @@ class NetAppClientCmodeTestCase(test.TestCase): self.client, '_add_nfs_export_rule') mock_update_nfs_export_rule = self.mock_object( self.client, '_update_nfs_export_rule') + auth_methods = ['sys'] self.client.add_nfs_export_rule(fake.EXPORT_POLICY_NAME, fake.IP_ADDRESS, - False) + False, + auth_methods) mock_get_nfs_export_rule_indices.assert_called_once_with( fake.EXPORT_POLICY_NAME, fake.IP_ADDRESS) mock_add_nfs_export_rule.assert_called_once_with( - fake.EXPORT_POLICY_NAME, fake.IP_ADDRESS, False) + fake.EXPORT_POLICY_NAME, fake.IP_ADDRESS, False, auth_methods) self.assertFalse(mock_update_nfs_export_rule.called) def test_add_nfs_export_rule_single_existing(self): @@ -4818,16 +4894,19 @@ class NetAppClientCmodeTestCase(test.TestCase): self.client, '_update_nfs_export_rule') mock_remove_nfs_export_rules = self.mock_object( self.client, '_remove_nfs_export_rules') + auth_methods = ['sys'] self.client.add_nfs_export_rule(fake.EXPORT_POLICY_NAME, fake.IP_ADDRESS, - False) + False, + auth_methods) mock_get_nfs_export_rule_indices.assert_called_once_with( fake.EXPORT_POLICY_NAME, fake.IP_ADDRESS) self.assertFalse(mock_add_nfs_export_rule.called) mock_update_nfs_export_rule.assert_called_once_with( - fake.EXPORT_POLICY_NAME, fake.IP_ADDRESS, False, '1') + fake.EXPORT_POLICY_NAME, fake.IP_ADDRESS, False, '1', + auth_methods) mock_remove_nfs_export_rules.assert_called_once_with( fake.EXPORT_POLICY_NAME, []) @@ -4842,71 +4921,82 @@ class NetAppClientCmodeTestCase(test.TestCase): self.client, '_update_nfs_export_rule') mock_remove_nfs_export_rules = self.mock_object( self.client, '_remove_nfs_export_rules') - + auth_methods = ['sys'] self.client.add_nfs_export_rule(fake.EXPORT_POLICY_NAME, fake.IP_ADDRESS, - False) + False, + auth_methods) mock_get_nfs_export_rule_indices.assert_called_once_with( fake.EXPORT_POLICY_NAME, fake.IP_ADDRESS) self.assertFalse(mock_add_nfs_export_rule.called) mock_update_nfs_export_rule.assert_called_once_with( - fake.EXPORT_POLICY_NAME, fake.IP_ADDRESS, False, '2') + fake.EXPORT_POLICY_NAME, fake.IP_ADDRESS, False, '2', auth_methods) mock_remove_nfs_export_rules.assert_called_once_with( fake.EXPORT_POLICY_NAME, ['4', '6']) - @ddt.data({'readonly': False, 'rw_security_flavor': 'sys'}, - {'readonly': True, 'rw_security_flavor': 'never'}) + @ddt.data({'readonly': False, 'auth_method': 'sys'}, + {'readonly': True, 'auth_method': 'sys'}) @ddt.unpack - def test__add_nfs_export_rule(self, readonly, rw_security_flavor): + def test__add_nfs_export_rule(self, readonly, auth_method): self.mock_object(self.client, 'send_request') self.client._add_nfs_export_rule(fake.EXPORT_POLICY_NAME, fake.IP_ADDRESS, - readonly) - + readonly, + [auth_method]) export_rule_create_args = { 'policy-name': fake.EXPORT_POLICY_NAME, 'client-match': fake.IP_ADDRESS, - 'ro-rule': { - 'security-flavor': 'sys', - }, - 'rw-rule': { - 'security-flavor': rw_security_flavor, - }, - 'super-user-security': { - 'security-flavor': 'sys', - }, + 'ro-rule': [ + {'security-flavor': auth_method}, + ], + 'rw-rule': [ + {'security-flavor': auth_method}, + ], + 'super-user-security': [ + {'security-flavor': auth_method}, + ], } + if readonly: + export_rule_create_args['rw-rule'] = [ + {'security-flavor': 'never'} + ] + self.client.send_request.assert_has_calls( [mock.call('export-rule-create', export_rule_create_args)]) - @ddt.data({'readonly': False, 'rw_security_flavor': 'sys', 'index': '2'}, - {'readonly': True, 'rw_security_flavor': 'never', 'index': '4'}) + @ddt.data({'readonly': False, 'auth_method': 'sys', 'index': '2'}, + {'readonly': True, 'auth_method': 'krb5', 'index': '4'}) @ddt.unpack - def test_update_nfs_export_rule(self, readonly, rw_security_flavor, index): + def test_update_nfs_export_rule(self, readonly, auth_method, index): self.mock_object(self.client, 'send_request') self.client._update_nfs_export_rule(fake.EXPORT_POLICY_NAME, fake.IP_ADDRESS, readonly, - index) + index, + [auth_method]) export_rule_modify_args = { 'policy-name': fake.EXPORT_POLICY_NAME, 'rule-index': index, 'client-match': fake.IP_ADDRESS, - 'ro-rule': { - 'security-flavor': 'sys', - }, - 'rw-rule': { - 'security-flavor': rw_security_flavor, - }, - 'super-user-security': { - 'security-flavor': 'sys', - }, + 'ro-rule': [ + {'security-flavor': auth_method}, + ], + 'rw-rule': [ + {'security-flavor': auth_method}, + ], + 'super-user-security': [ + {'security-flavor': auth_method}, + ], } + if readonly: + export_rule_modify_args['rw-rule'] = [ + {'security-flavor': 'never'} + ] self.client.send_request.assert_has_calls( [mock.call('export-rule-modify', export_rule_modify_args)]) diff --git a/manila/tests/share/drivers/netapp/dataontap/protocols/test_nfs_cmode.py b/manila/tests/share/drivers/netapp/dataontap/protocols/test_nfs_cmode.py index 3ce0240fee..be9886beaf 100644 --- a/manila/tests/share/drivers/netapp/dataontap/protocols/test_nfs_cmode.py +++ b/manila/tests/share/drivers/netapp/dataontap/protocols/test_nfs_cmode.py @@ -83,6 +83,10 @@ class NetAppClusteredNFSHelperTestCase(test.TestCase): '_get_temp_export_policy_name', mock.Mock(side_effect=['fake_new_export_policy', 'fake_old_export_policy'])) + fake_auth_method = 'fake_auth_method' + self.mock_object(self.helper, + '_get_auth_methods', + mock.Mock(return_value=fake_auth_method)) self.helper.update_access(fake.CIFS_SHARE, fake.SHARE_NAME, @@ -91,7 +95,8 @@ class NetAppClusteredNFSHelperTestCase(test.TestCase): self.mock_client.create_nfs_export_policy.assert_called_once_with( 'fake_new_export_policy') self.mock_client.add_nfs_export_rule.assert_called_once_with( - 'fake_new_export_policy', fake.CLIENT_ADDRESS_1, False) + 'fake_new_export_policy', fake.CLIENT_ADDRESS_1, False, + fake_auth_method) (self.mock_client.set_nfs_export_policy_for_volume. assert_called_once_with(fake.SHARE_NAME, 'fake_new_export_policy')) (self.mock_client.soft_delete_nfs_export_policy. @@ -219,3 +224,12 @@ class NetAppClusteredNFSHelperTestCase(test.TestCase): self.assertFalse(self.mock_client.create_nfs_export_policy.called) self.mock_client.rename_nfs_export_policy.assert_called_once_with( 'fake', fake.EXPORT_POLICY_NAME) + + @ddt.data((False, ['sys']), (True, ['krb5', 'krb5i', 'krb5p'])) + @ddt.unpack + def test__get_security_flavors(self, kerberos_enabled, security_flavors): + self.mock_client.is_kerberos_enabled.return_value = kerberos_enabled + + result = self.helper._get_auth_methods() + + self.assertEqual(security_flavors, result) diff --git a/releasenotes/notes/1901189-netapp-fix-kerberos-setup-357753068a5645ad.yaml b/releasenotes/notes/1901189-netapp-fix-kerberos-setup-357753068a5645ad.yaml new file mode 100644 index 0000000000..dda65abfb0 --- /dev/null +++ b/releasenotes/notes/1901189-netapp-fix-kerberos-setup-357753068a5645ad.yaml @@ -0,0 +1,13 @@ +--- +fixes: + - | + NetApp ONTAP driver is now fixed to properly configure and clean up share + servers with Kerberos security service, for clustered ONTAP 8.3 or higher. + Access rules are now configured with the correct NFS authentication methods + based on the security service configured in the share server. Please refer to + `Launchpad Bug #1901189 `_, + `Launchpad Bug #1904746 `_, + and `Launchpad Bug #1907669 `_ + for more details. + +