[Unity driver] VLAN enhancement
The enhancement includes: 1. Creates tenant for each vlan on the unity system. So that the nas server in different vlan will have isolated IP address space. 2. Select the appropriate port on the system to create interface based on MTU. 3. Remove the option unity_server_container (the old emc_nas_server_container option) to enable the storage processors load balance and the auto-selection of ports (by mtu). DocImpact Change-Id: Ic1140112f8cfbed7c89d5f66e6bee0c22d64b3ed Closes-Bug: 1649458
This commit is contained in:
parent
75e685d40d
commit
c90515d64e
@ -12,11 +12,10 @@
|
|||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
import random
|
|
||||||
import six
|
import six
|
||||||
|
|
||||||
from oslo_log import log
|
from oslo_log import log
|
||||||
|
from oslo_utils import excutils
|
||||||
from oslo_utils import importutils
|
from oslo_utils import importutils
|
||||||
from oslo_utils import units
|
from oslo_utils import units
|
||||||
|
|
||||||
@ -27,7 +26,7 @@ if storops:
|
|||||||
|
|
||||||
from manila.common import constants as const
|
from manila.common import constants as const
|
||||||
from manila import exception
|
from manila import exception
|
||||||
from manila.i18n import _, _LI, _LE
|
from manila.i18n import _, _LI, _LE, _LW
|
||||||
|
|
||||||
LOG = log.getLogger(__name__)
|
LOG = log.getLogger(__name__)
|
||||||
|
|
||||||
@ -121,9 +120,10 @@ class UnityClient(object):
|
|||||||
except storops_ex.UnityResourceNotFoundError:
|
except storops_ex.UnityResourceNotFoundError:
|
||||||
LOG.info(_LI('Filesystem %s is already removed.'), filesystem.name)
|
LOG.info(_LI('Filesystem %s is already removed.'), filesystem.name)
|
||||||
|
|
||||||
def create_nas_server(self, name, sp, pool):
|
def create_nas_server(self, name, sp, pool, tenant=None):
|
||||||
try:
|
try:
|
||||||
return self.system.create_nas_server(name, sp, pool)
|
return self.system.create_nas_server(name, sp, pool,
|
||||||
|
tenant=tenant)
|
||||||
except storops_ex.UnityNasServerNameUsedError:
|
except storops_ex.UnityNasServerNameUsedError:
|
||||||
LOG.info(_LI('Share server %s already exists, ignoring share '
|
LOG.info(_LI('Share server %s already exists, ignoring share '
|
||||||
'server creation.'), name)
|
'server creation.'), name)
|
||||||
@ -137,12 +137,32 @@ class UnityClient(object):
|
|||||||
raise
|
raise
|
||||||
|
|
||||||
def delete_nas_server(self, name, username=None, password=None):
|
def delete_nas_server(self, name, username=None, password=None):
|
||||||
|
tenant = None
|
||||||
try:
|
try:
|
||||||
nas_server = self.get_nas_server(name=name)
|
nas_server = self.get_nas_server(name=name)
|
||||||
|
tenant = nas_server.tenant
|
||||||
nas_server.delete(username=username, password=password)
|
nas_server.delete(username=username, password=password)
|
||||||
except storops_ex.UnityResourceNotFoundError:
|
except storops_ex.UnityResourceNotFoundError:
|
||||||
LOG.info(_LI('NAS server %s not found.'), name)
|
LOG.info(_LI('NAS server %s not found.'), name)
|
||||||
|
|
||||||
|
if tenant is not None:
|
||||||
|
self._delete_tenant(tenant)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _delete_tenant(tenant):
|
||||||
|
if tenant.nas_servers:
|
||||||
|
LOG.debug('There are NAS servers belonging to the tenant %s. ',
|
||||||
|
'Do not delete it.',
|
||||||
|
tenant.get_id())
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
tenant.delete(delete_hosts=True)
|
||||||
|
except storops_ex.UnityException as ex:
|
||||||
|
LOG.warning(_LW('Delete tenant %(tenant)s failed with error: '
|
||||||
|
'%(ex)s. Leave the tenant on the system.'),
|
||||||
|
{'tenant': tenant.get_id(),
|
||||||
|
'ex': ex})
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def create_dns_server(nas_server, domain, dns_ip):
|
def create_dns_server(nas_server, domain, dns_ip):
|
||||||
try:
|
try:
|
||||||
@ -152,12 +172,10 @@ class UnityClient(object):
|
|||||||
'ignoring DNS server creation.'), domain)
|
'ignoring DNS server creation.'), domain)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def create_interface(nas_server, ip_addr, netmask, gateway, ports,
|
def create_interface(nas_server, ip_addr, netmask, gateway, port_id,
|
||||||
vlan_id=None):
|
vlan_id=None):
|
||||||
port_list = list(ports)
|
|
||||||
random.shuffle(port_list)
|
|
||||||
try:
|
try:
|
||||||
nas_server.create_file_interface(port_list[0],
|
nas_server.create_file_interface(port_id,
|
||||||
ip_addr,
|
ip_addr,
|
||||||
netmask=netmask,
|
netmask=netmask,
|
||||||
gateway=gateway,
|
gateway=gateway,
|
||||||
@ -222,9 +240,13 @@ class UnityClient(object):
|
|||||||
def get_pool(self, name=None):
|
def get_pool(self, name=None):
|
||||||
return self.system.get_pool(name=name)
|
return self.system.get_pool(name=name)
|
||||||
|
|
||||||
def get_storage_processor(self, sp_id):
|
def get_storage_processor(self, sp_id=None):
|
||||||
sp = self.system.get_sp(sp_id)
|
sp = self.system.get_sp(sp_id)
|
||||||
return sp if sp.existed else None
|
if sp_id is None:
|
||||||
|
# `sp` is a list of SPA and SPB.
|
||||||
|
return [s for s in sp if s is not None and s.existed]
|
||||||
|
else:
|
||||||
|
return sp if sp.existed else None
|
||||||
|
|
||||||
def cifs_clear_access(self, share_name, white_list=None):
|
def cifs_clear_access(self, share_name, white_list=None):
|
||||||
share = self.system.get_cifs_share(name=share_name)
|
share = self.system.get_cifs_share(name=share_name)
|
||||||
@ -266,14 +288,11 @@ class UnityClient(object):
|
|||||||
LOG.info(_LI('%(host)s access to %(share)s is already removed.'),
|
LOG.info(_LI('%(host)s access to %(share)s is already removed.'),
|
||||||
{'host': host_ip, 'share': share_name})
|
{'host': host_ip, 'share': share_name})
|
||||||
|
|
||||||
def get_ip_ports(self, sp=None):
|
def get_file_ports(self):
|
||||||
ports = self.system.get_ip_port()
|
ports = self.system.get_file_port()
|
||||||
link_up_ports = []
|
link_up_ports = []
|
||||||
for port in ports:
|
for port in ports:
|
||||||
if port.is_link_up and 'eth' in port.id:
|
if port.is_link_up and self._is_external_port(port.id):
|
||||||
if sp and port.sp.id != sp.id:
|
|
||||||
continue
|
|
||||||
|
|
||||||
link_up_ports.append(port)
|
link_up_ports.append(port)
|
||||||
|
|
||||||
return link_up_ports
|
return link_up_ports
|
||||||
@ -294,3 +313,31 @@ class UnityClient(object):
|
|||||||
LOG.debug('The size of the file system %(id)s is %(size)s '
|
LOG.debug('The size of the file system %(id)s is %(size)s '
|
||||||
'bytes.', {'id': fs.get_id(), 'size': size})
|
'bytes.', {'id': fs.get_id(), 'size': size})
|
||||||
return size
|
return size
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _is_external_port(port_id):
|
||||||
|
return 'eth' in port_id or '_la' in port_id
|
||||||
|
|
||||||
|
def get_tenant(self, name, vlan_id):
|
||||||
|
if not vlan_id:
|
||||||
|
# Do not create vlan for flat network
|
||||||
|
return None
|
||||||
|
|
||||||
|
tenant = None
|
||||||
|
try:
|
||||||
|
tenant_name = "vlan_%(vlan_id)s_%(name)s" % {'vlan_id': vlan_id,
|
||||||
|
'name': name}
|
||||||
|
tenant = self.system.create_tenant(tenant_name, vlans=[vlan_id])
|
||||||
|
except (storops_ex.UnityVLANUsedByOtherTenantError,
|
||||||
|
storops_ex.UnityTenantNameInUseError,
|
||||||
|
storops_ex.UnityVLANAlreadyHasInterfaceError):
|
||||||
|
with excutils.save_and_reraise_exception() as exc:
|
||||||
|
tenant = self.system.get_tenant_use_vlan(vlan_id)
|
||||||
|
if tenant is not None:
|
||||||
|
LOG.debug("The VLAN %s is already added into a tenant. "
|
||||||
|
"Use the existing VLAN tenant.", vlan_id)
|
||||||
|
exc.reraise = False
|
||||||
|
except storops_ex.SystemAPINotSupported:
|
||||||
|
LOG.info(_LI("This system doesn't support tenant."))
|
||||||
|
|
||||||
|
return tenant
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
"""Unity backend for the EMC Manila driver."""
|
"""Unity backend for the EMC Manila driver."""
|
||||||
|
import random
|
||||||
|
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
from oslo_log import log
|
from oslo_log import log
|
||||||
@ -34,7 +35,7 @@ from manila.share.drivers.dell_emc.plugins.vnx import utils as emc_utils
|
|||||||
from manila.share import utils as share_utils
|
from manila.share import utils as share_utils
|
||||||
from manila import utils
|
from manila import utils
|
||||||
|
|
||||||
VERSION = "1.0.0"
|
VERSION = "2.0.0"
|
||||||
|
|
||||||
LOG = log.getLogger(__name__)
|
LOG = log.getLogger(__name__)
|
||||||
SUPPORTED_NETWORK_TYPES = (None, 'flat', 'vlan')
|
SUPPORTED_NETWORK_TYPES = (None, 'flat', 'vlan')
|
||||||
@ -43,9 +44,6 @@ UNITY_OPTS = [
|
|||||||
cfg.StrOpt('unity_server_meta_pool',
|
cfg.StrOpt('unity_server_meta_pool',
|
||||||
deprecated_name='emc_nas_server_pool',
|
deprecated_name='emc_nas_server_pool',
|
||||||
help='Pool to persist the meta-data of NAS server.'),
|
help='Pool to persist the meta-data of NAS server.'),
|
||||||
cfg.StrOpt('unity_server_container',
|
|
||||||
deprecated_name='emc_nas_server_container',
|
|
||||||
help='Storage processor to host the NAS server.'),
|
|
||||||
cfg.ListOpt('unity_share_data_pools',
|
cfg.ListOpt('unity_share_data_pools',
|
||||||
deprecated_name='emc_nas_pool_names',
|
deprecated_name='emc_nas_pool_names',
|
||||||
help='Comma separated list of pools that can be used to '
|
help='Comma separated list of pools that can be used to '
|
||||||
@ -54,7 +52,12 @@ UNITY_OPTS = [
|
|||||||
deprecated_name='emc_interface_ports',
|
deprecated_name='emc_interface_ports',
|
||||||
help='Comma separated list of ports that can be used for '
|
help='Comma separated list of ports that can be used for '
|
||||||
'share server interfaces. Members of the list '
|
'share server interfaces. Members of the list '
|
||||||
'can be Unix-style glob expressions.')
|
'can be Unix-style glob expressions.'),
|
||||||
|
cfg.StrOpt('emc_nas_server_container',
|
||||||
|
deprecated_for_removal=True,
|
||||||
|
deprecated_reason='Unity driver supports nas server auto load '
|
||||||
|
'balance.',
|
||||||
|
help='Storage processor to host the NAS server. Obsolete.'),
|
||||||
]
|
]
|
||||||
|
|
||||||
CONF = cfg.CONF
|
CONF = cfg.CONF
|
||||||
@ -76,11 +79,10 @@ class UnityStorageConnection(driver.StorageConnection):
|
|||||||
|
|
||||||
self.client = None
|
self.client = None
|
||||||
self.pool_set = None
|
self.pool_set = None
|
||||||
self.port_set = None
|
|
||||||
self.nas_server_pool = None
|
self.nas_server_pool = None
|
||||||
self.storage_processor = None
|
|
||||||
self.reserved_percentage = None
|
self.reserved_percentage = None
|
||||||
self.max_over_subscription_ratio = None
|
self.max_over_subscription_ratio = None
|
||||||
|
self.port_ids_conf = None
|
||||||
|
|
||||||
# props from super class.
|
# props from super class.
|
||||||
self.driver_handles_share_servers = True
|
self.driver_handles_share_servers = True
|
||||||
@ -91,7 +93,6 @@ class UnityStorageConnection(driver.StorageConnection):
|
|||||||
storage_ip = config.emc_nas_server
|
storage_ip = config.emc_nas_server
|
||||||
username = config.emc_nas_login
|
username = config.emc_nas_login
|
||||||
password = config.emc_nas_password
|
password = config.emc_nas_password
|
||||||
sp_name = config.unity_server_container
|
|
||||||
self.client = client.UnityClient(storage_ip, username, password)
|
self.client = client.UnityClient(storage_ip, username, password)
|
||||||
|
|
||||||
pool_conf = config.safe_get('unity_share_data_pools')
|
pool_conf = config.safe_get('unity_share_data_pools')
|
||||||
@ -104,16 +105,45 @@ class UnityStorageConnection(driver.StorageConnection):
|
|||||||
|
|
||||||
self.max_over_subscription_ratio = config.safe_get(
|
self.max_over_subscription_ratio = config.safe_get(
|
||||||
'max_over_subscription_ratio')
|
'max_over_subscription_ratio')
|
||||||
|
self.port_ids_conf = config.safe_get('unity_ethernet_ports')
|
||||||
self._config_sp(sp_name)
|
self.validate_port_configuration(self.port_ids_conf)
|
||||||
|
|
||||||
port_conf = config.safe_get('unity_ethernet_ports')
|
|
||||||
self.port_set = self._get_managed_ports(
|
|
||||||
port_conf, self.storage_processor)
|
|
||||||
|
|
||||||
pool_name = config.unity_server_meta_pool
|
pool_name = config.unity_server_meta_pool
|
||||||
self._config_pool(pool_name)
|
self._config_pool(pool_name)
|
||||||
|
|
||||||
|
def validate_port_configuration(self, port_ids_conf):
|
||||||
|
"""Initializes the SP and ports based on the port option."""
|
||||||
|
|
||||||
|
ports = self.client.get_file_ports()
|
||||||
|
|
||||||
|
sp_ports_map, unmanaged_port_ids = unity_utils.match_ports(
|
||||||
|
ports, port_ids_conf)
|
||||||
|
|
||||||
|
if not sp_ports_map:
|
||||||
|
msg = (_("All the specified storage ports to be managed "
|
||||||
|
"do not exist. Please check your configuration "
|
||||||
|
"unity_ethernet_ports in manila.conf. "
|
||||||
|
"The available ports in the backend are %s.") %
|
||||||
|
",".join([port.get_id() for port in ports]))
|
||||||
|
raise exception.BadConfigurationException(reason=msg)
|
||||||
|
|
||||||
|
if unmanaged_port_ids:
|
||||||
|
LOG.info(_LI("The following specified ports are not managed by "
|
||||||
|
"the backend: %(unmanaged)s. This host will only "
|
||||||
|
"manage the storage ports: %(exist)s"),
|
||||||
|
{'unmanaged': ",".join(unmanaged_port_ids),
|
||||||
|
'exist': ",".join(map(",".join,
|
||||||
|
sp_ports_map.values()))})
|
||||||
|
else:
|
||||||
|
LOG.debug("Ports: %s will be managed.",
|
||||||
|
",".join(map(",".join, sp_ports_map.values())))
|
||||||
|
|
||||||
|
if len(sp_ports_map) == 1:
|
||||||
|
LOG.info(_LI("Only ports of %s are configured. Configure ports "
|
||||||
|
"of both SPA and SPB to use both of the SPs."),
|
||||||
|
list(sp_ports_map)[0])
|
||||||
|
|
||||||
|
return sp_ports_map
|
||||||
|
|
||||||
def check_for_setup_error(self):
|
def check_for_setup_error(self):
|
||||||
"""Check for setup error."""
|
"""Check for setup error."""
|
||||||
|
|
||||||
@ -365,13 +395,25 @@ class UnityStorageConnection(driver.StorageConnection):
|
|||||||
def setup_server(self, network_info, metadata=None):
|
def setup_server(self, network_info, metadata=None):
|
||||||
"""Set up and configures share server with given network parameters."""
|
"""Set up and configures share server with given network parameters."""
|
||||||
server_name = network_info['server_id']
|
server_name = network_info['server_id']
|
||||||
nas_server = self.client.create_nas_server(server_name,
|
segmentation_id = network_info['segmentation_id']
|
||||||
self.storage_processor,
|
network = self.validate_network(network_info)
|
||||||
self.nas_server_pool)
|
mtu = network['mtu']
|
||||||
|
tenant = self.client.get_tenant(network_info['server_id'],
|
||||||
|
segmentation_id)
|
||||||
|
|
||||||
|
sp_ports_map = unity_utils.find_ports_by_mtu(
|
||||||
|
self.client.get_file_ports(),
|
||||||
|
self.port_ids_conf, mtu)
|
||||||
|
|
||||||
|
sp = self._choose_sp(sp_ports_map)
|
||||||
|
nas_server = self.client.create_nas_server(server_name,
|
||||||
|
sp,
|
||||||
|
self.nas_server_pool,
|
||||||
|
tenant=tenant)
|
||||||
|
sp = nas_server.home_sp
|
||||||
|
port_id = self._choose_port(sp_ports_map, sp)
|
||||||
try:
|
try:
|
||||||
for network in network_info['network_allocations']:
|
self._create_network_interface(nas_server, network, port_id)
|
||||||
self._create_network_interface(nas_server, network)
|
|
||||||
|
|
||||||
self._handle_security_services(
|
self._handle_security_services(
|
||||||
nas_server, network_info['security_services'])
|
nas_server, network_info['security_services'])
|
||||||
@ -425,35 +467,46 @@ class UnityStorageConnection(driver.StorageConnection):
|
|||||||
LOG.exception(message)
|
LOG.exception(message)
|
||||||
raise exception.BadConfigurationException(reason=message)
|
raise exception.BadConfigurationException(reason=message)
|
||||||
|
|
||||||
def _config_sp(self, sp_name):
|
@staticmethod
|
||||||
self.storage_processor = self.client.get_storage_processor(
|
def validate_network(network_info):
|
||||||
sp_name.lower())
|
network = network_info['network_allocations'][0]
|
||||||
if self.storage_processor is None:
|
|
||||||
message = (_("The storage processor %s does not exist or "
|
|
||||||
"is unavailable. Please reconfigure it in "
|
|
||||||
"manila.conf.") % sp_name)
|
|
||||||
LOG.error(message)
|
|
||||||
raise exception.BadConfigurationException(reason=message)
|
|
||||||
|
|
||||||
def _create_network_interface(self, nas_server, network):
|
|
||||||
ip_addr = network['ip_address']
|
|
||||||
netmask = utils.cidr_to_netmask(network['cidr'])
|
|
||||||
gateway = network['gateway']
|
|
||||||
vlan_id = network['segmentation_id']
|
|
||||||
if network['network_type'] not in SUPPORTED_NETWORK_TYPES:
|
if network['network_type'] not in SUPPORTED_NETWORK_TYPES:
|
||||||
msg = _('The specified network type %s is unsupported by '
|
msg = _('The specified network type %s is unsupported by '
|
||||||
'the EMC Unity driver')
|
'the EMC Unity driver')
|
||||||
raise exception.NetworkBadConfigurationException(
|
raise exception.NetworkBadConfigurationException(
|
||||||
reason=msg % network['network_type'])
|
reason=msg % network['network_type'])
|
||||||
|
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']
|
||||||
# Create the interfaces on NAS server
|
# Create the interfaces on NAS server
|
||||||
self.client.create_interface(nas_server,
|
self.client.create_interface(nas_server,
|
||||||
ip_addr,
|
ip_addr,
|
||||||
netmask,
|
netmask,
|
||||||
gateway,
|
gateway,
|
||||||
ports=self.port_set,
|
port_id=port_id,
|
||||||
vlan_id=vlan_id)
|
vlan_id=vlan_id)
|
||||||
|
|
||||||
|
def _choose_sp(self, sp_ports_map):
|
||||||
|
sp = None
|
||||||
|
if len(sp_ports_map.keys()) == 1:
|
||||||
|
# Only one storage processor has usable ports,
|
||||||
|
# create NAS server on that SP.
|
||||||
|
sp = self.client.get_storage_processor(
|
||||||
|
sp_id=list(sp_ports_map.keys())[0])
|
||||||
|
LOG.debug('All the usable ports belong to %s. '
|
||||||
|
'Creating NAS server on this SP without '
|
||||||
|
'load balance.', sp.get_id())
|
||||||
|
return sp
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _choose_port(sp_ports_map, sp):
|
||||||
|
ports = sp_ports_map[sp.get_id()]
|
||||||
|
return random.choice(list(ports))
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _get_cifs_location(file_interfaces, share_name):
|
def _get_cifs_location(file_interfaces, share_name):
|
||||||
return [
|
return [
|
||||||
@ -466,7 +519,7 @@ class UnityStorageConnection(driver.StorageConnection):
|
|||||||
|
|
||||||
def _get_managed_pools(self, pool_conf):
|
def _get_managed_pools(self, pool_conf):
|
||||||
# Get the real pools from the backend storage
|
# Get the real pools from the backend storage
|
||||||
real_pools = set([pool.name for pool in self.client.get_pool()])
|
real_pools = set(pool.name for pool in self.client.get_pool())
|
||||||
|
|
||||||
if not pool_conf:
|
if not pool_conf:
|
||||||
LOG.debug("No storage pool is specified, so all pools in storage "
|
LOG.debug("No storage pool is specified, so all pools in storage "
|
||||||
@ -497,39 +550,6 @@ class UnityStorageConnection(driver.StorageConnection):
|
|||||||
|
|
||||||
return matched_pools
|
return matched_pools
|
||||||
|
|
||||||
def _get_managed_ports(self, port_conf, sp):
|
|
||||||
# Get the real ports from the backend storage
|
|
||||||
real_ports = set([port.id for port in self.client.get_ip_ports(sp)])
|
|
||||||
|
|
||||||
if not port_conf:
|
|
||||||
LOG.debug("No ports are specified, so all ports in storage "
|
|
||||||
"system will be managed.")
|
|
||||||
return real_ports
|
|
||||||
|
|
||||||
matched_ports, unmanaged_ports = unity_utils.do_match(real_ports,
|
|
||||||
port_conf)
|
|
||||||
|
|
||||||
if not matched_ports:
|
|
||||||
msg = (_("All the specified storage ports to be managed "
|
|
||||||
"do not exist. Please check your configuration "
|
|
||||||
"unity_ethernet_ports in manila.conf. "
|
|
||||||
"The available ports in the backend are %s") %
|
|
||||||
",".join(real_ports))
|
|
||||||
raise exception.BadConfigurationException(reason=msg)
|
|
||||||
|
|
||||||
if unmanaged_ports:
|
|
||||||
LOG.info(_LI("The following specified ports "
|
|
||||||
"are not managed by the backend: "
|
|
||||||
"%(un_managed)s. This host will only manage "
|
|
||||||
"the storage ports: %(exist)s"),
|
|
||||||
{'un_managed': ",".join(unmanaged_ports),
|
|
||||||
'exist': ",".join(matched_ports)})
|
|
||||||
else:
|
|
||||||
LOG.debug("Ports: %s will be managed.",
|
|
||||||
",".join(matched_ports))
|
|
||||||
|
|
||||||
return matched_ports
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _get_nfs_location(file_interfaces, share_name):
|
def _get_nfs_location(file_interfaces, share_name):
|
||||||
return [
|
return [
|
||||||
|
@ -16,6 +16,9 @@
|
|||||||
|
|
||||||
from oslo_utils import fnmatch
|
from oslo_utils import fnmatch
|
||||||
|
|
||||||
|
from manila import exception
|
||||||
|
from manila.i18n import _
|
||||||
|
|
||||||
|
|
||||||
def do_match(full, matcher_list):
|
def do_match(full, matcher_list):
|
||||||
matched = set()
|
matched = set()
|
||||||
@ -32,3 +35,41 @@ def do_match(full, matcher_list):
|
|||||||
if fnmatch.fnmatchcase(item, matcher):
|
if fnmatch.fnmatchcase(item, matcher):
|
||||||
matched.add(item)
|
matched.add(item)
|
||||||
return matched, full - matched
|
return matched, full - matched
|
||||||
|
|
||||||
|
|
||||||
|
def match_ports(ports_list, port_ids_conf):
|
||||||
|
"""Filters the port in `ports_list` with the port id in `port_ids_conf`.
|
||||||
|
|
||||||
|
A tuple of (`sp_ports_map`, `unmanaged_port_ids`) is returned, in which
|
||||||
|
`sp_ports_map` is a dict whose key is SPA or SPB, value is the matched port
|
||||||
|
id set, `unmanaged_port_ids` is the un-matched port id set.
|
||||||
|
"""
|
||||||
|
patterns = (set('*') if port_ids_conf is None
|
||||||
|
else set(item.strip() for item in port_ids_conf
|
||||||
|
if item.strip()))
|
||||||
|
if not patterns:
|
||||||
|
patterns = set('*')
|
||||||
|
|
||||||
|
sp_ports_map = {}
|
||||||
|
unmanaged_port_ids = set()
|
||||||
|
for port in ports_list:
|
||||||
|
port_id = port.get_id()
|
||||||
|
for pattern in patterns:
|
||||||
|
if fnmatch.fnmatchcase(port_id, pattern):
|
||||||
|
sp_id = port.parent_storage_processor.get_id()
|
||||||
|
ports_set = sp_ports_map.setdefault(sp_id, set())
|
||||||
|
ports_set.add(port_id)
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
unmanaged_port_ids.add(port_id)
|
||||||
|
return sp_ports_map, unmanaged_port_ids
|
||||||
|
|
||||||
|
|
||||||
|
def find_ports_by_mtu(all_ports, port_ids_conf, mtu):
|
||||||
|
valid_ports = list(filter(lambda p: p.mtu == mtu, all_ports))
|
||||||
|
managed_port_map, unmatched = match_ports(valid_ports, port_ids_conf)
|
||||||
|
if not managed_port_map:
|
||||||
|
msg = (_('None of the configured port %(conf)s matches the mtu '
|
||||||
|
'%{mtu}s.') % {'conf': port_ids_conf, 'mtu': mtu})
|
||||||
|
raise exception.ShareBackendException(msg=msg)
|
||||||
|
return managed_port_map
|
||||||
|
@ -68,3 +68,19 @@ class UnityHostNotFoundException(UnityException):
|
|||||||
|
|
||||||
class UnityNothingToModifyError(UnityException):
|
class UnityNothingToModifyError(UnityException):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class UnityTenantNameInUseError(UnityException):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class UnityVLANUsedByOtherTenantError(UnityException):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class SystemAPINotSupported(UnityException):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class UnityVLANAlreadyHasInterfaceError(UnityException):
|
||||||
|
pass
|
||||||
|
@ -7,12 +7,14 @@ network_allocations:
|
|||||||
segmentation_id: null
|
segmentation_id: null
|
||||||
gateway: '192.168.1.1'
|
gateway: '192.168.1.1'
|
||||||
network_type: flat
|
network_type: flat
|
||||||
|
mtu: 1500
|
||||||
- id: '0cf87de7-5c65-4036-8b6a-e8176c356958'
|
- id: '0cf87de7-5c65-4036-8b6a-e8176c356958'
|
||||||
ip_address: 'fake_ip_addr_2'
|
ip_address: 'fake_ip_addr_2'
|
||||||
cidr: '192.168.1.0/24'
|
cidr: '192.168.1.0/24'
|
||||||
segmentation_id: null
|
segmentation_id: null
|
||||||
gateway: '192.168.1.1'
|
gateway: '192.168.1.1'
|
||||||
network_type: flat
|
network_type: flat
|
||||||
|
mtu: 1500
|
||||||
|
|
||||||
network_allocations_vlan:
|
network_allocations_vlan:
|
||||||
_type: 'network_allocations'
|
_type: 'network_allocations'
|
||||||
@ -23,12 +25,14 @@ network_allocations_vlan:
|
|||||||
segmentation_id: 160
|
segmentation_id: 160
|
||||||
gateway: '192.168.1.1'
|
gateway: '192.168.1.1'
|
||||||
network_type: vlan
|
network_type: vlan
|
||||||
|
mtu: 1500
|
||||||
- id: '0cf87de7-5c65-4036-8b6a-e8176c356958'
|
- id: '0cf87de7-5c65-4036-8b6a-e8176c356958'
|
||||||
ip_address: 'fake_ip_addr_2'
|
ip_address: 'fake_ip_addr_2'
|
||||||
cidr: '192.168.1.0/24'
|
cidr: '192.168.1.0/24'
|
||||||
segmentation_id: 160
|
segmentation_id: 160
|
||||||
gateway: '192.168.1.1'
|
gateway: '192.168.1.1'
|
||||||
network_type: vlan
|
network_type: vlan
|
||||||
|
mtu: 1500
|
||||||
|
|
||||||
network_allocations_vxlan:
|
network_allocations_vxlan:
|
||||||
_type: 'network_allocations'
|
_type: 'network_allocations'
|
||||||
@ -39,6 +43,7 @@ network_allocations_vxlan:
|
|||||||
segmentation_id: 123
|
segmentation_id: 123
|
||||||
gateway: '192.168.1.1'
|
gateway: '192.168.1.1'
|
||||||
network_type: vxlan
|
network_type: vxlan
|
||||||
|
mtu: 1500
|
||||||
|
|
||||||
active_directory:
|
active_directory:
|
||||||
_type: 'security_service'
|
_type: 'security_service'
|
||||||
@ -71,8 +76,10 @@ network_info__flat:
|
|||||||
neutron_net_id: 'e6c96730-2bcf-4ce3-86fa-7cb7740086cb'
|
neutron_net_id: 'e6c96730-2bcf-4ce3-86fa-7cb7740086cb'
|
||||||
ip_version: 4
|
ip_version: 4
|
||||||
id: '232d8218-2743-41d1-832b-4194626e691e'
|
id: '232d8218-2743-41d1-832b-4194626e691e'
|
||||||
|
mtu: 1500
|
||||||
network_allocations: *network_allocations_prop
|
network_allocations: *network_allocations_prop
|
||||||
server_id: '78fd845f-8e7d-487f-bfde-051d83e78103'
|
server_id: '78fd845f-8e7d-487f-bfde-051d83e78103'
|
||||||
|
segmentation_id: 0
|
||||||
security_services: []
|
security_services: []
|
||||||
|
|
||||||
network_info__vlan:
|
network_info__vlan:
|
||||||
@ -81,6 +88,7 @@ network_info__vlan:
|
|||||||
<<: *network_info_flat_prop
|
<<: *network_info_flat_prop
|
||||||
network_type: 'vlan'
|
network_type: 'vlan'
|
||||||
network_allocations: *network_allocations_vlan_prop
|
network_allocations: *network_allocations_vlan_prop
|
||||||
|
segmentation_id: 160
|
||||||
|
|
||||||
network_info__vxlan:
|
network_info__vxlan:
|
||||||
_type: 'network_info'
|
_type: 'network_info'
|
||||||
|
@ -3,12 +3,16 @@ sp_a: &sp_a
|
|||||||
name: 'SPA'
|
name: 'SPA'
|
||||||
id: 'SPA'
|
id: 'SPA'
|
||||||
existed: true
|
existed: true
|
||||||
|
_methods:
|
||||||
|
get_id: 'SPA'
|
||||||
|
|
||||||
sp_b: &sp_b
|
sp_b: &sp_b
|
||||||
_properties:
|
_properties:
|
||||||
name: 'SPB'
|
name: 'SPB'
|
||||||
id: 'SPB'
|
id: 'SPB'
|
||||||
existed: true
|
existed: true
|
||||||
|
_methods:
|
||||||
|
get_id: 'SPB'
|
||||||
|
|
||||||
sp_c: &sp_invalid
|
sp_c: &sp_invalid
|
||||||
_properties:
|
_properties:
|
||||||
@ -28,6 +32,7 @@ nas_server: &nas_server
|
|||||||
name: '78fd845f-8e7d-487f-bfde-051d83e78103'
|
name: '78fd845f-8e7d-487f-bfde-051d83e78103'
|
||||||
file_interface: [*interface_1, *interface_2]
|
file_interface: [*interface_1, *interface_2]
|
||||||
current_sp: *sp_a
|
current_sp: *sp_a
|
||||||
|
home_sp: *sp_a
|
||||||
|
|
||||||
filesystem_base: &filesystem_base
|
filesystem_base: &filesystem_base
|
||||||
_properties: &filesystem_base_prop
|
_properties: &filesystem_base_prop
|
||||||
@ -101,35 +106,65 @@ port_base:
|
|||||||
_properties: &port_base_prop
|
_properties: &port_base_prop
|
||||||
is_link_up: true
|
is_link_up: true
|
||||||
id: 'fake_name'
|
id: 'fake_name'
|
||||||
sp: *sp_a
|
parent_storage_processor: *sp_a
|
||||||
|
|
||||||
port_1: &port_1
|
port_1: &port_1
|
||||||
_properties:
|
_properties:
|
||||||
<<: *port_base_prop
|
<<: *port_base_prop
|
||||||
is_link_up: true
|
is_link_up: true
|
||||||
id: 'spa_eth1'
|
id: 'spa_eth1'
|
||||||
sp: *sp_a
|
parent_storage_processor: *sp_a
|
||||||
|
_methods:
|
||||||
|
get_id: 'spa_eth1'
|
||||||
|
|
||||||
port_2: &port_2
|
port_2: &port_2
|
||||||
_properties:
|
_properties:
|
||||||
<<: *port_base_prop
|
<<: *port_base_prop
|
||||||
is_link_up: true
|
is_link_up: true
|
||||||
id: 'spa_eth2'
|
id: 'spa_eth2'
|
||||||
sp: *sp_a
|
parent_storage_processor: *sp_a
|
||||||
|
_methods:
|
||||||
|
get_id: 'spa_eth2'
|
||||||
|
|
||||||
port_3: &port_internal_port
|
port_3: &port_internal_port
|
||||||
_properties:
|
_properties:
|
||||||
<<: *port_base_prop
|
<<: *port_base_prop
|
||||||
is_link_up: true
|
is_link_up: true
|
||||||
id: 'internal_port'
|
id: 'internal_port'
|
||||||
sp: *sp_a
|
parent_storage_processor: *sp_a
|
||||||
|
_methods:
|
||||||
|
get_id: 'internal_port'
|
||||||
|
|
||||||
|
port_4: &port_4
|
||||||
|
_properties:
|
||||||
|
<<: *port_base_prop
|
||||||
|
is_link_up: true
|
||||||
|
id: 'spb_eth1'
|
||||||
|
parent_storage_processor: *sp_b
|
||||||
|
_methods:
|
||||||
|
get_id: 'spb_eth1'
|
||||||
|
|
||||||
|
la_port: &la_port
|
||||||
|
_properties:
|
||||||
|
is_link_up: true
|
||||||
|
id: 'spa_la_4'
|
||||||
|
parent_storage_processor: *sp_a
|
||||||
|
_methods:
|
||||||
|
get_id: 'spa_la_4'
|
||||||
|
|
||||||
|
tenant_1: &tenant_1
|
||||||
|
_properties:
|
||||||
|
id: "tenant_1"
|
||||||
|
name: "Tenant1"
|
||||||
|
uuid: "173ca6c3-5952-427d-82a6-df88f49e3926"
|
||||||
|
vlans: [2]
|
||||||
|
|
||||||
unity_base: &unity_base
|
unity_base: &unity_base
|
||||||
_methods: &unity_base_method
|
_methods: &unity_base_method
|
||||||
get_sp: *sp_a
|
get_sp: *sp_a
|
||||||
get_pool:
|
get_pool:
|
||||||
_side_effect: [[*pool_1, *pool_2, *nas_server_pool], *nas_server_pool]
|
_side_effect: [[*pool_1, *pool_2, *nas_server_pool], *nas_server_pool]
|
||||||
get_ip_port: [*port_1, *port_2]
|
get_file_port: [*port_1, *port_2]
|
||||||
|
|
||||||
test_connect: &test_connect
|
test_connect: &test_connect
|
||||||
unity: *unity_base
|
unity: *unity_base
|
||||||
@ -159,8 +194,7 @@ test_create_nfs_share:
|
|||||||
_methods:
|
_methods:
|
||||||
<<: *unity_base_method
|
<<: *unity_base_method
|
||||||
get_pool:
|
get_pool:
|
||||||
_side_effect: [[*pool_1, *pool_2, *nas_server_pool],
|
_side_effect: [*pool__test_create_nfs_share]
|
||||||
*nas_server_pool, *pool__test_create_nfs_share]
|
|
||||||
get_nas_server: *nas_server
|
get_nas_server: *nas_server
|
||||||
|
|
||||||
test_create_cifs_share:
|
test_create_cifs_share:
|
||||||
@ -189,8 +223,7 @@ test_create_cifs_share:
|
|||||||
_methods:
|
_methods:
|
||||||
<<: *unity_base_method
|
<<: *unity_base_method
|
||||||
get_pool:
|
get_pool:
|
||||||
_side_effect: [[*pool_1, *pool_2, *nas_server_pool], *nas_server_pool,
|
_side_effect: [*pool__test_create_cifs_share]
|
||||||
*pool__test_create_cifs_share]
|
|
||||||
get_nas_server: *nas_server
|
get_nas_server: *nas_server
|
||||||
|
|
||||||
test_create_share_with_invalid_share_server:
|
test_create_share_with_invalid_share_server:
|
||||||
@ -203,8 +236,7 @@ test_create_share_with_invalid_share_server:
|
|||||||
_methods:
|
_methods:
|
||||||
<<: *unity_base_method
|
<<: *unity_base_method
|
||||||
get_pool:
|
get_pool:
|
||||||
_side_effect: [[*pool_1, *pool_2, *nas_server_pool], *nas_server_pool,
|
_side_effect: [*pool__test_create_share_with_invalid_share_server]
|
||||||
*pool__test_create_share_with_invalid_share_server]
|
|
||||||
get_nas_server:
|
get_nas_server:
|
||||||
_raise:
|
_raise:
|
||||||
UnityResourceNotFoundError: 'Failed to get NAS server.'
|
UnityResourceNotFoundError: 'Failed to get NAS server.'
|
||||||
@ -460,14 +492,14 @@ test_update_share_stats:
|
|||||||
_methods:
|
_methods:
|
||||||
<<: *unity_base_method
|
<<: *unity_base_method
|
||||||
get_pool:
|
get_pool:
|
||||||
_side_effect: [[*pool_1, *pool_2], *pool_1, [*pool_1, *pool_2]]
|
_side_effect: [[*pool_1, *pool_2]]
|
||||||
|
|
||||||
test_update_share_stats__nonexistent_pools:
|
test_update_share_stats__nonexistent_pools:
|
||||||
unity:
|
unity:
|
||||||
_methods:
|
_methods:
|
||||||
<<: *unity_base_method
|
<<: *unity_base_method
|
||||||
get_pool:
|
get_pool:
|
||||||
_side_effect: [[*pool_1, *pool_2], *pool_1, []]
|
_side_effect: [[]]
|
||||||
|
|
||||||
test_get_pool:
|
test_get_pool:
|
||||||
filesystem: &filesystem__test_get_pool
|
filesystem: &filesystem__test_get_pool
|
||||||
@ -492,7 +524,10 @@ test_setup_server: &test_setup_server
|
|||||||
_properties:
|
_properties:
|
||||||
<<: *nas_server_prop
|
<<: *nas_server_prop
|
||||||
existed: false
|
existed: false
|
||||||
|
home_sp: *sp_a
|
||||||
|
ip_port: &ip_port
|
||||||
|
_methods:
|
||||||
|
set_mtu:
|
||||||
nas_server_2: &nas_server_2__test_setup_server
|
nas_server_2: &nas_server_2__test_setup_server
|
||||||
_properties:
|
_properties:
|
||||||
<<: *nas_server_prop
|
<<: *nas_server_prop
|
||||||
@ -505,6 +540,7 @@ test_setup_server: &test_setup_server
|
|||||||
<<: *unity_base_method
|
<<: *unity_base_method
|
||||||
get_nas_server: *nas_server_1__test_setup_server
|
get_nas_server: *nas_server_1__test_setup_server
|
||||||
create_nas_server: *nas_server_2__test_setup_server
|
create_nas_server: *nas_server_2__test_setup_server
|
||||||
|
get_ip_port: *ip_port
|
||||||
|
|
||||||
test_setup_server__vlan_network:
|
test_setup_server__vlan_network:
|
||||||
<<: *test_setup_server
|
<<: *test_setup_server
|
||||||
@ -521,6 +557,20 @@ test_setup_server__vlan_network:
|
|||||||
_methods:
|
_methods:
|
||||||
<<: *unity_method__test_setup_server
|
<<: *unity_method__test_setup_server
|
||||||
get_nas_server: *nas_server__test_setup_server_flat_network
|
get_nas_server: *nas_server__test_setup_server_flat_network
|
||||||
|
create_tenant: *tenant_1
|
||||||
|
|
||||||
|
test_setup_server__vxlan_network:
|
||||||
|
<<: *test_setup_server
|
||||||
|
nas_server_2: &nas_server_2__test_setup_server__vxlan_network
|
||||||
|
_properties:
|
||||||
|
<<: *nas_server_prop
|
||||||
|
_methods:
|
||||||
|
delete:
|
||||||
|
|
||||||
|
unity:
|
||||||
|
_methods:
|
||||||
|
<<: *unity_method__test_setup_server
|
||||||
|
get_nas_server: *nas_server_2__test_setup_server__vxlan_network
|
||||||
|
|
||||||
test_setup_server__active_directory:
|
test_setup_server__active_directory:
|
||||||
<<: *test_setup_server
|
<<: *test_setup_server
|
||||||
@ -537,6 +587,7 @@ test_setup_server__active_directory:
|
|||||||
_methods: &unity_method__test_setup_server__active_directory
|
_methods: &unity_method__test_setup_server__active_directory
|
||||||
<<: *unity_method__test_setup_server
|
<<: *unity_method__test_setup_server
|
||||||
create_nas_server: *nas_server_2__test_setup_server__active_directory
|
create_nas_server: *nas_server_2__test_setup_server__active_directory
|
||||||
|
create_tenant: *tenant_1
|
||||||
|
|
||||||
test_setup_server__kerberos: *test_setup_server
|
test_setup_server__kerberos: *test_setup_server
|
||||||
|
|
||||||
@ -550,6 +601,7 @@ test_setup_server__throw_exception:
|
|||||||
nas_server_2: &nas_server_2__test_setup_server__throw_exception
|
nas_server_2: &nas_server_2__test_setup_server__throw_exception
|
||||||
_properties:
|
_properties:
|
||||||
<<: *nas_server_prop
|
<<: *nas_server_prop
|
||||||
|
tenant:
|
||||||
_methods:
|
_methods:
|
||||||
create_file_interface:
|
create_file_interface:
|
||||||
create_dns_server:
|
create_dns_server:
|
||||||
@ -564,11 +616,18 @@ test_setup_server__throw_exception:
|
|||||||
<<: *unity_method__test_setup_server
|
<<: *unity_method__test_setup_server
|
||||||
get_nas_server: *nas_server_2__test_setup_server__throw_exception
|
get_nas_server: *nas_server_2__test_setup_server__throw_exception
|
||||||
create_nas_server: *nas_server_2__test_setup_server__throw_exception
|
create_nas_server: *nas_server_2__test_setup_server__throw_exception
|
||||||
|
create_tenant: *tenant_1
|
||||||
|
|
||||||
test_teardown_server:
|
test_teardown_server:
|
||||||
|
tenant:
|
||||||
|
_properties:
|
||||||
|
nas_servers: []
|
||||||
|
_methods:
|
||||||
|
delete:
|
||||||
nas_server: &nas_server__test_teardown_server
|
nas_server: &nas_server__test_teardown_server
|
||||||
_properties:
|
_properties:
|
||||||
<<: *nas_server_prop
|
<<: *nas_server_prop
|
||||||
|
tenant:
|
||||||
_methods:
|
_methods:
|
||||||
delete:
|
delete:
|
||||||
|
|
||||||
@ -585,14 +644,15 @@ test__get_managed_pools: &test__get_managed_pools
|
|||||||
|
|
||||||
test__get_managed_pools__invalid_pool_configuration: *test__get_managed_pools
|
test__get_managed_pools__invalid_pool_configuration: *test__get_managed_pools
|
||||||
|
|
||||||
test__get_managed_ports: &test__get_managed_ports
|
test_validate_port_configuration: &test_validate_port_configuration
|
||||||
unity:
|
unity:
|
||||||
_methods:
|
_methods:
|
||||||
<<: *unity_base_method
|
<<: *unity_base_method
|
||||||
get_pool: [*pool_1, *pool_2, *nas_server_pool]
|
get_file_port: [*port_1, *port_2, *port_internal_port, *port_4, *la_port]
|
||||||
get_ip_port: [*port_1, *port_2, *port_internal_port]
|
|
||||||
|
|
||||||
test__get_managed_pools__invalid_port_configuration: *test__get_managed_ports
|
test_validate_port_configuration_exception: *test_validate_port_configuration
|
||||||
|
|
||||||
|
test__get_managed_pools__invalid_port_configuration: *test_validate_port_configuration
|
||||||
|
|
||||||
|
|
||||||
test_create_cifs_share_from_snapshot:
|
test_create_cifs_share_from_snapshot:
|
||||||
@ -878,6 +938,7 @@ test_delete_nas_server__nonexistent_expt:
|
|||||||
nas_server: &nas_server__test_delete_nas_server__nonexistent_expt
|
nas_server: &nas_server__test_delete_nas_server__nonexistent_expt
|
||||||
_properties:
|
_properties:
|
||||||
<<: *nas_server_prop
|
<<: *nas_server_prop
|
||||||
|
tenant:
|
||||||
_methods:
|
_methods:
|
||||||
delete:
|
delete:
|
||||||
_raise:
|
_raise:
|
||||||
@ -980,3 +1041,44 @@ test_extend_filesystem:
|
|||||||
extend:
|
extend:
|
||||||
_raise:
|
_raise:
|
||||||
UnityNothingToModifyError:
|
UnityNothingToModifyError:
|
||||||
|
|
||||||
|
test_get_tenant:
|
||||||
|
unity:
|
||||||
|
_methods:
|
||||||
|
create_tenant: *tenant_1
|
||||||
|
|
||||||
|
test_get_tenant_preexist:
|
||||||
|
unity:
|
||||||
|
_methods:
|
||||||
|
create_tenant:
|
||||||
|
_raise:
|
||||||
|
UnityVLANUsedByOtherTenantError:
|
||||||
|
get_tenant_use_vlan: *tenant_1
|
||||||
|
|
||||||
|
test_get_tenant_name_inuse_but_vlan_not_used:
|
||||||
|
unity:
|
||||||
|
_methods:
|
||||||
|
create_tenant:
|
||||||
|
_raise:
|
||||||
|
UnityTenantNameInUseError:
|
||||||
|
get_tenant_use_vlan:
|
||||||
|
|
||||||
|
test_get_tenant_for_vlan_already_has_interfaces:
|
||||||
|
unity:
|
||||||
|
_methods:
|
||||||
|
create_tenant:
|
||||||
|
_raise:
|
||||||
|
UnityVLANAlreadyHasInterfaceError:
|
||||||
|
get_tenant_use_vlan: *tenant_1
|
||||||
|
|
||||||
|
test_get_file_ports:
|
||||||
|
link_down_port: &down_port
|
||||||
|
_properties:
|
||||||
|
<<: *port_base_prop
|
||||||
|
is_link_up: false
|
||||||
|
id: 'down_port'
|
||||||
|
_methods:
|
||||||
|
get_id: 'down_port'
|
||||||
|
unity:
|
||||||
|
_methods:
|
||||||
|
get_file_port: [*port_1, *port_internal_port, *down_port, *la_port]
|
||||||
|
@ -87,7 +87,6 @@ class FakeEMCShareDriver(object):
|
|||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.configuration = conf.Configuration(None)
|
self.configuration = conf.Configuration(None)
|
||||||
self.configuration.emc_share_backend = 'unity'
|
self.configuration.emc_share_backend = 'unity'
|
||||||
self.configuration.unity_server_container = 'SPA'
|
|
||||||
self.configuration.emc_nas_server = '192.168.1.1'
|
self.configuration.emc_nas_server = '192.168.1.1'
|
||||||
self.configuration.emc_nas_login = 'fake_user'
|
self.configuration.emc_nas_login = 'fake_user'
|
||||||
self.configuration.emc_nas_password = 'fake_password'
|
self.configuration.emc_nas_password = 'fake_password'
|
||||||
@ -218,7 +217,7 @@ class StorageMethodMock(mock.Mock):
|
|||||||
super(StorageMethodMock, self).__init__(
|
super(StorageMethodMock, self).__init__(
|
||||||
name=name,
|
name=name,
|
||||||
side_effect=_build_mock_object(side_effect))
|
side_effect=_build_mock_object(side_effect))
|
||||||
elif return_value:
|
elif return_value is not None:
|
||||||
super(StorageMethodMock, self).__init__(
|
super(StorageMethodMock, self).__init__(
|
||||||
name=name,
|
name=name,
|
||||||
return_value=_build_mock_object(return_value))
|
return_value=_build_mock_object(return_value))
|
||||||
@ -322,6 +321,20 @@ def patch_connection_init(func):
|
|||||||
return connection_decorator
|
return connection_decorator
|
||||||
|
|
||||||
|
|
||||||
|
def do_connection_connect(conn, res):
|
||||||
|
conn.config = None
|
||||||
|
conn.client = client.UnityClient(host='fake_host',
|
||||||
|
username='fake_user',
|
||||||
|
password='fake_passwd')
|
||||||
|
conn.pool_conf = ['pool_1', 'pool_2']
|
||||||
|
conn.pool_set = set(['pool_1', 'pool_2'])
|
||||||
|
conn.reserved_percentage = 0
|
||||||
|
conn.max_over_subscription_ratio = 20
|
||||||
|
conn.port_set = set(['spa_eth1', 'spa_eth2'])
|
||||||
|
conn.nas_server_pool = StorageObjectMock(res['nas_server_pool'])
|
||||||
|
conn.storage_processor = StorageObjectMock(res['sp_a'])
|
||||||
|
|
||||||
|
|
||||||
def patch_connection(func):
|
def patch_connection(func):
|
||||||
def connection_decorator(cls, *args, **kwargs):
|
def connection_decorator(cls, *args, **kwargs):
|
||||||
storage_res = {}
|
storage_res = {}
|
||||||
@ -329,10 +342,11 @@ def patch_connection(func):
|
|||||||
storage_res = (
|
storage_res = (
|
||||||
STORAGE_RES_MAPPING[cls.__class__.__name__][func.__name__])
|
STORAGE_RES_MAPPING[cls.__class__.__name__][func.__name__])
|
||||||
with utils.patch_system as patched_system:
|
with utils.patch_system as patched_system:
|
||||||
|
conn = connection.UnityStorageConnection(LOG)
|
||||||
if 'unity' in storage_res:
|
if 'unity' in storage_res:
|
||||||
patched_system.return_value = storage_res['unity']
|
patched_system.return_value = storage_res['unity']
|
||||||
conn = connection.UnityStorageConnection(LOG)
|
do_connection_connect(
|
||||||
conn.connect(FakeEMCShareDriver(), None)
|
conn, STORAGE_RES_MAPPING[cls.__class__.__name__])
|
||||||
return func(cls, conn, *args, **kwargs)
|
return func(cls, conn, *args, **kwargs)
|
||||||
|
|
||||||
return connection_decorator
|
return connection_decorator
|
||||||
|
@ -18,6 +18,7 @@ from oslo_utils import units
|
|||||||
|
|
||||||
from manila import exception
|
from manila import exception
|
||||||
from manila import test
|
from manila import test
|
||||||
|
from manila.tests.share.drivers.dell_emc.plugins.unity import fake_exceptions
|
||||||
from manila.tests.share.drivers.dell_emc.plugins.unity import res_mock
|
from manila.tests.share.drivers.dell_emc.plugins.unity import res_mock
|
||||||
|
|
||||||
|
|
||||||
@ -114,11 +115,9 @@ class TestClient(test.TestCase):
|
|||||||
@res_mock.patch_client
|
@res_mock.patch_client
|
||||||
def test_create_interface__existed_expt(self, client, mocked_input):
|
def test_create_interface__existed_expt(self, client, mocked_input):
|
||||||
nas_server = mocked_input['nas_server']
|
nas_server = mocked_input['nas_server']
|
||||||
port_set = ('fake_port',)
|
|
||||||
|
|
||||||
self.assertRaises(exception.IPAddressInUse, client.create_interface,
|
self.assertRaises(exception.IPAddressInUse, client.create_interface,
|
||||||
nas_server, 'fake_ip_addr', 'fake_mask',
|
nas_server, 'fake_ip_addr', 'fake_mask',
|
||||||
'fake_gateway', ports=port_set)
|
'fake_gateway', port_id='fake_port_id')
|
||||||
|
|
||||||
@res_mock.mock_client_input
|
@res_mock.mock_client_input
|
||||||
@res_mock.patch_client
|
@res_mock.patch_client
|
||||||
@ -187,3 +186,33 @@ class TestClient(test.TestCase):
|
|||||||
size = client.extend_filesystem(fs, 5)
|
size = client.extend_filesystem(fs, 5)
|
||||||
|
|
||||||
self.assertEqual(5 * units.Gi, size)
|
self.assertEqual(5 * units.Gi, size)
|
||||||
|
|
||||||
|
@res_mock.patch_client
|
||||||
|
def test_get_file_ports(self, client):
|
||||||
|
ports = client.get_file_ports()
|
||||||
|
self.assertEqual(2, len(ports))
|
||||||
|
|
||||||
|
@res_mock.patch_client
|
||||||
|
def test_get_tenant(self, client):
|
||||||
|
tenant = client.get_tenant('test', 5)
|
||||||
|
self.assertEqual('tenant_1', tenant.id)
|
||||||
|
|
||||||
|
@res_mock.patch_client
|
||||||
|
def test_get_tenant_preexist(self, client):
|
||||||
|
tenant = client.get_tenant('test', 6)
|
||||||
|
self.assertEqual('tenant_1', tenant.id)
|
||||||
|
|
||||||
|
@res_mock.patch_client
|
||||||
|
def test_get_tenant_name_inuse_but_vlan_not_used(self, client):
|
||||||
|
self.assertRaises(fake_exceptions.UnityTenantNameInUseError,
|
||||||
|
client.get_tenant, 'test', 7)
|
||||||
|
|
||||||
|
@res_mock.patch_client
|
||||||
|
def test_get_tenant_for_vlan_0(self, client):
|
||||||
|
tenant = client.get_tenant('tenant', 0)
|
||||||
|
self.assertIsNone(tenant)
|
||||||
|
|
||||||
|
@res_mock.patch_client
|
||||||
|
def test_get_tenant_for_vlan_already_has_interfaces(self, client):
|
||||||
|
tenant = client.get_tenant('tenant', 3)
|
||||||
|
self.assertEqual('tenant_1', tenant.id)
|
||||||
|
@ -15,7 +15,6 @@
|
|||||||
|
|
||||||
import copy
|
import copy
|
||||||
import ddt
|
import ddt
|
||||||
import mock
|
|
||||||
from oslo_utils import units
|
from oslo_utils import units
|
||||||
import six
|
import six
|
||||||
|
|
||||||
@ -23,6 +22,7 @@ from manila import exception
|
|||||||
from manila import test
|
from manila import test
|
||||||
from manila.tests.share.drivers.dell_emc.plugins.unity import fake_exceptions
|
from manila.tests.share.drivers.dell_emc.plugins.unity import fake_exceptions
|
||||||
from manila.tests.share.drivers.dell_emc.plugins.unity import res_mock
|
from manila.tests.share.drivers.dell_emc.plugins.unity import res_mock
|
||||||
|
from manila.tests.share.drivers.dell_emc.plugins.unity import utils
|
||||||
|
|
||||||
|
|
||||||
@ddt.ddt
|
@ddt.ddt
|
||||||
@ -37,12 +37,6 @@ class TestConnection(test.TestCase):
|
|||||||
def test_connect(self, connection):
|
def test_connect(self, connection):
|
||||||
connection.connect(res_mock.FakeEMCShareDriver(), None)
|
connection.connect(res_mock.FakeEMCShareDriver(), None)
|
||||||
|
|
||||||
@res_mock.patch_connection_init
|
|
||||||
def test_connect__invalid_sp_configuration(self, connection):
|
|
||||||
self.assertRaises(exception.BadConfigurationException,
|
|
||||||
connection.connect,
|
|
||||||
res_mock.FakeEMCShareDriver(), None)
|
|
||||||
|
|
||||||
@res_mock.patch_connection
|
@res_mock.patch_connection
|
||||||
def test_connect__invalid_pool_configuration(self, connection):
|
def test_connect__invalid_pool_configuration(self, connection):
|
||||||
f = connection.client.system.get_pool
|
f = connection.client.system.get_pool
|
||||||
@ -256,7 +250,6 @@ class TestConnection(test.TestCase):
|
|||||||
@res_mock.patch_connection
|
@res_mock.patch_connection
|
||||||
def test_update_share_stats(self, connection):
|
def test_update_share_stats(self, connection):
|
||||||
stat_dict = copy.deepcopy(res_mock.STATS)
|
stat_dict = copy.deepcopy(res_mock.STATS)
|
||||||
|
|
||||||
connection.update_share_stats(stat_dict)
|
connection.update_share_stats(stat_dict)
|
||||||
self.assertEqual(5, len(stat_dict))
|
self.assertEqual(5, len(stat_dict))
|
||||||
pool = stat_dict['pools'][0]
|
pool = stat_dict['pools'][0]
|
||||||
@ -285,46 +278,71 @@ class TestConnection(test.TestCase):
|
|||||||
|
|
||||||
connection.get_pool(share)
|
connection.get_pool(share)
|
||||||
|
|
||||||
|
@utils.patch_find_ports_by_mtu
|
||||||
@res_mock.mock_manila_input
|
@res_mock.mock_manila_input
|
||||||
@res_mock.patch_connection
|
@res_mock.patch_connection
|
||||||
def test_setup_server(self, connection, mocked_input):
|
def test_setup_server(self, connection, mocked_input, find_ports):
|
||||||
|
find_ports.return_value = {'SPA': {'spa_eth1'}}
|
||||||
network_info = mocked_input['network_info__flat']
|
network_info = mocked_input['network_info__flat']
|
||||||
|
server_info = connection.setup_server(network_info)
|
||||||
|
self.assertEqual(
|
||||||
|
{'share_server_name':
|
||||||
|
'78fd845f-8e7d-487f-bfde-051d83e78103'},
|
||||||
|
server_info)
|
||||||
|
self.assertIsNone(connection.client.system.create_nas_server.
|
||||||
|
call_args[1]['tenant'])
|
||||||
|
|
||||||
connection.setup_server(network_info)
|
@utils.patch_find_ports_by_mtu
|
||||||
|
|
||||||
@res_mock.mock_manila_input
|
@res_mock.mock_manila_input
|
||||||
@res_mock.patch_connection
|
@res_mock.patch_connection
|
||||||
def test_setup_server__vlan_network(self, connection, mocked_input):
|
def test_setup_server__vlan_network(self, connection, mocked_input,
|
||||||
|
find_ports):
|
||||||
|
find_ports.return_value = {'SPA': {'spa_eth1'}}
|
||||||
network_info = mocked_input['network_info__vlan']
|
network_info = mocked_input['network_info__vlan']
|
||||||
|
|
||||||
connection.setup_server(network_info)
|
connection.setup_server(network_info)
|
||||||
|
self.assertEqual('tenant_1',
|
||||||
|
connection.client.system.create_nas_server
|
||||||
|
.call_args[1]['tenant'].id)
|
||||||
|
|
||||||
|
@utils.patch_find_ports_by_mtu
|
||||||
@res_mock.mock_manila_input
|
@res_mock.mock_manila_input
|
||||||
@res_mock.patch_connection
|
@res_mock.patch_connection
|
||||||
def test_setup_server__vxlan_network(self, connection, mocked_input):
|
def test_setup_server__vxlan_network(self, connection, mocked_input,
|
||||||
|
find_ports):
|
||||||
|
find_ports.return_value = {'SPA': {'spa_eth1'}}
|
||||||
network_info = mocked_input['network_info__vxlan']
|
network_info = mocked_input['network_info__vxlan']
|
||||||
|
|
||||||
self.assertRaises(exception.NetworkBadConfigurationException,
|
self.assertRaises(exception.NetworkBadConfigurationException,
|
||||||
connection.setup_server,
|
connection.setup_server,
|
||||||
network_info)
|
network_info)
|
||||||
|
|
||||||
|
@utils.patch_find_ports_by_mtu
|
||||||
@res_mock.mock_manila_input
|
@res_mock.mock_manila_input
|
||||||
@res_mock.patch_connection
|
@res_mock.patch_connection
|
||||||
def test_setup_server__active_directory(self, connection, mocked_input):
|
def test_setup_server__active_directory(self, connection, mocked_input,
|
||||||
|
find_ports):
|
||||||
|
find_ports.return_value = {'SPA': {'spa_eth1'}}
|
||||||
network_info = mocked_input['network_info__active_directory']
|
network_info = mocked_input['network_info__active_directory']
|
||||||
|
|
||||||
connection.setup_server(network_info)
|
connection.setup_server(network_info)
|
||||||
|
|
||||||
|
@utils.patch_find_ports_by_mtu
|
||||||
@res_mock.mock_manila_input
|
@res_mock.mock_manila_input
|
||||||
@res_mock.patch_connection
|
@res_mock.patch_connection
|
||||||
def test_setup_server__kerberos(self, connection, mocked_input):
|
def test_setup_server__kerberos(self, connection, mocked_input,
|
||||||
|
find_ports):
|
||||||
|
find_ports.return_value = {'SPA': {'spa_eth1'}}
|
||||||
network_info = mocked_input['network_info__kerberos']
|
network_info = mocked_input['network_info__kerberos']
|
||||||
|
|
||||||
connection.setup_server(network_info)
|
connection.setup_server(network_info)
|
||||||
|
|
||||||
|
@utils.patch_find_ports_by_mtu
|
||||||
@res_mock.mock_manila_input
|
@res_mock.mock_manila_input
|
||||||
@res_mock.patch_connection
|
@res_mock.patch_connection
|
||||||
def test_setup_server__throw_exception(self, connection, mocked_input):
|
def test_setup_server__throw_exception(self, connection, mocked_input,
|
||||||
|
find_ports):
|
||||||
|
find_ports.return_value = {'SPA': {'spa_eth1'}}
|
||||||
network_info = mocked_input['network_info__flat']
|
network_info = mocked_input['network_info__flat']
|
||||||
|
|
||||||
self.assertRaises(fake_exceptions.UnityException,
|
self.assertRaises(fake_exceptions.UnityException,
|
||||||
@ -385,40 +403,20 @@ class TestConnection(test.TestCase):
|
|||||||
connection._get_managed_pools,
|
connection._get_managed_pools,
|
||||||
configured_pools)
|
configured_pools)
|
||||||
|
|
||||||
@ddt.data({'configured_ports': None,
|
|
||||||
'matched_ports': {'spa_eth1', 'spa_eth2'}},
|
|
||||||
{'configured_ports': ['*'],
|
|
||||||
'matched_ports': {'spa_eth1', 'spa_eth2'}},
|
|
||||||
{'configured_ports': ['spa_*'],
|
|
||||||
'matched_ports': {'spa_eth1', 'spa_eth2'}},
|
|
||||||
{'configured_ports': ['*_eth1'],
|
|
||||||
'matched_ports': {'spa_eth1'}},
|
|
||||||
{'configured_ports': ['spa_eth1'],
|
|
||||||
'matched_ports': {'spa_eth1'}},
|
|
||||||
{'configured_ports': ['spa_eth1', 'spa_eth2'],
|
|
||||||
'matched_ports': {'spa_eth1', 'spa_eth2'}})
|
|
||||||
@res_mock.patch_connection
|
@res_mock.patch_connection
|
||||||
@ddt.unpack
|
def test_validate_port_configuration(self, connection):
|
||||||
def test__get_managed_ports(self, connection, mocked_input):
|
sp_ports_map = connection.validate_port_configuration(['sp*'])
|
||||||
sp = mock.Mock()
|
|
||||||
sp.id = 'SPA'
|
|
||||||
configured_ports = mocked_input['configured_ports']
|
|
||||||
matched_ports = mocked_input['matched_ports']
|
|
||||||
|
|
||||||
ports = connection._get_managed_ports(configured_ports, sp)
|
self.assertEqual({'spa_eth1', 'spa_eth2', 'spa_la_4'},
|
||||||
|
sp_ports_map['SPA'])
|
||||||
self.assertEqual(matched_ports, ports)
|
self.assertEqual({'spb_eth1'}, sp_ports_map['SPB'])
|
||||||
|
|
||||||
@res_mock.patch_connection
|
@res_mock.patch_connection
|
||||||
def test__get_managed_ports__invalid_port_configuration(self, connection):
|
def test_validate_port_configuration_exception(self, connection):
|
||||||
configured_ports = 'fake_port'
|
|
||||||
sp = mock.Mock()
|
|
||||||
sp.id = 'SPA'
|
|
||||||
|
|
||||||
self.assertRaises(exception.BadConfigurationException,
|
self.assertRaises(exception.BadConfigurationException,
|
||||||
connection._get_managed_ports,
|
connection.validate_port_configuration,
|
||||||
configured_ports,
|
['xxxx*'])
|
||||||
sp)
|
|
||||||
|
|
||||||
@res_mock.patch_connection
|
@res_mock.patch_connection
|
||||||
def test__get_pool_name_from_host__no_pool_name(self, connection):
|
def test__get_pool_name_from_host__no_pool_name(self, connection):
|
||||||
|
@ -19,6 +19,40 @@ from manila.share.drivers.dell_emc.plugins.unity import utils
|
|||||||
from manila import test
|
from manila import test
|
||||||
|
|
||||||
|
|
||||||
|
class MockPort(object):
|
||||||
|
def __init__(self, sp_id):
|
||||||
|
self.sp_id = sp_id
|
||||||
|
|
||||||
|
def get_id(self):
|
||||||
|
return self.sp_id
|
||||||
|
|
||||||
|
|
||||||
|
SPA = MockPort('spa')
|
||||||
|
SPB = MockPort('spb')
|
||||||
|
|
||||||
|
|
||||||
|
class MockPort(object):
|
||||||
|
def __init__(self, sp, port_id, mtu):
|
||||||
|
self._sp = sp
|
||||||
|
self.port_id = port_id
|
||||||
|
self.mtu = mtu
|
||||||
|
|
||||||
|
def get_id(self):
|
||||||
|
return self.port_id
|
||||||
|
|
||||||
|
@property
|
||||||
|
def parent_storage_processor(self):
|
||||||
|
return self._sp
|
||||||
|
|
||||||
|
|
||||||
|
SPA_ETH0 = MockPort(SPA, 'spa_eth0', 1500)
|
||||||
|
SPA_ETH1 = MockPort(SPA, 'spa_eth1', 9000)
|
||||||
|
SPB_ETH0 = MockPort(SPB, 'spb_eth0', 1500)
|
||||||
|
SPB_ETH1 = MockPort(SPB, 'spb_eth1', 9000)
|
||||||
|
SPA_LA1 = MockPort(SPA, 'spa_la_1', 1500)
|
||||||
|
SPB_LA1 = MockPort(SPB, 'spb_la_1', 1500)
|
||||||
|
|
||||||
|
|
||||||
@ddt.ddt
|
@ddt.ddt
|
||||||
class TestUtils(test.TestCase):
|
class TestUtils(test.TestCase):
|
||||||
@ddt.data({'matcher': None,
|
@ddt.data({'matcher': None,
|
||||||
@ -48,3 +82,32 @@ class TestUtils(test.TestCase):
|
|||||||
matched, not_matched = utils.do_match(full, matcher)
|
matched, not_matched = utils.do_match(full, matcher)
|
||||||
self.assertEqual(expected_matched, matched)
|
self.assertEqual(expected_matched, matched)
|
||||||
self.assertEqual(expected_not_matched, not_matched)
|
self.assertEqual(expected_not_matched, not_matched)
|
||||||
|
|
||||||
|
@ddt.data({'ports': [SPA_ETH0, SPB_ETH0],
|
||||||
|
'ids_conf': None,
|
||||||
|
'port_map': {'spa': {'spa_eth0'}, 'spb': {'spb_eth0'}},
|
||||||
|
'unmanaged': set()},
|
||||||
|
{'ports': [SPA_ETH0, SPB_ETH0],
|
||||||
|
'ids_conf': [' '],
|
||||||
|
'port_map': {'spa': {'spa_eth0'}, 'spb': {'spb_eth0'}},
|
||||||
|
'unmanaged': set()},
|
||||||
|
{'ports': [SPA_ETH0, SPB_ETH0, SPA_ETH1],
|
||||||
|
'ids_conf': ['spa*'],
|
||||||
|
'port_map': {'spa': {'spa_eth0', 'spa_eth1'}},
|
||||||
|
'unmanaged': {'spb_eth0'}},
|
||||||
|
)
|
||||||
|
@ddt.unpack
|
||||||
|
def test_match_ports(self, ports, ids_conf, port_map, unmanaged):
|
||||||
|
sp_ports_map, unmanaged_port_ids = utils.match_ports(ports,
|
||||||
|
ids_conf)
|
||||||
|
self.assertEqual(port_map, sp_ports_map)
|
||||||
|
self.assertEqual(unmanaged, unmanaged_port_ids)
|
||||||
|
|
||||||
|
def test_find_ports_by_mtu(self):
|
||||||
|
all_ports = [SPA_ETH0, SPB_ETH0, SPA_ETH1, SPB_ETH1, SPA_LA1,
|
||||||
|
SPB_LA1]
|
||||||
|
port_ids_conf = '*'
|
||||||
|
port_map = utils.find_ports_by_mtu(all_ports, port_ids_conf, 1500)
|
||||||
|
self.assertEqual({'spa': {'spa_eth0', 'spa_la_1'},
|
||||||
|
'spb': {'spb_eth0', 'spb_la_1'}},
|
||||||
|
port_map)
|
||||||
|
@ -30,3 +30,6 @@ def load_yaml(file_name):
|
|||||||
res = yaml.load(f)
|
res = yaml.load(f)
|
||||||
LOG.debug('Loaded yaml mock objects from %s.', yaml_file)
|
LOG.debug('Loaded yaml mock objects from %s.', yaml_file)
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
patch_find_ports_by_mtu = mock.patch('manila.share.drivers.dell_emc.plugins.'
|
||||||
|
'unity.utils.find_ports_by_mtu')
|
||||||
|
@ -4,7 +4,6 @@ upgrade:
|
|||||||
emc_nas_pool_names with unity_share_data_pools,
|
emc_nas_pool_names with unity_share_data_pools,
|
||||||
emc_nas_server_pool with unity_server_meta_pool,
|
emc_nas_server_pool with unity_server_meta_pool,
|
||||||
emc_interface_ports with unity_ethernet_ports,
|
emc_interface_ports with unity_ethernet_ports,
|
||||||
emc_nas_server_container with unity_server_container.
|
|
||||||
- For Dell EMC VNX Manila driver, replaced
|
- For Dell EMC VNX Manila driver, replaced
|
||||||
emc_nas_pool_names with vnx_share_data_pools,
|
emc_nas_pool_names with vnx_share_data_pools,
|
||||||
emc_interface_ports with vnx_ethernet_ports,
|
emc_interface_ports with vnx_ethernet_ports,
|
||||||
|
@ -0,0 +1,10 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- Dell EMC Unity driver deprecated the option `emc_nas_server_container`.
|
||||||
|
The driver will choose storage processor automatically to load balance
|
||||||
|
the nas servers.
|
||||||
|
- Dell EMC Unity driver is enhanced to use different tenant in Unity for
|
||||||
|
each vlan. Thus the nas server in different vlan could have isolated IP
|
||||||
|
address space.
|
||||||
|
- Dell EMC Unity driver is enhanced to select the appropriate port on the
|
||||||
|
system to create interfaces based on the network MTU.
|
Loading…
Reference in New Issue
Block a user