Implement IPv6 support for Manila Dell EMC Unity driver

Major changes:
  * Support to create/delete/extend/access NFS and CIFS share/snapshot
    in the IPv6 network which created by Neutron
  * Support to connect Unity management interface using IPv6 address

Change-Id: I590d569c6fe1a0f8b146bec9c74513269b8358f4
Implements: blueprint unity-manila-ipv6-support
This commit is contained in:
Che, Roger 2017-12-29 16:25:06 +08:00
parent afe308098a
commit eb65711063
11 changed files with 158 additions and 17 deletions

View File

@ -35,7 +35,7 @@ Requirements
------------
- Unity OE 4.0.1 or higher.
- StorOps 0.2.17 or higher is installed on Manila node.
- StorOps 0.5.7 or higher is installed on Manila node.
- Following licenses are activated on Unity:
* CIFS/SMB Support
@ -138,6 +138,31 @@ for the Unity driver.
Restart of :term:`manila-share` service is needed for the configuration changes to take
effect.
IPv6 support
------------
IPv6 support for Unity driver is introduced in Queens release. The feature is divided
into two parts:
1. The driver is able to manage share or snapshot in the Neutron IPv6 network.
2. The driver is able to connect Unity management interface using its IPv6 address.
Pre-Configurations for IPv6 support
===================================
The following parameters need to be configured in `/etc/manila/manila.conf`
for the Unity driver:
network_plugin_ipv6_enabled = True
- `network_plugin_ipv6_enabled` indicates IPv6 is enabled.
If you want to connect Unity using IPv6 address, you should configure IPv6 address
by `/net/if/mgmt` uemcli command, `mgmtInterfaceSettings` RESTful api or the system
settings of Unity GUI for Unity and specify the address in `/etc/manila/manila.conf`:
emc_nas_server = <IPv6 address>
Restrictions
------------

View File

@ -108,7 +108,7 @@ Mapping of share drivers and share access rules support
+----------------------------------------+--------------+--------------+----------------+------------+--------------+--------------+--------------+----------------+------------+------------+
| EMC VNX | NFS (J) | NFS (Q) | CIFS (J) | \- | \- | NFS (L) | NFS (Q) | CIFS (L) | \- | \- |
+----------------------------------------+--------------+--------------+----------------+------------+--------------+--------------+--------------+----------------+------------+------------+
| EMC Unity | NFS (N) | \- | CIFS (N) | \- | \- | NFS (N) | \- | CIFS (N) | \- | \- |
| EMC Unity | NFS (N) | NFS (Q) | CIFS (N) | \- | \- | NFS (N) | NFS (Q) | CIFS (N) | \- | \- |
+----------------------------------------+--------------+--------------+----------------+------------+--------------+--------------+--------------+----------------+------------+------------+
| EMC Isilon | NFS,CIFS (K) | \- | CIFS (M) | \- | \- | NFS (M) | \- | CIFS (M) | \- | \- |
+----------------------------------------+--------------+--------------+----------------+------------+--------------+--------------+--------------+----------------+------------+------------+
@ -232,7 +232,7 @@ More information: :ref:`capabilities_and_extra_specs`
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+
| EMC VNX | J | \- | \- | \- | \- | L | \- | J | \- | \- | P | Q |
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+
| EMC Unity | N | \- | \- | \- | N | \- | \- | N | \- | \- | P | \- |
| EMC Unity | N | \- | \- | \- | N | \- | \- | N | \- | \- | P | Q |
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+
| EMC Isilon | \- | K | \- | \- | \- | L | \- | K | \- | \- | P | \- |
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+

View File

@ -77,6 +77,7 @@ class EMCShareDriver(driver.ShareDriver):
configuration=self.configuration)
super(EMCShareDriver, self).__init__(
self.plugin.driver_handles_share_servers, *args, **kwargs)
self.ipv6_implemented = self.plugin.ipv6_implemented
if hasattr(self.plugin, 'ipv6_implemented'):
self.ipv6_implemented = self.plugin.ipv6_implemented

View File

