第七章 frr sysrepo纳管初始化流程分析

本章节主要是通过分析frr sysrepo纳管实现来讲讲frr-7.5是如何实现对sysrepo的支持。以isis协议纳管实现为例。了解本章节的内容需要先了解前面章节的内容。本章节的内容不会过多重复前面的内容。通过前一章节的内容,首先是要确保frr-isisd.yang能在sysrepo中成功加载,这是前提,需要先确保;其次,启动isisd进程。

1 isisd进程的启动分析

Isisd集成sysrepo的启动方法如下:

/usr/lib/frr/isisd -d -M sysrepo:/usr/lib64/frr/modules/ --log-level debugging
【说明1】:调试时,可以增加--log-level选项,便于调试
【说明2】:sysrepo属于frr的一个模块,要加载模块时一定要带-M [module],
          否则模块不会启动,如果不带模块的路径,使用的模块路径是编译时指定的默认路径。

 isisd进程启动的入口位于{$frr}\isisd\isis_main.c的main函数中,main()函数的frr主线程初始化函数frr_init()。但是在了解frr_init()之前,需要将frr中涉及到di的来源先搞清楚,static struct frr_daemon_info *di = NULL; di是一个指向frr_daemon_info结构的static指 针,在frr_preinit(struct frr_daemon_info ,*daemon,...)函数有这样一行代码   di = daemon; daemon实际是指向所在isisd_di地址空间,所以,di最终指向的是isisd_di所指向的空间,所以看看isisd_di是如实实现完成初始化的:


static struct frr_daemon_info isisd_di;  //全局isisd_di结构定义

FRR_DAEMON_INFO(isisd, ISIS, .vty_port = ISISD_VTY_PORT,
		.proghelp = "Implementation of the IS-IS routing protocol.",
		.copyright =
			"Copyright (c) 2001-2002 Sampo Saaristo,"
			" Ofer Wald and Hannes Gredler",
		.signals = isisd_signals,
		.n_signals = array_size(isisd_signals),
		.privs = &isisd_privs, .yang_modules = isisd_yang_modules,
		.n_yang_modules = array_size(isisd_yang_modules), )

#define FRR_DAEMON_INFO(execname, constname, ...)                              \
	static struct frr_daemon_info execname##_di = {.name = #execname,      \
						       .logname = #constname,  \
						       .module = THIS_MODULE,  \
						       __VA_ARGS__};           \
	FRR_COREMOD_SETUP(.name = #execname,                                   \
			  .description = #execname " daemon",                  \
			  .version = FRR_VERSION, )  

再看看frr_daemon_info数据结构的定义,
struct frr_daemon_info {
	unsigned flags;

	const char *progname;
	const char *name;
	const char *logname;
	unsigned short instance;
	struct frrmod_runtime *module;

	char *vty_addr;
	int vty_port;
	char *vty_sock_path;
	bool dryrun;
	bool daemon_mode;
	bool terminal;
	enum frr_cli_mode cli_mode;

	struct thread *read_in;
	const char *config_file;
	const char *backup_config_file;
	const char *pid_file;
#ifdef HAVE_SQLITE3
	const char *db_file;
#endif
	const char *vty_path;
	const char *module_path;

	const char *pathspace;
	bool zpathspace;

	const char *early_logging;
	const char *early_loglevel;

	const char *proghelp;
	void (*printhelp)(FILE *target);
	const char *copyright;
	char startinfo[128];

	struct quagga_signal_t *signals;
	size_t n_signals;

	struct zebra_privs_t *privs;

	const struct frr_yang_module_info *const *yang_modules;
	size_t n_yang_modules;

	bool log_always;
};
所以,isisd_di结构的初始是通过宏FRR_DAEMON_INFO完成的,
FRR_DAEMON_INFO()中的每项的定义,各项都封装成可变__VA_ARGS__,
保存到frr_daemon_info的对应的各项参数中,
例如.yang_modules = isisd_yang_modules,
就是保存到frr_daemon_info->yang_modules。

