#Seeking help to convert my ZAPI based playbook to REST based

1 messages · Page 1 of 1 (latest)

rough ridge
#

I am working on a playbook to create export policy rules based on the entered volumes and clients for Linux admin who doesn't have the access to the cluster. It works but there are still a lot of things to work out. Before I continue on those things, I would like to convert it to REST based first.
`---

  • hosts: localhost
    collections:
    • netapp.ontap
      name: create export-policy-rule task
      gather_facts: no
      vars_files:
    • variables.yml
      vars:
      vol_name: volume123
      vserver: initialization
      export_policy: initialization
      tasks:
    • name: Info
      na_ontap_info:
      state: info
      hostname: "{{ hostname }}"
      username: "{{ username }}"
      password: "{{ password }}"
      https: true
      validate_certs: false
      gather_subset:
      - volume_info
      register: my_ontap
    • set_fact:
      vserver: "{{ my_ontap.ontap_info.volume_info[item].volume_id_attributes.owning_vserver_name }}"
      export_policy: "{{ my_ontap.ontap_info.volume_info[item].volume_export_attributes.policy }}"
      with_items: "{{ my_ontap.ontap_info.volume_info }}"
      when: my_ontap.ontap_info.volume_info[item] is search ("{{ vol_name }}")
    • name: create the rule
      na_ontap_export_policy_rule:
      state: present
      policy_name: "{{ export_policy }}"
      vserver: "{{ vserver }}"
      client_match: 1.1.1.0/24
      ro_rule: sys
      rw_rule: sys
      protocol: nfs
      super_user_security: sys
      allow_suid: true
      hostname: "{{ hostname }}"
      username: "{{ username }}"
      password: "{{ password }}"
      https: true
      validate_certs: false
    • debug:
      msg:
      • "{{ vserver }}"
      • "{{ export_policy }}"
        `
#

The playbook above works. But, if I added/changed the task "-name: Info" to REST based and as shown below, it won't work:
tasks:
` - name: Info
na_ontap_info:

  use_rest: auto       ### two new added lines
  state: info
  hostname: "{{ hostname }}"
  username: "{{ username }}"
  password: "{{ password }}"
  https: true
  validate_certs: false
  gather_subset:
    - volume_info
register: my_ontap`

Below are errors:
TASK [set_fact] *****************************************************************************************************************************************************************************************************************************
fatal: [localhost]: FAILED! => {"msg": "'dict object' has no attribute 'volume_info'"}

fresh vapor
#

I believe the module is called:
netapp.ontap.na_ontap_rest_info

rough ridge
#

My bad. I did use the correct module in the changed playbook, but got the same error messages as posted above.

  • name: Info

netapp.ontap.na_ontap_rest_info: ### two new added lines
use_rest: auto
state: info
hostname: "{{ hostname }}"
username: "{{ username }}"
password: "{{ password }}"
https: true
validate_certs: false
gather_subset:
- volume_info
register: my_ontap

grim jetty
rough ridge
#

Not sure of if I added right, but still the same error. Following is the updated code, and warning / errors. "volume_info" in the error is referring to the one used in "set_fact", right?
- name: Info netapp.ontap.na_ontap_rest_info: use_rest: auto hostname: "{{ hostname }}" username: "{{ username }}" password: "{{ password }}" https: true validate_certs: false use_python_keys: true gather_subset: - storage/volumes register: my_ontap

TASK [Info] ************************************************************************************************************************************
ok: [localhost]
[WARNING]: conditional statements should not include jinja2 templating delimiters such as {{ }} or {% %}. Found:
my_ontap.ontap_info.volume_info[item] is search ("{{ vol_name }}")

TASK [set_fact] ********************************************************************************************************************************
fatal: [localhost]: FAILED! => {"msg": "'dict object' has no attribute 'volume_info'"}

grim jetty
#

Sorry i can't tell from the below. If the info task executing with an error

or is it a task after when your getting data?

#

