Exemple #1
0
    def disassociate(self, req, id):
        """Disassociate a qos specs from a volume type."""
        context = req.environ["cinder.context"]
        authorize(context)

        type_id = req.params.get("vol_type_id", None)

        if not type_id:
            msg = _("Volume Type id must not be None.")
            notifier_err = dict(id=id, error_message=msg)
            self._notify_qos_specs_error(context, "qos_specs.delete", notifier_err)
            raise webob.exc.HTTPBadRequest(explanation=msg)
        LOG.debug("Disassociate qos_spec: %(id)s from type: %(type_id)s" % {"id": id, "type_id": type_id})

        try:
            qos_specs.disassociate_qos_specs(context, id, type_id)
            notifier_info = dict(id=id, type_id=type_id)
            notifier_api.notify(context, "QoSSpecs", "qos_specs.disassociate", notifier_api.INFO, notifier_info)
        except exception.VolumeTypeNotFound as err:
            notifier_err = dict(id=id, error_message=str(err))
            self._notify_qos_specs_error(context, "qos_specs.disassociate", notifier_err)
            raise webob.exc.HTTPNotFound(explanation=str(err))
        except exception.QoSSpecsNotFound as err:
            notifier_err = dict(id=id, error_message=str(err))
            self._notify_qos_specs_error(context, "qos_specs.disassociate", notifier_err)
            raise webob.exc.HTTPNotFound(explanation=str(err))
        except exception.QoSSpecsDisassociateFailed as err:
            notifier_err = dict(id=id, error_message=str(err))
            self._notify_qos_specs_error(context, "qos_specs.disassociate", notifier_err)
            raise webob.exc.HTTPInternalServerError(explanation=str(err))

        return webob.Response(status_int=202)
Exemple #2
0
    def test_disassociate_qos_specs(self):
        def fake_db_disassociate(context, id, type_id):
            raise db_exc.DBError()

        type_ref = volume_types.create(self.ctxt, 'TypeName')
        specs_id = self._create_qos_specs('QoSName')

        qos_specs.associate_qos_with_type(self.ctxt, specs_id, type_ref['id'])
        res = qos_specs.get_associations(self.ctxt, specs_id)
        self.assertEqual(1, len(res))

        qos_specs.disassociate_qos_specs(self.ctxt, specs_id, type_ref['id'])
        res = qos_specs.get_associations(self.ctxt, specs_id)
        self.assertEqual(0, len(res))

        self.assertRaises(exception.VolumeTypeNotFound,
                          qos_specs.disassociate_qos_specs, self.ctxt,
                          specs_id, 'NotFound')

        # Verify we can disassociate specs from volume_type even if they are
        # not associated with no error
        qos_specs.disassociate_qos_specs(self.ctxt, specs_id, type_ref['id'])
        qos_specs.associate_qos_with_type(self.ctxt, specs_id, type_ref['id'])
        self.stubs.Set(db, 'qos_specs_disassociate', fake_db_disassociate)
        self.assertRaises(exception.QoSSpecsDisassociateFailed,
                          qos_specs.disassociate_qos_specs, self.ctxt,
                          specs_id, type_ref['id'])
