Ansible exit expect from virsh console - ansible

Code:
- expect:
command: virsh console myguest
responses:
'Escape character is': ''
'root>': 'show interfaces em0 | grep Current'
timeout: 5
register: result
This task will hang forever. I believe it was because of after finishing the show interface command, the system still get back to the 'root>' prompt for next command, and because of my 'root>' response provided, the prompt-response got into a dead loop.
I know ideally I could provided 2 responses as list to 'root>', one would be the show command, the other would be Ctrl-]. But there is no text expression of Ctrl-], I cannot really provide it.
So I set the timeout, hoped it would just loop up to 5 seconds. But that's not the case.
I set the single response as list item, but the task error out for next response.
So what I can do to run the show command in virsh console, and then get out of it, so my playbook can continue?

You can use \u001d to represent ^] (CTRL-]) and allow the expect call to either complete successfully, or timeout and fail.
Also, you can add ignore_errors: true to allow you to process the error rather than have Ansible fail immediately. For example:
- expect:
command: "virsh console {{ guest }}"
responses:
'Escape character is': ''
'root>':
- 'show interfaces em0 | grep Current'
- "\u001d"
register: expect_response
ignore_errors: true
- debug:
msg: "Expect failed with rc={{ expect_response.rc }}"
when: expect_response.failed

Related

How to set a Ansible extra variable to answer a pause prompt in a playbook

We use an Ansible playbook for running a few commands like version_get, device_status on the target machine. Intentionally we have restricted reboot option from being executed. But occasionally, we would like to automatically answer the yes prompt by setting some variables in the --extra-vars option.
A simple representation of our playbook minimized to run on localhost.
---
- hosts: localhost
gather_facts: no
tasks:
- name: Confirm Execution
pause:
prompt: "You are about to execute a '{{cmd}}' command on the device. Enter 'yes' to proceed"
register: pause_result
run_once: True
when: not (cmd|regex_search('(get|status)'))
- meta: end_play
when: pause_result.user_input|default('yes') != 'yes'
I know I can add reboot as part of the existing get|status list, but I don't want to do that, because I want the users exercise special precaution when running it. So with the current code as above, if I run reboot I'm left with a prompt like
$ ansible-playbook loc.yml -e 'cmd=reboot'
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'
PLAY [localhost] ***********************************************************************************************************************************************************
TASK [Confirm Execution] ***************************************************************************************************************************************************
[Confirm Execution]
You are about to execute a 'reboot' command on this device. Enter 'yes' to proceed:
I just know how to set a variable to automatically answer this prompt. Tried passing echo yes | to the playbook and seeing an error as
$ echo yes | ansible-playbook loc.yml -e 'cmd=reboot'
[WARNING]: Not waiting for response to prompt as stdin is not interactive
I also tried to pass the --extra-vars as below but none of them seemed to work
-e 'cmd=reboot {"pause_result": "yes"}'
-e 'cmd=reboot pause_result=yes'
I would simply use an other var and condition the prompt to this one. Some lines of code being more expressive than a long speech:
The playbook
---
- hosts: localhost
gather_facts: no
tasks:
- name: Confirm Execution
pause:
prompt: "You are about to execute a '{{cmd}}' command on the device. Enter 'yes' to proceed"
register: pause_result
run_once: True
when:
- not (cmd | regex_search('(get|status)'))
- not (skip_confirm | default(false) | bool)
- meta: end_play
when: pause_result.user_input | default('yes') != 'yes'
- name: dummy task to see if end of play was effective
debug:
msg: "In real world, I would play {{ cmd }}"
Example calls:
$ ansible-playbook test.yml -e cmd=reboot
$ ansible-playbook test.yml -e cmd=reboot -e skip_confirm=true
If you do not want to introduce a new var, you can use the existing one but that will still require a modification of your current playbook.
The when clause of your prompt should become:
when:
- not (cmd | regex_search('(get|status)'))
- pause_result is not defined
And the call:
$ ansible-playbook test.yml -e cmd=reboot -e "pause_result={user_input: yes}"

How to find a string in ansible "retries and until" block?

I am installing some plugins and then checking the status in a command loop. I want to check the result of the status of the command and if the plugins are not installed I want to install it again with the help of retry module.
- name: install plugins
command: "run {{ item }}"
with_items:
- install plugins
- status
register: result
until: result.stdout.find("InstallPlugin1 and InstallPlugin2") != -1
retries: 5
delay: 10
I am using register to save the result and I know register saves the result in results and in this case it will save the result in "results" dict. Now I want to check a string in result of status command in until, which should be the 2nd value of results dictionary but I am not able to grab it.
when I use
debug: msg="{{ result['results'][1]['stdout'] }}"
I can see the output of the status command but I dont know how to use this in until module. whenever I use results there it gives an error. I want to use something like
until: result['results'][1]['stdout'].find("all systems go") != -1
If both run install plugins and run status return something like
installed: InstallPlugin1, InstallPlugin2
the task below will do the job
- name: install plugins
command: "run {{ item }}"
loop:
- install plugins
- status
register: result
until:
- result.stdout is search('InstallPlugin1')
- result.stdout is search('InstallPlugin2')
retries: 5
delay: 10
It's not possible to use the loop if only run status returns the confirmation, because the until statement is evaluated in each iteration. An option would be to concatenate the commands. For example
- name: install plugins
command: "run install plugins; run status"
register: result
until:
- result.stdout is search('InstallPlugin1')
- result.stdout is search('InstallPlugin2')
retries: 5
delay: 10
It's possible to test the registered result in each loop. After the loop is done the variable result will keep accumulated result.results. It might be worth to review it.
- debug:
var: result
I think this is what you're looking for:
until: "all systems go" in item['stdout']
The register statement you have there will be a list of the aggregate results from all irritations in the with_items loop and what you want to conditional on is the item itself. Depending on what what you're doing, you might not even need to register that variable.

