Skip to content

Commit

Permalink
Made local WBEM URIs compliant to DSP0207.
Browse files Browse the repository at this point in the history
Details:
* DSP0207 requires that there is always a leading slash in local WBEM URIs
  (even when namespace is not set) and always a colon before the class name
  (even when namespace is not set).
  The str() functions of CIMInstanceName and CIMClassName so far omitted that
  slash for local WBEM URIs, and omitted the colon for local WBEM URIs that
  had no namespace set.
  This was fixed by putting the code into new public methods to_wbem_uri()
  where the generation of the leading slash can be controlled via a parameter.
  The str() functions now use to_wbem_uri() with the parameter set so that a
  leading slash is now generated for local WBEM URIs; and that the colon is
  now always generated. This fixes the non-compliance with DSP0207.
  The internal function get_cimobject_header() which determines the value of
  the CIMObject header field needs the WBEM URI without a leading slash (it is
  always local) case, and calls to_wbem_uri() with the parameter set
  accordingly.
* Improved the way real values in keybindings are generated in WBEM URIs, so
  that the shortest decimal representation is used that does not change the
  binary value (only for Python 2.7 and 3.1 and higher).
* Added testcases for the new to_wbem_uri() methods, also covering the testing
  of the two __str__() methods. Removed the old testcases for the two __str__()
  methods, because they were not very exhaustive.
* Adjusted a testcase in CIMInstanceToMOF.test_all() to accomodate for the
  now compliant WBEM URI returned by CIMInstanceName.str() which is also used
  in CIMInstanceName.tomof().

Signed-off-by: Andreas Maier <maiera@de.ibm.com>
  • Loading branch information
