# Copyright (c) 2013 VMware, Inc.
# 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
#    a copy of the License at
#
#         http://www.apache.org/licenses/LICENSE-2.0
#
#    Unless required by applicable law or agreed to in writing, software
#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
#    License for the specific language governing permissions and limitations
#    under the License.

"""Test suite for VMware vCenter VMDK driver."""

import re
from unittest import mock

import ddt
from oslo_utils import timeutils
from oslo_utils import units
from oslo_utils import versionutils
from oslo_vmware import exceptions
from oslo_vmware import image_transfer
from oslo_vmware import vim_util

from cinder import context
from cinder import exception as cinder_exceptions
from cinder.tests.unit import fake_constants
from cinder.tests.unit import fake_snapshot
from cinder.tests.unit import fake_volume
from cinder.tests.unit import test
from cinder.tests.unit import utils as test_utils
from cinder.tests.unit.volume.drivers.vmware import fake as vmware_fake
from cinder.volume.drivers.vmware import datastore as hub
from cinder.volume.drivers.vmware import exceptions as vmdk_exceptions
from cinder.volume.drivers.vmware import vmdk
from cinder.volume.drivers.vmware import volumeops


class MockConfiguration(object):
    def __init__(self, **kwargs):
        for kw in kwargs:
            setattr(self, kw, kwargs[kw])

    def safe_get(self, name):
        return getattr(self, name) if hasattr(self, name) else None

    def append_config_values(self, opts):
        for opt in opts:
            if not hasattr(self, opt.name):
                setattr(self, opt.name, opt.default or None)

# TODO(vbala) Split test methods handling multiple cases into multiple methods,
# each handling a specific case.


