Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Puppet hiera equivalent in Ansible

hiera.yaml

---
:hierarchy:
- node/%{host_fqdn}
  - site_config/%{host_site_name}
  - site_config/perf_%{host_performance_class}
  - site_config/%{host_type}_v%{host_type_version}
  - site/%{host_site_name}
  - environments/%{site_environment}
  - types/%{host_type}_v%{host_type_version}
  - hosts
  - sites
  - users
  - common
# options are native, deep, deeper
:merge_behavior: deeper

We currently have this hiera config. So the config gets merged in the following sequence common.yaml > users.yaml > sites.yaml > hosts.yaml > types/xxx_vxxx.yaml > etc. For the variable top hierarchies, it gets overwritten only if that file exists.

eg: common.yaml

server:
  instance_type: m3.medium

site_config/mysite.yaml

server:
  instance_type: m4.large

So for all other sites, the instance type will be m3.medium, but only for mysite it will be m4.large.

How can I achieve the same in Ansible?

like image 406
MavWolverine Avatar asked Sep 13 '16 15:09

MavWolverine


People also ask

What is puppet hiera?

Hiera is a built-in key-value configuration data lookup system, used for separating data from Puppet code. About Hiera. Puppet's strength is in reusable code. Code that serves many needs must be configurable: put site-specific information in external configuration data files, rather than in the code itself.


2 Answers

I'm not familiar with Puppet, so this may not be a direct mapping. But what I understand your question to be is "how do I use values in one shared location but override their definitions for different servers?". In Ansible, you do this with variables.

You can define variables directly in your inventory. You can define variables in host- and group-specific files. You can define variables at a playbook level. You can define variables at a role level. Heck, you can even define variables with command-line switches.

Between all of these places, you should be able to define overrides to suit your situation. You'll probably want to take a look at the documentation section on how to decide where to define a variable for more info.

like image 76
Xiong Chiamiov Avatar answered Sep 17 '22 05:09

Xiong Chiamiov


I think that @Xiong is right that you should go the variables way in Ansible.
You can set up flexible inventory with vars precedence from general to specific.

But you can try this snippet if it helps:

---
- hosts: loc-test
  tasks:
    - include_vars: hiera/{{ item }}
      with_items:
        - common.yml
        - "node/{{ ansible_fqdn }}/users.yml"
        - "node/{{ ansible_fqdn }}/sites.yml"
        - "node/{{ ansible_fqdn }}/types/{{ host_type }}_v{{ host_type_version }}.yml"
      failed_when: false

    - debug: var=server

This will try to load variables from files with structure similar to your question.
Nonexistent files are ignored (because of failed_when: false).
Files are loaded in order of this list (from top to bottom), overwriting previous values.

Gotchas:

  • all variables that you use in the list must be defined (e.g. host_type in this example can't be defined in common.yml), because list of items to iterate is templated before the whole loop is executed (see update for workaround).

  • Ansible overwrite(replace) dicts by default, I guess your use case expects merging behavior. This can be achieved with hash_behavior setting – but this is unusual for Ansible playbooks.

P.S. You may alter top-to-bottom-merge behavior by changing with_items to with_first_found and reverse the list (from specific to general). In this case Ansible will load variables from first file found.

Update: use variables from previous includes in file path.

You can split the loop into multiple tasks, so Ansible will evaluate each task's result before templating next file's include path.
Make hiera_inc.yml:

- include_vars: hiera/common.yml
  failed_when: false
- include_vars: hiera/node/{{ ansible_fqdn }}/users.yml
  failed_when: false
- include_vars: hiera/node/{{ ansible_fqdn }}/sites.yml
  failed_when: false
- include_vars: hiera/node/{{ ansible_fqdn }}/types/{{ host_type | default('none') }}_v{{ host_type_version | default('none') }}.yml
  failed_when: false

And in your main playbook:

- include: hiera_inc.yml

This looks a bit clumsy, but this way you can define host_type in common.yaml and it will be honored in the path templating for next tasks.

With Ansible 2.2 it will be possible to include_vars into named variable (not global host space), so you can include_vars into hiera_facts and use combine filter to merge them without altering global hash behavior.

like image 33
Konstantin Suvorov Avatar answered Sep 18 '22 05:09

Konstantin Suvorov