glusterfs源代码情景分析:编写一个自己的gluster CLI命令集

750 阅读5分钟
原文链接: aifei8.net

这篇文章的目的在于探究如何自定义一个glusterfs CLI命令集合,如果你对glusterfs到目前为止接触不多,甚至不理解分布式存储的概念,笔者建议你可以先了解一下他的架构:浅谈glusterfs分布式文件系统的软件架构.如果你刚那个入门,建议先了解一下cli命令的原理:glusterfs 源代码情景分析:CLI(命令行脚本). 希望能对你有所帮助!

笔者当前使用的是glusterfs最新的3.10版本,你可以使用如下指令来检出当前代码:

➜  glusterfs git:(release-3.10) git checkout release-3.10

一.说在前面的话

glusterfs 源代码情景分析:CLI(命令行脚本)这一片文章的思路一样,我们也以创建卷作为一个切入点.在上一篇中,我们有说过,可以在cli/src/cli-cmd-volume.c的第3166行发现这么一个结构体:

struct cli_cmd volume_cmds[] = {
        { "volume info [all|<VOLNAME>]",
          cli_cmd_volume_info_cbk,
          "list information of all volumes"},
...

当我们在终端运行gluster volume help,可以看到类似下面的输出:

[root@node1 ~]# gluster volume help 
volume create <NEW-VOLNAME> [stripe <COUNT>] [replica <COUNT> [arbiter <COUNT>]] [disperse [<COUNT>]] [disperse-data <COUNT>] [redundancy <COUNT>] [transport <tcp|rdma|tcp,rdma>] <NEW-BRICK>?<vg_name>... [force] - create a new volume of specified type with mentioned bricks
volume geo-replication [<VOLNAME>] [<SLAVE-URL>] {create [[ssh-port n] [[no-verify]|[push-pem]]] [force]|start [force]|stop [force]|pause [force]|resume [force]|config|status [detail]|delete [reset-sync-time]} [options...] - Geo-sync operations
...

而这部分文本就定义在这个名为volume_cmdscli_cmd结构体的实例中.这个结构体的定义在cli/src/cli-cmd.h中,内容如下:

struct cli_cmd {
        const char         *pattern;
        cli_cmd_cbk_t      *cbk;
        const char         *desc;
        cli_cmd_reg_cbk_t  *reg_cbk; /* callback to check in runtime if the   *
                                      * command should be enabled or disabled */
        gf_boolean_t       disable;
};

这是一个非常重要的结构体,我们将对他的成员逐一说明:

  • pattern:用于匹配命令,例如volume create,volume delete等等
  • cbk :这是一个函数指针,当命令匹配成功以后,就会调用这个回调函数
  • desc :这是一个字符数组,用于描述命令的用法
  • disable:这是一个枚举,类似于布尔值的变量,他被定义在libglusterfs/src/common-utils.h中,你可以在第163行看到他的申明,在129行看到他的定义:
    enum _gf_boolean
    {
      _gf_false = 0,
      _gf_true = 1
    };
    ...
    typedef enum _gf_boolean gf_boolean_t;
    
    当你的函数使用cli_cmd_register函数注册以后,这个命令才会启用,这在后面会说道

而这个gluster volume help命令定义在cli/src/cli-cmd-volume.c的第3233行:

