Skip to content

Commit

Permalink
Strip lead/trailing slashes from namespace; Fix namespace in OpenQuer…
Browse files Browse the repository at this point in the history
…yInstances

Details:

- Leading and trailing slash characters in namespaces are now stripped
  before the namespace is stored as an attribute or used.
  This was done in the following places:

  - In `CIMClassName` for the `namespace` attribute, when initializing an
    object or when setting the attribute.
  - In `CIMInstanceName` for the `namespace` attribute, when initializing an
    object or when setting the attribute.
  - In `WBEMConnection` for the `default_namespace` attribute, when
    initializing an object or when setting the attribute. As part of this,
    changed the public attribute to become a property with getter/setter.
  - In all operations of `WBEMConnection` for the namespace that is
    specified as an argument to the operation method.

- Added according unit testcases for `CIMClassName`, `CIMInstanceName`, and
  `WBEMConnection`.

- Extended the testclient yaml testcases so that each operation is called
  with leading and trailing slashes in namespace for all possibilities how
  the namespace can be passed.

- Fixed an error in OpenQueryInstances where the specified namespace
  had been ignored.

Signed-off-by: Andreas Maier <maiera@de.ibm.com>
  • Loading branch information
andy-maier committed Jan 23, 2018
1 parent 32dd5d6 commit c760648
Show file tree
Hide file tree
Showing 38 changed files with 5,587 additions and 183 deletions.
6 changes: 6 additions & 0 deletions docs/changes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -416,6 +416,12 @@ Bug fixes
For methods, that attribute is called 'return_type'. Testcases for methods
and parameters have now been added.

