Dynamically fetch values using env variable from yaml file using yq - yaml

I'm trying to get the values of tenants in below yaml file using yq. The intent is to dynamically fetch the value depending on env variable.
Let's assume there's an env variable var="az-dev", then tenants of az-dev should be retrieved.
I have given some tries as mentioned below but no luck.
YAML file
tenantlist:
az-dev:
tenants:
- myazdev
az-stage:
tenants:
- myazstg1 myazstg2
aw-dev:
tenants:
- myawdev1 myawdev2
aw-stage:
tenants:
- myawstg1 myawstg2
Tries:
var="az-dev" yq e '.tenantlist | select(. == env(var))' file.txt
var=az-dev; yq '.tenantlist |= strenv(var) has("az-dev")' file.txt
Any help would be appreciated. TIA.

With mikefarah/yq, you can simply index the required key name by [..] and get the corresponding tenants list
var="az-dev" yq '.tenantlist[strenv(var)].tenants[]' yaml
Or just pick the keys of interest from the map (available since v4.22.1)
var="az-dev" yq '.tenantlist | pick([strenv(var)]) | .[].tenants[]' yaml
Note: Since 4.18.1, yq's eval/e command is the default command and no longer needs to be specified.

Related

yq command is not writing contents to the file persistently

I am using yq tool to write data to a yaml file , but i am unable to write the data persistently to the yaml file. when executed the below command it returns the output i have expected to the console but it is writing only first section to the file... any help is greatly appreciated.
yq version: 3.4.1
Command :
yq n affinity-controller.fresh_install "False" > history.yaml
yq w -d'*' history.yaml snapshot-validation-webhook.fresh_install "False"
contents of history.yaml after executing the above command
affinity-controller:
fresh_install: False
expected output :
affinity-controller:
fresh_install: False
snapshot-validation-webhook:
fresh_install: False
For yq v3, according to doc, you should do it like this:
yq w -i history.yaml snapshot-validation-webhook.fresh_install "False"
For yq v4 (please note the leading dot):
yq e '.snapshot-validation-webhook.fresh_install=False' -i history.yaml
Tested and verified on localhost.
Snap users: Please note that there's a bug in yq:4.30.7
https://github.com/mikefarah/yq/issues/1521
Symptom: You'll get the following error when running yq:
Error: chown/tmp/temp1636774104: operation not permitted
Downgrade yq to last working version (4.30.3): sudo snap refresh yq --channel=v4/stable

AnsibleUnsafeText enhancement to an existing playbook

