Full Stack Automation with Ansible and OpenStack

Ansible offers great flexibility. Because of this the community has figured out many useful ways to leverage Ansible modules and playbook structures to automate frequent operations on multiple layers, including using it with OpenStack.

In this blog we’ll cover the many use-cases for Ansible, the most popular automation software, with OpenStack, the most popular cloud infrastructure software. We’ll help you understand here how and why you should use Ansible to make your life easier, in what we like to call Full-Stack Automation.ansible openstack automation

Let’s begin by analyzing the layers of Full-Stack Automation, shown in the diagram above. At the bottom, we have the hardware resources (servers, storage area networks, and networking gear). Above, is the operating system (Linux or Windows). On the Linux side, you can install OpenStack to abstract all of your datacenter resources and offer a software-defined version of your compute, network, and storage resources. On top of OpenStack, are the tenant-defined services needed to create the virtual machines where the applications will reside. Finally, you have to manage the operating system (Linux or Windows) to deploy the actual applications and workloads that you really care about (databases, web servers, mobile application backends, etc.). If you use containers (like Docker or Rkt), you’ll package those applications in images that will be deployed on top of your Guest OS. In addition to that, some languages introduce the concept of application servers, which adds another layer (i.e. J2EE).

Ansible management possibilities

With Ansible, you have a module to manage every layer. This is true even for the networking hardware, although technically speaking it’s for the network operating system, like IOS or NXOS (see the full list of Ansible network modules here).

  1. General interaction with the Operating System: install packages, change or enforce file content or permissions, manage services, create/remove users and groups, etc.
    1. Linux and BSD via SSH (the first and most popular use-case)
    2. Windows via PowerShell (since 1.7)
  2. IaaS Software: install the IaaS software and its dependencies (databases, load balancers, configuration files, services, and other helper tools)
    1. OpenStack-ansible installer https://github.com/openstack/openstack-ansible, as used in some upstream-based OpenStack distributions from other vendors. Note that the Red Hat OpenStack Platform does not use Ansible, but Heat and Puppet. Future releases will leverage Ansible to perform certain validations and to help operators perform their updates and upgrades.
    2. CloudStack installer is also an Ansible-based project.
  3. Virtual Resources: define the resource, like a Virtual Machine or Instance, in terms of how big it is, who can access it, what content should it have, what security profile and network access it requires, etc.
    1. OpenStack Ansible modules (since Ansible 2.0): for instance, Nova or Neutron. It’s based on the OpenStack “shade” library, a common tool for all CLI tools in OpenStack.
    2. It can also manage not so virtual network resources, via netconf (since 2.2) https://docs.ansible.com/ansible/netconf_config_module.html
    3. VmWare vSphere Ansible modules
    4. RHV or oVirt or Libvirt for bare KVM
    5. It also has modules for public cloud providers, like Amazon, Google Cloud, Azure and Digital Ocean
  4. Guest OS: the same components as described for the Host OS. But how do you discover how many Guests you have?
    1. Ansible Dynamic Inventory will dynamically interrogate the IaaS/VM layer and discover which instances are currently available. It detects their hostname, IPs, and security settings and replaces the static Inventory concept. This is especially useful if you leverage Auto Scaling Groups in your cloud infrastructure, which makes your list of instances very variable over time.
  5. Containers Engine (optional)
    1. Docker: Note that the old Docker module is deprecated for a new, native version, in Ansible 2.1.
    2. Kubernetes
    3. Atomic Host
  6. Tenant Software: databases, web servers, load balancers, data processing engines, etc.
    1. Ansible Galaxy is the repository of recipes (playbooks) to deploy the most popular software, and it’s the result of the contributions of thousands of community members.
    2. You can also manage web Infrastructure such as JBoss, allowing Ansible to define how an app is deployed in the application server.

How to install the latest Ansible on a Python virtual environment

As you have seen, some features are only available with very recent Ansible versions, like 2.2. However, your OS may not ship it yet. For example, RHEL 7 or CentOS 7 only comes with Ansible 1.9.

Given that Ansible is a command-line tool written in Python, which supports multiple versions on a system, you may not need the security hardening in Ansible that your distribution offers, and you may want to try the latest version instead.

