In Ansible, how to combine variables from separate

2019-02-22 03:02发布

问题:

In Ansible, in a role, I have vars files like this:

vars/
    app1.yml
    app2.yml

Each file contains vars specific to an app/website like this:

name: app1
git_repo: https://github.com/philgyford/app1.git
# ...

Ideally, without the task knowing in advance which apps have variable files, I'd like to end up with an array called apps like this:

apps:
  - name: app1
    git_repo: https://github.com/philgyford/app1.git
    # ...
  - name: app2
    git_repo: https://github.com/philgyford/app2.git
    # ...

ie, that combines the variables from the files into one.

I know I can load all the variable files like this:

- name: Load var files
  with_fileglob:
    - ../vars/*.yml
  include_vars: '{{ item }}'

But given each file has identical variable names, it will overwrite each previous set of variables. I can't see a way to load the variables and put them into an apps array.

I'm open to rearranging things slightly if it's the only way to make something like this possible.

回答1:

You can not do that. Variables will always override variables with the same name. The only thing you could do with this exact setup is to write your own vars plugin which reads those files and merges them into an array.

If you are open to change the structure of your apps definition you can use a hash and set your hash_behavior=merge. In each vars file then you'd have a definition like:

apps:
  app1:
    git_repo: https://github.com/philgyford/app1.git

apps:
  app2:
    git_repo: https://github.com/philgyford/app2.git

When Ansible loads both files it will merge it automatically together into:

apps:
  app1:
    git_repo: https://github.com/philgyford/app1.git
  app2:
    git_repo: https://github.com/philgyford/app2.git</pre>

But be advised that hash_behavior=merge fundamentally changes the default behavior of Ansible on a global level. Make sure all your roles do not have issues with this setting. The documentation mentions:

We generally recommend not using this setting unless you think you have an absolute need for it

If you still use Ansible 1 you could use one of my old plugins instead: include_vars_merged. Basically this adds the behavior of hash_behavior=merge to only a single task.

I have not yet looked into migrating this to Ansible 2 though and currently it looks like I won't have the need for it any longer.



回答2:

Starting with Ansible v2.0 you can do it:

- name: merging hash_a and hash_b into hash_c
  set_fact: hash_c="{{ hash_a|combine(hash_b) }}"

Check more under Ansible filters - Combining hashes/dictionaries (coming from Jinja2)



回答3:

Well, you cannot directly build an array, but you can achieve the same effort with a dict.

Suppose you want to construct an array:

[{
    name: 'bob',
    age: 30
}, {
    name: 'alice',
    age: 35 
}]

You can put each element in a file like:

bob.yml

bob:
  name: bob
  age: 30

alice.yml

alice:
  name: alice
  age: 35

Place these files in the same dir (e.g. user), then use include_vars to load the whole dir:

- name: Include vars
  include_vars:
    name: users
    dir: user

This will give you a dict users:

users:
  alice:
    name: alice
    age: 35
  bob:
    name: bob
    age: 30

User the dict2items filter in ansible, you get the array you want



回答4:

Wanted to post a possible alternate solution by getting a list of variables that match a pattern, then you can just process all those variables, sorta manual merge.

The following code block gives an example of pulling all variables that match a specific pattern and looping. you could set a new fact with them merged, or simply process them all individually.

- name: "debug2"   debug:
    msg: "value is: {{ lookup('vars', item) }} "   
  loop: "{{ hostvars[inventory_hostname] | select('match', '^linux_hosts_entries') |list  }}"

see the following post for further details.



回答5:

Since Ansible 2.2, the include_vars (link) module has been expanded quite a bit.

It's now possible to do something like:

- include_vars:
    name: 'apps'
    dir: '../vars'
    extensions:
      - 'yaml'
      - 'yml'

name is the key there. From the module page:

The name of a variable into which assign the included vars. If omitted (null) they will be made top level vars.

This allows you to convert:

vars/
    app1.yml
    app2.yml
    ...

app1.yml:

name: app1
git_repo: https://github.com/philgyford/app1.git
# ...

app2.yml:

name: app2
git_repo: https://github.com/philgyford/app2.git
# ...

Into...

apps:
  - name: app1
    git_repo: https://github.com/philgyford/app1.git
    # ...
  - name: app2
    git_repo: https://github.com/philgyford/app2.git
    # ...


标签: ansible