0%

clixon_backend_restconf

实习期间主要做的就是RESTCONF,Clixon这个项目是我接触的第一个restconf(也是唯一一个)实现,虽然到后来看这个项目只是给我提供了基本框架的思路,但是当时一开始看这个还是挺费劲的。

记录很详尽但是没什么用,后半段有一些RESTCONF实现的调研,可能后续整理资料的时候会再提取出来,这篇记录应该不会再有改动了。

clixon的安装等去看项目README以及doc目录下。

clixon启动命令

1
2
3
4
5
6
7
8
# backend
sudo /usr/local/sbin/clixon_backend -F -s init -f /usr/local/etc/example.xml
# restconf
sudo su -c "/www-data/clixon_restconf -f /usr/local/etc/example.xml " -s /bin/sh www-data
# unset http_proxy (will get redirected error it not do so)
unset http_proxy
# send http GET
curl -G http://127.0.0.1/restconf/data

debug mode

1
2
3
4
5
6
# run restconf in debug mode
sudo su -c "/www-data/clixon_restconf -D 1 -f /usr/local/etc/example.xml" -s /bin/sh www-data
# use authorization
sudo su -c "/www-data/clixon_restconf -D 1 -f /usr/local/etc/example.xml -- -a" -s /bin/sh www-data
# look at syslog
tail -f /var/log/syslog | grep clixon_restconf

test curl

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
new "restconf root discovery. RFC 8040 3.1 (xml+xrd)"
expecteq "$(curl -s -X GET http://localhost/.well-known/host-meta)" 0 "<XRD xmlns='http://docs.oasis-open.org/ns/xri/xrd-1.0'>
<Link rel='restconf' href='/restconf'/>
</XRD>
"
new "restconf get restconf resource. RFC 8040 3.3 (json)"
expecteq "$(curl -sG http://localhost/restconf)" 0 '{"restconf": {"data": null,"operations": null,"yang-library-version": "2016-06-21"}}

'
new "restconf get restconf resource. RFC 8040 3.3 (xml)"
# Get XML instead of JSON?
expecteq "$(curl -s -H 'Accept: application/yang-data+xml' -G http://localhost/restconf)" 0 '<restconf><data/><operations/><yang-library-version>2016-06-21</yang-library-version></restconf>''

# Should be alphabetically ordered
new "restconf get restconf/operations. RFC8040 3.3.2 (json)"
expecteq "$(curl -sG http://localhost/restconf/operations)" 0 '{"operations": {"clixon-example:client-rpc": null,"clixon-example:empty": null,"clixon-example:optional": null,"clixon-example:example": null,"clixon-lib:debug": null,"clixon-lib:ping": null,"ietf-netconf:get-config": null,"ietf-netconf:edit-config": null,"ietf-netconf:copy-config": null,"ietf-netconf:delete-config": null,"ietf-netconf:lock": null,"ietf-netconf:unlock": null,"ietf-netconf:get": null,"ietf-netconf:close-session": null,"ietf-netconf:kill-session": null,"ietf-netconf:commit": null,"ietf-netconf:discard-changes": null,"ietf-netconf:validate": null,"clixon-rfc5277:create-subscription": null}}

# "restconf Add interfaces subtree eth/0/0 using POST"
curl -s -X POST -d {"ietf-interfaces:interface":{"name":"eth/0/0","type":"ex:eth","enabled":true}} http://localhost/restconf/data/ietf-interfaces:interfaces

使用Postman

  • 终端unset http_proxy
  • Postman 关闭File > Settings > Proxy > Use system proxy
  • Postman 关闭**File > Settings > General > SSL Certificate Verification

socket 相关

  • 看这一段的目的是为了搞清楚下面两次注册的*sss*的区别.
1
2
3
4
5
6
// ss是<CLICON_SOCK>的fd
// 如果ss(server socket)有变动,那么backend_accept_client会被调用???
// ss是socket返回的套接字,只用于监听listen,不直接用于发送接受数据,也就是说,这一层的event_reg_fd的目的是监听server端口,发现client用,如果有client试图连接ss这个套接字,那么backend_accept_client函数被调用,client和server的连接会建立(因为可能会有多个client来连接server)
event_reg_fd(ss, backend_accept_client, h, "server socket")
// s是accept返回的套接字,用于client实际的通信,也就是说在event_loop机制下,如果套接字s有变动,就会触发from_client回调函数
event_reg_fd(s, from_client, (void*)ce, "local netconf client socket")

服务端:创建socket—绑定文件(端口)—监听—接受客户端连接—接收/发送数据—…—关闭

socket()--bind()--listen()--accept()--read()/write()-------close()

客户端:创建socket—绑定文件(端口)—连接—发送/接收数据—…—关闭

socket()--bind()--connect()--read()/write()-------close()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
ss = backend_socket_init(clicon_handle h)	// 连接<CLICON_SOCK> ss = socket
config_socket_init_unix(h, sock); // sock = <CLICON_SOCK> 配置文件定义的
/* 创建socket*/
s = socket(AF_UNIX, SOCK_STREAM, 0))
/* 设定family与path*/
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, sock, sizeof(addr.sun_path)-1);// sock = <CLICON_SOCK>
/* 绑定 */
bind(s, (struct sockaddr *)&addr, SUN_LEN(&addr))
listen(s, 5)
return s // return s 回去是一开始的ss
event_reg_fd(ss, backend_accept_client, h, "server socket")
/////////////////////////////////////////////////////////////
int backend_accept_client(int fd, void *arg)
len = sizeof(from);
s = accept(fd, (struct sockaddr*)&from, &len)) // accept, 把addr写到from里面(from空的)
/* Add new client, typically frontend such as cli, netconf, restconf*/
ce = backend_client_add(h, (struct sockaddr*)&from)
event_reg_fd(s, from_client, (void*)ce, "local netconf client socket")

从restconf连接backend看

