这篇文章的目的在于探究如何自定义一个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_cmds的cli_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变量里面的所有pattern和desc.也就是我们执行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数组定义的pattern和desc