If I end setting up the same thing twice or need to step through a tedious workflow I tend to find myself looking to Ansible for pain relief. I've picked Ansible as my configuration management tool of choice as I can bend it to do whatever I like to anything I like with a language I can read and understand.

 

Ansible is written in Python and as with all things Python, Ansible is available in the Python Package Index (PyPi), on a node where pip is installed, simply run:

$ pip install ansible


Comprehensive documentation about Ansible is available on docs.ansible.com.

 

Today I was delighted to discover that the built-in URI module is capable of manipulating NimbleOS arrays via the comprehensive REST API (check it out on InfoSight!). Needless to say, we now have a fully fledged orchestration tool at our disposal!

 

I'm currently doing a lot testing of an upcoming and exciting Nimble Storage offering and find myself with an array completely cluttered with volumes in all shapes and sizes. I wanted to revert my array to a clean state without disrupting the rest of my configuration and volumes not affected by these tests. The volumes I want to get rid of have two distinct features: they are offline and the volume name contains a specific string. Challenge accepted.

 

The following Ansible playbook (it's a YAML file, attached below) contains some commentary of what some of the interesting parameters are doing (for clarity, I've put the entire workflow in one playbook):

# volume-reaper.yml

---

 

# We will use this node to interface with my Nimble Storage Group

- hosts: localhost

 

  # Since our array is remote, we don't care about collecting local facts

  gather_facts: no

 

  # Variables local for this play

  vars:

    base_url: https://nimble-array.local:5392/v1

    username: admin

    password: admin

 

  tasks:

  # First we need a token to be able to use the APIs

  - name: Get API token

    uri:

      url: "{{ vars.base_url }}/tokens"

      validate_certs: no

      method: POST

 

      # This is the HTTP status code that designates success

      status_code: 201

 

      # This ensures the correct Content-Type header is set

      body_format: json

 

      # The following structure will be converted to JSON on the fly

      body:

       data:

         username: "{{ vars.username }}"

         password: "{{ vars.password }}"

 

      # We want to access the content we retrieve

      return_content: yes

 

    # The returned data JSON object will be accessible in the token dictionary

    register: token

 

  # List all the volumes

  - name: Fetch all volumes and metadata

    uri:

      url: "{{ vars.base_url }}/volumes/detail?fields=name,online,id"

      method: GET

      validate_certs: no

      status_code: 200

      return_content: yes

 

      # Here's the token we got from a successful login above

      HEADER_X-Auth-Token: "{{ token.json.data.session_token }}"

 

    # All volume data is accessible through the volume dictionary

    register: volumes

 

  - name: Destroy Volumes if offline and contains the name MyTestVol

    uri:

      url: "{{ vars.base_url }}/volumes/{{ item.id }}"

      method: DELETE

      validate_certs: no

      status_code: 200

      return_content: yes

      HEADER_X-Auth-Token: "{{ token.json.data.session_token }}"

 

    # This task will iterate over all volumes

    with_items: "{{ volumes.json.data }}"

 

    # ... but only execute the task if the volume name contains MyTestVol and is offline

    when: "('MyTestVol' in item.name and not item.online)"

 

    # Note: The condition in the when: statement is a Jinja2 expression

 

So, to simply wipe my array from offline volumes that contains the keyword 'MyTestVol' in the volume name, one would simply run:

$ ansible-playbook volume-reaper.yml

[WARNING]: provided hosts list is empty, only localhost is available

 

 

PLAY [localhost] ***************************************************************

 

TASK [Get API token] ***********************************************************

ok: [localhost]

 

TASK [Fetch all volumes and metadata] ******************************************

ok: [localhost]

 

TASK [Destroy Volumes if offline and contains the name MyTestVol] *****************

skipping: [localhost] => (item={u'id': u'0633cfd76905aa2a50000000000000000000000016', u'name': u'MyProdVol3', u'online': True})

ok: [localhost] => (item={u'id': u'0633cfd76905aa2a5000000000000000000000001d', u'name': u'MyTestVol40', u'online': False})

skipping: [localhost] => (item={u'id': u'0633cfd76905aa2a5000000000000000000000001b', u'name': u'MyProdVol2', u'online': True})

 

PLAY RECAP *********************************************************************

localhost                  : ok=3    changed=0    unreachable=0    failed=0


$ _


Until there is a bleeding need for a native NimbleOS Ansible module, this will more than suffice for most use cases. Please note, for this particular Ansible playbook I was using NimbleOS 3.3, earlier versions have not been tested.

 

Please tell me what you automated today.

 

Michael