1# Copyright (c) 2014 Andrew Kerr.  All rights reserved.
2# Copyright (c) 2015 Tom Barron.  All rights reserved.
3#
4#    Licensed under the Apache License, Version 2.0 (the "License"); you may
5#    not use this file except in compliance with the License. You may obtain
6#    a copy of the License at
7#
8#         http://www.apache.org/licenses/LICENSE-2.0
9#
10#    Unless required by applicable law or agreed to in writing, software
11#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13#    License for the specific language governing permissions and limitations
14#    under the License.
15"""
16Mock unit tests for the NetApp cmode nfs storage driver
17"""
18
19import hashlib
20import uuid
21
22import ddt
23import mock
24from os_brick.remotefs import remotefs as remotefs_brick
25from oslo_utils import units
26
27from cinder import exception
28from cinder.image import image_utils
29from cinder.objects import fields
30from cinder import test
31from cinder.tests.unit.volume.drivers.netapp.dataontap import fakes as fake
32from cinder.tests.unit.volume.drivers.netapp.dataontap.utils import fakes as \
33    fake_ssc
34from cinder.tests.unit.volume.drivers.netapp import fakes as na_fakes
35from cinder import utils
36from cinder.volume.drivers.netapp.dataontap.client import api as netapp_api
37from cinder.volume.drivers.netapp.dataontap.client import client_cmode
38from cinder.volume.drivers.netapp.dataontap import nfs_base
39from cinder.volume.drivers.netapp.dataontap import nfs_cmode
40from cinder.volume.drivers.netapp.dataontap.performance import perf_cmode
41from cinder.volume.drivers.netapp.dataontap.utils import capabilities
42from cinder.volume.drivers.netapp.dataontap.utils import data_motion
43from cinder.volume.drivers.netapp.dataontap.utils import loopingcalls
44from cinder.volume.drivers.netapp.dataontap.utils import utils as dot_utils
45from cinder.volume.drivers.netapp import utils as na_utils
46from cinder.volume.drivers import nfs
47from cinder.volume import utils as volume_utils
48
49
50@ddt.ddt
51class NetAppCmodeNfsDriverTestCase(test.TestCase):
52    def setUp(self):
53        super(NetAppCmodeNfsDriverTestCase, self).setUp()
54
55        kwargs = {
56            'configuration': self.get_config_cmode(),
57            'host': 'openstack@nfscmode',
58        }
59
60        with mock.patch.object(utils, 'get_root_helper',
61                               return_value=mock.Mock()):
62            with mock.patch.object(remotefs_brick, 'RemoteFsClient',
63                                   return_value=mock.Mock()):
64                self.driver = nfs_cmode.NetAppCmodeNfsDriver(**kwargs)
65                self.driver._mounted_shares = [fake.NFS_SHARE]
66                self.driver.ssc_vols = True
67                self.driver.vserver = fake.VSERVER_NAME
68                self.driver.ssc_enabled = True
69                self.driver.perf_library = mock.Mock()
70                self.driver.ssc_library = mock.Mock()
71                self.driver.zapi_client = mock.Mock()
72                self.driver.using_cluster_credentials = True
73
74    def get_config_cmode(self):
75        config = na_fakes.create_configuration_cmode()
76        config.netapp_storage_protocol = 'nfs'
77        config.netapp_login = 'admin'
78        config.netapp_password = 'pass'
79        config.netapp_server_hostname = '127.0.0.1'
80        config.netapp_transport_type = 'http'
81        config.netapp_server_port = '80'
82        config.netapp_vserver = fake.VSERVER_NAME
83        config.netapp_copyoffload_tool_path = 'copyoffload_tool_path'
84        config.netapp_api_trace_pattern = 'fake_regex'
85        return config
86
87    @ddt.data({'active_backend_id': None, 'targets': ['dev1', 'dev2']},
88              {'active_backend_id': None, 'targets': []},
89              {'active_backend_id': 'dev1', 'targets': []},
90              {'active_backend_id': 'dev1', 'targets': ['dev1', 'dev2']})
91    @ddt.unpack
92    def test_init_driver_for_replication(self, active_backend_id,
93                                         targets):
94        kwargs = {
95            'configuration': self.get_config_cmode(),
96            'host': 'openstack@nfscmode',
97            'active_backend_id': active_backend_id,
98        }
99        self.mock_object(data_motion.DataMotionMixin,
100                         'get_replication_backend_names',
101                         return_value=targets)
102        with mock.patch.object(utils, 'get_root_helper',
103                               return_value=mock.Mock()):
104            with mock.patch.object(remotefs_brick, 'RemoteFsClient',
105                                   return_value=mock.Mock()):
106                nfs_driver = nfs_cmode.NetAppCmodeNfsDriver(**kwargs)
107
108                self.assertEqual(active_backend_id,
109                                 nfs_driver.failed_over_backend_name)
110                self.assertEqual(active_backend_id is not None,
111                                 nfs_driver.failed_over)
112                self.assertEqual(len(targets) > 0,
113                                 nfs_driver.replication_enabled)
114
115    @mock.patch.object(perf_cmode, 'PerformanceCmodeLibrary', mock.Mock())
116    @mock.patch.object(client_cmode, 'Client', mock.Mock())
117    @mock.patch.object(capabilities.CapabilitiesLibrary,
118                       'cluster_user_supported')
119    @mock.patch.object(capabilities.CapabilitiesLibrary,
120                       'check_api_permissions')
121    @mock.patch.object(nfs.NfsDriver, 'do_setup')
122    @mock.patch.object(na_utils, 'check_flags')
123    def test_do_setup(self, mock_check_flags, mock_super_do_setup,
124                      mock_check_api_permissions, mock_cluster_user_supported):
125        self.mock_object(
126            dot_utils, 'get_backend_configuration',
127            return_value=self.get_config_cmode())
128
129        self.driver.do_setup(mock.Mock())
130
131        self.assertTrue(mock_check_flags.called)
132        self.assertTrue(mock_super_do_setup.called)
133        mock_check_api_permissions.assert_called_once_with()
134        mock_cluster_user_supported.assert_called_once_with()
135
136    def test__update_volume_stats(self):
137        mock_debug_log = self.mock_object(nfs_cmode.LOG, 'debug')
138        self.mock_object(self.driver, 'get_filter_function')
139        self.mock_object(self.driver, 'get_goodness_function')
140        self.mock_object(self.driver, '_spawn_clean_cache_job')
141        self.driver.zapi_client = mock.Mock()
142        self.mock_object(self.driver, '_get_pool_stats', return_value={})
143        expected_stats = {
144            'driver_version': self.driver.VERSION,
145            'pools': {},
146            'sparse_copy_volume': True,
147            'replication_enabled': False,
148            'storage_protocol': 'nfs',
149            'vendor_name': 'NetApp',
150            'volume_backend_name': 'NetApp_NFS_Cluster_direct',
151        }
152
153        retval = self.driver._update_volume_stats()
154
155        self.assertIsNone(retval)
156        self.assertTrue(self.driver._spawn_clean_cache_job.called)
157        self.assertEqual(1, mock_debug_log.call_count)
158        self.assertEqual(expected_stats, self.driver._stats)
159
160    @ddt.data({'replication_backends': [], 'cluster_credentials': False},
161              {'replication_backends': ['target_1', 'target_2'],
162               'cluster_credentials': True})
163    @ddt.unpack
164    def test_get_pool_stats(self, replication_backends, cluster_credentials):
165        self.driver.using_cluster_credentials = cluster_credentials
166        self.driver.zapi_client = mock.Mock()
167        ssc = {
168            'vola': {
169                'pool_name': '10.10.10.10:/vola',
170                'thick_provisioning_support': True,
171                'thin_provisioning_support': False,
172                'netapp_thin_provisioned': 'false',
173                'netapp_compression': 'false',
174                'netapp_mirrored': 'false',
175                'netapp_dedup': 'true',
176                'netapp_aggregate': 'aggr1',
177                'netapp_raid_type': 'raid_dp',
178                'netapp_disk_type': 'SSD',
179                'consistent_group_snapshot_enabled': True,
180            },
181        }
182        mock_get_ssc = self.mock_object(self.driver.ssc_library,
183                                        'get_ssc',
184                                        return_value=ssc)
185        mock_get_aggrs = self.mock_object(self.driver.ssc_library,
186                                          'get_ssc_aggregates',
187                                          return_value=['aggr1'])
188
189        self.mock_object(self.driver, 'get_replication_backend_names',
190                         return_value=replication_backends)
191
192        total_capacity_gb = na_utils.round_down(
193            fake.TOTAL_BYTES // units.Gi, '0.01')
194        free_capacity_gb = na_utils.round_down(
195            fake.AVAILABLE_BYTES // units.Gi, '0.01')
196        capacity = {
197            'reserved_percentage': fake.RESERVED_PERCENTAGE,
198            'max_over_subscription_ratio': fake.MAX_OVER_SUBSCRIPTION_RATIO,
199            'total_capacity_gb': total_capacity_gb,
200            'free_capacity_gb': free_capacity_gb,
201        }
202        self.mock_object(self.driver,
203                         '_get_share_capacity_info',
204                         return_value=capacity)
205        self.mock_object(self.driver.zapi_client,
206                         'get_flexvol_dedupe_used_percent',
207                         return_value=55.0)
208
209        aggr_capacities = {
210            'aggr1': {
211                'percent-used': 45,
212                'size-available': 59055800320.0,
213                'size-total': 107374182400.0,
214            },
215        }
216        mock_get_aggr_capacities = self.mock_object(
217            self.driver.zapi_client, 'get_aggregate_capacities',
218            return_value=aggr_capacities)
219
220        self.driver.perf_library.get_node_utilization_for_pool = (
221            mock.Mock(return_value=30.0))
222
223        result = self.driver._get_pool_stats(filter_function='filter',
224                                             goodness_function='goodness')
225
226        expected = [{
227            'pool_name': '10.10.10.10:/vola',
228            'reserved_percentage': fake.RESERVED_PERCENTAGE,
229            'max_over_subscription_ratio': fake.MAX_OVER_SUBSCRIPTION_RATIO,
230            'multiattach': False,
231            'total_capacity_gb': total_capacity_gb,
232            'free_capacity_gb': free_capacity_gb,
233            'netapp_dedupe_used_percent': 55.0,
234            'netapp_aggregate_used_percent': 45,
235            'utilization': 30.0,
236            'filter_function': 'filter',
237            'goodness_function': 'goodness',
238            'thick_provisioning_support': True,
239            'thin_provisioning_support': False,
240            'netapp_thin_provisioned': 'false',
241            'netapp_compression': 'false',
242            'netapp_mirrored': 'false',
243            'netapp_dedup': 'true',
244            'netapp_aggregate': 'aggr1',
245            'netapp_raid_type': 'raid_dp',
246            'netapp_disk_type': 'SSD',
247            'consistencygroup_support': True,
248            'consistent_group_snapshot_enabled': True,
249            'replication_enabled': False,
250        }]
251
252        expected[0].update({'QoS_support': cluster_credentials})
253        if not cluster_credentials:
254            expected[0].update({
255                'netapp_aggregate_used_percent': 0,
256                'netapp_dedupe_used_percent': 0
257            })
258
259        if replication_backends:
260            expected[0].update({
261                'replication_enabled': True,
262                'replication_count': len(replication_backends),
263                'replication_targets': replication_backends,
264                'replication_type': 'async',
265            })
266
267        self.assertEqual(expected, result)
268        mock_get_ssc.assert_called_once_with()
269        if cluster_credentials:
270            mock_get_aggrs.assert_called_once_with()
271            mock_get_aggr_capacities.assert_called_once_with(['aggr1'])
272
273    @ddt.data({}, None)
274    def test_get_pool_stats_no_ssc_vols(self, ssc):
275
276        mock_get_ssc = self.mock_object(self.driver.ssc_library,
277                                        'get_ssc',
278                                        return_value=ssc)
279
280        pools = self.driver._get_pool_stats()
281
282        self.assertListEqual([], pools)
283        mock_get_ssc.assert_called_once_with()
284
285    def test_update_ssc(self):
286
287        mock_ensure_shares_mounted = self.mock_object(
288            self.driver, '_ensure_shares_mounted')
289        mock_get_pool_map = self.mock_object(
290            self.driver, '_get_flexvol_to_pool_map',
291            return_value='fake_map')
292        mock_update_ssc = self.mock_object(
293            self.driver.ssc_library, 'update_ssc')
294
295        result = self.driver._update_ssc()
296
297        self.assertIsNone(result)
298        mock_ensure_shares_mounted.assert_called_once_with()
299        mock_get_pool_map.assert_called_once_with()
300        mock_update_ssc.assert_called_once_with('fake_map')
301
302    def test_get_pool_map(self):
303
304        self.driver.zapi_client = mock.Mock()
305        mock_get_operational_lif_addresses = self.mock_object(
306            self.driver.zapi_client, 'get_operational_lif_addresses',
307            return_value=[fake.SHARE_IP])
308        mock_resolve_hostname = self.mock_object(
309            na_utils, 'resolve_hostname', return_value=fake.SHARE_IP)
310        mock_get_flexvol = self.mock_object(
311            self.driver.zapi_client, 'get_flexvol',
312            return_value={'name': fake.NETAPP_VOLUME})
313
314        result = self.driver._get_flexvol_to_pool_map()
315
316        expected = {
317            fake.NETAPP_VOLUME: {
318                'pool_name': fake.NFS_SHARE,
319            },
320        }
321        self.assertEqual(expected, result)
322        mock_get_operational_lif_addresses.assert_called_once_with()
323        mock_resolve_hostname.assert_called_once_with(fake.SHARE_IP)
324        mock_get_flexvol.assert_called_once_with(flexvol_path=fake.EXPORT_PATH)
325
326    def test_get_pool_map_address_not_found(self):
327
328        self.driver.zapi_client = mock.Mock()
329        self.mock_object(self.driver.zapi_client,
330                         'get_operational_lif_addresses',
331                         return_value=[])
332        self.mock_object(na_utils,
333                         'resolve_hostname',
334                         return_value=fake.SHARE_IP)
335
336        result = self.driver._get_flexvol_to_pool_map()
337
338        self.assertEqual({}, result)
339
340    def test_get_pool_map_flexvol_not_found(self):
341
342        self.driver.zapi_client = mock.Mock()
343        self.mock_object(self.driver.zapi_client,
344                         'get_operational_lif_addresses',
345                         return_value=[fake.SHARE_IP])
346        self.mock_object(na_utils,
347                         'resolve_hostname',
348                         return_value=fake.SHARE_IP)
349        side_effect = exception.VolumeBackendAPIException(data='fake_data')
350        self.mock_object(self.driver.zapi_client,
351                         'get_flexvol',
352                         side_effect=side_effect)
353
354        result = self.driver._get_flexvol_to_pool_map()
355
356        self.assertEqual({}, result)
357
358    @ddt.data(['/mnt/img-id1', '/mnt/img-id2'], [])
359    def test__shortlist_del_eligible_files(self, old_files):
360        self.driver.zapi_client = mock.Mock()
361        self.driver.zapi_client.get_file_usage = mock.Mock(return_value='1000')
362        mock_debug_log = self.mock_object(nfs_cmode.LOG, 'debug')
363        self.mock_object(self.driver, '_get_vserver_and_exp_vol',
364                         return_value=('openstack', 'fake_share'))
365        expected_list = [(o, '1000') for o in old_files]
366
367        observed_list = self.driver._shortlist_del_eligible_files(
368            'fake_ip:fake_share', old_files)
369
370        self.assertEqual(expected_list, observed_list)
371        self.assertEqual(1, mock_debug_log.call_count)
372
373    @ddt.data({'ip': None, 'shares': None},
374              {'ip': 'fake_ip', 'shares': ['fip:/fsh1']})
375    @ddt.unpack
376    def test__share_match_for_ip_no_match(self, ip, shares):
377        def side_effect(arg):
378            if arg == 'fake_ip':
379                return 'openstack'
380            return None
381
382        self.mock_object(self.driver, '_get_vserver_for_ip',
383                         side_effect=side_effect)
384        mock_debug_log = self.mock_object(nfs_cmode.LOG, 'debug')
385
386        retval = self.driver._share_match_for_ip(ip, shares)
387
388        self.assertIsNone(retval)
389        self.assertEqual(1, mock_debug_log.call_count)
390
391    def test__share_match_for_ip(self):
392        shares = ['fip:/fsh1']
393        self.mock_object(self.driver, '_get_vserver_for_ip',
394                         return_value='openstack')
395        mock_debug_log = self.mock_object(nfs_cmode.LOG, 'debug')
396
397        retval = self.driver._share_match_for_ip('fip', shares)
398
399        self.assertEqual('fip:/fsh1', retval)
400        self.assertEqual(1, mock_debug_log.call_count)
401
402    def test__get_vserver_for_ip_ignores_zapi_exception(self):
403        self.driver.zapi_client = mock.Mock()
404        self.driver.zapi_client.get_if_info_by_ip = mock.Mock(
405            side_effect=exception.NotFound)
406
407        vserver = self.driver._get_vserver_for_ip('FAKE_IP')
408
409        self.assertIsNone(vserver)
410
411    def test__get_vserver_for_ip(self):
412        self.driver.zapi_client = mock.Mock()
413        self.driver.zapi_client.get_if_info_by_ip = mock.Mock(
414            return_value=fake.get_fake_ifs())
415
416        vserver = self.driver._get_vserver_for_ip('FAKE_IP')
417
418        self.assertIsNone(vserver)
419
420    def test_check_for_setup_error(self):
421        super_check_for_setup_error = self.mock_object(
422            nfs_base.NetAppNfsDriver, 'check_for_setup_error')
423        mock_add_looping_tasks = self.mock_object(
424            self.driver, '_add_looping_tasks')
425
426        self.driver.check_for_setup_error()
427
428        self.assertEqual(1, super_check_for_setup_error.call_count)
429        self.assertEqual(1, mock_add_looping_tasks.call_count)
430        mock_add_looping_tasks.assert_called_once_with()
431
432    @ddt.data({'replication_enabled': True, 'failed_over': False,
433               'cluster_credentials': True},
434              {'replication_enabled': True, 'failed_over': True,
435               'cluster_credentials': True},
436              {'replication_enabled': False, 'failed_over': False,
437               'cluster_credentials': False})
438    @ddt.unpack
439    def test_handle_housekeeping_tasks(
440            self, replication_enabled, failed_over, cluster_credentials):
441        self.driver.using_cluster_credentials = cluster_credentials
442        ensure_mirrors = self.mock_object(data_motion.DataMotionMixin,
443                                          'ensure_snapmirrors')
444        self.mock_object(self.driver.ssc_library, 'get_ssc_flexvol_names',
445                         return_value=fake_ssc.SSC.keys())
446        mock_remove_unused_qos_policy_groups = self.mock_object(
447            self.driver.zapi_client, 'remove_unused_qos_policy_groups')
448        self.driver.replication_enabled = replication_enabled
449        self.driver.failed_over = failed_over
450
451        self.driver._handle_housekeeping_tasks()
452
453        if self.driver.using_cluster_credentials:
454            mock_remove_unused_qos_policy_groups.assert_called_once_with()
455        else:
456            mock_remove_unused_qos_policy_groups.assert_not_called()
457
458        if replication_enabled and not failed_over:
459            ensure_mirrors.assert_called_once_with(
460                self.driver.configuration, self.driver.backend_name,
461                fake_ssc.SSC.keys())
462        else:
463            self.assertFalse(ensure_mirrors.called)
464
465    def test_handle_ems_logging(self):
466
467        volume_list = ['vol0', 'vol1', 'vol2']
468        self.mock_object(
469            self.driver, '_get_backing_flexvol_names',
470            return_value=volume_list)
471        self.mock_object(
472            dot_utils, 'build_ems_log_message_0',
473            return_value='fake_base_ems_log_message')
474        self.mock_object(
475            dot_utils, 'build_ems_log_message_1',
476            return_value='fake_pool_ems_log_message')
477        mock_send_ems_log_message = self.mock_object(
478            self.driver.zapi_client, 'send_ems_log_message')
479
480        self.driver._handle_ems_logging()
481
482        mock_send_ems_log_message.assert_has_calls([
483            mock.call('fake_base_ems_log_message'),
484            mock.call('fake_pool_ems_log_message'),
485        ])
486        dot_utils.build_ems_log_message_0.assert_called_once_with(
487            self.driver.driver_name, self.driver.app_version)
488        dot_utils.build_ems_log_message_1.assert_called_once_with(
489            self.driver.driver_name, self.driver.app_version,
490            self.driver.vserver, volume_list, [])
491
492    def test_delete_volume(self):
493        fake_provider_location = 'fake_provider_location'
494        fake_volume = {'provider_location': fake_provider_location}
495        self.mock_object(self.driver, '_delete_backing_file_for_volume')
496        self.mock_object(na_utils,
497                         'get_valid_qos_policy_group_info',
498                         return_value='fake_qos_policy_group_info')
499
500        self.driver.delete_volume(fake_volume)
501
502        self.driver._delete_backing_file_for_volume.assert_called_once_with(
503            fake_volume)
504        na_utils.get_valid_qos_policy_group_info.assert_called_once_with(
505            fake_volume)
506        (self.driver.zapi_client.mark_qos_policy_group_for_deletion.
507         assert_called_once_with('fake_qos_policy_group_info'))
508
509    def test_delete_volume_exception_path(self):
510        fake_provider_location = 'fake_provider_location'
511        fake_volume = {'provider_location': fake_provider_location}
512        self.mock_object(self.driver, '_delete_backing_file_for_volume')
513        self.mock_object(na_utils,
514                         'get_valid_qos_policy_group_info',
515                         return_value='fake_qos_policy_group_info')
516        self.mock_object(
517            self.driver.zapi_client,
518            'mark_qos_policy_group_for_deletion',
519            side_effect=exception.NetAppDriverException)
520
521        self.driver.delete_volume(fake_volume)
522
523        self.driver._delete_backing_file_for_volume.assert_called_once_with(
524            fake_volume)
525        na_utils.get_valid_qos_policy_group_info.assert_called_once_with(
526            fake_volume)
527        (self.driver.zapi_client.mark_qos_policy_group_for_deletion.
528         assert_called_once_with('fake_qos_policy_group_info'))
529
530    def test_delete_backing_file_for_volume(self):
531        mock_filer_delete = self.mock_object(self.driver, '_delete_file')
532        mock_super_delete = self.mock_object(nfs_base.NetAppNfsDriver,
533                                             'delete_volume')
534
535        self.driver._delete_backing_file_for_volume(fake.NFS_VOLUME)
536
537        mock_filer_delete.assert_called_once_with(
538            fake.NFS_VOLUME['id'], fake.NFS_VOLUME['name'])
539        self.assertEqual(0, mock_super_delete.call_count)
540
541    @ddt.data(True, False)
542    def test_delete_backing_file_for_volume_exception_path(self, super_exc):
543        mock_exception_log = self.mock_object(nfs_cmode.LOG, 'exception')
544        exception_call_count = 2 if super_exc else 1
545        mock_filer_delete = self.mock_object(self.driver, '_delete_file')
546        mock_filer_delete.side_effect = [Exception]
547        mock_super_delete = self.mock_object(nfs_base.NetAppNfsDriver,
548                                             'delete_volume')
549        if super_exc:
550            mock_super_delete.side_effect = [Exception]
551
552        self.driver._delete_backing_file_for_volume(fake.NFS_VOLUME)
553
554        mock_filer_delete.assert_called_once_with(
555            fake.NFS_VOLUME['id'], fake.NFS_VOLUME['name'])
556        mock_super_delete.assert_called_once_with(fake.NFS_VOLUME)
557        self.assertEqual(exception_call_count, mock_exception_log.call_count)
558
559    def test_delete_snapshot(self):
560        mock_get_location = self.mock_object(self.driver,
561                                             '_get_provider_location')
562        mock_get_location.return_value = fake.PROVIDER_LOCATION
563        mock_delete_backing = self.mock_object(
564            self.driver, '_delete_backing_file_for_snapshot')
565
566        self.driver.delete_snapshot(fake.test_snapshot)
567
568        mock_delete_backing.assert_called_once_with(fake.test_snapshot)
569
570    def test_delete_backing_file_for_snapshot(self):
571        mock_filer_delete = self.mock_object(self.driver, '_delete_file')
572        mock_super_delete = self.mock_object(nfs_base.NetAppNfsDriver,
573                                             'delete_snapshot')
574
575        self.driver._delete_backing_file_for_snapshot(fake.test_snapshot)
576
577        mock_filer_delete.assert_called_once_with(
578            fake.test_snapshot['volume_id'], fake.test_snapshot['name'])
579        self.assertEqual(0, mock_super_delete.call_count)
580
581    @ddt.data(True, False)
582    def test_delete_backing_file_for_snapshot_exception_path(self, super_exc):
583        mock_exception_log = self.mock_object(nfs_cmode.LOG, 'exception')
584        exception_call_count = 2 if super_exc else 1
585        mock_filer_delete = self.mock_object(self.driver, '_delete_file')
586        mock_filer_delete.side_effect = [Exception]
587        mock_super_delete = self.mock_object(nfs_base.NetAppNfsDriver,
588                                             'delete_snapshot')
589        if super_exc:
590            mock_super_delete.side_effect = [Exception]
591
592        self.driver._delete_backing_file_for_snapshot(fake.test_snapshot)
593
594        mock_filer_delete.assert_called_once_with(
595            fake.test_snapshot['volume_id'], fake.test_snapshot['name'])
596        mock_super_delete.assert_called_once_with(fake.test_snapshot)
597        self.assertEqual(exception_call_count, mock_exception_log.call_count)
598
599    def test_delete_file(self):
600        mock_get_vs_ip = self.mock_object(self.driver, '_get_export_ip_path')
601        mock_get_vs_ip.return_value = (fake.SHARE_IP, fake.EXPORT_PATH)
602        mock_get_vserver = self.mock_object(self.driver, '_get_vserver_for_ip')
603        mock_get_vserver.return_value = fake.VSERVER_NAME
604        mock_zapi_get_vol = self.driver.zapi_client.get_vol_by_junc_vserver
605        mock_zapi_get_vol.return_value = fake.FLEXVOL
606        mock_zapi_delete = self.driver.zapi_client.delete_file
607
608        self.driver._delete_file(
609            fake.test_snapshot['volume_id'], fake.test_snapshot['name'])
610
611        mock_get_vs_ip.assert_called_once_with(
612            volume_id=fake.test_snapshot['volume_id'])
613        mock_get_vserver.assert_called_once_with(fake.SHARE_IP)
614        mock_zapi_get_vol.assert_called_once_with(
615            fake.VSERVER_NAME, fake.EXPORT_PATH)
616        mock_zapi_delete.assert_called_once_with(
617            '/vol/%s/%s' % (fake.FLEXVOL, fake.test_snapshot['name']))
618
619    def test_do_qos_for_volume_no_exception(self):
620
621        mock_get_info = self.mock_object(na_utils,
622                                         'get_valid_qos_policy_group_info')
623        mock_get_info.return_value = fake.QOS_POLICY_GROUP_INFO
624        mock_provision_qos = self.driver.zapi_client.provision_qos_policy_group
625        mock_set_policy = self.mock_object(self.driver,
626                                           '_set_qos_policy_group_on_volume')
627        mock_error_log = self.mock_object(nfs_cmode.LOG, 'error')
628        mock_debug_log = self.mock_object(nfs_cmode.LOG, 'debug')
629        mock_cleanup = self.mock_object(self.driver,
630                                        '_cleanup_volume_on_failure')
631
632        self.driver._do_qos_for_volume(fake.NFS_VOLUME, fake.EXTRA_SPECS)
633
634        mock_get_info.assert_has_calls([
635            mock.call(fake.NFS_VOLUME, fake.EXTRA_SPECS)])
636        mock_provision_qos.assert_has_calls([
637            mock.call(fake.QOS_POLICY_GROUP_INFO)])
638        mock_set_policy.assert_has_calls([
639            mock.call(fake.NFS_VOLUME, fake.QOS_POLICY_GROUP_INFO)])
640        self.assertEqual(0, mock_error_log.call_count)
641        self.assertEqual(0, mock_debug_log.call_count)
642        self.assertEqual(0, mock_cleanup.call_count)
643
644    def test_do_qos_for_volume_exception_w_cleanup(self):
645        mock_get_info = self.mock_object(na_utils,
646                                         'get_valid_qos_policy_group_info')
647        mock_get_info.return_value = fake.QOS_POLICY_GROUP_INFO
648        mock_provision_qos = self.driver.zapi_client.provision_qos_policy_group
649        mock_set_policy = self.mock_object(self.driver,
650                                           '_set_qos_policy_group_on_volume')
651        mock_set_policy.side_effect = netapp_api.NaApiError
652        mock_error_log = self.mock_object(nfs_cmode.LOG, 'error')
653        mock_debug_log = self.mock_object(nfs_cmode.LOG, 'debug')
654        mock_cleanup = self.mock_object(self.driver,
655                                        '_cleanup_volume_on_failure')
656
657        self.assertRaises(netapp_api.NaApiError,
658                          self.driver._do_qos_for_volume,
659                          fake.NFS_VOLUME,
660                          fake.EXTRA_SPECS)
661
662        mock_get_info.assert_has_calls([
663            mock.call(fake.NFS_VOLUME, fake.EXTRA_SPECS)])
664        mock_provision_qos.assert_has_calls([
665            mock.call(fake.QOS_POLICY_GROUP_INFO)])
666        mock_set_policy.assert_has_calls([
667            mock.call(fake.NFS_VOLUME, fake.QOS_POLICY_GROUP_INFO)])
668        self.assertEqual(1, mock_error_log.call_count)
669        self.assertEqual(1, mock_debug_log.call_count)
670        mock_cleanup.assert_has_calls([
671            mock.call(fake.NFS_VOLUME)])
672
673    def test_do_qos_for_volume_exception_no_cleanup(self):
674
675        mock_get_info = self.mock_object(na_utils,
676                                         'get_valid_qos_policy_group_info')
677        mock_get_info.side_effect = exception.Invalid
678        mock_provision_qos = self.driver.zapi_client.provision_qos_policy_group
679        mock_set_policy = self.mock_object(self.driver,
680                                           '_set_qos_policy_group_on_volume')
681        mock_error_log = self.mock_object(nfs_cmode.LOG, 'error')
682        mock_debug_log = self.mock_object(nfs_cmode.LOG, 'debug')
683        mock_cleanup = self.mock_object(self.driver,
684                                        '_cleanup_volume_on_failure')
685
686        self.assertRaises(exception.Invalid, self.driver._do_qos_for_volume,
687                          fake.NFS_VOLUME, fake.EXTRA_SPECS, cleanup=False)
688
689        mock_get_info.assert_has_calls([
690            mock.call(fake.NFS_VOLUME, fake.EXTRA_SPECS)])
691        self.assertEqual(0, mock_provision_qos.call_count)
692        self.assertEqual(0, mock_set_policy.call_count)
693        self.assertEqual(1, mock_error_log.call_count)
694        self.assertEqual(0, mock_debug_log.call_count)
695        self.assertEqual(0, mock_cleanup.call_count)
696
697    def test_set_qos_policy_group_on_volume(self):
698
699        mock_get_name_from_info = self.mock_object(
700            na_utils, 'get_qos_policy_group_name_from_info')
701        mock_get_name_from_info.return_value = fake.QOS_POLICY_GROUP_NAME
702
703        mock_extract_host = self.mock_object(volume_utils, 'extract_host')
704        mock_extract_host.return_value = fake.NFS_SHARE
705
706        mock_get_flex_vol_name =\
707            self.driver.zapi_client.get_vol_by_junc_vserver
708        mock_get_flex_vol_name.return_value = fake.FLEXVOL
709
710        mock_file_assign_qos = self.driver.zapi_client.file_assign_qos
711
712        self.driver._set_qos_policy_group_on_volume(fake.NFS_VOLUME,
713                                                    fake.QOS_POLICY_GROUP_INFO)
714
715        mock_get_name_from_info.assert_has_calls([
716            mock.call(fake.QOS_POLICY_GROUP_INFO)])
717        mock_extract_host.assert_has_calls([
718            mock.call(fake.NFS_HOST_STRING, level='pool')])
719        mock_get_flex_vol_name.assert_has_calls([
720            mock.call(fake.VSERVER_NAME, fake.EXPORT_PATH)])
721        mock_file_assign_qos.assert_has_calls([
722            mock.call(fake.FLEXVOL, fake.QOS_POLICY_GROUP_NAME,
723                      fake.NFS_VOLUME['name'])])
724
725    def test_set_qos_policy_group_on_volume_no_info(self):
726
727        mock_get_name_from_info = self.mock_object(
728            na_utils, 'get_qos_policy_group_name_from_info')
729
730        mock_extract_host = self.mock_object(volume_utils, 'extract_host')
731
732        mock_get_flex_vol_name =\
733            self.driver.zapi_client.get_vol_by_junc_vserver
734
735        mock_file_assign_qos = self.driver.zapi_client.file_assign_qos
736
737        self.driver._set_qos_policy_group_on_volume(fake.NFS_VOLUME,
738                                                    None)
739
740        self.assertEqual(0, mock_get_name_from_info.call_count)
741        self.assertEqual(0, mock_extract_host.call_count)
742        self.assertEqual(0, mock_get_flex_vol_name.call_count)
743        self.assertEqual(0, mock_file_assign_qos.call_count)
744
745    def test_set_qos_policy_group_on_volume_no_name(self):
746
747        mock_get_name_from_info = self.mock_object(
748            na_utils, 'get_qos_policy_group_name_from_info')
749        mock_get_name_from_info.return_value = None
750
751        mock_extract_host = self.mock_object(volume_utils, 'extract_host')
752
753        mock_get_flex_vol_name =\
754            self.driver.zapi_client.get_vol_by_junc_vserver
755
756        mock_file_assign_qos = self.driver.zapi_client.file_assign_qos
757
758        self.driver._set_qos_policy_group_on_volume(fake.NFS_VOLUME,
759                                                    fake.QOS_POLICY_GROUP_INFO)
760
761        mock_get_name_from_info.assert_has_calls([
762            mock.call(fake.QOS_POLICY_GROUP_INFO)])
763        self.assertEqual(0, mock_extract_host.call_count)
764        self.assertEqual(0, mock_get_flex_vol_name.call_count)
765        self.assertEqual(0, mock_file_assign_qos.call_count)
766
767    @ddt.data({'share': None, 'is_snapshot': False},
768              {'share': None, 'is_snapshot': True},
769              {'share': 'fake_share', 'is_snapshot': False},
770              {'share': 'fake_share', 'is_snapshot': True})
771    @ddt.unpack
772    def test_clone_backing_file_for_volume(self, share, is_snapshot):
773
774        mock_get_vserver_and_exp_vol = self.mock_object(
775            self.driver, '_get_vserver_and_exp_vol',
776            return_value=(fake.VSERVER_NAME, fake.FLEXVOL))
777
778        self.driver._clone_backing_file_for_volume(
779            fake.FLEXVOL, 'fake_clone', fake.VOLUME_ID, share=share,
780            is_snapshot=is_snapshot)
781
782        mock_get_vserver_and_exp_vol.assert_called_once_with(
783            fake.VOLUME_ID, share)
784        self.driver.zapi_client.clone_file.assert_called_once_with(
785            fake.FLEXVOL, fake.FLEXVOL, 'fake_clone', fake.VSERVER_NAME,
786            is_snapshot=is_snapshot)
787
788    def test__clone_backing_file_for_volume(self):
789        body = fake.get_fake_net_interface_get_iter_response()
790        self.driver.zapi_client.get_if_info_by_ip = mock.Mock(
791            return_value=[netapp_api.NaElement(body)])
792        self.driver.zapi_client.get_vol_by_junc_vserver = mock.Mock(
793            return_value='nfsvol')
794        self.mock_object(self.driver, '_get_export_ip_path',
795                         return_value=('127.0.0.1', 'fakepath'))
796
797        retval = self.driver._clone_backing_file_for_volume(
798            'vol', 'clone', 'vol_id', share='share', is_snapshot=True)
799
800        self.assertIsNone(retval)
801        self.driver.zapi_client.clone_file.assert_called_once_with(
802            'nfsvol', 'vol', 'clone', None, is_snapshot=True)
803
804    def test_copy_from_img_service_copyoffload_nonexistent_binary_path(self):
805        self.mock_object(nfs_cmode.LOG, 'debug')
806        drv = self.driver
807        context = object()
808        volume = {'id': 'vol_id', 'name': 'name',
809                  'host': 'openstack@nfscmode#192.128.1.1:/mnt_point'}
810        image_service = mock.Mock()
811        image_service.get_location.return_value = (mock.Mock(), mock.Mock())
812        image_service.show.return_value = {'size': 0}
813        image_id = 'image_id'
814        drv._client = mock.Mock()
815        drv._client.get_api_version = mock.Mock(return_value=(1, 20))
816        nfs_base.NetAppNfsDriver._find_image_in_cache = mock.Mock(
817            return_value=[])
818        drv._construct_image_nfs_url = mock.Mock(return_value=["nfs://1"])
819        drv._check_get_nfs_path_segs = mock.Mock(
820            return_value=("test:test", "dr"))
821        drv._get_ip_verify_on_cluster = mock.Mock(return_value="192.128.1.1")
822        drv._get_mount_point_for_share = mock.Mock(return_value='mnt_point')
823        drv._check_share_can_hold_size = mock.Mock()
824        # Raise error as if the copyoffload file can not be found
825        drv._clone_file_dst_exists = mock.Mock(side_effect=OSError())
826        drv._discover_file_till_timeout = mock.Mock()
827
828        # Verify the original error is propagated
829        self.assertRaises(OSError, drv._copy_from_img_service,
830                          context, volume, image_service, image_id)
831
832        drv._discover_file_till_timeout.assert_not_called()
833
834    @mock.patch.object(image_utils, 'qemu_img_info')
835    def test_copy_from_img_service_raw_copyoffload_workflow_success(
836            self, mock_qemu_img_info):
837        drv = self.driver
838        volume = {'id': 'vol_id', 'name': 'name', 'size': 1,
839                  'host': 'openstack@nfscmode#ip1:/mnt_point'}
840        image_id = 'image_id'
841        context = object()
842        image_service = mock.Mock()
843        image_service.get_location.return_value = ('nfs://ip1/openstack/img',
844                                                   None)
845        image_service.show.return_value = {'size': 1, 'disk_format': 'raw'}
846
847        drv._check_get_nfs_path_segs =\
848            mock.Mock(return_value=('ip1', '/openstack'))
849        drv._get_ip_verify_on_cluster = mock.Mock(return_value='ip1')
850        drv._get_host_ip = mock.Mock(return_value='ip2')
851        drv._get_export_path = mock.Mock(return_value='/exp_path')
852        drv._get_provider_location = mock.Mock(return_value='share')
853        drv._execute = mock.Mock()
854        drv._get_mount_point_for_share = mock.Mock(return_value='mnt_point')
855        drv._discover_file_till_timeout = mock.Mock(return_value=True)
856        img_inf = mock.Mock()
857        img_inf.file_format = 'raw'
858        mock_qemu_img_info.return_value = img_inf
859        drv._check_share_can_hold_size = mock.Mock()
860        drv._move_nfs_file = mock.Mock(return_value=True)
861        drv._delete_file_at_path = mock.Mock()
862        drv._clone_file_dst_exists = mock.Mock()
863        drv._post_clone_image = mock.Mock()
864
865        retval = drv._copy_from_img_service(
866            context, volume, image_service, image_id)
867
868        self.assertTrue(retval)
869        drv._get_ip_verify_on_cluster.assert_any_call('ip1')
870        drv._check_share_can_hold_size.assert_called_with(
871            'ip1:/mnt_point', 1)
872        self.assertEqual(1, drv._execute.call_count)
873
874    @mock.patch.object(image_utils, 'convert_image')
875    @mock.patch.object(image_utils, 'qemu_img_info')
876    @mock.patch('os.path.exists')
877    def test_copy_from_img_service_qcow2_copyoffload_workflow_success(
878            self, mock_exists, mock_qemu_img_info, mock_cvrt_image):
879        drv = self.driver
880        cinder_mount_point_base = '/opt/stack/data/cinder/mnt/'
881        # To get the cinder mount point directory, we use:
882        mount_dir = hashlib.md5(
883            '203.0.113.122:/cinder-flexvol1'.encode('utf-8')).hexdigest()
884        cinder_mount_point = cinder_mount_point_base + mount_dir
885        destination_copied_file = (
886            '/cinder-flexvol1/a155308c-0290-497b-b278-4cdd01de0253'
887        )
888        volume = {'id': 'vol_id', 'name': 'name', 'size': 1,
889                  'host': 'openstack@nfscmode#203.0.113.122:/cinder-flexvol1'}
890        image_id = 'image_id'
891        context = object()
892        image_service = mock.Mock()
893        image_service.get_location.return_value = (
894            'nfs://203.0.113.122/glance-flexvol1', None)
895        image_service.show.return_value = {'size': 1,
896                                           'disk_format': 'qcow2'}
897        drv._check_get_nfs_path_segs = (
898            mock.Mock(return_value=('203.0.113.122', '/openstack'))
899        )
900
901        drv._get_ip_verify_on_cluster = mock.Mock(return_value='203.0.113.122')
902        drv._execute = mock.Mock()
903        drv._execute_as_root = False
904        drv._get_mount_point_for_share = mock.Mock(
905            return_value=cinder_mount_point)
906        img_inf = mock.Mock()
907        img_inf.file_format = 'raw'
908        mock_qemu_img_info.return_value = img_inf
909        drv._check_share_can_hold_size = mock.Mock()
910
911        drv._move_nfs_file = mock.Mock(return_value=True)
912        drv._delete_file_at_path = mock.Mock()
913        drv._clone_file_dst_exists = mock.Mock()
914        drv._post_clone_image = mock.Mock()
915        self.mock_object(uuid, 'uuid4', mock.Mock(
916            return_value='a155308c-0290-497b-b278-4cdd01de0253'))
917
918        retval = drv._copy_from_img_service(
919            context, volume, image_service, image_id)
920
921        self.assertTrue(retval)
922        drv._get_ip_verify_on_cluster.assert_any_call('203.0.113.122')
923        drv._check_share_can_hold_size.assert_called_with(
924            '203.0.113.122:/cinder-flexvol1', 1)
925
926        # _execute must be called once for copy-offload and again to touch
927        # the top directory to refresh cache
928        drv._execute.assert_has_calls(
929            [
930                mock.call(
931                    'copyoffload_tool_path', '203.0.113.122',
932                    '203.0.113.122', '/openstack/glance-flexvol1',
933                    destination_copied_file, run_as_root=False,
934                    check_exit_code=0
935                ),
936                mock.call('touch', cinder_mount_point, run_as_root=False)
937            ]
938        )
939        self.assertEqual(2, drv._execute.call_count)
940        self.assertEqual(2, drv._delete_file_at_path.call_count)
941        self.assertEqual(1, drv._clone_file_dst_exists.call_count)
942
943    def test_copy_from_cache_copyoffload_success(self):
944        drv = self.driver
945        volume = {'id': 'vol_id', 'name': 'name', 'size': 1,
946                  'host': 'openstack@nfscmode#192.128.1.1:/exp_path'}
947        image_id = 'image_id'
948        cache_result = [('ip1:/openstack', 'img-cache-imgid')]
949        drv._get_ip_verify_on_cluster = mock.Mock(return_value='ip1')
950        drv._execute = mock.Mock()
951        drv._register_image_in_cache = mock.Mock()
952        drv._post_clone_image = mock.Mock()
953
954        copied = drv._copy_from_cache(volume, image_id, cache_result)
955
956        self.assertTrue(copied)
957        drv._get_ip_verify_on_cluster.assert_any_call('ip1')
958        drv._execute.assert_called_once_with(
959            'copyoffload_tool_path', 'ip1', 'ip1',
960            '/openstack/img-cache-imgid', '/exp_path/name',
961            run_as_root=False, check_exit_code=0)
962
963    def test_unmanage(self):
964        mock_get_info = self.mock_object(na_utils,
965                                         'get_valid_qos_policy_group_info')
966        mock_get_info.return_value = fake.QOS_POLICY_GROUP_INFO
967
968        mock_mark_for_deletion =\
969            self.driver.zapi_client.mark_qos_policy_group_for_deletion
970
971        super_unmanage = self.mock_object(nfs_base.NetAppNfsDriver, 'unmanage')
972
973        self.driver.unmanage(fake.NFS_VOLUME)
974
975        mock_get_info.assert_has_calls([mock.call(fake.NFS_VOLUME)])
976        mock_mark_for_deletion.assert_has_calls([
977            mock.call(fake.QOS_POLICY_GROUP_INFO)])
978        super_unmanage.assert_has_calls([mock.call(fake.NFS_VOLUME)])
979
980    def test_unmanage_invalid_qos(self):
981        mock_get_info = self.mock_object(na_utils,
982                                         'get_valid_qos_policy_group_info')
983        mock_get_info.side_effect = exception.Invalid
984
985        super_unmanage = self.mock_object(nfs_base.NetAppNfsDriver, 'unmanage')
986
987        self.driver.unmanage(fake.NFS_VOLUME)
988
989        mock_get_info.assert_has_calls([mock.call(fake.NFS_VOLUME)])
990        super_unmanage.assert_has_calls([mock.call(fake.NFS_VOLUME)])
991
992    def test_add_looping_tasks(self):
993        mock_update_ssc = self.mock_object(self.driver, '_update_ssc')
994        mock_handle_housekeeping = self.mock_object(
995            self.driver, '_handle_housekeeping_tasks')
996        mock_add_task = self.mock_object(self.driver.loopingcalls, 'add_task')
997        mock_super_add_looping_tasks = self.mock_object(
998            nfs_base.NetAppNfsDriver, '_add_looping_tasks')
999
1000        self.driver._add_looping_tasks()
1001
1002        mock_update_ssc.assert_called_once_with()
1003        mock_add_task.assert_has_calls([
1004            mock.call(mock_update_ssc,
1005                      loopingcalls.ONE_HOUR,
1006                      loopingcalls.ONE_HOUR),
1007            mock.call(mock_handle_housekeeping,
1008                      loopingcalls.TEN_MINUTES,
1009                      0)])
1010        mock_super_add_looping_tasks.assert_called_once_with()
1011
1012    @ddt.data({'type_match': True, 'expected': True},
1013              {'type_match': False, 'expected': False})
1014    @ddt.unpack
1015    def test_is_share_clone_compatible(self, type_match, expected):
1016
1017        mock_get_flexvol_name_for_share = self.mock_object(
1018            self.driver, '_get_flexvol_name_for_share',
1019            return_value='fake_flexvol')
1020        mock_is_share_vol_type_match = self.mock_object(
1021            self.driver, '_is_share_vol_type_match', return_value=type_match)
1022
1023        result = self.driver._is_share_clone_compatible(fake.VOLUME,
1024                                                        fake.NFS_SHARE)
1025
1026        self.assertEqual(expected, result)
1027        mock_get_flexvol_name_for_share.assert_called_once_with(fake.NFS_SHARE)
1028        mock_is_share_vol_type_match.assert_called_once()
1029
1030    @ddt.data({'flexvols': ['volume1', 'volume2'], 'expected': True},
1031              {'flexvols': ['volume3', 'volume4'], 'expected': False},
1032              {'flexvols': [], 'expected': False})
1033    @ddt.unpack
1034    def test_is_share_vol_type_match(self, flexvols, expected):
1035
1036        mock_get_volume_extra_specs = self.mock_object(
1037            na_utils, 'get_volume_extra_specs',
1038            return_value='fake_extra_specs')
1039        mock_get_matching_flexvols_for_extra_specs = self.mock_object(
1040            self.driver.ssc_library, 'get_matching_flexvols_for_extra_specs',
1041            return_value=flexvols)
1042
1043        result = self.driver._is_share_vol_type_match(fake.VOLUME,
1044                                                      fake.NFS_SHARE,
1045                                                      'volume1')
1046
1047        self.assertEqual(expected, result)
1048        mock_get_volume_extra_specs.assert_called_once_with(fake.VOLUME)
1049        mock_get_matching_flexvols_for_extra_specs.assert_called_once_with(
1050            'fake_extra_specs')
1051
1052    @ddt.data({'share': 'volume1', 'expected': 'volume1'},
1053              {'share': 'volume3', 'expected': None})
1054    @ddt.unpack
1055    def test_get_flexvol_name_for_share(self, share, expected):
1056
1057        mock_get_ssc = self.mock_object(
1058            self.driver.ssc_library, 'get_ssc', return_value=fake_ssc.SSC)
1059
1060        result = self.driver._get_flexvol_name_for_share(share)
1061
1062        self.assertEqual(expected, result)
1063        mock_get_ssc.assert_called_once_with()
1064
1065    def test_get_flexvol_name_for_share_no_ssc_vols(self):
1066
1067        mock_get_ssc = self.mock_object(
1068            self.driver.ssc_library, 'get_ssc', return_value={})
1069
1070        result = self.driver._get_flexvol_name_for_share('fake_share')
1071
1072        self.assertIsNone(result)
1073        mock_get_ssc.assert_called_once_with()
1074
1075    def test_find_image_location_with_local_copy(self):
1076        local_share = '/share'
1077        cache_result = [
1078            ('ip1:/openstack', 'img-cache-imgid'),
1079            ('ip2:/openstack', 'img-cache-imgid'),
1080            (local_share, 'img-cache-imgid'),
1081            ('ip3:/openstack', 'img-cache-imgid'),
1082        ]
1083
1084        mock_extract_host = self.mock_object(volume_utils, 'extract_host')
1085        mock_extract_host.return_value = local_share
1086
1087        cache_copy, found_local_copy = self.driver._find_image_location(
1088            cache_result, fake.VOLUME)
1089
1090        self.assertEqual(cache_result[2], cache_copy)
1091        self.assertTrue(found_local_copy)
1092
1093    def test_find_image_location_with_remote_copy(self):
1094        cache_result = [('ip1:/openstack', 'img-cache-imgid')]
1095
1096        mock_extract_host = self.mock_object(volume_utils, 'extract_host')
1097        mock_extract_host.return_value = '/share'
1098
1099        cache_copy, found_local_copy = self.driver._find_image_location(
1100            cache_result, fake.VOLUME)
1101
1102        self.assertEqual(cache_result[0], cache_copy)
1103        self.assertFalse(found_local_copy)
1104
1105    def test_find_image_location_without_cache_copy(self):
1106        cache_result = []
1107        mock_extract_host = self.mock_object(volume_utils, 'extract_host')
1108        mock_extract_host.return_value = '/share'
1109
1110        cache_copy, found_local_copy = self.driver._find_image_location(
1111            cache_result, fake.VOLUME)
1112
1113        self.assertIsNone(cache_copy)
1114        self.assertFalse(found_local_copy)
1115
1116    def test_clone_file_dest_exists(self):
1117        self.driver._get_vserver_and_exp_vol = mock.Mock(
1118            return_value=(fake.VSERVER_NAME, fake.EXPORT_PATH))
1119        self.driver.zapi_client.clone_file = mock.Mock()
1120
1121        self.driver._clone_file_dst_exists(
1122            fake.NFS_SHARE, fake.IMAGE_FILE_ID, fake.VOLUME['name'],
1123            dest_exists=True)
1124
1125        self.driver._get_vserver_and_exp_vol.assert_called_once_with(
1126            share=fake.NFS_SHARE)
1127        self.driver.zapi_client.clone_file.assert_called_once_with(
1128            fake.EXPORT_PATH, fake.IMAGE_FILE_ID, fake.VOLUME['name'],
1129            fake.VSERVER_NAME, dest_exists=True)
1130
1131    @ddt.data((fake.NFS_SHARE, fake.SHARE_IP),
1132              (fake.NFS_SHARE_IPV6, fake.IPV6_ADDRESS))
1133    @ddt.unpack
1134    def test_get_source_ip_and_path(self, share, ip):
1135        self.driver._get_ip_verify_on_cluster = mock.Mock(
1136            return_value=ip)
1137
1138        src_ip, src_path = self.driver._get_source_ip_and_path(
1139            share, fake.IMAGE_FILE_ID)
1140
1141        self.assertEqual(ip, src_ip)
1142        assert_path = fake.EXPORT_PATH + '/' + fake.IMAGE_FILE_ID
1143        self.assertEqual(assert_path, src_path)
1144        self.driver._get_ip_verify_on_cluster.assert_called_once_with(ip)
1145
1146    def test_get_destination_ip_and_path(self):
1147        self.driver._get_ip_verify_on_cluster = mock.Mock(
1148            return_value=fake.SHARE_IP)
1149        mock_extract_host = self.mock_object(volume_utils, 'extract_host')
1150        mock_extract_host.return_value = fake.NFS_SHARE
1151
1152        dest_ip, dest_path = self.driver._get_destination_ip_and_path(
1153            fake.VOLUME)
1154
1155        self.assertEqual(fake.SHARE_IP, dest_ip)
1156        assert_path = fake.EXPORT_PATH + '/' + fake.LUN_NAME
1157        self.assertEqual(assert_path, dest_path)
1158        self.driver._get_ip_verify_on_cluster.assert_called_once_with(
1159            fake.SHARE_IP)
1160
1161    def test_clone_image_copyoffload_from_cache_success(self):
1162        drv = self.driver
1163        context = object()
1164        volume = {'id': 'vol_id', 'name': 'name',
1165                  'host': 'openstack@nfscmode#192.128.1.1:/mnt_point'}
1166        image_service = object()
1167        image_location = 'img-loc'
1168        image_id = 'image_id'
1169        image_meta = {'id': image_id}
1170        drv.zapi_client = mock.Mock()
1171        drv.zapi_client.get_ontapi_version = mock.Mock(return_value=(1, 20))
1172        nfs_base.NetAppNfsDriver._find_image_in_cache = mock.Mock(
1173            return_value=[('share', 'img')])
1174        nfs_base.NetAppNfsDriver._direct_nfs_clone = mock.Mock(
1175            return_value=False)
1176        drv._copy_from_cache = mock.Mock(return_value=True)
1177
1178        drv.clone_image(context, volume, image_location, image_meta,
1179                        image_service)
1180
1181        drv._copy_from_cache.assert_called_once_with(
1182            volume, image_id, [('share', 'img')])
1183
1184    def test_clone_image_copyoffload_from_img_service(self):
1185        drv = self.driver
1186        context = object()
1187        volume = {'id': 'vol_id', 'name': 'name',
1188                  'host': 'openstack@nfscmode#192.128.1.1:/mnt_point',
1189                  'provider_location': '192.128.1.1:/mnt_point'}
1190        image_service = object()
1191        image_id = 'image_id'
1192        image_meta = {'id': image_id}
1193        image_location = 'img-loc'
1194        drv.zapi_client = mock.Mock()
1195        drv.zapi_client.get_ontapi_version = mock.Mock(return_value=(1, 20))
1196        nfs_base.NetAppNfsDriver._find_image_in_cache = mock.Mock(
1197            return_value=[])
1198        nfs_base.NetAppNfsDriver._direct_nfs_clone = mock.Mock(
1199            return_value=False)
1200        nfs_base.NetAppNfsDriver._post_clone_image = mock.Mock(
1201            return_value=True)
1202        drv._copy_from_img_service = mock.Mock(return_value=True)
1203
1204        retval = drv.clone_image(
1205            context, volume, image_location, image_meta, image_service)
1206
1207        self.assertEqual(retval, (
1208            {'provider_location': '192.128.1.1:/mnt_point',
1209             'bootable': True}, True))
1210        drv._copy_from_img_service.assert_called_once_with(
1211            context, volume, image_service, image_id)
1212
1213    def test_clone_image_copyoffload_failure(self):
1214        mock_log = self.mock_object(nfs_cmode, 'LOG')
1215        drv = self.driver
1216        context = object()
1217        volume = {'id': 'vol_id', 'name': 'name'}
1218        image_service = object()
1219        image_id = 'image_id'
1220        image_meta = {'id': image_id}
1221        image_location = 'img-loc'
1222        drv.zapi_client = mock.Mock()
1223        drv.zapi_client.get_ontapi_version = mock.Mock(return_value=(1, 20))
1224        nfs_base.NetAppNfsDriver._find_image_in_cache = mock.Mock(
1225            return_value=[])
1226        nfs_base.NetAppNfsDriver._direct_nfs_clone = mock.Mock(
1227            return_value=False)
1228        drv._copy_from_img_service = mock.Mock(side_effect=Exception())
1229
1230        retval = drv.clone_image(
1231            context, volume, image_location, image_meta, image_service)
1232
1233        self.assertEqual(retval, ({'bootable': False,
1234                                   'provider_location': None}, False))
1235        drv._copy_from_img_service.assert_called_once_with(
1236            context, volume, image_service, image_id)
1237        mock_log.info.assert_not_called()
1238
1239    def test_copy_from_remote_cache(self):
1240        source_ip = '192.0.1.1'
1241        source_path = '/openstack/img-cache-imgid'
1242        cache_copy = ('192.0.1.1:/openstack', fake.IMAGE_FILE_ID)
1243        dest_path = fake.EXPORT_PATH + '/' + fake.VOLUME['name']
1244        self.driver._execute = mock.Mock()
1245        self.driver._get_source_ip_and_path = mock.Mock(
1246            return_value=(source_ip, source_path))
1247        self.driver._get_destination_ip_and_path = mock.Mock(
1248            return_value=(fake.SHARE_IP, dest_path))
1249        self.driver._register_image_in_cache = mock.Mock()
1250
1251        self.driver._copy_from_remote_cache(
1252            fake.VOLUME, fake.IMAGE_FILE_ID, cache_copy)
1253
1254        self.driver._execute.assert_called_once_with(
1255            'copyoffload_tool_path', source_ip, fake.SHARE_IP,
1256            source_path, dest_path, run_as_root=False, check_exit_code=0)
1257        self.driver._get_source_ip_and_path.assert_called_once_with(
1258            cache_copy[0], fake.IMAGE_FILE_ID)
1259        self.driver._get_destination_ip_and_path.assert_called_once_with(
1260            fake.VOLUME)
1261        self.driver._register_image_in_cache.assert_called_once_with(
1262            fake.VOLUME, fake.IMAGE_FILE_ID)
1263
1264    def test_copy_from_cache_workflow_remote_location(self):
1265        cache_result = [('ip1:/openstack', fake.IMAGE_FILE_ID),
1266                        ('ip2:/openstack', fake.IMAGE_FILE_ID),
1267                        ('ip3:/openstack', fake.IMAGE_FILE_ID)]
1268        self.driver._find_image_location = mock.Mock(return_value=[
1269            cache_result[0], False])
1270        self.driver._copy_from_remote_cache = mock.Mock()
1271        self.driver._post_clone_image = mock.Mock()
1272
1273        copied = self.driver._copy_from_cache(
1274            fake.VOLUME, fake.IMAGE_FILE_ID, cache_result)
1275
1276        self.assertTrue(copied)
1277        self.driver._copy_from_remote_cache.assert_called_once_with(
1278            fake.VOLUME, fake.IMAGE_FILE_ID, cache_result[0])
1279
1280    def test_copy_from_cache_workflow_remote_location_no_copyoffload(self):
1281        cache_result = [('ip1:/openstack', fake.IMAGE_FILE_ID),
1282                        ('ip2:/openstack', fake.IMAGE_FILE_ID),
1283                        ('ip3:/openstack', fake.IMAGE_FILE_ID)]
1284        self.driver._find_image_location = mock.Mock(return_value=[
1285            cache_result[0], False])
1286        self.driver._copy_from_remote_cache = mock.Mock()
1287        self.driver._post_clone_image = mock.Mock()
1288        self.driver.configuration.netapp_copyoffload_tool_path = None
1289
1290        copied = self.driver._copy_from_cache(
1291            fake.VOLUME, fake.IMAGE_FILE_ID, cache_result)
1292
1293        self.assertFalse(copied)
1294        self.driver._copy_from_remote_cache.assert_not_called()
1295
1296    def test_copy_from_cache_workflow_local_location(self):
1297        local_share = '/share'
1298        cache_result = [
1299            ('ip1:/openstack', 'img-cache-imgid'),
1300            ('ip2:/openstack', 'img-cache-imgid'),
1301            (local_share, 'img-cache-imgid'),
1302            ('ip3:/openstack', 'img-cache-imgid'),
1303        ]
1304        self.driver._find_image_location = mock.Mock(return_value=[
1305            cache_result[2], True])
1306        self.driver._clone_file_dst_exists = mock.Mock()
1307        self.driver._post_clone_image = mock.Mock()
1308
1309        copied = self.driver._copy_from_cache(
1310            fake.VOLUME, fake.IMAGE_FILE_ID, cache_result)
1311
1312        self.assertTrue(copied)
1313        self.driver._clone_file_dst_exists.assert_called_once_with(
1314            local_share, fake.IMAGE_FILE_ID, fake.VOLUME['name'],
1315            dest_exists=True)
1316
1317    def test_copy_from_cache_workflow_no_location(self):
1318        cache_result = []
1319        self.driver._find_image_location = mock.Mock(
1320            return_value=(None, False))
1321
1322        copied = self.driver._copy_from_cache(
1323            fake.VOLUME, fake.IMAGE_FILE_ID, cache_result)
1324
1325        self.assertFalse(copied)
1326
1327    def test_copy_from_cache_workflow_exception(self):
1328        cache_result = [('ip1:/openstack', fake.IMAGE_FILE_ID)]
1329        self.driver._find_image_location = mock.Mock(return_value=[
1330            cache_result[0], False])
1331        self.driver._copy_from_remote_cache = mock.Mock(
1332            side_effect=Exception)
1333        self.driver._post_clone_image = mock.Mock()
1334
1335        copied = self.driver._copy_from_cache(
1336            fake.VOLUME, fake.IMAGE_FILE_ID, cache_result)
1337
1338        self.assertFalse(copied)
1339        self.driver._copy_from_remote_cache.assert_called_once_with(
1340            fake.VOLUME, fake.IMAGE_FILE_ID, cache_result[0])
1341        self.assertFalse(self.driver._post_clone_image.called)
1342
1343    @ddt.data({'secondary_id': 'dev0', 'configured_targets': ['dev1']},
1344              {'secondary_id': 'dev3', 'configured_targets': ['dev1', 'dev2']},
1345              {'secondary_id': 'dev1', 'configured_targets': []},
1346              {'secondary_id': None, 'configured_targets': []})
1347    @ddt.unpack
1348    def test_failover_host_invalid_replication_target(self, secondary_id,
1349                                                      configured_targets):
1350        """This tests executes a method in the DataMotionMixin."""
1351        self.driver.backend_name = 'dev0'
1352        self.mock_object(data_motion.DataMotionMixin,
1353                         'get_replication_backend_names',
1354                         return_value=configured_targets)
1355        complete_failover_call = self.mock_object(
1356            data_motion.DataMotionMixin, '_complete_failover')
1357
1358        self.assertRaises(exception.InvalidReplicationTarget,
1359                          self.driver.failover_host, 'fake_context', [],
1360                          secondary_id=secondary_id)
1361        self.assertFalse(complete_failover_call.called)
1362
1363    def test_failover_host_unable_to_failover(self):
1364        """This tests executes a method in the DataMotionMixin."""
1365        self.driver.backend_name = 'dev0'
1366        self.mock_object(data_motion.DataMotionMixin, '_complete_failover',
1367                         side_effect=exception.NetAppDriverException)
1368        self.mock_object(data_motion.DataMotionMixin,
1369                         'get_replication_backend_names',
1370                         return_value=['dev1', 'dev2'])
1371        self.mock_object(self.driver.ssc_library, 'get_ssc_flexvol_names',
1372                         return_value=fake_ssc.SSC.keys())
1373        self.mock_object(self.driver, '_update_zapi_client')
1374
1375        self.assertRaises(exception.UnableToFailOver,
1376                          self.driver.failover_host, 'fake_context', [],
1377                          secondary_id='dev1')
1378        data_motion.DataMotionMixin._complete_failover.assert_called_once_with(
1379            'dev0', ['dev1', 'dev2'], fake_ssc.SSC.keys(), [],
1380            failover_target='dev1')
1381        self.assertFalse(self.driver._update_zapi_client.called)
1382
1383    def test_failover_host(self):
1384        """This tests executes a method in the DataMotionMixin."""
1385        self.driver.backend_name = 'dev0'
1386        self.mock_object(data_motion.DataMotionMixin, '_complete_failover',
1387                         return_value=('dev1', []))
1388        self.mock_object(data_motion.DataMotionMixin,
1389                         'get_replication_backend_names',
1390                         return_value=['dev1', 'dev2'])
1391        self.mock_object(self.driver.ssc_library, 'get_ssc_flexvol_names',
1392                         return_value=fake_ssc.SSC.keys())
1393        self.mock_object(self.driver, '_update_zapi_client')
1394
1395        actual_active, vol_updates, __ = self.driver.failover_host(
1396            'fake_context', [], secondary_id='dev1', groups=[])
1397
1398        data_motion.DataMotionMixin._complete_failover.assert_called_once_with(
1399            'dev0', ['dev1', 'dev2'], fake_ssc.SSC.keys(), [],
1400            failover_target='dev1')
1401        self.driver._update_zapi_client.assert_called_once_with('dev1')
1402        self.assertTrue(self.driver.failed_over)
1403        self.assertEqual('dev1', self.driver.failed_over_backend_name)
1404        self.assertEqual('dev1', actual_active)
1405        self.assertEqual([], vol_updates)
1406
1407    def test_delete_group_snapshot(self):
1408        mock_delete_backing_file = self.mock_object(
1409            self.driver, '_delete_backing_file_for_snapshot')
1410        snapshots = [fake.VG_SNAPSHOT]
1411
1412        model_update, snapshots_model_update = (
1413            self.driver.delete_group_snapshot(
1414                fake.VG_CONTEXT, fake.VG_SNAPSHOT, snapshots))
1415
1416        mock_delete_backing_file.assert_called_once_with(fake.VG_SNAPSHOT)
1417        self.assertIsNone(model_update)
1418        self.assertIsNone(snapshots_model_update)
1419
1420    def test_get_snapshot_backing_flexvol_names(self):
1421        snapshots = [
1422            {'volume': {'host': 'hostA@192.168.99.25#/fake/volume1'}},
1423            {'volume': {'host': 'hostA@192.168.1.01#/fake/volume2'}},
1424            {'volume': {'host': 'hostA@192.168.99.25#/fake/volume3'}},
1425            {'volume': {'host': 'hostA@192.168.99.25#/fake/volume1'}},
1426        ]
1427
1428        ssc = {
1429            'volume1': {'pool_name': '/fake/volume1', },
1430            'volume2': {'pool_name': '/fake/volume2', },
1431            'volume3': {'pool_name': '/fake/volume3', },
1432        }
1433
1434        mock_get_ssc = self.mock_object(self.driver.ssc_library, 'get_ssc')
1435        mock_get_ssc.return_value = ssc
1436
1437        hosts = [snap['volume']['host'] for snap in snapshots]
1438        flexvols = self.driver._get_flexvol_names_from_hosts(hosts)
1439
1440        mock_get_ssc.assert_called_once_with()
1441        self.assertEqual(3, len(flexvols))
1442        self.assertIn('volume1', flexvols)
1443        self.assertIn('volume2', flexvols)
1444        self.assertIn('volume3', flexvols)
1445
1446    def test_get_backing_flexvol_names(self):
1447        mock_ssc_library = self.mock_object(
1448            self.driver.ssc_library, 'get_ssc')
1449
1450        self.driver._get_backing_flexvol_names()
1451
1452        mock_ssc_library.assert_called_once_with()
1453
1454    def test_create_group(self):
1455
1456        model_update = self.driver.create_group(
1457            fake.VG_CONTEXT, fake.VOLUME_GROUP)
1458
1459        self.assertEqual('available', model_update['status'])
1460
1461    def test_update_group(self):
1462
1463        model_update, add_volumes_update, remove_volumes_update = (
1464            self.driver.update_group(fake.VG_CONTEXT, "foo"))
1465
1466        self.assertIsNone(add_volumes_update)
1467        self.assertIsNone(remove_volumes_update)
1468
1469    @ddt.data(None,
1470              {'replication_status': fields.ReplicationStatus.ENABLED})
1471    def test_create_group_from_src(self, volume_model_update):
1472        volume_model_update = volume_model_update or {}
1473        volume_model_update.update(
1474            {'provider_location': fake.PROVIDER_LOCATION})
1475        mock_create_volume_from_snapshot = self.mock_object(
1476            self.driver, 'create_volume_from_snapshot',
1477            return_value=volume_model_update)
1478
1479        model_update, volumes_model_update = (
1480            self.driver.create_group_from_src(
1481                fake.VG_CONTEXT, fake.VOLUME_GROUP, [fake.VOLUME],
1482                group_snapshot=fake.VG_SNAPSHOT,
1483                sorted_snapshots=[fake.SNAPSHOT]))
1484
1485        expected_volumes_model_updates = [{'id': fake.VOLUME['id']}]
1486        expected_volumes_model_updates[0].update(volume_model_update)
1487        mock_create_volume_from_snapshot.assert_called_once_with(
1488            fake.VOLUME, fake.SNAPSHOT)
1489        self.assertIsNone(model_update)
1490        self.assertEqual(expected_volumes_model_updates, volumes_model_update)
1491
1492    @ddt.data(None,
1493              {'replication_status': fields.ReplicationStatus.ENABLED})
1494    def test_create_group_from_src_source_vols(self, volume_model_update):
1495        self.driver.zapi_client = mock.Mock()
1496        mock_get_snapshot_flexvols = self.mock_object(
1497            self.driver, '_get_flexvol_names_from_hosts')
1498        mock_get_snapshot_flexvols.return_value = (set([fake.VG_POOL_NAME]))
1499        mock_clone_backing_file = self.mock_object(
1500            self.driver, '_clone_backing_file_for_volume')
1501        fake_snapshot_name = 'snapshot-temp-' + fake.VOLUME_GROUP['id']
1502        mock_busy = self.mock_object(
1503            self.driver.zapi_client, 'wait_for_busy_snapshot')
1504        self.mock_object(self.driver, '_get_volume_model_update',
1505                         return_value=volume_model_update)
1506
1507        model_update, volumes_model_update = (
1508            self.driver.create_group_from_src(
1509                fake.VG_CONTEXT, fake.VOLUME_GROUP, [fake.VG_VOLUME],
1510                source_group=fake.VOLUME_GROUP,
1511                sorted_source_vols=[fake.SOURCE_VG_VOLUME]))
1512
1513        expected_volumes_model_updates = [{
1514            'id': fake.VG_VOLUME['id'],
1515            'provider_location': fake.PROVIDER_LOCATION,
1516        }]
1517        if volume_model_update:
1518            expected_volumes_model_updates[0].update(volume_model_update)
1519        mock_get_snapshot_flexvols.assert_called_once_with(
1520            [fake.SOURCE_VG_VOLUME['host']])
1521        self.driver.zapi_client.create_cg_snapshot.assert_called_once_with(
1522            set([fake.VG_POOL_NAME]), fake_snapshot_name)
1523        mock_clone_backing_file.assert_called_once_with(
1524            fake.SOURCE_VG_VOLUME['name'], fake.VG_VOLUME['name'],
1525            fake.SOURCE_VG_VOLUME['id'], source_snapshot=fake_snapshot_name)
1526        mock_busy.assert_called_once_with(
1527            fake.VG_POOL_NAME, fake_snapshot_name)
1528        self.driver.zapi_client.delete_snapshot.assert_called_once_with(
1529            fake.VG_POOL_NAME, fake_snapshot_name)
1530        self.assertIsNone(model_update)
1531        self.assertEqual(expected_volumes_model_updates, volumes_model_update)
1532
1533    def test_create_group_from_src_invalid_parms(self):
1534        model_update, volumes_model_update = (
1535            self.driver.create_group_from_src(
1536                fake.VG_CONTEXT, fake.VOLUME_GROUP, [fake.VOLUME]))
1537
1538        self.assertIn('error', model_update['status'])
1539
1540    def test_create_group_snapshot_raise_exception(self):
1541        mock_is_cg_snapshot = self.mock_object(
1542            volume_utils, 'is_group_a_cg_snapshot_type', return_value=True)
1543        mock__get_flexvol_names = self.mock_object(
1544            self.driver, '_get_flexvol_names_from_hosts')
1545
1546        self.mock_object(self.driver.zapi_client, 'create_cg_snapshot',
1547                         side_effect=netapp_api.NaApiError)
1548
1549        self.assertRaises(exception.NetAppDriverException,
1550                          self.driver.create_group_snapshot,
1551                          fake.VG_CONTEXT,
1552                          fake.VOLUME_GROUP,
1553                          [fake.VG_SNAPSHOT])
1554
1555        mock_is_cg_snapshot.assert_called_once_with(fake.VOLUME_GROUP)
1556        mock__get_flexvol_names.assert_called_once_with(
1557            [fake.VG_SNAPSHOT['volume']['host']])
1558
1559    def test_create_group_snapshot(self):
1560        mock_is_cg_snapshot = self.mock_object(
1561            volume_utils, 'is_group_a_cg_snapshot_type', return_value=False)
1562        mock__clone_backing_file_for_volume = self.mock_object(
1563            self.driver, '_clone_backing_file_for_volume')
1564
1565        model_update, snapshots_model_update = (
1566            self.driver.create_group_snapshot(fake.VG_CONTEXT,
1567                                              fake.VOLUME_GROUP,
1568                                              [fake.SNAPSHOT]))
1569
1570        self.assertIsNone(model_update)
1571        self.assertIsNone(snapshots_model_update)
1572        mock_is_cg_snapshot.assert_called_once_with(fake.VOLUME_GROUP)
1573        mock__clone_backing_file_for_volume.assert_called_once_with(
1574            fake.SNAPSHOT['volume_name'], fake.SNAPSHOT['name'],
1575            fake.SNAPSHOT['volume_id'], is_snapshot=True)
1576
1577    def test_create_consistent_group_snapshot(self):
1578        mock_is_cg_snapshot = self.mock_object(
1579            volume_utils, 'is_group_a_cg_snapshot_type', return_value=True)
1580
1581        self.driver.zapi_client = mock.Mock()
1582        mock_get_snapshot_flexvols = self.mock_object(
1583            self.driver, '_get_flexvol_names_from_hosts')
1584        mock_get_snapshot_flexvols.return_value = (set([fake.VG_POOL_NAME]))
1585        mock_clone_backing_file = self.mock_object(
1586            self.driver, '_clone_backing_file_for_volume')
1587        mock_busy = self.mock_object(
1588            self.driver.zapi_client, 'wait_for_busy_snapshot')
1589
1590        model_update, snapshots_model_update = (
1591            self.driver.create_group_snapshot(fake.VG_CONTEXT,
1592                                              fake.VOLUME_GROUP,
1593                                              [fake.VG_SNAPSHOT]))
1594
1595        self.assertIsNone(model_update)
1596        self.assertIsNone(snapshots_model_update)
1597        mock_is_cg_snapshot.assert_called_once_with(fake.VOLUME_GROUP)
1598        mock_get_snapshot_flexvols.assert_called_once_with(
1599            [fake.VG_SNAPSHOT['volume']['host']])
1600        self.driver.zapi_client.create_cg_snapshot.assert_called_once_with(
1601            set([fake.VG_POOL_NAME]), fake.VOLUME_GROUP_ID)
1602        mock_clone_backing_file.assert_called_once_with(
1603            fake.VG_SNAPSHOT['volume']['name'], fake.VG_SNAPSHOT['name'],
1604            fake.VG_SNAPSHOT['volume']['id'],
1605            source_snapshot=fake.VOLUME_GROUP_ID)
1606        mock_busy.assert_called_once_with(
1607            fake.VG_POOL_NAME, fake.VOLUME_GROUP_ID)
1608        self.driver.zapi_client.delete_snapshot.assert_called_once_with(
1609            fake.VG_POOL_NAME, fake.VOLUME_GROUP_ID)
1610
1611    def test_create_group_snapshot_busy_snapshot(self):
1612        self.mock_object(volume_utils, 'is_group_a_cg_snapshot_type',
1613                         return_value=True)
1614        self.driver.zapi_client = mock.Mock()
1615        snapshot = fake.VG_SNAPSHOT
1616        snapshot['volume'] = fake.VG_VOLUME
1617        mock_get_snapshot_flexvols = self.mock_object(
1618            self.driver, '_get_flexvol_names_from_hosts')
1619        mock_get_snapshot_flexvols.return_value = (set([fake.VG_POOL_NAME]))
1620        mock_clone_backing_file = self.mock_object(
1621            self.driver, '_clone_backing_file_for_volume')
1622        mock_busy = self.mock_object(
1623            self.driver.zapi_client, 'wait_for_busy_snapshot')
1624        mock_busy.side_effect = exception.SnapshotIsBusy(snapshot['name'])
1625        mock_mark_snapshot_for_deletion = self.mock_object(
1626            self.driver.zapi_client, 'mark_snapshot_for_deletion')
1627
1628        self.driver.create_group_snapshot(
1629            fake.VG_CONTEXT, fake.VG_SNAPSHOT, [snapshot])
1630
1631        mock_get_snapshot_flexvols.assert_called_once_with(
1632            [snapshot['volume']['host']])
1633        self.driver.zapi_client.create_cg_snapshot.assert_called_once_with(
1634            set([fake.VG_POOL_NAME]), fake.VG_SNAPSHOT_ID)
1635        mock_clone_backing_file.assert_called_once_with(
1636            snapshot['volume']['name'], snapshot['name'],
1637            snapshot['volume']['id'], source_snapshot=fake.VG_SNAPSHOT_ID)
1638        mock_busy.assert_called_once_with(
1639            fake.VG_POOL_NAME, fake.VG_SNAPSHOT_ID)
1640        self.driver.zapi_client.delete_snapshot.assert_not_called()
1641        mock_mark_snapshot_for_deletion.assert_called_once_with(
1642            fake.VG_POOL_NAME, fake.VG_SNAPSHOT_ID)
1643
1644    def test_delete_group_volume_delete_failure(self):
1645        self.mock_object(self.driver, '_delete_file', side_effect=Exception)
1646
1647        model_update, volumes = self.driver.delete_group(
1648            fake.VG_CONTEXT, fake.VOLUME_GROUP, [fake.VG_VOLUME])
1649
1650        self.assertEqual('deleted', model_update['status'])
1651        self.assertEqual('error_deleting', volumes[0]['status'])
1652
1653    def test_delete_group(self):
1654        mock_delete_file = self.mock_object(
1655            self.driver, '_delete_file')
1656
1657        model_update, volumes = self.driver.delete_group(
1658            fake.VG_CONTEXT, fake.VOLUME_GROUP, [fake.VG_VOLUME])
1659
1660        self.assertEqual('deleted', model_update['status'])
1661        self.assertEqual('deleted', volumes[0]['status'])
1662        mock_delete_file.assert_called_once_with(
1663            fake.VG_VOLUME_ID, fake.VG_VOLUME_NAME)
1664