Before and After Action Conditionals in Ansible - ansible

Say I wanted to perform a conditional action; to
check if a glibc library is updated, if so, skip the below steps
stop mongodb
update glibc
restart linux
So far I can do the following:
- name: install updated glibc
yum: name=glibc-2.12-1.166.el6_8.0.x86_64 state=present
sudo: yes
notify:
- stop mongodb
- restart vm
I have handlers to perform the subsequent actions, but I want to stop mongodb before I update glibc. As you can see, I am not able to perform the desired order.
I was looking through the ansible documentation and the stack overflow boards, but I could not find what I was looking for on how to accomplish this. Is this able to be accomplished in ansible? Should ansible be used for single updates such as these?
Thank you!

The only option here is to previously compare the versions. So you need to manually check which version is installed and which version should be installed and if it differs, do your things. Here's an example of how I did this once for git, quickly re-written for glibc. It might need some more work on it but should give an idea how to do it.
---
- name: Detect latest available glibc version
shell: "`which yum` info glibc | grep Version | rev | cut -d' ' -f1 | rev"
changed_when: false
register: glibc_install_latest_version
- name: Detect currenty installed glibc version
shell: "ldd --version | head -n 1 | cut -d' ' -f4"
changed_when: false
register: glibc_install_current_version
- name: Stop mongo!
...
when: glibc_install_latest_version.stdout != glibc_install_current_version.stdout
- name: "Install latest version of glibc"
yum:
name: glibc
state: latest
update_cache: yes
become: yes
when: glibc_install_latest_version.stdout != glibc_install_current_version.stdout
...
The reboot still can be triggered by your handler.

Related

Testing Perl on Windows with github actions

