Deployment trigger points

Orchestration actions run your code at fixed points in an order or job, applying reusable behavior everywhere it’s needed

An action attached to a blueprint runs only for that blueprint. The same action attached to a trigger point runs for every order or every server provisioned, which is ideal for policy, naming, validation, and notifications. This topic covers the trigger points a deployment passes through and what each one receives.

Where the trigger points fall

Deploying a blueprint that contains a single server runs two jobs: a Deploy Blueprint job that processes the order and a Provision Server job that builds the server, each with its own trigger points. Actions can also be triggered on order status changes. See Orchestration actions for the full list of trigger points.

Deploy Blueprint job

This job has two order-related trigger points. The last three columns in the following table contain the keyword arguments passed to your plugin’s run method; see Structure of a plugin.

Trigger point Runs in Logs to job order hook_point
Post-Order Approval Web server application.log None Order pre_order_execution
Post-Order Execution Job engine The Deploy Blueprint job’s log None Order post_order_execution

Both trigger points receive a logger of type ThreadLogger.

Post-Order Approval

This trigger point is named Post-Order Approval, but its hook_point value is pre_order_execution. It runs immediately after an order is approved and before any jobs are created. It runs on order approval, including automatic approvals.

Because it runs on the web server rather than the job engine, its output goes to application.log. In a high availability deployment, this is the log on the web server that handled the request, under /var/log/cloudbolt/.

Provision Server job

These trigger points all run in the job engine, log to the job’s log, and are listed below in execution order.

A server object exists from early in the job and becomes more complete as it progresses. At Pre-Create Resource, the server is still a stub you can modify before the resource handler creates it; after creation, it carries details including its IP address.

The trigger points a Provision Server job passes through, in order. (opens full-size image in a new tab)

Trigger point hook_point Also receives
Pre-Provision pre_provision No server yet
Generated Hostname Overwrite generated_hostname_overwrite server, servers
Validate Hostname validate_hostname server, servers
Pre-Create Resource pre_create_resource server, servers
Pre-Network Configuration pre_networkconfig server, servers
Post-Network Configuration post_networkconfig server, servers
Network Verification network_verification server, servers, and nic, a ServerNetworkCard
Post-Network Verification post_network_verification server, servers
Post-Provision post_provision server, servers

They all also receive logger and job. Where servers appears it is a list containing one server.

A plugin that reports where it ran

Attach this to any trigger point to see what its kwargs contain.

from common.methods import set_progress

def run(job, *args, **kwargs):
    hook_point = kwargs.get('hook_point', '')
    set_progress("Running at hook_point={} and job.id={}".format(hook_point, job.id))

    server = kwargs.get('server')
    if server:
        set_progress("This plug-in is running for server {}".format(server))

    set_progress("Dictionary of keyword args passed to this plug-in: {}".format(kwargs.items()))

    return "SUCCESS", "Sample output message", ""

Reusing a blueprint’s action everywhere

An action created in a blueprint’s build phase is added to the global actions list and referenced from that blueprint. To apply the same logic everywhere, make it a shared plugin and attach it to the relevant trigger point: changes then take effect wherever it is referenced, leaving one copy to maintain rather than one per blueprint.

Notification trigger points

Two trigger points customize notifications. For both, returning FAILURE or raising an unhandled exception does not change the order’s status.

Order Approval Notification

This replaces the notification approvers get when an order is submitted. It does not run if:

  • An Order Approval action is enabled, since approval is then handled outside Cloud Management Platform’s default process.
  • The order is approved automatically, because there is nobody to notify.

You can define more than one, but each will send its own notification. Use execution filters to control which one runs in a given context.

Example

This walks the order to build a summary of what was requested, including blueprint parameters, the build item’s arguments, and any preconfiguration values.

def order_approval_notification(order, request, *args, **kwargs):
    try:
        # The top-level order item carries the blueprint-level parameters.
        boi = order.orderitem_set.get(parent_order_item=None).cast()
    except Exception as e:
        return "FAILURE", "Could not find the order's top-level item", e

    parts = []

    # Blueprint parameters whose destination includes the resource live on the
    # order item; those targeting build items live on each item's arguments.
    for cfv in boi.custom_field_values.all():
        parts.append(f"{cfv.field.name}: {cfv.value}")

    bia = boi.blueprintitemarguments_set.first()
    if bia:
        try:
            parts.append(f"quantity: {bia.quantity}")
            parts.append(f"environment: {bia.environment.name}")
            parts.append(f"os_build: {bia.os_build.name}")
        except AttributeError:
            # This order may not contain a server tier.
            pass

        for cfv in bia.custom_field_values.all():
            parts.append(f"{cfv.field.name}: {cfv.value}")

        for pcvs in bia.preconfiguration_values.all():
            parts.append(f"preconfiguration: {pcvs.preconfiguration.name}")
            for cfv in pcvs.custom_field_values.all():
                parts.append(f"    {cfv.field.name}: {cfv.value}")

    return "SUCCESS", "; ".join(parts), ""

What it receives

Argument What it is
order The order, from which you can reach the blueprint as order.blueprint, the environment as order.environment where one applies, and the order’s items.
request The HTTP request from the person who placed the order.

What to return

Return a tuple of status, message, and error. They decide what is logged and what the user sees.

Position What to put there
status One of SUCCESS, INFO, WARNING, or FAILURE. It sets both the log level and the color of the message shown to the user.
message The text to log and show. Leave it empty: nothing is logged, and no message appears.
error A string or a Python exception. Pass the exception, and its traceback is logged too.

SUCCESS and INFO both log at INFO, shown green and blue; WARNING logs a warning, shown orange; FAILURE logs an error, shown red.

Post-Order Execution Notification

This runs after the Post-Order Execution actions have finished, and takes only order. Its return tuple works as above, except that there is no user request to show a message to, so the status and message reach only the logs.

As with approval notifications, you can define several, and each will fire. Consider using execution filters.

Example

This example deliberately calls a function that does not exist, to show how returning an exception affects the logs.

def post_order_execution_notification(order, *args, **kwargs):
    msg = ""
    try:
        # This function does not exist, so this raises an exception.
        return_value = some_function_that_doesnt_exist()
    except Exception as e:
        msg = "Error running some_function_that_doesnt_exist()"
        # Returning the exception logs its traceback as an error.
        return "FAILURE", msg, e

    return "SUCCESS", msg, ""