ZStack的API原语是JSON格式的消息,可以在各种消息总线上调用,目前ZStack使用的默认消息总线是RabbitMQ。由于这个特性,ZStack的API可以被封装成各种格式。为了方便大家使用,ZStack自带一个HTTP server,用户可以通过HTTP POST发送API JSON文本,该HTTP Server会自动将其转换成API消息实现调用。
我们未来会将ZStack API封装成Restful格式,但当前HTTP POST发送API JSON原语的方式将一直保留
ZStack的命令行工具(zstack-cli)是完全使用HTTP POST方法调用API的,为了方便用户阅读,我们用户手册对所有API的解释都是以zstack-cli为示例,每个cli命令都对应一个API。用户可以在中文用户手册获得每个API的详细解释。
通过zstack-cli的log,用户可以查看所有API调用和返回的具体格式(即HTTP POST的BODY)。
Log路径: /var/log/zstack/zstack-cli
我们建议用户通过tail -f /var/log/zstack/zstack-cli实时查看log,这样你可以在zstack-cli中执行API时实时观测到具体调用和输出。例如:
- >>LogInByAccount accountName=admin password=password
执行了登录操作后,你会看到LOG中输出类似如下内容:
- 2016-02-16 16:29:48,664 DEBUG [apibinding.api] async call[url: http://localhost:8080/zstack/api/, request: {"org.zstack.header.identity.APILogInByAccountMsg": {"password": "b109f3bbbc244eb82441917ed06d618b9008dd09b3befd1b5e07394c706a8bb980b1d7785e5976ec049b46df5f1326af5a2ea6d103fd07c95385ffab0cacbc86", "session": {}, "accountName": "admin"}}]
- 2016-02-16 16:29:48,741 DEBUG [apibinding.api] async call[url: http://localhost:8080/zstack/api/, response: {"org.zstack.header.identity.APILogInReply":{"inventory":{"uuid":"083f3518d1bd469fa0ff789fc1a25382","accountUuid":"36c27e8ff05c4780bf6d2fa65700f22e","userUuid":"36c27e8ff05c4780bf6d2fa65700f22e","expiredDate":"Feb 17, 2016 12:29:48 PM","createDate":"Feb 16, 2016 4:29:48 PM"},"success":true}}]
这里http://localhost:8080/zstack/api/是HTTP调用的URL,request后的内容是API调用时HTTP POST的BODY,例如这里LogInByAccount这个API的HTTP POST BODY就是:
- {
- "org.zstack.header.identity.APILogInByAccountMsg": {
- "password": "b109f3bbbc244eb82441917ed06d618b9008dd09b3befd1b5e07394c706a8bb980b1d7785e5976ec049b46df5f1326af5a2ea6d103fd07c95385ffab0cacbc86",
- "session": {},
- "accountName": "admin"
- }
- }
response后的内容是API返回的HTTP response的BODY,例如这里LogInByAccount的返回是:
- {
- "org.zstack.header.identity.APILogInReply": {
- "inventory": {
- "uuid": "083f3518d1bd469fa0ff789fc1a25382",
- "accountUuid": "36c27e8ff05c4780bf6d2fa65700f22e",
- "userUuid": "36c27e8ff05c4780bf6d2fa65700f22e",
- "expiredDate": "Feb 17, 2016 12:29:48 PM",
- "createDate": "Feb 16, 2016 4:29:48 PM"
- },
- "success": true
- }
- }
ZStack API的调用和返回都是以JSON原语描述的,格式统一为:只有一个entry的JSON map,该entry的key是API描述符,value是承载具体内容的一个map。API描述符用来唯一标示一个API调用或返回,用户可以把它看做API的名称。在上一节的例子中,API调用的描述符是org.zstack.header.identity.APILogInByAccountMsg,API返回的描述符是org.zstack.header.identity.APILogInReply。
返回码 | 描述 |
---|---|
200 | HTTP调用成功。但并不代表API执行成功,API的执行结果由API返回中的success字段决定,true表示成功;false表示失败 |
其他 | 含义跟标准的HTTP返回码相同,例如500代表Internal Server Error;404代表No such page |
API的HTTP response返回的是一个Job结构,调用者需要用该Job的uuid去轮询查询API是否已经完成。API Job的格式如下:
字段 | 类型 | 描述 |
---|---|---|
uuid | 字符串 | JOB UUID,用户可以用该UUID去查询一个API JOB当前的执行状态。当state == Done时,该字段可能为NULL。 |
state | 字符串 | JOB状态。包括两种状态:Processing,该JOB还在执行;Done,JOB已经完成 |
result | 字符串 | API结果,JSON字符串。当state = Processing时,该字段为NULL |
createdDate | 字符串 | JOB创建时间 |
finishedDate | 字符串 | JOB完成时间 |
- {
- "uuid": "fee83ed8529340cd8528b3b408dc16b0",
- "state": "Processing",
- "createdDate": "Feb 16, 2016 5:01:11 PM"
- }
- {
- "uuid": "fee83ed8529340cd8528b3b408dc16b0",
- "state": "Done",
- "createdDate": "Feb 16, 2016 5:01:11 PM",
- "finishedDate": "Feb 16, 2016 5:01:12 PM",
- "result": "{"org.zstack.header.zone.APICreateZoneEvent":{"inventory":{"uuid":"f1ed8f95163c4b6c88a8c0673b7913b7","name":"xx","state":"Enabled","type":"zstack","createDate":"Feb 16, 2016 5:01:12 PM","lastOpDate":"Feb 16, 2016 5:01:12 PM"},"success":true}}"
- }
当调用一个API后,HTTP response返回一个Job结构,其中包含有该Job的uuid,用户需要通过该uuid去查询该Job的状态。
查询的方法是向http://locahost:8080/api/result/{job uuid}发送一个HTTP Get请求,例如:
- curl http://localhost:8080/zstack/api/result/fee83ed8529340cd8528b3b408dc16b0
其HTTP response返回一个Job结构,用户可以通过判断Job的state字段来判断Job是否完成,从而决定是否需要继续轮询Job的状态
ZStack API的调用的标准流程是:
5.1 登录
在调用其他API前,用户需要首先调用LogInByAccount以获得一个session UUID,以此作为后续API调用的token。该API的描述符为:org.zstack.header.identity.APILogInByAccountMsg,包含以下字段:
字段 | 描述 |
---|---|
accountName | 用户名。例如admin| |
password | 密码。zstack不对密码进行任何处理,只是单纯将其与数据库中存储的密码数据进行字符串比对。我们建议在调用CreateAccount这样的API时,密码使用sha512加密。 |
默认用户名密码:ZStack默认的用户名是admin,其密码明文为password,sha512哈希后的结果为
b109f3bbbc244eb82441917ed06d618b9008dd09b3befd1b5e07394c706a8bb980b1d7785e5976ec049b46df5f1326af5a2ea6d103fd07c95385ffab0cacbc86
所以该API的JSON描述类似于:
- {
- "org.zstack.header.identity.APILogInByAccountMsg": {
- "password": "b109f3bbbc244eb82441917ed06d618b9008dd09b3befd1b5e07394c706a8bb980b1d7785e5976ec049b46df5f1326af5a2ea6d103fd07c95385ffab0cacbc86",
- "accountName": "admin"
- }
- }
用curl调用该API:
- curl -H "Content-Type: application/json" -d '{"org.zstack.header.identity.APILogInByAccountMsg": {"password": "b109f3bbbc244eb82441917ed06d618b9008dd09b3befd1b5e07394c706a8bb980b1d7785e5976ec049b46df5f1326af5a2ea6d103fd07c95385ffab0cacbc86", "accountName": "admin"}}' http://localhost:8080/zstack/api
返回的Job结构为:
- {
- "state": "Done",
- "createdDate": "Feb 16, 2016 7:15:01 PM",
- "finishedDate": "Feb 16, 2016 7:15:01 PM",
- "result": "{"org.zstack.header.identity.APILogInReply":{"inventory":{"uuid":"9eba9754f8c2470cbb377088e61c4da2","accountUuid":"36c27e8ff05c4780bf6d2fa65700f22e","userUuid":"36c27e8ff05c4780bf6d2fa65700f22e","expiredDate":"Feb 17, 2016 3:15:01 PM","createDate":"Feb 16, 2016 7:15:01 PM"},"success":true}}"
- }
其result字段包含的是API的结果,JSON表达为:
- {
- "org.zstack.header.identity.APILogInReply": {
- "inventory": {
- "uuid": "9eba9754f8c2470cbb377088e61c4da2",
- "accountUuid": "36c27e8ff05c4780bf6d2fa65700f22e",
- "userUuid": "36c27e8ff05c4780bf6d2fa65700f22e",
- "expiredDate": "Feb 17, 2016 3:15:01 PM",
- "createDate": "Feb 16, 2016 7:15:01 PM"
- },
- "success": true
- }
- }
其中uuid字段即是我们需要的Session UUID。
除登录(LogInByAccount)、登出(LogOut)等少数API外,剩下的都是普通API,在调用时需要传入LogIn拿到的Session UUID,其JSON原语为:
再次提醒:你可以通过查看/var/log/zstack/zstack-cli得到你在zstack-cli执行的每个API的描述符、request和response。
- {
- "API描述符": {
- "session": {
- "uuid": "LogIn时拿到的session UUID"
- },
- "API字段名1": ...,
- "API字段名2”: ...
- }
- }
当你执行完需要的API后,应该调用LogOut API退出当前session。该API的描述符是org.zstack.header.identity.APILogOutMsg,其JSON原语是:
- {
- "org.zstack.header.identity.APILogOutMsg": {
- "sessionUuid": "你要LogOut的Session UUID"
- }
- }
用curl调用:
- curl -H "Content-Type: application/json" -d '{"org.zstack.header.identity.APILogOutMsg": {"sessionUuid": "083f3518d1bd469fa0ff789fc1a25382"}}' http://localhost:8080/zstack/api
这里我们将用创建Zone这个例子,展示如何用Curl手动调用ZStack的API。
调用:
- curl -H "Content-Type: application/json" -d '{"org.zstack.header.identity.APILogInByAccountMsg": {"password": "b109f3bbbc244eb82441917ed06d618b9008dd09b3befd1b5e07394c706a8bb980b1d7785e5976ec049b46df5f1326af5a2ea6d103fd07c95385ffab0cacbc86", "accountName": "admin"}}' http://localhost:8080/zstack/api
返回:
- {"state":"Done","createdDate":"Feb 16, 2016 7:53:14 PM","finishedDate":"Feb 16, 2016 7:53:14 PM","result":"{"org.zstack.header.identity.APILogInReply":{"inventory":{"uuid":"2f68cf0a5783400694bd8efdd536a2a8","accountUuid":"36c27e8ff05c4780bf6d2fa65700f22e","userUuid":"36c27e8ff05c4780bf6d2fa65700f22e","expiredDate":"Feb 17, 2016 3:53:14 PM","createDate":"Feb 16, 2016 7:53:14 PM"},"success":true}}"}
调用:返回:
- curl -H "Content-Type: application/json" -d '{"org.zstack.header.zone.APICreateZoneMsg": {"session": {"uuid": "2f68cf0a5783400694bd8efdd536a2a8"}, "name": "zone1"}}' http://localhost:8080/zstack/api
- {"uuid":"2252332b14fb42599558577cb24fa168","state":"Processing","createdDate":"Feb 16, 2016 7:55:34 PM"}
这里我们得到了一个Job UUID 2252332b14fb42599558577cb24fa168,通过它我们可以轮询创建Zone这个API的结果。
轮询结果:
- curl http://localhost:8080/zstack/api/result/2252332b14fb42599558577cb24fa168
返回:
- {"uuid":"2252332b14fb42599558577cb24fa168","state":"Done","createdDate":"Feb 16, 2016 7:55:34 PM","finishedDate":"Feb 16, 2016 7:55:34 PM","result":"{"org.zstack.header.zone.APICreateZoneEvent":{"inventory":{"uuid":"dd625f8fb2074f4fa1c0870ef76bb24a","name":"zone1","state":"Enabled","type":"zstack","createDate":"Feb 16, 2016 7:55:34 PM","lastOpDate":"Feb 16, 2016 7:55:34 PM"},"success":true}}"}
这里我们看到该API已经完成了,state == Done。API的结果以一个JSON字符串的形式保存在result字段中。可以看到该API调用成功,因为success字段为true。
调用:
- curl -H "Content-Type: application/json" -d '{"org.zstack.header.identity.APILogOutMsg": {"sessionUuid": "2252332b14fb42599558577cb24fa168"}}' http://localhost:8080/zstack/api
返回:
- {"state":"Done","createdDate":"Feb 16, 2016 8:04:36 PM","finishedDate":"Feb 16, 2016 8:04:37 PM","result":"{"org.zstack.header.identity.APILogOutReply":{"success":true}}"}
你可以点击这里获得完整代码。
- import httplib
- import json
- import time
- # return a dict containing API return value
- def api_call(session_uuid, api_id, api_content):
- conn = httplib.HTTPConnection("localhost", 8080)
- headers = {"Content-Type": "application/json"}
- if session_uuid:
- api_content["session"] = {"uuid": session_uuid}
- api_body = {api_id: api_content}
- conn.request("POST", "/zstack/api", json.dumps(api_body))
- response = conn.getresponse()
- if response.status != 200:
- raise Exception("failed to make an API call, %s, %s" % (response.status, response.reason))
- rsp_body = response.read()
- rsp = json.loads(rsp_body)
- if rsp["state"] == "Done":
- return json.loads(rsp["result"])
- job_uuid = rsp["uuid"]
- def query_until_done():
- conn.request("GET", "/zstack/api/result/%s" % job_uuid)
- response = conn.getresponse()
- if response.status != 200:
- raise Exception("failed to query API result, %s, %s" % (response.status, response.reason))
- rsp_body = response.read()
- rsp = json.loads(rsp_body)
- if rsp["state"] == "Done":
- return json.loads(rsp["result"])
- time.sleep(1)
- print "Job[uuid:%s] is still in processing" % job_uuid
- return query_until_done()
- return query_until_done()
- def error_if_fail(rsp):
- success = rsp.values()[0]["success"]
- if not success:
- error = rsp.values()[0]["error"]
- raise Exception("failed to login, %s" % json.dumps(error))
- def login():
- content = {
- "accountName": "admin",
- "password": "b109f3bbbc244eb82441917ed06d618b9008dd09b3befd1b5e07394c706a8bb980b1d7785e5976ec049b46df5f1326af5a2ea6d103fd07c95385ffab0cacbc86"
- }
- rsp = api_call(None, "org.zstack.header.identity.APILogInByAccountMsg", content)
- error_if_fail(rsp)
- session_uuid = rsp.values()[0]["inventory"]["uuid"]
- print "successfully login, session uuid is: %s" % session_uuid
- return session_uuid
- def create_zone(session_uuid):
- content = {"name": "zone1"}
- rsp = api_call(session_uuid, "org.zstack.header.zone.APICreateZoneMsg", content)
- error_if_fail(rsp)
- print "successfully created zone1"
- def logout(session_uuid):
- content = {"sessionUuid": session_uuid}
- rsp = api_call(None, "org.zstack.header.identity.APILogOutMsg", content)
- error_if_fail(rsp)
- print "successfully logout"
- session_uuid = login()
- create_zone(session_uuid)
- logout(session_uuid)