def visit_enum(self, typ): """ Visit a enum type python value :type typ: :class:`vmware.vapi.bindings.type.EnumType` :param typ: Binding type of the value """ # If the binding type is EnumType, we will accept either # a plain string (or) a class variable of # :class:`vmware.vapi.bindings.enum.Enum` type. # String will be sent when the user invoked an API call # by sending a plain dictionary object (deserialized # from a human readable JSON). # Enum will be sent when the user use a language binding type # to invoke an operation. if not isinstance(self._in_value, six.string_types): msg = message_factory.get_message( 'vapi.bindings.typeconverter.unexpected.python.type', typ.name, type(self._in_value).__name__) logger.debug(msg) raise CoreException(msg) if isinstance(self._in_value, Enum): # Check if it is the expected enum class expected_name = typ.name actual_name = self._in_value.get_binding_type().name # TODO: compare the types instead of names. if expected_name != actual_name: msg = message_factory.get_message( 'vapi.bindings.typeconverter.unexpected.enum.type', expected_name, actual_name) logger.debug(msg) raise CoreException(msg) # Convert from vmware.vapi.bindings.Enum instance to string value enum_string_value = str(self._in_value) self._out_value = typ.definition.new_value(enum_string_value)
def validate(self, value): """ The validation for DynamicStructDefinition will succeed against any StructValue. :type value: :class:`vmware.vapi.data.value.DataValue` :param value: the data value to validate :rtype: :class:`list` of :class:`vmware.vapi.message.Message` or ``None`` :return: a stack of messages indicating why the value is not an instance of this data-definition, or None if the value is an instance of this data-definition """ if value is None: msg = message_factory.get_message( 'vapi.data.dynamicstruct.validate.mismatch', self._valid_types, 'None') return [msg] if value.type not in self._valid_types: msg = message_factory.get_message( 'vapi.data.dynamicstruct.validate.mismatch', self._valid_types, value.type) return [msg] return None
def target(self, definition): """ Resolves the reference. An unresolved reference can be resolved exactly once. A resolved reference cannot be re-resolved. :type definition: :class:`DataDefinition` :param definition: structure definition :raise: :class:`vmware.vapi.exception.CoreException`: if the reference is already resolved (already has) a target) or if the name of the reference does not match the name of the definition """ if not definition: raise ValueError('StructDefinition may not be None') if self._definition: msg = message_factory.get_message( 'vapi.data.structref.already.resolved', self.name) logger.debug(msg) raise CoreException(msg) if self.name != definition.name: msg = message_factory.get_message( 'vapi.data.structref.resolve.type.mismatch', self.name, definition.name) logger.debug(msg) raise CoreException(msg) self._definition = definition
def validate(self, value): """ Validates that the specified :class:`vmware.vapi.data.value.DataValue` is an instance of this data-definition :type value: :class:`vmware.vapi.data.value.DataValue` :param value: the data value to validate :rtype: :class:`list` of :class:`vmware.vapi.message.Message` or ``None`` :return: a stack of messages indicating why the value is not an instance of this data-definition, or None if the value is an instance of this data-definition """ if value is None: msg = message_factory.get_message('vapi.data.validate.mismatch', self.type, 'None') return [msg] if value.type != self.type: msg = message_factory.get_message('vapi.data.validate.mismatch', self.type, value.type) return [msg] return None
def validate(self, other): """ Apart from the validation checks specified in the validate method of DataDefinition class, this method does some additional checks Validation will fail if - the name of the input StructValue does not match the name of this StructDefinition. - any of the fields (either required or optional) in this StructDefinition are not present in the input StructValue - the validation fails for any field value in the input StructValue with its corresponding definition in this StructDefinition The method performs inclusive validation. i.e. If there are any extra fields in the input StructValue which are not present in the StructDefinition, validation will not fail. :type other: :class:`vmware.vapi.data.value.StructValue` :param other: StructValue to be validated :rtype: :class:`list` of :class:`vmware.vapi.message.Message` of ``None`` :return: a stack of messages indicating why the value is not an instance of this data-definition, or None if the value is an instance of this data-definition """ errors = DataDefinition.validate(self, other) if errors: return errors if (other.name != self.name): # Remove the following if condition in R28 # To handle old client talking to new server if self.name in [MAP_ENTRY, OPERATION_INPUT]: pass else: msg = message_factory.get_message( 'vapi.data.structure.name.mismatch', self.name, other.name) logger.debug(msg) return [msg] for field in self._keys: if (not other.has_field(field) and self._dict.get(field).type != Type.OPTIONAL): msg = message_factory.get_message( 'vapi.data.structure.field.missing', self.name, field) logger.debug(msg) return [msg] field_def = self._dict.get(field) field_val = other.get_field(field) errors = field_def.validate(field_val) if errors: msg = message_factory.get_message( 'vapi.data.structure.field.invalid', field, self.name) logger.debug(msg) return [msg] + errors return None
def _visit_vapi_struct(self, typ): """ Visit an instance of VapiStruct class :type typ: :class:`vmware.vapi.bindings.type.StructType` :param typ: Binding type of the value :rtype: :class:`vmware.vapi.data.value.DataValue` :return: vAPI Data value """ # Check if it is the expected struct class expected_name = typ.name actual_name = self._in_value.get_binding_type().name # TODO: compare the types instead of names. if expected_name != actual_name: msg = message_factory.get_message( 'vapi.bindings.typeconverter.unexpected.struct.class', expected_name, actual_name) logger.debug(msg) raise CoreException(msg) in_value = self._in_value out_value = typ.definition.new_value() field_names = typ.get_field_names() for field in field_names: try: self._in_value = in_value.get_field(field) # If self._in_value is None and field type is not # optional, raise an error field_type = typ.get_field(field) if (not isinstance(field_type, OptionalType) and self._in_value is None): raise AttributeError except AttributeError: msg = message_factory.get_message( 'vapi.bindings.typeconverter.struct.missing.field', field, typ.name) logger.debug(msg) raise CoreException(msg) try: self.visit(typ.get_field(field)) except Exception as e: msg = message_factory.get_message( 'vapi.bindings.typeconverter.struct.invalid.field', field, typ.name) logger.debug(msg) raise CoreException(msg, e) out_value.set_field(field, self._out_value) for k, v in six.iteritems(in_value._get_extra_fields()): # pylint: disable=W0212 out_value.set_field(k, v) self._in_value = in_value self._out_value = out_value
def _invoke_int(self, service_id, operation_id, input_value, ctx): """ Internal implementation of invoke method :type service_id: :class:`str` :param service_id: Service identifier :type operation_id: :class:`str` :param operation_id: Operation identifier :type input_value: :class:`vmware.vapi.data.value.StructValue` :param input_value: Method input parameters :type ctx: :class:`vmware.vapi.core.ExecutionContext` :param ctx: Execution context for this method :rtype: :class:`vmware.vapi.core.MethodResult` :return: Result of the method invocation """ # Check if the provider exists provider = self._service_id_map.get(service_id) if not provider: msg = message_factory.get_message( 'vapi.method.input.invalid.interface', service_id) logger.error(msg) error_value = make_error_value_from_msgs( self._operation_not_found_def, msg) return MethodResult(error=error_value) # Continue the authentication chain only if the target service # is not in the local provider of this process. i.e. for only # remote calls if service_id not in self._local_service_ids: for processor in self._authn_chain_processors: ctx.security_context = processor.next_context( ctx.security_context) # Actual method execution try: method_result = provider.invoke(service_id, operation_id, input_value, ctx) return method_result except Exception as e: stack_trace = traceback.format_exc() logger.error( 'Service: %s, Operation: %s, input_value %s: exception: %s', service_id, operation_id, input_value, stack_trace) msg = message_factory.get_message('vapi.method.invoke.exception', str(e)) error_value = make_error_value_from_msgs( self._internal_server_error_def, msg) method_result = MethodResult(error=error_value) return method_result
def validate(self, list_value): """ Apart from the validation checks specified in the validate method of DataDefinition class, this method does some additional checks Validation will fail if any element in the ListValue does not validate against the DataDefinition of the elementType of this ListDefinition :type other: :class:`vmware.vapi.data.value.ListValue` :param other: ListValue to be validated :rtype: :class:`list` of :class:`vmware.vapi.message.Message` or ``None`` :return: a stack of messages indicating why the value is not an instance of this data-definition, or None if the value is an instance of this data-definition """ errors = DataDefinition.validate(self, list_value) if errors: return errors for index, value in enumerate(list_value): errors = self.element_type.validate(value) if errors: msg = message_factory.get_message( 'vapi.data.list.invalid.entry', str(value), index) return [msg] + errors return None
def _validate_error(error_value, method_definition): """ Validate the error_value is allowed to be reported by the method described by method_definition. :type error_value: :class:`vmware.vapi.data.value.ErrorValue` :param error_value: Error value to validate :type method_definition: :class:`vmware.vapi.core.MethodDefinition` :param method_definition: definition of the method to validate against. :rtype: :class:`vmware.vapi.message.Message` or None :return: the messages describing the validation failure or None if validation succeeded """ error_def = method_definition.get_error_definition(error_value.name) if error_def is None: method_id = method_definition.get_identifier() logger.error( "Method %s reported the error %s which is not in " + "MethodDefinition", method_id.get_name(), error_value.name) message = message_factory.get_message( 'vapi.method.status.errors.invalid', error_value.name, str(method_id.get_name())) return [message] messages = error_def.validate(error_value) return messages
def test_report_undeclared_error(self): msg = message_factory.get_message( 'vapi.method.status.errors.invalid', not_found_error.name, str(method_id_dict[report_undeclared_error].get_name())) expected_error = make_error_value_from_msgs(internal_server_error_def, *([msg, _msg])) self._test_method_failure(report_undeclared_error, expected_error)
def _convert_to_string(data_value): """ Serialize data value to string for use in Request URL :type data_value: :class:`vmware.vapi.data.value.DataValue` :param data_value: Data value :rtype :class:`str` or :class:`NoneType` :return: Serialized string for use in Request URL or None """ if isinstance(data_value, OptionalValue): if data_value.is_set(): return RequestSerializer._convert_to_string(data_value.value) else: return None elif isinstance(data_value, BooleanValue): return DataValueConverter.convert_to_json(data_value) elif isinstance(data_value, StringValue): return data_value.value elif isinstance(data_value, IntegerValue): return str(data_value.value) else: msg = message_factory.get_message( 'vapi.data.serializers.rest.unsupported_data_value', data_value.type) raise CoreException(msg)
def visit_map(self, typ): """ Visit a List Value. This ListValue must represent a map. Each element of the ListValue is a StructValue with two fields, namely 'key' and 'value'. The 'key' field represents the key of the map and the 'value' field represents the value of the map. Also, since this represents a map, there should not be duplicate keys. :type typ: :class:`vmware.vapi.bindings.type.MapType` :param typ: Binding type of the value """ in_value = self._in_value key_typ = typ.key_type value_typ = typ.value_type out_value = {} for elt_value in in_value: key = self._visit_struct_field(elt_value.get_field(MAP_KEY_FIELD), key_typ) if key in out_value: msg = message_factory.get_message( 'vapi.bindings.typeconverter.map.duplicate.key', key) logger.debug(msg) raise CoreException(msg) value = self._visit_struct_field( elt_value.get_field(MAP_VALUE_FIELD), value_typ) out_value[key] = value self._out_value = out_value self._in_value = in_value
def test_unknown_message(self): actual_result = message_factory.get_message('bogus', 'hello') expected_result = Message( 'vapi.message.unknown', "Unknown message ID bogus requested with parameters ('hello',)", ) self.assertEqual(expected_result, actual_result)
def validate(self, value): """ Apart from the validation checks specified in the validate method of DataDefinition class, this method does some additional checks. Since this is an OptionalValue, validation will succeed if the element value is None. If the element value is not None, validation will fail if element in the OptionalValue does not validate against the DataDefinition of the element_type of this OptionalDefinition. :type other: :class:`vmware.vapi.data.value.OptionalValue` :param other: OptionalValue to be validated :rtype: :class:`list` of :class:`vmware.vapi.message.Message` :return: a stack of messages indicating why the value is not an instance of this data-definition, or None if the value is an instance of this data-definition """ errors = DataDefinition.validate(self, value) if errors: return errors if value.value is None: # None is a valid value for optional pass elif value.is_set(): errors = self.element_type.validate(value.value) if errors: msg = message_factory.get_message( 'vapi.data.optional.validate') return [msg] + errors return None
def check_resolved(self): """ Check if the reference is resolved or not """ if self._definition is None: msg = message_factory.get_message( 'vapi.data.structref.not.resolved', self.name) logger.debug(msg) raise CoreException(msg)
def _convert_data_value_to_data_def_int(data_value, ctx): """ Convert :class:`vmware.vapi.data.value.DataValue` object that corresponds to DataDefinition structure in introspection VMODL2 into :class:`vmware.vapi.data.definition.DataDefinition` object using the ReferenceResolver context. ReferenceResolver is used to resolve any STRUCTURE_REF objects present in the input value. This function registers all the STRUCTURE and STRUCTURE_REF objects with ReferenceResolver. The caller of this function must call "resolve()" on the ReferenceResolver to resolve the StructDefinition references in StructRefDefinition :type data_value: :class:`vmware.vapi.data.value.DataValue` :param data_value: Data value representing the data definition object :type ctx: :class:`vmware.vapi.data.definition.ReferenceResolver` :param ctx: Context to resolve structure reference definitions :rtype: :class:`vmware.vapi.data.definition.DataDefinition` :return: Data definition """ type_name = data_value.get_field('type').value data_def_class = reverse_data_type_map.get(type_name) # Primitive types if type_name in [ 'VOID', 'LONG', 'DOUBLE', 'STRING', 'OPAQUE', 'BOOLEAN', 'SECRET', 'BINARY', 'DYNAMIC_STRUCTURE', 'ANY_ERROR' ]: return data_def_class() # Generic types elif type_name in ['OPTIONAL', 'LIST']: element_type_def = _convert_data_value_to_data_def_int( data_value.get_field('element_definition').value, ctx) return data_def_class(element_type_def) # Struct Ref definition elif type_name == 'STRUCTURE_REF': data_def = data_def_class(data_value.get_field('name').value.value) ctx.add_reference(data_def) return data_def # Structure and Error types elif type_name in ['STRUCTURE', 'ERROR']: name = data_value.get_field('name').value.value field_defs = [] fields = data_value.get_field('fields').value for field_info in fields: field_name = field_info.get_field('key').value field_def = _convert_data_value_to_data_def_int( field_info.get_field('value'), ctx) field_defs.append((field_name, field_def)) data_def = data_def_class(name, field_defs) ctx.add_definition(data_def) return data_def else: msg = message_factory.get_message('vapi.introspection.invalid.type', type_name) logger.debug(msg) raise CoreException(msg)
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 []
def _test_operation_not_found(self, interface_name, method_name, error, message_id, *args): ctx = ExecutionContext() expected_msg = message_factory.get_message(message_id, *args) expected_error_value = make_error_value_from_msgs(error, expected_msg) method_result = self.local_provider.invoke(interface_name, method_name, StructValue(), ctx) self.assertEquals(None, method_result.output) self.assertEquals(expected_error_value, method_result.error)
def get_operations(self, service_id): service_info = self._service_data.get(service_id) if service_info: method_ids = service_info.get_definition().get_method_identifiers() return MethodResult(output=ListValue(values=[ StringValue(method_id.get_name()) for method_id in method_ids ])) else: msg = message_factory.get_message( 'vapi.introspection.operation.service.not_found', service_id) error_value = make_error_value_from_msgs(not_found_def, msg) return MethodResult(error=error_value)
def check_for_unknown_fields(cls, value_type, value): """ Check if the StructValues inside the input DataValue has any unknown fields :type value_type: :class:`vmware.vapi.bindings.type.BindingType` :param value_type: Binding Type :type value: :class:`vmware.vapi.data.value.DataValue` :param value: DataValue :rtype: :class:`vmware.vapi.data.value.ErrorValue` or ``None`` :return: ErrorValue describing the unknown fields or None if no unknown fields were found """ if isinstance(value_type, ReferenceType): value_type = value_type.resolved_type if isinstance(value_type, DynamicStructType): pass elif isinstance(value_type, StructType): expected_field_names = value_type.get_field_names() unexpected_fields = set([ # pylint: disable=consider-using-set-comprehension field for field in value.get_field_names() if field not in expected_field_names and cls.is_set_optional_field(value.get_field(field)) ]) if unexpected_fields: msg = message_factory.get_message( 'vapi.data.structure.field.unexpected', repr(unexpected_fields), value.name) logger.debug(msg) return make_error_value_from_msgs(cls._unexpected_input_def, msg) for field in expected_field_names: field_value = value.get_field(field) field_type = value_type.get_field(field) error = ApiInterfaceSkeleton.check_for_unknown_fields( field_type, field_value) if error: return error elif isinstance(value_type, ListType): element_type = value_type.element_type if isinstance(element_type, ( ListType, OptionalType, StructType, ReferenceType)): for element in value: error = ApiInterfaceSkeleton.check_for_unknown_fields( element_type, element) if error: return error elif isinstance(value_type, OptionalType): if value.is_set(): return ApiInterfaceSkeleton.check_for_unknown_fields( value_type.element_type, value.value) return None
def get_service_info(self, service_id): provider = self._get_provider(service_id) if provider: ctx = ExecutionContext() struct_value = StructValue(name=OPERATION_INPUT, values={'id': StringValue(service_id)}) return provider.invoke(Introspection.SERVICE_SVC, 'get', struct_value, ctx) else: msg = message_factory.get_message( 'vapi.introspection.operation.service.not_found', service_id) error_value = make_error_value_from_msgs(not_found_def, msg) return MethodResult(error=error_value)
def test_input_validation_failure(self): ctx = ExecutionContext() expected_msg0 = message_factory.get_message( 'vapi.data.structure.field.invalid', 'param', OPERATION_INPUT) expected_msg1 = message_factory.get_message( 'vapi.data.validate.mismatch', 'Integer', 'Structure') expected_error_value = make_error_value_from_msgs( invalid_argument_def, expected_msg0, expected_msg1) service_id = raise_python_exception_id.get_interface_identifier( ).get_name() operation_id = raise_python_exception_id.get_name() method_def = self.introspection.get_method(service_id, operation_id) input_value = method_def.get_input_definition().new_value() input_value.set_field(param_name, StructValue()) method_result = self.local_provider.invoke(service_id=service_id, operation_id=operation_id, input_value=input_value, ctx=ctx) self.assertEquals(None, method_result.output) self.assertEquals(expected_error_value, method_result.error)
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 visit_set(self, typ): """ Visit a python set :type typ: :class:`vmware.vapi.bindings.type.SetType` :param typ: Binding type of the value """ if not isinstance(self._in_value, (set, frozenset)): msg = message_factory.get_message( 'vapi.bindings.typeconverter.invalid', 'set', type(self._in_value)) logger.debug(msg) raise CoreException(msg) self.visit_list(typ)
def visit_void(self, typ): """ Visit a void value (i.e. None) :type typ: :class:`vmware.vapi.bindings.type.VoidType` :param typ: Binding type of the value """ if self._in_value is not None: msg = message_factory.get_message( 'vapi.bindings.typeconverter.voiddef.expect.null', type(self._in_value).__name__) logger.debug(msg) raise CoreException(msg) self._out_value = typ.definition.new_value()
def resolve(self): """ Traverses all references and resolves the unresolved ones. """ for reference in self._references: if reference.target is None: definition = self._definitions.get(reference.name) if definition is None: msg = message_factory.get_message( 'vapi.data.structref.structure.not.defined', reference.name) logger.debug(msg) raise CoreException(msg) reference.target = definition
def visit_error(self, typ): """ Visit an error value :type typ: :class:`vmware.vapi.bindings.type.ErrorType` :param typ: Binding type of the value """ if isinstance(self._in_value, VapiError): self._visit_vapi_struct(typ) else: msg = message_factory.get_message( 'vapi.bindings.typeconverter.unexpected.error.type', type(self._in_value).__name__) logger.debug(msg) raise CoreException(msg)
def test_invalid_input(self): ctx = ExecutionContext() expected_msg = message_factory.get_message('vapi.method.input.invalid') expected_error_value = make_error_value_from_msgs( invalid_argument_def, expected_msg) method_result = self.local_provider.invoke( service_id=raise_python_exception_id.get_interface_identifier( ).get_name(), operation_id=raise_python_exception_id.get_name(), input_value=IntegerValue(), ctx=ctx) self.assertEquals(None, method_result.output) self.assertEquals(expected_error_value, method_result.error)
def visit_opaque(self, typ): """ Visit an opaque value. Don't do any conversion. :type typ: :class:`vmware.vapi.bindings.type.OpaqueType` :param typ: Binding type of the value """ if not isinstance(self._in_value, DataValue): msg = message_factory.get_message( 'vapi.bindings.typeconverter.unexpected.python.type', DataValue.__name__, type(self._in_value).__name__) logger.debug(msg) raise CoreException(msg) self._out_value = self._in_value
def _visit(self, obj): """ Visit json object :type obj: :class:`object` :param obj: Python object :rtype: :class:`vmware.vapi.data.value.DataValue` :return: Data value """ obj_type = type(obj) if obj_type not in self._dispatch_map: msg = message_factory.get_message( 'vapi.data.serializers.invalid.type', obj_type) raise CoreException(msg) return self._dispatch_map.get(type(obj))(obj)