Ansible - Define Inventory at run time
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
Khan
Updated on June 14, 2022Comments
-
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 over 9 yearsThank You Ramesh, but I need something built into ansible working for me, as I have to pass the playbook(s) on to someone
-
Khan over 9 yearsI 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 over 9 yearsI didn't get it, sorry. What are you trying to achieve?
-
Khan over 9 yearsThe 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 over 9 yearsFirst 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 over 9 yearsAh, I see, you don't want to make loops for hosts, you need dynamic hosts array. Let me think a little.
-
Khan over 9 yearsYes, Perfect. Thanks!
-
Khan over 9 yearshad 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"