[libvirt] PATCH: Bash auto-completion for virsh

Daniel P. Berrange berrange at redhat.com
Mon Jul 14 10:57:07 UTC 2008


Everyone[1] seems to be doing funky bash auto-completion for commands these
days, so I thought I'd make a stab at doing something for virsh.

First of all I needed the command line help in an easier format to deal
with, so I've added

   virsh _complete-command commands|arguments|options

eg,

   $ virsh _complete-command commands
   help
   attach-device
   attach-disk
   attach-interface
   autostart
   capabilities
   connect
   console
   ...snip rest...

   $ virsh _complete-command arguments vcpupin
   domain
   vcpu
   cpulist

   $ virsh _complete-command options
   -c
   --connect
   -r
   --readonly
   -d
   --debug
   -h
   --help
   -q
   --quiet
   -t
   --timing
   -l
   --log
   -v
   --version

   $ virsh _complete-command options net-list
   --inactive
   --all


That's enough to get all the basic commands & options auto-completing.
For the arguments though I wanted to be able to auto-complete domain
names, ids and UUIDs. likewise for storage pools and networks. I also
wanted to auto-complete connect URIs ,eg test://default, qemu:///system

   $ virsh _complete-domain
   ISCSIServer
   35e5efcc-f6a3-b489-fc33-99f2e4792e6d
   untangle
   dc502cb8-4bdd-7a15-f35a-cf76287ea2bd
   VirtTest
   82038f2a-1344-aaf7-1a85-2a7250be2076
   xenner
   18c23d31-4d74-0bff-986d-e4251d2a2ff0

And 

   $ virsh _complete-uri
   test:///default
   qemu:///system
   lxc:///

For this latter command, I needed to add a new API

  /**
   * virConnectURIs:
   * uris: pointer to a list to be filled with URI strings
   *
   * This functions returns the list of available connection
   * URIs. The caller is responsible for freeing the returned
   * strings, and the array they're stored in.
   *
   * Returns the number of URIs in case of success or -1 in
   * case of error.
   */
  int virConnectURIs(char ***uris);

This simply calls the existing probe() method for each driver and
stores the list of URIs returned.

Finally to tie this all together simply needs a little helper script
which by convention is added to the /etc/bash_completion.d/. On Fedora
this isn't enable by default, so you have to turn it on with

   source /etc/bash_completion.d/virsh  

To your $HOME/.bashrc

Daniel

[1] For a definition of 'everyone' which includes git & mercurial

 b/src/virsh.bash             |   79 +++++++
 include/libvirt/libvirt.h    |    1 
 include/libvirt/libvirt.h.in |    1 
 libvirt.spec.in              |    1 
 src/Makefile.am              |   10 
 src/libvirt.c                |   54 +++++
 src/libvirt_sym.version      |    2 
 src/test.c                   |    6 
 src/virsh.c                  |  442 ++++++++++++++++++++++++++++++++++++++++++-
 9 files changed, 581 insertions(+), 15 deletions(-)


diff -r f3413463b3ff include/libvirt/libvirt.h
--- a/include/libvirt/libvirt.h	Sun Jul 13 13:14:50 2008 +0100
+++ b/include/libvirt/libvirt.h	Mon Jul 14 11:54:10 2008 +0100
@@ -397,6 +397,7 @@
                                                  virConnectAuthPtr auth,
                                                  int flags);
 int                     virConnectClose         (virConnectPtr conn);
+int                     virConnectURIs          (char ***uris);
 const char *            virConnectGetType       (virConnectPtr conn);
 int                     virConnectGetVersion    (virConnectPtr conn,
                                                  unsigned long *hvVer);
diff -r f3413463b3ff include/libvirt/libvirt.h.in
--- a/include/libvirt/libvirt.h.in	Sun Jul 13 13:14:50 2008 +0100
+++ b/include/libvirt/libvirt.h.in	Mon Jul 14 11:54:10 2008 +0100
@@ -397,6 +397,7 @@
                                                  virConnectAuthPtr auth,
                                                  int flags);
 int                     virConnectClose         (virConnectPtr conn);
