Volume matching via the API in Purity 5.0

Core to most scripting with the FlashArray is figuring out what volume is on what FlashArray. Then you proceed to do what you need with it (snapshot, report metrics, whatever).

Traditionally how this was done, at least from a VMware perspective was via the NAA (network address authority). You take this number, which is how ESXi uniquely addresses a volume and slice it up to find the array serial and the volume serial. By matching the section with a particular array serial, you have identified what array owns it. Then you can make the calls to that array.

Details on how this worked are beyond the scope of this post, but you can see this here:

VMFS Snapshots and the FlashArray Part IV: How to correlate a VMFS to a FlashArray volume

Anyways, Purity 5.0 can throw a wrench into the gears of this. Specifically ActiveCluster. ActiveCluster allows a volume to exist on two FlashArrays at once. The procedure is, you create a group called a pod, put one or more volumes in it, then stretch it to another array. This synchronizes the data to the other side and then offers up those volumes from each array simultaneously.

You can then unstretch the pod and remove the first array. This effectively non-disruptively migrates a volume from one array to another. This also means the serial number of that volume will no longer match its hosting array. So this breaks the original paradigm.

So what is the best way to match?

Well for VVols, this is a non-issue, VMware tells you immediately what array hosts that VVol. But that is a blog post for another day.

So how about VMFS? Well the longer option is get all of the volumes from each array an iterate until you hit a match. This is a lot more work then necessary. Why not just ask the array if it has that volume?

This can be done with REST filters. Let me show you in Postman:

So if I pull all volumes with GET and https://10.21.202.52/api/1.13/volume

I get all of the volumes on that array. Yuck. This could be 1000s! So then I have to iterate or do an indexOf or whatever.

The FlashArray, in the CLI and the REST supports filtering. You can specify a property and a value and have the array middleware parse its response for you.

So add the filter ?filter=serial=’4BFAB20D4B694DF70001C268′ to the end of the call. The serial being the serial of your volume. It is case insensitive by the way.

This returns just one volume instead of many. If the response is null, then you know that array does not host that volume.

Let’s look at this with PowerShell and then vRealize Orchestrator.

PowerShell/PowerCLI

This can be done in PowerShell fairly easily. You can of course use invoke-webrequest if you want to do it manually, or you can use our PowerShellSDK. Our PowerShell SDK does not directly support filtering yet, but you can use the cmdlet new-pfaCLICommand to call the operation. This cmdlet basically makes an SSH session and runs the input command.

Here is a quick and dirty example:

$creds = Get-Credential
$flasharrays = @()
$faCount = read-host "How many FlashArrays do you want to search"
for ($fa =0; $fa -lt $faCount; $fa++)
{
     $faIP=read-host"Enter FlashArray FQDN or IP"
     $flasharrays+=new-pfaarray-EndPoint $faIP-IgnoreCertificateError -Credentials $creds
}
$vcenter = read-host "Enter vCenter IP"
connect-viserver -Server $vcenter
$datastoreName = read-host "Enter datastore name"
$datastore = get-datastore -name $datastoreName
$lun=$datastore.ExtensionData.Info.Vmfs.Extent.DiskName
$volserial = ($lun.ToUpper()).substring(12)
foreach ($flasharray in $flasharrays)
{
     $volume=New-PfaCLICommand-EndPoint         
     $flasharray.endpoint-credentials $creds-CommandText "purevol list --filter `"serial = `'$($volserial)`'`""

    if($volume.length-gt0)
    {
          Write-host"Volume is on FlashArray   $($flasharray.endpoint)"
        break
     }
}

Takes in a few FlashArray IPs/FQDNs and credentials and then a vCenter. Give it a datastore name and it writes out the FlashArray that owns it.

The pfa-CLIcommand requires the IP or FQDN, credentials and then the command itself:

-CommandText "purevol list --filter `"serial = `'$($volserial)`'`""

Note I have to use escape characters (`) before the quotes because the whole thing needs to be passed in via quotes.

This feels a bit clunky and it is. For PowerShell I prefer just pulling all of the volumes with the PowerShellSDK directly and having PowerShell parse it:

$datastore = get-datastore $vmfsname
$lun=$datastore.ExtensionData.Info.Vmfs.Extent.DiskName
$volserial=($lun.ToUpper()).substring(12)
$purevolumes = Get-PfaVolumes -Array $EndPoint
$purevol=$purevolumes|where-object { $_.serial-eq$volserial }
You may or may not prefer that method.

vRealize Orchestrator

Using our vRO plugin, the filtering process is much slicker. For two major reasons:

  1. It doesnt create an SSH session, it makes a REST call, so it is not separate authentication.
  2. The response is in JSON which is much easier to parse than the SSH response in PowerShell.

There are a million ways to find out what array holds a volume in vRO. A nice benefit of vRO is that the inventory of the registered FlashArrays is always synced with vRO, so you can look through the inventory just like you would VMs or datastores:

There are of course built-in workflows to do this work for you, but the purpose of this post is really to show how you can do filtering. So how do we do it in vRO?

Well it is pretty simply. There is an action that allows you to make REST calls in the plugin.

So let’s say we want a workflow to take in a datastore and tell me what FlashArray it is on. So the input is a datastore and the output is a FlashArray object:

So in this case, pull in all of the FlashArrays and then iterate through them and make the REST GET call:

var volSerial = datastore.info.vmfs.extent[0].diskName.substring(12);
var flasharrays = PSFlashArrayConnectionManager.getFlashArrayConnections();
var uri = ("/volume?filter=serial='" + volSerial + "'");
for each (var fa in flasharrays)
{
     var flasharrayObj = PSFlashArrayManager.getFlashArray(fa);
     var volume = System.getModule("com.purestorage.flasharray.restapi").runAPI(flasharrayObj,"GET",null,uri);
     volume = JSON.parse(volume);
     System.debug(volume.length);
     if (volume.length == 1)
     {
          System.log("The volume is on FlashArray " + fa.name);
 flasharray = fa;
     }
}

I pass in the URI which in full is:

https://10.21.202.52/api/1.13/volume?filter=serial='4bFAB20D4B694DF70001C268'

But I only need the part after the API version, so it really it is:

/volume?filter=serial='4bFAB20D4B694DF70001C268'

Once again will pull in the serial from the datastore and then pass it to the array to filter.

Conclusion

Is filtering the best way to go? Not always, you might prefer to pull in all of the volumes and iterate yourself. It depends on what you are trying to do and with what tool.

The real point is, because a serial number of a volume is no longer tied to a FlashArray, do not assume it is on the FlashArray it was originally created on. There is no real direct correlation any more. The serial prefix only tells you what array it was originally created on, not necessarily where it is today.

So in the end, update your scripts with this logic. Whether you use REST filtering, or you use scripting tool logic, that is up to you.

Leave a Reply

Your email address will not be published. Required fields are marked *