通过GDB isisd进程初始化时,打印isisd_di结构就明显可以看出,简单贴一个调试的方法:

 gdb isisd
 (gdb) set args -d -M sysrepo:/usr/lib64/frr/modules/ --log-level debugging
 (gdb) b frr_preinit
 (gdb) b frr_init
 (gdb) run

执行完frr_preinit后,我们再来看来isisd_di的变化,基本没有变化,这里因为都是使用初始配置,才基本没有变化,如果指定到相应的pid,配置保存文件,也就会有相应的变化,这个不多说。

通过上面的过程的di是怎么来的,在frr_init()中还有一个很重要的概念,module_path,是指sysrepo.so模块路径所在路径,请看调试信息

这个路径是由编译器在编译时指定MODULE_PATH。

而我们在命令行中提供的路径是指定yang文件所在的路径,这个路径是在命令行参数解析时,保存在全局变量中

static struct option_chain *modules = NULL, **modnext = &modules;

通过上面的分析,将模块加载的需要的资源已经详细指出,我们再来看看frr_init()它的实现如下,已经将不相关的删除,只留与module相关的代码,有兴趣的可以对照源码一起看:

struct thread_master *frr_init(void)
{
	struct option_chain *oc;
	struct frrmod_runtime *module;  //正在启动运行的模块
	char moderr[256];
	//char p_instance[16] = "", p_pathspace[256] = "";
	const char *dir;
	dir = di->module_path ? di->module_path : frr_moduledir;
    
	/*这段不相关,不在这细说*/
    //di->module是指要加载运行的sysrep.so模块
    //frrmod_init(),是使用modinfo信息对全局就是的初始化
	frrmod_init(di->module);  
	while (modules) { 
        //modules是命令行解析所记录的:sysrepo:/usr/lib64/frr/modules/
		modules = (oc = modules)->next;
        /* oc->arg,sysrepo:/usr/lib64/frr/modules
         * /usr/lib64/frr/modules
         * 通过dlopen完成syrepo.so的加载
         */
		module = frrmod_load(oc->arg, dir, moderr, sizeof(moderr)); 
		if (!module) {
			fprintf(stderr, "%s\n", moderr);
			exit(1);
		}
		XFREE(MTYPE_TMP, oc);
	}
	/*这段不相关,不在这细说*/
	yang_init();  //为作用于全部容器的yang上下文的初始化,影响全部的yangy文件。
	//debug_init_cli();
    
     /* di->yang_modules是指向isisd_yang_modules[]
     * di->n_yang_modules yang的数量,isis就与3个yang相关。
     * nb_init(),北向初始化,sysrepo是在frr的上层,与sysrepo间的通道也是北向接口
     */
	nb_init(master, di->yang_modules, di->n_yang_modules);
	if (nb_db_init() != NB_OK)
		flog_warn(EC_LIB_NB_DATABASE,
			  "%s: failed to initialize northbound database",
			  __func__);

	return master;
}

2 北向接口初始化(nb_init)分析

在分析nb_init()之前,需要讲讲引入的一个非常重要的数据结构红黑树(rb_tree)

红黑树是节点颜色作为额外属性的二叉搜索树,它满足于如下一系列条件:

1)、从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点

2)、根节点是黑色

3)、所有叶子都是黑色

4)、每个红色节点(除了根节点)都有一个黑色的父节点

红黑树每个操作的时间复杂度不超过O(log n),最大深度2log(n+1)

#define RB_BLACK	0
#define RB_RED		1

struct rbt_tree {
	struct rb_entry *rbt_root;
};

struct rb_entry {
	struct rb_entry *rbt_parent;
	struct rb_entry *rbt_left;
	struct rb_entry *rbt_right;
	unsigned int rbt_color;
};

//rbtree type定义
struct rb_type {
	int (*t_compare)(const void *, const void *);
	void (*t_augment)(void *);
	unsigned int t_offset; /* offset of rb_entry in type */
};
其它的定义可以参考openbsd-tree.h