* Fixed the issue that leading and trailing slash characters in namespace
names were preserved. This was leading to empty NAMESPACE.NAME elements,
which can be rejected by WBEM servers. Now, leading and trailing slash
characters on namespace names are stripped off in pywbem before sending
the request to the server. (Issue #255).

Cleanup
^^^^^^^

Expand Down
12 changes: 12 additions & 0 deletions pywbem/cim_obj.py
Original file line number Diff line number Diff line change
Expand Up @@ -1662,6 +1662,7 @@ def __init__(self, classname, keybindings=None, host=None, namespace=None):
same-named attribute in the ``CIMInstanceName`` object will also be
`None`.
Leading and trailing slash characters will be stripped.
The lexical case of the string is preserved. Object comparison and
hash value calculation are performed case-insensitively.
Expand Down Expand Up @@ -1790,6 +1791,11 @@ def namespace(self, namespace):
"""Setter method; for a description see the getter method."""
# pylint: disable=attribute-defined-outside-init
self._namespace = _ensure_unicode(namespace)
if self._namespace is not None:
# In Python 3, a byte string cannot be stripped by a unicode char
# Therefore, the stripping needs to be done after the unicode
# conversion.
self._namespace = self._namespace.strip('/')

@property
def host(self):
Expand Down Expand Up @@ -3206,6 +3212,7 @@ def __init__(self, classname, host=None, namespace=None):
same-named attribute in the ``CIMClassName`` object will also be
`None`.
Leading and trailing slash characters will be stripped.
The lexical case of the string is preserved. Object comparison and
hash value calculation are performed case-insensitively.
Expand Down Expand Up @@ -3266,6 +3273,11 @@ def namespace(self, namespace):
"""Setter method; for a description see the getter method."""
# pylint: disable=attribute-defined-outside-init
self._namespace = _ensure_unicode(namespace)
if self._namespace is not None:
# In Python 3, a byte string cannot be stripped by a unicode char
# Therefore, the stripping needs to be done after the unicode
# conversion.
self._namespace = self._namespace.strip('/')

@property
def host(self):
Expand Down
99 changes: 94 additions & 5 deletions pywbem/cim_operations.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@
CIM_ERR_NOT_SUPPORTED
from .cim_types import CIMType, CIMDateTime, atomic_to_cim_xml
from .cim_obj import CIMInstance, CIMInstanceName, CIMClass, CIMClassName, \
CIMParameter, NocaseDict, tocimxml, tocimobj
CIMParameter, NocaseDict, tocimxml, tocimobj, _ensure_unicode
from .cim_http import get_cimobject_header, wbem_request
from .tupleparse import parse_cim
from .tupletree import xml_to_tupletree_sax
Expand Down Expand Up @@ -469,7 +469,11 @@ def __init__(self, url, creds=None, default_namespace=DEFAULT_NAMESPACE,
Name of the CIM namespace to be used by default (if no namespace
is specified for an operation).
Default: :data:`~pywbem.cim_constants.DEFAULT_NAMESPACE`.
Leading and trailing slash characters will be stripped. The lexical
case will be preserved.
`None` will cause the following namespace to be used as a default
namespace: :data:`~pywbem.cim_constants.DEFAULT_NAMESPACE`.
x509 (:class:`py:dict`):
:term:`X.509` client certificate and key file to be presented
Expand Down Expand Up @@ -645,7 +649,7 @@ def __init__(self, url, creds=None, default_namespace=DEFAULT_NAMESPACE,
self.verify_callback = verify_callback
self.ca_certs = ca_certs
self.no_verification = no_verification
self.default_namespace = default_namespace
self.default_namespace = default_namespace # setter
self.timeout = timeout

self.debug = False
Expand Down Expand Up @@ -682,6 +686,31 @@ def __init__(self, url, creds=None, default_namespace=DEFAULT_NAMESPACE,
self._last_operation_time = None
self._last_server_response_time = None

@property
def default_namespace(self):
"""
:term:`unicode string`: Name of the CIM namespace to be used by default
(if no namespace is specified for an operation).
`None` means that the following namespace will be used as a default
namespace: :data:`~pywbem.cim_constants.DEFAULT_NAMESPACE`.
Leading and trailing slash characters will be stripped. The lexical
case will be preserved.
This attribute is settable. For details, see the description of the
same-named constructor parameter.
"""
return self._default_namespace

@default_namespace.setter
def default_namespace(self, default_namespace):
"""Setter method; for a description see the getter method."""
# pylint: disable=attribute-defined-outside-init
if default_namespace is not None:
default_namespace = default_namespace.strip('/')
self._default_namespace = _ensure_unicode(default_namespace)

@property
def operation_recorders(self):
"""
Expand Down Expand Up @@ -1365,7 +1394,7 @@ def _iparam_namespace_from_namespace(self, obj):
imethodcall()."""

if isinstance(obj, six.string_types):
namespace = obj
namespace = obj.strip('/')
elif obj is None:
namespace = obj
else:
Expand Down Expand Up @@ -1479,6 +1508,9 @@ def EnumerateInstanceNames(self, ClassName, namespace=None, **extra):
namespace (:term:`string`):
Name of the CIM namespace to be used (case independent).
Leading and trailing slash characters will be stripped. The lexical
case will be preserved.
If `None`, the namespace of the `ClassName` parameter will be used,
if specified as a :class:`~pywbem.CIMClassName` object. If that is
also `None`, the default namespace of the connection will be used.
Expand Down Expand Up @@ -1576,6 +1608,9 @@ def EnumerateInstances(self, ClassName, namespace=None, LocalOnly=None,
namespace (:term:`string`):
Name of the CIM namespace to be used (case independent).
Leading and trailing slash characters will be stripped. The lexical
case will be preserved.
If `None`, the namespace of the `ClassName` parameter will be used,
if specified as a :class:`~pywbem.CIMClassName` object. If that is
also `None`, the default namespace of the connection will be used.
Expand Down Expand Up @@ -1859,6 +1894,9 @@ def IterEnumerateInstances(self, ClassName, namespace=None,
namespace (:term:`string`):
Name of the CIM namespace to be used (case independent).
Leading and trailing slash characters will be stripped. The lexical
case will be preserved.
If `None`, the namespace of the `ClassName` parameter will be used,
if specified as a :class:`~pywbem.CIMClassName` object. If that is
also `None`, the default namespace of the connection will be used.
Expand Down Expand Up @@ -2200,6 +2238,9 @@ def IterEnumerateInstancePaths(self, ClassName, namespace=None,
namespace (:term:`string`):
Name of the CIM namespace to be used (case independent).
Leading and trailing slash characters will be stripped. The lexical
case will be preserved.
If `None`, the namespace of the `ClassName` parameter will be used,
if specified as a :class:`~pywbem.CIMClassName` object. If that is
also `None`, the default namespace of the connection will be used.
Expand Down Expand Up @@ -3629,6 +3670,9 @@ def IterQueryInstances(self, FilterQueryLanguage, FilterQuery,
namespace (:term:`string`):
Name of the CIM namespace to be used (case independent).
Leading and trailing slash characters will be stripped. The lexical
case will be preserved.
If `None`, the default namespace of the connection object will be
used.
Expand Down Expand Up @@ -3870,6 +3914,9 @@ def OpenEnumerateInstancePaths(self, ClassName, namespace=None,
namespace (:term:`string`):
Name of the CIM namespace to be used (case independent).
Leading and trailing slash characters will be stripped. The lexical
case will be preserved.
If `None`, the namespace of the `ClassName` parameter will be used,
if specified as a :class:`~pywbem.CIMClassName` object. If that is
also `None`, the default namespace of the connection will be used.
Expand Down Expand Up @@ -4090,6 +4137,9 @@ def OpenEnumerateInstances(self, ClassName, namespace=None, LocalOnly=None,
namespace (:term:`string`):
Name of the CIM namespace to be used (case independent).
Leading and trailing slash characters will be stripped. The lexical
case will be preserved.
If `None`, the namespace of the `ClassName` parameter will be used,
if specified as a :class:`~pywbem.CIMClassName` object. If that is
also `None`, the default namespace of the connection will be used.
Expand Down Expand Up @@ -5378,6 +5428,9 @@ def OpenQueryInstances(self, FilterQueryLanguage, FilterQuery,
namespace (:term:`string`):
Name of the CIM namespace to be used (case independent).
Leading and trailing slash characters will be stripped. The lexical
case will be preserved.
If `None`, the default namespace of the connection object will be
used.
Expand Down Expand Up @@ -5520,7 +5573,7 @@ def _GetQueryRsltClass(result):
try:

stats = self.statistics.start_timer(method_name)
namespace = self._iparam_namespace_from_objectname(namespace)
namespace = self._iparam_namespace_from_namespace(namespace)

result = self._imethodcall(
method_name,
Expand Down Expand Up @@ -6446,6 +6499,9 @@ class for the new instance.
Name of the CIM namespace to be used (case independent).
Leading and trailing slash characters will be stripped. The lexical
case will be preserved.
Keyword Arguments:
extra :
Expand Down Expand Up @@ -7425,6 +7481,9 @@ def ExecQuery(self, QueryLanguage, Query, namespace=None, **extra):
namespace (:term:`string`):
Name of the CIM namespace to be used (case independent).
Leading and trailing slash characters will be stripped. The lexical
case will be preserved.
If `None`, the default namespace of the connection object will be
used.
Expand Down Expand Up @@ -7518,6 +7577,9 @@ def EnumerateClassNames(self, namespace=None, ClassName=None,
Name of the namespace in which the class names are to be
enumerated (case independent).
Leading and trailing slash characters will be stripped. The lexical
case will be preserved.
If `None`, the namespace of the `ClassName` parameter will be used,
if specified as a :class:`~pywbem.CIMClassName` object. If that is
also `None`, the default namespace of the connection will be used.
Expand Down Expand Up @@ -7632,6 +7694,9 @@ def EnumerateClasses(self, namespace=None, ClassName=None,
Name of the namespace in which the classes are to be enumerated
(case independent).
Leading and trailing slash characters will be stripped. The lexical
case will be preserved.
If `None`, the namespace of the `ClassName` parameter will be used,
if specified as a :class:`~pywbem.CIMClassName` object. If that is
also `None`, the default namespace of the connection will be used.
Expand Down Expand Up @@ -7790,6 +7855,9 @@ def GetClass(self, ClassName, namespace=None, LocalOnly=None,
Name of the namespace of the class to be retrieved
(case independent).
Leading and trailing slash characters will be stripped. The lexical
case will be preserved.
If `None`, the namespace of the `ClassName` parameter will be used,
if specified as a :class:`~pywbem.CIMClassName` object. If that is
also `None`, the default namespace of the connection will be used.
Expand Down Expand Up @@ -7927,6 +7995,9 @@ def ModifyClass(self, ModifiedClass, namespace=None, **extra):
Name of the namespace in which the class is to be modified
(case independent).
Leading and trailing slash characters will be stripped. The lexical
case will be preserved.
If `None`, the default namespace of the connection object will be
used.
Expand Down Expand Up @@ -8004,6 +8075,9 @@ def CreateClass(self, NewClass, namespace=None, **extra):
Name of the namespace in which the class is to be created
(case independent).
Leading and trailing slash characters will be stripped. The lexical
case will be preserved.
If `None`, the default namespace of the connection object will be
used.
Expand Down Expand Up @@ -8078,6 +8152,9 @@ def DeleteClass(self, ClassName, namespace=None, **extra):
Name of the namespace of the class to be deleted
(case independent).
Leading and trailing slash characters will be stripped. The lexical
case will be preserved.
If `None`, the namespace of the `ClassName` parameter will be used,
if specified as a :class:`~pywbem.CIMClassName` object. If that is
also `None`, the default namespace of the connection will be used.
Expand Down Expand Up @@ -8153,6 +8230,9 @@ def EnumerateQualifiers(self, namespace=None, **extra):
Name of the namespace in which the qualifier declarations are to be
enumerated (case independent).
Leading and trailing slash characters will be stripped. The lexical
case will be preserved.
If `None`, the default namespace of the connection object will be
used.
Expand Down Expand Up @@ -8233,6 +8313,9 @@ def GetQualifier(self, QualifierName, namespace=None, **extra):
Name of the namespace of the qualifier declaration
(case independent).
Leading and trailing slash characters will be stripped. The lexical
case will be preserved.
If `None`, the default namespace of the connection object will be
used.
Expand Down Expand Up @@ -8313,6 +8396,9 @@ def SetQualifier(self, QualifierDeclaration, namespace=None, **extra):
Name of the namespace in which the qualifier declaration is to be
created or modified (case independent).
Leading and trailing slash characters will be stripped. The lexical
case will be preserved.
If `None`, the default namespace of the connection object will be
used.
Expand Down Expand Up @@ -8383,6 +8469,9 @@ def DeleteQualifier(self, QualifierName, namespace=None, **extra):
Name of the namespace in which the qualifier declaration is to be
deleted (case independent).
Leading and trailing slash characters will be stripped. The lexical
case will be preserved.
If `None`, the default namespace of the connection object will be
used.
Expand Down
44 changes: 44 additions & 0 deletions testsuite/test_cim_obj.py
Original file line number Diff line number Diff line change
Expand Up @@ -706,6 +706,28 @@ def test_CIMInstanceName_init_pos_args(self):
namespace=u'root/cimv2'),
None, True
),
(
"Verify that one leading and trailing slash in namespace get "
"stripped",
dict(
classname='CIM_Foo',
namespace='/root/cimv2/'),
dict(
classname=u'CIM_Foo',
namespace=u'root/cimv2'),
None, True
),
(
"Verify that two leading and trailing slashes in namespace get "
"stripped",
dict(
classname='CIM_Foo',
namespace='//root/cimv2//'),
dict(
classname=u'CIM_Foo',
namespace=u'root/cimv2'),
None, True
),

# Host tests
(
Expand Down Expand Up @@ -9166,6 +9188,28 @@ def test_CIMClassName_init_pos_args(self):
namespace=u'root/cimv2'),
None, True
),
(
"Verify that one leading and trailing slash in namespace get "
"stripped",
dict(
classname='CIM_Foo',
namespace='/root/cimv2/'),
dict(
classname=u'CIM_Foo',
namespace=u'root/cimv2'),
None, True
),
(
"Verify that two leading and trailing slashes in namespace get "
"stripped",
dict(
classname='CIM_Foo',
namespace='//root/cimv2//'),
dict(
classname=u'CIM_Foo',
namespace=u'root/cimv2'),
None, True
),

# Host tests
(
Expand Down

0 comments on commit c760648

Please sign in to comment.