<?xml version="1.0" encoding="UTF-8" standalone="yes"?><oembed><version><![CDATA[1.0]]></version><provider_name><![CDATA[CloudForms Now]]></provider_name><provider_url><![CDATA[http://cloudformsblog.redhat.com]]></provider_url><author_name><![CDATA[Victor Estival Lopez]]></author_name><author_url><![CDATA[https://cloudformsblog.redhat.com/author/vestival271017/]]></author_url><title><![CDATA[Calling an Embedded Ansible Playbook from the VM Provision State&nbsp;Machine]]></title><type><![CDATA[link]]></type><html><![CDATA[<p>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.</p>
<p><!--more--></p>
<p>In this example an Ansible playbook method is used at the <em>AcquireIPAddress </em>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.</p>
<p>&nbsp;</p>
<h2>Creating the Instance and Method</h2>
<p>&nbsp;</p>
<p>A new <em>acquire_ip_address</em> instance and method are defined in the usual manner. The method is of <strong>Type: playbook </strong>and is defined to run on <strong>Hosts: localhost</strong></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><img data-attachment-id="2512" data-permalink="https://cloudformsblog.redhat.com/2018/07/23/calling-an-embedded-ansible-playbook-from-the-vm-provision-state-machine/method/" data-orig-file="https://cloudformsredhat.files.wordpress.com/2018/07/method.png" data-orig-size="582,362" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="method" data-image-description="" data-medium-file="https://cloudformsredhat.files.wordpress.com/2018/07/method.png?w=300" data-large-file="https://cloudformsredhat.files.wordpress.com/2018/07/method.png?w=582" class="alignnone size-full wp-image-2512" src="https://cloudformsredhat.files.wordpress.com/2018/07/method.png" alt="" /></p>
<p>&nbsp;</p>
<p>The input parameters for the playbook method are dynamic. Two parameters <em>miq_provision_request_id</em> (the request ID) and <em>miq_provision_id</em> (the task ID), are defined as follows:</p>
<p><img data-attachment-id="2513" data-permalink="https://cloudformsblog.redhat.com/2018/07/23/calling-an-embedded-ansible-playbook-from-the-vm-provision-state-machine/input_parameters/" data-orig-file="https://cloudformsredhat.files.wordpress.com/2018/07/input_parameters.png" data-orig-size="1836,268" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="input_parameters" data-image-description="" data-medium-file="https://cloudformsredhat.files.wordpress.com/2018/07/input_parameters.png?w=300" data-large-file="https://cloudformsredhat.files.wordpress.com/2018/07/input_parameters.png?w=1024" class="alignnone size-large wp-image-2513" src="https://cloudformsredhat.files.wordpress.com/2018/07/input_parameters.png?w=1024" alt="" srcset="https://cloudformsredhat.files.wordpress.com/2018/07/input_parameters.png?w=1024 1024w, https://cloudformsredhat.files.wordpress.com/2018/07/input_parameters.png?w=150 150w, https://cloudformsredhat.files.wordpress.com/2018/07/input_parameters.png?w=300 300w, https://cloudformsredhat.files.wordpress.com/2018/07/input_parameters.png?w=768 768w, https://cloudformsredhat.files.wordpress.com/2018/07/input_parameters.png 1836w" sizes="(max-width: 1024px) 100vw, 1024px" /></p>
<p>&nbsp;</p>
<p>The new instance is added to the <em>AcquireIPAddress</em> state of the VM Provision state machine:</p>
<p><img data-attachment-id="2514" data-permalink="https://cloudformsblog.redhat.com/2018/07/23/calling-an-embedded-ansible-playbook-from-the-vm-provision-state-machine/state_machine/" data-orig-file="https://cloudformsredhat.files.wordpress.com/2018/07/state_machine.png" data-orig-size="1316,442" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="state_machine" data-image-description="" data-medium-file="https://cloudformsredhat.files.wordpress.com/2018/07/state_machine.png?w=300" data-large-file="https://cloudformsredhat.files.wordpress.com/2018/07/state_machine.png?w=1024" class="alignnone size-large wp-image-2514" src="https://cloudformsredhat.files.wordpress.com/2018/07/state_machine.png?w=1024" alt="" srcset="https://cloudformsredhat.files.wordpress.com/2018/07/state_machine.png?w=1024 1024w, https://cloudformsredhat.files.wordpress.com/2018/07/state_machine.png?w=150 150w, https://cloudformsredhat.files.wordpress.com/2018/07/state_machine.png?w=300 300w, https://cloudformsredhat.files.wordpress.com/2018/07/state_machine.png?w=768 768w, https://cloudformsredhat.files.wordpress.com/2018/07/state_machine.png 1316w" sizes="(max-width: 1024px) 100vw, 1024px" /></p>
<p>&nbsp;</p>
<h2>Inserting the IP Details into the VM Provision Workflow</h2>
<p>&nbsp;</p>
<p>The playbook can write the acquired IP details back into the provision task&#8217;s options hash in either of two ways: using the RESTful API, or using an Ansible role.</p>
<p>&nbsp;</p>
<h3>Calling the CloudForms RESTful API</h3>
<p>&nbsp;</p>
<p>The first example playbook uses the CloudForms RESTful API to write the retrieved IP details back in to the provision task&#8217;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.</p>
<p>&nbsp;</p>
<pre class="language-none line-numbers"><code>---
- 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
</code></pre>
<p>&nbsp;</p>
<h3>Using the <em>manageiq-vmdb</em> Ansible Role</h3>
<p>&nbsp;</p>
<p>The second example playbook uses the <em>manageiq-vmdb</em> Ansible role (<a class="jive-link-external-small" href="https://mojo.redhat.com/external-link.jspa?url=https%3A%2F%2Fgithub.com%2Fsyncrou%2Fmanageiq-vmdb" target="_blank" rel="nofollow noopener">GitHub &#8211; syncrou/manageiq-vmdb: Manageiq Role to modify / lookup vmdb objects</a> ) to write the retrieved IP details back into the provision task&#8217;s options hash. Once again the IP address, netmask and gateway are defined as static vars for simplicity of illustration.</p>
<p>&nbsp;</p>
<pre class="language-none line-numbers"><code>---
- 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 }}"
</code></pre>
<p>&nbsp;</p>
<p>In these example playbooks the <em>netmask</em> variable is defined in CIDR format rather than as octets, to be compatible with nmcli.</p>
<p>&nbsp;</p>
<h2>Configuring the IP Address at First Boot</h2>
<p>&nbsp;</p>
<p>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 <strong>Compute -&gt; Infrastructure -&gt; PXE -&gt; Customization Templates</strong> in the WebUI, as follows:</p>
<p>&nbsp;</p>
<pre class="language-none line-numbers"><code>&lt;% 
   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]