而frr中又是如何使用rbtree结构的
首先:
/*构建一颗rb_tree的头结点,root节点*/
RB_HEAD(_name, _type)   
/*定义一颗指定类型的RB树的全部操作,该RB树的操作包括rb_init,rb_insert,rb_remove等等*/
#define RB_PROTOTYPE(_name, _type, _field, _cmp) 
/*构建指定type的rb_type的比较函数*/
#define RB_GENERATE(_name, _type, _field, _cmp)                                \
	RB_GENERATE_INTERNAL(_name, _type, _field, _cmp, NULL)
/*初始化*/
#define RB_INITIALIZER(_head)	{ { NULL } }     

所以,FRR的北侧用到了很多RB操作了,配置的增删改查,都是通过rb-tree实现的。
其次,实际看一下yang模块是如何使用rb-tree的。
/*yang_module的数据结构定义*/
struct yang_module {
	RB_ENTRY(yang_module) entry;
	const char *name;
	const struct lys_module *info;
#ifdef HAVE_CONFD
	int confd_hash;
#endif
#ifdef HAVE_SYSREPO
	sr_subscription_ctx_t *sr_subscription;
#endif
};
1、
/*构建头节点*/
RB_HEAD(yang_modules, yang_module);   
扩展出来的样式为:
struct yang_modules {
    struct rbt_tree rbh_root; 
}

2、yang_module rb-trees基本操作库函数的构建
RB_PROTOTYPE(yang_modules, yang_module, entry, yang_module_compare);

3、yang_modules的内部entry的比较操作
RB_GENERATE(yang_modules, yang_module, entry, yang_module_compare)

4、定义一个静态的yang_modules结构,用于保存记录
struct yang_modules yang_modules = RB_INITIALIZER(&yang_modules);

5、按实际需求将数据插入到yang_modules的rb_tree中
RB_INSERT(yang_modules, &yang_modules, module);

通过以上,至少对rb的使用有一个简单的理解,再来看看rb_init是操作的

1、yang模块的加载
  frr_init()为nb_init()函数传入了yang_module_info结构的实际数据如下:
  static const struct frr_yang_module_info *const isisd_yang_modules[] = {
	  &frr_interface_info,
	  &frr_isisd_info,
	  &frr_route_map_info,
 };

/*调用libyang提供的解析函数ly_ctx_load_module(),
*将yang文件的内容解析到数据结构struct lys_module,
*并将解析出的module_info插入本地的yang_modules rbtree中。
*/
struct yang_module *yang_module_load(const char *module_name)
{
	struct yang_module *module;
	const struct lys_module *module_info;

	module_info = ly_ctx_load_module(ly_native_ctx, module_name, NULL);
    /*错误检查不看,省点空间*/
	module = XCALLOC(MTYPE_YANG_MODULE, sizeof(*module));
	module->name = module_name;
	module->info = module_info;
    //模块插入
	if (RB_INSERT(yang_modules, &yang_modules, module) != NULL) {
	     /*错误检查不看,省点空间*/
	}

	return module;
}

2、frr nb_nodes的创建,YANG文件通过ly_ctx_load_module()的解析
已经将数据保存到于在yang_modules rb tress中, 
nb_nodes_create()完成将libyang所支持snode与本地的nb_node建立连接

void nb_nodes_create(void)
{
    /*iterate是遍历,是要完成已经解析出的全部YANG模块的全部节点*/
	yang_snodes_iterate_all(nb_node_new_cb, 0, NULL);
}

int yang_snodes_iterate_all(yang_iterate_cb cb, uint16_t flags, void *arg)
{
	struct yang_module *module;
	int ret = YANG_ITER_CONTINUE;
    
    //yang_modules,也就是在解析时,已经完成本地创建的,
    //并且所保存的module_info就是YANG的全部数据
	RB_FOREACH (module, yang_modules, &yang_modules) {
		ret = yang_snodes_iterate_module(module->info, cb, flags, arg);
		if (ret == YANG_ITER_STOP)
			return ret;
	}

	return ret;
}


