Skip to content

Repository files navigation

Purest (or Pure Rest, if you will) -- a simple gem for interacting with Pure Storage's FlashArray REST API

A simple to use library for Ruby, inspired by the WeAreFarmGeek's Diplomat gem (seriously, those guys are awesome), allowing for easy interaction with Pure Storage's FlashArray REST API.

Disclaimer

This started as a labor of love/learning exercise, and sort of blossomed into this. That being said, it means a few things:

  1. I may have made some stupid mistakes in here, if so..so be it. Raise them in issues or submit PRs, and I'll gladly fix/merge if I feel the code submitted carries the spirit of my little project. Odds are I won't reject a PR unless you try to rewrite everything for some obtuse reason I don't agree with.

  2. I am not an employee of Pure Storage, just a random nerd.

  3. While all of the classes exist, currently only up to API version 1.16 is 'officially' supported- meaning it may work on newer versions, but I can't verify since I've only been able to develop against versions 1.16 and lower.

Table of contents

Requirements

To be captain obvious, this does require you have access to a Pure Storage FlashArray.

This library requires you use Ruby 2.3 or above.

Installation

gem install purest

Authentication

Purest allows you to authenticate using two different methods, either username and password OR using your API key (you don't need both). See "Configuration" below for examples. We recommend using the API key method as it is - slightly - more secure.

Configuration

There are now two ways to configure Purest, the first is directly in your ruby code like so:

require'purest'Purest.configuredo |config|
config.api_key='1234-567-89'config.api_version='1.16'config.options={ssl: {verify: true}}config.url="https://purehost.yourdomain.com"end

or with the user credentials method:

require'purest'Purest.configuredo |config|
config.username='api-enabled-user'config.password='password'config.api_version='1.16'config.options={ssl: {verify: true}}config.url="https://purehost.yourdomain.com"end

The second method is to create a .purest.yaml file in your home directory (~/.purest.yaml) that looks like this:

---
api_key: '1234-567-89'api_version: '1.16'url: 'https://purehost.yourdomain.com'options:
ssl:
verify: true

or with the user credentials method:

---
username: 'api-enabled-user'password: 'password'api_version: '1.16'url: 'https://purehost.yourdomain.com'options:
ssl:
verify: true

This yaml file is loaded in when your code is executed and the values can be individually or wholesale overridden during runtime by using the first configuration method, if you so desire.

API options

First: Authentication and session management are handled behind the scenes, you just need to supply your username/password or API key in the configuration block or YAML file (as shown in the example above). That's it.

Second: The various class methods of this gem turn the provided options into HTTP parameters, and are named accordingly. For instance, Purest::Volume.get({:snap: true}) translates to http://purehost.yourdomain.com/api/1.16/volume?snap=true. For a full list of options for a given class, Pure provides good documentation at: https://purehost.yourdomain.com/static/0/help/rest/.

Below I'll provide a large group of examples but I won't be detailing every single method call with all of its possible options, for that I will again refer you to Pure's REST API docs.

Usage

Alerts

Get information about alerts/alerting

# List email recipientsPurest::Alerts.get# List information about a specific email recipientPurest::Alerts.get(name: 'email@example.com')

Designate an email address to receive alerts

Purest::Alerts.create(name: 'new-user@example.com')

Updating/performing alert actions

# Send a test alertPurest::Alerts.update(action: 'test')# Turn off alert sending for new-user@example.comPurest::Alerts.update(name: 'new-user@example.com',enabled: false)

Deleting an email address from list of addresses that receive alerts

Purest::Alerts.delete(name: 'new-user@example.com')

App

# Teeny tiny class, can get apps or get apps and initiatorsPurest::App.getPurest::App.get(initiators: true)

Cert

List certificate attributes or export a certificate

# Get certificate attributesPurest::Cert.get# Export current certificatePurest::Cert.get(certificate: true)# Export current intermediate certificatePurest::Cert.get(intermediate_certificate: true)# Get a CSR with current certificate attributesPurest::Cert.get(csr: true)# Get a CSR with current certificate attributes, but change the CNPurest::Cert.get(csr: true,common_name: 'host.example.com')

Create self signed certificate, or import one. If you're wondering why this is an update action and not a create action, I believe it comes down to the fact that the Pure Array already has a self signed cert from the get go so we're just updating in place.

# Create a self signed certificate, using existing attributesPurest::Cert.update(self_signed: true)# Create a self signed cert, using all existing attributes except the statePurest::Cert.update(self_signed: true,state: 'FL')# Import a cert signed by a CAPurest::Cert.update(certificate: 'your_huge_certificate_string')

Directory Service

Get information about directory service

Purest::DirectoryService.get

Update information about a directory service

Purest::DirectoryService.update(bind_password: 'superpassword')

DNS

Listing DNS attributes

Purest::DNS.get

Updating DNS attributes

Purest::DNS.update(nameservers: ['newdns1','newdns2'])

Drive information

List flash modules, NVRAM modules, and their attributes

Purest::Drive.getPurest::Drive.get(name: 'SH0.BAY0')

Hardware

List hardware components

Purest::Hardware.get# List a specific hardware component's attributesPurest::Hardware.get(name: 'SH0.BAY0')

Control visual identification of specified components

# Turn the lights onPurest::Hardware.update(name: 'SH0.BAY0',identify: on)# Turn the lights off :(Purest::Hardware.update(name: 'SH0.BAY0',identify: off)

Hosts

Getting hosts:

# Get a list of all the hosts on an arrayPurest::Host.get# Get a list of hosts with performance dataPurest::Host.get(action: 'monitor')# Get a single host on an arrayPurest::Host.get(name: 'host123')# Get a list of hosts, by name, on an arrayPurest::Host.get(names: ['host123','host456'])# Get volumes associated with specific hostPurest::Host.get(name: 'hgroup1',show_volume: true)

Creating hosts:

# Create a hostPurest::Host.create(name: 'host123')# Create a host and set ISCSI IQNsPurest::Host.create(name: 'host123',iqnlist: ['iqnstuff-1','iqnstuff-2'])# Add a host to a protection groupPurest::Host.create(name: 'host123',protection_group: 'pgroup123')# Connect a volume to a hostPurest::Host.create(name: 'host123',volume: 'volume123')

Updating hosts:

# Rename a hostPurest::Host.update(name: 'host123',new_name: 'host456')# Set the host username/password for CHAPPurest::Host.update(name: 'host123',host_user: 'username',host_password: 'supersecretpassword')

Deleting hosts:

# Delete a hostPurest::Host.delete(name: 'host123')# Remove a host from a protection groupPurest::Host.delete(name: 'host123',protection_group: 'pgroup123')# Remove the connection between a host and a volumePurest::Host.delete(name: 'host123',volume: 'volume123')

Host Groups

Getting information about host groups

# Get a list of host groupsPurest::HostGroup.get# Get a single host groupPurest::HostGroup.get(name: 'hgroup1')# Get a list of host groups, with monitoring informationPurest::HostGroup.get(names: ['hgroup1','hgroup2'],action: 'monitor')# Get a list of volumes associated with a specified hostPurest::HostGroup.get(name: 'hgroup1',show_volume: true)

Creating host groups

# Create a host group with a specified namePurest::HostGroup.create(name: 'hgroup1')# Create a host group and supply its host membersPurest::HostGroup.create(name: 'hgroup1',hostlist: ['host1','host2'])# Add a host group to a protection groupPurest::HostGroup.create(name: 'hgroup1',protection_group: 'pgroup1')# Connect a volume to all hosts in a specified host groupPurest::HostGroup.create(name: 'hgroup1',volume: 'v3')

Updating host groups

# Renaming a host groupPurest::HostGroup.update(name: 'hgroup1',new_name: 'hgroup1-renamed')# Replace the list of member hostsPurest::HostGroup.update(name: 'hgroup1',hostlist: ['host1','host2'])# Add a list of hosts to existing host listPurest::HostGroup.update(name: 'hgroup1',addhostlist: ['host3','host4'])# Remove a list of hosts from a host listPurest::HostGroup.update(name: 'hgroup1',remhostlist: ['host1'])

Deleting host groups

# Delete a host groupPurest::HostGroup.delete(name: 'hgroup1')# Remove a host group member from a protection groupPurest::HostGroup.delete(name: 'hgroup1',protection_group: 'pgroup1')# Break the connection between a host group and a volumePurest::HostGroup.delete(name: 'hgroup1',volume: 'volume1')

Messages

List alert events, audit records, etc

Purest::Messages.get

Flag/unflag a message

Purest::Messages.update(id: 2,flagged: true)

Network

List network interfaces

# Get network interfaces and their statusesPurest::Network.get# Get attributes about a specific network componentPurest::Network.get(name: 'ct0.eth0')

Create a VLAN interface

# Create a vlan with a specified subnetPurest::Network.create(subnet: 'subnet10')

Perform network interface actions or update attributes

# Set MTUPurest::Network.update(mtu: 2000)

Delete a VLAN interface

Purest::Network.delete(name: 'ct0.eth0')

Physical Arrays

List the attributes on an array:

# List all the attributesPurest::PhysicalArray.get# List connected arraysPurest::PhysicalArray.get(connection: true)

Create a connection between two arrays:

Purest::PhysicalArray.create(connection_key: '<key>',management_address: 'hostname',type: ['replication'])

Update the attributes on an array:

# rename an arrayPurest::PhysicalArray.update(new_name: 'new_name')

Disconnect the current array from a specified array:

# Given that your pure is purehost.yourdomain.com, as defined in the config block above# Disconnect purehost2.yourdomain.com from purehost.yourdomain.comPurest::PhysicalArray.delete(name: 'purehost2.yourdomain.com')

Pod

Getting attributes or displaying performance metrics for pods

# Get a list of pods and their detailsPurest::Pod.get# Show space information for each podPurest::Pod.get(space: true)# Get a list of attributes for a specific podPurest::Pod.get(name: 'superpod')

Creating a new pod

# Basic pod creationPurest::Pod.create(name: 'superpod2')# Create a pod with a failover preferencePurest::Pod.create(name: 'superpod2',failover_preference: ['array1'])

Stretching a pod to a peer array

Purest::Pod.create(name: 'superpod2',array: 'array1')

Updating a pod

Purest::Pod.update(name: 'superpod2',failover_preference: ['array2'])

Unstretching a pod from a peer array

Purest::Pod.delete(name: 'superpod2',array: 'array1')

Deleting a pod

Purest::Pod.delete(name: 'superpod2')# Eradicating a podPurest::Pod.delete(name: 'superpod2',eradicate: true)

Port

Getting information about ports, 'cause that's all you get to do

# Get port informationPurest::Port.get# Get port information + initiator information = winningPurest::Port.get(initiators: true)

Protection Groups

Getting information about protection groups

# Get a list of protection groupsPurest::ProtectionGroup.get# Get a list of protection groups pending deletionPurest::ProtectionGroup.get(pending: true)

Creating protection groups

# Create a protection groupPurest::ProtectionGroup.create(name: 'pgroup1')# Create a protection group with a host listPurest::ProtectionGroup.create(name: 'pgroup1',hostlist: ['host1','host2'])

Updating protection groups

# Renaming a protection groupPurest::ProtectionGroup.update(name: 'pgroup1',new_name: 'pgroup1-renamed')# Add a list of hosts to an existing protection group's host listPurest::ProtectionGroup.update(name: 'pgroup1',addhostlist: ['host3','host4'])

Deleting a protection group

# Delete it, but allow for it to remain during the 24 hour grace periodPurest::ProtectionGroup.delete(name: 'pgroup1')# Delete it, with extreme prejudicePurest::ProtectionGroup.delete(name: 'pgroup1',eradicate: true)

SNMP

Getting SNMP information

# Get a list of SNMP managersPurest::SNMP.get# List SNMP v3 engine IDPurest::SNMP.get(engine_id: true)

Creating an SNMP manager

# Create an SNMP managerPurest::SNMP.create(name: 'snmp-manager1',host: 'snmp.yourdomain.com')# Create an SNMP manager pointed at an IPv4 address with a custom portPurest::SNMP.create(name: 'snmp-manager1',host: '111.11.11.111:222')# For those brave few; the strong souls using IPv6 with a custom portPurest::SNMP.create(name: 'snmp-manager1',host: '[2001:db8:0:1]:222')

Updating an existing SNMP manager

# Renaming an SNMP managerPurest::SNMP.update(name: 'snmp-manager1',new_name: 'snmp-manager-renamed')# For those not so brave; the weak souls who give up using IPv6 with a custom portPurest::SNMP.update(name: 'snmp-manager1',host: '111.11.11.111:222')

Deleting an SNMP manager

# Delete an SNMP managerPurest::SNMP.delete(name: 'snmp-manager1')

Subnet

Getting information about subnets

Purest::Subnet.get# Specify a subnetPurest::Subnet.get(name: 'subnet10')

Creating a subnet

Purest::Subnet.create(name: 'subnet20',gateway: '11.11.11.1')

Updating a subnet

# Rename a subnetPurest::Subnet.update(name: 'subnet20',new_name: 'subnet21')

Deleting a subnet

Purest::Subnet.delete(name: 'subnet21')

Users

Getting information about users

Purest::Users.get# Specific user informationPurest::Users.get(name: 'paxton.fettle')# List the API token of a given userPurest::Users.get(name: 'paxton.fettle',api_token: true)

Create an API token for an existing user

Purest::Users.create(name: 'paxton.fettle')

Updating user related stuff

# Clear all user permission cache entriesPurest::Users.update(clear: true)# Update a specific user's public keyPurest::Users.update(name: 'paxton.fettle',publickey: 'hugepublickeystring')

Delete API token for a user

Purest::Users.delete(name: 'paxton.fettle')

Volumes

Getting volumes:

# Get the full list of volumesPurest::Volume.get# Get a single volumePurest::Volume.get(name: 'volume1')# Get monitoring information about a volumePurest::Volume.get(name: 'volume1',action: 'monitor')# Get multiple volumesPurest::Volume.get(names: ['volume1','volume2'])# Get a list of snapshotsPurest::Volume.get(snap: true)# Get a single snapshotPurest::Volume.get(name: 'volume1',snap: true)# Get multiple snapshotsPurest::Volume.get(names: ['volume1','volume2'],snap: true)# List block differences for the specified snapshotPurest::Volume.get(name: 'volume1.snap',show_diff: true,block_size: 512,length: '2G')# List shared connections for a specified volumePurest::Volume.get(name: 'volume1',show_hgroup: true)# List private connections for a specified volumePurest::Volume.get(:name: 'volume1',:show_host: true)

Creating volumes:

# Creating a new volumePurest::Volume.create(name: 'volume',size: '10G')# Creating a new volume by copying anotherPurest::Volume.create(name: 'volume',source: 'other_vol')# Overwriting a volume by copying anotherPurest::Volume.create(name: 'volume',source: 'other_vol',overwrite: true)# Add a volume to a protection groupPurest::Volume.create(name: 'volume',protection_group: 'protection-group')

Updating volumes

# Growing a volumePurest::Volume.update(name: 'volume',size: "15G")# Truncate (shrink) a volumePurest::Volume.update(name: 'volume',size: "10G",truncate: true)# Rename a volumePurest::Volume.update(name: 'volume',new_name: 'volume_renamed')

Deleting volumes

# Delete a volumePurest::Volume.delete(name: 'volume_to_delete')# Eradicating a volumePurest::Volume.delete(name: 'volume_to_delete',eradicate: true)# Deleting a volume from a protection groupPurest::Volume.delete(name: 'volume',protection_group: 'pgroup1')

Specs

This library is tested with rspec, to execute the specs merely run

rspec

This project also supports an integration test suite. However, you must have API access to a pure array for this integration test suite to work.

Create an .integration.yaml inside the project, containing your pure array URL and credentials like so:

$ cat ~/ruby/purest/.integration.yaml
---
username: 'api-user'
password: 'api-password'
url: 'https://yoursuperawesomepurehost.com'

There are a few ways you can execute the integration tests:

# This will execute against the latest API version the gem is aware of, right now that is 1.16
rspec -t integration
# This will execute against a specific version
API_VERSION=1.10 rspec -t integration
# This will execute against all specified versions
API_VERSION=1.1,1.2,1.5 rspec -t integration
# And finally, if you wish to execute integration tests against every version
ALL_VERSIONS=true rspec -t integration

It is worth mentioning that this generates a fair bit of work for your Pure array so...you've been warned. All of that being said, the integration testing is somewhat sparse at the moment.

License

Purest is released under the MIT License.

About

Purest: A Ruby library for interacting Pure Storage's FlashArray REST API 1.x

Topics

Resources

Contributing

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages