ansible-aci icon indicating copy to clipboard operation
ansible-aci copied to clipboard

ACI lookup plugin

Open netgab opened this issue 2 years ago • 7 comments

Community Note

  • Please vote on this issue by adding a 👍 reaction to the original issue to help the community and maintainers prioritize this request
  • Please do not leave "+1" or other comments that do not add relevant new information or questions, they generate extra noise for issue followers and do not help prioritize the request
  • If you are interested in working on this issue or have submitted a pull request, please leave a comment

Description

An Ansible lookup plugin would be helpful to query the REST API from the APIC and directly store the returned data (list) in a task / role variable or to define an Ansible loop. Currently, an aci_rest operation is needed as a dedicated task. The problem here is, that there is no way to register a variable for aci_rest operations to store the output. Therefore, a lookup plugin would be helpful

Example usage:

  tasks:
    - name: "Lookup test 1"
      vars:
        aci_query_target_filters:
          match: "and"
          filters:
            - attribute: "fvTenant.name"
              operator: "eq"
              value: "common"
      ansible.builtin.debug:
        msg: "{{ query('aci_lookup',
                        host='apic1.example.com',
                        port=443,
                        username='apic_admin',
                        password='apic_adminPw',
                        query_scope='class',
                        query_object='fvTenant',
                        query_target='self',
                        target_subtree_class=['fvAEPg', 'fvBD'],
                        query_target_filters=aci_query_target_filters) }}"

Of course, all possible REST options shall be implemented (e.g. rsp_subtree, rsp_subtree_class, ....)

New or Affected Module(s):

  • Proposed lookup name: aci_lookup

APIC version and APIC Platform

not relevant, because this is a generic approach

Collection versions

  • cisco.aci 2.1.0

References

  • #0000

netgab avatar Apr 21 '22 05:04 netgab

I like it. It would be great to export the results to a CSV file as an option.

JT252 avatar Apr 26 '22 17:04 JT252

So I wrote it myself to fulfill my project requirements and it's really helpful to fillout variables based on lookups within a single task. However I did the whole thing very quick and potentially very dirty so it does not make sense to contribute

netgab avatar Apr 27 '22 05:04 netgab

Hi @netgab,

I am not sure what you mean with your remark: "The problem here is, that there is no way to register a variable for aci_rest operations to store the output". Could you please elaborate a bit more what your exact requirement is and why this is not possible with current aci_rest module?

akinross avatar Aug 26 '22 11:08 akinross

Hi @akinross,

so the "problem" is as follows (maybe it's because of lacking Ansible skills) :)

If someone wants to query information from ACI using aci_rest, each query requires a dedicated Ansible task. Example:

- name: "ACI REST query operation"
  cisco.aci.aci_rest:
    #...
    method: get
    path: "/api/class/fvTenant.json"
  delegate_to: localhost
  register: aci_query_result

If I want to query a lot of stuff, I need one task per query and register one "fact" per query. My initial statement:

The problem here is, that there is no way to register a variable for aci_rest operations to store the output.

is not quite correct, because aci_rest provides a register option.

However a lookup plugin allows you to perform complex single tasks like:

- name: "Check if baremetal EPGs exists"
  vars:
    queryFilter:
      filters:
        - attribute: "fvAEPg.name"
          value: "{{ item.name }}"
    epgQuery: "{{ lookup('aci_lookup',
                  host=apic_connection,
                  username=apic_username,
                  password=apic_password,
                  validate_certs=(apic_validate_cert | default(True) | bool),
                  query_object='fvAEPg',
                  query_target_filter=queryFilter) }}"
  ansible.builtin.assert:
    that: "epgQuery | length == 1"
    fail_msg: "EPG {{ item.name }} does not exist - abort"
    success_msg: "EPG {{ item.name }} exists - continue"
  loop: "{{ epgs }}"
  loop_control:
    label: "{{ item.name }}"  

I'm (right now) not saying you cannot solve this using aci_rest, but you need at least two tasks to get this.... and this might become very complex, because if checking the example above:

Based on a list of EPG names (variable epgs), the task checks if the epgs exists in ACI using a query. The variable epgQuery above is the result from the lookup plugin, which is at the end of the day a list of dictionaries (imdata). If you use aci_query in a loop and register a variable, I guess the variable will be somehow magically concanated... so at the end of the day if the task is done, we have a new fact (hopefully), containing a list of dictionaries (imdata). An alternative would be to query all EPGs without a loop and register the result... It doesn't matter...

We cannot do an assert or anything else within this task, because the task action is aci_rest.

Now we need to check whether our EPGs (names in the list epgs) exists within this registered fact. So we need to verify if an loop item exists within a list of complex dictionaries. Either you can solve this somehow with inner and outer loop magic or using special stuff like map, selectattr, json_query magic... At least I'm not clever enough for this. Even if I manage it, I don't understand this jq expression when I check back to my code after some time.

I'm keen to learn how to solve my little task above with the use of aci_rest only.

netgab avatar Aug 26 '22 11:08 netgab

Hi @netgab,

Yes you would need two tasks but is that a bad thing?

A possibility would be to do some sort of assert like:

  - name: query epgs
    vars:
      epgs:
        - consumer_epg_a
        - missing_epg
    cisco.aci.aci_rest:
      <<: *aci_info
      method: get
      path: /api/class/uni/tn-common/fvAEPg.json?query-target-filter=eq(fvAEPg.name,"{{ item }}")
    register: query_result_class_epg
    loop: "{{ epgs }}"

  - name: assert epgs
    vars:
      input_epg: "{{ item.item }}"
      output_epg: "{{ (item.imdata | length != 0) | ternary(item.imdata.0.fvAEPg.attributes.name, 'MISSING') }}"
    ansible.builtin.assert:
      that: input_epg == output_epg
    loop: "{{ query_result_class_epg.results }}"

akinross avatar Aug 26 '22 13:08 akinross

Hi @akinross, nicely done ... I have some tasks, with four or more variables, which are filled by the lookup plugin. But anyway. I thought it's a good idea and simplifies stuff for the user (similar to the great Netbox lookup plugin https://docs.ansible.com/ansible/latest/collections/netbox/netbox/nb_lookup_lookup.html).

Anyway - I solved it myself and wrote such a plugin. Since there are not a lot of "thumbsup" regarding this issue here, you may close it.

netgab avatar Aug 26 '22 13:08 netgab

Hi @netgab,

We will keep this issue open in case there is more interest in time. For now this has low priority, but in time perhaps there will be more interest from the community. Thank you for the idea contribution.

akinross avatar Aug 29 '22 13:08 akinross