Clixon cli

  • clixon cli 备忘录
  • 更新
1
2025.05.00 初始 初始

https://clixon-docs.readthedocs.io/en/latest/cli.html

Clixon CLI

  • 基于 cligen 实现, 但是提供了一系列 auto_x 的简便写法;
  • 每次都会实例化一个进程;
  • 所有请求都会转换为 netconf 传递给 (ipc) backend;

定制 cli: 3 个部分, 配置文件; .cli ; plugin;

  • 配置文件:
1
2
3
4
// 初始 cli 启动时候的 mode
<CLICON_CLI_MODE>example</CLICON_CLI_MODE>
// cli plugin 位置
<CLICON_CLI_DIR>/usr/lib64/clixon/plugins/cli</CLICON_CLI_DIR>

^afd63e

  • .cli 是定制 cli 环境
    • 每个 .cli 只能通过 CLICON_PLUGIN= 加载一个 cli plugin
    • 但是 clixon 可以加载多个 .cli 文件, 后面互相覆盖合并;
  • plugin 回调函数可以在 .cli 中调用 Clixon cli#^ua1ak

cligen 资料在 cligen_tutorial.pdf

启动调试 cli

1
clixon_cli -f clixon.xml -D all -D detail -l f'cli.log' -U root

数据结构

Cvec