However, as any other Python software, there are many dependencies, and it’s very dangerous to mix untested upstream libraries with your system-provided ones. Those libraries may be shared and used in other parts of your system, and untested newer libraries can break other applications. The quick solution is to install the latest Ansible version, with all its dependencies, in a isolated folder under your non-privileged user account. This is called a Python Virtual Environment (virtualenv), and if done properly, allows you to safely play with the latest Ansible modules for a full-stack orchestration. Of course, we do not recommend this practice for any production use-case; consider it a learning exercise to improve your DevOps skills.

1) Install prerequisites (pip, virtualenv)

The only system-wide python library we need here is “virtualenvwrapper”. Other than that, you should not do “sudo pip install” as it will replace system python libraries with untested, newer ones. We only trust one here, “virtualenvwrapper”. The virtual environment method is a good mechanism for installing and testing newer python modules in your non-privileged user account.

$ sudo yum install python-pip
$ sudo pip install virtualenvwrapper
$ sudo yum install python-heatclient python-openstackclient python2-shade

Alternatively, if your account is not a “sudoer”, you can use these commands

wget https://bootstrap.pypa.io/get-pip.py
python get-pip.py --user
 .local/bin/pip install virtualenvwrapper --user
export PATH=$PATH:~/.local/bin #Remember to include this in your .bashrc

2) Setup a fresh virtualenv, where we’ll install the latest Ansible release

First, create a directory to hold the virtual environments.

$ mkdir $HOME/.virtualenvs

Then, add the following lines to your .bashrc

export WORKON_HOME=$HOME/.virtualenvs

source /usr/bin/virtualenvwrapper.sh

Now source it.

$ source ~/.bashrc

At this point, wrapper links are created, but only the first time you run it. To see the list of environments, just execute “workon”, which should be empty. Next, we’ll create a new virtualenv named “ansible2” , which will be automatically enabled, with access to the default RPM-installed packages.

$ mkvirtualenv ansible2 --system-site-packages

To exit the virtualenv, type “deactivate”, and to re-enter again, use “workon”.

$ deactivate
$ workon ansible2

3) Enter the new virtualenv and install Ansible2 via PIP (as regular user, not root)

You can notice your shell prompt has changed and it shows the virtualenv name in brackets.

(ansible2) $ pip install ansible 

The above command will install just the ansible 2 dependencies, leveraging your system-wide RPM-provided python packages (thanks to the –system-site-packages flag we used earlier). Alternatively, if you want to try the development branch:

(ansible2) $ pip install git+git://github.com/ansible/ansible.git@devel
(ansible2) $ ansible --version

If you ever want to remove the virtualenv, and all its dependencies, just use use “rmvirtualenv ansible2”.

NOTE: if the above commands don’t work, make sure you have installed GCC and OpenSSL libraries in your systems, as some python dependencies involve recent cryptography modules

4) Install OpenStack client dependencies

The first command below ensures you have the latest stable OpenStack API versions, although you can also try a pip install to get the latest CLI. The second command provides the latest python “shade” library to connect to latest OpenStack API versions using ansible, regardless of the CLI tool.

(ansible2) $ yum install python-openstackclient python-heatclient
(ansible2) $ pip install shade --upgrade

5) Test it

(ansible2) $ ansible -m ping localhost

localhost | SUCCESS => {

"changed": false,

"ping": "pong"

}

NOTE: you cannot run this version of ansible outside the virtualenv, so always remember to do “workon ansible2” before usi.


Using Ansible to orchestrate OpenStack

Our savvy readers will notice that using Ansible to orchestrate OpenStack seems to ignore the fact that Heat is the official orchestration module for OpenStack. Indeed, an Ansible Playbook will do almost the same as a HOT template (HOT is the YAML-based syntax for Heat, an evolution of AWS CloudFormation). However, there are many DevOps professionals out there who don’t like to learn new syntax, and they are already consolidating all their process for their hybrid infrastructure.

The Ansible team recognized that and leveraged Shade, the official library from the OpenStack project, to build interfaces to OpenStack APIs. At the time of this writing, Ansible 2.2 includes modules to call the following APIs

  • Keystone: users, groups, roles, projects
  • Nova: servers, keypairs, security-groups, flavors
  • Neutron: ports, network, subnets, routers, floating IPs
  • Ironic: nodes, introspection
  • Swift Objects
  • Cinder volumes
  • Glance images