+int                     virConnectURIs          (char ***uris);
 const char *            virConnectGetType       (virConnectPtr conn);
 int                     virConnectGetVersion    (virConnectPtr conn,
                                                  unsigned long *hvVer);
diff -r f3413463b3ff libvirt.spec.in
--- a/libvirt.spec.in	Sun Jul 13 13:14:50 2008 +0100
+++ b/libvirt.spec.in	Mon Jul 14 11:54:10 2008 +0100
@@ -227,6 +227,7 @@
 %dir %attr(0700, root, root) %{_sysconfdir}/libvirt/qemu/
 %dir %attr(0700, root, root) %{_sysconfdir}/libvirt/qemu/networks/
 %dir %attr(0700, root, root) %{_sysconfdir}/libvirt/qemu/networks/autostart
+%{_sysconfdir}/bash_completion.d/virsh
 %{_sysconfdir}/rc.d/init.d/libvirtd
 %config(noreplace) %{_sysconfdir}/sysconfig/libvirtd
 %config(noreplace) %{_sysconfdir}/libvirt/libvirtd.conf
diff -r f3413463b3ff src/Makefile.am
--- a/src/Makefile.am	Sun Jul 13 13:14:50 2008 +0100
+++ b/src/Makefile.am	Mon Jul 14 11:54:10 2008 +0100
@@ -25,8 +25,9 @@
 confdir = $(sysconfdir)/libvirt/
 conf_DATA = qemu.conf
 
+bashdir = $(sysconfdir)/bash_completion.d
 
-EXTRA_DIST = libvirt_sym.version $(conf_DATA)
+EXTRA_DIST = libvirt_sym.version $(conf_DATA) virsh.bash
 
 lib_LTLIBRARIES = libvirt.la
 
@@ -153,7 +154,12 @@
 endif
 
 # Create the /var/cache/libvirt directory when installing.
-install-exec-local:
+install-exec-local::
 	$(MKDIR_P) $(DESTDIR)$(localstatedir)/cache/libvirt
+	$(MKDIR_P) $(DESTDIR)$(bashdir)
+	$(INSTALL_SCRIPT) virsh.bash $(bashdir)/virsh
+
+uninstall-local::
+	$(RM) -f $(bashdir)/virsh
 
 CLEANFILES = *.gcov .libs/*.gcda .libs/*.gcno *.gcno *.gcda
diff -r f3413463b3ff src/libvirt.c
--- a/src/libvirt.c	Sun Jul 13 13:14:50 2008 +0100
+++ b/src/libvirt.c	Mon Jul 14 11:54:10 2008 +0100
@@ -37,6 +37,7 @@
 #include "uuid.h"
 #include "util.h"
 #include "test.h"
+#include "memory.h"
 #include "xen_unified.h"
 #include "remote_internal.h"
 #include "qemu_driver.h"
@@ -701,7 +702,8 @@
             int probes = 0;
             for (i = 0; i < virDriverTabCount; i++) {
                 if ((virDriverTab[i]->probe != NULL) &&
-                    ((latest = virDriverTab[i]->probe()) != NULL)) {
+                    ((latest = virDriverTab[i]->probe()) != NULL) &&
+                    STRNEQ(latest, "test:///default")) {
                     probes++;
 
                     DEBUG("Probed %s", latest);
@@ -946,6 +948,56 @@
     if (virUnrefConnect(conn) < 0)
         return (-1);
     return (0);
+}
+
+
+/**
+ * virConnectURIs:
+ * uris: pointer to a list to be filled with URI strings
+ *
+ * This functions returns the list of available connection
+ * URIs. The caller is responsible for freeing the returned
+ * strings, and the array they're stored in.
+ *
+ * Returns the number of URIs in case of success or -1 in
+ * case of error.
+ */
+int
+virConnectURIs(char ***uris)
+{
+    const char *uri;
+    char **ret = NULL;
+    int nuris = 0, i;
+    DEBUG("uris=%p", uris);
+
+    if (!initialized)
+        if (virInitialize() < 0)
+            return -1;
+
+    if (!uris)
+        return -1;
+
+    for (i = 0; i < virDriverTabCount; i++) {
+        if ((virDriverTab[i]->probe != NULL) &&
+            ((uri = virDriverTab[i]->probe()) != NULL)) {
+            if (VIR_REALLOC_N(ret, nuris + 1) < 0)
+                goto no_memory;
+            if ((ret[nuris++] = strdup(uri)) == NULL)
+                goto no_memory;
+
+            DEBUG("Probed %s", uri);
+        }
+    }
+
+    *uris = ret;
+    return (nuris);
+
+no_memory:
+    for (i = 0 ; i < nuris ; i++)
+        VIR_FREE(ret[i]);
+    VIR_FREE(ret);
+    virLibConnError(NULL, VIR_ERR_NO_MEMORY, NULL);
+    return -1;
 }
 
 /* Not for public use.  This function is part of the internal
diff -r f3413463b3ff src/libvirt_sym.version
--- a/src/libvirt_sym.version	Sun Jul 13 13:14:50 2008 +0100
+++ b/src/libvirt_sym.version	Mon Jul 14 11:54:10 2008 +0100
@@ -5,7 +5,7 @@
         virConnectOpenReadOnly;
         virConnectOpenAuth;
         virConnectAuthPtrDefault;
-
+        virConnectURIs;
 	virConnectClose;
 	virConnectGetType;
 	virConnectGetVersion;
diff -r f3413463b3ff src/test.c
--- a/src/test.c	Sun Jul 13 13:14:50 2008 +0100
+++ b/src/test.c	Mon Jul 14 11:54:10 2008 +0100
@@ -215,6 +215,10 @@
 "  </ip>"
 "</network>";
 
+
+static const char *testProbe(void) {
+    return "test:///default";
+}
 
 static int testOpenDefault(virConnectPtr conn) {
     int u;
@@ -1545,7 +1549,7 @@
     VIR_DRV_TEST,
     "Test",
     LIBVIR_VERSION_NUMBER,
-    NULL, /* probe */
+    testProbe, /* probe */
     testOpen, /* open */
     testClose, /* close */
     NULL, /* supports_feature */
