コード例 #1
0
    def test_create_passed_mapped_exception_and_detail(self, mock_utcnow):
        # passed Detail should be ignored because this is a mapped exception
        CONF.set_override('message_ttl', 300)
        mock_utcnow.return_value = datetime.datetime.utcnow()
        expected_expires_at = timeutils.utcnow() + datetime.timedelta(
            seconds=300)
        expected_message_record = {
            'project_id': 'fakeproject',
            'request_id': 'fakerequestid',
            'resource_type': 'fake_resource_type',
            'resource_uuid': None,
            'action_id': message_field.Action.UPDATE_ATTACHMENT[0],
            'detail_id': message_field.Detail.NOT_ENOUGH_SPACE_FOR_IMAGE[0],
            'message_level': 'ERROR',
            'expires_at': expected_expires_at,
            'event_id': "VOLUME_fake_resource_type_004_007",
        }
        exc = exception.ImageTooBig(image_id='fake_image', reason='MYOB')
        self.message_api.create(
            self.ctxt,
            action=message_field.Action.UPDATE_ATTACHMENT,
            detail=message_field.Detail.VOLUME_ATTACH_MODE_INVALID,
            exception=exc,
            resource_type="fake_resource_type")

        self.message_api.db.message_create.assert_called_once_with(
            self.ctxt, expected_message_record)
        mock_utcnow.assert_called_with()
コード例 #2
0
    def test_create_passed_exception_no_detail(self, mock_utcnow):
        # Detail should be automatically supplied based on the
        # message_field.Detail.EXCEPTION_DETAIL_MAPPINGS
        CONF.set_override('message_ttl', 300)
        mock_utcnow.return_value = datetime.datetime.utcnow()
        expected_expires_at = timeutils.utcnow() + datetime.timedelta(
            seconds=300)
        expected_message_record = {
            'project_id': 'fakeproject',
            'request_id': 'fakerequestid',
            'resource_type': 'fake_resource_type',
            'resource_uuid': None,
            'action_id': message_field.Action.SCHEDULE_ALLOCATE_VOLUME[0],
            # this is determined by the exception we'll be passing
            'detail_id': message_field.Detail.NOT_ENOUGH_SPACE_FOR_IMAGE[0],
            'message_level': 'ERROR',
            'expires_at': expected_expires_at,
            'event_id': "VOLUME_fake_resource_type_001_007",
        }
        exc = exception.ImageTooBig(image_id='fake_image', reason='MYOB')
        self.message_api.create(
            self.ctxt,
            action=message_field.Action.SCHEDULE_ALLOCATE_VOLUME,
            exception=exc,
            resource_type="fake_resource_type")

        self.message_api.db.message_create.assert_called_once_with(
            self.ctxt, expected_message_record)
        mock_utcnow.assert_called_with()
コード例 #3
0
ファイル: image_utils.py プロジェクト: parthipan1/cinder
def check_available_space(dest, image_size, image_id):
    # TODO(e0ne): replace psutil with shutil.disk_usage when we drop
    # Python 2.7 support.
    if not os.path.isdir(dest):
        dest = os.path.dirname(dest)

    free_space = psutil.disk_usage(dest).free
    if free_space <= image_size:
        msg = ('There is no space to convert image. '
               'Requested: %(image_size)s, available: %(free_space)s'
               ) % {'image_size': image_size, 'free_space': free_space}
        raise exception.ImageTooBig(image_id=image_id, reason=msg)
コード例 #4
0
def fetch(context: context.RequestContext,
          image_service: glance.GlanceImageService, image_id: str, path: str,
          _user_id, _project_id) -> None:
    # TODO(vish): Improve context handling and add owner and auth data
    #             when it is added to glance.  Right now there is no
    #             auth checking in glance, so we assume that access was
    #             checked before we got here.
    start_time = timeutils.utcnow()
    with fileutils.remove_path_on_error(path):
        with open(path, "wb") as image_file:
            try:
                image_service.download(context, image_id,
                                       tpool.Proxy(image_file))
            except IOError as e:
                if e.errno == errno.ENOSPC:
                    params = {'path': os.path.dirname(path), 'image': image_id}
                    reason = _("No space left in image_conversion_dir "
                               "path (%(path)s) while fetching "
                               "image %(image)s.") % params
                    LOG.exception(reason)
                    raise exception.ImageTooBig(image_id=image_id,
                                                reason=reason)

                reason = ("IOError: %(errno)s %(strerror)s" % {
                    'errno': e.errno,
                    'strerror': e.strerror
                })
                LOG.error(reason)
                raise exception.ImageDownloadFailed(image_href=image_id,
                                                    reason=reason)

    duration = timeutils.delta_seconds(start_time, timeutils.utcnow())

    # NOTE(jdg): use a default of 1, mostly for unit test, but in
    # some incredible event this is 0 (cirros image?) don't barf
    if duration < 1:
        duration = 1
    fsz_mb = os.stat(image_file.name).st_size / units.Mi
    mbps = (fsz_mb / duration)
    msg = ("Image fetch details: dest %(dest)s, size %(sz).2f MB, "
           "duration %(duration).2f sec")
    LOG.debug(msg, {
        "dest": image_file.name,
        "sz": fsz_mb,
        "duration": duration
    })
    msg = "Image download %(sz).2f MB at %(mbps).2f MB/s"
    LOG.info(msg, {"sz": fsz_mb, "mbps": mbps})
