virsh: add support for virConnectListAllDomains and clean up cmdList

This patch makes use of the newly added api virConnectListAllDomains()
to list domains in virsh.

Virsh now represents lists of domains using an internal structure
vshDomainList. This structure contains the virDomainPtr list as provided
by virConnectListAllDomains() and the count of domains in the list.

For backwards compatibility, the function vshDomainListCollect was added
that tries to enumerate the domains using the new API and if the API is
not supported falls back to the older approach with the two list
functions.  The helper function also simulates filtering by all
currently supported flags added with virConnectListAllDomains().

This patch also cleans up the "list" command handler to use the new
helpers and adds new command line flags to make use of filtering.
This commit is contained in:
Peter Krempa 2012-06-19 15:18:16 +02:00
parent f1b872b25a
commit d62e51f86b
2 changed files with 435 additions and 192 deletions

View File

@ -64,6 +64,8 @@
#include "util/bitmap.h"
#include "conf/domain_conf.h"
#include "virtypedparam.h"
#include "intprops.h"
#include "conf/virdomainlist.h"
static char *progname;
@ -490,16 +492,6 @@ _vshStrdup(vshControl *ctl, const char *s, const char *filename, int line)
#define realloc use_vshRealloc_instead_of_realloc
#define strdup use_vshStrdup_instead_of_strdup
static int idsorter(const void *a, const void *b) {
const int *ia = (const int *)a;
const int *ib = (const int *)b;
if (*ia > *ib)
return 1;
else if (*ia < *ib)
return -1;
return 0;
}
static int
vshNameSorter(const void *a, const void *b)
{
@ -990,11 +982,306 @@ static const vshCmdInfo info_list[] = {
{NULL, NULL}
};
/* compare domains, pack NULLed ones at the end*/
static int
vshDomainSorter(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 strcasecmp(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 vshDomainList {
virDomainPtr *domains;
size_t ndomains;
};
typedef struct vshDomainList *vshDomainListPtr;
static void
vshDomainListFree(vshDomainListPtr domlist)
{
int i;
if (domlist && domlist->domains) {
for (i = 0; i < domlist->ndomains; i++) {
if (domlist->domains[i])
virDomainFree(domlist->domains[i]);
}
VIR_FREE(domlist->domains);
}
VIR_FREE(domlist);
}
#define MATCH(FLAG) (flags & (FLAG))
static vshDomainListPtr
vshDomainListCollect(vshControl *ctl, unsigned int flags)
{
vshDomainListPtr list = vshMalloc(ctl, sizeof(*list));
int 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 mansave;
/* try the list with flags support (0.9.13 and later) */
if ((ret = virConnectListAllDomains(ctl->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) {
virFreeError(last_error);
last_error = NULL;
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);
virFreeError(last_error);
last_error = NULL;
if ((ret = virConnectListAllDomains(ctl->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) */
virResetLastError();
/* list active domains, if necessary */
if (!MATCH(VIR_CONNECT_LIST_FILTERS_ACTIVE) ||
MATCH(VIR_CONNECT_LIST_DOMAINS_ACTIVE)) {
if ((nids = virConnectNumOfDomains(ctl->conn)) < 0) {
vshError(ctl, "%s", _("Failed to list active domains"));
goto cleanup;
}
if (nids) {
ids = vshMalloc(ctl, sizeof(int) * nids);
if ((nids = virConnectListDomains(ctl->conn, ids, nids)) < 0) {
vshError(ctl, "%s", _("Failed to list active domains"));
goto cleanup;
}
}
}
if (!MATCH(VIR_CONNECT_LIST_FILTERS_ACTIVE) ||
MATCH(VIR_CONNECT_LIST_DOMAINS_INACTIVE)) {
if ((nnames = virConnectNumOfDefinedDomains(ctl->conn)) < 0) {
vshError(ctl, "%s", _("Failed to list inactive domains"));
goto cleanup;
}
if (nnames) {
names = vshMalloc(ctl, sizeof(char *) * nnames);
if ((nnames = virConnectListDefinedDomains(ctl->conn, names,
nnames)) < 0) {
vshError(ctl, "%s", _("Failed to list inactive domains"));
goto cleanup;
}
}
}
list->domains = vshMalloc(ctl, sizeof(virDomainPtr) * (nids + nnames));
list->ndomains = 0;
/* get active domains */
for (i = 0; i < nids; i++) {
if (!(dom = virDomainLookupByID(ctl->conn, ids[i])))
continue;
list->domains[list->ndomains++] = dom;
}
/* get inactive domains */
for (i = 0; i < nnames; i++) {
if (!(dom = virDomainLookupByName(ctl->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 (MATCH(VIR_CONNECT_LIST_FILTERS_PERSISTENT)) {
if ((persistent = virDomainIsPersistent(dom)) < 0) {
vshError(ctl, "%s", _("Failed to get domain persistence info"));
goto cleanup;
}
if (!((MATCH(VIR_CONNECT_LIST_DOMAINS_PERSISTENT) && persistent) ||
(MATCH(VIR_CONNECT_LIST_DOMAINS_TRANSIENT) && !persistent)))
goto remove_entry;
}
/* domain state filter */
if (MATCH(VIR_CONNECT_LIST_FILTERS_STATE)) {
if (virDomainGetState(dom, &state, NULL, 0) < 0) {
vshError(ctl, "%s", _("Failed to get domain state"));
goto cleanup;
}
if (!((MATCH(VIR_CONNECT_LIST_DOMAINS_RUNNING) &&
state == VIR_DOMAIN_RUNNING) ||
(MATCH(VIR_CONNECT_LIST_DOMAINS_PAUSED) &&
state == VIR_DOMAIN_PAUSED) ||
(MATCH(VIR_CONNECT_LIST_DOMAINS_SHUTOFF) &&
state == VIR_DOMAIN_SHUTOFF) ||
(MATCH(VIR_CONNECT_LIST_DOMAINS_OTHER) &&
(state != VIR_DOMAIN_RUNNING &&
state != VIR_DOMAIN_PAUSED &&
state != VIR_DOMAIN_SHUTOFF))))
goto remove_entry;
}
/* autostart filter */
if (MATCH(VIR_CONNECT_LIST_FILTERS_AUTOSTART)) {
if (virDomainGetAutostart(dom, &autostart) < 0) {
vshError(ctl, "%s", _("Failed to get domain autostart state"));
goto cleanup;
}
if (!((MATCH(VIR_CONNECT_LIST_DOMAINS_AUTOSTART) && autostart) ||
(MATCH(VIR_CONNECT_LIST_DOMAINS_NO_AUTOSTART) && !autostart)))
goto remove_entry;
}
/* managed save filter */
if (MATCH(VIR_CONNECT_LIST_FILTERS_MANAGEDSAVE)) {
if ((mansave = virDomainHasManagedSaveImage(dom, 0)) < 0) {
vshError(ctl, "%s",
_("Failed to check for managed save image"));
goto cleanup;
}
if (!((MATCH(VIR_CONNECT_LIST_DOMAINS_MANAGEDSAVE) && mansave) ||
(MATCH(VIR_CONNECT_LIST_DOMAINS_NO_MANAGEDSAVE) && !mansave)))
goto remove_entry;
}
/* snapshot filter */
if (MATCH(VIR_CONNECT_LIST_FILTERS_SNAPSHOT)) {
if ((nsnap = virDomainSnapshotNum(dom, 0)) < 0) {
vshError(ctl, "%s", _("Failed to get snapshot count"));
goto cleanup;
}
if (!((MATCH(VIR_CONNECT_LIST_DOMAINS_HAS_SNAPSHOT) && nsnap > 0) ||
(MATCH(VIR_CONNECT_LIST_DOMAINS_NO_SNAPSHOT) && nsnap == 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 */
virDomainFree(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),
vshDomainSorter);
/* truncate the list if filter simulation deleted entries */
if (deleted)
VIR_SHRINK_N(list->domains, list->ndomains, deleted);
success = true;
cleanup:
for (i = 0; i < nnames; i++)
VIR_FREE(names[i]);
if (!success) {
vshDomainListFree(list);
list = NULL;
}
VIR_FREE(names);
VIR_FREE(ids);
return list;
}
#undef MATCH
static const vshCmdOptDef opts_list[] = {
{"inactive", VSH_OT_BOOL, 0, N_("list inactive domains")},
{"all", VSH_OT_BOOL, 0, N_("list inactive & active domains")},
{"transient", VSH_OT_BOOL, 0, N_("list transient domains")},
{"persistent", VSH_OT_BOOL, 0, N_("list persistent domains")},
{"with-snapshot", VSH_OT_BOOL, 0,
N_("list domains with existing snapshot")},
{"without-snapshot", VSH_OT_BOOL, 0,
N_("list domains without a snapshot")},
{"state-running", VSH_OT_BOOL, 0, N_("list domains in running state")},
{"state-paused", VSH_OT_BOOL, 0, N_("list domains in paused state")},
{"state-shutoff", VSH_OT_BOOL, 0, N_("list domains in shutoff state")},
{"state-other", VSH_OT_BOOL, 0, N_("list domains in other states")},
{"autostart", VSH_OT_BOOL, 0, N_("list domains with autostart enabled")},
{"no-autostart", VSH_OT_BOOL, 0,
N_("list domains with autostart disabled")},
{"with-managed-save", VSH_OT_BOOL, 0,
N_("list domains with managed save state")},
{"without-managed-save", VSH_OT_BOOL, 0,
N_("list domains without managed save")},
{"uuid", VSH_OT_BOOL, 0, N_("list uuid's only")},
{"name", VSH_OT_BOOL, 0, N_("list domain names only")},
{"table", VSH_OT_BOOL, 0, N_("list table (default)")},
@ -1005,38 +1292,52 @@ static const vshCmdOptDef opts_list[] = {
};
#define FILTER(NAME, FLAG) \
if (vshCommandOptBool(cmd, NAME)) \
flags |= (FLAG)
static bool
cmdList(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
{
bool inactive = vshCommandOptBool(cmd, "inactive");
bool all = vshCommandOptBool(cmd, "all");
bool active = !inactive || all;
int *ids = NULL, maxid = 0, i;
char **names = NULL;
int maxname = 0;
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 optPersistent = vshCommandOptBool(cmd, "persistent");
bool optTransient = vshCommandOptBool(cmd, "transient");
bool persistUsed = true;
virDomainPtr dom = NULL;
int i;
char *title;
char uuid[VIR_UUID_STRING_BUFLEN];
int state;
int persistent;
bool ret = false;
vshDomainListPtr list = NULL;
virDomainPtr dom;
char id_buf[INT_BUFSIZE_BOUND(unsigned int)];
unsigned int id;
unsigned int flags = VIR_CONNECT_LIST_DOMAINS_ACTIVE;
inactive |= all;
/* construct filter flags */
if (vshCommandOptBool(cmd, "inactive"))
flags = VIR_CONNECT_LIST_DOMAINS_INACTIVE;
/* process arguments */
if (!optPersistent && !optTransient) {
optPersistent = true;
optTransient = true;
persistUsed = false;
}
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("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);
if (optTable + optName + optUUID > 1) {
vshError(ctl, "%s",
@ -1051,169 +1352,69 @@ cmdList(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
if (!vshConnectionUsability(ctl, ctl->conn))
return false;
if (active) {
maxid = virConnectNumOfDomains(ctl->conn);
if (maxid < 0) {
vshError(ctl, "%s", _("Failed to list active domains"));
return false;
}
if (maxid) {
ids = vshMalloc(ctl, sizeof(int) * maxid);
if ((maxid = virConnectListDomains(ctl->conn, ids, maxid)) < 0) {
vshError(ctl, "%s", _("Failed to list active domains"));
goto cleanup;
}
qsort(ids, maxid, sizeof(int), idsorter);
}
}
if (inactive) {
maxname = virConnectNumOfDefinedDomains(ctl->conn);
if (maxname < 0) {
vshError(ctl, "%s", _("Failed to list inactive domains"));
goto cleanup;
}
if (maxname) {
names = vshMalloc(ctl, sizeof(char *) * maxname);
if ((maxname = virConnectListDefinedDomains(ctl->conn,
names,
maxname)) < 0) {
vshError(ctl, "%s", _("Failed to list inactive domains"));
goto cleanup;
}
qsort(&names[0], maxname, sizeof(char*), vshNameSorter);
}
}
if (!(list = vshDomainListCollect(ctl, flags)))
goto cleanup;
/* print table header in legacy mode */
if (optTable) {
if (optTitle) {
if (optTitle)
vshPrintExtra(ctl, " %-5s %-30s %-10s %-20s\n%s\n",
_("Id"), _("Name"), _("State"), _("Title"),
"-----------------------------------------"
"-----------------------------------------");
} else {
else
vshPrintExtra(ctl, " %-5s %-30s %s\n%s\n",
_("Id"), _("Name"), _("State"),
"-----------------------------------------"
"-----------");
}
}
for (i = 0; i < maxid; i++) {
dom = virDomainLookupByID(ctl->conn, ids[i]);
for (i = 0; i < list->ndomains; i++) {
dom = list->domains[i];
id = virDomainGetID(dom);
if (id != (unsigned int) -1)
snprintf(id_buf, sizeof(id_buf), "%d", id);
else
ignore_value(virStrcpyStatic(id_buf, "-"));
/* this kind of work with domains is not atomic operation */
if (!dom)
continue;
state = vshDomainState(ctl, dom, NULL);
if (optTable && managed && state == VIR_DOMAIN_SHUTOFF &&
virDomainHasManagedSaveImage(dom, 0) > 0)
state = -2;
if (persistUsed) {
persistent = virDomainIsPersistent(dom);
if (persistent < 0) {
vshError(ctl, "%s",
_("Failed to determine domain's persistent state"));
if (optTable) {
if (optTitle) {
if (!(title = vshGetDomainDescription(ctl, dom, true, 0)))
goto cleanup;
vshPrint(ctl, " %-5s %-30s %-10s %-20s\n", id_buf,
virDomainGetName(dom),
state == -2 ? _("saved") : _(vshDomainStateToString(state)),
title);
VIR_FREE(title);
} else {
vshPrint(ctl, " %-5s %-30s %s\n", id_buf,
virDomainGetName(dom),
state == -2 ? _("saved") : _(vshDomainStateToString(state)));
}
} else if (optUUID) {
if (virDomainGetUUIDString(dom, uuid) < 0) {
vshError(ctl, "%s", _("Failed to get domain's UUID"));
goto cleanup;
}
if (!(persistent ? optPersistent : optTransient)) {
virDomainFree(dom);
dom = NULL;
continue;
}
}
if (optTable) {
if (optTitle) {
if (!(title = vshGetDomainDescription(ctl, dom, true, 0)))
goto cleanup;
vshPrint(ctl, " %-5d %-30s %-10s %-20s\n",
virDomainGetID(dom),
virDomainGetName(dom),
_(vshDomainStateToString(vshDomainState(ctl, dom, NULL))),
title);
VIR_FREE(title);
} else {
vshPrint(ctl, " %-5d %-30s %s\n",
virDomainGetID(dom),
virDomainGetName(dom),
_(vshDomainStateToString(vshDomainState(ctl, dom, NULL))));
}
} else if (optUUID) {
if (virDomainGetUUIDString(dom, uuid) < 0) {
vshError(ctl, "%s",
_("Failed to get domain's UUID"));
goto cleanup;
}
vshPrint(ctl, "%s\n", uuid);
} else if (optName) {
vshPrint(ctl, "%s\n", virDomainGetName(dom));
}
virDomainFree(dom);
dom = NULL;
}
if (optPersistent) {
for (i = 0; i < maxname; i++) {
dom = virDomainLookupByName(ctl->conn, names[i]);
/* this kind of work with domains is not atomic operation */
if (!dom)
continue;
if (optTable) {
state = vshDomainState(ctl, dom, NULL);
if (managed && state == VIR_DOMAIN_SHUTOFF &&
virDomainHasManagedSaveImage(dom, 0) > 0)
state = -2;
if (optTitle) {
if (!(title = vshGetDomainDescription(ctl, dom, true, 0)))
goto cleanup;
vshPrint(ctl, " %-5s %-30s %-10s %-20s\n",
"-",
names[i],
state == -2 ? _("saved") : _(vshDomainStateToString(state)),
title);
VIR_FREE(title);
} else {
vshPrint(ctl, " %-5s %-30s %s\n",
"-",
names[i],
state == -2 ? _("saved") : _(vshDomainStateToString(state)));
}
} else if (optUUID) {
if (virDomainGetUUIDString(dom, uuid) < 0) {
vshError(ctl, "%s",
_("Failed to get domain's UUID"));
goto cleanup;
}
vshPrint(ctl, "%s\n", uuid);
} else if (optName) {
vshPrint(ctl, "%s\n", names[i]);
}
virDomainFree(dom);
dom = NULL;
vshPrint(ctl, "%s\n", uuid);
} else if (optName) {
vshPrint(ctl, "%s\n", virDomainGetName(dom));
}
}
ret = true;
cleanup:
if (dom)
virDomainFree(dom);
VIR_FREE(ids);
for (i = 0; i < maxname; i++)
VIR_FREE(names[i]);
VIR_FREE(names);
vshDomainListFree(list);
return ret;
}
#undef FILTER
/*
* "desc" command for managing domain description and title

View File

@ -306,9 +306,15 @@ The XML also show the NUMA topology information if available.
Inject NMI to the guest.
=item B<list> [I<--inactive> | I<--all>] [I<--managed-save>] [I<--title>]
{ [I<--table>] | I<--name> | I<--uuid> } [I<--persistent>]
[I<--transient>]
=item B<list> [I<--inactive> | I<--all>]
[I<--managed-save>] [I<--title>]
{ [I<--table>] | I<--name> | I<--uuid> }
[I<--persistent>] [I<--transient>]
[I<--with-managed-save>] [I<--without-managed-save>]
[I<--autostart>] [I<--no-autostart>]
[I<--with-snapshot>] [I<--without-snapshot>]
[I<--state-running>] [I<--state-paused>]
[I<--state-shutoff>] [I<--state-other>]
Prints information about existing domains. If no options are
specified it prints out information about running domains.
@ -316,13 +322,10 @@ specified it prints out information about running domains.
An example format for the list is as follows:
B<virsh> list
Id Name State
----------------------------------
0 Domain-0 running
2 fedora paused
Id Name State
----------------------------------------------------
0 Domain-0 running
2 fedora paused
Name is the name of the domain. ID the domain numeric id.
State is the run state (see below).
@ -380,11 +383,56 @@ into s3 state.
=back
If I<--managed-save> is specified, then domains that have managed save
state (only possible if they are in the B<shut off> state, so you need to
specify I<--inactive> or I<--all> to actually list them) will instead
show as B<saved> in the listing. This flag is usable only with the
default I<--table> output.
Normally only active domains are listed. To list inactive domains specify
I<--inactive> or I<--all> to list both active and inactive domains.
To filter the list of domains present on the hypervisor you may specify one or
more of filtering flags supported by the B<list> command. These flags are
grouped by function. Specifying one or more flags from a group enables the
filter group. Supported filtering flags and groups:
=over 4
=item B<Persistence>
Flag I<--persistent> is used to include persistent domains in the returned
list. To include transient domains specify I<--transient>.
=item B<Existence of managed save image>
To list domains having a managed save image specify flag
I<--with-managed-save>. For domains that don't have a managed save image
specify I<--without-managed-save>.
=item B<Domain state>
The following filter flags select a domain by its state:
I<--state-running> for running domains, I<--state-paused> for paused domains,
I<--state-shutoff> for turned off domains and I<--state-other> for all
other states as a fallback.
=item B<Autostarting domains>
To list autostarting domains use the flag I<--autostart>. To list domains with
this feature disabled use I<--no-autostart>.
=item B<Snapshot existence>
Domains that have snapshot images can be listed using flag I<--with-snapshot>,
domains without a snapshot I<--without-snapshot>.
=back
When talking to older servers, this command is forced to use a series of API
calls with an inherent race, where a domain might not be listed or might appear
more than once if it changed state between calls while the list was being
collected. Newer servers do not have this problem.
If I<--managed-save> is specified, then domains that have managed save state
(only possible if they are in the B<shut off> state, so you need to specify
I<--inactive> or I<--all> to actually list them) will instead show as B<saved>
in the listing. This flag is usable only with the default I<--table> output.
Note that this flag does not filter the list of domains.
If I<--name> is specified, domain names are printed instead of the table
formatted one per line. If I<--uuid> is specified domain's UUID's are printed
@ -392,12 +440,6 @@ instead of names. Flag I<--table> specifies that the legacy table-formatted
output should be used. This is the default. All of these are mutually
exclusive.
Flag I<--persistent> specifies that persistent domains should be printed.
Similarly I<--transient> enables output of transient domains. These flags
may be combined. Default behavior is as though both were specified. (Note that
if any of these flags is specified, the check for the persistence state is done
and may fail. If none of these flags is specified, the check is skipped.)
If I<--title> is specified, then the short domain description (title) is
printed in an extra column. This flag is usable only with the default
I<--table> output.
@ -405,10 +447,10 @@ I<--table> output.
Example:
B<virsh> list --title
Id Name State Title
-----------------------------------------------
0 Domain-0 running Mailserver 1
2 fedora paused
Id Name State Title
--------------------------------------------------------------------------
0 Domain-0 running Mailserver 1
2 fedora paused
=item B<freecell> [{ [I<--cellno>] B<cellno> | I<--all> }]