diff -r f3413463b3ff src/virsh.bash
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/virsh.bash	Mon Jul 14 11:54:10 2008 +0100
@@ -0,0 +1,79 @@
+
+_virsh()
+{
+    local cur prev cmd cmd_index arg arg_index i results
+    local virsh="$1"
+    local canonical=0
+
+    COMPREPLY=()
+    cur="$2"
+    prev="$3"
+
+    if [[ $COMP_CWORD > 1 && ${COMP_WORDS[COMP_CWORD-1]} = '--connect' ]]; then
+        results="$("$virsh" _complete-uri)" || nets=""
+    fi
+
+    if [ -z "$results" ]; then
+        # searching for the command name
+        for ((i=1; $i<=$COMP_CWORD; i++)); do
+            if [[ ${COMP_WORDS[i]} != -* ]]; then
+                if [[ ${COMP_WORDS[i-1]} != '--connect' ]]; then
+                    if [[ $i < $COMP_CWORD ]]; then
+                       cmd="${COMP_WORDS[i]}"
+                       cmd_index=$i
+                       arg_index=`expr $COMP_CWORD - $cmd_index`
+                       break
+                    fi
+                fi
+            fi
+        done
+
+        if [[ "$cur" == -* ]]; then
+            # Generate args - global or command specific
+            results="$("$virsh" _complete-command options "$cmd")"
+        else
+            if [ -z "$cmd" ]; then
+                # No command set, so generate list of all commands
+                results="$("$virsh" _complete-command commands)" || commands=""
+            else
+                # Command set, to generate command specific args
+                n=0
+                for i in "$("$virsh" _complete-command arguments "$cmd")"
+                do
+                    n=`expr $n + 1`
+                    if [ $n = $arg_index ]; then
+                        arg=$i
+                        break
+                    fi
+                done
+
+                case $arg in
+                  file)
+                    COMPREPLY=(${COMPREPLY[@]:-} $(compgen -o "default" -- "$cur"))
+                    ;;
+
+                  domain)
+                    results="$("$virsh" _complete-domain)" || doms=""
+                    ;;
+
+                  network)
+                    results="$("$virsh" _complete-network)" || nets=""
+                    ;;
+
+                  pool)
+                    results="$("$virsh" _complete-pool)" || nets=""
+                    ;;
+
+                  uri)
+                    results="$("$virsh" _complete-uri)" || nets=""
+                    ;;
+                esac
+            fi
+        fi
+    fi
+    if [ ! -z "$results" ]; then
+        COMPREPLY=(${COMPREPLY[@]:-} $(compgen -W '$results' -- "$cur"))
+    fi
+}
+
+complete -F _virsh virsh
diff -r f3413463b3ff src/virsh.c
--- a/src/virsh.c	Sun Jul 13 13:14:50 2008 +0100
+++ b/src/virsh.c	Mon Jul 14 11:54:10 2008 +0100
@@ -329,6 +329,409 @@
  * ---------------
  */
 