コード例 #5
0
    def test_volume_reimage_raise_exception(self):
        volume = tests_utils.create_volume(self.context)
        self.volume.create_volume(self.context, volume)

        with mock.patch.object(self.volume.driver, 'copy_image_to_volume'
                               ) as mock_cp_img:
            mock_cp_img.side_effect = processutils.ProcessExecutionError
            self.assertRaises(exception.ImageCopyFailure, self.volume.reimage,
                              self.context, volume, self.image_meta)
            self.assertEqual(volume.previous_status, 'available')
            self.assertEqual(volume.status, 'error')

            mock_cp_img.side_effect = exception.ImageUnacceptable(
                image_id=self.image_meta['id'], reason='')
            self.assertRaises(exception.ImageUnacceptable, self.volume.reimage,
                              self.context, volume, self.image_meta)

            mock_cp_img.side_effect = exception.ImageConversionNotAllowed(
                image_id=self.image_meta['id'], reason='')

            with mock.patch.object(
                self.volume.message_api, 'create'
            ) as mock_msg_create:
                self.assertRaises(
                    exception.ImageConversionNotAllowed, self.volume.reimage,
                    self.context, volume, self.image_meta)
                mock_msg_create.assert_called_with(
                    self.context,
                    message_field.Action.REIMAGE_VOLUME,
                    resource_uuid=volume.id,
                    detail=message_field.Detail.IMAGE_FORMAT_UNACCEPTABLE)

            mock_cp_img.side_effect = exception.ImageTooBig(
                image_id=self.image_meta['id'], reason='')
            self.assertRaises(exception.ImageTooBig, self.volume.reimage,
                              self.context, volume, self.image_meta)

            mock_cp_img.side_effect = Exception
            self.assertRaises(exception.ImageCopyFailure, self.volume.reimage,
                              self.context, volume, self.image_meta)

            mock_cp_img.side_effect = exception.ImageCopyFailure(reason='')
            self.assertRaises(exception.ImageCopyFailure, self.volume.reimage,
                              self.context, volume, self.image_meta)
