Ansible - Define Inventory at run time

12,376

Solution 1

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.

Solution 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

Share:
12,376
Khan
Author by

Khan

Updated on June 14, 2022

Comments

  • Khan
    Khan almost 2 years

    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!

  • Khan
    Khan over 9 years
    Thank You Ramesh, but I need something built into ansible working for me, as I have to pass the playbook(s) on to someone
  • Khan
    Khan over 9 years
    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 ??
  • gli
    gli over 9 years
    I didn't get it, sorry. What are you trying to achieve?
  • Khan
    Khan over 9 years
    The code snippet you posted does not help to contain the inventory according to user input. I have simply posted the results of your code
  • gli
    gli over 9 years
    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.
  • gli
    gli over 9 years
    Ah, I see, you don't want to make loops for hosts, you need dynamic hosts array. Let me think a little.
  • Khan
    Khan over 9 years
    Yes, Perfect. Thanks!
  • Khan
    Khan over 9 years
    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"