Skip to content

Instantly share code, notes, and snippets.

@rwestphal
Created March 26, 2020 00:18
Show Gist options
  • Save rwestphal/2663b33c9b13a6c14ab5f0fd247c0280 to your computer and use it in GitHub Desktop.
Save rwestphal/2663b33c9b13a6c14ab5f0fd247c0280 to your computer and use it in GitHub Desktop.
#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