Exemple #3
0
    def test_disassociate_qos_specs(self):
        def fake_qos_specs_get(context, id):
            if id == 'NotFound':
                raise exception.QoSSpecsNotFound(specs_id=id)
            else:
                pass

        def fake_db_disassociate(context, id, type_id):
            if id == 'Trouble':
                raise db_exc.DBError()
            elif type_id == 'NotFound':
                raise exception.VolumeTypeNotFound(volume_type_id=type_id)
            pass

        type_ref = volume_types.create(self.ctxt, 'TypeName')
        specs_id = self._create_qos_specs('QoSName')

        qos_specs.associate_qos_with_type(self.ctxt, specs_id, type_ref['id'])
        res = qos_specs.get_associations(self.ctxt, specs_id)
        self.assertEqual(len(res), 1)

        qos_specs.disassociate_qos_specs(self.ctxt, specs_id, type_ref['id'])
        res = qos_specs.get_associations(self.ctxt, specs_id)
        self.assertEqual(len(res), 0)

        self.stubs.Set(db, 'qos_specs_disassociate', fake_db_disassociate)
        self.stubs.Set(qos_specs, 'get_qos_specs', fake_qos_specs_get)
        self.assertRaises(exception.VolumeTypeNotFound,
                          qos_specs.disassociate_qos_specs, self.ctxt,
                          'specs-id', 'NotFound')
        self.assertRaises(exception.QoSSpecsDisassociateFailed,
                          qos_specs.disassociate_qos_specs, self.ctxt,
                          'Trouble', 'id')
    def test_disassociate_qos_specs(self):
        def fake_db_disassociate(context, id, type_id):
            raise db_exc.DBError()

        type_ref = volume_types.create(self.ctxt, 'TypeName')
        specs_id = self._create_qos_specs('QoSName')

        qos_specs.associate_qos_with_type(self.ctxt, specs_id,
                                          type_ref['id'])
        res = qos_specs.get_associations(self.ctxt, specs_id)
        self.assertEqual(1, len(res))

        qos_specs.disassociate_qos_specs(self.ctxt, specs_id, type_ref['id'])
        res = qos_specs.get_associations(self.ctxt, specs_id)
        self.assertEqual(0, len(res))

        self.assertRaises(exception.VolumeTypeNotFound,
                          qos_specs.disassociate_qos_specs,
                          self.ctxt, specs_id, 'NotFound')

        # Verify we can disassociate specs from volume_type even if they are
        # not associated with no error
        qos_specs.disassociate_qos_specs(self.ctxt, specs_id, type_ref['id'])
        qos_specs.associate_qos_with_type(self.ctxt, specs_id, type_ref['id'])
        self.mock_object(db, 'qos_specs_disassociate',
                         fake_db_disassociate)
        self.assertRaises(exception.QoSSpecsDisassociateFailed,
                          qos_specs.disassociate_qos_specs,
                          self.ctxt, specs_id, type_ref['id'])
Exemple #5
0
    def test_disassociate_qos_specs(self):
        def fake_db_disassociate(context, id, type_id):
            if id == 'Trouble':
                raise db_exc.DBError()
            elif type_id == 'NotFound':
                raise exception.VolumeTypeNotFound(volume_type_id=type_id)
            pass

        type_ref = volume_types.create(self.ctxt, 'TypeName')
        specs_id = self._create_qos_specs('QoSName')

        qos_specs.associate_qos_with_type(self.ctxt, specs_id,
                                          type_ref['id'])
        res = qos_specs.get_associations(self.ctxt, specs_id)
        self.assertEquals(len(res[specs_id].keys()), 1)

        qos_specs.disassociate_qos_specs(self.ctxt, specs_id, type_ref['id'])
        res = qos_specs.get_associations(self.ctxt, specs_id)
        self.assertEquals(len(res[specs_id].keys()), 0)

        self.stubs.Set(db, 'qos_specs_disassociate',
                       fake_db_disassociate)
        self.assertRaises(exception.VolumeTypeNotFound,
                          qos_specs.disassociate_qos_specs,
                          self.ctxt, 'specs-id', 'NotFound')
        self.assertRaises(exception.QoSSpecsDisassociateFailed,
                          qos_specs.disassociate_qos_specs,
                          self.ctxt, 'Trouble', 'id')
