IaaS軟件用戶面臨的共同挑戰是如何快速、準確地找到一個想要的資源;例如,從10,000臺虛擬機中發現有EIP(16.16.16.16)的虛擬機。大多數IaaS軟件經過API中的特定查詢邏輯解決這個問題。ZStack不用特定查詢,而是配備了一個框架,這個框架能夠自動爲每一個資源的每一個字段生成查詢,並聯合跨越了多個資源的查詢,幫助用戶管理雲端數量龐大的資源。web
動機數據庫
一箇中型的雲能夠管理幾百臺物理主機和成千上萬臺虛擬機,由於IaaS軟件不多有所有的查詢API,致使尋找想要的資源成爲挑戰。大多數IaaS軟件只容許用戶使用少許條件(如name,UUID)查詢資源,這些條件硬編碼在查詢API中。若是用戶想要使用硬編碼以外的條件作一個查詢,例如,經過建立日期查詢虛擬機,他們可能不得不最終列出全部虛擬機,而後用for..loop來過濾結果。使用任意字段查詢資源至今在大多數IaaS軟件都不被徹底支持,更不用說聯合查詢;例如,若是用戶想要找到一個虛擬機,這個虛擬機的網卡應用了特定的安全組規則,他們可能不得不列出全部資源(虛擬機,安全組),而後作兩次for..loop。安全
另外一方面,相比相似JIRA的軟件,大多數IaaS軟件的UI是最粗糙簡陋的。許多開發人員可能並無意識到糟糕UI的根源不是由於UI開發人員缺少CSS/HTML/JavaScript的技能,而是軟件自己並不能提供強大的API來支持複雜的UI;例如,爲了實現一個相似JIRA過濾器的功能,即只顯示知足指定條件的資源,UI可能須要作不少的須要listing all then filtering by for..loop的後置處理工做,這些後置處理工做將把大量的資源擰在一塊兒。網絡
IaaS軟件所以飽受折磨了一段時間;對此的解藥就是要提供一種機制,這種機制能夠自動爲每一個資源的每一個字段都生成查詢,並且能夠處理join查詢。app
問題框架
大多數IaaS軟件使用關係型數據庫(如MySQL)做爲後臺數據庫,在這種數據庫中資源一般被安排在單獨的表中,好比虛擬機表,主機表,雲盤表。對於每個資源,都有一個API用來獲取該資源的單獨的一條,它可能被命名爲describe API
、list API
或query API
;這些API一般有硬編碼的參數,用來暴露一部分數據庫表的列,容許用戶經過少許的查詢條件查詢資源;這些參數是精心選擇的,一般是對API設計者自身很是重要的列,例如,name、UUID。然而,因爲並非全部的列都被暴露,用戶常常遇到他們想查詢的列不存在的狀況,這樣他們就必須檢索全部的資源,而後使用一個或多個for..loop
進行後置處理。工具
一個複雜的查詢場景,可能須要使用聯合查詢,這種查詢一般跨越多個數據庫表;例如,找到一個EIP爲16.16.16.16的虛擬機,它可能涉及虛擬表、網卡表和EIP表。一些IaaS軟件使用數據庫視圖解決這個問題,這是另外一種硬編碼方式,只能以固定的格式join選中的表,而在現實中表是能以很是複雜的方式被join的。在軟件升級過程當中,若是一個視圖指向的任一表已經改變了的話,視圖也須要進行數據庫遷移操做。oop
查詢APIui
爲了不在API中手動編碼查詢邏輯,並給用戶提供能在任何地方查詢任何東西的靈活的查詢,ZStack建立了一個框架,這個框架能夠自動爲全部資源生成查詢,而且不須要開發者寫代碼去實現查詢邏輯;更進一步,該框架還能夠生成各類join查詢,只要所需的表已經經過外鍵鏈接。編碼
在如下篇幅中,咱們將用zone做爲一個例子來闡述這個使人驚歎的框架。一個zone在數據庫中有下面的這些列:
FIELD |
DESCRIPTION |
uuid |
zone UUID |
name |
zone name |
description |
zone description |
state |
zone state |
type |
zone type |
createDate |
the time the zone was created |
lastOpDate |
the last time the zone was operated |
用戶能夠經過任何一個字段或字段組合來查詢zone,並採用常規的SQL比較運算符如'=', '!=', '>', '>=', '<', '<=', 'in', 'not in', 'is null', 'is not null', 'like', 'not like'。
注意:在命令行工具中,一些運算符有不一樣的格式:'in'(?=), 'not in'(!?=), 'is null'(=null), 'is not null'(!=null), 'like'(~=), 'not like'(!~=).
QueryZone name=west-coast-zone
QueryZone name=west-coast-zone state=Enabled
由於zone是ZStack中主要資源的祖先,不少資源都或多或少和它有關係;例如,一個運行中的虛擬機老是在一個zone內。像這種關係能夠生成聯合查詢,如:
QueryZone vmInstance.name=web-vm1
如上表格所示,一個zone不會暴露任何叫vmInstance的字段,但在上述查詢中有一個條件是由'vmInstance'開始的。這種查詢在ZStack中稱爲擴展查詢。這裏vmInstance
表明VM表,VM表有一個字段爲zoneUuid
(外鍵)指向zone表,所以查詢框架能夠理解它們的關係並生成聯合查詢。上面的例子能夠被解釋爲「尋找運行着名字爲web-vm1的虛擬機的zone」。進一步擴展這個例子,由於虛擬機網卡表有外鍵指向VM表,而且EIP表有外鍵指向虛擬機網卡表,查詢zone也可使用EIP做爲條件:
QueryZone vmInstance.vmNics.eip.vipIp=16.16.16.16
查詢被解釋爲「查找一個區域,它上面的虛擬機的網卡的EIP爲 16.16.16.16」。如今您知道了查詢接口的強大之處了!咱們甚至能夠建立一些很是複雜的查詢:
QueryVolumeSnapshot volume.vmInstance.vmNics.l3Network.l2Network.attachedClusterUuids=13238c8e0591444e9160df4d3636be82
這個複雜的查詢目的是找到磁盤快照,目標磁盤快照是由虛擬機磁盤建立的,而該虛擬機有網卡在L3網絡上,這個L3網絡的父L2網絡則是附加在一個集羣上的,這個集羣的uuid是13238c8e0591444e9160df4d3636be82。不要驚慌,你不多須要這麼複雜的查詢,但它確實證實了框架的能力。此外,SQL的一些特性例如選擇字段、排序、計數和分頁也是支持的:
QueryL3Network name=L3-SYSTEM-PUBLIC count=true
QueryL3Network l2NetworkUuid=33107835aee84c449ac04c9622892dec limit=10
QueryL3Network l2NetworkUuid=33107835aee84c449ac04c9622892dec start=10 limit=100
QueryL3Network fields=name,uuid l2NetworkUuid=33107835aee84c449ac04c9622892dec
QueryL3Network l2NetworkUuid=33107835aee84c449ac04c9622892dec sortBy=createDate sortDirection=desc
實現
儘管查詢API功能是如此強大,實現倒是很是簡潔的。當添加一個新的資源時,開發人員不須要寫任何關於查詢邏輯的代碼,除了定義查詢API和資源自己。要實現zone的查詢API,開發人員須要:
@Inventory(mappingVOClass=ZoneVO.class)
@PythonClassInventory
@ExpandedQueries({
@ExpandedQuery(expandedField="vmInstance",inventoryClass=VmInstanceInventory.class,
foreignKey="uuid",expandedInventoryKey="zoneUuid"),
@ExpandedQuery(expandedField="cluster",inventoryClass=ClusterInventory.class,
foreignKey="uuid",expandedInventoryKey="zoneUuid"),
@ExpandedQuery(expandedField="host",inventoryClass=HostInventory.class,
foreignKey="uuid",expandedInventoryKey="zoneUuid"),
@ExpandedQuery(expandedField="primaryStorage",inventoryClass=PrimaryStorageInventory.class,
foreignKey="uuid",expandedInventoryKey="zoneUuid"),
@ExpandedQuery(expandedField="l2Network",inventoryClass=L2NetworkInventory.class,
foreignKey="uuid",expandedInventoryKey="zoneUuid"),
@ExpandedQuery(expandedField="l3Network",inventoryClass=L3NetworkInventory.class,
foreignKey="uuid",expandedInventoryKey="zoneUuid"),
@ExpandedQuery(expandedField="backupStorageRef",inventoryClass=BackupStorageZoneRefInventory.class,
foreignKey="uuid",expandedInventoryKey="zoneUuid",hidden=true),
})
@ExpandedQueryAliases({
@ExpandedQueryAlias(alias="backupStorage",expandedField="backupStorageRef.backupStorage")
})
publicclassZoneInventoryimplementsSerializable{
privateStringuuid;
privateStringname;
privateStringdescription;
privateStringstate;
privateStringtype;
privateTimestampcreateDate;
privateTimestamplastOpDate;
}
上面的註解聲明瞭zone和其餘資源之間的關係,這是zone擴展查詢的基礎。
2.定義一個查詢API
@AutoQuery(replyClass=APIQueryZoneReply.class,inventoryClass=ZoneInventory.class)
publicclassAPIQueryZoneMsgextendsAPIQueryMessage{
}
3.在區域的API配置文件中聲明查詢API
<?xml version="1.0" encoding="UTF-8"?>
<servicexmlns="http://zstack.org/schema/zstack">
<id></id> zone
<interceptor></interceptor> ZoneApiInterceptor
<message>
<name></name> org.zstack.header.zone.APIQueryZoneMsg
<serviceId></serviceId> query
</message>
</service>
API APIQueryZoneMsg
經過指定服務ID query被路由到查詢服務。就是這樣了,查詢邏輯不須要一行代碼;查詢服務會把其他部分自動完成。全部的ZStack查詢API都像這樣定義,添加新資源的查詢API是很是容易的。
當前限制
主要的限制是在查詢條件中,只有邏輯AND
是被支持的,OR是不被支持的。例如:
QueryZone name=west-coast-zone state=Enabled
上述查詢語句能夠被解釋爲「尋找區域名字爲west-coast且state是Enabled的區域」咱們這麼作的緣由是咱們由ZStack源代碼中SQL的使用分析得出99%的組合的查詢條件都是基於AND邏輯的。另外一方面,若是邏輯OR
在沒有建立DSL的狀況下就被引入,要保持代碼簡潔是很是困難的。然而,在不少狀況下,OR可使用比較操做in(?=)實現:
QueryZone name=west-coast-zone state?=Enabled,Disabled
上述例子表述的是「尋找名字爲west-coast的區域,而且它的狀態是Enabled或Disabled」,未來,咱們將引入DSL風格的查詢語言,例如:
QueryZone name=west-coast-zone AND (state=Enabled OR state=Disabled)
總結
這篇文章中,咱們演示了ZStack的查詢API。經過使用這個強大的工具,用戶能以相似關係型數據庫的方式查詢任何資源。未來,ZStack將創建一套高級的UI,它可使用查詢API建立各類各樣的視圖(過濾器),例如,展現全部運行在同一L3網絡的虛擬機,爲IaaS UI的用戶體驗帶來革命性的改變。