Ansible - Define Inventory at run time
Asked Answered
M

2

6

I am liitle new to ansible so bear with me if my questions are a bit basic.

Scenario:

I have a few group of Remote hosts such as [EPCs] [Clients] and [Testers] I am able to configure them just the way I want them to be.

Problem:

I need to write a playbook, which when runs, asks the user for the inventory at run time. As an example when a playbook is run the user should be prompted in the following way: "Enter the number of EPCs you want to configure" "Enter the number of clients you want to configure" "Enter the number of testers you want to configure"

What should happen:

Now for instance the user enters 2,5 and 8 respectively. Now the playbook should only address the first 2 nodes in the group [EPCs], the first 5 nodes in group [Clients] and the first 7 nodes in the group [Testers] . I don't want to create a large number of sub-groups, for instance if I have 20 EPCs, then I don't want to define 20 groups for my EPCs, I want somewhat of a dynamic inventory, which should automatically configure the machines according to the user input at run time using the vars_prompt option or something similar to that

Let me post a partial part of my playbook for better understanding of what is to happen:

---
- hosts: epcs # Now this is the part where I need a lot of flexibility

  vars_prompt:
    name: "what is your name?"
    quest: "what is your quest?"

  gather_facts: no

  tasks:

  - name: Check if path exists
    stat: path=/home/khan/Desktop/tobefetched/file1.txt
    register: st

  - name: It exists
    debug: msg='Path existence verified!'
    when: st.stat.exists

   - name: It doesn't exist
     debug: msg="Path does not exist"
     when: st.stat.exists == false

   - name: Copy file2 if it exists
     fetch: src=/home/khan/Desktop/tobefetched/file2.txt dest=/home/khan/Desktop/fetched/   flat=yes
     when: st.stat.exists

   - name: Run remotescript.sh and save the output of script to output.txt on the Desktop
     shell: cd /home/imran/Desktop; ./remotescript.sh > output.txt

   - name: Find and replace a word in a file placed on the remote node using variables
     shell: cd /home/imran/Desktop/tobefetched; sed -i 's/{{name}}/{{quest}}/g' file1.txt

    tags:
       - replace

@gli I tried your solution, I have a group in my inventory named test with two nodes in it. When I enter 0..1 I get:

TASK: [echo sequence] ********************************************************* 
changed: [vm2] => (item=some_prefix0)
changed: [vm1] => (item=some_prefix0)
changed: [vm1] => (item=some_prefix1)
changed: [vm2] => (item=some_prefix1)

Similarly when I enter 1..2 I get:

TASK: [echo sequence] ********************************************************* 
changed: [vm2] => (item=some_prefix1)
changed: [vm1] => (item=some_prefix1)
changed: [vm2] => (item=some_prefix2)
changed: [vm1] => (item=some_prefix2)