调用的机制:没有长连接,每次都是短的连接。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// restconf_main.c 不细讲前面的初始化过程,从main()中最后的死循环开始讲
// 每次fastcgi接收到nginx转发的http消息,解析URL,根据类型交给api_restconf / api_stream
// simplified version (remove debug part)
while (1) {
finish = 1; /* If zero, dont finish request, initiate new */
/* fastcgi receive http message forwarded by nginx */
FCGX_Accept_r(r);
if ((path = FCGX_GetParam("REQUEST_URI", r->envp)) != NULL){
/* match request URL*/
if (strncmp(path, "/" RESTCONF_API, strlen("/" RESTCONF_API)) == 0)
api_restconf(h, r); /* This is the function */
else if (strncmp(path+1, stream_path, strlen(stream_path)) == 0) {
api_stream(h, r, stream_path, &finish);
}
}
if (finish)
FCGX_Finish_r(r);
else{ /* A handler is forked so we initiate a new request after instead
of finnishing the old */
FCGX_InitRequest(r, sock, 0)
}
}
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
// restconf_main.c 不细讲前面的初始化过程,从main()中最后的死循环开始讲
// 每次fastcgi接收到nginx转发的http消息,解析URL,根据类型交给api_restconf / api_stream
while (1) {
finish = 1; /* If zero, dont finish request, initiate new */
/* fastcgi接收到nginx转发的http消息*/
if (FCGX_Accept_r(r) < 0) {
clicon_err(OE_CFG, errno, "FCGX_Accept_r");
goto done;
}
clicon_debug(1, "------------");
if ((path = FCGX_GetParam("REQUEST_URI", r->envp)) != NULL){
clicon_debug(1, "path: %s", path);
/* 匹配了请求的URL*/
if (strncmp(path, "/" RESTCONF_API, strlen("/" RESTCONF_API)) == 0)
api_restconf(h, r); /* This is the function 关键部分*/
else if (strncmp(path+1, stream_path, strlen(stream_path)) == 0) {
api_stream(h, r, stream_path, &finish);
}
else if (strncmp(path, RESTCONF_WELL_KNOWN, strlen(RESTCONF_WELL_KNOWN)) == 0) {
api_well_known(h, r); /* */
}
else{
clicon_debug(1, "top-level %s not found", path);
notfound(r);
}
}
else
clicon_debug(1, "NULL URI");
if (finish)
FCGX_Finish_r(r);
else{ /* A handler is forked so we initiate a new request after instead
of finnishing the old */
if (FCGX_InitRequest(r, sock, 0) != 0){
clicon_err(OE_CFG, errno, "FCGX_InitRequest");
goto done;
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
api_restconf(h, r)
/* prase url, forward to api_data */
api_data(h, r, path, pcvec, 2, qvec, data, pretty, use_xml, parse_xml);
/* dispatch to different function according to http operations, GET for example*/
api_data_get(h, r, pcvec, pi, qvec, pretty, use_xml);
/* implement by this function*/
api_data_get2(h, r, pcvec, pi, qvec, pretty, use_xml, 0);
clicon_rpc_get(h, path, &xret);
clicon_rpc_msg(h, msg, &xret, NULL);
sock = clicon_sock(h); // sock = <CLICON_SOCK> in congfig file
/* Connect to server, send a clicon_msg message
and wait for result using unix socket*/
clicon_rpc_connect_unix(msg, sock, &retdata, sock0);
s = clicon_connect_unix(sockpath) // sockpath = prev sock
connect(s, (struct sockaddr *)&addr, SUN_LEN(&addr))
/* Send a clicon_msg message and wait for result.*/
clicon_rpc(s, msg, retdata)
clicon_msg_send(s, msg)
/*Ensure all of data on socket comes through.
fn is either read or write*/
atomicio((ssize_t (*)(int, void *, size_t))write,
s, msg, ntohl(msg->op_len))
clicon_msg_rcv(s, &reply, &eof)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 这两步从api_path转到xpath
api_path2xpath(yspec, pcvec, pi, cbpath)
path = cbuf_get(cbpath);
// get方法,输入xpath,构建rpc msg, 发送并接受(backend)
clicon_rpc_get(h, path, &xret);
if ((cb = cbuf_new()) == NULL)
goto done;
cprintf(cb, "<rpc");
if ((username = clicon_username_get(h)) != NULL)
cprintf(cb, " username=\"%s\"", username);
cprintf(cb, "><get>");
if (xpath && strlen(xpath))
cprintf(cb, "<filter type=\"xpath\" select=\"%s\"/>", xpath);
cprintf(cb, "</get></rpc>");
if ((msg = clicon_msg_encode("%s", cbuf_get(cb))) == NULL)
goto done;
if (clicon_rpc_msg(h, msg, &xret, NULL) < 0)
goto done;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 看一下post,post要上传数据好像还不太一样
api_data_post(h, r, api_path, pcvec, pi, qvec, data, pretty, use_xml, parse_xml);
/* Create config top-of-tree */
xtop = xml_new("config", NULL, NULL)
/* Translate api_path to xtop/xbot */
api_path2xml(api_path, yspec, xtop, YC_DATANODE, 1, &xbot, &ybot)
/* Parse input data as json or xml into xml */
...
... // 可能忽略了部分内容,暂时不看(包括上面一些也是随便贴的,可以不看)
cbx = cbuf_new()
/* For internal XML protocol: add username attribute for access control
*/
username = clicon_username_get(h);
cprintf(cbx, "<rpc username=\"%s\">", username?username:"");
cprintf(cbx, "<edit-config><target><candidate /></target>");
cprintf(cbx, "<default-operation>none</default-operation>");
if (clicon_xml2cbuf(cbx, xtop, 0, 0) < 0) // xtop
cprintf(cbx, "</edit-config></rpc>");
/* 关键语句,与get里面的clicon_rpc_get差不多的感觉*/
clicon_rpc_netconf(h, cbuf_get(cbx), &xret, NULL)
clicon_rpc_msg(h, msg, xret, sp)
// 见上
1
2
3
4
5
6
7
8
9
10
// 这个其实没什么用
// clixon/yang/clixon/clixon-cnfig@2019-06-05.yang
// 是fastcgi的socket
leaf CLICON_RESTCONF_PATH {
type string;
default "/www-data/fastcgi_restconf.sock";
description
"FastCGI unix socket. Should be specified in webserver
Eg in nginx: fastcgi_pass unix:/www-data/clicon_restconf.sock";
}

rpc 角度

  • 注册rpc_callback

  • callback调用(在event_loop

1562831058358

1562830981427

1562810441329

backend event reg & loop

Q

  • 猜想是event_dataEVENT_FD的条目是检测到相关的fd变化后会去执行该条目的回调函数,但是我还没有看懂event_loop过程.

  • event_loop是main函数最后进去的,里面包含一个while(1)

A

  • event_reg分为event_reg_fd&event_reg_timeout相应的,event_loop里面存在分开处理这两种机制的两部分。
    • 下面提到的都是event_reg_fd的方式
    • event_reg_timeout用于stream里面,暂时不考虑
1
2
3
4
5
6
7
8
9
/*
struct event_data : data structure
event_reg_fd : Register a callback function to be called on input on a file descriptor.
event_loop: event main loop
*/
extern int select (int __nfds, fd_set *__restrict __readfds,
fd_set *__restrict __writefds,
fd_set *__restrict __exceptfds,
struct timeval *__restrict __timeout);

1562813831598

1562812050268

1562813232585

1562813408598

callback 注册流程

  • 问题是,from_client是怎么被触发的,或者说,event_reg_fd+event_loop是怎么处理事件并且调用注册的cb的
    • event_loop写的注释是Dispatch file descriptor events (and timeouts) by invoking callbacks.,我觉得我们用到的不是timeout,那么就应该是fd。
    • sock连接是怎么被读取的,找sock的read?
  • answer:Linux: fd_set用法
    • 当调用select()时,由内核根据IO状态修改fd_set的内容,由此来通知执行了select()的进程哪一socket或文件可读。
    • select()函数常常用在用一个进程监听多个服务器端socket。
    • 后来看到,这种方式实现io监听是比较古老的做法,现在用poll以及epoll性能更好
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//首先main中调用
ss = backend_server_socket(h) // 包含两个操作
/* ss is a server socket that the clients connect to. The callback
therefore accepts clients on ss */
ss = backend_socket_init(clicon_handle h) // 连接<CLICON_SOCK>
config_socket_init_unix(h, sock); //sock = <CLICON_SOCK> 配置文件定义的
event_reg_fd(ss, backend_accept_client, h, "server socket")//注册backend_accept_client
backend_accept_client(int fd,void *arg) // 注意在这个函数里面又注册一次的理解
event_reg_fd(s, from_client, (void*)ce, "local netconf client socket")
/* An internal clicon message has arrived from a client. Receive and dispatch.*/
// 实际调用的时候应该主要是from_client 这个callback
from_client(int s, void* arg)
clicon_msg_rcv(ce->ce_s, &msg, &eof)
from_client_msg(h, ce, msg)
/* Search RPC callbacks and invoke if XML match with tag*/
ret = rpc_callback_call(h, xe, cbret, ce))
/* send cbret back*/
send_msg_reply(ce->ce_s, cbuf_get(cbret), cbuf_len(cbret)+1)
clicon_msg_send(s, reply)
atomicio((ssize_t (*)(int, void *, size_t))write,
s, msg, ntohl(msg->op_len)) < 0)

1562811464793

1562811349774

1562823944280

1562811383999

1562810539358

1562810474656

1562810441329

1562897454820

TODO:细看xmldb中的操作

  • 对xmldb的操作全部来自backend_rpc_init中注册的操作。

xml 数据结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
struct xml{
char *x_name; /* name of node */
char *x_prefix; /* namespace localname N, called prefix */
struct xml *x_up; /* parent node in hierarchy if any */
struct xml **x_childvec; /* vector of children nodes */
int x_childvec_len;/* Number of children */
int x_childvec_max;/* Length of allocated vector */
enum cxobj_type x_type; /* type of node: element, attribute, body */
char *x_value; /* attribute and body nodes have values */
int x_flags; /* Flags according to XML_FLAG_* */
yang_stmt *x_spec; /* Pointer to specification, eg yang, by
reference, dont free */
cg_var *x_cv; /* Cached value as cligen variable
(eg xml_cmp) */
char *x_ns_cache; /* Cached namespace */
int _x_vector_i; /* internal use: xml_child_each */
int _x_i; /* internal use for sorting:
see xml_enumerate and xml_cmp */
};
typedef struct xml cxobj; /* struct defined in clicon_xml.c */

xmldb相关操作

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
//xmldb操作通过rpc_callback来完成,相关的函数在clixon_datastore*中,以下是一个例子
int backend_rpc_init(clicon_handle h)
rpc_callback_register(h, from_client_get_config, NULL,
"urn:ietf:params:xml:ns:netconf:base:1.0", "get-config")
from_client_get_config(clicon_handle h, cxobj *xe,
cbuf *cbret, void *arg, void *regarg)
xmldb_get(h, db, xpath, &xret)
xmldb_get0(h, db, xpath, 1, xret, NULL);
/* Get content of database using xpath. return a
set of matching sub-trees
* The function returns a minimal tree that
includes all sub-trees that match
* xpath.
*/
d(h, db, xpath, xret, msd);
xmldb_readfile(h, db, yspec, &xt, msd) // 看下面详细的
/* Here xt looks like: <config>...</config> */
/* Given the xpath, return a vector of matches in xvec */
xpath_vec(xt, "%s", &xvec, &xlen, xpath?xpath:"/") // TODO
/* If vectors are specified then mark the nodes found
with all ancestors
* and filter out everything else,
* otherwise return complete tree.
*/
xml_apply(xt, CX_ELMNT, (xml_applyfn_t*)xml_flag_reset,
(void*)XML_FLAG_MARK)
1
2
3
4
5
6
7
/* Here xt looks like: <config>...</config> */
xmldb_readfile(h, db, yspec, &xt, msd)
xmldb_db2file(h, db, &dbfile) // 从db name转化到dbfile的完整路径
format = clicon_option_str(h, "CLICON_XMLDB_FORMAT")// 从配置文件(clixon.config)看是xml
/* Read an XML definition from file and parse it into a parse-tree. */
xml_parse_file(fd, "</config>", yspec, &x0) //TODO 细看 while循环里面还没看明白
/* Always assert a top-level called "config". Two cases, omit now*/
1
2
3
// edit 部分会不太一样,涉及lock之类的 TODO
from_client_edit_config()
xmldb_put(h, target, operation, xc, username, cbret)

curl -G http://127.0.0.1/restconf/data

xmldb 调用流程

  • 这次我们关注传入参数,涉及yang的部分
  • xt means xtop means Top of XML parse tree
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
// 回顾上面的调用的流程
// 实际调用的时候应该主要是from_client 这个callback
from_client(int s, void* arg) // 这个callback因为sock通信被触发
/* 从sock接收到信息在 &msg ce是一个handle一样的保存了连接的客户端信息:
Client entry. Keep state about every connected client.*/
clicon_msg_rcv(ce->ce_s, &msg, &eof)
/* 传入msg,TODO */
from_client_msg(h, ce, msg)
yspec = clicon_dbspec_yang(h); //Get YANG specification for application
clicon_msg_decode(msg, yspec, &xt); // 从msg解析到[out] **xml &xt
xml_parse_string(xmlstr, yspec, xml) // 从xmlstr解析到[out] xml
*xtop = xml_new(XML_TOP_SYMBOL, NULL, NULL) // 产生一个xtop
/* 从xmlstr 解析到 xml(上一条生成了xtop,实际作用语句是这条 */
_xml_parse(str, yspec, *xtop) // TODO 想知道yspec作用的话需要看这个




/* Search RPC callbacks and invoke if XML match with tag*/
ret = rpc_callback_call(h, xe, cbret, ce))
/* send cbret back*/
send_msg_reply(ce->ce_s, cbuf_get(cbret), cbuf_len(cbret)+1)
clicon_msg_send(s, reply)
atomicio((ssize_t (*)(int, void *, size_t))write,
s, msg, ntohl(msg->op_len)) < 0)

