Today I would like to share with you some of the integration work with Ansible 2.3 that was done in the latest oVirt 4.1 release. The Ansible integration work was quite extensive and included Ansible modules that can be utilized for automating a wide range of oVirt tasks, including tiered application deployment and virtualization infrastructure management.

While Ansible has multiple levels of integrations, I would like to focus this article on oVirt Ansible roles. As stated in the Ansible documentation: “Roles in Ansible build on the idea of include files and combine them to form clean, reusable abstractions – they allow you to focus more on the big picture and only dive into the details when needed.”

We used the above logic as a guideline for developing the oVirt Ansible roles. We will cover three of the many Ansible roles available for oVirt:

For each example, I will describe the role's purpose and how it is used.

oVirt Infra

The purpose of this role is to automatically configure and manage an oVirt datacenter. It will take a newly deployed- but not yet configured- oVirt engine (RHV-M for RHV users), hosts, and storage and configure them to your requirements and specifications. The oVirt engine should already be installed (post engine-setup). Hosts and storage should be deployed but not configured.

This role will take a yml-based variable file such as the following (/path/to/file):


###########################
# REST API variables
###########################
engine_url: https://ovirt-engine.example.com/ovirt-engine/api
engine_user: admin@internal
engine_cafile: /etc/pki/ovirt-engine/ca.pem



###########################
# Common
###########################
compatibility_version: 4.1


###########################
# Data center
data_center_name: mydatacenter
###########################


###########################
# Clusters
###########################
clusters:
- name: production
  cpu_type: Intel Conroe Family
  profile: production



###########################
# Hosts
###########################
hosts:
- name: myhost
  address: 1.2.3.5
  cluster: production
  password: 123456
- name: myhost1
  address: 1.2.3.6
  cluster: production
  password: 123456



###########################
# Storage
###########################
storages:
mynfsstorage:
 master: true
 state: present
 nfs:
  address: 1.2.3.4
  path: /om02
myiscsistorage:
 iscsi:
  target: iqn.2014-07.org.ovirt:storage
  port: 3260
  address: 192.168.200.3
  username: username
  password: password
  lun_id: 3600140551fcc8348ea74a99b6760fbb4
mytemplates:
 domain_function: export
 nfs:
  address: 192.168.200.3
  path: /exports/nfs/exported
myisostorage:
 domain_function: iso
 nfs:
  address: 192.168.200.3
  path: /exports/nfs/iso



###########################
# Networks
###########################
logical_networks:
-  name: mynetwork
  clusters:
   - name: development
     assigned: false
     required: true
     display: false
     migration: true
     gluster: false



host_networks:
- name: myhost1
  check: true
  save: true
  bond:
   name: bond0
   mode: 2
   interfaces:
    - eth2
    - eth3
  networks:
  -  name: mynetwork
     boot_protocol: dhcp



###########################
# Users & Groups
###########################
users:
- name: user1
  authz_name: internal-authz
  password: 1234568
  valid_to: "2018-01-01 00:00:00Z"
- name: user2
  authz_name: internal-authz
  password: 1234568
  valid_to: "2018-01-01 00:00:00Z"

The yml variable file describes the environment configuration and translates it to API calls that will configure the datacenter in minimal time. It will utilize the oVirt Ansible component roles to configure the datacenters, clusters, hosts, storage domains, networks, and optionally add users.

Not only can the role configure a new environment but it can also manage and reconfigure an existing one, giving the capabilities to add and remove entities from the system. In addition, it also includes some special tweaks like cluster profiles. We defined two cluster profiles which designate the cluster as either “production” (with specific SLA and management level characteristics) or “development” (to squeeze more compute power out of the physical hardware).

ovirt-image-template

How many times have you performed the routine task of creating a VM for the sole purpose of converting it to a template? If you find this task tedious, this role comes to the rescue. It’s a very simple and straightforward role. Specify the disk image URL and template properties, and the role automates the process of downloading the qcow2, creating an image, and then converting it into a template for you.



 name: oVirt image template
 hosts: localhost
 connection: local
 gather_facts: false



 vars_files:
   # Contains encrypted `engine_password` varibale using ansible-vault
   - passwords.yml



 vars:
   engine_url: https://ovirt-engine.example.com/ovirt-engine/api
   engine_user: admin@internal
   engine_cafile: /etc/pki/ovirt-engine/ca.pem



   qcow_url: https://cloud.centos.org/centos/7/images/CentOS-7-x86_64-GenericCloud.qcow2
   template_cluster: production
   template_name: centos7_template
   template_memory: 2GiB
   template_cpu: 2
   template_disk_size: 10GiB
   template_disk_storage: nfs



 roles:
   - ovirt-image-template

Ovirt-vm-infra

This role is for laying down the application infrastructure. Let’s take a simple example, say you would like to deploy a LAMP application, one VM for the database and one VM for the web-server. This role can help you to create a consistent configuration for this deployment type and redeployment, with one command line (This is the base for a N-tier application deployment using Ansible dynamic inventory, a topic for another article). To use this role, edit a simple yml file which describes the virtual application infrastructure you would like to create. Here is one such example:


 name: oVirt infra
 hosts: localhost
 connection: local
 gather_facts: false



 vars_files:
   # Contains encrypted `engine_password` varibale using ansible-vault
   - passwords.yml



 vars:
   engine_url: https://ovirt-engine.example.com/ovirt-engine/api
   engine_user: admin@internal
   engine_cafile: /etc/pki/ovirt-engine/ca.pem



   httpd_vm:
     cluster: production
     domain: example.com
     template: rhel7
     memory: 2GiB
     cores: 2
     ssh_key: ssh-rsa AAA...LGx user@fqdn
     disks:
       - size: 10GiB
         name: data
         storage_domain: mynfsstorage
         interface: virtio



   db_vm:
     cluster: production
     domain: example.com
     template: rhel7
     memory: 4GiB
     cores: 1
     ssh_key: ssh-rsa AAA...LGx user@fqdn
     disks:
       - size: 50GiB
         name: data
         storage_domain: mynfsstorage
         interface: virtio



   vms:
     - name: postgresql-vm-0
       tag: postgresql_vm
       profile: "{{ db_vm }}"
     - name: postgresql-vm-1
       tag: postgresql_vm
       profile: "{{ db_vm }}"
     - name: apache-vm
       tag: httpd_vm
       profile: "{{ httpd_vm }}"



 roles:
   - ovirt-vm-infra

Note the profiles concept. Each VM type is specified with a profile from which many instances can be created later on.

Additional information and recommendations:

Get all the Ansible roles and modules for oVirt from Ansible Galaxy, straight from oVirt GitHub. This is where you will also find documentation and demos for each role. In the future, we plan to package all of the roles into a single RPM.

The preferred means of running playbooks is to use a bastion machine on which the oVirt python SDK and Ansible 2.3 would be installed. This way, the Ansible playbook will be translated into REST API calls which will be executed against the engine. You can also run it from the local engine or in other topologies, but using the bastion host would be more secure and performant. If you decide to go with this option, make sure that the connection parameters are set correctly in the playbook:


 name: oVirt infra

 hosts: localhost

 connection: local

 gather_facts: false

 vars:

   engine_url: https://ovirt-engine.example.com/ovirt-engine/api

   engine_user: admin@internal

   engine_cafile: /etc/pki/ovirt-engine/ca.pem

To avoid sending passwords in cleartext, use Ansible vault and encrypt them in an extra-vars file

Wrapping up, I just wanted to give kudos to Ondra Machacek the lead developer of this integration, and the whole oVirt infra team which helped to push this integration forward.

We would love to get your feedback and patches in order to improve those roles and others which are in development.