In part 3 of our Ansible for SAN Automation series I'm going to document how I build automations targeting deployment and configuration management and routine tasks on a Pure Storage FlashArray//X. Take my word for it, Pure Storage's FlashArray//X has to be the nicest all-flash storage array on the market. I cant overstate the value that Pure has packed into this product, but one of its most useful features I find is that Pure offers a very feature complete Ansible module collection for both FlashArray and FlashBlade. Lets jump right in to using it.
An important prerequisite to use the ansible Pure Storage module is that you will need to log into your array and generate an API key for your user account. Or even better create a new ansible service account and generate for that user. In current versions of Purity OS this usually found in Settings > Access > Users > Create API Token. The API key will be used below assigned as the var fa_api
.
First things first, your inventory of your control node should have entires that appear as follows for your Pure Storage FlashArrays. Where you keep your vars is a bit of a personal preference, in the example below I have placed them in my inventory file which is a sin to some. Place them where you prefer just bear in mind Ansible's variable precedence.
[purefa]
PSFAX01.example.net fa_api=asfq1394_jfas923nas
PSFAX02.example.net fa_api=asfq1394_jfas923nas
[purefa:vars]
fa_url="{{ inventory_hostname }}"
ansible_user=admin
ansible_password=!vault...
ansible_become=yes
[all:vars]
domain_name=example.net
Now lets make our first playbook. How about something simple, updating the Array name in Purity OS.
#!/bin/ansible-playbook
---
- name: "##### Sets the Array name on FlashArray #####"
hosts: '{{target|default("purefa")}}'
gather_facts: False
tasks:
# Set the hostname on FlashArrays
- name: Set the FlashArray name
delegate_to: localhost
purestorage.flasharray.purefa_arrayname:
name: "{{ inventory_hostname_short }}"
state: present
fa_url: "{{ fa_url }}"
api_token: "{{ fa_api }}"
Above "create-hosts.yml" Playbook available on my Github.
So let me explain a few things about our above playbook.
#! /path/to/ansible-playbook
as the first line of my playbooks, this allows the playbook to be ran like a normal script assuming it has the posix execute mode bit.purefa
and we put our flasharray hostnames in this group. In the example I have PSFAX01.example.net and PSFAX02.example.net, instead you would want to list your flasharray's DNS resolvable hostname. Short names like PSFAX01
are just fine in your inventory as long as its resolvable and your DNS supports it. Whatever you make this value we will name the array however I have specified to always name the array the short DNS name using the special variable. Alter this value as you see fit.delegate_to: localhost
. I recommend you go read up about ansible delegation. What I'm saying is all the "work" for this play is going to be done on the localhost. We don't run the whole playbook against the localhost because that solution doesn't scale if you have more than one flasharray. This way we can run the playbook against our entire inventory, all of our flash arrays, or we can --limit PSFAX01.example.net
and only run against a single host. When we build a playbook with this method we will almost always delegate_to: localhost
on nearly every task. Just one of the idiosyncrasies of working with a fleet of storage devices like this.After your have your playbook file created you can run it on your ansible control node. When you run this it should update the array name to whatever you have listed as the name in the inventory file. This is great for forcing configurations against a fleet and preventing unauthorized of unintended changes. You could schedule a playbook like this to run nightly, it could set and reset any configuration variable like defining DNS servers and NTP sources just for example. Or maybe setting a MOTD message banner. Sky is the limit.
Ok, lets do something a bit more advanced. Lets create a playbook that helps us do a pretty common task; define new hosts and associate the WWPNs (or IQNs or NQNs). I have found a very user friendly way to do this is to pull the host's values from a comma separated value spreadsheet so each row represents a host. This method also scales very well, need to add 100 vmware hosts to 4 different storage arrays? Using this .csv method it's no problem.
So open Excel and create a spreadsheet called "hosts.csv". Fill it as the example below:
Hostname,Platform,WWPNA,WWPNB,IQN,NQN
esx-server01,esxi,56:2:22:11:22:88:11:67,56:2:22:11:22:88:11:68,,
esx-server02,esxi,56:2:22:11:22:88:11:71,56:2:22:11:22:88:11:72,iqn.1994-05.com.vmware:7d366003914,
aix-server01,aix,,,,nqn.2014-08.com.vendor:nvme:nvm-subsystem-sn-d78432
Put your hostnames for the new servers in the Hostname
column. If using Fibre Channel put your WWPNs in the two columns WWPNA
and WWPNB
. If using iscsi put it in the IQN
column. NVMe-oF put it in the NQN
column. It is possible to fill out more than one column type for a single host (ie both fibre channel + iscsi) and pure says it will use the preferred method and fall back to the slower but I dont do this in production because I dont trust it but to be fair I havent tested it. If you have positive experience with this I would love to hear about it.
After our hosts.csv file is created and looking as you intend lets make our playbook:
#!/bin/ansible-playbook
########################
# DESCRIPTION
# Creates and Removes Hosts on Pure Storage FlashArrays
# DIRECTIONS
# Usage to create a single host
# Run ./create-hosts.yml --tags create-adhoc -e "Hostname=esx-server01,Platform=ESXi,WWPNA=56:2:22:11:22:88:11:67,WWPNB=56:2:22:11:22:88:11:68" -v
# Usage to remove a single host (note, Platform and WWPN/IQN values not required to remove)
# Run ./create-hosts.yml --tags remove-adhoc -e "Hostname=esx-server01" -v
# Usage to create multiple hosts using hosts.csv file:
# 1. create a file called hosts.csv in the same directory as this playbook.
# 2. Make columns titled "Hostname", "Platform", "WWPNA", and "WWPNB" if using FC. A column named IQN for iscsi, or NQN column for NVMe. Read about "Platform" values here under personaility - https://docs.ansible.com/ansible/latest/collections/purestorage/flasharray/purefa_host_module.html
# 3. fill in the values in all columns for all the hosts you want to create.
#
# Run ./create-hosts.yml --tags create-csv -v
# Usage to remove multiple hosts using hosts.csv file:
# Run ./create-hosts.yml --tags remove-csv -v
---
- name: "##### Create and Removes hosts on FlashArrays #####"
hosts: '{{target|default("purefa")}}'
gather_facts: False
strategy: free
tasks:
- name: Reading the csv file
read_csv:
path: hosts.csv
register: hosts_list
delegate_to: localhost
tags:
- create-csv
- remove-csv
- name: Creates all hosts from CSV file on FlashArrays
delegate_to: localhost
purestorage.flasharray.purefa_host:
name: "{{ item.Hostname }}"
personality: "{{ item.Platform }}"
nqn:
- "{{ item.NQN }}"
iqn:
- "{{ item.IQN }}"
wwns:
- "{{ item.WWPNA }}"
- "{{ item.WWPNB }}"
fa_url: "{{ fa_url }}"
api_token: "{{ fa_api }}"
loop: "{{ hosts_list.list }}"
tags:
- create-csv
- name: Removes all hosts from CSV file on FlashArrays
delegate_to: localhost
purestorage.flasharray.purefa_host:
name: "{{ item.Hostname }}"
state: absent
fa_url: "{{ fa_url }}"
api_token: "{{ fa_api }}"
loop: "{{ hosts_list.list }}"
tags:
- remove-csv
- pause:
delegate_to: localhost
prompt: "Enter the hostname to create:"
echo: true
register: hostname
- set_fact:
ask_user: "{{ hostname.user_input }}"
when: hostname == ""
tags:
- create-adhoc
- remove-adhoc
- name: Creates a single FC host on FlashArrays
delegate_to: localhost
purestorage.flasharray.purefa_host:
name: "{{ hostname }}"
personality: "{{ platform }}"
# nqn:
# - "{{ NQN }}"
# iqn:
# - "{{ IQN }}"
wwns:
- "{{ wwpna }}"
- "{{ wwpnb }}"
fa_url: "{{ fa_url }}"
api_token: "{{ fa_api }}"
tags:
- create-adhoc
- name: Removes a single FC host on FlashArrays
delegate_to: localhost
purestorage.flasharray.purefa_host:
name: "{{ hostname }}"
state: absent
fa_url: "{{ fa_url }}"
api_token: "{{ fa_api }}"
tags:
- remove-adhoc
Playbook "create-hosts.yml" also available on my Github.
Notice in the Creates a single FC host on FlashArrays
play task that all of the transport types other than FC WWPNA
and WWPNB
are commented out. This is done because when using the --tag create-adhoc
any options you are not using will fail if they are not commented out as ansible will complain there are "undefined variables". Basically, if you are using iscsi as your preferred connection method youll want to uncomment IQN and comment the other transport types. I prefer Fibre Channel so thats what I leave uncommented.
Directions for use are in the header comments of the playbook. So, give it a go and if you have any questions about its use feel free to leave a comment below.