conf: add support for handling breaking model changes

Add metadata 'version' field to ietf-system to be able to detect .cfg
file version.  Fixes #308

Add early boot script to automatically migrate configuration files of
older version to new syntax.  Fixes #178

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
This commit is contained in:
Joachim Wiberg
2024-06-25 17:22:36 +02:00
parent 8851b279f5
commit 99117fc90f
18 changed files with 208 additions and 6 deletions
+102
View File
@@ -0,0 +1,102 @@
#!/bin/sh
# Run .cfg migration jq scripts to backup and transform older .cfg files
ident=$(basename "$0")
MIGRATIONS_DIR="/usr/share/confd/migrate"
CONFIG_FILE="/cfg/startup-config.cfg"
BACKUP_DIR="/cfg/backup"
note()
{
logger -k -p user.notice -t "$ident" "$1"
}
err()
{
logger -k -p user.err -t "$ident" "$1"
}
file_version()
{
jq -r '
if .["infix-meta:meta"] | has("version") then
.["infix-meta:meta"]["version"]
else
"0.0"
end
' "$1"
}
atoi()
{
echo "$1" | awk -F. '{print $1 * 1000 + $2}'
}
if [ ! -f "$CONFIG_FILE" ]; then
# Nothing to migrate
note "No $(basename "$CONFIG_FILE" .cfg) yet, likely factory reset."
exit 0
fi
cfg_version=$(file_version "$CONFIG_FILE")
current_version=$(atoi "$cfg_version")
# Find the latest version by examining the highest numbered directory
sys_version=$(find "$MIGRATIONS_DIR" -mindepth 1 -maxdepth 1 -type d | sort -V | tail -n1 | xargs -n1 basename)
latest_version=$(atoi "$sys_version")
# Check for downgrade
if [ "$current_version" -gt "$latest_version" ]; then
err "Configuration file $CONFIG_FILE version ($cfg_version) is newer than the latest supported version ($sys_version). Exiting."
exit 1
fi
# If the current version is already the latest, exit the script
if [ "$current_version" -eq "$latest_version" ]; then
note "Configuration is already at the latest version ($sys_version). No migration needed."
exit 0
fi
note "Configuration file $CONFIG_FILE is of version $cfg_version, migrating ..."
# Create the backup directory if it doesn't exist
mkdir -p "$BACKUP_DIR"
# Create a backup of the current configuration file
nm=$(basename "$CONFIG_FILE" .cfg)
BACKUP_FILE="$BACKUP_DIR/${nm}-${cfg_version}.cfg"
if cp "$CONFIG_FILE" "$BACKUP_FILE"; then
note "Backup created: $BACKUP_FILE"
else
err "Failed creating backup: $BACKUP_FILE"
exit 1
fi
# Apply the scripts for each version directory in sequence
for version_dir in $(find "$MIGRATIONS_DIR" -mindepth 1 -maxdepth 1 -type d | sort -V); do
dir=$(basename "$version_dir")
version=$(atoi "$dir")
# Step by step upgrade file to latest version
if [ "$current_version" -lt "$version" ]; then
note "Applying migrations for version $dir ..."
# Apply all scripts in the version directory in order
for script in $(find "$version_dir" -type f -name '*.sh' | sort -V); do
note "Calling $script for $CONFIG_FILE ..."
sh "$script" "$CONFIG_FILE"
done
# File now at $version ...
current_version="$version"
fi
done
# Update the JSON file to the latest version
if jq --arg version "$sys_version" '.["infix-meta:meta"]["infix-meta:version"] = $version' "$CONFIG_FILE" \
> "${CONFIG_FILE}.tmp" && mv "${CONFIG_FILE}.tmp" "$CONFIG_FILE"; then
note "Configuration updated to version $sys_version."
else
err "Failed updating configuration to version $sys_version!"
exit 1
fi
+1 -1
View File
@@ -12,7 +12,7 @@ CONFD_LICENSE_FILES = LICENSE
CONFD_REDISTRIBUTE = NO
CONFD_DEPENDENCIES = host-sysrepo sysrepo netopeer2 jansson libite sysrepo libsrx libglib2
CONFD_AUTORECONF = YES
CONFD_CONF_OPTS += --disable-silent-rules
CONFD_SYSREPO_SHM_PREFIX = sr_buildroot$(subst /,_,$(CONFIG_DIR))_confd
define CONFD_CONF_ENV
+1 -1
View File
@@ -1,3 +1,3 @@
pkglibexec_SCRIPTS = bootstrap error load gen-service gen-hostkeys \
gen-hostname gen-interfaces gen-motd gen-hardware
gen-hostname gen-interfaces gen-motd gen-hardware gen-version
sbin_SCRIPTS = dagger
+2
View File
@@ -101,6 +101,7 @@ factory()
gen-hardware >"$FACTORY_D/20-hardware.json"
# shellcheck disable=SC2086
gen-interfaces $GEN_IFACE_OPTS >"$FACTORY_D/20-interfaces.json"
gen-version >"$FACTORY_D/20-version.json"
[ -s "$FACTORY_D/20-hostkey.json" ] || gen-hostkeys >"$FACTORY_D/20-hostkey.json"
@@ -120,6 +121,7 @@ failure()
gen-hostname "$FAIL_HOSTNAME" >"$FAILURE_D/20-hostname.json"
gen-interfaces >"$FAILURE_D/20-interfaces.json"
gen-version >"$FACTORY_D/20-version.json"
[ -s "$FAILURE_D/20-hostkey.json" ] || gen-hostkeys >"$FAILURE_D/20-hostkey.json"
+9
View File
@@ -0,0 +1,9 @@
#!/bin/sh
cat <<EOF
{
"infix-meta:meta": {
"infix-meta:version": "@VERSION@"
}
}
EOF
+5 -1
View File
@@ -1,14 +1,18 @@
AC_PREREQ(2.61)
AC_INIT([confd], [1.0.0], [https://github.com/kernelkit/infix/issues])
# confd version is same as system YANG model version, step on breaking changes
AC_INIT([confd], [1.0], [https://github.com/kernelkit/infix/issues])
AM_INIT_AUTOMAKE(1.11 foreign subdir-objects)
AM_SILENT_RULES(yes)
AC_CONFIG_FILES([
Makefile
bin/Makefile
bin/gen-version
share/Makefile
share/factory.d/Makefile
share/failure.d/Makefile
share/migrate/Makefile
share/migrate/1.0/Makefile
src/Makefile
yang/Makefile
])
+1
View File
@@ -53,6 +53,7 @@ MODULES=(
"infix-routing@2024-03-06.yang"
"ieee802-dot1ab-lldp@2022-03-15.yang"
"infix-lldp@2023-08-23.yang"
"infix-meta@2024-06-19.yang"
"infix-dhcp-client@2024-04-12.yang"
"infix-system@2024-06-15.yang"
"infix-services@2024-05-30.yang"
+1 -1
View File
@@ -1,2 +1,2 @@
SUBDIRS = factory.d failure.d
SUBDIRS = factory.d failure.d migrate
dist_pkgdata_DATA = README
@@ -0,0 +1,9 @@
#!/bin/sh
# Migrate infix-shell-type:bash -> infix-system:bash
file=$1
temp=$1.tmp
jq '.["ietf-system:system"].authentication.user |= map(.["infix-system:shell"] |= gsub("infix-shell-type:"; ""))' "$file" > "$temp"
mv "$temp" "$file"
+3
View File
@@ -0,0 +1,3 @@
migratedir = $(pkgdatadir)/migrate/1.0
dist_migrate_DATA = 10-infix-shell-type.sh
+2
View File
@@ -0,0 +1,2 @@
SUBDIRS = 1.0
migratedir = $(pkgdatadir)/migrate
+2 -1
View File
@@ -29,8 +29,9 @@ confd_plugin_la_SOURCES = \
ietf-factory-default.c \
ietf-routing.c \
infix-dhcp.c \
infix-services.c \
infix-factory.c \
infix-meta.c \
infix-services.c \
infix-system-software.c \
ietf-hardware.c
+3
View File
@@ -140,6 +140,9 @@ int sr_plugin_init_cb(sr_session_ctx_t *session, void **priv)
if (rc)
goto err;
rc = ietf_routing_init(&confd);
if (rc)
goto err;
rc = infix_meta_init(&confd);
if (rc)
goto err;
rc = infix_system_sw_init(&confd);
+3
View File
@@ -191,6 +191,9 @@ int ietf_routing_init(struct confd *confd);
/* infix-factory.c */
int infix_factory_init(struct confd *confd);
/* infix-factory.c */
int infix_meta_init(struct confd *confd);
/* infix-system-software.c */
int infix_system_sw_init(struct confd *confd);
+1 -1
View File
@@ -1666,7 +1666,7 @@ static int hostname_cb(sr_session_ctx_t *session, uint32_t sub_id, const char *m
int ietf_system_init(struct confd *confd)
{
int rc = 0;
int rc;
os_init();
+40
View File
@@ -0,0 +1,40 @@
/* SPDX-License-Identifier: BSD-3-Clause */
#include "core.h"
#define META_XPATH "/infix-meta:meta/version"
static int set_version(sr_session_ctx_t *session)
{
int rc;
rc = sr_set_item_str(session, META_XPATH, VERSION, NULL, 0);
if (rc) {
ERROR("Failed setting .cfg version %s: %d", VERSION, rc);
return rc;
}
return SR_ERR_OK;
}
static int change_cb(sr_session_ctx_t *session, uint32_t sub_id, const char *module,
const char *xpath, sr_event_t event, unsigned request_id, void *priv)
{
if (event == SR_EV_UPDATE)
return set_version(session);
return SR_ERR_OK;
}
int infix_meta_init(struct confd *confd)
{
int rc;
REGISTER_CHANGE(confd->session, "infix-meta", META_XPATH, SR_SUBSCR_UPDATE,
change_cb, confd, &confd->sub);
return SR_ERR_OK;
fail:
ERROR("%s(): failed. %s", __func__, sr_strerror(rc));
return rc;
}
@@ -25,6 +25,7 @@ MODULES=(
"ieee802-dot1ab-lldp@2022-03-15.yang"
"infix-lldp@2023-08-23.yang"
"infix-dhcp-client@2024-04-12.yang"
"infix-meta@2024-06-19.yang"
"infix-system@2024-06-15.yang"
"infix-services@2024-05-30.yang"
"ieee802-ethernet-interface@2019-06-21.yang"
+22
View File
@@ -0,0 +1,22 @@
module infix-meta {
yang-version 1.1;
namespace "urn:infix:meta:ns:yang:1.0";
prefix infix-meta;
organization "KernelKit";
contact "kernelkit@googlegroups.com";
description "Infix metadata.";
revision 2024-06-19 {
description "Initial revision.";
reference "internal";
}
container meta {
leaf version {
status obsolete; // Ensure frontends don't show this, used for migration.
description "Configuration file format version, automatically generated.";
type string;
}
}
}