I've released MooseX::Extended to the CPAN (github repository here).
I'm trying to set up github actions and the linux tests run just fine. However, (Windows is failing with this error:
Configuring true-v1.0.2 ... OK
==> Found dependencies: Function::Parameters
--> Working on Function::Parameters
Fetching http://www.cpan.org/authors/id/M/MA/MAUKE/Function-Parameters-2.001003.tar.gz ... OK
Configuring Function-Parameters-2.001003 ... OK
Building Function-Parameters-2.001003 ... OK
Successfully installed Function-Parameters-2.001003
! Installing true failed. See C:\Users\RUNNER~1\.cpanm\work\1653412748.5640\build.log for details. Retry with --force to force install it.
Building true-v1.0.2 ... FAIL
Of course, I can't see that C:\Users\RUNNER~1\.cpanm\work\1653412748.5640\build.log to understand what happened.
The true module passes its CPAN testers tests on Windows, so I don't know why it's failing in Github Actions.
My workflow looks like this:
# Hacked from https://github.com/skaji/perl-github-actions-sample/blob/master/.github/workflows/windows.yml
# See also: https://perlmaven.com/github-actions-running-on-3-operating-systems
name: windows
on:
push:
branches:
- '*'
tags-ignore:
- '*'
pull_request:
jobs:
perl:
runs-on: windows-latest
strategy:
fail-fast: false
matrix:
perl-version:
- '5.20'
- '5.22'
- '5.24'
- '5.26'
- '5.28'
- '5.30'
- '5.32'
- '5.34'
- 'latest'
steps:
- uses: actions/checkout#v2
- name: Set up Perl
run: |
choco install strawberryperl
echo "C:\strawberry\c\bin;C:\strawberry\perl\site\bin;C:\strawberry\perl\bin" >> $GITHUB_PATH
- name: perl -V
run: perl -V
- name: Install Dependencies
run: curl -sL https://git.io/cpm | perl - install -g --show-build-log-on-failure Dist::Zilla
- name: Run Tests
run: |
dzil authordeps --missing | cpanm --notest
dzil listdeps --author --missing | cpanm --notest
dzil test --author --release
This is the PR to which the actions are attached.
I don't have access to a Windows box. Does anyone know what I missed?
Since GitHub Actions/Workflows uses a container for Windows that already has a version of Strawberry Perl pre-installed, it will not allow you to install any other version. You cannot remove the version of Perl that's pre-installed, and removing/installing a new one via Chocolatey is also next to impossible. If you re-install the version from Chocolatey that's already on the container, it seems to allow this, but it's basically a NOOP for you as a test setup.
The container also has MinGW installed; this can be bad for us as well. Having MinGW installed separately prevents XS modules from building (whether they be a dependency or if your own module is an XS module). Granted, this only happens if MinGW appears in the PATH ahead of your Perl install, but when you remove one Perl and add another, you're going to hit this problem.
To get around this, the best course of action is to remove the currently installed version of Perl from the PATH environment variable, along with their currently installed version of MinGW. Once both are safely out of the PATH, you can install a Portable[1] Strawberry Perl, put that Perl's paths in your PATH and begin testing with a fresh install of Strawberry Perl. GitHub recently broke our ability to do this directly in an Action YAML file.
That all sounds like a big headache, but it's really not. There's an Action available to us for this very purpose: actions-setup-perl. With this action you can easily test using any version of Perl you like. So, if you're hearing someone report a bug on Perl v5.26 on Windows, you can now add that to your matrix and test easily without the need for any back-and-forth from the user:
name: windows
on:
push:
branches:
- '*'
tags-ignore:
- '*'
pull_request:
jobs:
perl:
runs-on: windows-latest
strategy:
fail-fast: true
matrix:
perl-version:
- '5.30'
# - '5.28'
# - '5.26'
# - '5.24'
# - '5.22'
# - '5.20'
# - '5.18'
# - '5.16'
- '5.14'
steps:
- name: Setup perl
uses: shogo82148/actions-setup-perl#v1
with:
perl-version: ${{ matrix.perl-version }}
distribution: strawberry
- name: Set git to use LF
run: |
git config --global core.autocrlf false
git config --global core.eol lf
- uses: actions/checkout#v2
- name: perl -V
run: perl -V
- name: Ensure we have a working toolchain
run: cpanm ExtUtils::Manifest App::cpanminus
- name: Install Dependencies
run: cpanm -n --installdeps .
- name: Run Tests
run: cpanm --test-only -v .
[1] Portable versions of Strawberry Perl are zipped up, already compiled versions of Perl that do not require you to run an installer on Windows. This means that no heightened privileges are required, etc. You just unzip the archive in the directory you want to run Perl from, then add the relevant paths to Perl in your $env:PATH variable. It takes away any annoyances of build irregularities, etc. I've found it to be the most sane way to test on Windows.

How 'not to run multiple times' command/shell task in Ansible playbook