I have a playbook that includes a set of tasks to obtain the status of an IBM MQ Channel. The playbook is passed the Channel name and I run the runmqsc command on each server
and register the output which unfortunately is on many lines, so i need to get this onto one line
echo "DISPLAY CHS('{{CHANNEL_NAME}}') STATUS RQMNAME"| runmqsc {{QMGR}}|grep -v DISPLAY|sed 's/^[^ ].*$/%/g' | tr -s " " | tr -d "\n" | tr "%" "\n"|grep CHANNEL|sed 's/ CURRENT//g' | sed 's/^ //g'|sed 's/ *$//g'
which gave the output
CHANNEL(CHANNEL) CHLTYPE(CLUSSDR) CONNAME(1.2.3.4(1414)) RQMNAME(QMGR) STATUS(RUNNING) SUBSTATE(MQGET) XMITQ(XMIT_QUEUE)
this I parsed to create a list CHSstatus (when CHANNEL_NAME is a wildcard I can have more than one result). From this list I then set variables to the STATUS of the channel.
- set_fact:
allStopped: "{%if splitStatus.STATUS == 'STOPPED'%}{{allStopped + [splitStatus.CHANNEL]}}{%else%}{{allStopped}}{%endif%}"
allRunning: "{%if splitStatus.STATUS == 'RUNNING'%}{{allRunning + [splitStatus.CHANNEL]}}{%else%}{{allRunning}}{%endif%}"
mixedState: "{%if splitStatus.STATUS != 'STOPPED' and splitStatus.STATUS != 'RUNNING'%}{{mixedState + [splitStatus.CHANNEL]}}{%else%}{{mixedState}}{%endif%}"
with_items: "{{CHSstatus}}"
loop_control:
loop_var: splitStatus
This has been working fine for a while with no issues. The problem with this is that you actually need to know the Channel name and as I wanted to enhance it to get the status of all the channels in a CLUSTER I put a 'wrapper' task in front of it to collect the channel names and use these to pass them.
My playbook now calls the MQ command to get these channel names, we can pass the CLUSTER name and register the result in clusterChannels, and include the previously working task with each channel name
- name: Status of Cluster channels
include: channelstatus.yml CHANNEL_NAME={{item}}
with_items: "{{clusterChannels.stdout_lines}}"
This now fails when trying to access the CHSStatus list, I get the error is ERROR! Unexpected Exception: unhashable type: 'dict'
I have tried both methods and compared some variables for each call of the original tasks
Original
"CHSstatus = [{'STATUS': u'RUNNING', 'CHANNEL': u'CHANNEL1'}]",
"CHSstatus Type = list",
"CHANNEL_NAME = CHANNEL1",
"CHANNEL_NAME type = AnsibleUnicode"
with the wrapper
"CHSstatus = [{'STATUS': u'RUNNING', 'CHANNEL': u'CHANNEL1'}]",
"CHSstatus Type = list",
"CHANNEL_NAME = CHANNEL1",
"CHANNEL_NAME type = AnsibleUnsafeText"
I have tried to find information about what AnsibleUnsafeText variable is and found that
'Since you are registering the result of a command in a variable, Ansible can't know what will be the content which becomes delivered. Therefore the registered Text output is marked as Unsafe.'
This has confused me a bit as I am running two commands, one to get the list of Channels in the wrapper and another to get the status of each individual channel in the original unchanged code. The registered variable is of type list but when I pass each channel from the wrapper it becomes an AnsibleUnsafeText type, I noticed that each of the registered variables for both commands when run with_items do indeed have an AnsibleUnsafeText type.
Can i convert this in any way? I have seen answers on how to convert to int, and i have tried item|string but this did not work either.
My original playbook used roles and i created a cutdown version of this using tasks only, this included cut down versions of channelstatus.yml and this worked OK, i then converted this to a role again including the same file and this failed.
One thing i notices in both cases of my MQ command registered results gets parsed successfully but the error occurs when I try to use this list of dict.
Example below is a list which contains a single dict, this can be a list of multiple dict's when run
CHSstatus = [{'STATUS': u'RUNNING', 'CHANNEL': u'CHANNEL1'}]
and a simple debug command is enough for this error to fail the playbook...
- debug:
msg:
- "{{item.CHANNEL}}"
- "{{item.STATUS}}"
with_items: "{{CHSstatus}}"
ERROR! Unexpected Exception: unhashable type: 'dict'
however
- debug:
msg:
- "{{CHSstatus[0].CHANNEL}}"
- "{{CHSstatus[0].STATUS}}"
works fine, which really does not make sense
Any help appreciated

Extract the lines using sed or awk and save them in file

Dear Stackoverflow Community,
I am trying to grab the value or the part of the string or lines.
The Kubernetes init gives 2 kubeadm join commands.
I want to extract the first one and save it in a file and similarly extract the 2nd one and save it in a file.
Below is the text that I am trying to extract from the file:
You can now join any number of the control-plane node running the following command on each as root:
kubeadm join 10.0.0.0:6443 --token jh88qi.uch1l58ri160bve1 \
--discovery-token-ca-cert-hash sha256:f9c9ab441d913fec7d157c20f1c5e93c496123456ac4ec14ca8e02ab7f916d7fb \
--control-plane --certificate-key 179e288571e33d3d68f5691b6d8e7cefa4657550fc0886856a52e2431hjkl7155
Please note that the certificate-key gives access to cluster sensitive data, keep it secret!
As a safeguard, uploaded-certs will be deleted in two hours; If necessary, you can use
"kubeadm init phase upload-certs --upload-certs" to reload certs afterward.
Then you can join any number of worker nodes by running the following on each as root:
kubeadm join 10.0.0.0:6443 --token jh88qi.uch1l58ri160bve1 \
--discovery-token-ca-cert-hash sha256:f9c9ab441d913fec7d157c20f1c5e93c496123456ac4ec14ca8e02ab7f916d7fb
Goal -
Extract both kubeadm join commands and save them in different files for automation.
Commands Used till now -
sed -ne '/--control-plane --certificate-key/p' token
With the above command, I want to extract value if I can and save it in a file.
The other command -
awk '/kubeadm join/{x=NR+2}(NR<=x){print}' token
token is the filename
You didn't show the expected output so it's a bit of a guess but this:
awk -v RS= '/^ *kubeadm join/{print > ("out"NR); close("out"NR)}' file
should do what I think you want given the input you provided.

