Generated parameter options

Build a parameter’s option list at runtime from a plugin

A parameter’s options can be generated by an orchestration action rather than entered by hand. In Cloud Management Platform (CMP) there are two ways to do this: add an orchestration action whose job is to generate the options, or, when the parameter is an action input on a plugin, generate them from inside that plugin.

Plugins that generate parameter options

Both approaches need a plugin attached to the Generated Parameter Options trigger point and associated with the parameter in question. There are two ways for that plugin to return its options:

  • Synchronously: the plugin returns all options at once, rendered as a dropdown. The plugin must implement get_options_list(custom_field, **kwargs).
  • Asynchronously: the plugin returns only the options matching what the user has typed. The plugin must implement suggest_options(custom_field, query, **kwargs), where query is the string the user has typed. A plugin that implements suggest_options must also define get_options_list(custom_field, **kwargs), which can simply return None. See Suggest options as the user types.

get_options_list must return a list or a dictionary. Returning anything else raises an error naming the plugin. For what the dictionary form can contain, see Option return formats.

To create the plugin, see Set up a generated parameter options action below.

Suggest options as the user types

A plugin that defines suggest_options turns the parameter into a search field. As the user types, CMP calls suggest_options and shows returned options. Define suggest_options as a top-level function in the plugin:

def suggest_options(custom_field, query, **kwargs):
  • custom_field: the parameter the options are for.
  • query: the text the user has typed. Can be empty or None. Keep the name query; custom forms pass it by name.

The group, environment, user, and other form values are not available to suggest_options. CMP also does not apply the plugin’s environment, group, or resource technology constraints when it calls suggest_options, so associate only one suggest_options plugin with each parameter.

Return a list of (value, label) tuples. Unlike get_options_list, suggest_options cannot return a dictionary or plain strings.

This example filters a fixed list of regions by what the user types:

# Required even when the plugin only suggests options.
def get_options_list(custom_field, **kwargs):
    return None


def suggest_options(custom_field, query, **kwargs):
    regions = [
        ("us-east-1", "US East (N. Virginia)"),
        ("us-east-2", "US East (Ohio)"),
        ("us-west-1", "US West (N. California)"),
        ("us-west-2", "US West (Oregon)"),
        ("eu-west-1", "Europe (Ireland)"),
    ]
    if not query:
        return regions

    query = query.lower()
    return [
        (value, label)
        for value, label in regions
        if query in value.lower() or query in label.lower()
    ]

Plugins that generate action input options

When option generation is embedded in a plugin, the method signature is generate_options_for_<action_input_name>(**kwargs). The method name depends on the action input’s name, with any _a<number> suffix stripped. Learn more about action inputs in Plugins.

When naming these action inputs, consider which inputs already have values supplied in that context. If an action input is already provided, option generation does not apply to it. Several CMP objects may be available as context for template-enabled action attributes, such as environment, group, and profile. See the Action context table.

Generated options are still filtered against any options defined in the UI, and only options present in both are used. To bypass that filtering, return a dictionary with override set to True, as described in Option return formats.

For how UI-defined lists combine, see Static options.

Keyword arguments available to these methods

Which keyword arguments a method receives depends on the context in which it is called. These keyword arguments do not apply to suggest_options, which receives only the parameter and the typed text.

Keyword Available when
group Setting options on a group; ordering a blueprint; modifying a server; modifying a resource.
environment Setting options on an environment; ordering a blueprint — for build items that have an environment, such as server tiers; modifying a server.
blueprint Ordering a blueprint; setting options on a blueprint; modifying a resource; modifying a server, if that server was provisioned from a blueprint on the same CMP instance. Discovered servers are not associated with a blueprint.
server Modifying a server.
servers Modifying more than one server at once.
resource Modifying a resource.
resources Modifying more than one resource at once.
profile All contexts. Represents the currently logged-in user.
recipient Any context involving an order, a server, or a resource. A userprofile object for the recipient of a blueprint order, either the current order at ordering time or a past one otherwise, such as the order that provisioned the server being modified.
service_item Generating options for a blueprint’s service item.

Set up a generated parameter options action

  1. Go to Admin > Orchestration Actions.
  2. In the tree filter on the left, select Parameters > Generated Parameter Options.
  3. Select Add Generated Parameter Options Action. The button appears once a single trigger point is selected.
  4. Select CloudBolt Plug-in, then select Add a new CloudBolt plug-in.
  5. Complete the form, making sure to select the parameter from the Custom fields dropdown.
  6. Select Save.

