Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

ACI lookup plugin #272

Open
netgab opened this issue Apr 21, 2022 · 7 comments
Open

ACI lookup plugin #272

netgab opened this issue Apr 21, 2022 · 7 comments
Labels
enhancement New feature or request

Comments

@netgab
Copy link

netgab commented Apr 21, 2022

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 netgab added the enhancement New feature or request label Apr 21, 2022
@JT252
Copy link
Contributor

JT252 commented Apr 26, 2022

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

@netgab
Copy link
Author

netgab commented Apr 27, 2022

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

@akinross
Copy link
Collaborator

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?

@netgab
Copy link
Author

netgab commented Aug 26, 2022

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.

@akinross
Copy link
Collaborator

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 }}"

@netgab
Copy link
Author

netgab commented Aug 26, 2022

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.

@akinross
Copy link
Collaborator

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

3 participants