gnocchiclient使用查錯及工作原理淺析

一、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è):

gnocchi.png

參考鏈接: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é)

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

  • /usr/bin/gnocchi
  • /gnocchiclient 目錄下的 shell.py
  • shell.py 中 GnocchiShell 繼承 cliff 框架 app 類,對命令行參數(shù)進(jìn)行解析,最后決定進(jìn)入交互模式還是直接執(zhí)行子命令

除了 gnocchiclient,OpenStack 中其他諸如 novaclient、neutronclient 的工作原理也大致相似。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容