"""
Introspection services
"""
__author__ = 'VMware, Inc.'
__copyright__ = 'Copyright 2013-2014 VMware, Inc. All rights reserved. -- VMware Confidential'
import logging
from vmware.vapi.core import (
ApiInterface, InterfaceIdentifier, MethodIdentifier, InterfaceDefinition,
MethodDefinition, MethodResult, ExecutionContext)
from vmware.vapi.data.definition import (
StructDefinition, StringDefinition, ListDefinition, StructRefDefinition,
OptionalDefinition)
from vmware.vapi.lib.constants import (
Introspection, MAP_ENTRY, OPERATION_INPUT)
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.data.serializers.introspection import (
convert_data_def_to_data_value)
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 = logging.getLogger(__name__)
[docs]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
"""
return generate_fingerprint(str(api_services))
[docs]class IntrospectionBaseApiInterface(ApiInterface):
"""
Helper base class for all Introspection VMODL2 dynamic services
"""
def __init__(self, iface_id, method_defs, methods):
"""
Initialize the Api Interface instance
:type iface_id: :class:`vmware.vapi.core.InterfaceIdentifier`
:param iface_id: Interface identifier
:type method_defs: :class:`dict`
:param method_defs: Dictionary of method identifiers to method definitions
:type methods: :class:`dict`
:param methods: Dictionary of method identifiers to method references
"""
ApiInterface.__init__(self)
self._id = iface_id
self._method_defs = method_defs
self._methods = methods
[docs] def get_identifier(self):
"""
Returns interface identifier
:rtype: :class:`InterfaceIdentifier`
:return: Interface identifier
"""
return self._id
[docs] def get_definition(self):
"""
Returns interface definition
:rtype: :class:`InterfaceDefinition`
:return: Interface definition
"""
return InterfaceDefinition(self._id, list(self._methods.keys()))
[docs] def get_method_definition(self, method_id):
"""
Returns the method definition
:rtype: :class:`MethodDefinition`
:return: Method definition
"""
return self._method_defs.get(method_id)
[docs] def invoke(self, ctx, method_id, input_value):
"""
Invokes the specified method using the execution context and
the input provided
:type ctx: :class:`ExecutionContext`
:param ctx: Execution context for this method
:type method_id: :class:`MethodIdentifier`
:param method_id: Method identifier
:type input_value: :class:`vmware.vapi.data.value.StructValue`
:param input_value: Method input parameters
:rtype: :class:`MethodResult`
:return: Result of the method invocation
"""
method = self._methods.get(method_id)
method_def = self._method_defs.get(method_id)
return method(method_def, ctx, input_value)
[docs]class ProviderApiInterface(IntrospectionBaseApiInterface):
"""
This service provides operations to retrieve information of a
vAPI Provider. A provider represents a vAPI endpoint that is exposing a
collection of vAPI services.
"""
def __init__(self, name, introspection_adapter):
"""
Initialize ProviderApiInterface
:type name: :class:`str`
:param name: Name of the provider
:type introspection_adapter:
:class:`vmware.vapi.provider.introspection.ApiProviderIntrospector`
:param introspection_adapter: Adapter for fetching introspection information
"""
self._name = name
self._adapter = introspection_adapter
iface_id = InterfaceIdentifier(
'com.vmware.vapi.std.introspection.provider')
method_defs = {}
# get method
get_method_id = MethodIdentifier(iface_id, 'get')
output_def = StructDefinition(
'com.vmware.vapi.std.introspection.provider.info',
[('id', StringDefinition()),
('checksum', StringDefinition())]
)
method_defs[get_method_id] = MethodDefinition(
get_method_id,
StructDefinition(OPERATION_INPUT, []),
output_def,
[]
)
methods = {}
methods[get_method_id] = self._get
IntrospectionBaseApiInterface.__init__(self,
iface_id,
method_defs,
methods)
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)
[docs]class ServiceApiInterface(IntrospectionBaseApiInterface):
"""
This service exposes operations to retrieve information about the
services exposed by a vAPI endpoint
"""
def __init__(self, name, introspection_adapter):
"""
Initialize ServiceApiInterface
:type name: :class:`str`
:param name: Name of the provider
:type introspection_adapter:
:class:`vmware.vapi.provider.introspection.ApiProviderIntrospector`
:param introspection_adapter: Adapter for fetching introspection information
"""
self._adapter = introspection_adapter
iface_id = InterfaceIdentifier(
'com.vmware.vapi.std.introspection.service')
method_defs = {}
methods = {}
# list method
list_method_id = MethodIdentifier(iface_id, 'list')
output_def = ListDefinition(StringDefinition())
method_defs[list_method_id] = MethodDefinition(
list_method_id,
StructDefinition(OPERATION_INPUT, []),
output_def,
[]
)
methods[list_method_id] = self._list
# get method
get_method_id = MethodIdentifier(iface_id, 'get')
output_def = StructDefinition(
'com.vmware.vapi.std.introspection.service.info',
[('operations', ListDefinition(StringDefinition()))])
method_defs[get_method_id] = MethodDefinition(
get_method_id,
StructDefinition(OPERATION_INPUT,
[('id', StringDefinition())]),
output_def,
[not_found_def]
)
methods[get_method_id] = self._get
IntrospectionBaseApiInterface.__init__(self,
iface_id,
method_defs,
methods)
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 specified service
: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 specified service
"""
service_id = str(input_value.get_field('id').value)
return self._adapter.get_service_info(service_id)
[docs]class OperationApiInterface(IntrospectionBaseApiInterface):
"""
This service exposes a list of operations to retrieve
information about the operations present in a vAPI service
"""
def __init__(self, name, introspection_adapter):
"""
Initialize OperationApiInterface
:type name: :class:`str`
:param name: Name of the provider
:type introspection_adapter:
:class:`vmware.vapi.provider.introspection.ApiProviderIntrospector`
:param introspection_adapter: Adapter for fetching introspection information
"""
self._adapter = introspection_adapter
iface_id = InterfaceIdentifier(
'com.vmware.vapi.std.introspection.operation')
method_defs = {}
methods = {}
# list method
list_method_id = MethodIdentifier(iface_id, 'list')
output_def = ListDefinition(StringDefinition())
method_defs[list_method_id] = MethodDefinition(
list_method_id,
StructDefinition(OPERATION_INPUT,
[('service_id', StringDefinition())]),
output_def,
[not_found_def]
)
methods[list_method_id] = self._list
# get method
get_method_id = MethodIdentifier(iface_id, 'get')
data_ref_def = StructRefDefinition(
'com.vmware.vapi.std.introspection.operation.data_definition')
field_def = StructDefinition(
MAP_ENTRY,
[('key', StringDefinition()),
('value', data_ref_def)])
data_def = StructDefinition(
'com.vmware.vapi.std.introspection.operation.data_definition',
[('type', StringDefinition()),
('element_definition', OptionalDefinition(data_ref_def)),
('name', OptionalDefinition(StringDefinition())),
('fields', OptionalDefinition(ListDefinition(field_def)))])
data_ref_def.target = data_def
output_def = StructDefinition(
'com.vmware.vapi.std.introspection.operation.info',
[('input_definition', data_def),
('output_definition', data_def),
('error_definitions', ListDefinition(data_def))])
method_defs[get_method_id] = MethodDefinition(
get_method_id,
StructDefinition(OPERATION_INPUT,
[('service_id', StringDefinition()),
('operation_id', StringDefinition())]),
output_def,
[not_found_def]
)
methods[get_method_id] = self._get
IntrospectionBaseApiInterface.__init__(self,
iface_id,
method_defs,
methods)
def _list(self, method_def, ctx, input_value):
"""
Get the set of operation 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 operation identifiers
"""
service_id = str(input_value.get_field('service_id').value)
return self._adapter.get_operations(service_id)
def _get(self, method_def, ctx, input_value):
"""
Returns information about a vAPI operation
: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 a vAPI operation
"""
service_id = str(input_value.get_field('service_id').value)
operation_id = str(input_value.get_field('operation_id').value)
return self._adapter.get_operation_info(service_id, operation_id)
[docs]class ApiProviderIntrospector(object):
"""
Abstract class for fetching introspection information
"""
def __init__(self, name):
"""
Initialize ApiProviderIntrospector
:type name: :class:`str`
:param name: Name of the provider
"""
self._services = [
ProviderApiInterface(name, self),
ServiceApiInterface(name, self),
OperationApiInterface(name, self)]
[docs] def get_introspection_services(self):
"""
Returns the list of introspection services
:rtype: :class:`list` of :class:`vmware.vapi.core.ApiInterface`
:return: list of introspection services
"""
return self._services
[docs] def get_checksum(self):
"""
Returns the checksum of the API information available
:rtype: :class:`str`
:return: Checksum of the API information available
"""
raise NotImplementedError
[docs] def get_services(self):
"""
Returns the list of available services
:rtype: :class:`list` of :class:`str`
:return: List of available services
"""
raise NotImplementedError
[docs] def get_service_info(self, service_id):
"""
Get information about a particular service
:type service_id: :class:`str`
:param service_id: Service identifier
:rtype: :class:`vmware.vapi.core.MethodResult`
:return: Introspection service info. The DataValue in the output
represents :class:'com.vmware.vapi.std.introspection.Service.Info'
"""
raise NotImplementedError
[docs] def get_operations(self, service_id):
"""
Get the list of available operations for a particular service
:type service_id: :class:`str`
:param service_id: Service identifier
:rtype: :class:`vmware.vapi.core.MethodResult`
:return: Result that contains list of available operations. The DataValue
in the output is list of StringValues containing the operation names.
"""
raise NotImplementedError
[docs] def get_operation_info(self, service_id, operation_id):
"""
Get the operation information
:type service_id: :class:`str`
:param service_id: Service identifier
:type operation_id: :class:`str`
:param operation_id: Operation identifier
:rtype: :class:`vmware.vapi.core.MethodResult`
:return: Result that contains operation information. The DataValue in the output
represents :class:'com.vmware.vapi.std.introspection.Operation.Info'
"""
raise NotImplementedError
[docs]class LocalProviderIntrospector(ApiProviderIntrospector):
"""
Specialization of :class:`vmware.vapi.provider.introspection.ApiProviderIntrospector`
that uses data available in Local Provider to process the Introspection
service API requests
"""
def __init__(self, name):
"""
Initialize LocalProviderIntrospector
:type name: :class:`str`
:param name: Name of the provider
"""
ApiProviderIntrospector.__init__(self, name)
self._service_data = {}
[docs] def add_service(self, service_id, api_interface):
"""
Add a new service to the introspector
:type service_id: :class:`str`
:param service_id: Service identifier
:type api_interface: :class:`vmware.vapi.core.ApiInterface`
:param api_interface: ApiInterface corresponding to the service
"""
self._service_data[service_id] = api_interface
[docs] def get_checksum(self):
return generate_fingerprint(str(self._service_data))
[docs] def get_services(self):
return list(self._service_data.keys())
[docs] def get_service_info(self, service_id):
service_info = self._service_data.get(service_id)
if service_info:
method_ids = service_info.get_definition().get_method_identifiers()
output = StructValue(
name='com.vmware.vapi.std.introspection.service.info',
values={'operations': ListValue(
values=[StringValue(method_id.get_name())
for method_id in method_ids])}
)
return MethodResult(output=output)
else:
msg = message_factory.get_message(
'vapi.introspection.service.not_found', service_id)
error_value = make_error_value_from_msgs(not_found_def, msg)
return MethodResult(error=error_value)
[docs] 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)
@staticmethod
def _convert_method_def_to_data_value(method_def):
"""
Converts a :class:`vmware.vapi.core.MethodDefinition` object
to :class:`vmware.vapi.data.value.DataValue` object
:type method_def: :class:`vmware.vapi.core.MethodDefinition`
:param method_def: Method definition
:rtype: :class:`vmware.vapi.data.value.DataValue`
:return: Data value object representing method definition
"""
output = StructValue(
name='com.vmware.vapi.std.introspection.operation.info',
values={
'input_definition': convert_data_def_to_data_value(
method_def.get_input_definition()),
'output_definition': convert_data_def_to_data_value(
method_def.get_output_definition()),
'error_definitions': ListValue(
values=[convert_data_def_to_data_value(error_def)
for error_def in method_def.get_error_definitions()])
})
return output
[docs] 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)
[docs]class AggregatorIntrospector(ApiProviderIntrospector):
"""
Specialization of :class:`vmware.vapi.provider.introspection.ApiProviderIntrospector`
that uses data available in Aggregator Provider to process the Introspection
service API requests
"""
def __init__(self, name, local_provider):
"""
Initialize AggregatorIntrospector
:type name: :class:`str`
:param name: Name of the provider
:type local_provider: :class:`vmware.vapi.core.ApiProvider`
:param local_provider: LocalProvider that will be used to serve
introspection requests for introspection services itself.
"""
self._local_provider = local_provider
self._local_provider_class_name = local_provider.__class__.__name__
self._service_data = {}
ApiProviderIntrospector.__init__(self, name)
[docs] def add_service(self, service_id, api_provider):
"""
Add a new service to the introspector
:type service_id: :class:`str`
:param service_id: Service identifier
:type api_provider: :class:`vmware.vapi.core.ApiProvider`
:param api_provider: ApiProvider that is exposing the service
"""
self._service_data[service_id] = api_provider
[docs] def remove_service(self, service_id):
"""
Add a new service to the introspector
:type service_id: :class:`str`
:param service_id: Service identifier
"""
if service_id in self._service_data:
del self._service_data[service_id]
def _get_provider(self, service_id):
"""
Returns the ApiProvider instance to be used for invoking the
introspection calls
:type service_id: :class:`str`
:param service_id: Service identifier
:rtype: :class:`vmware.vapi.core.ApiProvider`
:return: ApiProvider instance to be used
"""
#
# Aggregator routes based on a mapping from Service identifier to
# ApiProvider. There is an introspection service in the aggregator
# that is aggregator the data from local provider and all remote
# providers. So, the mapping in aggregator would be:
# com.vmware.vapi.std.introspection.service -> Introspection service
# in aggregator
# But if we route based on this rule, we will go into infinite recursion
#
# So, the introspection calls for introspection services should be
# routed to local provider in the aggregator, as that would be a terminal
# point and can return data.
# The introspection data for introspection calls is same if that
# is serviced by the one in aggregator or by the one in local provider
#
if service_id.startswith(Introspection.PACKAGE):
provider = self._local_provider
else:
provider = self._service_data.get(service_id)
return provider
[docs] def get_checksum(self):
# XXX: Resultant checksum does not include the data from the services
# registered in the local provider of the aggregator. Since no one is
# using Python aggregator, making this a low priority now.
ctx = ExecutionContext()
struct_value = StructValue(name=OPERATION_INPUT)
providers = [provider for provider in list(self._service_data.values())
if provider.__class__.__name__ != self._local_provider_class_name]
method_results = [
provider.invoke(Introspection.PROVIDER_SVC, 'get', struct_value, ctx)
for provider in providers]
checksums = [result.output.get_field('checksum')
for result in method_results if result.success()]
return generate_fingerprint(str(checksums))
[docs] def get_services(self):
return list(self._service_data.keys())
[docs] def get_service_info(self, service_id):
provider = self._get_provider(service_id)
ctx = ExecutionContext()
struct_value = StructValue(
name=OPERATION_INPUT,
values={'id': StringValue(service_id)})
return provider.invoke(
Introspection.SERVICE_SVC, 'get',
struct_value, ctx)
[docs] def get_operations(self, service_id):
provider = self._get_provider(service_id)
ctx = ExecutionContext()
struct_value = StructValue(
name=OPERATION_INPUT,
values={'service_id': StringValue(service_id)})
return provider.invoke(
Introspection.OPERATION_SVC, 'list',
struct_value, ctx)
[docs] def get_operation_info(self, service_id, operation_id):
provider = self._get_provider(service_id)
ctx = ExecutionContext()
struct_value = StructValue(
name=OPERATION_INPUT,
values={'service_id': StringValue(service_id),
'operation_id': StringValue(operation_id)})
return provider.invoke(
Introspection.OPERATION_SVC, 'get',
struct_value, ctx)
[docs]def register_instance():
"""
Services to be registered with LocalProvider
:rtype: :class:`list` of :class:`vmware.vapi.core.ApiInterface`
:return: List of services to be registered with LocalProvider
"""
return [ProviderApiInterface(),
ServiceApiInterface(),
OperationApiInterface()]