From e38fc65339cb3fcaced4c0d4d80c41d1140dd9e9 Mon Sep 17 00:00:00 2001 From: kschopmeyer Date: Sun, 22 May 2016 21:56:44 -0500 Subject: [PATCH] WIP Update to remove Enum_Context and clean up tests to match new proposal namespace. Implemented the result of all of the pull operations as a named tuple with three parts, instances/paths, end_of_sequence, enumeration_context Cleans up most of the documentation for OpenEnumerateInstances and PullInstancesWithPath. Note that there are a couple of issues around MaxObjectCount to be worked out. This is only a WIP Add INSTANCEPATH as IRETURNVALUE child in tupleparse.py Doc modifications per Andy's comments Implements all of the operations. Add a number of tests against pegasus for OpenEnumInstances and OpenEnumPaths This adds the remaining operations to cim_operations. The result of all of the pull operations is a named tuple. The documentation is update for OpenEnumerationInstances and PullInstancesWithPath. There is still work including a)add documentation to the other operations once we agree on the documentation for the OpenEnum, etc. b)Add tests against pegasus c) get the mock working. --- pywbem/cim_operations.py | 541 +++++++++++++------ pywbem/tupleparse.py | 6 +- testsuite/run_cim_operations.py | 446 +++++++++++++-- testsuite/test_client/openenuminstances.yaml | 1 - 4 files changed, 759 insertions(+), 235 deletions(-) diff --git a/pywbem/cim_operations.py b/pywbem/cim_operations.py index 6d9e6bc78..afd4301a3 100644 --- a/pywbem/cim_operations.py +++ b/pywbem/cim_operations.py @@ -54,44 +54,44 @@ :meth:`~pywbem.WBEMConnection.InvokeMethod` Invoke a method on a target instance or on a target class ------------------------------------------------------ -------------------------------------------------------------- :meth:`~pywbem.WBEMConnection.ExecQuery` Execute a query in a namespace ------------------------------------------------------- -------------------------------------------------------------- -:meth:`~pywbem.WBEMConnection.EnumerateClassNames` Enumerate the names of subclasses of a class, or of the - top-level classes in a namespace -:meth:`~pywbem.WBEMConnection.EnumerateClasses` Enumerate the subclasses of a class, or the top-level classes - in a namespace -:meth:`~pywbem.WBEMConnection.GetClass` Retrieve a class -:meth:`~pywbem.WBEMConnection.ModifyClass` Modify a class -:meth:`~pywbem.WBEMConnection.CreateClass` Create a class -:meth:`~pywbem.WBEMConnection.DeleteClass` Delete a class ------------------------------------------------------- -------------------------------------------------------------- -:meth:`~pywbem.WBEMConnection.EnumerateQualifiers` Enumerate qualifier declarations -:meth:`~pywbem.WBEMConnection.GetQualifier` Retrieve a qualifier declaration -:meth:`~pywbem.WBEMConnection.SetQualifier` Create or modify a qualifier declaration -:meth:`~pywbem.WBEMConnection.DeleteQualifier` Delete a qualifier declaration ------------------------------------------------------- --------------------------------------------------------- -:meth:`~pywbem.WBEMConnection.OpenEnumerateInstances` Open an enumeration sequence to retrieve instances of +:meth:`~pywbem.WBEMConnection.OpenEnumerateInstances` Open an enumeration session to retrieve instances of of a class (including instances of its subclass) -:meth:`~pywbem.WBEMConnection.OpenAssocatorInstances` Open an enumeration sequence to retrieve the instances +:meth:`~pywbem.WBEMConnection.OpenAssocators` Open an enumeration session to retrieve the instances associated to a source instance -:meth:`~pywbem.WBEMConnection.OpenReferenceInstances` Open an enumeration sequence to retrieve the instances +:meth:`~pywbem.WBEMConnection.OpenReferences` Open an enumeration session to retrieve the instances that reference a source instance -:meth:`~pywbem.WBEMConnection.PullInstancesWithPath` Continue enumeration of an enumeration sequence opened +:meth:`~pywbem.WBEMConnection.PullInstancesWithPath` Continue enumeration of an enumeration session opened with OpenEnumerateInstances, OpenAssociators, or OpenReferences -:meth:`~pywbem.WBEMConnection.OpenEnumerateInstancePaths` Open an enumeration sequence to retrieve instances of +:meth:`~pywbem.WBEMConnection.OpenEnumerateInstancePaths` Open an enumeration session to retrieve instances of of a class (including instances of its subclass) -:meth:`~pywbem.WBEMConnection.OpenAssocatorInstancePaths` Open an enumeration sequence to retrieve the instances +:meth:`~pywbem.WBEMConnection.OpenAssocatorPaths` Open an enumeration session to retrieve the instances associated to a source instance -:meth:`~pywbem.WBEMConnection.OpenReferenceInstancesPaths` Open an enumeration sequence to retrieve the instances +:meth:`~pywbem.WBEMConnection.OpenReferencePaths` Open an enumeration session to retrieve the instances that reference a source instance -:meth:`~pywbem.WBEMConnection.PullInstance Paths` Continue enumeration of an enumeration sequence opened +:meth:`~pywbem.WBEMConnection.PullInstancePaths` Continue enumeration of an enumeration session opened with OpenEnumerateInstancePaths, OpenAssociatorInstancePaths, or OpenReferenceInstancePaths :meth:`~pywbem.WBEMConnection.OpenExecQuery` Open query request to retrieve instances defined by the query parameter in a namespace -:meth:`~pywbem.WBEMConnection.PullInstances` Continue enumeration of an enumeration sequence opened +:meth:`~pywbem.WBEMConnection.PullInstances` Continue enumeration of an enumeration session opened with OpenExecQuery -:meth:`~pywbem.WBEMConnection.CloseEnumeration` Close an enumeration sequence in process. +:meth:`~pywbem.WBEMConnection.CloseEnumeration` Close an enumeration session in process. +------------------------------------------------------ -------------------------------------------------------------- +:meth:`~pywbem.WBEMConnection.EnumerateClassNames` Enumerate the names of subclasses of a class, or of the + top-level classes in a namespace +:meth:`~pywbem.WBEMConnection.EnumerateClasses` Enumerate the subclasses of a class, or the top-level classes + in a namespace +:meth:`~pywbem.WBEMConnection.GetClass` Retrieve a class +:meth:`~pywbem.WBEMConnection.ModifyClass` Modify a class +:meth:`~pywbem.WBEMConnection.CreateClass` Create a class +:meth:`~pywbem.WBEMConnection.DeleteClass` Delete a class +------------------------------------------------------ -------------------------------------------------------------- +:meth:`~pywbem.WBEMConnection.EnumerateQualifiers` Enumerate qualifier declarations +:meth:`~pywbem.WBEMConnection.GetQualifier` Retrieve a qualifier declaration +:meth:`~pywbem.WBEMConnection.SetQualifier` Create or modify a qualifier declaration +:meth:`~pywbem.WBEMConnection.DeleteQualifier` Delete a qualifier declaration ====================================================== ============================================================== """ # pylint: enable=line-too-long @@ -107,9 +107,9 @@ from xml.dom import minidom from xml.parsers.expat import ExpatError import warnings +from collections import namedtuple import six - from . import cim_xml from .cim_constants import DEFAULT_NAMESPACE from .cim_types import CIMType, CIMDateTime, atomic_to_cim_xml @@ -124,7 +124,7 @@ from .cim_constants import * # pylint: disable=wildcard-import __all__ = ['WBEMConnection', 'PegasusUDSConnection', 'SFCBUDSConnection', - 'OpenWBEMUDSConnection', 'EnumContext'] + 'OpenWBEMUDSConnection'] if len(u'\U00010122') == 2: @@ -139,42 +139,6 @@ _ILL_FORMED_UTF8_RE = re.compile( b'(\xED[\xA0-\xBF][\x80-\xBF])') # U+D800...U+DFFF -class EnumContext(object): - """ - Container for the current state of an enumeration sequence. - Allows passing parameters between operations of an enumeration - sequence including namespace, max_object_count, and the - state of the previous response (EnumerationContext, etc.) - - """ - - def __init__(self, end_of_sequence, enumeration_context, namespace, \ - max_object_count): - """TODO""" - self.end_of_sequence = end_of_sequence - self.enumeration_context = enumeration_context - self.namespace = namespace - self.max_object_count = max_object_count - - # use the @property on these. - def is_end_of_sequence(self): - """Returns value True if at end of sequence""" - return self.end_of_sequence - - def more(self): - """Returns value True if the sequence is incomplete""" - return not self.end_of_sequence - - def set_max_object_count(self, max_object_count): - """TODO""" - self.max_object_count = max_object_count - # TODO document the above - - def __repr__(self): - return "end_of_sequence=%s, enumeration_context=%s, " \ - "namespace=%s, max_object_count=%s" % \ - (self.end_of_sequence, self.enumeration_context, \ - self.namespace, self.max_object_count) def _check_classname(val): """ @@ -664,7 +628,8 @@ def __repr__(self): self.default_namespace, self.x509, self.verify_callback, self.ca_certs, self.no_verification, self.timeout) - def imethodcall(self, methodname, namespace, pull_op=None, **params): + def imethodcall(self, methodname, namespace, response_params_reqd=None, \ + **params): """ This is a low-level method that is used by the operation-specific methods of this class @@ -680,10 +645,12 @@ def imethodcall(self, methodname, namespace, pull_op=None, **params): warnings.warn( "Calling imethodcall() directly is deprecated", DeprecationWarning) - return self._imethodcall(methodname, namespace, pull_op=pull_op, \ + return self._imethodcall(methodname, namespace, + response_params_reqd=response_params_reqd, **params) - def _imethodcall(self, methodname, namespace, pull_op=None, **params): + def _imethodcall(self, methodname, namespace, response_params_reqd=None, \ + **params): """ Perform an intrinsic CIM-XML operation. """ @@ -827,7 +794,7 @@ def _imethodcall(self, methodname, namespace, pull_op=None, **params): if 'DESCRIPTION' in err[1]: raise CIMError(code, err[1]['DESCRIPTION']) raise CIMError(code, 'Error code %s' % err[1]['CODE']) - if pull_op is None: + if response_params_reqd is None: #expect either ERROR | IRETURNVALUE* err = tup_tree[0] if err[0] != 'IRETURNVALUE': @@ -1341,10 +1308,11 @@ class are to be included in the returned instances, as follows: return instances - def _process_pull_result(self, result): + def _proc_pull_rslt(self, result, namespace): """Common processing for pull results to separate - end-of-sequence, enum-context, and instances - Returns tuple of instances, end_of_sequence, enumeration_context) + end-of-sequence, enum-context, and endities in IRETURNVALUE. + Returns tuple of entities in IRETURNVALUE, end_of_sequence, + and enumeration_context) """ #TODO make this None??? rtn_objects = [] @@ -1365,22 +1333,62 @@ def _process_pull_result(self, result): else: # TODO ks 5/16 Invalid xml error or just ignore??? print('Nothing found at p0 %s ' % (p[0])) + if not valid_result: raise CIMError(CIM_ERR_INVALID_PARAMETER, "EndOfSequence " \ "or EnumerationContext required") - ####print('Return lenobj=%s, eos=%s enum_ctxt=%s' % - #### (len(rtn_objects), end_of_sequence, enumeration_context)) - return (rtn_objects, end_of_sequence, enumeration_context) + + rtn_ctxt = None if end_of_sequence else (enumeration_context, + namespace) + + return (rtn_objects, end_of_sequence, rtn_ctxt) + + def OpenEnumerateInstancePaths(self, ClassName, namespace=None, + FilterQueryLanguage=None, FilterQuery=None, + OperationTimeout=None, ContinueOnError=None, + MaxObjectCount=None, **extra): + # pylint: disable=invalid-name + + """ + TODO Add this documentation once we agree to the doc in the + openEnumerateInstances + """ + if namespace is None and isinstance(ClassName, CIMClassName): + namespace = ClassName.namespace + namespace = self._iparam_namespace_from(namespace) + classname = self._iparam_classname(ClassName) + + result = self._imethodcall( + 'OpenEnumerateInstancePaths', + namespace, + ClassName=classname, + FilterQueryLanguage=FilterQueryLanguage, + FilterQuery=FilterQuery, + OperationTimeout=OperationTimeout, + ContinueOnError=ContinueOnError, + MaxObjectCount=MaxObjectCount, + response_params_reqd=True, + **extra) + + # TODO ks 5/16 why are we setting the namespace attribute?? + #[setattr(i.path, 'namespace', namespace) for i in instances] + + paths, end_of_sequence, enum_ctxt = \ + self._proc_pull_rslt(result, namespace) + + result = namedtuple("result", ["paths", "end_of_sequence", \ + "enumeration_context"]) + return result(paths, end_of_sequence, enum_ctxt) def OpenEnumerateInstances(self, ClassName, namespace=None, LocalOnly=None, DeepInheritance=None, IncludeQualifiers=None, IncludeClassOrigin=None, PropertyList=None, FilterQueryLanguage=None, FilterQuery=None, - ContinueOnError=None, MaxObjectCount=None, - **extra): + OperationTimeout=None, ContinueOnError=None, + MaxObjectCount=None, **extra): # pylint: disable=invalid-name """ - Open and enumeration sequence to get instances of a class + Open an enumeration session to get instances of a class (including instances of its subclasses). This method performs the OpenEnumerateInstances operation @@ -1465,22 +1473,29 @@ class are to be included in the returned instances, as follows: If `None`, all properties are included. FilterQueryLanguage (:term:`string`): - Optional: a String defining the name of the query language - used for the Query argument. This argument must exist if the - filterQuery argument is provided. - - QueryLanguage (:term:`string`): - Optional: a String defining the query that is to be sent - to the WBEM server. - #TODO define typing for this correctly - OperationTimeout (term: `Uint32`): - Optional: If provided it defines a timeout value to be - sent to the WBEM Server - * If `Uint32` value, this value is sent to the server as the - proposed timeout for the operation sequence. If the client - waits longer than this timeout in seconds, the server may - terminate the operation - * if `None` + A string defining the name of the query language + used for the Query argument. + + FilterQuery (:term:`string`): + A string defining the query that is to be sent + to the WBEM server in using the query language defined by + the `FilterQueryLanguage` parameter. + + OperationTimeout (:class: `~pywbem.Uint32`): + Determines the minimum time in seconds the WBEM Server shall + maintain an open enumeration session after a previous Open or Pull + request is sent to the client. Once this timeout time has + expired, the WBEM server may close the enumeration session. + + If provided it defines a timeout value to be sent to the WBEM + Server. If not provided, this parameter is not passed to the + WBEM server and the server uses its own default: + * If non-zero `Uint32` value, this value is sent to the server + as the proposed timeout for the enumeration session. + * If value is zero, the server is expected to never timeout + or to reject the open request if this value is not allowed. + * If `None` this parameter is not passed to the WBEM server + and the WBEM server uses its own default. ContinueOnError (:class:`py:bool`): Indicates to the server to continue sending responses @@ -1489,43 +1504,53 @@ class are to be included in the returned instances, as follows: sending an error response. * If `False` the server must not continue sending responses after and error response is sent and must close the enumeration - sequence after sending any error response. - * if `None` this parameter is not sent to the WBEM server and the - WBEM server is to close the enumeration sequence upon error. + session after sending any error response. + * if `None` this parameter is not sent to the WBEM Server and + the WBEM server uses its own default. + NOTE: Not all servers allow this parameter. MaxObjectCount (term: `Uint32`) - Defines a value that sets the maximum number of instances the - WBEM server can return for this request. any Uint32 number including - zero is valid. + Optional Parameter (default None). Defines a value that sets + the maximum number of instances the WBEM server shall return + for this request. Any Uint32 number including zero is valid. + + If None, nothing is sent to the WBEM server. The DMTF + Specification states that in this case, the server returns + zero instances. + * If non-zero `Uint32` the WBEM server must return no more than the number of instances defined by this value. * If zero the WBEM server must return zero instances. This may be used by a client to restart the timeout. - * If `None` a value of zero is sent to the WBEM server. :Returns: - A tuple containing: - * A list of :class:`~pywbem.CIMInstance` objects + A namedtuple containing: + * instances A list of :class:`~pywbem.CIMInstance` objects that are representations of the enumerated instances. - * instance of enumeration_context which contains - information about the state returned by the successfully - completed operation. This object may be queried with - the more() method to determine if this response - terminates the enumeration sequence. + * end_of-sequence (:class:`py:bool`). If `True` this enumeration + session is complete. The WBEM server has sent all of the + instances possible and has closed the enumeration session. + * enumeration_context (tuple of (:term:`string`) and `namespace`. + `None` if the end-of-sequence == False. Otherwise this is + the context for the next operation (PullInstancesWith Path + or CloseEnumeration) of this enumeration session. + This should be considered as an opaque value. :Exceptions: See the list of exceptions described in `WBEMConnection`. """ - if namespace is None: - namespace = self.default_namespace + if namespace is None and isinstance(ClassName, CIMClassName): + namespace = ClassName.namespace + namespace = self._iparam_namespace_from(namespace) + classname = self._iparam_classname(ClassName) result = self._imethodcall( 'OpenEnumerateInstances', namespace, - ClassName=CIMClassName(ClassName), + ClassName=classname, LocalOnly=LocalOnly, DeepInheritance=DeepInheritance, IncludeQualifiers=IncludeQualifiers, @@ -1533,66 +1558,209 @@ class are to be included in the returned instances, as follows: PropertyList=PropertyList, FilterQueryLanguage=FilterQueryLanguage, FilterQuery=FilterQuery, + OperationTimeout=OperationTimeout, ContinueOnError=ContinueOnError, MaxObjectCount=MaxObjectCount, - pull_op=True, + response_params_reqd=True, **extra) # TODO ks 5/16 why are we setting the namespace attribute?? #[setattr(i.path, 'namespace', namespace) for i in instances] - # returns a tuple containing instances and enumeration state + insts, end_of_sequence, enum_ctxt = \ + self._proc_pull_rslt(result, namespace) - rslt_tuple = self._process_pull_result(result) - enum_ctxt = EnumContext(rslt_tuple[1], rslt_tuple[2], namespace, - MaxObjectCount) + result = namedtuple("result", ["instances", "end_of_sequence", \ + "enumeration_context"]) + return result(insts, end_of_sequence, enum_ctxt) - rtn_tuple = (rslt_tuple[0], enum_ctxt) + def OpenReferencePaths(self, InstanceName, ResultClass=None, + Role=None, + FilterQueryLanguage=None, FilterQuery=None, + OperationTimeout=None, ContinueOnError=None, + MaxObjectCount=None, **extra): + # pylint: disable=invalid-name + """TODO add doc""" - return rtn_tuple + #TODO Limit to instance name. No classname allowed. + namespace = self._iparam_namespace_from(InstanceName) + instancename = self._iparam_objectname(InstanceName) - def OpenEnumerateInstancePaths(self, ClassName, namespace=None, - FilterQueryLanguage=None, FilterQuery=None, - ContinueOnError=None, MaxObjectCount=None, - **extra): + result = self._imethodcall( + 'OpenReferencePaths', + namespace, + InstanceName=instancename, + ResultClass=self._iparam_classname(ResultClass), + Role=Role, + FilterQueryLanguage=FilterQueryLanguage, + FilterQuery=FilterQuery, + OperationTimeout=OperationTimeout, + ContinueOnError=ContinueOnError, + MaxObjectCount=MaxObjectCount, + response_params_reqd=True, + **extra) + + # TODO ks 5/16 why are we setting the namespace attribute?? + #[setattr(i.path, 'namespace', namespace) for i in instances] + + insts, end_of_sequence, enum_ctxt = \ + self._proc_pull_rslt(result, namespace) + + result = namedtuple("result", ["instances", "end_of_sequence", \ + "enumeration_context"]) + return result(insts, end_of_sequence, enum_ctxt) + + def OpenReferences(self, InstanceName, ResultClass=None, + Role=None, IncludeQualifiers=None, + IncludeClassOrigin=None, PropertyList=None, + FilterQueryLanguage=None, FilterQuery=None, + OperationTimeout=None, ContinueOnError=None, + MaxObjectCount=None, **extra): # pylint: disable=invalid-name + """TODO add doc""" - """ - TODO Fix this - """ - if namespace is None and isinstance(ClassName, CIMClassName): - namespace = ClassName.namespace - namespace = self._iparam_namespace_from(namespace) - classname = self._iparam_classname(ClassName) + #TODO Limit to instance name. No classname allowed. + namespace = self._iparam_namespace_from(InstanceName) + instancename = self._iparam_objectname(InstanceName) result = self._imethodcall( - 'OpenEnumerateInstancePaths', + 'OpenReferenceInstances', namespace, - ClassName=classname, + InstanceName=instancename, + ResultClass=self._iparam_classname(ResultClass), + Role=Role, + IncludeQualifiers=IncludeQualifiers, + IncludeClassOrigin=IncludeClassOrigin, + PropertyList=PropertyList, + FilterQueryLanguage=FilterQueryLanguage, + FilterQuery=FilterQuery, + OperationTimeout=OperationTimeout, + ContinueOnError=ContinueOnError, + MaxObjectCount=MaxObjectCount, + response_params_reqd=True, + **extra) + + # TODO ks 5/16 why are we setting the namespace attribute?? + #[setattr(i.path, 'namespace', namespace) for i in instances] + + insts, end_of_sequence, enum_ctxt = \ + self._proc_pull_rslt(result, namespace) + + result = namedtuple("result", ["instances", "end_of_sequence", \ + "enumeration_context"]) + return result(insts, end_of_sequence, enum_ctxt) + + def OpenAssociatorPaths(self, InstanceName, AssocClass=None, + ResultClass=None, Role=None, ResultRole=None, + FilterQueryLanguage=None, FilterQuery=None, + OperationTimeout=None, ContinueOnError=None, + MaxObjectCount=None, **extra): + # pylint: disable=invalid-name + """TODO add doc""" + + #TODO Limit to instance name. No classname allowed. + namespace = self._iparam_namespace_from(InstanceName) + instancename = self._iparam_objectname(InstanceName) + + result = self._imethodcall( + 'OpenAssociatorPaths', + namespace, + InstanceName=instancename, + AssocClass=self._iparam_classname(AssocClass), + ResultClass=self._iparam_classname(ResultClass), + Role=Role, + ResultRole=ResultRole, + FilterQueryLanguage=FilterQueryLanguage, + FilterQuery=FilterQuery, + OperationTimeout=OperationTimeout, + ContinueOnError=ContinueOnError, + MaxObjectCount=MaxObjectCount, + response_params_reqd=True, + **extra) + + # TODO ks 5/16 why are we setting the namespace attribute?? + #[setattr(i.path, 'namespace', namespace) for i in instances] + + insts, end_of_sequence, enum_ctxt = \ + self._proc_pull_rslt(result, namespace) + + result = namedtuple("result", ["instances", "end_of_sequence", \ + "enumeration_context"]) + return result(insts, end_of_sequence, enum_ctxt) + + def OpenAssociators(self, InstanceName, AssocClass=None, ResultClass=None, + Role=None, ResultRole=None, IncludeQualifiers=None, + IncludeClassOrigin=None, PropertyList=None, + FilterQueryLanguage=None, FilterQuery=None, + OperationTimeout=None, ContinueOnError=None, + MaxObjectCount=None, **extra): + # pylint: disable=invalid-name + """TODO add doc""" + + #TODO Limit to instance name. No classname allowed. + namespace = self._iparam_namespace_from(InstanceName) + instancename = self._iparam_objectname(InstanceName) + + result = self._imethodcall( + 'OpenAssociatorInstances', + namespace, + InstanceName=instancename, + AssocClass=self._iparam_classname(AssocClass), + ResultClass=self._iparam_classname(ResultClass), + Role=Role, + ResultRole=ResultRole, + IncludeQualifiers=IncludeQualifiers, + IncludeClassOrigin=IncludeClassOrigin, + PropertyList=PropertyList, FilterQueryLanguage=FilterQueryLanguage, FilterQuery=FilterQuery, + OperationTimeout=OperationTimeout, ContinueOnError=ContinueOnError, MaxObjectCount=MaxObjectCount, - pull_op=True, + response_params_reqd=True, **extra) # TODO ks 5/16 why are we setting the namespace attribute?? #[setattr(i.path, 'namespace', namespace) for i in instances] - # returns a tuple containing instances and enumeration state + insts, end_of_sequence, enum_ctxt = \ + self._proc_pull_rslt(result, namespace) - rslt_tuple = self._process_pull_result(result) + result = namedtuple("result", ["instances", "end_of_sequence", \ + "enumeration_context"]) + return result(insts, end_of_sequence, enum_ctxt) - enum_ctxt = EnumContext(rslt_tuple[1], rslt_tuple[2], namespace, - MaxObjectCount) + def OpenExecQuery(self, QueryLanguage, Query, namespace=None, + FilterQueryLanguage=None, FilterQuery=None, + OperationTimeout=None, ContinueOnError=None, + MaxObjectCount=None, **extra): + # pylint: disable=invalid-name + """ TODO add help""" + namespace = self._iparam_namespace_from(namespace) - rtn_tuple = (rslt_tuple[0], enum_ctxt) + result = self._imethodcall( + 'OpenExecQuery', + namespace, + QueryLanguage=QueryLanguage, + Query=Query, + FilterQueryLanguage=FilterQueryLanguage, + FilterQuery=FilterQuery, + OperationTimeout=OperationTimeout, + ContinueOnError=ContinueOnError, + MaxObjectCount=MaxObjectCount, + response_params_reqd=True, + **extra) - return rtn_tuple + #TODO the execquery gets results from tt[2] + insts, end_of_sequence, enum_ctxt = \ + self._proc_pull_rslt(result, namespace) + result = namedtuple("result", ["instances", "end_of_sequence", \ + "enumeration_context"]) + return result(insts, end_of_sequence, enum_ctxt) - def PullInstancesWithPath(self, enum_context, MaxObjectCount=None, + def PullInstancesWithPath(self, enumeration_context, MaxObjectCount, **extra): # pylint: disable=invalid-name @@ -1608,26 +1776,18 @@ def PullInstancesWithPath(self, enum_context, MaxObjectCount=None, Otherwise, this method raises an exception. :Parameters: - enum_context (TODO) - The sequence state variable returned from the previous - operation (open or pull) for the enumeration sequence. + enumeration_context (TODO This is opaque) + The enumeraton session state variable returned from the previous + operation (open or pull) for this enumeration session. MaxObjectCount (term: `Uint32`) Defines a value that sets the maximum number of instances the WBEM server can return for this request. any Uint32 number - including zero is valid. This is used only to change the value - from a previous request of the sequence. If the MaxObjectCount - is to be the same as the previous request it is already defined - bu the enum_sequence_state variable. + including zero is valid. * If non-zero `Uint32` the WBEM server must return no more than the number of instances defined by this value. * If zero the WBEM server must return zero instances. This may be used by a client to restart the timeout. - * If `None` the method uses the max_object_count contained in - the enum_sequence_state object. - - If MaxObjectCount is not `None` the current value is set into - the enum_sequence_state returned from the operation. :Returns: @@ -1646,68 +1806,89 @@ def PullInstancesWithPath(self, enum_context, MaxObjectCount=None, See the list of exceptions described in `WBEMConnection`. """ - if MaxObjectCount is not None: - enum_context = MaxObjectCount + namespace = enumeration_context[1] result = self._imethodcall( 'PullInstancesWithPath', - namespace=enum_context.namespace, - EnumerationContext=enum_context.enumeration_context, - MaxObjectCount=enum_context.max_object_count, - pull_op=True, + namespace=namespace, + EnumerationContext=enumeration_context[0], + MaxObjectCount=MaxObjectCount, + response_params_reqd=True, **extra) - rslt_tuple = self._process_pull_result(result) - - # returns a tuple containing instances and enum sequence state - enum_context.end_of_sequence = rslt_tuple[1] - enum_context.enumeration_context = rslt_tuple[2] + insts, end_of_sequence, enum_ctxt = \ + self._proc_pull_rslt(result, namespace) - rtn_tuple = (rslt_tuple[0], enum_context) + result = namedtuple("result", ["instances", "end_of_sequence", \ + "enumeration_context"]) + return result(insts, end_of_sequence, enum_ctxt) - return rtn_tuple - - def PullInstancePaths(self, enum_context, MaxObjectCount=None, + def PullInstancePaths(self, enumeration_context, MaxObjectCount=None, \ **extra): # pylint: disable=invalid-name """ - TODO + TODO - Add help """ - if MaxObjectCount is not None: - enum_context = MaxObjectCount + namespace = enumeration_context[1] result = self._imethodcall( 'PullInstancePaths', - namespace=enum_context.namespace, - enumeration_context=enum_context.enumeration_context, - max_object_count=enum_context.max_object_count, - pull_op=True, + namespace=namespace, + EnumerationContext=enumeration_context[0], + MaxObjectCount=MaxObjectCount, + response_params_reqd=True, **extra) - rslt_tuple = self._process_pull_result(result) + paths, end_of_sequence, enum_ctxt = \ + self._proc_pull_rslt(result, namespace) + + result = namedtuple("result", ["paths", "end_of_sequence", \ + "enumeration_context"]) - # returns a tuple containing instances and enum sequence state - enum_context.end_of_sequence = rslt_tuple[1] - enum_context.enumeration_context = rslt_tuple[2] + return result(paths, end_of_sequence, enum_ctxt) - rtn_tuple = (rslt_tuple[0], enum_context) + def PullInstances(self, enumeration_context, MaxObjectCount=None, \ + **extra): + # pylint: disable=invalid-name + + """ + TODO - Add help + """ + namespace = enumeration_context[1] + + result = self._imethodcall( + 'PullInstances', + namespace=namespace, + EnumerationContext=enumeration_context[0], + MaxObjectCount=MaxObjectCount, + response_params_reqd=True, + **extra) - return rtn_tuple + paths, end_of_sequence, enum_ctxt = \ + self._proc_pull_rslt(result, namespace) - def CloseEnumeration(self, enum_sequence_state, **extra): + result = namedtuple("result", ["paths", "end_of_sequence", \ + "enumeration_context"]) + + return result(paths, end_of_sequence, enum_ctxt) + + def CloseEnumeration(self, enumeration_context, **extra): # pylint: disable=invalid-name """ The CloseEnumeration closes an open enumeration sequence performing and early termination of the enumeration sequence. + It passes the namespace defined in the enumeration_context to + the WBEM Server + This method performs the CloseEnumeration operation (see :term:`DSP0200`). This method should not used if the enumeration sequence terminates normally. - If the operation succeeds, this method returns. Otherwise, this method + If the operation succeeds, this method returns. Otherwise, it raises an exception. :Parameters: @@ -1725,8 +1906,8 @@ def CloseEnumeration(self, enum_sequence_state, **extra): self._imethodcall( 'CloseEnumeration', - enum_sequence_state.namespace, - EnumerationContext=enum_sequence_state.enumeration_context, + namespace=enumeration_context[1], + EnumerationContext=enumeration_context[0], **extra) def GetInstance(self, InstanceName, LocalOnly=None, IncludeQualifiers=None, diff --git a/pywbem/tupleparse.py b/pywbem/tupleparse.py index fd5926c52..ae659ba81 100644 --- a/pywbem/tupleparse.py +++ b/pywbem/tupleparse.py @@ -1639,8 +1639,9 @@ def parse_ireturnvalue(tup_tree): VALUE.OBJECTWITHLOCALPATH* | VALUE.OBJECT* | OBJECTPATH* | QUALIFIER.DECLARATION* | VALUE.ARRAY? | VALUE.REFERENCE? | CLASS* | - INSTANCE* | VALUE.NAMEDINSTANCE* | - VALUE.INSTANCEWITHPATH)> + INSTANCE* | INSTANCEPATH* | + VALUE.NAMEDINSTANCE* | + VALUE.INSTANCEWITHPATH*)> """ check_node(tup_tree, 'IRETURNVALUE', [], []) @@ -1655,6 +1656,7 @@ def parse_ireturnvalue(tup_tree): 'QUALIFIER.DECLARATION', 'VALUE.ARRAY', 'VALUE.REFERENCE', 'CLASS', 'INSTANCE', + 'INSTANCEPATH', 'VALUE.NAMEDINSTANCE', 'VALUE.INSTANCEWITHPATH']) diff --git a/testsuite/run_cim_operations.py b/testsuite/run_cim_operations.py index aa8ab70a4..6601c0bba 100755 --- a/testsuite/run_cim_operations.py +++ b/testsuite/run_cim_operations.py @@ -21,11 +21,10 @@ from pywbem.cim_constants import * from pywbem import CIMInstance, CIMInstanceName, CIMClass, CIMClassName, \ CIMProperty, CIMQualifier, CIMQualifierDeclaration, \ - CIMMethod, WBEMConnection, EnumContext, CIMError, \ + CIMMethod, WBEMConnection, CIMError, \ Uint8, Uint16, Uint32, Uint64, \ Sint8, Sint16, Sint32, Sint64, \ Real32, Real64, CIMDateTime -from pywbem.cim_operations import CIMError # Test for decorator for unimplemented tests # decorator is @unittest.skip(UNIMPLEMENTED) @@ -35,6 +34,7 @@ # A class that should be implemented in a wbem server and is used # for testing TEST_CLASS = 'CIM_ComputerSystem' +TEST_CLASS_NAMESPACE = 'root/cimv2' TEST_CLASS_PROPERTY1 = 'Name' TEST_CLASS_PROPERTY2 = 'CreationClassName' @@ -200,42 +200,344 @@ def test_all(self): class PullEnumerateInstances(ClientTest): - def test_enum_complete(self): + def test_open_complete(self): """Simplest invocation. Everything comes back in - initial response + initial response with end-of-sequence == True """ - instances, enum_context = self.cimcall(self.conn.OpenEnumerateInstances, - TEST_CLASS, MaxObjectCount=100) + result = self.cimcall(self.conn.OpenEnumerateInstances, + TEST_CLASS, + MaxObjectCount=100) + insts = result.instances - print('returned len=%s' % len(instances)) - print('enum_contest %s' % repr(enum_context)) - self.assertTrue(enum_context.more() is False) + while not result.end_of_sequence: + result = self.cimcall( + self.conn.PullInstancesWithPath, result.enumeration_context, + MaxObjectCount=1) - self.assertTrue(len(instances) >= 1) + self.assertTrue(len(result.instances) == 1) + insts.append(result.instances) - for i in instances: - self.assertTrue(isinstance(i, CIMInstance)) - self.assertTrue(isinstance(i.path, CIMInstanceName)) - self.assertTrue(len(i.path.namespace) > 0) + for inst in insts: + self.assertTrue(isinstance(inst, CIMInstance)) + self.assertTrue(isinstance(inst.path, CIMInstanceName)) + self.assertTrue(len(inst.path.namespace) > 0) - instances2 = self.cimcall(self.conn.EnumerateInstances, - TEST_CLASS) + insts2 = self.cimcall(self.conn.EnumerateInstances, TEST_CLASS) + + self.assertTrue(len(result.instances) == len(insts2)) + + def test_open_deepinheritance(self): + """Simple OpenEnumerateInstances but with DeepInheritance set + """ + + result = self.cimcall(self.conn.OpenEnumerateInstances, + TEST_CLASS, + MaxObjectCount=100, + DeepInheritance=True) + insts = result.instances + + while not result.end_of_sequence: + result = self.cimcall( + self.conn.PullInstancesWithPath, result.enumeration_context, + MaxObjectCount=100) + + self.assertTrue(len(result.instances) == 1) + insts.append(result.instances) + + for inst in insts: + self.assertTrue(isinstance(inst, CIMInstance)) + self.assertTrue(isinstance(inst.path, CIMInstanceName)) + self.assertTrue(len(inst.path.namespace) > 0) + + insts2 = self.cimcall(self.conn.EnumerateInstances, TEST_CLASS) + + self.assertTrue(len(result.instances) == len(insts2)) + + def test_open_includequalifiers(self): + """Simple OpenEnumerateInstances but with IncludeQualifiers set + """ + + result = self.cimcall(self.conn.OpenEnumerateInstances, + TEST_CLASS, + MaxObjectCount=100, + IncludeQualifiers=True) + + insts = result.instances + + while not result.end_of_sequence: + result = self.cimcall( + self.conn.PullInstancesWithPath, result.enumeration_context, + MaxObjectCount=1) + + self.assertTrue(len(result.instances) <= 1) + insts.append(result.instances) + + for inst in insts: + self.assertTrue(isinstance(inst, CIMInstance)) + self.assertTrue(isinstance(inst.path, CIMInstanceName)) + self.assertTrue(len(inst.path.namespace) > 0) + + insts2 = self.cimcall(self.conn.EnumerateInstances, TEST_CLASS) + + self.assertTrue(len(result.instances) == len(insts2)) + + def test_open_includeclassorigin(self): + """Simple OpenEnumerateInstances but with DeepInheritance set + """ + + result = self.cimcall(self.conn.OpenEnumerateInstances, + TEST_CLASS, + MaxObjectCount=100, + IncludeClassOrigin=True) + + insts = result.instances + + while not result.end_of_sequence: + result = self.cimcall( + self.conn.PullInstancesWithPath, result.enumeration_context, + MaxObjectCount=1) + + self.assertTrue(len(result.instances) <= 1) + insts.append(result.instances) + + for inst in result.instances: + self.assertTrue(isinstance(inst, CIMInstance)) + self.assertTrue(isinstance(inst.path, CIMInstanceName)) + self.assertTrue(len(inst.path.namespace) > 0) + + insts2 = self.cimcall(self.conn.EnumerateInstances, TEST_CLASS) + + self.assertTrue(len(result.instances) == len(insts2)) + + def test_open_complete_with_ns(self): + """Simple call that is complete with just the open and + with explicit namespace + """ + + result = self.cimcall(self.conn.OpenEnumerateInstances, TEST_CLASS, + MaxObjectCount=100, + namespace=TEST_CLASS_NAMESPACE) - self.assertTrue(len(instances) == len(instances2)) + insts = result.instances - ## Call with explicit CIM namespace that exists + while not result.end_of_sequence: + result = self.cimcall( + self.conn.PullInstancesWithPath, result.enumeration_context, + MaxObjectCount=1) - #self.cimcall(self.conn.EnumerateInstances, - #TEST_CLASS, - #namespace=self.conn.default_namespace) + self.assertTrue(len(result.instances) <= 1) + insts.append(result.instances) + + for inst in result.instances: + self.assertTrue(isinstance(inst, CIMInstance)) + self.assertTrue(isinstance(inst.path, CIMInstanceName)) + self.assertTrue(len(inst.path.namespace) > 0) + + insts2 = self.cimcall(self.conn.EnumerateInstances, TEST_CLASS) + + self.assertTrue(len(result.instances) == len(insts2)) def test_bad_namespace(self): """Call with explicit CIM namespace that does not exist""" try: + self.cimcall(self.conn.OpenEnumerateInstances, + TEST_CLASS, + namespace='root/blah') + + except CIMError, arg: + if arg[0] != CIM_ERR_INVALID_NAMESPACE: + raise + + def test_zero_open(self): + """ Test with default on open. Should return zero instances + """ + result = self.cimcall(self.conn.OpenEnumerateInstances, TEST_CLASS) + + self.assertTrue(result.end_of_sequence is False) + self.assertTrue(len(result.instances) == 0) + + result = self.cimcall(self.conn.PullInstancesWithPath, + result.enumeration_context, + MaxObjectCount=100) + + self.assertTrue(result.end_of_sequence is True) + + def test_close_early(self): + """"Close enumeration session after initial Open""" + result = self.cimcall(self.conn.OpenEnumerateInstances, TEST_CLASS) + self.assertFalse(result.end_of_sequence) + self.assertTrue(len(result.instances) == 0) + + self.cimcall(self.conn.CloseEnumeration, result.enumeration_context) + + def test_get_onebyone(self): + """Get instances with MaxObjectCount = 1)""" + + result = self.cimcall( + self.conn.OpenEnumerateInstances, 'CIM_ManagedElement', + MaxObjectCount=1) + + self.assertTrue(len(result.instances) <= 1) + + self.assertFalse(result.end_of_sequence) + + insts = result.instances + + while not result.end_of_sequence: + result = self.cimcall( + self.conn.PullInstancesWithPath, result.enumeration_context, + MaxObjectCount=1) + + self.assertTrue(len(result.instances) <= 1) + insts.append(result.instances) + + # get with EnumInstances and compare returns + insts2 = self.cimcall(self.conn.EnumerateInstances, + 'CIM_ManagedElement') + + if (len(insts) != len(insts2)): + print('ERROR. lengths do not match insts=%s insts2=%s' % \ + (len(insts), len(insts2))) + self.assertTrue(len(insts) == len(insts2)) + + def test_open_continueonerror(self): + """ContinueOnError. Pegasus does not support this parameter + """ + try: self.cimcall(self.conn.OpenEnumerateInstances, + TEST_CLASS, + MaxObjectCount=100, + ContinueOnError=True) + + except CIMError, arg: + if arg[0] != CIM_ERR_NOT_SUPPORTED: + raise + + def test_open_invalid_filterquerylanguage(self): + """Test invalid FilterQueryLanguage parameter """ + try: + self.cimcall(self.conn.OpenEnumerateInstances, + TEST_CLASS, + MaxObjectCount=100, + FilterQueryLanguage='BLAH') + # TODO why does pegasus return this + except CIMError, arg: + if arg[0] != CIM_ERR_FAILED: + raise + + def test_open_invalid_filterquerylanguagewithfilter(self): + """Test valid filter but invalid FilterQueryLanguage""" + try: + self.cimcall(self.conn.OpenEnumerateInstances, + TEST_CLASS, + MaxObjectCount=100, + FilterQueryLanguage='BLAH', + FilterQuery="p = 4") + + except CIMError, arg: + if arg[0] != CIM_ERR_QUERY_LANGUAGE_NOT_SUPPORTED: + raise + + def test_open_invalid_filterquery(self): + """Test Invalid FilterQuery + """ + try: + self.cimcall(self.conn.OpenEnumerateInstances, + TEST_CLASS, + MaxObjectCount=100, + FilterQueryLanguage='DMTF:FQL', + FilterQuery="blah") + + except CIMError, arg: + if arg[0] != CIM_ERR_INVALID_QUERY: + raise + + def test_open_filter_returnsnone(self): + """Test with filter that filters out all responses + """ + + filter_statement = "%s = 'blah'" % TEST_CLASS_PROPERTY2 + result = self.cimcall(self.conn.OpenEnumerateInstances, + TEST_CLASS, + MaxObjectCount=100, + FilterQueryLanguage='DMTF:FQL', + FilterQuery=filter_statement) + self.assertTrue(result.end_of_sequence) + self.assertTrue(len(result.instances) == 0) + + def test_open_timeoutset(self): + """Test with filter that filters out all responses + """ + + result = self.cimcall(self.conn.OpenEnumerateInstances, + TEST_CLASS, + MaxObjectCount=100, + OperationTimeout=10) + self.assertTrue(result.end_of_sequence) + self.assertTrue(len(result.instances) == 1) + +class PullEnumerateInstancePaths(ClientTest): + + def test_open_complete(self): + """Simplest invocation. Everything comes back in + initial response with end-of-sequence == True + Cannot depend on open returning anything because of + server timing. + """ + + result = self.cimcall(self.conn.OpenEnumerateInstancePaths, + TEST_CLASS, + MaxObjectCount=100) + + paths = result.paths + + while not result.end_of_sequence: + result = self.cimcall(self.conn.PullInstancePaths, + result.enumeration_context, + MaxObjectCount=1) + + self.assertTrue(len(result.paths) <= 1) + paths.append(result.paths) + + paths2 = self.cimcall(self.conn.EnumerateInstanceNames, TEST_CLASS) + + self.assertTrue(len(result.paths) == len(paths2)) + + def test_open_complete_with_ns(self): + """Simplest invocation. Everything comes back in + initial response with end-of-sequence == True + """ + + result = self.cimcall(self.conn.OpenEnumerateInstancePaths, + TEST_CLASS, namespace=TEST_CLASS_NAMESPACE, + MaxObjectCount=100) + + paths = result.paths + + while not result.end_of_sequence: + result = self.cimcall(self.conn.PullInstancePaths, + result.enumeration_context, + MaxObjectCount=1) + + self.assertTrue(len(result.paths) <= 1) + paths.append(result.paths) + + paths2 = self.cimcall(self.conn.EnumerateInstanceNames, TEST_CLASS) + + if (len(paths) != len(paths2)): + print('ERROR result.paths len %s ne paths2 len %s' % \ + (len(paths, len(paths2)))) + + self.assertTrue(len(paths) == len(paths2)) + + def test_bad_namespace(self): + """Call with explicit CIM namespace that does not exist""" + + try: + self.cimcall(self.conn.OpenEnumerateInstancePaths, TEST_CLASS, namespace='root/blah') @@ -246,46 +548,85 @@ def test_bad_namespace(self): def test_zero_open(self): """ TODO """ - instances, enum_context = self.cimcall(self.conn.OpenEnumerateInstances, - TEST_CLASS) + result = self.cimcall(self.conn.OpenEnumerateInstancePaths, TEST_CLASS) - print('returned len=%s' % len(instances)) - print('enum_context %s' % repr(enum_context)) - self.assertTrue(enum_context.more() is True) - enum_context.set_max_object_count(100) - print('enum_context %s' % repr(enum_context)) - instances, enum_context = self.cimcall(self.conn.PullInstancesWithPath, - enum_context) - self.assertTrue(enum_context.more() is False) + self.assertFalse(result.end_of_sequence) - print('returned len=%s' % len(instances)) - print('enum_contest %s' % repr(enum_context)) + # Pull the remainder of the paths + result = self.cimcall(self.conn.PullInstancePaths, + result.enumeration_context, + MaxObjectCount=100) + + self.assertTrue(result.end_of_sequence) def test_close_early(self): - """"TODO""" - instances, enum_context = self.cimcall(self.conn.OpenEnumerateInstances, - TEST_CLASS) - self.assertTrue(enum_context.more() is True) - self.assertTrue(len(instances) == 0) - self.cimcall(self.conn.CloseEnumeration, enum_context) + """"Close enumeration session after initial Open""" + result = self.cimcall(self.conn.OpenEnumerateInstancePaths, TEST_CLASS) + + self.assertFalse(result.end_of_sequence) + self.assertTrue(len(result.paths) == 0) + + self.cimcall(self.conn.CloseEnumeration, result.enumeration_context) def test_get_onebyone(self): """Get instances with MaxObjectCount = 1)""" - instances, enum_context = self.cimcall(self.conn.OpenEnumerateInstances, - 'CIM_ManagedElement', - MaxObjectCount=1) - self.assertTrue(len(instances) == 1) + result = self.cimcall( + self.conn.OpenEnumerateInstancePaths, 'CIM_ManagedElement', + MaxObjectCount=1) + + self.assertFalse(result.end_of_sequence) + + paths = result.paths + + while not result.end_of_sequence: + result = self.cimcall(self.conn.PullInstancePaths, + result.enumeration_context, + MaxObjectCount=1) + + self.assertTrue(len(result.paths) <= 1) + paths.append(result.paths) - while enum_context.more(): - more_instances, enum_context = self.cimcall( - self.conn.PullInstancesWithPath, enum_context) - self.assertTrue(len(more_instances) == 1) - instances.append(more_instances) + # get with EnumInstanceNamess and compare returns + paths2 = self.cimcall(self.conn.EnumerateInstanceNames, + 'CIM_ManagedElement') + self.assertTrue(len(paths) == len(paths2)) + +class PullReferences(ClientTest): + + def test_all_instances_in_ns(self): + """Simplest invocation. Everything comes back in + initial response with end-of-sequence == True + """ + # get all instances under CIM_ManagedElement + paths = self.cimcall(self.conn.EnumerateInstanceNames, + 'CIM_ManagedElement') - instances2 = self.cimcall(self.conn.EnumerateInstances, - 'CIM_ManagedElement') - self.assertTrue(len(instances) == len(instances2)) + for pathi in paths: + result = self.cimcall(self.conn.OpenReferences, + pathi, + MaxObjectCount=100) + + insts = result.instances + + while not result.end_of_sequence: + result = self.cimcall( + self.conn.PullInstancesWithPath, result.enumeration_context, + MaxObjectCount=1) + + self.assertTrue(len(result.instances) <= 1) + insts.append(result.instances) + + for inst in result.instances: + self.assertTrue(isinstance(inst, CIMInstanceName)) + self.assertTrue(len(inst.namespace) > 0) + + insts2 = self.cimcall(self.conn.References, pathi) + if len(insts2) != 0: + print('References %s count %s' % (pathi, len(insts2))) + self.assertTrue(len(insts) == len(insts2)) + #TODO ks 5/30 2016 add tests here + #Do this as a loop for all instances above. class ExecQuery(ClientTest): @@ -666,7 +1007,8 @@ def test_all(self): # Call on named instance - inst_names = self.cimcall(self.conn.EnumerateInstanceNames, TEST_CLASS) + inst_names = self.cimcall(self.conn.EnumerateInstanceNames, + TEST_CLASS) self.assertTrue(len(inst_names) >= 1) inst_name = inst_names[0] # Pick the first returned instance diff --git a/testsuite/test_client/openenuminstances.yaml b/testsuite/test_client/openenuminstances.yaml index 6c6767c9b..c70bacc6b 100644 --- a/testsuite/test_client/openenuminstances.yaml +++ b/testsuite/test_client/openenuminstances.yaml @@ -1,4 +1,3 @@ - - name: OpenEnumerateInstances1 description: OpenEnumerateInstances succeeds