コード例 #6
0
ファイル: test_driver.py プロジェクト: yangjunfeng1990/cinder
class GenericVolumeDriverTestCase(BaseDriverTestCase):
    """Test case for VolumeDriver."""
    driver_name = "cinder.tests.fake_driver.FakeLoggingVolumeDriver"

    def test_create_temp_cloned_volume(self):
        with mock.patch.object(
                self.volume.driver,
                'create_cloned_volume') as mock_create_cloned_volume:
            model_update = {'provider_location': 'dummy'}
            mock_create_cloned_volume.return_value = model_update
            vol = tests_utils.create_volume(self.context,
                                            status='backing-up')
            cloned_vol = self.volume.driver._create_temp_cloned_volume(
                self.context, vol)
            self.assertEqual('dummy', cloned_vol.provider_location)
            self.assertEqual('available', cloned_vol.status)

            mock_create_cloned_volume.return_value = None
            vol = tests_utils.create_volume(self.context,
                                            status='backing-up')
            cloned_vol = self.volume.driver._create_temp_cloned_volume(
                self.context, vol)
            self.assertEqual('available', cloned_vol.status)

    def test_get_backup_device_available(self):
        vol = tests_utils.create_volume(self.context)
        self.context.user_id = fake.USER_ID
        self.context.project_id = fake.PROJECT_ID
        backup_obj = tests_utils.create_backup(self.context,
                                               vol['id'])
        (backup_device, is_snapshot) = self.volume.driver.get_backup_device(
            self.context, backup_obj)
        volume = objects.Volume.get_by_id(self.context, vol.id)
        self.assertNotIn('temporary', backup_device.admin_metadata.keys())
        self.assertEqual(volume, backup_device)
        self.assertFalse(is_snapshot)
        backup_obj.refresh()
        self.assertIsNone(backup_obj.temp_volume_id)

    def test_get_backup_device_in_use(self):
        vol = tests_utils.create_volume(self.context,
                                        status='backing-up',
                                        previous_status='in-use')
        admin_meta = {'temporary': 'True'}
        temp_vol = tests_utils.create_volume(self.context,
                                             admin_metadata=admin_meta)
        self.context.user_id = fake.USER_ID
        self.context.project_id = fake.PROJECT_ID
        backup_obj = tests_utils.create_backup(self.context,
                                               vol['id'])
        with mock.patch.object(
                self.volume.driver,
                '_create_temp_cloned_volume') as mock_create_temp:
            mock_create_temp.return_value = temp_vol
            (backup_device, is_snapshot) = (
                self.volume.driver.get_backup_device(self.context,
                                                     backup_obj))
            self.assertEqual(temp_vol, backup_device)
            self.assertFalse(is_snapshot)
            backup_obj.refresh()
            self.assertEqual(temp_vol.id, backup_obj.temp_volume_id)

    def test_create_temp_volume_from_snapshot(self):
        volume_dict = {'id': fake.SNAPSHOT_ID,
                       'host': 'fakehost',
                       'cluster_name': 'fakecluster',
                       'availability_zone': 'fakezone',
                       'size': 1,
                       'volume_type_id': fake.VOLUME_TYPE_ID}
        vol = fake_volume.fake_volume_obj(self.context, **volume_dict)
        snapshot = fake_snapshot.fake_snapshot_obj(self.context)

        with mock.patch.object(
                self.volume.driver,
                'create_volume_from_snapshot'):
            temp_vol = self.volume.driver._create_temp_volume_from_snapshot(
                self.context,
                vol, snapshot)
            self.assertEqual(fields.VolumeAttachStatus.DETACHED,
                             temp_vol.attach_status)
            self.assertEqual('fakezone', temp_vol.availability_zone)
            self.assertEqual('fakecluster', temp_vol.cluster_name)

    @mock.patch.object(utils, 'brick_get_connector_properties')
    @mock.patch.object(cinder.volume.manager.VolumeManager, '_attach_volume')
    @mock.patch.object(cinder.volume.manager.VolumeManager, '_detach_volume')
    @mock.patch.object(volume_utils, 'copy_volume')
    @mock.patch.object(volume_rpcapi.VolumeAPI, 'get_capabilities')
    @mock.patch.object(cinder.volume.volume_types,
                       'volume_types_encryption_changed')
    @ddt.data(False, True)
    def test_copy_volume_data_mgr(self,
                                  encryption_changed,
                                  mock_encryption_changed,
                                  mock_get_capabilities,
                                  mock_copy,
                                  mock_detach,
                                  mock_attach,
                                  mock_get_connector):
        """Test function of _copy_volume_data."""

        src_vol = tests_utils.create_volume(self.context, size=1,
                                            host=CONF.host)
        dest_vol = tests_utils.create_volume(self.context, size=1,
                                             host=CONF.host)
        mock_get_connector.return_value = {}
        mock_encryption_changed.return_value = encryption_changed
        self.volume.driver._throttle = mock.MagicMock()

        attach_expected = [
            mock.call(self.context, dest_vol, {},
                      remote=False,
                      attach_encryptor=encryption_changed),
            mock.call(self.context, src_vol, {},
                      remote=False,
                      attach_encryptor=encryption_changed)]

        detach_expected = [
            mock.call(self.context, {'device': {'path': 'bar'}},
                      dest_vol, {}, force=True, remote=False,
                      attach_encryptor=encryption_changed),
            mock.call(self.context, {'device': {'path': 'foo'}},
                      src_vol, {}, force=True, remote=False,
                      attach_encryptor=encryption_changed)]

        attach_volume_returns = [
            {'device': {'path': 'bar'}},
            {'device': {'path': 'foo'}}
        ]

        #  Test case for sparse_copy_volume = False
        mock_attach.side_effect = attach_volume_returns
        mock_get_capabilities.return_value = {}
        self.volume._copy_volume_data(self.context,
                                      src_vol,
                                      dest_vol)

        self.assertEqual(attach_expected, mock_attach.mock_calls)
        mock_copy.assert_called_with('foo', 'bar', 1024, '1M', sparse=False)
        self.assertEqual(detach_expected, mock_detach.mock_calls)

        #  Test case for sparse_copy_volume = True
        mock_attach.reset_mock()
        mock_detach.reset_mock()
        mock_attach.side_effect = attach_volume_returns
        mock_get_capabilities.return_value = {'sparse_copy_volume': True}
        self.volume._copy_volume_data(self.context,
                                      src_vol,
                                      dest_vol)

        self.assertEqual(attach_expected, mock_attach.mock_calls)
        mock_copy.assert_called_with('foo', 'bar', 1024, '1M', sparse=True)
        self.assertEqual(detach_expected, mock_detach.mock_calls)

        # cleanup resource
        db.volume_destroy(self.context, src_vol['id'])
        db.volume_destroy(self.context, dest_vol['id'])

    @mock.patch(driver_name + '.initialize_connection')
    @mock.patch(driver_name + '.create_export', return_value=None)
    @mock.patch(driver_name + '._connect_device')
    def test_attach_volume_encrypted(self, connect_mock, export_mock,
                                     initialize_mock):
        properties = {'host': 'myhost', 'ip': '192.168.1.43',
                      'initiator': u'iqn.1994-05.com.redhat:d9be887375',
                      'multipath': False, 'os_type': 'linux2',
                      'platform': 'x86_64'}

        data = {'target_discovered': True,
                'target_iqn': 'iqn.2010-10.org.openstack:volume-00000001',
                'target_portal': '127.0.0.0.1:3260',
                'volume_id': 1,
                'discard': False}

        passed_conn = {'driver_volume_type': 'iscsi', 'data': data.copy()}
        initialize_mock.return_value = passed_conn

        # _attach_volume adds the encrypted value based on the volume
        expected_conn = {'driver_volume_type': 'iscsi', 'data': data.copy()}
        expected_conn['data']['encrypted'] = True

        volume = tests_utils.create_volume(
            self.context, status='available',
            size=2,
            encryption_key_id=fake.ENCRYPTION_KEY_ID)

        attach_info, vol = self.volume.driver._attach_volume(self.context,
                                                             volume,
                                                             properties)

        export_mock.assert_called_once_with(self.context, volume, properties)
        initialize_mock.assert_called_once_with(volume, properties)

        connect_mock.assert_called_once_with(expected_conn)

        self.assertEqual(connect_mock.return_value, attach_info)
        self.assertEqual(volume, vol)

    @mock.patch.object(os_brick.initiator.connector,
                       'get_connector_properties')
    @mock.patch.object(image_utils, 'fetch_to_raw')
    @mock.patch.object(cinder.volume.driver.VolumeDriver, '_attach_volume')
    @mock.patch.object(cinder.volume.driver.VolumeDriver, '_detach_volume')
    @mock.patch.object(cinder.utils, 'brick_attach_volume_encryptor')
    @mock.patch.object(cinder.utils, 'brick_detach_volume_encryptor')
    def test_copy_image_to_encrypted_volume(self,
                                            mock_detach_encryptor,
                                            mock_attach_encryptor,
                                            mock_detach_volume,
                                            mock_attach_volume,
                                            mock_fetch_to_raw,
                                            mock_get_connector_properties):
        properties = {}
        volume = tests_utils.create_volume(
            self.context, status='available',
            size=2,
            encryption_key_id=fake.ENCRYPTION_KEY_ID)
        volume_id = volume['id']
        volume = db.volume_get(context.get_admin_context(), volume_id)
        image_service = fake_image.FakeImageService()
        local_path = 'dev/sda'
        attach_info = {'device': {'path': local_path},
                       'conn': {'driver_volume_type': 'iscsi',
                                'data': {}, }}

        mock_get_connector_properties.return_value = properties
        mock_attach_volume.return_value = [attach_info, volume]

        self.volume.driver.copy_image_to_encrypted_volume(
            self.context, volume, image_service, fake.IMAGE_ID)

        encryption = {'encryption_key_id': fake.ENCRYPTION_KEY_ID}
        mock_attach_volume.assert_called_once_with(
            self.context, volume, properties)
        mock_attach_encryptor.assert_called_once_with(
            self.context, attach_info, encryption)
        mock_fetch_to_raw.assert_called_once_with(
            self.context, image_service, fake.IMAGE_ID,
            local_path, '1M', size=2)
        mock_detach_encryptor.assert_called_once_with(
            attach_info, encryption)
        mock_detach_volume.assert_called_once_with(
            self.context, attach_info, volume, properties, force=True)

    @mock.patch.object(os_brick.initiator.connector,
                       'get_connector_properties')
    @mock.patch.object(image_utils, 'fetch_to_raw')
    @mock.patch.object(cinder.volume.driver.VolumeDriver, '_attach_volume')
    @mock.patch.object(cinder.volume.driver.VolumeDriver, '_detach_volume')
    @mock.patch.object(cinder.utils, 'brick_attach_volume_encryptor')
    @mock.patch.object(cinder.utils, 'brick_detach_volume_encryptor')
    def test_copy_image_to_encrypted_volume_failed_attach_encryptor(
            self,
            mock_detach_encryptor,
            mock_attach_encryptor,
            mock_detach_volume,
            mock_attach_volume,
            mock_fetch_to_raw,
            mock_get_connector_properties):
        properties = {}
        volume = tests_utils.create_volume(
            self.context, status='available',
            size=2,
            encryption_key_id=fake.ENCRYPTION_KEY_ID)
        volume_id = volume['id']
        volume = db.volume_get(context.get_admin_context(), volume_id)
        image_service = fake_image.FakeImageService()
        attach_info = {'device': {'path': 'dev/sda'},
                       'conn': {'driver_volume_type': 'iscsi',
                                'data': {}, }}

        mock_get_connector_properties.return_value = properties
        mock_attach_volume.return_value = [attach_info, volume]
        raised_exception = os_brick.exception.VolumeEncryptionNotSupported(
            volume_id = "123",
            volume_type = "abc")
        mock_attach_encryptor.side_effect = raised_exception

        self.assertRaises(os_brick.exception.VolumeEncryptionNotSupported,
                          self.volume.driver.copy_image_to_encrypted_volume,
                          self.context, volume, image_service, fake.IMAGE_ID)

        encryption = {'encryption_key_id': fake.ENCRYPTION_KEY_ID}
        mock_attach_volume.assert_called_once_with(
            self.context, volume, properties)
        mock_attach_encryptor.assert_called_once_with(
            self.context, attach_info, encryption)
        self.assertFalse(mock_fetch_to_raw.called)
        self.assertFalse(mock_detach_encryptor.called)
        mock_detach_volume.assert_called_once_with(
            self.context, attach_info, volume, properties, force=True)

    @mock.patch.object(os_brick.initiator.connector,
                       'get_connector_properties')
    @mock.patch.object(image_utils, 'fetch_to_raw')
    @mock.patch.object(cinder.volume.driver.VolumeDriver, '_attach_volume')
    @mock.patch.object(cinder.volume.driver.VolumeDriver, '_detach_volume')
    @mock.patch.object(cinder.utils, 'brick_attach_volume_encryptor')
    @mock.patch.object(cinder.utils, 'brick_detach_volume_encryptor')
    @ddt.data(exception.ImageUnacceptable(
              reason='fake', image_id=fake.IMAGE_ID),
              exception.ImageTooBig(
              reason='fake image size exceeded', image_id=fake.IMAGE_ID))
    def test_copy_image_to_encrypted_volume_failed_fetch(
            self, excep,
            mock_detach_encryptor, mock_attach_encryptor,
            mock_detach_volume, mock_attach_volume, mock_fetch_to_raw,
            mock_get_connector_properties):
        properties = {}
        volume = tests_utils.create_volume(
            self.context, status='available',
            size=2,
            encryption_key_id=fake.ENCRYPTION_KEY_ID)
        volume_id = volume['id']
        volume = db.volume_get(context.get_admin_context(), volume_id)
        image_service = fake_image.FakeImageService()
        local_path = 'dev/sda'
        attach_info = {'device': {'path': local_path},
                       'conn': {'driver_volume_type': 'iscsi',
                                'data': {}, }}

        mock_get_connector_properties.return_value = properties
        mock_attach_volume.return_value = [attach_info, volume]
        mock_fetch_to_raw.side_effect = excep

        encryption = {'encryption_key_id': fake.ENCRYPTION_KEY_ID}
        self.assertRaises(type(excep),
                          self.volume.driver.copy_image_to_encrypted_volume,
                          self.context, volume, image_service, fake.IMAGE_ID)

        mock_attach_volume.assert_called_once_with(
            self.context, volume, properties)
        mock_attach_encryptor.assert_called_once_with(
            self.context, attach_info, encryption)
        mock_fetch_to_raw.assert_called_once_with(
            self.context, image_service, fake.IMAGE_ID,
            local_path, '1M', size=2)
        mock_detach_encryptor.assert_called_once_with(
            attach_info, encryption)
        mock_detach_volume.assert_called_once_with(
            self.context, attach_info, volume, properties, force=True)

    @mock.patch('cinder.volume.driver.brick_exception')
    @mock.patch('cinder.tests.fake_driver.FakeLoggingVolumeDriver.'
                'terminate_connection', side_effect=Exception)
    @mock.patch('cinder.tests.fake_driver.FakeLoggingVolumeDriver.'
                'remove_export', side_effect=Exception)
    def test_detach_volume_force(self, remove_mock, terminate_mock, exc_mock):
        """Test force parameter on _detach_volume.

        On the driver if we receive the force parameter we will do everything
        even with Exceptions on disconnect, terminate, and remove export.
        """
        connector = mock.Mock()
        connector.disconnect_volume.side_effect = Exception
        # TODO(geguileo): Remove this ExceptionChainer simulation once we
        # release OS-Brick version with it and bump min version.
        exc = exc_mock.ExceptionChainer.return_value
        exc.context.return_value.__enter__.return_value = exc
        exc.context.return_value.__exit__.return_value = True

        volume = {'id': fake.VOLUME_ID}
        attach_info = {'device': {},
                       'connector': connector,
                       'conn': {'data': {}, }}

        # TODO(geguileo): Change TypeError to ExceptionChainer once we release
        # OS-Brick version with it and bump min version.
        self.assertRaises(TypeError,
                          self.volume.driver._detach_volume, self.context,
                          attach_info, volume, {}, force=True)

        self.assertTrue(connector.disconnect_volume.called)
        self.assertTrue(remove_mock.called)
        self.assertTrue(terminate_mock.called)
        self.assertEqual(3, exc.context.call_count)

    @ddt.data({'cfg_value': '10', 'valid': True},
              {'cfg_value': 'auto', 'valid': True},
              {'cfg_value': '1', 'valid': True},
              {'cfg_value': '1.2', 'valid': True},
              {'cfg_value': '100', 'valid': True},
              {'cfg_value': '20.15', 'valid': True},
              {'cfg_value': 'True', 'valid': False},
              {'cfg_value': 'False', 'valid': False},
              {'cfg_value': '10.0.0', 'valid': False},
              {'cfg_value': '0.00', 'valid': True},
              {'cfg_value': 'anything', 'valid': False},)
    @ddt.unpack
    def test_auto_max_subscription_ratio_options(self, cfg_value, valid):
        # This tests the max_over_subscription_ratio option as it is now
        # checked by a regex
        def _set_conf(config, value):
            config.set_override('max_over_subscription_ratio', value)

        config = conf.Configuration(None)
        config.append_config_values(driver.volume_opts)

        if valid:
            _set_conf(config, cfg_value)
            self.assertEqual(cfg_value, config.safe_get(
                'max_over_subscription_ratio'))
        else:
            self.assertRaises(ValueError, _set_conf, config, cfg_value)
