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
In order for Ansible to run playbooks and tasks, it needs to know which
machines to operate on. The standard way that Ansible achieves this is to use
an inventory file which lists the hosts and groups that playbooks will run
against. This inventory is a plain text ini or yaml file that lives at
/etc/ansible/hosts
by default.
A dynamic inventory provides a way for Ansible to pull in inventory information from other systems. This means that you do not need to manually sync your local inventory with another source, rather you can invoke a tool that queries the source directly and makes the information available to Ansible. Dynamic inventories are scripts or plugins that output JSON in a predefined format that Ansible understands.
The Ansible project has an OpenStack dynamic inventory plugin available which we can use to integrate Ansible with the Catalyst Cloud. This allows us to use Ansible for configuration management of Catalyst Cloud instances irrespective of what method has been used to create those instances.
Use the ansible-galaxy command to install the openstack.cloud collection. The collection includes modules for using Ansible with Openstack as well as the inventory plugin.
$ ansible-galaxy collection install openstack.cloud
You can verify that the inventory plugin is available by running the following command to list inventory plugins:
$ ansible-doc -t inventory -l
You should see openstack.cloud.openstack
listed in the available plugins.
Create an inventory file named openstack.yml
with the following contents:
plugin: openstack.cloud.openstack
expand_hostvars: yes
fail_on_errors: yes
Now we can test the plugin:
$ ansible-inventory -i openstack.yml --list
This will output JSON data about your compute instances.
You can filter this output as required:
$ ansible-inventory -i openstack.yml --list | grep ansible_ssh_host
"ansible_ssh_host": "150.242.40.72",
"ansible_ssh_host": "150.242.40.71",
or if you have jq
installed:
$ ansible-inventory -i openstack.yml --list | jq -r '._meta.hostvars[].ansible_ssh_host'
150.242.40.72
150.242.40.71
$ ansible-inventory -i openstack.yml --list | jq -r '._meta.hostvars[].openstack.name'
example-instance-02
example-instance-01
Now that you have the inventory plugin working, you can use it in a playbook. You are going to use the following playbook:
---
- name: Ping cloud instances
hosts: all
remote_user: ubuntu
tasks:
- name: Test connection to instance
ping:
Let’s run this playbook with the dynamic inventory:
$ ansible-playbook -i ./openstack.yml ping.yml
PLAY [Ping cloud instances] ****************************************************
TASK [setup] *******************************************************************
ok: [example-instance-02]
ok: [example-instance-01]
TASK [Test connection to instance] *********************************************
ok: [example-instance-01]
ok: [example-instance-02]
PLAY RECAP *********************************************************************
example-instance-01 : ok=2 changed=0 unreachable=0 failed=0
example-instance-02 : ok=2 changed=0 unreachable=0 failed=0
You will notice that your playbook is configured to operate against all hosts
returned from the inventory plugin (set via hosts: all
). If you would like to
operate on a subset of hosts, there are a number of options.
If you look at the JSON output again, you can see the information about your
instances is contained under the _meta
key. The other top level keys of the
returned JSON object point to lists of instances. These keys relate to various
properties of your instances and are output by the inventory plugin dynamically.
In addition to the automatic key creation, users can generate their own groupings based on instance metadata. In the following example, you have added two metadata items to each instance:
$ nova show example-instance-01 | grep metadata | awk -F'|' '{ print $3 }' | jq '.'
{
"group": "group01",
"example": "foobar"
}
$ nova show example-instance-02 | grep metadata | awk -F'|' '{ print $3 }' | jq '.'
{
"group": "group02",
"example": "foobar"
}
In the example below, you are using jq
to remove the data associated with
the _meta
key so you can view just the instance lists.
$ ./openstack.py --list | jq -r '. | del(._meta)'
{
"envvars": [
"example-instance-01",
"example-instance-02"
],
"envvars_nz-por-1": [
"example-instance-01",
"example-instance-02"
],
"envvars_nz-por-1_nz-por-1a": [
"example-instance-01",
"example-instance-02"
],
"flavor-c1.c1r1": [
"example-instance-01",
"example-instance-02"
],
"group01": [
"example-instance-01"
],
"group02": [
"example-instance-02"
],
"image-ubuntu-14.04-x86_64": [
"example-instance-01",
"example-instance-02"
],
"instance-b495f9cc-47f9-49cc-9780-xxxxxxxxxxxx": [
"example-instance-02"
],
"instance-ca13f6c2-600c-493d-936d-xxxxxxxxxxxx": [
"example-instance-01"
],
"meta-example_foobar": [
"example-instance-01",
"example-instance-02"
],
"meta-group_group01": [
"example-instance-01"
],
"meta-group_group02": [
"example-instance-02"
],
"nz-por-1": [
"example-instance-01",
"example-instance-02"
],
"nz-por-1_nz-por-1a": [
"example-instance-01",
"example-instance-02"
],
"nz-por-1a": [
"example-instance-01",
"example-instance-02"
]
}
You can see a number of different groupings of instances are available,
including groupings based on the metadata you passed. Metadata with the key
group
is a special case that will be translated directly into an Ansible
host group of that name.
Any of these groups may be used within a playbook. For example, let’s make use
of the group01
group to run our playbook against only
example-instance-01
:
---
- name: Ping cloud instances
hosts: group01
remote_user: ubuntu
tasks:
- name: Test connection to instance
ping:
Let’s run this playbook with the dynamic inventory:
$ ansible-playbook -i ./openstack.yml ping.yml
PLAY [Ping cloud instances] ****************************************************
TASK [setup] *******************************************************************
ok: [example-instance-01]
TASK [Test connection to instance] *********************************************
ok: [example-instance-01]
PLAY RECAP *********************************************************************
example-instance-01 : ok=2 changed=0 unreachable=0 failed=0
You can associate metadata with an instance at instance creation time. It is also possible to add metadata to an instance after it has been created, for example using the nova command line client:
$ nova meta example-instance-01 set example-key=example-value
$ nova show example-instance-01 | grep metadata | awk -F'|' '{ print $3 }' | jq '.'
{
"example-key": "example-value",
"group": "group01",
"example": "foobar"
}
Note
Metadata keys do not natively support lists as keys, so you will overwrite the previous group if you reset a group.
An Ansible playbook for creating the instances used in this example is available at https://raw.githubusercontent.com/catalyst/catalystcloud-ansible/master/example-playbooks/two-instances-with-sequence.yml