I have a task in Ansible to install SASS utilities via RubyGems option, which works well without any issues. If I re-run the same playbook again, it will try to re-install this utility again.
In this case, how to run it once that are using command or shell usage in Ansible playbook. I have somehow handled it not to run the installation by using 'when' option, but need a guidance for better logic/implementation
Task Info:
- name: Install SASS packages by using npm utility.
command: /usr/bin/npm install -g sass
To eliminate re-running the above command task in Ansible playbook, I am using below validation logic and added 'when' option as provided below. Is this fine or do we have a better way of handling this one?
- name: Validation of SASS packages availability.
shell: /usr/local/bin/sass --version
register: result
- debug:
msg: "{{ result.stdout }}"
- name: Install SASS packages by using npm utility.
command: /usr/bin/npm install -g sass
when: "'No such file or directory' in result.stdout"
Results:
TASK [mean-stack : Validation of SASS packages availability.] ************************************************************************************
changed: [linuxosdev003.local.lab]
TASK [mean-stack : debug] ************************************************************************************************************************
ok: [linuxosdev003.local.lab] => {
"msg": "1.47.0 compiled with dart2js 2.15.1"
}
TASK [mean-stack : Install SASS packages by using npm utility.] **********************************************************************************
skipping: [linuxosdev003.local.lab]
PLAY RECAP ***************************************************************************************************************************************
linuxosdev003.local.lab : ok=6 changed=1 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0
You should avoid command or shell whenever possible, if you want an idempotent playbook. Especially your example - remove sass and try your playbook. It should fail. Either check if the file exists
- name: "Check, if file exists"
stat:
path: "/usr/local/bin/sass"
register: sass_installed
But - as already commented - use an ansible module to install NPM packages, like you would do with system packages (package or yum or apt) or Python modules (with pip). The module is also available in the last RedHat releease 2.9 under https://docs.ansible.com/ansible/2.9/modules/npm_module.html and should be as easy:
- name: "Install NPM package: sass"
npm:
name: "sass"
If you need to install more then one NPM package, you could loop over them
- name: "Install NPM packages"
npm:
name: "{{ item }}"
loop:
- "package1"
- "package2"
As #β.εηοιτ.βε and #TRW already mentioned, using a module will eliminate the need for such checks.
However, if it is as simple as checking whether a path exists, then command module itself can be used like:
- name: Install SASS packages by using npm utility.
command:
cmd: /usr/bin/npm install -g sass
creates: /usr/local/bin/sass
This is equal to your when condition, i.e. command will run when the path given in creates is not present.
Whereby I prefer the solution from #β.εηοιτ.βε and #TRW if package managers are available for the tasks, sometimes it might be necessary to stay on the shell module. For such cases I use the similar approach like in the original question.
---
- hosts: test.example.com
become: no
gather_facts: no
tasks:
- name: Gather installed Java version, if there is any
shell:
cmd: java -version 2>&1 | head -1 | cut -d '"' -f 2
register: result
check_mode: false
changed_when: false
failed_when: result.rc != 0 and result.rc != 127
- name: Set default version, if there is no
set_fact:
result:
stdout_lines: "0.0.0_000"
when: "'command not found' in result.stdout"
check_mode: false
- name: Report result
debug:
msg: "{{ result.stdout_lines }}"
check_mode: false
Based on the installed version an installer or updater would be called to install or update to the latest version if necessary.
Thanks to
Why does Java version go to stderr?

No package matching 'git2u' found available, installed or updated

I'm trying to run Ansible on my existing nodes and I'm getting errors on all of the nodes except one on which it works fine:
The error message:
No package matching 'git2u' found available, installed, or updated.
All of the nodes are CentOS 7.
What am I doing wrong?
Same when I'm using yum from the terminal:
yum info git2u
Failed to set locale, defaulting to C
Loaded plugins: fastestmirror, langpacks, ps
Loading mirror speeds from cached hostfile
* epel: d2lzkl7pfhq30w.cloudfront.net
Error: No matching Packages to list
My playbook:
- name: install epel7 and ius-release to install latest git
package:
name:
- epel-release
- 'https://repo.ius.io/ius-release-el7.rpm'
state: present
when: ansible_distribution == 'CentOS'
- name: install git2u
package:
name: git2u
state: present
when: ansible_distribution == 'CentOS'
The git2u pacakge was renamed to git216, which was later retired. If you use IUS I highly recommend watching the announce repository to be notified when packages are retired.
https://ius.io/faq#how-do-i-know-when-an-ius-package-is-being-retired

Ansible -playbook when deb not installed install

im trying to write a playbook and i want to check if a deb package is installed and if not installed then install
so iv'e tried so far using the package_facts module and i can't figure this out
- name: Gather package facts
package_facts:
manager: auto
- name: Debug if package is present
debug:
msg: 'yes, mypackage is present'
when: '"besagent" in ansible_facts.packages'
register: besagent
- name: Debug if package is absent
debug:
msg: 'no, mypackage is absent'
when: '"besagent" not in ansible_facts.packages'
and this is the command to install the deb
- name: Install_BigFix_DEB
apt: deb="/usr/BigFix/BESAgent-9.5.11.191-debian6.amd64.deb"
sudo: true
So i see if the package is installed or not and i have a command to install the package but how do i make it happen automatically.
If BigFix agent is not installed Then install the agent?
Thanks for the help!!
Ansible operations are idempotent in nature. If you are using Ansible modules then you don't need to check if deb package is installed or not. Ansible will take care of it. If the package is not installed it will install. Else it will skip.
You can directly use
- name: Install_BigFix_DEB
apt: deb="/usr/BigFix/BESAgent-9.5.11.191-debian6.amd64.deb"
sudo: true
As per Ansible document:
An operation is idempotent if the result of performing it once is exactly the same as the result of performing it repeatedly without any intervening actions.
You don't need to check if the package is already installed or not.
The apt module take care of that.
If the package is already installed, apt will do nothing and return a status: ok,
if not, it will install it and return a status: changed.