debug

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
106
107
108
109
110
111
sparta@vostrozhc:~$ tail -f /var/log/syslog | grep clixon_restconf
Jul 17 14:36:29 vostrozhc clixon_restconf[30315]: clixon_restconf: 30315 Started
Jul 17 14:36:29 vostrozhc clixon_restconf[30315]: CLICON_CONFIGFILE=/usr/local/etc/example.xml
Jul 17 14:36:29 vostrozhc clixon_restconf[30315]: yang_parse_filename /usr/local/share/clixon/clixon-config@2019-06-05.yang
Jul 17 14:36:29 vostrozhc clixon_restconf[30315]: clixon_plugins_load
Jul 17 14:36:29 vostrozhc clixon_restconf[30315]: DEBUG: Loading plugin '/usr/local/lib/example/restconf/example_restconf.so' ...
Jul 17 14:36:29 vostrozhc clixon_restconf[30315]: plugin_load_one file:/usr/local/lib/example/restconf/example_restconf.so function:clixon_plugin_init
Jul 17 14:36:29 vostrozhc clixon_restconf[30315]: clixon_plugin_init restconf
Jul 17 14:36:29 vostrozhc clixon_restconf[30315]: plugin_load_one
Jul 17 14:36:29 vostrozhc clixon_restconf[30315]: yang_parse_filename /usr/local/share/clixon/clixon-example@2019-01-13.yang
Jul 17 14:36:29 vostrozhc clixon_restconf[30315]: yang_parse_filename /usr/local/share/clixon/ietf-interfaces@2018-02-20.yang
Jul 17 14:36:29 vostrozhc clixon_restconf[30315]: yang_parse_filename /usr/local/share/clixon/ietf-yang-types@2013-07-15.yang
Jul 17 14:36:29 vostrozhc clixon_restconf[30315]: yang_parse_filename /usr/local/share/clixon/ietf-ip@2014-06-16.yang
Jul 17 14:36:29 vostrozhc clixon_restconf[30315]: yang_parse_filename /usr/local/share/clixon/ietf-inet-types@2013-07-15.yang
Jul 17 14:36:29 vostrozhc clixon_restconf[30315]: yang_parse_filename /usr/local/share/clixon/iana-if-type@2014-05-08.yang
Jul 17 14:36:29 vostrozhc clixon_restconf[30315]: yang_parse_filename /usr/local/share/clixon/clixon-lib@2019-06-05.yang
Jul 17 14:36:29 vostrozhc clixon_restconf[30315]: yang_parse_filename /usr/local/share/clixon/ietf-yang-library@2016-06-21.yang
Jul 17 14:36:29 vostrozhc clixon_restconf[30315]: yang_parse_filename /usr/local/share/clixon/ietf-netconf@2011-06-01.yang
Jul 17 14:36:29 vostrozhc clixon_restconf[30315]: yang_parse_filename /usr/local/share/clixon/clixon-rfc5277@2008-07-01.yang
Jul 17 14:36:29 vostrozhc clixon_restconf[30315]: CLICON_CLI_DIR =#011 "/usr/local/lib/example/cli"
Jul 17 14:36:29 vostrozhc clixon_restconf[30315]: CLICON_MODULE_LIBRARY_RFC7895 =#011 "true"
Jul 17 14:36:29 vostrozhc clixon_restconf[30315]: CLICON_CLI_MODE =#011 "example"
Jul 17 14:36:29 vostrozhc clixon_restconf[30315]: CLICON_CLI_UTF8 =#011 "0"
Jul 17 14:36:29 vostrozhc clixon_restconf[30315]: CLICON_NACM_MODE =#011 "disabled"
Jul 17 14:36:29 vostrozhc clixon_restconf[30315]: CLICON_XMLDB_DIR =#011 "/usr/local/var/example"
Jul 17 14:36:29 vostrozhc clixon_restconf[30315]: CLICON_SOCK_PORT =#011 "4535"
Jul 17 14:36:29 vostrozhc clixon_restconf[30315]: CLICON_CLI_GENMODEL_COMPLETION =#011 "1"
Jul 17 14:36:29 vostrozhc clixon_restconf[30315]: CLICON_CONFIGFILE =#011 "/usr/local/etc/example.xml"
Jul 17 14:36:29 vostrozhc clixon_restconf[30315]: CLICON_STREAM_DISCOVERY_RFC8040 =#011 "false"
Jul 17 14:36:29 vostrozhc clixon_restconf[30315]: CLICON_STREAM_DISCOVERY_RFC5277 =#011 "false"
Jul 17 14:36:29 vostrozhc clixon_restconf[30315]: CLICON_AUTOCOMMIT =#011 "0"
Jul 17 14:36:29 vostrozhc clixon_restconf[30315]: CLICON_SOCK_GROUP =#011 "clicon"
Jul 17 14:36:29 vostrozhc clixon_restconf[30315]: CLICON_STREAM_URL =#011 "https://localhost"
Jul 17 14:36:29 vostrozhc clixon_restconf[30315]: CLICON_BACKEND_DIR =#011 "/usr/local/lib/example/backend"
Jul 17 14:36:29 vostrozhc clixon_restconf[30315]: CLICON_CLISPEC_DIR =#011 "/usr/local/lib/example/clispec"
Jul 17 14:36:29 vostrozhc clixon_restconf[30315]: CLICON_NETCONF_DIR =#011 "/usr/local/lib/example/netconf"
Jul 17 14:36:29 vostrozhc clixon_restconf[30315]: CLICON_SOCK_FAMILY =#011 "UNIX"
Jul 17 14:36:29 vostrozhc clixon_restconf[30315]: CLICON_STREAM_PATH =#011 "streams"
Jul 17 14:36:29 vostrozhc clixon_restconf[30315]: CLICON_YANG_REGEXP =#011 "posix"
Jul 17 14:36:29 vostrozhc clixon_restconf[30315]: CLICON_CLI_VARONLY =#011 "1"
Jul 17 14:36:29 vostrozhc clixon_restconf[30315]: CLICON_CLI_GENMODEL =#011 "1"
Jul 17 14:36:29 vostrozhc clixon_restconf[30315]: CLICON_CLI_TAB_MODE =#011 "0"
Jul 17 14:36:29 vostrozhc clixon_restconf[30315]: CLICON_XMLDB_FORMAT =#011 "xml"
Jul 17 14:36:29 vostrozhc clixon_restconf[30315]: CLICON_RESTCONF_DIR =#011 "/usr/local/lib/example/restconf"
Jul 17 14:36:29 vostrozhc clixon_restconf[30315]: CLICON_STARTUP_MODE =#011 "init"
Jul 17 14:36:29 vostrozhc clixon_restconf[30315]: CLICON_XMLDB_PRETTY =#011 "true"
Jul 17 14:36:29 vostrozhc clixon_restconf[30315]: CLICON_XML_CHANGELOG =#011 "false"
Jul 17 14:36:29 vostrozhc clixon_restconf[30315]: CLICON_CLI_HIST_FILE =#011 "~/.clixon_cli_history"
Jul 17 14:36:29 vostrozhc clixon_restconf[30315]: CLICON_RESTCONF_PATH =#011 "/www-data/fastcgi_restconf.sock"
Jul 17 14:36:29 vostrozhc clixon_restconf[30315]: CLICON_MODULE_SET_ID =#011 "0"
Jul 17 14:36:29 vostrozhc clixon_restconf[30315]: CLICON_CLI_HIST_SIZE =#011 "300"
Jul 17 14:36:29 vostrozhc clixon_restconf[30315]: CLICON_BACKEND_REGEXP =#011 "(.so)$"
Jul 17 14:36:29 vostrozhc clixon_restconf[30315]: CLICON_XMLDB_MODSTATE =#011 "false"
Jul 17 14:36:29 vostrozhc clixon_restconf[30315]: CLICON_BACKEND_PIDFILE =#011 "/usr/local/var/example/example.pidfile"
Jul 17 14:36:29 vostrozhc clixon_restconf[30315]: CLICON_DATASTORE_CACHE =#011 "cache"
Jul 17 14:36:29 vostrozhc clixon_restconf[30315]: CLICON_TRANSACTION_MOD =#011 "false"
Jul 17 14:36:29 vostrozhc clixon_restconf[30315]: CLICON_RESTCONF_PRETTY =#011 "true"
Jul 17 14:36:29 vostrozhc clixon_restconf[30315]: CLICON_YANG_MODULE_MAIN =#011 "clixon-example"
Jul 17 14:36:29 vostrozhc clixon_restconf[30315]: CLICON_STREAM_RETENTION =#011 "3600"
Jul 17 14:36:29 vostrozhc clixon_restconf[30315]: CLICON_CLI_LINESCROLLING =#011 "0"
Jul 17 14:36:29 vostrozhc clixon_restconf[30315]: CLICON_CLI_GENMODEL_TYPE =#011 "VARS"
Jul 17 14:36:29 vostrozhc clixon_restconf[30315]: CLICON_SOCK =#011 "/usr/local/var/example/example.sock"
Jul 17 14:36:29 vostrozhc clixon_restconf[30315]: CLICON_CLI_MODEL_TREENAME =#011 "datamodel"
Jul 17 14:36:29 vostrozhc clixon_restconf[30315]: CLICON_YANG_DIR =#011 "/usr/local/share/clixon"
Jul 17 14:36:29 vostrozhc clixon_restconf[30315]: CLICON_FEATURE =#011 "ietf-netconf:candidate"
Jul 17 14:36:29 vostrozhc clixon_restconf[30315]: CLICON_FEATURE =#011 "ietf-netconf:startup"
Jul 17 14:36:29 vostrozhc clixon_restconf[30315]: CLICON_FEATURE =#011 "ietf-netconf:validate"
Jul 17 14:36:29 vostrozhc clixon_restconf[30315]: CLICON_FEATURE =#011 "ietf-netconf:xpath"
Jul 17 14:36:29 vostrozhc clixon_restconf[30315]: example_restconf_start
Jul 17 14:36:29 vostrozhc clixon_restconf[30315]: restconf_main: Opening FCGX socket: /www-data/fastcgi_restconf.sock
Jul 17 14:37:31 vostrozhc clixon_restconf[30315]: ------------
Jul 17 14:37:31 vostrozhc clixon_restconf[30315]: path: /restconf/data
Jul 17 14:37:31 vostrozhc clixon_restconf[30315]: api_restconf
Jul 17 14:37:31 vostrozhc clixon_restconf[30315]: QUERY_STRING = ''
Jul 17 14:37:31 vostrozhc clixon_restconf[30315]: REQUEST_METHOD = 'GET'
Jul 17 14:37:31 vostrozhc clixon_restconf[30315]: CONTENT_TYPE = ''
Jul 17 14:37:31 vostrozhc clixon_restconf[30315]: CONTENT_LENGTH = ''
Jul 17 14:37:31 vostrozhc clixon_restconf[30315]: SCRIPT_FILENAME = ''
Jul 17 14:37:31 vostrozhc clixon_restconf[30315]: SCRIPT_NAME = '/restconf/data'
Jul 17 14:37:31 vostrozhc clixon_restconf[30315]: REQUEST_URI = '/restconf/data'
Jul 17 14:37:31 vostrozhc clixon_restconf[30315]: DOCUMENT_URI = '/restconf/data'
Jul 17 14:37:31 vostrozhc clixon_restconf[30315]: DOCUMENT_ROOT = '/var/www/html'
Jul 17 14:37:31 vostrozhc clixon_restconf[30315]: SERVER_PROTOCOL = 'HTTP/1.1'
Jul 17 14:37:31 vostrozhc clixon_restconf[30315]: GATEWAY_INTERFACE = 'CGI/1.1'
Jul 17 14:37:31 vostrozhc clixon_restconf[30315]: SERVER_SOFTWARE = 'nginx/1.10.3'
Jul 17 14:37:31 vostrozhc clixon_restconf[30315]: REMOTE_ADDR = '127.0.0.1'
Jul 17 14:37:31 vostrozhc clixon_restconf[30315]: REMOTE_PORT = '37466'
Jul 17 14:37:31 vostrozhc clixon_restconf[30315]: SERVER_ADDR = '127.0.0.1'
Jul 17 14:37:31 vostrozhc clixon_restconf[30315]: SERVER_PORT = '80'
Jul 17 14:37:31 vostrozhc clixon_restconf[30315]: SERVER_NAME = '_'
Jul 17 14:37:31 vostrozhc clixon_restconf[30315]: HTTP_COOKIE = ''
Jul 17 14:37:31 vostrozhc clixon_restconf[30315]: HTTPS = ''
Jul 17 14:37:31 vostrozhc clixon_restconf[30315]: HTTP_ACCEPT = '*/*'
Jul 17 14:37:31 vostrozhc clixon_restconf[30315]: HTTP_CONTENT_TYPE = ''
Jul 17 14:37:31 vostrozhc clixon_restconf[30315]: HTTP_AUTHORIZATION = ''
Jul 17 14:37:31 vostrozhc clixon_restconf[30315]: api_restconf: method=data
Jul 17 14:37:31 vostrozhc clixon_restconf[30315]: api_restconf DATA=
Jul 17 14:37:31 vostrozhc clixon_restconf[30315]: example_restconf_credentials retval:1
Jul 17 14:37:31 vostrozhc clixon_restconf[30315]: api_restconf auth:1 (null)
Jul 17 14:37:31 vostrozhc clixon_restconf[30315]: api_restconf auth2:1 none
Jul 17 14:37:31 vostrozhc clixon_restconf[30315]: api_data
Jul 17 14:37:31 vostrozhc clixon_restconf[30315]: api_data method:GET
Jul 17 14:37:31 vostrozhc clixon_restconf[30315]: api_data_get2
Jul 17 14:37:31 vostrozhc clixon_restconf[30315]: api_data_get2 path:/
Jul 17 14:37:31 vostrozhc clixon_restconf[30315]: clicon_rpc_msg request:<rpc username="none"><get><filter type="xpath" select="/"/></get></rpc>
Jul 17 14:37:31 vostrozhc clixon_restconf[30315]: Send msg on /usr/local/var/example/example.sock
Jul 17 14:37:31 vostrozhc clixon_restconf[30315]: clicon_rpc_msg retdata:<rpc-reply><data><modules-state xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-library"><module-set-id>0</module-set-id><module><name>clixon-example</name><revision>2019-01-13</revision><namespace>urn:example:clixon</namespace><conformance-type>implement</conformance-type></module><module><name>ietf-interfaces</name><revision>2018-02-20</revision><namespace>urn:ietf:params:xml:ns:yang:ietf-interfaces</namespace><conformance-type>implement</conformance-type></module><module><name>ietf-yang-types</name><revision>2013-07-15</revision><namespace>urn:ietf:params:xml:ns:yang:ietf-yang-types</namespace><conformance-type>implement</conformance-type></module><module><name>ietf-ip</name><revision>2014-06-16</revision><namespace>urn:ietf:params:xml:ns:yang:ietf-ip</namespace><conformance-type>implement</conformance-type></module><module><name>ietf-inet-types</name><revision>2013-07-15</revision><namespace>urn:ietf:params:xml:ns:yang:ietf-inet-types</namespace><conformance-type>implement</conformance-type></module><module><name>iana-if-type</name><revision>2014-05-08</revision><namespace>urn:ietf:params:xml:ns:yang:iana-if-type</namespace><conformance-type>implement</conformance-type></module><module><name>clixon-lib</name><revision>2019-06-05</revision><namespace>http://clicon.org/lib</namespace><conformance-type>implement</conformance-type></module><module><name>ietf-yang-library</name><revision>2016-06-21</revision><namespace>urn:ietf:params:xml:ns:yang:ietf-yang-library</namespace><conformance-type>implement</conformance-type></module><module><name>ietf-netconf</name><revision>2011-06-01</revision><namespace>urn:ietf:params:xml:ns:netconf:base:1.0</namespace><feature>candidate</feature><feature>validate</feature><feature>startup</feature><feature>xpath</feature><conformance-type>implement</conformance-type></module><module><name>clixon-rfc5277</name><revision>2008-07-01</revision><namespace>urn:ietf:params:xml:ns:netmod:notification</namespace><conformance-type>implement</conformance-type></module></modules-state></data></rpc-reply>
Jul 17 14:37:31 vostrozhc clixon_restconf[30315]: api_data_get2 cbuf:{#012 "data": {#012 "ietf-yang-library:modules-state": {#012 "module-set-id": "0",#012 "module": [#012 {#012 "name": "clixon-example",#012 "revision": "2019-01-13",#012 "namespace": "urn:example:clixon",#012 "conformance-type": "implement"#012 },#012 { #012 "name": "ietf-interfaces",#012 "revision": "2018-02-20",#012 "namespace": "urn:ietf:params:xml:ns:yang:ietf-interfaces",#012 "conformance-type": "implement"#012 },#012 { #012 "name": "ietf-yang-types",#012 "revision": "2013-07-15",#012 "namespace": "urn:ietf:params:xml:ns:yang:ietf-yang-types",#012 "conformance-type": "implement"#012 },#012 { #012 "name": "ietf-ip",#012 "revision": "2014-06-16",#012 "namespace": "urn:ietf:params:xml:ns:yang:ietf-ip",#012 "conformance-type": "implement"#012 },#012 { #012 "name": "ietf-inet-types",#012 "revision": "2013-07-15",#012 "namespace": "urn:ietf:params:xml:ns:yang:ietf-inet-types",#012 "conformance-type": "implement"#012 },#012 { #012 "name": "iana-if-type",#012 "revision": "2014-05-08",#012 "namespace": "urn:ietf:params:xml:ns:yang:iana-if-type",#012 "conformance-type": "implement"#012 },#012 { #012 "name": "clixon-lib",#012 "revision": "2019-06-05",#012 "namespace": "http://clicon.org/lib",#012 "conformance-type": "implement"#012 },#012 { #012 "name": "ietf-yang-library",#012 "revision": "2016-06-21",#012 "namespace": "urn:ietf:params:xml:ns:yang:ietf-yang-library",#012 "conformance-type": "implement"#012 },#012 { #012 "name": "ietf-netconf",#012 "revision": "2011-06-01",#012 "namespace": "urn:ietf:params:xml:ns:netconf:base:1.0",#012 "feature": [#012 "candidate",#012 "validate",#012 "startup",#012 "xpath"#012 ],#012 "conformance-type": "implement"#012 },#012 { #012 "name": "clixon-rfc5277",#012 "revision": "2008-07-01",#012 "namespace": "urn:ietf:params:xml:ns:netmod:notification",#012 "conformance-type": "implement"#012 }#012 ]#012 }#012 }#012}
Jul 17 14:37:31 vostrozhc clixon_restconf[30315]: api_data_get2 retval:0
Jul 17 14:37:31 vostrozhc clixon_restconf[30315]: api_data retval:0
Jul 17 14:37:31 vostrozhc clixon_restconf[30315]: api_restconf retval:0

长url

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
Aug  9 14:44:07 vostrozhc clixon_restconf[21683]: ------------
Aug 9 14:44:07 vostrozhc clixon_restconf[21683]: path: /restconf/data/ietf-yang-library:modules-state/module=ietf-netconf,2011-06-21
Aug 9 14:44:07 vostrozhc clixon_restconf[21683]: api_restconf
Aug 9 14:44:07 vostrozhc clixon_restconf[21683]: QUERY_STRING = ''
Aug 9 14:44:07 vostrozhc clixon_restconf[21683]: REQUEST_METHOD = 'GET'
Aug 9 14:44:07 vostrozhc clixon_restconf[21683]: CONTENT_TYPE = ''
Aug 9 14:44:07 vostrozhc clixon_restconf[21683]: CONTENT_LENGTH = ''
Aug 9 14:44:07 vostrozhc clixon_restconf[21683]: SCRIPT_FILENAME = ''
Aug 9 14:44:07 vostrozhc clixon_restconf[21683]: SCRIPT_NAME = '/restconf/data/ietf-yang-library:modules-state/module=ietf-netconf,2011-06-21'
Aug 9 14:44:07 vostrozhc clixon_restconf[21683]: REQUEST_URI = '/restconf/data/ietf-yang-library:modules-state/module=ietf-netconf,2011-06-21'
Aug 9 14:44:07 vostrozhc clixon_restconf[21683]: DOCUMENT_URI = '/restconf/data/ietf-yang-library:modules-state/module=ietf-netconf,2011-06-21'
Aug 9 14:44:07 vostrozhc clixon_restconf[21683]: DOCUMENT_ROOT = '/var/www/html'
Aug 9 14:44:07 vostrozhc clixon_restconf[21683]: SERVER_PROTOCOL = 'HTTP/1.1'
Aug 9 14:44:07 vostrozhc clixon_restconf[21683]: GATEWAY_INTERFACE = 'CGI/1.1'
Aug 9 14:44:07 vostrozhc clixon_restconf[21683]: SERVER_SOFTWARE = 'nginx/1.10.3'
Aug 9 14:44:07 vostrozhc clixon_restconf[21683]: REMOTE_ADDR = '127.0.0.1'
Aug 9 14:44:07 vostrozhc clixon_restconf[21683]: REMOTE_PORT = '56262'
Aug 9 14:44:07 vostrozhc clixon_restconf[21683]: SERVER_ADDR = '127.0.0.1'
Aug 9 14:44:07 vostrozhc clixon_restconf[21683]: SERVER_PORT = '80'
Aug 9 14:44:07 vostrozhc clixon_restconf[21683]: SERVER_NAME = '_'
Aug 9 14:44:07 vostrozhc clixon_restconf[21683]: HTTP_COOKIE = ''
Aug 9 14:44:07 vostrozhc clixon_restconf[21683]: HTTPS = ''
Aug 9 14:44:07 vostrozhc clixon_restconf[21683]: HTTP_ACCEPT = '*/*'
Aug 9 14:44:07 vostrozhc clixon_restconf[21683]: HTTP_CONTENT_TYPE = ''
Aug 9 14:44:07 vostrozhc clixon_restconf[21683]: HTTP_AUTHORIZATION = ''
Aug 9 14:44:07 vostrozhc clixon_restconf[21683]: api_restconf: method=data
Aug 9 14:44:07 vostrozhc clixon_restconf[21683]: api_restconf DATA=
Aug 9 14:44:07 vostrozhc clixon_restconf[21683]: example_restconf_credentials retval:1
Aug 9 14:44:07 vostrozhc clixon_restconf[21683]: api_restconf auth:1 none
Aug 9 14:44:07 vostrozhc clixon_restconf[21683]: api_restconf auth2:1 none
Aug 9 14:44:07 vostrozhc clixon_restconf[21683]: api_data
Aug 9 14:44:07 vostrozhc clixon_restconf[21683]: api_data method:GET
Aug 9 14:44:07 vostrozhc clixon_restconf[21683]: api_data_get2
Aug 9 14:44:07 vostrozhc clixon_restconf[21683]: api_path2xpath [2] cvname:modules-state
Aug 9 14:44:07 vostrozhc clixon_restconf[21683]: api_path2xpath [3] cvname:module
Aug 9 14:44:07 vostrozhc clixon_restconf[21683]: api_data_get2 path:/modules-state/module[name='ietf-netconf'][revision='2011-06-21']
Aug 9 14:44:07 vostrozhc clixon_restconf[21683]: clicon_rpc_msg request:<rpc username="none"><get><filter type="xpath" select="/modules-state/module[name='ietf-netconf'][revision='2011-06-21']"/></get></rpc>

authentication

Is BASIC-Auth secure if done over HTTPS?

  • at least we should basic -auth over https

A sequence diagram illustrating HTTP messages between a client and a server lifeline.

  • work as a plugin
  • clixon/examplemain/example_restconf.c是plugin定义的文件
1
2
3
4
5
6
7
8
9
10
11
12
13
// restconf_main.c/main plugin加载过程
if (clixon_plugins_load(h, CLIXON_PLUGIN_INIT, dir, NULL) < 0)
if (clixon_plugin_start(h) < 0)
// api是这句定义的
clixon_plugin_api * clixon_plugin_init(clicon_handle h);

static clixon_plugin_api api = {
"example", /* name */
clixon_plugin_init, /* init */
example_restconf_start,/* start */
NULL, /* exit */
.ca_auth=example_restconf_credentials /* auth */
};
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
// restconf_main.c/api_restconf
/* If present, check credentials. See "plugin_credentials" in plugin
* See RFC 8040 section 2.5
*/
// 在这个函数里面调用cp_api.ca_auth, 相当于每次处理请求都会检查一次认证
if ((authenticated = clixon_plugin_auth(h, r)) < 0)
goto done;
clicon_debug(1, "%s auth:%d %s", __FUNCTION__, authenticated, clicon_username_get(h));

/* If set but no user, we set a dummy user */
if (authenticated){
if (clicon_username_get(h) == NULL)
clicon_username_set(h, "none");
}
else{
if (netconf_access_denied_xml(&xret, "protocol", "The requested URL was unauthorized") < 0)
goto done;
if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){
if (api_return_err(h, r, xerr, pretty, use_xml, 0) < 0)
goto done;
goto ok;
}
goto ok;
}
clicon_debug(1, "%s auth2:%d %s", __FUNCTION__, authenticated, clicon_username_get(h));
1
2
3
4
5
6
authenticated = clixon_plugin_auth(h, r))
cp = &_clixon_plugins[i];
authfn = cp->cp_api.ca_auth;
/* authfn 是 example_restconf.c中的example_restconf_credentials
这个函数里面默认的basic_auth=0, 要修改再编译 或者--*/
retval = authfn(h, arg);
1
2
3
4
5
6
7
8
9
10
11
12
// 打开了auth机制 basic_auth = 1 后
// 通过启动命令加参数打开sudo su -c "/www-data/clixon_restconf -D 1 -f /usr/local/etc/example.xml -- -a" -s /bin/sh www-data
sparta@vostrozhc:~$ curl -G http://127.0.0.1/restconf/data
{
"ietf-restconf:errors" : {
"error": {
"error-type": "protocol",
"error-tag": "access-denied",
"error-severity": "error",
"error-message": "The requested URL was unauthorized"
}
}

现在还剩下的问题是,我如何写http header 去通过认证

  • three hardwired users andy, wilma and guest from RFC8341 A.1 *密码是 *bar**

  • 这个认证机制似乎是http的一个定义好的,可以在postman选这个认证方法

  • 1563412333055

clixon-sysrepo

  • 尝试加入sysrepo,失败,问题应该是编译的时候没有加上sysrepo的动态连接库
1
2
3
4
5
6
7
gcc  backend_main.o backend_socket.o backend_client.o backend_commit.o backend_plugin.o backend_startup.o -L. libclixon_backend.so.3.10 -L../../lib/src -ldl -lnsl -lcrypt -lfcgi -lcligen -lm  ../../lib/src/libclixon.so.3.10 -lpthread -o clixon_backend
backend_main.o:在函数‘main’中:
backend_main.c:(.text.startup+0x7ac):对‘sr_connect’未定义的引用
backend_main.c:(.text.startup+0x7b7):对‘sr_strerror’未定义的引用
backend_main.c:(.text.startup+0x829):对‘sr_session_start’未定义的引用
backend_main.c:(.text.startup+0x834):对‘sr_strerror’未定义的引用
collect2: error: ld returned 1 exit status

stream notification

  • 需要在/usr/local/etc/example.xml里面打开设置=#011 “true”

  • 这样才会加载ietf-restconf-monitoring.yang

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
# 查询streams信息
GET /restconf/data/ietf-restconf-monitoring:restconf-state/\
streams HTTP/1.1
Host: example.com
Accept: application/yang-data+xml

{
"ietf-restconf-monitoring:restconf-state": {
"streams": {
"stream": [
{
"name": "EXAMPLE",
"description": "Example event stream",
"replay-support": true,
"access": [
{
"encoding": "xml",
"location": "https://localhost/streams/EXAMPLE"
}
]
}
]
}
}
}
  • 通过查询到的location 订阅 –但是我失败了 404
1
2
3
4
5
6
7
8
curl -H "Accept: text/event-stream" -s -X GET http://localhost/streams/EXAMPLE

GET /streams/NETCONF HTTP/1.1
Host: example.com
Accept: text/event-stream
Cache-Control: no-cache
Connection: keep-alive
Accept-Encoding: gzip, deflate
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// clixon的这个部分是定义在clixon-example.yang里面 对应RFC8040 6.4
/* Example notification as used in RFC 5277 and RFC 8040 */
notification event {
description "Example notification event.";
leaf event-class {
type string;
description "Event class identifier.";
}
container reportingEntity {
description "Event specific information.";
leaf card {
type string;
description "Line card identifier.";
}
}
leaf severity {
type string;
description "Event severity description.";
}
}

path2xpath

  • restconf要求的url path形式
1
2
https://<ADDRESS>/<ROOT>/data/<[YANG MODULE:]CONTAINER>/<LEAF=KEY>[?<OPTIONS>]
https://<ADDRESS>/restconf/data/ietf-interfaces:interfaces/interface=GigabitEthernet1?depth=unbounded
  • netconf使用的xpath
1
2
3
4
5
6
7
8
/MODULE:CONTAINER/LEAF[KEY=key]
get --filter-xpath "/building:rooms/room[room-number=1] # 如果key是int
get --filter-xpath "/building:rooms/room[room-name='rooma'] # 如果key是string
curl -G localhost/restconf/data/building:rooms/room=rooma

# 多个key的写法,这样似乎必须每次都写完整每个key,而且必须保证顺序一样 这个似乎是协议规定的rfc-8040p27
curl -sS -X GET http://localhost/restconf/data/ietf-yang-library:modules-state/module=ietf-netconf,2011-06-21
转换成xpath /ietf-yang-library:modules-state/modules-state/module[name='ietf-netconf'][revision='2011-06-21']
  • 需要一个转换从path到xpath

  • clixon的案例:

    • str2cvec(path, '/', '=', &pcvec) < 0
    • ret = api_path2xpath(yspec, pcvec, pi, cbpath)
    • clixon第一步先把path用两个标识符打断做成了他的cvec,我也可以用类似的结构体
    • 第二步用得到的cvec加上一个offset,配合yang制作成xpath,其实主要是room[room-number=1]这里,需要leaf节点的key值
    • 问题是,用来确定leaf的key值不是就一个吗,我用序号不行吗,否则还要问sysrepo拿leaf,很麻烦
      • 不行,只有key为数字的时候可以这么干,如果key是string的话就不可以,所以必须问sysrepo拿
  • 所以现在又回到了yang的问题,yang要怎么写,对应怎么样的xpath定位?

    • leaf-list的问题?

    • 问题:如果没有key呢? –还不知道

    • 一个list有多个leaf作为key呢?

      • 好像每次用一个key检索没问题
    • 问题:restconf不知道它用的是哪一个key,需要找一下匹配?

      • A: 如果有多个key,协议规定必须每个key都指定,
    • 一些测试与说明:

      • 我修改了building.yang,设定了两个key。
      • 然后我edit-config了两个对象,room-number一样,room-name不一样
      • 搜索一样的key会出两个
    • 可以通过key的逻辑and or来继续限制 – 不这样做

      • 问题:rfc7950 yang协议有没有限制对象的key的唯一性? A :要确定的,所有key一起确定同一个对象
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
> get --filter-xpath "/building:rooms/room[room-number='3']"
DATA
<rooms xmlns="urn:building:test">
<room>
<room-number>3</room-number>
<room-name>roomc</room-name>
<size>200</size>
</room>
<room>
<room-number>3</room-number>
<room-name>roomd</room-name>
<size>100</size>
</room>
</rooms>

> get --filter-xpath "/building:rooms/room[room-number='3' and room-name="roomc"]"
DATA
<rooms xmlns="urn:building:test">
<room>
<room-number>3</room-number>
<room-name>roomc</room-name>
<size>200</size>
</room>
</rooms>

> get --filter-xpath "/building:rooms/room[room-number='3' or room-name="roomc"]"
DATA
<rooms xmlns="urn:building:test">
<room>
<room-number>3</room-number>
<room-name>roomc</room-name>
<size>200</size>
</room>
<room>
<room-number>3</room-number>
<room-name>roomd</room-name>
<size>100</size>
</room>
</rooms>

Etag

  • etag header 似乎是通过配置nginx来实现的。是http cache机制。
  • 但是我改了配置文件也出不来,有可能是和我用的方式也有关系,cache使用环境好像一般是静态网页的加载。
1
2
3
4
5
6
7
8
9
configure arguments: --with-cc-opt='-g -O2 -fPIE -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2' --with-ld-opt='-Wl,-Bsymbolic-functions -fPIE -pie -Wl,-z,relro -Wl,-z,now' --prefix=/usr/share/nginx --conf-path=/etc/nginx/nginx.conf --http-log-path=/var/log/nginx/access.log --error-log-path=/var/log/nginx/error.log --lock-path=/var/lock/nginx.lock --pid-path=/run/nginx.pid --http-client-body-temp-path=/var/lib/nginx/body --http-fastcgi-temp-path=/var/lib/nginx/fastcgi --http-proxy-temp-path=/var/lib/nginx/proxy --http-scgi-temp-path=/var/lib/nginx/scgi --http-uwsgi-temp-path=/var/lib/nginx/uwsgi --with-debug --with-pcre-jit --with-ipv6 --with-http_ssl_module --with-http_stub_status_module --with-http_realip_module --with-http_auth_request_module --with-http_addition_module --with-http_dav_module --with-http_geoip_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_image_filter_module --with-http_v2_module --with-http_sub_module --with-http_xslt_module --with-stream --with-stream_ssl_module --with-mail --with-mail_ssl_module --with-threads

sparta@vostrozhc:/etc/nginx/sites-available$ sudo apt install nginx-extras

sparta@vostrozhc:/etc/nginx/sites-available$ sudo nginx -V
nginx version: nginx/1.10.3 (Ubuntu)
built with OpenSSL 1.0.2g 1 Mar 2016
TLS SNI support enabled
configure arguments: --with-cc-opt='-g -O2 -fPIE -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2' --with-ld-opt='-Wl,-Bsymbolic-functions -fPIE -pie -Wl,-z,relro -Wl,-z,now' --prefix=/usr/share/nginx --conf-path=/etc/nginx/nginx.conf --http-log-path=/var/log/nginx/access.log --error-log-path=/var/log/nginx/error.log --lock-path=/var/lock/nginx.lock --pid-path=/run/nginx.pid --http-client-body-temp-path=/var/lib/nginx/body --http-fastcgi-temp-path=/var/lib/nginx/fastcgi --http-proxy-temp-path=/var/lib/nginx/proxy --http-scgi-temp-path=/var/lib/nginx/scgi --http-uwsgi-temp-path=/var/lib/nginx/uwsgi --with-debug --with-pcre-jit --with-ipv6 --with-http_ssl_module --with-http_stub_status_module --with-http_realip_module --with-http_auth_request_module --with-http_addition_module --with-http_dav_module --with-http_flv_module --with-http_geoip_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_image_filter_module --with-http_mp4_module --with-http_perl_module --with-http_random_index_module --with-http_secure_link_module --with-http_v2_module --with-http_sub_module --with-http_xslt_module --with-mail --with-mail_ssl_module --with-stream --with-stream_ssl_module --with-threads --add-module=/build/nginx-Bl0K4k/nginx-1.10.3/debian/modules/headers-more-nginx-module --add-module=/build/nginx-Bl0K4k/nginx-1.10.3/debian/modules/nginx-auth-pam --add-module=/build/nginx-Bl0K4k/nginx-1.10.3/debian/modules/nginx-cache-purge --add-module=/build/nginx-Bl0K4k/nginx-1.10.3/debian/modules/nginx-dav-ext-module --add-module=/build/nginx-Bl0K4k/nginx-1.10.3/debian/modules/nginx-development-kit --add-module=/build/nginx-Bl0K4k/nginx-1.10.3/debian/modules/nginx-echo --add-module=/build/nginx-Bl0K4k/nginx-1.10.3/debian/modules/ngx-fancyindex --add-module=/build/nginx-Bl0K4k/nginx-1.10.3/debian/modules/nginx-http-push --add-module=/build/nginx-Bl0K4k/nginx-1.10.3/debian/modules/nginx-lua --add-module=/build/nginx-Bl0K4k/nginx-1.10.3/debian/modules/nginx-upload-progress --add-module=/build/nginx-Bl0K4k/nginx-1.10.3/debian/modules/nginx-upstream-fair --add-module=/build/nginx-Bl0K4k/nginx-1.10.3/debian/modules/ngx_http_substitutions_filter_module

欢迎关注我的其它发布渠道