gnocchiclient使用查错及工作原理浅析
一、gnocchiclient not working
在 OpenStack
环境中安装了 gnocchi
后,调用 gnocchi
命令,出现如下提示:
[root@controller1 ~]# gnocchi status
gnocchi: 'status' is not a gnocchi command. See 'gnocchi --help'.
Did you mean one of these?
complete
help
照理说,是没安装 gnocchiclient 。于是检查一下环境:
[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'.
谷歌一下,点开几个链接,貌似大家都没遇到过这种问题。
与此同时,我用一台centos7虚拟机装了 gnocchiclient 作为试验,运行 gnocchi status
,可以使用该指令,只不过由于没安装别的服务,指令报错:
[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',))
迫不得已看了源码,还是找不到头绪,百思不得其解之际,在某个地方看到了这个:
gnocchi.png参考链接:https://github.com/gnocchixyz/python-gnocchiclient/issues/56
我的 gnocchiclient 版本正是 3.3.1 ,会不会是别的依赖版本不对?
于是我在测试的虚拟机上查看了 cliff
的版本:
[root@centos-test gnocchiclient]# pip list | grep cliff
cliff (2.8.0)
在 controller 机子上查看 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 |
+-----------------------------------------------------+-------+
成功了。
有时候就是这么巧。
二、cliff是什么?
之前看 gnocchiclient 的时候就发现了 cliff
,也去查了下它的作用,这里再次梳理一下。
cliff(CommandLine Interface Formulation Framework)即命令行接口制定框架。可定义多级命令、输出格式以及其他一些扩展来创建命令行相关项目。Cliff框架中定义的主程序处理参数解析,并调用子命令来完成工作。
以上来自这篇博客 https://blog.csdn.net/bc_vnetwork/article/details/53939946
OpenStack
各种 client
就是基于 cliff
框架写的,让你在命令行就可以操作 OpenStack
服务并获得格式化的输出。
gnocchiclient
大概是怎么工作的呢?
三、gnocchiclient工作原理浅析
1 shell.py
首先看 /usr/lib/python2.7/site-packages/gnocchiclient/shell.py
shell文件的 main
方法接收命令行发来的指令,调用 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
这里的关键是 247
行的 reminder
,它是cliff
中一个参数解析器解析参数后的返回值。
我们进行断点调试,看一下 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)
可以看到,当执行 gnocchi status
命令时,reminder
的值就是 gnocchi
后面跟的参数。
结合 /usr/lib/python2.7/site-packages/cliff/app.py
第249和276行往下可以明白,只输入 gnocchi
时,就进入了 gnocchiclient
的交互模式,而后面跟上参数,就直接执行 gnocchi
后面参数的子命令。
至于后面如何执行子命令,这里就不继续了。
4. /usr/bin/gnocchi
最后一个疑问,为什么我们在命令行里输入 gnocchi xxx
,就会调用到 /usr/lib/python2.7/site-packages/gnocchiclient/shell.py
里的方法呢?
看到这个文件你就明白了 /usr/bin/gnocchi
import sys
from gnocchiclient.shell import main
if __name__ == "__main__":
sys.exit(main())
5. 总结
那么 OpenStack
中 gnocchiclient
的工作模式我们就理清了,大概是
- /usr/bin/gnocchi
- /gnocchiclient 目录下的 shell.py
- shell.py 中
GnocchiShell
继承cliff
框架app
类,对命令行参数进行解析,最后决定进入交互模式还是直接执行子命令
除了 gnocchiclient
,OpenStack
中其他诸如 novaclient
、neutronclient
的工作原理也大致相似。