Skip to content

Commit

Permalink
Merge pull request #1144 from Fryguy/embedded-ansible-tags
Browse files Browse the repository at this point in the history
Add embedded-ansible tags to some blog posts
  • Loading branch information
agrare authored Jun 19, 2023
2 parents 868fe16 + 54271f4 commit 72ca067
Show file tree
Hide file tree
Showing 5 changed files with 37 additions and 37 deletions.
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
---
title: Calling an Embedded Ansible Playbook from the VM Provision State Machine
---
title: Calling an Embedded Ansible Playbook from the VM Provision State Machine
date: 2018-07-23
tags: ansible cloudforms automate
tags: ansible cloudforms automate embedded-ansible
author: Peter McGowan
---

CloudForms 4.6 provided the ability to run embedded Ansible playbooks as methods, and it can be useful to include such a playbook in an existing workflow such as the VM Provision state machine.

In this example an Ansible playbook method is used at the AcquireIPAddress state to insert an IP address, netmask and gateway into the VM provisioning workflow. A cloud-init script is then used at first boot to set the values in the new VM using nmcli.

## Creating the Instance and Method ##

A new acquire_ip_address instance and method are defined in the usual manner. The method is of Type: playbook and is defined to run on Hosts: localhost
Expand All @@ -20,25 +20,25 @@ The new instance is added to the AcquireIPAddress state of the VM Provision stat
## Inserting the IP Details into the VM Provision Workflow ##

The playbook can write the acquired IP details back into the provision task’s options hash in either of two ways: using the RESTful API, or using an Ansible role.

## Calling the CloudForms RESTful API ##

The first example playbook uses the CloudForms RESTful API to write the retrieved IP details back in to the provision task’s options hash. To simplify the example the IP address, netmask and gateway are defined as static vars; in reality these would be retrieved from a corporate IPAM solution such as Infobox.

---- name: Acquire and Set an IP Address hosts: all gather_facts: no vars: - ip_addr: 192.168.1.66 - netmask: 24 - gateway: 192.168.1.254 tasks: - debug: var=miq_provision_id - debug: var=miq_provision_request_id - name: Update Task with New IP and Hostname Information uri: url: "{{ manageiq.api_url }}/api/provision_requests/{{ miq_provision_request_id }}/request_tasks/{{ miq_provision_id }}" method: POST body_format: json body: action: edit resource: options: addr_mode: ["static", "Static"] ip_addr: "{{ ip_addr }}" subnet_mask: "{{ netmask }}" gateway: "{{ gateway }}" validate_certs: no headers: X-Auth-Token: "{{ manageiq.api_token }}" body_format: json status_code: 200

## Using the manageiq-vmdb Ansible Role ##

