[libvirt] [PATCH v2] virsh: Add domdisplay cmd for VNC, SPICE and RDP

Doug Goldstein cardoe at cardoe.com
Sun Jun 24 21:14:54 UTC 2012


v2:
- Refactored to use virBuffer
- Refactored to use virXPath wrappers
- Added support for tls-port and password for SPICE
- Added optional flag to disable SPICE password to the URI
- Added support for RDP
- Fixed code reviews

Add a new 'domdisplay' command that provides a URI for VNC, SPICE and
RDP connections. Presently the 'vncdisplay' command provides you with
the port info that QEMU is listening on but there is no counterpart for
SPICE and RDP. Additionally this provides you with the bind address as
specified in the XML, which the existing 'vncdisplay' lacks. For SPICE
connections it supports secure and unsecure channels and optionally
providing the password for the SPICE channel.

Signed-off-by: Doug Goldstein <cardoe at cardoe.com>
---
 tools/virsh.c   |  173 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 tools/virsh.pod |    6 ++
 2 files changed, 179 insertions(+), 0 deletions(-)

diff --git a/tools/virsh.c b/tools/virsh.c
index 0354822..da80477 100644
--- a/tools/virsh.c
+++ b/tools/virsh.c
@@ -13827,6 +13827,178 @@ cmdSysinfo (vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
 }
 
 /*
+ * "display" command
+ */
+static const vshCmdInfo info_domdisplay[] = {
+    {"help", N_("domain display connection URI")},
+    {"desc", N_("Output the IP address and port number for the graphical display.")},
+    {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_domdisplay[] = {
+    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+    {"include-password", VSH_OT_BOOL, VSH_OFLAG_NONE, N_("includes the password into the connection URI if available")},
+    {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdDomDisplay(vshControl *ctl, const vshCmd *cmd)
+{
+    xmlDocPtr xml = NULL;
+    xmlXPathContextPtr ctxt = NULL;
+    virDomainPtr dom;
+    virBuffer buf = VIR_BUFFER_INITIALIZER;
+    bool ret = false;
+    char *doc;
+    char *xpath;
+    char *listen_addr;
+    int port, tls_port = 0;
+    char *passwd = NULL;
+    const char *scheme[] = { "vnc", "spice", "rdp", NULL };
+    int iter = 0;
+    int tmp;
+
+    if (!vshConnectionUsability(ctl, ctl->conn))
+        return false;
+
+    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+        return false;
+
+    if (!virDomainIsActive(dom)) {
+        vshError(ctl, _("Domain is not running"));
+        goto cleanup;
+    }
+
+    doc = virDomainGetXMLDesc(dom, 0);
+    if (!doc)
+        goto cleanup;
+
+    xml = virXMLParseStringCtxt(doc, _("(domain_definition)"), &ctxt);
+    VIR_FREE(doc);
+    if (!xml)
+        goto cleanup;
+
+    /* Attempt to grab our display info */
+    for (iter = 0; scheme[iter] != NULL; iter++) {
+        /* Create our XPATH lookup for the current display's port */
+        virAsprintf(&xpath, "string(/domain/devices/graphics[@type='%s']"
+                "/@port)", scheme[iter]);
+        if (!xpath) {
+            virReportOOMError();
+            goto cleanup;
+        }
+
+        /* Attempt to get the port number for the current graphics scheme */
+        tmp = virXPathInt(xpath, ctxt, &port);
+        VIR_FREE(xpath);
+
+        /* If there is no port number for this type, then jump to the next
+         * scheme
+         */
+        if (tmp)
+            continue;
+
+        /* Create our XPATH lookup for the current display's address */
+        virAsprintf(&xpath, "string(/domain/devices/graphics[@type='%s']"
+                "/@listen)", scheme[iter]);
+        if (!xpath) {
+            virReportOOMError();
+            goto cleanup;
+        }
+
+        /* Attempt to get the listening addr if set for the current
+         * graphics scheme
+         */
+        listen_addr = virXPathString(xpath, ctxt);
+        VIR_FREE(xpath);
+    
+        /* Per scheme data mangling */
+        if (STREQ(scheme[iter], "vnc")) {
+            /* VNC protocol handlers take their port number as 'port' - 5900 */
+            port -= 5900;
+        } else if (STREQ(scheme[iter], "spice")) {
+            /* Create our XPATH lookup for the SPICE TLS Port */
+            virAsprintf(&xpath, "string(/domain/devices/graphics[@type='%s']"
+                    "/@tlsPort)", scheme[iter]);
+            if (!xpath) {
+                virReportOOMError();
+                goto cleanup;
+            }
+
+            /* Attempt to get the TLS port number for SPICE */
+            tmp = virXPathInt(xpath, ctxt, &tls_port);
+            VIR_FREE(xpath);
+            if (tmp)
+                tls_port = 0;
+
+            if (vshCommandOptBool(cmd, "daemon")) {
+                /* Create our XPATH lookup for the SPICE password */
+                virAsprintf(&xpath, "string(/domain/devices/graphics"
+                        "[@type='%s']/@passwd)", scheme[iter]);
+                if (!xpath) {
+                    virReportOOMError();
+                    goto cleanup;
+                }
+
+                /* Attempt to get the SPICE password */
+                passwd = virXPathString(xpath, ctxt);
+                VIR_FREE(xpath);
+            }
+        }
+
+        /* Build up the full URI, starting with the scheme */
+        virBufferAsprintf(&buf, "%s://", scheme[iter]);
+
+        /* Then host name or IP */
+        if (!listen_addr || STREQ((const char *)listen_addr, "0.0.0.0"))
+            virBufferAddLit(&buf, "localhost");
+        else
+            virBufferAsprintf(&buf, "%s", listen_addr);
+
+        /* Clean up our memory */
+        if (listen_addr)
+            VIR_FREE(listen_addr);
+
+        /* Add the port */
+        if (STREQ(scheme[iter], "spice"))
+            virBufferAsprintf(&buf, "?port=%d", port);
+        else
+            virBufferAsprintf(&buf, ":%d", port);
+
+        /* TLS Port */
+        if (tls_port)
+            virBufferAsprintf(&buf, "&tls-port=%d", tls_port);
+
+        /* Password */
+        if (passwd) {
+            virBufferAsprintf(&buf, "&password=%s", passwd);
+            VIR_FREE(passwd);
+        }
+
+        /* Ensure we can print our URI */
+        if (virBufferError(&buf)) {
+            vshPrint(ctl, "%s", _("Failed to create display URI"));
+            goto cleanup;
+        }
+
+        /* Print out our full URI */
+        output = virBufferContentAndReset(&buf);
+        vshPrint(ctl, "%s", output);
+        VIR_FREE(output);
+
+        /* We got what we came for so return successfully */
+        ret = true;
+        break;
+    }
+
+cleanup:
+    xmlXPathFreeContext(ctxt);
+    xmlFreeDoc(xml);
+    virDomainFree(dom);
+    return ret;
+}
+
+/*
  * "vncdisplay" command
  */
 static const vshCmdInfo info_vncdisplay[] = {
@@ -17974,6 +18146,7 @@ static const vshCmdDef domManagementCmds[] = {
     {"detach-disk", cmdDetachDisk, opts_detach_disk, info_detach_disk, 0},
     {"detach-interface", cmdDetachInterface, opts_detach_interface,
      info_detach_interface, 0},
+    {"domdisplay", cmdDomDisplay, opts_domdisplay, info_domdisplay, 0},
     {"domid", cmdDomid, opts_domid, info_domid, 0},
     {"domif-setlink", cmdDomIfSetLink, opts_domif_setlink, info_domif_setlink, 0},
     {"domiftune", cmdDomIftune, opts_domiftune, info_domiftune, 0},
diff --git a/tools/virsh.pod b/tools/virsh.pod
index f83a29d..558105f 100644
--- a/tools/virsh.pod
+++ b/tools/virsh.pod
@@ -820,6 +820,12 @@ I<size> is a scaled integer (see B<NOTES> above) which defaults to KiB
 "B" to get bytes (note that for historical reasons, this differs from
 B<vol-resize> which defaults to bytes without a suffix).
 
+=item B<domdisplay> I<domain-id> [I<--include-password>]
+
+Output a URI which can be used to connect to the graphical display of the
+domain via VNC, SPICE or RDP. If I<--include-password> is specified, the
+SPICE channel password will be included in the URI.
+
 =item B<dominfo> I<domain-id>
 
 Returns basic information about the domain.
-- 
1.7.3.4




More information about the libvir-list mailing list