Ansible register fails when there is no output from shell command

I am trying to check if a service is running then register its output to some variable if its not running then start the service. Below is my Ansible playbook snippet.
- hosts: localhost
tasks:
- name: check if service is running
shell: pgrep node
register: pgrep
- name: stop running service
shell: pkill node
when: pgrep.stdout_lines != ''
tags:
- stop
- name: start running service
shell: pkill node
when: pgrep.stdout_lines == ''
tags:
- start
Now in the above case if the process is not running then the pgrep node command returns exit status code as 1 which fails the "check if service is running" tasks and aborts further execution of tasks. I know by setting ignore_errors: true will ignore the error and proceed ahead, but it fails the Ansible runs. Is there a way where we can handle this gracefully?
You can control what defines failure and set the condition to fail when the return code from pgrep is 2 or 3.
Per man pgrep:
The pgrep and pkill utilities return one of the following values upon exit:
0 One or more processes were matched.
1 No processes were matched.
2 Invalid options were specified on the command line.
3 An internal error occurred.
So the Ansible task should look like:
- name: check if service is running
shell: pgrep node
register: pgrep
failed_when: "pgrep.rc == 2 or pgrep.rc == 3"
Adding answer for future reference. How to kill multiple processes, should they exist. Only indicate a change if rc==0. Don't show failed unless it's rc==2 or rc==3.
- name: "Kill any processes"
become: True
vars:
processes_to_kill: ["p1", "p2", "p3", "p4"]
shell: "pkill -f {{ item }}"
with_items: "{{ processes_to_kill }}"
register: pkill
failed_when: "pkill.rc == 2 or pkill.rc == 3"
changed_when: "pkill.rc == 0"

conditional execution of playbook based on exit status of the command module?

I am trying to write a playbook which would execute some tasks only if a certain package is installed on the hosts.
Is it possible to register the output from a command module and run the tasks depending upon the exit status of the command ?
Something like this:
You are on the right path. If httpd doesnt exist, the playbook execution will fail. You can use ignore_errors to continue execution and then run subsequent tasks based on the return code of httpd_result. I have given an example below:
- hosts: localhost
tasks:
- command: "which httpd"
register: httpd_result
ignore_errors: true
- debug: msg="found http"
when: httpd_result.rc == 0
- debug: msg="not found httpd"
when: httpd_result.rc!=0
Here, instead of debug statements, you can put whatever conditional tasks you need to run. Hope this helps.

Ansible - Check if string exists in file

I'm very new to Ansible
Is it possible to check if a string exists in a file using Ansible.
I want to check is a user has access to a server.
this can be done on the server using cat /etc/passwd | grep username
but I want Ansible to stop if the user is not there.
I have tried to use the lineinfile but can't seem to get it to return.
code
- name: find
lineinfile: dest=/etc/passwd
regexp=[user]
state=present
line="user"
The code above adds user to the file if he is not there. All i want to do is check. I don't want to modify the file in any way, is this possible
Thanks.
It's a tricky one. the lineinfile module is specifically intended for modifying the content of a file, but you can use it for a validation check as well.
- name: find
lineinfile:
dest: /etc/passwd
line: "user"
check_mode: yes
register: presence
failed_when: presence.changed
check_mode ensures it never updates the file.
register saves the variable as noted.
failed_when allows you to set the failure condition i.e. by adding the user because it was not found in the file.
There are multiple iterations of this that you can use based on what you want the behavior to be. lineinfile docs particular related to state and regexp should allow you to determine whether or not presence or absence is failure etc, or you can do the not presence.changed etc.
I'd probably register and evaluate a variable.
The following simple playbook works for me:
- hosts: localhost
tasks:
- name: read the passwd file
shell: cat /etc/passwd
register: user_accts
- name: a task that only happens if the user exists
when: user_accts.stdout.find('hillsy') != -1
debug: msg="user hillsy exists"
If you want to fail if there is no user:
tasks:
- shell: grep username /etc/passwd
changed_when: false
By default shell module will fail if command exit code is non zero.
So it will give you ok if username is there and fails otherwise.
I use changed_when: false to prevent changed state when grepping.
I am using the following approach, using only a grep -q and a registered variable.
Upside is that it's simple, downside is that you have a FAILED line in your output. YMMV.
- name: Check whether foobar is defined in /bar/baz
command:
cmd: 'grep -q foobar /bar/baz'
register: foobar_in_barbaz
changed_when: false
ignore_errors: true
- when: not foobar_in_barbaz.failed
name: Do something when foobar is in /bar/baz
....
- when: foobar_in_barbaz.failed
pause:
seconds: 1
content: |
You do not seem to have a foobar line in /bar/baz
If you add it, then magic stuff will happen!

Resources