+
+
+/*
+ * "_compcmds" command
+ */
+static vshCmdInfo info_compcommand[] = {
+    {"syntax", "_complete-command [commands|arguments|options] [<command>]"},
+    {"help", gettext_noop("print shell completion data")},
+    {"desc", gettext_noop("Prints data useful for shell autocompletion of commands.")},
+
+    {NULL, NULL}
+};
+
+static vshCmdOptDef opts_compcommand[] = {
+    {"type", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("type of data")},
+    {"cmd", VSH_OT_DATA, 0, gettext_noop("command name")},
+    {NULL, 0, 0, NULL}
+};
+
+static int
+cmdCompCommand(vshControl * ctl, vshCmd * cmd)
+{
+    const char *type = vshCommandOptString(cmd, "type", NULL);
+    const char *name = vshCommandOptString(cmd, "cmd", NULL);
+
+    if (STREQ(type, "commands")) {
+        vshCmdDef *def;
+        for (def = commands; def->name; def++)
+            if (def->name[0] != '_')
+                vshPrint(ctl, "%s\n", def->name);
+    } else if (STREQ(type, "options")) {
+        if (name) {
+            vshCmdDef *def = vshCmddefSearch(name);
+            if (!def) {
+                vshError(ctl, FALSE, _("command '%s' doesn't exist"), name);
+                return FALSE;
+            } else if (def->opts) {
+                vshCmdOptDef *opt;
+                for (opt = def->opts; opt->name; opt++)
+                    if (opt->type == VSH_OT_BOOL ||
+                        opt->type == VSH_OT_INT ||
+                        opt->type == VSH_OT_STRING)
+                        vshPrint(ctl, "--%s\n", opt->name);
+            }
+        } else {
+            vshPrint(ctl,
+                     "-c\n--connect\n"
+                     "-r\n--readonly\n"
+                     "-d\n--debug\n"
+                     "-h\n--help\n"
+                     "-q\n--quiet\n"
+                     "-t\n--timing\n"
+                     "-l\n--log\n"
+                     "-v\n--version\n");
+        }
+    } else if (STREQ(type, "arguments")) {
+        if (!name) {
+            vshError(ctl, FALSE, "%s", _("no command specified"));
+            return FALSE;
+        } else {
+            vshCmdDef *def = vshCmddefSearch(name);
+            if (!def) {
+                vshError(ctl, FALSE, _("command '%s' doesn't exist"), name);
+                return FALSE;
+            } else if (def->opts) {
+                vshCmdOptDef *opt;
+                for (opt = def->opts; opt->name; opt++)
+                    if (opt->type == VSH_OT_DATA)
+                        vshPrint(ctl, "%s\n", opt->name);
+            }
+        }
+    }
+    return TRUE;
+}
+
+/*
+ * "_compuris" command
+ */
+static vshCmdInfo info_compuri[] = {
+    {"syntax", "_complete-uri"},
+    {"help", gettext_noop("print shell completion data for URIs")},
+    {"desc", gettext_noop("Prints data useful for shell autocompletion of URIs.")},
+
+    {NULL, NULL}
+};
+
+static vshCmdOptDef opts_compuri[] = {
+    {NULL, 0, 0, NULL}
+};
+
+static int
+cmdCompUri(vshControl * ctl, vshCmd * cmd ATTRIBUTE_UNUSED)
+{
+    char **uris = NULL;
+    int nuris, i;
+
+    nuris = virConnectURIs(&uris);
+    if (nuris < 0) {
+        vshError(ctl, FALSE, "%s", _("Failed to list connection URIs"));
+        return FALSE;
+    }
+    for (i = 0 ; i < nuris ; i++) {
+        vshPrint(ctl, "%s\n", uris[i]);
+        free(uris[i]);
+    }
+
+    free(uris);
+    return TRUE;
+}
+/*
+ * "_compdomains" command
+ */
+static vshCmdInfo info_compdomain[] = {
+    {"syntax", "_complete-domain"},
+    {"help", gettext_noop("print shell completion data for domains")},
+    {"desc", gettext_noop("Prints data useful for shell autocompletion of domains.")},
+
+    {NULL, NULL}
+};
+
+static vshCmdOptDef opts_compdomain[] = {
+    {NULL, 0, 0, NULL}
+};
+
+static int
+cmdCompDomain(vshControl * ctl, vshCmd * cmd ATTRIBUTE_UNUSED)
+{
+    int *ids = NULL;
+    char **names = NULL;
+    int maxname = 0, maxid = 0, i;
+
+    if (!vshConnectionUsability(ctl, ctl->conn, TRUE))
+        return FALSE;
+
+    maxid = virConnectNumOfDomains(ctl->conn);
+    if (maxid < 0) {
+        vshError(ctl, FALSE, "%s", _("Failed to list active domains"));
+        return FALSE;
+    }
+    if (maxid) {
+        ids = vshMalloc(ctl, sizeof(int) * maxid);
+
+        if ((maxid = virConnectListDomains(ctl->conn, &ids[0], maxid)) < 0) {
+            vshError(ctl, FALSE, "%s", _("Failed to list active domains"));
+            free(ids);
+            return FALSE;
+        }
+
+        qsort(&ids[0], maxid, sizeof(int), idsorter);
+    }
+    maxname = virConnectNumOfDefinedDomains(ctl->conn);
+    if (maxname < 0) {
+        vshError(ctl, FALSE, "%s", _("Failed to list inactive domains"));
+        free(ids);
+        return FALSE;
+    }
+    if (maxname) {
+        names = vshMalloc(ctl, sizeof(char *) * maxname);
+
+        if ((maxname = virConnectListDefinedDomains(ctl->conn, names, maxname)) < 0) {
+            vshError(ctl, FALSE, "%s", _("Failed to list inactive domains"));
+            free(ids);
+            free(names);
+            return FALSE;
+        }
+
+        qsort(&names[0], maxname, sizeof(char*), namesorter);
+    }
+
+    for (i = 0; i < maxid; i++) {
+        char uuid[VIR_UUID_STRING_BUFLEN];
+        virDomainPtr dom = virDomainLookupByID(ctl->conn, ids[i]);
+        /* this kind of work with domains is not atomic operation */
+        if (!dom)
+            continue;
+        virDomainGetUUIDString(dom, uuid);
+        vshPrint(ctl, "%d\n%s\n%s\n",
+                 virDomainGetID(dom),
+                 virDomainGetName(dom),
+                 uuid);
+        virDomainFree(dom);
+    }
+    for (i = 0; i < maxname; i++) {
+        char uuid[VIR_UUID_STRING_BUFLEN];
+        virDomainPtr dom = virDomainLookupByName(ctl->conn, names[i]);
+
+        /* this kind of work with domains is not atomic operation */
+        if (!dom) {
+            free(names[i]);
+            continue;
+        }
+        virDomainGetUUIDString(dom, uuid);
+        vshPrint(ctl, "%s\n%s\n",
+                 virDomainGetName(dom),
+                 uuid);
+        virDomainFree(dom);
+        free(names[i]);
+    }
+    free(ids);
+    free(names);
+    return TRUE;
+}
+
+/*
+ * "_compnetworks" command
+ */
+static vshCmdInfo info_compnetwork[] = {
+    {"syntax", "_complete-network"},
+    {"help", gettext_noop("print shell completion data for networks")},
+    {"desc", gettext_noop("Prints data useful for shell autocompletion of networks.")},
+
+    {NULL, NULL}
+};
+
+static vshCmdOptDef opts_compnetwork[] = {
+    {NULL, 0, 0, NULL}
+};
+
+static int
+cmdCompNetwork(vshControl * ctl, vshCmd * cmd ATTRIBUTE_UNUSED)
+{
+    char **activeNames = NULL, **inactiveNames = NULL;
+    int maxactive = 0, maxinactive = 0, i;
+
+    if (!vshConnectionUsability(ctl, ctl->conn, TRUE))
+        return FALSE;
+
+    maxactive = virConnectNumOfNetworks(ctl->conn);
+    if (maxactive < 0) {
+        vshError(ctl, FALSE, "%s", _("Failed to list active networks"));
+        return FALSE;
+    }
+    if (maxactive) {
+        activeNames = vshMalloc(ctl, sizeof(char *) * maxactive);
+
+        if ((maxactive = virConnectListNetworks(ctl->conn, activeNames,
+                                                maxactive)) < 0) {
+            vshError(ctl, FALSE, "%s", _("Failed to list active networks"));
+            free(activeNames);
+            return FALSE;
+        }
+
+        qsort(&activeNames[0], maxactive, sizeof(char *), namesorter);
+    }
+    maxinactive = virConnectNumOfDefinedNetworks(ctl->conn);
+    if (maxinactive < 0) {
+        vshError(ctl, FALSE, "%s", _("Failed to list inactive networks"));
+        free(activeNames);
+        return FALSE;
+    }
+    if (maxinactive) {
+        inactiveNames = vshMalloc(ctl, sizeof(char *) * maxinactive);
+
+        if ((maxinactive = virConnectListDefinedNetworks(ctl->conn, inactiveNames, maxinactive)) < 0) {
+            vshError(ctl, FALSE, "%s", _("Failed to list inactive networks"));
+            free(activeNames);
+            free(inactiveNames);
+            return FALSE;
+        }
+
+        qsort(&inactiveNames[0], maxinactive, sizeof(char*), namesorter);
+    }
+
+    for (i = 0; i < maxactive; i++) {
+        char uuid[VIR_UUID_STRING_BUFLEN];
+        virNetworkPtr network = virNetworkLookupByName(ctl->conn, activeNames[i]);
+
+        /* this kind of work with networks is not atomic operation */
+        if (!network) {
+            free(activeNames[i]);
+            continue;
+        }
+        virNetworkGetUUIDString(network, uuid);
+        vshPrint(ctl, "%s\n%s\n",
+                 virNetworkGetName(network),
+                 uuid);
+        virNetworkFree(network);
+        free(activeNames[i]);
+    }
+    for (i = 0; i < maxinactive; i++) {
+        char uuid[VIR_UUID_STRING_BUFLEN];
+        virNetworkPtr network = virNetworkLookupByName(ctl->conn, inactiveNames[i]);
+
+        /* this kind of work with networks is not atomic operation */
+        if (!network) {
+            free(inactiveNames[i]);
+            continue;
+        }
+        virNetworkGetUUIDString(network, uuid);
+        vshPrint(ctl, "%s\n%s\n",
+                 virNetworkGetName(network),
+                 uuid);
+
+        virNetworkFree(network);
+        free(inactiveNames[i]);
+    }
+    free(activeNames);
+    free(inactiveNames);
+
+    return TRUE;
+}
+
+
+/*
+ * "_comppools" command
+ */
+static vshCmdInfo info_comppool[] = {
+    {"syntax", "_complete-pool"},
+    {"help", gettext_noop("print shell completion data for pools")},
+    {"desc", gettext_noop("Prints data useful for shell autocompletion of pools.")},
+
+    {NULL, NULL}
+};
+
+static vshCmdOptDef opts_comppool[] = {
+    {NULL, 0, 0, NULL}
+};
+
+static int
+cmdCompPool(vshControl * ctl, vshCmd * cmd ATTRIBUTE_UNUSED)
+{
+    char **activeNames = NULL, **inactiveNames = NULL;
+    int maxactive = 0, maxinactive = 0, i;
+
+    if (!vshConnectionUsability(ctl, ctl->conn, TRUE))
+        return FALSE;
+
+    maxactive = virConnectNumOfStoragePools(ctl->conn);
+    if (maxactive < 0) {
+        vshError(ctl, FALSE, "%s", _("Failed to list active pools"));
+        return FALSE;
+    }
+    if (maxactive) {
+        activeNames = vshMalloc(ctl, sizeof(char *) * maxactive);
+
+        if ((maxactive = virConnectListStoragePools(ctl->conn, activeNames,
+                                                maxactive)) < 0) {
+            vshError(ctl, FALSE, "%s", _("Failed to list active pools"));
+            free(activeNames);
+            return FALSE;
+        }
+
+        qsort(&activeNames[0], maxactive, sizeof(char *), namesorter);
+    }
+    maxinactive = virConnectNumOfDefinedStoragePools(ctl->conn);
+    if (maxinactive < 0) {
+        vshError(ctl, FALSE, "%s", _("Failed to list inactive pools"));
+        free(activeNames);
+        return FALSE;
+    }
+    if (maxinactive) {
+        inactiveNames = vshMalloc(ctl, sizeof(char *) * maxinactive);
+
+        if ((maxinactive = virConnectListDefinedStoragePools(ctl->conn, inactiveNames, maxinactive)) < 0) {
+            vshError(ctl, FALSE, "%s", _("Failed to list inactive pools"));
+            free(activeNames);
+            free(inactiveNames);
+            return FALSE;
+        }
+
+        qsort(&inactiveNames[0], maxinactive, sizeof(char*), namesorter);
+    }
+
+    for (i = 0; i < maxactive; i++) {
+        char uuid[VIR_UUID_STRING_BUFLEN];
+        virStoragePoolPtr pool = virStoragePoolLookupByName(ctl->conn, activeNames[i]);
+
+        /* this kind of work with pools is not atomic operation */
+        if (!pool) {
+            free(activeNames[i]);
+            continue;
+        }
+        virStoragePoolGetUUIDString(pool, uuid);
+        vshPrint(ctl, "%s\n%s\n",
+                 virStoragePoolGetName(pool),
+                 uuid);
+        virStoragePoolFree(pool);
+        free(activeNames[i]);
+    }
+    for (i = 0; i < maxinactive; i++) {
+        char uuid[VIR_UUID_STRING_BUFLEN];
+        virStoragePoolPtr pool = virStoragePoolLookupByName(ctl->conn, inactiveNames[i]);
+
+        /* this kind of work with pools is not atomic operation */
+        if (!pool) {
+            free(inactiveNames[i]);
+            continue;
+        }
+        virStoragePoolGetUUIDString(pool, uuid);
+        vshPrint(ctl, "%s\n%s\n",
+                 virStoragePoolGetName(pool),
+                 uuid);
+
+        virStoragePoolFree(pool);
+        free(inactiveNames[i]);
+    }
+    free(activeNames);
+    free(inactiveNames);
+
+    return TRUE;
+}
+
+
 /*
  * "help" command
  */
