Created
March 26, 2020 00:18
-
-
Save rwestphal/2663b33c9b13a6c14ab5f0fd247c0280 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdlib.h> | |
#include <stdio.h> | |
#include <stdint.h> | |
#include <string.h> | |
#include <err.h> | |
#include <assert.h> | |
#include <arpa/inet.h> | |
#include <stdbool.h> | |
#include <libyang/libyang.h> | |
static struct ly_ctx *ly_ctx; | |
static struct lyd_node *running, *candidate; | |
static void print(struct lyd_node *config) | |
{ | |
lyd_print_fd(1, config, LYD_JSON, | |
LYP_FORMAT | LYP_KEEPEMPTYCONT | LYP_WITHSIBLINGS); | |
} | |
static void validate_config(struct lyd_node **config) | |
{ | |
if (lyd_validate(config, LYD_OPT_CONFIG, ly_ctx) != 0) | |
errx(1, "lyd_validate"); | |
} | |
struct lyd_node *dnode_find(const struct lyd_node *dnode, const char *xpath) | |
{ | |
struct ly_set *set; | |
struct lyd_node *dnode_ret = NULL; | |
set = lyd_find_path(dnode, xpath); | |
assert(set); | |
if (set->number == 0) | |
goto exit; | |
if (set->number > 1) { | |
warnx("%s: found %u elements (expected 0 or 1) [xpath %s]", | |
__func__, set->number, xpath); | |
goto exit; | |
} | |
dnode_ret = set->set.d[0]; | |
exit: | |
ly_set_free(set); | |
return dnode_ret; | |
} | |
static void diff(struct lyd_node *config1, struct lyd_node *config2) | |
{ | |
struct lyd_difflist *diff; | |
if (0) { | |
printf("=================\n"); | |
printf("DIFF CONFIG 1:\n"); | |
printf("=================\n"); | |
print(config1); | |
printf("=================\n"); | |
printf("DIFF CONFIG 2:\n"); | |
printf("=================\n"); | |
print(config2); | |
} | |
diff = lyd_diff(config1, config2, LYD_DIFFOPT_WITHDEFAULTS); | |
if (diff == NULL) | |
errx(1, "lyd_diff(): %s", ly_errmsg(ly_ctx)); | |
for (int i = 0; diff->type[i] != LYD_DIFF_END;) { | |
LYD_DIFFTYPE type; | |
struct lyd_node *first; | |
struct lyd_node *second; | |
const char *type_str; | |
char *xpath = NULL; | |
type = diff->type[i]; | |
first = diff->first[i]; | |
second = diff->second[i]; | |
i++; | |
switch (type) { | |
case LYD_DIFF_CREATED: | |
type_str = "LYD_DIFF_CREATED"; | |
xpath = lyd_path(second); | |
break; | |
case LYD_DIFF_DELETED: | |
type_str = "LYD_DIFF_DELETED"; | |
xpath = lyd_path(first); | |
break; | |
case LYD_DIFF_CHANGED: | |
type_str = "LYD_DIFF_CHANGED"; | |
xpath = lyd_path(second); | |
break; | |
case LYD_DIFF_MOVEDAFTER1: | |
case LYD_DIFF_MOVEDAFTER2: | |
/* Ignore. */ | |
continue; | |
default: | |
printf("unknown diff type:\n"); | |
continue; | |
} | |
printf(" %s: xpath: %s\n", type_str, xpath); | |
free(xpath); | |
} | |
lyd_free_diff(diff); | |
printf("\n"); | |
} | |
static void edit_candidate(const char *xpath, const char *value) | |
{ | |
struct lyd_node *node; | |
printf("Editing candidate: %s\n", xpath); | |
ly_errno = 0; | |
node = lyd_new_path(candidate, ly_ctx, xpath, (void *)value, 0, | |
LYD_PATH_OPT_UPDATE); | |
if (node == NULL && ly_errno) | |
errx(1, "lyd_new_path"); | |
} | |
static void commit_candidate(void) | |
{ | |
printf("Committing candidate...\n"); | |
diff(running, candidate); | |
lyd_free_withsiblings(running); | |
running = candidate; | |
candidate = lyd_dup_withsiblings(running, 1); | |
if (candidate == NULL) | |
errx(1, "lyd_dup_withsiblings"); | |
} | |
int main(int argc, char **argv) | |
{ | |
struct lyd_node *if_dnode; | |
/* initialization */ | |
ly_ctx = ly_ctx_new(NULL, 0); | |
ly_ctx_set_searchdir(ly_ctx, "/usr/local/share/yang/"); | |
ly_ctx_load_module(ly_ctx, "frr-interface", NULL); | |
validate_config(&running); | |
validate_config(&candidate); | |
// Edit config (1) | |
edit_candidate( | |
"/frr-interface:lib/interface[name='eth0'][vrf='default']", | |
NULL); | |
validate_config(&candidate); | |
commit_candidate(); | |
// Edit config (2) | |
edit_candidate( | |
"/frr-interface:lib/interface[name='eth1'][vrf='default']", | |
NULL); | |
validate_config(&candidate); | |
commit_candidate(); | |
// Edit config (3) | |
edit_candidate( | |
"/frr-interface:lib/interface[name='eth2'][vrf='default']", | |
NULL); | |
validate_config(&candidate); | |
commit_candidate(); | |
#if 1 | |
// Edit config (4) | |
edit_candidate( | |
"/frr-interface:lib/interface[name='eth3'][vrf='default']", | |
NULL); | |
validate_config(&candidate); | |
commit_candidate(); | |
#endif | |
// Edit the running configuration directly. | |
printf("Updating the running configuration directly...\n"); | |
if_dnode = dnode_find( | |
running, | |
"/frr-interface:lib/interface[name='eth0'][vrf='default']/vrf"); | |
if (if_dnode) | |
lyd_change_leaf((struct lyd_node_leaf_list *)if_dnode, "vrf0"); | |
// Regenerate the candidate config from the running config. | |
printf("Regenerating the candidate config from the running config...\n"); | |
lyd_free_withsiblings(candidate); | |
candidate = lyd_dup_withsiblings(running, 1); | |
// Compare the two configs. They are supposed to be equal since | |
// candidate is a copy of running. But lyd_diff() outputs the following | |
// changes: | |
// LYD_DIFF_DELETED: xpath: | |
// /frr-interface:lib/interface[name='eth0'][vrf='vrf0'] | |
// LYD_DIFF_CREATED: xpath: | |
// /frr-interface:lib/interface[name='eth0'][vrf='vrf0'] | |
// | |
// This problem however doesn't happen in two scenarios: | |
// 1 - when libyang is built with -DENABLE_CACHE=OFF; | |
// 2 - when less than four interfaces are created. | |
printf("\nComparing running vs candidate (identical configs)...\n"); | |
diff(running, candidate); | |
/* cleanup */ | |
lyd_free_withsiblings(candidate); | |
lyd_free_withsiblings(running); | |
ly_ctx_destroy(ly_ctx, NULL); | |
printf("Exiting...\n"); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment