Authentication using zCompute APIs¶
Symp CLI commands can be run by authenticating, without requiring a token.
However, it is advisable to authenticate and then generate a token for
use in any automated Symp CLI or API process. This will prevent possible
connection reset by peer
errors when rate limits are exceeded.
API Endpoints¶
The list of a cluster’s API endpoints is available in the UI.
To view the cluster’s API endpoints:
In the UI, in the upper right corner near the username, click Help (the ? icon).
The Help dropdown menu opens.
In the dropdown menu, select API Endpoints.
The API Endpoints dialog opens, listing the cluster’s API endpoints.
API Explorer¶
zCompute provides an interactive Swagger-based API Explorer.
API operations require authorization with a token.
Obtaining a token in the UI¶
To obtain a token in the UI:
In the UI, in the upper right corner near the username, click Help (the ? icon), and in the dropdown menu, select API Explorer.
The Open API Explorer dialog opens.
Click the Open API Explorer button.
Note
The token in the clipboard can be pasted and used in the API Explorer, or for running API operations using the CLI.
Running API operations in the API Explorer¶
After Obtaining a token in the UI, click an API in the zCompute Symp API Explorer list.
A Swagger UI opens, displaying the selected API’s REST methods.
Scroll down the list to locate the desired operation to run.
Click the selected REST method or its description, to expand the view to display its parameters.
Click the Lock icon on the right, to submit the authorization token.
Note
Alternatively, click the Authorize button in the screen header.
The Available Authorizations dialog opens.
In the token (apiKey) > Value field, paste the token from your clipboard.
Click Authorize.
Note
After the token is authorized and while it is still valid, REST methods for all APIs can be run.
It is not necessary to repeat this authorization step.
Click Close to close the Available Authorizations dialog and return to the selected API REST method screen.
Click Try it out.
Enter parameters for the REST method, and click the Execute bar.
The Responses pane expands, displaying:
The Curl command that was executed.
The Request URL.
The Server response, comprising the return code, response body and response headers.
Generating a token at a bash prompt¶
Since some commands are limited to the scope of a project, while others have the scope of an entire account (domain), it’s necessary to generate a token for the account (domain) scope, and a separate token for the project scope.
Using curl
on the /api/v2/identity/auth
endpoint,
to generate authentication tokens involves two steps:
First, generating an account (domain) token, and then using the domain token to generate a project token.
Generating a domain token:
The
/api/v2/identity/auth
endpoint’s authentication token request data structure that defines the scope of an entire account (domain):{ "auth": { "scope": { "domain": { "name": "<ACCOUNT_NAME>" } }, "identity": { "password": { "user": { "domain": { "name": "<ACCOUNT_NAME>" }, "password": "<PASSWORD>", "name": "<USER_NAME>" } }, "methods": [ "password" ] } } }
The
curl
command at the bash prompt for generating an account (domain) authentication token:curl -D - -k -H "Content-Type: application/json" -X POST https://<cluster IP address>/api/v2/identity/auth -d \ '{"auth": {"scope": {"domain": {"name": "<account>"}}, "identity": {"password": {"user": {"domain": {"name": "<account>"}, "password": "<password>", "name": "<user>"}}, "methods": ["password"]}}}'
For example:
curl -D - -k -H "Content-Type: application/json" -X POST https://10.11.12.13/api/v2/identity/auth -d \ '{"auth": {"scope": {"domain": {"name": "acc1"}}, "identity": {"password": {"user": {"domain": {"name": "acc1"}, "password": "Mypass1!", "name": "user1"}}, "methods": ["password"]}}}'
The domain token is returned in the
x-subject-token
header, and can be copy/pasted to assign it to an environment variable for later use.For example:
export ZC_DOMAIN_TOKEN=MIISUgYJKoZIhvcNAQcCoIISQzCC...<2K+ character string>...ngpXjEqQtxlRGuCEIvo46sGMfedc=
Using the previously generated domain token to generate a project token:
The
/api/v2/identity/auth
endpoint’s authentication token request data structure that defines the scope as limited to a project, and the identity method as a token:{ "auth": { "scope": { "project": { "domain": { "name": "<ACCOUNT_NAME>" }, "name": "<PROJECT_NAME>" } }, "identity": { "token": { "id": "'<DOMAIN_TOKEN>'" }, "methods": [ "token" ] } } }
The
curl
command at the bash prompt for generating a project authentication token:curl -D - -k -H "Content-Type: application/json" -X POST https:<cluster IP address>/api/v2/identity/auth -d \ '{"auth": {"scope": {"project": {"domain": {"name": "<account>"}, "name": "<project>"}}, "identity": {"token": {"id": "'<domain token string>'"}, "methods": ["token"]}}}'
For example:
curl -D - -k -H "Content-Type: application/json" -X POST https://10.11.12.13/api/v2/identity/auth -d \ '{"auth": {"scope": {"project": {"domain": {"name": "acc1"}, "name": "vpcproj1"}}, "identity": {"token": {"id": "'$ZC_DOMAIN_TOKEN'"}, "methods": ["token"]}}}'
The project token is returned in the
x-subject-token
header, and can be copy/pasted to assign it to an environment variable for later use.For example:
export ZC_PROJECT_TOKEN=MIIScwYJKoZIhvcNAQcCoIISZDCC...<2K+ character string>...VpADxR3RDrScUbgSwMAaZ8zCSrow=
To use
curl
to call a REST method on an endpoint, provide the token string in theX-Auth-Token
header:curl -k -L -X <REST method> "https://<cluster IP address>/api-explorer/api/v2/<endpoint>/" -H "accept: application/json" -H "X-Auth-Token: <token string>"
For example, to retrieve the project’s tags, use the
GET
method on the/api-explorer/api/v2/tags/
endpoint, and the project authentication token (assigned in the previous step to the environment variable$ZC_PROJECT_TOKEN
). This example usessed
andawk
to format the output:curl -k -L -X GET "https://10.11.12.13/api-explorer/api/v2/tags/" -H "accept: application/json" -H "X-Auth-Token: $ZC_PROJECT_TOKEN" | awk 'BEGIN{FS=",";OFS="\n"} FNR==1{$1=$1;print;exit}' | sed '/{/{x;p;x;}' % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 116 100 116 0 0 11756 0 --:--:-- --:--:-- --:--:-- 12888 100 2302 100 2302 0 0 59137 0 --:--:-- --:--:-- --:--:-- 0 [{"description": "Tag2" "updated-at": "2023-10-31T08:44:01Z" "created-at": "2023-10-31T08:44:01Z" "value": "" "scope": "public" "project-id": "7ff34832b6754f7d91a918302af3e77f" "name": "vpctag2"} {"description": "Tag 1" "updated-at": "2023-10-31T08:10:49Z" "created-at": "2023-10-31T08:10:49Z" "value": "" "scope": "public" "project-id": "7ff34832b6754f7d91a918302af3e77f" "name": "vpctag1"}]
Generating a token in Symp CLI¶
To generate an authentication token via the Symp CLI shell, use the Symp
login
command:login <user> <password> <domain> <project> -f value
For example:
login user1 Mypass1! acc1 vpcproj1 -f value MIISUgYJKoZIhvcNAQcCoIISQzCC...<2K+ character string>...ngpXjEqQtxlRGuCEIvo46sGMfedc=
Alternatively, an example of a single command at the bash prompt to generate an authentication token and assign it to an environment variable:
export ZC_PROJECT_TOKEN=`symp -k --url https://10.11.12.13 -d acc1 -u user1 --project vpcproj1 -p Mypass1! login user1 Mypass1! acc1 vpcproj1 -f value` echo $ZC_PROJECT_TOKEN MIISUgYJKoZIhvcNAQcCoIISQzCC...<2K+ character string>...ngpXjEqQtxlRGuCEIvo46sGMfedc=
Using the token to sign on to Symp from the bash prompt:
symp -k --url https://<cluster IP address> --project-token <token string>
For example, generating the token (as in the previous step), and using it to sign on to Symp:
export ZC_PROJECT_TOKEN=`symp -k --url https://10.11.12.13 -d acc1 -u user1 --project vpcproj1 -p Mypass1! login user1 Mypass1! acc1 vpcproj1 -f value` symp -k --url https://10.11.12.13 --project-token $ZC_PROJECT_TOKEN
Starting new HTTPS connection (1): 10.16.145.114 Connecting in insecure mode! Starting new HTTPS connection (1): 10.16.145.114 Starting new HTTPS connection (1): 10.16.145.114 d88888b dP dP 8888ba.88ba 888888ba dP dP .88888. 888888ba dP dP 88. "' Y8. .8P 88 `8b `8b 88 `8b 88 88 d8' `8b 88 `8b Y8. .8P `Y88888b. Y8aa8P 88 88 88 a88aaaa8P' 88aaaaa88a 88 88 88 88 Y8aa8P `8b 88 88 88 88 88 88 88 88 88 88 88 88 d8' .8P 88 88 88 88 88 88 88 Y8. .8P 88 88 88 Y88888P dP dP dP dP dP dP dP `8888P' dP dP dP Tap <TAB> twice to get list of available commands. Type --help to get help with any command Symphony >
Python authentication example¶
When using a zCompute API, the user is required to pass a valid token in an
X-Auth-Token
HTTP header.
Users can generate a valid token by logging into the system using the
identity/auth
API.
The following Python script is an example of this process:
#!/usr/bin/python
from __future__ import print_function
import copy
import getpass
import os
import sys
import argparse
import requests
try:
input = raw_input
except NameError:
pass
IDENTITY_API_SUFFIX = "identity"
VPC_API_SUFFIX = "vpc"
DEFAULT_HEADERS = {
"Accept": "application/json",
"Content-Type": "application/json"
}
_debug = False
def _debug_msg(msg, *args, **kwargs):
global _debug
if _debug:
print(msg.format(*args, **kwargs), file=sys.stderr)
def generate_totp_passcode(secret):
"""Generate TOTP passcode.
:param bytes secret: A base32 encoded secret for the TOTP authentication
:returns: totp passcode as bytes
"""
try:
import mintotp
except:
sys.exit("TOTP code generation library is not present. Please install mintotp using 'pip install mintotp'")
return mintotp.totp(secret)
class ZComputeApi(object):
def __init__(self, api_base_url, account_name, project_name, user, password,
insecure=False, totp_code=None, mfa_secret=None, debug=False):
self._api_base_url = '{}/api/v2'.format(api_base_url)
self._account_name = account_name
self._project_name = project_name
self._user = user
self._password = password
self._insecure = insecure
self._token = None
self._totp_code = totp_code
self._mfa_secret = mfa_secret
self._debug = debug
def reset_api_params(self, token=None, project_name=None):
if token:
self._token = token
if project_name:
self._project_name = project_name
def send_request(self, method='GET', api_path='', api_body=None, headers=None, raise_on_status=True):
api_url = '{}/{}'.format(self._api_base_url, api_path)
if headers:
headers = copy.deepcopy(headers)
headers.update(DEFAULT_HEADERS)
else:
headers = copy.deepcopy(DEFAULT_HEADERS)
if self._token:
headers['X-Auth-Token'] = self._token
try:
response = requests.request(
method=method,
url=api_url,
headers=headers,
json=api_body,
verify=not self._insecure
)
if raise_on_status:
response.raise_for_status()
except Exception:
_debug_msg("Failed sending {} {} reuquest".format(method, api_url))
raise
return response
def send_api_request(self, method='GET', api_path='', api_body=None):
return self.send_request(method=method, api_path=api_path, api_body=api_body).json()
def _get_project_scope(self):
return {"project": {"name": self._project_name, "domain": {"name": self._account_name}}}
def _get_domain_scope(self):
return {"domain": {"name": self._account_name}}
def _get_password_auth(self):
return {
"methods": [
"password"
],
"password": {
"user": {
"name": self._user,
"password": self._password,
"domain": {
"name": self._account_name
}
}
}
}
def _add_totp_code(self, identity_auth, totp_code):
if identity_auth.get('methods'):
identity_auth['methods'].append('totp')
else:
identity_auth['methods'] = ['totp']
identity_auth['totp'] = {
"user": {
"name": self._user,
"passcode": totp_code,
"domain": {
"name": self._account_name
}
}
}
return identity_auth
def get_project_token(self):
identity_auth = self._get_password_auth()
if self._mfa_secret:
self._totp_code = generate_totp_passcode(self._mfa_secret)
if self._totp_code:
self._add_totp_code(identity_auth, self._totp_code)
auth_json = {
"auth": {
"identity": identity_auth,
"scope": self._get_project_scope()
}
}
response = self.send_request('POST', '{}/auth'.format(IDENTITY_API_SUFFIX), auth_json, raise_on_status=False)
if response.status_code == requests.codes.UNAUTHORIZED and response.json().get('receipt'):
os_receipt = response.headers.get('openstack-auth-receipt')
if self._mfa_secret:
_debug_msg("Generating MFA secret")
totp_code = generate_totp_passcode(self._mfa_secret)
else:
totp_code = str(input('MFA Code: ')).lower().strip()
identity_auth = self._add_totp_code({}, totp_code)
auth_json = {
"auth": {
"identity": identity_auth,
"scope": self._get_project_scope()
}
}
response = self.send_request('POST', '{}/auth'.format(IDENTITY_API_SUFFIX),
auth_json,
headers={"openstack-auth-receipt": os_receipt})
else:
response.raise_for_status()
return response.headers['x-subject-token']
def get_account_token(self):
auth_json = {
"auth": {
"identity": self._get_password_auth(),
"scope": self._get_domain_scope()
}
}
auth_response = self.send_request('POST', '{}/auth'.format(IDENTITY_API_SUFFIX), auth_json)
return auth_response.headers['x-subject-token']
def get_user_default_project(self):
details_api_uri = '{}/users/myself/projects'.format(IDENTITY_API_SUFFIX)
projects = self.send_api_request('GET', details_api_uri)
if len(projects) > 1:
sys.exit("There are {} projects: {}\n"
"please select one using --project flag".format(
len(projects), ', '.join([p['name'] for p in projects])))
return projects[0]['name']
def ask_for_value(parameter, current, hide=False):
if hide:
value = getpass.getpass("{} []: ".format(parameter))
else:
value = input("{} [{}]: ".format(parameter, current or '')).strip()
if value:
return value
return current
def main():
global _debug
parser = argparse.ArgumentParser(description="Obtain zCompute API Token using user credentials")
parser.add_argument("cluster", help="Cluster API endpoint IP or host name")
parser.add_argument("--interactive",
help="Will get login credentials interactively",
action='store_true',
default=False)
parser.add_argument("--account",
help="Account name (will use environment variable ZCOMPUTE_ACCOUNT if not provided)",
default=os.environ.get('ZCOMPUTE_ACCOUNT', None),
required=False)
parser.add_argument("--username",
help="User name (will use environment variable ZCOMPUTE_USER if not provided)",
default=os.environ.get('ZCOMPUTE_USER', None),
required=False)
parser.add_argument("--mfa-secret",
help="MFA secret to generate totp code MFA enabled login "
"(will use environment variable ZCOMPUTE_MFASECRET if not provided)",
default=os.environ.get('ZCOMPUTE_MFASECRET', None),
required=False)
parser.add_argument("--totp-code",
help="TOTP code to use for MFA enabled login "
"(will use environment variable ZCOMPUTE_TOTP if not provided)",
default=os.environ.get('ZCOMPUTE_TOTP', None),
required=False)
parser.add_argument("--project",
dest='project_name',
help="Project name (will use environment variable ZCOMPUTE_PROJECT if not provided)"
" if not provided and only one exists in the account will it",
default=os.environ.get('ZCOMPUTE_PROJECT', None),
required=False)
parser.add_argument("--password",
help="Password - will use environment variable ZCOMPUTE_PASSWORD if not provided",
default=os.environ.get('ZCOMPUTE_PASSWORD', None))
parser.add_argument("--debug", help="Print debug messages", default=False, action='store_true')
parser.add_argument("-k", "--insecure", action='store_true', help="Do not verify cluster certificate", default=False)
args = parser.parse_args()
if args.debug:
_debug = True
if args.interactive:
args.account = ask_for_value('account', args.account)
args.project_name = ask_for_value('project name', args.project_name)
args.username = ask_for_value('User name', args.username)
args.mfa_secret = ask_for_value('MFA secret', args.mfa_secret, hide=True)
args.totp_code = ask_for_value('TOTP code', args.totp_code)
args.password = ask_for_value('Password', args.password, hide=True)
url = 'https://{}'.format(args.cluster)
if args.cluster.startswith('https://'):
url = args.cluster
api = ZComputeApi(url, args.account, args.project_name, args.username, args.password,
insecure=args.insecure, totp_code=args.totp_code, mfa_secret=args.mfa_secret, debug=args.debug)
if not args.password:
sys.exit("Please provide a password")
if not args.project_name:
if args.account is None:
sys.exit("Please provide the account name to login into")
if args.username is None:
sys.exit("Please provide the user name to login with")
api.reset_api_params(token=api.get_account_token())
project_name = api.get_user_default_project()
else:
if args.project_name is None:
sys.exit("Please provide the project name to login into")
project_name = args.project_name
api.reset_api_params(project_name=project_name)
print(api.get_project_token())
if __name__ == '__main__':
main()
|api-endpoints-list|