[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('CLUSTER_PEER_POLICY', supported=ontapi_1_30)
|
||||||
self.features.add_feature('ADVANCED_DISK_PARTITIONING',
|
self.features.add_feature('ADVANCED_DISK_PARTITIONING',
|
||||||
supported=ontapi_1_30)
|
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('FLEXVOL_ENCRYPTION', supported=ontapi_1_110)
|
||||||
self.features.add_feature('SVM_DR', supported=ontapi_1_140)
|
self.features.add_feature('SVM_DR', supported=ontapi_1_140)
|
||||||
self.features.add_feature('ADAPTIVE_QOS', 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,
|
def _terminate_vserver_services(self, vserver_name, vserver_client,
|
||||||
security_services):
|
security_services):
|
||||||
for service in security_services:
|
for service in security_services:
|
||||||
if service['type'] == 'active_directory':
|
if service['type'].lower() == 'active_directory':
|
||||||
api_args = {
|
api_args = {
|
||||||
'admin-password': service['password'],
|
'admin-password': service['password'],
|
||||||
'admin-username': service['user'],
|
'admin-username': service['user'],
|
||||||
@ -465,6 +466,8 @@ class NetAppCmodeClient(client_base.NetAppBaseClient):
|
|||||||
'Vserver %s.', vserver_name)
|
'Vserver %s.', vserver_name)
|
||||||
else:
|
else:
|
||||||
vserver_client.send_request('cifs-server-delete')
|
vserver_client.send_request('cifs-server-delete')
|
||||||
|
elif service['type'].lower() == 'kerberos':
|
||||||
|
vserver_client.disable_kerberos(service)
|
||||||
|
|
||||||
@na_utils.trace
|
@na_utils.trace
|
||||||
def is_nve_supported(self):
|
def is_nve_supported(self):
|
||||||
@ -1435,7 +1438,7 @@ class NetAppCmodeClient(client_base.NetAppBaseClient):
|
|||||||
vserver_name)
|
vserver_name)
|
||||||
|
|
||||||
elif security_service['type'].lower() == 'kerberos':
|
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_client.configure_kerberos(security_service,
|
||||||
vserver_name)
|
vserver_name)
|
||||||
|
|
||||||
@ -1549,12 +1552,16 @@ class NetAppCmodeClient(client_base.NetAppBaseClient):
|
|||||||
def create_kerberos_realm(self, security_service):
|
def create_kerberos_realm(self, security_service):
|
||||||
"""Creates Kerberos realm on cluster."""
|
"""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 = {
|
api_args = {
|
||||||
'admin-server-ip': security_service['server'],
|
'admin-server-ip': security_service['server'],
|
||||||
'admin-server-port': '749',
|
'admin-server-port': '749',
|
||||||
'clock-skew': '5',
|
'clock-skew': '5',
|
||||||
'comment': '',
|
'comment': '',
|
||||||
'config-name': security_service['id'],
|
|
||||||
'kdc-ip': security_service['server'],
|
'kdc-ip': security_service['server'],
|
||||||
'kdc-port': '88',
|
'kdc-port': '88',
|
||||||
'kdc-vendor': 'other',
|
'kdc-vendor': 'other',
|
||||||
@ -1575,6 +1582,11 @@ class NetAppCmodeClient(client_base.NetAppBaseClient):
|
|||||||
def configure_kerberos(self, security_service, vserver_name):
|
def configure_kerberos(self, security_service, vserver_name):
|
||||||
"""Configures Kerberos for NFS on Vserver."""
|
"""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)
|
self.configure_dns(security_service)
|
||||||
spn = self._get_kerberos_service_principal_name(
|
spn = self._get_kerberos_service_principal_name(
|
||||||
security_service, vserver_name)
|
security_service, vserver_name)
|
||||||
@ -1590,8 +1602,9 @@ class NetAppCmodeClient(client_base.NetAppBaseClient):
|
|||||||
'admin-user-name': security_service['user'],
|
'admin-user-name': security_service['user'],
|
||||||
'interface-name': lif_name,
|
'interface-name': lif_name,
|
||||||
'is-kerberos-enabled': 'true',
|
'is-kerberos-enabled': 'true',
|
||||||
'service-principal-name': spn,
|
'service-principal-name': spn
|
||||||
}
|
}
|
||||||
|
|
||||||
self.send_request('kerberos-config-modify', api_args)
|
self.send_request('kerberos-config-modify', api_args)
|
||||||
|
|
||||||
@na_utils.trace
|
@na_utils.trace
|
||||||
@ -1601,6 +1614,69 @@ class NetAppCmodeClient(client_base.NetAppBaseClient):
|
|||||||
security_service['domain'] + '@' +
|
security_service['domain'] + '@' +
|
||||||
security_service['domain'].upper())
|
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
|
@na_utils.trace
|
||||||
def configure_dns(self, security_service):
|
def configure_dns(self, security_service):
|
||||||
api_args = {
|
api_args = {
|
||||||
@ -2922,51 +2998,58 @@ class NetAppCmodeClient(client_base.NetAppBaseClient):
|
|||||||
self.send_request('cifs-share-delete', {'share-name': share_name})
|
self.send_request('cifs-share-delete', {'share-name': share_name})
|
||||||
|
|
||||||
@na_utils.trace
|
@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,
|
rule_indices = self._get_nfs_export_rule_indices(policy_name,
|
||||||
client_match)
|
client_match)
|
||||||
if not rule_indices:
|
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:
|
else:
|
||||||
# Update first rule and delete the rest
|
# Update first rule and delete the rest
|
||||||
self._update_nfs_export_rule(
|
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)
|
self._remove_nfs_export_rules(policy_name, rule_indices)
|
||||||
|
|
||||||
@na_utils.trace
|
@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 = {
|
api_args = {
|
||||||
'policy-name': policy_name,
|
'policy-name': policy_name,
|
||||||
'client-match': client_match,
|
'client-match': client_match,
|
||||||
'ro-rule': {
|
'ro-rule': [],
|
||||||
'security-flavor': 'sys',
|
'rw-rule': [],
|
||||||
},
|
'super-user-security': [],
|
||||||
'rw-rule': {
|
|
||||||
'security-flavor': 'sys' if not readonly else 'never',
|
|
||||||
},
|
|
||||||
'super-user-security': {
|
|
||||||
'security-flavor': 'sys',
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
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)
|
self.send_request('export-rule-create', api_args)
|
||||||
|
|
||||||
@na_utils.trace
|
@na_utils.trace
|
||||||
def _update_nfs_export_rule(self, policy_name, client_match, readonly,
|
def _update_nfs_export_rule(self, policy_name, client_match, readonly,
|
||||||
rule_index):
|
rule_index, auth_methods):
|
||||||
api_args = {
|
api_args = {
|
||||||
'policy-name': policy_name,
|
'policy-name': policy_name,
|
||||||
'rule-index': rule_index,
|
'rule-index': rule_index,
|
||||||
'client-match': client_match,
|
'client-match': client_match,
|
||||||
'ro-rule': {
|
'ro-rule': [],
|
||||||
'security-flavor': 'sys'
|
'rw-rule': [],
|
||||||
},
|
'super-user-security': [],
|
||||||
'rw-rule': {
|
|
||||||
'security-flavor': 'sys' if not readonly else 'never'
|
|
||||||
},
|
|
||||||
'super-user-security': {
|
|
||||||
'security-flavor': 'sys'
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
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)
|
self.send_request('export-rule-modify', api_args)
|
||||||
|
|
||||||
@na_utils.trace
|
@na_utils.trace
|
||||||
|
@ -92,11 +92,14 @@ class NetAppCmodeNFSHelper(base.NetAppBaseHelper):
|
|||||||
# Create new export policy
|
# Create new export policy
|
||||||
self._client.create_nfs_export_policy(temp_new_export_policy_name)
|
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
|
# Add new rules to new policy
|
||||||
for address in addresses:
|
for address in addresses:
|
||||||
self._client.add_nfs_export_rule(
|
self._client.add_nfs_export_rule(
|
||||||
temp_new_export_policy_name, address,
|
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
|
# Rename policy currently in force
|
||||||
LOG.info('Renaming NFS export policy for share %(share)s to '
|
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,
|
self._client.rename_nfs_export_policy(actual_export_policy,
|
||||||
expected_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
|
@na_utils.trace
|
||||||
def cleanup_demoted_replica(self, share, share_name):
|
def cleanup_demoted_replica(self, share, share_name):
|
||||||
return
|
return
|
||||||
|
@ -2713,6 +2713,20 @@ SNAPMIRROR_POLICY_GET_ITER_RESPONSE = etree.XML("""
|
|||||||
'vserver_name': VSERVER_NAME,
|
'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>
|
FAKE_VOL_XML = """<volume-info>
|
||||||
<name>open123</name>
|
<name>open123</name>
|
||||||
<state>online</state>
|
<state>online</state>
|
||||||
|
@ -2374,7 +2374,7 @@ class NetAppClientCmodeTestCase(test.TestCase):
|
|||||||
def test_setup_security_services_kerberos(self):
|
def test_setup_security_services_kerberos(self):
|
||||||
|
|
||||||
self.mock_object(self.client, 'send_request')
|
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.mock_object(self.vserver_client, 'configure_kerberos')
|
||||||
|
|
||||||
self.client.setup_security_services([fake.KERBEROS_SECURITY_SERVICE],
|
self.client.setup_security_services([fake.KERBEROS_SECURITY_SERVICE],
|
||||||
@ -2394,7 +2394,7 @@ class NetAppClientCmodeTestCase(test.TestCase):
|
|||||||
}
|
}
|
||||||
self.client.send_request.assert_has_calls([
|
self.client.send_request.assert_has_calls([
|
||||||
mock.call('vserver-modify', vserver_modify_args)])
|
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)])
|
mock.call(fake.KERBEROS_SECURITY_SERVICE)])
|
||||||
self.vserver_client.configure_kerberos.assert_has_calls([
|
self.vserver_client.configure_kerberos.assert_has_calls([
|
||||||
mock.call(fake.KERBEROS_SECURITY_SERVICE, fake.VSERVER_NAME)])
|
mock.call(fake.KERBEROS_SECURITY_SERVICE, fake.VSERVER_NAME)])
|
||||||
@ -2574,7 +2574,7 @@ class NetAppClientCmodeTestCase(test.TestCase):
|
|||||||
fake.VSERVER_NAME)
|
fake.VSERVER_NAME)
|
||||||
|
|
||||||
def test_create_kerberos_realm(self):
|
def test_create_kerberos_realm(self):
|
||||||
|
self.client.features.add_feature('KERBEROS_VSERVER')
|
||||||
self.mock_object(self.client, 'send_request')
|
self.mock_object(self.client, 'send_request')
|
||||||
|
|
||||||
self.client.create_kerberos_realm(fake.KERBEROS_SECURITY_SERVICE)
|
self.client.create_kerberos_realm(fake.KERBEROS_SECURITY_SERVICE)
|
||||||
@ -2584,7 +2584,6 @@ class NetAppClientCmodeTestCase(test.TestCase):
|
|||||||
'admin-server-port': '749',
|
'admin-server-port': '749',
|
||||||
'clock-skew': '5',
|
'clock-skew': '5',
|
||||||
'comment': '',
|
'comment': '',
|
||||||
'config-name': fake.KERBEROS_SECURITY_SERVICE['id'],
|
|
||||||
'kdc-ip': fake.KERBEROS_SECURITY_SERVICE['server'],
|
'kdc-ip': fake.KERBEROS_SECURITY_SERVICE['server'],
|
||||||
'kdc-port': '88',
|
'kdc-port': '88',
|
||||||
'kdc-vendor': 'other',
|
'kdc-vendor': 'other',
|
||||||
@ -2597,7 +2596,7 @@ class NetAppClientCmodeTestCase(test.TestCase):
|
|||||||
mock.call('kerberos-realm-create', kerberos_realm_create_args)])
|
mock.call('kerberos-realm-create', kerberos_realm_create_args)])
|
||||||
|
|
||||||
def test_create_kerberos_realm_already_present(self):
|
def test_create_kerberos_realm_already_present(self):
|
||||||
|
self.client.features.add_feature('KERBEROS_VSERVER')
|
||||||
self.mock_object(self.client,
|
self.mock_object(self.client,
|
||||||
'send_request',
|
'send_request',
|
||||||
self._mock_api_error(code=netapp_api.EDUPLICATEENTRY))
|
self._mock_api_error(code=netapp_api.EDUPLICATEENTRY))
|
||||||
@ -2609,7 +2608,6 @@ class NetAppClientCmodeTestCase(test.TestCase):
|
|||||||
'admin-server-port': '749',
|
'admin-server-port': '749',
|
||||||
'clock-skew': '5',
|
'clock-skew': '5',
|
||||||
'comment': '',
|
'comment': '',
|
||||||
'config-name': fake.KERBEROS_SECURITY_SERVICE['id'],
|
|
||||||
'kdc-ip': fake.KERBEROS_SECURITY_SERVICE['server'],
|
'kdc-ip': fake.KERBEROS_SECURITY_SERVICE['server'],
|
||||||
'kdc-port': '88',
|
'kdc-port': '88',
|
||||||
'kdc-vendor': 'other',
|
'kdc-vendor': 'other',
|
||||||
@ -2623,7 +2621,7 @@ class NetAppClientCmodeTestCase(test.TestCase):
|
|||||||
self.assertEqual(1, client_cmode.LOG.debug.call_count)
|
self.assertEqual(1, client_cmode.LOG.debug.call_count)
|
||||||
|
|
||||||
def test_create_kerberos_realm_api_error(self):
|
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.mock_object(self.client, 'send_request', self._mock_api_error())
|
||||||
|
|
||||||
self.assertRaises(exception.NetAppException,
|
self.assertRaises(exception.NetAppException,
|
||||||
@ -2631,7 +2629,7 @@ class NetAppClientCmodeTestCase(test.TestCase):
|
|||||||
fake.KERBEROS_SECURITY_SERVICE)
|
fake.KERBEROS_SECURITY_SERVICE)
|
||||||
|
|
||||||
def test_configure_kerberos(self):
|
def test_configure_kerberos(self):
|
||||||
|
self.client.features.add_feature('KERBEROS_VSERVER')
|
||||||
self.mock_object(self.client, 'send_request')
|
self.mock_object(self.client, 'send_request')
|
||||||
self.mock_object(self.client, 'configure_dns')
|
self.mock_object(self.client, 'configure_dns')
|
||||||
self.mock_object(self.client,
|
self.mock_object(self.client,
|
||||||
@ -2668,7 +2666,7 @@ class NetAppClientCmodeTestCase(test.TestCase):
|
|||||||
kerberos_config_modify_args2)])
|
kerberos_config_modify_args2)])
|
||||||
|
|
||||||
def test_configure_kerberos_no_network_interfaces(self):
|
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, 'send_request')
|
||||||
self.mock_object(self.client, 'configure_dns')
|
self.mock_object(self.client, 'configure_dns')
|
||||||
self.mock_object(self.client,
|
self.mock_object(self.client,
|
||||||
@ -2683,6 +2681,82 @@ class NetAppClientCmodeTestCase(test.TestCase):
|
|||||||
self.client.configure_dns.assert_called_with(
|
self.client.configure_dns.assert_called_with(
|
||||||
fake.KERBEROS_SECURITY_SERVICE)
|
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):
|
def test_get_kerberos_service_principal_name(self):
|
||||||
|
|
||||||
spn = self.client._get_kerberos_service_principal_name(
|
spn = self.client._get_kerberos_service_principal_name(
|
||||||
@ -4796,15 +4870,17 @@ class NetAppClientCmodeTestCase(test.TestCase):
|
|||||||
self.client, '_add_nfs_export_rule')
|
self.client, '_add_nfs_export_rule')
|
||||||
mock_update_nfs_export_rule = self.mock_object(
|
mock_update_nfs_export_rule = self.mock_object(
|
||||||
self.client, '_update_nfs_export_rule')
|
self.client, '_update_nfs_export_rule')
|
||||||
|
auth_methods = ['sys']
|
||||||
|
|
||||||
self.client.add_nfs_export_rule(fake.EXPORT_POLICY_NAME,
|
self.client.add_nfs_export_rule(fake.EXPORT_POLICY_NAME,
|
||||||
fake.IP_ADDRESS,
|
fake.IP_ADDRESS,
|
||||||
False)
|
False,
|
||||||
|
auth_methods)
|
||||||
|
|
||||||
mock_get_nfs_export_rule_indices.assert_called_once_with(
|
mock_get_nfs_export_rule_indices.assert_called_once_with(
|
||||||
fake.EXPORT_POLICY_NAME, fake.IP_ADDRESS)
|
fake.EXPORT_POLICY_NAME, fake.IP_ADDRESS)
|
||||||
mock_add_nfs_export_rule.assert_called_once_with(
|
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)
|
self.assertFalse(mock_update_nfs_export_rule.called)
|
||||||
|
|
||||||
def test_add_nfs_export_rule_single_existing(self):
|
def test_add_nfs_export_rule_single_existing(self):
|
||||||
@ -4818,16 +4894,19 @@ class NetAppClientCmodeTestCase(test.TestCase):
|
|||||||
self.client, '_update_nfs_export_rule')
|
self.client, '_update_nfs_export_rule')
|
||||||
mock_remove_nfs_export_rules = self.mock_object(
|
mock_remove_nfs_export_rules = self.mock_object(
|
||||||
self.client, '_remove_nfs_export_rules')
|
self.client, '_remove_nfs_export_rules')
|
||||||
|
auth_methods = ['sys']
|
||||||
|
|
||||||
self.client.add_nfs_export_rule(fake.EXPORT_POLICY_NAME,
|
self.client.add_nfs_export_rule(fake.EXPORT_POLICY_NAME,
|
||||||
fake.IP_ADDRESS,
|
fake.IP_ADDRESS,
|
||||||
False)
|
False,
|
||||||
|
auth_methods)
|
||||||
|
|
||||||
mock_get_nfs_export_rule_indices.assert_called_once_with(
|
mock_get_nfs_export_rule_indices.assert_called_once_with(
|
||||||
fake.EXPORT_POLICY_NAME, fake.IP_ADDRESS)
|
fake.EXPORT_POLICY_NAME, fake.IP_ADDRESS)
|
||||||
self.assertFalse(mock_add_nfs_export_rule.called)
|
self.assertFalse(mock_add_nfs_export_rule.called)
|
||||||
mock_update_nfs_export_rule.assert_called_once_with(
|
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(
|
mock_remove_nfs_export_rules.assert_called_once_with(
|
||||||
fake.EXPORT_POLICY_NAME, [])
|
fake.EXPORT_POLICY_NAME, [])
|
||||||
|
|
||||||
@ -4842,71 +4921,82 @@ class NetAppClientCmodeTestCase(test.TestCase):
|
|||||||
self.client, '_update_nfs_export_rule')
|
self.client, '_update_nfs_export_rule')
|
||||||
mock_remove_nfs_export_rules = self.mock_object(
|
mock_remove_nfs_export_rules = self.mock_object(
|
||||||
self.client, '_remove_nfs_export_rules')
|
self.client, '_remove_nfs_export_rules')
|
||||||
|
auth_methods = ['sys']
|
||||||
self.client.add_nfs_export_rule(fake.EXPORT_POLICY_NAME,
|
self.client.add_nfs_export_rule(fake.EXPORT_POLICY_NAME,
|
||||||
fake.IP_ADDRESS,
|
fake.IP_ADDRESS,
|
||||||
False)
|
False,
|
||||||
|
auth_methods)
|
||||||
|
|
||||||
mock_get_nfs_export_rule_indices.assert_called_once_with(
|
mock_get_nfs_export_rule_indices.assert_called_once_with(
|
||||||
fake.EXPORT_POLICY_NAME, fake.IP_ADDRESS)
|
fake.EXPORT_POLICY_NAME, fake.IP_ADDRESS)
|
||||||
self.assertFalse(mock_add_nfs_export_rule.called)
|
self.assertFalse(mock_add_nfs_export_rule.called)
|
||||||
mock_update_nfs_export_rule.assert_called_once_with(
|
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(
|
mock_remove_nfs_export_rules.assert_called_once_with(
|
||||||
fake.EXPORT_POLICY_NAME, ['4', '6'])
|
fake.EXPORT_POLICY_NAME, ['4', '6'])
|
||||||
|
|
||||||
@ddt.data({'readonly': False, 'rw_security_flavor': 'sys'},
|
@ddt.data({'readonly': False, 'auth_method': 'sys'},
|
||||||
{'readonly': True, 'rw_security_flavor': 'never'})
|
{'readonly': True, 'auth_method': 'sys'})
|
||||||
@ddt.unpack
|
@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.mock_object(self.client, 'send_request')
|
||||||
|
|
||||||
self.client._add_nfs_export_rule(fake.EXPORT_POLICY_NAME,
|
self.client._add_nfs_export_rule(fake.EXPORT_POLICY_NAME,
|
||||||
fake.IP_ADDRESS,
|
fake.IP_ADDRESS,
|
||||||
readonly)
|
readonly,
|
||||||
|
[auth_method])
|
||||||
export_rule_create_args = {
|
export_rule_create_args = {
|
||||||
'policy-name': fake.EXPORT_POLICY_NAME,
|
'policy-name': fake.EXPORT_POLICY_NAME,
|
||||||
'client-match': fake.IP_ADDRESS,
|
'client-match': fake.IP_ADDRESS,
|
||||||
'ro-rule': {
|
'ro-rule': [
|
||||||
'security-flavor': 'sys',
|
{'security-flavor': auth_method},
|
||||||
},
|
],
|
||||||
'rw-rule': {
|
'rw-rule': [
|
||||||
'security-flavor': rw_security_flavor,
|
{'security-flavor': auth_method},
|
||||||
},
|
],
|
||||||
'super-user-security': {
|
'super-user-security': [
|
||||||
'security-flavor': 'sys',
|
{'security-flavor': auth_method},
|
||||||
},
|
],
|
||||||
}
|
}
|
||||||
|
if readonly:
|
||||||
|
export_rule_create_args['rw-rule'] = [
|
||||||
|
{'security-flavor': 'never'}
|
||||||
|
]
|
||||||
|
|
||||||
self.client.send_request.assert_has_calls(
|
self.client.send_request.assert_has_calls(
|
||||||
[mock.call('export-rule-create', export_rule_create_args)])
|
[mock.call('export-rule-create', export_rule_create_args)])
|
||||||
|
|
||||||
@ddt.data({'readonly': False, 'rw_security_flavor': 'sys', 'index': '2'},
|
@ddt.data({'readonly': False, 'auth_method': 'sys', 'index': '2'},
|
||||||
{'readonly': True, 'rw_security_flavor': 'never', 'index': '4'})
|
{'readonly': True, 'auth_method': 'krb5', 'index': '4'})
|
||||||
@ddt.unpack
|
@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.mock_object(self.client, 'send_request')
|
||||||
self.client._update_nfs_export_rule(fake.EXPORT_POLICY_NAME,
|
self.client._update_nfs_export_rule(fake.EXPORT_POLICY_NAME,
|
||||||
fake.IP_ADDRESS,
|
fake.IP_ADDRESS,
|
||||||
readonly,
|
readonly,
|
||||||
index)
|
index,
|
||||||
|
[auth_method])
|
||||||
|
|
||||||
export_rule_modify_args = {
|
export_rule_modify_args = {
|
||||||
'policy-name': fake.EXPORT_POLICY_NAME,
|
'policy-name': fake.EXPORT_POLICY_NAME,
|
||||||
'rule-index': index,
|
'rule-index': index,
|
||||||
'client-match': fake.IP_ADDRESS,
|
'client-match': fake.IP_ADDRESS,
|
||||||
'ro-rule': {
|
'ro-rule': [
|
||||||
'security-flavor': 'sys',
|
{'security-flavor': auth_method},
|
||||||
},
|
],
|
||||||
'rw-rule': {
|
'rw-rule': [
|
||||||
'security-flavor': rw_security_flavor,
|
{'security-flavor': auth_method},
|
||||||
},
|
],
|
||||||
'super-user-security': {
|
'super-user-security': [
|
||||||
'security-flavor': 'sys',
|
{'security-flavor': auth_method},
|
||||||
},
|
],
|
||||||
}
|
}
|
||||||
|
if readonly:
|
||||||
|
export_rule_modify_args['rw-rule'] = [
|
||||||
|
{'security-flavor': 'never'}
|
||||||
|
]
|
||||||
|
|
||||||
self.client.send_request.assert_has_calls(
|
self.client.send_request.assert_has_calls(
|
||||||
[mock.call('export-rule-modify', export_rule_modify_args)])
|
[mock.call('export-rule-modify', export_rule_modify_args)])
|
||||||
|
@ -83,6 +83,10 @@ class NetAppClusteredNFSHelperTestCase(test.TestCase):
|
|||||||
'_get_temp_export_policy_name',
|
'_get_temp_export_policy_name',
|
||||||
mock.Mock(side_effect=['fake_new_export_policy',
|
mock.Mock(side_effect=['fake_new_export_policy',
|
||||||
'fake_old_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,
|
self.helper.update_access(fake.CIFS_SHARE,
|
||||||
fake.SHARE_NAME,
|
fake.SHARE_NAME,
|
||||||
@ -91,7 +95,8 @@ class NetAppClusteredNFSHelperTestCase(test.TestCase):
|
|||||||
self.mock_client.create_nfs_export_policy.assert_called_once_with(
|
self.mock_client.create_nfs_export_policy.assert_called_once_with(
|
||||||
'fake_new_export_policy')
|
'fake_new_export_policy')
|
||||||
self.mock_client.add_nfs_export_rule.assert_called_once_with(
|
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.
|
(self.mock_client.set_nfs_export_policy_for_volume.
|
||||||
assert_called_once_with(fake.SHARE_NAME, 'fake_new_export_policy'))
|
assert_called_once_with(fake.SHARE_NAME, 'fake_new_export_policy'))
|
||||||
(self.mock_client.soft_delete_nfs_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.assertFalse(self.mock_client.create_nfs_export_policy.called)
|
||||||
self.mock_client.rename_nfs_export_policy.assert_called_once_with(
|
self.mock_client.rename_nfs_export_policy.assert_called_once_with(
|
||||||
'fake', fake.EXPORT_POLICY_NAME)
|
'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