Pure Storage: py-pure-client and Error 404
I recently came across an interesting issue while working with a customer on the Pure Storage Python ‘py-pure-client‘ SDK. Once we got the issue resolved I felt the resolution was ambiguous enough that others may run into the same problem and thus decided to write a quick blog about it.
Why two different SDKs?
Before I explain the problem I want to provide a quick background here so that people do not get confused. Pure Storage actually has two Python API SDKs that you can use to interact with the FlashArray API:
– purestorage sdk
– py-pure-client
The first is used to interact with the FlashArray via 1.x REST API versions, while the second (py-pure-client) is used for REST API versions 2.0+.
We have been asked, why are there two different tools here and which one should I use?
The reason there are two is because the authentication methods between REST 1.x and REST 2.x are much different on the FlashArray. With REST 1.x you authenticate via an API token which is generated on the FlashArray after you have provided the username and password. While REST 2.x uses the OAuth 2.0 Token Exchange authorization grant and JSON Web Tokens (JWTs) to authenticate. So it is more complicated than simply providing an API token. I covered the authentication changes in a previous post here for those that want to review.
So you will need to use the appropriate SDK depending on which REST version you are wanting to interact with the FlashArray on. This particular issue we are describing is only applicable to ‘py-pure-client’.
The Problem
Now that we understand the differences between why you would use one sdk vs the other, we can look into the problem.
In this particular scenario the customer was wanting to use the more secure REST version to interact with the FlashArray. When attempting to connect to the FlashArray using this tool everything seemed to be okay:
from pypureclient import flasharray
client = flasharray.Client('Pure FlashArray IP',
private_key_file="purity-private-key.pem",
client_id="client_id",
key_id="key_id",
username="api_user",
issuer="valid_issuer")
The problem though was that when he attempted to query anything the FlashArray was returning a “404” error back to his client:
response = client.get_volumes()
print(response)
{'errors': [], 'headers': None, 'status_code': 404}
This didn’t make much sense because the authentication clearly worked. If the authentication had failed it would have reported that no access token was available.
We decided to review the FlashArray logs and were able to confirm the request was received but showed it was accessing an invalid endpoint (version 2.1). We verified that Purity 5.3+ was in use and thus the endpoint should be valid.
After looking a little closer we were able to identify the issue by querying the FlashArray to see what API versions it supported:
{"version": ["1.0", "1.1", "1.2", "1.3", "1.4", "1.5", "1.6", "1.7", "1.8", "1.9", "1.10", "1.11", "1.12", "1.13", "1.14", "1.15", "1.16", "1.17", "2.0"]}
Notice above that the highest value is 2.0 whereas our endpoint was looking for 2.1. So the question to answer now was: “Why is client requesting REST 2.1 when only 2.0 is supported?”
The Answer
In carefully reviewing the documentation the following was shown:

Notice above that it is showing the class containing the 2.1 client being instantiated. After some additional investigation we found that if you fail to explicitly choose a REST version then the SDK will instantiate the most recent REST version available in the SDK; which in this case was 2.1.
Thus, if you need to use a previous REST version (such as 2.0 in our case) you need to do one of two things:
1. Explicitly instantiate the 2.0 class as following:
client = flasharray.FA_2_0.client.Client(parameters_here)
2. Specify the version when instantiating the default client:
client = flasharray.Client('Pure FlashArray IP',
version='2.0',
private_key_file="purity-private-key.pem",
client_id="client_id",
key_id="key_id",
username="api_user",
issuer="valid_issuer")
It is up to you as the end-user / developer to choose which you prefer but both are valid options.
A documentation update request has been submitted to more clearly outline that the default instantiation is the most recent version available to the SDK. Until then, please feel free to forward this to anyone who may be hitting this same issue.
As usual, if there are any questions or comments please feel free to reach out!
-jhop
Awesome read.
Thank you, I appreciate the feedback!
-jhop