@ddt.ddt
class VMwareVcVmdkDriverTestCase(test.TestCase):
    """Unit tests for VMwareVcVmdkDriver."""

    IP = 'localhost'
    PORT = 2321
    USERNAME = 'username'
    PASSWORD = 'password'
    VOLUME_FOLDER = 'cinder-volumes'
    API_RETRY_COUNT = 3
    TASK_POLL_INTERVAL = 5.0
    IMG_TX_TIMEOUT = 10
    MAX_OBJECTS = 100
    TMP_DIR = "/vmware-tmp"
    CA_FILE = "/etc/ssl/rui-ca-cert.pem"
    VMDK_DRIVER = vmdk.VMwareVcVmdkDriver
    CLUSTERS = ["cls-1", "cls-2"]
    DEFAULT_VC_VERSION = '5.5'
    POOL_SIZE = 20
    SNAPSHOT_FORMAT = 'COW'

    VOL_ID = 'abcdefab-cdef-abcd-efab-cdefabcdefab'
    SRC_VOL_ID = '9b3f6f1b-03a9-4f1e-aaff-ae15122b6ccf'
    DISPLAY_NAME = 'foo'
    VOL_TYPE_ID = 'd61b8cb3-aa1b-4c9b-b79e-abcdbda8b58a'
    VOL_SIZE = 2
    PROJECT_ID = 'd45beabe-f5de-47b7-b462-0d9ea02889bc'
    SNAPSHOT_ID = '2f59670a-0355-4790-834c-563b65bba740'
    SNAPSHOT_NAME = 'snap-foo'
    SNAPSHOT_DESCRIPTION = 'test snapshot'
    IMAGE_ID = 'eb87f4b0-d625-47f8-bb45-71c43b486d3a'
    IMAGE_NAME = 'image-1'
    ADAPTER_TYPE = volumeops.VirtualDiskAdapterType.BUS_LOGIC

    def setUp(self):
        super(VMwareVcVmdkDriverTestCase, self).setUp()

        self._config = MockConfiguration(
            vmware_host_ip=self.IP,
            vmware_host_port=self.PORT,
            vmware_host_username=self.USERNAME,
            vmware_host_password=self.PASSWORD,
            vmware_wsdl_location=None,
            vmware_volume_folder=self.VOLUME_FOLDER,
            vmware_api_retry_count=self.API_RETRY_COUNT,
            vmware_task_poll_interval=self.TASK_POLL_INTERVAL,
            vmware_image_transfer_timeout_secs=self.IMG_TX_TIMEOUT,
            vmware_max_objects_retrieval=self.MAX_OBJECTS,
            vmware_tmp_dir=self.TMP_DIR,
            vmware_ca_file=self.CA_FILE,
            vmware_insecure=False,
            vmware_cluster_name=self.CLUSTERS,
            vmware_host_version=self.DEFAULT_VC_VERSION,
            vmware_connection_pool_size=self.POOL_SIZE,
            vmware_adapter_type=self.ADAPTER_TYPE,
            vmware_snapshot_format=self.SNAPSHOT_FORMAT,
            vmware_lazy_create=True,
            vmware_datastore_regex=None,
            reserved_percentage=0
        )

        self._driver = vmdk.VMwareVcVmdkDriver(configuration=self._config)

        self._context = context.get_admin_context()
        self.updated_at = timeutils.utcnow()

    @mock.patch.object(VMDK_DRIVER, 'volumeops')
    @mock.patch.object(VMDK_DRIVER, '_get_datastore_summaries')
    def test_get_volume_stats(self, _get_datastore_summaries, vops):
        FREE_GB = 7
        TOTAL_GB = 11

        class ObjMock(object):
            def __init__(self, **kwargs):
                self.__dict__.update(kwargs)

        _get_datastore_summaries.return_value = (ObjMock(objects= [
            ObjMock(propSet = [
                ObjMock(name = "host",
                        val = ObjMock(DatastoreHostMount = [])),
                ObjMock(name = "summary",
                        val = ObjMock(freeSpace = FREE_GB * units.Gi,
                                      capacity = TOTAL_GB * units.Gi,
                                      accessible = True))
            ])
        ]))

        vops._in_maintenance.return_value = False
        # Enable volume stats collection from backend
        self._driver.configuration.vmware_enable_volume_stats = True

        stats = self._driver.get_volume_stats()

        self.assertEqual('VMware', stats['vendor_name'])
        self.assertEqual(self._driver.VERSION, stats['driver_version'])
        self.assertEqual('vmdk', stats['storage_protocol'])
        self.assertEqual(self._config.reserved_percentage,
                         stats['reserved_percentage'])
        self.assertEqual(TOTAL_GB, stats['total_capacity_gb'])
        self.assertEqual(FREE_GB, stats['free_capacity_gb'])
        self.assertFalse(stats['shared_targets'])

    def test_test_volume_stats_disabled(self):
        RESERVED_PERCENTAGE = 0
        TOTAL_GB = 'unknown'
        FREE_GB = 'unknown'

        self._driver.configuration.vmware_enable_volume_stats = False
        stats = self._driver.get_volume_stats()

        self.assertEqual('VMware', stats['vendor_name'])
        self.assertEqual(self._driver.VERSION, stats['driver_version'])
        self.assertEqual('vmdk', stats['storage_protocol'])
        self.assertEqual(RESERVED_PERCENTAGE,
                         stats['reserved_percentage'])
        self.assertEqual(TOTAL_GB, stats['total_capacity_gb'])
        self.assertEqual(FREE_GB, stats['free_capacity_gb'])
        self.assertFalse(stats['shared_targets'])

    def _create_volume_dict(self,
                            vol_id=VOL_ID,
                            display_name=DISPLAY_NAME,
                            volume_type_id=VOL_TYPE_ID,
                            status='available',
                            size=VOL_SIZE,
                            attachment=None,
                            project_id=PROJECT_ID):
        return {'id': vol_id,
                'display_name': display_name,
                'name': 'volume-%s' % vol_id,
                'volume_type_id': volume_type_id,
                'status': status,
                'size': size,
                'volume_attachment': attachment,
                'project_id': project_id,
                }

    def _create_volume_obj(self,
                           vol_id=VOL_ID,
                           display_name=DISPLAY_NAME,
                           volume_type_id=VOL_TYPE_ID,
                           status='available',
                           size=VOL_SIZE,
                           attachment=None,
                           project_id=PROJECT_ID):
        vol = self._create_volume_dict(
            vol_id, display_name, volume_type_id, status, size, attachment,
            project_id)
        return fake_volume.fake_volume_obj(self._context, **vol)

    @mock.patch.object(VMDK_DRIVER, '_get_disk_type')
    @mock.patch.object(VMDK_DRIVER, '_get_storage_profile')
    @mock.patch.object(VMDK_DRIVER, 'ds_sel')
    @mock.patch.object(VMDK_DRIVER, '_get_adapter_type')
    def test_verify_volume_creation(self, get_adapter_type, ds_sel,
                                    get_storage_profile, get_disk_type):
        profile_name = mock.sentinel.profile_name
        get_storage_profile.return_value = profile_name

        volume = self._create_volume_obj()
        self._driver._verify_volume_creation(volume)

        get_disk_type.assert_called_once_with(volume)
        get_storage_profile.assert_called_once_with(volume)
        ds_sel.get_profile_id.assert_called_once_with(profile_name)
        get_adapter_type.assert_called_once_with(volume)

    @mock.patch.object(VMDK_DRIVER, '_verify_volume_creation')
    def test_create_volume(self, verify_volume_creation):
        volume = self._create_volume_dict()
        self._driver.create_volume(volume)

        verify_volume_creation.assert_called_once_with(volume)

    @mock.patch.object(VMDK_DRIVER, '_create_backing')
    def test_create_volume_with_lazy_create_disabled(self, create_backing):
        self._config.vmware_lazy_create = False
        volume = self._create_volume_dict()
        self._driver.create_volume(volume)

        create_backing.assert_called_once_with(volume)

    @mock.patch.object(VMDK_DRIVER, 'volumeops')
    def test_delete_volume_without_backing(self, vops):
        vops.get_backing.return_value = None

        volume = self._create_volume_dict()
        self._driver.delete_volume(volume)

        vops.get_backing.assert_called_once_with(volume['name'], volume['id'])
        self.assertFalse(vops.delete_backing.called)

    @mock.patch.object(VMDK_DRIVER, 'volumeops')
    def test_delete_volume(self, vops):
        backing = mock.sentinel.backing
        vops.get_backing.return_value = backing

        volume = self._create_volume_dict()
        self._driver.delete_volume(volume)

        vops.get_backing.assert_called_once_with(volume['name'], volume['id'])
        vops.delete_backing.assert_called_once_with(backing)

    @mock.patch('cinder.volume.drivers.vmware.vmdk.'
                '_get_volume_type_extra_spec')
    @mock.patch('cinder.volume.drivers.vmware.volumeops.'
                'VirtualDiskType.validate')
    def test_get_extra_spec_disk_type(self, validate,
                                      get_volume_type_extra_spec):
        vmdk_type = mock.sentinel.vmdk_type
        get_volume_type_extra_spec.return_value = vmdk_type

        type_id = mock.sentinel.type_id
        self.assertEqual(vmdk_type,
                         self._driver._get_extra_spec_disk_type(type_id))
        get_volume_type_extra_spec.assert_called_once_with(
            type_id, 'vmdk_type', default_value=vmdk.THIN_VMDK_TYPE)
        validate.assert_called_once_with(vmdk_type)

    @mock.patch.object(VMDK_DRIVER, '_get_extra_spec_disk_type')
    def test_get_disk_type(self, get_extra_spec_disk_type):
        vmdk_type = mock.sentinel.vmdk_type
        get_extra_spec_disk_type.return_value = vmdk_type

        volume = self._create_volume_dict()
        self.assertEqual(vmdk_type, self._driver._get_disk_type(volume))
        get_extra_spec_disk_type.assert_called_once_with(
            volume['volume_type_id'])

    @mock.patch('cinder.volume.drivers.vmware.vmdk.'
                '_get_volume_type_extra_spec')
    @mock.patch('cinder.volume.drivers.vmware.volumeops.'
                'VirtualDiskAdapterType.validate')
    def test_get_extra_spec_adapter_type(
            self, validate, get_volume_type_extra_spec):
        adapter_type = mock.sentinel.adapter_type
        get_volume_type_extra_spec.return_value = adapter_type

        type_id = mock.sentinel.type_id
        self.assertEqual(adapter_type,
                         self._driver._get_extra_spec_adapter_type(type_id))
        get_volume_type_extra_spec.assert_called_once_with(
            type_id, 'adapter_type',
            default_value=self._driver.configuration.vmware_adapter_type)
        validate.assert_called_once_with(adapter_type)

    @mock.patch.object(VMDK_DRIVER, '_get_extra_spec_adapter_type')
    def test_get_adapter_type(self, get_extra_spec_adapter_type):
        adapter_type = mock.sentinel.adapter_type
        get_extra_spec_adapter_type.return_value = adapter_type

        volume = self._create_volume_dict()
        self.assertEqual(adapter_type, self._driver._get_adapter_type(volume))
        get_extra_spec_adapter_type.assert_called_once_with(
            volume['volume_type_id'])

    def _create_snapshot_dict(self,
                              volume,
                              snap_id=SNAPSHOT_ID,
                              name=SNAPSHOT_NAME,
                              description=SNAPSHOT_DESCRIPTION,
                              provider_location=None):
        return {'id': snap_id,
                'volume': volume,
                'volume_name': volume['name'],
                'name': name,
                'display_description': description,
                'volume_size': volume['size'],
                'provider_location': provider_location
                }

    @mock.patch.object(VMDK_DRIVER, 'volumeops')
    @mock.patch.object(VMDK_DRIVER, '_get_volume_group_folder')
    def test_get_snapshot_group_folder(self, get_volume_group_folder, vops):
        dc = mock.sentinel.dc
        vops.get_dc.return_value = dc

        folder = mock.sentinel.folder
        get_volume_group_folder.return_value = folder

        volume = self._create_volume_obj()
        backing = mock.sentinel.backing
        self.assertEqual(folder, self._driver._get_snapshot_group_folder(
            volume, backing))
        vops.get_dc.assert_called_once_with(backing)
        get_volume_group_folder.assert_called_once_with(
            dc, volume.project_id, snapshot=True)

    @mock.patch.object(VMDK_DRIVER, '_get_snapshot_group_folder')
    @mock.patch.object(VMDK_DRIVER, 'volumeops')
    @mock.patch.object(VMDK_DRIVER, '_in_use')
    @mock.patch.object(VMDK_DRIVER, '_create_temp_backing_from_attached_vmdk')
    @mock.patch.object(VMDK_DRIVER, '_delete_temp_backing')
    def _test_create_snapshot_template_format(
            self, delete_temp_backing, create_temp_backing_from_attached_vmdk,
            in_use, vops, get_snapshot_group_folder, attached=False,
            mark_as_template_error=False):
        folder = mock.sentinel.folder
        get_snapshot_group_folder.return_value = folder

        datastore = mock.sentinel.datastore
        vops.get_datastore.return_value = datastore

        tmp_backing = mock.sentinel.tmp_backing
        if attached:
            in_use.return_value = True
            create_temp_backing_from_attached_vmdk.return_value = tmp_backing
        else:
            in_use.return_value = False
            vops.clone_backing.return_value = tmp_backing

        if mark_as_template_error:
            vops.mark_backing_as_template.side_effect = (
                exceptions.VimException())
        else:
            inv_path = mock.sentinel.inv_path
            vops.get_inventory_path.return_value = inv_path

        volume = self._create_volume_obj()
        snapshot = fake_snapshot.fake_snapshot_obj(
            self._context, volume=volume)
        backing = mock.sentinel.backing
        if mark_as_template_error:
            self.assertRaises(
                exceptions.VimException,
                self._driver._create_snapshot_template_format,
                snapshot,
                backing)
            delete_temp_backing.assert_called_once_with(tmp_backing)
        else:
            exp_result = {'provider_location': inv_path}
            self.assertEqual(exp_result,
                             self._driver._create_snapshot_template_format(
                                 snapshot, backing))
            delete_temp_backing.assert_not_called()
        get_snapshot_group_folder.test_assert_called_once_with(volume, backing)
        vops.get_datastore.assert_called_once_with(backing)
        in_use.assert_called_once_with(snapshot.volume)
        if attached:
            create_temp_backing_from_attached_vmdk.assert_called_once_with(
                snapshot.volume, None, None, folder, datastore,
                tmp_name=snapshot.name)
        else:
            vops.clone_backing.assert_called_once_with(
                snapshot.name, backing, None, volumeops.FULL_CLONE_TYPE,
                datastore, folder=folder)
        vops.mark_backing_as_template.assert_called_once_with(tmp_backing)

    def test_create_snapshot_template_format(self):
        self._test_create_snapshot_template_format()

    def test_create_snapshot_template_format_force(self):
        self._test_create_snapshot_template_format(attached=True)

    def test_create_snapshot_template_format_mark_template_error(self):
        self._test_create_snapshot_template_format(mark_as_template_error=True)

    @mock.patch.object(VMDK_DRIVER, '_in_use', return_value=False)
    @mock.patch.object(VMDK_DRIVER, 'volumeops')
    def test_create_snapshot_without_backing(self, vops, in_use):
        vops.get_backing.return_value = None

        volume = self._create_volume_dict()
        snapshot = self._create_snapshot_dict(volume)
        ret = self._driver.create_snapshot(snapshot)

        self.assertIsNone(ret)
        vops.get_backing.assert_called_once_with(snapshot['volume_name'],
                                                 snapshot['volume']['id'])
        self.assertFalse(vops.create_snapshot.called)

    @mock.patch.object(VMDK_DRIVER, '_in_use', return_value=False)
    @mock.patch.object(VMDK_DRIVER, 'volumeops')
    def test_create_snapshot_with_backing(self, vops, in_use):
        backing = mock.sentinel.backing
        vops.get_backing.return_value = backing

        volume = self._create_volume_dict()
        snapshot = self._create_snapshot_dict(volume)
        ret = self._driver.create_snapshot(snapshot)

        self.assertIsNone(ret)
        vops.get_backing.assert_called_once_with(snapshot['volume_name'],
                                                 snapshot['volume']['id'])
        vops.create_snapshot.assert_called_once_with(
            backing, snapshot['name'], snapshot['display_description'])

    @mock.patch.object(VMDK_DRIVER, '_in_use', return_value=True)
    def test_create_snapshot_when_attached(self, in_use):
        volume = self._create_volume_dict(status='in-use')
        snapshot = self._create_snapshot_dict(volume)
        self.assertRaises(cinder_exceptions.InvalidVolume,
                          self._driver.create_snapshot, snapshot)

    @mock.patch.object(VMDK_DRIVER, '_in_use', return_value=True)
    @mock.patch.object(VMDK_DRIVER, 'volumeops')
    @mock.patch.object(VMDK_DRIVER, '_create_snapshot_template_format')
    def test_create_snapshot_template(
            self, create_snapshot_template_format, vops, in_use):
        self._driver.configuration.vmware_snapshot_format = 'template'

        backing = mock.sentinel.backing
        vops.get_backing.return_value = backing

        model_update = mock.sentinel.model_update
        create_snapshot_template_format.return_value = model_update

        volume = self._create_volume_dict()
        snapshot = self._create_snapshot_dict(volume)
        ret = self._driver.create_snapshot(snapshot)

        self.assertEqual(model_update, ret)
        vops.get_backing.assert_called_once_with(snapshot['volume_name'],
                                                 snapshot['volume']['id'])
        create_snapshot_template_format.assert_called_once_with(
            snapshot, backing)

    @mock.patch.object(VMDK_DRIVER, 'volumeops')
    def test_get_template_by_inv_path(self, vops):
        template = mock.sentinel.template
        vops.get_entity_by_inventory_path.return_value = template

        inv_path = mock.sentinel.inv_path
        self.assertEqual(template,
                         self._driver._get_template_by_inv_path(inv_path))
        vops.get_entity_by_inventory_path.assert_called_once_with(inv_path)

    @mock.patch.object(VMDK_DRIVER, 'volumeops')
    def test_get_template_by_inv_path_invalid_path(self, vops):
        vops.get_entity_by_inventory_path.return_value = None

        inv_path = mock.sentinel.inv_path
        self.assertRaises(vmdk_exceptions.TemplateNotFoundException,
                          self._driver._get_template_by_inv_path,
                          inv_path)
        vops.get_entity_by_inventory_path.assert_called_once_with(inv_path)

    @mock.patch.object(VMDK_DRIVER, '_get_template_by_inv_path')
    @mock.patch.object(VMDK_DRIVER, 'volumeops')
    def test_delete_snapshot_template_format(
            self, vops, get_template_by_inv_path):
        template = mock.sentinel.template
        get_template_by_inv_path.return_value = template

        inv_path = '/dc-1/vm/foo'
        volume = self._create_volume_dict()
        snapshot = fake_snapshot.fake_snapshot_obj(self._context,
                                                   volume=volume,
                                                   provider_location=inv_path)
        self._driver._delete_snapshot_template_format(snapshot)

        get_template_by_inv_path.assert_called_once_with(inv_path)
        vops.delete_backing.assert_called_once_with(template)

    @mock.patch.object(VMDK_DRIVER, 'volumeops')
    def test_delete_snapshot_without_backing(self, vops):
        vops.get_backing.return_value = None

        volume = self._create_volume_dict()
        snapshot = fake_snapshot.fake_snapshot_obj(self._context,
                                                   volume=volume)
        self._driver.delete_snapshot(snapshot)

        vops.get_backing.assert_called_once_with(snapshot.volume_name,
                                                 snapshot.volume.id)
        vops.get_snapshot.assert_not_called()
        vops.delete_snapshot.assert_not_called()

    @mock.patch.object(VMDK_DRIVER, 'volumeops')
    @mock.patch.object(VMDK_DRIVER, '_in_use', return_value=False)
    def test_delete_snapshot_with_backing(self, in_use, vops):
        backing = mock.sentinel.backing
        vops.get_backing.return_value = backing

        volume = self._create_volume_dict(status='deleting')
        snapshot = fake_snapshot.fake_snapshot_obj(self._context,
                                                   volume=volume)
        self._driver.delete_snapshot(snapshot)

        vops.get_backing.assert_called_once_with(snapshot.volume_name,
                                                 snapshot.volume.id)
        vops.get_snapshot.assert_called_once_with(backing, snapshot.name)
        in_use.assert_called_once_with(snapshot.volume)
        vops.delete_snapshot.assert_called_once_with(
            backing, snapshot.name)

    @mock.patch.object(VMDK_DRIVER, 'volumeops')
    @mock.patch.object(VMDK_DRIVER, '_in_use', return_value=True)
    def test_delete_snapshot_when_attached(self, in_use, vops):
        volume = self._create_volume_dict(status='in-use')
        snapshot = fake_snapshot.fake_snapshot_obj(self._context,
                                                   volume=volume)

        self.assertRaises(cinder_exceptions.InvalidSnapshot,
                          self._driver.delete_snapshot, snapshot)
        in_use.assert_called_once_with(snapshot.volume)

    @mock.patch.object(VMDK_DRIVER, 'volumeops')
    def test_delete_snapshot_without_backend_snapshot(self, vops):
        backing = mock.sentinel.backing
        vops.get_backing.return_value = backing

        vops.get_snapshot.return_value = None

        volume = self._create_volume_dict(status='in-use')
        snapshot = fake_snapshot.fake_snapshot_obj(self._context,
                                                   volume=volume)
        self._driver.delete_snapshot(snapshot)

        vops.get_backing.assert_called_once_with(snapshot.volume_name,
                                                 snapshot.volume.id)
        vops.get_snapshot.assert_called_once_with(backing, snapshot.name)
        vops.delete_snapshot.assert_not_called()

    @mock.patch.object(VMDK_DRIVER, 'volumeops')
    @mock.patch.object(VMDK_DRIVER, '_in_use', return_value=True)
    @mock.patch.object(VMDK_DRIVER, '_delete_snapshot_template_format')
    def test_delete_snapshot_template(
            self, delete_snapshot_template_format, in_use, vops):
        backing = mock.sentinel.backing
        vops.get_backing.return_value = backing

        inv_path = '/dc-1/vm/foo'
        volume = self._create_volume_dict(status='deleting')
        snapshot = fake_snapshot.fake_snapshot_obj(self._context,
                                                   volume=volume,
                                                   provider_location=inv_path)
        self._driver.delete_snapshot(snapshot)

        vops.get_backing.assert_called_once_with(snapshot.volume_name,
                                                 snapshot.volume.id)
        vops.get_snapshot.assert_not_called()
        in_use.assert_called_once_with(snapshot.volume)
        delete_snapshot_template_format.assert_called_once_with(snapshot)

    @ddt.data('vmdk', 'VMDK', None)
    def test_validate_disk_format(self, disk_format):
        self._driver._validate_disk_format(disk_format)

    def test_validate_disk_format_with_invalid_format(self):
        self.assertRaises(cinder_exceptions.ImageUnacceptable,
                          self._driver._validate_disk_format,
                          'img')

    def _create_image_meta(self,
                           _id=IMAGE_ID,
                           name=IMAGE_NAME,
                           disk_format='vmdk',
                           size=1 * units.Gi,
                           container_format='bare',
                           vmware_disktype='streamOptimized',
                           vmware_adaptertype='lsiLogic',
                           is_public=True):
        return {'id': _id,
                'name': name,
                'disk_format': disk_format,
                'size': size,
                'container_format': container_format,
                'properties': {'vmware_disktype': vmware_disktype,
                               'vmware_adaptertype': vmware_adaptertype,
                               },
                'is_public': is_public,
                }

    @mock.patch.object(VMDK_DRIVER, 'copy_image_to_volume')
    def test_clone_image(self, copy_image_to_volume):
        model_update = mock.sentinel.model_update
        copy_image_to_volume.return_value = model_update

        context = mock.sentinel.context
        volume = mock.sentinel.volume
        image_meta = self._create_image_meta()
        image_service = mock.sentinel.image_service

        ret = self._driver.clone_image(
            context, volume, mock.sentinel.image_location, image_meta,
            image_service)
        self.assertEqual((model_update, True), ret)
        copy_image_to_volume.assert_called_once_with(
            context, volume, image_service, image_meta['id'])

    @mock.patch('cinder.volume.drivers.vmware.vmdk.VMwareVcVmdkDriver.'
                '_validate_disk_format')
    def test_copy_image_to_volume_with_invalid_container(self,
                                                         validate_disk_format):
        image_service = mock.Mock()
        image_meta = self._create_image_meta(container_format='ami')
        image_service.show.return_value = image_meta

        context = mock.sentinel.context
        volume = self._create_volume_dict()
        image_id = mock.sentinel.image_id

        self.assertRaises(
            cinder_exceptions.ImageUnacceptable,
            self._driver.copy_image_to_volume, context, volume, image_service,
            image_id)
        validate_disk_format.assert_called_once_with(image_meta['disk_format'])

    @mock.patch.object(VMDK_DRIVER, '_get_disk_type')
    @mock.patch('cinder.volume.drivers.vmware.vmdk.VMwareVcVmdkDriver.'
                '_validate_disk_format')
    @mock.patch.object(VMDK_DRIVER, '_get_adapter_type',
                       return_value=volumeops.VirtualDiskAdapterType.BUS_LOGIC)
    @mock.patch('cinder.volume.drivers.vmware.volumeops.'
                'VirtualDiskAdapterType.validate')
    @mock.patch('cinder.volume.drivers.vmware.vmdk.ImageDiskType.'
                'validate')
    @mock.patch.object(VMDK_DRIVER,
                       '_create_volume_from_non_stream_optimized_image')
    @mock.patch.object(VMDK_DRIVER,
                       '_fetch_stream_optimized_image')
    @mock.patch.object(VMDK_DRIVER, 'volumeops')
    @mock.patch.object(VMDK_DRIVER, '_extend_backing')
    def _test_copy_image_to_volume(self,
                                   extend_backing,
                                   vops,
                                   fetch_stream_optimized_image,
                                   create_volume_from_non_stream_opt_image,
                                   validate_image_disk_type,
                                   validate_image_adapter_type,
                                   get_adapter_type,
                                   validate_disk_format,
                                   get_disk_type,
                                   vmware_disk_type='streamOptimized',
                                   backing_disk_size=VOL_SIZE,
                                   call_extend_backing=False,
                                   container_format='bare'):

        image_service = mock.Mock()
        image_meta = self._create_image_meta(vmware_disktype=vmware_disk_type,
                                             container_format=container_format)
        image_service.show.return_value = image_meta

        backing = mock.sentinel.backing
        vops.get_backing.return_value = backing
        vops.get_disk_size.return_value = backing_disk_size * units.Gi

        disk_type = mock.sentinel.disk_type
        get_disk_type.return_value = disk_type

        context = mock.sentinel.context
        volume = self._create_volume_dict()
        image_id = mock.sentinel.image_id
        self._driver.copy_image_to_volume(
            context, volume, image_service, image_id)

        validate_disk_format.assert_called_once_with(image_meta['disk_format'])
        validate_image_disk_type.assert_called_once_with(
            image_meta['properties']['vmware_disktype'])
        validate_image_adapter_type.assert_called_once_with(
            image_meta['properties']['vmware_adaptertype'])

        if vmware_disk_type == 'streamOptimized':
            fetch_stream_optimized_image.assert_called_once_with(
                context, volume, image_service, image_id, image_meta['size'],
                image_meta['properties']['vmware_adaptertype'])
        else:
            create_volume_from_non_stream_opt_image.assert_called_once_with(
                context, volume, image_service, image_id, image_meta['size'],
                image_meta['properties']['vmware_adaptertype'],
                image_meta['properties']['vmware_disktype'])

        vops.get_disk_size.assert_called_once_with(backing)
        if call_extend_backing:
            extend_backing.assert_called_once_with(backing, volume['size'],
                                                   disk_type)
        else:
            self.assertFalse(extend_backing.called)

    @ddt.data('sparse', 'preallocated', 'streamOptimized')
    def test_copy_image_to_volume(self, vmware_disk_type):
        self._test_copy_image_to_volume(vmware_disk_type=vmware_disk_type)

    @ddt.data('sparse', 'preallocated', 'streamOptimized')
    def test_copy_image_to_volume_with_extend_backing(self, vmware_disk_type):
        self._test_copy_image_to_volume(vmware_disk_type=vmware_disk_type,
                                        backing_disk_size=1,
                                        call_extend_backing=True)

    def test_copy_image_to_volume_with_ova_container(self):
        self._test_copy_image_to_volume(container_format='ova')

    @mock.patch('cinder.volume.drivers.vmware.vmdk.VMwareVcVmdkDriver.'
                '_get_disk_type')
    @mock.patch.object(VMDK_DRIVER, '_check_disk_conversion')
    @mock.patch('oslo_utils.uuidutils.generate_uuid')
    @mock.patch.object(VMDK_DRIVER, '_create_backing')
    @mock.patch.object(VMDK_DRIVER, '_get_ds_name_folder_path')
    @mock.patch.object(VMDK_DRIVER, 'volumeops')
    @mock.patch.object(VMDK_DRIVER, '_create_virtual_disk_from_sparse_image')
    @mock.patch.object(VMDK_DRIVER,
                       '_create_virtual_disk_from_preallocated_image')
    @mock.patch.object(VMDK_DRIVER, '_get_storage_profile_id')
    @mock.patch.object(VMDK_DRIVER, '_select_ds_for_volume')
    @mock.patch.object(VMDK_DRIVER, '_delete_temp_backing')
    def _test_create_volume_from_non_stream_optimized_image(
            self,
            delete_tmp_backing,
            select_ds_for_volume,
            get_storage_profile_id,
            create_disk_from_preallocated_image,
            create_disk_from_sparse_image,
            vops,
            get_ds_name_folder_path,
            create_backing,
            generate_uuid,
            check_disk_conversion,
            get_disk_type,
            image_disk_type='sparse',
            disk_conversion=False):

        disk_type = mock.sentinel.disk_type
        get_disk_type.return_value = disk_type
        check_disk_conversion.return_value = disk_conversion

        volume = self._create_volume_dict()
        if disk_conversion:
            disk_name = "6b77b25a-9136-470e-899e-3c930e570d8e"
            generate_uuid.return_value = disk_name
        else:
            disk_name = volume['name']

        backing = mock.sentinel.backing
        create_backing.return_value = backing

        ds_name = mock.sentinel.ds_name
        folder_path = mock.sentinel.folder_path
        get_ds_name_folder_path.return_value = (ds_name, folder_path)

        host = mock.sentinel.host
        dc_ref = mock.sentinel.dc_ref
        vops.get_host.return_value = host
        vops.get_dc.return_value = dc_ref

        vmdk_path = mock.Mock(spec=volumeops.FlatExtentVirtualDiskPath)
        create_disk_from_sparse_image.return_value = vmdk_path
        create_disk_from_preallocated_image.return_value = vmdk_path

        profile_id = mock.sentinel.profile_id
        get_storage_profile_id.return_value = profile_id

        if disk_conversion:
            rp = mock.sentinel.rp
            folder = mock.sentinel.folder
            datastore = mock.sentinel.datastore
            summary = mock.Mock(datastore=datastore)
            select_ds_for_volume.return_value = (host, rp, folder, summary)

            clone = mock.sentinel.clone
            vops.clone_backing.return_value = clone

        context = mock.sentinel.context
        image_service = mock.sentinel.image_service
        image_id = mock.sentinel.image_id
        image_size_in_bytes = units.Gi
        adapter_type = mock.sentinel.adapter_type

        self._driver._create_volume_from_non_stream_optimized_image(
            context, volume, image_service, image_id, image_size_in_bytes,
            adapter_type, image_disk_type)

        check_disk_conversion.assert_called_once_with(image_disk_type,
                                                      mock.sentinel.disk_type)
        if disk_conversion:
            create_backing.assert_called_once_with(
                volume,
                create_params={vmdk.CREATE_PARAM_DISK_LESS: True,
                               vmdk.CREATE_PARAM_BACKING_NAME: disk_name,
                               vmdk.CREATE_PARAM_TEMP_BACKING: True})
        else:
            create_backing.assert_called_once_with(
                volume, create_params={vmdk.CREATE_PARAM_DISK_LESS: True})

        if image_disk_type == 'sparse':
            create_disk_from_sparse_image.assert_called_once_with(
                context, image_service, image_id, image_size_in_bytes,
                dc_ref, ds_name, folder_path, disk_name)
        else:
            create_disk_from_preallocated_image.assert_called_once_with(
                context, image_service, image_id, image_size_in_bytes,
                dc_ref, ds_name, folder_path, disk_name, adapter_type)

        get_storage_profile_id.assert_called_once_with(volume)
        vops.attach_disk_to_backing.assert_called_once_with(
            backing, image_size_in_bytes / units.Ki, disk_type,
            adapter_type, profile_id, vmdk_path.get_descriptor_ds_file_path())

        if disk_conversion:
            select_ds_for_volume.assert_called_once_with(volume)
            extra_config = {vmdk.EXTRA_CONFIG_VOLUME_ID_KEY: volume['id'],
                            volumeops.BACKING_UUID_KEY: volume['id']}
            vops.clone_backing.assert_called_once_with(
                volume['name'], backing, None, volumeops.FULL_CLONE_TYPE,
                datastore, disk_type=disk_type, host=host, resource_pool=rp,
                extra_config=extra_config, folder=folder)
            delete_tmp_backing.assert_called_once_with(backing)
            vops.update_backing_disk_uuid(clone, volume['id'])
        else:
            vops.update_backing_disk_uuid(backing, volume['id'])

    @ddt.data('sparse', 'preallocated')
    def test_create_volume_from_non_stream_optimized_image(self,
                                                           image_disk_type):
        self._test_create_volume_from_non_stream_optimized_image(
            image_disk_type=image_disk_type)

    @ddt.data('sparse', 'preallocated')
    def test_create_volume_from_non_stream_opt_image_with_disk_conversion(
            self, image_disk_type):
        self._test_create_volume_from_non_stream_optimized_image(
            image_disk_type=image_disk_type, disk_conversion=True)

    def _test_get_vsphere_url(self, direct_url, exp_vsphere_url=None):
        image_service = mock.Mock()
        image_service.get_location.return_value = (direct_url, [])

        context = mock.sentinel.context
        image_id = mock.sentinel.image_id
        ret = self._driver._get_vsphere_url(context, image_service, image_id)

        self.assertEqual(exp_vsphere_url, ret)
        image_service.get_location.assert_called_once_with(context, image_id)

    def test_get_vsphere_url(self):
        url = "vsphere://foo/folder/glance/img_uuid?dcPath=dc1&dsName=ds1"
        self._test_get_vsphere_url(url, exp_vsphere_url=url)

    def test_get_vsphere_url_(self):
        url = "http://foo/folder/glance/img_uuid?dcPath=dc1&dsName=ds1"
        self._test_get_vsphere_url(url)

    @mock.patch.object(VMDK_DRIVER, '_copy_temp_virtual_disk')
    @mock.patch.object(VMDK_DRIVER, '_get_temp_image_folder')
    @mock.patch('oslo_utils.uuidutils.generate_uuid')
    @mock.patch(
        'cinder.volume.drivers.vmware.volumeops.FlatExtentVirtualDiskPath')
    @mock.patch.object(VMDK_DRIVER, '_get_vsphere_url')
    @mock.patch.object(VMDK_DRIVER, '_copy_image')
    @mock.patch.object(VMDK_DRIVER, 'volumeops')
    def _test_create_virtual_disk_from_preallocated_image(
            self, vops, copy_image, get_vsphere_url, flat_extent_path,
            generate_uuid, get_temp_image_folder, copy_temp_virtual_disk,
            vsphere_url=None):
        dc_ref = vmware_fake.ManagedObjectReference(value=mock.sentinel.dc_ref)
        ds_name = mock.sentinel.ds_name
        folder_path = mock.sentinel.folder_path
        get_temp_image_folder.return_value = (dc_ref, ds_name, folder_path)

        uuid = mock.sentinel.uuid
        generate_uuid.return_value = uuid
        path = mock.Mock()
        dest_path = mock.Mock()
        flat_extent_path.side_effect = [path, dest_path]

        get_vsphere_url.return_value = vsphere_url

        context = mock.sentinel.context
        image_service = mock.sentinel.image_service
        image_id = mock.sentinel.image_id
        image_size_in_bytes = 2 * units.Gi
        dest_dc_ref = \
            vmware_fake.ManagedObjectReference(value=mock.sentinel.dest_dc_ref)
        dest_ds_name = mock.sentinel.dest_ds_name
        dest_folder_path = mock.sentinel.dest_folder_path
        dest_disk_name = mock.sentinel.dest_disk_name
        adapter_type = mock.sentinel.adapter_type
        ret = self._driver._create_virtual_disk_from_preallocated_image(
            context, image_service, image_id, image_size_in_bytes, dest_dc_ref,
            dest_ds_name, dest_folder_path, dest_disk_name, adapter_type)

        exp_flat_extent_path_calls = [
            mock.call(ds_name, folder_path, uuid),
            mock.call(dest_ds_name, dest_folder_path, dest_disk_name)]
        self.assertEqual(exp_flat_extent_path_calls,
                         flat_extent_path.call_args_list)
        create_descriptor = vops.create_flat_extent_virtual_disk_descriptor
        create_descriptor.assert_called_once_with(
            dc_ref, path, image_size_in_bytes / units.Ki, adapter_type,
            vmdk.EAGER_ZEROED_THICK_VMDK_TYPE)
        get_vsphere_url.assert_called_once_with(
            context, image_service, image_id)
        if vsphere_url:
            vops.copy_datastore_file.assert_called_once_with(
                vsphere_url, dc_ref, path.get_flat_extent_ds_file_path())
        else:
            copy_image.assert_called_once_with(
                context, dc_ref, image_service, image_id, image_size_in_bytes,
                ds_name, path.get_flat_extent_file_path())
        copy_temp_virtual_disk.assert_called_once_with(dc_ref, path,
                                                       dest_dc_ref, dest_path)
        self.assertEqual(dest_path, ret)

    def test_create_virtual_disk_from_preallocated_image(self):
        self._test_create_virtual_disk_from_preallocated_image()

    def test_create_virtual_disk_from_preallocated_image_on_vsphere(self):
        self._test_create_virtual_disk_from_preallocated_image(
            vsphere_url=mock.sentinel.vsphere_url)

    @mock.patch.object(VMDK_DRIVER, '_copy_temp_virtual_disk')
    @mock.patch.object(VMDK_DRIVER, '_get_temp_image_folder')
    @mock.patch(
        'cinder.volume.drivers.vmware.volumeops.FlatExtentVirtualDiskPath')
    @mock.patch.object(VMDK_DRIVER, '_get_vsphere_url', return_value=None)
    @mock.patch.object(VMDK_DRIVER, '_copy_image')
    @mock.patch.object(VMDK_DRIVER, 'volumeops')
    def test_create_virtual_disk_from_preallocated_image_with_no_disk_copy(
            self, vops, copy_image, get_vsphere_url, flat_extent_path,
            get_temp_image_folder, copy_temp_virtual_disk):
        dc_ref = vmware_fake.ManagedObjectReference(value=mock.sentinel.dc_ref)
        ds_name = mock.sentinel.ds_name
        folder_path = mock.sentinel.folder_path
        get_temp_image_folder.return_value = (dc_ref, ds_name, folder_path)

        path = mock.Mock()
        flat_extent_path.return_value = path

        context = mock.sentinel.context
        image_service = mock.sentinel.image_service
        image_id = mock.sentinel.image_id
        image_size_in_bytes = 2 * units.Gi
        dc_ref_value = mock.sentinel.dc_ref
        dest_dc_ref = vmware_fake.ManagedObjectReference(value=dc_ref_value)
        dest_ds_name = ds_name
        dest_folder_path = mock.sentinel.dest_folder_path
        dest_disk_name = mock.sentinel.dest_disk_name
        adapter_type = mock.sentinel.adapter_type
        ret = self._driver._create_virtual_disk_from_preallocated_image(
            context, image_service, image_id, image_size_in_bytes, dest_dc_ref,
            dest_ds_name, dest_folder_path, dest_disk_name, adapter_type)

        flat_extent_path.assert_called_once_with(
            dest_ds_name, dest_folder_path, dest_disk_name)
        create_descriptor = vops.create_flat_extent_virtual_disk_descriptor
        create_descriptor.assert_called_once_with(
            dc_ref, path, image_size_in_bytes / units.Ki, adapter_type,
            vmdk.EAGER_ZEROED_THICK_VMDK_TYPE)
        copy_image.assert_called_once_with(
            context, dc_ref, image_service, image_id, image_size_in_bytes,
            ds_name, path.get_flat_extent_file_path())
        self.assertFalse(copy_temp_virtual_disk.called)
        self.assertEqual(path, ret)

    @mock.patch.object(VMDK_DRIVER, '_copy_temp_virtual_disk')
    @mock.patch.object(VMDK_DRIVER, '_get_temp_image_folder')
    @mock.patch('oslo_utils.uuidutils.generate_uuid')
    @mock.patch(
        'cinder.volume.drivers.vmware.volumeops.FlatExtentVirtualDiskPath')
    @mock.patch.object(VMDK_DRIVER, '_get_vsphere_url', return_value=None)
    @mock.patch.object(VMDK_DRIVER, '_copy_image')
    @mock.patch.object(VMDK_DRIVER, 'volumeops')
    def test_create_virtual_disk_from_preallocated_image_with_copy_error(
            self, vops, copy_image, get_vsphere_url, flat_extent_path,
            generate_uuid, get_temp_image_folder, copy_temp_virtual_disk):
        dc_ref = vmware_fake.ManagedObjectReference(value=mock.sentinel.dc_ref)
        ds_name = mock.sentinel.ds_name
        folder_path = mock.sentinel.folder_path
        get_temp_image_folder.return_value = (dc_ref, ds_name, folder_path)

        uuid = mock.sentinel.uuid
        generate_uuid.return_value = uuid
        path = mock.Mock()
        dest_path = mock.Mock()
        flat_extent_path.side_effect = [path, dest_path]

        copy_image.side_effect = exceptions.VimException("error")

        context = mock.sentinel.context
        image_service = mock.sentinel.image_service
        image_id = mock.sentinel.image_id
        image_size_in_bytes = 2 * units.Gi
        dest_dc_ref = \
            vmware_fake.ManagedObjectReference(value=mock.sentinel.dest_dc_ref)
        dest_ds_name = mock.sentinel.dest_ds_name
        dest_folder_path = mock.sentinel.dest_folder_path
        dest_disk_name = mock.sentinel.dest_disk_name
        adapter_type = mock.sentinel.adapter_type
        self.assertRaises(
            exceptions.VimException,
            self._driver._create_virtual_disk_from_preallocated_image,
            context, image_service, image_id, image_size_in_bytes, dest_dc_ref,
            dest_ds_name, dest_folder_path, dest_disk_name, adapter_type)

        vops.delete_file.assert_called_once_with(
            path.get_descriptor_ds_file_path(), dc_ref)
        self.assertFalse(copy_temp_virtual_disk.called)

    @mock.patch('oslo_utils.uuidutils.generate_uuid')
    @mock.patch(
        'cinder.volume.drivers.vmware.volumeops.'
        'MonolithicSparseVirtualDiskPath')
    @mock.patch(
        'cinder.volume.drivers.vmware.volumeops.FlatExtentVirtualDiskPath')
    @mock.patch.object(VMDK_DRIVER, '_copy_temp_virtual_disk')
    @mock.patch.object(VMDK_DRIVER, '_get_vsphere_url')
    @mock.patch.object(VMDK_DRIVER, '_copy_image')
    @mock.patch.object(VMDK_DRIVER, 'volumeops')
    def _test_create_virtual_disk_from_sparse_image(
            self, vops, copy_image, get_vsphere_url, copy_temp_virtual_disk,
            flat_extent_path, sparse_path, generate_uuid, vsphere_url=None):
        uuid = mock.sentinel.uuid
        generate_uuid.return_value = uuid

        src_path = mock.Mock()
        sparse_path.return_value = src_path

        dest_path = mock.Mock()
        flat_extent_path.return_value = dest_path

        get_vsphere_url.return_value = vsphere_url

        context = mock.sentinel.context
        image_service = mock.sentinel.image_service
        image_id = mock.sentinel.image_id
        image_size_in_bytes = 2 * units.Gi
        dc_ref = mock.sentinel.dc_ref
        ds_name = mock.sentinel.ds_name
        folder_path = mock.sentinel.folder_path
        disk_name = mock.sentinel.disk_name

        ret = self._driver._create_virtual_disk_from_sparse_image(
            context, image_service, image_id, image_size_in_bytes, dc_ref,
            ds_name, folder_path, disk_name)

        sparse_path.assert_called_once_with(ds_name, folder_path, uuid)
        get_vsphere_url.assert_called_once_with(
            context, image_service, image_id)
        if vsphere_url:
            vops.copy_datastore_file.assert_called_once_with(
                vsphere_url, dc_ref, src_path.get_descriptor_ds_file_path())
        else:
            copy_image.assert_called_once_with(
                context, dc_ref, image_service, image_id, image_size_in_bytes,
                ds_name, src_path.get_descriptor_file_path())
        flat_extent_path.assert_called_once_with(
            ds_name, folder_path, disk_name)
        copy_temp_virtual_disk.assert_called_once_with(
            dc_ref, src_path, dc_ref, dest_path)
        self.assertEqual(dest_path, ret)

    def test_create_virtual_disk_from_sparse_image(self):
        self._test_create_virtual_disk_from_sparse_image()

    def test_create_virtual_disk_from_sparse_image_on_vsphere(self):
        self._test_create_virtual_disk_from_sparse_image(
            vsphere_url=mock.sentinel.vsphere_url)

    @mock.patch.object(VMDK_DRIVER, '_select_datastore')
    @mock.patch.object(VMDK_DRIVER, 'volumeops')
    def test_get_temp_image_folder(self, vops, select_datastore):
        host = mock.sentinel.host
        resource_pool = mock.sentinel.rp
        summary = mock.Mock()
        ds_name = mock.sentinel.ds_name
        summary.name = ds_name
        select_datastore.return_value = (host, resource_pool, summary)

        dc = mock.sentinel.dc
        vops.get_dc.return_value = dc

        image_size = 2 * units.Gi
        ret = self._driver._get_temp_image_folder(image_size)

        self.assertEqual((dc, ds_name, vmdk.TMP_IMAGES_DATASTORE_FOLDER_PATH),
                         ret)
        exp_req = {
            hub.DatastoreSelector.SIZE_BYTES: image_size,
            hub.DatastoreSelector.HARD_AFFINITY_DS_TYPE:
                {hub.DatastoreType.VMFS,
                 hub.DatastoreType.NFS,
                 hub.DatastoreType.NFS41}}
        select_datastore.assert_called_once_with(exp_req)
        vops.create_datastore_folder.assert_called_once_with(
            ds_name, vmdk.TMP_IMAGES_DATASTORE_FOLDER_PATH, dc)

    @mock.patch.object(VMDK_DRIVER, '_select_ds_for_volume')
    @mock.patch.object(VMDK_DRIVER, '_get_storage_profile_id')
    @mock.patch('cinder.volume.drivers.vmware.vmdk.VMwareVcVmdkDriver.'
                '_get_disk_type')
    @mock.patch.object(VMDK_DRIVER, '_get_extra_config')
    @mock.patch.object(VMDK_DRIVER, 'volumeops')
    @mock.patch.object(VMDK_DRIVER, 'session')
    @mock.patch.object(image_transfer, 'download_stream_optimized_image')
    def _test_copy_image_to_volume_stream_optimized(self,
                                                    download_image,
                                                    session,
                                                    vops,
                                                    get_extra_config,
                                                    get_disk_type,
                                                    get_profile_id,
                                                    select_ds_for_volume,
                                                    download_error=False):
        host = mock.sentinel.host
        rp = mock.sentinel.rp
        folder = mock.sentinel.folder
        # NOTE(mriedem): The summary.name gets logged so it has to be a string
        summary = mock.Mock(name=str(mock.sentinel.ds_name))
        select_ds_for_volume.return_value = (host, rp, folder, summary)

        profile_id = mock.sentinel.profile_id
        get_profile_id.return_value = profile_id

        disk_type = mock.sentinel.disk_type
        get_disk_type.return_value = disk_type

        extra_config = mock.sentinel.extra_config
        get_extra_config.return_value = extra_config

        vm_create_spec = mock.sentinel.vm_create_spec
        vops.get_create_spec.return_value = vm_create_spec

        import_spec = mock.Mock()
        session.vim.client.factory.create.return_value = import_spec

        backing = mock.sentinel.backing
        if download_error:
            download_image.side_effect = exceptions.VimException
            vops.get_backing.return_value = backing
        else:
            download_image.return_value = backing

        context = mock.sentinel.context
        volume = self._create_volume_dict(size=3)
        image_service = mock.sentinel.image_service
        image_id = mock.sentinel.image_id
        image_size = 2 * units.Gi
        adapter_type = mock.sentinel.adapter_type

        if download_error:
            self.assertRaises(
                exceptions.VimException,
                self._driver._fetch_stream_optimized_image,
                context, volume, image_service, image_id,
                image_size, adapter_type)
        else:
            self._driver._fetch_stream_optimized_image(
                context, volume, image_service, image_id, image_size,
                adapter_type)

        select_ds_for_volume.assert_called_once_with(volume)
        vops.get_create_spec.assert_called_once_with(
            volume['name'], 0, disk_type, summary.name, profile_id=profile_id,
            adapter_type=adapter_type, extra_config=extra_config)
        self.assertEqual(vm_create_spec, import_spec.configSpec)
        download_image.assert_called_with(
            context,
            self._config.vmware_image_transfer_timeout_secs,
            image_service,
            image_id,
            session=session,
            host=self._config.vmware_host_ip,
            port=self._config.vmware_host_port,
            resource_pool=rp,
            vm_folder=folder,
            vm_import_spec=import_spec,
            image_size=image_size,
            http_method='POST')
        if download_error:
            self.assertFalse(vops.update_backing_disk_uuid.called)
            vops.delete_backing.assert_called_once_with(backing)
        else:
            vops.update_backing_disk_uuid.assert_called_once_with(
                backing, volume['id'])

    def test_copy_image_to_volume_stream_optimized(self):
        self._test_copy_image_to_volume_stream_optimized()

    def test_copy_image_to_volume_stream_optimized_with_download_error(self):
        self._test_copy_image_to_volume_stream_optimized(download_error=True)

    @mock.patch.object(VMDK_DRIVER, '_in_use', return_value=True)
    def test_copy_volume_to_image_when_attached(self, in_use):
        volume = self._create_volume_dict(
            status="uploading",
            attachment=[mock.sentinel.attachment_1])
        self.assertRaises(
            cinder_exceptions.InvalidVolume,
            self._driver.copy_volume_to_image,
            mock.sentinel.context,
            volume,
            mock.sentinel.image_service,
            mock.sentinel.image_meta)
        in_use.assert_called_once_with(volume)

    @mock.patch.object(VMDK_DRIVER, '_validate_disk_format')
    @mock.patch.object(VMDK_DRIVER, 'volumeops')
    @mock.patch.object(VMDK_DRIVER, '_create_backing')
    @mock.patch('oslo_vmware.image_transfer.upload_image')
    @mock.patch.object(VMDK_DRIVER, 'session')
    def _test_copy_volume_to_image(
            self, session, upload_image, create_backing, vops,
            validate_disk_format, backing_exists=True):
        backing = mock.sentinel.backing
        if backing_exists:
            vops.get_backing.return_value = backing
        else:
            vops.get_backing.return_value = None
            create_backing.return_value = backing

        vmdk_file_path = mock.sentinel.vmdk_file_path
        vops.get_vmdk_path.return_value = vmdk_file_path

        context = mock.sentinel.context
        volume = test_utils.create_volume(
            self._context, volume_type_id = fake_constants.VOLUME_TYPE_ID,
            updated_at = self.updated_at)
        extra_specs = {
            'image_service:store_id': 'fake-store'
        }
        test_utils.create_volume_type(self._context.elevated(),
                                      id=fake_constants.VOLUME_TYPE_ID,
                                      name="test_type",
                                      extra_specs=extra_specs)

        image_service = mock.sentinel.image_service
        image_meta = self._create_image_meta()
        self._driver.copy_volume_to_image(
            context, volume, image_service, image_meta)

        validate_disk_format.assert_called_once_with(image_meta['disk_format'])
        vops.get_backing.assert_called_once_with(volume['name'], volume['id'])
        if not backing_exists:
            create_backing.assert_called_once_with(volume)
        vops.get_vmdk_path.assert_called_once_with(backing)
        upload_image.assert_called_once_with(
            context,
            self._config.vmware_image_transfer_timeout_secs,
            image_service,
            image_meta['id'],
            volume['project_id'],
            session=session,
            host=self._config.vmware_host_ip,
            port=self._config.vmware_host_port,
            store_id='fake-store',
            base_image_ref=None,
            vm=backing,
            vmdk_file_path=vmdk_file_path,
            vmdk_size=volume['size'] * units.Gi,
            image_name=image_meta['name'],
            image_version=1)

    def test_copy_volume_to_image(self):
        self._test_copy_volume_to_image()

    def test_copy_volume_to_image_with_no_backing(self):
        self._test_copy_volume_to_image(backing_exists=False)

    def test_in_use(self):
        volume = self._create_volume_dict(
            attachment=[mock.sentinel.attachment_1])
        self.assertTrue(self._driver._in_use(volume))

    def test_in_use_with_available_volume(self):
        volume = self._create_volume_dict()
        self.assertIsNone(self._driver._in_use(volume))

    @mock.patch.object(VMDK_DRIVER, '_in_use', return_value=True)
    def test_retype_with_in_use_volume(self, in_use):
        context = mock.sentinel.context
        volume = self._create_volume_dict(
            status='retyping', attachment=[mock.sentinel.attachment_1])
        new_type = mock.sentinel.new_type
        diff = mock.sentinel.diff
        host = mock.sentinel.host
        self.assertFalse(self._driver.retype(context, volume, new_type, diff,
                                             host))
        in_use.assert_called_once_with(volume)

    @mock.patch.object(VMDK_DRIVER, '_in_use', return_value=False)
    @mock.patch.object(VMDK_DRIVER, 'volumeops')
    def test_retype_with_no_volume_backing(self, vops, in_use):
        vops.get_backing.return_value = None

        context = mock.sentinel.context
        volume = self._create_volume_dict(status='retyping')
        new_type = mock.sentinel.new_type
        diff = mock.sentinel.diff
        host = mock.sentinel.host
        self.assertTrue(self._driver.retype(context, volume, new_type, diff,
                                            host))

    @mock.patch.object(VMDK_DRIVER, '_in_use', return_value=False)
    @mock.patch.object(VMDK_DRIVER, 'volumeops')
    @mock.patch('cinder.volume.drivers.vmware.vmdk.VMwareVcVmdkDriver.'
                '_get_disk_type')
    @mock.patch('cinder.volume.drivers.vmware.vmdk.VMwareVcVmdkDriver.'
                '_get_extra_spec_disk_type')
    @mock.patch.object(VMDK_DRIVER, '_get_storage_profile')
    @mock.patch.object(VMDK_DRIVER, '_get_extra_spec_storage_profile')
    @mock.patch.object(VMDK_DRIVER, 'ds_sel')
    @mock.patch.object(VMDK_DRIVER, '_select_datastore')
    @mock.patch.object(
        VMDK_DRIVER, '_get_adapter_type', return_value='lsiLogic')
    @mock.patch.object(
        VMDK_DRIVER, '_get_extra_spec_adapter_type', return_value='lsiLogic')
    def test_retype_with_diff_profile_and_ds_compliance(
            self,
            _get_extra_spec_adapter_type,
            _get_adapter_type,
            select_datastore,
            ds_sel,
            get_extra_spec_storage_profile,
            get_storage_profile,
            get_extra_spec_disk_type,
            get_disk_type,
            vops,
            in_use):
        backing = mock.sentinel.backing
        vops.get_backing.return_value = backing

        datastore = vmware_fake.ManagedObjectReference(value='ds1')
        vops.get_datastore.return_value = datastore

        disk_type = mock.sentinel.disk_type
        get_disk_type.return_value = disk_type
        get_extra_spec_disk_type.return_value = disk_type

        self._driver._storage_policy_enabled = True
        profile = 'gold'
        get_storage_profile.return_value = profile
        new_profile = 'silver'
        get_extra_spec_storage_profile.return_value = new_profile

        ds_sel.is_datastore_compliant.return_value = True

        new_profile_id = mock.sentinel.new_profile_id
        ds_sel.get_profile_id.return_value = new_profile_id

        context = mock.sentinel.context
        volume = self._create_volume_dict(status='retyping')
        new_type = {'id': 'f04a65e0-d10c-4db7-b4a5-f933d57aa2b5'}
        diff = mock.sentinel.diff
        host = mock.sentinel.host
        self.assertTrue(self._driver.retype(context, volume, new_type, diff,
                                            host))
        ds_sel.is_datastore_compliant.assert_called_once_with(datastore,
                                                              new_profile)
        select_datastore.assert_not_called()
        vops.change_backing_profile.assert_called_once_with(backing,
                                                            new_profile_id)

    @mock.patch.object(VMDK_DRIVER, '_in_use', return_value=False)
    @mock.patch.object(VMDK_DRIVER, 'volumeops')
    @mock.patch('cinder.volume.drivers.vmware.vmdk.VMwareVcVmdkDriver.'
                '_get_disk_type')
    @mock.patch('cinder.volume.drivers.vmware.vmdk.VMwareVcVmdkDriver.'
                '_get_extra_spec_disk_type')
    @mock.patch.object(VMDK_DRIVER, '_get_storage_profile')
    @mock.patch.object(VMDK_DRIVER, '_get_extra_spec_storage_profile')
    @mock.patch.object(VMDK_DRIVER, 'ds_sel')
    @mock.patch.object(VMDK_DRIVER, '_select_datastore')
    def test_retype_with_diff_profile_and_ds_sel_no_candidate(
            self, select_datastore, ds_sel, get_extra_spec_storage_profile,
            get_storage_profile, get_extra_spec_disk_type, get_disk_type,
            vops, in_use):
        backing = mock.sentinel.backing
        vops.get_backing.return_value = backing

        datastore = vmware_fake.ManagedObjectReference(value='ds1')
        vops.get_datastore.return_value = datastore

        disk_type = mock.sentinel.disk_type
        get_disk_type.return_value = disk_type
        get_extra_spec_disk_type.return_value = disk_type

        vops.snapshot_exists.return_value = False

        self._driver._storage_policy_enabled = True
        profile = 'gold'
        get_storage_profile.return_value = profile
        new_profile = 'silver'
        get_extra_spec_storage_profile.return_value = new_profile

        ds_sel.is_datastore_compliant.return_value = False
        select_datastore.side_effect = (
            vmdk_exceptions.NoValidDatastoreException)

        context = mock.sentinel.context
        volume = self._create_volume_dict(status='retyping')
        new_type = {'id': 'f04a65e0-d10c-4db7-b4a5-f933d57aa2b5'}
        diff = mock.sentinel.diff
        host = mock.sentinel.host
        self.assertFalse(self._driver.retype(context, volume, new_type, diff,
                                             host))
        ds_sel.is_datastore_compliant.assert_called_once_with(datastore,
                                                              new_profile)
        select_datastore.assert_called_once_with(
            {hub.DatastoreSelector.SIZE_BYTES: volume['size'] * units.Gi,
             hub.DatastoreSelector.PROFILE_NAME: new_profile})

    @mock.patch.object(VMDK_DRIVER, '_in_use', return_value=False)
    @mock.patch.object(VMDK_DRIVER, 'volumeops')
    @mock.patch('cinder.volume.drivers.vmware.vmdk.VMwareVcVmdkDriver.'
                '_get_disk_type')
    @mock.patch('cinder.volume.drivers.vmware.vmdk.VMwareVcVmdkDriver.'
                '_get_extra_spec_disk_type')
    @mock.patch.object(VMDK_DRIVER, '_get_storage_profile')
    @mock.patch.object(VMDK_DRIVER, '_get_extra_spec_storage_profile')
    @mock.patch.object(VMDK_DRIVER, 'ds_sel')
    @mock.patch.object(VMDK_DRIVER, '_select_datastore')
    @mock.patch.object(VMDK_DRIVER, '_get_dc')
    @mock.patch.object(VMDK_DRIVER, '_get_volume_group_folder')
    @mock.patch.object(
        VMDK_DRIVER, '_get_adapter_type', return_value='lsiLogic')
    @mock.patch.object(
        VMDK_DRIVER, '_get_extra_spec_adapter_type', return_value='lsiLogic')
    def test_retype_with_diff_extra_spec_and_vol_snapshot(
            self,
            get_extra_spec_adapter_type,
            get_adapter_type,
            get_volume_group_folder,
            get_dc,
            select_datastore,
            ds_sel, get_extra_spec_storage_profile,
            get_storage_profile,
            get_extra_spec_disk_type,
            get_disk_type,
            vops,
            in_use):
        backing = mock.sentinel.backing
        vops.get_backing.return_value = backing

        datastore = vmware_fake.ManagedObjectReference(value='ds1')
        vops.get_datastore.return_value = datastore

        get_disk_type.return_value = 'thin'
        new_disk_type = 'thick'
        get_extra_spec_disk_type.return_value = new_disk_type

        vops.snapshot_exists.return_value = True

        self._driver._storage_policy_enabled = True
        profile = 'gold'
        get_storage_profile.return_value = profile
        new_profile = 'silver'
        get_extra_spec_storage_profile.return_value = new_profile

        ds_sel.is_datastore_compliant.return_value = False
        host = mock.sentinel.host
        rp = mock.sentinel.rp
        new_datastore = mock.Mock(value='ds2')
        summary = mock.Mock(datastore=new_datastore)
        select_datastore.return_value = (host, rp, summary)

        dc = mock.sentinel.dc
        get_dc.return_value = dc

        folder = mock.sentinel.folder
        get_volume_group_folder.return_value = folder

        new_profile_id = mock.sentinel.new_profile_id
        ds_sel.get_profile_id.return_value = new_profile_id

        context = mock.sentinel.context
        volume = self._create_volume_dict(status='retyping')
        new_type = {'id': 'f04a65e0-d10c-4db7-b4a5-f933d57aa2b5'}
        diff = mock.sentinel.diff
        host = mock.sentinel.host
        self.assertTrue(self._driver.retype(context, volume, new_type, diff,
                                            host))
        ds_sel.is_datastore_compliant.assert_called_once_with(datastore,
                                                              new_profile)
        select_datastore.assert_called_once_with(
            {hub.DatastoreSelector.SIZE_BYTES: volume['size'] * units.Gi,
             hub.DatastoreSelector.HARD_ANTI_AFFINITY_DS: ['ds1'],
             hub.DatastoreSelector.PROFILE_NAME: new_profile})
        get_dc.assert_called_once_with(rp)
        get_volume_group_folder.assert_called_once_with(dc,
                                                        volume['project_id'])
        vops.relocate_backing.assert_called_once_with(
            backing, new_datastore, rp, host, new_disk_type)
        vops.move_backing_to_folder.assert_called_once_with(backing, folder)
        vops.change_backing_profile.assert_called_once_with(backing,
                                                            new_profile_id)

    @mock.patch.object(VMDK_DRIVER, '_in_use', return_value=False)
    @mock.patch.object(VMDK_DRIVER, 'volumeops')
    @mock.patch('cinder.volume.drivers.vmware.vmdk.VMwareVcVmdkDriver.'
                '_get_disk_type')
    @mock.patch('cinder.volume.drivers.vmware.vmdk.VMwareVcVmdkDriver.'
                '_get_extra_spec_disk_type')
    @mock.patch.object(VMDK_DRIVER, '_get_storage_profile')
    @mock.patch.object(VMDK_DRIVER, '_get_extra_spec_storage_profile')
    @mock.patch.object(VMDK_DRIVER, 'ds_sel')
    @mock.patch.object(VMDK_DRIVER, '_select_datastore')
    @mock.patch.object(VMDK_DRIVER, '_get_dc')
    @mock.patch.object(VMDK_DRIVER, '_get_volume_group_folder')
    @mock.patch('oslo_utils.uuidutils.generate_uuid')
    @mock.patch.object(VMDK_DRIVER, '_delete_temp_backing')
    @mock.patch.object(VMDK_DRIVER, '_get_adapter_type')
    @mock.patch.object(VMDK_DRIVER, '_get_extra_spec_adapter_type')
    def _test_retype_with_diff_extra_spec_and_ds_compliance(
            self,
            get_extra_spec_adapter_type,
            get_adapter_type,
            delete_temp_backing,
            generate_uuid,
            get_volume_group_folder,
            get_dc,
            select_datastore,
            ds_sel,
            get_extra_spec_storage_profile,
            get_storage_profile,
            get_extra_spec_disk_type,
            get_disk_type,
            vops,
            in_use,
            clone_error=False):
        backing = mock.sentinel.backing
        vops.get_backing.return_value = backing

        datastore = vmware_fake.ManagedObjectReference(value='ds1')
        vops.get_datastore.return_value = datastore

        get_disk_type.return_value = 'thin'
        new_disk_type = 'thick'
        get_extra_spec_disk_type.return_value = new_disk_type

        vops.snapshot_exists.return_value = False

        self._driver._storage_policy_enabled = True
        profile = 'gold'
        get_storage_profile.return_value = profile
        new_profile = 'silver'
        get_extra_spec_storage_profile.return_value = new_profile

        ds_sel.is_datastore_compliant.return_value = True
        host = mock.sentinel.host
        rp = mock.sentinel.rp
        summary = mock.Mock(datastore=datastore)
        select_datastore.return_value = (host, rp, summary)

        dc = mock.sentinel.dc
        get_dc.return_value = dc

        folder = mock.sentinel.folder
        get_volume_group_folder.return_value = folder

        new_profile_id = mock.sentinel.new_profile_id
        ds_sel.get_profile_id.return_value = new_profile_id

        uuid = '025b654b-d4ed-47f9-8014-b71a7744eafc'
        generate_uuid.return_value = uuid

        if clone_error:
            vops.clone_backing.side_effect = exceptions.VimException
        else:
            new_backing = mock.sentinel.new_backing
            vops.clone_backing.return_value = new_backing

        adapter_type = 'lsiLogic'
        get_adapter_type.return_value = adapter_type
        new_adapter_type = 'paraVirtual'
        get_extra_spec_adapter_type.return_value = new_adapter_type

        capacity = self.VOL_SIZE * units.Mi
        filename = mock.sentinel.filename
        disk_backing = mock.Mock(filename=filename)
        disk_device = mock.Mock(capacityInKB=capacity, backing=disk_backing)
        vops._get_disk_device.return_value = disk_device

        context = mock.sentinel.context
        volume = self._create_volume_dict(status='retyping')
        new_type = {'id': 'f04a65e0-d10c-4db7-b4a5-f933d57aa2b5'}
        diff = mock.sentinel.diff
        host = mock.sentinel.host
        if clone_error:
            self.assertRaises(exceptions.VimException, self._driver.retype,
                              context, volume, new_type, diff, host)
        else:
            self.assertTrue(self._driver.retype(context, volume, new_type,
                                                diff, host))
        ds_sel.is_datastore_compliant.assert_called_once_with(datastore,
                                                              new_profile)
        select_datastore.assert_called_once_with(
            {hub.DatastoreSelector.SIZE_BYTES: volume['size'] * units.Gi,
             hub.DatastoreSelector.PROFILE_NAME: new_profile})
        get_dc.assert_called_once_with(rp)
        get_volume_group_folder.assert_called_once_with(dc,
                                                        volume['project_id'])
        vops.clone_backing.assert_called_once_with(
            volume['name'], backing, None, volumeops.FULL_CLONE_TYPE,
            datastore, disk_type=new_disk_type, host=host, resource_pool=rp,
            folder=folder)
        if clone_error:
            exp_rename_calls = [mock.call(backing, uuid),
                                mock.call(backing, volume['name'])]
            self.assertEqual(exp_rename_calls,
                             vops.rename_backing.call_args_list)
        else:
            vops.rename_backing.assert_called_once_with(backing, uuid)
            vops.update_backing_uuid.assert_called_once_with(
                new_backing, volume['id'])
            vops.update_backing_disk_uuid.assert_called_once_with(
                new_backing, volume['id'])
            delete_temp_backing.assert_called_once_with(backing)
            vops.detach_disk_from_backing.assert_called_once_with(
                new_backing, disk_device)
            vops.attach_disk_to_backing.assert_called_once_with(
                new_backing, disk_device.capacityInKB, new_disk_type,
                new_adapter_type, None, disk_device.backing.fileName)
            vops.change_backing_profile.assert_called_once_with(new_backing,
                                                                new_profile_id)

    def test_retype_with_diff_extra_spec_and_ds_compliance(self):
        self._test_retype_with_diff_extra_spec_and_ds_compliance()

    def test_retype_with_diff_extra_spec_ds_compliance_and_clone_error(self):
        self._test_retype_with_diff_extra_spec_and_ds_compliance(
            clone_error=True)

    @mock.patch.object(VMDK_DRIVER, 'volumeops')
    def test_extend_backing(self, vops):
        vmdk_path = mock.sentinel.vmdk_path
        vops.get_vmdk_path.return_value = vmdk_path
        dc = mock.sentinel.datacenter
        vops.get_dc.return_value = dc
        disk_type = mock.sentinel.disk_type
        eager_zero = (True if disk_type == "eagerZeroedThick" else False)

        backing = mock.sentinel.backing
        new_size = 1
        self._driver._extend_backing(backing, new_size, disk_type)

        vops.get_vmdk_path.assert_called_once_with(backing)
        vops.get_dc.assert_called_once_with(backing)
        vops.extend_virtual_disk.assert_called_once_with(new_size,
                                                         vmdk_path,
                                                         dc,
                                                         eager_zero)

    @mock.patch.object(VMDK_DRIVER, 'session')
    @mock.patch('oslo_vmware.vim_util.get_vc_version')
    def test_get_vc_version(self, get_vc_version, session):
        self._driver.configuration.vmware_host_version = None

        version_str = '6.0.0'
        get_vc_version.return_value = version_str

        version = self._driver._get_vc_version()

        self.assertEqual(version_str, version)
        get_vc_version.assert_called_once_with(session)

    @mock.patch('oslo_vmware.vim_util.get_vc_version')
    def test_get_vc_version_override(self, get_vc_version):
        version = self._driver._get_vc_version()

        self.assertEqual(
            self._driver.configuration.vmware_host_version,
            version)
        get_vc_version.assert_not_called()

    @mock.patch('cinder.volume.drivers.vmware.vmdk.LOG')
    @ddt.data('5.5', '6.0')
    def test_validate_vcenter_version(self, version, log):
        # vCenter versions 5.5 and above should pass validation.
        self._driver._validate_vcenter_version(version)
        # Deprecation warning should be logged for vCenter versions which are
        # incompatible with next minimum supported version.
        if not versionutils.is_compatible(
                self._driver.NEXT_MIN_SUPPORTED_VC_VERSION, version,
                same_major=False):
            log.warning.assert_called_once()
        else:
            log.warning.assert_not_called()

    def test_validate_vcenter_version_with_less_than_min_supported_version(
            self):
        # Validation should fail for vCenter version less than 5.1.
        self.assertRaises(exceptions.VMwareDriverException,
                          self._driver._validate_vcenter_version,
                          '5.1')

    @mock.patch('oslo_vmware.vim_util.find_extension')
    @mock.patch('oslo_vmware.vim_util.register_extension')
    @mock.patch.object(VMDK_DRIVER, 'session')
    def _test_register_extension(
            self, session, register_extension, find_extension,
            ext_exists=False):
        if not ext_exists:
            find_extension.return_value = None

        self._driver._register_extension()

        find_extension.assert_called_once_with(session.vim, vmdk.EXTENSION_KEY)
        if not ext_exists:
            register_extension.assert_called_once_with(
                session.vim, vmdk.EXTENSION_KEY, vmdk.EXTENSION_TYPE,
                label='OpenStack Cinder')

    def test_register_extension(self):
        self._test_register_extension()

    def test_register_extension_with_existing_extension(self):
        self._test_register_extension(ext_exists=True)

    @mock.patch('oslo_vmware.vim_util.find_extension', return_value=None)
    @mock.patch('oslo_vmware.vim_util.register_extension')
    @mock.patch.object(VMDK_DRIVER, 'session')
    def test_concurrent_register_extension(
            self, session, register_extension, find_extension):
        register_extension.side_effect = exceptions.VimFaultException(
            ['InvalidArgument'], 'error')
        self._driver._register_extension()

        find_extension.assert_called_once_with(session.vim, vmdk.EXTENSION_KEY)
        register_extension.assert_called_once_with(
            session.vim, vmdk.EXTENSION_KEY, vmdk.EXTENSION_TYPE,
            label='OpenStack Cinder')

    @mock.patch('oslo_vmware.vim_util.find_extension', return_value=None)
    @mock.patch('oslo_vmware.vim_util.register_extension')
    @mock.patch.object(VMDK_DRIVER, 'session')
    def test_register_extension_failure(
            self, session, register_extension, find_extension):
        register_extension.side_effect = exceptions.VimFaultException(
            ['RuntimeFault'], 'error')

        self.assertRaises(exceptions.VimFaultException,
                          self._driver._register_extension)
        find_extension.assert_called_once_with(session.vim, vmdk.EXTENSION_KEY)
        register_extension.assert_called_once_with(
            session.vim, vmdk.EXTENSION_KEY, vmdk.EXTENSION_TYPE,
            label='OpenStack Cinder')

    @mock.patch.object(VMDK_DRIVER, '_validate_params')
    @mock.patch('re.compile')
    @mock.patch.object(VMDK_DRIVER, '_create_session')
    @mock.patch.object(VMDK_DRIVER, '_get_vc_version')
    @mock.patch.object(VMDK_DRIVER, '_validate_vcenter_version')
    @mock.patch('oslo_vmware.pbm.get_pbm_wsdl_location')
    @mock.patch.object(VMDK_DRIVER, '_register_extension')
    @mock.patch('cinder.volume.drivers.vmware.volumeops.VMwareVolumeOps')
    @mock.patch('cinder.volume.drivers.vmware.datastore.DatastoreSelector')
    @mock.patch.object(VMDK_DRIVER, 'volumeops')
    @mock.patch.object(VMDK_DRIVER, 'session')
    def _test_do_setup(
            self, session, vops, ds_sel_cls, vops_cls, register_extension,
            get_pbm_wsdl_loc, validate_vc_version, get_vc_version,
            create_session, re_compile, validate_params, enable_pbm=True,
            ds_regex_pat=None, invalid_regex=False):
        mock_session = mock.Mock()
        create_session.return_value = mock_session

        if enable_pbm:
            ver_str = '5.5'
            pbm_wsdl = mock.sentinel.pbm_wsdl
            get_pbm_wsdl_loc.return_value = pbm_wsdl
        else:
            ver_str = '5.1'
        get_vc_version.return_value = ver_str

        cls_1 = mock.sentinel.cls_1
        cls_2 = mock.sentinel.cls_2
        cluster_refs = {'cls-1': cls_1, 'cls-2': cls_2}
        vops.get_cluster_refs.return_value = cluster_refs

        self._driver.configuration.vmware_datastore_regex = ds_regex_pat
        ds_regex = None
        if ds_regex_pat:
            if invalid_regex:
                re_compile.side_effect = re.error("error")
            else:
                ds_regex = mock.sentinel.ds_regex
                re_compile.return_value = ds_regex

        if ds_regex_pat and invalid_regex:
            self.assertRaises(cinder_exceptions.InvalidInput,
                              self._driver.do_setup,
                              mock.ANY)
            validate_params.assert_called_once_with()
        else:
            self._driver.do_setup(mock.ANY)

            validate_params.assert_called_once_with()
            create_session.assert_called_once_with()
            get_vc_version.assert_called_once_with()
            validate_vc_version.assert_called_once_with(ver_str)
            if enable_pbm:
                get_pbm_wsdl_loc.assert_called_once_with(ver_str)
                mock_session.pbm_wsdl_loc_set.assert_called_once_with(pbm_wsdl)
            self.assertEqual(enable_pbm, self._driver._storage_policy_enabled)
            register_extension.assert_called_once()
            vops_cls.assert_called_once_with(
                session,
                self._driver.configuration.vmware_max_objects_retrieval,
                vmdk.EXTENSION_KEY,
                vmdk.EXTENSION_TYPE)
            self.assertEqual(vops_cls.return_value, self._driver._volumeops)
            ds_sel_cls.assert_called_once_with(
                vops,
                session,
                self._driver.configuration.vmware_max_objects_retrieval,
                ds_regex=ds_regex)
            self.assertEqual(ds_sel_cls.return_value, self._driver._ds_sel)
            vops.get_cluster_refs.assert_called_once_with(
                self._driver.configuration.vmware_cluster_name)
            vops.build_backing_ref_cache.assert_called_once_with()
            self.assertEqual(list(cluster_refs.values()),
                             list(self._driver._clusters))

        if ds_regex_pat:
            re_compile.assert_called_once_with(ds_regex_pat)

    def test_do_setup(self):
        self._test_do_setup()

    def test_do_setup_with_pbm_disabled(self):
        self._test_do_setup(enable_pbm=False)

    @mock.patch.object(VMDK_DRIVER, '_validate_params')
    @mock.patch.object(VMDK_DRIVER, '_create_session')
    @mock.patch.object(VMDK_DRIVER, '_get_vc_version')
    @mock.patch.object(VMDK_DRIVER, '_validate_vcenter_version')
    @mock.patch('oslo_vmware.pbm.get_pbm_wsdl_location')
    def test_do_setup_with_invalid_pbm_wsdl(
            self, get_pbm_wsdl_loc, validate_vc_version, get_vc_version,
            create_session, validate_params):
        ver_str = '5.5'
        get_vc_version.return_value = ver_str

        get_pbm_wsdl_loc.return_value = None

        self.assertRaises(exceptions.VMwareDriverException,
                          self._driver.do_setup,
                          mock.ANY)

        validate_params.assert_called_once_with()
        create_session.assert_called_once_with()
        get_vc_version.assert_called_once_with()
        validate_vc_version.assert_called_once_with(ver_str)
        get_pbm_wsdl_loc.assert_called_once_with(ver_str)

    def test_do_setup_with_ds_regex(self):
        self._test_do_setup(ds_regex_pat='foo')

    def test_do_setup_with_invalid_ds_regex(self):
        self._test_do_setup(ds_regex_pat='(foo', invalid_regex=True)

    @mock.patch.object(VMDK_DRIVER, 'volumeops')
    def test_get_dc(self, vops):
        dc_1 = mock.sentinel.dc_1
        dc_2 = mock.sentinel.dc_2
        vops.get_dc.side_effect = [dc_1, dc_2]

        # cache miss
        rp_1 = vmware_fake.ManagedObjectReference(value='rp-1')
        rp_2 = vmware_fake.ManagedObjectReference(value='rp-2')
        self.assertEqual(dc_1, self._driver._get_dc(rp_1))
        self.assertEqual(dc_2, self._driver._get_dc(rp_2))
        self.assertDictEqual({'rp-1': dc_1, 'rp-2': dc_2},
                             self._driver._dc_cache)

        # cache hit
        self.assertEqual(dc_1, self._driver._get_dc(rp_1))
        self.assertEqual(dc_2, self._driver._get_dc(rp_2))

        vops.get_dc.assert_has_calls([mock.call(rp_1), mock.call(rp_2)])

    @mock.patch.object(VMDK_DRIVER, '_get_storage_profile')
    @mock.patch.object(VMDK_DRIVER, '_select_datastore')
    @mock.patch.object(VMDK_DRIVER, '_get_dc')
    @mock.patch.object(VMDK_DRIVER, 'volumeops')
    @mock.patch.object(VMDK_DRIVER, '_get_volume_group_folder')
    @ddt.data(None, {vmdk.CREATE_PARAM_DISK_SIZE: 2 * VOL_SIZE})
    def test_select_ds_for_volume(
            self, create_params, get_volume_group_folder, vops, get_dc,
            select_datastore, get_storage_profile):

        profile = mock.sentinel.profile
        get_storage_profile.return_value = profile

        host = mock.sentinel.host
        rp = mock.sentinel.rp
        summary = mock.sentinel.summary
        select_datastore.return_value = (host, rp, summary)

        dc = mock.sentinel.dc
        get_dc.return_value = dc

        folder = mock.sentinel.folder
        get_volume_group_folder.return_value = folder

        vol = self._create_volume_dict()
        ret = self._driver._select_ds_for_volume(
            vol, host=host, create_params=create_params)

        self.assertEqual((host, rp, folder, summary), ret)
        if create_params:
            exp_size = create_params[vmdk.CREATE_PARAM_DISK_SIZE] * units.Gi
        else:
            exp_size = vol['size'] * units.Gi
        exp_req = {hub.DatastoreSelector.SIZE_BYTES: exp_size,
                   hub.DatastoreSelector.PROFILE_NAME: profile}
        select_datastore.assert_called_once_with(exp_req, host)
        get_dc.assert_called_once_with(rp)
        get_volume_group_folder.assert_called_once_with(dc, vol['project_id'])

    @mock.patch.object(VMDK_DRIVER, 'volumeops')
    @mock.patch.object(VMDK_DRIVER, '_get_storage_profile_id')
    def _test_get_connection_info(
            self, get_storage_profile_id, vops, vmdk_connector=False):
        volume = self._create_volume_obj()
        backing = vmware_fake.ManagedObjectReference(value='ref-1')

        profile_id = mock.sentinel.profile_id
        get_storage_profile_id.return_value = profile_id

        if vmdk_connector:
            vmdk_path = mock.sentinel.vmdk_path
            vops.get_vmdk_path.return_value = vmdk_path

            datastore = vmware_fake.ManagedObjectReference(value='ds-1')
            vops.get_datastore.return_value = datastore

            datacenter = vmware_fake.ManagedObjectReference(value='dc-1')
            vops.get_dc.return_value = datacenter

            connector = {'platform': mock.sentinel.platform,
                         'os_type': mock.sentinel.os_type}
        else:
            connector = {'instance': 'vm-1'}
        ret = self._driver._get_connection_info(volume, backing, connector)

        self.assertEqual('vmdk', ret['driver_volume_type'])
        self.assertEqual('ref-1', ret['data']['volume'])
        self.assertEqual(volume.id, ret['data']['volume_id'])
        self.assertEqual(volume.name, ret['data']['name'])
        self.assertEqual(profile_id, ret['data']['profile_id'])

        if vmdk_connector:
            self.assertEqual(volume.size * units.Gi, ret['data']['vmdk_size'])
            self.assertEqual(vmdk_path, ret['data']['vmdk_path'])
            self.assertEqual('ds-1', ret['data']['datastore'])
            self.assertEqual('dc-1', ret['data']['datacenter'])

            config = self._driver.configuration
            exp_config = {
                'vmware_host_ip': config.vmware_host_ip,
                'vmware_host_port': config.vmware_host_port,
                'vmware_host_username': config.vmware_host_username,
                'vmware_host_password': config.vmware_host_password,
                'vmware_api_retry_count': config.vmware_api_retry_count,
                'vmware_task_poll_interval': config.vmware_task_poll_interval,
                'vmware_ca_file': config.vmware_ca_file,
                'vmware_insecure': config.vmware_insecure,
                'vmware_tmp_dir': config.vmware_tmp_dir,
                'vmware_image_transfer_timeout_secs':
                    config.vmware_image_transfer_timeout_secs,
            }
            self.assertEqual(exp_config, ret['data']['config'])

    def test_get_connection_info(self):
        self._test_get_connection_info()

    def test_get_connection_info_vmdk_connector(self):
        self._test_get_connection_info(vmdk_connector=True)

    @mock.patch.object(VMDK_DRIVER, 'volumeops')
    @mock.patch('oslo_vmware.vim_util.get_moref')
    @mock.patch.object(VMDK_DRIVER, '_create_backing')
    @mock.patch.object(VMDK_DRIVER, '_relocate_backing')
    @mock.patch.object(VMDK_DRIVER, '_get_connection_info')
    def _test_initialize_connection(
            self, get_connection_info, relocate_backing, create_backing,
            get_moref, vops, backing_exists=True, instance_exists=True):

        backing_val = mock.sentinel.backing_val
        backing = vmware_fake.ManagedObjectReference(value=backing_val)
        if backing_exists:
            vops.get_backing.return_value = backing
        else:
            vops.get_backing.return_value = None
            create_backing.return_value = backing

        if instance_exists:
            instance_val = mock.sentinel.instance_val
            connector = {'instance': instance_val}

            instance_moref = mock.sentinel.instance_moref
            get_moref.return_value = instance_moref

            host = mock.sentinel.host
            vops.get_host.return_value = host
        else:
            connector = {}

        conn_info = mock.sentinel.conn_info
        get_connection_info.return_value = conn_info

        volume = self._create_volume_obj()
        ret = self._driver.initialize_connection(volume, connector)

        self.assertEqual(conn_info, ret)
        if instance_exists:
            vops.get_host.assert_called_once_with(instance_moref)
            if backing_exists:
                relocate_backing.assert_called_once_with(volume, backing, host)
                create_backing.assert_not_called()
            else:
                create_backing.assert_called_once_with(volume, host)
                relocate_backing.assert_not_called()
        elif not backing_exists:
            create_backing.assert_called_once_with(volume)
            relocate_backing.assert_not_called()
        else:
            create_backing.assert_not_called()
            relocate_backing.assert_not_called()
        get_connection_info.assert_called_once_with(volume, backing, connector)

    def test_initialize_connection_with_instance_and_backing(self):
        self._test_initialize_connection()

    def test_initialize_connection_with_instance_and_no_backing(self):
        self._test_initialize_connection(backing_exists=False)

    def test_initialize_connection_with_no_instance_and_no_backing(self):
        self._test_initialize_connection(
            backing_exists=False, instance_exists=False)

    def test_initialize_connection_with_no_instance_and_backing(self):
        self._test_initialize_connection(instance_exists=False)

    @mock.patch.object(VMDK_DRIVER, 'volumeops')
    def _test_get_volume_group_folder(self, vops, snapshot=False):
        folder = mock.sentinel.folder
        vops.create_vm_inventory_folder.return_value = folder

        datacenter = mock.sentinel.dc
        project_id = '63c19a12292549818c09946a5e59ddaf'
        self.assertEqual(folder,
                         self._driver._get_volume_group_folder(
                             datacenter, project_id, snapshot=snapshot))
        project_folder_name = 'Project (%s)' % project_id
        exp_folder_names = ['OpenStack',
                            project_folder_name,
                            self.VOLUME_FOLDER]
        if snapshot:
            exp_folder_names.append('Snapshots')
        vops.create_vm_inventory_folder.assert_called_once_with(
            datacenter, exp_folder_names)

    def test_get_volume_group_folder(self):
        self._test_get_volume_group_folder()

    def test_get_volume_group_folder_for_snapshot(self):
        self._test_get_volume_group_folder(snapshot=True)

    @mock.patch('cinder.volume.drivers.vmware.vmdk.'
                '_get_volume_type_extra_spec')
    @ddt.data('full', 'linked')
    def test_get_clone_type(self, clone_type, get_volume_type_extra_spec):
        get_volume_type_extra_spec.return_value = clone_type

        volume = self._create_volume_dict()
        self.assertEqual(clone_type, self._driver._get_clone_type(volume))
        get_volume_type_extra_spec.assert_called_once_with(
            volume['volume_type_id'], 'clone_type',
            default_value=volumeops.FULL_CLONE_TYPE)

    @mock.patch('cinder.volume.drivers.vmware.vmdk.'
                '_get_volume_type_extra_spec')
    def test_get_clone_type_invalid(
            self, get_volume_type_extra_spec):
        get_volume_type_extra_spec.return_value = 'foo'

        volume = self._create_volume_dict()
        self.assertRaises(
            cinder_exceptions.Invalid, self._driver._get_clone_type, volume)
        get_volume_type_extra_spec.assert_called_once_with(
            volume['volume_type_id'], 'clone_type',
            default_value=volumeops.FULL_CLONE_TYPE)

    @mock.patch.object(VMDK_DRIVER, '_get_disk_type')
    @mock.patch.object(VMDK_DRIVER, 'volumeops')
    @mock.patch.object(VMDK_DRIVER, '_select_ds_for_volume')
    @mock.patch.object(VMDK_DRIVER, '_extend_backing')
    def _test_clone_backing(
            self, extend_backing, select_ds_for_volume, vops, get_disk_type,
            clone_type=volumeops.FULL_CLONE_TYPE, extend_needed=False,
            vc60=False):
        host = mock.sentinel.host
        rp = mock.sentinel.rp
        folder = mock.sentinel.folder
        datastore = mock.sentinel.datastore
        summary = mock.Mock(datastore=datastore)
        select_ds_for_volume.return_value = (host, rp, folder, summary)

        clone = mock.sentinel.clone
        vops.clone_backing.return_value = clone

        disk_type = mock.sentinel.disk_type
        get_disk_type.return_value = disk_type

        if vc60:
            self._driver._vc_version = '6.0'
        else:
            self._driver._vc_version = '5.5'

        src_vsize = 1
        if extend_needed:
            size = 2
        else:
            size = 1
        volume = self._create_volume_obj(size=size)
        backing = mock.sentinel.backing
        snapshot = mock.sentinel.snapshot
        self._driver._clone_backing(
            volume, backing, snapshot, clone_type, src_vsize)

        extra_config = {vmdk.EXTRA_CONFIG_VOLUME_ID_KEY: volume['id'],
                        volumeops.BACKING_UUID_KEY: volume['id']}
        if volume.size > src_vsize or clone_type == volumeops.FULL_CLONE_TYPE:
            vops.clone_backing.assert_called_once_with(
                volume.name,
                backing,
                snapshot,
                volumeops.FULL_CLONE_TYPE,
                datastore,
                host=host,
                resource_pool=rp,
                extra_config=extra_config,
                folder=folder)
            vops.update_backing_disk_uuid.assert_called_once_with(clone,
                                                                  volume.id)
        else:
            vops.clone_backing.assert_called_once_with(
                volume.name,
                backing,
                snapshot,
                volumeops.LINKED_CLONE_TYPE,
                None,
                host=None,
                resource_pool=None,
                extra_config=extra_config,
                folder=None)
            if not vc60:
                vops.update_backing_disk_uuid.assert_called_once_with(
                    clone, volume.id)
            else:
                vops.update_backing_disk_uuid.assert_not_called()

        if volume.size > src_vsize:
            extend_backing.assert_called_once_with(clone, volume.size,
                                                   disk_type)
        else:
            extend_backing.assert_not_called()

    @ddt.data(volumeops.FULL_CLONE_TYPE, volumeops.LINKED_CLONE_TYPE)
    def test_clone_backing(self, clone_type):
        self._test_clone_backing(clone_type=clone_type)

    @ddt.data(volumeops.FULL_CLONE_TYPE, volumeops.LINKED_CLONE_TYPE)
    def test_clone_backing_with_extend(self, clone_type):
        self._test_clone_backing(clone_type=clone_type, extend_needed=True)

    def test_clone_backing_linked_vc_60(self):
        self._test_clone_backing(
            clone_type=volumeops.LINKED_CLONE_TYPE, vc60=True)

    @mock.patch.object(VMDK_DRIVER, '_get_template_by_inv_path')
    @mock.patch('oslo_utils.uuidutils.generate_uuid')
    @mock.patch.object(VMDK_DRIVER, '_select_ds_for_volume')
    @mock.patch.object(VMDK_DRIVER, '_get_disk_type')
    @mock.patch.object(VMDK_DRIVER, 'volumeops')
    @mock.patch.object(VMDK_DRIVER, '_create_volume_from_temp_backing')
    def test_create_volume_from_template(
            self, create_volume_from_temp_backing, vops, get_disk_type,
            select_ds_for_volume, generate_uuid, get_template_by_inv_path):
        template = mock.sentinel.template
        get_template_by_inv_path.return_value = template

        tmp_name = 'de4c648c-8403-4dcc-b14a-d2541b7cba2b'
        generate_uuid.return_value = tmp_name

        host = mock.sentinel.host
        rp = mock.sentinel.rp
        folder = mock.sentinel.folder
        datastore = mock.sentinel.datastore
        summary = mock.Mock(datastore=datastore)
        select_ds_for_volume.return_value = (host, rp, folder, summary)

        disk_type = mock.sentinel.disk_type
        get_disk_type.return_value = disk_type

        tmp_backing = mock.sentinel.tmp_backing
        vops.clone_backing.return_value = tmp_backing

        volume = self._create_volume_obj()
        inv_path = mock.sentinel.inv_path
        self._driver._create_volume_from_template(volume, inv_path)

        get_template_by_inv_path.assert_called_once_with(inv_path)
        select_ds_for_volume.assert_called_once_with(volume)
        get_disk_type.assert_called_once_with(volume)
        vops.clone_backing.assert_called_once_with(tmp_name,
                                                   template,
                                                   None,
                                                   volumeops.FULL_CLONE_TYPE,
                                                   datastore,
                                                   disk_type=disk_type,
                                                   host=host,
                                                   resource_pool=rp,
                                                   folder=folder)
        create_volume_from_temp_backing.assert_called_once_with(volume,
                                                                tmp_backing)

    @mock.patch.object(VMDK_DRIVER, 'volumeops')
    @mock.patch.object(VMDK_DRIVER, '_clone_backing')
    def test_create_volume_from_snapshot_without_backing(self, clone_backing,
                                                         vops):
        vops.get_backing.return_value = None

        volume = self._create_volume_dict()
        src_vref = self._create_volume_dict(vol_id=self.SRC_VOL_ID)
        snapshot = self._create_snapshot_dict(src_vref)
        self._driver.create_volume_from_snapshot(volume, snapshot)

        vops.get_backing.assert_called_once_with(snapshot['volume_name'],
                                                 snapshot['volume']['id'])
        clone_backing.assert_not_called()

    @mock.patch.object(VMDK_DRIVER, 'volumeops')
    @mock.patch.object(VMDK_DRIVER, '_clone_backing')
    def test_create_volume_from_snapshot_without_backing_snapshot(
            self, clone_backing, vops):
        backing = mock.sentinel.backing
        vops.get_backing.return_value = backing

        vops.get_snapshot.return_value = None

        volume = self._create_volume_dict()
        src_vref = self._create_volume_dict(vol_id=self.SRC_VOL_ID)
        snapshot = self._create_snapshot_dict(src_vref)
        self._driver.create_volume_from_snapshot(volume, snapshot)

        vops.get_backing.assert_called_once_with(snapshot['volume_name'],
                                                 snapshot['volume']['id'])
        vops.get_snapshot.assert_called_once_with(backing, snapshot['name'])
        clone_backing.assert_not_called()

    @mock.patch.object(VMDK_DRIVER, 'volumeops')
    @mock.patch.object(VMDK_DRIVER, '_get_clone_type')
    @mock.patch.object(VMDK_DRIVER, '_create_volume_from_template')
    @mock.patch.object(VMDK_DRIVER, '_clone_backing')
    def _test_create_volume_from_snapshot(
            self, clone_backing, create_volume_from_template, get_clone_type,
            vops, template=False):
        backing = mock.sentinel.backing
        vops.get_backing.return_value = backing

        snapshot_moref = mock.sentinel.snap_moref
        vops.get_snapshot.return_value = snapshot_moref

        get_clone_type.return_value = volumeops.FULL_CLONE_TYPE

        volume = self._create_volume_dict()
        src_vref = self._create_volume_dict(vol_id=self.SRC_VOL_ID)
        if template:
            provider_location = mock.sentinel.inv_path
        else:
            provider_location = None
        snapshot = self._create_snapshot_dict(
            src_vref, provider_location=provider_location)
        self._driver.create_volume_from_snapshot(volume, snapshot)

        vops.get_backing.assert_called_once_with(snapshot['volume_name'],
                                                 snapshot['volume']['id'])
        if template:
            create_volume_from_template.assert_called_once_with(
                volume, mock.sentinel.inv_path)
        else:
            vops.get_snapshot.assert_called_once_with(backing,
                                                      snapshot['name'])
            get_clone_type.assert_called_once_with(volume)
            clone_backing.assert_called_once_with(
                volume, backing, snapshot_moref, volumeops.FULL_CLONE_TYPE,
                snapshot['volume_size'])

    def test_create_volume_from_snapshot(self):
        self._test_create_volume_from_snapshot()

    def test_create_volume_from_snapshot_template(self):
        self._test_create_volume_from_snapshot(template=True)

    @mock.patch.object(VMDK_DRIVER, 'session')
    def test_get_volume_device_uuid(self, session):
        dev_uuid = mock.sentinel.dev_uuid
        opt_val = mock.Mock(value=dev_uuid)
        session.invoke_api.return_value = opt_val

        instance = mock.sentinel.instance
        ret = self._driver._get_volume_device_uuid(instance, self.VOL_ID)

        self.assertEqual(dev_uuid, ret)
        exp_prop = 'config.extraConfig["volume-%s"]' % self.VOL_ID
        session.invoke_api.assert_called_once_with(
            vim_util, 'get_object_property', session.vim, instance, exp_prop)

    @mock.patch.object(VMDK_DRIVER, 'volumeops')
    @mock.patch.object(VMDK_DRIVER, '_get_volume_device_uuid')
    @mock.patch('oslo_utils.uuidutils.generate_uuid')
    def test_create_temp_backing_from_attached_vmdk(
            self, generate_uuid, get_volume_device_uuid, vops):
        instance = mock.sentinel.instance
        vops.get_backing_by_uuid.return_value = instance

        vol_dev_uuid = mock.sentinel.vol_dev_uuid
        get_volume_device_uuid.return_value = vol_dev_uuid

        tmp_name = mock.sentinel.tmp_name
        generate_uuid.return_value = tmp_name

        tmp_backing = mock.sentinel.tmp_backing
        vops.clone_backing.return_value = tmp_backing

        instance_uuid = fake_constants.INSTANCE_ID
        attachment = fake_volume.fake_db_volume_attachment(
            instance_uuid=instance_uuid)
        src_vref = self._create_volume_dict(vol_id=fake_constants.VOLUME_ID,
                                            attachment=[attachment])
        host = mock.sentinel.host
        rp = mock.sentinel.rp
        folder = mock.sentinel.folder
        datastore = mock.sentinel.datastore
        ret = self._driver._create_temp_backing_from_attached_vmdk(
            src_vref, host, rp, folder, datastore)

        self.assertEqual(tmp_backing, ret)
        vops.get_backing_by_uuid.assert_called_once_with(instance_uuid)
        get_volume_device_uuid.assert_called_once_with(instance,
                                                       src_vref['id'])
        vops.clone_backing.assert_called_once_with(
            tmp_name, instance, None, volumeops.FULL_CLONE_TYPE, datastore,
            host=host, resource_pool=rp, folder=folder,
            disks_to_clone=[vol_dev_uuid])

    @mock.patch.object(VMDK_DRIVER, '_get_disk_type')
    @mock.patch.object(VMDK_DRIVER, 'volumeops')
    @mock.patch.object(VMDK_DRIVER, '_extend_backing')
    def _test_extend_backing_if_needed(
            self, extend_backing, vops, get_disk_type, extend=True):
        disk_type = mock.sentinel.disk_type
        get_disk_type.return_value = disk_type
        if extend:
            vol_size = 2
        else:
            vol_size = 1

        vops.get_disk_size.return_value = units.Gi

        volume = self._create_volume_obj(size=vol_size)
        backing = mock.sentinel.backing
        self._driver._extend_if_needed(volume, backing)

        vops.get_disk_size.assert_called_once_with(backing)
        if extend:
            extend_backing.assert_called_once_with(backing, vol_size,
                                                   disk_type)
        else:
            extend_backing.assert_not_called()

    def test_extend_backing_if_needed(self):
        self._test_extend_backing_if_needed()

    def test_extend_backing_if_needed_no_extend(self):
        self._test_extend_backing_if_needed(extend=False)

    @mock.patch.object(VMDK_DRIVER, 'volumeops')
    @mock.patch.object(VMDK_DRIVER, '_manage_existing_int')
    @mock.patch.object(VMDK_DRIVER, '_extend_if_needed')
    @mock.patch.object(VMDK_DRIVER, '_delete_temp_backing')
    def test_create_volume_from_temp_backing(
            self, delete_temp_backing, extend_if_needed, manage_existing_int,
            vops):
        disk_device = mock.sentinel.disk_device
        vops._get_disk_device.return_value = disk_device

        backing = mock.sentinel.backing
        manage_existing_int.return_value = backing

        volume = self._create_volume_dict()
        tmp_backing = mock.sentinel.tmp_backing
        self._driver._create_volume_from_temp_backing(volume, tmp_backing)

        vops._get_disk_device.assert_called_once_with(tmp_backing)
        manage_existing_int.assert_called_once_with(
            volume, tmp_backing, disk_device)
        extend_if_needed.assert_called_once_with(volume, backing)
        delete_temp_backing.assert_called_once_with(tmp_backing)

    @mock.patch.object(VMDK_DRIVER, '_select_ds_for_volume')
    @mock.patch.object(VMDK_DRIVER, '_create_temp_backing_from_attached_vmdk')
    @mock.patch.object(VMDK_DRIVER, '_create_volume_from_temp_backing')
    def test_clone_attached_volume(
            self, create_volume_from_temp_backing,
            create_temp_backing_from_attached_vmdk, select_ds_for_volume):
        host = mock.sentinel.host
        rp = mock.sentinel.rp
        folder = mock.sentinel.folder
        datastore = mock.sentinel.datastore
        summary = mock.Mock(datastore=datastore)
        select_ds_for_volume.return_value = (host, rp, folder, summary)

        tmp_backing = mock.sentinel.tmp_backing
        create_temp_backing_from_attached_vmdk.return_value = tmp_backing

        src_vref = mock.sentinel.src_vref
        volume = mock.sentinel.volume
        self._driver._clone_attached_volume(src_vref, volume)

        select_ds_for_volume.assert_called_once_with(volume)
        create_temp_backing_from_attached_vmdk.assert_called_once_with(
            src_vref, host, rp, folder, datastore)
        create_volume_from_temp_backing.assert_called_once_with(
            volume, tmp_backing)

    @mock.patch.object(VMDK_DRIVER, 'volumeops')
    @mock.patch.object(VMDK_DRIVER, '_clone_backing')
    def test_create_cloned_volume_without_backing(self, clone_backing, vops):
        vops.get_backing.return_value = None

        volume = self._create_volume_dict()
        src_vref = self._create_volume_dict(vol_id=self.SRC_VOL_ID)
        self._driver.create_cloned_volume(volume, src_vref)

        vops.get_backing.assert_called_once_with(src_vref['name'],
                                                 src_vref['id'])
        clone_backing.assert_not_called()

    @mock.patch.object(VMDK_DRIVER, 'volumeops')
    @mock.patch.object(VMDK_DRIVER, '_get_clone_type')
    @mock.patch.object(VMDK_DRIVER, '_in_use', return_value=False)
    @mock.patch.object(VMDK_DRIVER, '_clone_backing')
    def test_create_cloned_volume(
            self, clone_backing, in_use, get_clone_type, vops):
        backing = mock.sentinel.backing
        vops.get_backing.return_value = backing

        get_clone_type.return_value = volumeops.FULL_CLONE_TYPE

        volume = self._create_volume_dict()
        src_vref = self._create_volume_dict(vol_id=self.SRC_VOL_ID)
        self._driver.create_cloned_volume(volume, src_vref)

        vops.get_backing.assert_called_once_with(src_vref['name'],
                                                 src_vref['id'])
        get_clone_type.assert_called_once_with(volume)
        clone_backing.assert_called_once_with(
            volume, backing, None, volumeops.FULL_CLONE_TYPE, src_vref['size'])

    @mock.patch.object(VMDK_DRIVER, 'volumeops')
    @mock.patch.object(VMDK_DRIVER, '_get_clone_type')
    @mock.patch.object(VMDK_DRIVER, '_in_use', return_value=False)
    @mock.patch.object(VMDK_DRIVER, '_clone_backing')
    def test_create_cloned_volume_linked(
            self, clone_backing, in_use, get_clone_type, vops):
        backing = mock.sentinel.backing
        vops.get_backing.return_value = backing

        get_clone_type.return_value = volumeops.LINKED_CLONE_TYPE

        temp_snapshot = mock.sentinel.temp_snapshot
        vops.create_snapshot.return_value = temp_snapshot

        volume = self._create_volume_dict()
        src_vref = self._create_volume_dict(vol_id=self.SRC_VOL_ID)
        self._driver.create_cloned_volume(volume, src_vref)

        vops.get_backing.assert_called_once_with(src_vref['name'],
                                                 src_vref['id'])
        get_clone_type.assert_called_once_with(volume)
        temp_snap_name = 'temp-snapshot-%s' % volume['id']
        vops.create_snapshot.assert_called_once_with(
            backing, temp_snap_name, None)
        clone_backing.assert_called_once_with(
            volume, backing, temp_snapshot, volumeops.LINKED_CLONE_TYPE,
            src_vref['size'])
        vops.delete_snapshot.assert_called_once_with(backing, temp_snap_name)

    @mock.patch.object(VMDK_DRIVER, 'volumeops')
    @mock.patch.object(VMDK_DRIVER, '_get_clone_type')
    @mock.patch.object(VMDK_DRIVER, '_clone_backing')
    def test_create_cloned_volume_linked_when_attached(
            self, clone_backing, get_clone_type, vops):
        backing = mock.sentinel.backing
        vops.get_backing.return_value = backing

        get_clone_type.return_value = volumeops.LINKED_CLONE_TYPE

        volume = self._create_volume_dict()
        src_vref = self._create_volume_dict(vol_id=self.SRC_VOL_ID,
                                            status='in-use')
        self.assertRaises(cinder_exceptions.InvalidVolume,
                          self._driver.create_cloned_volume,
                          volume,
                          src_vref)
        vops.get_backing.assert_called_once_with(src_vref['name'],
                                                 src_vref['id'])
        get_clone_type.assert_called_once_with(volume)

    @mock.patch.object(VMDK_DRIVER, 'volumeops')
    @mock.patch.object(VMDK_DRIVER, '_get_clone_type')
    @mock.patch.object(VMDK_DRIVER, '_in_use', return_value=True)
    @mock.patch.object(VMDK_DRIVER, '_clone_attached_volume')
    def test_create_cloned_volume_when_attached(
            self, clone_attached_volume, in_use, get_clone_type, vops):
        backing = mock.sentinel.backing
        vops.get_backing.return_value = backing

        get_clone_type.return_value = volumeops.FULL_CLONE_TYPE

        volume = self._create_volume_dict(status='in-use')
        src_vref = self._create_volume_dict(vol_id=self.SRC_VOL_ID)
        self._driver.create_cloned_volume(volume, src_vref)

        vops.get_backing.assert_called_once_with(src_vref['name'],
                                                 src_vref['id'])
        get_clone_type.assert_called_once_with(volume)
        clone_attached_volume.assert_called_once_with(src_vref, volume)

    @mock.patch('cinder.volume.drivers.vmware.vmdk.'
                '_get_volume_type_extra_spec')
    def test_get_extra_spec_storage_profile(self, get_volume_type_extra_spec):
        vol_type_id = mock.sentinel.vol_type_id
        self._driver._get_extra_spec_storage_profile(vol_type_id)
        get_volume_type_extra_spec.assert_called_once_with(vol_type_id,
                                                           'storage_profile')

    @mock.patch.object(VMDK_DRIVER, '_get_extra_spec_storage_profile')
    def test_get_storage_profile(self, get_extra_spec_storage_profile):
        volume = self._create_volume_dict()
        self._driver._get_storage_profile(volume)
        get_extra_spec_storage_profile.assert_called_once_with(
            volume['volume_type_id'])

    @mock.patch.object(VMDK_DRIVER, '_get_storage_profile')
    @mock.patch.object(VMDK_DRIVER, 'session')
    @mock.patch('oslo_vmware.pbm.get_profile_id_by_name')
    def test_get_storage_profile_id(
            self, get_profile_id_by_name, session, get_storage_profile):
        get_storage_profile.return_value = 'gold'
        profile_id = mock.sentinel.profile_id
        get_profile_id_by_name.return_value = mock.Mock(uniqueId=profile_id)

        self._driver._storage_policy_enabled = True
        volume = self._create_volume_dict()
        self.assertEqual(profile_id,
                         self._driver._get_storage_profile_id(volume))
        get_storage_profile.assert_called_once_with(volume)
        get_profile_id_by_name.assert_called_once_with(session, 'gold')

    @mock.patch.object(VMDK_DRIVER, '_get_storage_profile')
    @mock.patch.object(VMDK_DRIVER, 'session')
    @mock.patch('oslo_vmware.pbm.get_profile_id_by_name')
    def test_get_storage_profile_id_with_missing_extra_spec(
            self, get_profile_id_by_name, session, get_storage_profile):
        get_storage_profile.return_value = None

        self._driver._storage_policy_enabled = True
        volume = self._create_volume_dict()
        self.assertIsNone(self._driver._get_storage_profile_id(volume))
        get_storage_profile.assert_called_once_with(volume)
        self.assertFalse(get_profile_id_by_name.called)

    @mock.patch.object(VMDK_DRIVER, '_get_storage_profile')
    @mock.patch.object(VMDK_DRIVER, 'session')
    @mock.patch('oslo_vmware.pbm.get_profile_id_by_name')
    def test_get_storage_profile_id_with_pbm_disabled(
            self, get_profile_id_by_name, session, get_storage_profile):
        get_storage_profile.return_value = 'gold'

        volume = self._create_volume_dict()
        self.assertIsNone(self._driver._get_storage_profile_id(volume))
        get_storage_profile.assert_called_once_with(volume)
        self.assertFalse(get_profile_id_by_name.called)

    @mock.patch.object(VMDK_DRIVER, '_get_storage_profile')
    @mock.patch.object(VMDK_DRIVER, 'session')
    @mock.patch('oslo_vmware.pbm.get_profile_id_by_name')
    def test_get_storage_profile_id_with_missing_profile(
            self, get_profile_id_by_name, session, get_storage_profile):
        get_storage_profile.return_value = 'gold'
        get_profile_id_by_name.return_value = None

        self._driver._storage_policy_enabled = True
        volume = self._create_volume_dict()
        self.assertIsNone(self._driver._get_storage_profile_id(volume))
        get_storage_profile.assert_called_once_with(volume)
        get_profile_id_by_name.assert_called_once_with(session, 'gold')

    @mock.patch.object(VMDK_DRIVER, 'volumeops')
    @mock.patch.object(VMDK_DRIVER, 'session')
    @mock.patch('cinder.image.image_utils.TemporaryImages.for_image_service')
    @mock.patch('cinder.volume.drivers.vmware.vmdk.open', create=True)
    @mock.patch('oslo_vmware.image_transfer.download_file')
    @mock.patch('oslo_vmware.image_transfer.download_flat_image')
    def _test_copy_image(self, download_flat_image, download_file, mock_open,
                         temp_images_img_service, session, vops,
                         expected_cacerts=False, use_temp_image=False):

        dc_name = mock.sentinel.dc_name
        vops.get_entity_name.return_value = dc_name

        mock_get = mock.Mock(return_value=None)
        tmp_images = mock.Mock(get=mock_get)
        temp_images_img_service.return_value = tmp_images
        if use_temp_image:
            mock_get.return_value = '/tmp/foo'
            mock_open_ret = mock.Mock()
            mock_open_ret.__enter__ = mock.Mock(
                return_value=mock.sentinel.read_handle)
            mock_open_ret.__exit__ = mock.Mock()
            mock_open.return_value = mock_open_ret

        context = mock.sentinel.context
        dc_ref = mock.sentinel.dc_ref
        image_service = mock.sentinel.image_service
        image_id = mock.sentinel.image_id
        image_size_in_bytes = 102400
        ds_name = mock.sentinel.ds_name
        upload_file_path = mock.sentinel.upload_file_path
        self._driver._copy_image(
            context, dc_ref, image_service, image_id, image_size_in_bytes,
            ds_name, upload_file_path)

        vops.get_entity_name.assert_called_once_with(dc_ref)
        cookies = session.vim.client.cookiejar
        if use_temp_image:
            mock_open.assert_called_once_with('/tmp/foo', 'rb')
            download_file.assert_called_once_with(
                mock.sentinel.read_handle,
                self._config.vmware_host_ip,
                self._config.vmware_host_port,
                dc_name,
                ds_name,
                cookies,
                upload_file_path,
                image_size_in_bytes,
                expected_cacerts,
                self._config.vmware_image_transfer_timeout_secs)
        else:
            download_flat_image.assert_called_once_with(
                context,
                self._config.vmware_image_transfer_timeout_secs,
                image_service,
                image_id,
                image_size=image_size_in_bytes,
                host=self._config.vmware_host_ip,
                port=self._config.vmware_host_port,
                data_center_name=dc_name,
                datastore_name=ds_name,
                cookies=cookies,
                file_path=upload_file_path,
                cacerts=expected_cacerts)

    def test_copy_image(self):
        # Default value of vmware_ca_file is not None; it should be passed
        # to download_flat_image as cacerts.
        self._test_copy_image(expected_cacerts=self._config.vmware_ca_file)

    def test_copy_image_insecure(self):
        # Set config options to allow insecure connections.
        self._config.vmware_ca_file = None
        self._config.vmware_insecure = True
        # Since vmware_ca_file is unset and vmware_insecure is True,
        # dowload_flat_image should be called with cacerts=False.
        self._test_copy_image()

    def test_copy_temp_image(self):
        self._test_copy_image(expected_cacerts=self._config.vmware_ca_file,
                              use_temp_image=True)

    @mock.patch.object(VMDK_DRIVER, '_select_ds_for_volume')
    @mock.patch.object(VMDK_DRIVER, '_get_storage_profile_id')
    @mock.patch.object(VMDK_DRIVER, 'volumeops')
    @mock.patch.object(VMDK_DRIVER, '_get_disk_type')
    @mock.patch.object(VMDK_DRIVER, '_get_adapter_type')
    def _test_create_backing(
            self, get_adapter_type, get_disk_type, vops,
            get_storage_profile_id, select_ds_for_volume, create_params=None):
        create_params = create_params or {}

        host = mock.sentinel.host
        resource_pool = mock.sentinel.resource_pool
        folder = mock.sentinel.folder
        summary = mock.sentinel.summary
        select_ds_for_volume.return_value = (host, resource_pool, folder,
                                             summary)

        profile_id = mock.sentinel.profile_id
        get_storage_profile_id.return_value = profile_id

        backing = mock.sentinel.backing
        vops.create_backing_disk_less.return_value = backing
        vops.create_backing.return_value = backing

        disk_type = mock.sentinel.disk_type
        get_disk_type.return_value = disk_type

        adapter_type = mock.sentinel.adapter_type
        get_adapter_type.return_value = adapter_type

        volume = self._create_volume_dict()
        ret = self._driver._create_backing(volume, host, create_params)

        self.assertEqual(backing, ret)
        select_ds_for_volume.assert_called_once_with(volume, host)
        get_storage_profile_id.assert_called_once_with(volume)

        exp_extra_config = {vmdk.EXTRA_CONFIG_VOLUME_ID_KEY: volume['id'],
                            volumeops.BACKING_UUID_KEY: volume['id']}
        if create_params.get(vmdk.CREATE_PARAM_DISK_LESS):
            vops.create_backing_disk_less.assert_called_once_with(
                volume['name'],
                folder,
                resource_pool,
                host,
                summary.name,
                profileId=profile_id,
                extra_config=exp_extra_config)
            vops.update_backing_disk_uuid.assert_not_called()
        else:
            get_disk_type.assert_called_once_with(volume)
            get_adapter_type.assert_called_once_with(volume)
            exp_backing_name = (
                create_params.get(vmdk.CREATE_PARAM_BACKING_NAME) or
                volume['name'])
            exp_adapter_type = (
                create_params.get(vmdk.CREATE_PARAM_ADAPTER_TYPE) or
                adapter_type)
            vops.create_backing.assert_called_once_with(
                exp_backing_name,
                volume['size'] * units.Mi,
                disk_type,
                folder,
                resource_pool,
                host,
                summary.name,
                profileId=profile_id,
                adapter_type=exp_adapter_type,
                extra_config=exp_extra_config)
            vops.update_backing_disk_uuid.assert_called_once_with(backing,
                                                                  volume['id'])

    def test_create_backing_disk_less(self):
        create_params = {vmdk.CREATE_PARAM_DISK_LESS: True}
        self._test_create_backing(create_params=create_params)

    def test_create_backing_with_adapter_type_override(self):
        create_params = {vmdk.CREATE_PARAM_ADAPTER_TYPE: 'ide'}
        self._test_create_backing(create_params=create_params)

    def test_create_backing_with_backing_name_override(self):
        create_params = {vmdk.CREATE_PARAM_BACKING_NAME: 'foo'}
        self._test_create_backing(create_params=create_params)

    def test_create_backing(self):
        self._test_create_backing()

    @mock.patch.object(VMDK_DRIVER, 'volumeops')
    def test_get_hosts(self, vops):
        host_1 = mock.sentinel.host_1
        host_2 = mock.sentinel.host_2
        host_3 = mock.sentinel.host_3
        vops.get_cluster_hosts.side_effect = [[host_1, host_2], [host_3]]

        cls_1 = mock.sentinel.cls_1
        cls_2 = mock.sentinel.cls_2
        self.assertEqual([host_1, host_2, host_3],
                         self._driver._get_hosts([cls_1, cls_2]))
        exp_calls = [mock.call(cls_1), mock.call(cls_2)]
        self.assertEqual(exp_calls, vops.get_cluster_hosts.call_args_list)

    @mock.patch.object(VMDK_DRIVER, '_get_hosts')
    @mock.patch.object(VMDK_DRIVER, 'ds_sel')
    def test_select_datastore(self, ds_sel, get_hosts):
        cls_1 = mock.sentinel.cls_1
        cls_2 = mock.sentinel.cls_2
        self._driver._clusters = [cls_1, cls_2]

        host_1 = mock.sentinel.host_1
        host_2 = mock.sentinel.host_2
        host_3 = mock.sentinel.host_3
        get_hosts.return_value = [host_1, host_2, host_3]

        best_candidate = mock.sentinel.best_candidate
        ds_sel.select_datastore.return_value = best_candidate

        req = mock.sentinel.req
        self.assertEqual(best_candidate, self._driver._select_datastore(req))
        get_hosts.assert_called_once_with(self._driver._clusters)
        ds_sel.select_datastore.assert_called_once_with(
            req, hosts=[host_1, host_2, host_3])

    @mock.patch.object(VMDK_DRIVER, '_get_hosts')
    @mock.patch.object(VMDK_DRIVER, 'ds_sel')
    def test_select_datastore_with_no_best_candidate(self, ds_sel, get_hosts):
        cls_1 = mock.sentinel.cls_1
        cls_2 = mock.sentinel.cls_2
        self._driver._clusters = [cls_1, cls_2]

        host_1 = mock.sentinel.host_1
        host_2 = mock.sentinel.host_2
        host_3 = mock.sentinel.host_3
        get_hosts.return_value = [host_1, host_2, host_3]

        ds_sel.select_datastore.return_value = ()

        req = mock.sentinel.req
        self.assertRaises(vmdk_exceptions.NoValidDatastoreException,
                          self._driver._select_datastore,
                          req)
        get_hosts.assert_called_once_with(self._driver._clusters)
        ds_sel.select_datastore.assert_called_once_with(
            req, hosts=[host_1, host_2, host_3])

    @mock.patch.object(VMDK_DRIVER, '_get_hosts')
    @mock.patch.object(VMDK_DRIVER, 'ds_sel')
    def test_select_datastore_with_single_host(self, ds_sel, get_hosts):
        best_candidate = mock.sentinel.best_candidate
        ds_sel.select_datastore.return_value = best_candidate

        req = mock.sentinel.req
        host_1 = mock.sentinel.host_1
        self.assertEqual(best_candidate,
                         self._driver._select_datastore(req, host_1))
        ds_sel.select_datastore.assert_called_once_with(req, hosts=[host_1])
        self.assertFalse(get_hosts.called)

    @mock.patch.object(VMDK_DRIVER, '_get_hosts')
    @mock.patch.object(VMDK_DRIVER, 'ds_sel')
    def test_select_datastore_with_empty_clusters(self, ds_sel, get_hosts):
        self._driver._clusters = None

        best_candidate = mock.sentinel.best_candidate
        ds_sel.select_datastore.return_value = best_candidate

        req = mock.sentinel.req
        self.assertEqual(best_candidate, self._driver._select_datastore(req))
        ds_sel.select_datastore.assert_called_once_with(req, hosts=None)
        self.assertFalse(get_hosts.called)

    @mock.patch.object(VMDK_DRIVER, '_get_hosts')
    @mock.patch.object(VMDK_DRIVER, 'ds_sel')
    def test_select_datastore_with_no_valid_host(self, ds_sel, get_hosts):
        cls_1 = mock.sentinel.cls_1
        cls_2 = mock.sentinel.cls_2
        self._driver._clusters = [cls_1, cls_2]

        get_hosts.return_value = []

        req = mock.sentinel.req
        self.assertRaises(vmdk_exceptions.NoValidHostException,
                          self._driver._select_datastore, req)
        get_hosts.assert_called_once_with(self._driver._clusters)
        self.assertFalse(ds_sel.called)

    @mock.patch.object(VMDK_DRIVER, 'volumeops')
    @mock.patch.object(VMDK_DRIVER, '_get_storage_profile')
    @mock.patch.object(VMDK_DRIVER, 'ds_sel')
    def test_relocate_backing_nop(self, ds_sel, get_profile, vops):
        self._driver._storage_policy_enabled = True
        volume = self._create_volume_dict()

        datastore = mock.sentinel.datastore
        vops.get_datastore.return_value = datastore

        profile = mock.sentinel.profile
        get_profile.return_value = profile

        vops.is_datastore_accessible.return_value = True
        ds_sel.is_datastore_compliant.return_value = True

        backing = mock.sentinel.backing
        host = mock.sentinel.host
        self._driver._relocate_backing(volume, backing, host)

        get_profile.assert_called_once_with(volume)
        vops.is_datastore_accessible.assert_called_once_with(datastore, host)
        ds_sel.is_datastore_compliant.assert_called_once_with(datastore,
                                                              profile)
        self.assertFalse(vops.relocate_backing.called)

    @mock.patch.object(VMDK_DRIVER, 'volumeops')
    @mock.patch.object(VMDK_DRIVER, '_get_storage_profile')
    @mock.patch.object(VMDK_DRIVER, 'ds_sel')
    def test_relocate_backing_with_no_datastore(
            self, ds_sel, get_profile, vops):
        self._driver._storage_policy_enabled = True
        volume = self._create_volume_dict()

        profile = mock.sentinel.profile
        get_profile.return_value = profile

        vops.is_datastore_accessible.return_value = True
        ds_sel.is_datastore_compliant.return_value = False

        ds_sel.select_datastore.return_value = []

        backing = mock.sentinel.backing
        host = mock.sentinel.host

        self.assertRaises(vmdk_exceptions.NoValidDatastoreException,
                          self._driver._relocate_backing,
                          volume,
                          backing,
                          host)
        get_profile.assert_called_once_with(volume)
        ds_sel.select_datastore.assert_called_once_with(
            {hub.DatastoreSelector.SIZE_BYTES: volume['size'] * units.Gi,
             hub.DatastoreSelector.PROFILE_NAME: profile}, hosts=[host])
        self.assertFalse(vops.relocate_backing.called)

    @mock.patch.object(VMDK_DRIVER, 'volumeops')
    @mock.patch.object(VMDK_DRIVER, '_get_dc')
    @mock.patch.object(VMDK_DRIVER, '_get_volume_group_folder')
    @mock.patch.object(VMDK_DRIVER, 'ds_sel')
    def test_relocate_backing(
            self, ds_sel, get_volume_group_folder, get_dc, vops):
        volume = self._create_volume_dict()

        vops.is_datastore_accessible.return_value = False
        ds_sel.is_datastore_compliant.return_value = True

        backing = mock.sentinel.backing
        host = mock.sentinel.host

        rp = mock.sentinel.rp
        datastore = mock.sentinel.datastore
        summary = mock.Mock(datastore=datastore)
        ds_sel.select_datastore.return_value = (host, rp, summary)

        dc = mock.sentinel.dc
        get_dc.return_value = dc

        folder = mock.sentinel.folder
        get_volume_group_folder.return_value = folder

        self._driver._relocate_backing(volume, backing, host)

        get_dc.assert_called_once_with(rp)
        get_volume_group_folder.assert_called_once_with(
            dc, volume['project_id'])
        vops.relocate_backing.assert_called_once_with(backing,
                                                      datastore,
                                                      rp,
                                                      host)
        vops.move_backing_to_folder.assert_called_once_with(backing,
                                                            folder)

    @mock.patch.object(VMDK_DRIVER, 'volumeops')
    @mock.patch.object(VMDK_DRIVER, '_get_dc')
    @mock.patch.object(VMDK_DRIVER, '_get_volume_group_folder')
    @mock.patch.object(VMDK_DRIVER, 'ds_sel')
    def test_relocate_backing_with_pbm_disabled(
            self, ds_sel, get_volume_group_folder, get_dc, vops):
        self._driver._storage_policy_enabled = False
        volume = self._create_volume_dict()

        vops.is_datastore_accessible.return_value = False

        backing = mock.sentinel.backing
        host = mock.sentinel.host

        rp = mock.sentinel.rp
        datastore = mock.sentinel.datastore
        summary = mock.Mock(datastore=datastore)
        ds_sel.select_datastore.return_value = (host, rp, summary)

        dc = mock.sentinel.dc
        get_dc.return_value = dc

        folder = mock.sentinel.folder
        get_volume_group_folder.return_value = folder

        self._driver._relocate_backing(volume, backing, host)

        self.assertFalse(vops.get_profile.called)
        get_dc.assert_called_once_with(rp)
        get_volume_group_folder.assert_called_once_with(
            dc, volume['project_id'])
        vops.relocate_backing.assert_called_once_with(backing,
                                                      datastore,
                                                      rp,
                                                      host)
        vops.move_backing_to_folder.assert_called_once_with(backing,
                                                            folder)
        ds_sel.select_datastore.assert_called_once_with(
            {hub.DatastoreSelector.SIZE_BYTES: volume['size'] * units.Gi,
             hub.DatastoreSelector.PROFILE_NAME: None}, hosts=[host])

    @mock.patch.object(VMDK_DRIVER, 'volumeops')
    def test_get_disk_device(self, vops):
        vm = mock.sentinel.vm
        vops.get_entity_by_inventory_path.return_value = vm

        dev = mock.sentinel.dev
        vops.get_disk_device.return_value = dev

        vm_inv_path = mock.sentinel.vm_inv_path
        vmdk_path = mock.sentinel.vmdk_path
        ret = self._driver._get_disk_device(vmdk_path, vm_inv_path)

        self.assertEqual((vm, dev), ret)
        vops.get_entity_by_inventory_path.assert_called_once_with(vm_inv_path)
        vops.get_disk_device.assert_called_once_with(vm, vmdk_path)

    def test_get_existing_with_empty_source_name(self):
        self.assertRaises(cinder_exceptions.InvalidInput,
                          self._driver._get_existing,
                          {})

    def test_get_existing_with_invalid_source_name(self):
        self.assertRaises(cinder_exceptions.InvalidInput,
                          self._driver._get_existing,
                          {'source-name': 'foo'})

    @mock.patch.object(VMDK_DRIVER, '_get_disk_device', return_value=None)
    def test_get_existing_with_invalid_existing_ref(self, get_disk_device):
        self.assertRaises(cinder_exceptions.ManageExistingInvalidReference,
                          self._driver._get_existing,
                          {'source-name': '[ds1] foo/foo.vmdk@/dc-1/vm/foo'})
        get_disk_device.assert_called_once_with('[ds1] foo/foo.vmdk',
                                                '/dc-1/vm/foo')

    @mock.patch.object(VMDK_DRIVER, '_get_disk_device')
    def test_get_existing(self, get_disk_device):
        vm = mock.sentinel.vm
        disk_device = mock.sentinel.disk_device
        get_disk_device.return_value = (vm, disk_device)
        self.assertEqual(
            (vm, disk_device),
            self._driver._get_existing({'source-name':
                                        '[ds1] foo/foo.vmdk@/dc-1/vm/foo'}))
        get_disk_device.assert_called_once_with('[ds1] foo/foo.vmdk',
                                                '/dc-1/vm/foo')

    @mock.patch.object(VMDK_DRIVER, '_get_existing')
    @ddt.data((16384, 1), (1048576, 1), (1572864, 2))
    def test_manage_existing_get_size(self, test_data, get_existing):
        (capacity_kb, exp_size) = test_data
        disk_device = mock.Mock(capacityInKB=capacity_kb)
        get_existing.return_value = (mock.sentinel.vm, disk_device)

        volume = mock.sentinel.volume
        existing_ref = mock.sentinel.existing_ref
        self.assertEqual(exp_size,
                         self._driver.manage_existing_get_size(volume,
                                                               existing_ref))
        get_existing.assert_called_once_with(existing_ref)

    @mock.patch.object(VMDK_DRIVER, '_create_backing')
    @mock.patch.object(VMDK_DRIVER, 'volumeops')
    @mock.patch.object(VMDK_DRIVER, '_get_ds_name_folder_path')
    @mock.patch.object(VMDK_DRIVER, '_get_storage_profile_id')
    @mock.patch('cinder.volume.drivers.vmware.vmdk.VMwareVcVmdkDriver.'
                '_get_disk_type')
    @mock.patch.object(VMDK_DRIVER, '_get_adapter_type')
    def test_manage_existing_int(
            self, get_adapter_type, get_disk_type, get_storage_profile_id,
            get_ds_name_folder_path, vops, create_backing):

        backing = mock.sentinel.backing
        create_backing.return_value = backing

        src_dc = mock.sentinel.src_dc
        dest_dc = mock.sentinel.dest_dc
        vops.get_dc.side_effect = [src_dc, dest_dc]

        volume = self._create_volume_dict()
        ds_name = "ds1"
        folder_path = "%s/" % volume['name']
        get_ds_name_folder_path.return_value = (ds_name, folder_path)

        profile_id = mock.sentinel.profile_id
        get_storage_profile_id.return_value = profile_id

        disk_type = mock.sentinel.disk_type
        get_disk_type.return_value = disk_type

        adapter_type = mock.sentinel.adapter_type
        get_adapter_type.return_value = adapter_type

        vm = mock.sentinel.vm
        src_path = mock.sentinel.src_path
        disk_backing = mock.Mock(fileName=src_path)
        disk_device = mock.Mock(backing=disk_backing, capacityInKB=1048576)
        ret = self._driver._manage_existing_int(volume, vm, disk_device)

        self.assertEqual(backing, ret)
        create_backing.assert_called_once_with(
            volume, create_params={vmdk.CREATE_PARAM_DISK_LESS: True})
        vops.detach_disk_from_backing.assert_called_once_with(vm, disk_device)
        dest_path = "[%s] %s%s.vmdk" % (ds_name, folder_path, volume['name'])
        vops.move_vmdk_file.assert_called_once_with(
            src_dc, src_path, dest_path, dest_dc_ref=dest_dc)
        get_storage_profile_id.assert_called_once_with(volume)
        get_adapter_type.assert_called_once_with(volume)
        vops.attach_disk_to_backing.assert_called_once_with(
            backing, disk_device.capacityInKB, disk_type,
            adapter_type, profile_id, dest_path)
        vops.update_backing_disk_uuid.assert_called_once_with(backing,
                                                              volume['id'])

    @mock.patch.object(VMDK_DRIVER, '_get_existing')
    @mock.patch.object(VMDK_DRIVER, '_manage_existing_int')
    def test_manage_existing(self, manage_existing_int, get_existing):
        vm = mock.sentinel.vm
        disk_device = mock.sentinel.disk_device
        get_existing.return_value = (vm, disk_device)

        volume = mock.sentinel.volume
        existing_ref = mock.sentinel.existing_ref
        self._driver.manage_existing(volume, existing_ref)

        get_existing.assert_called_once_with(existing_ref)
        manage_existing_int.assert_called_once_with(volume, vm, disk_device)

    @mock.patch.object(VMDK_DRIVER, 'volumeops')
    def test_unmanage(self, vops):
        backing = mock.sentinel.backing
        vops.get_backing.return_value = backing

        volume = self._create_volume_dict()
        self._driver.unmanage(volume)

        vops.get_backing.assert_called_once_with(volume['name'], volume['id'])
        vops.update_backing_extra_config.assert_called_once_with(
            backing, {vmdk.EXTRA_CONFIG_VOLUME_ID_KEY: '',
                      volumeops.BACKING_UUID_KEY: ''})

    @mock.patch('oslo_vmware.api.VMwareAPISession')
    def test_create_session(self, apiSession):
        session = mock.sentinel.session
        apiSession.return_value = session

        ret = self._driver._create_session()

        self.assertEqual(session, ret)
        config = self._driver.configuration
        apiSession.assert_called_once_with(
            config.vmware_host_ip,
            config.vmware_host_username,
            config.vmware_host_password,
            config.vmware_api_retry_count,
            config.vmware_task_poll_interval,
            wsdl_loc=config.safe_get('vmware_wsdl_location'),
            port=config.vmware_host_port,
            cacert=config.vmware_ca_file,
            insecure=config.vmware_insecure,
            pool_size=config.vmware_connection_pool_size,
            op_id_prefix='c-vol')

    @mock.patch.object(VMDK_DRIVER, 'volumeops')
    @mock.patch.object(VMDK_DRIVER, '_extend_backing')
    def test_extend_volume_with_no_backing(self, extend_backing, vops):
        vops.get_backing.return_value = None

        volume = self._create_volume_dict()
        self._driver.extend_volume(volume, 2)

        self.assertFalse(extend_backing.called)

    @mock.patch.object(VMDK_DRIVER, '_get_disk_type')
    @mock.patch.object(VMDK_DRIVER, 'volumeops')
    @mock.patch.object(VMDK_DRIVER, '_extend_backing')
    def test_extend_volume(self, extend_backing, vops, get_disk_type):
        backing = mock.sentinel.backing
        vops.get_backing.return_value = backing

        disk_type = mock.sentinel.disk_type
        get_disk_type.return_value = disk_type

        volume = self._create_volume_dict()
        new_size = 2
        self._driver.extend_volume(volume, new_size)

        extend_backing.assert_called_once_with(backing, new_size, disk_type)

    @mock.patch.object(VMDK_DRIVER, '_get_disk_type')
    @mock.patch.object(VMDK_DRIVER, 'volumeops')
    @mock.patch.object(VMDK_DRIVER, '_extend_backing')
    @mock.patch.object(VMDK_DRIVER, '_select_ds_for_volume')
    def test_extend_volume_with_no_disk_space(self, select_ds_for_volume,
                                              extend_backing, vops,
                                              get_disk_type):
        backing = mock.sentinel.backing
        vops.get_backing.return_value = backing

        disk_type = mock.sentinel.disk_type
        get_disk_type.return_value = disk_type

        extend_backing.side_effect = [exceptions.NoDiskSpaceException, None]

        host = mock.sentinel.host
        rp = mock.sentinel.rp
        folder = mock.sentinel.folder
        datastore = mock.sentinel.datastore
        summary = mock.Mock(datastore=datastore)
        select_ds_for_volume.return_value = (host, rp, folder, summary)

        volume = self._create_volume_dict()
        new_size = 2
        self._driver.extend_volume(volume, new_size)

        create_params = {vmdk.CREATE_PARAM_DISK_SIZE: new_size}
        select_ds_for_volume.assert_called_once_with(
            volume, create_params=create_params)

        vops.relocate_backing.assert_called_once_with(backing, datastore, rp,
                                                      host)
        vops.move_backing_to_folder(backing, folder)

        extend_backing_calls = [mock.call(backing, new_size, disk_type),
                                mock.call(backing, new_size, disk_type)]
        self.assertEqual(extend_backing_calls, extend_backing.call_args_list)

    @mock.patch.object(VMDK_DRIVER, '_get_disk_type')
    @mock.patch.object(VMDK_DRIVER, 'volumeops')
    @mock.patch.object(VMDK_DRIVER, '_extend_backing')
    def test_extend_volume_with_extend_backing_error(
            self, extend_backing, vops, get_disk_type):
        backing = mock.sentinel.backing
        vops.get_backing.return_value = backing

        disk_type = mock.sentinel.disk_type
        get_disk_type.return_value = disk_type

        extend_backing.side_effect = exceptions.VimException("Error")

        volume = self._create_volume_dict()
        new_size = 2
        self.assertRaises(exceptions.VimException, self._driver.extend_volume,
                          volume, new_size)
        extend_backing.assert_called_once_with(backing, new_size, disk_type)

    @mock.patch.object(VMDK_DRIVER, 'volumeops')
    @mock.patch.object(VMDK_DRIVER, '_get_volume_group_folder')
    def test_accept_transfer(self, get_volume_group_folder, vops):
        backing = mock.sentinel.backing
        vops.get_backing.return_value = backing

        dc = mock.sentinel.dc
        vops.get_dc.return_value = dc

        new_folder = mock.sentinel.new_folder
        get_volume_group_folder.return_value = new_folder

        context = mock.sentinel.context
        volume = self._create_volume_obj()
        new_project = mock.sentinel.new_project
        self._driver.accept_transfer(context, volume, mock.sentinel.new_user,
                                     new_project)

        vops.get_backing.assert_called_once_with(volume.name, volume.id)
        vops.get_dc.assert_called_once_with(backing)
        get_volume_group_folder.assert_called_once_with(dc, new_project)
        vops.move_backing_to_folder.assert_called_once_with(backing,
                                                            new_folder)

    @mock.patch.object(VMDK_DRIVER, 'volumeops')
    def test_revert_to_snapshot_with_no_backing(self, vops):
        vops.get_backing.return_value = None

        volume = self._create_volume_obj()
        snapshot = fake_snapshot.fake_snapshot_obj(self._context,
                                                   volume=volume)
        self._driver.revert_to_snapshot(
            mock.sentinel.context, volume, snapshot)

        vops.get_backing.assert_called_once_with(volume.name, volume.id)
        vops.revert_to_snapshot.assert_not_called()

    @mock.patch.object(VMDK_DRIVER, 'volumeops')
    def test_revert_to_snapshot_template_format(self, vops):
        volume = self._create_volume_obj()
        loc = '/test-dc/foo'
        snapshot = fake_snapshot.fake_snapshot_obj(self._context,
                                                   volume=volume,
                                                   provider_location=loc)
        self.assertRaises(cinder_exceptions.InvalidSnapshot,
                          self._driver.revert_to_snapshot,
                          mock.sentinel.context,
                          volume,
                          snapshot)
        vops.revert_to_snapshot.assert_not_called()

    @mock.patch.object(VMDK_DRIVER, 'volumeops')
    def test_revert_to_snapshot(self, vops):
        backing = mock.sentinel.backing
        vops.get_backing.return_value = backing

        volume = self._create_volume_obj()
        snapshot = fake_snapshot.fake_snapshot_obj(self._context,
                                                   volume=volume)
        self._driver.revert_to_snapshot(
            mock.sentinel.context, volume, snapshot)

        vops.get_backing.assert_called_once_with(volume.name, volume.id)
        vops.revert_to_snapshot.assert_called_once_with(backing,
                                                        snapshot.name)


@ddt.ddt
class ImageDiskTypeTest(test.TestCase):
    """Unit tests for ImageDiskType."""

    @ddt.data('thin', 'preallocated', 'streamOptimized', 'sparse')
    def test_is_valid(self, image_disk_type):
        self.assertTrue(vmdk.ImageDiskType.is_valid(image_disk_type))

    def test_is_valid_with_invalid_type(self):
        self.assertFalse(vmdk.ImageDiskType.is_valid('thick'))

    @ddt.data('thin', 'preallocated', 'streamOptimized', 'sparse')
    def test_validate(self, image_disk_type):
        vmdk.ImageDiskType.validate(image_disk_type)

    def test_validate_with_invalid_type(self):
        self.assertRaises(cinder_exceptions.ImageUnacceptable,
                          vmdk.ImageDiskType.validate,
                          "thick")
