Spectrum Virtualize RESTful API
Spectrum Virtualize Representational State Transfer (REST) 模型應用程式設計介面 (API),是由用於擷取系統資訊以及建立、修改及刪除系統資源的指令目標組成。這些指令目標容許將指令參數未經編輯傳遞至 Spectrum Virtualize 指令行介面,由該介面剖析參數規格以執行有效性及錯誤報告。使用「超文字安全傳送通訊協定 (HTTPS)」可與 RESTful API 伺服器順利通訊。
RESTful API 伺服器不會考量傳輸安全(如 SSL),但會假設要求是從本端安全伺服器起始。HTTPS 通訊協定可透過資料加密提供保密性。RESTful API 可透過要求指令鑑別(持續 2 小時活動或 30 分鐘閒置時間,以先達到者為準)來加強安全。
「統一資源定址器(URL)」會以系統上的不同節點物件作為目標。HTTPS POST 方法則會針對 URL 中指定的指令目標而執行。如需相關資訊,請參閱RESTful API 指令目標及性質。如果要進行變更或檢視系統上不同物件的相關資訊,您必須建立要求並將其傳送至系統。您需要為 RESTful API 伺服器提供特定元素,才能接收要求並將其轉換為指令(如下一節中所述)。
建立 HTTPS 要求
https://system_node_ip:7443/rest/command
其中:所有指令(包括 LS 指令)皆至少接受一個具名參數。因為 Spectrum Virtualize RESTful API 僅使用 POST 方法,所以它會實作如下慣例:將位置參數附加至 URI,並將具名參數作為「JavaScript 物件表示法 (JSON)」字串包裝至要求內文。
指令目標的較好方法是僅僅採用 POST。
如上所述,除了指令目標的 URL 及名稱以外,要求行及 HTTP 要求內文中還需要有關對指定物件所要採取動作的其他資訊。在要求行中,請併入 POST HTTP 方法。請將任何必要參數(如 RAID 層次或 IP 位址)併入要求內文。
{'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"}如果要檢視 API 指令目標及其性質,請參閱 RESTful API 指令目標及性質。如果要查看如何開始使用的範例,請參閱開始使用。如需您可能會遇到的 HTTP 錯誤碼完整清單,請參閱 RESTful API HTTP 錯誤訊息。
RESTful API 指令目標及性質
表 1 強調了所有指令(包括 /auth)的 POST 方法。它還顯示您必須使用 /auth 指令目標傳回的鑑別記號,來鑑別您執行的所有其他指令。除了 /auth 指令目標以外,您會針對系統 IP 位址執行指令,以讓配置節點執行這些指令。
| 指令目標 | 方法 | 需要鑑別 | 在配置節點/叢集中執行 |
|---|---|---|---|
| /auth | POST | 否 | 否 |
| 所有其他指令目標 | POST | 是 | 是 |
在產品說明文件的 CLI 指令小節中,將會提供這些指令目標及其參數的說明,以及其他不太常用的指令之說明。
| 指令目標 | ||
|---|---|---|
| /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 | ||
鑑別概觀
除了資料加密以外,HTTPS 伺服器還需要鑑別每個 API 階段作業的有效使用者名稱及密碼。請使用下列兩個鑑別標頭欄位來指定認證:X-Auth-Username 及 X-Auth-Password。
起始鑑別要求您利用使用者名稱及密碼,對鑑別目標 (/auth) 執行 POST。RESTful API 伺服器會傳回十六進位記號。單一階段作業最長會持續 2 小時作用中或 30 分鐘閒置時間,以先達到者為準。在階段作業因閒置而結束或您達到分配的時間上限時,錯誤碼 403 會指出喪失授權。請使用 /auth 指令目標,利用使用者名稱及密碼來重新鑑別。
https://192.168.10.109:7443/rest/auth, method="POST",
headers={'X-Auth-Username': 'superuser', 'X-Auth-Password': 'passw0rd'}傳送至 API 伺服器的 HTTP 要求如下所示: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"}其中:{
"token": "58cfd6acb1676e1cba78b7cb5a9a081d11d1d1cfeb0078083ef225d9c59bf4df"
}指令目標參數
開始使用
下列 Python 3 範例顯示如何完成起始設定,以開始與系統互動並執行指令。如需其他語言的範例,請參閱使用 Perl 的 RESTful API 用法範例及使用 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'))