andy-maier committed Jan 2, 2018
1 parent dde65ee commit 1fbc58f
Show file tree
Hide file tree
Showing 4 changed files with 831 additions and 200 deletions.
17 changes: 17 additions & 0 deletions docs/changes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,13 @@ Enhancements
exceptions are surfaced by WBEMConnection methods as a
pywbem.ConnectionError (issue #916).

* Added new methods `CIMInstanceName.to_wbem_uri()` and
`CIMClassName.to_wbem_uri()` that return the path as a WBEM URI compliant to
DSP0207, in order to make the conversion explicit instead of relying
on the string conversion methods. The string conversion methods are still
supported, but see the fix to make them compliant to DSP0207 (added as part
of fixing issue #928).

* Docs: Editorial improvements in the documentation (links, typos, formatting).

* Docs: Clarifications and small fixes in the documentation of the
Expand Down Expand Up @@ -236,6 +243,16 @@ Bug fixes
the return value of CIM methods, by raising `ValueError` if
`CIMMethod.return_value` is initialized or set to "reference".

* Fixed the `CIMInstanceName.__str__()` and `CIMClassName.__str__()` methods to
now return WBEM URI strings that are compliant to DSP0207. Changes include:

* Local WBEM URIs (i.e. when authority/host is not set) now have a leading
slash. That leading slash was previously omitted.
* WBEM URIs with no namespace set now have a colon before the class name.
Previously, the colon was produced only when a namespace was set.

Issue #928.

Cleanup
^^^^^^^

Expand Down
10 changes: 2 additions & 8 deletions pywbem/cim_http.py
Original file line number Diff line number Diff line change
Expand Up @@ -967,17 +967,11 @@ def get_cimobject_header(obj):

# Local class path
if isinstance(obj, CIMClassName):
return '%s:%s' % (obj.namespace, obj.classname)
return obj.to_wbem_uri(omit_local_slash=True)

# Local instance path
if isinstance(obj, CIMInstanceName):
# In order to represent the instance paths of associations properly, a
# recursive approach of escaping the key values is needed. The
# following code draws on the WBEM URI string generated by
# CIMInstanceName.__str__(), which happens to not produce the leading
# slash (by mistake?). If and when that is changed, this code here
# also needs to be adjusted.
return str(obj)
return obj.to_wbem_uri(omit_local_slash=True)

raise TypeError("Invalid object type %s to generate CIMObject header "
"value from" % type(obj))
236 changes: 187 additions & 49 deletions pywbem/cim_obj.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,8 @@
from .config import DEBUG_WARNING_ORIGIN
from .cim_types import _CIMComparisonMixin, type_from_name, cimtype, \
atomic_to_cim_xml, CIMType, CIMDateTime, Uint8, Sint8, Uint16, Sint16, \
Uint32, Sint32, Uint64, Sint64, Real32, Real64, number_types
Uint32, Sint32, Uint64, Sint64, Real32, Real64, number_types, \
CIMInt, CIMFloat, _Longint

if six.PY2:
# pylint: disable=wrong-import-order
Expand Down Expand Up @@ -1140,40 +1141,10 @@ def _cmp(self, other):
def __str__(self):
"""
Return the untyped WBEM URI of the CIM instance path represented
by the :class:`~pywbem.CIMInstanceName` object.
The returned WBEM URI is consistent with :term:`DSP0207`.
The key properties in the returned WBEM URI will be ordered by their
names.
by the :class:`~pywbem.CIMInstanceName` object, as returned by
:meth:`~pywbem.CIMInstanceName.to_wbem_uri`.
"""

ret_str = ''

if self.host is not None:
ret_str += '//%s/' % self.host

if self.namespace is not None:
ret_str += '%s:' % self.namespace

ret_str += '%s.' % self.classname

for key in sorted(self.keybindings.iterkeys()):
value = self.keybindings[key]

ret_str += '%s=' % key

if isinstance(value, (number_types, bool)):
ret_str += str(value)
elif isinstance(value, CIMInstanceName):
ret_str += '"%s"' % str(value).replace('\\', '\\\\').replace(
'"', '\\"')
else:
ret_str += '"%s"' % value

ret_str += ','

return ret_str[:-1]
return self.to_wbem_uri()

def __repr__(self):
"""
Expand Down Expand Up @@ -1472,6 +1443,124 @@ def from_wbem_uri(wbem_uri):
return CIMInstanceName(classname, host=host, namespace=nm_space,
keybindings=key_bindings)

def to_wbem_uri(self, omit_local_slash=False):
"""
Return the untyped WBEM URI of the CIM instance path represented
by the :class:`~pywbem.CIMInstanceName` object.
The returned WBEM URI is consistent with :term:`DSP0207`, and contains
its components as follows:
* it does not contain a namespace type (URI scheme).
* it contains an authority component according to the
:attr:`~pywbem.CIMInstanceName.host` attribute, if that is not
`None`. Othwerise, it does not contain the authority component.
* it contains a namespace component according to the
:attr:`~pywbem.CIMInstanceName.namespace` attribute, if that is not
`None`. Othwerise, it does not contain the namespace component.
* it contains a class name component according to the
:attr:`~pywbem.CIMInstanceName.classname` attribute.
* it contains keybindings according to the
:attr:`~pywbem.CIMInstanceName.keybindings` attribute, with
keys ordered by their names.
Parameters:
omit_local_slash (bool): Omit the leading slash in "local" WBEM URIs
(that is, in WBEM URIs without authority component). Note that
:term:`DSP0207` requires a leading slash in "local" WBEM URIs.
Examples:
* With authority and namespace::
//jdd:test@acme.com:5989/cimv2/test:CIM_RegisteredProfile.InstanceID="acme.1"
//acme.com/cimv2/test:CIM_RegisteredProfile.InstanceID="acme.1"
//acme.com/root/cimv2:CIM_ComputerSystem.CreationClassName="ACME_CS",Name="sys1"
* Without authority but with namespace::
/cimv2/test:CIM_RegisteredProfile.InstanceID="acme.1"
* Without authority and without namespace::
/:CIM_RegisteredProfile.InstanceID="acme.1"
/:CIM_SubProfile.Main="/:CIM_RegisteredProfile.InstanceID=\"acme.1\"",Sub="/:CIM_RegisteredProfile.InstanceID=\"acme.2\""
Returns:
:term:`unicode string`: Untyped WBEM URI of the CIM instance path.
Raises:
TypeError: Invalid type in keybindings
"""

ret = []

if self.host is not None:
ret.append('//')
ret.append(self.host)

if self.host is not None or not omit_local_slash:
ret.append('/')

if self.namespace is not None:
ret.append(self.namespace)

ret.append(':')
ret.append(self.classname)

ret.append('.')
for key in sorted(self.keybindings.iterkeys()):
value = self.keybindings[key]

ret.append(key)
ret.append('=')

if isinstance(value, bool):
# boolean
# Note that in Python a bool is an int, so test for bool first
ret.append(str(value).upper())
elif isinstance(value, (CIMFloat, float)):
# realNN
# Since Python 2.7 and Python 3.1, repr() prints float numbers
# with the shortest representation that does not change its
# value. When needed, it shows up to 17 significant digits,
# which is the precision needed to round-trip double precision
# IEE-754 floating point numbers between decimal and binary
# without loss.
ret.append(repr(value))
elif isinstance(value, (CIMInt, int, _Longint)):
# intNN
ret.append(str(value))
elif isinstance(value, CIMInstanceName):
# reference
ret.append('"')
ret.append(value.to_wbem_uri().
replace('\\', '\\\\').
replace('"', '\\"'))
ret.append('"')
elif isinstance(value, CIMDateTime):
# datetime
ret.append('"')
ret.append(str(value))
ret.append('"')
elif isinstance(value, six.string_types):
# string, char16
ret.append('"')
ret.append(value.
replace('\\', '\\\\').
replace('"', '\\"'))
ret.append('"')
else:
raise TypeError("Invalid type %s in keybinding value: %s=%r" %
(type(value), key, value))
ret.append(',')

del ret[-1]

return _ensure_unicode(''.join(ret))


class CIMInstance(_CIMComparisonMixin):
"""
Expand Down Expand Up @@ -2193,22 +2282,10 @@ def _cmp(self, other):
def __str__(self):
"""
Return the untyped WBEM URI of the CIM class path represented by the
:class:`~pywbem.CIMClassName` object.
The returned WBEM URI is consistent with :term:`DSP0207`.
:class:`~pywbem.CIMClassName` object, as returned by
:meth:`~pywbem.CIMClassName.to_wbem_uri`.
"""

ret_str = ''

if self.host is not None:
ret_str += '//%s/' % self.host

if self.namespace is not None:
ret_str += '%s:' % self.namespace

ret_str += self.classname

return ret_str
return self.to_wbem_uri()

def __repr__(self):
"""
Expand Down Expand Up @@ -2330,6 +2407,67 @@ def from_wbem_uri(wbem_uri):

return CIMClassName(head, host=host, namespace=nm_space)

def to_wbem_uri(self, omit_local_slash=False):
"""
Return the untyped WBEM URI of the CIM class path represented
by the :class:`~pywbem.CIMClassName` object.
The returned WBEM URI is consistent with :term:`DSP0207`, and contains
its components as follows:
* it does not contain a namespace type (URI scheme).
* it contains an authority component according to the
:attr:`~pywbem.CIMInstanceName.host` attribute, if that is not
`None`. Othwerise, it does not contain the authority component.
* it contains a namespace component according to the
:attr:`~pywbem.CIMInstanceName.namespace` attribute, if that is not
`None`. Othwerise, it does not contain the namespace component.
* it contains a class name component according to the
:attr:`~pywbem.CIMInstanceName.classname` attribute.
Parameters:
omit_local_slash (bool): Omit the leading slash in "local" WBEM URIs
(that is, in WBEM URIs without authority component). Note that
:term:`DSP0207` requires a leading slash in "local" WBEM URIs.
Examples:
* With authority and namespace::
//jdd:test@acme.com:5989/cimv2/test:CIM_RegisteredProfile
//acme.com/cimv2/test:CIM_RegisteredProfile
* Without authority but with namespace::
/cimv2/test:CIM_RegisteredProfile
* Without authority and without namespace::
/:CIM_RegisteredProfile
Returns:
:term:`unicode string`: Untyped WBEM URI of the CIM class path.
"""

ret = []

if self.host is not None:
ret.append('//')
ret.append(self.host)

if self.host is not None or not omit_local_slash:
ret.append('/')

if self.namespace is not None:
ret.append(self.namespace)

ret.append(':')
ret.append(self.classname)

return _ensure_unicode(''.join(ret))


class CIMClass(_CIMComparisonMixin):
"""
Expand Down

0 comments on commit 1fbc58f

Please sign in to comment.