예제 #1
0
    def __init__(self, next_provider=None, provider_config=None):
        """
        Initialize AuthorizationFilter

        :type  next_provider: :class:`vmware.vapi.core.ApiProvider`
        :param next_provider: API Provider to invoke the requests
        :type  provider_config:
            :class:`vmware.vapi.settings.config.ProviderConfig` or :class:`None`
        :param provider_config: Provider configuration object
        """
        handler_names = []
        self._metadata = None
        if provider_config:
            # Get the registered AuthN handlers from config file
            (handler_names, metadata_file) = \
                provider_config.get_authorization_handlers_and_file()
            self._metadata = get_metadata(metadata_file)

        from vmware.vapi.lib.load import dynamic_import
        self._authz_handlers = []
        for handler_name in handler_names:
            # Dynamically load the AuthZ handler
            handler_constructor = dynamic_import(handler_name)
            if handler_constructor is None:
                raise ImportError('Could not import %s' % handler_name)

            self._authz_handlers.append(handler_constructor())

        self._internal_server_error_def = make_std_error_def(
            'com.vmware.vapi.std.errors.internal_server_error')
        self._unauthorized_error_def = make_std_error_def(
            'com.vmware.vapi.std.errors.unauthorized')
        ApiProviderFilter.__init__(
            self, next_provider,
            [self._internal_server_error_def, self._unauthorized_error_def])
예제 #2
0
 def setUp(self):
     self.error_name = 'suites.test_error.TestErrorValue.SampleError'
     self.error_def = make_std_error_def(self.error_name)
     self.message1 = Message('suites.test_error.TestErrorValue.message1',
                             'Message 1 - %s %s', 'arg1', 'arg2')
     self.message2 = Message('suites.test_error.TestErrorValue.message2',
                             'Message 2')
     self.message3 = Message('suites.test_error.TestErrorValue.message3',
                             'Message 3')
     self.single_message = (self.message1, )
     self.multiple_messages = (self.message1, self.message2, self.message3)
예제 #3
0
                                             raise_python_exception)
return_invalid_output_id = MethodIdentifier(interface_id,
                                            return_invalid_output)

method_id_dict = {
    missing_input_definition: missing_input_definition_id,
    invalid_input_definition: invalid_input_definition_id,
    missing_output_definition: missing_output_definition_id,
    invalid_output_definition: invalid_output_definition_id,
    report_declared_error: report_declared_error_id,
    report_undeclared_error: report_undeclared_error_id,
    raise_python_exception: raise_python_exception_id,
    return_invalid_output: return_invalid_output_id,
}

internal_server_error_def = make_std_error_def(
    'com.vmware.vapi.std.errors.internal_server_error')
invalid_argument_def = make_std_error_def(
    'com.vmware.vapi.std.errors.invalid_argument')
not_found_error_def = make_std_error_def(
    'com.vmware.vapi.std.errors.not_found')
operation_not_found_def = make_std_error_def(
    'com.vmware.vapi.std.errors.operation_not_found')

error_values = ListValue([
    convert_data_def_to_data_value(internal_server_error_def),
    convert_data_def_to_data_value(invalid_argument_def),
    convert_data_def_to_data_value(operation_not_found_def)
])

_msg = Message('mockup.message.id', 'mockup error message')
not_found_error = make_error_value_from_msgs(not_found_error_def, _msg)
예제 #4
0
            instance.set_field(field_name, field_instance)
    elif data_def.type == Type.LIST:
        # TODO: Randomize list len?
        instance.add_all([build_adhoc_data_value(data_def.element_type) for val in range(1,7)])
    elif data_def.type == Type.OPTIONAL:
        # TODO: Randomize optional set or not set?
        instance = data_def.new_value(build_adhoc_data_value(data_def.element_type))
    return instance

fake_iface_id = 'vmware.test.fake.iface'
null_method_id = 'Null'
null_method_def = MethodDefinition(MethodIdentifier(
    InterfaceIdentifier(fake_iface_id), null_method_id),
    VoidDefinition(),
    VoidDefinition(),
    [make_std_error_def('error1'),
     make_std_error_def('error2')]
    )