int yang_snodes_iterate_module(const struct lys_module *module,
			       yang_iterate_cb cb, uint16_t flags, void *arg)
{
	struct lys_node *snode;
	int ret = YANG_ITER_CONTINUE;
    
    /从libyang tree型数据开始遍历,snode,对应info中所保存的data,
	LY_TREE_FOR (module->data, snode) {
        /*snodes的遍历是根据不同的节点类型做不同处理,就不一一说,可以看源码*/
		ret = yang_snodes_iterate_subtree(snode, cb, flags, arg);
		if (ret == YANG_ITER_STOP)
			return ret;
	}

	....

	return ret;
}

在snode遍历时,按节点类型解析后,将执行回调实现nb_node的创建;
static int nb_node_new_cb(const struct lys_node *snode, void *arg)
{
	struct nb_node *nb_node;
	struct lys_node *sparent, *sparent_list;
    
    /*分配nb_noded的内存和相应的初始化*/
	nb_node = XCALLOC(MTYPE_NB_NODE, sizeof(*nb_node));
	yang_snode_get_path(snode, YANG_PATH_DATA, nb_node->xpath,
			    sizeof(nb_node->xpath));  
	nb_node->priority = NB_DFLT_PRIORITY;
	sparent = yang_snode_real_parent(snode);
	if (sparent)
		nb_node->parent = sparent->priv;
	sparent_list = yang_snode_parent_list(snode);
	if (sparent_list)
		nb_node->parent_list = sparent_list->priv;

	/* Set flags. */
    /*下面非常重要,如果就CONTAINER节点和LIST型节点,需要递归遍历该节点下的全部子节点*/
	if (CHECK_FLAG(snode->nodetype, LYS_CONTAINER | LYS_LIST)) {
		bool config_only = true;

		yang_snodes_iterate_subtree(snode, nb_node_check_config_only,
					    YANG_ITER_ALLOW_AUGMENTATIONS,
					    &config_only);
		if (config_only)
			SET_FLAG(nb_node->flags, F_NB_NODE_CONFIG_ONLY);
	}
	if (CHECK_FLAG(snode->nodetype, LYS_LIST)) {
		struct lys_node_list *slist;

		slist = (struct lys_node_list *)snode;
		if (slist->keys_size == 0)
			SET_FLAG(nb_node->flags, F_NB_NODE_KEYLESS_LIST);
	}

	/*
	 * Link the northbound node and the libyang schema node with one
	 * another.
	 */
    /*在snode与nb_node建立连接*/
	nb_node->snode = snode;
	lys_set_private(snode, nb_node);

	return YANG_ITER_CONTINUE;
}

3、加载在yang_module_info中定义的全部回调,
  先看看frr_yang_module_info就如何定义的,其定义如下:
  frr_struct frr_yang_module_info {
	const char *name; /* YANG module name. */

	/* Northbound callbacks. */
	const struct {
		const char *xpath; /* Data path of this YANG node. */
		struct nb_callbacks cbs; /* Callbacks implemented for this node. */
		uint32_t priority; /* Priority - lower priorities are processed first. */
	} nodes[];
};

