Ansible dnf module enable Fedora Copr repository - ansible

I want to enable a Fedora Copr repository with Ansible. More specifically I want to convert this command:
dnf copr enable ganto/lxd
Using an Ansible command module I overcome this problem but break the task's idempotence (if run again, the role should not make any changes) (changed_when: false is not an option).
- name: Enable Fedora Copr for LXD
command: "dnf copr enable -y ganto/lxd"
Also, I tried this:
- name: Install LXD
dnf:
name: "{{ item }}"
state: latest
enablerepo: "xxx"
with_items:
- lxd
- lxd-client
Where I test many variations for the option enablerepo without any success.
Is that possible using the dnf Ansible module (or something else)?

You can use creates to make your command idempotent; if the .repo file already exists then the task won't run:
- name: Enable Fedora Copr for LXD
command:
cmd: dnf copr enable -y ganto/lxd
creates: /etc/yum.repos.d/_copr_ganto-lxd.repo
(You'd have to check that enabled=1 manually)
$ cat /etc/yum.repos.d/_copr_ganto-lxd.repo
[ganto-lxd]
name=Copr repo for lxd owned by ganto
baseurl=https://copr-be.cloud.fedoraproject.org/results/ganto/lxd/fedora-$releasever-$basearch/
type=rpm-md
skip_if_unavailable=True
gpgcheck=1
gpgkey=https://copr-be.cloud.fedoraproject.org/results/ganto/lxd/pubkey.gpg
repo_gpgcheck=0
enabled=1

It is now possible thanks to https://docs.ansible.com/ansible/latest/collections/community/general/copr_module.html
The original snippet using command
- name: Enable Fedora Copr for LXD
command: "dnf copr enable -y ganto/lxd"
can be changed to
- name: Enable Fedora Copr for LXD
community.general.copr:
name: ganto/lxd

No, the Ansible dnf module doesn't support enabling Copr repositories.
You can add a task that tests if your Copr repository is already enabled that guards the Copr enable task.
Example:
shell:
cmd: |
dnf -C repolist enabled -v | grep '^Repo-id' | awk '$3 == "copr:copr.fedorainfracloud.org:ganto:lxd" {print "enabled"}'
warn: no
check_mode: no
changed_when: false
register: lxd_copr
- name: Enable Fedora Copr for LXD
command:
cmd: dnf -y copr enable ganto/lxd
warn: no
when: lxd_copr.stdout == ''
Notes:
double check the id of your copr repository as it's different from the short name you use to enable it
I set warn: no because ansible warns about all dnf commands (because it suggests to use the dnf module, if possible)
I set check_mode: no since it's safe to execute it even in --check mode
I set changed_when: false because the command doesn't change system state
Alternatively you can add and enable a Copr repository with the yum_repository Ansible module.
Example:
- name: enable copr
yum_repository:
name: "copr:copr.fedorainfracloud.org:{{ item[0] }}:{{ item[1] }}"
file: "_copr:copr.fedorainfracloud.org:{{ item[0] }}:{{ item[1] }}"
description: "{{ item[2] }}"
baseurl: "{{ copr_url }}/results/{{ item[0] }}/{{ item[1] }}/fedora-$releasever-$basearch/"
gpgkey: "{{ copr_url }}/results/{{ item[0] }}/{{ item[1] }}/pubkey.gpg"
gpgcheck: yes
enabled: yes
skip_if_unavailable: yes
vars:
#copr_url: https://copr-be.cloud.fedoraproject.org
copr_url: https://download.copr.fedorainfracloud.org
loop:
- [ganto, lxd, "Copr repo for LXD"]
This approximates the effect of the dnf copr enable ganto/lxd call. But there are some minor textual differences in the resulting .repo file (e.g. True vs. 1, keys with default values missing) that would lead to this task reporting changed if e.g. the repository was already enabled with dnf copr.
Also, this arguably has a higher maintenance overhead as one would have to track changes Copr introduces to its .repo files.

Related

How to get latest package version with Ansible

I'm trying to replicate the command yum check-update package_name preferably with the Ansible yum module.
It provides an information to what version package would be updated after yum update execution (or ansible equivalent). For example:
root#host: $ yum check-update kernel
[...]
kernel.x86_64 3.10.0-1160.80.1.el7
[...]
root#host: $
I tried various combination of
- name: Xyz
ansible.builtin.yum:
list: updates
update_cache: true
But I can't limit it to a single package or pattern (like java*).
What I ended up with is ugly and slow (because of the download) workaround:
- name: Check latest available xyz version
yum:
name: xyz
state: latest
download_only: true
become: true
register: _result
- name: Register xyz version
set_fact:
latestXyz: "{{ _result.changes.updated[0][1] | regex_search('xyz-(.+).x86_64.*', '\\1') }}"
Is there any better way to achieve this?
One pretty easy way to achieve this is to run the yum module as you would to update the package, but enforce a dry run on it, with the check_mode option, and register the result of this task.
Inspecting the result, you'll realise that the module will list you the current version of the package and the version it would have been updated to, would it not have been run in dry run.
Given, for example
- yum:
name: expat
state: latest
check_mode: true
register: yum_latest_versions
Will populate the variable yum_latest_versions on a fedora:35 container with:
yum_latest_versions:
ansible_facts:
pkg_mgr: dnf
changed: true
failed: false
msg: 'Check mode: No changes made, but would have if not in check mode'
rc: 0
results:
- 'Installed: expat-2.5.0-1.fc35.x86_64'
- 'Removed: expat-2.4.9-1.fc35.x86_64'
Then to extract, you can indeed use a regex to search in the result:
- debug:
msg: >-
{{
(
yum_latest_versions.results
| map('regex_search', '^Installed: expat\-(.*)\.x86_64$', '\1')
).0.0
}}
Finally yields:
msg: 2.5.0-1.fc37

How to override a yum module state for multiple packages using environment variable in Ansible?

This is my code currently -
- name: software
hosts: localhost
tasks:
- name: install packages
yum:
name:
- ansible
- docker
state: latest
become: yes
So when I run this I get the latest ansible and docker installed.
What I want is for the default value of state to remain latest, so if I just run the playbook the latest versions are downloaded, as it is now. However I want a way for me to override the state for one or both using environment variables(extra vars) when running my playbook from the command line.
So I can choose what version of ansible or docker to install.
Is there a way?
Although I do not think this is the best way to manage software version and that the I consider the following a bit ugly, here is a in-a-nutshell example to get you on track for your experimentation (untested, you may have to adapt a bit):
---
- name: software
hosts: localhost
vars:
ansible_raw_suffix: "-{{ ansible_yum_version | default('') }}"
ansible_suffix: "{{ ansible_yum_version is defined | ternary(ansible_raw_suffix, '') }}"
docker_raw_suffix: "-{{ docker_yum_version | default('') }}"
docker_suffix: "{{ docker_yum_version is defined | ternary(docker_raw_suffix, '') }}"
tasks:
- name: install packages
yum:
name:
- "ansible{{ ansible_suffix }}"
- "docker{{ docker_suffix }}"
state: "{{ yum_state | default('present') }}"
become: yes
With the above, you can:
install to the latest version if first time install
ansible-playbook software.yml
install a specific version of one or both softwares:
ansible-playbook software.yml -e ansible_yum_version=2.9.2 -e docker_yum_version=20.10.6
upgrade to the lastest version
ansible-playbook software.yml -e yum_state=latest
I will let you go on with this to add more features (e.g. allow downgrades) if you feel you still want to walk that path.

Set language and timezone in CentOS 7 using Ansible: Can't install locales package

I found the locale_gen module to make sure that locales are present on the system and tried it like described here:
- name: Install EN locale
locale_gen:
name: "de_DE.UTF-8"
state: present
- name: Install DE locale
locale_gen:
name: "en_US.UTF-8"
state: present
This throws an error:
/etc/locale.gen and /var/lib/locales/supported.d/local are missing. Is the package \"locales\" installed?
So I tried to install it:
- name: Install locales package
become: yes
yum:
name: locales
state: present
Both Ansible and manual install using sudo yum install locales doesn't work. I also tried enabling the EPEL repo without success.
How can I make sure that the requestes languages are present on the target system?
There is an open bug report since Aug 27 2018 for centos support. I suggest you vote for it so we get a chance it climbs up the priority list (or submit a PR if you have the required skills and enough time)
Until this is fixed, you can probably apply the workarround proposed by #wojciech-kopras on april 2019 adapted below from your question (tested successfully against a docker centos:7 container):
- name: Define needed locales (for example, can be set in vars or inventory)
set_fact:
system_settings_locales:
- en_US.UTF-8
- de_DE.UTF-8
- name: Check existing locales
shell: "locale -a | grep -i {{ item | regex_replace('-', '') | quote }}"
register: found_locales
changed_when: false
failed_when: false
loop: "{{ system_settings_locales }}"
- name: Create missing locales
command: "localedef -i {{ item.item | regex_replace('(.*)\\..*', '\\1') | quote }} -f {{ item.item | regex_replace('.*\\.(.*)', '\\1') | quote }} {{ item.item | quote }}"
when: item.rc != 0
loop: "{{ found_locales.results }}"
Also, for some added context there's no package named locales which is why original task for yum is failing. The package name containing locale data is glibc-common (at least on RHEL/CentOS 7, should be the same for 8). However, as noted by the first commenter, there appears to be a bug.

package installation not considered in playbook

I got some trouble with automating an installation using ansible.
I use this role (https://github.com/elastic/ansible-elasticsearch) to install elasticsearch on my ubuntu 16.04 server.
The role depends on the package python-jmespath, as mentioned in the documentation.
The role DOES NOT install the package itsself, so i try to install it before role execution.
- hosts: elasticsearch_master_servers
become: yes
tasks:
- name: preinstall jmespath
command: "apt-get install python-jmespath"
- name: Run the equivalent of "apt-get update" as a separate step
apt:
update_cache: yes
- hosts: elasticsearch_master_servers
become: yes
roles:
- role: elastic.elasticsearch
vars:
...
When running the playbook i expect the python-jmespath package to be installed before execuction of role takes place, but role execution fails with
You need to install \"jmespath\" prior to running json_query filter"
When i check if the package is installed manually using dpkg -s python-jmespath i can see the package is installed correctly.
A second run of the playbook (with the package already installed) doesnt fail.
Do I miss an ansible configuration, that updates the list of installed packages during the playbook run ?
Am I doing something wrong in general ?
Thanks in advance
FWIW. It's possible to tag installation tasks and install the packages in the first step. For example
- name: install packages
package:
name: "{{ item.name }}"
state: "{{ item.state|default('present') }}"
state: present
loop: "{{ packages_needed_by_this_role }}"
tags: manage_packages
Install packages first
shell> ansible_playbook my-playbook.yml -t manage_packages
and then run the playbook
shell> ansible_playbook my-playbook.yml
Notes
This approach makes checking of the playbooks with "--check" much easier.
Checking idempotency is also easier.
With tags: [manage_packages, never] the package task will be skipped when not explicitly selected. This will speed up the playbook.

How to do best organization of install and delete in the same ansible playbook?

I try to figure how I must to construct an ansible playbooks where I can do some action and undo them (I can install or remove same packages; place file or remove this file).
If I create two ansible playbooks: delete.yml and install.yml. There are may be problem's like:
I added to installation someting, but don't change deletion
Example:
install.yml:
---
- name: Add x2go repository
apt_repository: repo='deb http://ppa.launchpad.net/x2go/stable/ubuntu precise main' state=present
apt_repository: repo='deb-src http://ppa.launchpad.net/x2go/stable/ubuntu precise main' state=present
when: ansible_os_family == "Debian"
tags:
- remote-access-x2go
- name: Install x2go application
apt: name=x2goserver update_cache=yes state=present
apt: name=x2goserver-xsession update_cache=no state=present
when: ansible_os_family == "Debian"
tags:
- remote-access-x2go
delete.yml:
---
- name: Add x2go repository
apt_repository: repo='deb http://ppa.launchpad.net/x2go/stable/ubuntu precise main' state=absent
apt_repository: repo='deb-src http://ppa.launchpad.net/x2go/stable/ubuntu precise main' state=present
when: ansible_os_family == "Debian"
tags:
- remote-access-x2go
- name: Install x2go application
apt: name=x2goserver update_cache=yes state=absent
apt: name=x2goserver-xsession update_cache=no state=absent
when: ansible_os_family == "Debian"
tags:
- remote-access-x2go
This is a very interesting idea. I have personally never tried the 'undoing' workflow, but I can see the nice things about this idea and would like to use it sometime. Here is what I would do.
In my ansible-role/defaults/main.yml I would define a variable flag
# defaults file for ansible-role
flag_undo: false
In my ansible-role/tasks/main.yml I would have
- name: task foo bar
command: falana dhimaka
- name: undoing task foo bar
command: undo falana dhimaka
when: flag_undo=true
So by default our flag is always false. So when installing things I would us the first command below to run my plays. And to uninstall I would use the second command.
ansible-playbook foo-play.yml
ansible-playbook foo-play.yml --extra-vars "flag_undo=true"
One approach that I use in some cases is to simply have lists of packages that you want installed and lists you want removed, then iterate over each list. I use this basic method not only for packages but other things as well, like users, groups, etc. For example, I have a "packages" role that has the following files in it:
vars/main.yml:
---
installed_system_packages:
- telnet
- screen
- postfix
latest_system_packages:
- glibc
removed_packages:
- sendmail
tasks/main.yml:
---
- name: Install system packages (latest)
yum: pkg={{ item }} state=latest
with_items: latest_system_packages
- name: Install system packages
action: yum pkg={{ item }} state=installed
with_items: installed_system_packages
- name: Remove unwanted packages
action: yum pkg={{ item }} state=removed
with_items: removed_packages
This way, if I decide that I no longer want a package like telnet installed I can just move it from installed_system_packages to removed_packages. Or if I want to ensure I'm running the latest version of screen I would simply move it to the latest_system_packages list. Then it's just a matter of re-running the role to have the changes applied.

Resources