QGA Pull request
-----BEGIN PGP SIGNATURE----- iQJQBAABCAA6FiEEh6m9kz+HxgbSdvYt2ujhCXWWnOUFAmJyToAcHG1hcmNhbmRy ZS5sdXJlYXVAcmVkaGF0LmNvbQAKCRDa6OEJdZac5e0tD/4y6+wWkn+ZjdNknpwB LzbCtpohTvdRVJ9f22i/niFhKfAS25aB7CeQIuDrsC0Bc7+4NV49DeGPh4pnH05B fNYJH+6/xYuc0cYML8FSqntbe1ElaIUP9vUPjIv3S4+IPMSkAyNI0xWkCiPsW8H4 rHpMMmtBJQNiS1KxE0ahj1WOkwnNsDWxL80fh5RBwb3g2ZHrIjilOrs6MnCDRM8r vYHK8cfGg+Lp/4hXB0mqTHw6oEmtCSiOZqE2rnIiHEI2kbW0DFu7yxtEX6aBjM6a s6NkzYSkBzUjV3HOU/aWCTJOz6jL+nzsdAe413ll0VWv8UUVAgDnOfG7BewIW7jn LFpIjsTen7VqVp6n96k8lPkoInnszyKVrJ/4AnS3PDrdfgg7B0RIx/exjrd249YX XsZjcW/vF4gRuu0YyNZESgeR0/y/7HRN6X9dCtF5vsjeqswEWoqH0vSkca8fbZCR yjE9z/dUuWwCBwq03UmtAy6POPhEw8QMld7SPGFl5n6Z7569hhWErYnu6ey/Xwwb W6W2EdFRCWF3cR4nxDFCoJVFjFybYIl2qNLu01tJ1kqsaIJMxfyt1fP09Jd4tifk CrAS1zpZSgDYRbzA5UGZ8Xir10uy+GyjFxIYnDYpqRy6nTDuG7wP2Ua+w182/2fd RwbzF98vV/AMoEqSAiHVQ56KMw== =Fm6Z -----END PGP SIGNATURE----- Merge tag 'qga-pull-request' of gitlab.com:marcandre.lureau/qemu into staging QGA Pull request # -----BEGIN PGP SIGNATURE----- # # iQJQBAABCAA6FiEEh6m9kz+HxgbSdvYt2ujhCXWWnOUFAmJyToAcHG1hcmNhbmRy # ZS5sdXJlYXVAcmVkaGF0LmNvbQAKCRDa6OEJdZac5e0tD/4y6+wWkn+ZjdNknpwB # LzbCtpohTvdRVJ9f22i/niFhKfAS25aB7CeQIuDrsC0Bc7+4NV49DeGPh4pnH05B # fNYJH+6/xYuc0cYML8FSqntbe1ElaIUP9vUPjIv3S4+IPMSkAyNI0xWkCiPsW8H4 # rHpMMmtBJQNiS1KxE0ahj1WOkwnNsDWxL80fh5RBwb3g2ZHrIjilOrs6MnCDRM8r # vYHK8cfGg+Lp/4hXB0mqTHw6oEmtCSiOZqE2rnIiHEI2kbW0DFu7yxtEX6aBjM6a # s6NkzYSkBzUjV3HOU/aWCTJOz6jL+nzsdAe413ll0VWv8UUVAgDnOfG7BewIW7jn # LFpIjsTen7VqVp6n96k8lPkoInnszyKVrJ/4AnS3PDrdfgg7B0RIx/exjrd249YX # XsZjcW/vF4gRuu0YyNZESgeR0/y/7HRN6X9dCtF5vsjeqswEWoqH0vSkca8fbZCR # yjE9z/dUuWwCBwq03UmtAy6POPhEw8QMld7SPGFl5n6Z7569hhWErYnu6ey/Xwwb # W6W2EdFRCWF3cR4nxDFCoJVFjFybYIl2qNLu01tJ1kqsaIJMxfyt1fP09Jd4tifk # CrAS1zpZSgDYRbzA5UGZ8Xir10uy+GyjFxIYnDYpqRy6nTDuG7wP2Ua+w182/2fd # RwbzF98vV/AMoEqSAiHVQ56KMw== # =Fm6Z # -----END PGP SIGNATURE----- # gpg: Signature made Wed 04 May 2022 02:59:28 AM PDT # gpg: using RSA key 87A9BD933F87C606D276F62DDAE8E10975969CE5 # gpg: issuer "marcandre.lureau@redhat.com" # gpg: Good signature from "Marc-André Lureau <marcandre.lureau@redhat.com>" [full] # gpg: aka "Marc-André Lureau <marcandre.lureau@gmail.com>" [full] * tag 'qga-pull-request' of gitlab.com:marcandre.lureau/qemu: qga: Introduce disk smart qga: Introduce NVMe disk bus type qga/commands-posix: 'guest-shutdown' for Solaris qga/commands-posix: Log all net stats failures qga/commands-posix: Fix listing ifaces for Solaris qga/commands-posix: Fix iface hw address detection qga/commands-posix: Use getifaddrs when available Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
This commit is contained in:
commit
9cf289af47
@ -1687,6 +1687,7 @@ config_host_data.set('CONFIG_SYNCFS', cc.has_function('syncfs'))
|
|||||||
config_host_data.set('CONFIG_SYNC_FILE_RANGE', cc.has_function('sync_file_range'))
|
config_host_data.set('CONFIG_SYNC_FILE_RANGE', cc.has_function('sync_file_range'))
|
||||||
config_host_data.set('CONFIG_TIMERFD', cc.has_function('timerfd_create'))
|
config_host_data.set('CONFIG_TIMERFD', cc.has_function('timerfd_create'))
|
||||||
config_host_data.set('HAVE_COPY_FILE_RANGE', cc.has_function('copy_file_range'))
|
config_host_data.set('HAVE_COPY_FILE_RANGE', cc.has_function('copy_file_range'))
|
||||||
|
config_host_data.set('HAVE_GETIFADDRS', cc.has_function('getifaddrs'))
|
||||||
config_host_data.set('HAVE_OPENPTY', cc.has_function('openpty', dependencies: util))
|
config_host_data.set('HAVE_OPENPTY', cc.has_function('openpty', dependencies: util))
|
||||||
config_host_data.set('HAVE_STRCHRNUL', cc.has_function('strchrnul'))
|
config_host_data.set('HAVE_STRCHRNUL', cc.has_function('strchrnul'))
|
||||||
config_host_data.set('HAVE_SYSTEM_FUNCTION', cc.has_function('system', prefix: '#include <stdlib.h>'))
|
config_host_data.set('HAVE_SYSTEM_FUNCTION', cc.has_function('system', prefix: '#include <stdlib.h>'))
|
||||||
|
@ -26,6 +26,7 @@
|
|||||||
#include "qemu/base64.h"
|
#include "qemu/base64.h"
|
||||||
#include "qemu/cutils.h"
|
#include "qemu/cutils.h"
|
||||||
#include "commands-common.h"
|
#include "commands-common.h"
|
||||||
|
#include "block/nvme.h"
|
||||||
|
|
||||||
#ifdef HAVE_UTMPX
|
#ifdef HAVE_UTMPX
|
||||||
#include <utmpx.h>
|
#include <utmpx.h>
|
||||||
@ -34,11 +35,8 @@
|
|||||||
#if defined(__linux__)
|
#if defined(__linux__)
|
||||||
#include <mntent.h>
|
#include <mntent.h>
|
||||||
#include <linux/fs.h>
|
#include <linux/fs.h>
|
||||||
#include <ifaddrs.h>
|
|
||||||
#include <arpa/inet.h>
|
|
||||||
#include <sys/socket.h>
|
|
||||||
#include <net/if.h>
|
|
||||||
#include <sys/statvfs.h>
|
#include <sys/statvfs.h>
|
||||||
|
#include <linux/nvme_ioctl.h>
|
||||||
|
|
||||||
#ifdef CONFIG_LIBUDEV
|
#ifdef CONFIG_LIBUDEV
|
||||||
#include <libudev.h>
|
#include <libudev.h>
|
||||||
@ -52,6 +50,17 @@
|
|||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_GETIFADDRS
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <net/if.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <ifaddrs.h>
|
||||||
|
#ifdef CONFIG_SOLARIS
|
||||||
|
#include <sys/sockio.h>
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
static void ga_wait_child(pid_t pid, int *status, Error **errp)
|
static void ga_wait_child(pid_t pid, int *status, Error **errp)
|
||||||
{
|
{
|
||||||
pid_t rpid;
|
pid_t rpid;
|
||||||
@ -78,13 +87,23 @@ void qmp_guest_shutdown(bool has_mode, const char *mode, Error **errp)
|
|||||||
pid_t pid;
|
pid_t pid;
|
||||||
int status;
|
int status;
|
||||||
|
|
||||||
|
#ifdef CONFIG_SOLARIS
|
||||||
|
const char *powerdown_flag = "-i5";
|
||||||
|
const char *halt_flag = "-i0";
|
||||||
|
const char *reboot_flag = "-i6";
|
||||||
|
#else
|
||||||
|
const char *powerdown_flag = "-P";
|
||||||
|
const char *halt_flag = "-H";
|
||||||
|
const char *reboot_flag = "-r";
|
||||||
|
#endif
|
||||||
|
|
||||||
slog("guest-shutdown called, mode: %s", mode);
|
slog("guest-shutdown called, mode: %s", mode);
|
||||||
if (!has_mode || strcmp(mode, "powerdown") == 0) {
|
if (!has_mode || strcmp(mode, "powerdown") == 0) {
|
||||||
shutdown_flag = "-P";
|
shutdown_flag = powerdown_flag;
|
||||||
} else if (strcmp(mode, "halt") == 0) {
|
} else if (strcmp(mode, "halt") == 0) {
|
||||||
shutdown_flag = "-H";
|
shutdown_flag = halt_flag;
|
||||||
} else if (strcmp(mode, "reboot") == 0) {
|
} else if (strcmp(mode, "reboot") == 0) {
|
||||||
shutdown_flag = "-r";
|
shutdown_flag = reboot_flag;
|
||||||
} else {
|
} else {
|
||||||
error_setg(errp,
|
error_setg(errp,
|
||||||
"mode is invalid (valid values are: halt|powerdown|reboot");
|
"mode is invalid (valid values are: halt|powerdown|reboot");
|
||||||
@ -99,8 +118,13 @@ void qmp_guest_shutdown(bool has_mode, const char *mode, Error **errp)
|
|||||||
reopen_fd_to_null(1);
|
reopen_fd_to_null(1);
|
||||||
reopen_fd_to_null(2);
|
reopen_fd_to_null(2);
|
||||||
|
|
||||||
|
#ifdef CONFIG_SOLARIS
|
||||||
|
execl("/sbin/shutdown", "shutdown", shutdown_flag, "-g0", "-y",
|
||||||
|
"hypervisor initiated shutdown", (char *)NULL);
|
||||||
|
#else
|
||||||
execl("/sbin/shutdown", "shutdown", "-h", shutdown_flag, "+0",
|
execl("/sbin/shutdown", "shutdown", "-h", shutdown_flag, "+0",
|
||||||
"hypervisor initiated shutdown", (char *)NULL);
|
"hypervisor initiated shutdown", (char *)NULL);
|
||||||
|
#endif
|
||||||
_exit(EXIT_FAILURE);
|
_exit(EXIT_FAILURE);
|
||||||
} else if (pid < 0) {
|
} else if (pid < 0) {
|
||||||
error_setg_errno(errp, errno, "failed to create child process");
|
error_setg_errno(errp, errno, "failed to create child process");
|
||||||
@ -868,7 +892,8 @@ static bool build_guest_fsinfo_for_pci_dev(char const *syspath,
|
|||||||
if (driver && (g_str_equal(driver, "ata_piix") ||
|
if (driver && (g_str_equal(driver, "ata_piix") ||
|
||||||
g_str_equal(driver, "sym53c8xx") ||
|
g_str_equal(driver, "sym53c8xx") ||
|
||||||
g_str_equal(driver, "virtio-pci") ||
|
g_str_equal(driver, "virtio-pci") ||
|
||||||
g_str_equal(driver, "ahci"))) {
|
g_str_equal(driver, "ahci") ||
|
||||||
|
g_str_equal(driver, "nvme"))) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -963,6 +988,8 @@ static bool build_guest_fsinfo_for_pci_dev(char const *syspath,
|
|||||||
g_debug("no host for '%s' (driver '%s')", syspath, driver);
|
g_debug("no host for '%s' (driver '%s')", syspath, driver);
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
} else if (strcmp(driver, "nvme") == 0) {
|
||||||
|
disk->bus_type = GUEST_DISK_BUS_TYPE_NVME;
|
||||||
} else {
|
} else {
|
||||||
g_debug("unknown driver '%s' (sysfs path '%s')", driver, syspath);
|
g_debug("unknown driver '%s' (sysfs path '%s')", driver, syspath);
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
@ -1366,6 +1393,76 @@ static GuestDiskInfoList *get_disk_partitions(
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void get_nvme_smart(GuestDiskInfo *disk)
|
||||||
|
{
|
||||||
|
int fd;
|
||||||
|
GuestNVMeSmart *smart;
|
||||||
|
NvmeSmartLog log = {0};
|
||||||
|
struct nvme_admin_cmd cmd = {
|
||||||
|
.opcode = NVME_ADM_CMD_GET_LOG_PAGE,
|
||||||
|
.nsid = NVME_NSID_BROADCAST,
|
||||||
|
.addr = (uintptr_t)&log,
|
||||||
|
.data_len = sizeof(log),
|
||||||
|
.cdw10 = NVME_LOG_SMART_INFO | (1 << 15) /* RAE bit */
|
||||||
|
| (((sizeof(log) >> 2) - 1) << 16)
|
||||||
|
};
|
||||||
|
|
||||||
|
fd = qemu_open_old(disk->name, O_RDONLY);
|
||||||
|
if (fd == -1) {
|
||||||
|
g_debug("Failed to open device: %s: %s", disk->name, g_strerror(errno));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ioctl(fd, NVME_IOCTL_ADMIN_CMD, &cmd)) {
|
||||||
|
g_debug("Failed to get smart: %s: %s", disk->name, g_strerror(errno));
|
||||||
|
close(fd);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
disk->has_smart = true;
|
||||||
|
disk->smart = g_new0(GuestDiskSmart, 1);
|
||||||
|
disk->smart->type = GUEST_DISK_BUS_TYPE_NVME;
|
||||||
|
|
||||||
|
smart = &disk->smart->u.nvme;
|
||||||
|
smart->critical_warning = log.critical_warning;
|
||||||
|
smart->temperature = lduw_le_p(&log.temperature); /* unaligned field */
|
||||||
|
smart->available_spare = log.available_spare;
|
||||||
|
smart->available_spare_threshold = log.available_spare_threshold;
|
||||||
|
smart->percentage_used = log.percentage_used;
|
||||||
|
smart->data_units_read_lo = le64_to_cpu(log.data_units_read[0]);
|
||||||
|
smart->data_units_read_hi = le64_to_cpu(log.data_units_read[1]);
|
||||||
|
smart->data_units_written_lo = le64_to_cpu(log.data_units_written[0]);
|
||||||
|
smart->data_units_written_hi = le64_to_cpu(log.data_units_written[1]);
|
||||||
|
smart->host_read_commands_lo = le64_to_cpu(log.host_read_commands[0]);
|
||||||
|
smart->host_read_commands_hi = le64_to_cpu(log.host_read_commands[1]);
|
||||||
|
smart->host_write_commands_lo = le64_to_cpu(log.host_write_commands[0]);
|
||||||
|
smart->host_write_commands_hi = le64_to_cpu(log.host_write_commands[1]);
|
||||||
|
smart->controller_busy_time_lo = le64_to_cpu(log.controller_busy_time[0]);
|
||||||
|
smart->controller_busy_time_hi = le64_to_cpu(log.controller_busy_time[1]);
|
||||||
|
smart->power_cycles_lo = le64_to_cpu(log.power_cycles[0]);
|
||||||
|
smart->power_cycles_hi = le64_to_cpu(log.power_cycles[1]);
|
||||||
|
smart->power_on_hours_lo = le64_to_cpu(log.power_on_hours[0]);
|
||||||
|
smart->power_on_hours_hi = le64_to_cpu(log.power_on_hours[1]);
|
||||||
|
smart->unsafe_shutdowns_lo = le64_to_cpu(log.unsafe_shutdowns[0]);
|
||||||
|
smart->unsafe_shutdowns_hi = le64_to_cpu(log.unsafe_shutdowns[1]);
|
||||||
|
smart->media_errors_lo = le64_to_cpu(log.media_errors[0]);
|
||||||
|
smart->media_errors_hi = le64_to_cpu(log.media_errors[1]);
|
||||||
|
smart->number_of_error_log_entries_lo =
|
||||||
|
le64_to_cpu(log.number_of_error_log_entries[0]);
|
||||||
|
smart->number_of_error_log_entries_hi =
|
||||||
|
le64_to_cpu(log.number_of_error_log_entries[1]);
|
||||||
|
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void get_disk_smart(GuestDiskInfo *disk)
|
||||||
|
{
|
||||||
|
if (disk->has_address
|
||||||
|
&& (disk->address->bus_type == GUEST_DISK_BUS_TYPE_NVME)) {
|
||||||
|
get_nvme_smart(disk);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
GuestDiskInfoList *qmp_guest_get_disks(Error **errp)
|
GuestDiskInfoList *qmp_guest_get_disks(Error **errp)
|
||||||
{
|
{
|
||||||
GuestDiskInfoList *ret = NULL;
|
GuestDiskInfoList *ret = NULL;
|
||||||
@ -1439,6 +1536,7 @@ GuestDiskInfoList *qmp_guest_get_disks(Error **errp)
|
|||||||
}
|
}
|
||||||
|
|
||||||
get_disk_deps(disk_dir, disk);
|
get_disk_deps(disk_dir, disk);
|
||||||
|
get_disk_smart(disk);
|
||||||
ret = get_disk_partitions(ret, de->d_name, disk_dir, dev_name);
|
ret = get_disk_partitions(ret, de->d_name, disk_dir, dev_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2133,223 +2231,6 @@ void qmp_guest_suspend_hybrid(Error **errp)
|
|||||||
guest_suspend(SUSPEND_MODE_HYBRID, errp);
|
guest_suspend(SUSPEND_MODE_HYBRID, errp);
|
||||||
}
|
}
|
||||||
|
|
||||||
static GuestNetworkInterface *
|
|
||||||
guest_find_interface(GuestNetworkInterfaceList *head,
|
|
||||||
const char *name)
|
|
||||||
{
|
|
||||||
for (; head; head = head->next) {
|
|
||||||
if (strcmp(head->value->name, name) == 0) {
|
|
||||||
return head->value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int guest_get_network_stats(const char *name,
|
|
||||||
GuestNetworkInterfaceStat *stats)
|
|
||||||
{
|
|
||||||
int name_len;
|
|
||||||
char const *devinfo = "/proc/net/dev";
|
|
||||||
FILE *fp;
|
|
||||||
char *line = NULL, *colon;
|
|
||||||
size_t n = 0;
|
|
||||||
fp = fopen(devinfo, "r");
|
|
||||||
if (!fp) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
name_len = strlen(name);
|
|
||||||
while (getline(&line, &n, fp) != -1) {
|
|
||||||
long long dummy;
|
|
||||||
long long rx_bytes;
|
|
||||||
long long rx_packets;
|
|
||||||
long long rx_errs;
|
|
||||||
long long rx_dropped;
|
|
||||||
long long tx_bytes;
|
|
||||||
long long tx_packets;
|
|
||||||
long long tx_errs;
|
|
||||||
long long tx_dropped;
|
|
||||||
char *trim_line;
|
|
||||||
trim_line = g_strchug(line);
|
|
||||||
if (trim_line[0] == '\0') {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
colon = strchr(trim_line, ':');
|
|
||||||
if (!colon) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (colon - name_len == trim_line &&
|
|
||||||
strncmp(trim_line, name, name_len) == 0) {
|
|
||||||
if (sscanf(colon + 1,
|
|
||||||
"%lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld",
|
|
||||||
&rx_bytes, &rx_packets, &rx_errs, &rx_dropped,
|
|
||||||
&dummy, &dummy, &dummy, &dummy,
|
|
||||||
&tx_bytes, &tx_packets, &tx_errs, &tx_dropped,
|
|
||||||
&dummy, &dummy, &dummy, &dummy) != 16) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
stats->rx_bytes = rx_bytes;
|
|
||||||
stats->rx_packets = rx_packets;
|
|
||||||
stats->rx_errs = rx_errs;
|
|
||||||
stats->rx_dropped = rx_dropped;
|
|
||||||
stats->tx_bytes = tx_bytes;
|
|
||||||
stats->tx_packets = tx_packets;
|
|
||||||
stats->tx_errs = tx_errs;
|
|
||||||
stats->tx_dropped = tx_dropped;
|
|
||||||
fclose(fp);
|
|
||||||
g_free(line);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fclose(fp);
|
|
||||||
g_free(line);
|
|
||||||
g_debug("/proc/net/dev: Interface '%s' not found", name);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Build information about guest interfaces
|
|
||||||
*/
|
|
||||||
GuestNetworkInterfaceList *qmp_guest_network_get_interfaces(Error **errp)
|
|
||||||
{
|
|
||||||
GuestNetworkInterfaceList *head = NULL, **tail = &head;
|
|
||||||
struct ifaddrs *ifap, *ifa;
|
|
||||||
|
|
||||||
if (getifaddrs(&ifap) < 0) {
|
|
||||||
error_setg_errno(errp, errno, "getifaddrs failed");
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
|
|
||||||
GuestNetworkInterface *info;
|
|
||||||
GuestIpAddressList **address_tail;
|
|
||||||
GuestIpAddress *address_item = NULL;
|
|
||||||
GuestNetworkInterfaceStat *interface_stat = NULL;
|
|
||||||
char addr4[INET_ADDRSTRLEN];
|
|
||||||
char addr6[INET6_ADDRSTRLEN];
|
|
||||||
int sock;
|
|
||||||
struct ifreq ifr;
|
|
||||||
unsigned char *mac_addr;
|
|
||||||
void *p;
|
|
||||||
|
|
||||||
g_debug("Processing %s interface", ifa->ifa_name);
|
|
||||||
|
|
||||||
info = guest_find_interface(head, ifa->ifa_name);
|
|
||||||
|
|
||||||
if (!info) {
|
|
||||||
info = g_malloc0(sizeof(*info));
|
|
||||||
info->name = g_strdup(ifa->ifa_name);
|
|
||||||
|
|
||||||
QAPI_LIST_APPEND(tail, info);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!info->has_hardware_address && ifa->ifa_flags & SIOCGIFHWADDR) {
|
|
||||||
/* we haven't obtained HW address yet */
|
|
||||||
sock = socket(PF_INET, SOCK_STREAM, 0);
|
|
||||||
if (sock == -1) {
|
|
||||||
error_setg_errno(errp, errno, "failed to create socket");
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
memset(&ifr, 0, sizeof(ifr));
|
|
||||||
pstrcpy(ifr.ifr_name, IF_NAMESIZE, info->name);
|
|
||||||
if (ioctl(sock, SIOCGIFHWADDR, &ifr) == -1) {
|
|
||||||
error_setg_errno(errp, errno,
|
|
||||||
"failed to get MAC address of %s",
|
|
||||||
ifa->ifa_name);
|
|
||||||
close(sock);
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
close(sock);
|
|
||||||
mac_addr = (unsigned char *) &ifr.ifr_hwaddr.sa_data;
|
|
||||||
|
|
||||||
info->hardware_address =
|
|
||||||
g_strdup_printf("%02x:%02x:%02x:%02x:%02x:%02x",
|
|
||||||
(int) mac_addr[0], (int) mac_addr[1],
|
|
||||||
(int) mac_addr[2], (int) mac_addr[3],
|
|
||||||
(int) mac_addr[4], (int) mac_addr[5]);
|
|
||||||
|
|
||||||
info->has_hardware_address = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ifa->ifa_addr &&
|
|
||||||
ifa->ifa_addr->sa_family == AF_INET) {
|
|
||||||
/* interface with IPv4 address */
|
|
||||||
p = &((struct sockaddr_in *)ifa->ifa_addr)->sin_addr;
|
|
||||||
if (!inet_ntop(AF_INET, p, addr4, sizeof(addr4))) {
|
|
||||||
error_setg_errno(errp, errno, "inet_ntop failed");
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
address_item = g_malloc0(sizeof(*address_item));
|
|
||||||
address_item->ip_address = g_strdup(addr4);
|
|
||||||
address_item->ip_address_type = GUEST_IP_ADDRESS_TYPE_IPV4;
|
|
||||||
|
|
||||||
if (ifa->ifa_netmask) {
|
|
||||||
/* Count the number of set bits in netmask.
|
|
||||||
* This is safe as '1' and '0' cannot be shuffled in netmask. */
|
|
||||||
p = &((struct sockaddr_in *)ifa->ifa_netmask)->sin_addr;
|
|
||||||
address_item->prefix = ctpop32(((uint32_t *) p)[0]);
|
|
||||||
}
|
|
||||||
} else if (ifa->ifa_addr &&
|
|
||||||
ifa->ifa_addr->sa_family == AF_INET6) {
|
|
||||||
/* interface with IPv6 address */
|
|
||||||
p = &((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr;
|
|
||||||
if (!inet_ntop(AF_INET6, p, addr6, sizeof(addr6))) {
|
|
||||||
error_setg_errno(errp, errno, "inet_ntop failed");
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
address_item = g_malloc0(sizeof(*address_item));
|
|
||||||
address_item->ip_address = g_strdup(addr6);
|
|
||||||
address_item->ip_address_type = GUEST_IP_ADDRESS_TYPE_IPV6;
|
|
||||||
|
|
||||||
if (ifa->ifa_netmask) {
|
|
||||||
/* Count the number of set bits in netmask.
|
|
||||||
* This is safe as '1' and '0' cannot be shuffled in netmask. */
|
|
||||||
p = &((struct sockaddr_in6 *)ifa->ifa_netmask)->sin6_addr;
|
|
||||||
address_item->prefix =
|
|
||||||
ctpop32(((uint32_t *) p)[0]) +
|
|
||||||
ctpop32(((uint32_t *) p)[1]) +
|
|
||||||
ctpop32(((uint32_t *) p)[2]) +
|
|
||||||
ctpop32(((uint32_t *) p)[3]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!address_item) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
address_tail = &info->ip_addresses;
|
|
||||||
while (*address_tail) {
|
|
||||||
address_tail = &(*address_tail)->next;
|
|
||||||
}
|
|
||||||
QAPI_LIST_APPEND(address_tail, address_item);
|
|
||||||
|
|
||||||
info->has_ip_addresses = true;
|
|
||||||
|
|
||||||
if (!info->has_statistics) {
|
|
||||||
interface_stat = g_malloc0(sizeof(*interface_stat));
|
|
||||||
if (guest_get_network_stats(info->name, interface_stat) == -1) {
|
|
||||||
info->has_statistics = false;
|
|
||||||
g_free(interface_stat);
|
|
||||||
} else {
|
|
||||||
info->statistics = interface_stat;
|
|
||||||
info->has_statistics = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
freeifaddrs(ifap);
|
|
||||||
return head;
|
|
||||||
|
|
||||||
error:
|
|
||||||
freeifaddrs(ifap);
|
|
||||||
qapi_free_GuestNetworkInterfaceList(head);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Transfer online/offline status between @vcpu and the guest system.
|
/* Transfer online/offline status between @vcpu and the guest system.
|
||||||
*
|
*
|
||||||
* On input either @errp or *@errp must be NULL.
|
* On input either @errp or *@errp must be NULL.
|
||||||
@ -2919,12 +2800,6 @@ void qmp_guest_suspend_hybrid(Error **errp)
|
|||||||
error_setg(errp, QERR_UNSUPPORTED);
|
error_setg(errp, QERR_UNSUPPORTED);
|
||||||
}
|
}
|
||||||
|
|
||||||
GuestNetworkInterfaceList *qmp_guest_network_get_interfaces(Error **errp)
|
|
||||||
{
|
|
||||||
error_setg(errp, QERR_UNSUPPORTED);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
GuestLogicalProcessorList *qmp_guest_get_vcpus(Error **errp)
|
GuestLogicalProcessorList *qmp_guest_get_vcpus(Error **errp)
|
||||||
{
|
{
|
||||||
error_setg(errp, QERR_UNSUPPORTED);
|
error_setg(errp, QERR_UNSUPPORTED);
|
||||||
@ -2966,6 +2841,252 @@ GuestMemoryBlockInfo *qmp_guest_get_memory_block_info(Error **errp)
|
|||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_GETIFADDRS
|
||||||
|
static GuestNetworkInterface *
|
||||||
|
guest_find_interface(GuestNetworkInterfaceList *head,
|
||||||
|
const char *name)
|
||||||
|
{
|
||||||
|
for (; head; head = head->next) {
|
||||||
|
if (strcmp(head->value->name, name) == 0) {
|
||||||
|
return head->value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int guest_get_network_stats(const char *name,
|
||||||
|
GuestNetworkInterfaceStat *stats)
|
||||||
|
{
|
||||||
|
#ifdef CONFIG_LINUX
|
||||||
|
int name_len;
|
||||||
|
char const *devinfo = "/proc/net/dev";
|
||||||
|
FILE *fp;
|
||||||
|
char *line = NULL, *colon;
|
||||||
|
size_t n = 0;
|
||||||
|
fp = fopen(devinfo, "r");
|
||||||
|
if (!fp) {
|
||||||
|
g_debug("failed to open network stats %s: %s", devinfo,
|
||||||
|
g_strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
name_len = strlen(name);
|
||||||
|
while (getline(&line, &n, fp) != -1) {
|
||||||
|
long long dummy;
|
||||||
|
long long rx_bytes;
|
||||||
|
long long rx_packets;
|
||||||
|
long long rx_errs;
|
||||||
|
long long rx_dropped;
|
||||||
|
long long tx_bytes;
|
||||||
|
long long tx_packets;
|
||||||
|
long long tx_errs;
|
||||||
|
long long tx_dropped;
|
||||||
|
char *trim_line;
|
||||||
|
trim_line = g_strchug(line);
|
||||||
|
if (trim_line[0] == '\0') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
colon = strchr(trim_line, ':');
|
||||||
|
if (!colon) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (colon - name_len == trim_line &&
|
||||||
|
strncmp(trim_line, name, name_len) == 0) {
|
||||||
|
if (sscanf(colon + 1,
|
||||||
|
"%lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld",
|
||||||
|
&rx_bytes, &rx_packets, &rx_errs, &rx_dropped,
|
||||||
|
&dummy, &dummy, &dummy, &dummy,
|
||||||
|
&tx_bytes, &tx_packets, &tx_errs, &tx_dropped,
|
||||||
|
&dummy, &dummy, &dummy, &dummy) != 16) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
stats->rx_bytes = rx_bytes;
|
||||||
|
stats->rx_packets = rx_packets;
|
||||||
|
stats->rx_errs = rx_errs;
|
||||||
|
stats->rx_dropped = rx_dropped;
|
||||||
|
stats->tx_bytes = tx_bytes;
|
||||||
|
stats->tx_packets = tx_packets;
|
||||||
|
stats->tx_errs = tx_errs;
|
||||||
|
stats->tx_dropped = tx_dropped;
|
||||||
|
fclose(fp);
|
||||||
|
g_free(line);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fclose(fp);
|
||||||
|
g_free(line);
|
||||||
|
g_debug("/proc/net/dev: Interface '%s' not found", name);
|
||||||
|
#else /* !CONFIG_LINUX */
|
||||||
|
g_debug("Network stats reporting available only for Linux");
|
||||||
|
#endif /* !CONFIG_LINUX */
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Build information about guest interfaces
|
||||||
|
*/
|
||||||
|
GuestNetworkInterfaceList *qmp_guest_network_get_interfaces(Error **errp)
|
||||||
|
{
|
||||||
|
GuestNetworkInterfaceList *head = NULL, **tail = &head;
|
||||||
|
struct ifaddrs *ifap, *ifa;
|
||||||
|
|
||||||
|
if (getifaddrs(&ifap) < 0) {
|
||||||
|
error_setg_errno(errp, errno, "getifaddrs failed");
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
|
||||||
|
GuestNetworkInterface *info;
|
||||||
|
GuestIpAddressList **address_tail;
|
||||||
|
GuestIpAddress *address_item = NULL;
|
||||||
|
GuestNetworkInterfaceStat *interface_stat = NULL;
|
||||||
|
char addr4[INET_ADDRSTRLEN];
|
||||||
|
char addr6[INET6_ADDRSTRLEN];
|
||||||
|
int sock;
|
||||||
|
struct ifreq ifr;
|
||||||
|
unsigned char *mac_addr;
|
||||||
|
void *p;
|
||||||
|
|
||||||
|
g_debug("Processing %s interface", ifa->ifa_name);
|
||||||
|
|
||||||
|
info = guest_find_interface(head, ifa->ifa_name);
|
||||||
|
|
||||||
|
if (!info) {
|
||||||
|
info = g_malloc0(sizeof(*info));
|
||||||
|
info->name = g_strdup(ifa->ifa_name);
|
||||||
|
|
||||||
|
QAPI_LIST_APPEND(tail, info);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!info->has_hardware_address) {
|
||||||
|
/* we haven't obtained HW address yet */
|
||||||
|
sock = socket(PF_INET, SOCK_STREAM, 0);
|
||||||
|
if (sock == -1) {
|
||||||
|
error_setg_errno(errp, errno, "failed to create socket");
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(&ifr, 0, sizeof(ifr));
|
||||||
|
pstrcpy(ifr.ifr_name, IF_NAMESIZE, info->name);
|
||||||
|
if (ioctl(sock, SIOCGIFHWADDR, &ifr) == -1) {
|
||||||
|
/*
|
||||||
|
* We can't get the hw addr of this interface, but that's not a
|
||||||
|
* fatal error. Don't set info->hardware_address, but keep
|
||||||
|
* going.
|
||||||
|
*/
|
||||||
|
if (errno == EADDRNOTAVAIL) {
|
||||||
|
/* The interface doesn't have a hw addr (e.g. loopback). */
|
||||||
|
g_debug("failed to get MAC address of %s: %s",
|
||||||
|
ifa->ifa_name, strerror(errno));
|
||||||
|
} else{
|
||||||
|
g_warning("failed to get MAC address of %s: %s",
|
||||||
|
ifa->ifa_name, strerror(errno));
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
#ifdef CONFIG_SOLARIS
|
||||||
|
mac_addr = (unsigned char *) &ifr.ifr_addr.sa_data;
|
||||||
|
#else
|
||||||
|
mac_addr = (unsigned char *) &ifr.ifr_hwaddr.sa_data;
|
||||||
|
#endif
|
||||||
|
info->hardware_address =
|
||||||
|
g_strdup_printf("%02x:%02x:%02x:%02x:%02x:%02x",
|
||||||
|
(int) mac_addr[0], (int) mac_addr[1],
|
||||||
|
(int) mac_addr[2], (int) mac_addr[3],
|
||||||
|
(int) mac_addr[4], (int) mac_addr[5]);
|
||||||
|
|
||||||
|
info->has_hardware_address = true;
|
||||||
|
}
|
||||||
|
close(sock);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ifa->ifa_addr &&
|
||||||
|
ifa->ifa_addr->sa_family == AF_INET) {
|
||||||
|
/* interface with IPv4 address */
|
||||||
|
p = &((struct sockaddr_in *)ifa->ifa_addr)->sin_addr;
|
||||||
|
if (!inet_ntop(AF_INET, p, addr4, sizeof(addr4))) {
|
||||||
|
error_setg_errno(errp, errno, "inet_ntop failed");
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
address_item = g_malloc0(sizeof(*address_item));
|
||||||
|
address_item->ip_address = g_strdup(addr4);
|
||||||
|
address_item->ip_address_type = GUEST_IP_ADDRESS_TYPE_IPV4;
|
||||||
|
|
||||||
|
if (ifa->ifa_netmask) {
|
||||||
|
/* Count the number of set bits in netmask.
|
||||||
|
* This is safe as '1' and '0' cannot be shuffled in netmask. */
|
||||||
|
p = &((struct sockaddr_in *)ifa->ifa_netmask)->sin_addr;
|
||||||
|
address_item->prefix = ctpop32(((uint32_t *) p)[0]);
|
||||||
|
}
|
||||||
|
} else if (ifa->ifa_addr &&
|
||||||
|
ifa->ifa_addr->sa_family == AF_INET6) {
|
||||||
|
/* interface with IPv6 address */
|
||||||
|
p = &((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr;
|
||||||
|
if (!inet_ntop(AF_INET6, p, addr6, sizeof(addr6))) {
|
||||||
|
error_setg_errno(errp, errno, "inet_ntop failed");
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
address_item = g_malloc0(sizeof(*address_item));
|
||||||
|
address_item->ip_address = g_strdup(addr6);
|
||||||
|
address_item->ip_address_type = GUEST_IP_ADDRESS_TYPE_IPV6;
|
||||||
|
|
||||||
|
if (ifa->ifa_netmask) {
|
||||||
|
/* Count the number of set bits in netmask.
|
||||||
|
* This is safe as '1' and '0' cannot be shuffled in netmask. */
|
||||||
|
p = &((struct sockaddr_in6 *)ifa->ifa_netmask)->sin6_addr;
|
||||||
|
address_item->prefix =
|
||||||
|
ctpop32(((uint32_t *) p)[0]) +
|
||||||
|
ctpop32(((uint32_t *) p)[1]) +
|
||||||
|
ctpop32(((uint32_t *) p)[2]) +
|
||||||
|
ctpop32(((uint32_t *) p)[3]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!address_item) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
address_tail = &info->ip_addresses;
|
||||||
|
while (*address_tail) {
|
||||||
|
address_tail = &(*address_tail)->next;
|
||||||
|
}
|
||||||
|
QAPI_LIST_APPEND(address_tail, address_item);
|
||||||
|
|
||||||
|
info->has_ip_addresses = true;
|
||||||
|
|
||||||
|
if (!info->has_statistics) {
|
||||||
|
interface_stat = g_malloc0(sizeof(*interface_stat));
|
||||||
|
if (guest_get_network_stats(info->name, interface_stat) == -1) {
|
||||||
|
info->has_statistics = false;
|
||||||
|
g_free(interface_stat);
|
||||||
|
} else {
|
||||||
|
info->statistics = interface_stat;
|
||||||
|
info->has_statistics = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
freeifaddrs(ifap);
|
||||||
|
return head;
|
||||||
|
|
||||||
|
error:
|
||||||
|
freeifaddrs(ifap);
|
||||||
|
qapi_free_GuestNetworkInterfaceList(head);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
GuestNetworkInterfaceList *qmp_guest_network_get_interfaces(Error **errp)
|
||||||
|
{
|
||||||
|
error_setg(errp, QERR_UNSUPPORTED);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* HAVE_GETIFADDRS */
|
||||||
|
|
||||||
#if !defined(CONFIG_FSFREEZE)
|
#if !defined(CONFIG_FSFREEZE)
|
||||||
|
|
||||||
GuestFilesystemInfoList *qmp_guest_get_fsinfo(Error **errp)
|
GuestFilesystemInfoList *qmp_guest_get_fsinfo(Error **errp)
|
||||||
@ -3028,8 +3149,7 @@ GList *ga_command_blacklist_init(GList *blacklist)
|
|||||||
{
|
{
|
||||||
const char *list[] = {
|
const char *list[] = {
|
||||||
"guest-suspend-disk", "guest-suspend-ram",
|
"guest-suspend-disk", "guest-suspend-ram",
|
||||||
"guest-suspend-hybrid", "guest-network-get-interfaces",
|
"guest-suspend-hybrid", "guest-get-vcpus", "guest-set-vcpus",
|
||||||
"guest-get-vcpus", "guest-set-vcpus",
|
|
||||||
"guest-get-memory-blocks", "guest-set-memory-blocks",
|
"guest-get-memory-blocks", "guest-set-memory-blocks",
|
||||||
"guest-get-memory-block-size", "guest-get-memory-block-info",
|
"guest-get-memory-block-size", "guest-get-memory-block-info",
|
||||||
NULL};
|
NULL};
|
||||||
@ -3041,6 +3161,11 @@ GList *ga_command_blacklist_init(GList *blacklist)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if !defined(HAVE_GETIFADDRS)
|
||||||
|
blacklist = g_list_append(blacklist,
|
||||||
|
g_strdup("guest-network-get-interfaces"));
|
||||||
|
#endif
|
||||||
|
|
||||||
#if !defined(CONFIG_FSFREEZE)
|
#if !defined(CONFIG_FSFREEZE)
|
||||||
{
|
{
|
||||||
const char *list[] = {
|
const char *list[] = {
|
||||||
|
@ -827,13 +827,14 @@
|
|||||||
# @mmc: Win multimedia card (MMC) bus type
|
# @mmc: Win multimedia card (MMC) bus type
|
||||||
# @virtual: Win virtual bus type
|
# @virtual: Win virtual bus type
|
||||||
# @file-backed-virtual: Win file-backed bus type
|
# @file-backed-virtual: Win file-backed bus type
|
||||||
|
# @nvme: NVMe disks (since 7.1)
|
||||||
#
|
#
|
||||||
# Since: 2.2; 'Unknown' and all entries below since 2.4
|
# Since: 2.2; 'Unknown' and all entries below since 2.4
|
||||||
##
|
##
|
||||||
{ 'enum': 'GuestDiskBusType',
|
{ 'enum': 'GuestDiskBusType',
|
||||||
'data': [ 'ide', 'fdc', 'scsi', 'virtio', 'xen', 'usb', 'uml', 'sata',
|
'data': [ 'ide', 'fdc', 'scsi', 'virtio', 'xen', 'usb', 'uml', 'sata',
|
||||||
'sd', 'unknown', 'ieee1394', 'ssa', 'fibre', 'raid', 'iscsi',
|
'sd', 'unknown', 'ieee1394', 'ssa', 'fibre', 'raid', 'iscsi',
|
||||||
'sas', 'mmc', 'virtual', 'file-backed-virtual' ] }
|
'sas', 'mmc', 'virtual', 'file-backed-virtual', 'nvme' ] }
|
||||||
|
|
||||||
|
|
||||||
##
|
##
|
||||||
@ -887,6 +888,55 @@
|
|||||||
'*serial': 'str', '*dev': 'str',
|
'*serial': 'str', '*dev': 'str',
|
||||||
'*ccw-address': 'GuestCCWAddress'} }
|
'*ccw-address': 'GuestCCWAddress'} }
|
||||||
|
|
||||||
|
##
|
||||||
|
# @GuestNVMeSmart:
|
||||||
|
#
|
||||||
|
# NVMe smart informations, based on NVMe specification,
|
||||||
|
# section <SMART / Health Information (Log Identifier 02h)>
|
||||||
|
#
|
||||||
|
# Since: 7.1
|
||||||
|
##
|
||||||
|
{ 'struct': 'GuestNVMeSmart',
|
||||||
|
'data': {'critical-warning': 'int',
|
||||||
|
'temperature': 'int',
|
||||||
|
'available-spare': 'int',
|
||||||
|
'available-spare-threshold': 'int',
|
||||||
|
'percentage-used': 'int',
|
||||||
|
'data-units-read-lo': 'uint64',
|
||||||
|
'data-units-read-hi': 'uint64',
|
||||||
|
'data-units-written-lo': 'uint64',
|
||||||
|
'data-units-written-hi': 'uint64',
|
||||||
|
'host-read-commands-lo': 'uint64',
|
||||||
|
'host-read-commands-hi': 'uint64',
|
||||||
|
'host-write-commands-lo': 'uint64',
|
||||||
|
'host-write-commands-hi': 'uint64',
|
||||||
|
'controller-busy-time-lo': 'uint64',
|
||||||
|
'controller-busy-time-hi': 'uint64',
|
||||||
|
'power-cycles-lo': 'uint64',
|
||||||
|
'power-cycles-hi': 'uint64',
|
||||||
|
'power-on-hours-lo': 'uint64',
|
||||||
|
'power-on-hours-hi': 'uint64',
|
||||||
|
'unsafe-shutdowns-lo': 'uint64',
|
||||||
|
'unsafe-shutdowns-hi': 'uint64',
|
||||||
|
'media-errors-lo': 'uint64',
|
||||||
|
'media-errors-hi': 'uint64',
|
||||||
|
'number-of-error-log-entries-lo': 'uint64',
|
||||||
|
'number-of-error-log-entries-hi': 'uint64' } }
|
||||||
|
|
||||||
|
##
|
||||||
|
# @GuestDiskSmart:
|
||||||
|
#
|
||||||
|
# Disk type related smart information.
|
||||||
|
#
|
||||||
|
# - @nvme: NVMe disk smart
|
||||||
|
#
|
||||||
|
# Since: 7.1
|
||||||
|
##
|
||||||
|
{ 'union': 'GuestDiskSmart',
|
||||||
|
'base': { 'type': 'GuestDiskBusType' },
|
||||||
|
'discriminator': 'type',
|
||||||
|
'data': { 'nvme': 'GuestNVMeSmart' } }
|
||||||
|
|
||||||
##
|
##
|
||||||
# @GuestDiskInfo:
|
# @GuestDiskInfo:
|
||||||
#
|
#
|
||||||
@ -898,12 +948,14 @@
|
|||||||
# @address: disk address information (only for non-virtual devices)
|
# @address: disk address information (only for non-virtual devices)
|
||||||
# @alias: optional alias assigned to the disk, on Linux this is a name assigned
|
# @alias: optional alias assigned to the disk, on Linux this is a name assigned
|
||||||
# by device mapper
|
# by device mapper
|
||||||
|
# @smart: disk smart information (Since 7.1)
|
||||||
#
|
#
|
||||||
# Since 5.2
|
# Since 5.2
|
||||||
##
|
##
|
||||||
{ 'struct': 'GuestDiskInfo',
|
{ 'struct': 'GuestDiskInfo',
|
||||||
'data': {'name': 'str', 'partition': 'bool', '*dependencies': ['str'],
|
'data': {'name': 'str', 'partition': 'bool', '*dependencies': ['str'],
|
||||||
'*address': 'GuestDiskAddress', '*alias': 'str'} }
|
'*address': 'GuestDiskAddress', '*alias': 'str',
|
||||||
|
'*smart': 'GuestDiskSmart'} }
|
||||||
|
|
||||||
##
|
##
|
||||||
# @guest-get-disks:
|
# @guest-get-disks:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user