Exemple #6
0
    def disassociate(self, req, id):
        """Disassociate a qos specs from a volume type."""
        context = req.environ['cinder.context']
        authorize(context)

        type_id = req.params.get('vol_type_id', None)

        if not type_id:
            msg = _('Volume Type id must not be None.')
            notifier_err = dict(id=id, error_message=msg)
            self._notify_qos_specs_error(context,
                                         'qos_specs.delete',
                                         notifier_err)
            raise webob.exc.HTTPBadRequest(explanation=msg)
        LOG.debug("Disassociate qos_spec: %(id)s from type: %(type_id)s" %
                  {'id': id, 'type_id': type_id})

        try:
            qos_specs.disassociate_qos_specs(context, id, type_id)
            notifier_info = dict(id=id, type_id=type_id)
            rpc.get_notifier('QoSSpecs').info(context,
                                              'qos_specs.disassociate',
                                              notifier_info)
        except exception.VolumeTypeNotFound as err:
            notifier_err = dict(id=id, error_message=err)
            self._notify_qos_specs_error(context,
                                         'qos_specs.disassociate',
                                         notifier_err)
            raise webob.exc.HTTPNotFound(explanation=six.text_type(err))
        except exception.QoSSpecsNotFound as err:
            notifier_err = dict(id=id, error_message=err)
            self._notify_qos_specs_error(context,
                                         'qos_specs.disassociate',
                                         notifier_err)
            raise webob.exc.HTTPNotFound(explanation=six.text_type(err))
        except exception.QoSSpecsDisassociateFailed as err:
            notifier_err = dict(id=id, error_message=err)
            self._notify_qos_specs_error(context,
                                         'qos_specs.disassociate',
                                         notifier_err)
            raise webob.exc.HTTPInternalServerError(
                explanation=six.text_type(err))

        return webob.Response(status_int=202)
Exemple #7
0
    def disassociate(self, req, id):
        """Disassociate a qos specs from a volume type."""
        context = req.environ['cinder.context']
        authorize(context)

        type_id = req.params.get('vol_type_id', None)

        if not type_id:
            msg = _('Volume Type id must not be None.')
            notifier_err = dict(id=id, error_message=msg)
            self._notify_qos_specs_error(context,
                                         'qos_specs.delete',
                                         notifier_err)
            raise webob.exc.HTTPBadRequest(explanation=msg)
        LOG.debug("Disassociate qos_spec: %(id)s from type: %(type_id)s",
                  {'id': id, 'type_id': type_id})

        try:
            qos_specs.disassociate_qos_specs(context, id, type_id)
            notifier_info = dict(id=id, type_id=type_id)
            rpc.get_notifier('QoSSpecs').info(context,
                                              'qos_specs.disassociate',
                                              notifier_info)
        except exception.VolumeTypeNotFound as err:
            notifier_err = dict(id=id, error_message=err)
            self._notify_qos_specs_error(context,
                                         'qos_specs.disassociate',
                                         notifier_err)
            raise webob.exc.HTTPNotFound(explanation=six.text_type(err))
        except exception.QoSSpecsNotFound as err:
            notifier_err = dict(id=id, error_message=err)
            self._notify_qos_specs_error(context,
                                         'qos_specs.disassociate',
                                         notifier_err)
            raise webob.exc.HTTPNotFound(explanation=six.text_type(err))
        except exception.QoSSpecsDisassociateFailed as err:
            notifier_err = dict(id=id, error_message=err)
            self._notify_qos_specs_error(context,
                                         'qos_specs.disassociate',
                                         notifier_err)
            raise webob.exc.HTTPInternalServerError(
                explanation=six.text_type(err))

        return webob.Response(status_int=202)
