[libvirt] Certificate pinning
by Anastasiya Ruzhanskaya
Hello!
Does libvirt uses certificate pinning in tls? I want to setup a transparent
proxy (mitmproxy) and can't do this even after I added mitmproxy ca
certificate to the trusted certificates in ubuntu.
5 years, 11 months
[libvirt] libxl driver does not notice domain killed with xl destroy
by Marek Marczykowski-Górecki
Hi,
If one kills a domain with `xl destroy`, libvirt will not notice it and
still report the domain as running. Also trying to destroy the domain
through libvirt will fail. The only way to recover from such a situation
is to restart libvirt daemon.
While (I think) the right answer is "don't use xl destroy", libvirt
could do better.
libxl actually report such cases via LIBXL_EVENT_TYPE_DOMAIN_DEATH, but
it is ignored by libvirt's libxl driver. The problem is that
LIBXL_EVENT_TYPE_DOMAIN_DEATH is also reported when it's triggered by
libvirt (for example libxlDomainShutdownHandleDestroy, or
libxlDomainDestroyFlags).
For a proper solution I need a race-free way for checking if given
LIBXL_EVENT_TYPE_DOMAIN_DEATH was caused by libvirt (and is already
handled - including setting appropriate domain object state etc).
A bit of context:
https://github.com/libvirt/libvirt/blob/master/src/libxl/libxl_domain.c#L...
libxlDomainDestroyInternal(libxlDriverPrivatePtr driver,
virDomainObjPtr vm)
{
libxlDriverConfigPtr cfg = libxlDriverConfigGet(driver);
int ret = -1;
/* Unlock virDomainObj during destroy, which can take considerable
* time on large memory domains.
*/
virObjectUnlock(vm);
ret = libxl_domain_destroy(cfg->ctx, vm->def->id, NULL);
virObjectLock(vm);
virObjectUnref(cfg);
return ret;
}
Example usage:
https://github.com/libvirt/libvirt/blob/master/src/libxl/libxl_driver.c#L...
static int
libxlDomainDestroyFlags(virDomainPtr dom,
unsigned int flags)
{
(...)
if (libxlDomainObjBeginJob(driver, vm, LIBXL_JOB_MODIFY) < 0)
goto cleanup;
if (virDomainObjCheckActive(vm) < 0)
goto endjob;
if (libxlDomainDestroyInternal(driver, vm) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Failed to destroy domain '%d'"), vm->def->id);
goto endjob;
}
virDomainObjSetState(vm, VIR_DOMAIN_SHUTOFF,
VIR_DOMAIN_SHUTOFF_DESTROYED);
event = virDomainEventLifecycleNewFromObj(vm, VIR_DOMAIN_EVENT_STOPPED,
VIR_DOMAIN_EVENT_STOPPED_DESTROYED);
(...)
}
It's tricky, because domain object lock is released for the
libxl_domain_destroy() call time and I think there is no guarantee that
the lock will be taken back by libxlDomainDestroyInternal() before
LIBXL_EVENT_TYPE_DOMAIN_DEATH is reported (and possibly handled). Which means
I can't use domain object state for checking if libvirt already know
about this domain death, because it is set to VIR_DOMAIN_SHUTOFF, after
libxlDomainDestroyInternal() call. And while setting domain
object state twice (once from LIBXL_EVENT_TYPE_DOMAIN_DEATH handler,
then from libxlDomainDestroyInternal() caller) wouldn't be that bad
(only ugly), but emitting domain lifecycle event twice would be a major
problem.
Any ideas? Some internal flag set by libxlDomainDestroyInternal() just
before libxl_domain_destroy() call? Where would be a place for such
thing?
--
Best Regards,
Marek Marczykowski-Górecki
Invisible Things Lab
A: Because it messes up the order in which people normally read text.
Q: Why is top-posting such a bad thing?
5 years, 11 months
[libvirt] [PATCH 0/2] Introduce chains per network
by Daniel P. Berrangé
The previous patch series created separate global libvirt chains for
virtual network rules
This goes further and creates chains per virtual network. The idea is
that when stopping networks, we can just delet the chains, instead of
every individual rule.
Unfortunately creating/deleting/flushing chains appears surprisingly
expensive.
With 100 networks running, this series slows down libvirtd restart
from 13 seconds to 30 seconds :-(
Thus I'm not proposing to continue with this idea unless there's a
more compelling reason to do it.
Daniel P. Berrangé (2):
util: add support for creating per-network chains
util: move firewall rules into per network chains
src/libvirt_private.syms | 3 +-
src/network/bridge_driver_linux.c | 28 ++-
src/util/viriptables.c | 201 +++++++++++++++---
src/util/viriptables.h | 8 +-
.../nat-default-linux.args | 128 +++++++++--
.../nat-ipv6-linux.args | 144 +++++++++++--
.../nat-many-ips-linux.args | 156 +++++++++++---
.../nat-no-dhcp-linux.args | 142 +++++++++++--
.../nat-tftp-linux.args | 130 +++++++++--
.../route-default-linux.args | 118 +++++++++-
10 files changed, 901 insertions(+), 157 deletions(-)
--
2.19.2
5 years, 11 months
[libvirt] [PATCH v4] openvswitch: Add new port VLAN mode "dot1q-tunnel"(802.1ad double-tagged)
by luzhipeng@uniudc.com
From: ZhiPeng Lu <luzhipeng(a)uniudc.com>
This patch adds functionality to allow libvirt to configure the 'dot1q-tunnel'
modes(802.1ad double-tagged) on openvswitch networks.
For example:
<interface type='bridge'>
<mac address='2c:da:41:1d:05:42'/>
<source bridge='ovs0'/>
<vlan>
<tag id='41' nativeMode='dot1q-tunnel'/>
</vlan>
<virtualport type='openvswitch'>
<parameters interfaceid='6401a152-0b99-40b5-92be-858810aa6d37'/>
</virtualport>
<model type='virtio'/>
<driver name='vhost'/>
<alias name='net0'/>
</interface>
Signed-off-by: ZhiPeng Lu <luzhipeng(a)uniudc.com>
---
v1->v2:
1. Fix "make syntax-check" failure
v2->v3:
1. remove other_config when updating vlan
v3->v4:
1. add commit message that has a brief description of the new
feature
2. add tests for 'dot1q-tunnel' vlan mode
docs/formatdomain.html.in | 20 ++++++++++++++------
docs/formatnetwork.html.in | 20 +++++++++++---------
docs/schemas/networkcommon.rng | 1 +
src/conf/netdev_vlan_conf.c | 2 +-
src/util/virnetdevopenvswitch.c | 7 +++++++
src/util/virnetdevvlan.h | 1 +
tests/networkxml2xmlin/openvswitch-net.xml | 9 +++++++++
tests/networkxml2xmlout/openvswitch-net.xml | 9 +++++++++
.../openvswitch-net-modified.xml | 9 +++++++++
.../openvswitch-net-more-portgroups.xml | 9 +++++++++
.../openvswitch-net-without-alice.xml | 9 +++++++++
11 files changed, 80 insertions(+), 16 deletions(-)
diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in
index 84259c4..ced21c0 100644
--- a/docs/formatdomain.html.in
+++ b/docs/formatdomain.html.in
@@ -6085,8 +6085,14 @@ qemu-kvm -net nic,model=? /dev/null
<b><tag id='42'/></b>
<b><tag id='123' nativeMode='untagged'/></b>
<b></vlan></b>
- ...
</interface>
+ <interface type='bridge'>
+ <b><vlan trunk='yes'></b>
+ <b><tag id='42'/></b>
+ <b><tag id='123' nativeMode='dot1q-tunnel'/></b>
+ <b></vlan></b>
+ </interface>
+...
</devices>
...</pre>
@@ -6122,11 +6128,13 @@ qemu-kvm -net nic,model=? /dev/null
</p>
<p>
For network connections using Open vSwitch it is also possible
- to configure 'native-tagged' and 'native-untagged' VLAN modes
- <span class="since">Since 1.1.0.</span> This is done with the
- optional <code>nativeMode</code> attribute on
- the <code><tag></code> subelement: <code>nativeMode</code>
- may be set to 'tagged' or 'untagged'. The <code>id</code>
+ to configure 'native-tagged' and 'native-untagged'
+ <span class="since">Since 1.1.0.</span> and 'dot1q-tunnel'
+ (802.1ad double-tagged) <span class="since">Since 5.0.0.</span>
+ VLAN modes This is done with the optional <code>nativeMode</code>
+ attribute on the <code><tag></code> subelement:
+ <code>nativeMode</code> may be set to 'tagged' or 'untagged' or
+ 'dot1q-tunnel'. The <code>id</code>
attribute of the <code><tag></code> subelement
containing <code>nativeMode</code> sets which VLAN is considered
to be the "native" VLAN for this interface, and
diff --git a/docs/formatnetwork.html.in b/docs/formatnetwork.html.in
index 363a72b..d9ac9f7 100644
--- a/docs/formatnetwork.html.in
+++ b/docs/formatnetwork.html.in
@@ -688,16 +688,18 @@
</p>
<p>
For network connections using Open vSwitch it is also possible
- to configure 'native-tagged' and 'native-untagged' VLAN modes
- <span class="since">Since 1.1.0.</span> This is done with the
- optional <code>nativeMode</code> attribute on
- the <code><tag></code> subelement: <code>nativeMode</code>
- may be set to 'tagged' or 'untagged'. The <code>id</code>
- attribute of the <code><tag></code> subelement
- containing <code>nativeMode</code> sets which VLAN is considered
- to be the "native" VLAN for this interface, and
+ to configure 'native-tagged' and 'native-untagged'
+ <span class="since">Since 1.1.0.</span>
+ and 'dot1q-tunnel'(802.1ad double-tagged) VLAN modes.
+ <span class="since">Since 5.0.0.</span> This is done with the
+ optional <code>nativeMode</code> attribute on the
+ <code><tag></code> subelement: <code>nativeMode</code>
+ may be set to 'tagged' or 'untagged' or 'dot1q-tunnel'.
+ The <code>id</code> attribute of the <code><tag></code>
+ subelement containing <code>nativeMode</code> sets which VLAN is
+ considered to be the "native" VLAN for this interface, and
the <code>nativeMode</code> attribute determines whether or not
- traffic for that VLAN will be tagged.
+ traffic for that VLAN will be tagged or QinQ.
</p>
<p>
<code><vlan></code> elements can also be specified in
diff --git a/docs/schemas/networkcommon.rng b/docs/schemas/networkcommon.rng
index 2699555..11c48ff 100644
--- a/docs/schemas/networkcommon.rng
+++ b/docs/schemas/networkcommon.rng
@@ -223,6 +223,7 @@
<choice>
<value>tagged</value>
<value>untagged</value>
+ <value>dot1q-tunnel</value>
</choice>
</attribute>
</optional>
diff --git a/src/conf/netdev_vlan_conf.c b/src/conf/netdev_vlan_conf.c
index dff49c6..79710d9 100644
--- a/src/conf/netdev_vlan_conf.c
+++ b/src/conf/netdev_vlan_conf.c
@@ -29,7 +29,7 @@
#define VIR_FROM_THIS VIR_FROM_NONE
VIR_ENUM_IMPL(virNativeVlanMode, VIR_NATIVE_VLAN_MODE_LAST,
- "default", "tagged", "untagged")
+ "default", "tagged", "untagged", "dot1q-tunnel")
int
virNetDevVlanParse(xmlNodePtr node, xmlXPathContextPtr ctxt, virNetDevVlanPtr def)
diff --git a/src/util/virnetdevopenvswitch.c b/src/util/virnetdevopenvswitch.c
index 8fe06fd..2cc576f 100644
--- a/src/util/virnetdevopenvswitch.c
+++ b/src/util/virnetdevopenvswitch.c
@@ -91,6 +91,11 @@ virNetDevOpenvswitchConstructVlans(virCommandPtr cmd, virNetDevVlanPtr virtVlan)
virCommandAddArg(cmd, "vlan_mode=native-untagged");
virCommandAddArgFormat(cmd, "tag=%d", virtVlan->nativeTag);
break;
+ case VIR_NATIVE_VLAN_MODE_DOT1Q_TUNNEL:
+ virCommandAddArg(cmd, "vlan_mode=dot1q-tunnel");
+ virCommandAddArg(cmd, "other_config:qinq-ethtype=802.1q");
+ virCommandAddArgFormat(cmd, "tag=%d", virtVlan->nativeTag);
+ break;
case VIR_NATIVE_VLAN_MODE_DEFAULT:
default:
break;
@@ -504,6 +509,8 @@ int virNetDevOpenvswitchUpdateVlan(const char *ifname,
"--", "--if-exists", "clear", "Port", ifname, "tag",
"--", "--if-exists", "clear", "Port", ifname, "trunk",
"--", "--if-exists", "clear", "Port", ifname, "vlan_mode",
+ "--", "--if-exists", "remove", "Port", ifname, "other_config",
+ "qinq-ethtype",
"--", "--if-exists", "set", "Port", ifname, NULL);
if (virNetDevOpenvswitchConstructVlans(cmd, virtVlan) < 0)
diff --git a/src/util/virnetdevvlan.h b/src/util/virnetdevvlan.h
index be85f59..0667f9d 100644
--- a/src/util/virnetdevvlan.h
+++ b/src/util/virnetdevvlan.h
@@ -29,6 +29,7 @@ typedef enum {
VIR_NATIVE_VLAN_MODE_DEFAULT = 0,
VIR_NATIVE_VLAN_MODE_TAGGED,
VIR_NATIVE_VLAN_MODE_UNTAGGED,
+ VIR_NATIVE_VLAN_MODE_DOT1Q_TUNNEL,
VIR_NATIVE_VLAN_MODE_LAST
} virNativeVlanMode;
diff --git a/tests/networkxml2xmlin/openvswitch-net.xml b/tests/networkxml2xmlin/openvswitch-net.xml
index 2f6084d..0952859 100644
--- a/tests/networkxml2xmlin/openvswitch-net.xml
+++ b/tests/networkxml2xmlin/openvswitch-net.xml
@@ -30,4 +30,13 @@
<parameters profileid='native-profile'/>
</virtualport>
</portgroup>
+ <portgroup name='8021ad'>
+ <vlan trunk='yes'>
+ <tag id='555' nativeMode='dot1q-tunnel'/>
+ <tag id='666'/>
+ </vlan>
+ <virtualport>
+ <parameters profileid='8021ad-profile'/>
+ </virtualport>
+ </portgroup>
</network>
diff --git a/tests/networkxml2xmlout/openvswitch-net.xml b/tests/networkxml2xmlout/openvswitch-net.xml
index 2f6084d..0952859 100644
--- a/tests/networkxml2xmlout/openvswitch-net.xml
+++ b/tests/networkxml2xmlout/openvswitch-net.xml
@@ -30,4 +30,13 @@
<parameters profileid='native-profile'/>
</virtualport>
</portgroup>
+ <portgroup name='8021ad'>
+ <vlan trunk='yes'>
+ <tag id='555' nativeMode='dot1q-tunnel'/>
+ <tag id='666'/>
+ </vlan>
+ <virtualport>
+ <parameters profileid='8021ad-profile'/>
+ </virtualport>
+ </portgroup>
</network>
diff --git a/tests/networkxml2xmlupdateout/openvswitch-net-modified.xml b/tests/networkxml2xmlupdateout/openvswitch-net-modified.xml
index cc0c344..e5794fc 100644
--- a/tests/networkxml2xmlupdateout/openvswitch-net-modified.xml
+++ b/tests/networkxml2xmlupdateout/openvswitch-net-modified.xml
@@ -30,4 +30,13 @@
<parameters profileid='native-profile'/>
</virtualport>
</portgroup>
+ <portgroup name='8021ad'>
+ <vlan trunk='yes'>
+ <tag id='555' nativeMode='dot1q-tunnel'/>
+ <tag id='666'/>
+ </vlan>
+ <virtualport>
+ <parameters profileid='8021ad-profile'/>
+ </virtualport>
+ </portgroup>
</network>
diff --git a/tests/networkxml2xmlupdateout/openvswitch-net-more-portgroups.xml b/tests/networkxml2xmlupdateout/openvswitch-net-more-portgroups.xml
index 7c19ad9..3df0b96 100644
--- a/tests/networkxml2xmlupdateout/openvswitch-net-more-portgroups.xml
+++ b/tests/networkxml2xmlupdateout/openvswitch-net-more-portgroups.xml
@@ -41,4 +41,13 @@
<parameters profileid='native-profile'/>
</virtualport>
</portgroup>
+ <portgroup name='8021ad'>
+ <vlan trunk='yes'>
+ <tag id='555' nativeMode='dot1q-tunnel'/>
+ <tag id='666'/>
+ </vlan>
+ <virtualport>
+ <parameters profileid='8021ad-profile'/>
+ </virtualport>
+ </portgroup>
</network>
diff --git a/tests/networkxml2xmlupdateout/openvswitch-net-without-alice.xml b/tests/networkxml2xmlupdateout/openvswitch-net-without-alice.xml
index 4104424..38846aa 100644
--- a/tests/networkxml2xmlupdateout/openvswitch-net-without-alice.xml
+++ b/tests/networkxml2xmlupdateout/openvswitch-net-without-alice.xml
@@ -20,4 +20,13 @@
<parameters profileid='native-profile'/>
</virtualport>
</portgroup>
+ <portgroup name='8021ad'>
+ <vlan trunk='yes'>
+ <tag id='555' nativeMode='dot1q-tunnel'/>
+ <tag id='666'/>
+ </vlan>
+ <virtualport>
+ <parameters profileid='8021ad-profile'/>
+ </virtualport>
+ </portgroup>
</network>
--
1.8.3.1
5 years, 11 months
[libvirt] More logs from libvirt+qemu+VNC+SASL
by Tomasz Barański
Hello
I'm working on supporting VNC console on FIPS-enabled oVirt hosts[1]. I
made qemu use SASL as authentication method instead of regular passwords.
However, no matter what I do, I can't get it to accept credentials provided
with a VNC client.
Is there a way to get some qemu/SASL logs? I need to understand why the
credentials are not accepted.
Any pointers to docs/code/old bugs appreciated.
Tomo
[1] https://bugzilla.redhat.com/show_bug.cgi?id=1595536
5 years, 11 months
[libvirt] [PATCH] domain: conf: graphics: Fix picking DRI renderer automatically for SPICE
by Erik Skultety
Commit 255e0732 introduced a few graphics-related helpers. The problem
is that virDomainGraphicsNeedsAutoRenderNode returns true if it gets
NULL as a response from virDomainGraphicsNeedsAutoRenderNode. That's
okay for egl-headless because that one always needs a DRM render node,
the same is not true for SPICE though, and unless the XML specifies
<gl enable='yes'> for SPICE, there's no need for any renderer.
https://bugzilla.redhat.com/show_bug.cgi?id=1656895
Signed-off-by: Erik Skultety <eskultet(a)redhat.com>
---
src/conf/domain_conf.c | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index b70dca6c61..efa0a94f39 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -30982,8 +30982,7 @@ virDomainGraphicsGetRenderNode(const virDomainGraphicsDef *graphics)
switch (graphics->type) {
case VIR_DOMAIN_GRAPHICS_TYPE_SPICE:
- if (graphics->data.spice.gl == VIR_TRISTATE_BOOL_YES)
- ret = graphics->data.spice.rendernode;
+ ret = graphics->data.spice.rendernode;
break;
case VIR_DOMAIN_GRAPHICS_TYPE_EGL_HEADLESS:
ret = graphics->data.egl_headless.rendernode;
@@ -31006,6 +31005,10 @@ virDomainGraphicsNeedsAutoRenderNode(const virDomainGraphicsDef *graphics)
if (!virDomainGraphicsSupportsRenderNode(graphics))
return false;
+ if (graphics->type == VIR_DOMAIN_GRAPHICS_TYPE_SPICE &&
+ graphics->data.spice.gl != VIR_TRISTATE_BOOL_YES)
+ return false;
+
if (virDomainGraphicsGetRenderNode(graphics))
return false;
--
2.19.2
5 years, 11 months
[libvirt] [PATCH] qemu: Qemu process unexpectedly killed in repeated reboot
by Wang King
The issue occurs when I make repeated calls to virDomainReboot with
VIR_DOMAIN_REBOOT_DEFAULT flag. In the first call to reboot domain,
the qemu driver chose ACPI path, and set priv->fakeReboot to true.
Then in a second call, qemu driver chose agent to reboot which set
fakeReboot to false. But because the guest already responded to ACPI
shut down, libvirtd daemon will process a SHUTDOWN event in
qemuProcessShutdownOrReboot and checks priv->fakeReboot. Since the
fakeReboot flag is now false, qemu process is unexpectedly killed.
I have no idea how to fix it.
Signed-off-by: Wang King <king.wang(a)huawei.com>
--
2.8.3
5 years, 11 months
[libvirt] [PATCH 0/3] libxl: add support for openvswitch
by Jim Fehlig
Patch1 adds support in the driver, patch2 adds support in the
config converter, and it ends with some news...
Jim Fehlig (3):
libxl: support openvswitch interfaces
xenconfig: add support for openvswitch configuration
news: Mention Xen support for openvswitch
docs/news.xml | 10 ++
src/libxl/libxl_conf.c | 47 +++++++-
src/xenconfig/xen_common.c | 113 +++++++++++++++++-
.../test-fullvirt-ovswitch-tagged.cfg | 25 ++++
.../test-fullvirt-ovswitch-tagged.xml | 50 ++++++++
.../test-fullvirt-ovswitch-trunked.cfg | 25 ++++
.../test-fullvirt-ovswitch-trunked.xml | 51 ++++++++
tests/xlconfigtest.c | 2 +
8 files changed, 317 insertions(+), 6 deletions(-)
create mode 100644 tests/xlconfigdata/test-fullvirt-ovswitch-tagged.cfg
create mode 100644 tests/xlconfigdata/test-fullvirt-ovswitch-tagged.xml
create mode 100644 tests/xlconfigdata/test-fullvirt-ovswitch-trunked.cfg
create mode 100644 tests/xlconfigdata/test-fullvirt-ovswitch-trunked.xml
--
2.18.0
5 years, 11 months
[libvirt] [PATCH] version: Add ParseVersion and a Version struct
by W. Trevor King
Make it easier to convert version integers to the more human-readable
major.minor.release format.
---
connect.go | 8 ++++++++
version.go | 52 ++++++++++++++++++++++++++++++++++++++++++++++
version_test.go | 64 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 124 insertions(+)
create mode 100644 version.go
create mode 100644 version_test.go
diff --git a/connect.go b/connect.go
index 8cc7cc7..fac45da 100644
--- a/connect.go
+++ b/connect.go
@@ -46,6 +46,7 @@ func init() {
}
const (
+ // VERSION_NUMBER is the version of the linked libvirt.
VERSION_NUMBER = uint32(C.LIBVIR_VERSION_NUMBER)
)
@@ -306,7 +307,9 @@ func releaseConnectionData(c *Connect) {
delete(connections, c.ptr)
}
+// GetVersion returns the version of the linked libvirt.
// See also https://libvirt.org/html/libvirt-libvirt-host.html#virGetVersion
+// and ParseVersion.
func GetVersion() (uint32, error) {
var version C.ulong
var err C.virError
@@ -540,7 +543,10 @@ func (c *Connect) GetHostname() (string, error) {
return hostname, nil
}
+// GetLibVersion returns the version of libvirt used by the daemon
+// running on the connected host.
// See also https://libvirt.org/html/libvirt-libvirt-host.html#virConnectGetLibVersion
+// and ParseVersion.
func (c *Connect) GetLibVersion() (uint32, error) {
var version C.ulong
var err C.virError
@@ -2272,7 +2278,9 @@ func (c *Connect) GetDomainCapabilities(emulatorbin string, arch string, machine
return C.GoString(ret), nil
}
+// GetVersion returns the hypervisor version.
// See also https://libvirt.org/html/libvirt-libvirt-host.html#virConnectGetVersion
+// and ParseVersion.
func (c *Connect) GetVersion() (uint32, error) {
var hvVer C.ulong
var err C.virError
diff --git a/version.go b/version.go
new file mode 100644
index 0000000..1a07fa3
--- /dev/null
+++ b/version.go
@@ -0,0 +1,52 @@
+/*
+ * This file is part of the libvirt-go project
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * Copyright (C) 2018 Red Hat, Inc.
+ */
+
+package libvirt
+
+import (
+ "fmt"
+)
+
+// Version represents the libvirt version.
+// See also https://libvirt.org/html/libvirt-libvirt-host.html#virGetVersion
+type Version struct {
+ Major uint32
+ Minor uint32
+ Release uint32
+}
+
+// String returns the "major.minor.release" version string.
+func (v *Version) String() string {
+ return fmt.Sprintf("%d.%d.%d", v.Major, v.Minor, v.Release)
+}
+
+// ParseVersion converts a version integer to a structured Version instance.
+// See also https://libvirt.org/html/libvirt-libvirt-host.html#virGetVersion
+func ParseVersion(version uint32) *Version {
+ return &Version{
+ Major: version / 1000000,
+ Minor: (version / 1000) % 1000,
+ Release: version % 1000,
+ }
+}
diff --git a/version_test.go b/version_test.go
new file mode 100644
index 0000000..ecafe5d
--- /dev/null
+++ b/version_test.go
@@ -0,0 +1,64 @@
+/*
+ * This file is part of the libvirt-go project
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * Copyright (C) 2018 Red Hat, Inc.
+ */
+
+package libvirt
+
+import (
+ "reflect"
+ "testing"
+)
+
+func TestVersionString(t *testing.T) {
+ version := Version{
+ Major: 3,
+ Minor: 9,
+ Release: 0,
+ }
+ actual := version.String()
+ expected := "3.9.0"
+ if actual != expected {
+ t.Fatalf("%q != %q", actual, expected)
+ }
+}
+
+func TestParseVersion(t *testing.T) {
+ for _, testCase := range []struct{
+ input uint32
+ expected *Version
+ }{
+ {
+ input: 3009000,
+ expected: &Version{Major: 3, Minor: 9},
+ },
+ {
+ input: 4001002,
+ expected: &Version{Major: 4, Minor: 1, Release: 2},
+ },
+ } {
+ actual := ParseVersion(testCase.input)
+ if !reflect.DeepEqual(actual, testCase.expected) {
+ t.Fatalf("%v != %v", actual, testCase.expected)
+ }
+ }
+}
--
1.8.3.1
5 years, 11 months