diff --git a/cinder/tests/unit/volume/drivers/netapp/dataontap/client/fakes.py b/cinder/tests/unit/volume/drivers/netapp/dataontap/client/fakes.py
index a8aed380a3d..47f2065c751 100644
--- a/cinder/tests/unit/volume/drivers/netapp/dataontap/client/fakes.py
+++ b/cinder/tests/unit/volume/drivers/netapp/dataontap/client/fakes.py
@@ -889,6 +889,47 @@ CLONE_SPLIT_STATUS_NO_DATA_RESPONSE = etree.XML("""
""")
+VOLUME_GET_ITER_ENCRYPTION_SSC_RESPONSE = etree.XML("""
+
+
+
+ true
+
+ %(aggr)s
+ /%(volume)s
+ %(volume)s
+ %(vserver)s
+ rw
+
+
+ false
+ false
+
+
+ fake_qos_policy_group_name
+
+
+ true
+ none
+ 5
+ 12345
+
+
+ default
+
+
+ en_US
+
+
+
+ 1
+
+""" % {
+ 'aggr': VOLUME_AGGREGATE_NAMES[0],
+ 'volume': VOLUME_NAMES[0],
+ 'vserver': VOLUME_VSERVER_NAME,
+})
+
STORAGE_DISK_GET_ITER_RESPONSE_PAGE_1 = etree.XML("""
diff --git a/cinder/tests/unit/volume/drivers/netapp/dataontap/client/test_client_cmode.py b/cinder/tests/unit/volume/drivers/netapp/dataontap/client/test_client_cmode.py
index 15dd93b65fd..dc47d6add44 100644
--- a/cinder/tests/unit/volume/drivers/netapp/dataontap/client/test_client_cmode.py
+++ b/cinder/tests/unit/volume/drivers/netapp/dataontap/client/test_client_cmode.py
@@ -1940,6 +1940,72 @@ class NetAppCmodeClientTestCase(test.TestCase):
self.assertFalse(result)
+ def test_is_flexvol_encrypted(self):
+
+ api_response = netapp_api.NaElement(
+ fake_client.VOLUME_GET_ITER_ENCRYPTION_SSC_RESPONSE)
+ self.client.features.add_feature('FLEXVOL_ENCRYPTION')
+ self.mock_object(self.client,
+ 'send_iter_request',
+ return_value=api_response)
+
+ result = self.client.is_flexvol_encrypted(
+ fake_client.VOLUME_NAMES[0], fake_client.VOLUME_VSERVER_NAME)
+
+ volume_get_iter_args = {
+ 'query': {
+ 'volume-attributes': {
+ 'encrypt': 'true',
+ 'volume-id-attributes': {
+ 'name': fake_client.VOLUME_NAME,
+ 'owning-vserver-name': fake_client.VOLUME_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_unsupported_version(self):
+
+ self.client.features.add_feature('FLEXVOL_ENCRYPTION', supported=False)
+ result = self.client.is_flexvol_encrypted(
+ fake_client.VOLUME_NAMES[0], fake_client.VOLUME_VSERVER_NAME)
+
+ self.assertFalse(result)
+
+ def test_is_flexvol_encrypted_no_records_found(self):
+
+ api_response = netapp_api.NaElement(
+ fake_client.NO_RECORDS_RESPONSE)
+ self.mock_object(self.client,
+ 'send_request',
+ return_value=api_response)
+
+ result = self.client.is_flexvol_encrypted(
+ fake_client.VOLUME_NAMES[0], fake_client.VOLUME_VSERVER_NAME)
+
+ self.assertFalse(result)
+
+ def test_is_flexvol_encrypted_api_error(self):
+
+ self.mock_object(self.client,
+ 'send_request',
+ side_effect=self._mock_api_error())
+
+ result = self.client.is_flexvol_encrypted(
+ fake_client.VOLUME_NAMES[0], fake_client.VOLUME_VSERVER_NAME)
+
+ self.assertFalse(result)
+
def test_get_aggregates(self):
api_response = netapp_api.NaElement(
diff --git a/cinder/tests/unit/volume/drivers/netapp/dataontap/utils/fakes.py b/cinder/tests/unit/volume/drivers/netapp/dataontap/utils/fakes.py
index adb4bc25052..47ffccaa2c4 100644
--- a/cinder/tests/unit/volume/drivers/netapp/dataontap/utils/fakes.py
+++ b/cinder/tests/unit/volume/drivers/netapp/dataontap/utils/fakes.py
@@ -41,6 +41,7 @@ SSC = {
'netapp_raid_type': 'raid_dp',
'netapp_disk_type': ['SSD'],
'netapp_hybrid_aggregate': 'false',
+ 'netapp_flexvol_encryption': 'true',
'pool_name': 'volume1',
},
'volume2': {
@@ -54,6 +55,7 @@ SSC = {
'netapp_raid_type': 'raid_dp',
'netapp_disk_type': ['FCAL', 'SSD'],
'netapp_hybrid_aggregate': 'true',
+ 'netapp_flexvol_encryption': 'false',
'pool_name': 'volume2',
},
}
@@ -84,6 +86,15 @@ SSC_DEDUPE_INFO = {
},
}
+SSC_ENCRYPTION_INFO = {
+ 'volume1': {
+ 'netapp_flexvol_encryption': 'true',
+ },
+ 'volume2': {
+ 'netapp_flexvol_encryption': 'false',
+ },
+}
+
SSC_MIRROR_INFO = {
'volume1': {
'netapp_mirrored': 'false',
@@ -118,6 +129,19 @@ PROVISIONING_OPTS = {
'size': 20,
}
+ENCRYPTED_PROVISIONING_OPTS = {
+ 'aggregate': 'fake_aggregate',
+ 'thin_provisioned': True,
+ 'snapshot_policy': None,
+ 'language': 'en_US',
+ 'dedupe_enabled': False,
+ 'compression_enabled': False,
+ 'snapshot_reserve': '12',
+ 'volume_type': 'rw',
+ 'size': 20,
+ 'encrypt': 'true',
+}
+
def get_fake_cmode_config(backend_name):
diff --git a/cinder/tests/unit/volume/drivers/netapp/dataontap/utils/test_capabilities.py b/cinder/tests/unit/volume/drivers/netapp/dataontap/utils/test_capabilities.py
index 5c11f366e76..ec8f7cbb7fc 100644
--- a/cinder/tests/unit/volume/drivers/netapp/dataontap/utils/test_capabilities.py
+++ b/cinder/tests/unit/volume/drivers/netapp/dataontap/utils/test_capabilities.py
@@ -134,6 +134,10 @@ class CapabilitiesLibraryTestCase(test.TestCase):
self.ssc_library, '_get_ssc_aggregate_info',
side_effect=[fake.SSC_AGGREGATE_INFO['volume1'],
fake.SSC_AGGREGATE_INFO['volume2']])
+ mock_get_ssc_encryption_info = self.mock_object(
+ self.ssc_library, '_get_ssc_encryption_info',
+ side_effect=[fake.SSC_ENCRYPTION_INFO['volume1'],
+ fake.SSC_ENCRYPTION_INFO['volume2']])
ordered_ssc = collections.OrderedDict()
ordered_ssc['volume1'] = fake.SSC_VOLUME_MAP['volume1']
ordered_ssc['volume2'] = fake.SSC_VOLUME_MAP['volume2']
@@ -150,6 +154,8 @@ class CapabilitiesLibraryTestCase(test.TestCase):
mock.call('volume1'), mock.call('volume2')])
mock_get_ssc_aggregate_info.assert_has_calls([
mock.call('aggr1'), mock.call('aggr2')])
+ mock_get_ssc_encryption_info.assert_has_calls([
+ mock.call('volume1'), mock.call('volume2')])
def test__update_for_failover(self):
self.mock_object(self.ssc_library, 'update_ssc')
@@ -282,6 +288,22 @@ class CapabilitiesLibraryTestCase(test.TestCase):
self.zapi_client.get_flexvol_dedupe_info.assert_called_once_with(
fake_client.VOLUME_NAMES[0])
+ def test_get_ssc_encryption_info(self):
+
+ self.mock_object(
+ self.ssc_library.zapi_client, 'is_flexvol_encrypted',
+ return_value=True)
+
+ result = self.ssc_library._get_ssc_encryption_info(
+ fake_client.VOLUME_NAMES[0])
+
+ expected = {
+ 'netapp_flexvol_encryption': 'true',
+ }
+ self.assertEqual(expected, result)
+ self.zapi_client.is_flexvol_encrypted.assert_called_once_with(
+ fake_client.VOLUME_NAMES[0], fake_client.VOLUME_VSERVER_NAME)
+
@ddt.data(True, False)
def test_get_ssc_mirror_info(self, mirrored):
diff --git a/cinder/tests/unit/volume/drivers/netapp/dataontap/utils/test_data_motion.py b/cinder/tests/unit/volume/drivers/netapp/dataontap/utils/test_data_motion.py
index f6e57e5738b..4e352126eb6 100644
--- a/cinder/tests/unit/volume/drivers/netapp/dataontap/utils/test_data_motion.py
+++ b/cinder/tests/unit/volume/drivers/netapp/dataontap/utils/test_data_motion.py
@@ -543,6 +543,9 @@ class NetAppCDOTDataMotionMixinTestCase(test.TestCase):
mock_get_provisioning_opts_call = self.mock_object(
self.mock_src_client, 'get_provisioning_options_from_flexvol',
return_value=provisioning_opts)
+ mock_is_flexvol_encrypted = self.mock_object(
+ self.mock_src_client, 'is_flexvol_encrypted',
+ return_value=False)
self.mock_object(self.dm_mixin, '_get_replication_aggregate_map',
return_value=aggr_map)
mock_client_call = self.mock_object(
@@ -560,6 +563,45 @@ class NetAppCDOTDataMotionMixinTestCase(test.TestCase):
mock_client_call.assert_called_once_with(
self.dest_flexvol_name, 'aggr01', fakes.PROVISIONING_OPTS['size'],
volume_type='dp', **expected_prov_opts)
+ mock_is_flexvol_encrypted.assert_called_once_with(
+ self.src_flexvol_name, self.src_vserver)
+
+ def test_create_encrypted_destination_flexvol(self):
+ aggr_map = {
+ fakes.ENCRYPTED_PROVISIONING_OPTS['aggregate']: 'aggr01',
+ 'aggr20': 'aggr02',
+ }
+ provisioning_opts = copy.deepcopy(fakes.ENCRYPTED_PROVISIONING_OPTS)
+ expected_prov_opts = copy.deepcopy(fakes.ENCRYPTED_PROVISIONING_OPTS)
+ expected_prov_opts.pop('volume_type', None)
+ expected_prov_opts.pop('size', None)
+ expected_prov_opts.pop('aggregate', None)
+ mock_get_provisioning_opts_call = self.mock_object(
+ self.mock_src_client, 'get_provisioning_options_from_flexvol',
+ return_value=provisioning_opts)
+ mock_is_flexvol_encrypted = self.mock_object(
+ self.mock_src_client, 'is_flexvol_encrypted',
+ return_value=True)
+ self.mock_object(self.dm_mixin, '_get_replication_aggregate_map',
+ return_value=aggr_map)
+ mock_client_call = self.mock_object(
+ self.mock_dest_client, 'create_flexvol')
+
+ retval = self.dm_mixin.create_destination_flexvol(
+ self.src_backend, self.dest_backend,
+ self.src_flexvol_name, self.dest_flexvol_name)
+
+ self.assertIsNone(retval)
+ mock_get_provisioning_opts_call.assert_called_once_with(
+ self.src_flexvol_name)
+ self.dm_mixin._get_replication_aggregate_map.assert_called_once_with(
+ self.src_backend, self.dest_backend)
+ mock_client_call.assert_called_once_with(
+ self.dest_flexvol_name, 'aggr01',
+ fakes.ENCRYPTED_PROVISIONING_OPTS['size'],
+ volume_type='dp', **expected_prov_opts)
+ mock_is_flexvol_encrypted.assert_called_once_with(
+ self.src_flexvol_name, self.src_vserver)
def test_ensure_snapmirrors(self):
flexvols = ['nvol1', 'nvol2']
diff --git a/cinder/volume/drivers/netapp/dataontap/client/client_cmode.py b/cinder/volume/drivers/netapp/dataontap/client/client_cmode.py
index a13097efedf..3cb8b8d8d95 100644
--- a/cinder/volume/drivers/netapp/dataontap/client/client_cmode.py
+++ b/cinder/volume/drivers/netapp/dataontap/client/client_cmode.py
@@ -2,6 +2,7 @@
# Copyright (c) 2014 Clinton Knight. All rights reserved.
# Copyright (c) 2015 Tom Barron. All rights reserved.
# Copyright (c) 2016 Mike Rooney. All rights reserved.
+# Copyright (c) 2017 Jose Porrua. All rights reserved.
#
# 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
@@ -60,6 +61,7 @@ class Client(client_base.Client):
ontapi_1_2x = (1, 20) <= ontapi_version < (1, 30)
ontapi_1_30 = ontapi_version >= (1, 30)
ontapi_1_100 = ontapi_version >= (1, 100)
+ ontapi_1_1xx = (1, 100) <= ontapi_version < (1, 200)
self.features.add_feature('SNAPMIRROR_V2', supported=ontapi_1_20)
self.features.add_feature('USER_CAPABILITY_LIST',
@@ -73,6 +75,7 @@ class Client(client_base.Client):
supported=ontapi_1_30)
self.features.add_feature('BACKUP_CLONE_PARAM', supported=ontapi_1_100)
self.features.add_feature('CLUSTER_PEER_POLICY', supported=ontapi_1_30)
+ self.features.add_feature('FLEXVOL_ENCRYPTION', supported=ontapi_1_1xx)
def _invoke_vserver_api(self, na_element, vserver):
server = copy.copy(self.connection)
@@ -1163,6 +1166,41 @@ class Client(client_base.Client):
return True
+ def is_flexvol_encrypted(self, flexvol_name, vserver_name):
+ """Check if a flexvol is encrypted."""
+
+ if not self.features.FLEXVOL_ENCRYPTION:
+ return False
+
+ api_args = {
+ 'query': {
+ 'volume-attributes': {
+ 'encrypt': 'true',
+ 'volume-id-attributes': {
+ 'name': flexvol_name,
+ 'owning-vserver-name': vserver_name,
+ },
+ },
+ },
+ 'desired-attributes': {
+ 'volume-attributes': {
+ 'encrypt': None,
+ },
+ },
+ }
+
+ try:
+ result = self.send_iter_request('volume-get-iter', api_args)
+ except netapp_api.NaApiError:
+ msg = _LE('Failed to get Encryption info for volume %s.')
+ LOG.exception(msg, flexvol_name)
+ return False
+
+ if not self._has_records(result):
+ return False
+
+ return True
+
def create_flexvol(self, flexvol_name, aggregate_name, size_gb,
space_guarantee_type=None, snapshot_policy=None,
language=None, dedupe_enabled=False,
diff --git a/cinder/volume/drivers/netapp/dataontap/utils/capabilities.py b/cinder/volume/drivers/netapp/dataontap/utils/capabilities.py
index 3df17f331c7..14353232d14 100644
--- a/cinder/volume/drivers/netapp/dataontap/utils/capabilities.py
+++ b/cinder/volume/drivers/netapp/dataontap/utils/capabilities.py
@@ -1,4 +1,5 @@
# Copyright (c) 2016 Clinton Knight. All rights reserved.
+# Copyright (c) 2017 Jose Porrua. All rights reserved.
#
# 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
@@ -44,7 +45,9 @@ SSC_API_MAP = {
'netapp_dedup',
'netapp_compression',
],
- ('volume', 'show', 'volume-get-iter'): [],
+ ('volume', 'show', 'volume-get-iter'): [
+ 'netapp_flexvol_encryption',
+ ],
}
@@ -129,6 +132,7 @@ class CapabilitiesLibrary(object):
ssc_volume.update(self._get_ssc_flexvol_info(flexvol_name))
ssc_volume.update(self._get_ssc_dedupe_info(flexvol_name))
ssc_volume.update(self._get_ssc_mirror_info(flexvol_name))
+ ssc_volume.update(self._get_ssc_encryption_info(flexvol_name))
# Get aggregate info
aggregate_name = ssc_volume.get('netapp_aggregate')
@@ -189,6 +193,13 @@ class CapabilitiesLibrary(object):
'netapp_compression': six.text_type(compression).lower(),
}
+ def _get_ssc_encryption_info(self, flexvol_name):
+ """Gather flexvol encryption info and recast into SSC-style stats."""
+ encrypted = self.zapi_client.is_flexvol_encrypted(
+ flexvol_name, self.vserver_name)
+
+ return {'netapp_flexvol_encryption': six.text_type(encrypted).lower()}
+
def _get_ssc_mirror_info(self, flexvol_name):
"""Gather SnapMirror info and recast into SSC-style volume stats."""
diff --git a/cinder/volume/drivers/netapp/dataontap/utils/data_motion.py b/cinder/volume/drivers/netapp/dataontap/utils/data_motion.py
index 231a8091144..3ca99142f6d 100644
--- a/cinder/volume/drivers/netapp/dataontap/utils/data_motion.py
+++ b/cinder/volume/drivers/netapp/dataontap/utils/data_motion.py
@@ -417,6 +417,12 @@ class DataMotionMixin(object):
src_flexvol_name)
)
+ # If the source is encrypted then the destination needs to be
+ # encrypted too. Using is_flexvol_encrypted because it includes
+ # a simple check to ensure that the NVE feature is supported.
+ if src_client.is_flexvol_encrypted(src_flexvol_name, src_vserver):
+ provisioning_options['encrypt'] = 'true'
+
# Remove size and volume_type
size = provisioning_options.pop('size', None)
if not size: