From: "Daniel P. Berrange" <berrange(a)redhat.com>
I finally got fed up of typing URIs when using virsh....
This adds support for a libvirt client configuration file
either /etc/libvirt/libvirt.conf for privileged clients,
or $HOME/.libvirt/libvirt.conf for unprivileged clients.
It allows one parameter
uri_aliases = [
"hail=qemu+ssh://root@hail.cloud.example.com/system",
"sleet=qemu+ssh://root@sleet.cloud.example.com/system",
]
Any call to virConnectOpen with a non-NULL URI will first
attempt to match against the uri_aliases list. An application
can disable this by using VIR_CONNECT_NO_ALIASES
* docs/uri.html.in: Document URI aliases
* include/libvirt/libvirt.h.in: Add VIR_CONNECT_NO_ALIASES
* libvirt.spec.in, mingw32-libvirt.spec.in: Add /etc/libvirt/libvirt.conf
* src/Makefile.am: Install default config file
* src/libvirt.c: Add support for URI aliases
* src/remote/remote_driver.c: Don't try to handle URIs
with no scheme and which clearly are not paths
* src/util/conf.c: Don't raise error on virConfFree(NULL)
* src/xen/xen_driver.c: Don't raise error on URIs
with no scheme
---
docs/uri.html.in | 101 ++++++++++++++-------------
include/libvirt/libvirt.h.in | 3 +-
libvirt.spec.in | 1 +
mingw32-libvirt.spec.in | 2 +
src/Makefile.am | 2 +-
src/libvirt.c | 157 ++++++++++++++++++++++++++++++++++++++----
src/libvirt.conf | 13 ++++
src/remote/remote_driver.c | 5 ++
src/util/conf.c | 6 +-
src/xen/xen_driver.c | 12 +---
10 files changed, 223 insertions(+), 79 deletions(-)
create mode 100644 src/libvirt.conf
diff --git a/docs/uri.html.in b/docs/uri.html.in
index e6326b2..5cc4dbf 100644
--- a/docs/uri.html.in
+++ b/docs/uri.html.in
@@ -2,6 +2,8 @@
<html>
<body>
<h1 >Connection URIs</h1>
+
+ <ul id="toc"></ul>
<p>
Since libvirt supports many different kinds of virtualization
(often referred to as "drivers" or "hypervisors"), we need a
@@ -13,41 +15,44 @@ machine over the network.
To this end, libvirt uses URIs as used on the Web and as defined in <a
href="http://www.ietf.org/rfc/rfc2396.txt">RFC 2396</a>. This page
documents libvirt URIs.
</p>
- <ul>
- <li>
- <a href="#URI_libvirt">Specifying URIs to libvirt</a>
- </li>
- <li>
- <a href="#URI_virsh">Specifying URIs to virsh, virt-manager and
virt-install</a>
- </li>
- <li>
- <a href="#URI_xen">xen:/// URI</a>
- </li>
- <li>
- <a href="#URI_qemu">qemu:///... QEMU and KVM URIs</a>
- </li>
- <li>
- <a href="#URI_remote">Remote URIs</a>
- </li>
- <li>
- <a href="#URI_test">test:///... Test URIs</a>
- </li>
- <li>
- <a href="#URI_legacy">Other & legacy URI
formats</a>
- </li>
- </ul>
- <h3>
- <a name="URI_libvirt">Specifying URIs to libvirt</a>
- </h3>
+ <h2><a name="URI_libvirt">Specifying URIs to
libvirt</a></h2>
+
<p>
The URI is passed as the <code>name</code> parameter to <a
href="html/libvirt-libvirt.html#virConnectOpen"><code>virConnectOpen</code></a>
or <a
href="html/libvirt-libvirt.html#virConnectOpenReadOnly"><code>virConnectOpenReadOnly</code></a>.
For example:
</p>
<pre>
virConnectPtr conn = virConnectOpenReadOnly
(<b>"test:///default"</b>);
</pre>
- <h3>
+ <h2>
+ <a name="URI_config">Configuring URI aliases</a>
+ </h2>
+
+ <p>
+To simplify live for administrators, it is possible to setup URI aliases in a
+libvirt client configuration file. The configuration file is
<code>/etc/libvirt/libvirt.conf</code>
+for the root user, or <code>$HOME/.libvirt/libvirt.conf</code> for any
unprivileged user.
+In this file, the following syntax can be used to setup aliases
+ </p>
+
+<pre>
+uri_aliases = [
+ "hail=qemu+ssh://root@hail.cloud.example.com/system",
+ "sleet=qemu+ssh://root@sleet.cloud.example.com/system",
+]
+</pre>
+
+<p>
+ A URI alias should be a string made up from the characters
+ <code>a-Z, 0-9, _, -</code>. Following the <code>=</code>
+ can be any libvirt URI string, including arbitrary URI parameters.
+ URI aliases will apply to any application opening a libvirt
+ connection, unless it has explicitly passed the
<code>VIR_CONNECT_NO_ALIASES</code>
+ parameter to <code>virConnectOpenAuth</code>.
+</p>
+
+ <h2>
<a name="URI_virsh">Specifying URIs to virsh, virt-manager and
virt-install</a>
- </h3>
+ </h2>
<p>
In virsh use the <code>-c</code> or <code>--connect</code>
option:
</p>
@@ -76,9 +81,9 @@ In virt-install use the
<code>--connect=</code><i>URI</i> option:
<pre>
virt-install <b>--connect=test:///default</b> <i>[other
options]</i>
</pre>
- <h3>
+ <h2>
<a name="URI_xen">xen:/// URI</a>
- </h3>
+ </h2>
<p>
<i>This section describes a feature which is new in libvirt >
0.2.3. For libvirt ≤ 0.2.3 use <a
href="#URI_legacy_xen"><code>"xen"</code></a>.</i>
@@ -87,9 +92,9 @@ virt-install <b>--connect=test:///default</b>
<i>[other options]</i>
To access a Xen hypervisor running on the local machine
use the URI <code>xen:///</code>.
</p>
- <h3>
+ <h2>
<a name="URI_qemu">qemu:///... QEMU and KVM URIs</a>
- </h3>
+ </h2>
<p>
To use QEMU support in libvirt you must be running the
<code>libvirtd</code> daemon (named <code>libvirt_qemud</code>
@@ -119,9 +124,9 @@ KVM URIs are identical. You select between qemu, qemu accelerated
and
KVM guests in the <a href="format.html#KVM1">guest XML as described
here</a>.
</p>
- <h3>
+ <h2>
<a name="URI_remote">Remote URIs</a>
- </h3>
+ </h2>
<p>
Remote URIs are formed by taking ordinary local URIs and adding a
hostname and/or transport name. As a special case, using a URI
@@ -182,9 +187,9 @@ We refer you to <a
href="remote.html#Remote_URI_reference">the libvirt
remote URI reference</a> and <a href="remote.html">full
documentation
for libvirt remote support</a>.
</p>
- <h3>
+ <h2>
<a name="URI_test">test:///... Test URIs</a>
- </h3>
+ </h2>
<p>
The test driver is a dummy hypervisor for test purposes.
The URIs supported are:
@@ -196,12 +201,12 @@ host definitions built into the driver. </li>
a set of host definitions held in the named file.
</li>
</ul>
- <h3>
+ <h2>
<a name="URI_legacy">Other & legacy URI formats</a>
- </h3>
- <h4>
+ </h2>
+ <h3>
<a name="URI_NULL">NULL and empty string URIs</a>
- </h4>
+ </h3>
<p>
Libvirt allows you to pass a <code>NULL</code> pointer to
<code>virConnectOpen*</code>. Empty string
(<code>""</code>) acts in
@@ -223,9 +228,9 @@ the user to type a URI in directly (if that is appropriate). If your
application wishes to connect specifically to a Xen hypervisor, then
for future proofing it should choose a full <a
href="#URI_xen"><code>xen:///</code> URI</a>.
</p>
- <h4>
+ <h3>
<a name="URI_file">File paths (xend-unix-server)</a>
- </h4>
+ </h3>
<p>
If XenD is running and configured in <code>/etc/xen/xend-config.sxp</code>:
</p>
@@ -240,9 +245,9 @@ using a file URI such as:
<pre>
virsh -c ///var/run/xend/xend-socket
</pre>
- <h4>
+ <h3>
<a name="URI_http">Legacy: <code>http://...</code>
(xend-http-server)</a>
- </h4>
+ </h3>
<p>
If XenD is running and configured in <code>/etc/xen/xend-config.sxp</code>:
@@ -276,17 +281,17 @@ Notes:
libvirt, only the old-style sexpr interface known in the Xen
documentation as "unix server" or "http server".</li>
</ol>
- <h4>
+ <h3>
<a name="URI_legacy_xen">Legacy:
<code>"xen"</code></a>
- </h4>
+ </h3>
<p>
Another legacy URI is to specify name as the string
<code>"xen"</code>. This will continue to refer to the Xen
hypervisor. However you should prefer a full <a
href="#URI_xen"><code>xen:///</code> URI</a> in all future
code.
</p>
- <h4>
+ <h3>
<a name="URI_legacy_proxy">Legacy: Xen proxy</a>
- </h4>
+ </h3>
<p>
Libvirt continues to support connections to a separately running Xen
proxy daemon. This provides a way to allow non-root users to make a
diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in
index c991dfc..6bb27c7 100644
--- a/include/libvirt/libvirt.h.in
+++ b/include/libvirt/libvirt.h.in
@@ -843,7 +843,8 @@ typedef virNodeMemoryStats *virNodeMemoryStatsPtr;
* Flags when opening a connection to a hypervisor
*/
typedef enum {
- VIR_CONNECT_RO = 1, /* A readonly connection */
+ VIR_CONNECT_RO = (1 << 0), /* A readonly connection */
+ VIR_CONNECT_NO_ALIASES = (1 << 1), /* Don't try to resolve URI aliases */
} virConnectFlags;
diff --git a/libvirt.spec.in b/libvirt.spec.in
index 7c63710..d6f4564 100644
--- a/libvirt.spec.in
+++ b/libvirt.spec.in
@@ -1085,6 +1085,7 @@ rm -f $RPM_BUILD_ROOT%{_sysconfdir}/sysctl.d/libvirtd
%defattr(-, root, root)
%doc AUTHORS ChangeLog.gz NEWS README COPYING.LIB TODO
+%config(noreplace) %{_sysconfdir}/libvirt/libvirt.conf
%{_mandir}/man1/virsh.1*
%{_mandir}/man1/virt-xml-validate.1*
%{_mandir}/man1/virt-pki-validate.1*
diff --git a/mingw32-libvirt.spec.in b/mingw32-libvirt.spec.in
index f651d11..f5e0d05 100644
--- a/mingw32-libvirt.spec.in
+++ b/mingw32-libvirt.spec.in
@@ -83,6 +83,8 @@ rm -rf $RPM_BUILD_ROOT
%files
%defattr(-,root,root)
+%config(noreplace) %{_mingw32_sysconfdir}/libvirt/libvirt.conf
+
%{_mingw32_bindir}/libvirt-0.dll
%{_mingw32_bindir}/virsh.exe
%{_mingw32_bindir}/virt-xml-validate
diff --git a/src/Makefile.am b/src/Makefile.am
index 302d395..fccc54d 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -39,7 +39,7 @@ moddir = $(libdir)/libvirt/connection-driver
mod_LTLIBRARIES =
confdir = $(sysconfdir)/libvirt
-conf_DATA =
+conf_DATA = libvirt.conf
augeasdir = $(datadir)/augeas/lenses
augeas_DATA =
diff --git a/src/libvirt.c b/src/libvirt.c
index f07c720..c285e31 100644
--- a/src/libvirt.c
+++ b/src/libvirt.c
@@ -40,6 +40,7 @@
#include "memory.h"
#include "configmake.h"
#include "intprops.h"
+#include "conf.h"
#include "rpc/virnettlscontext.h"
#ifndef WITH_DRIVER_MODULES
@@ -968,6 +969,125 @@ error:
return -1;
}
+static char *
+virConnectConfigFile(void)
+{
+ char *path;
+ if (geteuid() == 0) {
+ if (virAsprintf(&path, "%s/libvirt/libvirt.conf",
+ SYSCONFDIR) < 0)
+ goto no_memory;
+ } else {
+ char *userdir = virGetUserDirectory(geteuid());
+ if (!userdir)
+ goto error;
+
+ if (virAsprintf(&path, "%s/.libvirt/libvirt.conf",
+ userdir) < 0)
+ goto no_memory;
+ }
+
+ return path;
+
+no_memory:
+ virReportOOMError();
+error:
+ return NULL;
+}
+
+static int
+virConnectOpenFindURIAliasMatch(virConfValuePtr value, const char *alias, char **uri)
+{
+ virConfValuePtr entry;
+ if (value->type != VIR_CONF_LIST) {
+ virLibConnError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("Expected a list for 'uri_aliases' config
parameter"));
+ return -1;
+ }
+
+ entry = value->list;
+ while (entry) {
+ char *offset;
+ size_t safe;
+
+ if (entry->type != VIR_CONF_STRING) {
+ virLibConnError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("Expected a string for 'uri_aliases' config
parameter list entry"));
+ return -1;
+ }
+
+ if (!(offset = strchr(entry->str, '='))) {
+ virLibConnError(VIR_ERR_INTERNAL_ERROR,
+ _("Malformed 'uri_aliases' config entry
'%s', expected 'alias=uri://host/path'"),
+ entry->str);
+ return -1;
+ }
+
+ safe = strspn(entry->str,
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-");
+ if (safe < (offset - entry->str)) {
+ virLibConnError(VIR_ERR_INTERNAL_ERROR,
+ _("Malformed 'uri_aliases' config entry
'%s', aliases may only container 'a-Z, 0-9, _, -'"),
+ entry->str);
+ return -1;
+ }
+
+ if (STREQLEN(entry->str, alias, offset-entry->str)) {
+ VIR_DEBUG("Resolved alias '%s' to '%s'",
+ alias, offset+1);
+ if (!(*uri = strdup(offset+1))) {
+ virReportOOMError();
+ return -1;
+ }
+ return 0;
+ }
+
+ entry = entry->next;
+ }
+
+ VIR_DEBUG("No alias found for '%s', passing through to drivers",
+ alias);
+ return 0;
+}
+
+static int
+virConnectOpenResolveURIAlias(const char *alias, char **uri)
+{
+ char *config = NULL;
+ int ret = -1;
+ virConfPtr conf = NULL;
+ virConfValuePtr value = NULL;
+
+ *uri = NULL;
+
+ /* Short circuit to avoid doing URI alias resolution
+ * when it clearly isn't an alias */
+ if (strchr(alias, '/') ||
+ strchr(alias, ':'))
+ return 0;
+
+ if (!(config = virConnectConfigFile()))
+ goto cleanup;
+
+ if (!virFileExists(config)) {
+ ret = 0;
+ goto cleanup;
+ }
+
+ VIR_DEBUG("Loading config file '%s'", config);
+ if (!(conf = virConfReadFile(config, 0)))
+ goto cleanup;
+
+ if ((value = virConfGetValue(conf, "uri_aliases")))
+ ret = virConnectOpenFindURIAliasMatch(value, alias, uri);
+ else
+ ret = 0;
+
+cleanup:
+ virConfFree(conf);
+ VIR_FREE(config);
+ return ret;
+}
+
static virConnectPtr
do_open (const char *name,
virConnectAuthPtr auth,
@@ -998,6 +1118,7 @@ do_open (const char *name,
}
if (name) {
+ char *alias = NULL;
/* Convert xen -> xen:/// for back compat */
if (STRCASEEQ(name, "xen"))
name = "xen:///";
@@ -1008,26 +1129,34 @@ do_open (const char *name,
if (STREQ (name, "xen://"))
name = "xen:///";
- ret->uri = xmlParseURI (name);
+ if (!(flags & VIR_CONNECT_NO_ALIASES) &&
+ virConnectOpenResolveURIAlias(name, &alias) < 0)
+ goto failed;
+
+ ret->uri = xmlParseURI (alias ? alias : name);
if (!ret->uri) {
virLibConnError(VIR_ERR_INVALID_ARG,
- _("could not parse connection URI"));
+ _("could not parse connection URI %s"),
+ alias ? alias : name);
+ VIR_FREE(alias);
goto failed;
}
VIR_DEBUG("name \"%s\" to URI components:\n"
- " scheme %s\n"
- " opaque %s\n"
- " authority %s\n"
- " server %s\n"
- " user %s\n"
- " port %d\n"
- " path %s\n",
- name,
- NULLSTR(ret->uri->scheme), NULLSTR(ret->uri->opaque),
- NULLSTR(ret->uri->authority), NULLSTR(ret->uri->server),
- NULLSTR(ret->uri->user), ret->uri->port,
- NULLSTR(ret->uri->path));
+ " scheme %s\n"
+ " opaque %s\n"
+ " authority %s\n"
+ " server %s\n"
+ " user %s\n"
+ " port %d\n"
+ " path %s\n",
+ alias ? alias : name,
+ NULLSTR(ret->uri->scheme), NULLSTR(ret->uri->opaque),
+ NULLSTR(ret->uri->authority), NULLSTR(ret->uri->server),
+ NULLSTR(ret->uri->user), ret->uri->port,
+ NULLSTR(ret->uri->path));
+
+ VIR_FREE(alias);
} else {
VIR_DEBUG("no name, allowing driver auto-select");
}
diff --git a/src/libvirt.conf b/src/libvirt.conf
new file mode 100644
index 0000000..ffe8c21
--- /dev/null
+++ b/src/libvirt.conf
@@ -0,0 +1,13 @@
+
+#
+# This can be used to setup URI aliases for frequently
+# used connection URIs. Aliases may contain only the
+# characters a-Z, 0-9, _, -.
+#
+# Following the '=' may be any valid libvirt connection
+# URI, including arbitrary parameters
+
+#uri_aliases = [
+# "hail=qemu+ssh://root@hail.cloud.example.com/system",
+# "sleet=qemu+ssh://root@sleet.cloud.example.com/system",
+#]
diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c
index 4dc6974..1dea327 100644
--- a/src/remote/remote_driver.c
+++ b/src/remote/remote_driver.c
@@ -313,6 +313,11 @@ doRemoteOpen (virConnectPtr conn,
if (conn->uri) {
if (!conn->uri->scheme) {
/* This is the ///var/lib/xen/xend-socket local path style */
+ if (!conn->uri->path)
+ return VIR_DRV_OPEN_DECLINED;
+ if (conn->uri->path[0] != '/')
+ return VIR_DRV_OPEN_DECLINED;
+
transport = trans_unix;
} else {
transport_str = get_transport_from_scheme (conn->uri->scheme);
diff --git a/src/util/conf.c b/src/util/conf.c
index 00045b5..c8dcc7f 100644
--- a/src/util/conf.c
+++ b/src/util/conf.c
@@ -808,10 +808,8 @@ int
virConfFree(virConfPtr conf)
{
virConfEntryPtr tmp;
- if (conf == NULL) {
- virConfError(NULL, VIR_ERR_INVALID_ARG, __FUNCTION__);
- return(-1);
- }
+ if (conf == NULL)
+ return 0;
tmp = conf->entries;
while (tmp) {
diff --git a/src/xen/xen_driver.c b/src/xen/xen_driver.c
index 9c96fca..0a2267d 100644
--- a/src/xen/xen_driver.c
+++ b/src/xen/xen_driver.c
@@ -296,17 +296,7 @@ xenUnifiedOpen (virConnectPtr conn, virConnectAuthPtr auth, unsigned
int flags)
conn->uri->server)
return VIR_DRV_OPEN_DECLINED;
} else {
- /* Special case URI for Xen driver only:
- *
- * Treat a plain path as a Xen UNIX socket path, and give
- * error unless path is absolute
- */
- if (!conn->uri->path || conn->uri->path[0] != '/') {
- xenUnifiedError(VIR_ERR_INTERNAL_ERROR,
- _("unexpected Xen URI path '%s', try
///var/lib/xen/xend-socket"),
- NULLSTR(conn->uri->path));
- return VIR_DRV_OPEN_ERROR;
- }
+ return VIR_DRV_OPEN_DECLINED;
}
}
--
1.7.6.4