This tutorial assumes the following:
You have installed the OpenStack command line tools and sourced an OpenStack RC file, as explained at Installation on Linux and Mac
You have a basic understanding of how to use Ansible on the Catalyst Cloud as shown here
Normally Ansible requires an inventory file to be created, to know which machines it is meant to operate on.
This is typically a manual process but can be greatly improved by using the likes of the dynamic inventory to pull inventory information from other systems. Details for this approach are shown at Using ansible dynamic inventories on the Catalyst Cloud.
Suppose, however, you needed to create ‘x’ number of instances which were transient in nature and had no existing details available to populate an inventory file for Ansible to utilise? If ‘x’ is a small number, you could easily hand-craft the inventory file, but once this number gets into tens of instances it becomes onerous.
To get around this problem, we can make use of Ansible’s ability to populate an in-memory inventory, using the add_host module, with information it generates while creating new instances.
The add_host module makes use of variables to create an in-memory inventory of new hosts and groups that can be used in subsequent plays within the same playbook.
Parameter |
Required |
Comments |
---|---|---|
groups |
no |
The groups to add the hostname to, comma separated. aliases: groupname, group |
name |
yes |
The hostname/ip of the host to add to the inventory, can include a colon and a port number. aliases: hostname, host |
The table above shows the basic parameters required for using add_host:
name can be the hostname or IP address that will be used to reference the newly created instances
groups is optional and creates group labels to access the new instances using the ‘host:’ directive in subsequent plays
It is also possible to add custom variables at this time to help further define the new hosts.
# add host to group 'created_vms' with a variable foo=42
- add_host:
name: "{{ public_v4 }}"
groups: created_vms
foo: 42
The requirements of this playbook are as follows:
create a variable number of OpenStack instances on the Catalyst cloud
carry out identical configuration across all nodes
pause the playbook to allow for interaction with the new nodes, i.e. processing/testing etc.
on resuming playback, terminate all new instances
This makes the following assumptions:
an RC file has already been sourced
a private network called example-net already exists
a security group called example-sg already exists
an access key called example-key has already been uploaded
The number of instances to be created is controlled by a ‘count’ variable. As the instances are created, the results are captured in a registered variable called ‘newnodes’. This is in turn iterated over using a ‘loop’ to add the required details to the in-memory inventory, as shown in this snippet:
add_host:
name: "{{ item.server.public_v4 }}"
groups: created_nodes
ansible_user: ubuntu
instance_name: "{{ item.server.name }}"
loop: "{{ newnodes.results }}"
The newly created group ‘created_nodes’ is used to iterate over the new instances. Firstly it checks to see that SSH is responding and then it begins the configuration of the nodes.
For the purposes of this example, the configuration involves simply installing some packages onto each node. Once this has been completed, the playbook is paused and will remain that way until told to either continue or abort.
Assuming that playback is continued rather than aborted, the final play will delete all of the nodes in the ‘created_nodes’ group.
Here is the complete playbook containing the three plays outlined above:
---
#
# Play 1 - Create 'x' instances in Openstack based on 'count' var
#
- name: Deploy multiple duplicate cloud instances in OpenStack
hosts: localhost
vars:
image: ubuntu-18.04-x86_64
network: example-net
key_name: example-key
flavor: c1.c1r1
security_groups: example-sg
count: 3
tasks:
- name: Connect to the Catalyst Cloud
# assume RC file has already been sourced
os_auth:
- name: Launch web instances
os_server:
state: present
name: test0{{ item }}
flavor: "{{ flavor }}"
image: "{{ image }}"
key_name: "{{ key_name }}"
wait: true
network: "{{ network }}"
security_groups: "{{ security_groups }}"
auto_ip: true
loop: "{{ range(1, count + 1) | list }}"
register: newnodes
- name: Create in-memory Ansible inventory
add_host:
name: "{{ item.server.public_v4 }}"
groups: created_nodes
ansible_user: ubuntu
instance_name: "{{ item.server.name }}"
loop: "{{ newnodes.results }}"
#
# Play 2 - Configure nodes from in-memory inventory
#
- name: Configure nodes
hosts: created_nodes
become: true
gather_facts: false
tasks:
- name: Wait for SSH banners
wait_for_connection:
delay: 5
timeout: 300
- name: Install apps
apt:
state: present
name: ["git", "htop"]
update_cache: true
- name: Pause play to interact with the servers
pause:
prompt: "Playbook paused... hit <enter> to continue or <ctrl-c a> to abort"
#
# Play 3 - Destroy nodes
#
- name: Destroy nodes
hosts: localhost
tasks:
- name: Destroy instances
os_server:
state: absent
name: "{{ hostvars.item.instance_name }}"
loop: "{{ groups.created_nodes }}"