'Create dictionary keys from variable and add list of IPs as values

I would like to create a dictionary and populated from variables both the keys and values. The values will be a list of IPs.
If the key is already present in the dictionary, then, I need to add the IP to the existing list.

Here is the input data, which is a list of list.

parse1.json:

[
    [
        "/RAD33G4099/mobile_app/pl_crad.la-dc.vivo.com_80",
        "properties",
        "members",
        "/Common/10.50.100.25:80",
        "session"
    ],
    [
        "/RAD33G4099/mobile_app/pl_crad.la-dc.vivo.com_80",
        "properties",
        "members",
        "/Common/10.50.100.39:80",
        "session"
    ],
    [
        "/RAD33G4099/mobile_app/pl_crad.la-dc.vivo.com_80",
        "properties",
        "members",
        "/Common/10.50.100.49:80",
        "session"
    ],
    [
        "/RAD33G4099/mobile_app/pl_crad.la-dc.vivo.com_443",
        "properties",
        "members",
        "/Common/10.50.100.18:80",
        "session"
    ],
    [
        "/RAD33G4099/mobile_app/pl_crad.la-dc.vivo.com_443",
        "properties",
        "members",
        "/Common/10.50.100.28:80",
        "session"
    ],
    [
        "/RAD33G4099/mobile_app/pl_crad.la-dc.vivo.com_443",
        "properties",
        "members",
        "/Common/10.50.100.48:80",
        "session"
    ]
]

Here is my trial at solving it:

 - set_fact:
     my_dic: {} 
     disabled_host: "{{ lookup('file', 'parse1.json')   }}"  

 - name: add new key / value pairs to dict
      set_fact:
        my_dict_var: "{{ my_dict_var + [ my_dic | combine ( { name + '_' +  app_name : host } ,  recursive=True) ] }}"
      loop: "{{ disabled_host }}"
      vars:
        name: "{{ item[0]  | regex_replace('^\/.*\/(.*)$', '\\1') }}"
        app_name: "{{ item[0] | regex_replace('^\/.*\/(.*)\/.*$', '\\1')  }}"
        host: "{{ item[3] | regex_replace('^\/.*\/(.*):.*$', '\\1')  }}"

The actual output is

ok: [localhost] => {
    "msg": [
        {
            "pl_crad.la-dc.vivo.com_80_mobile_app": "10.50.100.25"
        },
        {
            "pl_crad.la-dc.vivo.com_80_mobile_app": "10.50.100.39"
        },
        {
            "pl_crad.la-dc.vivo.com_80_mobile_app": "10.50.100.49"
        },
        {
            "pl_crad.la-dc.vivo.com_443_mobile_app": "10.50.100.18"
        },
        {
            "pl_crad.la-dc.vivo.com_443_mobile_app": "10.50.100.28"
        },
        {
            "pl_crad.la-dc.vivo.com_443_mobile_app": "10.50.100.48"
        }
    ]
}

But, I would like to create a list of IPs if the key is same.

My desired output is

{
  "pl_crad.la-dc.vivo.com_80_mobile_app": [ 
    "10.50.100.25", 
    "10.50.100.39", 
    "10.50.100.49" 
  ],
  "pl_crad.la-dc.vivo.com_443_mobile_app": [ 
    "10.50.100.18",
    "10.50.100.28", 
    "10.50.100.48"
  ]
}


Solution 1:[1]

You don't have to go the hard way and use regex, you can just split those strings, and / or, use basename and dirname.

  • Using dirname and basename:

    - set_fact:
      disabled_hosts_ips: >-
        {{
          disabled_hosts_ips | default({})
          | combine({
            name: (disabled_hosts_ips | default({}))[name] | default([]) 
              + [ip] 
          })
        }}
      loop: "{{ disabled_hosts }}"
      vars:
        name: "{{ item[0] | basename ~ '_' ~ item[0] | dirname | basename }}"
        ip: "{{ (item[3] | basename).split(':')[0] }}"
    
  • Using split:

    - set_fact:
      disabled_hosts_ips: >-
        {{
          disabled_hosts_ips | default({})
          | combine({
            name: (disabled_hosts_ips | default({}))[name] | default([]) 
              + [ip]
          })
        }}
      loop: "{{ disabled_hosts }}"
      vars:
        name: "{{ item[0].split('/')[-2:] | reverse | join('_') }}"
        ip: "{{ item[3].split('/')[-1].split(':')[0] }}"
    

Those both give:

disabled_hosts_ips:
  pl_crad.la-dc.vivo.com_443_mobile_app:
  - 10.50.100.18
  - 10.50.100.28
  - 10.50.100.48
  pl_crad.la-dc.vivo.com_80_mobile_app:
  - 10.50.100.25
  - 10.50.100.39
  - 10.50.100.49

Solution 2:[2]

