Getting empty merged list when trying to merge two dict lists with multiple when conditions in Ansible - list

here are my two list -
listone -
list1":
[
{
"_ref": "LAB1/1/4094",
"vlan_id": 12,
"vlan_name": "test_vlan_1",
"vlan_subnet": "192.168.1.0/28",
"vlan_vrf": "vrf_1"
},
{
"_ref": "LAB2/1/4094",
"vlan_id": 12,
"vlan_name": "test_vlan_1",
"vlan_subnet": "192.168.2.0/28",
"vlan_vrf": "vrf_1"
}
]
secondlist -
"list2":
[
{
"_ref": "LAB1/1/4094",
"vlan_id": "12",
"vlan_name": "test_vlan_1",
"vlan_ref": "vlan/ZG5zLnZsYW4kLmNvbS5pbmZvYmxveC5kbnMudmxhbl92aWV3JElORlJBTEFCLjEuNDA5NC4xMg:LAB1/test_vlan_1/12"
}
]
- name: merge lists final
set_fact:
merged_list: "{{ merged_list|default([]) + [ item[0] | union(item[1]) ] }}"
when: item[0]._ref == item[1]._ref and item[0].vlan_id == item[1].vlan_id and item[0].vlan_name == item[1].vlan_name
loop: "{{ query('nested', list1, list2) }}"
- name: print results
debug:
var: merged_list
I get output as:
TASK [print results] ***********************************************************
ok: [localhost] => {
"merged_list": "VARIABLE IS NOT DEFINED!"
}
desired output: in below, if you notice list2 has a match with list1 first dict item so list2 item merged with list1 first item, in the final output i want merged with a match and also unmatched item of list1 which is second one.
"merged_list":
[
{
"_ref": "LAB1/1/4094",
"vlan_id": 12,
"vlan_name": "test_vlan_1",
"vlan_subnet": "192.168.1.0/28",
"vlan_vrf": "vrf_1",
"vlan_ref": "vlan/ZG5zLnZsYW4kLmNvbS5pbmZvYmxveC5kbnMudmxhbl92aWV3JElORlJBTEFCLjEuNDA5NC4xMg:LAB1/test_vlan_1/12"
},
{
"_ref": "LAB2/1/4094",
"vlan_id": 12,
"vlan_name": "test_vlan_1",
"vlan_subnet": "192.168.2.0/28",
"vlan_vrf": "vrf_1"
}
]
i believe when: item[0]._ref == item[1]._ref and item[0].vlan_id == item[1].vlan_id and item[0].vlan_name == item[1].vlan_name condition is not working as i expected. Any suggestions/clue to make this work and get merged_list as the way i am looking for.

The items in the lists are dictionaries. You need the filter combine to merge them. For example, the playbook below (simplified for testing)
- hosts: localhost
vars:
list1:
- {_ref: ZG5, name: LAB1, vlan_id: 12, vlan_name: test_vlan_1}
- {_ref: XXX, name: LAB2, vlan_id: 12, vlan_name: test_vlan_1}
- {_ref: YYY, name: LAB3, vlan_id: 13, vlan_name: test_vlan_1}
list2:
- {_ref: ZG5, vlan_id: 12, vlan_name: test_vlan_1, vlan_ref: vlan/ZG5}
tasks:
- set_fact:
merged_list: "{{ merged_list|d([]) + [_item] }}"
loop: "{{ query('nested', list1, list2) }}"
vars:
_conditions:
- "{{ item.0._ref == item.1._ref }}"
- "{{ item.0.vlan_id == item.1.vlan_id }}"
- "{{ item.0.vlan_name == item.1.vlan_name }}"
_item: "{{ _conditions is all|ternary(item.0|combine(item.1), item.0) }}"
- debug:
var: merged_list
gives
merged_list:
- {_ref: ZG5, name: LAB1, vlan_id: 12, vlan_name: test_vlan_1, vlan_ref: vlan/ZG5}
- {_ref: XXX, name: LAB2, vlan_id: 12, vlan_name: test_vlan_1}
- {_ref: YYY, name: LAB3, vlan_id: 13, vlan_name: test_vlan_1}

Related

How to start %index% with 1 in OS::Heat::ResourceGroup

I use %index% in OS::Heat::ResourceGroup and it starts with a 0 value.
I want it to start with the value of 1. How to do it?
resources:
server_group:
type: OS::Heat::ResourceGroup
properties:
count: 3
resource_def:
type: OS::Nova::Server
name: { get_param: [ server_names, '%index%' ] }
I tried with '%index% + 1' but it did not work:
Error return: invalid literal for int() with base 10: '0 + 1'

How to compare or lookup specific values from two lists in ansible and print