@@ -355,8 +758,9 @@
 
         vshPrint(ctl, "%s", _("Commands:\n\n"));
         for (def = commands; def->name; def++)
-            vshPrint(ctl, "    %-15s %s\n", def->name,
-                     N_(vshCmddefGetInfo(def, "help")));
+            if (def->name[0] != '_')
+                vshPrint(ctl, "    %-15s %s\n", def->name,
+                         N_(vshCmddefGetInfo(def, "help")));
         return TRUE;
     }
     return vshCmddefHelp(ctl, cmdname, FALSE);
@@ -426,7 +830,7 @@
 };
 
 static vshCmdOptDef opts_connect[] = {
-    {"name",     VSH_OT_DATA, 0, gettext_noop("hypervisor connection URI")},
+    {"uri",     VSH_OT_DATA, 0, gettext_noop("hypervisor connection URI")},
     {"readonly", VSH_OT_BOOL, 0, gettext_noop("read-only connection")},
     {NULL, 0, 0, NULL}
 };
@@ -446,7 +850,7 @@
     }
 
     free(ctl->name);
-    ctl->name = vshStrdup(ctl, vshCommandOptString(cmd, "name", NULL));
+    ctl->name = vshStrdup(ctl, vshCommandOptString(cmd, "uri", NULL));
 
     if (!ro) {
         ctl->conn = virConnectOpen(ctl->name);
@@ -1004,7 +1408,7 @@
 };
 
 static vshCmdOptDef opts_start[] = {
-    {"name", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("name of the inactive domain")},
+    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("name of the inactive domain")},
     {NULL, 0, 0, NULL}
 };
 
