一、gnocchiclient not working
在 OpenStack 環(huán)境中安裝了 gnocchi 后,調(diào)用 gnocchi 命令,出現(xiàn)如下提示:
[root@controller1 ~]# gnocchi status
gnocchi: 'status' is not a gnocchi command. See 'gnocchi --help'.
Did you mean one of these?
complete
help
照理說,是沒安裝 gnocchiclient 。于是檢查一下環(huán)境:
[root@controller1 ~]# rpm -qa | grep gnocchiclient
python2-gnocchiclient-3.3.1-1.el7.noarch
有啊,到 /usr/lib/python2.7/site-packages/gnocchiclient 目錄下看看代碼,都有啊,怎么回事兒?
用 gnocchi: 'status' is not a gnocchi command. See 'gnocchi --help'. 谷歌一下,點(diǎn)開幾個(gè)鏈接,貌似大家都沒遇到過這種問題。
與此同時(shí),我用一臺centos7虛擬機(jī)裝了 gnocchiclient 作為試驗(yàn),運(yùn)行 gnocchi status,可以使用該指令,只不過由于沒安裝別的服務(wù),指令報(bào)錯:
[root@centos-test gnocchiclient]# gnocchi status
Unable to establish connection to http://localhost:8041/v1/status?details=False: HTTPConnectionPool(host='localhost', port=8041): Max retries exceeded with url: /v1/status?details=False (Caused by NewConnectionError('<requests.packages.urllib3.connection.HTTPConnection object at 0x7fbe17fd7a50>: Failed to establish a new connection: [Errno 111] Connection refused',))
迫不得已看了源碼,還是找不到頭緒,百思不得其解之際,在某個(gè)地方看到了這個(gè):

