mirror of
				https://gitlab.com/libvirt/libvirt.git
				synced 2025-10-30 20:24:58 +03:00 
			
		
		
		
	The domblklist command is designed to show a brief information about the blocks of a domain. One piece of information that is shows is "Target "and "Source". Before the modification, the Vhost disk of SPDK is displayed as "-". After the modification, the socket associated with it can be displayed. Signed-off-by: dinglimin <dinglimin@cmss.chinamobile.com> Reviewed-by: Michal Privoznik <mprivozn@redhat.com>
		
			
				
	
	
		
			2490 lines
		
	
	
		
			73 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			2490 lines
		
	
	
		
			73 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * virsh-domain-monitor.c: Commands to monitor domain status
 | |
|  *
 | |
|  * Copyright (C) 2005, 2007-2016 Red Hat, Inc.
 | |
|  *
 | |
|  * This library is free software; you can redistribute it and/or
 | |
|  * modify it under the terms of the GNU Lesser General Public
 | |
|  * License as published by the Free Software Foundation; either
 | |
|  * version 2.1 of the License, or (at your option) any later version.
 | |
|  *
 | |
|  * This library is distributed in the hope that it will be useful,
 | |
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | |
|  * Lesser General Public License for more details.
 | |
|  *
 | |
|  * You should have received a copy of the GNU Lesser General Public
 | |
|  * License along with this library.  If not, see
 | |
|  * <http://www.gnu.org/licenses/>.
 | |
|  */
 | |
| 
 | |
| #include <config.h>
 | |
| #include "virsh-domain-monitor.h"
 | |
| #include "virsh-util.h"
 | |
| 
 | |
| #include <libxml/parser.h>
 | |
| #include <libxml/xpath.h>
 | |
| 
 | |
| #include "internal.h"
 | |
| #include "conf/virdomainobjlist.h"
 | |
| #include "viralloc.h"
 | |
| #include "virmacaddr.h"
 | |
| #include "virxml.h"
 | |
| #include "virstring.h"
 | |
| #include "vsh-table.h"
 | |
| #include "virenum.h"
 | |
| 
 | |
| VIR_ENUM_DECL(virshDomainIOError);
 | |
| VIR_ENUM_IMPL(virshDomainIOError,
 | |
|               VIR_DOMAIN_DISK_ERROR_LAST,
 | |
|               N_("no error"),
 | |
|               N_("unspecified error"),
 | |
|               N_("no space"),
 | |
| );
 | |
| 
 | |
| static const char *
 | |
| virshDomainIOErrorToString(int error)
 | |
| {
 | |
|     const char *str = virshDomainIOErrorTypeToString(error);
 | |
|     return str ? _(str) : _("unknown error");
 | |
| }
 | |
| 
 | |
| /* extract description or title from domain xml */
 | |
| char *
 | |
| virshGetDomainDescription(vshControl *ctl, virDomainPtr dom, bool title,
 | |
|                           unsigned int flags)
 | |
| {
 | |
|     char *desc = NULL;
 | |
|     g_autoptr(xmlDoc) doc = NULL;
 | |
|     g_autoptr(xmlXPathContext) ctxt = NULL;
 | |
|     int type;
 | |
| 
 | |
|     if (title)
 | |
|         type = VIR_DOMAIN_METADATA_TITLE;
 | |
|     else
 | |
|         type = VIR_DOMAIN_METADATA_DESCRIPTION;
 | |
| 
 | |
|     if ((desc = virDomainGetMetadata(dom, type, NULL, flags))) {
 | |
|         return desc;
 | |
|     } else {
 | |
|         int errCode = virGetLastErrorCode();
 | |
| 
 | |
|         if (errCode == VIR_ERR_NO_DOMAIN_METADATA) {
 | |
|             desc = g_strdup("");
 | |
|             vshResetLibvirtError();
 | |
|             return desc;
 | |
|         }
 | |
| 
 | |
|         if (errCode != VIR_ERR_NO_SUPPORT)
 | |
|             return desc;
 | |
|     }
 | |
| 
 | |
|     /* fall back to xml */
 | |
|     if (virshDomainGetXMLFromDom(ctl, dom, flags, &doc, &ctxt) < 0)
 | |
|         return NULL;
 | |
| 
 | |
|     if (title)
 | |
|         desc = virXPathString("string(./title[1])", ctxt);
 | |
|     else
 | |
|         desc = virXPathString("string(./description[1])", ctxt);
 | |
| 
 | |
|     if (!desc)
 | |
|         desc = g_strdup("");
 | |
| 
 | |
|     return desc;
 | |
| }
 | |
| 
 | |
| VIR_ENUM_DECL(virshDomainControlState);
 | |
| VIR_ENUM_IMPL(virshDomainControlState,
 | |
|               VIR_DOMAIN_CONTROL_LAST,
 | |
|               N_("ok"),
 | |
|               N_("background job"),
 | |
|               N_("occupied"),
 | |
|               N_("error"),
 | |
| );
 | |
| 
 | |
| static const char *
 | |
| virshDomainControlStateToString(int state)
 | |
| {
 | |
|     const char *str = virshDomainControlStateTypeToString(state);
 | |
|     return str ? _(str) : _("unknown");
 | |
| }
 | |
| 
 | |
| VIR_ENUM_DECL(virshDomainControlErrorReason);
 | |
| VIR_ENUM_IMPL(virshDomainControlErrorReason,
 | |
|               VIR_DOMAIN_CONTROL_ERROR_REASON_LAST,
 | |
|               "",
 | |
|               N_("unknown"),
 | |
|               N_("monitor failure"),
 | |
|               N_("internal (locking) error"),
 | |
| );
 | |
| 
 | |
| static const char *
 | |
| virshDomainControlErrorReasonToString(int reason)
 | |
| {
 | |
|     const char *ret = virshDomainControlErrorReasonTypeToString(reason);
 | |
|     return ret ? _(ret) : _("unknown");
 | |
| }
 | |
| 
 | |
| VIR_ENUM_DECL(virshDomainState);
 | |
| VIR_ENUM_IMPL(virshDomainState,
 | |
|               VIR_DOMAIN_LAST,
 | |
|               N_("no state"),
 | |
|               N_("running"),
 | |
|               N_("idle"),
 | |
|               N_("paused"),
 | |
|               N_("in shutdown"),
 | |
|               N_("shut off"),
 | |
|               N_("crashed"),
 | |
|               N_("pmsuspended"),
 | |
| );
 | |
| 
 | |
| static const char *
 | |
| virshDomainStateToString(int state)
 | |
| {
 | |
|     const char *str = virshDomainStateTypeToString(state);
 | |
|     return str ? _(str) : _("no state");
 | |
| }
 | |
| 
 | |
| VIR_ENUM_DECL(virshDomainNostateReason);
 | |
| VIR_ENUM_IMPL(virshDomainNostateReason,
 | |
|               VIR_DOMAIN_NOSTATE_LAST,
 | |
|               N_("unknown"),
 | |
| );
 | |
| 
 | |
| VIR_ENUM_DECL(virshDomainRunningReason);
 | |
| VIR_ENUM_IMPL(virshDomainRunningReason,
 | |
|               VIR_DOMAIN_RUNNING_LAST,
 | |
|               N_("unknown"),
 | |
|               N_("booted"),
 | |
|               N_("migrated"),
 | |
|               N_("restored"),
 | |
|               N_("from snapshot"),
 | |
|               N_("unpaused"),
 | |
|               N_("migration canceled"),
 | |
|               N_("save canceled"),
 | |
|               N_("event wakeup"),
 | |
|               N_("crashed"),
 | |
|               N_("post-copy"),
 | |
| );
 | |
| 
 | |
| VIR_ENUM_DECL(virshDomainBlockedReason);
 | |
| VIR_ENUM_IMPL(virshDomainBlockedReason,
 | |
|               VIR_DOMAIN_BLOCKED_LAST,
 | |
|               N_("unknown"),
 | |
| );
 | |
| 
 | |
| VIR_ENUM_DECL(virshDomainPausedReason);
 | |
| VIR_ENUM_IMPL(virshDomainPausedReason,
 | |
|               VIR_DOMAIN_PAUSED_LAST,
 | |
|               N_("unknown"),
 | |
|               N_("user"),
 | |
|               N_("migrating"),
 | |
|               N_("saving"),
 | |
|               N_("dumping"),
 | |
|               N_("I/O error"),
 | |
|               N_("watchdog"),
 | |
|               N_("from snapshot"),
 | |
|               N_("shutting down"),
 | |
|               N_("creating snapshot"),
 | |
|               N_("crashed"),
 | |
|               N_("starting up"),
 | |
|               N_("post-copy"),
 | |
|               N_("post-copy failed"),
 | |
| );
 | |
| 
 | |
| VIR_ENUM_DECL(virshDomainShutdownReason);
 | |
| VIR_ENUM_IMPL(virshDomainShutdownReason,
 | |
|               VIR_DOMAIN_SHUTDOWN_LAST,
 | |
|               N_("unknown"),
 | |
|               N_("user"),
 | |
| );
 | |
| 
 | |
| VIR_ENUM_DECL(virshDomainShutoffReason);
 | |
| VIR_ENUM_IMPL(virshDomainShutoffReason,
 | |
|               VIR_DOMAIN_SHUTOFF_LAST,
 | |
|               N_("unknown"),
 | |
|               N_("shutdown"),
 | |
|               N_("destroyed"),
 | |
|               N_("crashed"),
 | |
|               N_("migrated"),
 | |
|               N_("saved"),
 | |
|               N_("failed"),
 | |
|               N_("from snapshot"),
 | |
|               N_("daemon"),
 | |
| );
 | |
| 
 | |
| VIR_ENUM_DECL(virshDomainCrashedReason);
 | |
| VIR_ENUM_IMPL(virshDomainCrashedReason,
 | |
|               VIR_DOMAIN_CRASHED_LAST,
 | |
|               N_("unknown"),
 | |
|               N_("panicked"),
 | |
| );
 | |
| 
 | |
| VIR_ENUM_DECL(virshDomainPMSuspendedReason);
 | |
| VIR_ENUM_IMPL(virshDomainPMSuspendedReason,
 | |
|               VIR_DOMAIN_PMSUSPENDED_LAST,
 | |
|               N_("unknown"),
 | |
| );
 | |
| 
 | |
| static const char *
 | |
| virshDomainStateReasonToString(int state, int reason)
 | |
| {
 | |
|     const char *str = NULL;
 | |
|     switch ((virDomainState) state) {
 | |
|     case VIR_DOMAIN_NOSTATE:
 | |
|         str = virshDomainNostateReasonTypeToString(reason);
 | |
|         break;
 | |
|     case VIR_DOMAIN_RUNNING:
 | |
|         str = virshDomainRunningReasonTypeToString(reason);
 | |
|         break;
 | |
|     case VIR_DOMAIN_BLOCKED:
 | |
|         str = virshDomainBlockedReasonTypeToString(reason);
 | |
|         break;
 | |
|     case VIR_DOMAIN_PAUSED:
 | |
|         str = virshDomainPausedReasonTypeToString(reason);
 | |
|         break;
 | |
|     case VIR_DOMAIN_SHUTDOWN:
 | |
|         str = virshDomainShutdownReasonTypeToString(reason);
 | |
|         break;
 | |
|     case VIR_DOMAIN_SHUTOFF:
 | |
|         str = virshDomainShutoffReasonTypeToString(reason);
 | |
|         break;
 | |
|     case VIR_DOMAIN_CRASHED:
 | |
|         str = virshDomainCrashedReasonTypeToString(reason);
 | |
|         break;
 | |
|     case VIR_DOMAIN_PMSUSPENDED:
 | |
|         str = virshDomainPMSuspendedReasonTypeToString(reason);
 | |
|         break;
 | |
|     case VIR_DOMAIN_LAST:
 | |
|         ;
 | |
|     }
 | |
| 
 | |
|     return str ? _(str) : _("unknown");
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * "dommemstat" command
 | |
|  */
 | |
| static const vshCmdInfo info_dommemstat[] = {
 | |
|     {.name = "help",
 | |
|      .data = N_("get memory statistics for a domain")
 | |
|     },
 | |
|     {.name = "desc",
 | |
|      .data = N_("Get memory statistics for a running domain.")
 | |
|     },
 | |
|     {.name = NULL}
 | |
| };
 | |
| 
 | |
| static const vshCmdOptDef opts_dommemstat[] = {
 | |
|     VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_ACTIVE),
 | |
|     {.name = "period",
 | |
|      .type = VSH_OT_INT,
 | |
|      .flags = VSH_OFLAG_REQ_OPT,
 | |
|      .help = N_("period in seconds to set collection")
 | |
|     },
 | |
|     VIRSH_COMMON_OPT_CONFIG(N_("affect next boot")),
 | |
|     VIRSH_COMMON_OPT_LIVE(N_("affect running domain")),
 | |
|     VIRSH_COMMON_OPT_CURRENT(N_("affect current domain")),
 | |
|     {.name = NULL}
 | |
| };
 | |
| 
 | |
| static bool
 | |
| cmdDomMemStat(vshControl *ctl, const vshCmd *cmd)
 | |