@@ -1017,7 +1421,7 @@
     if (!vshConnectionUsability(ctl, ctl->conn, TRUE))
         return FALSE;
 
-    if (!(dom = vshCommandOptDomainBy(ctl, cmd, "name", NULL, VSH_BYNAME)))
+    if (!(dom = vshCommandOptDomainBy(ctl, cmd, "domain", NULL, VSH_BYNAME)))
         return FALSE;
 
     if (virDomainGetID(dom) != (unsigned int)-1) {
@@ -2650,7 +3054,7 @@
 };
 
 static vshCmdOptDef opts_network_start[] = {
-    {"name", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("name of the inactive network")},
+    {"network", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("name of the inactive network")},
     {NULL, 0, 0, NULL}
 };
 
@@ -2663,7 +3067,7 @@
     if (!vshConnectionUsability(ctl, ctl->conn, TRUE))
         return FALSE;
 
-    if (!(network = vshCommandOptNetworkBy(ctl, cmd, "name", NULL, VSH_BYNAME)))
+    if (!(network = vshCommandOptNetworkBy(ctl, cmd, "network", NULL, VSH_BYNAME)))
          return FALSE;
 
     if (virNetworkCreate(network) == 0) {
@@ -3554,7 +3958,7 @@
 };
 
 static vshCmdOptDef opts_pool_start[] = {
-    {"name", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("name of the inactive pool")},
+    {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("name of the inactive pool")},
     {NULL, 0, 0, NULL}
 };
 
