Merge "NetApp cDOT: Add NVE support in Manila"
This commit is contained in:
commit
5f55ccef74
@ -158,12 +158,23 @@ class NaServer(object):
|
|||||||
raise ValueError('Major and minor versions must be integers')
|
raise ValueError('Major and minor versions must be integers')
|
||||||
self._refresh_conn = True
|
self._refresh_conn = True
|
||||||
|
|
||||||
|
def set_system_version(self, system_version):
|
||||||
|
"""Set the ONTAP system version."""
|
||||||
|
self._system_version = system_version
|
||||||
|
self._refresh_conn = True
|
||||||
|
|
||||||
def get_api_version(self):
|
def get_api_version(self):
|
||||||
"""Gets the API version tuple."""
|
"""Gets the API version tuple."""
|
||||||
if hasattr(self, '_api_version'):
|
if hasattr(self, '_api_version'):
|
||||||
return (self._api_major_version, self._api_minor_version)
|
return (self._api_major_version, self._api_minor_version)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def get_system_version(self):
|
||||||
|
"""Gets the ONTAP system version."""
|
||||||
|
if hasattr(self, '_system_version'):
|
||||||
|
return self._system_version
|
||||||
|
return None
|
||||||
|
|
||||||
def set_port(self, port):
|
def set_port(self, port):
|
||||||
"""Set the server communication port."""
|
"""Set the server communication port."""
|
||||||
try:
|
try:
|
||||||
|
@ -49,9 +49,12 @@ class NetAppBaseClient(object):
|
|||||||
return major, minor
|
return major, minor
|
||||||
|
|
||||||
@na_utils.trace
|
@na_utils.trace
|
||||||
def get_system_version(self):
|
def get_system_version(self, cached=True):
|
||||||
"""Gets the current Data ONTAP version."""
|
"""Gets the current Data ONTAP version."""
|
||||||
|
|
||||||
|
if cached:
|
||||||
|
return self.connection.get_system_version()
|
||||||
|
|
||||||
result = self.send_request('system-get-version')
|
result = self.send_request('system-get-version')
|
||||||
|
|
||||||
version_tuple = result.get_child_by_name(
|
version_tuple = result.get_child_by_name(
|
||||||
@ -62,9 +65,9 @@ class NetAppBaseClient(object):
|
|||||||
version = {}
|
version = {}
|
||||||
version['version'] = result.get_child_content('version')
|
version['version'] = result.get_child_content('version')
|
||||||
version['version-tuple'] = (
|
version['version-tuple'] = (
|
||||||
system_version_tuple.get_child_content('generation'),
|
int(system_version_tuple.get_child_content('generation')),
|
||||||
system_version_tuple.get_child_content('major'),
|
int(system_version_tuple.get_child_content('major')),
|
||||||
system_version_tuple.get_child_content('minor'))
|
int(system_version_tuple.get_child_content('minor')))
|
||||||
|
|
||||||
return version
|
return version
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
# Copyright (c) 2014 Alex Meade. All rights reserved.
|
# Copyright (c) 2014 Alex Meade. All rights reserved.
|
||||||
# Copyright (c) 2015 Clinton Knight. All rights reserved.
|
# Copyright (c) 2015 Clinton Knight. All rights reserved.
|
||||||
# Copyright (c) 2015 Tom Barron. All rights reserved.
|
# Copyright (c) 2015 Tom Barron. All rights reserved.
|
||||||
|
# Copyright (c) 2018 Jose Porrua. All rights reserved.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
# not use this file except in compliance with the License. You may obtain
|
# not use this file except in compliance with the License. You may obtain
|
||||||
@ -55,6 +56,8 @@ class NetAppCmodeClient(client_base.NetAppBaseClient):
|
|||||||
self.connection.set_api_version(1, 15)
|
self.connection.set_api_version(1, 15)
|
||||||
(major, minor) = self.get_ontapi_version(cached=False)
|
(major, minor) = self.get_ontapi_version(cached=False)
|
||||||
self.connection.set_api_version(major, minor)
|
self.connection.set_api_version(major, minor)
|
||||||
|
system_version = self.get_system_version(cached=False)
|
||||||
|
self.connection.set_system_version(system_version)
|
||||||
|
|
||||||
self._init_features()
|
self._init_features()
|
||||||
|
|
||||||
@ -66,6 +69,7 @@ class NetAppCmodeClient(client_base.NetAppBaseClient):
|
|||||||
ontapi_1_20 = ontapi_version >= (1, 20)
|
ontapi_1_20 = ontapi_version >= (1, 20)
|
||||||
ontapi_1_2x = (1, 20) <= ontapi_version < (1, 30)
|
ontapi_1_2x = (1, 20) <= ontapi_version < (1, 30)
|
||||||
ontapi_1_30 = ontapi_version >= (1, 30)
|
ontapi_1_30 = ontapi_version >= (1, 30)
|
||||||
|
ontapi_1_110 = ontapi_version >= (1, 110)
|
||||||
|
|
||||||
self.features.add_feature('SNAPMIRROR_V2', supported=ontapi_1_20)
|
self.features.add_feature('SNAPMIRROR_V2', supported=ontapi_1_20)
|
||||||
self.features.add_feature('SYSTEM_METRICS', supported=ontapi_1_2x)
|
self.features.add_feature('SYSTEM_METRICS', supported=ontapi_1_2x)
|
||||||
@ -77,6 +81,7 @@ class NetAppCmodeClient(client_base.NetAppBaseClient):
|
|||||||
self.features.add_feature('CLUSTER_PEER_POLICY', supported=ontapi_1_30)
|
self.features.add_feature('CLUSTER_PEER_POLICY', supported=ontapi_1_30)
|
||||||
self.features.add_feature('ADVANCED_DISK_PARTITIONING',
|
self.features.add_feature('ADVANCED_DISK_PARTITIONING',
|
||||||
supported=ontapi_1_30)
|
supported=ontapi_1_30)
|
||||||
|
self.features.add_feature('FLEXVOL_ENCRYPTION', supported=ontapi_1_110)
|
||||||
|
|
||||||
def _invoke_vserver_api(self, na_element, vserver):
|
def _invoke_vserver_api(self, na_element, vserver):
|
||||||
server = copy.copy(self.connection)
|
server = copy.copy(self.connection)
|
||||||
@ -372,6 +377,31 @@ class NetAppCmodeClient(client_base.NetAppBaseClient):
|
|||||||
else:
|
else:
|
||||||
vserver_client.send_request('cifs-server-delete')
|
vserver_client.send_request('cifs-server-delete')
|
||||||
|
|
||||||
|
@na_utils.trace
|
||||||
|
def is_nve_supported(self):
|
||||||
|
"""Determine whether NVE is supported on this platform and version."""
|
||||||
|
nodes = self.list_cluster_nodes()
|
||||||
|
system_version = self.get_system_version()
|
||||||
|
version = system_version.get('version')
|
||||||
|
version_tuple = system_version.get('version-tuple')
|
||||||
|
|
||||||
|
# NVE requires an ONTAP version >= 9.1. Also, not all platforms
|
||||||
|
# support this feature. NVE is not supported if the version
|
||||||
|
# includes the substring '<1no-DARE>' (no Data At Rest Encryption).
|
||||||
|
if version_tuple >= (9, 1, 0) and "<1no-DARE>" not in version:
|
||||||
|
if nodes is not None:
|
||||||
|
return self.get_security_key_manager_nve_support(nodes[0])
|
||||||
|
else:
|
||||||
|
LOG.debug('Cluster credentials are required in order to '
|
||||||
|
'determine whether NetApp Volume Encryption is '
|
||||||
|
'supported or not on this platform.')
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
LOG.debug('NetApp Volume Encryption is not supported on this '
|
||||||
|
'ONTAP version: %(version)s, %(version_tuple)s. ',
|
||||||
|
{'version': version, 'version_tuple': version_tuple})
|
||||||
|
return False
|
||||||
|
|
||||||
@na_utils.trace
|
@na_utils.trace
|
||||||
def list_cluster_nodes(self):
|
def list_cluster_nodes(self):
|
||||||
"""Get all available cluster nodes."""
|
"""Get all available cluster nodes."""
|
||||||
@ -388,6 +418,25 @@ class NetAppCmodeClient(client_base.NetAppBaseClient):
|
|||||||
return [node_info.get_child_content('node') for node_info
|
return [node_info.get_child_content('node') for node_info
|
||||||
in nodes_info_list.get_children()]
|
in nodes_info_list.get_children()]
|
||||||
|
|
||||||
|
@na_utils.trace
|
||||||
|
def get_security_key_manager_nve_support(self, node):
|
||||||
|
"""Determine whether the cluster platform supports Volume Encryption"""
|
||||||
|
api_args = {'node': node}
|
||||||
|
try:
|
||||||
|
result = self.send_request(
|
||||||
|
'security-key-manager-volume-encryption-supported', api_args)
|
||||||
|
vol_encryption_supported = result.get_child_content(
|
||||||
|
'vol-encryption-supported') or 'false'
|
||||||
|
except netapp_api.NaApiError as e:
|
||||||
|
if (e.code == netapp_api.EAPIERROR and
|
||||||
|
"key manager is not enabled" in e.message):
|
||||||
|
LOG.debug("%s", e.message)
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
raise
|
||||||
|
|
||||||
|
return strutils.bool_from_string(vol_encryption_supported)
|
||||||
|
|
||||||
@na_utils.trace
|
@na_utils.trace
|
||||||
def list_node_data_ports(self, node):
|
def list_node_data_ports(self, node):
|
||||||
ports = self.get_node_data_ports(node)
|
ports = self.get_node_data_ports(node)
|
||||||
@ -1430,8 +1479,8 @@ class NetAppCmodeClient(client_base.NetAppBaseClient):
|
|||||||
language=None, dedup_enabled=False,
|
language=None, dedup_enabled=False,
|
||||||
compression_enabled=False, max_files=None,
|
compression_enabled=False, max_files=None,
|
||||||
snapshot_reserve=None, volume_type='rw',
|
snapshot_reserve=None, volume_type='rw',
|
||||||
qos_policy_group=None, **options):
|
qos_policy_group=None,
|
||||||
|
encrypt=False, **options):
|
||||||
"""Creates a volume."""
|
"""Creates a volume."""
|
||||||
api_args = {
|
api_args = {
|
||||||
'containing-aggr-name': aggregate_name,
|
'containing-aggr-name': aggregate_name,
|
||||||
@ -1452,6 +1501,14 @@ class NetAppCmodeClient(client_base.NetAppBaseClient):
|
|||||||
snapshot_reserve)
|
snapshot_reserve)
|
||||||
if qos_policy_group is not None:
|
if qos_policy_group is not None:
|
||||||
api_args['qos-policy-group-name'] = qos_policy_group
|
api_args['qos-policy-group-name'] = qos_policy_group
|
||||||
|
|
||||||
|
if encrypt is True:
|
||||||
|
if not self.features.FLEXVOL_ENCRYPTION:
|
||||||
|
msg = 'Flexvol encryption is not supported on this backend.'
|
||||||
|
raise exception.NetAppException(msg)
|
||||||
|
else:
|
||||||
|
api_args['encrypt'] = 'true'
|
||||||
|
|
||||||
self.send_request('volume-create', api_args)
|
self.send_request('volume-create', api_args)
|
||||||
|
|
||||||
# cDOT compression requires that deduplication be enabled.
|
# cDOT compression requires that deduplication be enabled.
|
||||||
@ -1705,6 +1762,41 @@ class NetAppCmodeClient(client_base.NetAppBaseClient):
|
|||||||
result = self.send_iter_request('volume-get-iter', api_args)
|
result = self.send_iter_request('volume-get-iter', api_args)
|
||||||
return self._has_records(result)
|
return self._has_records(result)
|
||||||
|
|
||||||
|
@na_utils.trace
|
||||||
|
def is_flexvol_encrypted(self, volume_name, vserver_name):
|
||||||
|
"""Checks whether the volume is encrypted or not."""
|
||||||
|
|
||||||
|
if not self.features.FLEXVOL_ENCRYPTION:
|
||||||
|
return False
|
||||||
|
|
||||||
|
api_args = {
|
||||||
|
'query': {
|
||||||
|
'volume-attributes': {
|
||||||
|
'encrypt': 'true',
|
||||||
|
'volume-id-attributes': {
|
||||||
|
'name': volume_name,
|
||||||
|
'owning-vserver-name': vserver_name,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'desired-attributes': {
|
||||||
|
'volume-attributes': {
|
||||||
|
'encrypt': None,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
result = self.send_iter_request('volume-get-iter', api_args)
|
||||||
|
if self._has_records(result):
|
||||||
|
attributes_list = result.get_child_by_name(
|
||||||
|
'attributes-list') or netapp_api.NaElement('none')
|
||||||
|
volume_attributes = attributes_list.get_child_by_name(
|
||||||
|
'volume-attributes') or netapp_api.NaElement('none')
|
||||||
|
encrypt = volume_attributes.get_child_content('encrypt')
|
||||||
|
if encrypt:
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
@na_utils.trace
|
@na_utils.trace
|
||||||
def get_aggregate_for_volume(self, volume_name):
|
def get_aggregate_for_volume(self, volume_name):
|
||||||
"""Get the name of the aggregate containing a volume."""
|
"""Get the name of the aggregate containing a volume."""
|
||||||
@ -3419,29 +3511,37 @@ class NetAppCmodeClient(client_base.NetAppBaseClient):
|
|||||||
|
|
||||||
@na_utils.trace
|
@na_utils.trace
|
||||||
def start_volume_move(self, volume_name, vserver, destination_aggregate,
|
def start_volume_move(self, volume_name, vserver, destination_aggregate,
|
||||||
cutover_action='wait'):
|
cutover_action='wait', encrypt_destination=None):
|
||||||
"""Moves a FlexVol across Vserver aggregates.
|
"""Moves a FlexVol across Vserver aggregates.
|
||||||
|
|
||||||
Requires cluster-scoped credentials.
|
Requires cluster-scoped credentials.
|
||||||
"""
|
"""
|
||||||
self._send_volume_move_request(
|
self._send_volume_move_request(
|
||||||
volume_name, vserver, destination_aggregate,
|
volume_name, vserver,
|
||||||
cutover_action=cutover_action)
|
destination_aggregate,
|
||||||
|
cutover_action=cutover_action,
|
||||||
|
encrypt_destination=encrypt_destination)
|
||||||
|
|
||||||
@na_utils.trace
|
@na_utils.trace
|
||||||
def check_volume_move(self, volume_name, vserver, destination_aggregate):
|
def check_volume_move(self, volume_name, vserver, destination_aggregate,
|
||||||
|
encrypt_destination=None):
|
||||||
"""Moves a FlexVol across Vserver aggregates.
|
"""Moves a FlexVol across Vserver aggregates.
|
||||||
|
|
||||||
Requires cluster-scoped credentials.
|
Requires cluster-scoped credentials.
|
||||||
"""
|
"""
|
||||||
self._send_volume_move_request(
|
self._send_volume_move_request(
|
||||||
volume_name, vserver, destination_aggregate, validation_only=True)
|
volume_name,
|
||||||
|
vserver,
|
||||||
|
destination_aggregate,
|
||||||
|
validation_only=True,
|
||||||
|
encrypt_destination=encrypt_destination)
|
||||||
|
|
||||||
@na_utils.trace
|
@na_utils.trace
|
||||||
def _send_volume_move_request(self, volume_name, vserver,
|
def _send_volume_move_request(self, volume_name, vserver,
|
||||||
destination_aggregate,
|
destination_aggregate,
|
||||||
cutover_action='wait',
|
cutover_action='wait',
|
||||||
validation_only=False):
|
validation_only=False,
|
||||||
|
encrypt_destination=None):
|
||||||
"""Send request to check if vol move is possible, or start it.
|
"""Send request to check if vol move is possible, or start it.
|
||||||
|
|
||||||
:param volume_name: Name of the FlexVol to be moved.
|
:param volume_name: Name of the FlexVol to be moved.
|
||||||
@ -3454,6 +3554,8 @@ class NetAppCmodeClient(client_base.NetAppBaseClient):
|
|||||||
intervention in case of errors.
|
intervention in case of errors.
|
||||||
:param validation_only: If set to True, only validates if the volume
|
:param validation_only: If set to True, only validates if the volume
|
||||||
move is possible, does not trigger data copy.
|
move is possible, does not trigger data copy.
|
||||||
|
:param encrypt_destination: If set to True, it encrypts the Flexvol
|
||||||
|
after the volume move is complete.
|
||||||
"""
|
"""
|
||||||
api_args = {
|
api_args = {
|
||||||
'source-volume': volume_name,
|
'source-volume': volume_name,
|
||||||
@ -3461,6 +3563,15 @@ class NetAppCmodeClient(client_base.NetAppBaseClient):
|
|||||||
'dest-aggr': destination_aggregate,
|
'dest-aggr': destination_aggregate,
|
||||||
'cutover-action': CUTOVER_ACTION_MAP[cutover_action],
|
'cutover-action': CUTOVER_ACTION_MAP[cutover_action],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if self.features.FLEXVOL_ENCRYPTION and encrypt_destination:
|
||||||
|
api_args['encrypt-destination'] = 'true'
|
||||||
|
elif encrypt_destination:
|
||||||
|
msg = 'Flexvol encryption is not supported on this backend.'
|
||||||
|
raise exception.NetAppException(msg)
|
||||||
|
else:
|
||||||
|
api_args['encrypt-destination'] = 'false'
|
||||||
|
|
||||||
if validation_only:
|
if validation_only:
|
||||||
api_args['perform-validation-only'] = 'true'
|
api_args['perform-validation-only'] = 'true'
|
||||||
self.send_request('volume-move-start', api_args)
|
self.send_request('volume-move-start', api_args)
|
||||||
|
@ -71,11 +71,13 @@ class NetAppCmodeFileStorageLibrary(object):
|
|||||||
'netapp:compression': 'compression_enabled',
|
'netapp:compression': 'compression_enabled',
|
||||||
'netapp:split_clone_on_create': 'split',
|
'netapp:split_clone_on_create': 'split',
|
||||||
}
|
}
|
||||||
|
|
||||||
STRING_QUALIFIED_EXTRA_SPECS_MAP = {
|
STRING_QUALIFIED_EXTRA_SPECS_MAP = {
|
||||||
'netapp:snapshot_policy': 'snapshot_policy',
|
'netapp:snapshot_policy': 'snapshot_policy',
|
||||||
'netapp:language': 'language',
|
'netapp:language': 'language',
|
||||||
'netapp:max_files': 'max_files',
|
'netapp:max_files': 'max_files',
|
||||||
}
|
}
|
||||||
|
|
||||||
# Maps standard extra spec keys to legacy NetApp keys
|
# Maps standard extra spec keys to legacy NetApp keys
|
||||||
STANDARD_BOOLEAN_EXTRA_SPECS_MAP = {
|
STANDARD_BOOLEAN_EXTRA_SPECS_MAP = {
|
||||||
'thin_provisioning': 'netapp:thin_provisioned',
|
'thin_provisioning': 'netapp:thin_provisioned',
|
||||||
@ -114,6 +116,7 @@ class NetAppCmodeFileStorageLibrary(object):
|
|||||||
self._clients = {}
|
self._clients = {}
|
||||||
self._ssc_stats = {}
|
self._ssc_stats = {}
|
||||||
self._have_cluster_creds = None
|
self._have_cluster_creds = None
|
||||||
|
self._cluster_info = {}
|
||||||
|
|
||||||
self._app_version = kwargs.get('app_version', 'unknown')
|
self._app_version = kwargs.get('app_version', 'unknown')
|
||||||
|
|
||||||
@ -126,10 +129,18 @@ class NetAppCmodeFileStorageLibrary(object):
|
|||||||
def do_setup(self, context):
|
def do_setup(self, context):
|
||||||
self._client = self._get_api_client()
|
self._client = self._get_api_client()
|
||||||
self._have_cluster_creds = self._client.check_for_cluster_credentials()
|
self._have_cluster_creds = self._client.check_for_cluster_credentials()
|
||||||
|
if self._have_cluster_creds is True:
|
||||||
|
self._set_cluster_info()
|
||||||
|
|
||||||
# Performance monitoring library
|
# Performance monitoring library
|
||||||
self._perf_library = performance.PerformanceLibrary(self._client)
|
self._perf_library = performance.PerformanceLibrary(self._client)
|
||||||
|
|
||||||
|
@na_utils.trace
|
||||||
|
def _set_cluster_info(self):
|
||||||
|
self._cluster_info['nve_support'] = (
|
||||||
|
self._client.is_nve_supported()
|
||||||
|
and self._client.features.FLEXVOL_ENCRYPTION)
|
||||||
|
|
||||||
@na_utils.trace
|
@na_utils.trace
|
||||||
def check_for_setup_error(self):
|
def check_for_setup_error(self):
|
||||||
self._licenses = self._get_licenses()
|
self._licenses = self._get_licenses()
|
||||||
@ -140,6 +151,7 @@ class NetAppCmodeFileStorageLibrary(object):
|
|||||||
|
|
||||||
@na_utils.trace
|
@na_utils.trace
|
||||||
def _get_api_client(self, vserver=None):
|
def _get_api_client(self, vserver=None):
|
||||||
|
|
||||||
# Use cached value to prevent calls to system-get-ontapi-version.
|
# Use cached value to prevent calls to system-get-ontapi-version.
|
||||||
client = self._clients.get(vserver)
|
client = self._clients.get(vserver)
|
||||||
|
|
||||||
@ -300,6 +312,9 @@ class NetAppCmodeFileStorageLibrary(object):
|
|||||||
else:
|
else:
|
||||||
qos_support = False
|
qos_support = False
|
||||||
|
|
||||||
|
netapp_flexvol_encryption = self._cluster_info.get(
|
||||||
|
'nve_support', False)
|
||||||
|
|
||||||
for aggr_name in sorted(aggregates):
|
for aggr_name in sorted(aggregates):
|
||||||
|
|
||||||
reserved_percentage = self.configuration.reserved_share_percentage
|
reserved_percentage = self.configuration.reserved_share_percentage
|
||||||
@ -325,6 +340,7 @@ class NetAppCmodeFileStorageLibrary(object):
|
|||||||
'reserved_percentage': reserved_percentage,
|
'reserved_percentage': reserved_percentage,
|
||||||
'dedupe': [True, False],
|
'dedupe': [True, False],
|
||||||
'compression': [True, False],
|
'compression': [True, False],
|
||||||
|
'netapp_flexvol_encryption': netapp_flexvol_encryption,
|
||||||
'thin_provisioning': [True, False],
|
'thin_provisioning': [True, False],
|
||||||
'snapshot_support': True,
|
'snapshot_support': True,
|
||||||
'create_share_from_snapshot_support': True,
|
'create_share_from_snapshot_support': True,
|
||||||
@ -685,8 +701,19 @@ class NetAppCmodeFileStorageLibrary(object):
|
|||||||
specs, self.STRING_QUALIFIED_EXTRA_SPECS_MAP)
|
specs, self.STRING_QUALIFIED_EXTRA_SPECS_MAP)
|
||||||
result = boolean_args.copy()
|
result = boolean_args.copy()
|
||||||
result.update(string_args)
|
result.update(string_args)
|
||||||
|
|
||||||
|
result['encrypt'] = self._get_nve_option(specs)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
def _get_nve_option(self, specs):
|
||||||
|
if 'netapp_flexvol_encryption' in specs:
|
||||||
|
nve = specs['netapp_flexvol_encryption'].lower() == 'true'
|
||||||
|
else:
|
||||||
|
nve = False
|
||||||
|
|
||||||
|
return nve
|
||||||
|
|
||||||
@na_utils.trace
|
@na_utils.trace
|
||||||
def _check_aggregate_extra_specs_validity(self, aggregate_name, specs):
|
def _check_aggregate_extra_specs_validity(self, aggregate_name, specs):
|
||||||
|
|
||||||
@ -981,9 +1008,6 @@ class NetAppCmodeFileStorageLibrary(object):
|
|||||||
# When calculating the size, round up to the next GB.
|
# When calculating the size, round up to the next GB.
|
||||||
volume_size = int(math.ceil(float(volume['size']) / units.Gi))
|
volume_size = int(math.ceil(float(volume['size']) / units.Gi))
|
||||||
|
|
||||||
# Ensure volume is manageable
|
|
||||||
self._validate_volume_for_manage(volume, vserver_client)
|
|
||||||
|
|
||||||
# Validate extra specs
|
# Validate extra specs
|
||||||
extra_specs = share_types.get_extra_specs_from_share(share)
|
extra_specs = share_types.get_extra_specs_from_share(share)
|
||||||
try:
|
try:
|
||||||
@ -993,6 +1017,10 @@ class NetAppCmodeFileStorageLibrary(object):
|
|||||||
except exception.ManilaException as ex:
|
except exception.ManilaException as ex:
|
||||||
raise exception.ManageExistingShareTypeMismatch(
|
raise exception.ManageExistingShareTypeMismatch(
|
||||||
reason=six.text_type(ex))
|
reason=six.text_type(ex))
|
||||||
|
|
||||||
|
# Ensure volume is manageable
|
||||||
|
self._validate_volume_for_manage(volume, vserver_client)
|
||||||
|
|
||||||
provisioning_options = self._get_provisioning_options(extra_specs)
|
provisioning_options = self._get_provisioning_options(extra_specs)
|
||||||
|
|
||||||
debug_args = {
|
debug_args = {
|
||||||
@ -1923,8 +1951,11 @@ class NetAppCmodeFileStorageLibrary(object):
|
|||||||
self._check_destination_vserver_for_vol_move(
|
self._check_destination_vserver_for_vol_move(
|
||||||
source_share, source_vserver, destination_share_server)
|
source_share, source_vserver, destination_share_server)
|
||||||
|
|
||||||
|
encrypt_dest = self._get_dest_flexvol_encryption_value(
|
||||||
|
destination_share)
|
||||||
self._client.check_volume_move(
|
self._client.check_volume_move(
|
||||||
share_volume, source_vserver, destination_aggregate)
|
share_volume, source_vserver, destination_aggregate,
|
||||||
|
encrypt_destination=encrypt_dest)
|
||||||
|
|
||||||
except Exception:
|
except Exception:
|
||||||
msg = ("Cannot migrate share %(shr)s efficiently between "
|
msg = ("Cannot migrate share %(shr)s efficiently between "
|
||||||
@ -1963,8 +1994,18 @@ class NetAppCmodeFileStorageLibrary(object):
|
|||||||
destination_aggregate = share_utils.extract_host(
|
destination_aggregate = share_utils.extract_host(
|
||||||
destination_share['host'], level='pool')
|
destination_share['host'], level='pool')
|
||||||
|
|
||||||
|
# If the destination's share type extra-spec for Flexvol encryption
|
||||||
|
# is different than the source's, then specify the volume-move
|
||||||
|
# operation to set the correct 'encrypt' attribute on the destination
|
||||||
|
# volume.
|
||||||
|
encrypt_dest = self._get_dest_flexvol_encryption_value(
|
||||||
|
destination_share)
|
||||||
|
|
||||||
self._client.start_volume_move(
|
self._client.start_volume_move(
|
||||||
share_volume, vserver, destination_aggregate)
|
share_volume,
|
||||||
|
vserver,
|
||||||
|
destination_aggregate,
|
||||||
|
encrypt_destination=encrypt_dest)
|
||||||
|
|
||||||
msg = ("Began volume move operation of share %(shr)s from %(src)s "
|
msg = ("Began volume move operation of share %(shr)s from %(src)s "
|
||||||
"to %(dest)s.")
|
"to %(dest)s.")
|
||||||
@ -1981,6 +2022,15 @@ class NetAppCmodeFileStorageLibrary(object):
|
|||||||
status = self._client.get_volume_move_status(share_volume, vserver)
|
status = self._client.get_volume_move_status(share_volume, vserver)
|
||||||
return status
|
return status
|
||||||
|
|
||||||
|
def _get_dest_flexvol_encryption_value(self, destination_share):
|
||||||
|
dest_share_type_encrypted_val = share_types.get_share_type_extra_specs(
|
||||||
|
destination_share['share_type_id'],
|
||||||
|
'netapp_flexvol_encryption')
|
||||||
|
encrypt_destination = share_types.parse_boolean_extra_spec(
|
||||||
|
'netapp_flexvol_encryption', dest_share_type_encrypted_val)
|
||||||
|
|
||||||
|
return encrypt_destination
|
||||||
|
|
||||||
def migration_continue(self, context, source_share, destination_share,
|
def migration_continue(self, context, source_share, destination_share,
|
||||||
source_snapshots, snapshot_mappings,
|
source_snapshots, snapshot_mappings,
|
||||||
share_server=None, destination_share_server=None):
|
share_server=None, destination_share_server=None):
|
||||||
|
@ -33,6 +33,8 @@ REMOTE_CLUSTER_NAME = 'fake_cluster_2'
|
|||||||
CLUSTER_ADDRESS_1 = 'fake_cluster_address'
|
CLUSTER_ADDRESS_1 = 'fake_cluster_address'
|
||||||
CLUSTER_ADDRESS_2 = 'fake_cluster_address_2'
|
CLUSTER_ADDRESS_2 = 'fake_cluster_address_2'
|
||||||
VERSION = 'NetApp Release 8.2.1 Cluster-Mode: Fri Mar 21 14:25:07 PDT 2014'
|
VERSION = 'NetApp Release 8.2.1 Cluster-Mode: Fri Mar 21 14:25:07 PDT 2014'
|
||||||
|
VERSION_NO_DARE = 'NetApp Release 9.1.0: Tue May 10 19:30:23 2016 <1no-DARE>'
|
||||||
|
VERSION_TUPLE = (9, 1, 0)
|
||||||
NODE_NAME = 'fake_node1'
|
NODE_NAME = 'fake_node1'
|
||||||
NODE_NAMES = ('fake_node1', 'fake_node2')
|
NODE_NAMES = ('fake_node1', 'fake_node2')
|
||||||
VSERVER_NAME = 'fake_vserver'
|
VSERVER_NAME = 'fake_vserver'
|
||||||
@ -454,6 +456,18 @@ SYSTEM_NODE_GET_ITER_RESPONSE = etree.XML("""
|
|||||||
</results>
|
</results>
|
||||||
""" % NODE_NAME)
|
""" % NODE_NAME)
|
||||||
|
|
||||||
|
SECUTITY_KEY_MANAGER_NVE_SUPPORT_RESPONSE_TRUE = etree.XML("""
|
||||||
|
<results status="passed">
|
||||||
|
<vol-encryption-supported>true</vol-encryption-supported>
|
||||||
|
</results>
|
||||||
|
""")
|
||||||
|
|
||||||
|
SECUTITY_KEY_MANAGER_NVE_SUPPORT_RESPONSE_FALSE = etree.XML("""
|
||||||
|
<results status="passed">
|
||||||
|
<vol-encryption-supported>false</vol-encryption-supported>
|
||||||
|
</results>
|
||||||
|
""")
|
||||||
|
|
||||||
NET_PORT_GET_ITER_RESPONSE = etree.XML("""
|
NET_PORT_GET_ITER_RESPONSE = etree.XML("""
|
||||||
<results status="passed">
|
<results status="passed">
|
||||||
<attributes-list>
|
<attributes-list>
|
||||||
@ -1884,6 +1898,35 @@ GET_AGGREGATE_FOR_VOLUME_RESPONSE = etree.XML("""
|
|||||||
'share': SHARE_NAME
|
'share': SHARE_NAME
|
||||||
})
|
})
|
||||||
|
|
||||||
|
GET_VOLUME_FOR_ENCRYPTED_RESPONSE = etree.XML("""
|
||||||
|
<results status="passed">
|
||||||
|
<attributes-list>
|
||||||
|
<volume-attributes>
|
||||||
|
<encrypt>true</encrypt>
|
||||||
|
<volume-id-attributes>
|
||||||
|
<name>%(volume)s</name>
|
||||||
|
<owning-vserver-name>manila_svm</owning-vserver-name>
|
||||||
|
</volume-id-attributes>
|
||||||
|
</volume-attributes>
|
||||||
|
</attributes-list>
|
||||||
|
<num-records>1</num-records>
|
||||||
|
</results>
|
||||||
|
""" % {'volume': SHARE_NAME})
|
||||||
|
|
||||||
|
GET_VOLUME_FOR_ENCRYPTED_OLD_SYS_VERSION_RESPONSE = etree.XML("""
|
||||||
|
<results status="passed">
|
||||||
|
<attributes-list>
|
||||||
|
<volume-attributes>
|
||||||
|
<volume-id-attributes>
|
||||||
|
<name>%(volume)s</name>
|
||||||
|
<owning-vserver-name>manila_svm</owning-vserver-name>
|
||||||
|
</volume-id-attributes>
|
||||||
|
</volume-attributes>
|
||||||
|
</attributes-list>
|
||||||
|
<num-records>1</num-records>
|
||||||
|
</results>
|
||||||
|
""" % {'volume': SHARE_NAME})
|
||||||
|
|
||||||
EXPORT_RULE_GET_ITER_RESPONSE = etree.XML("""
|
EXPORT_RULE_GET_ITER_RESPONSE = etree.XML("""
|
||||||
<results status="passed">
|
<results status="passed">
|
||||||
<attributes-list>
|
<attributes-list>
|
||||||
@ -2476,3 +2519,6 @@ FAKE_MANAGE_VOLUME = {
|
|||||||
'style': 'fake_style',
|
'style': 'fake_style',
|
||||||
'size': SHARE_SIZE,
|
'size': SHARE_SIZE,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FAKE_KEY_MANAGER_ERROR = "The onboard key manager is not enabled. To enable \
|
||||||
|
it, run \"security key-manager setup\"."
|
||||||
|
@ -31,6 +31,16 @@ from manila.tests.share.drivers.netapp.dataontap.client import fakes as fake
|
|||||||
class NetAppApiElementTransTests(test.TestCase):
|
class NetAppApiElementTransTests(test.TestCase):
|
||||||
"""Test case for NetApp API element translations."""
|
"""Test case for NetApp API element translations."""
|
||||||
|
|
||||||
|
def test_get_set_system_version(self):
|
||||||
|
napi = api.NaServer('localhost')
|
||||||
|
|
||||||
|
# Testing calls before version is set
|
||||||
|
version = napi.get_system_version()
|
||||||
|
self.assertIsNone(version)
|
||||||
|
napi.set_system_version(fake.VERSION_TUPLE)
|
||||||
|
version = napi.get_system_version()
|
||||||
|
self.assertEqual(fake.VERSION_TUPLE, version)
|
||||||
|
|
||||||
def test_translate_struct_dict_unique_key(self):
|
def test_translate_struct_dict_unique_key(self):
|
||||||
"""Tests if dict gets properly converted to NaElements."""
|
"""Tests if dict gets properly converted to NaElements."""
|
||||||
root = api.NaElement('root')
|
root = api.NaElement('root')
|
||||||
|
@ -66,10 +66,22 @@ class NetAppBaseClientTestCase(test.TestCase):
|
|||||||
fake.SYSTEM_GET_VERSION_RESPONSE)
|
fake.SYSTEM_GET_VERSION_RESPONSE)
|
||||||
self.connection.invoke_successfully.return_value = version_response
|
self.connection.invoke_successfully.return_value = version_response
|
||||||
|
|
||||||
|
result = self.client.get_system_version(cached=False)
|
||||||
|
|
||||||
|
self.assertEqual(fake.VERSION, result['version'])
|
||||||
|
self.assertEqual((8, 2, 1), result['version-tuple'])
|
||||||
|
|
||||||
|
def test_get_system_version_cached(self):
|
||||||
|
|
||||||
|
self.connection.get_system_version.return_value = {
|
||||||
|
'version': fake.VERSION,
|
||||||
|
'version-tuple': (8, 2, 1)
|
||||||
|
}
|
||||||
|
|
||||||
result = self.client.get_system_version()
|
result = self.client.get_system_version()
|
||||||
|
|
||||||
self.assertEqual(fake.VERSION, result['version'])
|
self.assertEqual(fake.VERSION, result['version'])
|
||||||
self.assertEqual(('8', '2', '1'), result['version-tuple'])
|
self.assertEqual((8, 2, 1), result['version-tuple'])
|
||||||
|
|
||||||
def test_init_features(self):
|
def test_init_features(self):
|
||||||
|
|
||||||
|
@ -53,6 +53,13 @@ class NetAppClientCmodeTestCase(test.TestCase):
|
|||||||
'get_ontapi_version',
|
'get_ontapi_version',
|
||||||
mock.Mock(return_value=(1, 20)))
|
mock.Mock(return_value=(1, 20)))
|
||||||
|
|
||||||
|
self.mock_object(client_base.NetAppBaseClient,
|
||||||
|
'get_system_version',
|
||||||
|
mock.Mock(return_value={
|
||||||
|
'version-tuple': (8, 3, 0),
|
||||||
|
'version': fake.VERSION,
|
||||||
|
}))
|
||||||
|
|
||||||
self.client = client_cmode.NetAppCmodeClient(**fake.CONNECTION_INFO)
|
self.client = client_cmode.NetAppCmodeClient(**fake.CONNECTION_INFO)
|
||||||
self.client.connection = mock.MagicMock()
|
self.client.connection = mock.MagicMock()
|
||||||
|
|
||||||
@ -76,6 +83,7 @@ class NetAppClientCmodeTestCase(test.TestCase):
|
|||||||
self.assertFalse(self.client.features.BROADCAST_DOMAINS)
|
self.assertFalse(self.client.features.BROADCAST_DOMAINS)
|
||||||
self.assertFalse(self.client.features.IPSPACES)
|
self.assertFalse(self.client.features.IPSPACES)
|
||||||
self.assertFalse(self.client.features.SUBNETS)
|
self.assertFalse(self.client.features.SUBNETS)
|
||||||
|
self.assertFalse(self.client.features.FLEXVOL_ENCRYPTION)
|
||||||
|
|
||||||
@ddt.data((1, 30), (1, 40), (2, 0))
|
@ddt.data((1, 30), (1, 40), (2, 0))
|
||||||
def test_init_features_ontapi_1_30(self, ontapi_version):
|
def test_init_features_ontapi_1_30(self, ontapi_version):
|
||||||
@ -90,6 +98,145 @@ class NetAppClientCmodeTestCase(test.TestCase):
|
|||||||
self.assertTrue(self.client.features.IPSPACES)
|
self.assertTrue(self.client.features.IPSPACES)
|
||||||
self.assertTrue(self.client.features.SUBNETS)
|
self.assertTrue(self.client.features.SUBNETS)
|
||||||
|
|
||||||
|
@ddt.data((1, 110), (2, 0))
|
||||||
|
def test_init_features_ontap_1_110(self, ontapi_version):
|
||||||
|
|
||||||
|
self.mock_object(client_base.NetAppBaseClient,
|
||||||
|
'get_ontapi_version',
|
||||||
|
mock.Mock(return_value=ontapi_version))
|
||||||
|
|
||||||
|
self.client._init_features()
|
||||||
|
|
||||||
|
self.assertTrue(self.client.features.BROADCAST_DOMAINS)
|
||||||
|
self.assertTrue(self.client.features.IPSPACES)
|
||||||
|
self.assertTrue(self.client.features.SUBNETS)
|
||||||
|
self.assertTrue(self.client.features.FLEXVOL_ENCRYPTION)
|
||||||
|
|
||||||
|
@ddt.data(((9, 1, 0), fake.VERSION_NO_DARE), ((8, 3, 2), fake.VERSION))
|
||||||
|
@ddt.unpack
|
||||||
|
def test_is_nve_supported_unsupported_release_or_platform(self, gen, ver):
|
||||||
|
|
||||||
|
system_version = {'version-tuple': gen, 'version': ver}
|
||||||
|
self.mock_object(client_base.NetAppBaseClient,
|
||||||
|
'get_system_version',
|
||||||
|
mock.Mock(return_value=system_version))
|
||||||
|
self.mock_object(self.client,
|
||||||
|
'get_security_key_manager_nve_support',
|
||||||
|
mock.Mock(return_value=True))
|
||||||
|
self.mock_object(self.client,
|
||||||
|
'list_cluster_nodes',
|
||||||
|
mock.Mock(return_value=fake.NODE_NAMES))
|
||||||
|
|
||||||
|
result = self.client.is_nve_supported()
|
||||||
|
|
||||||
|
self.assertFalse(result)
|
||||||
|
|
||||||
|
def test_is_nve_supported_valid_platform_and_supported_release(self):
|
||||||
|
|
||||||
|
system_version = {
|
||||||
|
'version-tuple': (9, 1, 0),
|
||||||
|
'version': fake.VERSION,
|
||||||
|
}
|
||||||
|
self.mock_object(client_base.NetAppBaseClient,
|
||||||
|
'get_system_version',
|
||||||
|
mock.Mock(return_value=system_version))
|
||||||
|
self.mock_object(self.client,
|
||||||
|
'get_security_key_manager_nve_support',
|
||||||
|
mock.Mock(return_value=True))
|
||||||
|
self.mock_object(self.client,
|
||||||
|
'list_cluster_nodes',
|
||||||
|
mock.Mock(return_value=fake.NODE_NAMES))
|
||||||
|
|
||||||
|
result = self.client.is_nve_supported()
|
||||||
|
self.assertTrue(result)
|
||||||
|
|
||||||
|
def test_is_nve_supported_key_manager_not_enabled(self):
|
||||||
|
|
||||||
|
system_version = {
|
||||||
|
'version-tuple': (9, 1, 0),
|
||||||
|
'version': fake.VERSION,
|
||||||
|
}
|
||||||
|
self.mock_object(client_base.NetAppBaseClient,
|
||||||
|
'get_system_version',
|
||||||
|
mock.Mock(return_value=system_version))
|
||||||
|
self.mock_object(self.client,
|
||||||
|
'get_security_key_manager_nve_support',
|
||||||
|
mock.Mock(return_value=False))
|
||||||
|
self.mock_object(self.client,
|
||||||
|
'list_cluster_nodes',
|
||||||
|
mock.Mock(return_value=fake.NODE_NAMES))
|
||||||
|
|
||||||
|
result = self.client.is_nve_supported()
|
||||||
|
|
||||||
|
self.assertFalse(result)
|
||||||
|
|
||||||
|
def test_get_security_key_manager_nve_support_enabled(self):
|
||||||
|
api_response = netapp_api.NaElement(
|
||||||
|
fake.SECUTITY_KEY_MANAGER_NVE_SUPPORT_RESPONSE_TRUE)
|
||||||
|
self.mock_object(self.client,
|
||||||
|
'send_request',
|
||||||
|
mock.Mock(return_value=api_response))
|
||||||
|
|
||||||
|
result = self.client.get_security_key_manager_nve_support(
|
||||||
|
fake.NODE_NAME)
|
||||||
|
|
||||||
|
self.assertTrue(result)
|
||||||
|
api_args = {'node': fake.NODE_NAME}
|
||||||
|
self.client.send_request.assert_has_calls([
|
||||||
|
mock.call('security-key-manager-volume-encryption-supported',
|
||||||
|
api_args)])
|
||||||
|
|
||||||
|
def test_get_security_key_manager_nve_support_disabled(self):
|
||||||
|
api_response = netapp_api.NaElement(
|
||||||
|
fake.SECUTITY_KEY_MANAGER_NVE_SUPPORT_RESPONSE_FALSE)
|
||||||
|
self.mock_object(self.client, 'send_request',
|
||||||
|
mock.Mock(return_value=api_response))
|
||||||
|
|
||||||
|
result = self.client.get_security_key_manager_nve_support(
|
||||||
|
fake.NODE_NAME)
|
||||||
|
|
||||||
|
self.assertFalse(result)
|
||||||
|
api_args = {'node': fake.NODE_NAME}
|
||||||
|
self.client.send_request.assert_has_calls([
|
||||||
|
mock.call('security-key-manager-volume-encryption-supported',
|
||||||
|
api_args)])
|
||||||
|
|
||||||
|
self.mock_object(self.client, 'send_request', self._mock_api_error())
|
||||||
|
self.assertRaises(netapp_api.NaApiError,
|
||||||
|
self.client.get_security_key_manager_nve_support,
|
||||||
|
fake.NODE_NAME)
|
||||||
|
self.mock_object(self.client, 'send_request', self._mock_api_error(
|
||||||
|
code=netapp_api.EAPIERROR, message=fake.FAKE_KEY_MANAGER_ERROR))
|
||||||
|
result = self.client.get_security_key_manager_nve_support(
|
||||||
|
fake.NODE_NAME)
|
||||||
|
self.assertFalse(result)
|
||||||
|
|
||||||
|
@ddt.data((True, True, True), (False, None, False))
|
||||||
|
@ddt.unpack
|
||||||
|
def test_send_volume_move_request_success(self, validation_only,
|
||||||
|
encrypt_dst, fv_encryption):
|
||||||
|
self.mock_object(self.client, 'features',
|
||||||
|
mock.Mock(FLEXVOL_ENCRYPTION=fv_encryption))
|
||||||
|
self.client._send_volume_move_request(fake.ROOT_VOLUME_NAME,
|
||||||
|
fake.NODE_VSERVER_NAME,
|
||||||
|
fake.SHARE_AGGREGATE_NAME,
|
||||||
|
validation_only=validation_only,
|
||||||
|
encrypt_destination=encrypt_dst)
|
||||||
|
|
||||||
|
@ddt.data((True, True, False))
|
||||||
|
@ddt.unpack
|
||||||
|
def test_send_volume_move_request_failure(self, validation_only,
|
||||||
|
encrypt_dst, fv_encrypt):
|
||||||
|
self.mock_object(self.client, 'features',
|
||||||
|
mock.Mock(FLEXVOL_ENCRYPTION=fv_encrypt))
|
||||||
|
self.assertRaises(exception.NetAppException,
|
||||||
|
self.client._send_volume_move_request,
|
||||||
|
fake.ROOT_VOLUME_NAME,
|
||||||
|
fake.NODE_VSERVER_NAME,
|
||||||
|
fake.SHARE_AGGREGATE_NAME,
|
||||||
|
validation_only=validation_only,
|
||||||
|
encrypt_destination=encrypt_dst)
|
||||||
|
|
||||||
def test_invoke_vserver_api(self):
|
def test_invoke_vserver_api(self):
|
||||||
|
|
||||||
self.client._invoke_vserver_api('fake-api', 'fake_vserver')
|
self.client._invoke_vserver_api('fake-api', 'fake_vserver')
|
||||||
@ -2536,6 +2683,143 @@ class NetAppClientCmodeTestCase(test.TestCase):
|
|||||||
self.client.enable_dedup.assert_called_once_with(fake.SHARE_NAME)
|
self.client.enable_dedup.assert_called_once_with(fake.SHARE_NAME)
|
||||||
self.client.enable_compression.assert_called_once_with(fake.SHARE_NAME)
|
self.client.enable_compression.assert_called_once_with(fake.SHARE_NAME)
|
||||||
|
|
||||||
|
def test_create_encrypted_volume(self):
|
||||||
|
|
||||||
|
self.mock_object(self.client, 'send_request')
|
||||||
|
self.client.features.add_feature('FLEXVOL_ENCRYPTION')
|
||||||
|
|
||||||
|
self.client.create_volume(
|
||||||
|
fake.SHARE_AGGREGATE_NAME, fake.SHARE_NAME, 100, encrypt=True)
|
||||||
|
|
||||||
|
volume_create_args = {
|
||||||
|
'containing-aggr-name': fake.SHARE_AGGREGATE_NAME,
|
||||||
|
'size': '100g',
|
||||||
|
'volume': fake.SHARE_NAME,
|
||||||
|
'volume-type': 'rw',
|
||||||
|
'junction-path': '/%s' % fake.SHARE_NAME,
|
||||||
|
'encrypt': 'true',
|
||||||
|
}
|
||||||
|
|
||||||
|
self.client.send_request.assert_called_once_with('volume-create',
|
||||||
|
volume_create_args)
|
||||||
|
|
||||||
|
def test_create_non_encrypted_volume(self):
|
||||||
|
|
||||||
|
self.mock_object(self.client, 'send_request')
|
||||||
|
self.client.features.add_feature('FLEXVOL_ENCRYPTION')
|
||||||
|
|
||||||
|
self.client.create_volume(
|
||||||
|
fake.SHARE_AGGREGATE_NAME, fake.SHARE_NAME, 100, encrypt=False)
|
||||||
|
|
||||||
|
volume_create_args = {
|
||||||
|
'containing-aggr-name': fake.SHARE_AGGREGATE_NAME,
|
||||||
|
'size': '100g',
|
||||||
|
'volume': fake.SHARE_NAME,
|
||||||
|
'volume-type': 'rw',
|
||||||
|
'junction-path': '/%s' % fake.SHARE_NAME,
|
||||||
|
}
|
||||||
|
|
||||||
|
self.client.send_request.assert_called_once_with('volume-create',
|
||||||
|
volume_create_args)
|
||||||
|
|
||||||
|
def test_create_encrypted_volume_not_supported(self):
|
||||||
|
|
||||||
|
self.assertRaises(exception.NetAppException,
|
||||||
|
self.client.create_volume,
|
||||||
|
fake.SHARE_AGGREGATE_NAME,
|
||||||
|
fake.SHARE_NAME,
|
||||||
|
100,
|
||||||
|
encrypt=True)
|
||||||
|
|
||||||
|
def test_is_flexvol_encrypted_unsupported(self):
|
||||||
|
|
||||||
|
self.client.features.add_feature('FLEXVOL_ENCRYPTION', supported=False)
|
||||||
|
|
||||||
|
result = self.client.is_flexvol_encrypted(fake.SHARE_NAME,
|
||||||
|
fake.VSERVER_NAME)
|
||||||
|
|
||||||
|
self.assertFalse(result)
|
||||||
|
|
||||||
|
def test_is_flexvol_encrypted_no_records_found(self):
|
||||||
|
|
||||||
|
api_response = netapp_api.NaElement(fake.NO_RECORDS_RESPONSE)
|
||||||
|
self.mock_object(self.client,
|
||||||
|
'send_iter_request',
|
||||||
|
mock.Mock(return_value=api_response))
|
||||||
|
|
||||||
|
result = self.client.is_flexvol_encrypted(fake.SHARE_NAME,
|
||||||
|
fake.VSERVER_NAME)
|
||||||
|
|
||||||
|
self.assertFalse(result)
|
||||||
|
|
||||||
|
def test_is_flexvol_encrypted(self):
|
||||||
|
|
||||||
|
self.client.features.add_feature('FLEXVOL_ENCRYPTION', supported=True)
|
||||||
|
api_response = netapp_api.NaElement(
|
||||||
|
fake.GET_VOLUME_FOR_ENCRYPTED_RESPONSE)
|
||||||
|
self.mock_object(self.client,
|
||||||
|
'send_iter_request',
|
||||||
|
mock.Mock(return_value=api_response))
|
||||||
|
|
||||||
|
result = self.client.is_flexvol_encrypted(fake.SHARE_NAME,
|
||||||
|
fake.VSERVER_NAME)
|
||||||
|
|
||||||
|
volume_get_iter_args = {
|
||||||
|
'query': {
|
||||||
|
'volume-attributes': {
|
||||||
|
'encrypt': 'true',
|
||||||
|
'volume-id-attributes': {
|
||||||
|
'name': fake.SHARE_NAME,
|
||||||
|
'owning-vserver-name': fake.VSERVER_NAME,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'desired-attributes': {
|
||||||
|
'volume-attributes': {
|
||||||
|
'encrypt': None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.client.send_iter_request.assert_called_once_with(
|
||||||
|
'volume-get-iter', volume_get_iter_args)
|
||||||
|
|
||||||
|
self.assertTrue(result)
|
||||||
|
|
||||||
|
def test_is_flexvol_encrypted_8_x_system_version_response(self):
|
||||||
|
|
||||||
|
self.client.features.add_feature('FLEXVOL_ENCRYPTION', supported=True)
|
||||||
|
api_response = netapp_api.NaElement(
|
||||||
|
fake.GET_VOLUME_FOR_ENCRYPTED_OLD_SYS_VERSION_RESPONSE)
|
||||||
|
self.mock_object(self.client,
|
||||||
|
'send_iter_request',
|
||||||
|
mock.Mock(return_value=api_response))
|
||||||
|
|
||||||
|
result = self.client.is_flexvol_encrypted(fake.SHARE_NAME,
|
||||||
|
fake.VSERVER_NAME)
|
||||||
|
|
||||||
|
volume_get_iter_args = {
|
||||||
|
'query': {
|
||||||
|
'volume-attributes': {
|
||||||
|
'encrypt': 'true',
|
||||||
|
'volume-id-attributes': {
|
||||||
|
'name': fake.SHARE_NAME,
|
||||||
|
'owning-vserver-name': fake.VSERVER_NAME,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'desired-attributes': {
|
||||||
|
'volume-attributes': {
|
||||||
|
'encrypt': None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.client.send_iter_request.assert_called_once_with(
|
||||||
|
'volume-get-iter', volume_get_iter_args)
|
||||||
|
|
||||||
|
self.assertFalse(result)
|
||||||
|
|
||||||
def test_enable_dedup(self):
|
def test_enable_dedup(self):
|
||||||
|
|
||||||
self.mock_object(self.client, 'send_request')
|
self.mock_object(self.client, 'send_request')
|
||||||
@ -5812,6 +6096,7 @@ class NetAppClientCmodeTestCase(test.TestCase):
|
|||||||
'vserver': fake.VSERVER_NAME,
|
'vserver': fake.VSERVER_NAME,
|
||||||
'dest-aggr': fake.SHARE_AGGREGATE_NAME,
|
'dest-aggr': fake.SHARE_AGGREGATE_NAME,
|
||||||
'cutover-action': 'wait',
|
'cutover-action': 'wait',
|
||||||
|
'encrypt-destination': 'false'
|
||||||
}
|
}
|
||||||
if method_name.startswith('check'):
|
if method_name.startswith('check'):
|
||||||
expected_api_args['perform-validation-only'] = 'true'
|
expected_api_args['perform-validation-only'] = 'true'
|
||||||
|
@ -111,13 +111,27 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
|
|||||||
self.mock_object(
|
self.mock_object(
|
||||||
performance, 'PerformanceLibrary',
|
performance, 'PerformanceLibrary',
|
||||||
mock.Mock(return_value='fake_perf_library'))
|
mock.Mock(return_value='fake_perf_library'))
|
||||||
|
self.mock_object(
|
||||||
|
self.library._client, 'check_for_cluster_credentials',
|
||||||
|
mock.Mock(return_value=True))
|
||||||
self.library.do_setup(self.context)
|
self.library.do_setup(self.context)
|
||||||
|
|
||||||
mock_get_api_client.assert_called_once_with()
|
mock_get_api_client.assert_called_once_with()
|
||||||
(self.library._client.check_for_cluster_credentials.
|
(self.library._client.check_for_cluster_credentials.
|
||||||
assert_called_once_with())
|
assert_called_once_with())
|
||||||
self.assertEqual('fake_perf_library', self.library._perf_library)
|
self.assertEqual('fake_perf_library', self.library._perf_library)
|
||||||
|
self.mock_object(self.library._client,
|
||||||
|
'check_for_cluster_credentials',
|
||||||
|
mock.Mock(return_value=True))
|
||||||
|
mock_set_cluster_info = self.mock_object(
|
||||||
|
self.library, '_set_cluster_info')
|
||||||
|
self.library.do_setup(self.context)
|
||||||
|
mock_set_cluster_info.assert_called_once()
|
||||||
|
|
||||||
|
def test_set_cluster_info(self):
|
||||||
|
self.library._set_cluster_info()
|
||||||
|
self.assertTrue(self.library._cluster_info['nve_support'],
|
||||||
|
fake.CLUSTER_NODES)
|
||||||
|
|
||||||
def test_check_for_setup_error(self):
|
def test_check_for_setup_error(self):
|
||||||
|
|
||||||
@ -415,6 +429,7 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
|
|||||||
self.library, '_get_aggregate_space',
|
self.library, '_get_aggregate_space',
|
||||||
mock.Mock(return_value=fake.AGGREGATE_CAPACITIES))
|
mock.Mock(return_value=fake.AGGREGATE_CAPACITIES))
|
||||||
self.library._have_cluster_creds = True
|
self.library._have_cluster_creds = True
|
||||||
|
self.library._cluster_info = fake.CLUSTER_INFO
|
||||||
self.library._ssc_stats = fake.SSC_INFO
|
self.library._ssc_stats = fake.SSC_INFO
|
||||||
self.library._perf_library.get_node_utilization_for_pool = (
|
self.library._perf_library.get_node_utilization_for_pool = (
|
||||||
mock.Mock(side_effect=[30.0, 42.0]))
|
mock.Mock(side_effect=[30.0, 42.0]))
|
||||||
@ -430,6 +445,7 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
|
|||||||
self.library, '_get_aggregate_space',
|
self.library, '_get_aggregate_space',
|
||||||
mock.Mock(return_value=fake.AGGREGATE_CAPACITIES_VSERVER_CREDS))
|
mock.Mock(return_value=fake.AGGREGATE_CAPACITIES_VSERVER_CREDS))
|
||||||
self.library._have_cluster_creds = False
|
self.library._have_cluster_creds = False
|
||||||
|
self.library._cluster_info = fake.CLUSTER_INFO
|
||||||
self.library._ssc_stats = fake.SSC_INFO_VSERVER_CREDS
|
self.library._ssc_stats = fake.SSC_INFO_VSERVER_CREDS
|
||||||
self.library._perf_library.get_node_utilization_for_pool = (
|
self.library._perf_library.get_node_utilization_for_pool = (
|
||||||
mock.Mock(side_effect=[50.0, 50.0]))
|
mock.Mock(side_effect=[50.0, 50.0]))
|
||||||
@ -672,7 +688,7 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
|
|||||||
vserver_client.create_volume.assert_called_once_with(
|
vserver_client.create_volume.assert_called_once_with(
|
||||||
fake.POOL_NAME, fake.SHARE_NAME, fake.SHARE['size'],
|
fake.POOL_NAME, fake.SHARE_NAME, fake.SHARE['size'],
|
||||||
thin_provisioned=True, snapshot_policy='default',
|
thin_provisioned=True, snapshot_policy='default',
|
||||||
language='en-US', dedup_enabled=True, split=True,
|
language='en-US', dedup_enabled=True, split=True, encrypt=False,
|
||||||
compression_enabled=False, max_files=5000, snapshot_reserve=8)
|
compression_enabled=False, max_files=5000, snapshot_reserve=8)
|
||||||
|
|
||||||
def test_remap_standard_boolean_extra_specs(self):
|
def test_remap_standard_boolean_extra_specs(self):
|
||||||
@ -703,7 +719,7 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
|
|||||||
fake.POOL_NAME, fake.SHARE_NAME, fake.SHARE['size'],
|
fake.POOL_NAME, fake.SHARE_NAME, fake.SHARE['size'],
|
||||||
thin_provisioned=True, snapshot_policy='default',
|
thin_provisioned=True, snapshot_policy='default',
|
||||||
language='en-US', dedup_enabled=True, split=True,
|
language='en-US', dedup_enabled=True, split=True,
|
||||||
compression_enabled=False, max_files=5000,
|
compression_enabled=False, max_files=5000, encrypt=False,
|
||||||
snapshot_reserve=8, volume_type='dp')
|
snapshot_reserve=8, volume_type='dp')
|
||||||
|
|
||||||
def test_allocate_container_no_pool_name(self):
|
def test_allocate_container_no_pool_name(self):
|
||||||
@ -834,18 +850,6 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
|
|||||||
mock_get_provisioning_options.assert_called_once_with(extra_specs)
|
mock_get_provisioning_options.assert_called_once_with(extra_specs)
|
||||||
mock_get_normalized_qos_specs.assert_called_once_with(extra_specs)
|
mock_get_normalized_qos_specs.assert_called_once_with(extra_specs)
|
||||||
|
|
||||||
def test_get_provisioning_options(self):
|
|
||||||
result = self.library._get_provisioning_options(fake.EXTRA_SPEC)
|
|
||||||
|
|
||||||
self.assertEqual(fake.PROVISIONING_OPTIONS, result)
|
|
||||||
|
|
||||||
def test_get_provisioning_options_missing_spec(self):
|
|
||||||
result = self.library._get_provisioning_options(
|
|
||||||
fake.SHORT_BOOLEAN_EXTRA_SPEC)
|
|
||||||
|
|
||||||
self.assertEqual(
|
|
||||||
fake.PROVISIONING_OPTIONS_BOOLEAN_THIN_PROVISIONED_TRUE, result)
|
|
||||||
|
|
||||||
def test_get_provisioning_options_implicit_false(self):
|
def test_get_provisioning_options_implicit_false(self):
|
||||||
result = self.library._get_provisioning_options(
|
result = self.library._get_provisioning_options(
|
||||||
fake.EMPTY_EXTRA_SPEC)
|
fake.EMPTY_EXTRA_SPEC)
|
||||||
@ -858,6 +862,7 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
|
|||||||
'compression_enabled': False,
|
'compression_enabled': False,
|
||||||
'dedup_enabled': False,
|
'dedup_enabled': False,
|
||||||
'split': False,
|
'split': False,
|
||||||
|
'encrypt': False,
|
||||||
}
|
}
|
||||||
|
|
||||||
self.assertEqual(expected, result)
|
self.assertEqual(expected, result)
|
||||||
@ -1048,7 +1053,7 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
|
|||||||
vserver_client.create_volume_clone.assert_called_once_with(
|
vserver_client.create_volume_clone.assert_called_once_with(
|
||||||
share_name, parent_share_name, parent_snapshot_name,
|
share_name, parent_share_name, parent_snapshot_name,
|
||||||
thin_provisioned=True, snapshot_policy='default',
|
thin_provisioned=True, snapshot_policy='default',
|
||||||
language='en-US', dedup_enabled=True, split=True,
|
language='en-US', dedup_enabled=True, split=True, encrypt=False,
|
||||||
compression_enabled=False, max_files=5000)
|
compression_enabled=False, max_files=5000)
|
||||||
if size > original_snapshot_size:
|
if size > original_snapshot_size:
|
||||||
vserver_client.set_volume_size.assert_called_once_with(
|
vserver_client.set_volume_size.assert_called_once_with(
|
||||||
@ -1587,8 +1592,6 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
|
|||||||
|
|
||||||
mock_get_volume_to_manage.assert_called_once_with(
|
mock_get_volume_to_manage.assert_called_once_with(
|
||||||
fake.POOL_NAME, fake.FLEXVOL_NAME)
|
fake.POOL_NAME, fake.FLEXVOL_NAME)
|
||||||
mock_validate_volume_for_manage.assert_called_once_with(
|
|
||||||
fake.FLEXVOL_TO_MANAGE, vserver_client)
|
|
||||||
mock_check_extra_specs_validity.assert_called_once_with(
|
mock_check_extra_specs_validity.assert_called_once_with(
|
||||||
share_to_manage, extra_specs)
|
share_to_manage, extra_specs)
|
||||||
mock_check_aggregate_extra_specs_validity.assert_called_once_with(
|
mock_check_aggregate_extra_specs_validity.assert_called_once_with(
|
||||||
@ -1603,6 +1606,7 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
|
|||||||
fake.POOL_NAME, fake.SHARE_NAME, **provisioning_opts)
|
fake.POOL_NAME, fake.SHARE_NAME, **provisioning_opts)
|
||||||
mock_modify_or_create_qos_policy.assert_called_once_with(
|
mock_modify_or_create_qos_policy.assert_called_once_with(
|
||||||
share_to_manage, extra_specs, fake.VSERVER1, vserver_client)
|
share_to_manage, extra_specs, fake.VSERVER1, vserver_client)
|
||||||
|
mock_validate_volume_for_manage.assert_called()
|
||||||
|
|
||||||
original_data = {
|
original_data = {
|
||||||
'original_name': fake.FLEXVOL_TO_MANAGE['name'],
|
'original_name': fake.FLEXVOL_TO_MANAGE['name'],
|
||||||
@ -4209,6 +4213,8 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
|
|||||||
self.mock_object(self.library, '_check_aggregate_extra_specs_validity')
|
self.mock_object(self.library, '_check_aggregate_extra_specs_validity')
|
||||||
mock_vserver_compatibility_check = self.mock_object(
|
mock_vserver_compatibility_check = self.mock_object(
|
||||||
self.library, '_check_destination_vserver_for_vol_move')
|
self.library, '_check_destination_vserver_for_vol_move')
|
||||||
|
self.mock_object(self.library, '_get_dest_flexvol_encryption_value',
|
||||||
|
mock.Mock(return_value=False))
|
||||||
|
|
||||||
migration_compatibility = self.library.migration_check_compatibility(
|
migration_compatibility = self.library.migration_check_compatibility(
|
||||||
self.context, fake_share.fake_share_instance(),
|
self.context, fake_share.fake_share_instance(),
|
||||||
@ -4326,6 +4332,8 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
|
|||||||
mock_move_check = self.mock_object(
|
mock_move_check = self.mock_object(
|
||||||
self.client, 'check_volume_move',
|
self.client, 'check_volume_move',
|
||||||
mock.Mock(side_effect=netapp_api.NaApiError))
|
mock.Mock(side_effect=netapp_api.NaApiError))
|
||||||
|
self.mock_object(self.library, '_get_dest_flexvol_encryption_value',
|
||||||
|
mock.Mock(return_value=False))
|
||||||
|
|
||||||
migration_compatibility = self.library.migration_check_compatibility(
|
migration_compatibility = self.library.migration_check_compatibility(
|
||||||
self.context, fake_share.fake_share_instance(),
|
self.context, fake_share.fake_share_instance(),
|
||||||
@ -4344,7 +4352,8 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
|
|||||||
data_motion.get_backend_configuration.assert_called_once_with(
|
data_motion.get_backend_configuration.assert_called_once_with(
|
||||||
'destination_backend')
|
'destination_backend')
|
||||||
mock_move_check.assert_called_once_with(
|
mock_move_check.assert_called_once_with(
|
||||||
fake.SHARE_NAME, fake.VSERVER1, 'destination_pool')
|
fake.SHARE_NAME, fake.VSERVER1, 'destination_pool',
|
||||||
|
encrypt_destination=False)
|
||||||
self.library._get_vserver.assert_has_calls(
|
self.library._get_vserver.assert_has_calls(
|
||||||
[mock.call(share_server=fake.SHARE_SERVER),
|
[mock.call(share_server=fake.SHARE_SERVER),
|
||||||
mock.call(share_server='dst_srv')])
|
mock.call(share_server='dst_srv')])
|
||||||
@ -4362,6 +4371,8 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
|
|||||||
self.mock_object(share_utils, 'extract_host', mock.Mock(
|
self.mock_object(share_utils, 'extract_host', mock.Mock(
|
||||||
side_effect=['destination_backend', 'destination_pool']))
|
side_effect=['destination_backend', 'destination_pool']))
|
||||||
mock_move_check = self.mock_object(self.client, 'check_volume_move')
|
mock_move_check = self.mock_object(self.client, 'check_volume_move')
|
||||||
|
self.mock_object(self.library, '_get_dest_flexvol_encryption_value',
|
||||||
|
mock.Mock(return_value=False))
|
||||||
|
|
||||||
migration_compatibility = self.library.migration_check_compatibility(
|
migration_compatibility = self.library.migration_check_compatibility(
|
||||||
self.context, fake_share.fake_share_instance(),
|
self.context, fake_share.fake_share_instance(),
|
||||||
@ -4379,7 +4390,51 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
|
|||||||
data_motion.get_backend_configuration.assert_called_once_with(
|
data_motion.get_backend_configuration.assert_called_once_with(
|
||||||
'destination_backend')
|
'destination_backend')
|
||||||
mock_move_check.assert_called_once_with(
|
mock_move_check.assert_called_once_with(
|
||||||
fake.SHARE_NAME, fake.VSERVER1, 'destination_pool')
|
fake.SHARE_NAME, fake.VSERVER1, 'destination_pool',
|
||||||
|
encrypt_destination=False)
|
||||||
|
self.library._get_vserver.assert_has_calls(
|
||||||
|
[mock.call(share_server=fake.SHARE_SERVER),
|
||||||
|
mock.call(share_server='dst_srv')])
|
||||||
|
|
||||||
|
def test_migration_check_compatibility_destination_type_is_encrypted(self):
|
||||||
|
self.library._have_cluster_creds = True
|
||||||
|
self.mock_object(self.library, '_get_backend_share_name',
|
||||||
|
mock.Mock(return_value=fake.SHARE_NAME))
|
||||||
|
self.mock_object(data_motion, 'get_backend_configuration')
|
||||||
|
self.mock_object(self.library, '_get_vserver',
|
||||||
|
mock.Mock(return_value=(fake.VSERVER1, mock.Mock())))
|
||||||
|
self.mock_object(share_utils, 'extract_host', mock.Mock(
|
||||||
|
side_effect=['destination_backend', 'destination_pool']))
|
||||||
|
mock_move_check = self.mock_object(self.client, 'check_volume_move')
|
||||||
|
self.mock_object(self.library, '_get_dest_flexvol_encryption_value',
|
||||||
|
mock.Mock(return_value=True))
|
||||||
|
self.mock_object(share_types, 'get_extra_specs_from_share',
|
||||||
|
mock.Mock(return_value={'spec1': 'spec-data'}))
|
||||||
|
self.mock_object(self.library,
|
||||||
|
'_check_extra_specs_validity')
|
||||||
|
self.mock_object(self.library,
|
||||||
|
'_check_aggregate_extra_specs_validity')
|
||||||
|
|
||||||
|
migration_compatibility = self.library.migration_check_compatibility(
|
||||||
|
self.context, fake_share.fake_share_instance(),
|
||||||
|
fake_share.fake_share_instance(), share_server=fake.SHARE_SERVER,
|
||||||
|
destination_share_server='dst_srv')
|
||||||
|
|
||||||
|
expected_compatibility = {
|
||||||
|
'compatible': True,
|
||||||
|
'writable': True,
|
||||||
|
'nondisruptive': True,
|
||||||
|
'preserve_metadata': True,
|
||||||
|
'preserve_snapshots': True,
|
||||||
|
}
|
||||||
|
self.assertDictMatch(expected_compatibility, migration_compatibility)
|
||||||
|
data_motion.get_backend_configuration.assert_called_once_with(
|
||||||
|
'destination_backend')
|
||||||
|
|
||||||
|
mock_move_check.assert_called_once_with(
|
||||||
|
fake.SHARE_NAME, fake.VSERVER1, 'destination_pool',
|
||||||
|
encrypt_destination=True)
|
||||||
|
|
||||||
self.library._get_vserver.assert_has_calls(
|
self.library._get_vserver.assert_has_calls(
|
||||||
[mock.call(share_server=fake.SHARE_SERVER),
|
[mock.call(share_server=fake.SHARE_SERVER),
|
||||||
mock.call(share_server='dst_srv')])
|
mock.call(share_server='dst_srv')])
|
||||||
@ -4395,6 +4450,8 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
|
|||||||
self.mock_object(share_utils, 'extract_host',
|
self.mock_object(share_utils, 'extract_host',
|
||||||
mock.Mock(return_value='destination_pool'))
|
mock.Mock(return_value='destination_pool'))
|
||||||
mock_move = self.mock_object(self.client, 'start_volume_move')
|
mock_move = self.mock_object(self.client, 'start_volume_move')
|
||||||
|
self.mock_object(self.library, '_get_dest_flexvol_encryption_value',
|
||||||
|
mock.Mock(return_value=False))
|
||||||
|
|
||||||
retval = self.library.migration_start(
|
retval = self.library.migration_start(
|
||||||
self.context, fake_share.fake_share_instance(),
|
self.context, fake_share.fake_share_instance(),
|
||||||
@ -4405,7 +4462,34 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
|
|||||||
self.assertIsNone(retval)
|
self.assertIsNone(retval)
|
||||||
self.assertTrue(mock_info_log.called)
|
self.assertTrue(mock_info_log.called)
|
||||||
mock_move.assert_called_once_with(
|
mock_move.assert_called_once_with(
|
||||||
fake.SHARE_NAME, fake.VSERVER1, 'destination_pool')
|
fake.SHARE_NAME, fake.VSERVER1, 'destination_pool',
|
||||||
|
encrypt_destination=False)
|
||||||
|
|
||||||
|
def test_migration_start_encrypted_destination(self):
|
||||||
|
mock_info_log = self.mock_object(lib_base.LOG, 'info')
|
||||||
|
source_snapshots = mock.Mock()
|
||||||
|
snapshot_mappings = mock.Mock()
|
||||||
|
self.mock_object(self.library, '_get_vserver',
|
||||||
|
mock.Mock(return_value=(fake.VSERVER1, mock.Mock())))
|
||||||
|
self.mock_object(self.library, '_get_backend_share_name',
|
||||||
|
mock.Mock(return_value=fake.SHARE_NAME))
|
||||||
|
self.mock_object(share_utils, 'extract_host',
|
||||||
|
mock.Mock(return_value='destination_pool'))
|
||||||
|
mock_move = self.mock_object(self.client, 'start_volume_move')
|
||||||
|
self.mock_object(self.library, '_get_dest_flexvol_encryption_value',
|
||||||
|
mock.Mock(return_value=True))
|
||||||
|
|
||||||
|
retval = self.library.migration_start(
|
||||||
|
self.context, fake_share.fake_share_instance(),
|
||||||
|
fake_share.fake_share_instance(),
|
||||||
|
source_snapshots, snapshot_mappings,
|
||||||
|
share_server=fake.SHARE_SERVER, destination_share_server='dst_srv')
|
||||||
|
|
||||||
|
self.assertIsNone(retval)
|
||||||
|
self.assertTrue(mock_info_log.called)
|
||||||
|
mock_move.assert_called_once_with(
|
||||||
|
fake.SHARE_NAME, fake.VSERVER1, 'destination_pool',
|
||||||
|
encrypt_destination=True)
|
||||||
|
|
||||||
def test_migration_continue_volume_move_failed(self):
|
def test_migration_continue_volume_move_failed(self):
|
||||||
source_snapshots = mock.Mock()
|
source_snapshots = mock.Mock()
|
||||||
|
@ -103,6 +103,7 @@ SHARE = {
|
|||||||
'replica_state': constants.REPLICA_STATE_ACTIVE,
|
'replica_state': constants.REPLICA_STATE_ACTIVE,
|
||||||
'status': constants.STATUS_AVAILABLE,
|
'status': constants.STATUS_AVAILABLE,
|
||||||
'share_server': None,
|
'share_server': None,
|
||||||
|
'encrypt': False,
|
||||||
}
|
}
|
||||||
|
|
||||||
FLEXVOL_TO_MANAGE = {
|
FLEXVOL_TO_MANAGE = {
|
||||||
@ -126,6 +127,16 @@ QOS_POLICY_GROUP = {
|
|||||||
'num-workloads': 1,
|
'num-workloads': 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FLEXVOL = {
|
||||||
|
'aggregate': POOL_NAME,
|
||||||
|
'junction-path': '/%s' % FLEXVOL_NAME,
|
||||||
|
'name': FLEXVOL_NAME,
|
||||||
|
'type': 'rw',
|
||||||
|
'style': 'flex',
|
||||||
|
'size': '1610612736', # rounds down to 1 GB,
|
||||||
|
'owning-vserver-name': VSERVER1,
|
||||||
|
}
|
||||||
|
|
||||||
EXTRA_SPEC = {
|
EXTRA_SPEC = {
|
||||||
'netapp:thin_provisioned': 'true',
|
'netapp:thin_provisioned': 'true',
|
||||||
'netapp:snapshot_policy': 'default',
|
'netapp:snapshot_policy': 'default',
|
||||||
@ -136,6 +147,7 @@ EXTRA_SPEC = {
|
|||||||
'netapp:split_clone_on_create': 'true',
|
'netapp:split_clone_on_create': 'true',
|
||||||
'netapp_disk_type': 'FCAL',
|
'netapp_disk_type': 'FCAL',
|
||||||
'netapp_raid_type': 'raid4',
|
'netapp_raid_type': 'raid4',
|
||||||
|
'netapp_flexvol_encryption': 'true',
|
||||||
}
|
}
|
||||||
|
|
||||||
EXTRA_SPEC_WITH_QOS = copy.deepcopy(EXTRA_SPEC)
|
EXTRA_SPEC_WITH_QOS = copy.deepcopy(EXTRA_SPEC)
|
||||||
@ -158,6 +170,7 @@ PROVISIONING_OPTIONS = {
|
|||||||
'compression_enabled': False,
|
'compression_enabled': False,
|
||||||
'max_files': 5000,
|
'max_files': 5000,
|
||||||
'split': True,
|
'split': True,
|
||||||
|
'encrypt': False,
|
||||||
}
|
}
|
||||||
|
|
||||||
PROVISIONING_OPTIONS_WITH_QOS = copy.deepcopy(PROVISIONING_OPTIONS)
|
PROVISIONING_OPTIONS_WITH_QOS = copy.deepcopy(PROVISIONING_OPTIONS)
|
||||||
@ -179,6 +192,7 @@ PROVISIONING_OPTIONS_BOOLEAN_THIN_PROVISIONED_TRUE = {
|
|||||||
'compression_enabled': False,
|
'compression_enabled': False,
|
||||||
'max_files': None,
|
'max_files': None,
|
||||||
'split': False,
|
'split': False,
|
||||||
|
'encrypt': False,
|
||||||
}
|
}
|
||||||
|
|
||||||
PROVISIONING_OPTIONS_STRING = {
|
PROVISIONING_OPTIONS_STRING = {
|
||||||
@ -626,6 +640,7 @@ POOLS = [
|
|||||||
'dedupe': [True, False],
|
'dedupe': [True, False],
|
||||||
'compression': [True, False],
|
'compression': [True, False],
|
||||||
'thin_provisioning': [True, False],
|
'thin_provisioning': [True, False],
|
||||||
|
'netapp_flexvol_encryption': True,
|
||||||
'netapp_raid_type': 'raid4',
|
'netapp_raid_type': 'raid4',
|
||||||
'netapp_disk_type': 'FCAL',
|
'netapp_disk_type': 'FCAL',
|
||||||
'netapp_hybrid_aggregate': 'false',
|
'netapp_hybrid_aggregate': 'false',
|
||||||
@ -648,6 +663,7 @@ POOLS = [
|
|||||||
'dedupe': [True, False],
|
'dedupe': [True, False],
|
||||||
'compression': [True, False],
|
'compression': [True, False],
|
||||||
'thin_provisioning': [True, False],
|
'thin_provisioning': [True, False],
|
||||||
|
'netapp_flexvol_encryption': True,
|
||||||
'netapp_raid_type': 'raid_dp',
|
'netapp_raid_type': 'raid_dp',
|
||||||
'netapp_disk_type': ['SATA', 'SSD'],
|
'netapp_disk_type': ['SATA', 'SSD'],
|
||||||
'netapp_hybrid_aggregate': 'true',
|
'netapp_hybrid_aggregate': 'true',
|
||||||
@ -673,6 +689,7 @@ POOLS_VSERVER_CREDS = [
|
|||||||
'dedupe': [True, False],
|
'dedupe': [True, False],
|
||||||
'compression': [True, False],
|
'compression': [True, False],
|
||||||
'thin_provisioning': [True, False],
|
'thin_provisioning': [True, False],
|
||||||
|
'netapp_flexvol_encryption': True,
|
||||||
'utilization': 50.0,
|
'utilization': 50.0,
|
||||||
'filter_function': None,
|
'filter_function': None,
|
||||||
'goodness_function': None,
|
'goodness_function': None,
|
||||||
@ -692,6 +709,7 @@ POOLS_VSERVER_CREDS = [
|
|||||||
'dedupe': [True, False],
|
'dedupe': [True, False],
|
||||||
'compression': [True, False],
|
'compression': [True, False],
|
||||||
'thin_provisioning': [True, False],
|
'thin_provisioning': [True, False],
|
||||||
|
'netapp_flexvol_encryption': True,
|
||||||
'utilization': 50.0,
|
'utilization': 50.0,
|
||||||
'filter_function': None,
|
'filter_function': None,
|
||||||
'goodness_function': None,
|
'goodness_function': None,
|
||||||
@ -715,6 +733,11 @@ SSC_AGGREGATES = [
|
|||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
CLUSTER_INFO = {
|
||||||
|
'nodes': CLUSTER_NODES,
|
||||||
|
'nve_support': True,
|
||||||
|
}
|
||||||
|
|
||||||
SSC_DISK_TYPES = ['FCAL', ['SATA', 'SSD']]
|
SSC_DISK_TYPES = ['FCAL', ['SATA', 'SSD']]
|
||||||
|
|
||||||
NODE = 'cluster1-01'
|
NODE = 'cluster1-01'
|
||||||
|
@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- Now Manila NetApp ONTAP driver supports NetApp Volume Encryption
|
||||||
|
(NVE) which allows the creation of volumes that will be encrypted
|
||||||
|
at rest.
|
Loading…
Reference in New Issue
Block a user