echo_method_id = 'Echo'
echo_method_def = MethodDefinition(MethodIdentifier(
    InterfaceIdentifier(fake_iface_id), echo_method_id),
    StructDefinition('struct',
       [('int', IntegerDefinition()),
        ('str', StringDefinition())]
    ),
    StructDefinition('struct',
       [('int', IntegerDefinition()),
        ('str', StringDefinition())]
    ),
    [make_std_error_def('e1'),
     make_std_error_def('e2'),
예제 #5
0
class InterposerProvider(ApiProviderFilter):
    """
    InterposerProvider is an interposer implementation of the
    ApiProvider interface. It provides hook in capabilities
    for adding interposers to invoke method calls
    """
    _internal_server_error_def = make_std_error_def(
            'com.vmware.vapi.std.errors.internal_server_error')

    def __init__(self, next_provider=None):
        """
        Initializer InterposerProvider.

        :type  next_provider: :class:`vmware.vapi.core.ApiProvider` or ``None``
        :param next_provider: API Provider to invoke the requests
        """
        ApiProviderFilter.__init__(self, next_provider,
                                   [self._internal_server_error_def])
        # Key: Interposer type, Value: set of interposer ids of that type
        self._interposers = {}

        # Key: Interposer Id, Value: Interposer Map
        self._interposer_map = {}

        # Key: Interposer Id, Value: Method Identifier
        self._interposer_def = {}

        # Method identifiers of all the interposers
        self._interposer_methods = set()

        connector = LocalConnector(self.next_provider)
        self._introspection = IntrospectableApiProvider(connector)

    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 add(self, spec):
        """
        Register a new interposer

        :type  spec:
            :class:`com.vmware.vapi.admin_provider.Interposer.InterposerInfo`
        :param spec: The specification of the interposer. This encapsulates
            details such as the type of the interposer and the list of packages
            that the interposer operates on.
        :rtype: :class:`str`
        :return: ID of the interposer
        :raise: :class:`com.vmware.vapi.std.errors_provider.NotFound`: If
            service name or method name specified in the operation identity
            structure does not exist
        :raise: :class:`vmware.vapi.provider.interposer.InvalidArgument`: If the
            interposer type is invalid
        """
        method_def = self._introspection.get_method(spec.service_name,
                                                    spec.operation_name)
        if spec.type not in [InterposerType.PRE,
                             InterposerType.POST,
                             InterposerType.VETO]:
            raise InvalidArgument()

        interposer_id = str(uuid.uuid4())
        self._interposers.setdefault(spec.type, []).append(interposer_id)
        self._interposer_map[interposer_id] = spec
        self._interposer_methods.add((spec.service_name, spec.operation_name))
        self._interposer_def[interposer_id] = method_def
        return interposer_id

    def get(self, interposer_id):
        """
        Get the information about an interposer

        :type  interposerId: :class:`str`
        :param interposerId: Identifier of the interposer for which the
            information has to be retrieved
        :rtype: :class:`com.vmware.vapi.admin_provider.Interposer.InterposerInfo`
        :return: Information about the interposerId passed in the argument
        :raise: :class:`vmware.vapi.provider.interposer.NotFound`: If the interposer_id does
        """
        info = self._interposer_map.get(interposer_id)
        if info is None:
            raise NotFound()
        else:
            return info

    def list(self):
        """
        List all the registered interposer identifiers

        :rtype: :class:`list` of :class:`str`
        :return: Interposer identifiers
        """
        return sum(list(self._interposers.values()), [])

    def remove(self, interposer_id):
        """
        Unregister an interposer

        :type  interposerId: :class:`str`
        :param interposerId: Identifier of the interposer that has to be
            unregistered
        :raise: :class:`vmware.vapi.provider.interposer.NotFound`: If the interposer_id does
            not exist
        """
        info = self._interposer_map.get(interposer_id)
        if info is None:
            raise NotFound()
        else:
            self._interposers[info.type].remove(interposer_id)
            self._interposer_methods.remove((info.service_name,
                                             info.operation_name))
            del self._interposer_map[interposer_id]

    def _execute_int(self,
                     interposer_id,
                     interposer_type,
                     ctx,
                     service_name,
                     operation_name,
                     opeartion_input):
        """
        Execute an interposer

        :type  interposer_id: :class:`str`
        :param interposer_id: Interposer id
        :type  interposer_type: :class:`InterposerType`
        :param interposer_type: Interposer type
        :type  ctx: :class:`vmware.vapi.core.ExecutionContext`
        :param ctx: Execution context for the invocation
        :type  service_name: :class:`str`
        :param service_name: Name of the service that is being interposed
        :type  operation_name: :class:`str`
        :param operation_name: Name of the operation that is being interposed
        :type  opeartion_input: :class:`vmware.vapi.data.value.DataValue`
        :param opeartion_input: Input for the operation that is being interposed
        :rtype: :class:`vmware.vapi.data.value.ErrorValue`
        :return: Error is returned if the veto interposer execution
                 reported an error, else None
        """
        in_method_def = self._interposer_def.get(interposer_id)
        if in_method_def:
            interposer_input = in_method_def.get_input_definition().new_value()
            interposer_input.set_field(SERVICE_NAME,
                                       StringValue(service_name))
            interposer_input.set_field(OPERATION_NAME,
                                       StringValue(operation_name))
            interposer_input.set_field(OPERATION_INPUT,
                                       opeartion_input)
            in_method_id = in_method_def.get_identifier()
            method_result = ApiProviderFilter.invoke(
                self, in_method_id.get_interface_identifier().get_name(),
                in_method_id.get_name(),
                interposer_input, ctx)

            # Only veto interposers can break the control flow
            if interposer_type == InterposerType.VETO and \
                    not method_result.success():
                return method_result.error

    def execute(self, interposer_type, service_id, operation_id, ctx, input_):
        """
        Execute an interposer

        :type  interposer_type: :class:`InterposerType`
        :param interposer_type: Type of the interposer
        :type  ctx: :class:`vmware.vapi.core.ExecutionContext`
        :param ctx: Execution context for the invocation
        :type  input_: :class:`vmware.vapi.data.value.StructValue`
        :param input_: Input for the method
        :rtype: :class:`vmware.vapi.data.value.ErrorValue`
        :return: Error is returned if the veto interposer execution
                 reported an error, else None
        """
        # Interposers are not allowed to call other interposers
        if (service_id, operation_id) in self._interposer_methods:
            return

        interposer_ids = self._interposers.get(interposer_type)
        if not interposer_ids:
            return

        try:
            for interposer_id in interposer_ids:
                info = self._interposer_map.get(interposer_id)
                pkgs = info.packages
                execute = True if not pkgs else False
                for pkg in pkgs:
                    if service_id.startswith(pkg):
                        execute = True
                if execute:
                    return self._execute_int(interposer_id,
                                             info.type,
                                             ctx,
                                             service_id,
                                             operation_id,
                                             input_)
        except Exception as e:
            logger.error('%s', str(e))
예제 #6
0
    def __call__(self, request):
        """
        The implementation of WsgiApplication

        :type  request: :class:`werkzeug.wrappers.Request`
        :param request: Request object
        :rtype: :class:`werkzeug.wrappers.Response`
        :return: Response object
        """
        try:
            urls = self.rule_map.bind_to_environ(request.environ)
            endpoint, args = urls.match()
            if provider_wire_logger.isEnabledFor(logging.DEBUG):
                provider_wire_logger.debug('HTTP %s %s ', request.method,
                                           request.url)
                provider_wire_logger.debug('REQUEST HEADERS: %s',
                                           request.headers)
                provider_wire_logger.debug('REQUEST BODY: %s',
                                           request.get_data())
            if endpoint == JSONRPC:
                result, headers = self._handle_jsonrpc_call(request)
                response = Response(result)
                if headers:
                    response.headers = headers
            else:
                status, result, cookies, headers = self._handle_rest_call(
                    request, endpoint, args)
                response = Response(result)
                response.status_code = status
                if headers:
                    response.headers = headers
                if cookies:
                    path = self._rest_prefix
                    for k, v in six.iteritems(cookies):
                        response.set_cookie(k, v, path=path)
            response.content_type = JSON_CONTENT_TYPE
            if provider_wire_logger.isEnabledFor(logging.DEBUG):
                provider_wire_logger.debug('RESPONSE STATUS: %s',
                                           response.status_code)
                provider_wire_logger.debug('RESPONSE HEADERS: %s',
                                           response.headers)
                provider_wire_logger.debug('RESPONSE BODY: %s',
                                           response.response)
            return response
        except HTTPException as e:
            try:
                internal_server_error_def = make_std_error_def(
                    'com.vmware.vapi.std.errors.invalid_request')
                msg = message_factory.get_message(
                    'vapi.method.invoke.exception',  # pylint: disable=line-too-long
                    e.description)
                error_value = make_error_value_from_msgs(
                    internal_server_error_def, msg)
                json_output = DataValueConverter.convert_to_json(error_value)
                response = e.get_response()
                response.set_data(json_output)
                response.headers['Content-Type'] = 'application/json'
                return response
            except Exception:
                # if there is error in serialize the error,
                # just return the original raw exception
                return e
예제 #7
0
class ApiInterfaceSkeleton(ApiInterface):
    """
    Skeleton class for :class:`ApiInterface`. This class implements the
    :class:`ApiInterface` interface.
    """

    # XXX These error definitions should be eliminated when we figure out
    #     where/how to get the error definitions used by the local provider
    _internal_server_error_def = make_std_error_def(
        'com.vmware.vapi.std.errors.internal_server_error')
    _invalid_argument_def = make_std_error_def(
        'com.vmware.vapi.std.errors.invalid_argument')
    _operation_not_found_def = make_std_error_def(
        'com.vmware.vapi.std.errors.operation_not_found')
    _unexpected_input_def = make_std_error_def(
        'com.vmware.vapi.std.errors.unexpected_input')

    # Errors that are reported by this class
    _error_defs_to_augment = (
        _internal_server_error_def,
        _invalid_argument_def,
        _unexpected_input_def
    )

    # Errors that are allowed be reported by provider implementations even
    # though they are not included in the VMODL2 throws clause.
    #
    # XXX: This should be removed when once the runtime/bindings
    # check for invocations using API features that are disabled by feature
    # switch and report these errors (so provider implementations don't need
    # to report them).
    _unchecked_error_defs = (
        _operation_not_found_def,
        _unexpected_input_def
    )

    def __init__(self, iface_name, impl, operations, error_types):
        """
        Initialize the ApiInterface skeleton class

        :type  iface_name: :class:`str`
        :param iface_name: Interface name
        :type  impl: :class:`VapiInterface`
        :param impl: Class that implements this interface
        :type  operations: :class:`dict`
        :param operations: Description of the operations in this service
        :type  error_types: :class:`list` of
            :class:`vmware.vapi.bindings.type.ErrorType`
        :param error_types: error types to be registered in this configuration
        """
        self._operations = operations
        self._iface_id = InterfaceIdentifier(iface_name)
        operation_ids = [MethodIdentifier(self._iface_id, operation_name)
                         for operation_name in six.iterkeys(self._operations)]
        self._iface_def = InterfaceDefinition(self._iface_id,
                                              operation_ids)
        self._impl = impl
        error_types = error_types or []
        self._resolver = NameToTypeResolver(
            {e.definition.name: e for e in error_types})
        ApiInterface.__init__(self)

    @staticmethod
    def _pepify_args(meth_args):
        """
        Converts all the keys of given keyword arguments into PEP8 standard
        names

        :type  meth_args: :class:`dict`
        :param meth_args: The keyword arguments to be converted
        :rtype:  :class:`dict`
        :return: The converted keyword arguments
        """
        new_args = {}
        for k, v in six.iteritems(meth_args):
            new_args[Converter.canonical_to_pep(k)] = v
        return new_args

    @classmethod
    def is_set_optional_field(cls, value):
        """
        Returns true if the value is OptionalValue and it is set

        :type  value: :class:`vmware.vapi.data.value.DataValue`
        :param value: value to be checked
        :rtype: :class:`bool`
        :return: True if the value is OptionalValue and is set, False otherwise
        """
        return not isinstance(value, OptionalValue) or value.is_set()

    @classmethod
    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_identifier(self):
        return self._iface_id

    def get_definition(self):
        return self._iface_def

    def get_method_definition(self, method_id):
        op_info = self._operations.get(method_id.get_name())
        if op_info is None:
            return None

        errors_defs = [e.definition for e in op_info['errors']]
        for error_def in self._error_defs_to_augment:
            if error_def not in errors_defs:
                errors_defs.append(error_def)

        # XXX: This loop should be removed when once the runtime/bindings
        # check for invocations using API features that are disabled by feature
        # switch and report these errors (so provider implementations don't need
        # to report them).
        for error_def in self._unchecked_error_defs:
            if error_def not in errors_defs:
                errors_defs.append(error_def)

        return MethodDefinition(method_id,
                                op_info['input_type'].definition,
                                op_info['output_type'].definition,
                                errors_defs)

    def _invoke_task_operation(self, ctx, method_id, method, method_args):
        """
        Invokes the task operations by adding them in thread pool queue.

        :type  ctx: :class:`vmware.vapi.core.ExecutionContext`
        :param ctx: Execution context for this method
        :type  method_id: :class:`str`
        :param method_id: Method id
        :type  method: :class:`str`
        :param method: Method name
        :type  method_args: :class:`args`
        :param method_args: Method input parameters

        :rtype: :class:`tuple` of :class:`str` or
                :class:`vmware.vapi.core.MethodResult` and :class:`bool`
        :return: Tuple of task id and boolean value or Method Result
        """

        from vmware.vapi.lib.thread_pool import get_threadpool
        from vmware.vapi.task.task_manager_impl import get_task_manager
        task_manager = get_task_manager()
        thread_pool = get_threadpool()
        service_id = method_id.get_interface_identifier().get_name()
        operation_id = method_id.get_name()
        op_info = self._operations.get(operation_id)
        task_id = task_manager.create_task(
                                None,
                                service_id,
                                operation_id,
                                False,
                                op_info['errors'])
        insert_task_id(ctx.application_context, task_id)
        event = threading.Event()
        accept_timeout = 30
        if is_task_operation(method_id.get_name()):
            work_item = thread_pool.queue_work(
                                        self._invoke_method,
                                        ctx, method,
                                        method_args, event)
            event_set = event.wait(accept_timeout)

            # In case of provider throwing an error before  accepting task
            # remove task from task manager.
            if work_item.err:
                task_manager.remove_task(task_id)
                return work_item.err, None

            # In case of provider not accepting task remove task from task
            # manager.
            if not event_set:
                task_manager.remove_task(task_id)
                return None, False

            return task_id, event_set
        else:
            # If $task is not present in method id for a task operation
            # even then we will still use a separate thread in threadpool
            # and wait for its completion to return the result that's
            # to ensure provider implementation can be same irrespective
            # of client calling for task or non-task version of operation
            result = thread_pool.queue_work_and_wait(self._invoke_method,
                                                     ctx,
                                                     method,
                                                     method_args,
                                                     event)

            return result[1], None

    def _invoke_method(self, ctx, method, method_args, event):
        """
        Invoke the provider method after setting appropriate context and event

        :type  ctx: :class:`vmware.vapi.core.ExecutionContext`
        :param ctx: Execution context for this method
        :type  method: :class:`str`
        :param method: Method name
        :type  method_args: :class:`args`
        :param method_args: Method input parameters
        :type  event: :class:`threading.Event`
        :param event: Event object

        :rtype: :class:`vmware.vapi.core.MethodResult`
        :return: Result of the method invocation
        """
        logger.debug('Setting execution ctx for the new thread')
        set_context(ctx)
        set_event(event)
        result = method(**method_args)
        clear_context()
        return result

    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
예제 #8
0
veto_method_id = MethodIdentifier(interface_id, veto_method_name)

method_id_dict = {
    mock_method_name: mock_method_id,
    pre_method_name: pre_method_id,
    post_method_name: post_method_id,
    veto_method_name: veto_method_id,
}

input_def = StructDefinition('input', [
    ('service_name', StringDefinition()),
    ('operation_name', StringDefinition()),
    ('operation_input', OpaqueDefinition()),
])
mock_input_def = StructDefinition('input', [('raise', BooleanDefinition())])
not_found_error_def = make_std_error_def(
    'com.vmware.vapi.std.errors.not_found')
errors = [
    'com.vmware.vapi.std.errors.not_found',
    'com.vmware.vapi.std.errors.internal_server_error',
    'com.vmware.vapi.std.errors.invalid_argument',
    'com.vmware.vapi.std.errors.operation_not_found',
]
error_defs = [make_std_error_def(error) for error in errors]
error_values = ListValue(
    [convert_data_def_to_data_value(error_def) for error_def in error_defs])


class MockupApiInterface(ApiInterface):
    def __init__(self, interface_id):
        self._interface_id = interface_id
        self.counters = {}
예제 #9
0
                              MethodDefinition, MethodResult, ExecutionContext)
from vmware.vapi.data.definition import (StructDefinition, StringDefinition,
                                         ListDefinition, StructRefDefinition,
                                         OptionalDefinition)
from vmware.vapi.data.serializers.introspection import (
    convert_data_def_to_data_value)
from vmware.vapi.lib.constants import (Introspection, MAP_ENTRY,
                                       OPERATION_INPUT)
from vmware.vapi.lib.log import get_vapi_logger
from vmware.vapi.lib.std import make_error_value_from_msgs, make_std_error_def
from vmware.vapi.data.value import (StringValue, ListValue, StructValue)
from vmware.vapi.lib.fingerprint import generate_fingerprint
from vmware.vapi.l10n.runtime import message_factory

# errors
not_found_def = make_std_error_def('com.vmware.vapi.std.errors.not_found')

logger = get_vapi_logger(__name__)


def get_checksum(api_services):
    """
    Returns the checksum of services registered with LocalProvider

    :type  api_services: :class:`dict`
    :param api_services: Dictionary of all the services registered with local provider
        Key is :class:`vmware.vapi.core.InterfaceIdentifier` and value is
        :class:`vmware.vapi.core.ApiInterface`
    :rtype: :class:`str`
    :return: checksum of the service information
    """
예제 #10
0
class LocalProvider(ApiProvider):
    """
    LocalProvider is a local in-process implementation of the
    ApiProvider interface
    """
    # XXX These error definitions should be eliminated when we figure out
    #     where/how to get the error definitions used by the local provider
    _internal_server_error_def = make_std_error_def(
        'com.vmware.vapi.std.errors.internal_server_error')
    _invalid_argument_def = make_std_error_def(
        'com.vmware.vapi.std.errors.invalid_argument')
    _operation_not_found_def = make_std_error_def(
        'com.vmware.vapi.std.errors.operation_not_found')

    def __init__(self, load_introspection=True):
        """
        Initialize LocalProvider

        :type  load_introspection: :class:`bool`
        :param load_introspection: If true, load introspection services
        """
        ApiProvider.__init__(self)
        self._name = "LocalProvider"
        # key = service id, value = api interface
        self._service_map = {}

        self._introspector = introspection.LocalProviderIntrospector(
            self._name)
        if load_introspection:
            self.add_interface(self._introspector.get_introspection_services())

        self._error_defs_to_augment = [
            self._internal_server_error_def,
            self._invalid_argument_def,
            self._operation_not_found_def,
        ]
        # these are the errors that should be augmented
        # when `get` operation is invoked on
        # com.vmware.vapi.std.introspection.Operation
        self._error_values_to_augment = [
            convert_data_def_to_data_value(error_def)
            for error_def in self._error_defs_to_augment
        ]

    def register_by_properties(self, provider_cfg):
        """
        Register set of interfaces using provider configuration object

        :type  provider_cfg: :class:`vmware.vapi.settings.config.ProviderConfig`
        :param provider_cfg: Configuration for this vAPI provider
        """
        self._name = provider_cfg.get_provider_name()

        services = provider_cfg.get_interfaces()
        for service in services:
            service = service.strip()
            if service:

                # Get the service specific configuration from the config
                service_cfg = provider_cfg.get_service_config(service)

                fn = "register_instance"
                try:
                    module = __import__(service, globals(), locals(), [fn])
                    register_fn = getattr(module, fn)

                    # XXX: Remove the if condition once all the implementations
                    # have been updated to use the new cfg parameter
                    if inspect.getargspec(register_fn).args:  # pylint: disable=W1505
                        ifaces = register_fn(service_cfg)
                    else:
                        ifaces = register_fn()
                    self.add_interface(ifaces)
                except Exception:
                    stack_trace = traceback.format_exc()
                    raise Exception('Could not add service %s due to %s' %
                                    (service, stack_trace))

    def add_interface(self, ifaces):
        """
        Register an interface with LocalProvider

        :type  ifaces: :class:`list` of :class:`vmware.vapi.core.ApiInterface`
        :param ifaces: Interfaces to be registered
        """
        if not isinstance(ifaces, list):
            ifaces = [ifaces]

        for iface in ifaces:
            # iface could be VapiInterface if bindings layer is registering
            # it's interfaces. In that case, extract the skeleton from
            # VapiInterface
            if isinstance(iface, VapiInterface):
                api_iface = iface.api_interface
            # In Dynamic providers, where ApiInterface is implemented directly
            # instead of using bindings, an instance of ApiInterface is passed
            elif isinstance(iface, ApiInterface):
                api_iface = iface
            else:
                raise Exception(
                    'Could not register the interface %s. It has to be either '
                    'an instance of vmware.vapi.bindings.VapiInterface or '
                    'an instance of vmware.vapi.core.ApiInterface' %
                    str(iface.__class__))

            service_id = api_iface.get_identifier().get_name()
            if service_id in self._service_map:
                raise Exception('Service already registered: %s' % service_id)

            logger.info('Registering service: %s', service_id)
            self._service_map[service_id] = api_iface
            if self._introspector:
                self._introspector.add_service(service_id, api_iface)

    @staticmethod
    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 _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, 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
예제 #11
0
from vmware.vapi.protocol.client.connector import Connector

### Data used by the mockup classes and the test methods

# Method names
report_vapi_success = 'report_vapi_success'
report_vapi_error = 'report_vapi_error'
report_vapi_exception = 'report_vapi_exception'
nonexistent_method = 'nonexistent_method'

# Type names
not_found = 'not_found'
operation_not_found = 'operation_not_found'

# Error definitions
not_found_def = make_std_error_def(not_found)
operation_not_found_def = make_std_error_def(operation_not_found)

### Utility functions used by the mockup classes and the test methods


def input_struct_name(method_name):
    return 'Input%s' % method_name


class MockupNotFound(VapiError):
    def __init__(self, **kwargs):
        self.messages = kwargs.get('messages')
        VapiError.__init__(self)

예제 #12
0
class AggregatorProvider(ApiProvider):
    """
    AggregatorProvider is an aggregating implementation of the
    ApiProvider interface. It aggregates a bunch of ApiProvider
    instances and expose it.
    """
    def __init__(self):
        ApiProvider.__init__(self)
        self._name = 'ApiAggregator'

        # key = service_id, value = api interface
        self._service_id_map = {}

        # key : name, value : provider
        self._providers = {}

        # key : name, value : connection info
        self._provider_data = {}

        # List of all the services present in the local provider
        self._local_service_ids = []

        self._introspection_service_names = [
            Introspection.PROVIDER_SVC, Introspection.SERVICE_SVC,
            Introspection.OPERATION_SVC
        ]
        self._introspector = None
        self._authn_chain_processors = []

    _operation_not_found_def = make_std_error_def(
        'com.vmware.vapi.std.errors.operation_not_found')
    _internal_server_error_def = make_std_error_def(
        'com.vmware.vapi.std.errors.internal_server_error')
    _augmented_errors = (_operation_not_found_def, _internal_server_error_def)

    ###########################################################################
    #
    # Support functions for aggregator services
    #
    ###########################################################################

    @staticmethod
    def get_service_identifiers(api_provider):
        """
        Invokes introspection service list on the Api Provider and retrieves
        the list of services

        :type  api_provider: :class:`vmware.vapi.core.ApiProvider`
        :param api_provider: ApiProvider instance to be used for retrieving
            service identifiers
        :rtype: :class:`list` of :class:`str`
        :return: List of service identifiers
        :raise: :class:`Exception`: if service identifiers could not be
            retrieved
        """
        ctx = ExecutionContext()
        service_name = Introspection.SERVICE_SVC
        operation_name = 'list'
        struct_value = StructValue(OPERATION_INPUT)
        method_result = api_provider.invoke(service_name, operation_name,
                                            struct_value, ctx)
        if method_result.success():
            return [service.value for service in method_result.output]
        else:
            raise Exception('Could not retrieve service identifiers: %s',
                            repr(method_result.error))

    def _process_child(self, provider_cfg, child):
        """
        Process a aggregator child provider_cfg

        :type  provider_cfg: :class:`dict`
        :param provider_cfg: Properties dictionary
        :type  child: :class:`str`
        :param child: Child to be processed
        """
        try:
            url = provider_cfg.get_child_url(child)
            connector = connect.get_requests_connector(
                session=requests.session(), url=url)
            api_provider = connector.get_api_provider()
            rpc_protocol = get_url_scheme(url)
            msg_protocol = provider_cfg.get_provider_message_format(child)
            if api_provider:
                self.register_provider(api_provider,
                                       child,
                                       rpc_protocol,
                                       msg_protocol,
                                       addr=url)
        except Exception as e:
            stack_trace = traceback.format_exc()
            raise Exception('Could not register %s at %s due to %s' %
                            (child, url, stack_trace))

    def register_by_properties(self, provider_cfg):
        """
        Register a provider using a provider_cfg dictionary

        :type  provider_cfg: :class:`dict`
        :param provider_cfg: Properties dictionary
        """
        self._name = provider_cfg.get_provider_name()
        if not self._name:
            self._name = 'ApiAggregator'

        #
        # We have three providers here:
        #   Aggregator: To aggregate services from local and remote providers
        #   IntrospectionProvider: To serve introspection requests. This has
        #       all the introspection services but it will decide whether to
        #       route the introspection call to LocalProvider or Remote provider
        #       based on where it is located.
        #   LocalProvider: To serve requests for services specified in
        #       'local.interfaces'. This also has introspection services that
        #       only provides introspection for services in this LocalProvider.
        #
        # For non-introspection calls:
        # - If service is in LocalProvider, flow is Aggregator -> LocalProvider
        # - If service is in remote ApiProvider, flow is Aggregator -> Remote
        #        api provider
        #
        # For introspection calls:
        # - For a service in remote ApiProvider, flow is
        # Aggregator -> Introspection services in IntrospectionProvider
        # - For a service in LocalProvider, flow is
        # Aggregator -> Introspection services in IntrospectionProvider
        # - For a service in introspection set of services, flow is
        # Aggregator -> IntrospectionProvider -> Introspection services in
        # LocalProvider (If it is handled by IntrospectionProvider, then we
        # will go into infinite recursive calls)
        #

        # Retrieve the local provider singleton instance to hold all the
        # services present in 'local.interfaces' property of in the properties
        # file. Even if there are no interfaces in local.interfaces, this is
        # required as it would serve introspection requests for introspection
        # api.
        l_provider = local_provider.get_provider()
        self._introspector = AggregatorIntrospector(self._name, l_provider)
        l_provider.register_by_properties(provider_cfg)

        # Create a local provider to hold the introspection services that
        # aggregates introspection information from the local provider and
        # all the remote providers.
        introspection_provider = LocalProvider(load_introspection=False)
        for service in self._introspector.get_introspection_services():
            # Register the introspection service with the local provider
            introspection_provider.add_interface(service)
            # Register the introspection service with this aggregator
            self.register_service(service.get_identifier().get_name(),
                                  introspection_provider)

        # Registering services from local provider
        service_ids = self.get_service_identifiers(l_provider)
        self._local_service_ids = service_ids
        for service_id in service_ids:
            if service_id not in self._introspection_service_names:
                self.register_service(service_id, l_provider)

        # Register the children
        for child in provider_cfg.get_child_providers():
            self._process_child(provider_cfg, child)

    def get_providers(self):
        """
        Return the connection information of ApiProviders
        registered with the AggregatorProvider

        :rtype: :class:`list` of :class:`tuple` of (
            :class:`str`, :class:`str`, :class:`str`)
        :return: Tuple containing rpc protocol, msg protocol and uri
        """
        return self._provider_data

    def register_service(self, service_id, provider):
        """
        Register an service with the AggregatorProvider

        :type  service_id: :class:`str`
        :param service_id: Service identifier
        :type  provider: :class:`vmware.vapi.core.ApiProvider`
        :param provider: ApiProvider impl. for the specified service identifier
        """
        if service_id in self._service_id_map:
            logger.error('Service already registered: %s', service_id)
        else:
            self._service_id_map[service_id] = provider
            self._introspector.add_service(service_id, provider)
            logger.info('Registering service: %s', service_id)

    def register_provider(self, provider, name, rpc, msg, addr):
        """
        Register a ApiProvider with the AggregatorProvider

        :type  provider: :class:`vmware.vapi.core.ApiProvider`
        :param provider: ApiProvider to be registered
        :type  name: :class:`str`
        :param name: Provider name
        :type  rpc: :class:`str`
        :param rpc: RPC Protocol
        :type  msg: :class:`str`
        :param msg: Msg Protocol
        :type  addr: :class:`str`
        :param addr: URI of the ApiProvider
        """
        self._provider_data[name] = (rpc, msg, addr)
        old_provider = self._providers.get(name)
        if old_provider:
            return

        service_ids = self.get_service_identifiers(provider)
        for service_id in service_ids:
            if service_id not in self._introspection_service_names:
                self.register_service(service_id, provider)
        self._providers[name] = provider

    def unregister_service(self, service_id):
        """
        Unregister an service from AggregatorProvider

        :type  service_id: :class:`str`
        :param service_id: service to be unregistered
        """
        if service_id in self._service_id_map:
            del self._service_id_map[service_id]
            self._introspector.remove_service(service_id)

    def unregister_provider(self, provider_name):
        """
        Unregister a provider from AggregatorProvider

        :type  provider_name: :class:`str`
        :param provider_name: Provider to be unregistered
        """
        provider = self._providers.get(provider_name)
        if not provider:
            return

        service_ids = self.get_service_identifiers(provider)
        for service_id in service_ids:
            self.unregister_service(service_id)
        del self._providers[provider_name]
        del self._provider_data[provider_name]

        logger.info('Unregistering Provider %s', provider.get_definition())

    def unregister_provider_by_name(self, provider_name):
        """
        Unregister a provider from AggregatorProvider

        :type  provider_name: :class:`str`
        :param provider_name: Provider to be unregistered
        """
        self.unregister_provider(provider_name)

    def reload_providers(self):
        """
        Reload all the providers in the AggregatorProvider
        """
        providers = list(
            self._providers.values()) + [local_provider.get_provider()]
        for provider in providers:
            service_ids = self.get_service_identifiers(provider)
            for service_id in service_ids:
                self.register_service(service_id, provider)

    ###########################################################################
    #
    # Implementation of Api Provider interface
    #
    ###########################################################################

    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 invoke(self, service_id, operation_id, input_value, ctx):
        """
        Invokes the specified method using the execution context and
        the input provided

        :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
        """
        logger.info("Started: Service: %s, Operation: %s, Ctx: %s", service_id,
                    operation_id, ctx)
        method_result = self._invoke_int(service_id, operation_id, input_value,
                                         ctx)
        logger.info("Finished: Service: %s. Operation %s, Ctx: %s", service_id,
                    operation_id, ctx)
        return method_result
                        'resources')