[[cligen_tutorial-3.pdf#page=22&selection=155,0,155,23&color=yellow|Cligen variable vectors]] cligen 的动态数组 (存入不同类型不同长度的数据)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
if (cvec_len(argv) > 2){
formatstr = cv_string_get(cvec_i(argv, 2));
if ((int)(format = format_str2int(formatstr)) < 0){
clixon_err(OE_PLUGIN, 0, "Not valid format: %s", formatstr);
goto done;
}
}

dbstr = cv_string_get(cvec_i(argv, 0)); // 执行的命令
if (strcmp(dbstr, "running") != 0 &&
strcmp(dbstr, "candidate") != 0 &&
strcmp(dbstr, "startup") != 0) {
clixon_err(OE_PLUGIN, 0, "No such db name: %s", dbstr);
goto done;
}
varstr = cv_string_get(cvec_i(argv, 1));
if ((cv = cvec_find(cvv, varstr)) == NULL){
clixon_err(OE_PLUGIN, 0, "No such var name: %s", varstr);
goto done;
}

cb 中打印似乎是打印最后一次 clixon_err 的内容; 只要调用 clixon 后不覆盖, 就能打印其错误;

typedef struct cvec cvec;

定义在 cligen

1
2
3
4
6.2.1 Multiple callbacks
Several callbacks may be associated with a syntax. Example:
hello world, callback("arg"), extra();
hello world, extra2()

.cli

clixon 在 cligen 基础上提供了 @auto 函数方便定制; 示例 cli 文件 -> example_cli.cli

.cli 文件开头

1
2
3
4
# Clixon example specification
CLICON_MODE="example";
CLICON_PROMPT="%U@%H %W> ";
CLICON_PLUGIN="example_cli";
  • CLICON_MODE 对应 <CLICON_CLI_MODE> 字段名
  • CLICON_PROMPT 是提示符的样式
  • CLICON_PLUGIN 是当前 .cli 文件内引用的 plugin

Mode

CLI 可以有多个 CLICON_MODE, 每种 MODE 下有不同的命令;

  • 例如 Juniper CLI 风格下的 operation 和 config; 仅在 config 下才能修改配置;

CLICON_MODE="example"; Mode 名称;

一个 .cli 可以对应多个 mode 非常灵活 , 最终运行时候合并多个 .cli.

1
2
3
4
5
6
7
8
9
10
11
# file 1
CLICON_MODE="configure";
show configure;

# file 2
CLICON_MODE="operation:configure";
show("Show") files("Show files");

# file 3
CLICON_MODE="*"; // 任意 mode 都会添加 show all
show("Show") all("Show all");
1
2
3
4
5
6
7
8
# operation mode
user@host> show <TAB>
all files

# configure mode
> clixon_cli -m configure
user@host> show <TAB>
all routing files

Autocli

cli_show_auto 和 cli_show_auto_mode 这两个函数都是用于在 CLI 中显示配置数据,但它们的工作方式有所不同:

  1. 获取 XPath 的方式不同:
  • cli_show_auto: 通过命令行扩展 (expansion) 和参数 api_path_fmt 来构建 XPath
  • cli_show_auto_mode: 从当前的编辑模式 (edit mode) 获取 XPath
  1. 参数列表不同:
  • cli_show_auto 需要 api_path_fmt 和 dbname 作为必需参数
  • cli_show_auto_mode 只需要 dbname 作为必需参数
  1. 使用场景不同:
  • cli_show_auto 主要用于展开语法树的场景
  • cli_show_auto_mode 主要用于编辑模式 (edit modes) 场景

Plugin

cli 回调函数原型 int mycallback(clixon_handle h, cvec *cvv, cvec *argv); ^ua1ak

  • cvv 是用户输入的动态内容,
    • 0 是整条命令 example 23
    • 其他都是 key-value 这样的定义, 可以直接取
  • argv 是静态参数, 是 .cli 中直接传递给函数的参数
    • 直接就是 value 没有 name;
1
2
3
4
5
6
7
8
9
10
11
12
13
// .cli
example("Callback example") <var:int32>("any number"), mycallback("myarg");
// plugin
int mycallback(clixon_handle h, cvec *cvv, cvec *argv);
// shell
user@host> example 23

// 此时 回调中收到的
cvv:
0: example 23
1: 23
argv:
0: "myarg"

clixon cli 函数中通常是

  • cvv 0 是执行的语句, 1-x 是动态参数
  • 而如何使用动态参数是 argv 提供 (例: 动态参数拼接路径, 使用 argv 参数格式化)
  • 根据参数执行动作

Help Function

dump_args 打印 cli plugin 回调参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
/**
* @brief Dump variables and arguments
*
* @param h clixon handle
* @param cvv cvec of variables
* @param argv cvec of arguments
* @return int 0 on success, -1 on failure
*/
int dump_args(clixon_handle h, cvec* cvv, cvec* argv)
{
int i = 0;
cg_var* cv;
char buf[256];

clixon_debug(CLIXON_DBG_DEFAULT, "variables:\n");
cv = NULL;
while ((cv = cvec_each(cvv, cv)) != NULL) {
cv2str(cv, buf, sizeof(buf) - 1);
clixon_debug(CLIXON_DBG_DEFAULT, "\t%d name:%s type:%s value:%s\n", i++,
cv_name_get(cv), cv_type2str(cv_type_get(cv)), buf);
}
if (argv) {
clixon_debug(CLIXON_DBG_DEFAULT, "arguments:\n");
cv = NULL;
i = 0;
while ((cv = cvec_each(argv, cv)) != NULL) {
cv2str(cv, buf, sizeof(buf) - 1);
clixon_debug(CLIXON_DBG_DEFAULT, "\t%d name:%s type:%s value:%s\n",
i++, cv_name_get(cv), cv_type2str(cv_type_get(cv)),
buf);
}
}
return 0;
}

append_var_to_cvec 拼接 cvv argv

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
/**
* @brief Append variable to cvec
*
* @param vec cvec to append to
* @param name name of the variable
* @param type type of the variable, supported types are:
* - CGV_STRING
* - CGV_UINT64
* - CGV_UINT32
* - CGV_INT32
* - CGV_INT64
* - CGV_BOOL
* - CGV_REST
* @param value value of the variable
* @param value_len length of the value
* @return int 0 on success, -1 on failure
*/
int append_var_to_cvec(cvec* vec, const char* name, enum cv_type type,
void* value, size_t value_len)
{
cg_var* cv;
int ret = -1;

clixon_debug(CLIXON_DBG_DEFAULT,
"append_var_to_cvec, name %s, type %d, value %p, value_len %d",
name, type, value, value_len);

if ((cv = cv_new(type)) == NULL) {
clixon_debug(CLIXON_DBG_DEFAULT, "Failed to create cv");
goto done;
}

if (name && cv_name_set(cv, name) == NULL) {
clixon_debug(CLIXON_DBG_DEFAULT, "Failed to set name");
cv_free(cv);
goto done;
}

switch (type) {
case CGV_STRING:
if (cv_string_set(cv, (char*)value) < 0) {
clixon_debug(CLIXON_DBG_DEFAULT, "Failed to set string");
cv_free(cv);
goto done;
}
break;
case CGV_UINT64:
if (cv_uint64_set(cv, *(uint64_t*)value) < 0) {
clixon_debug(CLIXON_DBG_DEFAULT, "Failed to set uint64");
cv_free(cv);
goto done;
}
break;
case CGV_UINT32:
if (cv_uint32_set(cv, *(uint32_t*)value) < 0) {
clixon_debug(CLIXON_DBG_DEFAULT, "Failed to set uint32");
cv_free(cv);
goto done;
}
break;
case CGV_INT32:
if (cv_int32_set(cv, *(int32_t*)value) < 0) {
clixon_debug(CLIXON_DBG_DEFAULT, "Failed to set int32");
cv_free(cv);
goto done;
}
break;
case CGV_INT64:
if (cv_int64_set(cv, *(int64_t*)value) < 0) {
clixon_debug(CLIXON_DBG_DEFAULT, "Failed to set int64");
cv_free(cv);
goto done;
}
break;
case CGV_BOOL:
if (cv_bool_set(cv, *(char*)value) < 0) {
clixon_debug(CLIXON_DBG_DEFAULT, "Failed to set bool");
cv_free(cv);
goto done;
}
break;
case CGV_REST:
if (cv_string_set(cv, (char*)value) < 0) {
clixon_debug(CLIXON_DBG_DEFAULT, "Failed to set rest");
cv_free(cv);
goto done;
}
break;
default:
cv_free(cv);
goto done;
}

clixon_debug(CLIXON_DBG_DEFAULT, "append_var_to_cvec, cv %p, vec %p", cv,
vec);
if (cvec_append_var(vec, cv) == NULL) {
clixon_debug(CLIXON_DBG_DEFAULT, "Failed to append var");
cv_free(cv);
goto done;
}

ret = 0;
done:
return ret;
}