Exemple #8
0
    def disassociate(self, req, id):
        """Disassociate a qos specs from a volume type."""
        context = req.environ['cinder.context']
        context.authorize(policy.UPDATE_POLICY)

        type_id = req.params.get('vol_type_id', None)

        if not type_id:
            msg = _('Volume Type id must not be None.')
            notifier_err = dict(id=id, error_message=msg)
            self._notify_qos_specs_error(context, 'qos_specs.delete',
                                         notifier_err)
            raise webob.exc.HTTPBadRequest(explanation=msg)
        LOG.debug("Disassociate qos_spec: %(id)s from type: %(type_id)s", {
            'id': id,
            'type_id': type_id
        })

        try:
            spec = qos_specs.get_qos_specs(context, id)

            qos_specs.disassociate_qos_specs(context, id, type_id)
            notifier_info = dict(id=id,
                                 type_id=type_id,
                                 created_at=spec.created_at)
            rpc.get_notifier('QoSSpecs').info(context,
                                              'qos_specs.disassociate',
                                              notifier_info)
        except exception.NotFound as err:
            notifier_err = dict(id=id, error_message=err)
            self._notify_qos_specs_error(context, 'qos_specs.disassociate',
                                         notifier_err)
            # Not found exception will be handled at the wsgi level
            raise
        except exception.QoSSpecsDisassociateFailed as err:
            notifier_err = dict(id=id, error_message=err)
            self._notify_qos_specs_error(context, 'qos_specs.disassociate',
                                         notifier_err)
            raise webob.exc.HTTPInternalServerError(
                explanation=six.text_type(err))

        return webob.Response(status_int=http_client.ACCEPTED)
    def disassociate(self, req, id):
        """Disassociate a qos specs from a volume type."""
        context = req.environ['cinder.context']
        context.authorize(policy.UPDATE_POLICY)

        type_id = req.params.get('vol_type_id', None)

        if not type_id:
            msg = _('Volume Type id must not be None.')
            notifier_err = dict(id=id, error_message=msg)
            self._notify_qos_specs_error(context,
                                         'qos_specs.delete',
                                         notifier_err)
            raise webob.exc.HTTPBadRequest(explanation=msg)
        LOG.debug("Disassociate qos_spec: %(id)s from type: %(type_id)s",
                  {'id': id, 'type_id': type_id})

        try:
            spec = qos_specs.get_qos_specs(context, id)

            qos_specs.disassociate_qos_specs(context, id, type_id)
            notifier_info = dict(id=id, type_id=type_id,
                                 created_at=spec.created_at)
            rpc.get_notifier('QoSSpecs').info(context,
                                              'qos_specs.disassociate',
                                              notifier_info)
        except exception.NotFound as err:
            notifier_err = dict(id=id, error_message=err)
            self._notify_qos_specs_error(context,
                                         'qos_specs.disassociate',
                                         notifier_err)
            # Not found exception will be handled at the wsgi level
            raise
        except exception.QoSSpecsDisassociateFailed as err:
            notifier_err = dict(id=id, error_message=err)
            self._notify_qos_specs_error(context,
                                         'qos_specs.disassociate',
                                         notifier_err)
            raise webob.exc.HTTPInternalServerError(
                explanation=six.text_type(err))

        return webob.Response(status_int=http_client.ACCEPTED)
    def test_volume_types_diff(self):
        #type_ref 1 and 2 have the same extra_specs, while 3 has different
        keyvals1 = {"key1": "val1", "key2": "val2"}
        keyvals2 = {"key1": "val0", "key2": "val2"}
        type_ref1 = volume_types.create(self.ctxt, "type1", keyvals1)
        type_ref2 = volume_types.create(self.ctxt, "type2", keyvals1)
        type_ref3 = volume_types.create(self.ctxt, "type3", keyvals2)

        # Check equality with only extra_specs
        diff, same = volume_types.volume_types_diff(self.ctxt, type_ref1['id'],
                                                    type_ref2['id'])
        self.assertEqual(True, same)
        self.assertEqual(('val1', 'val1'), diff['extra_specs']['key1'])
        diff, same = volume_types.volume_types_diff(self.ctxt, type_ref1['id'],
                                                    type_ref3['id'])
        self.assertEqual(False, same)
        self.assertEqual(('val1', 'val0'), diff['extra_specs']['key1'])

        #qos_ref 1 and 2 have the same specs, while 3 has different
        qos_keyvals1 = {'k1': 'v1', 'k2': 'v2', 'k3': 'v3'}
        qos_keyvals2 = {'k1': 'v0', 'k2': 'v2', 'k3': 'v3'}
        qos_ref1 = qos_specs.create(self.ctxt, 'qos-specs-1', qos_keyvals1)
        qos_ref2 = qos_specs.create(self.ctxt, 'qos-specs-2', qos_keyvals1)
        qos_ref3 = qos_specs.create(self.ctxt, 'qos-specs-3', qos_keyvals2)

        # Check equality with qos specs too
        qos_specs.associate_qos_with_type(self.ctxt, qos_ref1['id'],
                                          type_ref1['id'])
        qos_specs.associate_qos_with_type(self.ctxt, qos_ref2['id'],
                                          type_ref2['id'])
        diff, same = volume_types.volume_types_diff(self.ctxt, type_ref1['id'],
                                                    type_ref2['id'])
        self.assertEqual(True, same)
        self.assertEqual(('val1', 'val1'), diff['extra_specs']['key1'])
        self.assertEqual(('v1', 'v1'), diff['qos_specs']['k1'])
        qos_specs.disassociate_qos_specs(self.ctxt, qos_ref2['id'],
                                         type_ref2['id'])
        qos_specs.associate_qos_with_type(self.ctxt, qos_ref3['id'],
                                          type_ref2['id'])
        diff, same = volume_types.volume_types_diff(self.ctxt, type_ref1['id'],
                                                    type_ref2['id'])
        self.assertEqual(False, same)
        self.assertEqual(('val1', 'val1'), diff['extra_specs']['key1'])
        self.assertEqual(('v1', 'v0'), diff['qos_specs']['k1'])
        qos_specs.disassociate_qos_specs(self.ctxt, qos_ref3['id'],
                                         type_ref2['id'])
        qos_specs.associate_qos_with_type(self.ctxt, qos_ref2['id'],
                                          type_ref2['id'])

        # And add encryption for good measure
        enc_keyvals1 = {'cipher': 'c1', 'key_size': 256, 'provider': 'p1',
                        'control_location': 'front-end'}
        enc_keyvals2 = {'cipher': 'c1', 'key_size': 128, 'provider': 'p1',
                        'control_location': 'front-end'}
        db.volume_type_encryption_create(self.ctxt, type_ref1['id'],
                                         enc_keyvals1)
        db.volume_type_encryption_create(self.ctxt, type_ref2['id'],
                                         enc_keyvals2)
        diff, same = volume_types.volume_types_diff(self.ctxt, type_ref1['id'],
                                                    type_ref2['id'])
        self.assertEqual(False, same)
        self.assertEqual(('val1', 'val1'), diff['extra_specs']['key1'])
        self.assertEqual(('v1', 'v1'), diff['qos_specs']['k1'])
        self.assertEqual((256, 128), diff['encryption']['key_size'])
    def test_volume_types_diff(self):
        # type_ref 1 and 2 have the same extra_specs, while 3 has different
        keyvals1 = {"key1": "val1", "key2": "val2"}
        keyvals2 = {"key1": "val0", "key2": "val2"}
        type_ref1 = volume_types.create(self.ctxt, "type1", keyvals1)
        type_ref2 = volume_types.create(self.ctxt, "type2", keyvals1)
        type_ref3 = volume_types.create(self.ctxt, "type3", keyvals2)

        # Check equality with only extra_specs
        diff, same = volume_types.volume_types_diff(self.ctxt, type_ref1['id'],
                                                    type_ref2['id'])
        self.assertTrue(same)
        self.assertEqual(('val1', 'val1'), diff['extra_specs']['key1'])
        diff, same = volume_types.volume_types_diff(self.ctxt, type_ref1['id'],
                                                    type_ref3['id'])
        self.assertFalse(same)
        self.assertEqual(('val1', 'val0'), diff['extra_specs']['key1'])

        # qos_ref 1 and 2 have the same specs, while 3 has different
        qos_keyvals1 = {'k1': 'v1', 'k2': 'v2', 'k3': 'v3'}
        qos_keyvals2 = {'k1': 'v0', 'k2': 'v2', 'k3': 'v3'}
        qos_ref1 = qos_specs.create(self.ctxt, 'qos-specs-1', qos_keyvals1)
        qos_ref2 = qos_specs.create(self.ctxt, 'qos-specs-2', qos_keyvals1)
        qos_ref3 = qos_specs.create(self.ctxt, 'qos-specs-3', qos_keyvals2)

        # Check equality with qos specs too
        qos_specs.associate_qos_with_type(self.ctxt, qos_ref1['id'],
                                          type_ref1['id'])
        qos_specs.associate_qos_with_type(self.ctxt, qos_ref2['id'],
                                          type_ref2['id'])
        diff, same = volume_types.volume_types_diff(self.ctxt, type_ref1['id'],
                                                    type_ref2['id'])
        self.assertTrue(same)
        self.assertEqual(('val1', 'val1'), diff['extra_specs']['key1'])
        self.assertEqual(('v1', 'v1'), diff['qos_specs']['k1'])
        qos_specs.disassociate_qos_specs(self.ctxt, qos_ref2['id'],
                                         type_ref2['id'])
        qos_specs.associate_qos_with_type(self.ctxt, qos_ref3['id'],
                                          type_ref2['id'])
        diff, same = volume_types.volume_types_diff(self.ctxt, type_ref1['id'],
                                                    type_ref2['id'])
        self.assertFalse(same)
        self.assertEqual(('val1', 'val1'), diff['extra_specs']['key1'])
        self.assertEqual(('v1', 'v0'), diff['qos_specs']['k1'])
        qos_specs.disassociate_qos_specs(self.ctxt, qos_ref3['id'],
                                         type_ref2['id'])
        qos_specs.associate_qos_with_type(self.ctxt, qos_ref2['id'],
                                          type_ref2['id'])

        # And add encryption for good measure
        enc_keyvals1 = {'cipher': 'c1', 'key_size': 256, 'provider': 'p1',
                        'control_location': 'front-end',
                        'encryption_id': 'uuid1'}
        enc_keyvals2 = {'cipher': 'c1', 'key_size': 128, 'provider': 'p1',
                        'control_location': 'front-end',
                        'encryption_id': 'uuid2'}
        db.volume_type_encryption_create(self.ctxt, type_ref1['id'],
                                         enc_keyvals1)
        db.volume_type_encryption_create(self.ctxt, type_ref2['id'],
                                         enc_keyvals2)
        diff, same = volume_types.volume_types_diff(self.ctxt, type_ref1['id'],
                                                    type_ref2['id'])
        self.assertFalse(same)
        self.assertEqual(('val1', 'val1'), diff['extra_specs']['key1'])
        self.assertEqual(('v1', 'v1'), diff['qos_specs']['k1'])
        self.assertEqual((256, 128), diff['encryption']['key_size'])

        # Check diff equals type specs when one type is None
        diff, same = volume_types.volume_types_diff(self.ctxt, None,
                                                    type_ref1['id'])
        self.assertFalse(same)
        self.assertEqual({'key1': (None, 'val1'), 'key2': (None, 'val2')},
                         diff['extra_specs'])
        self.assertEqual({'consumer': (None, 'back-end'),
                          'k1': (None, 'v1'),
                          'k2': (None, 'v2'),
                          'k3': (None, 'v3')}, diff['qos_specs'])
        self.assertEqual({'cipher': (None, 'c1'),
                          'control_location': (None, 'front-end'),
                          'deleted': (None, False),
                          'key_size': (None, 256),
                          'provider': (None, 'p1'),
                          'encryption_id': (None, 'uuid1')},
                         diff['encryption'])
    def test_volume_types_diff(self):
        # type_ref 1 and 2 have the same extra_specs, while 3 has different
        keyvals1 = {"key1": "val1", "key2": "val2"}
        keyvals2 = {"key1": "val0", "key2": "val2"}
        type_ref1 = volume_types.create(self.ctxt, "type1", keyvals1)
        type_ref2 = volume_types.create(self.ctxt, "type2", keyvals1)
        type_ref3 = volume_types.create(self.ctxt, "type3", keyvals2)

        # Check equality with only extra_specs
        diff, same = volume_types.volume_types_diff(self.ctxt, type_ref1["id"], type_ref2["id"])
        self.assertTrue(same)
        self.assertEqual(diff["extra_specs"]["key1"], ("val1", "val1"))
        diff, same = volume_types.volume_types_diff(self.ctxt, type_ref1["id"], type_ref3["id"])
        self.assertFalse(same)
        self.assertEqual(diff["extra_specs"]["key1"], ("val1", "val0"))

        # qos_ref 1 and 2 have the same specs, while 3 has different
        qos_keyvals1 = {"k1": "v1", "k2": "v2", "k3": "v3"}
        qos_keyvals2 = {"k1": "v0", "k2": "v2", "k3": "v3"}
        qos_ref1 = qos_specs.create(self.ctxt, "qos-specs-1", qos_keyvals1)
        qos_ref2 = qos_specs.create(self.ctxt, "qos-specs-2", qos_keyvals1)
        qos_ref3 = qos_specs.create(self.ctxt, "qos-specs-3", qos_keyvals2)

        # Check equality with qos specs too
        qos_specs.associate_qos_with_type(self.ctxt, qos_ref1["id"], type_ref1["id"])
        qos_specs.associate_qos_with_type(self.ctxt, qos_ref2["id"], type_ref2["id"])
        diff, same = volume_types.volume_types_diff(self.ctxt, type_ref1["id"], type_ref2["id"])
        self.assertTrue(same)
        self.assertEqual(diff["extra_specs"]["key1"], ("val1", "val1"))
        self.assertEqual(diff["qos_specs"]["k1"], ("v1", "v1"))
        qos_specs.disassociate_qos_specs(self.ctxt, qos_ref2["id"], type_ref2["id"])
        qos_specs.associate_qos_with_type(self.ctxt, qos_ref3["id"], type_ref2["id"])
        diff, same = volume_types.volume_types_diff(self.ctxt, type_ref1["id"], type_ref2["id"])
        self.assertFalse(same)
        self.assertEqual(diff["extra_specs"]["key1"], ("val1", "val1"))
        self.assertEqual(diff["qos_specs"]["k1"], ("v1", "v0"))
        qos_specs.disassociate_qos_specs(self.ctxt, qos_ref3["id"], type_ref2["id"])
        qos_specs.associate_qos_with_type(self.ctxt, qos_ref2["id"], type_ref2["id"])

        # And add encryption for good measure
        enc_keyvals1 = {
            "cipher": "c1",
            "key_size": 256,
            "provider": "p1",
            "control_location": "front-end",
            "encryption_id": "uuid1",
        }
        enc_keyvals2 = {
            "cipher": "c1",
            "key_size": 128,
            "provider": "p1",
            "control_location": "front-end",
            "encryption_id": "uuid2",
        }
        db.volume_type_encryption_create(self.ctxt, type_ref1["id"], enc_keyvals1)
        db.volume_type_encryption_create(self.ctxt, type_ref2["id"], enc_keyvals2)
        diff, same = volume_types.volume_types_diff(self.ctxt, type_ref1["id"], type_ref2["id"])
        self.assertFalse(same)
        self.assertEqual(diff["extra_specs"]["key1"], ("val1", "val1"))
        self.assertEqual(diff["qos_specs"]["k1"], ("v1", "v1"))
        self.assertEqual(diff["encryption"]["key_size"], (256, 128))

        # Check diff equals type specs when one type is None
        diff, same = volume_types.volume_types_diff(self.ctxt, None, type_ref1["id"])
        self.assertFalse(same)
        self.assertEqual(diff["extra_specs"], {"key1": (None, "val1"), "key2": (None, "val2")})
        self.assertEqual(
            diff["qos_specs"],
            {"consumer": (None, "back-end"), "k1": (None, "v1"), "k2": (None, "v2"), "k3": (None, "v3")},
        )
        self.assertEqual(
            diff["encryption"],
            {
                "cipher": (None, "c1"),
                "control_location": (None, "front-end"),
                "deleted": (None, False),
                "key_size": (None, 256),
                "provider": (None, "p1"),
                "encryption_id": (None, "uuid1"),
            },
        )