%&gt;
#cloud-config
ssh_pwauth: true
disable_root: false
users:
  - default
  - name: ansible-remote
    shell: /bin/bash
    sudo: ['ALL=(ALL) NOPASSWD:ALL']
    ssh_authorized_keys:
      - ssh-rsa AAAAB3NzaC1yc2E...

chpasswd:
  list: |
    root:&lt;%= root_password %&gt;
  expire: false
runcmd:
  ## Setup motd
  - echo Welcome to VM &lt;%= hostname %&gt;, provisioned by Red Hat CloudForms on $(date) &gt; /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 "&lt;%= ip_addr %&gt;/&lt;%= subnet_mask %&gt;"
    gw4 "&lt;%= gateway %&gt;"
  - nmcli con mod eth0
    ipv4.dns "&lt;%= dns_servers %&gt;"
    ipv4.dns-search "&lt;%= dns_suffixes %&gt;"
    connection.autoconnect yes
  - nmcli con up eth0
  - hostnamectl set-hostname &lt;%= hostname %&gt;
  - systemctl mask cloud-init-local cloud-init cloud-config cloud-final</code></pre>
<p>&nbsp;</p>
<p>If the cloud-init script is selected from the <strong>Customize</strong> 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.</p>
]]></html><thumbnail_url><![CDATA[https://cloudformsredhat.files.wordpress.com/2018/07/laptop-1478822_640.jpg?fit=440%2C330]]></thumbnail_url><thumbnail_width><![CDATA[440]]></thumbnail_width><thumbnail_height><![CDATA[293]]></thumbnail_height></oembed>