Development tips for plugins

Techniques for reaching outside Cloud Management Platform (CMP) from plugin code: remote hosts, stored credentials, HTTP calls, and child jobs

Run remote scripts with connection info objects

Connection info objects let a plugin run scripts against systems that have no REST API. They are also the way to run commands remotely on Windows servers via WinRM or on Linux servers via SSH.

Windows servers

  1. Create a connection info object and set its protocol to winrm.
  2. In the CMP CLI (shell_plus), set the transport type. Supported types are ntlm, kerberos, credssp, and basic, depending on what the target requires.
from utilities.models import ConnectionInfo

connection_info = ConnectionInfo.objects.get(name='my_connection_info')
connection_info.transport = 'ntlm'
connection_info.save()

Linux servers

  1. Create a connection info object and set its protocol to ssh.

Execute a script on the connection info host

This works for both Windows and Linux targets. The example shows every parameter execute_script accepts, with its default.

script_contents = 'hostname'

connection_info.execute_script(
    script_path=None,
    script_contents=script_contents,
    script_args=None,
    timeout=120,
    file_extension=None,
    runas_username=None,
    runas_password=None,
    runas_key=None,
    run_with_sudo=False,
)

Use stored credentials

When a plugin needs credentials, storing them in a connection info object keeps them out of your code. Go to Admin > Connection Info, create an entry with an easily identifiable name, then read it back in the plugin.

from utilities.models import ConnectionInfo

connection_info = ConnectionInfo.objects.get(name='my_connection_info')
ip = connection_info.ip
username = connection_info.username
password = connection_info.password

Use the requests library

Requests is the usual way to make HTTP calls from Python. For HTTPS endpoints, it verifies certificates against the root certificates supplied by the Certifi library. CMP can append additional certificates to that list or disable verification for installations that require it at Admin > SSL Root Certificates.

Rather than hard-coding a choice, call get_ssl_verification() so the plugin picks up whatever each instance is configured for. That keeps the same plugin working across instances.

import requests
from utilities.helpers import get_ssl_verification

response = requests.get('https://github.com', verify=get_ssl_verification())

Create child jobs from a parent job

A plugin can create child jobs under an existing job and have the parent wait until they finish. Creating a job takes the following arguments:

  • job_parameters: The parameters the job being run requires.
  • type: A predefined job type, which determines what runs.
  • owner: The profile of the user requesting the job.
  • status: PENDING when the job is first created.
  • label: The job’s title.
  • parent_job: The Job object that will be the parent.

Given a job object named parent_job, this creates a child job and waits for it:

from jobs.models import HookParameters, Job

# Example job parameters. Pass a JobParameter if the job expects one.
hook_params = {
    "selected_sec_grps": [],
    "resource_handler_id": self.resource_handler.id,
}
hook_param_obj = HookParameters.objects.create(arguments=hook_params)

job_type = "sync_rh_security_groups"  # the type of job to run
child_job = Job(
    job_parameters=hook_param_obj,
    type=job_type,
    owner=profile,
    status="PENDING",
    label="Sync security groups of resource handler",
    parent_job=parent_job,
)
child_job.save()

# Make parent_job wait until its child jobs finish
status, output, errors = parent_job.wait_for_sub_jobs()