How to compare two lists in ansible and print desired output below, i have looked other posts in stackoverflow regarding comparing/lookup list , but the way i want desired output is a bit different, unable to figure out the logic to get this working. Please point me to any post that can help me.Thanks
List1:
[
{
"DES": "server1",
"PORT": "E46",
},
{
"DESCRIP": "server2",
"PORT": "E47",
},
{
"DESCRIP": "server3",
"PORT": "E4",
},
{
"DESCRIP": "server4",
"PORT": "E7",
}
]
List2:
[
{
"INTERFACES": [
"E6",
"E8",
"E9",
"E10",
"E11",
"E12",
"E13",
"E14",
"E27",
"E28",
"E37",
"E43",
"E44",
"E45",
"E46",
"E47"
],
"VLAN_ID": "17"
},
{
"INTERFACES": [
"E7",
"E10",
"E11",
"E12",
"E13",
"E14",
"E27",
"E28",
"E45",
"E46"
],
"VLAN_ID": "16"
}
]
desired output:
If list1 PORT value matches/present in list2 INTERFACES then print something like this: server1 E46 has: VLAN_ID: 17 ; VLAN_ID: 16 or server2 E47 has: VLAN_ID: 17 ; NO VLAN_ID: 16
If list1 PORT value dosen't matches/present in list2 INTERFACES then print something like server3 E4 has NO VLAN_ID: 17 NO VLAN_ID: 16
below is full desired print:
server1 E46 has: VLAN_ID: 17 ; VLAN_ID: 16
server2 E47 has: VLAN_ID: 17 ; NO VLAN_ID: 16
server3 E4 has: NO VLAN_ID: 17 ; NO VLAN_ID: 16
server4 E7 has: VLAN_ID: 16 ; NO VLAN_ID: 17
For example
- set_fact:
srv_id: "{{ srv_id|d({})|combine({item.DESCRIP: _id}) }}"
loop: "{{ List1 }}"
vars:
_id: "{{ List2|
selectattr('INTERFACES', 'contains', item.PORT)|
map(attribute='VLAN_ID')|
list }}"
gives
srv_id:
server1: ['17', '16']
server2: ['17']
server3: []
server4: ['16']
(Formatting should be trivial.)

with_subelements when subelement is a dictionary

Say I have the following dict defined in my yml
st1:
d1:
f1: fv1
f2: fv2
d:
df1: dfv1
df2: dfv2
d2:
f1: fv3
f2: fv4
d:
df1: dfv3
df2: dfv4
I was able to iterate over the above using with_subelements. like this
with_subelements:
- st1
- d
But I get the following error when I run the playbook.
FAILED! => {"msg": "the key d should point to a list, got '{u'df1':
u'dfv1', u'df2': u'dfv2'}'"}
I am using ansible version 2.4
how can I take only values of dict d into a list to get success with the above playbook.
dictsort or dict2items didn't work for me
It's unfortunate that you are stuck with a bad design. Bad designs result in bad code. So here's some bad code:
---
- hosts: localhost
become: no
connection: local
tasks:
- set_fact:
st1_json: "{{ st1 | to_json }}"
- shell: echo '{{ st1_json }}' | jq '.[].d' | grep ':' | cut -d'"' -f4 | tr '\n' ',' | sed -e 's/^/[/' -e 's/,$/]/' -e 's/,/, /g'
register: output
- debug:
msg: "{{ output.stdout }}"
Pretty hideous. But it gets the desired output:
TASK [debug] ************************************************************************************************************************
ok: [localhost] => {
"msg": "[dfv3, dfv4, dfv1, dfv2]"
}
Enjoy.
OK -- I'm not entirely sure what you want for output, but....
I first restructured the yaml file:
---
st1:
- name: d1
f1: fv1
f2: fv2
d:
- df1: dfv1
- df2: dfv2
- name: d2
f1: fv3
f2: fv4
d:
- df1: dfv3
- df2: dfv4
Then, the play looks like this:
- debug:
var: item.1
with_subelements:
- "{{ st1 }}"
- d
The output, then, looks like this:
TASK [debug] **********************************************************************************************************************************
ok: [localhost] => (item=None) => {
"item.1": {
"df1": "dfv1"
}
}
ok: [localhost] => (item=None) => {
"item.1": {
"df2": "dfv2"
}
}
ok: [localhost] => (item=None) => {
"item.1": {
"df1": "dfv3"
}
}
ok: [localhost] => (item=None) => {
"item.1": {
"df2": "dfv4"
}
}
It would help if you give us some expected output. However, you need lists to use with_subelements. If you cannot change the data file, you have to change the playbook. If you want to see all the df1 values, then this will do it:
- debug:
msg: "{{ item.key }}: {{ item.value.d.df1 }}"
with_dict: "{{ st1 }}"
Using your dictionary file, I got this:
TASK [debug] **********************************************************************************************************************************
ok: [localhost] => (item=None) => {
"msg": "d2: dfv3"
}
ok: [localhost] => (item=None) => {
"msg": "d1: dfv1"
}

