Score:1

selected hosts in inventory based on subset of name in ansible

dz flag

I'm fairly new to ansible so maybe this is easy to do.

We have a large number of devices that have a consistent naming convention among different environments.

app0[01:25].dev.domain.com
app[01:25].qa.domain.com
app[01:25].uat.domain.com
app[01:25].prod.domain.com

Various devices inside those app servers have specific roles like web server, app server, api server, etc and it is the same across environment. So app05 is a web server in dev, qa, uat, uat, prod.

Right now I'm using group_vars and assigning specific server roles to the group names. But I would prefer not to have to set up group names for each environment(webdev, webqa, webuat, etc)

Is there a simple solution to this? I think roles would complicate things a bit but maybe that is the solution?

Score:0
br flag

Q: "Set up group names for each environment(webdev, webqa, webuat, etc)"

A: Create the variable dynamically, e.g.

- hosts: all
  gather_facts: false
  vars:
    env:
      app05: web
  tasks:
    - set_fact:
        env_local: "{{ env[_host]|d('none') }}{{ _group }}"
      vars:
        _arr: "{{ inventory_hostname.split('.') }}"
        _host: "{{ _arr.0 }}"
        _group: "{{ _arr.1 }}"
    - debug:
        var: env_local

gives

ok: [app01.dev.domain.com] => 
  env_local: nonedev
ok: [app02.dev.domain.com] => 
  env_local: nonedev
ok: [app03.dev.domain.com] => 
  env_local: nonedev
ok: [app04.dev.domain.com] => 
  env_local: nonedev
ok: [app05.dev.domain.com] => 
  env_local: webdev

Use the module add_host and create the groups dynamically. For example, create the file

shell> cat add_host_to_groups.yml
- add_host:
    name: "{{ i }}"
    groups: "{{ item }}{{ (i.split('.')).1 }}"
  loop: "{{ _hosts }}"
  loop_control:
    loop_var: i

and include it in the loop of the assignment rules

- hosts: all
  gather_facts: false
  vars:
    env:
      web: app05
  tasks:
    - include_tasks: add_host_to_groups.yml
      loop: "{{ env.keys()|list }}"
      vars:
        _hosts: "{{ ansible_play_hosts_all|select('match', env[item] ~ '.*') }}"
      run_once: true
    - debug:
        var: groups
      run_once: true

gives

  groups:
    all:
    - app01.dev.domain.com
    - app02.dev.domain.com
    - app03.dev.domain.com
      ...
    - app25.prod.domain.com
    webdev:
    - app05.dev.domain.com
    webprod:
    - app05.prod.domain.com
    webqa:
    - app05.qa.domain.com
    webuat:
    - app05.uat.domain.com

Use the groups in consecutive plays, e.g.

- hosts: webqa
  gather_facts: false
  tasks:
    - debug:
        var: ansible_play_hosts_all

gives

  ansible_play_hosts_all:
  - app05.qa.domain.com

Fit the dictionary env to your needs.

flyerhawk avatar
dz flag
OK. Gonna need to process this a bit but that looks awesome. I'll come back once I figured out what you are suggesting here. LOL.
Score:0
cn flag

An inventory scheme that establishes a source of truth would be to query these groups from some system. Something more sophisticated than strings in the DNS name.

For example, netbox inventory database system has a device role that could fit. Not to pick on netbox, but it has an Ansible inventory plugin, and an open database model so is easy to talk about.


However, maybe a nice external database does not exist yet. Such a regular naming scheme can be generated. With that most recursive of Ansible inventory plugins, generator:

# inventory.yml
plugin: generator
hosts:
    name: "{{ application }}{{ number }}.{{ environment }}.example.com"
    parents:
      - name: "{{ application }}_{{ environment }}"
        parents:
          - name: "{{ application }}"
            vars:
              application: "{{ application }}"
          - name: "{{ environment }}"
            vars:
              environment: "{{ environment }}"
layers:
    application:
        - app
        - api
    environment:
        - dev
        - qa
        - uat
        - prod
    number:
        - "01"
        - "02"
        - "05"

Layer names are arbitrary. Given the "hosts" root and the "parents" notation, deeper indented names are groups that contain the outer names.

ansible-inventory -i inventory.yml --list will print hosts in Ansible's inventory JSON document. Partial output:

{
    "_meta": {
        "hostvars": {

           "app05.qa.example.com": {
                "application": "app",
                "environment": "qa"
            }
        }
    },

    "app_qa": {
        "hosts": [
            "app01.qa.example.com",
            "app02.qa.example.com",
            "app05.qa.example.com"
        ]
    },

    "app": {
        "children": [
            "app_dev",
            "app_prod",
            "app_qa",
            "app_uat"
        ]
    },

    "qa": {
        "children": [
            "api_qa",
            "app_qa"
        ]
    },


}

And continuing on for other combinations.

Notice it made:

  • "application" groups
  • "environment" groups
  • "application environment" combination groups
  • host names conforming to the DNS names pattern
  • vars containing the "application" and "environment" for each host

Limitations of this plugin include:

Always doing the cartesian product of the layer combinations. Cannot have more or less of a certain group, nor start the numbering schemes on different values.

Not having compact host ranges. Neither [01:25] syntax nor the range() function works. Consider requesting that by filing an issue. As a workaround, dozens of numbers in the config file is functional.

mangohost

Post an answer

Most people don’t grasp that asking a lot of questions unlocks learning and improves interpersonal bonding. In Alison’s studies, for example, though people could accurately recall how many questions had been asked in their conversations, they didn’t intuit the link between questions and liking. Across four studies, in which participants were engaged in conversations themselves or read transcripts of others’ conversations, people tended not to realize that question asking would influence—or had influenced—the level of amity between the conversationalists.