ansible deprecation warning with custom objects in apt - linux

I am getting a deprecated warning with Ansible
[DEPRECATION WARNING]: Invoking "apt" only once while using a loop via squash_actions is deprecated. Instead of using a loop to supply multiple items and specifying name: "{{ item.name | default(item) }}", please use name: '{{ apt_dependencies }}' and remove the loop. This feature will be removed in version 2.11. Deprecation warnings can be disabled by setting deprecation_warnings=False in ansible.cfg.
- name: 'Install system dependencies'
apt:
name: "{{ item.name | default(item) }}"
state: "{{ item.state | default('present') }}"
with_items: "{{ apt_dependencies }}"
This allows me to do
apt_dependencies:
- name: curl
state: absent
- name: ntp
state: present
- docker
It suggests replacing name with "{{ apt_dependencies }}" but that wont work right with the custom name/state
I am doing this so I can install dependencies as well as remove any I don't want on the server
Any ideas how to change this to work without having a warning, which I get I can turn off but I'd rather fix it before its removed

It suggests replacing name with "{{ apt_dependencies }}" but that wont work right with the custom name/state
It will if you batch them into two steps, adds and removals:
- set_fact:
remove_apt_packages: >-
{{ apt_dependencies
| selectattr("state", "==", "absent")
| map(attribute="name")
| list }}
# using "!= absent" allows for the case where the list item
# doesn't say "present" such as your "docker" example
add_apt_packages: >-
{{ apt_dependencies
| selectattr("state", "!=", "absent")
| map(attribute="name")
| list }}
- name: 'Remove system dependencies'
apt:
name: "{{ remove_apt_packages }}"
state: absent
- name: 'Install system dependencies'
apt:
name: "{{ add_apt_packages }}"
state: present

Related

How to use "maps" in anisble along with "with_items"

I am trying to add labels to each node where a label is a map as:
set_node_labels:
topology.kubernetes.io/region: "syd"
topology.kubernetes.io/zone: "syd01"
I have written the Ansible task as follows, however it does not work as expected:
- name: Get all Nodes
shell: "oc get nodes | awk '(NR>1) { print $1 }'"
register: node_names
- name: Print phone records
k8s:
state: present
kind: Node
name: "{{ item }}"
definition:
metadata:
labels: "{{ item.key }} {{ item.value }}"
loop: "{{ lookup('dict', set_node_labels) }}"
with_items: "{{ node_names.stdout_lines }}"
You can also use your existing code with a little tweak
- name: Get all Nodes
shell: "oc get nodes | awk '(NR>1) { print $1 }'"
register: node_names
- name: Print phone records
k8s:
state: present
kind: Node
name: "{{ item }}"
definition:
metadata:
labels: "{{ set_node_labels }}"
with_items: "{{ node_names.stdout_lines }}"
First things first, you should use the existing module, when they are available instead of a shell module.
In your case, you can get the information about your nodes thanks to the k8s_info module.
So, your first taks should be:
- name: Get all Nodes
k8s_info:
kind: Node
register: node_names
Then in order to pass your labels, those should actually be in a dictionary, so you should be able to pass the whole set_node_labels as labels:
- name: Print phone records
k8s:
state: present
kind: Node
name: "{{ item.metadata.name }}"
definition:
metadata:
labels: "{{ set_node_labels }}"
loop: "{{ node_names.resources }}"

Multiple with_items in an Ansible module block

