"""
vAPI DataValues
"""
__author__ = 'VMware, Inc.'
__copyright__ = 'Copyright 2011-2014 VMware, Inc. All rights reserved. -- VMware Confidential'
import logging
import math
import six
from decimal import Decimal
from vmware.vapi.exception import CoreException
from vmware.vapi.data.type import Type
from vmware.vapi.lib.visitor import VapiVisitor
from vmware.vapi.l10n.runtime import message_factory
logger = logging.getLogger(__name__)
[docs]class DataValue(object):
"""
A piece of introspectable value in vAPI infrastructure
:type type: :class:`vmware.vapi.data.type.Type`
:ivar type: Type of DataValue
"""
def __init__(self, data_type=None):
"""
Initialize DataValue
:type data_type: :class:`vmware.vapi.data.type.Type`
:param data_type: Type of DataValue
"""
self.type = data_type
[docs] def accept(self, visitor):
"""
Applies a visitor to this DataValue
:type visitor: :class:`SimpleValueVisitor`
:param visitor: visitor operating on this DataValue
"""
visitor.visit(self)
[docs]class PrimitiveDataValue(DataValue):
"""
Base class for all primitive DataValues
:type value: :class:`object`
:ivar value: Primitive value
"""
def __init__(self, *args, **kwargs):
DataValue.__init__(self, *args, **kwargs)
self.value = None
def __repr__(self):
return '%s(value=%s)' % (self.__class__.__name__,
repr(self.value))
[docs]class ComparableValueMixin(object):
"""
Helper class to implement the rich comparator operations.
"""
def __eq__(self, other):
if isinstance(other, self.__class__):
return self.value == other.value
else:
return NotImplemented
def __ne__(self, other):
if isinstance(other, self.__class__):
return self.value != other.value
else:
return NotImplemented
def __lt__(self, other):
if isinstance(other, self.__class__):
return self.value < other.value
else:
return NotImplemented
def __gt__(self, other):
if isinstance(other, self.__class__):
return self.value > other.value
else:
return NotImplemented
def __le__(self, other):
if isinstance(other, self.__class__):
return self.value <= other.value
else:
return NotImplemented
def __ge__(self, other):
if isinstance(other, self.__class__):
return self.value >= other.value
else:
return NotImplemented
def __hash__(self):
return hash(self.value)
[docs]class IntegerValue(PrimitiveDataValue, ComparableValueMixin):
"""
DataValue class for integer values
:type value: :class:`int`
:ivar value: 64 bit signed int present in IntegerValue
"""
def __init__(self, value=0):
"""
Initialize IntegerValue
:type value: :class:`int`
:kwarg value: Integer value to be initialized
"""
PrimitiveDataValue.__init__(self, Type.INTEGER)
ComparableValueMixin.__init__(self)
if not isinstance(value, six.integer_types):
msg = message_factory.get_message("vapi.data.invalid",
self.type,
type(value).__name__)
logger.debug(msg)
raise CoreException(msg)
self.value = value
[docs]class DoubleValue(PrimitiveDataValue, ComparableValueMixin):
"""
DataValue class for float values
:type value: :class:`float` or :class:`decimal.Decimal`
:ivar value: 64 bit signed float present in DoubleValue
"""
def __init__(self, value=0.0):
"""
Initialize DoubleValue
:type value: :class:`float` or :class:`decimal.Decimal`
:kwarg value: Float or decimal.Decimal value to be initialized
"""
PrimitiveDataValue.__init__(self, Type.DOUBLE)
ComparableValueMixin.__init__(self)
if not isinstance(value, float) and not isinstance(value, Decimal):
msg = message_factory.get_message('vapi.data.invalid',
self.type,
type(value).__name__)
logger.debug(msg)
raise CoreException(msg)
# Accept floats, but store as decimal
if isinstance(value, float):
value = Decimal(str(value))
if isinstance(value, Decimal):
if math.isinf(float(value)):
msg = message_factory.get_message('vapi.data.invalid.double.inf',
value)
logger.debug(msg)
raise CoreException(msg)
self.value = value
[docs]class StringValue(PrimitiveDataValue, ComparableValueMixin):
"""
DataValue class for strings
:type value: :class:`str`
:ivar value: String present in StringValue
"""
def __init__(self, value=""):
"""
Initialize StringValue
:type value: :class:`str`
:kwarg value: String value to be initialized
"""
PrimitiveDataValue.__init__(self, Type.STRING)
ComparableValueMixin.__init__(self)
if not isinstance(value, six.string_types):
msg = message_factory.get_message("vapi.data.invalid",
self.type,
type(value).__name__)
logger.debug(msg)
raise CoreException(msg)
self.value = value
[docs]class BooleanValue(PrimitiveDataValue, ComparableValueMixin):
"""
DataValue class for bool values
:type value: :class:`bool`
:ivar value: Bool present in BooleanValue
"""
def __init__(self, value=False):
"""
Initialize BooleanValue
:type value: :class:`bool`
:kwarg value: Bool value to be initialized
"""
PrimitiveDataValue.__init__(self, Type.BOOLEAN)
ComparableValueMixin.__init__(self)
if not isinstance(value, bool):
msg = message_factory.get_message("vapi.data.invalid",
self.type,
type(value).__name__)
logger.debug(msg)
raise CoreException(msg)
self.value = value
[docs]class BlobValue(PrimitiveDataValue, ComparableValueMixin):
"""
DataValue class for binary values
:type value: :class:`bytes`
:ivar value: Binary present in BlobValue
"""
def __init__(self, value=""):
"""
Initialize BooleanValue
:type value: :class:`bytes`
:kwarg value: Binary value to be initialized
"""
PrimitiveDataValue.__init__(self, Type.BLOB)
ComparableValueMixin.__init__(self)
self.value = value
[docs]class VoidValue(PrimitiveDataValue):
"""
DataValue class for None
"""
def __init__(self):
"""
Initialize VoidValue
"""
PrimitiveDataValue.__init__(self, Type.VOID)
def __eq__(self, other):
if isinstance(other, VoidValue):
return True
else:
return False
def __ne__(self, other):
return not (self == other)
def __hash__(self):
return hash(None)
def __repr__(self):
return 'VoidValue()'
# Disabling the pylint complaint about badly implemented container
# as we don't need __setitem__, __delitem__ and __getitem__
[docs]class ListValue(DataValue): # pylint: disable=R0924
"""
DataValue class for lists
"""
def __init__(self, values=None):
"""
Initialize ListValue
"""
DataValue.__init__(self, Type.LIST)
if values is not None and not isinstance(values, list):
msg = message_factory.get_message("vapi.data.invalid",
self.type,
type(values).__name__)
logger.debug(msg)
raise CoreException(msg)
self._list_val = values if values is not None else []
[docs] def add(self, value):
"""
Add an element to ListValue
:type value: :class:`DataValue`
:param value: DataValue to be added
"""
self._list_val.append(value)
[docs] def add_all(self, values):
"""
Add all the elements from input list to ListValue
:type values: :class:`list` of :class:`DataValue`
:param values: List of DataValues to be added
"""
for value in values:
self.add(value)
[docs] def is_empty(self):
"""
Returns true if the list is empty
:rtype: :class:`bool`
:return: True if the list is empty, false otherwise
"""
return not self._list_val
[docs] def size(self):
"""
Returns the size of the list
:rtype: :class:`int`
:return: Size of the list value
"""
return len(self._list_val)
def __eq__(self, other):
if not isinstance(other, ListValue):
return False
if self.size() != other.size():
return False
for self_item, other_item in zip(self, other):
if self_item != other_item:
return False
return True
def __ne__(self, other):
return not (self == other)
def __iter__(self):
return self._list_val.__iter__()
def __len__(self):
return len(self._list_val)
def __repr__(self):
ret = ', '.join([repr(val) for val in self._list_val])
return 'ListValue(values=[%s])' % ret
[docs]class OptionalValue(DataValue, ComparableValueMixin):
"""
DataValue class for optionals
:type value: :class:`DataValue`
:ivar value: DataValue present in this OptionalValue
"""
def __init__(self, value=None):
"""
Initialize OptionalValue
:type value: :class:`DataValue`
:kwarg value: DataValue to be used in OptionalValue
"""
DataValue.__init__(self, Type.OPTIONAL)
ComparableValueMixin.__init__(self)
self.value = value
[docs] def is_set(self):
"""
Returns true if OptionalValue has a DataValue present in it
:rtype: :class:`bool`
:return: Returns true if OptionalValue is initialized with some
DataValue, false otherwise
"""
return self.value is not None
def __repr__(self):
return 'OptionalValue(%s)' % repr(self.value)
[docs]class StructValue(DataValue):
"""
DataValue class for Structures
:type name: :class:`str`
:ivar name: Name of the structure
"""
def __init__(self, name=None, data_type=Type.STRUCTURE, values=None):
"""
Initialize StructValue
:type name: :class:`str`
:kwarg name: Name of the StructValue
"""
DataValue.__init__(self, data_type)
self.name = name
if values is not None and not isinstance(values, dict):
msg = message_factory.get_message("vapi.data.invalid",
self.type,
type(values).__name__)
logger.debug(msg)
raise CoreException(msg)
self._fields = values if values is not None else {}
[docs] def get_fields(self):
"""
Returns the map of field names and values present in this
StructValue.
:rtype: :class:`dict` of :class:`str` and :class:`DataValue`
:return: Fields in this struct value
"""
return six.iteritems(self._fields)
[docs] def get_field_names(self):
"""
Returns the list of field names present in this StructValue.
The ordering of fields is not preserved.
:rtype: :class:`list` of :class:`str`
:return: List of field names present in this StructValue
"""
return list(self._fields.keys())
[docs] def has_field(self, field):
"""
Returns true if the field is present in the StructValue, false otherwise
:type field: :class:`str`
:param field: Name of the field
:rtype: :class:`bool`
:return: Returns true if the field is present in the StructValue,
false otherwise
"""
return field in self._fields
[docs] def get_field(self, field):
"""
Returns the field value of the field present in the StructValue
:type field: :class:`str`
:param field: Name of the field
:rtype: :class:`DataValue`
:return: Returns the field value of the field present in the StructValue
"""
value = self._fields.get(field)
if value is None:
msg = message_factory.get_message(
"vapi.data.structure.getfield.unknown",
field)
logger.debug(msg)
raise CoreException(msg)
return value
[docs] def set_field(self, field, value):
"""
Set the field value for the field name passed in the argument
:type field: :class:`str`
:param field: Name of the field
:type field: :class:`str`
:param field: Name of the field to be set
:type value: :class:`DataValue`
:param value: DataValue to be used for the field
"""
if value is None:
msg = message_factory.get_message(
"vapi.data.structure.setfield.null",
field)
logger.debug(msg)
raise CoreException(msg)
self._fields[field] = value
def __eq__(self, other):
if not isinstance(other, StructValue):
return False
if self.name != other.name:
return False
if sorted(self.get_field_names()) != sorted(other.get_field_names()):
return False
for key, value in six.iteritems(self._fields):
if value != other.get_field(key):
return False
return True
def __ne__(self, other):
return not (self == other)
def __repr__(self):
return "StructValue(name=%s, values=%s)" % (repr(self.name),
repr(self._fields))
[docs] def keys(self):
"""
Returns the list of field names
:rtype: :class:`list` or :class:`str`
:return: List of field names
"""
return six.iterkeys(self._fields)
[docs]class ErrorValue(StructValue):
"""
DataValue class for Errors
"""
def __init__(self, name=None, values=None):
"""
Initialize ErrorValue
:type name: :class:`str`
:kwarg name: Name of the ErrorValue
"""
StructValue.__init__(self, name, Type.ERROR, values)
def __eq__(self, other):
if not isinstance(other, ErrorValue):
return False
return StructValue.__eq__(self, other)
def __ne__(self, other):
return not (self == other)
[docs]class SecretValue(PrimitiveDataValue, ComparableValueMixin):
"""
DataValue class for secret values. Only strings are allowed to be
secrets.
:type value: :class:`str`
:ivar value: String present in SecretValue
"""
def __init__(self, value=""):
"""
Initialize StringValue
:type value: :class:`str`
:kwarg value: String value to be initialized
"""
PrimitiveDataValue.__init__(self, Type.SECRET)
ComparableValueMixin.__init__(self)
if not isinstance(value, six.string_types):
msg = message_factory.get_message("vapi.data.invalid",
self.type,
type(value).__name__)
raise CoreException(msg)
self.value = value
def __repr__(self):
return 'SecretValue(<secret>)'
def __str__(self):
return '<secret>'
[docs]class SimpleValueVisitor(VapiVisitor):
"""
Base no-op implementation of a DataValue visitor
"""
def __init__(self):
"""
Initialize SimpleValueVisitor
"""
VapiVisitor.__init__(self, 'Value')
[docs] def visit_void(self, value):
"""
Visit a VoidValue
:type value: :class:`VoidValue`
:param value: Data value
"""
pass
[docs] def visit_integer(self, value):
"""
Visit a IntegerValue
:type value: :class:`IntegerValue`
:param value: Data value
"""
pass
[docs] def visit_double(self, value):
"""
Visit a DoubleValue
:type value: :class:`DoubleValue`
:param value: Data value
"""
pass
[docs] def visit_string(self, value):
"""
Visit a StringValue
:type value: :class:`StringValue`
:param value: Data value
"""
pass
[docs] def visit_boolean(self, value):
"""
Visit a BooleanValue
:type value: :class:`BooleanValue`
:param value: Data value
"""
pass
[docs] def visit_blob(self, value):
"""
Visit a BlobValue
:type value: :class:`BlobValue`
:param value: Data value
"""
pass
[docs] def visit_list(self, value):
"""
Visit a ListValue
:type value: :class:`ListValue`
:param value: Data value
"""
pass
[docs] def visit_optional(self, value):
"""
Visit a OptionalValue
:type value: :class:`OptionalValue`
:param value: Data value
"""
pass
[docs] def visit_struct(self, value):
"""
Visit a StructValue
:type value: :class:`StructValue`
:param value: Data value
"""
pass
[docs] def visit_error(self, value):
"""
Visit an ErrorValue
:type value: :class:`ErrorValue`
:param value: Data value
"""
pass
[docs] def visit_secret(self, value):
"""
Visit a SecretValue
:type value: :class:`SecretValue`
:param value: Data value
"""
pass
# vapi type name to vapi type value map
type_map = {
Type.VOID: VoidValue,
Type.INTEGER: IntegerValue,
Type.DOUBLE: DoubleValue,
Type.STRING: StringValue,
Type.BOOLEAN: BooleanValue,
Type.LIST: ListValue,
Type.STRUCTURE: StructValue,
Type.ERROR: ErrorValue,
Type.OPTIONAL: OptionalValue,
Type.BLOB: BlobValue,
Type.SECRET: SecretValue,
}
[docs]def data_value_factory(data_type, *args):
"""
Convenience method to create datavalues
:type data_type: :class:`str`
:param data_type: String representation of the data value type
:param args: The argument list to be passed to the data value constructor
"""
constructor = type_map.get(data_type)
return constructor(*args)