How to make Ansible execute a shell script if a package is not installed

How can I make Ansible execute a shell script if a (rpm) package is not installed? Is it somehow possible to leverage the yum module?
I don't think the yum module would help in this case. It currently has 3 states: absent, present, and latest. Since it sounds like you don't want to actually install or remove the package (at least at this point) then you would need to do this in two manual steps. The first task would check to see if the package exists, then the second task would invoke a command based on the output of the first command.
If you use "rpm -q" to check if a package exists then the output would look like this for a package that exists:
# rpm -q httpd
httpd-2.2.15-15.el6.centos.1.x86_64
and like this if the package doesn't exist:
# rpm -q httpdfoo
package httpdfoo is not installed
So your ansible tasks would look something like this:
- name: Check if foo.rpm is installed
command: rpm -q foo.rpm
register: rpm_check
- name: Execute script if foo.rpm is not installed
command: somescript
when: rpm_check.stdout.find('is not installed') != -1
The rpm command will also exit with a 0 if the package exists, or a 1 if the package isn't found, so another possibility is to use:
when: rpm_check.rc == 1
Based on the Bruce P answer above, a similar approach for apt/deb files is
- name: Check if foo is installed
command: dpkg-query -l foo
register: deb_check
- name: Execute script if foo is not installed
command: somescript
when: deb_check.stdout.find('no packages found') != -1
If the package is installable through the system package manager (yum, apt, etc) itself, then you can make use of the check mode flag of ansible to register installation status without actually installing the package.
- name: check if package is installed
package:
name: mypackage
state: present
check_mode: true
register: mypackage_check
- name: run script if package installed
shell: myscript.sh
when: not mypackage_check.changed
Related to this.
Putting everything together, complete playbook for Debian (Ubuntu) which Updates package only if it's already installed:
---
- name: Update package only if already installed (Debian)
hosts: all
sudo: yes
tasks:
- name: Check if Package is installed
shell: dpkg-query -W -f='${Status}' {{ package }} | grep 'install ok installed'
register: is_installed
failed_when: no
changed_when: no
- name: Update Package only if installed
apt:
name: {{ package }}
state: latest
update_cache: yes
when: is_installed.rc == 0
Sadly Ansible still hasn't built-in support for making simple package updating, see ex: https://github.com/ansible/ansible/issues/10856
Since Ansible 2.5, you can use the package_facts module:
- name: Gather package facts
package_facts:
manager: auto
- name: Debug if package is present
debug:
msg: 'yes, mypackage is present'
when: '"mypackage" in ansible_facts.packages'
- name: Debug if package is absent
debug:
msg: 'no, mypackage is absent'
when: '"mypackage" not in ansible_facts.packages'
Note: you need the python bindings for apt/rpm installed on the target, e.g. python-apt for Debian.
You shouldn't be using dpkg -l package because it has no idea if your package has been removed or is still installed.
Instead it's probably better to use dpkg -s package.
To check if the package is installed :
- shell: dpkg -s package | grep 'install ok installed'
or if you don't mind the package on hold or other states :
- shell: dpkg -s package | grep 'installed'
This return 0 when installed and 1 if not.
(It's important to use the shell as we are using a pipe |)
I find using shell or command module is not "ansiblic".
I prefer to use yum module and json_query filter to check if a package is already installed. E.g. httpd package :
- yum:
list: httpd
register: apache_service
- assert:
that:
- "'installed' in apache_service|json_query('results[*].yumstate')"
msg: 'httpd is not installed'

Resources