Advertise IPv6 support in the NetApp driver

Escape IPv6 exports correctly and setup the default export
policy to allow both IPv4 and IPv6.

Partially implements: bp netapp-ontap-ipv6-support

Change-Id: I84437b140e2a9561cc4092683209800101f45815
This commit is contained in:
Ben Swartzlander 2017-10-19 16:25:44 -04:00
parent 5228e6ade7
commit ebeca4c035
10 changed files with 84 additions and 58 deletions

View File

@ -126,4 +126,5 @@ class NetAppDriver(object):
driver = importutils.import_object(driver_loc, *args, **kwargs)
LOG.info('NetApp driver of family %(storage_family)s and mode '
'%(driver_mode)s loaded.', fmt)
driver.ipv6_implemented = True
return driver

View File

@ -1269,7 +1269,7 @@ class NetAppCmodeClient(client_base.NetAppBaseClient):
"""Enables NFS on Vserver."""
self.send_request('nfs-enable')
self._enable_nfs_protocols(versions)
self._create_default_nfs_export_rule()
self._create_default_nfs_export_rules()
@na_utils.trace
def _enable_nfs_protocols(self, versions):
@ -1286,7 +1286,7 @@ class NetAppCmodeClient(client_base.NetAppBaseClient):
self.send_request('nfs-service-modify', nfs_service_modify_args)
@na_utils.trace
def _create_default_nfs_export_rule(self):
def _create_default_nfs_export_rules(self):
"""Create the default export rule for the NFS service."""
export_rule_create_args = {
@ -1300,6 +1300,8 @@ class NetAppCmodeClient(client_base.NetAppBaseClient):
},
}
self.send_request('export-rule-create', export_rule_create_args)
export_rule_create_args['client-match'] = '::/0'
self.send_request('export-rule-create', export_rule_create_args)
@na_utils.trace
def configure_ldap(self, security_service):
@ -3652,3 +3654,15 @@ class NetAppCmodeClient(client_base.NetAppBaseClient):
msg = 'Could not delete QoS policy groups. Details: %(ex)s'
msg_args = {'ex': ex}
LOG.debug(msg, msg_args)
@na_utils.trace
def get_net_options(self):
result = self.send_request('net-options-get', None, False)
options = result.get_child_by_name('net-options')
ipv6_enabled = False
ipv6_info = options.get_child_by_name('ipv6-options-info')
if ipv6_info:
ipv6_enabled = ipv6_info.get_child_content('enabled') == 'true'
return {
'ipv6-enabled': ipv6_enabled,
}

View File

@ -234,3 +234,6 @@ class NetAppCmodeMultiSvmShareDriver(driver.ShareDriver):
snapshot_dict,
fallback_create,
share_server)
def get_configured_ip_version(self):
return self.library.get_configured_ip_version()

View File

@ -250,3 +250,6 @@ class NetAppCmodeSingleSvmShareDriver(driver.ShareDriver):
snapshot_dict,
fallback_create,
share_server)
def get_configured_ip_version(self):
return self.library.get_configured_ip_version()

View File