Why playbook take wrong values for group variables?

I have a problem with groups variables.
Example: I have two inventory groups group_A and group_B, and also have the same name files in group_vars:
inventories/
hosts.inv
[group_A]
server1
server2
[group_B]
server3
server4
group_vars/
group_A - file
var_port: 9001
group_B - file
var_port: 9002
The problem is when i execute:
ansible-playbook playbooks/playbook.yml -i inventories/hosts.inv -l group_B
playbook was executed for proper scope of servers (server3, server4) but it takes variables from group variables file group_A.
expected result: var_port: 9002
in realty : var_port: 9001
ansible 2.4.2.0
BR Oleg
I included ANSIBLE_DEBUG , and what i have found:
2018-05-03 15:23:23,663 p=129458 u=user | 129458 1525353803.66336: Loading data from /ansible/inventories/prod/group_vars/group_B.yml
2018-05-03 15:23:23,663 p=129458 u=user | 129661 1525353803.66060: in run() - task 00505680-eccc-d94e-2b1b-0000000000f4
2018-05-03 15:23:23,664 p=129458 u=user | 129661 1525353803.66458: calling self._execute()
2018-05-03 15:23:23,665 p=129458 u=user | 129458 1525353803.66589: Loading data from /ansible/inventories/prod/group_vars/group_A.yml
on playbook execution ansible scan all files with variables in folder group_vars which have variable "var_port", last will win.....
as you can found in another topic:
Ansible servers/groups in development/production
and from documentation:
http://docs.ansible.com/ansible/latest/user_guide/playbooks_variables.html#variable-precedence-where-should-i-put-a-variable
Note
Within any section, redefining a var will overwrite the previous instance. If multiple groups have the same variable, **the last one loaded wins**. If you define a variable twice in a play’s vars: section, the **2nd one wins**.
For me now NOT clear how to manage configuration files. In this case I must use unique variables names for each group, but it is not possible regarding roles, or should I use include_vars when i call playbook?
Super example how to manage variables files in multistage environment from DigitalOcean
How to Manage Multistage Environments with Ansible
I believe that the problem here, while not explicitly stated in the original question, is that Server{1,2} and Server{3,4} are actually the same servers in 2 different groups at the same level.
I ran into this problem which caused me to do some digging. I don't agree with it, but it is as designed. This was even fixed with full compatibility and the pull request was rejected
Discussion
Pull Request

Ansible get column from stdout

I'm new to Ansible an thus this question may seem silly to more advanced users.
Anyway, I need to get the value 362496 for the column LDFree.
I know I can use the shell module with pipes and awk, but I was wondering if it's posisble to achive it in Ansible using some sort of "filter" for STDOUT?
This is the STDOUT from the CLI:
-------------------------(MB)-------------------------
CPG ---EstFree---- -------Usr------- ---Snp---- ---Adm---- -Capacity Efficiency-
Name RawFree LDFree Total Used Total Used Total Used Compaction Dedup
SSD_r6 483328 362496 12693504 12666880 12288 2048 8192 1024 1.0 -
You can done this knowing the fact that Ansible/Jinja support calling methods of native types:
- command: cat test.txt
register: cmd_res
- debug:
msg: "{{ cmd_res.stdout_lines[3].split()[2] }}"
stdout_lines[3] – take forth line, .split() – split it into tokens, [2] – take third token.

Resources