def invoke(self, service_id, operation_id, input_value, ctx): """ Invoke an API request :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 """ sec_ctx = ctx.security_context app_ctx = ctx.application_context authn_result = None request_scheme = sec_ctx.get(SCHEME_ID) if (self._authn_handlers and request_scheme and request_scheme != NO_AUTH): for handler in self._authn_handlers: # Call authenticate method and get the UserIdentity try: authn_result = handler.authenticate(sec_ctx) except Exception as e: logger.exception( 'Error in invoking authentication handler %s - %s', handler, e) error_value = make_error_value_from_msg_id( self._internal_server_error_def, 'vapi.security.authentication.exception', str(e)) return MethodResult(error=error_value) # authn result false means authentication failed if authn_result is False: error_value = make_error_value_from_msg_id( self._unauthenticated_error_def, 'vapi.security.authentication.invalid') return MethodResult(error=error_value) if authn_result is not None: # Matching authN handler found break if authn_result: # Add the authN identity to the security context to pass on to next # provider sec_ctx[AUTHN_IDENTITY] = authn_result ctx = ExecutionContext(app_ctx, sec_ctx) else: # No AuthN handler found pass an empty security context ctx = ExecutionContext(app_ctx, SecurityContext()) return ApiProviderFilter.invoke(self, service_id, operation_id, input_value, ctx)
def invoke(self, ctx, method_id, input_value): name = method_id.get_name() if name == raise_python_exception: raise RuntimeError() elif name == return_invalid_output: return MethodResult(output=StructValue()) elif name == mock_definition: return MethodResult(output=VoidValue()) return MethodResult(error=not_found_error)
def test_method_result(self): # Method result error_value = self.get_error_value() method_result = MethodResult(error=error_value) self.assertEqual(self.to_vapi_method_result(self.to_json(method_result)).error, method_result.error) data_values = self.get_data_values() for data_value in data_values: method_result = MethodResult(output=data_value) self.assertEqual(self.to_vapi_method_result(self.to_json(method_result)).output, method_result.output)
def invoke(self, ctx, method_id, input_): name = method_id.get_name() self.counters[name] = self.counters.get(name, 0) + 1 if name == veto_method_name: method_input = input_.get_field('operation_input') raise_error = method_input.get_field('raise') if raise_error.value: msg = Message('mockup.message.id', 'mockup error message') return MethodResult( error=make_error_value_from_msgs(not_found_error_def, msg)) return MethodResult(output=VoidValue())
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 invoke(self, service_id, operation_id, input_value, ctx): # Allowed service_id: 'svc' if service_id != 'svc': return MethodResult(error=error_values[2]) # Allowed operation_ids: 'op1', 'op2' if operation_id == 'op1': return MethodResult(output=IntegerValue(10)) elif operation_id == 'op2': if ctx.security_context: return MethodResult(output=IntegerValue(20)) else: return MethodResult(error=error_values[3]) else: return MethodResult(error=error_values[2])
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 test_string_value(self): input_status = 200 input_response = '"vm-100"' expected_result = MethodResult(output=StringValue('vm-100')) actual_result = RestSerializer.deserialize_response( status=input_status, response_str=input_response, is_vapi_rest=False) self.assertEqual(expected_result.output, actual_result.output) self.assertEqual(expected_result.error, actual_result.error)
def test_successful_status_202(self): input_status = 202 input_response = '{"value":"vm-100"}' expected_result = MethodResult(output=StringValue('vm-100')) actual_result = RestSerializer.deserialize_response( status=input_status, response_str=input_response, is_vapi_rest=True) self.assertEqual(expected_result.output, actual_result.output) self.assertEqual(expected_result.error, actual_result.error)
def test_void_value(self): input_status = 200 input_response = '' expected_result = MethodResult(output=VoidValue()) actual_result = RestSerializer.deserialize_response( status=input_status, response_str=input_response, is_vapi_rest=True) self.assertEqual(expected_result.output, actual_result.output) self.assertEqual(expected_result.error, actual_result.error)
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_invoke_long_running_sync(self): ctx = ExecutionContext() input_ = StructValue(method_name) input_.set_field('message', StringValue('hello')) input_.set_field('throw', BooleanValue(False)) actual_method_result = self.provider.invoke(interface_name, task_method_name, input_, ctx) expected_method_result = MethodResult(output=StringValue('hello')) self.assertEqual(actual_method_result.output, expected_method_result.output) self.assertEqual(actual_method_result.error, expected_method_result.error)
def test_struct_value(self): input_status = 200 input_response = '{"id":"vm-100", "name":"Linux VM"}' expected_result = MethodResult(output=StructValue( values={ 'id': StringValue('vm-100'), 'name': StringValue('Linux VM') })) actual_result = RestSerializer.deserialize_response( status=input_status, response_str=input_response, is_vapi_rest=False) self.assertEqual(expected_result.output, actual_result.output) self.assertEqual(expected_result.error, actual_result.error)
def invoke(self, service_id, operation_id, input_value, ctx): """ Invoke an API request :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 """ try: veto_error = self.execute( 'veto', service_id, operation_id, ctx, input_value) if veto_error is not None: return MethodResult(error=veto_error) else: self.execute('pre', service_id, operation_id, ctx, input_value) method_result = ApiProviderFilter.invoke( self, service_id, operation_id, input_value, ctx) self.execute('post', service_id, operation_id, ctx, input_value) return method_result except Exception as e: logger.exception('service: %s, operation: %s, input_value: %s', service_id, operation_id, input_value) error_value = make_error_value_from_msg_id( self._internal_server_error_def, 'vapi.method.invoke.exception', str(e)) method_result = MethodResult(error=error_value) return method_result
def get_operation_info(self, service_id, operation_id): service_info = self._service_data.get(service_id) if service_info: method_ids = service_info.get_definition().get_method_identifiers() operation_names = [ method_id.get_name() for method_id in method_ids ] if operation_id in operation_names: method_def = service_info.get_method_definition( MethodIdentifier(InterfaceIdentifier(service_id), operation_id)) output = self._convert_method_def_to_data_value(method_def) return MethodResult(output=output) else: msg = message_factory.get_message( 'vapi.introspection.operation.not_found', operation_id, service_id) error_value = make_error_value_from_msgs(not_found_def, msg) return MethodResult(error=error_value) 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 method_result(result): """ get method result from jsonrpc dict :type result: :class:`dict` :param result: json method result :rtype: :class:`vmware.vapi.core.MethodResult` :return: method result """ output = None if 'output' in result: output = JsonRpcDictToVapi.data_value(result['output']) error = None if 'error' in result: error = JsonRpcDictToVapi.error_value(result['error']) return MethodResult(output=output, error=error)
def _list(self, method_def, ctx, input_value): """ Get the set of service identifiers :type method_def: :class:`vmware.vapi.core.MethodDefinition` :param method_def: Method definition :type ctx: :class:`vmware.vapi.core.ExecutionContext` :param ctx: Execution context :type input_value: :class:`vmware.vapi.data.value.StructValue` :param input_value: Struct value input :rtype: :class:`vmware.vapi.core.MethodResult` :return: Set of service identifiers """ output = method_def.get_output_definition().new_value() for service in self._adapter.get_services(): output.add(StringValue(service)) return MethodResult(output=output)
def _get(self, method_def, ctx, input_value): """ Returns information about the vAPI provider :type method_def: :class:`vmware.vapi.core.MethodDefinition` :param method_def: Method definition :type ctx: :class:`vmware.vapi.core.ExecutionContext` :param ctx: Execution context :type input_value: :class:`vmware.vapi.data.value.StructValue` :param input_value: Struct value input :rtype: :class:`vmware.vapi.core.MethodResult` :return: Information about the vAPI provider """ output = method_def.get_output_definition().new_value() output.set_field('id', StringValue(self._name)) output.set_field('checksum', StringValue(self._adapter.get_checksum())) return MethodResult(output=output)
def augment_method_result_with_errors(service_id, operation_id, method_result, errors_to_augment): """ Returns a new method result that is identical to `method_result` except that the `errors_definition` field in the `output` (which is of type Operation.Info from Introspection service) contains the errors from the Info structure in `method_result` plus the errors in `errors_to_augment`. This code will be executed only for "get" operation in vAPI Operation Introspection service. :type service_id: :class:`str` :param service_id: Service identifier :type operation_id: :class:`str` :param operation_id: Operation identifier :type method_result: :class:`vmware.vapi.core.MethodResult` :param method_result: Operation result :type errors_to_augment: :class:`list` of :class:`vmware.vapi.data.value.StructValue` :param errors_to_augment: Errors to augment. These are struct values of type com.vmware.vapi.std.introspection.Operation.DataDefinition whose `type` field has the value ERROR to the DataDefinition type in Introspection service IDL. :rtype: :class:`vmware.vapi.data.value.DataValue` :return: Output data value """ if method_result.success(): if (service_id == Introspection.OPERATION_SVC and operation_id == 'get'): output = method_result.output augmented_output = StructValue( 'com.vmware.vapi.std.introspection.operation.info') augmented_output.set_field('input_definition', output.get_field('input_definition')) augmented_output.set_field('output_definition', output.get_field('output_definition')) errors = ListValue() error_names = [] for error_def in output.get_field('error_definitions'): errors.add(error_def) error_names.append(error_def.get_field('name').value.value) for error_def in errors_to_augment: if error_def.get_field('name').value.value not in error_names: errors.add(error_def) augmented_output.set_field('error_definitions', errors) return MethodResult(output=augmented_output) return method_result
def deserialize_response(status, response_str, is_vapi_rest): """ Deserialize the REST response :type status: :class:`int` :param status: HTTP response status code :type response_str: :class:`str` :param response_str: HTTP response body :type is_vapi_rest: :class:`bool` :param is_vapi_rest: Whether the Rest json message format is VAPI Rest or not :rtype :class:`vmware.vapi.core.MethodResult` :return: VAPI MethodResult """ output, error = None, None if response_str is not None and response_str != '': response_value = DataValueConverter.convert_to_data_value( response_str) if status in successful_status_codes: # Successful response, create output # Skyscraper uses 202 for returning tasks if is_vapi_rest: # VAPI REST output has a value wrapper output = response_value.get_field('value') else: output = response_value else: # Create error if is_vapi_rest: # VAPI REST error has specific format name = response_value.get_field('type').value values = dict( response_value.get_field('value').get_fields()) error = ErrorValue(name=name, values=values) else: # For other REST APIs create generic error for now error = ErrorValue(name=http_to_vapi_error_map[status], values={ 'messages': ListValue(), 'data': response_value, }) else: # No response body if status == 200: output = VoidValue() return MethodResult(output=output, error=error)
def test_invoke_long_running(self): ctx = ExecutionContext() input_ = StructValue(method_name) input_.set_field('message', StringValue('hello')) input_.set_field('throw', BooleanValue(False)) actual_method_result = self.provider.invoke(interface_name, '%s$task' % task_method_name, input_, ctx) expected_method_result = MethodResult(output=StringValue('hello')) try: id_split = actual_method_result.output.value.split(':') uuid.UUID(id_split[0], version=4) uuid.UUID(id_split[1], version=4) except ValueError: # There's no assertNotRaises so explicitly fail assert # in case a ValueError is thrown for invalid uuid. self.assertTrue(False) self.assertEqual(actual_method_result.error, expected_method_result.error)
def invoke(self, service_id, operation_id, input_value, ctx): logger.debug( 'Operation started: Service: %s, Operation: %s, Ctx: %s, Input: %s', service_id, operation_id, ctx, input_value) try: method_result = self._invoke_int(service_id, operation_id, input_value, ctx) if method_result.success(): method_result = augment_method_result_with_errors( service_id, operation_id, method_result, self._error_values_to_augment) except Exception as e: logger.exception("Error in invoking %s in %s - %s", service_id, operation_id, e) error_value = make_error_value_from_msg_id( self._internal_server_error_def, 'vapi.method.invoke.exception', str(e)) method_result = MethodResult(error=error_value) logger.debug( 'Operation finished: Service: %s, Operation %s, Ctx: %s, ' 'Output: %s', service_id, operation_id, ctx, method_result) return method_result
def invoke(self, service_id, operation_id, input, ctx): if operation_id == report_vapi_error: error_value = make_error_value_from_msgs(not_found_def, self.msg) return MethodResult(error=error_value) elif operation_id == report_vapi_success: return MethodResult(output=BooleanValue(True)) elif operation_id == 'echo': msg = input.get_field('message') return MethodResult(output=msg) elif operation_id == 'echo_long_running': msg = input.get_field('message') return MethodResult(output=msg) elif operation_id == 'echo_long_running$task': return MethodResult(output=StringValue(str(uuid.uuid4()))) else: error_value = make_error_value_from_msgs(operation_not_found_def, self.msg) return MethodResult(error=error_value)
def test_not_found_error_value(self): input_status = 404 input_response = '{"type":"com.vmware.vapi.std.errors.not_found","value":{"messages":[{"args":["datacenter-21"],"default_message":"Datacenter with identifier \'datacenter-21\' does not exist.","id":"com.vmware.api.vcenter.datacenter.not_found"}]}}' msg_val = StructValue( values={ 'id': StringValue('com.vmware.api.vcenter.datacenter.not_found'), 'default_message': StringValue( 'Datacenter with identifier \'datacenter-21\' does not exist.' ), 'args': ListValue([StringValue('datacenter-21')]) }) error_val = ErrorValue('com.vmware.vapi.std.errors.not_found', {'messages': ListValue([msg_val])}) expected_result = MethodResult(error=error_val) logging.debug(expected_result) actual_result = RestSerializer.deserialize_response( status=input_status, response_str=input_response, is_vapi_rest=True) self.assertEqual(expected_result.output, actual_result.output) self.assertEqual(expected_result.error, actual_result.error)
def invoke(self, ctx, method_id, input_value): return MethodResult(output=input_value)
def invoke(self, service_id, operation_id, input_value, ctx): """ Invoke an API request :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 """ sec_ctx = ctx.security_context authn_result = sec_ctx.get(AUTHN_IDENTITY) try: allowed_authn_schemes = self._allowed_schemes( service_id, operation_id) except Exception: error_value = make_error_value_from_msg_id( self._internal_server_error_def, 'vapi.security.authentication.metadata.invalid', operation_id, service_id) return MethodResult(error=error_value) if allowed_authn_schemes is None or NO_AUTH in allowed_authn_schemes: is_no_auth_allowed = True else: is_no_auth_allowed = False # No valid AuthN info received from AuthN filter for an # operation which requires authentication if (authn_result is None and not is_no_auth_allowed): error_value = make_error_value_from_msg_id( self._unauthorized_error_def, 'vapi.security.authorization.invalid') return MethodResult(error=error_value) if is_no_auth_allowed: return ApiProviderFilter.invoke(self, service_id, operation_id, input_value, ctx) else: result = None for handler in self._authz_handlers: # Call authorize method and validate authZ info try: result = handler.authorize(service_id, operation_id, sec_ctx) except Exception as e: logger.exception( 'Error in invoking authorization handler %s - %s', handler, e) error_value = make_error_value_from_msg_id( self._internal_server_error_def, 'vapi.security.authorization.exception', str(e)) return MethodResult(error=error_value) if result: return ApiProviderFilter.invoke(self, service_id, operation_id, input_value, ctx) error_value = make_error_value_from_msg_id( self._unauthorized_error_def, 'vapi.security.authorization.invalid') return MethodResult(error=error_value)
def invoke(self, ctx, method_id, input_value): """ Invokes the specified method using the execution context and the input provided :type ctx: :class:`vmware.vapi.core.ExecutionContext` :param ctx: Execution context for this method :type input_value: :class:`vmware.vapi.data.value.StructValue` :param input_value: Method input parameters :rtype: :class:`vmware.vapi.core.MethodResult` :return: Result of the method invocation """ op_info = self._operations.get(method_id.get_name()) # Set execution context set_context(ctx) try: # input validators validators = op_info['input_value_validator_list'] input_type = op_info['input_type'] for validator in validators: msg_list = validator.validate(input_value, input_type) if msg_list: error_value = make_error_value_from_msgs( self._invalid_argument_def, *msg_list) return MethodResult(error=error_value) error_value = ApiInterfaceSkeleton.check_for_unknown_fields( input_type, input_value) if error_value: return MethodResult(error=error_value) try: meth_args = TypeConverter.convert_to_python( input_value, binding_type=input_type, resolver=self._resolver) except CoreException as e: error_value = make_error_value_from_msgs( self._invalid_argument_def, *e.messages) return MethodResult(error=error_value) method_name = Converter.canonical_to_pep( get_non_task_operation_name(method_id.get_name())) method = getattr(self._impl, method_name) # If it's a task method then create a new thread to run provider # impl and return task id or result depending upon type of # invocation. if op_info['task_type'] != TaskType.NONE: meth_output, task_accepted = self._invoke_task_operation( ctx, method_id, method, meth_args) if isinstance(meth_output, VapiError): raise meth_output # pylint: disable=raising-bad-type # In case of provider not accepting task throw an error. if task_accepted is False: msg_list = [message_factory.get_message( 'vapi.bindings.skeleton.task.invalidstate')] error_value = make_error_value_from_msgs( self._internal_server_error_def, *msg_list) return MethodResult(error=error_value) else: meth_output = method(**meth_args) output_type = op_info['output_type'] # output validators validators = op_info['output_validator_list'] output = TypeConverter.convert_to_vapi(meth_output, output_type) for validator in validators: msg_list = validator.validate(output, output_type) if msg_list: error_value = make_error_value_from_msgs( self._internal_server_error_def, *msg_list) return MethodResult(error=error_value) result = MethodResult(output=output) except CoreException as e: logger.exception("Error in invoking %s - %s", str(method_id), e) error_value = make_error_value_from_msgs( self._internal_server_error_def, *e.messages) result = MethodResult(error=error_value) except VapiError as e: exc_info = sys.exc_info() error_type = e.__class__.get_binding_type() try: error = TypeConverter.convert_to_vapi(e, error_type) except CoreException as ce: logger.error('Failed to convert %s to error type %s because %s', e, error_type.name, ' because '.join((e.def_msg for e in ce.messages)), exc_info=exc_info) raise result = MethodResult(error=error) finally: # Reset the execution context after the operation clear_context() return result
def invoke(self, service_id, operation_id, input_value, ctx): return MethodResult(output=IntegerValue(10))
def _invoke_int(self, service_id, operation_id, input_value, ctx): # pylint: disable=R0911 """ Internal implementation of InvokeMethod :type service_id: :class:`str` :param service_id: Service identifier :type operation_id: :class:`str` :param operation_id: Operation identifier :type ctx: :class:`vmware.vapi.core.ExecutionContext` :param ctx: Execution context for this method :type input_value: :class:`vmware.vapi.data.value.StructValue` :param input_value: Method input parameters :rtype: :class:`vmware.vapi.core.MethodResult` :return: Result of the method invocation """ # Step 0: Verify input types if (input_value and not (input_value.type == Type.STRUCTURE)): logger.error("Invalid inputs") error_value = make_error_value_from_msg_id( self._invalid_argument_def, 'vapi.method.input.invalid') return MethodResult(error=error_value) iface_id = InterfaceIdentifier(service_id) method_id = MethodIdentifier(iface_id, operation_id) # Step 1: Get method definition iface = self._service_map.get(service_id) if not iface: logger.error('Could not find service: %s', service_id) error_value = make_error_value_from_msg_id( self._operation_not_found_def, 'vapi.method.input.invalid.interface', service_id) return MethodResult(error=error_value) method_def = iface.get_method_definition(method_id) if not method_def: logger.error("Could not find method %s", method_id.get_name()) error_value = make_error_value_from_msg_id( self._operation_not_found_def, 'vapi.method.input.invalid.method', method_id.get_name()) return MethodResult(error=error_value) input_def = method_def.get_input_definition() if not isinstance(input_def, StructDefinition): error_value = make_error_value_from_msg_id( self._internal_server_error_def, 'vapi.method.input.invalid.definition') return MethodResult(error=error_value) output_def = method_def.get_output_definition() if not isinstance(output_def, DataDefinition): error_value = make_error_value_from_msg_id( self._internal_server_error_def, 'vapi.method.output.invalid.definition') return MethodResult(error=error_value) # Step 2: Validate input with input def input_def.complete_value(input_value) messages = input_def.validate(input_value) if messages: logger.error("Input validation failed for method %s", method_id.get_name()) error_value = make_error_value_from_msgs( self._invalid_argument_def, *messages) return MethodResult(error=error_value) # Step 3: Execute method method_result = iface.invoke(ctx, method_id, input_value) # Step 4: Validate output with output def or error against errors set if method_result.success(): messages = output_def.validate(method_result.output) if messages: logger.error("Output validation failed for method %s", method_id.get_name()) error_value = make_error_value_from_msgs( self._internal_server_error_def, *messages) return MethodResult(error=error_value) else: error_value = method_result.error messages = self._validate_error(error_value, method_def) if messages: new_error_value = make_error_value_from_error_value_and_msgs( self._internal_server_error_def, error_value, *messages) return MethodResult(error=new_error_value) return method_result
def invoke(self, ctx, method_id, input_value): return MethodResult(output=IntegerValue(10))