confd: Add a sysrepo interface to install RAUC bundles

This commit is contained in:
Tobias Waldekranz
2023-06-30 15:33:10 +02:00
committed by Joachim Wiberg
parent 6ca6a4e2f8
commit 4fb09267cf
8 changed files with 541 additions and 0 deletions
+1
View File
@@ -35,6 +35,7 @@ AS_IF([test "x$with_login_shell" != "xno"], [
PKG_PROG_PKG_CONFIG
PKG_CHECK_MODULES([augeas], [augeas >= 1.12.0])
PKG_CHECK_MODULES([glib], [glib-2.0 >= 2.50 gio-2.0 gio-unix-2.0])
PKG_CHECK_MODULES([jansson], [jansson >= 2.0.0])
PKG_CHECK_MODULES([libite], [libite >= 2.5.0])
PKG_CHECK_MODULES([sysrepo], [sysrepo >= 2.2.36])
+21
View File
@@ -8,6 +8,7 @@ confd_plugin_la_LDFLAGS = -module -avoid-version -shared
confd_plugin_la_CFLAGS = \
$(augeas_CFLAGS) \
$(glib_CFLAGS) \
$(jansson_CFLAGS) \
$(libite_CFLAGS) \
$(sysrepo_CFLAGS) \
@@ -15,15 +16,35 @@ confd_plugin_la_CFLAGS = \
confd_plugin_la_LIBADD = \
$(augeas_LIBS) \
$(glib_LIBS) \
$(jansson_LIBS) \
$(libite_LIBS) \
$(sysrepo_LIBS)
rauc_installer_sources = \
rauc-installer.c \
rauc-installer.h
BUILT_SOURCES = $(rauc_installer_sources)
EXTRA_DIST = ../lib/de.pengutronix.rauc.Installer.xml
$(rauc_installer_sources): ../lib/de.pengutronix.rauc.Installer.xml
$(AM_V_GEN) gdbus-codegen \
--generate-c-code rauc-installer \
--c-generate-autocleanup all \
--c-namespace Rauc \
--interface-prefix de.pengutronix.rauc. \
$<
nodist_confd_plugin_la_SOURCES = \
$(rauc_installer_sources)
confd_plugin_la_SOURCES = \
ietf-interfaces.c \
ietf-system.c \
infix-dhcp.c \
infix-factory.c \
infix-system-software.c \
\
../lib/common.h \
../lib/helpers.c ../lib/helpers.h \
+3
View File
@@ -104,6 +104,9 @@ int sr_plugin_init_cb(sr_session_ctx_t *session, void **priv)
if (rc)
goto err;
rc = infix_factory_init(&confd);
if (rc)
goto err;
rc = infix_system_sw_init(&confd);
if (rc)
goto err;
+3
View File
@@ -134,4 +134,7 @@ int infix_dhcp_init(struct confd *confd);
/* infix-factory.c */
int infix_factory_init(struct confd *confd);
/* infix-system-software.c */
int infix_system_sw_init(struct confd *confd);
#endif /* CONFD_CORE_H_ */
+256
View File
@@ -0,0 +1,256 @@
/* SPDX-License-Identifier: BSD-3-Clause */
#include "core.h"
#include "../lib/common.h"
#include "../lib/lyx.h"
#include "rauc-installer.h"
#include <sysrepo/error_format.h>
#define SW_STATE_PATH_ "/ietf-system:system-state/infix-system:software"
static const struct lys_module *infix_system;
static RaucInstaller *infix_system_sw_new_rauc(void)
{
RaucInstaller *rauc;
GError *raucerr = NULL;
rauc = rauc_installer_proxy_new_for_bus_sync(G_BUS_TYPE_SYSTEM, G_DBUS_PROXY_FLAGS_NONE,
"de.pengutronix.rauc", "/", NULL, &raucerr);
if (raucerr) {
ERROR("Unable to connect to RAUC: %s", raucerr->message);
g_error_free(raucerr);
return NULL;
}
return rauc;
}
static sr_error_t infix_system_sw_state_fill_installer(struct lyd_node *inst,
RaucInstaller *rauc)
{
struct lyd_node *progress;
GVariant *props, *val;
const char *strval;
strval = rauc_installer_get_operation(rauc);
if (strval && strval[0] &&
lyd_new_term(inst, NULL, "operation", strval, 0, NULL))
return SR_ERR_INTERNAL;
strval = rauc_installer_get_last_error(rauc);
if (strval && strval[0] &&
lyd_new_term(inst, NULL, "last-error", strval, 0, NULL))
return SR_ERR_INTERNAL;
props = rauc_installer_get_progress(rauc);
if (props) {
if (lyd_new_inner(inst, NULL, "progress", 0, &progress))
return SR_ERR_INTERNAL;
g_variant_get(props, "(@isi)", &val, &strval, NULL);
if (lyd_new_term(progress, NULL, "percentage",
g_variant_print(val, FALSE), 0, NULL))
return SR_ERR_INTERNAL;
if (strval && strval[0] &&
lyd_new_term(progress, NULL, "message", strval, 0, NULL))
return SR_ERR_INTERNAL;
}
return SR_ERR_OK;
}
static sr_error_t infix_system_sw_state_fill_slot(struct lyd_node *slot,
GVariant *props)
{
static const char *strprops[] = {
"bootname",
"class",
"state",
"sha256",
NULL
};
struct lyd_node *section;
const char **strprop;
const char *strval;
GVariant *val;
for (strprop = strprops; *strprop; strprop++) {
if (g_variant_lookup(props, *strprop, "s", &strval) &&
lyd_new_term(slot, NULL, *strprop, strval, 0, NULL))
return SR_ERR_INTERNAL;
}
if (g_variant_lookup(props, "size", "@t", &val) &&
lyd_new_term(slot, NULL, "size", g_variant_print(val, FALSE), 0, NULL))
return SR_ERR_INTERNAL;
if (lyd_new_inner(slot, NULL, "bundle", 0, &section))
return SR_ERR_INTERNAL;
if (g_variant_lookup(props, "bundle.compatible", "s", &strval) &&
lyd_new_term(section, NULL, "compatible", strval, 0, NULL))
return SR_ERR_INTERNAL;
if (g_variant_lookup(props, "bundle.version", "s", &strval) &&
lyd_new_term(section, NULL, "version", strval, 0, NULL))
return SR_ERR_INTERNAL;
if (lyd_new_inner(slot, NULL, "installed", 0, &section))
return SR_ERR_INTERNAL;
if (g_variant_lookup(props, "installed.timestamp", "s", &strval) &&
lyd_new_term(section, NULL, "datetime", strval, 0, NULL))
return SR_ERR_INTERNAL;
if (g_variant_lookup(props, "installed.count", "@u", &val) &&
lyd_new_term(section, NULL, "count", g_variant_print(val, FALSE), 0, NULL))
return SR_ERR_INTERNAL;
if (lyd_new_inner(slot, NULL, "activated", 0, &section))
return SR_ERR_INTERNAL;
if (g_variant_lookup(props, "activated.timestamp", "s", &strval) &&
lyd_new_term(section, NULL, "datetime", strval, 0, NULL))
return SR_ERR_INTERNAL;
if (g_variant_lookup(props, "activated.count", "@u", &val) &&
lyd_new_term(section, NULL, "count", g_variant_print(val, FALSE), 0, NULL))
return SR_ERR_INTERNAL;
return SR_ERR_OK;
}
static sr_error_t infix_system_sw_state_fill(struct lyd_node *sw,
RaucInstaller *rauc)
{
struct lyd_node *slot, *inst;
GVariant *slots, *props;
GError *raucerr = NULL;
GVariantIter slotiter;
sr_error_t srerr;
char *slotname;
const char *val;
val = rauc_installer_get_compatible(rauc);
if (val && val[0] && lyd_new_term(sw, NULL, "compatible", val, 0, NULL))
return SR_ERR_INTERNAL;
val = rauc_installer_get_variant(rauc);
if (val && val[0] && lyd_new_term(sw, NULL, "variant", val, 0, NULL))
return SR_ERR_INTERNAL;
val = rauc_installer_get_boot_slot(rauc);
if (val && val[0] && lyd_new_term(sw, NULL, "booted", val, 0, NULL))
return SR_ERR_INTERNAL;
if (lyd_new_inner(sw, NULL, "installer", 0, &inst))
return SR_ERR_INTERNAL;
srerr = infix_system_sw_state_fill_installer(inst, rauc);
if (srerr)
return srerr;
if (!rauc_installer_call_get_slot_status_sync(rauc, &slots, NULL, &raucerr)) {
/* Slot status is not available while an installation
* is in progress, so we don't consider that an error.
*/
g_error_free(raucerr);
return SR_ERR_OK;
}
for (g_variant_iter_init(&slotiter, slots);
g_variant_iter_next(&slotiter, "(s@a{sv})", &slotname, &props);) {
if (lyd_new_list(sw, NULL, "slot", 0, &slot, slotname))
return SR_ERR_INTERNAL;
srerr = infix_system_sw_state_fill_slot(slot, props);
if (srerr)
break;
}
return srerr;
}
static int infix_system_sw_state(sr_session_ctx_t *session, uint32_t sub_id,
const char *module, const char *path,
const char *request_path, uint32_t request_id,
struct lyd_node **parent, void *priv)
{
sr_error_t srerr = SR_ERR_INTERNAL;
RaucInstaller *rauc;
struct lyd_node *sw;
DEBUG("");
rauc = infix_system_sw_new_rauc();
if (!rauc)
return SR_ERR_INTERNAL;
if (lyd_new_inner(*parent, infix_system, "software", 0, &sw))
goto out;
srerr = infix_system_sw_state_fill(sw, rauc);
out:
g_object_unref(rauc);
return srerr;
}
static int infix_system_sw_install(sr_session_ctx_t *session, uint32_t sub_id,
const char *path, const sr_val_t *input,
const size_t input_cnt, sr_event_t event,
unsigned request_id, sr_val_t **output,
size_t *output_cnt, void *priv)
{
char *url = input->data.string_val;
sr_error_t srerr = SR_ERR_OK;
GError *raucerr = NULL;
RaucInstaller *rauc;
DEBUG("url:%s", url);
rauc = infix_system_sw_new_rauc();
if (!rauc)
return SR_ERR_INTERNAL;
rauc_installer_call_install_sync(rauc, url, NULL, &raucerr);
if (raucerr) {
sr_session_set_netconf_error(session, "application", "operation-failed",
NULL, NULL, raucerr->message, 0);
g_error_free(raucerr);
srerr = SR_ERR_OPERATION_FAILED;
}
g_object_unref(rauc);
return srerr;
}
int infix_system_sw_init(struct confd *confd)
{
const struct ly_ctx *ly;
int rc = 0;
ly = sr_acquire_context(sr_session_get_connection(confd->session));
infix_system = ly_ctx_get_module_implemented(ly, "infix-system");
sr_release_context(sr_session_get_connection(confd->session));
if (!infix_system) {
ERROR("infix-system module not found");
rc = -ENOENT;
goto fail;
}
REGISTER_OPER(confd->session, "ietf-system", SW_STATE_PATH_,
infix_system_sw_state, NULL, 0, &confd->sub);
REGISTER_RPC(confd->session, "/infix-system:install-bundle",
infix_system_sw_install, NULL, &confd->sub);
fail:
return rc;
}
@@ -0,0 +1,108 @@
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "https://specifications.freedesktop.org/dbus/introspect-latest.dtd">
<!-- License note: This file is considered data and thus no license is needed for any usage. -->
<node>
<interface name="de.pengutronix.rauc.Installer">
<!--
Install:
@source: Path to bundle to be installed
Triggers an installation.
-->
<method name="Install">
<arg name="source" type="s" direction="in"/>
</method>
<!--
InstallBundle:
@source: Path to bundle to be installed
@args: Additional arguments to pass
Triggers an installation.
-->
<method name="InstallBundle">
<arg name="source" type="s" direction="in"/>
<arg name="args" type="a{sv}" direction="in"/>
<annotation name="org.qtproject.QtDBus.QtTypeName.In1" value="QVariantMap"/>
</method>
<!--
Info: D-Bus variant of rauc info <bundle>
@bundle: full path to the queried bundle.
@compatible: the compatible string from the bundle
@version: the version string from the bundle
-->
<method name="Info">
<arg name="bundle" type="s" direction="in" />
<arg name="compatible" type="s" direction="out" />
<arg name="version" type="s" direction="out" />
</method>
<!--
Mark:
@state: operation to perform (one out of "good", "bad" or "active")
@slot_identifier: can be "booted", "other" or <SLOT_NAME> (e.g.
"rootfs.1")
@slot_name: name of the slot which has ultimately been marked
@message: message describing what has been done successfully (e.g.
"activated slot rootfs.0")
Keeps a slot bootable (state == "good"), makes it unbootable (state ==
"bad") or explicitly activates it for the next boot (state == "active").
-->
<method name="Mark">
<arg name="state" type="s" direction="in"/>
<arg name="slot_identifier" type="s" direction="in"/>
<arg name="slot_name" type="s" direction="out"/>
<arg name="message" type="s" direction="out"/>
</method>
<!--
GetSlotStatus:
@slot_status_array: array of (slotname, dict) tuples with each
dictionary representing the status of the corresponding slot
Access method to get all slots' status.
-->
<method name="GetSlotStatus">
<arg name="slot_status_array" type="a(sa{sv})" direction="out"/>
<annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="RaucSlotStatusArray"/>
</method>
<!-- Operation: Represents the current (global) operation rauc performs -->
<property name="Operation" type="s" access="read"/>
<!-- LastError: Holds a message describing the last error that occurred -->
<property name="LastError" type="s" access="read"/>
<!-- Progress: Provides installation progress information in the form
(percentage, message, nesting depth) -->
<property name="Progress" type="(isi)" access="read">
<annotation name="org.qtproject.QtDBus.QtTypeName" value="RaucProgress"/>
</property>
<!-- Compatible: Represents the system's compatible -->
<property name="Compatible" type="s" access="read"/>
<!-- Variant: Represents the system's variant -->
<property name="Variant" type="s" access="read"/>
<!-- BootSlot: Represents the slot booted from -->
<property name="BootSlot" type="s" access="read"/>
<!--
GetPrimary:
@slot_status_array: array of (slotname, dict) tuples with each
dictionary representing the status of the corresponding slot
Call the bootloader interface to get the primary boot slot
-->
<method name="GetPrimary">
<arg name="primary" type="s" direction="out"/>
</method>
<!--
Completed:
@result: return code (0 for success)
This signal is emitted when installation completed, either
successfully or with an error.
-->
<signal name="Completed">
<arg name="result" type="i"/>
</signal>
</interface>
</node>
@@ -0,0 +1,147 @@
submodule infix-system-software {
yang-version 1.1;
belongs-to infix-system {
prefix ixsys;
}
import ietf-yang-types {
prefix yang;
}
import ietf-netconf-acm {
prefix nacm;
}
import ietf-system {
prefix sys;
}
organization "KernelKit";
contact "kernelkit@googlegroups.com";
description "Software status and upgrade.";
revision 2023-06-27 {
description "Initial revision.";
reference "internal";
}
grouping rauc-stage-log {
leaf datetime {
type yang:date-and-time;
}
leaf count {
type uint32;
}
}
grouping installer-state {
leaf operation {
type string;
}
container progress {
leaf percentage {
type uint8 {
range "0 .. 100";
}
}
leaf message {
type string;
}
}
leaf last-error {
type string;
}
}
augment "/sys:system-state" {
container software {
leaf compatible {
type string;
}
leaf variant {
type string;
}
leaf booted {
type string;
}
container installer {
uses installer-state;
notification state-changed {
uses installer-state;
}
}
list slot {
key "name";
leaf name {
type string;
}
leaf bootname {
type string;
}
leaf class {
type string;
}
leaf state {
type string;
}
container bundle {
leaf compatible {
type string;
}
leaf version {
type string;
}
}
leaf size {
type uint64;
}
leaf sha256 {
type string {
pattern '[a-fA-F0-9]{64}';
}
}
container installed {
uses rauc-stage-log;
}
container activated {
uses rauc-stage-log;
}
}
}
}
rpc install-bundle {
nacm:default-deny-all;
description
"Upgrade the system's software by installing the specified bundle.";
input {
leaf url {
type string;
mandatory true;
description
"The location of the software bundle, specified as a Uniform
Resource Locator (URL).
Supported protocols include FTP, HTTP(S) and SCP.";
}
}
}
}
@@ -7,6 +7,8 @@ module infix-system {
prefix sys;
}
include infix-system-software;
organization "KernelKit";
contact "kernelkit@googlegroups.com";
description "Infix augments and deviations to ietf-system.";