This is a text-only version of the following page on https://raymii.org:
---
Title : Ansible - Create OpenStack servers with Ansible 2.0 and the os_server module and a dynamic inventory
Author : Remy van Elst
Date : 10-09-2016
URL : https://raymii.org/s/tutorials/Ansible_-_create_OpenStack_servers_with_os_server.html
Format : Markdown/HTML
---
I regularly deploy clusters and single servers on OpenStack with Ansible.
However, Ansible 2.0 comes with new OpenStack modules my playbooks still used
the old ones. I reserved some time to convert these playbooks to the new modules
and ansible 2. This article shows a very simple example, it creates three
servers in OpenStack and adds them to different hostgroups based on variables.
For example, to create one loadbalancer and two appservers and run specific
playbooks on those hosts based on their role.
Since Ansible 2 the OpenStack modules are renewed. The old `nova_*` modules are
replaced by the `os_server` modules. Ansible 2 also changed some deperecated
stuff regarding `yaml` parsing and variable concatination. Therefore some of my
old playbooks were not working anymore and I had to figure out how to get it
working with the new versions.
I've written about [Ansible before][1], click that link to get all the articles
about Ansible.
![ansible][2]
Recently I removed all Google Ads from this site due to their invasive tracking, as well as Google Analytics. Please, if you found this content useful, consider a small donation using any of the options below:
I'm developing an open source monitoring app called Leaf Node Monitoring, for windows, linux & android. Go check it out!
Consider sponsoring me on Github. It means the world to me if you show your appreciation and you'll help pay the server costs.
You can also sponsor me by getting a Digital Ocean VPS. With this referral link you'll get $200 credit for 60 days. Spend $25 after your credit expires and I'll get $25!
I [found][4] documentation on the new modules but that just created a single
instance. I need to spawn multiple instances and add those to specific
hostgroups based on variables, so this guide wasn't complete enough for me.
This is my folder structure:
$ tree -I "*.git"
.
|-- ansible.cfg
|-- group_vars
| `-- all.yml
|-- roles
| |-- haproxy
| | |-- handlers
| | | `-- main.yml
| | |-- tasks
| | | `-- main.yml
| | |-- templates
| | | `-- haproxy.cfg.j2
| | `-- vars
| | `-- main.yml
| `-- create_instances
| |-- tasks
| | `-- main.yml
| `-- vars
| `-- main.yml
`-- site.yml
`site.yml` is the main playbook and `create-instances` and `appservers` are the
specific roles. [Create][5] the folder structure if you're starting from zero.
On the local host you need the OpenStack tools and some python modules
installed. The [os_server][6] page lists all the requirements. You also need an
`openstackrc` file with credentials in your environment:
$ cat openstackrc
export OS_AUTH_URL="https://identity.stack.cloudvps.com/v2.0"
export OS_USERNAME="username"
export OS_PASSWORD="password"
export OS_TENANT_ID="UUID"
export OS_TENANT_NAME="UUID"
Source it before running the playbooks:
source openstackrc
### Main playbook
The `site.yml` playbook first runs on `locahost` and creates the OpenStack
instances. It also adds them to the specific hostgroups. Those hostgroups are
only available when you run this playbook.
Then it starts a new `play` (if that is how you call multiple runs) to the first
newly created hostgroup (loadbalancers) and you can add a new `play` to run on
the appservers as well.
---
- name: create instances
hosts: localhost
roles:
- { role: create_instances }
- name: deploy haproxy
hosts: loadbalancers
user: root
roles:
- { role: haproxy }
- { role: keepalived }
### Instance creation playbook
The first playbook, `roles/create-instances/tasks/main.yml` runs on localhost
and creates the OpenStack instances:
---
- name: launch instances
os_server:
name: "{{ prefix }}-{{ item.name }}"
state: present
key_name: "{{ item.key }}"
availability_zone: "{{ item.availability_zone }}"
nics: "{{ item.nics }}"
image: "{{ item.image }}"
flavor: "{{ item.flavor }}"
with_items: "{{ servers }}"
register: "os_hosts"
- name: add hosts to inventory
add_host:
name: "{{ item['openstack']['human_id'] }}"
groups: "{{ item['item']['meta']['group'] }}"
ansible_host: "{{ item.openstack.accessIPv4 }}"
with_items: "{{ os_hosts.results }}"
Based on the name we defined and the group we gave it also is adds the hosts to
a new hostgroup. That hostgroup is only active within this playbook run.
The parameters we used to create the instances are also available in the
`result` of that action. It's a `dict`, so you can access all three the servers
we created. That is were we get the `group` value from.
If you're using a `jumphost` and other machines with private IP's you need to
use a different `ansible_ssh_host`, but you can achieve that by adding extra
data to the variables. The `debug` module is your friend here.
If the instances are already created, it will not create them again but it will
add then to the hostgroup again.
The variables required for this playbook are the following (`roles/create-
instances/vars/main.yml`):
---
prefix: demo
servers:
- name: lb1
image: CloudVPS Ubuntu 16.04
flavor: Standard 2
key: SSH-Key
nics: "net-id=00000000-0000-0000-0000-000000000000"
availability_zone: NL1
meta:
group: loadbalancers
- name: app1
image: CloudVPS Ubuntu 16.04
flavor: Standard 2
key: SSH-Key
nics: "net-id=00000000-0000-0000-0000-000000000000"
availability_zone: NL1
meta:
group: appservers
- name: app2
image: CloudVPS Ubuntu 16.04
flavor: Standard 2
key: SSH-Key
nics: "net-id=00000000-0000-0000-0000-000000000000"
availability_zone: NL2
meta:
group: appservers
The `prefix` is used in the servername and can be used to create different
pseudo-groups in the same tenant/project. I do recommend to create different
projects/tenants per environment (accept/staging etc) instead of prefixes, since
you can then manage the rights more fine grained.
### Role specific playbooks
The role specific playbooks are just regular playbook roles you would run. For
the guide you can use an example play which just does a ping
(`roles/ping/tasks/main.yml`):
---
- name: ping instances
ping:
The full play then results in:
$ ansible-playbook site.yml
[WARNING]: provided hosts list is empty, only localhost is available
PLAY [create instances] ********************************************************
TASK [setup] *******************************************************************
ok: [localhost]
TASK [create_instances : launch instances]
************************************* changed: [localhost] =>
(item={u'name': u'lb1', u'availability_zone': u'NL1', u'nics':
u'net-id=00000000-0000-0000-0000-000000000000', u'image': u'CloudVPS
Ubuntu 16.04', u'meta': {u'group': u'loadbalancers'}, u'key':
u'SSH-Key', u'flavor': u'Standard 2'}) changed: [localhost] =>
(item={u'name': u'app1', u'availability_zone': u'NL1', u'nics':
u'net-id=00000000-0000-0000-0000-000000000000', u'image': u'CloudVPS
Ubuntu 16.04', u'meta': {u'group': u'appservers'}, u'key': u'SSH-Key',
u'flavor': u'Standard 2'}) changed: [localhost] => (item={u'name':
u'app2', u'availability_zone': u'NL1', u'nics':
u'net-id=00000000-0000-0000-0000-000000000000', u'image': u'CloudVPS
Ubuntu 16.04', u'meta': {u'group': u'appservers'}, u'key': u'SSH-Key',
u'flavor': u'Standard 2'})
TASK [create_instances : add hosts to inventory]
******************************* changed: [localhost] =>
(item={u'changed': True, '_ansible_no_log': False,
'_ansible_item_result': True, u'server': [...] # a lot of json
u'volumes': [], u'metadata': {}, u'human_id': u'demo-app2'}, u'id':
u'eff00345-977f-4c72-4684-4aa22d1dfc9f'})
PLAY [ping instances] **********************************************************
TASK [setup] *******************************************************************
ok: [demo-app1]
ok: [demo-app2]
TASK [common : ping instances] *************************************************
ok: [demo-app1]
ok: [demo-app2]
PLAY RECAP *********************************************************************
demo-app1 : ok=2 changed=0 unreachable=0 failed=0
demo-app2 : ok=2 changed=0 unreachable=0 failed=0
localhost : ok=3 changed=2 unreachable=0 failed=0
[1]: https://raymii.org/s/tags/ansible.html
[2]: https://raymii.org/s/inc/img/ansible_meme.jpg
[3]: https://www.digitalocean.com/?refcode=7435ae6b8212
[4]: http://blog.oddbit.com/2015/10/26/ansible-20-new-openstack-modules/
[5]: https://raymii.org/s/snippets/Ansible_-_create_playbooks_and_role_file_and_folder_structure.html
[6]: https://docs.ansible.com/ansible/os_server_module.html
---
License:
All the text on this website is free as in freedom unless stated otherwise.
This means you can use it in any way you want, you can copy it, change it
the way you like and republish it, as long as you release the (modified)
content under the same license to give others the same freedoms you've got
and place my name and a link to this site with the article as source.
This site uses Google Analytics for statistics and Google Adwords for
advertisements. You are tracked and Google knows everything about you.
Use an adblocker like ublock-origin if you don't want it.
All the code on this website is licensed under the GNU GPL v3 license
unless already licensed under a license which does not allows this form
of licensing or if another license is stated on that page / in that software:
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
Just to be clear, the information on this website is for meant for educational
purposes and you use it at your own risk. I do not take responsibility if you
screw something up. Use common sense, do not 'rm -rf /' as root for example.
If you have any questions then do not hesitate to contact me.
See https://raymii.org/s/static/About.html for details.