mirror of
https://github.com/OpenKMIP/PyKMIP.git
synced 2025-07-20 04:24:23 +02:00
Adding KmipEngine support for Destroy
This change adds support for the Destroy operation to the KmipEngine. New exceptions and test cases are included.
This commit is contained in:
parent
81222e23f1
commit
27befcb85c
@ -57,6 +57,24 @@ class CryptographicFailure(KmipError):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class IndexOutOfBounds(KmipError):
|
||||||
|
"""
|
||||||
|
An error generated when exceeding the attribute instance limit.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, message):
|
||||||
|
"""
|
||||||
|
Create an IndexOutOfBounds exception.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
message (string): A string containing information about the error.
|
||||||
|
"""
|
||||||
|
super(IndexOutOfBounds, self).__init__(
|
||||||
|
reason=enums.ResultReason.INDEX_OUT_OF_BOUNDS,
|
||||||
|
message=message
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class InvalidField(KmipError):
|
class InvalidField(KmipError):
|
||||||
"""
|
"""
|
||||||
An error generated when an invalid field value is processed.
|
An error generated when an invalid field value is processed.
|
||||||
@ -93,6 +111,24 @@ class InvalidMessage(KmipError):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class ItemNotFound(KmipError):
|
||||||
|
"""
|
||||||
|
An error generated when a request item cannot be located.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, message):
|
||||||
|
"""
|
||||||
|
Create an ItemNotFound exception.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
message (string): A string containing information about the error.
|
||||||
|
"""
|
||||||
|
super(ItemNotFound, self).__init__(
|
||||||
|
reason=enums.ResultReason.ITEM_NOT_FOUND,
|
||||||
|
message=message
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class OperationNotSupported(KmipError):
|
class OperationNotSupported(KmipError):
|
||||||
"""
|
"""
|
||||||
An error generated when an unsupported operation is invoked.
|
An error generated when an unsupported operation is invoked.
|
||||||
|
@ -14,22 +14,31 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
import sqlalchemy
|
||||||
|
|
||||||
|
from sqlalchemy.orm import exc
|
||||||
|
|
||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
|
|
||||||
import kmip
|
import kmip
|
||||||
|
|
||||||
|
from kmip.core import attributes
|
||||||
from kmip.core import enums
|
from kmip.core import enums
|
||||||
from kmip.core import exceptions
|
from kmip.core import exceptions
|
||||||
|
|
||||||
from kmip.core.messages import contents
|
from kmip.core.messages import contents
|
||||||
from kmip.core.messages import messages
|
from kmip.core.messages import messages
|
||||||
|
|
||||||
|
from kmip.core.messages.payloads import destroy
|
||||||
from kmip.core.messages.payloads import discover_versions
|
from kmip.core.messages.payloads import discover_versions
|
||||||
from kmip.core.messages.payloads import query
|
from kmip.core.messages.payloads import query
|
||||||
|
|
||||||
from kmip.core import misc
|
from kmip.core import misc
|
||||||
|
|
||||||
|
from kmip.pie import sqltypes
|
||||||
|
from kmip.pie import objects
|
||||||
|
|
||||||
from kmip.services.server.crypto import engine
|
from kmip.services.server.crypto import engine
|
||||||
|
|
||||||
|
|
||||||
@ -47,6 +56,8 @@ class KmipEngine(object):
|
|||||||
* User authentication
|
* User authentication
|
||||||
* Batch processing options: UNDO
|
* Batch processing options: UNDO
|
||||||
* Asynchronous operations
|
* Asynchronous operations
|
||||||
|
* Operation policies
|
||||||
|
* Object archival
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
@ -56,6 +67,16 @@ class KmipEngine(object):
|
|||||||
self._logger = logging.getLogger(__name__)
|
self._logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
self._cryptography_engine = engine.CryptographyEngine()
|
self._cryptography_engine = engine.CryptographyEngine()
|
||||||
|
|
||||||
|
self._data_store = sqlalchemy.create_engine(
|
||||||
|
'sqlite:///:memory:',
|
||||||
|
echo=False
|
||||||
|
)
|
||||||
|
sqltypes.Base.metadata.create_all(self._data_store)
|
||||||
|
self._data_store_session_factory = sqlalchemy.orm.sessionmaker(
|
||||||
|
bind=self._data_store
|
||||||
|
)
|
||||||
|
|
||||||
self._lock = threading.RLock()
|
self._lock = threading.RLock()
|
||||||
|
|
||||||
self._id_placeholder = None
|
self._id_placeholder = None
|
||||||
@ -68,6 +89,17 @@ class KmipEngine(object):
|
|||||||
|
|
||||||
self._protocol_version = self._protocol_versions[0]
|
self._protocol_version = self._protocol_versions[0]
|
||||||
|
|
||||||
|
self._object_map = {
|
||||||
|
enums.ObjectType.CERTIFICATE: objects.X509Certificate,
|
||||||
|
enums.ObjectType.SYMMETRIC_KEY: objects.SymmetricKey,
|
||||||
|
enums.ObjectType.PUBLIC_KEY: objects.PublicKey,
|
||||||
|
enums.ObjectType.PRIVATE_KEY: objects.PrivateKey,
|
||||||
|
enums.ObjectType.SPLIT_KEY: None,
|
||||||
|
enums.ObjectType.TEMPLATE: None,
|
||||||
|
enums.ObjectType.SECRET_DATA: objects.SecretData,
|
||||||
|
enums.ObjectType.OPAQUE_DATA: objects.OpaqueObject
|
||||||
|
}
|
||||||
|
|
||||||
def _kmip_version_supported(supported):
|
def _kmip_version_supported(supported):
|
||||||
def decorator(function):
|
def decorator(function):
|
||||||
def wrapper(self, *args, **kwargs):
|
def wrapper(self, *args, **kwargs):
|
||||||
@ -266,6 +298,9 @@ class KmipEngine(object):
|
|||||||
|
|
||||||
def _process_batch(self, request_batch, batch_handling, batch_order):
|
def _process_batch(self, request_batch, batch_handling, batch_order):
|
||||||
response_batch = list()
|
response_batch = list()
|
||||||
|
|
||||||
|
self._data_session = self._data_store_session_factory()
|
||||||
|
|
||||||
for batch_item in request_batch:
|
for batch_item in request_batch:
|
||||||
error_occurred = False
|
error_occurred = False
|
||||||
|
|
||||||
@ -342,6 +377,8 @@ class KmipEngine(object):
|
|||||||
return response_batch
|
return response_batch
|
||||||
|
|
||||||
def _process_operation(self, operation, payload):
|
def _process_operation(self, operation, payload):
|
||||||
|
if operation == enums.Operation.DESTROY:
|
||||||
|
return self._process_destroy(payload)
|
||||||
if operation == enums.Operation.QUERY:
|
if operation == enums.Operation.QUERY:
|
||||||
return self._process_query(payload)
|
return self._process_query(payload)
|
||||||
elif operation == enums.Operation.DISCOVER_VERSIONS:
|
elif operation == enums.Operation.DISCOVER_VERSIONS:
|
||||||
@ -353,6 +390,64 @@ class KmipEngine(object):
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@_kmip_version_supported('1.0')
|
||||||
|
def _process_destroy(self, payload):
|
||||||
|
self._logger.info("Processing operation: Destroy")
|
||||||
|
|
||||||
|
if payload.unique_identifier:
|
||||||
|
unique_identifier = payload.unique_identifier.value
|
||||||
|
else:
|
||||||
|
unique_identifier = self._id_placeholder
|
||||||
|
|
||||||
|
try:
|
||||||
|
object_type = self._data_session.query(
|
||||||
|
objects.ManagedObject._object_type
|
||||||
|
).filter(
|
||||||
|
objects.ManagedObject.unique_identifier == unique_identifier
|
||||||
|
).one()[0]
|
||||||
|
except exc.NoResultFound as e:
|
||||||
|
self._logger.warning(
|
||||||
|
"Could not identify object type for object: {0}".format(
|
||||||
|
unique_identifier
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self._logger.exception(e)
|
||||||
|
raise exceptions.ItemNotFound(
|
||||||
|
"Could not locate object: {0}".format(unique_identifier)
|
||||||
|
)
|
||||||
|
except exc.MultipleResultsFound as e:
|
||||||
|
self._logger.warning(
|
||||||
|
"Multiple objects found for ID: {0}".format(
|
||||||
|
unique_identifier
|
||||||
|
)
|
||||||
|
)
|
||||||
|
raise e
|
||||||
|
|
||||||
|
table = self._object_map.get(object_type)
|
||||||
|
if table is None:
|
||||||
|
name = object_type.name
|
||||||
|
raise exceptions.InvalidField(
|
||||||
|
"The {0} object type is not supported.".format(
|
||||||
|
''.join(
|
||||||
|
[x.capitalize() for x in name[9:].split('_')]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
# TODO (peterhamilton) Process attributes to see if destroy possible
|
||||||
|
# 1. Check object state. If invalid, error out.
|
||||||
|
# 2. Check object deactivation date. If invalid, error out.
|
||||||
|
|
||||||
|
self._data_session.query(table).filter(
|
||||||
|
table.unique_identifier == unique_identifier
|
||||||
|
).delete()
|
||||||
|
|
||||||
|
response_payload = destroy.DestroyResponsePayload(
|
||||||
|
unique_identifier=attributes.UniqueIdentifier(unique_identifier)
|
||||||
|
)
|
||||||
|
|
||||||
|
return response_payload
|
||||||
|
|
||||||
@_kmip_version_supported('1.0')
|
@_kmip_version_supported('1.0')
|
||||||
def _process_query(self, payload):
|
def _process_query(self, payload):
|
||||||
self._logger.info("Processing operation: Query")
|
self._logger.info("Processing operation: Query")
|
||||||
@ -368,6 +463,7 @@ class KmipEngine(object):
|
|||||||
|
|
||||||
if enums.QueryFunction.QUERY_OPERATIONS in queries:
|
if enums.QueryFunction.QUERY_OPERATIONS in queries:
|
||||||
operations = list([
|
operations = list([
|
||||||
|
contents.Operation(enums.Operation.DESTROY),
|
||||||
contents.Operation(enums.Operation.QUERY)
|
contents.Operation(enums.Operation.QUERY)
|
||||||
])
|
])
|
||||||
|
|
||||||
|
@ -14,11 +14,16 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
import mock
|
import mock
|
||||||
|
import sqlalchemy
|
||||||
|
|
||||||
|
from sqlalchemy.orm import exc
|
||||||
|
|
||||||
import testtools
|
import testtools
|
||||||
import time
|
import time
|
||||||
|
|
||||||
import kmip
|
import kmip
|
||||||
|
|
||||||
|
from kmip.core import attributes
|
||||||
from kmip.core import enums
|
from kmip.core import enums
|
||||||
from kmip.core import exceptions
|
from kmip.core import exceptions
|
||||||
from kmip.core import misc
|
from kmip.core import misc
|
||||||
@ -27,9 +32,13 @@ from kmip.core import objects
|
|||||||
from kmip.core.messages import contents
|
from kmip.core.messages import contents
|
||||||
from kmip.core.messages import messages
|
from kmip.core.messages import messages
|
||||||
|
|
||||||
|
from kmip.core.messages.payloads import destroy
|
||||||
from kmip.core.messages.payloads import discover_versions
|
from kmip.core.messages.payloads import discover_versions
|
||||||
from kmip.core.messages.payloads import query
|
from kmip.core.messages.payloads import query
|
||||||
|
|
||||||
|
from kmip.pie import objects as pie_objects
|
||||||
|
from kmip.pie import sqltypes
|
||||||
|
|
||||||
from kmip.services.server import engine
|
from kmip.services.server import engine
|
||||||
|
|
||||||
|
|
||||||
@ -50,6 +59,14 @@ class TestKmipEngine(testtools.TestCase):
|
|||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(TestKmipEngine, self).setUp()
|
super(TestKmipEngine, self).setUp()
|
||||||
|
|
||||||
|
self.engine = sqlalchemy.create_engine(
|
||||||
|
'sqlite:///:memory:',
|
||||||
|
)
|
||||||
|
sqltypes.Base.metadata.create_all(self.engine)
|
||||||
|
self.session_factory = sqlalchemy.orm.sessionmaker(
|
||||||
|
bind=self.engine
|
||||||
|
)
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
super(TestKmipEngine, self).tearDown()
|
super(TestKmipEngine, self).tearDown()
|
||||||
|
|
||||||
@ -621,12 +638,15 @@ class TestKmipEngine(testtools.TestCase):
|
|||||||
e = engine.KmipEngine()
|
e = engine.KmipEngine()
|
||||||
e._logger = mock.MagicMock()
|
e._logger = mock.MagicMock()
|
||||||
|
|
||||||
|
e._process_destroy = mock.MagicMock()
|
||||||
e._process_query = mock.MagicMock()
|
e._process_query = mock.MagicMock()
|
||||||
e._process_discover_versions = mock.MagicMock()
|
e._process_discover_versions = mock.MagicMock()
|
||||||
|
|
||||||
|
e._process_operation(enums.Operation.DESTROY, None)
|
||||||
e._process_operation(enums.Operation.QUERY, None)
|
e._process_operation(enums.Operation.QUERY, None)
|
||||||
e._process_operation(enums.Operation.DISCOVER_VERSIONS, None)
|
e._process_operation(enums.Operation.DISCOVER_VERSIONS, None)
|
||||||
|
|
||||||
|
e._process_destroy.assert_called_with(None)
|
||||||
e._process_query.assert_called_with(None)
|
e._process_query.assert_called_with(None)
|
||||||
e._process_discover_versions.assert_called_with(None)
|
e._process_discover_versions.assert_called_with(None)
|
||||||
|
|
||||||
@ -649,6 +669,178 @@ class TestKmipEngine(testtools.TestCase):
|
|||||||
*args
|
*args
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_destroy(self):
|
||||||
|
"""
|
||||||
|
Test that a Destroy request can be processed correctly.
|
||||||
|
"""
|
||||||
|
e = engine.KmipEngine()
|
||||||
|
e._data_store = self.engine
|
||||||
|
e._data_store_session_factory = self.session_factory
|
||||||
|
e._data_session = e._data_store_session_factory()
|
||||||
|
e._logger = mock.MagicMock()
|
||||||
|
|
||||||
|
obj_a = pie_objects.OpaqueObject(b'', enums.OpaqueDataType.NONE)
|
||||||
|
obj_b = pie_objects.OpaqueObject(b'', enums.OpaqueDataType.NONE)
|
||||||
|
|
||||||
|
e._data_session.add(obj_a)
|
||||||
|
e._data_session.add(obj_b)
|
||||||
|
e._data_session.commit()
|
||||||
|
e._data_session = e._data_store_session_factory()
|
||||||
|
|
||||||
|
id_a = str(obj_a.unique_identifier)
|
||||||
|
id_b = str(obj_b.unique_identifier)
|
||||||
|
|
||||||
|
# Test by specifying the ID of the object to destroy.
|
||||||
|
payload = destroy.DestroyRequestPayload(
|
||||||
|
unique_identifier=attributes.UniqueIdentifier(id_a)
|
||||||
|
)
|
||||||
|
|
||||||
|
response_payload = e._process_destroy(payload)
|
||||||
|
e._data_session.commit()
|
||||||
|
e._data_session = e._data_store_session_factory()
|
||||||
|
|
||||||
|
e._logger.info.assert_called_once_with(
|
||||||
|
"Processing operation: Destroy"
|
||||||
|
)
|
||||||
|
self.assertEqual(str(id_a), response_payload.unique_identifier.value)
|
||||||
|
self.assertRaises(
|
||||||
|
exc.NoResultFound,
|
||||||
|
e._data_session.query(pie_objects.OpaqueObject).filter(
|
||||||
|
pie_objects.ManagedObject.unique_identifier == id_a
|
||||||
|
).one
|
||||||
|
)
|
||||||
|
|
||||||
|
e._data_session.commit()
|
||||||
|
e._data_store_session_factory()
|
||||||
|
e._logger.reset_mock()
|
||||||
|
e._id_placeholder = str(id_b)
|
||||||
|
|
||||||
|
# Test by using the ID placeholder to specify the object to destroy.
|
||||||
|
payload = destroy.DestroyRequestPayload()
|
||||||
|
|
||||||
|
response_payload = e._process_destroy(payload)
|
||||||
|
e._data_session.commit()
|
||||||
|
e._data_session = e._data_store_session_factory()
|
||||||
|
|
||||||
|
e._logger.info.assert_called_once_with(
|
||||||
|
"Processing operation: Destroy"
|
||||||
|
)
|
||||||
|
self.assertEqual(str(id_b), response_payload.unique_identifier.value)
|
||||||
|
self.assertRaises(
|
||||||
|
exc.NoResultFound,
|
||||||
|
e._data_session.query(pie_objects.OpaqueObject).filter(
|
||||||
|
pie_objects.ManagedObject.unique_identifier == id_b
|
||||||
|
).one
|
||||||
|
)
|
||||||
|
|
||||||
|
e._data_session.commit()
|
||||||
|
|
||||||
|
def test_destroy_missing_object(self):
|
||||||
|
"""
|
||||||
|
Test that an ItemNotFound error is generated when attempting to
|
||||||
|
destroy an object that does not exist.
|
||||||
|
"""
|
||||||
|
e = engine.KmipEngine()
|
||||||
|
e._data_store = self.engine
|
||||||
|
e._data_store_session_factory = self.session_factory
|
||||||
|
e._data_session = e._data_store_session_factory()
|
||||||
|
e._logger = mock.MagicMock()
|
||||||
|
|
||||||
|
payload = destroy.DestroyRequestPayload(
|
||||||
|
unique_identifier=attributes.UniqueIdentifier('1')
|
||||||
|
)
|
||||||
|
|
||||||
|
args = (payload, )
|
||||||
|
regex = "Could not locate object: 1"
|
||||||
|
self.assertRaisesRegexp(
|
||||||
|
exceptions.ItemNotFound,
|
||||||
|
regex,
|
||||||
|
e._process_destroy,
|
||||||
|
*args
|
||||||
|
)
|
||||||
|
e._data_session.commit()
|
||||||
|
e._logger.info.assert_called_once_with(
|
||||||
|
"Processing operation: Destroy"
|
||||||
|
)
|
||||||
|
e._logger.warning.assert_called_once_with(
|
||||||
|
"Could not identify object type for object: 1"
|
||||||
|
)
|
||||||
|
self.assertTrue(e._logger.exception.called)
|
||||||
|
|
||||||
|
def test_destroy_multiple_objects(self):
|
||||||
|
"""
|
||||||
|
Test that a sqlalchemy.orm.exc.MultipleResultsFound error is generated
|
||||||
|
when multiple objects map to the same object ID.
|
||||||
|
"""
|
||||||
|
e = engine.KmipEngine()
|
||||||
|
e._data_store = self.engine
|
||||||
|
e._data_store_session_factory = self.session_factory
|
||||||
|
e._data_session = e._data_store_session_factory()
|
||||||
|
test_exception = exc.MultipleResultsFound()
|
||||||
|
e._data_session.query = mock.MagicMock(side_effect=test_exception)
|
||||||
|
e._logger = mock.MagicMock()
|
||||||
|
|
||||||
|
payload = destroy.DestroyRequestPayload(
|
||||||
|
unique_identifier=attributes.UniqueIdentifier('1')
|
||||||
|
)
|
||||||
|
|
||||||
|
args = (payload, )
|
||||||
|
self.assertRaises(
|
||||||
|
exc.MultipleResultsFound,
|
||||||
|
e._process_destroy,
|
||||||
|
*args
|
||||||
|
)
|
||||||
|
e._data_session.commit()
|
||||||
|
e._logger.info.assert_called_once_with(
|
||||||
|
"Processing operation: Destroy"
|
||||||
|
)
|
||||||
|
e._logger.warning.assert_called_once_with(
|
||||||
|
"Multiple objects found for ID: 1"
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_destroy_unsupported_object_type(self):
|
||||||
|
"""
|
||||||
|
Test that an InvalidField error is generated when attempting to
|
||||||
|
destroy an unsupported object type.
|
||||||
|
"""
|
||||||
|
e = engine.KmipEngine()
|
||||||
|
e._object_map = {enums.ObjectType.OPAQUE_DATA: None}
|
||||||
|
e._data_store = self.engine
|
||||||
|
e._data_store_session_factory = self.session_factory
|
||||||
|
e._data_session = e._data_store_session_factory()
|
||||||
|
e._logger = mock.MagicMock()
|
||||||
|
|
||||||
|
obj_a = pie_objects.OpaqueObject(b'', enums.OpaqueDataType.NONE)
|
||||||
|
|
||||||
|
e._data_session.add(obj_a)
|
||||||
|
e._data_session.commit()
|
||||||
|
e._data_session = e._data_store_session_factory()
|
||||||
|
|
||||||
|
id_a = str(obj_a.unique_identifier)
|
||||||
|
|
||||||
|
payload = destroy.DestroyRequestPayload(
|
||||||
|
unique_identifier=attributes.UniqueIdentifier(id_a)
|
||||||
|
)
|
||||||
|
|
||||||
|
args = (payload, )
|
||||||
|
name = enums.ObjectType.OPAQUE_DATA.name
|
||||||
|
regex = "The {0} object type is not supported.".format(
|
||||||
|
''.join(
|
||||||
|
[x.capitalize() for x in name[9:].split('_')]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertRaisesRegexp(
|
||||||
|
exceptions.InvalidField,
|
||||||
|
regex,
|
||||||
|
e._process_destroy,
|
||||||
|
*args
|
||||||
|
)
|
||||||
|
e._data_session.commit()
|
||||||
|
e._logger.info.assert_called_once_with(
|
||||||
|
"Processing operation: Destroy"
|
||||||
|
)
|
||||||
|
|
||||||
def test_query(self):
|
def test_query(self):
|
||||||
"""
|
"""
|
||||||
Test that a Query request can be processed correctly, for different
|
Test that a Query request can be processed correctly, for different
|
||||||
@ -678,8 +870,15 @@ class TestKmipEngine(testtools.TestCase):
|
|||||||
e._logger.info.assert_called_once_with("Processing operation: Query")
|
e._logger.info.assert_called_once_with("Processing operation: Query")
|
||||||
self.assertIsInstance(result, query.QueryResponsePayload)
|
self.assertIsInstance(result, query.QueryResponsePayload)
|
||||||
self.assertIsNotNone(result.operations)
|
self.assertIsNotNone(result.operations)
|
||||||
self.assertEqual(1, len(result.operations))
|
self.assertEqual(2, len(result.operations))
|
||||||
self.assertEqual(enums.Operation.QUERY, result.operations[0].value)
|
self.assertEqual(
|
||||||
|
enums.Operation.DESTROY,
|
||||||
|
result.operations[0].value
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
enums.Operation.QUERY,
|
||||||
|
result.operations[1].value
|
||||||
|
)
|
||||||
self.assertEqual(list(), result.object_types)
|
self.assertEqual(list(), result.object_types)
|
||||||
self.assertIsNotNone(result.vendor_identification)
|
self.assertIsNotNone(result.vendor_identification)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
@ -698,11 +897,18 @@ class TestKmipEngine(testtools.TestCase):
|
|||||||
|
|
||||||
e._logger.info.assert_called_once_with("Processing operation: Query")
|
e._logger.info.assert_called_once_with("Processing operation: Query")
|
||||||
self.assertIsNotNone(result.operations)
|
self.assertIsNotNone(result.operations)
|
||||||
self.assertEqual(2, len(result.operations))
|
self.assertEqual(3, len(result.operations))
|
||||||
self.assertEqual(enums.Operation.QUERY, result.operations[0].value)
|
self.assertEqual(
|
||||||
|
enums.Operation.DESTROY,
|
||||||
|
result.operations[0].value
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
enums.Operation.QUERY,
|
||||||
|
result.operations[1].value
|
||||||
|
)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
enums.Operation.DISCOVER_VERSIONS,
|
enums.Operation.DISCOVER_VERSIONS,
|
||||||
result.operations[1].value
|
result.operations[2].value
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_discover_versions(self):
|
def test_discover_versions(self):
|
||||||
|
Loading…
x
Reference in New Issue
Block a user