VMware Cloud Director object extensions are external applications that can participate in, influence, or override the logic that VMware Cloud Director applies to workflows like vApp instantiation and placement. Their operation is transparent to system users.
A service provider uses the REST api to register such extensions and select them on certain phases of various extensible core workflows. The extension must implement a certain protocol and handle request/responses from/to VMware Cloud Director.
The following is a high level diagram of the components of the object extensibility framework
where:
Object Extension
defines an extension backend. Based on the type, the way a backend is built varies. There are two flavours:
/api/admin/extension/object
. They are deprecated since VMware Cloud Director 10.6./cloudapi/1.0.0/extensions/object-extension
. This document describes the interaction only with this kind. The first channel type that is supported since the introduction of this kind of extensions(10.5.1), is a generic RDE standalone mqtt behavior(part of the VMware Cloud Director RDE framework).Selector Extension
binds an object extension to a selector type AND
a set of phases.
Selector type
represents a context of a workflow execution(for ex. “Place VM” is running withing the context of a VDC). Each workflow may have a distinct sub-set of applicable contexts. When VMware Cloud Director executes a workflow, it uses its applicable contexts to select the object extensions
which are bound to them.object extension
to narrow down the selector types to which it can be bound./api/admin/{selector-type}/extension
- selector extensions
registered here will have the object extension
triggered for any selector of that type(for ex. any organisation)/api/admin/{selector-type}/{selector-id}/extension
- selector extensions
registered here will have the object extension
triggered for one particular selector of that type and that id(for ex. the organisation whose id==selector-id
)Phase
represents a lifecycle stage from one or more workflows. When executing a workflow, VMware Cloud Director invokes object extensions
bound to that phase(and to the selector type context).An object extension can participate as a peer of the system to:
phase.type=blocking
; for example, it might use information provided by the system to place a VM on a specific host or assign it a specific storage profilephase.type=async
, but phase.type=blocking
may also be used for such cases, where the extension has to only conditionally change the outcomeOnly a sub-set of core workflows are extensible and have well-defined lifecycle phases and applicable selector types. Multiple object-extensions
at a time can be candidates for execution for a given phase
. When VMware Cloud Director selects the object-extensions
based on the phase
and the selector types
, it then proceeds to order them based on the selectorExtension.priority
field and filter based on the phase cardinality. The direction of the ordering(ascending or descending) is specific to the workflow and the cardinality depends on the particular phase
. The cardinality defines the applicable phase.type
selections and the number of extensions that may be invoked:
ALLOW_ONE
- up to one extension, either async
or blocking
ALLOW_ONE_BLOCKING
- up to one blocking
extensionUNLIMITED
- any number of extensions of any phase.type
Once the ordered list of extensions is determined, it then proceeds to invoke them one by one. It will wait for a response if the phase is selected as phase.type=blocking
, or continue immediately(to the next extension, or phase, etc) if it is async
. Each blocking extension influences the input of the next extension for the phase. The way it influences it is specific to the phase - sometimes it could be the output of the previous is the input of the next, or the output is overlayed in some way on the input.
If any of the extension invocations ends in an error, including when a blocking
extension deliberately triggers it(by returning a payload of type ExtensionErrorMessage
), VMware Cloud Director follows the following procedure:
selectorExtension.optional=true
, it will continue with the execution(invoking the next extension/phase/workflow stage, whatever the case may be). Otherwise, the workflow ends and the corresponding workflow task
will contain details for the error(which may have been forwarded from the extension, in case it triggered it).Note:
- It is possible for a
phase
to not get any extensions invoked, even though there are selector extensions on it. This can happen if its cardinality does not permit thephase.type
of the selectors.- If an
object-extension
is selected on multiple differentselector types
, it will be invoked once with a priority which is well-defined if it is the same across allselector extensions
, or one will be chosen otherwise. It is currently undefined as to which priority will be chosen in the latter case.
phase.name | cardinality | request | response |
---|---|---|---|
urn:extensionPoint:vm:calculateSolution | UNLIMITED | PlacementProposalType | PlacementProposalType |
urn:extensionPoint:vm:gatherRequirements | UNLIMITED | Requirements | Requirements |
urn:extensionPoint:vm:create | ALLOW_ONE_BLOCKING | ProvisioningRequest | ProvisioningResponse |
urn:extensionPoint:vm:customize:create | UNLIMITED | VmCustomizationRequest | VmCustomizationResponse |
urn:extensionPoint:vm:customize:reconfigure | UNLIMITED | VmCustomizationRequest | VmCustomizationResponse |
urn:extensionPoint:vm:customize:relocate | UNLIMITED | VmCustomizationRequest | VmCustomizationResponse |
urn:extensionPoint:vm:customize:clone | UNLIMITED | VmCustomizationRequest | VmCustomizationResponse |
** The
phase
can be called multiple times per workflow depending on the parameters that are being set or updated
Once the object-
and selector extensions
are registered, a service provider may test the extensions that are going to be invoked for a given phase
and a set of selector types
via the Echo Object Extensions
RESTapi resource. For this purpose, at least a basic object extension
backend should be stood up, which can handle the phases
and the requests that will be tested.
Think of this resource as a pseudo workflow, where you can specify the phase
, the invocation order
, the selector context
and the request
payload.
Note:
A real workflow’s phases have pre-defined request/response schemas
{
"request": { "message": "Hello World"},
"invocationOrder": "LOWEST_PRIORITY_FIRST",
"selectors": [
"urn:vcloud:vdc:05472504-6b79-48c8-bb74-7493c14cad7d"
],
"phase": "urn:extensionPoint:vm:customize:create"
}
object-extension
backendVMware Cloud Director supports various channels for the communication with the object-extension
. Each phase has predefined request and response payload schema associated with it, which VMware Cloud Director sends, and in the case of phase.type=blocking
selections, expects to receive. For all channel types, the payloads are wrapped in envelopes: * HalfDuplexEnvelope
- used by VMware Cloud Director when sending a request, and by the extension when sending a response * ErrorMessageEnvelope
- used by VMware Cloud Director when the last invocation ended in an error
The envelope follows the same pattern mentioned in message-broker.md#message-schema. This structure allows a single object extension
to handle varying workflows/phases. The schemas of the payloads related to each particular phase
are described in the HalfDuplexEnvelope.payloadType
field. In this way the object-extension
knows not only how to (de)serialize the payloads, but also how to route the requests to its internal specific handling logic for each phase
.
Alternatively, extension developers may choose to group object extensions
to phases
in a 1:1 relationship, meaning there is a dedicated extension for each particular phase. In this case the routing mentioned above is effectively performed by VMware Cloud Director through the selector extensions
configuration.
The extension must implement the general RDE behavior invocation contract. The messages sent/received are of type
BEHAVIOR_INVOCATION
/BEHAVIOR_RESPONSE
. The envelope is json encoded in the payload
(InvocationArguments.arguments
). Once you decode it, the phase request is again json encoded in its payload
.
Note:
This means that in order to extract the particular phase related request, you must unwrap(decode) two
payload
fields in total, one of which is nested in the other.
Ideally, the first two points should be part of the installation procedure of the solution addon, which packages the overall extension. It should persist the ids of the extension service, RDE interface and behavior, and it should use them when standing up the backend in the third point.
Extension Service
, or reuse an existing one(/cloudapi/extensions/api
){vcd.host}/messaging/mqtt
monitor
topic of the extension service from point 1. It must publish responses to the respond
topic.phase.type=blocking
extensions). Here is an example fully decoded BEHAVIOR_INVOCATION
, corresponding to the test invocation mentioned above:{
"type": "BEHAVIOR_INVOCATION",
"headers": {
"taskId": "7ce94181-6b78-4147-b7fe-b21558790500",
"behaviorId": "urn:vcloud:behavior-interface:818c62fc-2316-445c-a4e1-4cee621cbf4c:vmware:BEHAVIOR_ECHO:1.0.0",
"context": { // not to be confused with the selector types; this is part of the core RDE framework
"orgId": "urn:vcloud:org:a93c9db9-7471-3192-8d09-a8f7eeda85f9",
"userId": "urn:vcloud:user:c24078ed-fc83-4474-9130-503a51b6bd94"
}
},
"payload": { // this is a json encoded string originally
"_execution_properties": {
"serviceId": "urn:vcloud:extension-api:vmware:echo:0.0.1",
"actAsToken": false,
"invocation_timeout": 300
},
"typeId": "urn:vcloud:interface:vmware:BEHAVIOR_ECHO:1.0.0",
"arguments": { // this is the HalfDuplexEnvelope
"clientRequestId": "obj-ext-test",
"namespace": "BEHAVIOR_ECHO",
"envelopeType": "HalfDuplexEnvelope",
"payloadType": "Echo",
"phase": "urn:extensionPoint:vm:create",
"payload": {"message":"Hello World!"}, // this is a json encoded string originally
"correlationId": "16e8a458-ec93-4285-888e-293c94b5e004"
},
"_metadata": {
"executionId": "730ae625-3fae-4987-b178-cef6bae1f9d9",
"invocation": {},
"apiVersion": "39.0",
"behaviorId": "urn:vcloud:behavior-interface:818c62fc-2316-445c-a4e1-4cee621cbf4c:vmware:BEHAVIOR_ECHO:1.0.0",
"requestId": "obj-ext-test-f37cbac5-2192-4114-bc58-c1cc9e3a883c",
"executionType": "MQTT",
"invocationId": "298c37ce-4854-4576-a3aa-903a92d0904f",
"taskId": "7ce94181-6b78-4147-b7fe-b21558790500"
}
}
}
Note:
If you are using the same extension service with multiple different mqtt behaviors, the messages my not always represent an object extension phase request. In such cases you may route the execution(and (de)serialization) to a proper handler, based on the
BehaviorInvocation.Headers.behaviorId
, or otherwise use a dedicated extension service for the object extension RDE mqtt behavior.