mirror of
https://git.proxmox.com/git/fwupd
synced 2025-05-30 14:51:04 +00:00

This allows the Redfish plugin to "just work" when there is no username or password in the SMBIOS data. Using KCS we can create an admin account from the host OS and then automatically enumerate devices.
369 lines
11 KiB
C
369 lines
11 KiB
C
/*
|
|
* Copyright (C) 2017 Richard Hughes <richard@hughsie.com>
|
|
*
|
|
* SPDX-License-Identifier: LGPL-2.1+
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <fwupd.h>
|
|
|
|
#include "fu-context-private.h"
|
|
#include "fu-device-private.h"
|
|
#ifdef HAVE_LINUX_IPMI_H
|
|
#include "fu-ipmi-device.h"
|
|
#endif
|
|
#include "fu-plugin-private.h"
|
|
#include "fu-redfish-common.h"
|
|
#include "fu-redfish-network.h"
|
|
|
|
typedef struct {
|
|
FuPlugin *plugin;
|
|
} FuTest;
|
|
|
|
static void
|
|
fu_test_self_init(FuTest *self)
|
|
{
|
|
gboolean ret;
|
|
g_autoptr(FuContext) ctx = fu_context_new();
|
|
g_autoptr(GError) error = NULL;
|
|
g_autofree gchar *pluginfn = NULL;
|
|
|
|
ret = fu_context_load_quirks(ctx,
|
|
FU_QUIRKS_LOAD_FLAG_NO_CACHE | FU_QUIRKS_LOAD_FLAG_NO_VERIFY,
|
|
&error);
|
|
g_assert_no_error(error);
|
|
g_assert(ret);
|
|
|
|
self->plugin = fu_plugin_new(ctx);
|
|
pluginfn = g_build_filename(PLUGINBUILDDIR, "libfu_plugin_redfish." G_MODULE_SUFFIX, NULL);
|
|
ret = fu_plugin_open(self->plugin, pluginfn, &error);
|
|
g_assert_no_error(error);
|
|
g_assert(ret);
|
|
ret = fu_plugin_runner_startup(self->plugin, &error);
|
|
if (g_error_matches(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE)) {
|
|
g_test_skip("no redfish.py running");
|
|
return;
|
|
}
|
|
g_assert_no_error(error);
|
|
g_assert(ret);
|
|
ret = fu_plugin_runner_coldplug(self->plugin, &error);
|
|
g_assert_no_error(error);
|
|
g_assert(ret);
|
|
}
|
|
|
|
static void
|
|
fu_test_redfish_ipmi_func(void)
|
|
{
|
|
#ifdef HAVE_LINUX_IPMI_H
|
|
gboolean ret;
|
|
g_autoptr(FuIpmiDevice) device = fu_ipmi_device_new(NULL);
|
|
g_autoptr(FuDeviceLocker) locker = NULL;
|
|
g_autoptr(GError) error = NULL;
|
|
g_autofree gchar *username = NULL;
|
|
g_autofree gchar *str = NULL;
|
|
|
|
/* sanity check */
|
|
if (!g_file_test("/dev/ipmi0", G_FILE_TEST_EXISTS)) {
|
|
g_test_skip("no IPMI hardware");
|
|
return;
|
|
}
|
|
|
|
/* create device */
|
|
locker = fu_device_locker_new(device, &error);
|
|
g_assert_no_error(error);
|
|
g_assert_nonnull(locker);
|
|
str = fu_device_to_string(FU_DEVICE(device));
|
|
g_debug("%s", str);
|
|
|
|
/* add user that can do redfish commands */
|
|
if (g_getenv("FWUPD_REDFISH_SELF_TEST") == NULL) {
|
|
g_test_skip("not doing destructive tests");
|
|
return;
|
|
}
|
|
ret = fu_ipmi_device_set_user_name(device, 0x04, "fwupd", &error);
|
|
g_assert_no_error(error);
|
|
g_assert_true(ret);
|
|
username = fu_ipmi_device_get_user_password(device, 0x04, &error);
|
|
g_assert_no_error(error);
|
|
g_assert_nonnull(username);
|
|
g_debug("username=%s", username);
|
|
ret = fu_ipmi_device_set_user_enable(device, 0x04, TRUE, &error);
|
|
g_assert_no_error(error);
|
|
g_assert_true(ret);
|
|
ret = fu_ipmi_device_set_user_priv(device, 0x04, 0x4, 1, &error);
|
|
g_assert_no_error(error);
|
|
g_assert_true(ret);
|
|
ret = fu_ipmi_device_set_user_password(device, 0x04, "Passw0rd123", &error);
|
|
g_assert_no_error(error);
|
|
g_assert_true(ret);
|
|
#else
|
|
g_test_skip("no linux/ipmi.h, so skipping");
|
|
#endif
|
|
}
|
|
|
|
static void
|
|
fu_test_redfish_common_func(void)
|
|
{
|
|
const guint8 buf[16] = {0x00,
|
|
0x01,
|
|
0x02,
|
|
0x03,
|
|
0x04,
|
|
0x05,
|
|
0x06,
|
|
0x07,
|
|
0x08,
|
|
0x09,
|
|
0x0a,
|
|
0x0b,
|
|
0x0c,
|
|
0x0d,
|
|
0x0e,
|
|
0x0f};
|
|
g_autofree gchar *ipv4 = NULL;
|
|
g_autofree gchar *ipv6 = NULL;
|
|
g_autofree gchar *maca = NULL;
|
|
|
|
ipv4 = fu_redfish_common_buffer_to_ipv4(buf);
|
|
g_assert_cmpstr(ipv4, ==, "0.1.2.3");
|
|
ipv6 = fu_redfish_common_buffer_to_ipv6(buf);
|
|
g_assert_cmpstr(ipv6, ==, "00010203:04050607:08090a0b:0c0d0e0f");
|
|
maca = fu_redfish_common_buffer_to_mac(buf);
|
|
g_assert_cmpstr(maca, ==, "00:01:02:03:04:05");
|
|
}
|
|
|
|
static void
|
|
fu_test_redfish_common_version_func(void)
|
|
{
|
|
struct {
|
|
const gchar *in;
|
|
const gchar *op;
|
|
} strs[] = {{"1.2.3", "1.2.3"},
|
|
{"P50 v1.2.3 PROD", "1.2.3"},
|
|
{"P50 1.2.3 DEV", "1.2.3"},
|
|
{NULL, NULL}};
|
|
for (guint i = 0; strs[i].in != NULL; i++) {
|
|
g_autofree gchar *tmp = fu_redfish_common_fix_version(strs[i].in);
|
|
g_assert_cmpstr(tmp, ==, strs[i].op);
|
|
}
|
|
}
|
|
|
|
static void
|
|
fu_test_redfish_common_lenovo_func(void)
|
|
{
|
|
struct {
|
|
const gchar *in;
|
|
gboolean ret;
|
|
const gchar *build;
|
|
const gchar *version;
|
|
} values[] = {{"11A-1.02", TRUE, "11A", "1.02"},
|
|
{"11A-0.00", TRUE, "11A", "0.00"},
|
|
{"99Z-9.99", TRUE, "99Z", "9.99"},
|
|
{"9-9-9.99", FALSE, NULL, NULL},
|
|
{"999-9.99", FALSE, NULL, NULL},
|
|
{"ACB-9.99", FALSE, NULL, NULL},
|
|
{NULL, FALSE, NULL, NULL}};
|
|
for (guint i = 0; values[i].in != NULL; i++) {
|
|
gboolean ret;
|
|
g_autofree gchar *build = NULL;
|
|
g_autofree gchar *version = NULL;
|
|
ret = fu_redfish_common_parse_version_lenovo(values[i].in, &build, &version, NULL);
|
|
g_assert_cmpint(ret, ==, values[i].ret);
|
|
g_assert_cmpstr(build, ==, values[i].build);
|
|
g_assert_cmpstr(version, ==, values[i].version);
|
|
}
|
|
}
|
|
|
|
static void
|
|
fu_test_redfish_network_mac_addr_func(void)
|
|
{
|
|
FuRedfishNetworkDeviceState state = FU_REDFISH_NETWORK_DEVICE_STATE_UNKNOWN;
|
|
gboolean ret;
|
|
g_autofree gchar *ip_addr = NULL;
|
|
g_autoptr(FuRedfishNetworkDevice) device = NULL;
|
|
g_autoptr(GError) error = NULL;
|
|
|
|
device = fu_redfish_network_device_for_mac_addr("00:13:F7:29:C2:D8", &error);
|
|
if (device == NULL && g_error_matches(error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND)) {
|
|
g_test_skip("no hardware");
|
|
return;
|
|
}
|
|
if (device == NULL && g_error_matches(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED)) {
|
|
g_autofree gchar *str = g_strdup_printf("not supported: %s", error->message);
|
|
g_test_skip(str);
|
|
return;
|
|
}
|
|
g_assert_no_error(error);
|
|
g_assert_nonnull(device);
|
|
ret = fu_redfish_network_device_get_state(device, &state, &error);
|
|
g_assert_no_error(error);
|
|
g_assert_true(ret);
|
|
if (state == FU_REDFISH_NETWORK_DEVICE_STATE_DISCONNECTED) {
|
|
ret = fu_redfish_network_device_connect(device, &error);
|
|
g_assert_no_error(error);
|
|
g_assert_true(ret);
|
|
}
|
|
ip_addr = fu_redfish_network_device_get_address(device, &error);
|
|
g_assert_no_error(error);
|
|
g_assert_nonnull(ip_addr);
|
|
}
|
|
|
|
static void
|
|
fu_test_redfish_network_vid_pid_func(void)
|
|
{
|
|
g_autofree gchar *ip_addr = NULL;
|
|
g_autoptr(FuRedfishNetworkDevice) device = NULL;
|
|
g_autoptr(GError) error = NULL;
|
|
|
|
device = fu_redfish_network_device_for_vid_pid(0x0707, 0x0201, &error);
|
|
if (device == NULL && g_error_matches(error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND)) {
|
|
g_test_skip("no hardware");
|
|
return;
|
|
}
|
|
if (device == NULL && g_error_matches(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED)) {
|
|
g_autofree gchar *str = g_strdup_printf("not supported: %s", error->message);
|
|
g_test_skip(str);
|
|
return;
|
|
}
|
|
g_assert_no_error(error);
|
|
g_assert_nonnull(device);
|
|
ip_addr = fu_redfish_network_device_get_address(device, &error);
|
|
g_assert_no_error(error);
|
|
g_assert_nonnull(ip_addr);
|
|
}
|
|
|
|
static void
|
|
fu_test_redfish_devices_func(gconstpointer user_data)
|
|
{
|
|
FuDevice *dev;
|
|
FuTest *self = (FuTest *)user_data;
|
|
GPtrArray *devices;
|
|
g_autofree gchar *devstr0 = NULL;
|
|
g_autofree gchar *devstr1 = NULL;
|
|
|
|
devices = fu_plugin_get_devices(self->plugin);
|
|
g_assert_nonnull(devices);
|
|
if (devices->len == 0) {
|
|
g_test_skip("no redfish support");
|
|
return;
|
|
}
|
|
g_assert_cmpint(devices->len, ==, 2);
|
|
|
|
/* BMC */
|
|
dev = g_ptr_array_index(devices, 1);
|
|
devstr0 = fu_device_to_string(dev);
|
|
g_debug("%s", devstr0);
|
|
g_assert_cmpstr(fu_device_get_id(dev), ==, "62c1cd95692c5225826cf8568a460427ea3b1827");
|
|
g_assert_cmpstr(fu_device_get_name(dev), ==, "BMC Firmware");
|
|
g_assert_cmpstr(fu_device_get_vendor(dev), ==, "Lenovo");
|
|
g_assert_cmpstr(fu_device_get_version(dev), ==, "1.02");
|
|
g_assert_cmpstr(fu_device_get_version_lowest(dev), ==, "0.12");
|
|
g_assert_cmpint(fu_device_get_version_format(dev), ==, FWUPD_VERSION_FORMAT_PAIR);
|
|
g_assert_cmpint(fu_device_get_version_build_date(dev), ==, 1552608000);
|
|
g_assert_true(fu_device_has_flag(dev, FWUPD_DEVICE_FLAG_UPDATABLE));
|
|
g_assert_true(fu_device_has_protocol(dev, "org.dmtf.redfish"));
|
|
g_assert_true(
|
|
fu_device_has_guid(dev, "REDFISH\\VENDOR_Lenovo&SOFTWAREID_UEFI-AFE1-6&TYPE_UNSIGNED"));
|
|
g_assert_true(fu_device_has_vendor_id(dev, "REDFISH:LENOVO"));
|
|
|
|
/* BIOS */
|
|
dev = g_ptr_array_index(devices, 0);
|
|
devstr1 = fu_device_to_string(dev);
|
|
g_debug("%s", devstr1);
|
|
g_assert_cmpstr(fu_device_get_id(dev), ==, "562313e34c756a05a2e878861377765582bbf971");
|
|
g_assert_cmpstr(fu_device_get_name(dev), ==, "BIOS Firmware");
|
|
g_assert_cmpstr(fu_device_get_vendor(dev), ==, "Contoso");
|
|
g_assert_cmpstr(fu_device_get_version(dev), ==, "1.45");
|
|
g_assert_cmpstr(fu_device_get_serial(dev), ==, "12345");
|
|
g_assert_cmpstr(fu_device_get_version_lowest(dev), ==, "1.10");
|
|
g_assert_cmpint(fu_device_get_version_format(dev), ==, FWUPD_VERSION_FORMAT_PAIR);
|
|
g_assert_cmpint(fu_device_get_version_build_date(dev), ==, 1552608000);
|
|
g_assert_true(fu_device_has_flag(dev, FWUPD_DEVICE_FLAG_UPDATABLE));
|
|
g_assert_true(fu_device_has_icon(dev, "network-wired"));
|
|
g_assert_true(fu_device_has_protocol(dev, "org.dmtf.redfish"));
|
|
g_assert_true(fu_device_has_guid(dev, "fee82a67-6ce2-4625-9f44-237ad2402c28"));
|
|
g_assert_true(fu_device_has_guid(dev, "a6d3294e-37e5-50aa-ae2f-c0c457af16f3"));
|
|
g_assert_true(fu_device_has_vendor_id(dev, "REDFISH:CONTOSO"));
|
|
}
|
|
|
|
static void
|
|
fu_test_redfish_update_func(gconstpointer user_data)
|
|
{
|
|
FuDevice *dev;
|
|
FuTest *self = (FuTest *)user_data;
|
|
GPtrArray *devices;
|
|
gboolean ret;
|
|
g_autoptr(GError) error = NULL;
|
|
g_autoptr(GBytes) blob_fw = NULL;
|
|
|
|
devices = fu_plugin_get_devices(self->plugin);
|
|
g_assert_nonnull(devices);
|
|
if (devices->len == 0) {
|
|
g_test_skip("no redfish support");
|
|
return;
|
|
}
|
|
g_assert_cmpint(devices->len, ==, 2);
|
|
|
|
/* BMC */
|
|
dev = g_ptr_array_index(devices, 1);
|
|
blob_fw = g_bytes_new_static("hello", 5);
|
|
ret = fu_plugin_runner_write_firmware(self->plugin,
|
|
dev,
|
|
blob_fw,
|
|
FWUPD_INSTALL_FLAG_NONE,
|
|
&error);
|
|
g_assert_no_error(error);
|
|
g_assert_true(ret);
|
|
g_assert_true(fu_device_has_flag(dev, FWUPD_DEVICE_FLAG_NEEDS_REBOOT));
|
|
|
|
/* try again */
|
|
ret = fu_plugin_runner_write_firmware(self->plugin,
|
|
dev,
|
|
blob_fw,
|
|
FWUPD_INSTALL_FLAG_NONE,
|
|
&error);
|
|
g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_WRITE);
|
|
g_assert_false(ret);
|
|
}
|
|
|
|
static void
|
|
fu_test_self_free(FuTest *self)
|
|
{
|
|
if (self->plugin != NULL)
|
|
g_object_unref(self->plugin);
|
|
g_free(self);
|
|
}
|
|
|
|
#pragma clang diagnostic push
|
|
#pragma clang diagnostic ignored "-Wunused-function"
|
|
G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuTest, fu_test_self_free)
|
|
#pragma clang diagnostic pop
|
|
|
|
int
|
|
main(int argc, char **argv)
|
|
{
|
|
g_autoptr(FuTest) self = g_new0(FuTest, 1);
|
|
g_autofree gchar *smbios_data_fn = NULL;
|
|
|
|
g_setenv("FWUPD_REDFISH_VERBOSE", "1", TRUE);
|
|
|
|
smbios_data_fn = g_build_filename(TESTDATADIR, "redfish-smbios.bin", NULL);
|
|
g_setenv("FWUPD_REDFISH_SMBIOS_DATA", smbios_data_fn, TRUE);
|
|
g_setenv("FWUPD_SYSFSFWDIR", TESTDATADIR, TRUE);
|
|
g_setenv("CONFIGURATION_DIRECTORY", TESTDATADIR, TRUE);
|
|
g_test_init(&argc, &argv, NULL);
|
|
|
|
g_log_set_fatal_mask(NULL, G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL);
|
|
fu_test_self_init(self);
|
|
g_test_add_func("/redfish/ipmi", fu_test_redfish_ipmi_func);
|
|
g_test_add_func("/redfish/common", fu_test_redfish_common_func);
|
|
g_test_add_func("/redfish/common{version}", fu_test_redfish_common_version_func);
|
|
g_test_add_func("/redfish/common{lenovo}", fu_test_redfish_common_lenovo_func);
|
|
g_test_add_func("/redfish/network{mac_addr}", fu_test_redfish_network_mac_addr_func);
|
|
g_test_add_func("/redfish/network{vid_pid}", fu_test_redfish_network_vid_pid_func);
|
|
g_test_add_data_func("/redfish/plugin{devices}", self, fu_test_redfish_devices_func);
|
|
g_test_add_data_func("/redfish/plugin{update}", self, fu_test_redfish_update_func);
|
|
return g_test_run();
|
|
}
|