Spectrum Virtualize RESTful API
The Spectrum Virtualize Representational State Transfer (REST) model application programming interface (API) consists of command targets that are used to retrieve system information and to create, modify, and delete system resources. These command targets allow command parameters to pass through unedited to the Spectrum Virtualize command-line interface, which handles parsing parameter specifications for validity and error reporting. Use Hypertext Transfer Protocol Secure (HTTPS) to successfully communicate with the RESTful API server.
The RESTful API server does not consider transport security (such as SSL), but instead assumes that requests are initiated from a local, secured server. The HTTPS protocol provides privacy through data encryption. The RESTful API provides more security by requiring command authentication, which persists for two hours of activity, or 30 minutes of inactivity, whichever occurs first.
Uniform Resource Locators (URLs) target different node objects on the system. The HTTPS POST method acts on command targets that are specified in the URL. For more information, see RESTful API command targets and characteristics. To make changes or view information about different objects on the system, you must create and send a request to the system. You are required to provide certain elements for the RESTful API server to receive and transform the request into a command, as described in the next section.
Making an HTTPS request
https://system_node_ip:7443/rest/command
Where:All commands, including LS commands, accept at least one named parameter. Because the Spectrum Virtualize RESTful API is POST method only, it implements a convention of appending positional parameters to the URI, and packing named parameters into the request body as JavaScript Object Notation (JSON) strings.
The more elegant method for the command targets was to adopt POST exclusively.
As mentioned, in addition to the URL and the name of the command target, other information is required in the request line and in the body of the HTTP request that regards the action to take on the specified object. In the request line, include the POST HTTP method. Include any required parameters (such as RAID level or IP address) in the body of the request.
{'X-Auth-Username': 'superuser'}
HTTP/1.1 200 OK
Server: lighttpd/1.4.31
Date: date
Content-type: application/json; charset=UTF-8
Content-length: content_length
Connection: close
{"attribute": "value"}
To view API command targets and their characteristics, see RESTful API command targets and characteristics. To see an example of how to get started, see Getting started. See RESTful API HTTP error messages for a complete list of HTTP error codes that you might encounter.
RESTful API command targets and characteristics
Table 1 stresses the POST method for all commands, including /auth. It also shows that you must use the authentication token that is returned by the /auth command target to authenticate every other command you run. Except for the /auth command target, you run commands against the system IP address so that they are run by the configuration node.
Command targets | Method | Authentication required | Run in Configuration Node / Cluster |
---|---|---|---|
/auth | POST | No | No |
All other command targets | POST | Yes | Yes |
Descriptions of the command targets and their parameters, and descriptions of other less frequently used commands are available in the CLI command section of the product documentation.
Command targets | ||
---|---|---|
/addhostclustermember | /addhostiogrp | /addhostport |
/addvdiskaccess | /addvdiskcopy | /addvolumecopy |
/auth | /chhost | /chnode |
/chnodecanister | /chrcconsistgrp | /chrcrelationship |
/chvdisk | /expandvdisksize | /lscurrentuser |
/lseventlog | /lsfcconsistgrp | /lsfcmap |
/lsfcmapcandidate | /lsfcmapdependentmaps | /lsfcmapprogress |
/lshost | /lshostcluster | /lshostclustermember |
/lshostclustervolumemap | /lshostiogrp | /lshostvdiskmap |
/lsiogrp | /lsiogrphost | /lsmdiskgrp |
/lsnode | /lsnodecanister | /lsnodehw |
/lsnodecanisterhw | /lsnodecanisterstats | /lsnodecanistervpd |
/lsnodehw | /lsnodestats | /lsnodevpd |
/lspartnership | /lsrcrelationshipprogress | /lssystem |
/lssystemip | /lssystemstats | /lsvdisk |
/lsvdiskaccess | /lsvdiskcopy | /lsvdiskfcmapcopies |
/lsvdiskfcmappings | /lsvdiskhostmap | /lsvdisksyncprogress |
/mkfcconsistgrp | /mkfcmap | /mkfcpartnership |
/mkhost | /mkhostcluster | /mkrcconsistgrp |
/mkrcrelationship | /mkvdisk | /mkvdiskhostmap |
/mkvolume | /mkvolumehostclustermap | /movevdisk |
/prestartfcconsistgrp | /prestartfcmap | /rmfcmap |
/rmhost | /rmhostcluster | /rmhostclustermember |
/rmhostiogrp | /rmhostport | /rmvdisk |
/rmvdiskaccess | /rmvdiskcopy | /rmvdiskhostmap |
/rmvolume | /rmvolumecopy | /rmvolumehostclustermap |
/startfcmap | /startrcconsistgrp | /startrcrelationship |
/stopfcconsistgrp | /stopfcmap | /stoprcconsistgrp |
/stoprcrelationship |
Authentication overview
Aside from data encryption, the HTTPS server requires authentication of a valid user name and password for each API session. Use two authentication header fields to specify your credentials: X-Auth-Username and X-Auth-Password.
Initial authentication requires that you POST the authentication target (/auth) with the user name and password. The RESTful API server returns a hexadecimal token. A single session lasts a maximum of two active hours or thirty inactive minutes, whichever occurs first. When your session ends due to inactivity, or if you reach the maximum time that is allotted, error code 403 indicates the loss of authorization. Use the /auth command target to reauthenticate with the user name and password.
https://192.168.10.109:7443/rest/auth, method="POST",
headers={'X-Auth-Username': 'superuser', 'X-Auth-Password': 'passw0rd'}
The
HTTP request that was sent to the API server looks
like:POST /auth HTTPS/HTTPS_version
Host: https://192.168.10.109:7443
Content-type: application/json; charset=UTF-8
Content-length: message_size
X-Auth-Token: 58cfd6acb1676e1cba78b7cb5a9a081d11d1d1cfeb0078083ef225d9c59bf4df
{"attribute": "value","attribute": "value"}
Where:{
"token": "58cfd6acb1676e1cba78b7cb5a9a081d11d1d1cfeb0078083ef225d9c59bf4df"
}
Command target parameters
Getting started
The following Python 3 example shows how to complete initial setup to start interacting with the system and running commands. For examples in other languages, see RESTful API usage examples in Perl and Usage examples in CURL.
import ssl
import json
import pprint
import urllib.request
import urllib.error
import urllib.parse
no_verify = ssl.create_default_context()
no_verify.check_hostname = False
no_verify.verify_mode = ssl.CERT_NONE
if getattr(ssl, '_https_verify_certificates', None):
ssl._https_verify_certificates(False)
class HostString(str):
"""
Comment: Special subclass of string, for storing arbitrary host-related
attributes (such as auth tokens) without losing any string behavior
"""
def __new__(cls, *args, **kwds):
return super(HostString, cls).__new__(cls, *args, **kwds)
class RESTUtil(object):
show_default=False
default_headers = {}
port = 80
def __init__(self, show=None, catch=True):
self.hosts = {}
self.curr_host = None
self.catch=catch
self.show_default=show if show != None else self.show_default
@property
def host(self):
return self.curr_host
@host.setter
def host(self, hostname):
"""
Comment: Retrieve the HostString object of a known host from its
host name or string definition. Even if the host definition
is provided, we still need to key into self.hosts in case the
client classes are storing things on their HostString objects.
"""
try:
if hostname in self.hosts:
self.curr_host = self.hosts[hostname]
else:
self.curr_host = [h for h in self.hosts.values() if h == hostname][0]
return self.curr_host
except IndexError:
raise KeyError("Unrecognized host/name %s" % hostname)
def add_host(self, hostdef, hostname=None):
hostname = hostname if hostname is None else hostdef
self.hosts[hostname] = HostString(hostdef)
if self.curr_host == None:
self.curr_host = self.hosts[hostname]
return hostname
def command(self, protocol, postfix, method='POST', headers=None, show=None, **cmd_kwds):
"""
Comment: A fairly generic RESTful API request builder.
See subclasses for examples of use.
"""
if show == None:
show = self.show_default
headers = {} if headers == None else headers
url = '%s://%s:%s/%s' % (
protocol,
self.curr_host,
self.port,
postfix
)
request = urllib.request.Request(
url,
headers =dict(self.default_headers, **headers),
data=bytes(json.dumps(cmd_kwds), encoding="utf-8") if cmd_kwds else None)
request.get_method = lambda: method
if show:
self.request_pprint(request)
try:
cmd_out = urllib.request.urlopen(request, context=no_verify).read().decode('utf-8')
except urllib.error.HTTPError as e:
self.exception_pprint(e)
if not self.catch:
raise Exception("RESTful API command failed.")
return
try:
cmd_out = json.loads(cmd_out)
except ValueError:
pass
if show:
print("\nCommand Output:")
pprint.pprint(cmd_out)
print("")
return cmd_out
@staticmethod
def request_pprint(request):
"""
Comment: Request info print function
(for self.command with show=True)
"""
print(request.get_method(), request.get_full_url(), 'HTTP/1.1')
print('Host:', request.host)
for key, value in request.headers.items():
print(key.upper() + ':', str(value))
if request.data != None:
print()
pprint.pprint(request.data)
@staticmethod
def exception_pprint(http_error):
"""
Comment: HTTPError info print function
"""
print(http_error.code, '--', http_error.reason)
print(http_error.fp.read())
print("")
class SVCREST(RESTUtil):
"""
Comment: RESTful wrapper for the SVC CLI
"""
def __init__(self, host, *args, **kwds):
self.debug = kwds.pop('debug', False)
super().__init__(*args, **kwds)
self.add_host(host)
@property
def default_headers(self):
return {'X-Auth-Token': getattr(self.curr_host, 'token', 'badtoken'),
'Content-Type': 'application/json'}
@property
def port(self):
return getattr(self, '_port', None) or ('7665' if self.debug else '7443')
@property
def protocol(self):
return getattr(self, '_protocol', None) or ('http' if self.debug else 'https')
def command(self, cmd, *args, method="POST", headers=None, show=None, **cmd_kwds):
postfix = '/'.join(
['rest'] + [cmd] + [urllib.parse.quote(str(a)) for a in args]
)
return super().command(
self.protocol,
postfix,
method=method,
headers=headers,
show=show,
**cmd_kwds
)
def authenticate(self, username='superuser', password='passw0rd', show=None):
cmd_out = self.command(
'auth', show=show, method="POST", headers={'X-Auth-Username': username, 'X-Auth-Password': password}
)
if cmd_out:
self.curr_host.token = cmd_out['token']
"""
Comment: First, set your cluster ipaddress.
It's assumed superuser/passw0rd (6 lines above) is the crednetial.
After the authenticate call, you can issue any command in
s.command('') that is an svcinfo or svctask cmmand)
"""
s = SVCREST('192.168.10.109')
s.authenticate()
print(s.command('lssystem'))