I want to create multiple logical volumes with a variable file but it return a sintax error found character that cannot start any token, I have tried in different ways but still doesn't work
main.yml
---
- name: playbook for create volume groups
hosts: localhost
become: true
tasks:
- include_vars: vars.yml
- name: Create a logical volume
lvol:
vg: vg03
lv: "{{ item.var1 }}"
size: "{{ item.var2 }}"
with_items:
- { var1: "{{ var_lv_name }}", var2: "{{ var_lv_size }}" }
vars.yml
var_lv_name:
- lv05
- lv06
var_lv_size:
- 1g
- 1g
Use with_together. Test it first. For example,
- debug:
msg: "Create lv: {{ item.0 }} size: {{ item.1 }}"
with_together:
- "{{ var_lv_name }}"
- "{{ var_lv_size }}"
gives (abridged)
msg: 'Create lv: lv05 size: 1g'
msg: 'Create lv: lv06 size: 1g'
Optionally, put the declaration below into the file vars.yml
var_lv: "{{ var_lv_name|zip(var_lv_size) }}"
This creates the list
var_lv:
- [lv05, 1g]
- [lv06, 1g]
Use it in the code. The simplified task below gives the same results
- debug:
msg: "Create lv: {{ item.0 }} size: {{ item.1 }}"
loop: "{{ var_lv }}"
The previous answer it's totally correct but In my humble opinion we should be getting into the new way to do the things with loop and filters.
Here's my answer:
---
- name: playbook for create volume groups
hosts: localhost
gather_facts: no
become: true
vars_files: vars.yml
tasks:
- name: Create a logical volume
lvol:
vg: vg03
lv: "{{ item[0] }}"
size: "{{ item[1] }}"
loop: "{{ var_lv_name | zip(var_lv_size) | list }}"
In this answer you're using the new way to use loops with keyword loop and using filters like zip and turning the result into a list type for iteration in the loop.

Ansible sorting doesn't pick the highest number

I'm trying to get few docker tags from Artifactory docker api and pick the latest tag in the list using Anisble sort but when I try sort its picking the single digit value over double digits ( example 9 over 15)
here are some example tags output list
the api output looks similar to this:
registered to tag_output
{
1.1.0-ab.7-9,
1.1.0-ab.8-10,
1.1.0-ab.9-12,
}
With the regex it picks up last char in the string after "-" which i'm trying to sort.
https://regex101.com/r/O7xH4u/1
using the same regex in the Ansible tasks when doing the sort, ansible is picking 1.1.0-ab.7-9 as it thinks 9 is greater then 12 (however, if we add '0' before 9 then it picks 12)
- name: Get tags
command: curl -u "{{artifactoryUser}}:{{artifactoryPwd}}" https://artifactory-mars.cd.genesaas.io/v2/xxxx/releases-candidates/1.1.0/from-service/tags/list
register: tag_output
- name: SetFact TAG
set_fact:
DCM_TAG: "{{ _dict|dict2items| sort(attribute='key')| map(attribute='value')|last }}"
vars:
_index: "{{ (tag_output.stdout | from_json).tags|map('regex_replace', '^(.*)-(.*)$', '\\2')|list }}"
_dict: "{{ dict(_index|zip((tag_output.stdout | from_json).tags)) }}"
output:
TASK [SetFact TAG] **********************************************************************************************************************************************************
ok: [localhost] => {"ansible_facts": {"DCM_TAG": "1.1.0-ab.7-9"}, "changed": false}
but I need expected output:
1.1.0-ab.9-12
Put the tags into a list
tags_list: "{{ tag_output.keys()|list }}"
gives
tags_list:
- 1.1.0-ab.7-9
- 1.1.0-ab.8-10
- 1.1.0-ab.9-12
Parse the attributes
- set_fact:
tags: "{{ tags|d([]) + [{'tag': item,
'major': major|int,
'minor': minor|int}] }}"
loop: "{{ tags_list }}"
vars:
ext: "{{ item|split('.')|last }}"
major: "{{ ext|split('-')|first }}"
minor: "{{ ext|split('-')|last }}"
gives
tags:
- major: 7
minor: 9
tag: 1.1.0-ab.7-9
- major: 8
minor: 10
tag: 1.1.0-ab.8-10
- major: 9
minor: 12
tag: 1.1.0-ab.9-12
Now, find the largest major and minor, and select the tag
- debug:
msg: |
Latest version: {{ tags_max.major }}-{{ tags_max.minor }}
DCM_TAG: {{ DCM_TAG }}
vars:
major_max: "{{ tags|map(attribute='major')|max }}"
tags_major_max: "{{ tags|selectattr('major', 'eq', major_max|int) }}"
minor_max: "{{ tags_major_max|map(attribute='minor')|max }}"
tags_max: "{{ tags_major_max|selectattr('minor', 'eq', minor_max|int)|first }}"
DCM_TAG: "{{ tags_max.tag }}"
gives
Latest version: 9-12
DCM_TAG: 1.1.0-ab.9-12
Note
Testing multiple minors, e.g.
tag_output:
{
1.1.0-ab.7-9,
1.1.0-ab.8-10,
1.1.0-ab.9-10,
1.1.0-ab.9-12,
1.1.0-ab.9-7,
}
gives the correct result too
Latest version: 9-12
DCM_TAG: 1.1.0-ab.9-12