For example, using the variable my_dict_var

  my_dict_groups: "{{ dict(my_dict_keys|zip(my_dict_vals)) }}"
  my_dict_keys: "{{ dh_groups|map('first')|list }}"
  my_dict_vals: "{{ dh_groups|map('last')|map('map', attribute='value')|list }}"
  dh_groups: "{{ my_dict_var|map('dict2items')|flatten|groupby('key') }}"

gives

  my_dict_groups:
    pl_crad.la-dc.vivo.com_443_mobile_app:
    - 10.50.100.18
    - 10.50.100.28
    - 10.50.100.48
    pl_crad.la-dc.vivo.com_80_mobile_app:
    - 10.50.100.25
    - 10.50.100.39
    - 10.50.100.49

The complete set of the declarations is below

  my_dict_groups: "{{ dict(my_dict_keys|zip(my_dict_vals)) }}"
  my_dict_keys: "{{ dh_groups|map('first')|list }}"
  my_dict_vals: "{{ dh_groups|map('last')|map('map', attribute='value')|list }}"
  dh_groups: "{{ dh_keys|zip(dh_vals)|map('combine')|groupby('key') }}"
  dh_vals: "{{ disabled_host|
               map(attribute=3)|
               map('split', '/')|map('last')|
               map('split', ':')|map('first')|
               map('community.general.dict_kv', 'value')|
               list }}"
  dh_keys: "{{ disabled_host|
               map(attribute=0)|
               map('regex_replace', dh_regex, dh_replace)|
               map('community.general.dict_kv', 'key')|
               list }}"
  dh_regex: '^/.*?/(.*?)/(.*)$'
  dh_replace: '\2_\1'
  disabled_host: "{{ lookup('file', 'parse1.json') }}"

Details

In both cases use the filter groupby and create the list

  dh_groups:
  - - pl_crad.la-dc.vivo.com_443_mobile_app
    - - key: pl_crad.la-dc.vivo.com_443_mobile_app
        value: 10.50.100.18
      - key: pl_crad.la-dc.vivo.com_443_mobile_app
        value: 10.50.100.28
      - key: pl_crad.la-dc.vivo.com_443_mobile_app
        value: 10.50.100.48
  - - pl_crad.la-dc.vivo.com_80_mobile_app
    - - key: pl_crad.la-dc.vivo.com_80_mobile_app
        value: 10.50.100.25
      - key: pl_crad.la-dc.vivo.com_80_mobile_app
        value: 10.50.100.39
      - key: pl_crad.la-dc.vivo.com_80_mobile_app
        value: 10.50.100.49

Create the lists of the keys my_dict_keys and values my_dict_vals, and create the dictionary my_dict_groups

  my_dict_keys:
  - pl_crad.la-dc.vivo.com_443_mobile_app
  - pl_crad.la-dc.vivo.com_80_mobile_app
  my_dict_vals:
  - - 10.50.100.18
    - 10.50.100.28
    - 10.50.100.48
  - - 10.50.100.25
    - 10.50.100.39
    - 10.50.100.49

For example, put the declarations into the playbook vars

- hosts: localhost

  vars:
    my_dict_groups: "{{ dict(my_dict_keys|zip(my_dict_vals)) }}"
    my_dict_keys: "{{ dh_groups|map('first')|list }}"
    my_dict_vals: "{{ dh_groups|map('last')|map('map', attribute='value')|list }}"
    # dh_groups: "{{ my_dict_var|map('dict2items')|flatten|groupby('key') }}"
    dh_groups: "{{ dh_keys|zip(dh_vals)|map('combine')|groupby('key') }}"
    dh_vals: "{{ disabled_host|
                 map(attribute=3)|
                 map('split', '/')|
                 map('last')|map('split', ':')|map('first')|
                 map('community.general.dict_kv', 'value')|
                 list }}"
    dh_keys: "{{ disabled_host|
                 map(attribute=0)|
                 map('regex_replace', dh_regex, dh_replace)|
                 map('community.general.dict_kv', 'key')|
                 list }}"
    dh_regex: '^/.*?/(.*?)/(.*)$'
    dh_replace: '\2_\1'
    disabled_host: "{{ lookup('file', 'parse1.json') }}"
    # my_dict_var:
    #   - pl_crad.la-dc.vivo.com_80_mobile_app: 10.50.100.25
    #   - pl_crad.la-dc.vivo.com_80_mobile_app: 10.50.100.39
    #   - pl_crad.la-dc.vivo.com_80_mobile_app: 10.50.100.49
    #   - pl_crad.la-dc.vivo.com_443_mobile_app: 10.50.100.18
    #   - pl_crad.la-dc.vivo.com_443_mobile_app: 10.50.100.28
    #   - pl_crad.la-dc.vivo.com_443_mobile_app: 10.50.100.48

  tasks:
    - debug:
        var: my_dict_groups

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2