Set up a regenerated options dependency

A dependent parameter’s options can be rebuilt whenever a controlling parameter’s value changes. All of the configuration happens on the dependent parameter; the controlling parameter needs no changes of its own. For the other kinds of dependency, see Parameter options.

  1. Go to Admin > Parameters.
  2. Create two parameters: one is the controlling field, and the other is the dependent field.
  3. Set up the dependency relationship:
    1. Open the detail page of the dependent parameter.
    2. In the Dependencies section, select Add a dependency.
    3. Select the Controlling field and set Dependency type to Regenerate Options.
    4. Select Create.
  4. On the same page, in the Programmatically Generated Options section, select Add an Action. CMP either creates a plugin associated with this parameter at the Generated Parameter Options trigger point and opens the plugin’s detail page, or opens the detail page for an existing plugin.
  5. Write the plugin code. It must define get_options_list and accept keyword arguments including field (the dependent field) and control_value or control_values (the controlling field). Options must be a list of values or (value, label) tuples, returned either directly or under the options key of a dictionary.
  6. Select Save Code.

Examples

Example 1: a simple case

A regenerated options dependency where you select one value for the controlling field and one for the dependent field.

def get_options_list(field, control_value=None, **kwargs):
    """
    A plug-in for regenerating 'Species' options based on the value of the controlling field 'Genus'
    field: This is the dependent field.
    control_value: The value entered on the form for the control field. This value determines what will be displayed in "field"
    """
    options = []

    if control_value == 'Acer':
        options = [
            ('rubrum', 'rubrum'),
            ('saccharinum', 'saccharinum'),
            ('macrophyllum', 'macrophyllum')
        ]
    elif control_value == 'Sequoia':
        options = [
            ('magnifica', 'magnifica'),
            ('langsdorfii', 'langsdorfii'),
            ('giganteum', 'giganteum')
        ]
    return {
        'options': options,
        'override': True
    }

Example 2: using multiple choice fields

A regenerated options dependency where allow_multiple is set on the controlling field. Use the control_values keyword argument to accept a list of values from it.

def get_options_list(field, control_value=None, control_values=[], **kwargs):
    """
    A plug-in for building a list of options for 'Plants'
    based on the values selected for the controlling field 'Plant Type'
    field: This is the dependent field.
    control_value: Not needed in this case, but still required in the function definition.
    control_values: A list of values selected on the form for the controlling field.
    """
    options = []

    if 'Trees' in control_values:
        options.extend([
            ('red_maple', 'Red Maple'),
            ('coastal_redwood', 'Coastal Redwood'),
            ('douglas_fir', 'Douglas Fir'),
        ])

    if 'Shrubs' in control_values:
        options.extend([
            ('red_twig_dogwood', 'Red Twig Dogwood'),
            ('wild_lilac', 'Wild Lilac'),
            ('summersweet', 'Summersweet'),
        ])

    return options

Example 3: using multiple controlling fields

More than one controlling field can drive a single dependent field. Use the control_value_dict keyword argument to fetch the values of all of them.

def get_options_list(field, control_value=None, control_value_dict=None, **kwargs):
    options = []
    if not control_value_dict:
        return options
    monitoring_tool = control_value_dict.get("monitoring_tool", "")
    monitoring_policy = control_value_dict.get("monitoring_policy", "")
    # Add any logic to determine options based on the monitoring tool and policy.
    # Some trivial example logic follows.
    options = [
        f"Severity 1 for {monitoring_tool} {monitoring_policy}",
        f"Severity 2 for {monitoring_tool} {monitoring_policy}",
    ]
    return options

Example 4: using OS Build as a controlling field

You can pick an OS build from a dropdown by name, but the controlling value is the integer ID of the OSBuild object, and those IDs may differ between CMP instances. os_build is only available on server tiers.

def get_options_list(field, control_value=None, control_value_dict=None, **kwargs):
    options = []
    if not control_value and not control_value_dict:
        return options

    if control_value:
        if control_value.os_family.name == "Red Hat":
            if control_value.name == "RHEL8_9":
                # Use separate keys so the custom form cleans up after it regenerates the options
                options.append(("sat_server_one", "Satellite server ONE for RHEL 8"))
                options.append(("sat_server_two", "Satellite server TWO for RHEL 8"))

    return dict(
        options=options,
        override=True,
        sort=True,
    )

Option return formats

Control the labels, initial value, sort order, and filtering of the options a plugin generates