@@ -3567,7 +3971,7 @@
     if (!vshConnectionUsability(ctl, ctl->conn, TRUE))
         return FALSE;
 
-    if (!(pool = vshCommandOptPoolBy(ctl, cmd, "name", NULL, VSH_BYNAME)))
+    if (!(pool = vshCommandOptPoolBy(ctl, cmd, "pool", NULL, VSH_BYNAME)))
          return FALSE;
 
     if (virStoragePoolCreate(pool, 0) == 0) {
@@ -5074,6 +5478,12 @@
  * Commands
  */
 static vshCmdDef commands[] = {
+    {"_complete-command", cmdCompCommand, opts_compcommand, info_compcommand},
+    {"_complete-uri", cmdCompUri, opts_compuri, info_compuri},
+    {"_complete-domain", cmdCompDomain, opts_compdomain, info_compdomain},
+    {"_complete-network", cmdCompNetwork, opts_compnetwork, info_compnetwork},
+    {"_complete-pool", cmdCompPool, opts_comppool, info_comppool},
+
     {"help", cmdHelp, opts_help, info_help},
     {"attach-device", cmdAttachDevice, opts_attach_device, info_attach_device},
     {"attach-disk", cmdAttachDisk, opts_attach_disk, info_attach_disk},
@@ -6514,6 +6924,18 @@
         ret = vshCommandParse(ctl, cmdstr);
 
         free(cmdstr);
+
+        /* Special case 'help' to avoid virConnectOpen */
+        if (ctl->cmd &&
+            ctl->cmd->def &&
+            ctl->cmd->def->name &&
+            (STREQ(ctl->cmd->def->name, "help") ||
+             STREQ(ctl->cmd->def->name, "_complete-command") ||
+             STREQ(ctl->cmd->def->name, "_complete-uri"))) {
+            ret = vshCommandRun(ctl, ctl->cmd);
+            exit(ret ? EXIT_SUCCESS : EXIT_FAILURE);
+        }
+
         return ret;
     }
     return TRUE;


-- 
|: Red Hat, Engineering, London   -o-   http://people.redhat.com/berrange/ :|
|: http://libvirt.org  -o-  http://virt-manager.org  -o-  http://ovirt.org :|
|: http://autobuild.org       -o-         http://search.cpan.org/~danberr/ :|
|: GnuPG: 7D3B9505  -o-  F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|




More information about the libvir-list mailing list