cli: fix 'copy FILE running-config' use-case

When copying to the running datastore we cannot use sr_copy_config(),
instead we must use sr_replace_config().  This fix covers both the case
of 'copy startup-config running-config' and 'copy FILE running-config'.

Fixes #1203

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
This commit is contained in:
Joachim Wiberg
2025-10-23 15:23:57 +02:00
parent ce8dc0de9c
commit 3b24fab324
3 changed files with 79 additions and 13 deletions
+2 -2
View File
@@ -6,8 +6,8 @@ bin_PROGRAMS = copy erase files
copy_SOURCES = copy.c util.c util.h
copy_CPPFLAGS = -D_DEFAULT_SOURCE -D_GNU_SOURCE
copy_CFLAGS = -W -Wall -Wextra
copy_CFLAGS += $(libite_CFLAGS) $(sysrepo_CFLAGS)
copy_LDADD = $(libite_LIBS) $(sysrepo_LIBS)
copy_CFLAGS += $(libite_CFLAGS) $(libyang_CFLAGS) $(sysrepo_CFLAGS)
copy_LDADD = $(libite_LIBS) $(libyang_LIBS) $(sysrepo_LIBS)
erase_SOURCES = erase.c util.c util.h
erase_CPPFLAGS = -D_DEFAULT_SOURCE -D_GNU_SOURCE
+3 -2
View File
@@ -15,6 +15,7 @@ AC_PROG_INSTALL
PKG_PROG_PKG_CONFIG
PKG_CHECK_MODULES([libite], [libite >= 2.5.0])
PKG_CHECK_MODULES([libyang], [libyang >= 3.0.0])
PKG_CHECK_MODULES([sysrepo], [sysrepo >= 2.2.36])
# Misc variable replacements for below Summary
@@ -55,8 +56,8 @@ cat <<EOF
System environment....: ${sysconfig_path:-${sysconfig}}
C Compiler............: $CC $CFLAGS $CPPFLAGS $LDFLAGS $LIBS
Linker................: $LD $LLDP_LDFLAGS $LLDP_BIN_LDFLAGS $LDFLAGS $LIBS
CFLAGS................: $libite_CFLAGS $sysrepo_CFLAGS
LIBS..................: $libite_LIBS $sysrepo_LIBS
CFLAGS................: $libite_CFLAGS $libyang_CFLAGS $sysrepo_CFLAGS
LIBS..................: $libite_LIBS $libyang_LIBS $sysrepo_LIBS
------------- Compiler version --------------
$($CC --version || true)
+74 -9
View File
@@ -177,6 +177,39 @@ static const char *infix_ds(const char *text, struct infix_ds **ds)
return text;
}
/*
* Load configuration from a file and replace running datastore.
* This triggers all sysrepo change callbacks, unlike sr_copy_config().
*/
static int replace_running(sr_conn_ctx_t *conn, sr_session_ctx_t *sess,
const char *file, int timeout)
{
uint32_t flags = LYD_PARSE_NO_STATE | LYD_PARSE_ONLY;
struct lyd_node *data = NULL;
const struct ly_ctx *ctx;
LY_ERR err;
int rc;
ctx = sr_acquire_context(conn);
err = lyd_parse_data_path(ctx, file, LYD_JSON, flags, 0, &data);
sr_release_context(conn);
if (err) {
fprintf(stderr, ERRMSG "unable to load %s, error %d: %s\n", file, err, ly_errmsg(ctx));
return 1;
}
/* Replace running config (triggers callbacks, takes ownership on success) */
rc = sr_replace_config(sess, NULL, data, timeout * 1000);
if (rc) {
emsg(sess, ERRMSG "unable to replace configuration, err %d: %s\n",
rc, sr_strerror(rc));
lyd_free_all(data);
}
return rc;
}
static int copy(const char *src, const char *dst, const char *remote_user)
{
@@ -229,12 +262,24 @@ static int copy(const char *src, const char *dst, const char *remote_user)
fprintf(stderr, ERRMSG "unable to open transaction to %s\n", dst);
} else {
sr_nacm_set_user(sess, user);
rc = sr_copy_config(sess, NULL, srcds->datastore, timeout * 1000);
if (rc)
emsg(sess, ERRMSG "unable to copy configuration, err %d: %s\n",
rc, sr_strerror(rc));
else
set_owner(dstds->path, user);
/*
* When copying TO running-config, use sr_replace_config()
* to trigger change callbacks. Otherwise use sr_copy_config()
* for direct datastore copy without callbacks.
*/
if (dstds->datastore == SR_DS_RUNNING && srcds->path) {
rc = replace_running(conn, sess, srcds->path, timeout);
if (!rc)
set_owner(dstds->path, user);
} else {
/* Direct copy for other datastores (no callbacks needed) */
rc = sr_copy_config(sess, NULL, srcds->datastore, timeout * 1000);
if (rc)
emsg(sess, ERRMSG "unable to copy configuration, err %d: %s\n", rc, sr_strerror(rc));
else
set_owner(dstds->path, user);
}
}
rc = sr_disconnect(conn);
@@ -320,9 +365,29 @@ static int copy(const char *src, const char *dst, const char *remote_user)
if (rc) {
fprintf(stderr, ERRMSG "failed downloading %s", src);
} else {
rc = systemf("sysrepocfg -d %s -I%s -f json", dstds->sysrepocfg, fn);
if (rc)
fprintf(stderr, ERRMSG "failed loading %s from %s", dst, src);
/* Use replace_config() for running-config (triggers callbacks), sysrepocfg for others */
if (dstds->datastore == SR_DS_RUNNING) {
if (sr_connect(SR_CONN_DEFAULT, &conn)) {
fprintf(stderr, ERRMSG "connection to datastore failed\n");
rc = 1;
} else {
sr_log_syslog("klishd", SR_LL_WRN);
if (sr_session_start(conn, SR_DS_RUNNING, &sess)) {
fprintf(stderr, ERRMSG "unable to open transaction to %s\n", dst);
rc = 1;
} else {
sr_nacm_set_user(sess, user);
rc = replace_running(conn, sess, fn, timeout);
if (!rc)
set_owner(dstds->path, user);
}
sr_disconnect(conn);
}
} else {
rc = systemf("sysrepocfg -d %s -I%s -f json", dstds->sysrepocfg, fn);
if (rc)
fprintf(stderr, ERRMSG "failed loading %s from %s", dst, src);
}
}
} else {
if (strstr(src, "://") && strstr(dst, "://")) {