@ -26,6 +26,7 @@ if storops:
from manila.common import constants as const
from manila import exception
from manila.i18n import _
from manila.share.drivers.dell_emc.common.enas import utils as enas_utils
from manila.share.drivers.dell_emc.plugins.unity import utils
LOG = log.getLogger(__name__)
@ -171,11 +172,12 @@ class UnityClient(object):
@staticmethod
def create_interface(nas_server, ip_addr, netmask, gateway, port_id,
vlan_id=None):
vlan_id=None, prefix_length=None):
try:
nas_server.create_file_interface(port_id,
ip_addr,
netmask=netmask,
v6_prefix_length=prefix_length,
gateway=gateway,
vlan_id=vlan_id)
except storops_ex.UnityIpAddressUsedError:
@ -262,6 +264,7 @@ class UnityClient(object):
def nfs_allow_access(self, share_name, host_ip, access_level):
share = self.system.get_nfs_share(name=share_name)
host_ip = enas_utils.convert_ipv6_format_if_needed(host_ip)
if access_level == const.ACCESS_LEVEL_RW:
share.allow_read_write_access(host_ip, force_create_host=True)
share.allow_root_access(host_ip, force_create_host=True)

View File

@ -19,6 +19,7 @@ from oslo_config import cfg
from oslo_log import log
from oslo_utils import excutils
from oslo_utils import importutils
from oslo_utils import netutils
storops = importutils.try_import('storops')
if storops:
@ -35,7 +36,7 @@ from manila.share.drivers.dell_emc.plugins.unity import utils as unity_utils
from manila.share import utils as share_utils
from manila import utils
VERSION = "3.0.0"
VERSION = "4.0.0"
LOG = log.getLogger(__name__)
SUPPORTED_NETWORK_TYPES = (None, 'flat', 'vlan')
@ -83,6 +84,7 @@ class UnityStorageConnection(driver.StorageConnection):
self.reserved_percentage = None
self.max_over_subscription_ratio = None
self.port_ids_conf = None
self.ipv6_implemented = True
# props from super class.
self.driver_handles_share_servers = True
@ -475,17 +477,20 @@ class UnityStorageConnection(driver.StorageConnection):
return network
def _create_network_interface(self, nas_server, network, port_id):
ip_addr = network['ip_address']
netmask = utils.cidr_to_netmask(network['cidr'])
gateway = network['gateway']
vlan_id = network['segmentation_id']
kargs = {'ip_addr': network['ip_address'],
'gateway': network['gateway'],
'vlan_id': network['segmentation_id'],
'port_id': port_id}
if netutils.is_valid_ipv6_cidr(kargs['ip_addr']):
kargs['netmask'] = None
kargs['prefix_length'] = str(utils.cidr_to_prefixlen(
network['cidr']))
else:
kargs['netmask'] = utils.cidr_to_netmask(network['cidr'])
# Create the interfaces on NAS server
self.client.create_interface(nas_server,
ip_addr,
netmask,
gateway,
port_id=port_id,
vlan_id=vlan_id)
self.client.create_interface(nas_server, **kargs)
def _choose_sp(self, sp_ports_map):
sp = None
@ -508,7 +513,7 @@ class UnityStorageConnection(driver.StorageConnection):
def _get_cifs_location(file_interfaces, share_name):
return [
{'path': r'\\%(interface)s\%(share_name)s' % {
'interface': interface.ip_address,
'interface': enas_utils.export_unc_path(interface.ip_address),
'share_name': share_name}
}
for interface in file_interfaces
@ -551,7 +556,8 @@ class UnityStorageConnection(driver.StorageConnection):
def _get_nfs_location(file_interfaces, share_name):
return [
{'path': '%(interface)s:/%(share_name)s' % {
'interface': interface.ip_address,
'interface': enas_utils.convert_ipv6_format_if_needed(
interface.ip_address),
'share_name': share_name}
}
for interface in file_interfaces

View File

@ -45,6 +45,17 @@ network_allocations_vxlan:
network_type: vxlan
mtu: 1500
network_allocations_ipv6:
_type: 'network_allocations'
_properties: &network_allocations_ipv6_prop
- id: '04ac4c27-9cf7-4406-809c-13edc93e9844'
ip_address: '2001:db8:0:1:f816:3eff:fe76:35c4'
cidr: '2001:db8:0:1:f816:3eff:fe76:35c4/64'
segmentation_id: 170
gateway: '2001:db8:0:1::1'
network_type: vlan
mtu: 1500
active_directory:
_type: 'security_service'
_properties: &active_directory_prop
@ -97,6 +108,13 @@ network_info__vxlan:
network_type: 'vxlan'
network_allocations: *network_allocations_vxlan_prop
network_info__ipv6:
_type: 'network_info'
_properties: &network_info__ipv6_prop
<<: *network_info_flat_prop
network_allocations: *network_allocations_ipv6_prop
segmentation_id: 170
network_info__active_directory:
_type: 'network_info'
_properties:

View File

@ -27,6 +27,13 @@ interface_2: &interface_2
_properties:
ip_address: 'fake_ip_addr_2'
interface_ipv6: &interface_ipv6
_properties:
ip_addr: '2001:db8:0:1:f816:3eff:fe76:35c4'
gateway: '2001:db8:0:1::1'
prefix_length: '64'
vlan_id: '201'
nas_server: &nas_server
_properties: &nas_server_prop
name: '78fd845f-8e7d-487f-bfde-051d83e78103'
@ -34,6 +41,13 @@ nas_server: &nas_server
current_sp: *sp_a
home_sp: *sp_a
nas_server_ipv6: &nas_server_ipv6
_properties: &nas_server_ipv6_prop
name: 'af1eef2f-be66-4df1-8f25-9720f087da05'
file_interface: [*interface_ipv6]
current_sp: *sp_a
home_sp: *sp_a
filesystem_base: &filesystem_base
_properties: &filesystem_base_prop
name: 'fake_filesystem_name'
@ -1082,3 +1096,9 @@ test_get_file_ports:
unity:
_methods:
get_file_port: [*port_1, *port_internal_port, *down_port, *la_port]
test_create_file_interface_ipv6:
file_interface: *interface_ipv6
nas_server:
_methods:
create_file_interface:

View File

@ -14,6 +14,7 @@
# under the License.
import ddt
import mock
from oslo_utils import units
from manila import exception
@ -207,3 +208,26 @@ class TestClient(test.TestCase):
def test_get_tenant_for_vlan_already_has_interfaces(self, client):
tenant = client.get_tenant('tenant', 3)
self.assertEqual('tenant_1', tenant.id)
@res_mock.mock_client_input
@res_mock.patch_client
def test_create_file_interface_ipv6(self, client, mocked_input):
mock_nas_server = mock.Mock()
mock_nas_server.create_file_interface = mock.Mock(return_value=None)
mock_file_interface = mocked_input['file_interface']
mock_port_id = mock.Mock()
client.create_interface(mock_nas_server,
mock_file_interface.ip_addr,
netmask=None,
gateway=mock_file_interface.gateway,
port_id=mock_port_id,
vlan_id=mock_file_interface.vlan_id,
prefix_length=mock_file_interface.prefix_length
)
mock_nas_server.create_file_interface.assert_called_once_with(
mock_port_id,
mock_file_interface.ip_addr,
netmask=None,
v6_prefix_length=mock_file_interface.prefix_length,
gateway=mock_file_interface.gateway,
vlan_id=mock_file_interface.vlan_id)

View File

@ -15,6 +15,7 @@
import copy
import ddt
import mock
from oslo_utils import units
import six
@ -613,3 +614,42 @@ class TestConnection(test.TestCase):
self.assertRaises(exception.InvalidShareAccessLevel,
connection.allow_access,
None, share, rw_access)
@res_mock.patch_connection
def test__create_network_interface_ipv6(self, connection):
connection.client.create_interface = mock.Mock(return_value=None)
nas_server = mock.Mock()
network = {'ip_address': '2001:db8:0:1:f816:3eff:fe76:35c4',
'cidr': '2001:db8:0:1:f816:3eff:fe76:35c4/64',
'gateway': '2001:db8:0:1::1',
'segmentation_id': '201'}
port_id = mock.Mock()
connection._create_network_interface(nas_server, network, port_id)
expected = {'ip_addr': '2001:db8:0:1:f816:3eff:fe76:35c4',
'netmask': None,
'gateway': '2001:db8:0:1::1',
'port_id': port_id,
'vlan_id': '201',
'prefix_length': '64'}
connection.client.create_interface.assert_called_once_with(nas_server,
**expected)
@res_mock.patch_connection
def test__create_network_interface_ipv4(self, connection):
connection.client.create_interface = mock.Mock(return_value=None)
nas_server = mock.Mock()
network = {'ip_address': '192.168.1.10',
'cidr': '192.168.1.10/24',
'gateway': '192.168.1.1',
'segmentation_id': '201'}
port_id = mock.Mock()
connection._create_network_interface(nas_server, network, port_id)
expected = {'ip_addr': '192.168.1.10',
'netmask': '255.255.255.0',
'gateway': '192.168.1.1',
'port_id': port_id,
'vlan_id': '201'}
connection.client.create_interface.assert_called_once_with(nas_server,
**expected)

View File

@ -24,6 +24,7 @@ from manila import test
class FakeConnection(base.StorageConnection):
def __init__(self, *args, **kwargs):
self.ipv6_implemented = True
pass
@property

View File

@ -0,0 +1,3 @@
---
features:
- IPv6 support for Dell EMC Unity Manila driver.