The second example playbook uses the manageiq-vmdb Ansible role ([GitHub – syncrou/manageiq-vmdb: Manageiq Role to modify / lookup vmdb objects](<https://github.com/syncrou/manageiq-vmdb>)) to write the retrieved IP details back into the provision task’s options hash. Once again the IP address, netmask and gateway are defined as static vars for simplicity of illustration.

---- name: Acquire and Set an IP Address hosts: all gather_facts: no vars: - ip_addr: 192.168.1.66 - netmask: 24 - gateway: 192.168.1.254 - auto_commit: true - manageiq_validate_certs: false roles: - syncrou.manageiq-vmdb tasks: - debug: var=miq_provision_id - debug: var=miq_provision_request_id - name: Get the task vmdb object manageiq_vmdb: href: "provision_requests/{{ miq_provision_request_id }}/request_tasks/{{ miq_provision_id }}" register: task_object - name: Update Task with new IP and Hostname Information manageiq_vmdb: vmdb: "{{ task_object }}" action: edit data: options: addr_mode: ["static", "Static"] ip_addr: "{{ ip_addr }}" subnet_mask: "{{ netmask }}" gateway: "{{ gateway }}"

In these example playbooks the netmask variable is defined in CIDR format rather than as octets, to be compatible with nmcli.

## Configuring the IP Address at First Boot ##

Configuring a NIC with IP address details is a guest operating system operation, and so must be performed when the VM or instance first boots. For this example a template cloud-init script is defined in Compute -> Infrastructure -> PXE -> Customization Templates in the WebUI, as follows:

<% root_password = MiqPassword.decrypt(evm[:root_password]) hostname = evm[:hostname] ip_addr = evm[:ip_addr] subnet_mask = evm[:subnet_mask] gateway = evm[:gateway] dns_servers = evm[:dns_servers] dns_suffixes = evm[:dns_suffixes]%>#cloud-configssh_pwauth: truedisable_root: falseusers: - default - name: ansible-remote shell: /bin/bash sudo: ['ALL=(ALL) NOPASSWD:ALL'] ssh_authorized_keys: - ssh-rsa AAAAB3NzaC1yc2E...chpasswd: list: | root:<%= root_password %> expire: falseruncmd: ## Setup motd - echo Welcome to VM <%= hostname %>, provisioned by Red Hat CloudForms on $(date) > /etc/motd - rm -f /root/* - nmcli --fields UUID con show | awk '!/UUID/ {print}' | while read line; do nmcli con delete uuid $line; done - nmcli con add con-name eth0 ifname eth0 type ethernet ip4 "<%= ip_addr %>/<%= subnet_mask %>" gw4 "<%= gateway %>" - nmcli con mod eth0 ipv4.dns "<%= dns_servers %>" ipv4.dns-search "<%= dns_suffixes %>" connection.autoconnect yes - nmcli con up eth0 - hostnamectl set-hostname <%= hostname %> - systemctl mask cloud-init-local cloud-init cloud-config cloud-final

If the cloud-init script is selected from the Customize tab of the provisioning dialog, CloudForms will make the variable substitutions at run-time and inject the resultant script into the VM or instance to be run at first boot.
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ author: Peter McGowan
date: 2020-02-12
comments: true
published: true
tags: tutorials
tags: tutorials embedded-ansible
---

This is the first of four blog posts in a series written by Peter McGowan about Embedded Ansible capabilities released in ManageIQ Fine.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ author: Peter McGowan
date: 2020-02-12
comments: true
published: true
tags: tutorials
tags: tutorials embedded-ansible automate services
---


Expand All @@ -27,14 +27,14 @@ For playbook methods one or more target hosts can be specified in the *Hosts* di
The substitution string enables the target *hosts* value to be dynamically translated at run-time and passed to the playbook, enhancing the flexibility of the playbook method.

## Ansible Playbook Input Parameters
To benefit from reusability and flexibility, playbooks are often written to work with *extra_var* variables passed as input parameters. Embedded Ansible playbook services and playbook methods handle input parameters slightly differently.
To benefit from reusability and flexibility, playbooks are often written to work with *extra_var* variables passed as input parameters. Embedded Ansible playbook services and playbook methods handle input parameters slightly differently.

### Playbook Service Input Parameters
Input parameters for playbook services are defined in the *Variables & Default Values* section of the service definition WebUI page, as follows:

![vars_and_default_values](/assets/images/blog/vars_and_default_values-embedded-2.3.jpg)

These variables and their string values are passed into the playbook as extra_vars when the playbook is launched.
These variables and their string values are passed into the playbook as extra_vars when the playbook is launched.

The default values can optionally be overridden from a service dialog when the service is ordered. Any of the service dialog’s elements that are named with the prefix “param_” will be passed as extra_vars to the playbook (with the string “param_” removed).

Expand All @@ -47,8 +47,8 @@ TEMPLATE_CLASS = "ServiceTemplate"
service_template = $evm.vmdb(TEMPLATE_CLASS).where(:name => 'Install a Package').first
credential = $evm.vmdb(CREDENTIAL_CLASS).where(:name => 'Root Password').first
options = {
"credential" => credential.id,
"hosts" => "infra1.cloud.uk.bit63.com",
"credential" => credential.id,
"hosts" => "infra1.cloud.uk.bit63.com",
"param_package" => "mlocate"
}
$evm.execute('create_service_provision_request', service_template, options)
Expand All @@ -60,7 +60,7 @@ The Automate Explorer allows for input parameters (also called method parameters
![input_params](/assets/images/blog/input_params-embedded-2.4.jpg)


Input parameters are passed to the playbook as extra_vars, so can be referred to in the playbook just as any other variable. As an example the first input parameter in this screenshot can be accessed using the
Input parameters are passed to the playbook as extra_vars, so can be referred to in the playbook just as any other variable. As an example the first input parameter in this screenshot can be accessed using the
<code>{% raw %} "{{ ipam_url }}" {% endraw %} </code> syntax.

As can be seen from the _**ipam_user**_ parameter name, the value of an input parameter for an Ansible playbook *method* can be a dynamic variable read from a substitution string at run-time (this substitution is not available for a playbook service).
Expand Down Expand Up @@ -132,17 +132,17 @@ It should be noted that the return from each of these tasks (stored by the *regi
#### Reading Service Dialog Values as Input Parameters
It is often the case that the values input into a service dialog should be passed to an Ansible playbook method somewhere in the workflow. A typical example is when calling an Ansible playbook method from the VM Provision state machine, where the VM provision has been initiated from a service catalog.

The service dialog entries are stored in the service request object’s options hash `:dialog` key, the value of which is itself a hash of dialog element name/value pairs.
The service dialog entries are stored in the service request object’s options hash `:dialog` key, the value of which is itself a hash of dialog element name/value pairs.

From the VM Provision state machine this is accessible from `$evm.root['miq_provision'].miq_provision_request.options`, for example:

```
$evm.root['miq_provision'].miq_provision_request.options[:dialog] = {
"dialog_service_name" => "New Engineering VM",
"dialog_vm_name" => "pemcg-eng-03",
"dialog_option_0_cores_per_socket" => 2,
"dialog_option_0_vm_memory" => 2048,
"dialog_option_0_hostname" => "pemcg-eng-03.lon.redhat.com", "Array::dialog_tag_0_department" => "Classification::1000000000046",
"dialog_service_name" => "New Engineering VM",
"dialog_vm_name" => "pemcg-eng-03",
"dialog_option_0_cores_per_socket" => 2,
"dialog_option_0_vm_memory" => 2048,
"dialog_option_0_hostname" => "pemcg-eng-03.lon.redhat.com", "Array::dialog_tag_0_department" => "Classification::1000000000046",
"password::dialog_option_0_root_password" => "********"
}
```
Expand All @@ -166,12 +166,12 @@ An input parameter can be defined as being of type “password”, for example:
A parameter of this type is decrypted automatically and is available to the playbook as the named extra variable, for example {% raw %}"{{ scrambled_this }}"{% endraw %}. It should be noted that an input parameter that has the text string “password” anywhere in the name will not be passed as a method parameter, and so will not appear in the list of method parameters returned by the `get_method_parameters` function. The variable will however be available as an extra_var with the password value decrypted correctly.

#### Password Defined Earlier in Workflow
A variable encrypted earlier in the workflow (for example when input into a service dialog) can generally be identified as having a name prefixed by the string “password::”. This signifies that the object is of type *MiqPassword*.
A variable encrypted earlier in the workflow (for example when input into a service dialog) can generally be identified as having a name prefixed by the string “password::”. This signifies that the object is of type *MiqPassword*.

A password object of this type can be used as an input parameter if it is passed as a string data type, also prefixed by the string “password::”. The encrypted value will be automatically decrypted and is usable by the playbook as the named extra variable.

For example, to inject the `root_password` value from the previous service dialog using substitution syntax, an input parameter should be defined with a string data type and the following input parameter value:

```
password::${/#miq_provision.miq_provision_request.get_option(:dialog).fetch(password::dialog_option_0_root_password)}
```
Expand All @@ -183,4 +183,4 @@ A screenshot of these input parameters for illustration is as follows:
## Summary
In part two of this series we have looked at how inventory hosts are defined for embedded Ansible playbook methods and services. We have also seen how input parameters can be passed to playbook services and methods and decrypted if necessary, and how to retrieve and use service dialog element values.

Each of these techniques is usable for standalone playbooks, however the real benefit of embedded Ansible comes when using playbook methods as part of a larger automation workflow. Part three will examine how a playbook can interact with other components in an automation workflow.
Each of these techniques is usable for standalone playbooks, however the real benefit of embedded Ansible comes when using playbook methods as part of a larger automation workflow. Part three will examine how a playbook can interact with other components in an automation workflow.
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ author: Peter McGowan
date: 2020-02-13
comments: true
published: true
tags: tutorials
tags: tutorials embedded-ansible automate
---

[Part one](https://www.manageiq.org/blog/2020/02/Embedded-Ansible-Part-1-Built-In-Roles/) of this series described the functionality for using built-in roles to perform tasks with Ansible playbooks rather than Ruby methods in Fine. [Part two](https://www.manageiq.org/blog/2020/02/Embedded-Ansible-Part-2-Passing-Parameters-Into-A-Playbook/) of this series looked at how host lists and input parameters can be passed to embedded Ansible playbook services and methods. This article will discuss how a playbook method can form part of a larger automation workflow by interacting with the ManageIQ Automate workspace.
Expand Down Expand Up @@ -57,7 +57,7 @@ ok: [localhost]
TASK [debug] *******************************************************************
ok: [localhost] => {"msg": "Result:[
u'root',
u'root',
u'/ManageIQ/System/Request/call_instance',
u'/pemcg/Stuff/StateMachines/Methods/state1',
u'/Configuration/OpenShift/Parameters/default',
Expand All @@ -83,7 +83,7 @@ set_encrypted_attribute
Their use is illustrated as follows:

##### get_object_attribute_names
The `get_object_attribute_names` function lists the readable attributes of any object loaded into the workspace (hint: use `get_object_names` to confirm that the object is loaded first).
The `get_object_attribute_names` function lists the readable attributes of any object loaded into the workspace (hint: use `get_object_names` to confirm that the object is loaded first).

This example lists the queryable attributes in the configuration domain `/Configuration/OpenShift/Parameters/default`

Expand Down Expand Up @@ -132,7 +132,7 @@ ok: [localhost]
TASK [debug] *******************************************************************
ok: [localhost] => {
"msg": "Result:True"
}
}
```
<br/>
##### get_attribute
Expand Down Expand Up @@ -243,7 +243,7 @@ $evm.log(:info, "ip_address from Ansible = #{$evm.root['ipam_ip_address']}")
$evm.log(:info, "netmask from Ansible = #{$evm.root['ipam_netmask']}")
$evm.log(:info, "gateway from Ansible = #{$evm.root['ipam_gateway']}")
$evm.log(:info, "root_passwd from Ansible = #{$evm.root.decrypt('root_passwd')}")
...<AEMethod state3> ip_address from Ansible = 192.168.1.23
...<AEMethod state3> netmask from Ansible = 255.255.255.0
...<AEMethod state3> gateway from Ansible = 192.168.1.254
Expand All @@ -257,7 +257,7 @@ The manageiq-vmdb role can be used to update the provisioning task’s options h
roles:
- manageiq-core.manageiq-vmdb
tasks:
tasks:
- name: Update task with new IP information
manageiq_vmdb:
href: {% raw %}"{{ manageiq.request_task }}" {% endraw %}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@ author: Peter McGowan
date: 2020-02-14
comments: true
published: true
tags: tutorials
tags: tutorials embedded-ansible automate
---

Parts [two](https://www.manageiq.org/blog/2020/02/Embedded-Ansible-Part-2-Passing-Parameters-Into-A-Playbook/) and [three](https://www.manageiq.org/blog/2020/02/Embedded-Ansible-Part-3-Ansible-In-An-Automation-Workflow/) of this series looked at passing input parameters into playbooks, and how to work with the Automate workspace. This article discusses how an Ansible playbook can be effectively used in a state machine.

## Working with State Machines

A state machine is a specially constructed Automate class that comprises a sequence of states. Each state typically runs a Ruby or Ansible playbook method, and progression through the state machine is only achieved as each state completes successfully. Individual states can be periodically retried, which gives a state machine the ability to initiate a long-running asynchronous task, followed by a retrying check-completed stage to monitor for task completion.
A state machine is a specially constructed Automate class that comprises a sequence of states. Each state typically runs a Ruby or Ansible playbook method, and progression through the state machine is only achieved as each state completes successfully. Individual states can be periodically retried, which gives a state machine the ability to initiate a long-running asynchronous task, followed by a retrying check-completed stage to monitor for task completion.

A state machine is the best way to model a workflow in ManageIQ Automate.

Expand Down Expand Up @@ -155,7 +155,7 @@ A state retry is triggered by the Ruby or Ansible playbook method running in tha
<br/>
### Passing Values Between Successive Playbook or Ruby Methods in ManageIQ Ivanchuk (5.11.0.28)

Ivanchuk has added the capability for values to be saved from a playbook using the `set_stats` module so that any follow-on Ruby or Ansible methods in a state machine can also access them. The following code sample shows a playbook that has retrieved IP details which are in a hash called *ipam_return*. The example shows how the playbook can use *set_stats* to copy the hash keys and their values back to the state machine workspace.
Ivanchuk has added the capability for values to be saved from a playbook using the `set_stats` module so that any follow-on Ruby or Ansible methods in a state machine can also access them. The following code sample shows a playbook that has retrieved IP details which are in a hash called *ipam_return*. The example shows how the playbook can use *set_stats* to copy the hash keys and their values back to the state machine workspace.

```
- name: Save IPAM detail back to the workspace
Expand Down

0 comments on commit 72ca067

Please sign in to comment.