[NetApp] Fix kerberos security service issues
- Fixes the zapi calls for setting up a kerberos, which have changed since ONTAP 8.3. - Fixes kerberos configuration cleanup when deleting a share server. - Fixes access rules authentication methods for NFS when a share server is configured for Kerberos. Change-Id: I60b4f92979045b1fdb90ad8df4f65c1dfe463ae8 Closes-Bug: #1901189 Closes-Bug: #1904746 Closes-Bug: #1907669 Co-Authored-By: Felipe Rodrigues <felipefuty01@gmail.com> Signed-off-by: Douglas Viroel <viroel@gmail.com>
This commit is contained in:
parent
dcdc1a98bb
commit
68ad590c37
@ -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
|
||||
|
@ -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
|
||||
|
@ -2713,6 +2713,20 @@ SNAPMIRROR_POLICY_GET_ITER_RESPONSE = etree.XML("""
|
||||
'vserver_name': VSERVER_NAME,
|
||||
})
|
||||
|
||||
KERBEROS_CONFIG_GET_RESPONSE = etree.XML("""
|
||||
<results status="passed">
|
||||
<attributes>
|
||||
<kerberos-config-info>
|
||||
<interface-name>%(lif_name)s</interface-name>
|
||||
<is-kerberos-enabled>true</is-kerberos-enabled>
|
||||
<vserver>%(vserver_name)s</vserver>
|
||||
</kerberos-config-info>
|
||||
</attributes>
|
||||
</results>""" % {
|
||||
'lif_name': LIF_NAME,
|
||||
'vserver_name': VSERVER_NAME,
|
||||
})
|
||||
|
||||
FAKE_VOL_XML = """<volume-info>
|
||||
<name>open123</name>
|
||||
<state>online</state>
|
||||
|
@ -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)])
|
||||
|
@ -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)
|
||||
|
@ -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 <https://bugs.launchpad.net/manila/+bug/1901189>`_,
|
||||
`Launchpad Bug #1904746 <https://bugs.launchpad.net/manila/+bug/1904746>`_,
|
||||
and `Launchpad Bug #1907669 <https://bugs.launchpad.net/manila/+bug/1907669>`_
|
||||
for more details.
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user