@ -384,3 +384,10 @@ class NetAppCmodeMultiSVMFileStorageLibrary(
self._client.delete_vlan(node, port, vlan)
except exception.NetAppException:
LOG.exception("Deleting Vserver VLAN failed.")
def get_configured_ip_version(self):
versions = [4]
options = self._client.get_net_options()
if options['ipv6-enabled']:
versions.append(6)
return versions

View File

@ -146,3 +146,22 @@ class NetAppCmodeSingleSVMFileStorageLibrary(
def get_admin_network_allocations_number(self):
"""Get number of network allocations for creating admin LIFs."""
return 0
@na_utils.trace
def get_configured_ip_version(self):
ipv4 = False
ipv6 = False
vserver_client = self._get_api_client(vserver=self._vserver)
interfaces = vserver_client.get_network_interfaces()
for interface in interfaces:
address = interface['address']
if ':' in address:
ipv6 = True
else:
ipv4 = True
versions = []
if ipv4:
versions.append(4)
if ipv6:
versions.append(6)
return versions

View File

@ -17,7 +17,6 @@ NetApp cDOT NFS protocol helper class.
import uuid
import netaddr
from oslo_log import log
import six
@ -33,6 +32,13 @@ LOG = log.getLogger(__name__)
class NetAppCmodeNFSHelper(base.NetAppBaseHelper):
"""NetApp cDOT NFS protocol helper class."""
@staticmethod
def _escaped_address(address):
if ':' in address:
return '[%s]' % address
else:
return address
@na_utils.trace
def create_share(self, share, share_name,
clear_current_export_policy=True):
@ -45,7 +51,8 @@ class NetAppCmodeNFSHelper(base.NetAppBaseHelper):
# Return a callback that may be used for generating export paths
# for this share.
return (lambda export_address, export_path=export_path:
':'.join([export_address, export_path]))
':'.join([self._escaped_address(export_address),
export_path]))
@na_utils.trace
@base.access_rules_synchronized
@ -67,7 +74,7 @@ class NetAppCmodeNFSHelper(base.NetAppBaseHelper):
# Sort rules by ascending network size
new_rules = {rule['access_to']: rule['access_level'] for rule in rules}
addresses = self._get_sorted_access_rule_addresses(new_rules)
addresses = sorted(new_rules, reverse=True)
# Ensure current export policy has the name we expect
self._ensure_export_policy(share, share_name)
@ -123,22 +130,6 @@ class NetAppCmodeNFSHelper(base.NetAppBaseHelper):
if rule['access_level'] not in constants.ACCESS_LEVELS:
raise exception.InvalidShareAccessLevel(level=rule['access_level'])
@na_utils.trace
def _get_sorted_access_rule_addresses(self, rules):
"""Given a dict of access rules, sort by increasing network size."""
networks = sorted([self._get_network_object_from_rule(rule)
for rule in rules], reverse=True)
return [six.text_type(network) for network in networks]
def _get_network_object_from_rule(self, rule):
"""Get most appropriate netaddr object for address or network rule."""
try:
return netaddr.IPAddress(rule)
except ValueError:
return netaddr.IPNetwork(rule)
@na_utils.trace
def get_target(self, share):
"""Returns ID of target OnTap device based on export location."""

View File

@ -2176,14 +2176,14 @@ class NetAppClientCmodeTestCase(test.TestCase):
self.mock_object(self.client, 'send_request')
self.mock_object(self.client, '_enable_nfs_protocols')
self.mock_object(self.client, '_create_default_nfs_export_rule')
self.mock_object(self.client, '_create_default_nfs_export_rules')
self.client.enable_nfs(fake.NFS_VERSIONS)
self.client.send_request.assert_called_once_with('nfs-enable')
self.client._enable_nfs_protocols.assert_called_once_with(
fake.NFS_VERSIONS)
self.client._create_default_nfs_export_rule.assert_called_once_with()
self.client._create_default_nfs_export_rules.assert_called_once_with()
@ddt.data((True, True, True), (True, False, False), (False, True, True))
@ddt.unpack
@ -2209,11 +2209,17 @@ class NetAppClientCmodeTestCase(test.TestCase):
self.client.send_request.assert_called_once_with(
'nfs-service-modify', nfs_service_modify_args)
def test_create_default_nfs_export_rule(self):
def test_create_default_nfs_export_rules(self):
self.mock_object(self.client, 'send_request')
class CopyingMock(mock.Mock):
def __call__(self, *args, **kwargs):
args = copy.deepcopy(args)
kwargs = copy.deepcopy(kwargs)
return super(CopyingMock, self).__call__(*args, **kwargs)
self.client._create_default_nfs_export_rule()
self.mock_object(self.client, 'send_request', CopyingMock())
self.client._create_default_nfs_export_rules()
export_rule_create_args = {
'client-match': '0.0.0.0/0',
@ -2225,8 +2231,11 @@ class NetAppClientCmodeTestCase(test.TestCase):
'security-flavor': 'never'
}
}
self.client.send_request.assert_called_once_with(
'export-rule-create', export_rule_create_args)
export_rule_create_args2 = export_rule_create_args.copy()
export_rule_create_args2['client-match'] = '::/0'
self.client.send_request.assert_has_calls([
mock.call('export-rule-create', export_rule_create_args),
mock.call('export-rule-create', export_rule_create_args2)])
def test_configure_ldap(self):

View File

@ -20,7 +20,6 @@ import uuid
import ddt
import mock
import netaddr
from manila import exception
from manila.share.drivers.netapp.dataontap.protocols import nfs_cmode
@ -40,6 +39,11 @@ class NetAppClusteredNFSHelperTestCase(test.TestCase):
self.helper = nfs_cmode.NetAppCmodeNFSHelper()
self.helper.set_client(self.mock_client)
@ddt.data(('1.2.3.4', '1.2.3.4'), ('fc00::1', '[fc00::1]'))
@ddt.unpack
def test__escaped_address(self, raw, escaped):
self.assertEqual(escaped, self.helper._escaped_address(raw))
def test_create_share(self):
mock_ensure_export_policy = self.mock_object(self.helper,
@ -121,35 +125,6 @@ class NetAppClusteredNFSHelperTestCase(test.TestCase):
self.helper._validate_access_rule,
rule)
def test_get_sorted_access_rule_addresses(self):
result = self.helper._get_sorted_access_rule_addresses(
fake.NEW_NFS_RULES)
expected = [
'10.10.20.10',
'10.10.20.0/24',
'10.10.10.10',
'10.10.10.0/30',
'10.10.10.0/24',
]
self.assertEqual(expected, result)
@ddt.data({'rule': '1.2.3.4', 'out': netaddr.IPAddress('1.2.3.4')},
{'rule': '1.2.3.4/32', 'out': netaddr.IPNetwork('1.2.3.4/32')})
@ddt.unpack
def test_get_network_object_from_rule(self, rule, out):
result = self.helper._get_network_object_from_rule(rule)
self.assertEqual(out, result)
def test_get_network_object_from_rule_invalid(self):
self.assertRaises(netaddr.AddrFormatError,
self.helper._get_network_object_from_rule,
'invalid')
def test_get_target(self):
target = self.helper.get_target(fake.NFS_SHARE)

View File

@ -0,0 +1,4 @@
---
features:
Added support for IPv6 export location and access rules to
the NetApp driver.