參考鏈接:https://github.com/gnocchixyz/python-gnocchiclient/issues/56
我的 gnocchiclient 版本正是 3.3.1 ,會不會是別的依賴版本不對?
于是我在測試的虛擬機(jī)上查看了 cliff 的版本:
[root@centos-test gnocchiclient]# pip list | grep cliff
cliff (2.8.0)
在 controller 機(jī)子上查看 cliff 版本:
[root@controller1 ~]# pip list | grep cliff
cliff 2.14.1
有戲?
于是慎重地下載了 cliff 2.8.0 版本:
pip install cliff==2.8.0
再試一下指令:
gnocchi status
+-----------------------------------------------------+-------+
| Field | Value |
+-----------------------------------------------------+-------+
| storage/number of metric having measures to process | 48 |
| storage/total number of measures to process | 48 |
+-----------------------------------------------------+-------+
成功了。
有時(shí)候就是這么巧。
二、cliff是什么?
之前看 gnocchiclient 的時(shí)候就發(fā)現(xiàn)了 cliff,也去查了下它的作用,這里再次梳理一下。
cliff(CommandLine Interface Formulation Framework)即命令行接口制定框架??啥x多級命令、輸出格式以及其他一些擴(kuò)展來創(chuàng)建命令行相關(guān)項(xiàng)目。Cliff框架中定義的主程序處理參數(shù)解析,并調(diào)用子命令來完成工作。
以上來自這篇博客 https://blog.csdn.net/bc_vnetwork/article/details/53939946
OpenStack 各種 client 就是基于 cliff 框架寫的,讓你在命令行就可以操作 OpenStack 服務(wù)并獲得格式化的輸出。
gnocchiclient大概是怎么工作的呢?
三、gnocchiclient工作原理淺析
1 shell.py
首先看 /usr/lib/python2.7/site-packages/gnocchiclient/shell.py
shell文件的 main 方法接收命令行發(fā)來的指令,調(diào)用 GnocchiShell().run(args) 開始工作
def main(args=None):
if args is None:
args = sys.argv[1:]
return GnocchiShell().run(args)
2 GnocchiShell
大致看一眼 GnocchiShell
from cliff import app
......
class GnocchiShell(app.App):
def __init__(self):
super(GnocchiShell, self).__init__(
description='Gnocchi command line client',
# FIXME(sileht): get version from pbr
version=__version__,
command_manager=GnocchiCommandManager(None),
deferred_help=True,
)
self._client = None
def build_option_parser(self, description, version):
"""Return an argparse option parser for this application.
Subclasses may override this method to extend
the parser with more global options.
:param description: full description of the application
:paramtype description: str
:param version: version number for the application
:paramtype version: str
"""
parser = super(GnocchiShell, self).build_option_parser(
description,
version,
argparse_kwargs={'allow_abbrev': False})
parser.add_argument(
'--gnocchi-api-version',
default=os.environ.get('GNOCCHI_API_VERSION', '1'),
help='Defaults to env[GNOCCHI_API_VERSION] or 1.')
gnocchishell 繼承了 cliff 里的 app,在 /usr/lib/python2.7/site-packages/gnocchiclient/shell.py 里,我們沒有找到 run 方法,需要到 cliff 源碼里找
3. cliff app
locate 一下 cliff,找到 /usr/lib/python2.7/site-packages/cliff/app.py,看一下 run 方法:
66 def __init__(self, description, version, command_manager,
67 stdin=None, stdout=None, stderr=None,
68 interactive_app_factory=None,
69 deferred_help=False):
70 """Initialize the application.
71 """
72 self.command_manager = command_manager
73 self.command_manager.add_command('help', help.HelpCommand)
74 self.command_manager.add_command('complete', complete.CompleteCommand)
75 self._set_streams(stdin, stdout, stderr)
76 self.interactive_app_factory = interactive_app_factory
77 self.deferred_help = deferred_help
78 self.parser = self.build_option_parser(description, version)
79 self.interactive_mode = False
80 self.interpreter = None
240 def run(self, argv):
241 """Equivalent to the main program for the application.
242
243 :param argv: input arguments and options
244 :paramtype argv: list of str
245 """
246 try:
247 self.options, remainder = self.parser.parse_known_args(argv)
248 self.configure_logging()
249 self.interactive_mode = not remainder
250 if self.deferred_help and self.options.deferred_help and remainder:
251 # When help is requested and `remainder` has any values disable
252 # `deferred_help` and instead allow the help subcommand to
253 # handle the request during run_subcommand(). This turns
254 # "app foo bar --help" into "app help foo bar". However, when
255 # `remainder` is empty use print_help_if_requested() to allow
256 # for an early exit.
257 # Disabling `deferred_help` here also ensures that
258 # print_help_if_requested will not fire if called by a subclass
259 # during its initialize_app().
260 self.options.deferred_help = False
261 remainder.insert(0, "help")
262 self.initialize_app(remainder)
263 self.print_help_if_requested()
264 except Exception as err:
265 if hasattr(self, 'options'):
266 debug = self.options.debug
267 else:
268 debug = True
269 if debug:
270 self.LOG.exception(err)
271 raise
272 else:
273 self.LOG.error(err)
274 return 1
275 result = 1
276 if self.interactive_mode:
277 result = self.interact()
278 else:
279 result = self.run_subcommand(remainder)
280 return result
這里的關(guān)鍵是 247 行的 reminder,它是cliff 中一個(gè)參數(shù)解析器解析參數(shù)后的返回值。
我們進(jìn)行斷點(diǎn)調(diào)試,看一下 reminder 的返回值。
[root@controller1 cliff]# gnocchi status
> /usr/lib/python2.7/site-packages/cliff/app.py(248)run()
-> self.options, remainder = self.parser.parse_known_args(argv)
(Pdb) n
> /usr/lib/python2.7/site-packages/cliff/app.py(249)run()
-> self.configure_logging()
(Pdb) p remainder
['status']
(Pdb)
可以看到,當(dāng)執(zhí)行 gnocchi status 命令時(shí),reminder 的值就是 gnocchi 后面跟的參數(shù)。
結(jié)合 /usr/lib/python2.7/site-packages/cliff/app.py 第249和276行往下可以明白,只輸入 gnocchi 時(shí),就進(jìn)入了 gnocchiclient 的交互模式,而后面跟上參數(shù),就直接執(zhí)行 gnocchi 后面參數(shù)的子命令。
至于后面如何執(zhí)行子命令,這里就不繼續(xù)了。
4. /usr/bin/gnocchi
最后一個(gè)疑問,為什么我們在命令行里輸入 gnocchi xxx,就會調(diào)用到 /usr/lib/python2.7/site-packages/gnocchiclient/shell.py 里的方法呢?
看到這個(gè)文件你就明白了 /usr/bin/gnocchi
import sys
from gnocchiclient.shell import main
if __name__ == "__main__":
sys.exit(main())
5. 總結(jié)
那么 OpenStack 中 gnocchiclient 的工作模式我們就理清了,大概是
- /usr/bin/gnocchi
- /gnocchiclient 目錄下的 shell.py
- shell.py 中
GnocchiShell繼承cliff框架app類,對命令行參數(shù)進(jìn)行解析,最后決定進(jìn)入交互模式還是直接執(zhí)行子命令
除了 gnocchiclient,OpenStack 中其他諸如 novaclient、neutronclient 的工作原理也大致相似。