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的用户体验带来革命性的改变。