fwupd/plugins/redfish/fu-self-test.c
Richard Hughes 1210aa4ae7 redfish: Create user accounts automatically using IPMI
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.
2021-09-07 17:25:37 +01:00

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();
}