[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:
Tina Tang 2016-09-22 13:53:13 +08:00 committed by Tina
parent 75e685d40d
commit c90515d64e
13 changed files with 507 additions and 157 deletions

View File

@ -12,11 +12,10 @@
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import random
import six
from oslo_log import log
from oslo_utils import excutils
from oslo_utils import importutils
from oslo_utils import units
@ -27,7 +26,7 @@ if storops:
from manila.common import constants as const
from manila import exception
from manila.i18n import _, _LI, _LE
from manila.i18n import _, _LI, _LE, _LW
LOG = log.getLogger(__name__)
@ -121,9 +120,10 @@ class UnityClient(object):
except storops_ex.UnityResourceNotFoundError:
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:
return self.system.create_nas_server(name, sp, pool)
return self.system.create_nas_server(name, sp, pool,
tenant=tenant)
except storops_ex.UnityNasServerNameUsedError:
LOG.info(_LI('Share server %s already exists, ignoring share '
'server creation.'), name)
@ -137,12 +137,32 @@ class UnityClient(object):
raise
def delete_nas_server(self, name, username=None, password=None):
tenant = None
try:
nas_server = self.get_nas_server(name=name)
tenant = nas_server.tenant
nas_server.delete(username=username, password=password)
except storops_ex.UnityResourceNotFoundError:
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
def create_dns_server(nas_server, domain, dns_ip):
try:
@ -152,12 +172,10 @@ class UnityClient(object):
'ignoring DNS server creation.'), domain)
@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):
port_list = list(ports)
random.shuffle(port_list)
try:
nas_server.create_file_interface(port_list[0],
nas_server.create_file_interface(port_id,
ip_addr,
netmask=netmask,
gateway=gateway,
@ -222,9 +240,13 @@ class UnityClient(object):
def get_pool(self, name=None):
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)
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):
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.'),
{'host': host_ip, 'share': share_name})
def get_ip_ports(self, sp=None):
ports = self.system.get_ip_port()
def get_file_ports(self):
ports = self.system.get_file_port()
link_up_ports = []
for port in ports:
if port.is_link_up and 'eth' in port.id:
if sp and port.sp.id != sp.id:
continue
if port.is_link_up and self._is_external_port(port.id):
link_up_ports.append(port)
return link_up_ports
@ -294,3 +313,31 @@ class UnityClient(object):
LOG.debug('The size of the file system %(id)s is %(size)s '
'bytes.', {'id': fs.get_id(), 'size': 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

View File

@ -13,6 +13,7 @@
# License for the specific language governing permissions and limitations
# under the License.
"""Unity backend for the EMC Manila driver."""
import random
from oslo_config import cfg
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 import utils
VERSION = "1.0.0"
VERSION = "2.0.0"
LOG = log.getLogger(__name__)
SUPPORTED_NETWORK_TYPES = (None, 'flat', 'vlan')
@ -43,9 +44,6 @@ UNITY_OPTS = [
cfg.StrOpt('unity_server_meta_pool',
deprecated_name='emc_nas_server_pool',
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',
deprecated_name='emc_nas_pool_names',
help='Comma separated list of pools that can be used to '
@ -54,7 +52,12 @@ UNITY_OPTS = [
deprecated_name='emc_interface_ports',
help='Comma separated list of ports that can be used for '
'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
@ -76,11 +79,10 @@ class UnityStorageConnection(driver.StorageConnection):
self.client = None
self.pool_set = None
self.port_set = None
self.nas_server_pool = None
self.storage_processor = None
self.reserved_percentage = None
self.max_over_subscription_ratio = None
self.port_ids_conf = None
# props from super class.
self.driver_handles_share_servers = True
@ -91,7 +93,6 @@ class UnityStorageConnection(driver.StorageConnection):
storage_ip = config.emc_nas_server
username = config.emc_nas_login
password = config.emc_nas_password
sp_name = config.unity_server_container
self.client = client.UnityClient(storage_ip, username, password)
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(
'max_over_subscription_ratio')
self._config_sp(sp_name)
port_conf = config.safe_get('unity_ethernet_ports')
self.port_set = self._get_managed_ports(
port_conf, self.storage_processor)
self.port_ids_conf = config.safe_get('unity_ethernet_ports')
self.validate_port_configuration(self.port_ids_conf)
pool_name = config.unity_server_meta_pool
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):
"""Check for setup error."""
@ -365,13 +395,25 @@ class UnityStorageConnection(driver.StorageConnection):
def setup_server(self, network_info, metadata=None):
"""Set up and configures share server with given network parameters."""
server_name = network_info['server_id']
nas_server = self.client.create_nas_server(server_name,
self.storage_processor,
self.nas_server_pool)
segmentation_id = network_info['segmentation_id']
network = self.validate_network(network_info)
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:
for network in network_info['network_allocations']:
self._create_network_interface(nas_server, network)
self._create_network_interface(nas_server, network, port_id)
self._handle_security_services(
nas_server, network_info['security_services'])
@ -425,35 +467,46 @@ class UnityStorageConnection(driver.StorageConnection):
LOG.exception(message)
raise exception.BadConfigurationException(reason=message)
def _config_sp(self, sp_name):
self.storage_processor = self.client.get_storage_processor(
sp_name.lower())
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']
@staticmethod
def validate_network(network_info):
network = network_info['network_allocations'][0]
if network['network_type'] not in SUPPORTED_NETWORK_TYPES:
msg = _('The specified network type %s is unsupported by '
'the EMC Unity driver')
raise exception.NetworkBadConfigurationException(
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
self.client.create_interface(nas_server,
ip_addr,
netmask,
gateway,
ports=self.port_set,
port_id=port_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
def _get_cifs_location(file_interfaces, share_name):
return [
@ -466,7 +519,7 @@ class UnityStorageConnection(driver.StorageConnection):
def _get_managed_pools(self, pool_conf):
# 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:
LOG.debug("No storage pool is specified, so all pools in storage "
@ -497,39 +550,6 @@ class UnityStorageConnection(driver.StorageConnection):
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
def _get_nfs_location(file_interfaces, share_name):
return [

View File

@ -16,6 +16,9 @@
from oslo_utils import fnmatch
from manila import exception
from manila.i18n import _
def do_match(full, matcher_list):
matched = set()
@ -32,3 +35,41 @@ def do_match(full, matcher_list):
if fnmatch.fnmatchcase(item, matcher):
matched.add(item)
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

View File

@ -68,3 +68,19 @@ class UnityHostNotFoundException(UnityException):
class UnityNothingToModifyError(UnityException):
pass
class UnityTenantNameInUseError(UnityException):
pass
class UnityVLANUsedByOtherTenantError(UnityException):
pass
class SystemAPINotSupported(UnityException):
pass
class UnityVLANAlreadyHasInterfaceError(UnityException):
pass

View File

@ -7,12 +7,14 @@ network_allocations:
segmentation_id: null
gateway: '192.168.1.1'
network_type: flat
mtu: 1500
- id: '0cf87de7-5c65-4036-8b6a-e8176c356958'
ip_address: 'fake_ip_addr_2'
cidr: '192.168.1.0/24'
segmentation_id: null
gateway: '192.168.1.1'
network_type: flat
mtu: 1500
network_allocations_vlan:
_type: 'network_allocations'
@ -23,12 +25,14 @@ network_allocations_vlan:
segmentation_id: 160
gateway: '192.168.1.1'
network_type: vlan
mtu: 1500
- id: '0cf87de7-5c65-4036-8b6a-e8176c356958'
ip_address: 'fake_ip_addr_2'
cidr: '192.168.1.0/24'
segmentation_id: 160
gateway: '192.168.1.1'
network_type: vlan
mtu: 1500
network_allocations_vxlan:
_type: 'network_allocations'
@ -39,6 +43,7 @@ network_allocations_vxlan:
segmentation_id: 123
gateway: '192.168.1.1'
network_type: vxlan
mtu: 1500
active_directory:
_type: 'security_service'
@ -71,8 +76,10 @@ network_info__flat:
neutron_net_id: 'e6c96730-2bcf-4ce3-86fa-7cb7740086cb'
ip_version: 4
id: '232d8218-2743-41d1-832b-4194626e691e'
mtu: 1500
network_allocations: *network_allocations_prop
server_id: '78fd845f-8e7d-487f-bfde-051d83e78103'
segmentation_id: 0
security_services: []
network_info__vlan:
@ -81,6 +88,7 @@ network_info__vlan:
<<: *network_info_flat_prop
network_type: 'vlan'
network_allocations: *network_allocations_vlan_prop
segmentation_id: 160
network_info__vxlan:
_type: 'network_info'

View File

@ -3,12 +3,16 @@ sp_a: &sp_a
name: 'SPA'
id: 'SPA'
existed: true
_methods:
get_id: 'SPA'
sp_b: &sp_b
_properties:
name: 'SPB'
id: 'SPB'
existed: true
_methods:
get_id: 'SPB'
sp_c: &sp_invalid
_properties:
@ -28,6 +32,7 @@ nas_server: &nas_server
name: '78fd845f-8e7d-487f-bfde-051d83e78103'
file_interface: [*interface_1, *interface_2]
current_sp: *sp_a
home_sp: *sp_a
filesystem_base: &filesystem_base
_properties: &filesystem_base_prop
@ -101,35 +106,65 @@ port_base:
_properties: &port_base_prop
is_link_up: true
id: 'fake_name'
sp: *sp_a
parent_storage_processor: *sp_a
port_1: &port_1
_properties:
<<: *port_base_prop
is_link_up: true
id: 'spa_eth1'
sp: *sp_a
parent_storage_processor: *sp_a
_methods:
get_id: 'spa_eth1'
port_2: &port_2
_properties:
<<: *port_base_prop
is_link_up: true
id: 'spa_eth2'
sp: *sp_a
parent_storage_processor: *sp_a
_methods:
get_id: 'spa_eth2'
port_3: &port_internal_port
_properties:
<<: *port_base_prop
is_link_up: true
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
_methods: &unity_base_method
get_sp: *sp_a
get_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
unity: *unity_base
@ -159,8 +194,7 @@ test_create_nfs_share:
_methods:
<<: *unity_base_method
get_pool:
_side_effect: [[*pool_1, *pool_2, *nas_server_pool],
*nas_server_pool, *pool__test_create_nfs_share]
_side_effect: [*pool__test_create_nfs_share]
get_nas_server: *nas_server
test_create_cifs_share:
@ -189,8 +223,7 @@ test_create_cifs_share:
_methods:
<<: *unity_base_method
get_pool:
_side_effect: [[*pool_1, *pool_2, *nas_server_pool], *nas_server_pool,
*pool__test_create_cifs_share]
_side_effect: [*pool__test_create_cifs_share]
get_nas_server: *nas_server
test_create_share_with_invalid_share_server:
@ -203,8 +236,7 @@ test_create_share_with_invalid_share_server:
_methods:
<<: *unity_base_method
get_pool:
_side_effect: [[*pool_1, *pool_2, *nas_server_pool], *nas_server_pool,
*pool__test_create_share_with_invalid_share_server]
_side_effect: [*pool__test_create_share_with_invalid_share_server]
get_nas_server:
_raise:
UnityResourceNotFoundError: 'Failed to get NAS server.'
@ -460,14 +492,14 @@ test_update_share_stats:
_methods:
<<: *unity_base_method
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:
unity:
_methods:
<<: *unity_base_method
get_pool:
_side_effect: [[*pool_1, *pool_2], *pool_1, []]
_side_effect: [[]]
test_get_pool:
filesystem: &filesystem__test_get_pool
@ -492,7 +524,10 @@ test_setup_server: &test_setup_server
_properties:
<<: *nas_server_prop
existed: false
home_sp: *sp_a
ip_port: &ip_port
_methods:
set_mtu:
nas_server_2: &nas_server_2__test_setup_server
_properties:
<<: *nas_server_prop
@ -505,6 +540,7 @@ test_setup_server: &test_setup_server
<<: *unity_base_method
get_nas_server: *nas_server_1__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
@ -521,6 +557,20 @@ test_setup_server__vlan_network:
_methods:
<<: *unity_method__test_setup_server
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
@ -537,6 +587,7 @@ test_setup_server__active_directory:
_methods: &unity_method__test_setup_server__active_directory
<<: *unity_method__test_setup_server
create_nas_server: *nas_server_2__test_setup_server__active_directory
create_tenant: *tenant_1
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
_properties:
<<: *nas_server_prop
tenant:
_methods:
create_file_interface:
create_dns_server:
@ -564,11 +616,18 @@ test_setup_server__throw_exception:
<<: *unity_method__test_setup_server
get_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:
tenant:
_properties:
nas_servers: []
_methods:
delete:
nas_server: &nas_server__test_teardown_server
_properties:
<<: *nas_server_prop
tenant:
_methods:
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_ports: &test__get_managed_ports
test_validate_port_configuration: &test_validate_port_configuration
unity:
_methods:
<<: *unity_base_method
get_pool: [*pool_1, *pool_2, *nas_server_pool]
get_ip_port: [*port_1, *port_2, *port_internal_port]
get_file_port: [*port_1, *port_2, *port_internal_port, *port_4, *la_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:
@ -878,6 +938,7 @@ test_delete_nas_server__nonexistent_expt:
nas_server: &nas_server__test_delete_nas_server__nonexistent_expt
_properties:
<<: *nas_server_prop
tenant:
_methods:
delete:
_raise:
@ -980,3 +1041,44 @@ test_extend_filesystem:
extend:
_raise:
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]

View File

@ -87,7 +87,6 @@ class FakeEMCShareDriver(object):
def __init__(self):
self.configuration = conf.Configuration(None)
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_login = 'fake_user'
self.configuration.emc_nas_password = 'fake_password'
@ -218,7 +217,7 @@ class StorageMethodMock(mock.Mock):
super(StorageMethodMock, self).__init__(
name=name,
side_effect=_build_mock_object(side_effect))
elif return_value:
elif return_value is not None:
super(StorageMethodMock, self).__init__(
name=name,
return_value=_build_mock_object(return_value))
@ -322,6 +321,20 @@ def patch_connection_init(func):
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 connection_decorator(cls, *args, **kwargs):
storage_res = {}
@ -329,10 +342,11 @@ def patch_connection(func):
storage_res = (
STORAGE_RES_MAPPING[cls.__class__.__name__][func.__name__])
with utils.patch_system as patched_system:
conn = connection.UnityStorageConnection(LOG)
if 'unity' in storage_res:
patched_system.return_value = storage_res['unity']
conn = connection.UnityStorageConnection(LOG)
conn.connect(FakeEMCShareDriver(), None)
do_connection_connect(
conn, STORAGE_RES_MAPPING[cls.__class__.__name__])
return func(cls, conn, *args, **kwargs)
return connection_decorator

View File

@ -18,6 +18,7 @@ from oslo_utils import units
from manila import exception
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
@ -114,11 +115,9 @@ class TestClient(test.TestCase):
@res_mock.patch_client
def test_create_interface__existed_expt(self, client, mocked_input):
nas_server = mocked_input['nas_server']
port_set = ('fake_port',)
self.assertRaises(exception.IPAddressInUse, client.create_interface,
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.patch_client
@ -187,3 +186,33 @@ class TestClient(test.TestCase):
size = client.extend_filesystem(fs, 5)
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)

View File

@ -15,7 +15,6 @@
import copy
import ddt
import mock
from oslo_utils import units
import six
@ -23,6 +22,7 @@ from manila import exception
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 utils
@ddt.ddt
@ -37,12 +37,6 @@ class TestConnection(test.TestCase):
def test_connect(self, connection):
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
def test_connect__invalid_pool_configuration(self, connection):
f = connection.client.system.get_pool
@ -256,7 +250,6 @@ class TestConnection(test.TestCase):
@res_mock.patch_connection
def test_update_share_stats(self, connection):
stat_dict = copy.deepcopy(res_mock.STATS)
connection.update_share_stats(stat_dict)
self.assertEqual(5, len(stat_dict))
pool = stat_dict['pools'][0]
@ -285,46 +278,71 @@ class TestConnection(test.TestCase):
connection.get_pool(share)
@utils.patch_find_ports_by_mtu
@res_mock.mock_manila_input
@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']
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.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']
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.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']
self.assertRaises(exception.NetworkBadConfigurationException,
connection.setup_server,
network_info)
@utils.patch_find_ports_by_mtu
@res_mock.mock_manila_input
@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']
connection.setup_server(network_info)
@utils.patch_find_ports_by_mtu
@res_mock.mock_manila_input
@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']
connection.setup_server(network_info)
@utils.patch_find_ports_by_mtu
@res_mock.mock_manila_input
@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']
self.assertRaises(fake_exceptions.UnityException,
@ -385,40 +403,20 @@ class TestConnection(test.TestCase):
connection._get_managed_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
@ddt.unpack
def test__get_managed_ports(self, connection, mocked_input):
sp = mock.Mock()
sp.id = 'SPA'
configured_ports = mocked_input['configured_ports']
matched_ports = mocked_input['matched_ports']
def test_validate_port_configuration(self, connection):
sp_ports_map = connection.validate_port_configuration(['sp*'])
ports = connection._get_managed_ports(configured_ports, sp)
self.assertEqual(matched_ports, ports)
self.assertEqual({'spa_eth1', 'spa_eth2', 'spa_la_4'},
sp_ports_map['SPA'])
self.assertEqual({'spb_eth1'}, sp_ports_map['SPB'])
@res_mock.patch_connection
def test__get_managed_ports__invalid_port_configuration(self, connection):
configured_ports = 'fake_port'
sp = mock.Mock()
sp.id = 'SPA'
def test_validate_port_configuration_exception(self, connection):
self.assertRaises(exception.BadConfigurationException,
connection._get_managed_ports,
configured_ports,
sp)
connection.validate_port_configuration,
['xxxx*'])
@res_mock.patch_connection
def test__get_pool_name_from_host__no_pool_name(self, connection):

View File

@ -19,6 +19,40 @@ from manila.share.drivers.dell_emc.plugins.unity import utils
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
class TestUtils(test.TestCase):
@ddt.data({'matcher': None,
@ -48,3 +82,32 @@ class TestUtils(test.TestCase):
matched, not_matched = utils.do_match(full, matcher)
self.assertEqual(expected_matched, 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)

View File

@ -30,3 +30,6 @@ def load_yaml(file_name):
res = yaml.load(f)
LOG.debug('Loaded yaml mock objects from %s.', yaml_file)
return res
patch_find_ports_by_mtu = mock.patch('manila.share.drivers.dell_emc.plugins.'
'unity.utils.find_ports_by_mtu')

View File

@ -4,7 +4,6 @@ upgrade:
emc_nas_pool_names with unity_share_data_pools,
emc_nas_server_pool with unity_server_meta_pool,
emc_interface_ports with unity_ethernet_ports,
emc_nas_server_container with unity_server_container.
- For Dell EMC VNX Manila driver, replaced
emc_nas_pool_names with vnx_share_data_pools,
emc_interface_ports with vnx_ethernet_ports,

View File

@ -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.