const struct frr_yang_module_info frr_isisd_info = {
	.name = "frr-isisd",  //模块名
	.nodes = {
		{
            //就是YANG文件实际对应的路径,可以对照frr_isis.yang文件理解
			.xpath = "/frr-isisd:isis/instance",  
			.cbs = {   //创建的回调函数
				.cli_show = cli_show_router_isis,
				.create = isis_instance_create,
				.destroy = isis_instance_destroy,
			},
			.priority = NB_DFLT_PRIORITY - 1,
		},
     .....
}

/*将预定义的CB挂到指定的nb_node中*/
static void nb_load_callbacks(const struct frr_yang_module_info *module)
{
	for (size_t i = 0; module->nodes[i].xpath; i++) {
		struct nb_node *nb_node;
		uint32_t priority;
        /*nb_node_new_cb()的最后完将新建的nb_node挂到snode->priv结构中,
          * nb_node_find()是要通过module->nodes[i].xpath定义的path,找到对应的nb_node*/
		nb_node = nb_node_find(module->nodes[i].xpath);
		if (!nb_node) {
			continue;
		}
        
        /*找到nb_node后,将预定义的cbs, priority挂载到nb_node,
         *需要注意的是,nb_node是保存在 snode->priv的
        */
		nb_node->cbs = module->nodes[i].cbs;
		priority = module->nodes[i].priority;
		if (priority != 0)
			nb_node->priority = priority;
	}
}

4、用户定义CB的有效性检验,yang_snodes_iterate_all就不在看吧,主要看它的回调实现
static int nb_node_validate(const struct lys_node *snode, void *arg)
{
   /*nb_load_callbacks()函数已经实现将用户定义的回调都保存snode->priv中*/
	struct nb_node *nb_node = snode->priv;  
	unsigned int *errors = arg;

	/* Validate callbacks and priority. */
	*errors += nb_node_validate_cbs(nb_node);
	*errors += nb_node_validate_priority(nb_node);

	return YANG_ITER_CONTINUE;
}

在看用户定义回调的检验之前,这里就是先大致了解YANG文件,和用户根据yang文件定义的CB间的关系。
module frr-isisd {
  yang-version 1.1;
  namespace "http://frrouting.org/yang/isisd";
  prefix frr-isisd;
  <......>
  container isis {
    description
      "Configuration of the IS-IS routing daemon.";
    list instance {
      key "area-tag";
      description
        "IS-IS routing instance.";
      leaf area-tag {
        type string;
        description
          "Area-tag associated to this routing instance.";
      }
     <.......>
   }
  }
}
通过上面的分析,已经它知道,通过libyang提供的的库函数,YANG文件的数据解析并保存与snode中

const struct frr_yang_module_info frr_isisd_info = {
	.name = "frr-isisd",
	.nodes = {
		{
			.xpath = "/frr-isisd:isis/instance",
			.cbs = {
				.cli_show = cli_show_router_isis,
				.create = isis_instance_create,
				.destroy = isis_instance_destroy,
			},
			.priority = NB_DFLT_PRIORITY - 1,
		},
     <..........>
}

通过上面的分析,已经将用户自定义的yang_module_info,
YANG文件映射snode中,用户定义模块CB保存到snode->priv中,也是nb_node中。

bool nb_operation_is_valid(enum nb_operation operation,
			   const struct lys_node *snode)
{
    //看到这,就能感觉到在nb_node_new_cb中将nb_node与snode建立连接的妙处,这是一种双向通道
	struct nb_node *nb_node = snode->priv;  
	struct lys_node_container *scontainer;
	struct lys_node_leaf *sleaf;

	switch (operation) {
	case NB_OP_CREATE:
		if (!CHECK_FLAG(snode->flags, LYS_CONFIG_W))
			return false;

		switch (snode->nodetype) {
		case LYS_LEAF:
			sleaf = (struct lys_node_leaf *)snode;
			if (sleaf->type.base != LY_TYPE_EMPTY)
				return false;
			break;
		case LYS_CONTAINER:
			scontainer = (struct lys_node_container *)snode;
			if (!scontainer->presence)
				return false;
			break;
		case LYS_LIST:
		case LYS_LEAFLIST:
			break;
		default:
			return false;
		}
		return true;
    <.......>
 }

5、最后在内存中创建一份空的配置,用于后续直接在内存中增,删,改,查配置。
	running_config = nb_config_new(NULL);
	running_config_entries = hash_create(running_config_entry_key_make,
					     running_config_entry_cmp,
					     "Running Configuration Entries");

通过nb_init(),完成了将用户YANG文件的解析并保存到yang_modules.info的rb_tree中,也完成了用户对yang文件回调的解析保存到snode->priv中,也完成用户自定义的回调的校验,完成上面工作,前期的相关初始化算完成了。

3 北向syrepo初始化分析

1、先看北向sysrepo的初始化入口定义FRR_MODULE_SETUP, 这个处理在前面聊过,可以回头看。
2、再看看hook_register的扩展

/*向hook添加回调*/
extern void _hook_register(struct hook *hook, void *funcptr, void *arg,
			   bool has_arg, struct frrmod_runtime *module,
			   const char *funcname, int priority);

#define hook_register(hookname, func)                                          \
	_hook_register(&_hook_##hookname, _hook_typecheck_##hookname(func),    \
		       NULL, false, THIS_MODULE, #func, HOOK_DEFAULT_PRIORITY)

static int frr_sr_module_init(void)
{
    /*向_hook_frr_late_init,添加frr_sr_module_late_init回调*/
	hook_register(frr_late_init, frr_sr_module_late_init);

     /*向_hook_frr_very_late_init,添加frr_sr_module_very_late_init回调*/
    hook_register(frr_very_late_init, frr_sr_module_very_late_init);

	return 0;
}

FRR_MODULE_SETUP(.name = "frr_sysrepo", .version = FRR_VERSION,
		 .description = "FRR sysrepo integration module",
		 .init = frr_sr_module_init, )

综上,frr北向syrepo的初始化入口函数,实质是注册了两个hook函数。

 frr_sysrepo的hook回调已经注册上,那么,是如何启动相应的模块的初始化,多说一句的,FRR的全部模块都是使用这一模式初始化的。

再回过头看看isis的main()函数,main()函数在快要结束是调用frr_config_fork()函数,

void frr_config_fork(void)
{
	hook_call(frr_late_init, master); //这就对应执行frr_sr_module_late_init初始化

	if (!(di->flags & FRR_NO_CFG_PID_DRY)) {
		/* Don't start execution if we are in dry-run mode */
		if (di->dryrun) {
			frr_config_read_in(NULL);
			exit(0);
		}

		thread_add_event(master, frr_config_read_in, NULL, 0,
				 &di->read_in);
	}

	if (di->daemon_mode || di->terminal)
		frr_daemonize();

	if (!di->pid_file)
		di->pid_file = pidfile_default;
	pid_output(di->pid_file);
	zlog_tls_buffer_init();
}
再看看hook_call的执行
#define hook_call(hookname, ...) hook_call_##hookname(__VA_ARGS__)
hook_call实际就是hook_call_frr_late_init(master),也就是执行的frr_sr_module_late_init()

而frr_config_read_in()又注册了hook_call(frr_very_late_init, master);
这个hook与上面的原理一到致,最后调用到frr_sr_module_very_late_init().
因此在main()的config_fork中完成模块的最后初始化。

frr_sr_module_very_late_init()的处理流程

static int frr_sr_module_very_late_init(struct thread_master *tm)
{
	master = tm;

	if (frr_sr_init() < 0) {
		flog_err(EC_LIB_SYSREPO_INIT,
			 "failed to initialize the Sysrepo module");
		return -1;
	}
    //注册frr_sr_finish的hook,在进程结束退出时用于回收分配的sysrepo资源
	hook_register(frr_fini, frr_sr_finish);

	return 0;
}

/* FRR's Sysrepo 初始化*/
static int frr_sr_init(const char *program_name)
{
	struct yang_module *module;
	int  ret;

	</*frr-sysrepo相当于在sysrepo新创建一个插件,插件如何初始化,可以再回头看看前面的章节./>
   
   /* 与sysrepo完成建立链接并启用一个会话后,需要对相应的回调做相应的订阅.
     * yang_modules还是前面nb_init()所解析出并保存在yang_modules rb_tree中的yang_modules.
     * 还是一个一个yang module的订阅,完成module_change,state, rpc,action的订阅
	/* Perform subscriptions. */
	RB_FOREACH (module, yang_modules, &yang_modules) 
	{
		int event_pipe;

		frr_sr_subscribe_config(module);
		yang_snodes_iterate(module->info, frr_sr_subscribe_state, 0,
				    module);
		yang_snodes_iterate(module->info, frr_sr_subscribe_rpc, 0,
				    module);

		/* Watch subscriptions. */
		ret = sr_get_event_pipe(module->sr_subscription, &event_pipe);
		if (ret != SR_ERR_OK) {
			flog_err(EC_LIB_SYSREPO_INIT,
				 "%s: sr_get_event_pipe(): %s", __func__,
				 sr_strerror(ret));
			goto cleanup;
		}
		thread_add_read(master, frr_sr_read_cb, module->sr_subscription,
				event_pipe, &module->sr_thread);
	}
    
    /*注册nb_notification_send的hook函数*/
	hook_register(nb_notification_send, frr_sr_notification_send);


	return 0;
}


static void frr_sr_subscribe_config(struct yang_module *module)
{
	int ret;
    /* frr使用对整个模块的订阅,不具体区分特定的子订阅,这样的操作是,将订阅实现简单我我化.
     * 回调frr_sr_config_change_cb是对整个模块变化的响应,
     * 基于module的订阅,需要相应的处理的请求就大幅度降低,从而提高了整个sysrepo的处理性能.
     */
	ret = sr_module_change_subscribe(
		session, module->name, NULL, frr_sr_config_change_cb, NULL, 0,
		SR_SUBSCR_DEFAULT | SR_SUBSCR_ENABLED | SR_SUBSCR_NO_THREAD,
		&module->sr_subscription);
	if (ret != SR_ERR_OK)
		flog_err(EC_LIB_LIBSYSREPO, "sr_module_change_subscribe(): %s",
			 sr_strerror(ret));
}

/*  state、rpc、action的订阅类似,都与节点类型相关,非不相关的节点,不需要执行相应的订阅
 *  以state的订阅为例,
 *  const struct lys_node *snode  //yang文件解析出的数据,
 *                                //包括用户遍历时创建的私有数据,这个就厉 
 *                                //害,snode->priv与nb_node相互转化, 
 *                                //都是指向同一块内存空间
*/
static int frr_sr_subscribe_state(const struct lys_node *snode, void *arg)
{
	struct yang_module *module = arg;
	struct nb_node *nb_node;
	int ret;
    /*节点类型的检查,非config false节点,不需要订阅*/
	if (!CHECK_FLAG(snode->flags, LYS_CONFIG_R))
		return YANG_ITER_CONTINUE;
	/* We only need to subscribe to the root of the state subtrees. */
	if (snode->parent && CHECK_FLAG(snode->parent->flags, LYS_CONFIG_R))
		return YANG_ITER_CONTINUE;
    /*通过snode->priv得到nb_node,nb_node结构中有xpath,就可以相应的路径做state的订阅.*/
	nb_node = snode->priv;

	DEBUGD(&nb_dbg_client_sysrepo, "%s: providing data to '%s'", __func__,
	       nb_node->xpath);

	ret = sr_dp_get_items_subscribe(
		session, nb_node->xpath, frr_sr_state_cb, NULL,
		SR_SUBSCR_CTX_REUSE, &module->sr_subscription);
	if (ret != SR_ERR_OK)
		flog_err(EC_LIB_LIBSYSREPO, "sr_dp_get_items_subscribe(): %s",
			 sr_strerror(ret));

	return YANG_ITER_CONTINUE;
}

 通过以上过程,FRR完成对sysrepo的纳管,整个过程中,最重要的是nb_init()、frr_sysrepo()的初始化,nb_init()完成将yang文件映射到内存中,核心的两个结构是yang_modules,snode, nb_node三个数据结构,以及相互的转换,这点处理的非常巧妙,frr_sysrepo()是完成对sysrepo的订阅,以上就是frr对整个sysrepo纳管的初始化流程分析。

而用户在frr_yang_module_info中依照模块YANG定义的回调,又是如何响应与sysrepo处理的实际请求。通俗些,ODL控制器下发一份配置,是怎样通过sysrepo-->frr-sysrepo->isis完成整个过程的处理,就放在下章节再细说。

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值