#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#
"""
The :class:`~pywbem.ValueMapping` class supports translating the values of an
integer-typed CIM element (e.g. property, method, or parameter) that is
qualified with the `ValueMap` and `Values` qualifiers, to the corresponding
value of the `Values` qualifier.
This class supports value ranges (e.g. ``"4..6"``) and the unclaimed marker
(``".."``).
"""
import re
import six
from .cim_types import CIMInt, type_from_name
__all__ = ['ValueMapping']
[docs]class ValueMapping(object):
"""
*New in pywbem 0.9 as experimental and finalized in 0.10.*
A utility class that supports translating the values of an integer-typed
CIM element (property, method, parameter) that is qualified with the
`ValueMap` and `Values` qualifiers, to the corresponding value of the
`Values` qualifier.
This is done by retrieving the CIM class definition defining the CIM
element in question, and by inspecting its `ValueMap` and `Values`
qualifiers.
The translation of the values is performed by the
:meth:`~pywbem.ValueMapping.tovalues` method.
Instances of this class must be created through one of the factory class
methods: :meth:`~pywbem.ValueMapping.for_property`,
:meth:`~pywbem.ValueMapping.for_method`, or
:meth:`~pywbem.ValueMapping.for_parameter`.
Value ranges (``"2..4"``) and the indicator for unclaimed values (``".."``)
in the `ValueMap` qualifier are supported.
Example:
Given the following definition of a property in MOF:
.. code-block:: text
class CIM_Foo {
[ValueMap{ "0", "2..4", "..6", "7..", "9", ".." },
Values{ "zero", "two-four", "five-six", "seven-eight", "nine",
"unclaimed"}]
uint16 MyProp;
};
Assuming this class exists in a WBEM server, the following code will
create a value mapping for this property and will print a few property
values and their corresponding `Values` strings::
namespace = 'root/cimv2'
conn = pywbem.WBEMConnection(...) # WBEM server
myprop_vm = pywbem.ValueMapping.for_property(
conn, namespace, 'CIM_Foo', 'MyProp')
print("value: Values")
for value in range(0, 12):
print("%5s: %r" % (value, myprop_vm.tovalues(value))
Resulting output:
.. code-block:: text
value: Values
0: 'zero'
1: 'unclaimed'
2: 'two-four'
3: 'two-four'
4: 'two-four'
5: 'five-six'
6: 'five-six'
7: 'seven-eight'
8: 'seven-eight'
9: 'nine'
10: 'unclaimed'
11: 'unclaimed'
"""
def __init__(self):
self._conn = None
self._namespace = None
self._classname = None
self._propname = None
self._methodname = None
self._parametername = None
self._element_obj = None
self._single_dict = {} # for single values; elem_val: values_str)
self._range_tuple_list = [] # for value ranges; tuple(lo,hi,values_str)
self._unclaimed = None # value of the unclaimed indicator '..'
[docs] @classmethod
def for_property(cls, server, namespace, classname, propname):
# pylint: disable=line-too-long
"""
Factory method that returns a new :class:`~pywbem.ValueMapping`
instance that maps CIM property values to the `Values` qualifier
defined on that property.
If a `Values` qualifier is defined but no `ValueMap` qualifier, a
default of 0-based consecutive numbers is applied (that is the default
defined in :term:`DSP0004`).
Parameters:
server (:class:`~pywbem.WBEMConnection` or :class:`~pywbem.WBEMServer`):
The connection to the WBEM server containing the namespace.
namespace (:term:`string`):
Name of the CIM namespace containing the class.
If `None`, the default namespace of the connection will be used.
classname (:term:`string`):
Name of the CIM class exposing the property. The property can be
defined in that class or inherited into that class.
propname (:term:`string`):
Name of the CIM property that defines the `Values` / `ValueMap`
qualifiers.
Returns:
The new :class:`~pywbem.ValueMapping` instance.
Raises:
Exceptions raised by :class:`~pywbem.WBEMConnection`.
ValueError: No `Values` qualifier defined.
TypeError: The property is not integer-typed.
KeyError: The property does not exist in the class.
""" # noqa: E501
conn = server
try:
get_class = conn.GetClass
except AttributeError:
conn = server.conn
get_class = conn.GetClass
class_obj = get_class(ClassName=classname,
namespace=namespace,
LocalOnly=False,
IncludeQualifiers=True)
property_obj = class_obj.properties[propname]
new_vm = cls._create_for_element(property_obj, conn, namespace,
classname, propname=propname)
return new_vm
[docs] @classmethod
def for_method(cls, server, namespace, classname, methodname):
# pylint: disable=line-too-long
"""
Factory method that returns a new :class:`~pywbem.ValueMapping`
instance that maps CIM method return values to the `Values` qualifier
of that method.
If a `Values` qualifier is defined but no `ValueMap` qualifier, a
default of 0-based consecutive numbers is applied (that is the default
defined in :term:`DSP0004`).
Parameters:
server (:class:`~pywbem.WBEMConnection` or :class:`~pywbem.WBEMServer`):
The connection to the WBEM server containing the namespace.
namespace (:term:`string`):
Name of the CIM namespace containing the class.
classname (:term:`string`):
Name of the CIM class exposing the method. The method can be
defined in that class or inherited into that class.
methodname (:term:`string`):
Name of the CIM method that defines the `Values` / `ValueMap`
qualifiers.
Returns:
The new :class:`~pywbem.ValueMapping` instance.
Raises:
Exceptions raised by :class:`~pywbem.WBEMConnection`.
ValueError: No `Values` qualifier defined.
TypeError: The method is not integer-typed.
KeyError: The method does not exist in the class.
""" # noqa: E501
conn = server
try:
get_class = conn.GetClass
except AttributeError:
conn = server.conn
get_class = conn.GetClass
class_obj = get_class(ClassName=classname,
namespace=namespace,
LocalOnly=False,
IncludeQualifiers=True)
method_obj = class_obj.methods[methodname]
new_vm = cls._create_for_element(method_obj, conn, namespace,
classname, methodname=methodname)
return new_vm
[docs] @classmethod
def for_parameter(cls, server, namespace, classname, methodname,
parametername):
# pylint: disable=line-too-long
"""
Factory method that returns a new :class:`~pywbem.ValueMapping`
instance that maps CIM parameter values to the `Values` qualifier
defined on that parameter.
If a `Values` qualifier is defined but no `ValueMap` qualifier, a
default of 0-based consecutive numbers is applied (that is the default
defined in :term:`DSP0004`).
Parameters:
server (:class:`~pywbem.WBEMConnection` or :class:`~pywbem.WBEMServer`):
The connection to the WBEM server containing the namespace.
namespace (:term:`string`):
Name of the CIM namespace containing the class.
classname (:term:`string`):
Name of the CIM class exposing the method. The method can be
defined in that class or inherited into that class.
methodname (:term:`string`):
Name of the CIM method that has the parameter.
parametername (:term:`string`):
Name of the CIM parameter that defines the `Values` / `ValueMap`
qualifiers.
Returns:
The new :class:`~pywbem.ValueMapping` instance.
Raises:
Exceptions raised by :class:`~pywbem.WBEMConnection`.
ValueError: No `Values` qualifier defined.
TypeError: The parameter is not integer-typed.
KeyError: The method does not exist in the class, or the
parameter does not exist in the method.
""" # noqa: E501
conn = server
try:
get_class = conn.GetClass
except AttributeError:
conn = server.conn
get_class = conn.GetClass
class_obj = get_class(ClassName=classname,
namespace=namespace,
LocalOnly=False,
IncludeQualifiers=True)
method_obj = class_obj.methods[methodname]
parameter_obj = method_obj.parameters[parametername]
new_vm = cls._create_for_element(parameter_obj, conn, namespace,
classname, methodname=methodname,
parametername=parametername)
return new_vm
@classmethod
def _values_tuple(cls, i, valuemap_list, values_list, cimtype):
"""
Return a tuple for the value range at position i, with these items:
* lo - low value of the range
* hi - high value of the range (can be equal to lo)
* values - value of `Values` qualifier for this position
Parameters:
i (integer): position into valuemap_list and values_list
valuemap_list (list of strings): `ValueMap` qualifier value
values_list (list of strings): `Values` qualifier value
cimtype (type): CIM type of the CIM element
Raises:
ValueError: Invalid `ValueMap` entry.
"""
values_str = values_list[i]
valuemap_str = valuemap_list[i]
try:
valuemap_int = int(valuemap_str)
return (valuemap_int, valuemap_int, values_str)
except ValueError:
m = re.match(r'^([+-]?[0-9]*)\.\.([+-]?[0-9]*)$', valuemap_str)
if m is None:
raise ValueError("Invalid ValueMap entry: %r" % valuemap_str)
lo = m.group(1)
if lo == '':
if i == 0:
lo = cimtype.minvalue
else:
_, previous_hi, _ = cls._values_tuple(
i - 1, valuemap_list, values_list, cimtype)
lo = previous_hi + 1
else:
lo = int(lo)
hi = m.group(2)
if hi == '':
if i == len(valuemap_list) - 1:
hi = cimtype.maxvalue
else:
next_lo, _, _ = cls._values_tuple(
i + 1, valuemap_list, values_list, cimtype)
hi = next_lo - 1
else:
hi = int(hi)
return (lo, hi, values_str)
@classmethod
def _create_for_element(cls, element_obj, conn, namespace, classname,
propname=None, methodname=None, parametername=None):
# pylint: disable=line-too-long
"""
Return a new :class:`~pywbem.ValueMapping` instance for the specified
CIM element.
If a `Values` qualifier is defined but no `ValueMap` qualifier, a
default of 0-based consecutive numbers is applied (that is the default
defined in :term:`DSP0004`).
Parameters:
element_obj (:class:`~pywbem.CIMProperty`, :class:`~pywbem.CIMMethod`, or :class:`~pywbem.CIMParameter`):
The CIM element on which the qualifiers are defined.
conn (:class:`~pywbem.WBEMConnection`):
The connection to the WBEM server containing the namespace.
namespace (:term:`string`):
Name of the CIM namespace containing the class.
classname (:term:`string`):
Name of the CIM class exposing the method. The method can be
defined in that class or inherited into that class.
propname (:term:`string`):
Name of the CIM property that defines the `Values` / `ValueMap`
qualifiers.
methodname (:term:`string`):
Name of the CIM method that has the parameter.
parametername (:term:`string`):
Name of the CIM parameter that defines the `Values` / `ValueMap`
qualifiers.
Returns:
The created :class:`~pywbem.ValueMapping` instance for the specified
CIM element.
Raises:
ValueError: No `Values` qualifier defined.
ValueError: Invalid `ValueMap` entry.
TypeError: The CIM element is not integer-typed.
""" # noqa: E501
# pylint: disable=protected-access
try:
typename = element_obj.type # Property, Parameter
except AttributeError:
typename = element_obj.return_type # Method
cimtype = type_from_name(typename)
if not issubclass(cimtype, CIMInt):
raise TypeError("The CIM element is not integer-typed: %s" %
typename)
vm = ValueMapping()
vm._element_obj = element_obj
vm._conn = conn
vm._namespace = namespace
vm._classname = classname
vm._propname = propname
vm._methodname = methodname
vm._parametername = parametername
values_qual = element_obj.qualifiers.get('Values', None)
if values_qual is None:
# DSP0004 defines no default for a missing Values qualifier
raise ValueError("No Values qualifier defined")
values_list = values_qual.value
valuemap_qual = element_obj.qualifiers.get('ValueMap', None)
if valuemap_qual is None:
# DSP0004 defines a default of consecutive index numbers
vm._single_dict = dict(zip(range(0, len(values_list)), values_list))
vm._range_tuple_list = []
vm._unclaimed = None
else:
vm._single_dict = {}
vm._range_tuple_list = []
vm._unclaimed = None
valuemap_list = valuemap_qual.value
for i, valuemap_str in enumerate(valuemap_list):
values_str = values_list[i]
if valuemap_str == '..':
vm._unclaimed = values_str
else:
lo, hi, values_str = cls._values_tuple(
i, valuemap_list, values_list, cimtype)
if lo == hi:
# single value
vm._single_dict[lo] = values_str
else:
# value range
vm._range_tuple_list.append((lo, hi, values_str))
# pylint: enable=protected-access
return vm
[docs] def __repr__(self):
"""
Return a representation of the :class:`~pywbem.ValueMapping` object
with all attributes, that is suitable for debugging.
"""
return "%s(_conn=%r, _namespace=%r, " \
"_classname=%r, _propname=%r, _methodname=%r, " \
"_parametername=%r, _element_obj=%r, " \
"_single_dict=%r, _range_tuple_list=%r, _unclaimed=%r)" % \
(self.__class__.__name__, self._conn, self._namespace,
self._classname, self._propname, self._methodname,
self._parametername, self._element_obj,
self._single_dict, self._range_tuple_list, self._unclaimed)
@property
def conn(self):
"""
:class:`~pywbem.WBEMConnection`:
Connection to the WBEM server containing the CIM namespace (that
contains the mapped CIM element).
"""
return self._conn
@property
def namespace(self):
"""
:term:`string`: Name of the CIM namespace containing the class that
defines the mapped CIM element.
"""
return self._namespace
@property
def classname(self):
"""
:term:`string`: Name of the CIM class defining the mapped CIM element.
"""
return self._classname
@property
def propname(self):
"""
:term:`string`: Name of the CIM property that is mapped. `None`, if
no property is mapped.
"""
return self._propname
@property
def methodname(self):
"""
:term:`string`: Name of the CIM method, that either is mapped itself,
or that has the parameter that is mapped. `None`, if no method or
parameter is mapped.
"""
return self._methodname
@property
def parametername(self):
"""
:term:`string`: Name of the CIM parameter that is mapped. `None`, if
no parameter is mapped.
"""
return self._parametername
@property
def element(self):
# pylint: disable=line-too-long
"""
:class:`~pywbem.CIMProperty`, :class:`~pywbem.CIMMethod`, or :class:`~pywbem.CIMParameter`:
The mapped CIM element.
""" # noqa: E501
return self._element_obj
[docs] def tovalues(self, element_value):
"""
Return the `Values` string for an element value, based upon this value
mapping.
Parameters:
element_value (:term:`integer` or :class:`~pywbem.CIMInt`):
The value of the CIM element (property, method, parameter).
Returns:
:term:`string`:
The `Values` string for the element value.
Raises:
ValueError: Element value outside of the set defined by `ValueMap`.
TypeError: Element value is not an integer type.
"""
if not isinstance(element_value, (six.integer_types, CIMInt)):
raise TypeError("Element value is not an integer type: %s" %
type(element_value))
# try single value
try:
return self._single_dict[element_value]
except KeyError:
pass
# try value ranges
for range_tuple in self._range_tuple_list:
lo, hi, values_str = range_tuple
if lo <= element_value <= hi:
return values_str
# try catch-all '..'
if self._unclaimed is not None:
return self._unclaimed
raise ValueError("Element value outside of the set defined by "
"ValueMap: %r" % element_value)