コード例 #7
0
ファイル: test_image.py プロジェクト: lubidl0/cinder-1
 def fake_copy_image_to_volume(context, volume, image_service,
                               image_id):
     raise exception.ImageTooBig(image_id=image_id, reason='')
コード例 #8
0
class GenericVolumeDriverTestCase(BaseDriverTestCase):
    """Test case for VolumeDriver."""
    driver_name = "cinder.tests.fake_driver.FakeLoggingVolumeDriver"

    def test_create_temp_cloned_volume(self):
        with mock.patch.object(
                self.volume.driver,
                'create_cloned_volume') as mock_create_cloned_volume:
            model_update = {'provider_location': 'dummy'}
            mock_create_cloned_volume.return_value = model_update
            vol = tests_utils.create_volume(self.context,
                                            status='backing-up')
            cloned_vol = self.volume.driver._create_temp_cloned_volume(
                self.context, vol)
            self.assertEqual('dummy', cloned_vol.provider_location)
            self.assertEqual('available', cloned_vol.status)

            mock_create_cloned_volume.return_value = None
            vol = tests_utils.create_volume(self.context,
                                            status='backing-up')
            cloned_vol = self.volume.driver._create_temp_cloned_volume(
                self.context, vol)
            self.assertEqual('available', cloned_vol.status)

    def test_get_backup_device_available(self):
        vol = tests_utils.create_volume(self.context)
        self.context.user_id = fake.USER_ID
        self.context.project_id = fake.PROJECT_ID
        backup_obj = tests_utils.create_backup(self.context,
                                               vol['id'])
        (backup_device, is_snapshot) = self.volume.driver.get_backup_device(
            self.context, backup_obj)
        volume = objects.Volume.get_by_id(self.context, vol.id)
        self.assertEqual(volume, backup_device)
        self.assertFalse(is_snapshot)
        backup_obj.refresh()
        self.assertIsNone(backup_obj.temp_volume_id)

    def test_get_backup_device_in_use(self):
        vol = tests_utils.create_volume(self.context,
                                        status='backing-up',
                                        previous_status='in-use')
        temp_vol = tests_utils.create_volume(self.context)
        self.context.user_id = fake.USER_ID
        self.context.project_id = fake.PROJECT_ID
        backup_obj = tests_utils.create_backup(self.context,
                                               vol['id'])
        with mock.patch.object(
                self.volume.driver,
                '_create_temp_cloned_volume') as mock_create_temp:
            mock_create_temp.return_value = temp_vol
            (backup_device, is_snapshot) = (
                self.volume.driver.get_backup_device(self.context,
                                                     backup_obj))
            self.assertEqual(temp_vol, backup_device)
            self.assertFalse(is_snapshot)
            backup_obj.refresh()
            self.assertEqual(temp_vol.id, backup_obj.temp_volume_id)

    def test__create_temp_volume_from_snapshot(self):
        volume_dict = {'id': fake.SNAPSHOT_ID,
                       'host': 'fakehost',
                       'cluster_name': 'fakecluster',
                       'availability_zone': 'fakezone',
                       'size': 1}
        vol = fake_volume.fake_volume_obj(self.context, **volume_dict)
        snapshot = fake_snapshot.fake_snapshot_obj(self.context)

        with mock.patch.object(
                self.volume.driver,
                'create_volume_from_snapshot'):
            temp_vol = self.volume.driver._create_temp_volume_from_snapshot(
                self.context,
                vol, snapshot)
            self.assertEqual(fields.VolumeAttachStatus.DETACHED,
                             temp_vol.attach_status)
            self.assertEqual('fakezone', temp_vol.availability_zone)
            self.assertEqual('fakecluster', temp_vol.cluster_name)

    @mock.patch.object(utils, 'brick_get_connector_properties')
    @mock.patch.object(cinder.volume.manager.VolumeManager, '_attach_volume')
    @mock.patch.object(cinder.volume.manager.VolumeManager, '_detach_volume')
    @mock.patch.object(volutils, 'copy_volume')
    @mock.patch.object(volume_rpcapi.VolumeAPI, 'get_capabilities')
    @mock.patch.object(cinder.volume.volume_types,
                       'volume_types_encryption_changed')
    @ddt.data(False, True)
    def test_copy_volume_data_mgr(self,
                                  encryption_changed,
                                  mock_encryption_changed,
                                  mock_get_capabilities,
                                  mock_copy,
                                  mock_detach,
                                  mock_attach,
                                  mock_get_connector):
        """Test function of _copy_volume_data."""

        src_vol = tests_utils.create_volume(self.context, size=1,
                                            host=CONF.host)
        dest_vol = tests_utils.create_volume(self.context, size=1,
                                             host=CONF.host)
        mock_get_connector.return_value = {}
        mock_encryption_changed.return_value = encryption_changed
        self.volume.driver._throttle = mock.MagicMock()

        attach_expected = [
            mock.call(self.context, dest_vol, {},
                      remote=False,
                      attach_encryptor=encryption_changed),
            mock.call(self.context, src_vol, {},
                      remote=False,
                      attach_encryptor=encryption_changed)]

        detach_expected = [
            mock.call(self.context, {'device': {'path': 'bar'}},
                      dest_vol, {}, force=True, remote=False,
                      attach_encryptor=encryption_changed),
            mock.call(self.context, {'device': {'path': 'foo'}},
                      src_vol, {}, force=True, remote=False,
                      attach_encryptor=encryption_changed)]

        attach_volume_returns = [
            {'device': {'path': 'bar'}},
            {'device': {'path': 'foo'}}
        ]

        #  Test case for sparse_copy_volume = False
        mock_attach.side_effect = attach_volume_returns
        mock_get_capabilities.return_value = {}
        self.volume._copy_volume_data(self.context,
                                      src_vol,
                                      dest_vol)

        self.assertEqual(attach_expected, mock_attach.mock_calls)
        mock_copy.assert_called_with('foo', 'bar', 1024, '1M', sparse=False)
        self.assertEqual(detach_expected, mock_detach.mock_calls)

        #  Test case for sparse_copy_volume = True
        mock_attach.reset_mock()
        mock_detach.reset_mock()
        mock_attach.side_effect = attach_volume_returns
        mock_get_capabilities.return_value = {'sparse_copy_volume': True}
        self.volume._copy_volume_data(self.context,
                                      src_vol,
                                      dest_vol)

        self.assertEqual(attach_expected, mock_attach.mock_calls)
        mock_copy.assert_called_with('foo', 'bar', 1024, '1M', sparse=True)
        self.assertEqual(detach_expected, mock_detach.mock_calls)

        # cleanup resource
        db.volume_destroy(self.context, src_vol['id'])
        db.volume_destroy(self.context, dest_vol['id'])

    @mock.patch.object(os_brick.initiator.connector,
                       'get_connector_properties')
    @mock.patch.object(image_utils, 'fetch_to_raw')
    @mock.patch.object(cinder.volume.driver.VolumeDriver, '_attach_volume')
    @mock.patch.object(cinder.volume.driver.VolumeDriver, '_detach_volume')
    @mock.patch.object(cinder.utils, 'brick_attach_volume_encryptor')
    @mock.patch.object(cinder.utils, 'brick_detach_volume_encryptor')
    def test_copy_image_to_encrypted_volume(self,
                                            mock_detach_encryptor,
                                            mock_attach_encryptor,
                                            mock_detach_volume,
                                            mock_attach_volume,
                                            mock_fetch_to_raw,
                                            mock_get_connector_properties):
        properties = {}
        volume = tests_utils.create_volume(
            self.context, status='available',
            size=2,
            encryption_key_id=fake.ENCRYPTION_KEY_ID)
        volume_id = volume['id']
        volume = db.volume_get(context.get_admin_context(), volume_id)
        image_service = fake_image.FakeImageService()
        local_path = 'dev/sda'
        attach_info = {'device': {'path': local_path},
                       'conn': {'driver_volume_type': 'iscsi',
                                'data': {}, }}

        mock_get_connector_properties.return_value = properties
        mock_attach_volume.return_value = [attach_info, volume]

        self.volume.driver.copy_image_to_encrypted_volume(
            self.context, volume, image_service, fake.IMAGE_ID)

        encryption = {'encryption_key_id': fake.ENCRYPTION_KEY_ID}
        mock_attach_volume.assert_called_once_with(
            self.context, volume, properties)
        mock_attach_encryptor.assert_called_once_with(
            self.context, attach_info, encryption)
        mock_fetch_to_raw.assert_called_once_with(
            self.context, image_service, fake.IMAGE_ID,
            local_path, '1M', size=2)
        mock_detach_encryptor.assert_called_once_with(
            attach_info, encryption)
        mock_detach_volume.assert_called_once_with(
            self.context, attach_info, volume, properties, force=True)

    @mock.patch.object(os_brick.initiator.connector,
                       'get_connector_properties')
    @mock.patch.object(image_utils, 'fetch_to_raw')
    @mock.patch.object(cinder.volume.driver.VolumeDriver, '_attach_volume')
    @mock.patch.object(cinder.volume.driver.VolumeDriver, '_detach_volume')
    @mock.patch.object(cinder.utils, 'brick_attach_volume_encryptor')
    @mock.patch.object(cinder.utils, 'brick_detach_volume_encryptor')
    def test_copy_image_to_encrypted_volume_failed_attach_encryptor(
            self,
            mock_detach_encryptor,
            mock_attach_encryptor,
            mock_detach_volume,
            mock_attach_volume,
            mock_fetch_to_raw,
            mock_get_connector_properties):
        properties = {}
        volume = tests_utils.create_volume(
            self.context, status='available',
            size=2,
            encryption_key_id=fake.ENCRYPTION_KEY_ID)
        volume_id = volume['id']
        volume = db.volume_get(context.get_admin_context(), volume_id)
        image_service = fake_image.FakeImageService()
        attach_info = {'device': {'path': 'dev/sda'},
                       'conn': {'driver_volume_type': 'iscsi',
                                'data': {}, }}

        mock_get_connector_properties.return_value = properties
        mock_attach_volume.return_value = [attach_info, volume]
        raised_exception = os_brick.exception.VolumeEncryptionNotSupported(
            volume_id = "123",
            volume_type = "abc")
        mock_attach_encryptor.side_effect = raised_exception

        self.assertRaises(os_brick.exception.VolumeEncryptionNotSupported,
                          self.volume.driver.copy_image_to_encrypted_volume,
                          self.context, volume, image_service, fake.IMAGE_ID)

        encryption = {'encryption_key_id': fake.ENCRYPTION_KEY_ID}
        mock_attach_volume.assert_called_once_with(
            self.context, volume, properties)
        mock_attach_encryptor.assert_called_once_with(
            self.context, attach_info, encryption)
        self.assertFalse(mock_fetch_to_raw.called)
        self.assertFalse(mock_detach_encryptor.called)
        mock_detach_volume.assert_called_once_with(
            self.context, attach_info, volume, properties, force=True)

    @mock.patch.object(os_brick.initiator.connector,
                       'get_connector_properties')
    @mock.patch.object(image_utils, 'fetch_to_raw')
    @mock.patch.object(cinder.volume.driver.VolumeDriver, '_attach_volume')
    @mock.patch.object(cinder.volume.driver.VolumeDriver, '_detach_volume')
    @mock.patch.object(cinder.utils, 'brick_attach_volume_encryptor')
    @mock.patch.object(cinder.utils, 'brick_detach_volume_encryptor')
    @ddt.data(exception.ImageUnacceptable(
              reason='fake', image_id=fake.IMAGE_ID),
              exception.ImageTooBig(
              reason='fake image size exceeded', image_id=fake.IMAGE_ID))
    def test_copy_image_to_encrypted_volume_failed_fetch(
            self, excep,
            mock_detach_encryptor, mock_attach_encryptor,
            mock_detach_volume, mock_attach_volume, mock_fetch_to_raw,
            mock_get_connector_properties):
        properties = {}
        volume = tests_utils.create_volume(
            self.context, status='available',
            size=2,
            encryption_key_id=fake.ENCRYPTION_KEY_ID)
        volume_id = volume['id']
        volume = db.volume_get(context.get_admin_context(), volume_id)
        image_service = fake_image.FakeImageService()
        local_path = 'dev/sda'
        attach_info = {'device': {'path': local_path},
                       'conn': {'driver_volume_type': 'iscsi',
                                'data': {}, }}

        mock_get_connector_properties.return_value = properties
        mock_attach_volume.return_value = [attach_info, volume]
        mock_fetch_to_raw.side_effect = excep

        encryption = {'encryption_key_id': fake.ENCRYPTION_KEY_ID}
        self.assertRaises(type(excep),
                          self.volume.driver.copy_image_to_encrypted_volume,
                          self.context, volume, image_service, fake.IMAGE_ID)

        mock_attach_volume.assert_called_once_with(
            self.context, volume, properties)
        mock_attach_encryptor.assert_called_once_with(
            self.context, attach_info, encryption)
        mock_fetch_to_raw.assert_called_once_with(
            self.context, image_service, fake.IMAGE_ID,
            local_path, '1M', size=2)
        mock_detach_encryptor.assert_called_once_with(
            attach_info, encryption)
        mock_detach_volume.assert_called_once_with(
            self.context, attach_info, volume, properties, force=True)

    @mock.patch('cinder.volume.driver.brick_exception')
    @mock.patch('cinder.tests.fake_driver.FakeLoggingVolumeDriver.'
                'terminate_connection', side_effect=Exception)
    @mock.patch('cinder.tests.fake_driver.FakeLoggingVolumeDriver.'
                'remove_export', side_effect=Exception)
    def test_detach_volume_force(self, remove_mock, terminate_mock, exc_mock):
        """Test force parameter on _detach_volume.

        On the driver if we receive the force parameter we will do everything
        even with Exceptions on disconnect, terminate, and remove export.
        """
        connector = mock.Mock()
        connector.disconnect_volume.side_effect = Exception
        # TODO(geguileo): Remove this ExceptionChainer simulation once we
        # release OS-Brick version with it and bump min version.
        exc = exc_mock.ExceptionChainer.return_value
        exc.context.return_value.__enter__.return_value = exc
        exc.context.return_value.__exit__.return_value = True

        volume = {'id': fake.VOLUME_ID}
        attach_info = {'device': {},
                       'connector': connector,
                       'conn': {'data': {}, }}

        # TODO(geguileo): Change TypeError to ExceptionChainer once we release
        # OS-Brick version with it and bump min version.
        self.assertRaises(TypeError,
                          self.volume.driver._detach_volume, self.context,
                          attach_info, volume, {}, force=True)

        self.assertTrue(connector.disconnect_volume.called)
        self.assertTrue(remove_mock.called)
        self.assertTrue(terminate_mock.called)
        self.assertEqual(3, exc.context.call_count)