mock_interface_id = InterfaceIdentifier('mock')
mock_method_id = MethodIdentifier(mock_interface_id, 'mock')
mock_interface_def = InterfaceDefinition(mock_interface_id, [mock_method_id])
mock_method_def = MethodDefinition(
    mock_method_id, StructDefinition('input', [('input', VoidDefinition())]),
    IntegerDefinition(), [])

errors = [
    'com.vmware.vapi.std.errors.internal_server_error',
    'com.vmware.vapi.std.errors.invalid_argument',
    'com.vmware.vapi.std.errors.operation_not_found',
    'com.vmware.vapi.std.errors.unauthenticated',
]
error_defs = [make_std_error_def(error) for error in errors]
error_values = ListValue(
    [convert_data_def_to_data_value(error_def) for error_def in error_defs])
logging.basicConfig(level=logging.INFO)


class MockupApiInterface(ApiInterface):
    def __init__(self):
        self.iface_id = InterfaceIdentifier('mock')
        self.method_id = MethodIdentifier(self.iface_id, 'mock')

    def get_identifier(self):
        return self.iface_id

    def get_definition(self):
        return InterfaceDefinition(self.iface_id, [self.method_id])
예제 #14
0
class TaskHandle(object):
    """
    The TaskHandle interface for publishing task info.
    This class is not thread safe. Info returned from the get is mutable.
    Task method will not return task ID to client until provider calls publish
    method with accept=True once.
    Provider must call publish to save the updates to task state into
    TaskManager.
    """

    _internal_server_error_def = make_std_error_def(
        'com.vmware.vapi.std.errors.internal_server_error')

    def __init__(self, info_type, result_type):
        self.task_manager = get_task_manager()
        self.info_type = info_type
        self.result_type = result_type
        self.error_types = []
        self.task_id = get_task_id()
        self.info = info_type()
        self._initialize_common_info()

    def _initialize_common_info(self):
        """
        Initialize common task info fields
        """
        summary = self.task_manager.get_summary(self.task_id)
        published_info = summary.info
        self.error_types = summary.errors
        self._override_api_info(published_info)
        self.info.cancelable = TypeConverter.convert_to_python(
            published_info.get_field('cancelable'), BooleanType())
        desc = published_info.get_field('description')
        if desc is not None:
            self.info.description = TypeConverter.convert_to_python(
                desc, LocalizableMessage.get_binding_type())
        self.info.status = TypeConverter.convert_to_python(
            published_info.get_field('status'), StringType())

        try:
            user = published_info.get_field('user')
            self.info.user = TypeConverter.convert_to_python(
                user, StringType())
        except:  # pylint: disable=W0702
            pass

    def _override_api_info(self, published_info):
        """
        Override service and operation task info fields
        """
        self.info.service = TypeConverter.convert_to_python(
            published_info.get_field('service'), StringType())
        self.info.operation = TypeConverter.convert_to_python(
            published_info.get_field('operation'), StringType())

    def get_info(self):
        """
        Returns the Task Info.
        """
        return self.info

    def get_published_info(self):
        """
        Returns the current published task Info
        """
        info = self.task_manager.get_info(self.task_id)
        converted_info = TypeConverter.convert_to_python(
            info, self.info_type.get_binding_type())
        return converted_info

    def publish(self, accept=False):
        """
        Publish the temporary task info into task manager

        :type  accept: :class:`bool`
        :param accept: Accept task and return task id to client
        """
        msg_list = None
        published_info = self.task_manager.get_info(self.task_id)

        # Override the common info fields which can't be modified by providers
        self._override_api_info(published_info)

        if hasattr(self.info, 'error') and self.info.error is not None:
            err_type = self.info.error.get_binding_type()
            # Verify if the error set by provider is amongst the ones
            # defined in VMODL2, if not throw InternalServerError
            if (err_type not in self.error_types):
                msg_list = [
                    message_factory.get_message('vapi.task.invalid.error',
                                                err_type.name,
                                                self.info.operation)
                ]

        if hasattr(self.info, 'result') and self.info.result is not None:
            # Check if result type is Opaque or actual type
            res_type = self.info_type.get_binding_type().get_field('result')
            if isinstance(res_type.element_type.definition, OpaqueDefinition):
                result = None
                try:
                    result = TypeConverter.convert_to_vapi(
                        self.info.result, self.result_type.get_binding_type())
                except AttributeError:
                    try:
                        result = TypeConverter.convert_to_vapi(
                            self.info.result, self.result_type)
                    except CoreException:
                        msg_list = [
                            message_factory.get_message(
                                'vapi.task.invalid.result', self.info.result,
                                self.info.operation)
                        ]
                        self.info.result = None

                if msg_list is None:
                    self.info.result = result

        info = TypeConverter.convert_to_vapi(self.info,
                                             self.info_type.get_binding_type())

        if msg_list is not None:
            info.set_field('status', FAILED_STRING_VALUE)
            logger.error(msg_list[0])
            error = make_error_value_from_msgs(self._internal_server_error_def,
                                               *msg_list)
            info.set_field('error', OptionalValue(error))

        self.task_manager.set_info(self.task_id, info)

        if accept:
            event = get_event()
            event.set()
            clear_event()
            # Validate that description is set while accepting the task
            desc = self.info.description
            if desc is None or (not desc.id and not desc.default_message):
                msg = message_factory.get_message(
                    'vapi.data.structure.field.missing', self.info_type,
                    'description')
                logger.debug(msg)
                raise CoreException(msg)

    def is_marked_for_cancellation(self):
        """
        Check whether a task is marked for cancellation.
        """
        return self.task_manager.is_marked_for_cancellation(self.task_id)