From an Ansible perspective, it needs to interact with a server where it can load the OpenStack credentials and open an HTTP connection to the OpenStack APIs. If that server is your machine (localhost), then it will work locally, load the Keystone credentials, and start talking to OpenStack.

Let’s see an example. We’ll use Ansible OpenStack modules to connect to Nova and start a small instance with the Cirros image. But we’ll first upload the latest Cirros image, if not present. We’ll use an existing SSH key from our current user. You can download this playbook from this github link.

---
# Setup according to Blogpost "Full Stack automation with Ansible and OpenStack". Execute with "ansible-playbook ansible-openstack-blogpost.yml  -c local -vv"
# #
# #
# #
- name: Execute the Blogpost demo tasks
  hosts: localhost
  tasks:
  - name: Download cirros image
    get_url:
      url: http://download.cirros-cloud.net/0.3.4/cirros-0.3.4-x86_64-disk.img
      dest: /tmp/cirros-0.3.4-x86_64-disk.img
  - name: Upload cirros image to openstack
    os_image:
      name: cirros
      container_format: bare
      disk_format: qcow2
      state: present
      filename: /tmp/cirros-0.3.4-x86_64-disk.img

  - name: Create new keypair from current user's default SSH key
    os_keypair:
      state: present
      name: ansible_key
      public_key_file: "{{ '~' | expanduser }}/.ssh/id_rsa.pub"

  - name: Create the test network
    os_network:
      state: present
      name: testnet
      external: False
      shared: False
      #provider_network_type: vlan
      #provider_physical_network: datacentre
    register: testnet_network

  - name: Create the test subnet
    os_subnet:
      state: present
      network_name: "{{ testnet_network.id }}"
      name: testnet_sub
      ip_version: 4
      cidr: 192.168.0.0/24
      gateway_ip: 192.168.0.1
      enable_dhcp: yes
      dns_nameservers:
        - 8.8.8.8
    register: testnet_sub

  - name: Create the test router
    ignore_errors: yes #for some reasons, re-running this task gives errors
    os_router:
      state: present
      name: testnet_router
      network: nova
      external_fixed_ips:
        - subnet: nova
      interfaces:
        - testnet_sub

  - name: Create a new security group
    os_security_group:
      state: present
      name: secgr
  - name: Create a new security group allowing any ICMP
    os_security_group_rule:
      security_group: secgr
      protocol: icmp
      remote_ip_prefix: 0.0.0.0/0
  - name: Create a new security group allowing any SSH connection
    os_security_group_rule:
      security_group: secgr
      protocol: tcp
      port_range_min: 22
      port_range_max: 22
      remote_ip_prefix: 0.0.0.0/0
     
  - name: Create server instance
    os_server:
      state: present
      name: testServer
      image: cirros
      flavor: m1.small
      security_groups: secgr
      key_name: ansible_key
      nics:
        - net-id: "{{ testnet_network.id }}"
    register: testServer

  - name: Show Server's IP
    debug: var=testServer.openstack.public_v4

After the execution, we see the IP of the instance. We write it down, and we can now use Ansible to connect into it via SSH. We assume Nova’s default network allows connections from our workstation, in our case via a provider network.


Comparison with OpenStack Heat

Using Ansible instead of Heat has it’s advantages and disadvantages. For instance, with Ansible you must keep track of the resources you create, and manually delete them (in reverse order) once you are done with them. This is especially tricky with Neutron ports, floating IPs and routers. With Heat, you just delete the stack, and all the created resources will be properly deleted.

Compare the above with a similar (but not equivalent) Heat Template, that can be downloaded from this github gist:

heat_template_version: 2015-04-30

description: >
  Node template. Launch with "openstack stack create   --parameter public_network=nova --parameter ctrl_network=default --parameter secgroups=default --parameter image=cirros --parameter key=ansible_key --parameter flavor=m1.small --parameter name=myserver -t openstack-blogpost-heat.yaml testStack"

parameters:
  name:
    type: string
    description: Name of node
  key:
    type: string
    description: Name of keypair to assign to server
  secgroups:
    type: comma_delimited_list
    description: List of security group to assign to server
  image:
    type: string
    description: Name of image to use for servers
  flavor:
    type: string
    description: Flavor to use for server
  availability_zone:
    type: string
    description: Availability zone for server
    default: nova
  ctrl_network:
    type: string
    label: Private network name or ID
    description: Network to attach instance to.
  public_network:
    type: string
    label: Public network name or ID
    description: Network to attach instance to.

