一、簡介
openstack的各個模塊中橡伞,都有相應的客戶端模塊實現(xiàn),其作用是為用戶訪問具體模塊提供了接口晋被,并且也作為模塊之間相互訪問的途徑兑徘。Cinder也一樣,有著自己的cinder-client羡洛。
二挂脑、argparse
argparse是python用于解析命令行參數(shù)和選項的標準模塊,作為optparse的一個替代被添加到Python2.7欲侮。Cinder-client主要就是調用了argparse這個工具包崭闲,在此先介紹下它的使用。
1.使用步驟
① import argparse
② parser = argparse.ArgumentParser()
③ parser.add_argument()
④ parser.parse_args()
解釋:首先導入該模塊威蕉;然后創(chuàng)建一個解析對象刁俭;然后向該對象中添加你要關注的命令行參數(shù)和選項,每一個add_argument方法對應一個你要關注的參數(shù)或選項韧涨;最后調用parse_args()方法進行解析牍戚;解析成功之后即可使用,下面簡單說明一下步驟2和3氓奈。
方法 ArgumentParser(prog=None, usage=None,description=None, epilog=None, parents=[],formatter_class=argparse.HelpFormatter, prefix_chars='-',fromfile_prefix_chars=None, argument_default=None,conflict_handler='error', add_help=True)
這些參數(shù)都有默認值翘魄,當調用parser.print_help()
或者運行程序時由于參數(shù)不正確(此時python解釋器其實也是調用了pring_help()
方法)時,會打印這些描述信息舀奶,一般只需要傳遞description參數(shù)暑竟,如上。
方法add_argument(name or flags...[, action][, nargs][, const][, default][, type][, choices][, required][, help][, metavar][, dest])
其中:
name or flags
:命令行參數(shù)名或者選項育勺,如上面的address或者-p,--port.其中命令行參數(shù)如果沒給定但荤,且沒有設置defualt,則出錯涧至。但是如果是選項的話腹躁,則設置為None。
nargs
:命令行參數(shù)的個數(shù)南蓬,一般使用通配符表示纺非,其中,'?'表示只用一個赘方,'*'表示0到多個烧颖,'+'表示至少一個。
default
:默認值窄陡。
type
:參數(shù)的類型炕淮,默認是字符串string類型,還有float跳夭、int等類型涂圆。
help
:和ArgumentParser方法中的參數(shù)作用相似们镜,出現(xiàn)的場合也一致。
dest
:如果提供dest润歉,例如dest="a"模狭,那么可以通過args.a訪問該參數(shù)
action
:參數(shù)出發(fā)的動作
store
:保存參數(shù),默認
store_const
:保存一個被定義為參數(shù)規(guī)格一部分的值(常量)踩衩,而不是一個來自參數(shù)解析而來的值胞皱。
store_ture/store_false
:保存相應的布爾值
append
:將值保存在一個列表中。
append_const
:將一個定義在參數(shù)規(guī)格中的值(常量)保存在一個列表中九妈。
`count:參數(shù)出現(xiàn)的次數(shù)
parser.add_argument("-v", "--verbosity", action="count", default=0, help="increase output verbosity")
version
:打印程序版本信息
choice
:允許的參數(shù)值
2.范例
import argparse
def parse_args():
description = '''This is a description of this command.
bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla'''
parser = argparse.ArgumentParser(description=description)
help = 'The addresses to connect.'
parser.add_argument('addresses', nargs='*', help=help)
help = 'The port to listen on. Default to a random available port.'
parser.add_argument('-p', '--port', type=int, help=help)
help = 'The interface to listen on. Default is localhost.'
parser.add_argument('--iface', help=help, default='localhost')
help = 'The number of seconds between sending bytes.'
parser.add_argument('--delay', type=float, help=help, default=.7)
help = 'The number of bytes to send at a time.'
parser.add_argument('--bytes', type=int, help=help, default=10)
args = parser.parse_args()
return args
if __name__ == '__main__':
args = parse_args()
for address in args.addresses:
print 'The address is : %s .' % address
print 'The port is : %d.' % args.port
print 'The interface is : %s.' % args.iface
print 'The number of seconds between sending bytes : %f' % args.delay
print 'The number of bytes to send at a time : %d.' % args.bytes
執(zhí)行結果:
D:\ruijie doc\vmware\testing>python test_parse.py --help
usage: test_parse.py [-h] [-p PORT] [--iface IFACE] [--delay DELAY]
[--bytes BYTES]
[addresses [addresses ...]]
This is a description of this command. bla bla bla bla bla bla bla bla bla bla
bla bla bla bla bla
positional arguments:
addresses The addresses to connect.
optional arguments:
-h, --help show this help message and exit
-p PORT, --port PORT The port to listen on. Default to a random available
port.
--iface IFACE The interface to listen on. Default is localhost.
--delay DELAY The number of seconds between sending bytes.
--bytes BYTES The number of bytes to send at a time.
D:\ruijie doc\vmware\testing>python test_parse.py --port 10000 --delay 1.2 127.0.0.1 172.16.55.67
The address is : 127.0.0.1 .
The port is : 10000.
The interface is : localhost.
The number of seconds between sending bytes : 1.200000
The number of bytes to send at a time : 10.
The address is : 172.16.55.67 .
The port is : 10000.
The interface is : localhost.
The number of seconds between sending bytes : 1.200000
The number of bytes to send at a time : 10.
三、Cinder-client 指令代碼分析
以cinder storage-show指令作為范例分析:
[root@node1 site-packages]# cinder help storage-show
usage: cinder storage-show [--detail] <id>
Show volume storage details.
Positional arguments:
<id> Name or ID of the storage.
Optional arguments:
--detail Show detailed information about storage.
輸出結果:
1.入口
\cinderclient\v2\shell.py定義了指令的入口函數(shù)
@utils.arg('id',
metavar='<id>',
help='Name or ID of the storage.')
@utils.arg('--detail',
action='store_true',
help='Show detailed information about storage.')
def do_storage_show(cs, args):
"""Show volume storage details."""
detailed = strutils.bool_from_string(args.detail)
storage = cs.storages.get(args.id, detailed)
info = dict()
info.update(storage._info)
# 打印字典雾鬼,并對'pool_info', 'metadatas'的值字典轉換字符串
utils.print_dict(info, formatters=['pool_info', 'metadatas'])
Cinder storage-show
對應shell.py里的do_storage_show
函數(shù)萌朱,同樣的,其他指令的定義函數(shù)名字格式都是:以do_
作為前綴策菜,分隔符-對應分隔符_晶疼。
do_storage_show函數(shù)的__doc__
會成為指令的解釋說明。
@utils.arg
定義了指令的參數(shù)又憨,參數(shù)格式可參考parser.add_argument翠霍。
cs.storages指向cinderclient.v2.client.Client里定義的self.storages = storages.StoragesManager(self)
Shell.py里有兩種輸出結果的方式:
print_list
用于輸出對象列表,輸出格式如下蠢莺,類似于常見的表格寒匙,對象的屬性名作為標題。
def print_list(objs, fields, exclude_unavailable=False, formatters=None, sortby_index=0)
參數(shù):
參數(shù) | 說明 |
---|---|
objs | 要打印的對象列表 |
fields | 規(guī)定每個對象要打印的字段 |
exclude_unavailable | 決定是否把fields里無效的字段刪除躏将,fields里的字段在object里不存在即為無效字段锄弱。 |
formatters | 要格式化的字段 |
sortby_index | 輸出結果排序字段 |
print_dict
用于輸出字典類型的結果,會把對formatters字段做字典轉字符串的格式化祸憋。
def print_dict(d, property="Property", formatters=None):
2.manager
一般会宪,我們將某種資源的操作集合定義在一個文件里,比如storage蚯窥,我們定義在\cinderclient\v2\storages.py掸鹅。文件里包含兩個class,一個是Storages拦赠,是storage這個資源的實體類巍沙;另一個是StoragesManager,是storage這個資源的操作管理類矛紫,可定義增刪改查等方法赎瞎。
比如storage = cs.storages.get(args.id, detailed),調用的是
cinderclient.v2.storages.StoragesManager#get:
def get(self, storage_id, detailed=False):
storage = self._get("/storages/{storage_id}?detail={detailed}".
format(storage_id=storage_id, detailed=detailed),
'storage')
return storage
這里調用了cinderclient.base.Manager#_get(self, url, response_key=None)方法颊咬,這個方法兩個參數(shù)务甥,一個是rest url地址牡辽,一個是url返回值key。比如url="/storages/{storage_id}?detail={detailed}"的返回值是{u'storage': {...}}敞临,那么response_key即’storage’态辛,_get方法會把返回值’storage’的volue轉換成資源類Storages對象。
cinderclient.base.Manager是所有資源manager的基類挺尿,除了_get奏黑,它還定義了其它幾種常用基礎操作函數(shù):
def _create(self, url, body, response_key, return_raw=False, **kwargs): 用于創(chuàng)建資源
def _delete(self, url): 用于刪除資源
def _update(self, url, body, response_key=None, **kwargs): 用于更新資源
def _list(self, url, response_key, obj_class=None, body=None, limit=None, items=None): 用于查詢資源列表
def _get(self, url, response_key=None): 用于查詢單個資源
范例:
def list(self, storage_name=None, device_id=None,
volume_backend_name=None, usage=None, nova_aggregate_id=None,
status=None, detailed=False, project_ids=None):
url = '/storages'
filters = {}
if storage_name:
filters['storage_name'] = storage_name
if device_id:
filters['device_id'] = device_id
if volume_backend_name:
filters['volume_backend_name'] = volume_backend_name
if usage:
filters['usage'] = usage
if nova_aggregate_id:
filters['nova_aggregate_id'] = nova_aggregate_id
if status:
filters['status'] = status
if detailed is True:
filters['detailed'] = 'true'
filters = utils.unicode_key_value_to_string(filters)
if filters:
params = sorted(filters.items(), key=lambda x: x[0])
query_string = "?%s" % parse.urlencode(params)
url = url + query_string
storages = self._list(url, 'storages')
return storages
def get(self, storage_id, detailed=False):
storage = self._get("/storages/{storage_id}?detail={detailed}".
format(storage_id=storage_id, detailed=detailed),
'storage')
return storage
def create(self, storage_name, device_id, metadata, usage, nova_aggregate_id):
body = {
"storage": {
"storage_name": storage_name,
"device_id": device_id,
"metadatas": metadata,
"usage": usage,
"nova_aggregate_id": nova_aggregate_id
}
}
return self._create('/storages', body, 'storage')
def update(self, storage_id, storage_name=None, device_id=None, metadatas=None):
body = {
"storage": {
}
}
if storage_name is not None:
body['storage']['storage_name'] = storage_name
if device_id is not None:
body['storage']['device_id'] = device_id
if metadatas is not None:
body['storage']['metadatas'] = metadatas
return self._update('/storages/%s' % storage_id, body, 'storage')
def delete(self, storage_id):
return self._delete("/storages/%s" % storage_id)
定義完資源的manager類,要記得在client.py的Client類里引入编矾,提供給shell.py和其他openstack組件調用熟史。比如:
四、Openstack其他組件調用cinder-client
Openstack組件之間調用窄俏,一般都是通過組件的client提供的接口蹂匹。比如nova的\nova\nova\volume\cinder.py定義了cinder-api的集合類nova.volume.cinder.API。要查詢cinder的volume列表凹蜈,可調用nova.volume.cinder.API#get_all
:
@translate_cinder_exception
def get_all(self, context, search_opts=None):
search_opts = search_opts or {}
items = cinderclient(context).volumes.list(detailed=True,
search_opts=search_opts)
rval = []
for item in items:
rval.append(_untranslate_volume_summary_view(context, item))
return rval
可以看到荔仁,get_all函數(shù)里調用cinderclient(context).volumes.list(detailed=True, search_opts=search_opts)
敢艰,即調用了cinderclient的cinderclient.v2.volumes.VolumeManager#list
方法。
當然我們cinder調用nova的方法,也是這樣亥啦。Cinder有\(zhòng)cinder\compute\nova.py醋粟,可通過cinder.compute.nova.API里的方法調用novaclient接口凤价。比如創(chuàng)建卷快照:
def create_volume_snapshot(self, context, volume_id, create_info):
nova = novaclient(context, privileged_user=True)
# pylint: disable=E1101
nova.assisted_volume_snapshots.create(
volume_id,
create_info=create_info)
nova.assisted_volume_snapshots.create
即調用novaclient.v2.assisted_volume_snapshots.AssistedSnapshotManager#create
褥蚯。
五、cinderclient-ext
安裝:pip install python-brick-cinderclient-ext
cinderclient-ext提供了cinder-client的拓展指令传泊。
入口定義在\brick_cinderclient_ext\__init__.py
鼠渺,增加了五個指令:
do_get_connector
do_local_attach
do_local_detach
do_get_volume_paths
do_get_all_volume_paths