If it the second do note that the structure that is returned will be diffrent
`
- name: Get info
netapp.ontap.na_ontap_rest_info:
use_python_keys: true
use_rest: Always
gather_subset:
- volume_info
<<: *login
register: results

- name: Print return information from the previous task
  ansible.builtin.debug:
    var: results`
#

Will return something like this

ok: [localhost] => { "results": { "changed": false, "failed": false, "ontap_info": { "storage_volumes": { "_links": { "self": { "href": "/api/storage/volumes?max_records=1024&fields=" } }, "num_records": 9, "records": [ { "_links": { "self": { "href": "/api/storage/volumes/2a21c556-a65a-11ed-8f2b-005056b3357c" } }, "name": "test", "uuid": "2a21c556-a65a-11ed-8f2b-005056b3357c" },

rough ridge
#

Yes, your code(without set_fact task) works for me as well. So, it means that "sec_fact" executing with an error. In REST API, "my_ontap.ontap_info" has no attribute "volume_info"?

  • set_fact:
    vserver: "{{ my_ontap.ontap_info.volume_info[item].volume_id_attributes.owning_vserver_name }}"
    export_policy: "{{ my_ontap.ontap_info.volume_info[item].volume_export_attributes.policy }}"
    with_items: "{{ my_ontap.ontap_info.volume_info }}"
    when: my_ontap.ontap_info.volume_info[item] is search ("{{ vol_name }}")

TASK [set_fact] *
fatal: [localhost]: FAILED! => {"msg": "'dict object' has no attribute 'volume_info'"}

grim jetty
#

Yes that correct

Volume_info was a zapi thing The rest version is going to be
storage_volumes

rough ridge
#

Not clear on what should I do to accommodate the chnage. Sorry, I am slow.

  1. If I use the following in task Info which I already tried:
    gather_subset:
    - storage/volumes
    It resulted in the same error as I posted above.

  2. Just to make sure we are in the same page that the error came from executing "set_fact". If I changed all 4 "volume_info" in task "set_fact", it resulted in following errors, if that was what you would want me to do?

TASK [set_fact] *****************************************************************************************************************************************************************************************************************************
fatal: [localhost]: FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: 'list object' has no attribute 'volume_id_attributes'\n\nThe error appears to be in './create_export_rule.yml': line 26, column 5, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n register: my_ontap\n - set_fact:\n ^ here\n"}

white panther
#

The return values for na_ontap_rest_info and na_ontap_info differ quite much. So you will have to adapt for that. As Chris showed you, you have to address ontap_info.storage_volumes.records in your set_fact tasks.

rough ridge
#

Thanks for your messages. I did some research.

Here is my difficulty:
In ZAPI, in set_fact, I can use: ontap_info.volume_info[item].volume_id_attributes.owning_vserver_name

In REST, "storage_volumes" can only provide volume name and uuid, nothing else. There are no attributes such as "volume_id_attributes.owning_vserver_name" and "ontap_info.volume_info[item].volume_export_attributes.policy" which I need to search for an export policy and vserver name upon the inputted volumes. With missing these attributes, how can I achieve the same from my original zapi code?

grim jetty
#

The REST version is based off the REST API. By default we return the fields the Api return by default. (Which is most cases is name and UUID).

To get additional fields you need a

fields: <list of fields>
You can get the list of fields here from the rest api as they may have different name from before
https://library.netapp.com/ecmdocs/ECMLP2884821/html/index.html#/storage/volume_efficiency_policy_collection_get
Or you can go here which include a mapping of Zapi fields to Rest fields
https://docs.netapp.com/us-en/ontap-restmap-9121/volume.html#volume-get-iter

In this case owning_vserver_name is now svm.name
And volume_export_attributes.policy is now nas.export_policy.name

So you would want your playbook to look like

netapp.ontap.na_ontap_rest_info: use_rest: auto hostname: "{{ hostname }}" username: "{{ username }}" password: "{{ password }}" https: true validate_certs: false use_python_keys: true gather_subset: - storage/volumes fields: - svm.name - nas.export_policy.name register: my_ontap

rough ridge
#

I did quite some practices on your message, Thanks, and wrote the following playbook. But, it cannot correctly print out fields 'name' and 'nas.export_policy.name' upon a known volume name as shown below, if you can please point out what went wrong?
`

  • hosts: localhost
    collections:
    • netapp.ontap
      gather_facts: false
      vars:
      login: &login
      hostname: "x.x.x.x"
      username: "admin"
      password: "netapp123"
      https: true
      validate_certs: false
      name: create an export policy rule
      tasks:
    • name: Get info
      netapp.ontap.na_ontap_rest_info:
      use_python_keys: true
      use_rest: Always
      fields:
      - 'name'
      - 'nas.export_policy.name'
      gather_subset:
      - storage/volumes
      parameters:
      name:
      ora_calprd_bkp_Azure
      <<: *login
      register: results2
    • set_fact:
      vserver: "{{ results2.ontap_info.storage_volumes }}"
    • name: Print return information from the previous task
      ansible.builtin.debug:
      #var: results2.ontap_info.storage_volumes.records[0].nas.export_policy.name
      msg: "vserver {{ vserver }}"`
grim jetty
#

It should be something like this

` tasks:
- name: Get info
netapp.ontap.na_ontap_rest_info:
use_python_keys: true
use_rest: Always
gather_subset:
- storage/volumes
fields:
- 'name'
- 'nas.export_policy.name'
<<: *login
register: results

- name: Print return information from the previous task
  ansible.builtin.debug:
    var: results

- name: Print the first name on the list
  ansible.builtin.debug:
    var: results.ontap_info.storage_volumes.records[0].nas.export_policy.name

- name: Print all names on a list
  ansible.builtin.debug:
    msg: "Here are the names: {{ item.nas.export_policy.name }}"
  with_items: "{{results.ontap_info.storage_volumes.records}}"`
rough ridge
#

Your playbook helped a lot. I have two more follow-ups:

  1. Instead of printing the first name on the list, I would like to print out a svm.name and nas.export_policy.name upon an inputted volume name, so, it could be any volume. As a first step, use a single volume for now. I tried to use "parameters:":
    parameters: name: "{{ vol_name }}"
    But, don't know where, which task I can put them in, or have to use a separated task? I couldn't get my head around
  2. Further, ultimately, my goal is to use svm name and export-policy name, along with the inputted volume name and clients to create a new task for creating a new export-policy-rule. So, how do I assign values of svm name and export-policy name that just retrieved from my question 1 to two variables and then I can use them for the new task?
    Appreciate your time!
grim jetty
#

For 1 it should be something like

tasks:
- name: Get info
netapp.ontap.na_ontap_rest_info:
use_python_keys: true
use_rest: Always
gather_subset:
- storage/volumes
fields:
- 'name'
- 'nas.export_policy.name'
parameters:
name: "{{ vol_name}}"
<<: *login
register: results

rough ridge
#

@grim jetty I have two final questions:

  1. There are 3 tasks in my playbook:
  • name retrieve vserver name and export-policy name upon entered volume in CLI
  • set_fact to assign values of vserver name andexport-policy name to two variables
  • create export-policy rule upon two variables above.
    I have to log into the cluster by using "<<: *login" in the first task. My question is: What is the technical reason of why I have to use "<<: *login" again in the third task? Cannot I just continue to execute the other module in the 3rd task without logging into the cluster again since I already did in first task?
  1. In my 3rd task, I have to use <<: *login before with_items statement like below and not after. Why?
    <<: *login
    with_items: "{{ lookup('file', 'files/clients.txt').splitlines() }}"

Appreciate all your help along the way.

grim jetty
#

each task in a playbook in a playbook make a separate call the REST api , which will valid your credential each time.

if your using one of the newer version of Ansible you can use Module Defaults (https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_module_defaults.html) to do something similar.

For the third question. the parms of the module your calling

Parms in Yaml have to be define under and indented under the module your calling

Loop are define after the module parmas.

`

  • name: with_items
    ansible.builtin.debug:
    msg: "{{ item }}"
    with_items: "{{ items }}"`