netopeer2开发(一)主要介绍了sysrepo初始化时如何读取设备端口信息,并将端口信息写入sysrepo数据库。本节主要介绍当操作sysrepo数据库时,sysrepo如何将配置信息写入设备,以达到对设备的管理目的。
由于两种对sysrepo数据库访问方式差不多,本节仅以独立进程方式展开介绍。
独立进程方式
主要代码实现
在netopeer2开发(一)基础上,继续进行项目开发。首先在sr_interfaces.c文件主函数中添加事件监听线程,监听模块数据变化,如下所示。
int main(int argc, char **argv)
{
sr_conn_ctx_t *connection = NULL;
sr_session_ctx_t *session = NULL;
int rc = SR_ERR_OK;
const char *xpath, *mod_name;
unsigned int dev_count;
char** devices, *msg = NULL;
int i;
/* turn logging on */
sr_log_stderr(SR_LL_WRN);
/* connect to sysrepo */
rc = sr_connect(0, &connection);
if (rc != SR_ERR_OK) {
goto cleanup;
}
/* start session */
rc = sr_session_start(connection, SR_DS_RUNNING, &session);
if (rc != SR_ERR_OK) {
goto cleanup;
}
/* 设备初始化 */
devices = iface_get_ifcs(1, &dev_count, &msg);
if (devices == NULL) {
goto cleanup;
}
for(i = 0; i < dev_count; i++){
rc = parse_iface_config(session, devices[i], &msg);
free(devices[i]);
if(msg != NULL){
free(msg);
msg = NULL;
}
}
free(devices);
/* apply the change */
rc = sr_apply_changes(session, 0, 0);
if (rc != SR_ERR_OK) {
return -1;
}
printf("sr_apply_changes end\n");
/*
* 添加事件监听线程,监听模块ietf-interfaces的数据变化,通过回调函数interfaces_if_interface_cb对变化数据进行处理
* sr_module_change_subscribe是sysrepo提供的API
*/
mod_name = "ietf-interfaces";
xpath = "/ietf-interfaces:interfaces/interface";
rc = sr_module_change_subscribe(session, mod_name, xpath, interfaces_if_interface_cb, NULL, 0, 0, &subscription);
if (rc != SR_ERR_OK) {
goto cleanup;
}
/* loop until ctrl-c is pressed / SIGINT is received */
signal(SIGINT, sigint_handler);
signal(SIGPIPE, SIG_IGN);
while (!exit_application) {
sleep(1000);
}
cleanup:
sr_disconnect(connection);
return rc ? EXIT_FAILURE : EXIT_SUCCESS;
}
interfaces_if_interface_cb函数定义如下,此函数只是举例说明回调函数的使用方法,并不一定完全正确,只作为参考。
static int
interfaces_if_interface_cb(sr_session_ctx_t *session, const char *module_name, const char *xpath, sr_event_t event,
uint32_t request_id, void *private_data)
{
sr_change_iter_t *iter;
sr_change_oper_t op;
const struct lyd_node *node;
const char *prev_val, *prev_list;
char *xpath2, *node_value, *ifname, *ipxpath;
char *ptr;
bool prev_dflt;
int rc, failed = 0;
unsigned char enabled = 2;
sr_val_t *val = NULL;
unsigned char loopback = 0;
char *xpath3;
unsigned short mtu;
/* 获取迭代路径 */
if (asprintf(&xpath2, "%s//.", xpath) == -1) {
printf("Memory allocation failed");
return SR_ERR_NOMEM;
}
/* 获取迭代参数 */
rc = sr_get_changes_iter(session, xpath2, &iter);
free(xpath2);
if (rc != SR_ERR_OK) {
printf("Getting changes iter failed.");
return rc;
}
printf("=============%s=================\n", __FUNCTION__);
while ((rc = sr_get_change_tree_next(session, iter, &op, &node, &prev_val, &prev_list, &prev_dflt)) == SR_ERR_OK) {
printf("%s: node schema name: %s\n", __FUNCTION__, node->schema->name);
if(!strcmp(node->schema->name, "address")){
/* ipv4 address
* 当ipv4值发生变化时,会执行该段代码
*/
/* 获取端口名称 */
ifname = ((struct lyd_node_leaf_list *)node->parent->parent->child)->value_str;
printf("%s: ipv4 address, ifname: %s, node schema name: %s\n", __FUNCTION__, ifname, node->schema->name);
if (asprintf(&ipxpath, "%s[name='%s']/ietf-ip:ipv4", xpath, ifname) == -1) {
printf("Memory allocation failed");
return SR_ERR_NOMEM;
}
/* 执行具体的操作,将需要修改的数据写入设备 */
interface_ipv4_address_cb(session, module_name, ipxpath, event, request_id, private_data);
free(ipxpath);
}else if(!strcmp(node->schema->name, "enabled") && !strcmp(node->parent->schema->name, "interface")){
/* interface enabled
* 当将端口进行UP/DOWN操作时,会执行该段代码
*/
ifname = ((struct lyd_node_leaf_list *)node->parent->child)->value_str;
node_value = ((struct lyd_node_leaf_list *)node)->value_str;
printf("%s: interface enabled, ifname: %s, node schema name: %s, line[%d]\n", __FUNCTION__, ifname, node->schema->name, __LINE__);
printf("iface_ignore[%d], node_value[%s], op[%d]\n", iface_ignore, node_value, op);
if(iface_ignore){
continue;
}
if(op == SR_OP_CREATED || op == SR_OP_MODIFIED){
if(!strcmp(node_value, "false")){
enabled = 0;
}else{
enabled = 1;
}
}
printf("enabled[%d], \n", enabled);
interface_if_enabled_cb(ifname, enabled);
}else if(!strcmp(node->schema->name, "enabled") && !strcmp(node->parent->schema->name, "ipv4")){
/* ipv4 enabled */
ifname = ((struct lyd_node_leaf_list *)node->parent->parent->child)->value_str;
node_value = ((struct lyd_node_leaf_list *)node)->value_str;
if (asprintf(&xpath3, "%s[name='%s']/type", xpath, ifname) == -1) {
printf("Memory allocation failed");
return SR_ERR_NOMEM;
}
sr_get_item(session, xpath3, 0, &val);
printf("%s: ipv4 enabled, ifname: %s, node value: %s, xpath3: %s\n", __FUNCTION__, ifname, node_value, xpath3);
free(xpath3);
ptr = (val->data).string_val;
if(strchr(ptr, ':') != NULL){
ptr = strchr(ptr, ':')+1;
}
if(strcmp(ptr, "softwareLoopback") == 0){
loopback = 1;
}
interface_ipv4_enabled_cb(node, ifname, node_value, op, loopback);
if(val != NULL){
free(val);
}
}else if(!strcmp(node->schema->name, "mtu")){
/* ipv4 mtu
* 设置ipv4 mtu值时,会进入该段代码
*/
ifname = ((struct lyd_node_leaf_list *)node->parent->parent->child)->value_str;
mtu = ((struct lyd_node_leaf_list *)node)->value.uint16;
printf("%s: ipv4 mtu, ifname: %s, node schema name: %s, mtu: %d\n", __FUNCTION__, ifname, node->schema->name, mtu);
interface_ipv4_mtu_cb(ifname, mtu, op);
}else if(!strcmp(node->schema->name, "forwarding")){
/* ipv4 forwarding
* 设置ipv4 forwarding值时,会进入该段代码
*/
ifname = ((struct lyd_node_leaf_list *)node->parent->parent->child)->value_str;
node_value = ((struct lyd_node_leaf_list *)node)->value_str;
printf("%s: ipv4 forwarding, ifname: %s, node schema name: %s, node_value: %s\n", __FUNCTION__, ifname, node->schema->name, node_value);
interface_ipv4_forwarding_cb(ifname, node_value, op);
}else if(!strcmp(node->schema->name, "interface")){
/* interface */
ifname = ((struct lyd_node_leaf_list *)node->child)->value_str;
printf("%s: ifname: %s, node schema name: %s, line[%d]\n", __FUNCTION__, ifname, node->schema->name, __LINE__);
iface_ignore = 0;
if(op & SR_OP_DELETED)
iface_ignore = 1;
}else if(!strcmp(node->schema->name, "ipv4")){
/* ipv4 */
ifname = ((struct lyd_node_leaf_list *)node->parent->child)->value_str;
printf("%s: ipv4, ifname: %s, node schema name: %s, line[%d]\n", __FUNCTION__, ifname, node->schema->name, __LINE__);
interface_ipv4_cb(ifname, op, loopback);
}
}
sr_free_change_iter(iter);
if (rc != SR_ERR_NOT_FOUND) {
printf("Getting next change failed.");
return rc;
}
return failed? SR_ERR_CALLBACK_FAILED:SR_ERR_OK;
}
例如,在端口ens32上增加一个地址,通过netopeer2-cli执行完相应操作会,interfaces_if_interface_cb函数监听到该变化后,会调用interface_ipv4_address_cb函数,interface_ipv4_address_cb函数实现如下:
static int interface_ipv4_address_cb(sr_session_ctx_t *session, const char *module_name, const char *xpath, sr_event_t event,
uint32_t request_id, void *private_data)
{
sr_change_iter_t *iter;
sr_change_oper_t op;
const struct lyd_node *node;
const char *prev_val, *prev_list;
char *xpath2, *ifname;
bool prev_dflt;
int rc, failed = 0, i;
char* msg = NULL, *netmask = NULL, *ip = NULL;
unsigned char prefix_len = 0, octet, mask;
if (iface_ignore || iface_ipv4addr_ignore) {
return SR_ERR_OK;
}
if (asprintf(&xpath2, "%s//.", xpath) == -1) {
printf("Memory allocation failed");
return SR_ERR_NOMEM;
}
rc = sr_get_changes_iter(session, xpath2, &iter);
free(xpath2);
if (rc != SR_ERR_OK) {
printf("Getting changes iter failed.");
return rc;
}
printf("=============%s=================\n", __FUNCTION__);
printf("%s: xpath: %s\n", __FUNCTION__, xpath);
while ((rc = sr_get_change_tree_next(session, iter, &op, &node, &prev_val, &prev_list, &prev_dflt)) == SR_ERR_OK) {
/* find name */
if(!strcmp(node->schema->name, "address")){
ifname = ((struct lyd_node_leaf_list *)node->parent->parent->child)->value_str;
printf("ifname: %s, node schema name: %s\n", ifname, node->schema->name);
}
if(!strcmp(node->schema->name, "ip")){
ip = ((struct lyd_node_leaf_list *)node)->value_str;
}
if(!strcmp(node->schema->name, "prefix-length")){
prefix_len = ((struct lyd_node_leaf_list *)node)->value.uint16;
}
if(!strcmp(node->schema->name, "netmask")){
netmask = ((struct lyd_node_leaf_list *)node)->value_str;
}
}
sr_free_change_iter(iter);
if (rc != SR_ERR_NOT_FOUND) {
printf("Getting next change failed.");
return rc;
}
if (ip == NULL) {
printf("Missing ip address in an IPv4 address.\n");
free(netmask);
return SR_ERR_CALLBACK_FAILED;
}
if ((prefix_len == 0 && netmask == NULL) || (prefix_len != 0 && netmask != NULL)) {
printf("Cannot get subnet for the IP \"%s\".", ip);
free(ip);
free(netmask);
return SR_ERR_CALLBACK_FAILED;
}
if (netmask != NULL) {
prefix_len = 0;
mask = 0x80;
octet = (unsigned)atoi(strtok(netmask, "."));
i = 0;
while (mask & octet) {
++prefix_len;
mask >>= 1;
++i;
if (i == 32) {
break;
}
if (i % 8 == 0) {
octet = (unsigned)atoi(strtok(NULL, "."));
mask = 0x80;
}
}
free(netmask);
}
printf("prefix_len: %d\n", prefix_len);
/* 获取到ip, 掩码信息后,写入设备 */
rc = iface_ipv4_ip(ifname, ip, prefix_len, op, &msg);
return failed? SR_ERR_CALLBACK_FAILED:SR_ERR_OK;
}
功能验证
当前系统配置如下:
[root@localhost examples]# ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: ens32: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
link/ether 00:0c:29:a8:fc:7f brd ff:ff:ff:ff:ff:ff
inet 192.168.32.10/24 brd 192.168.32.255 scope global ens32
valid_lft forever preferred_lft forever
inet6 2408:84e5:288:66bb:3953:8a7a:883b:24ba/64 scope global noprefixroute dynamic
valid_lft 2477sec preferred_lft 2477sec
inet6 fe80::4fe2:71fa:ac8c:9554/64 scope link
valid_lft forever preferred_lft forever
3: ens34: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
link/ether 00:0c:29:a8:fc:89 brd ff:ff:ff:ff:ff:ff
inet 192.168.34.10/24 brd 192.168.34.255 scope global ens34
valid_lft forever preferred_lft forever
inet6 2408:84e5:288:66bb:8d5:9a1c:c7b4:183b/64 scope global noprefixroute dynamic
valid_lft 2477sec preferred_lft 2477sec
inet6 fe80::6145:c454:ac17:75c3/64 scope link
valid_lft forever preferred_lft forever
分别启动编译生成的可执行程序ietf-interfaces, netopeer2-server, netopeer2-cli, 其中通过netopeer2-cli终端命令:get-config --source running得到如下信息:
<interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces">
<interface>
<name>ens32</name>
<type xmlns:ianaift="urn:ietf:params:xml:ns:yang:iana-if-type">ianaift:ethernetCsmacd</type>
<enabled>false</enabled>
<ipv4 xmlns="urn:ietf:params:xml:ns:yang:ietf-ip">
<enabled>true</enabled>
<forwarding>false</forwarding>
<mtu>1500</mtu>
<address>
<ip>192.168.32.10</ip>
<prefix-length>24</prefix-length>
</address>
</ipv4>
<ipv6 xmlns="urn:ietf:params:xml:ns:yang:ietf-ip">
<enabled>true</enabled>
<forwarding>false</forwarding>
<mtu>1500</mtu>
<dup-addr-detect-transmits>1</dup-addr-detect-transmits>
<autoconf>
<create-global-addresses>true</create-global-addresses>
</autoconf>
</ipv6>
</interface>
<interface>
<name>lo</name>
<type xmlns:ianaift="urn:ietf:params:xml:ns:yang:iana-if-type">ianaift:softwareLoopback</type>
<enabled>true</enabled>
<ipv4 xmlns="urn:ietf:params:xml:ns:yang:ietf-ip">
<enabled>true</enabled>
<forwarding>false</forwarding>
<address>
<ip>127.0.0.1</ip>
<prefix-length>8</prefix-length>
</address>
</ipv4>
<ipv6 xmlns="urn:ietf:params:xml:ns:yang:ietf-ip">
<enabled>true</enabled>
<forwarding>false</forwarding>
<dup-addr-detect-transmits>1</dup-addr-detect-transmits>
<autoconf>
<create-global-addresses>true</create-global-addresses>
</autoconf>
</ipv6>
</interface>
<interface>
<name>ens34</name>
<type xmlns:ianaift="urn:ietf:params:xml:ns:yang:iana-if-type">ianaift:ethernetCsmacd</type>
<enabled>true</enabled>
<ipv4 xmlns="urn:ietf:params:xml:ns:yang:ietf-ip">
<enabled>true</enabled>
<forwarding>false</forwarding>
<mtu>1500</mtu>
<address>
<ip>192.168.34.10</ip>
<prefix-length>24</prefix-length>
</address>
</ipv4>
<ipv6 xmlns="urn:ietf:params:xml:ns:yang:ietf-ip">
<enabled>true</enabled>
<forwarding>false</forwarding>
<mtu>1500</mtu>
<dup-addr-detect-transmits>1</dup-addr-detect-transmits>
<autoconf>
<create-global-addresses>true</create-global-addresses>
</autoconf>
</ipv6>
</interface>
</interfaces>
通过netopeer2-cli终端edit-config --target running --config=data-ip.xml命令配置端口ip,其中data-ip.xml配置如下:
<interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces">
<interface>
<name>ens34</name>
<description>Wire Connection</description>
<type xmlns:ift="urn:ietf:params:xml:ns:yang:iana-if-type">ift:ethernetCsmacd</type>
<enabled>true</enabled>
<ipv4 xmlns="urn:ietf:params:xml:ns:yang:ietf-ip">
<address><ip>192.168.34.15</ip><prefix-length>24</prefix-length></address>
</ipv4>
</interface>
</interfaces>
分别为端口ens32、ens34添加IP地址为192.168.32.15,192.168.34.15,完成后,ietf-interfaces端输出如下:
=============interfaces_if_interface_cb=================
interfaces_if_interface_cb: node schema name: description
interfaces_if_interface_cb: node schema name: enabled
/* 先调用接口ens32使能函数 */
interfaces_if_interface_cb: interface enabled, ifname: ens32, node schema name: enabled, line[648]
iface_ignore[0], node_value[true], op[1]
enabled[1],
enabled: 1
interfaces_if_interface_cb: node schema name: address
/* 由于ip地址信息发生改变,调用相应函数进行处理 */
interfaces_if_interface_cb: ipv4 address, ifname: ens32, node schema name: address
=============interface_ipv4_address_cb=================
interface_ipv4_address_cb: xpath: /ietf-interfaces:interfaces/interface[name='ens32']/ietf-ip:ipv4
ifname: ens32, node schema name: address
prefix_len: 24
interfaces_if_interface_cb: node schema name: ip
interfaces_if_interface_cb: node schema name: prefix-length
=============interfaces_if_interface_cb=================
interfaces_if_interface_cb: node schema name: description
interfaces_if_interface_cb: node schema name: enabled
interfaces_if_interface_cb: interface enabled, ifname: ens32, node schema name: enabled, line[648]
iface_ignore[0], node_value[true], op[1]
enabled[1],
enabled: 1
interfaces_if_interface_cb: node schema name: address
interfaces_if_interface_cb: ipv4 address, ifname: ens32, node schema name: address
=============interface_ipv4_address_cb=================
interface_ipv4_address_cb: xpath: /ietf-interfaces:interfaces/interface[name='ens32']/ietf-ip:ipv4
ifname: ens32, node schema name: address
prefix_len: 24
interfaces_if_interface_cb: node schema name: ip
interfaces_if_interface_cb: node schema name: prefix-length
=============interfaces_if_interface_cb=================
interfaces_if_interface_cb: node schema name: description
interfaces_if_interface_cb: node schema name: address
/* 调用接口ens34地址变化回调函数 */
interfaces_if_interface_cb: ipv4 address, ifname: ens34, node schema name: address
=============interface_ipv4_address_cb=================
interface_ipv4_address_cb: xpath: /ietf-interfaces:interfaces/interface[name='ens34']/ietf-ip:ipv4
ifname: ens34, node schema name: address
prefix_len: 24
interfaces_if_interface_cb: node schema name: ip
interfaces_if_interface_cb: node schema name: prefix-length
=============interfaces_if_interface_cb=================
interfaces_if_interface_cb: node schema name: description
interfaces_if_interface_cb: node schema name: address
interfaces_if_interface_cb: ipv4 address, ifname: ens34, node schema name: address
=============interface_ipv4_address_cb=================
interface_ipv4_address_cb: xpath: /ietf-interfaces:interfaces/interface[name='ens34']/ietf-ip:ipv4
ifname: ens34, node schema name: address
prefix_len: 24
interfaces_if_interface_cb: node schema name: ip
interfaces_if_interface_cb: node schema name: prefix-length
分析输入打印信息可知:当相关信息发生变化时,会触发监听函数进行函数回调,以及时进行处理。配置后,系统配置信息如下:
[root@localhost examples]# ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: ens32: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
link/ether 00:0c:29:a8:fc:7f brd ff:ff:ff:ff:ff:ff
inet 192.168.32.15/24 brd 192.168.32.255 scope global ens32
valid_lft forever preferred_lft forever
inet 192.168.32.10/24 scope global secondary ens32
valid_lft forever preferred_lft forever
inet6 2408:84e5:288:66bb:3953:8a7a:883b:24ba/64 scope global noprefixroute dynamic
valid_lft 2323sec preferred_lft 2323sec
inet6 fe80::4fe2:71fa:ac8c:9554/64 scope link
valid_lft forever preferred_lft forever
3: ens34: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
link/ether 00:0c:29:a8:fc:89 brd ff:ff:ff:ff:ff:ff
inet 192.168.34.10/24 brd 192.168.34.255 scope global ens34
valid_lft forever preferred_lft forever
inet 192.168.34.15/24 scope global secondary ens34
valid_lft forever preferred_lft forever
inet6 2408:84e5:288:66bb:8d5:9a1c:c7b4:183b/64 scope global noprefixroute dynamic
valid_lft 2323sec preferred_lft 2323sec
inet6 fe80::6145:c454:ac17:75c3/64 scope link
valid_lft forever preferred_lft forever
端口地址添加成功。下面再将刚刚添加的ip地址删除,data-ip.xml文件修改如下:
<interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces" xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0">
<interface>
<name>ens32</name>
<description>Wire Connection</description>
<type xmlns:ift="urn:ietf:params:xml:ns:yang:iana-if-type">ift:ethernetCsmacd</type>
<enabled>true</enabled>
<ipv4 xmlns="urn:ietf:params:xml:ns:yang:ietf-ip">
<address nc:operation="delete"><ip>192.168.32.15</ip><prefix-length>24</prefix-length></address>
</ipv4>
</interface>
</interfaces>
可以发现,可以正确删除端口的地址,端口配置恢复成之前的配置。
完毕!