Filter data being passed into a crossfilter group without using dimensional filters

I'm trying to figure out how to add a filter onto a crossfilter group that is not related to a dimensional filter. Let's look at an example:
var livingThings = crossfilter({
// Fact data.
{ name: “Rusty”, type: “human”, legs: 2 },
{ name: “Alex”, type: “human”, legs: 2 },
{ name: “Lassie”, type: “dog”, legs: 4 },
{ name: “Spot”, type: “dog”, legs: 4 },
{ name: “Polly”, type: “bird”, legs: 2 },
{ name: “Fiona”, type: “plant”, legs: 0 }
}); //taken from http://blog.rusty.io/2012/09/17/crossfilter-tutorial/
if we were to make a dimension on type and a group of that dimension:
var typeDim = livingThings.dimension(function(d){return d.type});
var typeGroup = typeDim.group();
we would expect typeGroup.top(Infinity) to output
{{human:2},
{dog:2},
{bird:1},
{plant:1}}
My question is how can we filter the data such that they include only 4 legged creatures in this grouping? I also don't want to use dimension.filter... because i don't want this filter to be global, just for this one grouping. In other words
var filterDim = livingThings.dimension(function(d){return d.legs}).filterExact(4);
is not allowed.
I'm thinking of something similar to what I did to post-filter dimensions as in https://stackoverflow.com/a/30467216/4624663
basically I want to go into the internals of the typeDim dimension, and filter the data before it is passed into the groups. Creating a fake group that calls typeDim.group().top() will most likely not work as the individual livingThings records are already grouped by that point. I know this is tricky: thanks for any help.
V
Probably best to use the reduceSum functionality to create a pseudo-count group that only counts records with 4 or more legs:
var livingThings = crossfilter({
// Fact data.
{ name: “Rusty”, type: “human”, legs: 2 },
{ name: “Alex”, type: “human”, legs: 2 },
{ name: “Lassie”, type: “dog”, legs: 4 },
{ name: “Spot”, type: “dog”, legs: 4 },
{ name: “Polly”, type: “bird”, legs: 2 },
{ name: “Fiona”, type: “plant”, legs: 0 }
}); //taken from http://blog.rusty.io/2012/09/17/crossfilter-tutorial/
var typeDim = livingThings.dimension(function(d){return d.type});
var typeGroup = typeDim.group().reduceSum(function(d) {
return d.legs === 4 ? 1 : 0;
});
That will sum across a calculated value that will be 1 for records with 4 legs and 0 for records with ... not 4 legs. In other words, it should just count 4-legged creatures.
I think, this is what you are looking for. Comment back if I'm wrong.
var dimByLegs = livingThings.dimension(function(d){return d.legs});
dimByLegs.filterExact(4);
var dogs = dimByLegs.group();
dimByLegs.top(Infinity).forEach(function(d){console.log(d.type, d.legs);});
dimByLegs.dispose();

Couchbase View _count Reduce For Given Keys

I am trying to write a view in Couchbase using a reduce such as _count which will give me a count of the products at an address.
I have some documents in the database in the following format;
Document 1
{
id: 1,
address: {
street: 'W Churchill St'
city: 'Chicago',
state: 'IL',
},
product: 'Cable'
}
Document 2
{
id: 2,
address: {
street: 'W Churchill St'
city: 'Chicago',
state: 'IL',
},
product: 'Cable'
}
Document 3
{
id: 3,
address: {
street: 'W Churchill St'
city: 'Chicago',
state: 'IL',
},
product: 'Satellite'
}
Document 4
{
id: 4,
address: {
street: 'E Foster Rd'
city: 'New York',
state: 'NY',
},
product: 'Free To Air'
}
I already have a view which gives me all the products at an address which uses a composite key such as;
emit([doc.address.street, doc.address.city, doc.address.state], null)
Now this leads me on to the actual problem, I want to be able to get a count of products at a address or addresses.
I want to be able to see for an array of "keys"
['W Churchill St','Chicago','IL']
['E Foster Rd','New York','NY']
which products and a count of them. So i would expect to see in my results.
'Cable' : 2,
'Satellite': 1,
'Free To Air': 1
however if I specified only this "key",
['W Churchill St','Chicago','IL']
I would expect to see
'Cable' : 2,
'Satellite': 1
How to write my view to accommodate this?
The solution to this was to append my product to the key like so;
emit([doc.address.street, doc.address.city, doc.address.state, doc.product], null)
Then using;
?start_key=[street,city,state]&end_key=[street,city,state,{}]&group_level=4
Result:
{"rows":[
{"key":['W Churchill St','Chicago','IL','Cable'], "value":2},
{"key":['W Churchill St','Chicago','IL','Satellite'], "value":1}
]}
I would then need to repeat this query for each of the addresses and sum the results.