Check conditions for all keys in a nested data using jmespath, rectify "leaning toothpick syndrome"

117 Views Asked by At

I've been trying to check conditions for all nodes,

('Config state '=='NSHUT' and ('State '=='OPERATIONAL' or 'State '=='IOS XR RUN'))`

I am expecting a single boolean value after the check.

Following is the nested dictionary I'm trying this on:

{
    "0/0/1": {
        "Config state ": "",
        "Node ": "0/0/1",
        "State ": "OK",
        "Type ": "NC55-MPA-12T-S"
    },
    "0/0/CPU0": {
        "Config state ": "NSHUT",
        "Node ": "0/0/CPU0",
        "State ": "IOS XR RUN",
        "Type ": "NC55-MOD-A-S"
    },
    "0/0/NPU0": {
        "Config state ": "",
        "Node ": "0/0/NPU0",
        "State ": "UP",
        "Type ": "Slice"
    },
    "0/FC0": {
        "Config state ": "NSHUT",
        "Node ": "0/FC0",
        "State ": "OPERATIONAL",
        "Type ": "NC55-5508-FC"
    },
    "0/FC1": {
        "Config state ": "NSHUT",
        "Node ": "0/FC1",
        "State ": "OPERATIONAL",
        "Type ": "NC55-5508-FC"
    },
    "0/FC2": {
        "Config state ": "NSHUT",
        "Node ": "0/FC2",
        "State ": "OPERATIONAL",
        "Type ": "NC55-5508-FC"
    },
    "0/FC3": {
        "Config state ": "NSHUT",
        "Node ": "0/FC3",
        "State ": "OPERATIONAL",
        "Type ": "NC55-5508-FC"
    },
    "0/FC4": {
        "Config state ": "NSHUT",
        "Node ": "0/FC4",
        "State ": "OPERATIONAL",
        "Type ": "NC55-5508-FC"
    },
    "0/FC5": {
        "Config state ": "NSHUT",
        "Node ": "0/FC5",
        "State ": "OPERATIONAL",
        "Type ": "NC55-5508-FC"
    },
    "0/FT0": {
        "Config state ": "NSHUT",
        "Node ": "0/FT0",
        "State ": "OPERATIONAL",
        "Type ": "NC55-5508-FAN"
    },
    "0/FT1": {
        "Config state ": "NSHUT",
        "Node ": "0/FT1",
        "State ": "OPERATIONAL",
        "Type ": "NC55-5508-FAN"
    },
    "0/FT2": {
        "Config state ": "NSHUT",
        "Node ": "0/FT2",
        "State ": "OPERATIONAL",
        "Type ": "NC55-5508-FAN"
    },
    "0/PM0": {
        "Config state ": "NSHUT",
        "Node ": "0/PM0",
        "State ": "OPERATIONAL",
        "Type ": "NC55-PWR-3KW-DC"
    },
    "0/PM1": {
        "Config state ": "NSHUT",
        "Node ": "0/PM1",
        "State ": "OPERATIONAL",
        "Type ": "NC55-PWR-3KW-DC"
    },
    "0/PM2": {
        "Config state ": "NSHUT",
        "Node ": "0/PM2",
        "State ": "OPERATIONAL",
        "Type ": "NC55-PWR-3KW-DC"
    },
    "0/PM3": {
        "Config state ": "NSHUT",
        "Node ": "0/PM3",
        "State ": "FAILED",
        "Type ": "NC55-PWR-3KW-DC"
    },
    "0/RP0/CPU0": {
        "Config state ": "NSHUT",
        "Node ": "0/RP0/CPU0",
        "State ": "IOS XR RUN",
        "Type ": "NC55-RP-E(Active)"
    },
    "0/RP1": {
        "Config state ": "NSHUT",
        "Node ": "0/RP1",
        "State ": "SW_INACTIVE",
        "Type ": "NC55-RP-E"
    },
    "0/SC0": {
        "Config state ": "NSHUT",
        "Node ": "0/SC0",
        "State ": "OPERATIONAL",
        "Type ": "NC55-SC"
    },
    "0/SC1": {
        "Config state ": "NSHUT",
        "Node ": "0/SC1",
        "State ": "OPERATIONAL",
        "Type ": "NC55-SC"
    }
}

I used JMESpath simulator and came up with

contains(*.["Config state "!='' && ("State "=='OPERATIONAL' || "State "=='IOS XR RUN')][], `false`)

This should give me the expected result but while using it in Ansible playbook, it produces the leaning toothpick syndrome, and it doesn't quite produce the expected output.

The Ansible task I wrote looks like:

- name: Query to display nodes
  set_fact:
    display_nodes: "{{ parsed_output | json_query(jmesquery) }}"
  vars:
    jmesquery: "contains(*.[\"Config state \"!='' && (\"State \"=='OPERATIONAL' || \"State \"=='IOS XR RUN')][], `false`)"
  
- debug:
    msg="{{ display_nodes }}"

Please help me rectify this.

1

There are 1 best solutions below

1
On

Two simple ways to avoid fighting with quotes in Ansible and JMESPath:

  1. Use YAML multilines notation

    some: >-
      as soon as you indent me properly, 
      I am a string that do not need quotes anymore
    
  2. As you did it already, use backticks in JMESPath literals, as advised by Ansible documentation:

    In the example above, quoting literals using backticks avoids escaping quotes and maintains readability.

    Source: https://docs.ansible.com/ansible/latest/user_guide/playbooks_filters.html#selecting-json-data-json-queries

So it ends up as simple as defining your jmesquery variable as:

jmesquery: >-
  contains(*.["Config state "!='' && ("State "=='OPERATIONAL' || "State "=='IOS XR RUN')][], `false`)

Given the playbook:

- hosts: all
  gather_facts: no

  tasks:
    - set_fact:
        display_nodes: "{{ parsed_output | json_query(jmesquery) }}"
      vars:
        jmesquery: >-
          contains(*.["Config state "!='' && ("State "=='OPERATIONAL' || "State "=='IOS XR RUN')][], `false`)
        parsed_output:
          0/0/1:
            'Config state ': ''
            'Node ': 0/0/1
            'State ': OK
            'Type ': NC55-MPA-12T-S
          0/0/CPU0:
            'Config state ': NSHUT
            'Node ': 0/0/CPU0
            'State ': IOS XR RUN
            'Type ': NC55-MOD-A-S
          0/0/NPU0:
            'Config state ': ''
            'Node ': 0/0/NPU0
            'State ': UP
            'Type ': Slice
          0/FC0:
            'Config state ': NSHUT
            'Node ': 0/FC0
            'State ': OPERATIONAL
            'Type ': NC55-5508-FC
          0/FC1:
            'Config state ': NSHUT
            'Node ': 0/FC1
            'State ': OPERATIONAL
            'Type ': NC55-5508-FC
          0/FC2:
            'Config state ': NSHUT
            'Node ': 0/FC2
            'State ': OPERATIONAL
            'Type ': NC55-5508-FC
          0/FC3:
            'Config state ': NSHUT
            'Node ': 0/FC3
            'State ': OPERATIONAL
            'Type ': NC55-5508-FC
          0/FC4:
            'Config state ': NSHUT
            'Node ': 0/FC4
            'State ': OPERATIONAL
            'Type ': NC55-5508-FC
          0/FC5:
            'Config state ': NSHUT
            'Node ': 0/FC5
            'State ': OPERATIONAL
            'Type ': NC55-5508-FC
          0/FT0:
            'Config state ': NSHUT
            'Node ': 0/FT0
            'State ': OPERATIONAL
            'Type ': NC55-5508-FAN
          0/FT1:
            'Config state ': NSHUT
            'Node ': 0/FT1
            'State ': OPERATIONAL
            'Type ': NC55-5508-FAN
          0/FT2:
            'Config state ': NSHUT
            'Node ': 0/FT2
            'State ': OPERATIONAL
            'Type ': NC55-5508-FAN
          0/PM0:
            'Config state ': NSHUT
            'Node ': 0/PM0
            'State ': OPERATIONAL
            'Type ': NC55-PWR-3KW-DC
          0/PM1:
            'Config state ': NSHUT
            'Node ': 0/PM1
            'State ': OPERATIONAL
            'Type ': NC55-PWR-3KW-DC
          0/PM2:
            'Config state ': NSHUT
            'Node ': 0/PM2
            'State ': OPERATIONAL
            'Type ': NC55-PWR-3KW-DC
          0/PM3:
            'Config state ': NSHUT
            'Node ': 0/PM3
            'State ': FAILED
            'Type ': NC55-PWR-3KW-DC
          0/RP0/CPU0:
            'Config state ': NSHUT
            'Node ': 0/RP0/CPU0
            'State ': IOS XR RUN
            'Type ': NC55-RP-E(Active)
          0/RP1:
            'Config state ': NSHUT
            'Node ': 0/RP1
            'State ': SW_INACTIVE
            'Type ': NC55-RP-E
          0/SC0:
            'Config state ': NSHUT
            'Node ': 0/SC0
            'State ': OPERATIONAL
            'Type ': NC55-SC
          0/SC1:
            'Config state ': NSHUT
            'Node ': 0/SC1
            'State ': OPERATIONAL
            'Type ': NC55-SC
    - debug:
        var: display_nodes

This yields the recap:

PLAY [all] *********************************************************************

TASK [set_fact] ****************************************************************
ok: [localhost]

TASK [debug] *******************************************************************
ok: [localhost] => 
  display_nodes: true

PLAY RECAP *********************************************************************
localhost                  : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0