mirror of
https://github.com/kernelkit/infix.git
synced 2026-07-23 09:23:02 +02:00
confd: Add a sysrepo interface to install RAUC bundles
This commit is contained in:
committed by
Joachim Wiberg
parent
6ca6a4e2f8
commit
4fb09267cf
@@ -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])
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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_ */
|
||||
|
||||
@@ -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, §ion))
|
||||
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, §ion))
|
||||
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, §ion))
|
||||
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.";
|
||||
|
||||
Reference in New Issue
Block a user