Likewise when I enter 4..5 (nodes not even present in the inventory, I get:

TASK: [echo sequence] ********************************************************* 
changed: [vm1] => (item=some_prefix4)
changed: [vm2] => (item=some_prefix4)
changed: [vm1] => (item=some_prefix5)
changed: [vm2] => (item=some_prefix5)

Any help would be really appreciated. Thanks!

Maeganmaelstrom answered 15/8, 2014 at 12:27 Comment(0)
A
16

You should use vars_prompt for getting information from user, add_host for updating hosts dynamically and with_sequence for loops:

$ cat aaa.yml
---
- hosts: localhost
  gather_facts: False
  vars_prompt:
    - name: range
      prompt: Enter range of EPCs (e.g. 1..5)
      private: False
      default: "1"
  pre_tasks:
    - name: Set node id variables
      set_fact:
        start: "{{ range.split('..')[0] }}"
        stop: "{{ range.split('..')[-1] }}"
    - name: "Add hosts:"
      add_host: name="host_{{item}}" groups=just_created
      with_sequence: "start={{start}} end={{stop}} "

- hosts: just_created
  gather_facts: False
  tasks:
    - name: echo sequence
      shell: echo "cmd"

The output will be:

$ ansible-playbook aaa.yml -i 'localhost,'

Enter range of EPCs (e.g. 1..5) [1]: 0..1

PLAY [localhost] **************************************************************

TASK: [Set node id variables] *************************************************
ok: [localhost]

TASK: [Add hosts:] ************************************************************
ok: [localhost] => (item=0)
ok: [localhost] => (item=1)

PLAY [just_created] ***********************************************************

TASK: [echo sequence] *********************************************************
fatal: [host_0] => SSH encountered an unknown error during the connection. We recommend you re-run the command using -vvvv, which will enable SSH debugging output to help diagnose the issue
fatal: [host_1] => SSH encountered an unknown error during the connection. We recommend you re-run the command using -vvvv, which will enable SSH debugging output to help diagnose the issue

FATAL: all hosts have already failed -- aborting

PLAY RECAP ********************************************************************
           to retry, use: --limit @/Users/gli/aaa.retry

host_0                     : ok=0    changed=0    unreachable=1    failed=0
host_1                     : ok=0    changed=0    unreachable=1    failed=0
localhost                  : ok=2    changed=0    unreachable=0    failed=0

Here, it failed as host_0 and host_1 are unreachable, for you it'll work fine.

btw, I used more powerful concept "range of nodes". If you don't need it, it is quite simple to have "start=0" and ask only for "stop" value in the prompt.

Accordion answered 17/8, 2014 at 5:43 Comment(6)
I found something very interesting. If I call my playbook.yml inside a mainplaybook.yml and pass on the target parameter then can I specify something like the following in my playbook.yml -hosts: localhost[{{target}}] and then increment target until I reach the users specified value. Anything like that possible ??Maeganmaelstrom
I didn't get it, sorry. What are you trying to achieve?Accordion
The code snippet you posted does not help to contain the inventory according to user input. I have simply posted the results of your codeMaeganmaelstrom
First of all you shouldn't call playbook from playbook, you should use roles instead. And inside the role task you can use loops. The same way as I used. So you can split one my answer into two files, one for main playbook, second (with loop) for role.Accordion
Ah, I see, you don't want to make loops for hosts, you need dynamic hosts array. Let me think a little.Accordion
had to make a a slight change'code' --- - hosts: localhost gather_facts: False vars_prompt: - name: range prompt: Enter range of EPCs (e.g. 1..5) private: False default: "1" pre_tasks: - name: Set node id variables set_fact: start: "{{ range.split('..')[0] }}" stop: "{{ range.split('..')[-1] }}" - name: "Add hosts:" add_host: name="vm{{item|int +1}}" groups=just_created with_sequence: "start={{start}} end={{stop}} " - hosts: just_created gather_facts: False tasks: - name: echo sequence shell: echo "cmd"Maeganmaelstrom
H
2

I don't think you can define an inventory at run time. One thing you can do is, write a wrapper script over Ansible which will first prompt user for the hosts and then dynamically structure an ansible-playbook command.

I would prefer doing this using python, but you can use any language of your choice.

$ cat ansible_wrapper.py 
import ConfigParser
import os

nodes = ''
inv = {}

hosts =  raw_input("Enter hosts: ")
hosts = hosts.split(",")

config = ConfigParser.ConfigParser(allow_no_value=True)
config.readfp(open('hosts'))
sections = config.sections()

for i in range(len(sections)):
    inv[sections[i]] = hosts[i]


for key, value in inv.items():
    for i in range(int(value)):
        nodes = nodes + config.items(key)[i][0] + ";"


command = 'ansible-playbook -i hosts myplaybook.yml -e "nodes=%s"' % (nodes)
print "Running command: ", command
os.system(command)

Note: I've tried running this script only using python2.7

Hendiadys answered 16/8, 2014 at 12:6 Comment(1)
Thank You Ramesh, but I need something built into ansible working for me, as I have to pass the playbook(s) on to someoneMaeganmaelstrom

© 2022 - 2024 — McMap. All rights reserved.