Ansible Undefined variable in vars_files item but the variable was defined using set_fact

I'm trying to select a local file based on what motherboard version exists on the machine on which Ansible will deploy those files.
So my approach was to use dictionary in format {"<board_name>" : "<local_file>.yml"}. And use this dictionary to populate a new variable (pcu_config here) that will finally store the name of the file to be used by ansible. I've gotten so far as shown in my implementation, and I'm get undefined variable error at line
vars_files:
- "{{ playbook_dir }}/pcu_config/{{ pcu_config }}"
But as can be seen in the debug msg in the image of the output just before this play, pcu_config is indeed defined(and is the correct filename too).
- name: Find the pcu_config
hosts: vehicle
gather_facts : no
vars:
pcu_config_dict:
VirtualBox: virtual.yml
n1: n1.yml
n2: n2.yml
n3: n3.yml
n4a: n4a.yml
n4b: n4b.yml
tasks:
- name: Find the motherboard name
shell: cat /sys/devices/virtual/dmi/id/board_name
register: board_name
- name: Find the motherboard version
shell: cat /sys/devices/virtual/dmi/id/chassis_version
register: board_version
- debug:
msg: "board_name : {{ board_name.stdout }}, board_version: {{ board_version.stdout }}, {{pcu_config_dict}} "
- name: Assign the PCU_config file for other than n4's
set_fact:
pcu_config: '{{pcu_config_dict[ board_name.stdout | default("this cpu does not exist in the dict")] | default("") }}'
when: board_name.stdout != "n4"
- name: Assign the PCU_config file for n4
set_fact:
pcu_config: '{{pcu_config_dict[board_version.stdout | default("this cpu_version does not exist in the dict")] | default("") }}'
when: board_name.stdout == "n4"
- name : Check pcu_config is available elsewhere in playbook
hosts: vehicle
tasks:
- debug:
msg: "{{ pcu_config }}, {{ playbook_dir }}"
- name: Deploy software to vehicle
hosts: vehicle
vars_files:
- "{{ playbook_dir }}/pcu_config/{{ pcu_config }}"
- "{{ playbook_dir }}/os_config/deb-files-cache.yml"
- "{{ playbook_dir }}/os_config/python_dep.yml"
roles:
- role: logger_network
tags: base
Here is the output when I run this playbook on my virtual machine.
Suggestions welcome to solve the X or Y problem.(ref #Zeitounator's comment)
Not the best approach but to access the variables set by set_facts elsewhere in the playbook, using cacheable: yes works.
- name: Assign the PCU_config file for other than n4's
set_fact:
pcu_config: '{{pcu_config_dict[ board_name.stdout | default("this cpu does not exist in the dict")] | default("") }}'
cacheable: yes
when: board_name.stdout != "n4"
- name: Assign the PCU_config file for n4
set_fact:
pcu_config: '{{pcu_config_dict[board_version.stdout | default("this cpu_version does not exist in the dict")] | default("") }}'
cacheable: yes
when: board_name.stdout == "n4"

Why Ansible didn't see attribute in variable?

I have Ansible role "db" with simple task:
- name: Check repos
apt_repository: repo="{{ item.repo }}" state={{ item.state }}
with_items:
- "{{ apt_repos }}"
In /defaults/mail.yml:
apt_repos:
# Percona
- { state: present, repo: 'deb http://repo.percona.com/apt wheezy main', keyserver: 'keyserver.ubuntu.com', key: '1C4CBDCDCD2EFD2A', needkey: True }
- { state: present, repo: 'deb-src http://repo.percona.com/apt wheezy main', needkey: False }
When i try to run this ansible-playbook:
---
- hosts: test
roles:
- db
i see error:
fatal: [10.10.10.10] => One or more undefined variables: 'unicode object' has no attribute 'repo'
FATAL: all hosts have already failed -- aborting
But i have another role with same task and variable and it work perfectly. What's wrong?
You want to be doing this:
with_items: apt_repos
apt_repos is a list. By referencing it as - "{{ apt_repos }}" the extra - is turning it into a list of lists. You also don't need the quotes or braces in this case - those are pretty much just redundant in this type of situation.

Resources