resources:

  ctrl_port:
    type: OS::Neutron::Port
    properties:
      network: { get_param: ctrl_network }
      security_groups: { get_param: secgroups }

  floating_ip:
    type: OS::Neutron::FloatingIP
    properties:
      floating_network: { get_param: public_network }
      port_id: { get_resource: ctrl_port }

  instance:
    type: OS::Nova::Server
    properties:
      name: { get_param: name }
      image: { get_param: image }
      flavor: { get_param: flavor }
      availability_zone: { get_param: availability_zone }
      key_name: { get_param: key }
      networks:
       - port: { get_resource: ctrl_port }

Combining Dynamic Inventory with the OpenStack modules

Now let’s see what happens when we create many instances, but forget to write down their IP’s. The perfect example to leverage Dynamic Inventory for OpenStack is to learn the current state of our tenant virtualized resources, and gather all server IP’s so we can check their kernel version, for instance. This is transparently done by Ansible Tower, for instance, which will periodically run the inventory and collect the updated list of OpenStack servers to manage.

Before you execute this, ensure you don’t have stale cloud.yaml files in either ~/.config/openstack, /etc/openstack, or /etc/ansible. The Dynamic Inventory script will look for environment variables first (OS_*), and then it will search for those files.

#ensure you are using latest ansible version

$ workon ansible2
$ wget https://raw.githubusercontent.com/ansible/ansible/devel/contrib/inventory/openstack.py
$ chmod +x openstack.py
$ ansible -i openstack.py all -m ping
bdef428a-10fe-4af7-ae70-c78a0aba7a42 | SUCCESS => {
    "changed": false,
    "ping": "pong"
}
343c6e76-b3f6-4e78-ae59-a7cf31f8cc44 | SUCCESS => {
    "changed": false,
    "ping": "pong"
}

You can have fun by looking at all the information that the Inventory script above returns if you just executed as follows:

$ ./openstack.py –list

{
 "": [
    "777a3e02-a7e1-4bec-86b7-47ae7679d214",
    "bdef428a-10fe-4af7-ae70-c78a0aba7a42",
    "0a0c2f0e-4ac6-422d-8d9b-12b7a87daa72",
    "9d4ee5c0-b53d-4cdb-be0f-c77fece0a8b9",
    "343c6e76-b3f6-4e78-ae59-a7cf31f8cc44"
 ],
 "_meta": {
    "hostvars": {
     "0a0c2f0e-4ac6-422d-8d9b-12b7a87daa72": {
       "ansible_ssh_host": "172.31.1.42",
       "openstack": {
         "HUMAN_ID": true,
         "NAME_ATTR": "name",
         "OS-DCF:diskConfig": "MANUAL",
         "OS-EXT-AZ:availability_zone": "nova",
         "OS-EXT-SRV-ATTR:host": "compute-0.localdomain",
         "OS-EXT-SRV-ATTR:hypervisor_hostname": "compute-0.localdomain",
         "OS-EXT-SRV-ATTR:instance_name": "instance-000003e7",
         "OS-EXT-STS:power_state": 1,
         "OS-EXT-STS:task_state": null,
         "OS-EXT-STS:vm_state": "active",
         "OS-SRV-USG:launched_at": "2016-10-10T21:13:24.000000",
         "OS-SRV-USG:terminated_at": null,
         "accessIPv4": "172.31.1.42",
         "accessIPv6": "",
(....)


Conclusion

Even though Heat is very useful, some people may prefer to learn Ansible to do their workload orchestration, as it offers a common language to define and automate the full stack of I.T. resources. I hope this article has provided you with a practical example, with a very basic use case for Ansible to launch OpenStack resources. If you are interested in trying Ansible and Ansible Tower, please visit https://www.ansible.com/openstack. A good starting point would be connecting Heat with Ansible Tower callbacks, as described in this other blog post

Also, if you want to learn more about Red Hat OpenStack Platform, you’ll find lots of valuable resources (including videos and whitepapers) on our website. https://www.redhat.com/en/technologies/linux-platforms/openstack-platform