 { "volume help",
          cli_cmd_volume_help_cbk,
          "display help for the volume command"},

我们可以看到,这条指令的回调函数为cli_cmd_volume_help_cbk,我们可以在cli/src/cli-cmd-volume.c的第3338行看到他的定义:

int cli_cmd_volume_help_cbk (struct cli_state *state, struct cli_cmd_word *in_word,
                      const char **words, int wordcount)
{
        struct cli_cmd        *cmd     = NULL;
        struct cli_cmd        *vol_cmd = NULL;
        int                   count    = 0;

        cmd = GF_CALLOC (1, sizeof (volume_cmds), cli_mt_cli_cmd);
        memcpy (cmd, volume_cmds, sizeof (volume_cmds));
        count = (sizeof (volume_cmds) / sizeof (struct cli_cmd));
        cli_cmd_sort (cmd, count);

        for (vol_cmd = cmd; vol_cmd->pattern; vol_cmd++)
                if (_gf_false == vol_cmd->disable)
                        cli_out ("%s - %s", vol_cmd->pattern, vol_cmd->desc);

        GF_FREE (cmd);
        return 0;
}

我们可以看到,这个函数打印出了volume_cmds变量里面的所有patterndesc.也就是我们执行gluster volume help所看到的. 在cli/src/cli-cmd-volume.c的第3359行我们可以看到cli_cmd_volume_register函数里面循环调用cli_cmd_register函数来注册事件:

int cli_cmd_volume_register (struct cli_state *state)
{
        ...
        for (cmd = volume_cmds; cmd->pattern; cmd++) {
                ret = cli_cmd_register (&state->tree, cmd);
                if (ret)
                        goto out;
        }
out:
        return ret;
}

我们再看一下这个注册函数是如何回调的.在cli/src/cli-cmd.c的第209行,有一个名为cli_cmds_register的函数.内容如下:

int cli_cmds_register (struct cli_state *state)
{
        int  ret = 0;
        ret = cli_cmd_volume_register (state);
        if (ret)
                goto out;
        ret = cli_cmd_probe_register (state);
        if (ret)
                goto out;
        ...

也就是说我们所定义的所有命令都将会在cli_cmds_register命令里面注册,当命令匹配的时候,就会调用指定的回调函数.只要你开心,你可以在这个回调函数里面做任何你想做的事情.

二.编写第一个cli指令:

现在,我们开始编写一个我们自己的指令集合.接下来,我们新建一个名为cli/src/cli-cmd-ourscli.c的文件,写入如下内容:

#include <stdio.h>
#include <string.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#include "cli.h"
#include "cli-cmd.h"
#include "cli-mem-types.h"
#include "cli1-xdr.h"
#include "run.h"
#include "syscall.h"
#include "common-utils.h"
#include "events.h"



int
cli_cmd_ourscli_help_cbk (struct cli_state *state, struct cli_cmd_word *in_word,
                      const char **words, int wordcount);
struct cli_cmd ourscli_cmds[] = {
        { "ourscli  help",
          cli_cmd_ourscli_help_cbk,
          "Hello world,this is our first gluster cli command"},

        { NULL, NULL, NULL }
};

int
cli_cmd_ourscli_help_cbk (struct cli_state *state, struct cli_cmd_word *in_word,
                      const char **words, int wordcount)
{
        struct cli_cmd        *cmd     = NULL;
        struct cli_cmd        *ourscli_cmd = NULL;
        int                   count    = 0;

        cmd = GF_CALLOC (1, sizeof (ourscli_cmds), cli_mt_cli_cmd);
        memcpy (cmd, ourscli_cmds, sizeof (ourscli_cmds));
        count = (sizeof (ourscli_cmds) / sizeof (struct cli_cmd));
        cli_cmd_sort (cmd, count);

        for (ourscli_cmd = cmd; ourscli_cmd->pattern; ourscli_cmd++)
                if (_gf_false == ourscli_cmd->disable)
                        cli_out ("%s - %s", ourscli_cmd->pattern, ourscli_cmd->desc);

        GF_FREE (cmd);
        return 0;
}

int
cli_cmd_ourscli_register (struct cli_state *state)
{
        int  ret = 0;
        struct cli_cmd *cmd = NULL;

        for (cmd = ourscli_cmds; cmd->pattern; cmd++) {

                ret = cli_cmd_register (&state->tree, cmd);
                if (ret)
                        goto out;
        }
out:
        return ret;
}

然后使用cli_cmds_register注册这个指令集,打开cli/src/cli-cmd.c,在第217行写入如下内容:

ret = cli_cmd_ourscli_register(state);
        if (ret)
                goto out;

到了这一步,我们还需要对这个函数做一个申明,在cli/src/cli-cmd.h加入如下内容:

int cli_cmd_ourscli_register(struct cli_state *state);

最后,修改makefile编译规则,打开cli/src/Makefile.am,将cli-cmd-ourscli.c加入到gluster_SOURCES变量末尾:

gluster_SOURCES = cli.c registry.c input.c cli-cmd.c cli-rl.c cli-cmd-global.c \
     cli-cmd-volume.c cli-cmd-peer.c cli-rpc-ops.c cli-cmd-parser.c\
     cli-cmd-system.c cli-cmd-misc.c cli-xml-output.c cli-quotad-client.c cli-cmd-snapshot.c  cli-cmd-ourscli.c

如果你觉得我表述的还不够清晰,那莫你可以在gitlab上看到我对每一个文件详细的更改:git diff

三.编译验证:

好了,激动人心的时刻来了,最后的环节,我们来编译验证.由于我使用的debian发行版的kali linux,所有在这里我使用我自己用centos搭建的docker容器来编译rpm包.你可以使用如下指令来获取这个容器:

docker pull pengfeigao/gluster-dev

为了便于使用,我将docker启动容器的命令编写为一个名为docker-gluster-dev的脚本,内容如下:

#!/bin/bash
docker run -it --name $1 -v  "$2:/root/glusterfs" pengfeigao/gluster-dev /bin/bash

你可以直接使用如下命令来启动并登入这个容器:

#必须在gluster源代码根目录,当然,你也可以使用第二个参数来指定路径
docker-gluster-dev compile1 $(pwd)
#启动容器
docker start compile1
#登入容器
docker attach compile1
cd /root/gluster

进入项目目录以后,使用git将代码检出到3.10的稳定版本:

git checkout release-3.10

然后进入extras/LinuxRPM目录,执行编译脚本:

./make_glusterrpms

编译完成以后我们可以看到有如下这些包:

glusterfs-3.10.5-0.3.git8416e31.el7.centos.src.rpm
glusterfs-3.10.5-0.3.git8416e31.el7.centos.x86_64.rpm
glusterfs-api-3.10.5-0.3.git8416e31.el7.centos.x86_64.rpm
glusterfs-api-devel-3.10.5-0.3.git8416e31.el7.centos.x86_64.rpm
glusterfs-cli-3.10.5-0.3.git8416e31.el7.centos.x86_64.rpm
glusterfs-client-xlators-3.10.5-0.3.git8416e31.el7.centos.x86_64.rpm
glusterfs-debuginfo-3.10.5-0.3.git8416e31.el7.centos.x86_64.rpm
glusterfs-devel-3.10.5-0.3.git8416e31.el7.centos.x86_64.rpm
glusterfs-events-3.10.5-0.3.git8416e31.el7.centos.x86_64.rpm
glusterfs-extra-xlators-3.10.5-0.3.git8416e31.el7.centos.x86_64.rpm
glusterfs-fuse-3.10.5-0.3.git8416e31.el7.centos.x86_64.rpm
glusterfs-ganesha-3.10.5-0.3.git8416e31.el7.centos.x86_64.rpm
glusterfs-geo-replication-3.10.5-0.3.git8416e31.el7.centos.x86_64.rpm
glusterfs-libs-3.10.5-0.3.git8416e31.el7.centos.x86_64.rpm
glusterfs-rdma-3.10.5-0.3.git8416e31.el7.centos.x86_64.rpm
glusterfs-regression-tests-3.10.5-0.3.git8416e31.el7.centos.x86_64.rpm
glusterfs-resource-agents-3.10.5-0.3.git8416e31.el7.centos.noarch.rpm
glusterfs-server-3.10.5-0.3.git8416e31.el7.centos.x86_64.rpm
Makefile
Makefile.am
Makefile.in
make_glusterrpms
python2-gluster-3.10.5-0.3.git8416e31.el7.centos.x86_64.rpm

使用yum安装所有的包:

yum install ./*.rpm -y

安装完成后我们可以执行gluster ourscli help来看一下输出:

[root@78f83d6e7c57 LinuxRPM]# gluster ourscli help
ourscli  help - Hello world,this is our first gluster cli command

输出就是我们在ourscli_cmds数组定义的patterndesc

沉着,勇猛,有辨别,不自私...