| {
 | |
|     g_autoptr(virshDomain) dom = NULL;
 | |
|     const char *name;
 | |
|     virDomainMemoryStatStruct stats[VIR_DOMAIN_MEMORY_STAT_NR];
 | |
|     unsigned int nr_stats;
 | |
|     size_t i;
 | |
|     int rv = 0;
 | |
|     int period = -1;
 | |
|     bool config = vshCommandOptBool(cmd, "config");
 | |
|     bool live = vshCommandOptBool(cmd, "live");
 | |
|     bool current = vshCommandOptBool(cmd, "current");
 | |
|     unsigned int flags = VIR_DOMAIN_AFFECT_CURRENT;
 | |
| 
 | |
|     VSH_EXCLUSIVE_OPTIONS_VAR(current, live);
 | |
|     VSH_EXCLUSIVE_OPTIONS_VAR(current, config);
 | |
|     if (config)
 | |
|         flags |= VIR_DOMAIN_AFFECT_CONFIG;
 | |
|     if (live)
 | |
|         flags |= VIR_DOMAIN_AFFECT_LIVE;
 | |
| 
 | |
|     if (!(dom = virshCommandOptDomain(ctl, cmd, &name)))
 | |
|         return false;
 | |
| 
 | |
|     /* If none of the options were specified and we're active
 | |
|      * then be sure to allow active modification */
 | |
|     if (!current && !live && !config && virDomainIsActive(dom) == 1)
 | |
|         flags |= VIR_DOMAIN_AFFECT_LIVE;
 | |
| 
 | |
|     /* Providing a period will adjust the balloon driver collection period.
 | |
|      * This is not really an unsigned long, but it
 | |
|      */
 | |
|     if ((rv = vshCommandOptInt(ctl, cmd, "period", &period)) < 0)
 | |
|         return false;
 | |
|     if (rv > 0) {
 | |
|         if (period < 0) {
 | |
|             vshError(ctl, _("Invalid collection period value '%d'"), period);
 | |
|             return false;
 | |
|         }
 | |
| 
 | |
|         if (virDomainSetMemoryStatsPeriod(dom, period, flags) < 0) {
 | |
|             vshError(ctl, "%s",
 | |
|                      _("Unable to change balloon collection period."));
 | |
|             return false;
 | |
|         }
 | |
|         return true;
 | |
|     }
 | |
| 
 | |
|     nr_stats = virDomainMemoryStats(dom, stats, VIR_DOMAIN_MEMORY_STAT_NR, 0);
 | |
|     if (nr_stats == -1) {
 | |
|         vshError(ctl, _("Failed to get memory statistics for domain %s"), name);
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     for (i = 0; i < nr_stats; i++) {
 | |
|         if (stats[i].tag == VIR_DOMAIN_MEMORY_STAT_SWAP_IN)
 | |
|             vshPrint(ctl, "swap_in %llu\n", stats[i].val);
 | |
|         if (stats[i].tag == VIR_DOMAIN_MEMORY_STAT_SWAP_OUT)
 | |
|             vshPrint(ctl, "swap_out %llu\n", stats[i].val);
 | |
|         if (stats[i].tag == VIR_DOMAIN_MEMORY_STAT_MAJOR_FAULT)
 | |
|             vshPrint(ctl, "major_fault %llu\n", stats[i].val);
 | |
|         if (stats[i].tag == VIR_DOMAIN_MEMORY_STAT_MINOR_FAULT)
 | |
|             vshPrint(ctl, "minor_fault %llu\n", stats[i].val);
 | |
|         if (stats[i].tag == VIR_DOMAIN_MEMORY_STAT_UNUSED)
 | |
|             vshPrint(ctl, "unused %llu\n", stats[i].val);
 | |
|         if (stats[i].tag == VIR_DOMAIN_MEMORY_STAT_AVAILABLE)
 | |
|             vshPrint(ctl, "available %llu\n", stats[i].val);
 | |
|         if (stats[i].tag == VIR_DOMAIN_MEMORY_STAT_USABLE)
 | |
|             vshPrint(ctl, "usable %llu\n", stats[i].val);
 | |
|         if (stats[i].tag == VIR_DOMAIN_MEMORY_STAT_ACTUAL_BALLOON)
 | |
|             vshPrint(ctl, "actual %llu\n", stats[i].val);
 | |
|         if (stats[i].tag == VIR_DOMAIN_MEMORY_STAT_RSS)
 | |
|             vshPrint(ctl, "rss %llu\n", stats[i].val);
 | |
|         if (stats[i].tag == VIR_DOMAIN_MEMORY_STAT_LAST_UPDATE)
 | |
|             vshPrint(ctl, "last_update %llu\n", stats[i].val);
 | |
|         if (stats[i].tag == VIR_DOMAIN_MEMORY_STAT_DISK_CACHES)
 | |
|             vshPrint(ctl, "disk_caches %llu\n", stats[i].val);
 | |
|         if (stats[i].tag == VIR_DOMAIN_MEMORY_STAT_HUGETLB_PGALLOC)
 | |
|             vshPrint(ctl, "hugetlb_pgalloc %llu\n", stats[i].val);
 | |
|         if (stats[i].tag == VIR_DOMAIN_MEMORY_STAT_HUGETLB_PGFAIL)
 | |
|             vshPrint(ctl, "hugetlb_pgfail %llu\n", stats[i].val);
 | |
|     }
 | |
| 
 | |
|     return true;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * "domblkinfo" command
 | |
|  */
 | |
| static const vshCmdInfo info_domblkinfo[] = {
 | |
|     {.name = "help",
 | |
|      .data = N_("domain block device size information")
 | |
|     },
 | |
|     {.name = "desc",
 | |
|      .data = N_("Get block device size info for a domain.")
 | |
|     },
 | |
|     {.name = NULL}
 | |
| };
 | |
| 
 | |
| static const vshCmdOptDef opts_domblkinfo[] = {
 | |
|     VIRSH_COMMON_OPT_DOMAIN_FULL(0),
 | |
|     {.name = "device",
 | |
|      .type = VSH_OT_STRING,
 | |
|      .completer = virshDomainDiskTargetCompleter,
 | |
|      .help = N_("block device")
 | |
|     },
 | |
|     {.name = "human",
 | |
|      .type = VSH_OT_BOOL,
 | |
|      .help = N_("Human readable output")
 | |
|     },
 | |
|     {.name = "all",
 | |
|      .type = VSH_OT_BOOL,
 | |
|      .help = N_("display all block devices info")
 | |
|     },
 | |
|     {.name = NULL}
 | |
| };
 | |
| 
 | |
| static bool
 | |
| cmdDomblkinfoGet(const virDomainBlockInfo *info,
 | |
|                  char **cap,
 | |
|                  char **alloc,
 | |
|                  char **phy,
 | |
|                  bool human)
 | |
| {
 | |
|     if (info->capacity == 0 && info->allocation == 0 && info->physical == 0) {
 | |
|         *cap = g_strdup("-");
 | |
|         *alloc = g_strdup("-");
 | |
|         *phy = g_strdup("-");
 | |
|     } else if (!human) {
 | |
|         *cap = g_strdup_printf("%llu", info->capacity);
 | |
|         *alloc = g_strdup_printf("%llu", info->allocation);
 | |
|         *phy = g_strdup_printf("%llu", info->physical);
 | |
|     } else {
 | |
|         double val_cap, val_alloc, val_phy;
 | |
|         const char *unit_cap, *unit_alloc, *unit_phy;
 | |
| 
 | |
|         val_cap = vshPrettyCapacity(info->capacity, &unit_cap);
 | |
|         val_alloc = vshPrettyCapacity(info->allocation, &unit_alloc);
 | |
|         val_phy = vshPrettyCapacity(info->physical, &unit_phy);
 | |
| 
 | |
|         *cap = g_strdup_printf("%.3lf %s", val_cap, unit_cap);
 | |
|         *alloc = g_strdup_printf("%.3lf %s", val_alloc, unit_alloc);
 | |
|         *phy = g_strdup_printf("%.3lf %s", val_phy, unit_phy);
 | |
|     }
 | |
| 
 | |
|     return true;
 | |
| }
 | |
| 
 | |
| 
 | |
| static bool
 | |
| cmdDomblkinfo(vshControl *ctl, const vshCmd *cmd)
 | |
| {
 | |
|     virDomainBlockInfo info;
 | |
|     g_autoptr(virshDomain) dom = NULL;
 | |
|     bool human = false;
 | |
|     bool all = false;
 | |
|     const char *device = NULL;
 | |
|     g_autoptr(xmlDoc) xmldoc = NULL;
 | |
|     g_autoptr(xmlXPathContext) ctxt = NULL;
 | |
|     int ndisks;
 | |
|     size_t i;
 | |
|     g_autofree xmlNodePtr *disks = NULL;
 | |
|     g_autoptr(vshTable) table = NULL;
 | |
| 
 | |
|     VSH_EXCLUSIVE_OPTIONS("all", "device");
 | |
| 
 | |
|     if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
 | |
|         return false;
 | |
| 
 | |
|     all = vshCommandOptBool(cmd, "all");
 | |
|     if (!all && vshCommandOptStringQuiet(ctl, cmd, "device", &device) <= 0) {
 | |
|         vshError(ctl, "command 'domblkinfo' requires <device> option");
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     human = vshCommandOptBool(cmd, "human");
 | |
| 
 | |
|     if (all) {
 | |
|         bool active = virDomainIsActive(dom) == 1;
 | |
|         int rc;
 | |
| 
 | |
|         if (virshDomainGetXML(ctl, cmd, 0, &xmldoc, &ctxt) < 0)
 | |
|             return false;
 | |
| 
 | |
|         ndisks = virXPathNodeSet("./devices/disk", ctxt, &disks);
 | |
|         if (ndisks < 0)
 | |
|             return false;
 | |
| 
 | |
|         /* title */
 | |
|         table = vshTableNew(_("Target"), _("Capacity"), _("Allocation"), _("Physical"), NULL);
 | |
|         if (!table)
 | |
|             return false;
 | |
| 
 | |
|         for (i = 0; i < ndisks; i++) {
 | |
|             g_autofree char *target = NULL;
 | |
|             g_autofree char *protocol = NULL;
 | |
|             g_autofree char *cap = NULL;
 | |
|             g_autofree char *alloc = NULL;
 | |
|             g_autofree char *phy = NULL;
 | |
| 
 | |
|             ctxt->node = disks[i];
 | |
|             protocol = virXPathString("string(./source/@protocol)", ctxt);
 | |
|             target = virXPathString("string(./target/@dev)", ctxt);
 | |
| 
 | |
|             if (virXPathBoolean("boolean(./source)", ctxt) == 1) {
 | |
| 
 | |
|                 rc = virDomainGetBlockInfo(dom, target, &info, 0);
 | |
| 
 | |
|                 if (rc < 0) {
 | |
|                     /* If protocol is present that's an indication of a
 | |
|                      * networked storage device which cannot provide statistics,
 | |
|                      * so generate 0 based data and get the next disk. */
 | |
|                     if (protocol && !active &&
 | |
|                         virGetLastErrorCode() == VIR_ERR_INTERNAL_ERROR &&
 | |
|                         virGetLastErrorDomain() == VIR_FROM_STORAGE) {
 | |
|                         memset(&info, 0, sizeof(info));
 | |
|                         vshResetLibvirtError();
 | |
|                     } else {
 | |
|                         return false;
 | |
|                     }
 | |
|                 }
 | |
|             } else {
 | |
|                 /* if we don't call virDomainGetBlockInfo() who clears 'info'
 | |
|                  * we have to do it manually */
 | |
|                 memset(&info, 0, sizeof(info));
 | |
|             }
 | |
| 
 | |
|             if (!cmdDomblkinfoGet(&info, &cap, &alloc, &phy, human))
 | |
|                 return false;
 | |
|             if (vshTableRowAppend(table, target, cap, alloc, phy, NULL) < 0)
 | |
|                 return false;
 | |
|         }
 | |
| 
 | |
|         vshTablePrintToStdout(table, ctl);
 | |
| 
 | |
|     } else {
 | |
|         g_autofree char *cap = NULL;
 | |
|         g_autofree char *alloc = NULL;
 | |
|         g_autofree char *phy = NULL;
 | |
| 
 | |
|         if (virDomainGetBlockInfo(dom, device, &info, 0) < 0)
 | |
|             return false;
 | |
| 
 | |
|         if (!cmdDomblkinfoGet(&info, &cap, &alloc, &phy, human))
 | |
|             return false;
 | |
|         vshPrint(ctl, "%-15s %s\n", _("Capacity:"), cap);
 | |
|         vshPrint(ctl, "%-15s %s\n", _("Allocation:"), alloc);
 | |
|         vshPrint(ctl, "%-15s %s\n", _("Physical:"), phy);
 | |
|     }
 | |
| 
 | |
|     return true;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * "domblklist" command
 | |
|  */
 | |
| static const vshCmdInfo info_domblklist[] = {
 | |
|     {.name = "help",
 | |
|      .data = N_("list all domain blocks")
 | |
|     },
 | |
|     {.name = "desc",
 | |
|      .data = N_("Get the summary of block devices for a domain.")
 | |
|     },
 | |
|     {.name = NULL}
 | |
| };
 | |
| 
 | |
| static const vshCmdOptDef opts_domblklist[] = {
 | |
|     VIRSH_COMMON_OPT_DOMAIN_FULL(0),
 | |
|     {.name = "inactive",
 | |
|      .type = VSH_OT_BOOL,
 | |
|      .help = N_("get inactive rather than running configuration")
 | |
|     },
 | |
|     {.name = "details",
 | |
|      .type = VSH_OT_BOOL,
 | |
|      .help = N_("additionally display the type and device value")
 | |
|     },
 | |
|     {.name = NULL}
 | |
| };
 | |
| 
 | |
| static bool
 | |
| cmdDomblklist(vshControl *ctl, const vshCmd *cmd)
 | |
| {
 | |
|     unsigned int flags = 0;
 | |
|     g_autoptr(xmlDoc) xmldoc = NULL;
 | |
|     g_autoptr(xmlXPathContext) ctxt = NULL;
 | |
|     int ndisks;
 | |
|     g_autofree xmlNodePtr *disks = NULL;
 | |
|     size_t i;
 | |
|     bool details = false;
 | |
|     g_autoptr(vshTable) table = NULL;
 | |
| 
 | |
|     if (vshCommandOptBool(cmd, "inactive"))
 | |
|         flags |= VIR_DOMAIN_XML_INACTIVE;
 | |
| 
 | |
|     details = vshCommandOptBool(cmd, "details");
 | |
| 
 | |
|     if (virshDomainGetXML(ctl, cmd, flags, &xmldoc, &ctxt) < 0)
 | |
|         return false;
 | |
| 
 | |
|     ndisks = virXPathNodeSet("./devices/disk", ctxt, &disks);
 | |
|     if (ndisks < 0)
 | |
|         return false;
 | |
| 
 | |
|     if (details)
 | |
|         table = vshTableNew(_("Type"), _("Device"), _("Target"), _("Source"), NULL);
 | |
|     else
 | |
|         table = vshTableNew(_("Target"), _("Source"), NULL);
 | |
| 
 | |
|     if (!table)
 | |
|         return false;
 | |
| 
 | |
|     for (i = 0; i < ndisks; i++) {
 | |
|         g_autofree char *type = NULL;
 | |
|         g_autofree char *device = NULL;
 | |
|         g_autofree char *target = NULL;
 | |
|         g_autofree char *source = NULL;
 | |
| 
 | |
|         ctxt->node = disks[i];
 | |
| 
 | |
|         type = virXPathString("string(./@type)", ctxt);
 | |
|         if (details) {
 | |
|             device = virXPathString("string(./@device)", ctxt);
 | |
|             if (!type || !device) {
 | |
|                 vshPrint(ctl, "unable to query block list details");
 | |
|                 return false;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         target = virXPathString("string(./target/@dev)", ctxt);
 | |
|         if (!target) {
 | |
|             vshError(ctl, "unable to query block list");
 | |
|             return false;
 | |
|         }
 | |
| 
 | |
|         if (STREQ_NULLABLE(type, "nvme")) {
 | |
|             g_autofree char *namespace = NULL;
 | |
|             virPCIDeviceAddress addr = { 0 };
 | |
|             xmlNodePtr addrNode = NULL;
 | |
| 
 | |
|             if (!(namespace = virXPathString("string(./source/@namespace)", ctxt)) ||
 | |
|                 !(addrNode = virXPathNode("./source/address", ctxt)) ||
 | |
|                 virPCIDeviceAddressParseXML(addrNode, &addr) < 0) {
 | |
|                 vshError(ctl, "Unable to query NVMe disk address");
 | |
|                 return false;
 | |
|             }
 | |
| 
 | |
|             source = g_strdup_printf("nvme://%04x:%02x:%02x.%d/%s",
 | |
|                                      addr.domain, addr.bus, addr.slot,
 | |
|                                      addr.function, namespace);
 | |
|         } else {
 | |
|             source = virXPathString("string(./source/@file"
 | |
|                                     "|./source/@dev"
 | |
|                                     "|./source/@dir"
 | |
|                                     "|./source/@name"
 | |
|                                     "|./source/@volume"
 | |
|                                     "|./source/@path)", ctxt);
 | |
|         }
 | |
| 
 | |
|         if (details) {
 | |
|             if (vshTableRowAppend(table, type, device, target,
 | |
|                                   NULLSTR_MINUS(source), NULL) < 0)
 | |
|                 return false;
 | |
|         } else {
 | |
|             if (vshTableRowAppend(table, target,
 | |
|                                   NULLSTR_MINUS(source), NULL) < 0)
 | |
|                 return false;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     vshTablePrintToStdout(table, ctl);
 | |
| 
 | |
|     return true;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * "domiflist" command
 | |
|  */
 | |
| static const vshCmdInfo info_domiflist[] = {
 | |
|     {"help", N_("list all domain virtual interfaces")},
 | |
|     {"desc", N_("Get the summary of virtual interfaces for a domain.")},
 | |
|     {NULL, NULL}
 | |
| };
 | |
| 
 | |
| static const vshCmdOptDef opts_domiflist[] = {
 | |
|     VIRSH_COMMON_OPT_DOMAIN_FULL(0),
 | |
|     {.name = "inactive",
 | |
|      .type = VSH_OT_BOOL,
 | |
|      .help = N_("get inactive rather than running configuration")
 | |
|     },
 | |
|     {.name = NULL}
 | |
| };
 | |
| 
 | |
| static bool
 | |
| cmdDomiflist(vshControl *ctl, const vshCmd *cmd)
 | |
| {
 | |
|     unsigned int flags = 0;
 | |
|     g_autoptr(xmlDoc) xmldoc = NULL;
 | |
|     g_autoptr(xmlXPathContext) ctxt = NULL;
 | |
|     int ninterfaces;
 | |
|     g_autofree xmlNodePtr *interfaces = NULL;
 | |
|     size_t i;
 | |
|     g_autoptr(vshTable) table = NULL;
 | |
| 
 | |
|     if (vshCommandOptBool(cmd, "inactive"))
 | |
|         flags |= VIR_DOMAIN_XML_INACTIVE;
 | |
| 
 | |
|     if (virshDomainGetXML(ctl, cmd, flags, &xmldoc, &ctxt) < 0)
 | |
|         return false;
 | |
| 
 | |
|     ninterfaces = virXPathNodeSet("./devices/interface", ctxt, &interfaces);
 | |
|     if (ninterfaces < 0)
 | |
|         return false;
 | |
| 
 | |
|     table = vshTableNew(_("Interface"), _("Type"),
 | |
|                         _("Source"), _("Model"), _("MAC"), NULL);
 | |
|     if (!table)
 | |
|         return false;
 | |
| 
 | |
|     for (i = 0; i < ninterfaces; i++) {
 | |
|         g_autofree char *type = NULL;
 | |
|         g_autofree char *source = NULL;
 | |
|         g_autofree char *target = NULL;
 | |
|         g_autofree char *model = NULL;
 | |
|         g_autofree char *mac = NULL;
 | |
| 
 | |
|         ctxt->node = interfaces[i];
 | |
|         type = virXPathString("string(./@type)", ctxt);
 | |
| 
 | |
|         source = virXPathString("string(./source/@bridge"
 | |
|                                 "|./source/@dev"
 | |
|                                 "|./source/@network"
 | |
|                                 "|./source/@name"
 | |
|                                 "|./source/@path)", ctxt);
 | |
| 
 | |
|         target = virXPathString("string(./target/@dev)", ctxt);
 | |
|         model = virXPathString("string(./model/@type)", ctxt);
 | |
|         mac = virXPathString("string(./mac/@address)", ctxt);
 | |
| 
 | |
|         if (vshTableRowAppend(table,
 | |
|                               target ? target : "-",
 | |
|                               type,
 | |
|                               source ? source : "-",
 | |
|                               model ? model : "-",
 | |
|                               mac ? mac : "-",
 | |
|                               NULL) < 0)
 | |
|             return false;
 | |
|     }
 | |
| 
 | |
|     vshTablePrintToStdout(table, ctl);
 | |
| 
 | |
|     return true;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * "domif-getlink" command
 | |
|  */
 | |
| static const vshCmdInfo info_domif_getlink[] = {
 | |
|     {.name = "help",
 | |
|      .data = N_("get link state of a virtual interface")
 | |
|     },
 | |
|     {.name = "desc",
 | |
|      .data = N_("Get link state of a domain's virtual interface.")
 | |
|     },
 | |
|     {.name = NULL}
 | |
| };
 | |
| 
 | |
| static const vshCmdOptDef opts_domif_getlink[] = {
 | |
|     VIRSH_COMMON_OPT_DOMAIN_FULL(0),
 | |
|     {.name = "interface",
 | |
|      .type = VSH_OT_DATA,
 | |
|      .flags = VSH_OFLAG_REQ,
 | |
|      .completer = virshDomainInterfaceCompleter,
 | |
|      .help = N_("interface device (MAC Address)")
 | |
|     },
 | |
|     {.name = "persistent",
 | |
|      .type = VSH_OT_ALIAS,
 | |
|      .help = "config"
 | |
|     },
 | |
|     VIRSH_COMMON_OPT_CONFIG(N_("Get persistent interface state")),
 | |
|     {.name = NULL}
 | |
| };
 | |
| 
 | |
| static bool
 | |
| cmdDomIfGetLink(vshControl *ctl, const vshCmd *cmd)
 | |
| {
 | |
|     const char *iface = NULL;
 | |
|     g_autofree char *state = NULL;
 | |
|     g_autofree char *xpath = NULL;
 | |
|     virMacAddr macaddr;
 | |
|     char macstr[VIR_MAC_STRING_BUFLEN] = "";
 | |
|     g_autoptr(xmlDoc) xml = NULL;
 | |
|     g_autoptr(xmlXPathContext) ctxt = NULL;
 | |
|     g_autofree xmlNodePtr *interfaces = NULL;
 | |
|     int ninterfaces;
 | |
|     unsigned int flags = 0;
 | |
| 
 | |
|     if (vshCommandOptStringReq(ctl, cmd, "interface", &iface) < 0)
 | |
|         return false;
 | |
| 
 | |
|     if (vshCommandOptBool(cmd, "config"))
 | |
|         flags = VIR_DOMAIN_XML_INACTIVE;
 | |
| 
 | |
|     if (virshDomainGetXML(ctl, cmd, flags, &xml, &ctxt) < 0)
 | |
|         return false;
 | |
| 
 | |
|     /* normalize the mac addr */
 | |
|     if (virMacAddrParse(iface, &macaddr) == 0)
 | |
|         virMacAddrFormat(&macaddr, macstr);
 | |
| 
 | |
|     xpath = g_strdup_printf("/domain/devices/interface[(mac/@address = '%s') or "
 | |
|                             "                          (target/@dev = '%s')]", macstr,
 | |
|                             iface);
 | |
| 
 | |
|     if ((ninterfaces = virXPathNodeSet(xpath, ctxt, &interfaces)) < 0) {
 | |
|         vshError(ctl, _("Failed to extract interface information"));
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     if (ninterfaces < 1) {
 | |
|         if (macstr[0])
 | |
|             vshError(ctl, _("Interface (mac: %s) not found."), macstr);
 | |
|         else
 | |
|             vshError(ctl, _("Interface (dev: %s) not found."), iface);
 | |
| 
 | |
|         return false;
 | |
|     } else if (ninterfaces > 1) {
 | |
|         vshError(ctl, _("multiple matching interfaces found"));
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     ctxt->node = interfaces[0];
 | |
| 
 | |
|     if ((state = virXPathString("string(./link/@state)", ctxt)))
 | |
|         vshPrint(ctl, "%s %s", iface, state);
 | |
|     else
 | |
|         vshPrint(ctl, "%s up", iface);
 | |
| 
 | |
|     return true;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * "domcontrol" command
 | |
|  */
 | |
| static const vshCmdInfo info_domcontrol[] = {
 | |
|     {.name = "help",
 | |
|      .data = N_("domain control interface state")
 | |
|     },
 | |
|     {.name = "desc",
 | |
|      .data = N_("Returns state of a control interface to the domain.")
 | |
|     },
 | |
|     {.name = NULL}
 | |
| };
 | |
| 
 | |
| static const vshCmdOptDef opts_domcontrol[] = {
 | |
|     VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_ACTIVE),
 | |
|     {.name = NULL}
 | |
| };
 | |
| 
 | |
| static bool
 | |
| cmdDomControl(vshControl *ctl, const vshCmd *cmd)
 | |
| {
 | |
|     g_autoptr(virshDomain) dom = NULL;
 | |
|     virDomainControlInfo info;
 | |
| 
 | |
|     if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
 | |
|         return false;
 | |
| 
 | |
|     if (virDomainGetControlInfo(dom, &info, 0) < 0)
 | |
|         return false;
 | |
| 
 | |
|     if (info.state != VIR_DOMAIN_CONTROL_OK &&
 | |
|         info.state != VIR_DOMAIN_CONTROL_ERROR) {
 | |
|         vshPrint(ctl, "%s (%0.3fs)\n",
 | |
|                  virshDomainControlStateToString(info.state),
 | |
|                  info.stateTime / 1000.0);
 | |
|     } else if (info.state == VIR_DOMAIN_CONTROL_ERROR && info.details > 0) {
 | |
|         vshPrint(ctl, "%s: %s\n",
 | |
|                  virshDomainControlStateToString(info.state),
 | |
|                  virshDomainControlErrorReasonToString(info.details));
 | |
|     } else {
 | |
|         vshPrint(ctl, "%s\n",
 | |
|                  virshDomainControlStateToString(info.state));
 | |
|     }
 | |
| 
 | |
|     return true;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * "domblkstat" command
 | |
|  */
 | |
| static const vshCmdInfo info_domblkstat[] = {
 | |
|     {.name = "help",
 | |
|      .data = N_("get device block stats for a domain")
 | |
|     },
 | |
|     {.name = "desc",
 | |
|      .data = N_("Get device block stats for a running domain. See man page or "
 | |
|                 "use --human for explanation of fields")
 | |
|     },
 | |
|     {.name = NULL}
 | |
| };
 | |
| 
 | |
| static const vshCmdOptDef opts_domblkstat[] = {
 | |
|     VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_ACTIVE),
 | |
|     {.name = "device",
 | |
|      .type = VSH_OT_STRING,
 | |
|      .flags = VSH_OFLAG_EMPTY_OK,
 | |
|      .completer = virshDomainDiskTargetCompleter,
 | |
|      .help = N_("block device")
 | |
|     },
 | |
|     {.name = "human",
 | |
|      .type = VSH_OT_BOOL,
 | |
|      .help = N_("print a more human readable output")
 | |
|     },
 | |
|     {.name = NULL}
 | |
| };
 | |
| 
 | |
| struct _domblkstat_sequence {
 | |
|     const char *field;  /* field name */
 | |
|     const char *legacy; /* legacy name from previous releases */
 | |
|     const char *human;  /* human-friendly explanation */
 | |
| };
 | |
| 
 | |
| /* sequence of values for output to honor legacy format from previous
 | |
|  * versions */
 | |
| static const struct _domblkstat_sequence domblkstat_output[] = {
 | |
|     { VIR_DOMAIN_BLOCK_STATS_READ_REQ,          "rd_req",
 | |
|       N_("number of read operations:") }, /* 0 */
 | |
|     { VIR_DOMAIN_BLOCK_STATS_READ_BYTES,        "rd_bytes",
 | |
|       N_("number of bytes read:") }, /* 1 */
 | |
|     { VIR_DOMAIN_BLOCK_STATS_WRITE_REQ,         "wr_req",
 | |
|       N_("number of write operations:") }, /* 2 */
 | |
|     { VIR_DOMAIN_BLOCK_STATS_WRITE_BYTES,       "wr_bytes",
 | |
|       N_("number of bytes written:") }, /* 3 */
 | |
|     { VIR_DOMAIN_BLOCK_STATS_ERRS,              "errs",
 | |
|       N_("error count:") }, /* 4 */
 | |
|     { VIR_DOMAIN_BLOCK_STATS_FLUSH_REQ,         NULL,
 | |
|       N_("number of flush operations:") }, /* 5 */
 | |
|     { VIR_DOMAIN_BLOCK_STATS_READ_TOTAL_TIMES,  NULL,
 | |
|       N_("total duration of reads (ns):") }, /* 6 */
 | |
|     { VIR_DOMAIN_BLOCK_STATS_WRITE_TOTAL_TIMES, NULL,
 | |
|       N_("total duration of writes (ns):") }, /* 7 */
 | |
|     { VIR_DOMAIN_BLOCK_STATS_FLUSH_TOTAL_TIMES, NULL,
 | |
|       N_("total duration of flushes (ns):") }, /* 8 */
 | |
|     { NULL, NULL, NULL }
 | |
| };
 | |
| 
 | |
| #define DOMBLKSTAT_LEGACY_PRINT(ID, VALUE) \
 | |
|     if (VALUE >= 0) \
 | |
|         vshPrint(ctl, "%s %-*s %lld\n", device, \
 | |
|                  human ? 31 : 0, \
 | |
|                  human ? _(domblkstat_output[ID].human) \
 | |
|                  : domblkstat_output[ID].legacy, \
 | |
|                  VALUE);
 | |
| 
 | |
| static bool
 | |
| cmdDomblkstat(vshControl *ctl, const vshCmd *cmd)
 | |
| {
 | |
|     g_autoptr(virshDomain) dom = NULL;
 | |
|     const char *name = NULL, *device = NULL;
 | |
|     virDomainBlockStatsStruct stats;
 | |
|     g_autofree virTypedParameterPtr params = NULL;
 | |
|     virTypedParameterPtr par = NULL;
 | |
|     const char *field = NULL;
 | |
|     int rc, nparams = 0;
 | |
|     size_t i;
 | |
|     bool human = vshCommandOptBool(cmd, "human"); /* human readable output */
 | |
| 
 | |
|     if (!(dom = virshCommandOptDomain(ctl, cmd, &name)))
 | |
|         return false;
 | |
| 
 | |
|     /* device argument is optional now. if it's missing, supply empty
 | |
|        string to denote 'all devices'. A NULL device arg would violate
 | |
|        API contract.
 | |
|      */
 | |
|     if (vshCommandOptStringReq(ctl, cmd, "device", &device) < 0)
 | |
|         return false;
 | |
| 
 | |
|     if (!device)
 | |
|         device = "";
 | |
| 
 | |
|     rc = virDomainBlockStatsFlags(dom, device, NULL, &nparams, 0);
 | |
| 
 | |
|     /* It might fail when virDomainBlockStatsFlags is not
 | |
|      * supported on older libvirt, fallback to use virDomainBlockStats
 | |
|      * then.
 | |
|      */
 | |
|     if (rc < 0) {
 | |
|         /* try older API if newer is not supported */
 | |
|         if (last_error->code != VIR_ERR_NO_SUPPORT)
 | |
|             return false;
 | |
| 
 | |
|         vshResetLibvirtError();
 | |
| 
 | |
|         if (virDomainBlockStats(dom, device, &stats,
 | |
|                                 sizeof(stats)) == -1) {
 | |
|             vshError(ctl, _("Failed to get block stats %s %s"),
 | |
|                      name, device);
 | |
|             return false;
 | |
|         }
 | |
| 
 | |
|         /* human friendly output */
 | |
|         if (human) {
 | |
|             vshPrint(ctl, N_("Device: %s\n"), device);
 | |
|             device = "";
 | |
|         }
 | |
| 
 | |
|         DOMBLKSTAT_LEGACY_PRINT(0, stats.rd_req);
 | |
|         DOMBLKSTAT_LEGACY_PRINT(1, stats.rd_bytes);
 | |
|         DOMBLKSTAT_LEGACY_PRINT(2, stats.wr_req);
 | |
|         DOMBLKSTAT_LEGACY_PRINT(3, stats.wr_bytes);
 | |
|         DOMBLKSTAT_LEGACY_PRINT(4, stats.errs);
 | |
|     } else {
 | |
|         params = g_new0(virTypedParameter, nparams);
 | |
|         if (virDomainBlockStatsFlags(dom, device, params, &nparams, 0) < 0) {
 | |
|             vshError(ctl, _("Failed to get block stats for domain '%s' device '%s'"), name, device);
 | |
|             return false;
 | |
|         }
 | |
| 
 | |
|         /* set for prettier output */
 | |
|         if (human) {
 | |
|             vshPrint(ctl, N_("Device: %s\n"), device);
 | |
|             device = "";
 | |
|         }
 | |
| 
 | |
|         /* at first print all known values in desired order */
 | |
|         for (i = 0; domblkstat_output[i].field != NULL; i++) {
 | |
|             g_autofree char *value = NULL;
 | |
| 
 | |
|             if (!(par = virTypedParamsGet(params, nparams,
 | |
|                                           domblkstat_output[i].field)))
 | |
|                 continue;
 | |
| 
 | |
|             value = vshGetTypedParamValue(ctl, par);
 | |
| 
 | |
|             /* to print other not supported fields, mark the already printed */
 | |
|             par->field[0] = '\0'; /* set the name to empty string */
 | |
| 
 | |
|             /* translate into human readable or legacy spelling */
 | |
|             field = NULL;
 | |
|             if (human)
 | |
|                 field = _(domblkstat_output[i].human);
 | |
|             else
 | |
|                 field = domblkstat_output[i].legacy;
 | |
| 
 | |
|             /* use the provided spelling if no translation is available */
 | |
|             if (!field)
 | |
|                 field = domblkstat_output[i].field;
 | |
| 
 | |
|             vshPrint(ctl, "%s %-*s %s\n", device,
 | |
|                      human ? 31 : 0, field, value);
 | |
|         }
 | |
| 
 | |
|         /* go through the fields again, for remaining fields */
 | |
|         for (i = 0; i < nparams; i++) {
 | |
|             g_autofree char *value = NULL;
 | |
| 
 | |
|             if (!*params[i].field)
 | |
|                 continue;
 | |
| 
 | |
|             value = vshGetTypedParamValue(ctl, params+i);
 | |
|             vshPrint(ctl, "%s %s %s\n", device, params[i].field, value);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     return true;
 | |
| }
 | |
| #undef DOMBLKSTAT_LEGACY_PRINT
 | |
| 
 | |
| /*
 | |
|  * "domifstat" command
 | |
|  */
 | |
| static const vshCmdInfo info_domifstat[] = {
 | |
|     {.name = "help",
 | |
|      .data = N_("get network interface stats for a domain")
 | |
|     },
 | |
|     {.name = "desc",
 | |
|      .data = N_("Get network interface stats for a running domain.")
 | |
|     },
 | |
|     {.name = NULL}
 | |
| };
 | |
| 
 | |
| static const vshCmdOptDef opts_domifstat[] = {
 | |
|     VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_ACTIVE),
 | |
|     {.name = "interface",
 | |
|      .type = VSH_OT_DATA,
 | |
|      .flags = VSH_OFLAG_REQ,
 | |
|      .completer = virshDomainInterfaceCompleter,
 | |
|      .help = N_("interface device specified by name or MAC Address")
 | |
|     },
 | |
|     {.name = NULL}
 | |
| };
 | |
| 
 | |
| static bool
 | |
| cmdDomIfstat(vshControl *ctl, const vshCmd *cmd)
 | |
| {
 | |
|     g_autoptr(virshDomain) dom = NULL;
 | |
|     const char *name = NULL, *device = NULL;
 | |
|     virDomainInterfaceStatsStruct stats;
 | |
| 
 | |
|     if (!(dom = virshCommandOptDomain(ctl, cmd, &name)))
 | |
|         return false;
 | |
| 
 | |
|     if (vshCommandOptStringReq(ctl, cmd, "interface", &device) < 0)
 | |
|         return false;
 | |
| 
 | |
|     if (virDomainInterfaceStats(dom, device, &stats, sizeof(stats)) == -1) {
 | |
|         vshError(ctl, _("Failed to get interface stats %s %s"), name, device);
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     if (stats.rx_bytes >= 0)
 | |
|         vshPrint(ctl, "%s rx_bytes %lld\n", device, stats.rx_bytes);
 | |
| 
 | |
|     if (stats.rx_packets >= 0)
 | |
|         vshPrint(ctl, "%s rx_packets %lld\n", device, stats.rx_packets);
 | |
| 
 | |
|     if (stats.rx_errs >= 0)
 | |
|         vshPrint(ctl, "%s rx_errs %lld\n", device, stats.rx_errs);
 | |
| 
 | |
|     if (stats.rx_drop >= 0)
 | |
|         vshPrint(ctl, "%s rx_drop %lld\n", device, stats.rx_drop);
 | |
| 
 | |
|     if (stats.tx_bytes >= 0)
 | |
|         vshPrint(ctl, "%s tx_bytes %lld\n", device, stats.tx_bytes);
 | |
| 
 | |
|     if (stats.tx_packets >= 0)
 | |
|         vshPrint(ctl, "%s tx_packets %lld\n", device, stats.tx_packets);
 | |
| 
 | |
|     if (stats.tx_errs >= 0)
 | |
|         vshPrint(ctl, "%s tx_errs %lld\n", device, stats.tx_errs);
 | |
| 
 | |
|     if (stats.tx_drop >= 0)
 | |
|         vshPrint(ctl, "%s tx_drop %lld\n", device, stats.tx_drop);
 | |
| 
 | |
|     return true;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * "domblkerror" command
 | |
|  */
 | |
| static const vshCmdInfo info_domblkerror[] = {
 | |
|     {.name = "help",
 | |
|      .data = N_("Show errors on block devices")
 | |
|     },
 | |
|     {.name = "desc",
 | |
|      .data = N_("Show block device errors")
 | |
|     },
 | |
|     {.name = NULL}
 | |
| };
 | |
| 
 | |
| static const vshCmdOptDef opts_domblkerror[] = {
 | |
|     VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_ACTIVE),
 | |
|     {.name = NULL}
 | |
| };
 | |
| 
 | |
| static bool
 | |
| cmdDomBlkError(vshControl *ctl, const vshCmd *cmd)
 | |
| {
 | |
|     g_autoptr(virshDomain) dom = NULL;
 | |
|     virDomainDiskErrorPtr disks = NULL;
 | |
|     unsigned int ndisks = 0;
 | |
|     size_t i;
 | |
|     int count;
 | |
|     bool ret = false;
 | |
| 
 | |
|     if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
 | |
|         return false;
 | |
| 
 | |
|     if ((count = virDomainGetDiskErrors(dom, NULL, 0, 0)) < 0)
 | |
|         goto cleanup;
 | |
| 
 | |
|     if (count > 0) {
 | |
|         disks = g_new0(virDomainDiskError, count);
 | |
|         ndisks = count;
 | |
| 
 | |
|         if ((count = virDomainGetDiskErrors(dom, disks, ndisks, 0)) == -1)
 | |
|             goto cleanup;
 | |
|     }
 | |
| 
 | |
|     if (count == 0) {
 | |
|         vshPrint(ctl, _("No errors found\n"));
 | |
|     } else {
 | |
|         for (i = 0; i < count; i++) {
 | |
|             vshPrint(ctl, "%s: %s\n",
 | |
|                      disks[i].disk,
 | |
|                      virshDomainIOErrorToString(disks[i].error));
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     ret = true;
 | |
| 
 | |
|  cleanup:
 | |
|     for (i = 0; i < ndisks; i++)
 | |
|         VIR_FREE(disks[i].disk);
 | |
|     VIR_FREE(disks);
 | |
|     return ret;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * "dominfo" command
 | |
|  */
 | |
| static const vshCmdInfo info_dominfo[] = {
 | |
|     {.name = "help",
 | |
|      .data = N_("domain information")
 | |
|     },
 | |
|     {.name = "desc",
 | |
|      .data = N_("Returns basic information about the domain.")
 | |
|     },
 | |
|     {.name = NULL}
 | |
| };
 | |
| 
 | |
| static const vshCmdOptDef opts_dominfo[] = {
 | |
|     VIRSH_COMMON_OPT_DOMAIN_FULL(0),
 | |
|     {.name = NULL}
 | |
| };
 | |
| 
 | |
| static bool
 | |
| cmdDominfo(vshControl *ctl, const vshCmd *cmd)
 | |
| {
 | |
|     virDomainInfo info;
 | |
|     g_autoptr(virshDomain) dom = NULL;
 | |
|     virSecurityModel secmodel;
 | |
|     virSecurityLabelPtr seclabel;
 | |
|     int persistent = 0;
 | |
|     bool ret = true;
 | |
|     int autostart;
 | |
|     unsigned int id;
 | |
|     char uuid[VIR_UUID_STRING_BUFLEN];
 | |
|     g_autofree char *ostype = NULL;
 | |
|     int has_managed_save = 0;
 | |
|     virshControl *priv = ctl->privData;
 | |
|     g_auto(GStrv) messages = NULL;
 | |
| 
 | |
|     if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
 | |
|         return false;
 | |
| 
 | |
|     id = virDomainGetID(dom);
 | |
|     if (id == ((unsigned int)-1))
 | |
|         vshPrint(ctl, "%-15s %s\n", _("Id:"), "-");
 | |
|     else
 | |
|         vshPrint(ctl, "%-15s %d\n", _("Id:"), id);
 | |
|     vshPrint(ctl, "%-15s %s\n", _("Name:"), virDomainGetName(dom));
 | |
| 
 | |
|     if (virDomainGetUUIDString(dom, &uuid[0]) == 0)
 | |
|         vshPrint(ctl, "%-15s %s\n", _("UUID:"), uuid);
 | |
| 
 | |
|     if ((ostype = virDomainGetOSType(dom)))
 | |
|         vshPrint(ctl, "%-15s %s\n", _("OS Type:"), ostype);
 | |
| 
 | |
|     if (virDomainGetInfo(dom, &info) == 0) {
 | |
|         vshPrint(ctl, "%-15s %s\n", _("State:"),
 | |
|                  virshDomainStateToString(info.state));
 | |
| 
 | |
|         vshPrint(ctl, "%-15s %d\n", _("CPU(s):"), info.nrVirtCpu);
 | |
| 
 | |
|         if (info.cpuTime != 0) {
 | |
|             double cpuUsed = info.cpuTime;
 | |
| 
 | |
|             cpuUsed /= 1000000000.0;
 | |
| 
 | |
|             vshPrint(ctl, "%-15s %.1lfs\n", _("CPU time:"), cpuUsed);
 | |
|         }
 | |
| 
 | |
|         if (info.maxMem != UINT_MAX)
 | |
|             vshPrint(ctl, "%-15s %lu KiB\n", _("Max memory:"),
 | |
|                  info.maxMem);
 | |
|         else
 | |
|             vshPrint(ctl, "%-15s %s\n", _("Max memory:"),
 | |
|                  _("no limit"));
 | |
| 
 | |
|         vshPrint(ctl, "%-15s %lu KiB\n", _("Used memory:"),
 | |
|                  info.memory);
 | |
| 
 | |
|     } else {
 | |
|         ret = false;
 | |
|     }
 | |
| 
 | |
|     /* Check and display whether the domain is persistent or not */
 | |
|     persistent = virDomainIsPersistent(dom);
 | |
|     vshDebug(ctl, VSH_ERR_DEBUG, "Domain persistent flag value: %d\n",
 | |
|              persistent);
 | |
|     if (persistent < 0)
 | |
|         vshPrint(ctl, "%-15s %s\n", _("Persistent:"), _("unknown"));
 | |
|     else
 | |
|         vshPrint(ctl, "%-15s %s\n", _("Persistent:"), persistent ? _("yes") : _("no"));
 | |
| 
 | |
|     /* Check and display whether the domain autostarts or not */
 | |
|     if (!virDomainGetAutostart(dom, &autostart)) {
 | |
|         vshPrint(ctl, "%-15s %s\n", _("Autostart:"),
 | |
|                  autostart ? _("enable") : _("disable"));
 | |
|     }
 | |
| 
 | |
|     has_managed_save = virDomainHasManagedSaveImage(dom, 0);
 | |
|     if (has_managed_save < 0)
 | |
|         vshPrint(ctl, "%-15s %s\n", _("Managed save:"), _("unknown"));
 | |
|     else
 | |
|         vshPrint(ctl, "%-15s %s\n", _("Managed save:"),
 | |
|                  has_managed_save ? _("yes") : _("no"));
 | |
| 
 | |
|     /* Security model and label information */
 | |
|     memset(&secmodel, 0, sizeof(secmodel));
 | |
|     if (virNodeGetSecurityModel(priv->conn, &secmodel) == -1) {
 | |
|         if (last_error->code != VIR_ERR_NO_SUPPORT) {
 | |
|             return false;
 | |
|         } else {
 | |
|             vshResetLibvirtError();
 | |
|         }
 | |
|     } else {
 | |
|         /* Only print something if a security model is active */
 | |
|         if (secmodel.model[0] != '\0') {
 | |
|             vshPrint(ctl, "%-15s %s\n", _("Security model:"), secmodel.model);
 | |
|             vshPrint(ctl, "%-15s %s\n", _("Security DOI:"), secmodel.doi);
 | |
| 
 | |
|             /* Security labels are only valid for active domains */
 | |
|             seclabel = g_new0(virSecurityLabel, 1);
 | |
| 
 | |
|             if (virDomainGetSecurityLabel(dom, seclabel) == -1) {
 | |
|                 VIR_FREE(seclabel);
 | |
|                 return false;
 | |
|             } else {
 | |
|                 if (seclabel->label[0] != '\0')
 | |
|                     vshPrint(ctl, "%-15s %s (%s)\n", _("Security label:"),
 | |
|                              seclabel->label, seclabel->enforcing ? "enforcing" : "permissive");
 | |
|             }
 | |
| 
 | |
|             VIR_FREE(seclabel);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     if (virDomainGetMessages(dom, &messages, 0) > 0) {
 | |
|         size_t i;
 | |
|         for (i = 0; messages[i] != NULL; i++) {
 | |
|             vshPrint(ctl, "%-15s %s\n",
 | |
|                      i == 0 ? _("Messages:") : "", messages[i]);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     return ret;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * "domstate" command
 | |
|  */
 | |
| static const vshCmdInfo info_domstate[] = {
 | |
|     {.name = "help",
 | |
|      .data = N_("domain state")
 | |
|     },
 | |
|     {.name = "desc",
 | |
|      .data = N_("Returns state about a domain.")
 | |
|     },
 | |
|     {.name = NULL}
 | |
| };
 | |
| 
 | |
| static const vshCmdOptDef opts_domstate[] = {
 | |
|     VIRSH_COMMON_OPT_DOMAIN_FULL(0),
 | |
|     {.name = "reason",
 | |
|      .type = VSH_OT_BOOL,
 | |
|      .help = N_("also print reason for the state")
 | |
|     },
 | |
|     {.name = NULL}
 | |
| };
 | |
| 
 | |
| static bool
 | |
| cmdDomstate(vshControl *ctl, const vshCmd *cmd)
 | |
| {
 | |
|     g_autoptr(virshDomain) dom = NULL;
 | |
|     bool showReason = vshCommandOptBool(cmd, "reason");
 | |
|     int state, reason;
 | |
| 
 | |
|     if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
 | |
|         return false;
 | |
| 
 | |
|     if ((state = virshDomainState(ctl, dom, &reason)) < 0)
 | |
|         return false;
 | |
| 
 | |
|     if (showReason) {
 | |
|         vshPrint(ctl, "%s (%s)\n",
 | |
|                  virshDomainStateToString(state),
 | |
|                  virshDomainStateReasonToString(state, reason));
 | |
|     } else {
 | |
|         vshPrint(ctl, "%s\n",
 | |
|                  virshDomainStateToString(state));
 | |
|     }
 | |
| 
 | |
|     return true;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * "domtime" command
 | |
|  */
 | |
| static const vshCmdInfo info_domtime[] = {
 | |
|     {.name = "help",
 | |
|      .data = N_("domain time")
 | |
|     },
 | |
|     {.name = "desc",
 | |
|      .data = N_("Gets or sets the domain's system time")
 | |
|     },
 | |
|     {.name = NULL}
 | |
| };
 | |
| 
 | |
| static const vshCmdOptDef opts_domtime[] = {
 | |
|     VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_ACTIVE),
 | |
|     {.name = "now",
 | |
|      .type = VSH_OT_BOOL,
 | |
|      .help = N_("set to the time of the host running virsh")
 | |
|     },
 | |
|     {.name = "pretty",
 | |
|      .type = VSH_OT_BOOL,
 | |
|      .help = N_("print domain's time in human readable form")
 | |
|     },
 | |
|     {.name = "sync",
 | |
|      .type = VSH_OT_BOOL,
 | |
|      .help = N_("instead of setting given time, synchronize from domain's RTC"),
 | |
|     },
 | |
|     {.name = "time",
 | |
|      .type = VSH_OT_INT,
 | |
|      .help = N_("time to set")
 | |
|     },
 | |
|     {.name = NULL}
 | |
| };
 | |
| 
 | |
| static bool
 | |
| cmdDomTime(vshControl *ctl, const vshCmd *cmd)
 | |
| {
 | |
|     g_autoptr(virshDomain) dom = NULL;
 | |
|     bool now = vshCommandOptBool(cmd, "now");
 | |
|     bool pretty = vshCommandOptBool(cmd, "pretty");
 | |
|     bool rtcSync = vshCommandOptBool(cmd, "sync");
 | |
|     long long seconds = 0;
 | |
|     unsigned int nseconds = 0;
 | |
|     unsigned int flags = 0;
 | |
|     bool doSet = false;
 | |
|     int rv;
 | |
| 
 | |
|     VSH_EXCLUSIVE_OPTIONS("time", "now");
 | |
|     VSH_EXCLUSIVE_OPTIONS("time", "sync");
 | |
|     VSH_EXCLUSIVE_OPTIONS("now", "sync");
 | |
| 
 | |
|     if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
 | |
|         return false;
 | |
| 
 | |
|     rv = vshCommandOptLongLong(ctl, cmd, "time", &seconds);
 | |
| 
 | |
|     if (rv < 0) {
 | |
|         /* invalid integer format */
 | |
|         return false;
 | |
|     } else if (rv > 0) {
 | |
|         /* valid integer to set */
 | |
|         doSet = true;
 | |
|     }
 | |
| 
 | |
|     if (doSet || now || rtcSync) {
 | |
|         if (now && ((seconds = time(NULL)) == (time_t) -1)) {
 | |
|             vshError(ctl, _("Unable to get current time"));
 | |
|             return false;
 | |
|         }
 | |
| 
 | |
|         if (rtcSync)
 | |
|             flags |= VIR_DOMAIN_TIME_SYNC;
 | |
| 
 | |
|         if (virDomainSetTime(dom, seconds, nseconds, flags) < 0)
 | |
|             return false;
 | |
| 
 | |
|     } else {
 | |
|         if (virDomainGetTime(dom, &seconds, &nseconds, flags) < 0)
 | |
|             return false;
 | |
| 
 | |
|         if (pretty) {
 | |
|             g_autoptr(GDateTime) then = NULL;
 | |
|             g_autofree char *thenstr = NULL;
 | |
| 
 | |
|             then = g_date_time_new_from_unix_utc(seconds);
 | |
|             thenstr = g_date_time_format(then, "%Y-%m-%d %H:%M:%S");
 | |
| 
 | |
|             vshPrint(ctl, _("Time: %s"), thenstr);
 | |
|         } else {
 | |
|             vshPrint(ctl, _("Time: %lld"), seconds);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     return true;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * "list" command
 | |
|  */
 | |
| static const vshCmdInfo info_list[] = {
 | |
|     {.name = "help",
 | |
|      .data = N_("list domains")
 | |
|     },
 | |
|     {.name = "desc",
 | |
|      .data = N_("Returns list of domains.")
 | |
|     },
 | |
|     {.name = NULL}
 | |
| };
 | |
| 
 | |
| /* compare domains, pack NULLed ones at the end */
 | |
| static int
 | |
| virshDomainSorter(const void *a, const void *b)
 | |
| {
 | |
|     virDomainPtr *da = (virDomainPtr *) a;
 | |
|     virDomainPtr *db = (virDomainPtr *) b;
 | |
|     unsigned int ida;
 | |
|     unsigned int idb;
 | |
|     unsigned int inactive = (unsigned int) -1;
 | |
| 
 | |
|     if (*da && !*db)
 | |
|         return -1;
 | |
| 
 | |
|     if (!*da)
 | |
|         return *db != NULL;
 | |
| 
 | |
|     ida = virDomainGetID(*da);
 | |
|     idb = virDomainGetID(*db);
 | |
| 
 | |
|     if (ida == inactive && idb == inactive)
 | |
|         return vshStrcasecmp(virDomainGetName(*da), virDomainGetName(*db));
 | |
| 
 | |
|     if (ida != inactive && idb != inactive) {
 | |
|         if (ida > idb)
 | |
|             return 1;
 | |
|         else if (ida < idb)
 | |
|             return -1;
 | |
|     }
 | |
| 
 | |
|     if (ida != inactive)
 | |
|         return -1;
 | |
|     else
 | |
|         return 1;
 | |
| }
 | |
| 
 | |
| struct virshDomainList {
 | |
|     virDomainPtr *domains;
 | |
|     size_t ndomains;
 | |
| };
 | |
| 
 | |
| static void
 | |
| virshDomainListFree(struct virshDomainList *domlist)
 | |
| {
 | |
|     size_t i;
 | |
| 
 | |
|     if (domlist && domlist->domains) {
 | |
|         for (i = 0; i < domlist->ndomains; i++)
 | |
|             virshDomainFree(domlist->domains[i]);
 | |
|         g_free(domlist->domains);
 | |
|     }
 | |
|     g_free(domlist);
 | |
| }
 | |
| 
 | |
| static struct virshDomainList *
 | |
| virshDomainListCollect(vshControl *ctl, unsigned int flags)
 | |
| {
 | |
|     struct virshDomainList *list = g_new0(struct virshDomainList, 1);
 | |
|     size_t i;
 | |
|     int ret;
 | |
|     int *ids = NULL;
 | |
|     int nids = 0;
 | |
|     char **names = NULL;
 | |
|     int nnames = 0;
 | |
|     virDomainPtr dom;
 | |
|     bool success = false;
 | |
|     size_t deleted = 0;
 | |
|     int persistent;
 | |
|     int autostart;
 | |
|     int state;
 | |
|     int nsnap;
 | |
|     int nchk;
 | |
|     int mansave;
 | |
|     virshControl *priv = ctl->privData;
 | |
| 
 | |
|     /* try the list with flags support (0.9.13 and later) */
 | |
|     if ((ret = virConnectListAllDomains(priv->conn, &list->domains,
 | |
|                                         flags)) >= 0) {
 | |
|         list->ndomains = ret;
 | |
|         goto finished;
 | |
|     }
 | |
| 
 | |
|     /* check if the command is actually supported */
 | |
|     if (last_error && last_error->code == VIR_ERR_NO_SUPPORT) {
 | |
|         vshResetLibvirtError();
 | |
|         goto fallback;
 | |
|     }
 | |
| 
 | |
|     if (last_error && last_error->code ==  VIR_ERR_INVALID_ARG) {
 | |
|         /* try the new API again but mask non-guaranteed flags */
 | |
|         unsigned int newflags = flags & (VIR_CONNECT_LIST_DOMAINS_ACTIVE |
 | |
|                                          VIR_CONNECT_LIST_DOMAINS_INACTIVE);
 | |
| 
 | |
|         vshResetLibvirtError();
 | |
|         if ((ret = virConnectListAllDomains(priv->conn, &list->domains,
 | |
|                                             newflags)) >= 0) {
 | |
|             list->ndomains = ret;
 | |
|             goto filter;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /* there was an error during the first or second call */
 | |
|     vshError(ctl, "%s", _("Failed to list domains"));
 | |
|     goto cleanup;
 | |
| 
 | |
| 
 | |
|  fallback:
 | |
|     /* fall back to old method (0.9.12 and older) */
 | |
|     vshResetLibvirtError();
 | |
| 
 | |
|     /* list active domains, if necessary */
 | |
|     if (!VSH_MATCH(VIR_CONNECT_LIST_DOMAINS_FILTERS_ACTIVE) ||
 | |
|         VSH_MATCH(VIR_CONNECT_LIST_DOMAINS_ACTIVE)) {
 | |
|         if ((nids = virConnectNumOfDomains(priv->conn)) < 0) {
 | |
|             vshError(ctl, "%s", _("Failed to list active domains"));
 | |
|             goto cleanup;
 | |
|         }
 | |
| 
 | |
|         if (nids) {
 | |
|             ids = g_new0(int, nids);
 | |
| 
 | |
|             if ((nids = virConnectListDomains(priv->conn, ids, nids)) < 0) {
 | |
|                 vshError(ctl, "%s", _("Failed to list active domains"));
 | |
|                 goto cleanup;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     if (!VSH_MATCH(VIR_CONNECT_LIST_DOMAINS_FILTERS_ACTIVE) ||
 | |
|         VSH_MATCH(VIR_CONNECT_LIST_DOMAINS_INACTIVE)) {
 | |
|         if ((nnames = virConnectNumOfDefinedDomains(priv->conn)) < 0) {
 | |
|             vshError(ctl, "%s", _("Failed to list inactive domains"));
 | |
|             goto cleanup;
 | |
|         }
 | |
| 
 | |
|         if (nnames) {
 | |
|             names = g_new0(char *, nnames);
 | |
| 
 | |
|             if ((nnames = virConnectListDefinedDomains(priv->conn, names,
 | |
|                                                       nnames)) < 0) {
 | |
|                 vshError(ctl, "%s", _("Failed to list inactive domains"));
 | |
|                 goto cleanup;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     list->domains = g_new0(virDomainPtr, nids + nnames);
 | |
|     list->ndomains = 0;
 | |
| 
 | |
|     /* get active domains */
 | |
|     for (i = 0; i < nids; i++) {
 | |
|         if (!(dom = virDomainLookupByID(priv->conn, ids[i])))
 | |
|             continue;
 | |
|         list->domains[list->ndomains++] = dom;
 | |
|     }
 | |
| 
 | |
|     /* get inactive domains */
 | |
|     for (i = 0; i < nnames; i++) {
 | |
|         if (!(dom = virDomainLookupByName(priv->conn, names[i])))
 | |
|             continue;
 | |
|         list->domains[list->ndomains++] = dom;
 | |
|     }
 | |
| 
 | |
|     /* truncate domains that weren't found */
 | |
|     deleted = (nids + nnames) - list->ndomains;
 | |
| 
 | |
|  filter:
 | |
|     /* filter list the list if the list was acquired by fallback means */
 | |
|     for (i = 0; i < list->ndomains; i++) {
 | |
|         dom = list->domains[i];
 | |
| 
 | |
|         /* persistence filter */
 | |
|         if (VSH_MATCH(VIR_CONNECT_LIST_DOMAINS_FILTERS_PERSISTENT)) {
 | |
|             if ((persistent = virDomainIsPersistent(dom)) < 0) {
 | |
|                 vshError(ctl, "%s", _("Failed to get domain persistence info"));
 | |
|                 goto cleanup;
 | |
|             }
 | |
| 
 | |
|             if (!((VSH_MATCH(VIR_CONNECT_LIST_DOMAINS_PERSISTENT) && persistent) ||
 | |
|                   (VSH_MATCH(VIR_CONNECT_LIST_DOMAINS_TRANSIENT) && !persistent)))
 | |
|                 goto remove_entry;
 | |
|         }
 | |
| 
 | |
|         /* domain state filter */
 | |
|         if (VSH_MATCH(VIR_CONNECT_LIST_DOMAINS_FILTERS_STATE)) {
 | |
|             if (virDomainGetState(dom, &state, NULL, 0) < 0) {
 | |
|                 vshError(ctl, "%s", _("Failed to get domain state"));
 | |
|                 goto cleanup;
 | |
|             }
 | |
| 
 | |
|             if (!((VSH_MATCH(VIR_CONNECT_LIST_DOMAINS_RUNNING) &&
 | |
|                    state == VIR_DOMAIN_RUNNING) ||
 | |
|                   (VSH_MATCH(VIR_CONNECT_LIST_DOMAINS_PAUSED) &&
 | |
|                    state == VIR_DOMAIN_PAUSED) ||
 | |
|                   (VSH_MATCH(VIR_CONNECT_LIST_DOMAINS_SHUTOFF) &&
 | |
|                    state == VIR_DOMAIN_SHUTOFF) ||
 | |
|                   (VSH_MATCH(VIR_CONNECT_LIST_DOMAINS_OTHER) &&
 | |
|                    (state != VIR_DOMAIN_RUNNING &&
 | |
|                     state != VIR_DOMAIN_PAUSED &&
 | |
|                     state != VIR_DOMAIN_SHUTOFF))))
 | |
|                 goto remove_entry;
 | |
|         }
 | |
| 
 | |
|         /* autostart filter */
 | |
|         if (VSH_MATCH(VIR_CONNECT_LIST_DOMAINS_FILTERS_AUTOSTART)) {
 | |
|             if (virDomainGetAutostart(dom, &autostart) < 0) {
 | |
|                 vshError(ctl, "%s", _("Failed to get domain autostart state"));
 | |
|                 goto cleanup;
 | |
|             }
 | |
| 
 | |
|             if (!((VSH_MATCH(VIR_CONNECT_LIST_DOMAINS_AUTOSTART) && autostart) ||
 | |
|                   (VSH_MATCH(VIR_CONNECT_LIST_DOMAINS_NO_AUTOSTART) && !autostart)))
 | |
|                 goto remove_entry;
 | |
|         }
 | |
| 
 | |
|         /* managed save filter */
 | |
|         if (VSH_MATCH(VIR_CONNECT_LIST_DOMAINS_FILTERS_MANAGEDSAVE)) {
 | |
|             if ((mansave = virDomainHasManagedSaveImage(dom, 0)) < 0) {
 | |
|                 vshError(ctl, "%s",
 | |
|                          _("Failed to check for managed save image"));
 | |
|                 goto cleanup;
 | |
|             }
 | |
| 
 | |
|             if (!((VSH_MATCH(VIR_CONNECT_LIST_DOMAINS_MANAGEDSAVE) && mansave) ||
 | |
|                   (VSH_MATCH(VIR_CONNECT_LIST_DOMAINS_NO_MANAGEDSAVE) && !mansave)))
 | |
|                 goto remove_entry;
 | |
|         }
 | |
| 
 | |
|         /* snapshot filter */
 | |
|         if (VSH_MATCH(VIR_CONNECT_LIST_DOMAINS_FILTERS_SNAPSHOT)) {
 | |
|             if ((nsnap = virDomainSnapshotNum(dom, 0)) < 0) {
 | |
|                 vshError(ctl, "%s", _("Failed to get snapshot count"));
 | |
|                 goto cleanup;
 | |
|             }
 | |
|             if (!((VSH_MATCH(VIR_CONNECT_LIST_DOMAINS_HAS_SNAPSHOT) && nsnap > 0) ||
 | |
|                   (VSH_MATCH(VIR_CONNECT_LIST_DOMAINS_NO_SNAPSHOT) && nsnap == 0)))
 | |
|                 goto remove_entry;
 | |
|         }
 | |
| 
 | |
|         /* checkpoint filter */
 | |
|         if (VSH_MATCH(VIR_CONNECT_LIST_DOMAINS_FILTERS_CHECKPOINT)) {
 | |
|             if ((nchk = virDomainListAllCheckpoints(dom, NULL, 0)) < 0) {
 | |
|                 vshError(ctl, "%s", _("Failed to get checkpoint count"));
 | |
|                 goto cleanup;
 | |
|             }
 | |
|             if (!((VSH_MATCH(VIR_CONNECT_LIST_DOMAINS_HAS_CHECKPOINT) && nchk > 0) ||
 | |
|                   (VSH_MATCH(VIR_CONNECT_LIST_DOMAINS_NO_CHECKPOINT) && nchk == 0)))
 | |
|                 goto remove_entry;
 | |
|         }
 | |
| 
 | |
|         /* the domain matched all filters, it may stay */
 | |
|         continue;
 | |
| 
 | |
|  remove_entry:
 | |
|         /* the domain has to be removed as it failed one of the filters */
 | |
|         virshDomainFree(list->domains[i]);
 | |
|         list->domains[i] = NULL;
 | |
|         deleted++;
 | |
|     }
 | |
| 
 | |
|  finished:
 | |
|     /* sort the list */
 | |
|     if (list->domains && list->ndomains)
 | |
|         qsort(list->domains, list->ndomains, sizeof(*list->domains),
 | |
|               virshDomainSorter);
 | |
| 
 | |
|     /* truncate the list if filter simulation deleted entries */
 | |
|     if (deleted)
 | |
|         VIR_SHRINK_N(list->domains, list->ndomains, deleted);
 | |
| 
 | |
|     success = true;
 | |
| 
 | |
|  cleanup:
 | |
|     for (i = 0; nnames != -1 && i < nnames; i++)
 | |
|         VIR_FREE(names[i]);
 | |
| 
 | |
|     if (!success) {
 | |
|         virshDomainListFree(list);
 | |
|         list = NULL;
 | |
|     }
 | |
| 
 | |
|     VIR_FREE(names);
 | |
|     VIR_FREE(ids);
 | |
|     return list;
 | |
| }
 | |
| 
 | |
| static const vshCmdOptDef opts_list[] = {
 | |
|     {.name = "inactive",
 | |
|      .type = VSH_OT_BOOL,
 | |
|      .help = N_("list inactive domains")
 | |
|     },
 | |
|     {.name = "all",
 | |
|      .type = VSH_OT_BOOL,
 | |
|      .help = N_("list inactive & active domains")
 | |
|     },
 | |
|     {.name = "transient",
 | |
|      .type = VSH_OT_BOOL,
 | |
|      .help = N_("list transient domains")
 | |
|     },
 | |
|     {.name = "persistent",
 | |
|      .type = VSH_OT_BOOL,
 | |
|      .help = N_("list persistent domains")
 | |
|     },
 | |
|     {.name = "with-snapshot",
 | |
|      .type = VSH_OT_BOOL,
 | |
|      .help = N_("list domains with existing snapshot")
 | |
|     },
 | |
|     {.name = "without-snapshot",
 | |
|      .type = VSH_OT_BOOL,
 | |
|      .help = N_("list domains without a snapshot")
 | |
|     },
 | |
|     {.name = "with-checkpoint",
 | |
|      .type = VSH_OT_BOOL,
 | |
|      .help = N_("list domains with existing checkpoint")
 | |
|     },
 | |
|     {.name = "without-checkpoint",
 | |
|      .type = VSH_OT_BOOL,
 | |
|      .help = N_("list domains without a checkpoint")
 | |
|     },
 | |
|     {.name = "state-running",
 | |
|      .type = VSH_OT_BOOL,
 | |
|      .help = N_("list domains in running state")
 | |
|     },
 | |
|     {.name = "state-paused",
 | |
|      .type = VSH_OT_BOOL,
 | |
|      .help = N_("list domains in paused state")
 | |
|     },
 | |
|     {.name = "state-shutoff",
 | |
|      .type = VSH_OT_BOOL,
 | |
|      .help = N_("list domains in shutoff state")
 | |
|     },
 | |
|     {.name = "state-other",
 | |
|      .type = VSH_OT_BOOL,
 | |
|      .help = N_("list domains in other states")
 | |
|     },
 | |
|     {.name = "autostart",
 | |
|      .type = VSH_OT_BOOL,
 | |
|      .help = N_("list domains with autostart enabled")
 | |
|     },
 | |
|     {.name = "no-autostart",
 | |
|      .type = VSH_OT_BOOL,
 | |
|      .help = N_("list domains with autostart disabled")
 | |
|     },
 | |
|     {.name = "with-managed-save",
 | |
|      .type = VSH_OT_BOOL,
 | |
|      .help = N_("list domains with managed save state")
 | |
|     },
 | |
|     {.name = "without-managed-save",
 | |
|      .type = VSH_OT_BOOL,
 | |
|      .help = N_("list domains without managed save")
 | |
|     },
 | |
|     {.name = "uuid",
 | |
|      .type = VSH_OT_BOOL,
 | |
|      .help = N_("list uuid's only")
 | |
|     },
 | |
|     {.name = "name",
 | |
|      .type = VSH_OT_BOOL,
 | |
|      .help = N_("list domain names only")
 | |
|     },
 | |
|     {.name = "id",
 | |
|      .type = VSH_OT_BOOL,
 | |
|      .help = N_("list domain IDs only")
 | |
|     },
 | |
|     {.name = "table",
 | |
|      .type = VSH_OT_BOOL,
 | |
|      .help = N_("list table (default)")
 | |
|     },
 | |
|     {.name = "managed-save",
 | |
|      .type = VSH_OT_BOOL,
 | |
|      .help = N_("mark inactive domains with managed save state")
 | |
|     },
 | |
|     {.name = "title",
 | |
|      .type = VSH_OT_BOOL,
 | |
|      .help = N_("show domain title")
 | |
|     },
 | |
|     {.name = NULL}
 | |
| };
 | |
| 
 | |
| #define FILTER(NAME, FLAG) \
 | |
|     if (vshCommandOptBool(cmd, NAME)) \
 | |
|         flags |= (FLAG)
 | |
| static bool
 | |
| cmdList(vshControl *ctl, const vshCmd *cmd)
 | |
| {
 | |
|     bool managed = vshCommandOptBool(cmd, "managed-save");
 | |
|     bool optTitle = vshCommandOptBool(cmd, "title");
 | |
|     bool optTable = vshCommandOptBool(cmd, "table");
 | |
|     bool optUUID = vshCommandOptBool(cmd, "uuid");
 | |
|     bool optName = vshCommandOptBool(cmd, "name");
 | |
|     bool optID = vshCommandOptBool(cmd, "id");
 | |
|     size_t i;
 | |
|     char uuid[VIR_UUID_STRING_BUFLEN];
 | |
|     int state;
 | |
|     bool ret = false;
 | |
|     struct virshDomainList *list = NULL;
 | |
|     virDomainPtr dom;
 | |
|     char id_buf[VIR_INT64_STR_BUFLEN];
 | |
|     unsigned int id;
 | |
|     unsigned int flags = VIR_CONNECT_LIST_DOMAINS_ACTIVE;
 | |
|     g_autoptr(vshTable) table = NULL;
 | |
| 
 | |
|     /* construct filter flags */
 | |
|     if (vshCommandOptBool(cmd, "inactive") ||
 | |
|         vshCommandOptBool(cmd, "state-shutoff"))
 | |
|         flags = VIR_CONNECT_LIST_DOMAINS_INACTIVE;
 | |
| 
 | |
|     if (vshCommandOptBool(cmd, "all"))
 | |
|         flags = VIR_CONNECT_LIST_DOMAINS_INACTIVE |
 | |
|                 VIR_CONNECT_LIST_DOMAINS_ACTIVE;
 | |
| 
 | |
|     FILTER("persistent", VIR_CONNECT_LIST_DOMAINS_PERSISTENT);
 | |
|     FILTER("transient",  VIR_CONNECT_LIST_DOMAINS_TRANSIENT);
 | |
| 
 | |
|     FILTER("with-managed-save",    VIR_CONNECT_LIST_DOMAINS_MANAGEDSAVE);
 | |
|     FILTER("without-managed-save", VIR_CONNECT_LIST_DOMAINS_NO_MANAGEDSAVE);
 | |
| 
 | |
|     FILTER("autostart",    VIR_CONNECT_LIST_DOMAINS_AUTOSTART);
 | |
|     FILTER("no-autostart", VIR_CONNECT_LIST_DOMAINS_NO_AUTOSTART);
 | |
| 
 | |
|     FILTER("with-snapshot",    VIR_CONNECT_LIST_DOMAINS_HAS_SNAPSHOT);
 | |
|     FILTER("without-snapshot", VIR_CONNECT_LIST_DOMAINS_NO_SNAPSHOT);
 | |
| 
 | |
|     FILTER("with-checkpoint",    VIR_CONNECT_LIST_DOMAINS_HAS_CHECKPOINT);
 | |
|     FILTER("without-checkpoint", VIR_CONNECT_LIST_DOMAINS_NO_CHECKPOINT);
 | |
| 
 | |
|     FILTER("state-running", VIR_CONNECT_LIST_DOMAINS_RUNNING);
 | |
|     FILTER("state-paused",  VIR_CONNECT_LIST_DOMAINS_PAUSED);
 | |
|     FILTER("state-shutoff", VIR_CONNECT_LIST_DOMAINS_SHUTOFF);
 | |
|     FILTER("state-other",   VIR_CONNECT_LIST_DOMAINS_OTHER);
 | |
| 
 | |
|     VSH_EXCLUSIVE_OPTIONS("table", "name");
 | |
|     VSH_EXCLUSIVE_OPTIONS("table", "uuid");
 | |
|     VSH_EXCLUSIVE_OPTIONS("table", "id");
 | |
| 
 | |
|     if (!optUUID && !optName && !optID)
 | |
|         optTable = true;
 | |
| 
 | |
|     if (!(list = virshDomainListCollect(ctl, flags)))
 | |
|         goto cleanup;
 | |
| 
 | |
|     /* print table header in legacy mode */
 | |
|     if (optTable) {
 | |
|         if (optTitle)
 | |
|             table = vshTableNew(_("Id"), _("Name"), _("State"), _("Title"), NULL);
 | |
|         else
 | |
|             table = vshTableNew(_("Id"), _("Name"), _("State"), NULL);
 | |
| 
 | |
|         if (!table)
 | |
|             goto cleanup;
 | |
|     }
 | |
| 
 | |
|     for (i = 0; i < list->ndomains; i++) {
 | |
|         const char *sep = "";
 | |
| 
 | |
|         dom = list->domains[i];
 | |
|         id = virDomainGetID(dom);
 | |
|         if (id != (unsigned int) -1)
 | |
|             g_snprintf(id_buf, sizeof(id_buf), "%d", id);
 | |
|         else
 | |
|             ignore_value(virStrcpyStatic(id_buf, "-"));
 | |
| 
 | |
|         if (optTable) {
 | |
|             state = virshDomainState(ctl, dom, NULL);
 | |
| 
 | |
|             /* Domain could've been removed in the meantime */
 | |
|             if (state < 0)
 | |
|                 continue;
 | |
| 
 | |
|             if (managed && state == VIR_DOMAIN_SHUTOFF &&
 | |
|                 virDomainHasManagedSaveImage(dom, 0) > 0)
 | |
|                 state = -2;
 | |
| 
 | |
|             if (optTitle) {
 | |
|                 g_autofree char *title = NULL;
 | |
| 
 | |
|                 if (!(title = virshGetDomainDescription(ctl, dom, true, 0)))
 | |
|                     goto cleanup;
 | |
|                 if (vshTableRowAppend(table, id_buf,
 | |
|                                       virDomainGetName(dom),
 | |
|                                       state == -2 ? _("saved")
 | |
|                                       : virshDomainStateToString(state),
 | |
|                                       title, NULL) < 0)
 | |
|                     goto cleanup;
 | |
|             } else {
 | |
|                 if (vshTableRowAppend(table, id_buf,
 | |
|                                       virDomainGetName(dom),
 | |
|                                       state == -2 ? _("saved")
 | |
|                                       : virshDomainStateToString(state),
 | |
|                                       NULL) < 0)
 | |
|                     goto cleanup;
 | |
|             }
 | |
| 
 | |
|         } else {
 | |
|             if (optUUID) {
 | |
|                 if (virDomainGetUUIDString(dom, uuid) < 0) {
 | |
|                     vshError(ctl, "%s", _("Failed to get domain's UUID"));
 | |
|                     goto cleanup;
 | |
|                 }
 | |
|                 vshPrint(ctl, "%s", uuid);
 | |
|                 sep = " ";
 | |
|             }
 | |
|             if (optID) {
 | |
|                 /* If we are asked to print IDs only then do that
 | |
|                  * only for live domains. */
 | |
|                 if (id == (unsigned int) -1 && !optUUID && !optName)
 | |
|                     continue;
 | |
|                 vshPrint(ctl, "%s%s", sep, id_buf);
 | |
|                 sep = " ";
 | |
|             }
 | |
|             if (optName) {
 | |
|                 vshPrint(ctl, "%s%s", sep, virDomainGetName(dom));
 | |
|                 sep = " ";
 | |
|             }
 | |
|             vshPrint(ctl, "\n");
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     if (optTable)
 | |
|         vshTablePrintToStdout(table, ctl);
 | |
| 
 | |
|     ret = true;
 | |
|  cleanup:
 | |
|     virshDomainListFree(list);
 | |
|     return ret;
 | |
| }
 | |
| #undef FILTER
 | |
| 
 | |
| /*
 | |
|  * "domstats" command
 | |
|  */
 | |
| static const vshCmdInfo info_domstats[] = {
 | |
|     {.name = "help",
 | |
|      .data = N_("get statistics about one or multiple domains")
 | |
|     },
 | |
|     {.name = "desc",
 | |
|      .data = N_("Gets statistics about one or more (or all) domains")
 | |
|     },
 | |
|     {.name = NULL}
 | |
| };
 | |
| 
 | |
| static const vshCmdOptDef opts_domstats[] = {
 | |
|     {.name = "state",
 | |
|      .type = VSH_OT_BOOL,
 | |
|      .help = N_("report domain state"),
 | |
|     },
 | |
|     {.name = "cpu-total",
 | |
|      .type = VSH_OT_BOOL,
 | |
|      .help = N_("report domain physical cpu usage"),
 | |
|     },
 | |
|     {.name = "balloon",
 | |
|      .type = VSH_OT_BOOL,
 | |
|      .help = N_("report domain balloon statistics"),
 | |
|     },
 | |
|     {.name = "vcpu",
 | |
|      .type = VSH_OT_BOOL,
 | |
|      .help = N_("report domain virtual cpu information"),
 | |
|     },
 | |
|     {.name = "interface",
 | |
|      .type = VSH_OT_BOOL,
 | |
|      .help = N_("report domain network interface information"),
 | |
|     },
 | |
|     {.name = "block",
 | |
|      .type = VSH_OT_BOOL,
 | |
|      .help = N_("report domain block device statistics"),
 | |
|     },
 | |
|     {.name = "perf",
 | |
|      .type = VSH_OT_BOOL,
 | |
|      .help = N_("report domain perf event statistics"),
 | |
|     },
 | |
|     {.name = "iothread",
 | |
|      .type = VSH_OT_BOOL,
 | |
|      .help = N_("report domain IOThread information"),
 | |
|     },
 | |
|     {.name = "memory",
 | |
|      .type = VSH_OT_BOOL,
 | |
|      .help = N_("report domain memory usage"),
 | |
|     },
 | |
|     {.name = "dirtyrate",
 | |
|      .type = VSH_OT_BOOL,
 | |
|      .help = N_("report domain dirty rate information"),
 | |
|     },
 | |
|     {.name = "list-active",
 | |
|      .type = VSH_OT_BOOL,
 | |
|      .help = N_("list only active domains"),
 | |
|     },
 | |
|     {.name = "list-inactive",
 | |
|      .type = VSH_OT_BOOL,
 | |
|      .help = N_("list only inactive domains"),
 | |
|     },
 | |
|     {.name = "list-persistent",
 | |
|      .type = VSH_OT_BOOL,
 | |
|      .help = N_("list only persistent domains"),
 | |
|     },
 | |
|     {.name = "list-transient",
 | |
|      .type = VSH_OT_BOOL,
 | |
|      .help = N_("list only transient domains"),
 | |
|     },
 | |
|     {.name = "list-running",
 | |
|      .type = VSH_OT_BOOL,
 | |
|      .help = N_("list only running domains"),
 | |
|     },
 | |
|     {.name = "list-paused",
 | |
|      .type = VSH_OT_BOOL,
 | |
|      .help = N_("list only paused domains"),
 | |
|     },
 | |
|     {.name = "list-shutoff",
 | |
|      .type = VSH_OT_BOOL,
 | |
|      .help = N_("list only shutoff domains"),
 | |
|     },
 | |
|     {.name = "list-other",
 | |
|      .type = VSH_OT_BOOL,
 | |
|      .help = N_("list only domains in other states"),
 | |
|     },
 | |
|     {.name = "raw",
 | |
|      .type = VSH_OT_BOOL,
 | |
|      .help = N_("do not pretty-print the fields"),
 | |
|     },
 | |
|     {.name = "enforce",
 | |
|      .type = VSH_OT_BOOL,
 | |
|      .help = N_("enforce requested stats parameters"),
 | |
|     },
 | |
|     {.name = "backing",
 | |
|      .type = VSH_OT_BOOL,
 | |
|      .help = N_("add backing chain information to block stats"),
 | |
|     },
 | |
|     {.name = "nowait",
 | |
|      .type = VSH_OT_BOOL,
 | |
|      .help = N_("report only stats that are accessible instantly"),
 | |
|     },
 | |
|     VIRSH_COMMON_OPT_DOMAIN_OT_ARGV(N_("list of domains to get stats for"), 0),
 | |
|     {.name = NULL}
 | |
| };
 | |
| 
 | |
| 
 | |
| static bool
 | |
| virshDomainStatsPrintRecord(vshControl *ctl G_GNUC_UNUSED,
 | |
|                             virDomainStatsRecordPtr record,
 | |
|                             bool raw G_GNUC_UNUSED)
 | |
| {
 | |
|     size_t i;
 | |
| 
 | |
|     vshPrint(ctl, "Domain: '%s'\n", virDomainGetName(record->dom));
 | |
| 
 | |
|     /* XXX: Implement pretty-printing */
 | |
| 
 | |
|     for (i = 0; i < record->nparams; i++) {
 | |
|         g_autofree char *param = NULL;
 | |
| 
 | |
|         if (!(param = vshGetTypedParamValue(ctl, record->params + i)))
 | |
|             return false;
 | |
| 
 | |
|         vshPrint(ctl, "  %s=%s\n", record->params[i].field, param);
 | |
|     }
 | |
| 
 | |
|     return true;
 | |
| }
 | |
| 
 | |
| static bool
 | |
| cmdDomstats(vshControl *ctl, const vshCmd *cmd)
 | |
| {
 | |
|     unsigned int stats = 0;
 | |
|     virDomainPtr *domlist = NULL;
 | |
|     virDomainPtr dom;
 | |
|     size_t ndoms = 0;
 | |
|     virDomainStatsRecordPtr *records = NULL;
 | |
|     virDomainStatsRecordPtr *next;
 | |
|     bool raw = vshCommandOptBool(cmd, "raw");
 | |
|     int flags = 0;
 | |
|     const vshCmdOpt *opt = NULL;
 | |
|     bool ret = false;
 | |
|     virshControl *priv = ctl->privData;
 | |
| 
 | |
|     if (vshCommandOptBool(cmd, "state"))
 | |
|         stats |= VIR_DOMAIN_STATS_STATE;
 | |
| 
 | |
|     if (vshCommandOptBool(cmd, "cpu-total"))
 | |
|         stats |= VIR_DOMAIN_STATS_CPU_TOTAL;
 | |
| 
 | |
|     if (vshCommandOptBool(cmd, "balloon"))
 | |
|         stats |= VIR_DOMAIN_STATS_BALLOON;
 | |
| 
 | |
|     if (vshCommandOptBool(cmd, "vcpu"))
 | |
|         stats |= VIR_DOMAIN_STATS_VCPU;
 | |
| 
 | |
|     if (vshCommandOptBool(cmd, "interface"))
 | |
|         stats |= VIR_DOMAIN_STATS_INTERFACE;
 | |
| 
 | |
|     if (vshCommandOptBool(cmd, "block"))
 | |
|         stats |= VIR_DOMAIN_STATS_BLOCK;
 | |
| 
 | |
|     if (vshCommandOptBool(cmd, "perf"))
 | |
|         stats |= VIR_DOMAIN_STATS_PERF;
 | |
| 
 | |
|     if (vshCommandOptBool(cmd, "iothread"))
 | |
|         stats |= VIR_DOMAIN_STATS_IOTHREAD;
 | |
| 
 | |
|     if (vshCommandOptBool(cmd, "memory"))
 | |
|         stats |= VIR_DOMAIN_STATS_MEMORY;
 | |
| 
 | |
|     if (vshCommandOptBool(cmd, "dirtyrate"))
 | |
|         stats |= VIR_DOMAIN_STATS_DIRTYRATE;
 | |
| 
 | |
|     if (vshCommandOptBool(cmd, "list-active"))
 | |
|         flags |= VIR_CONNECT_GET_ALL_DOMAINS_STATS_ACTIVE;
 | |
| 
 | |
|     if (vshCommandOptBool(cmd, "list-inactive"))
 | |
|         flags |= VIR_CONNECT_GET_ALL_DOMAINS_STATS_INACTIVE;
 | |
| 
 | |
|     if (vshCommandOptBool(cmd, "list-persistent"))
 | |
|         flags |= VIR_CONNECT_GET_ALL_DOMAINS_STATS_PERSISTENT;
 | |
| 
 | |
|     if (vshCommandOptBool(cmd, "list-transient"))
 | |
|         flags |= VIR_CONNECT_GET_ALL_DOMAINS_STATS_TRANSIENT;
 | |
| 
 | |
|     if (vshCommandOptBool(cmd, "list-running"))
 | |
|         flags |= VIR_CONNECT_GET_ALL_DOMAINS_STATS_RUNNING;
 | |
| 
 | |
|     if (vshCommandOptBool(cmd, "list-paused"))
 | |
|         flags |= VIR_CONNECT_GET_ALL_DOMAINS_STATS_PAUSED;
 | |
| 
 | |
|     if (vshCommandOptBool(cmd, "list-shutoff"))
 | |
|         flags |= VIR_CONNECT_GET_ALL_DOMAINS_STATS_SHUTOFF;
 | |
| 
 | |
|     if (vshCommandOptBool(cmd, "list-other"))
 | |
|         flags |= VIR_CONNECT_GET_ALL_DOMAINS_STATS_OTHER;
 | |
| 
 | |
|     if (vshCommandOptBool(cmd, "enforce"))
 | |
|         flags |= VIR_CONNECT_GET_ALL_DOMAINS_STATS_ENFORCE_STATS;
 | |
| 
 | |
|     if (vshCommandOptBool(cmd, "backing"))
 | |
|         flags |= VIR_CONNECT_GET_ALL_DOMAINS_STATS_BACKING;
 | |
| 
 | |
|     if (vshCommandOptBool(cmd, "nowait"))
 | |
|         flags |= VIR_CONNECT_GET_ALL_DOMAINS_STATS_NOWAIT;
 | |
| 
 | |
|     if (vshCommandOptBool(cmd, "domain")) {
 | |
|         domlist = g_new0(virDomainPtr, 1);
 | |
|         ndoms = 1;
 | |
| 
 | |
|         while ((opt = vshCommandOptArgv(ctl, cmd, opt))) {
 | |
|             if (!(dom = virshLookupDomainBy(ctl, opt->data,
 | |
|                                             VIRSH_BYID |
 | |
|                                             VIRSH_BYUUID | VIRSH_BYNAME)))
 | |
|                 goto cleanup;
 | |
| 
 | |
|             if (VIR_INSERT_ELEMENT(domlist, ndoms - 1, ndoms, dom) < 0)
 | |
|                 goto cleanup;
 | |
|         }
 | |
| 
 | |
|         if (virDomainListGetStats(domlist,
 | |
|                                   stats,
 | |
|                                   &records,
 | |
|                                   flags) < 0)
 | |
|             goto cleanup;
 | |
|     } else {
 | |
|        if ((virConnectGetAllDomainStats(priv->conn,
 | |
|                                         stats,
 | |
|                                         &records,
 | |
|                                         flags)) < 0)
 | |
|            goto cleanup;
 | |
|     }
 | |
| 
 | |
|     next = records;
 | |
|     while (*next) {
 | |
|         if (!virshDomainStatsPrintRecord(ctl, *next, raw))
 | |
|             goto cleanup;
 | |
| 
 | |
|         if (*(++next))
 | |
|             vshPrint(ctl, "\n");
 | |
|     }
 | |
| 
 | |
|     ret = true;
 | |
|  cleanup:
 | |
|     virDomainStatsRecordListFree(records);
 | |
|     virObjectListFree(domlist);
 | |
| 
 | |
|     return ret;
 | |
| }
 | |
| 
 | |
| /* "domifaddr" command
 | |
|  */
 | |
| static const vshCmdInfo info_domifaddr[] = {
 | |
|     {"help", N_("Get network interfaces' addresses for a running domain")},
 | |
|     {"desc", N_("Get network interfaces' addresses for a running domain")},
 | |
|     {NULL, NULL}
 | |
| };
 | |
| 
 | |
| static const vshCmdOptDef opts_domifaddr[] = {
 | |
|     VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_ACTIVE),
 | |
|     {.name = "interface",
 | |
|      .type = VSH_OT_STRING,
 | |
|      .flags = VSH_OFLAG_NONE,
 | |
|      .completer = virshDomainInterfaceCompleter,
 | |
|      .help = N_("network interface name")},
 | |
|     {.name = "full",
 | |
|      .type = VSH_OT_BOOL,
 | |
|      .flags = VSH_OFLAG_NONE,
 | |
|      .help = N_("always display names and MACs of interfaces")},
 | |
|     {.name = "source",
 | |
|      .type = VSH_OT_STRING,
 | |
|      .flags = VSH_OFLAG_NONE,
 | |
|      .completer = virshDomainInterfaceAddrSourceCompleter,
 | |
|      .help = N_("address source: 'lease', 'agent', or 'arp'")},
 | |
|     {.name = NULL}
 | |
| };
 | |
| 
 | |
| VIR_ENUM_IMPL(virshDomainInterfaceAddressesSource,
 | |
|               VIR_DOMAIN_INTERFACE_ADDRESSES_SRC_LAST,
 | |
|               "lease",
 | |
|               "agent",
 | |
|               "arp");
 | |
| 
 | |
| static bool
 | |
| cmdDomIfAddr(vshControl *ctl, const vshCmd *cmd)
 | |
| {
 | |
|     g_autoptr(virshDomain) dom = NULL;
 | |
|     const char *ifacestr = NULL;
 | |
|     virDomainInterfacePtr *ifaces = NULL;
 | |
|     size_t i, j;
 | |
|     int ifaces_count = 0;
 | |
|     bool ret = false;
 | |
|     bool full = vshCommandOptBool(cmd, "full");
 | |
|     const char *sourcestr = NULL;
 | |
|     int source = VIR_DOMAIN_INTERFACE_ADDRESSES_SRC_LEASE;
 | |
| 
 | |
|     if (vshCommandOptStringReq(ctl, cmd, "interface", &ifacestr) < 0)
 | |
|         return false;
 | |
|     if (vshCommandOptStringReq(ctl, cmd, "source", &sourcestr) < 0)
 | |
|         return false;
 | |
| 
 | |
|     if (sourcestr &&
 | |
|         (source = virshDomainInterfaceAddressesSourceTypeFromString(sourcestr)) < 0) {
 | |
|         vshError(ctl, _("Unknown data source '%s'"), sourcestr);
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
 | |
|         return false;
 | |
| 
 | |
|     if ((ifaces_count = virDomainInterfaceAddresses(dom, &ifaces, source, 0)) < 0) {
 | |
|         vshError(ctl, _("Failed to query for interfaces addresses"));
 | |
|         goto cleanup;
 | |
|     }
 | |
| 
 | |
|     vshPrintExtra(ctl, " %-10s %-20s %-8s     %s\n%s%s\n", _("Name"),
 | |
|                   _("MAC address"), _("Protocol"), _("Address"),
 | |
|                   _("-------------------------------------------------"),
 | |
|                   _("------------------------------"));
 | |
| 
 | |
|     for (i = 0; i < ifaces_count; i++) {
 | |
|         virDomainInterfacePtr iface = ifaces[i];
 | |
|         const char *type = NULL;
 | |
| 
 | |
|         if (ifacestr && STRNEQ(ifacestr, iface->name))
 | |
|             continue;
 | |
| 
 | |
|         /* When the interface has no IP address */
 | |
|         if (!iface->naddrs) {
 | |
|             vshPrint(ctl, " %-10s %-17s    %-12s %s\n",
 | |
|                      iface->name,
 | |
|                      iface->hwaddr ? iface->hwaddr : "N/A", "N/A", "N/A");
 | |
|             continue;
 | |
|         }
 | |
| 
 | |
|         for (j = 0; j < iface->naddrs; j++) {
 | |
|             g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
 | |
|             g_autofree char *ip_addr_str = NULL;
 | |
| 
 | |
|             switch (iface->addrs[j].type) {
 | |
|             case VIR_IP_ADDR_TYPE_IPV4:
 | |
|                 type = "ipv4";
 | |
|                 break;
 | |
|             case VIR_IP_ADDR_TYPE_IPV6:
 | |
|                 type = "ipv6";
 | |
|                 break;
 | |
|             }
 | |
| 
 | |
|             virBufferAsprintf(&buf, "%-12s %s/%d",
 | |
|                               type, iface->addrs[j].addr,
 | |
|                               iface->addrs[j].prefix);
 | |
| 
 | |
|             ip_addr_str = virBufferContentAndReset(&buf);
 | |
| 
 | |
|             if (!ip_addr_str)
 | |
|                 ip_addr_str = g_strdup("");
 | |
| 
 | |
|             /* Don't repeat interface name */
 | |
|             if (full || !j)
 | |
|                 vshPrint(ctl, " %-10s %-17s    %s\n",
 | |
|                          iface->name,
 | |
|                          NULLSTR_EMPTY(iface->hwaddr), ip_addr_str);
 | |
|             else
 | |
|                 vshPrint(ctl, " %-10s %-17s    %s\n",
 | |
|                          "-", "-", ip_addr_str);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     ret = true;
 | |
| 
 | |
|  cleanup:
 | |
|     if (ifaces && ifaces_count > 0) {
 | |
|         for (i = 0; i < ifaces_count; i++)
 | |
|             virDomainInterfaceFree(ifaces[i]);
 | |
|     }
 | |
|     VIR_FREE(ifaces);
 | |
| 
 | |
|     return ret;
 | |
| }
 | |
| 
 | |
| const vshCmdDef domMonitoringCmds[] = {
 | |
|     {.name = "domblkerror",
 | |
|      .handler = cmdDomBlkError,
 | |
|      .opts = opts_domblkerror,
 | |
|      .info = info_domblkerror,
 | |
|      .flags = 0
 | |
|     },
 | |
|     {.name = "domblkinfo",
 | |
|      .handler = cmdDomblkinfo,
 | |
|      .opts = opts_domblkinfo,
 | |
|      .info = info_domblkinfo,
 | |
|      .flags = 0
 | |
|     },
 | |
|     {.name = "domblklist",
 | |
|      .handler = cmdDomblklist,
 | |
|      .opts = opts_domblklist,
 | |
|      .info = info_domblklist,
 | |
|      .flags = 0
 | |
|     },
 | |
|     {.name = "domblkstat",
 | |
|      .handler = cmdDomblkstat,
 | |
|      .opts = opts_domblkstat,
 | |
|      .info = info_domblkstat,
 | |
|      .flags = 0
 | |
|     },
 | |
|     {.name = "domcontrol",
 | |
|      .handler = cmdDomControl,
 | |
|      .opts = opts_domcontrol,
 | |
|      .info = info_domcontrol,
 | |
|      .flags = 0
 | |
|     },
 | |
|     {.name = "domif-getlink",
 | |
|      .handler = cmdDomIfGetLink,
 | |
|      .opts = opts_domif_getlink,
 | |
|      .info = info_domif_getlink,
 | |
|      .flags = 0
 | |
|     },
 | |
|     {.name = "domifaddr",
 | |
|      .handler = cmdDomIfAddr,
 | |
|      .opts = opts_domifaddr,
 | |
|      .info = info_domifaddr,
 | |
|      .flags = 0
 | |
|     },
 | |
|     {.name = "domiflist",
 | |
|      .handler = cmdDomiflist,
 | |
|      .opts = opts_domiflist,
 | |
|      .info = info_domiflist,
 | |
|      .flags = 0
 | |
|     },
 | |
|     {.name = "domifstat",
 | |
|      .handler = cmdDomIfstat,
 | |
|      .opts = opts_domifstat,
 | |
|      .info = info_domifstat,
 | |
|      .flags = 0
 | |
|     },
 | |
|     {.name = "dominfo",
 | |
|      .handler = cmdDominfo,
 | |
|      .opts = opts_dominfo,
 | |
|      .info = info_dominfo,
 | |
|      .flags = 0
 | |
|     },
 | |
|     {.name = "dommemstat",
 | |
|      .handler = cmdDomMemStat,
 | |
|      .opts = opts_dommemstat,
 | |
|      .info = info_dommemstat,
 | |
|      .flags = 0
 | |
|     },
 | |
|     {.name = "domstate",
 | |
|      .handler = cmdDomstate,
 | |
|      .opts = opts_domstate,
 | |
|      .info = info_domstate,
 | |
|      .flags = 0
 | |
|     },
 | |
|     {.name = "domstats",
 | |
|      .handler = cmdDomstats,
 | |
|      .opts = opts_domstats,
 | |
|      .info = info_domstats,
 | |
|      .flags = 0
 | |
|     },
 | |
|     {.name = "domtime",
 | |
|      .handler = cmdDomTime,
 | |
|      .opts = opts_domtime,
 | |
|      .info = info_domtime,
 | |
|      .flags = 0
 | |
|     },
 | |
|     {.name = "list",
 | |
|      .handler = cmdList,
 | |
|      .opts = opts_list,
 | |
|      .info = info_list,
 | |
|      .flags = 0
 | |
|     },
 | |
|     {.name = NULL}
 | |
| };
 |