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. 总结

那么 OpenStackgnocchiclient 的工作模式我们就理清了,大概是

  • /usr/bin/gnocchi
  • /gnocchiclient 目录下的 shell.py
  • shell.py 中 GnocchiShell 继承 cliff 框架 app 类,对命令行参数进行解析,最后决定进入交互模式还是直接执行子命令

除了 gnocchiclientOpenStack 中其他诸如 novaclientneutronclient 的工作原理也大致相似。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,519评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,842评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,544评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,742评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,646评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,027评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,513评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,169评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,324评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,268评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,299评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,996评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,591评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,667评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,911评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,288评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,871评论 2 341