[libvirt] [test-API][PATCH 1/2] Add support for spice graphics
by Nan Zhang
* utils/Python/xmlgenerator.py: This extends graphics element for spice
XML composing, and support sub-elements settings for audio, images,
streaming and so on:
<graphics type='spice' autoport='yes'>
<image compression='auto_glz'/>
<jpeg compression='auto'/>
<zlib compression='auto'/>
<playback compression='on'/>
<streaming mode='filter'/>
<clipboard copypaste='no'/>
</graphics>
* utils/Python/xmlbuilder.py: Add 2 methods add_graphics() and
build_graphics() to XmlBuilder class.
---
utils/Python/xmlbuilder.py | 36 +++++++++++++++++++++++-
utils/Python/xmlgenerator.py | 62 +++++++++++++++++++++++++++++++++++++----
2 files changed, 91 insertions(+), 7 deletions(-)
diff --git a/utils/Python/xmlbuilder.py b/utils/Python/xmlbuilder.py
index 5a0f8c8..739eccb 100644
--- a/utils/Python/xmlbuilder.py
+++ b/utils/Python/xmlbuilder.py
@@ -64,6 +64,13 @@ class XmlBuilder:
hostdev_node, domain.getElementsByTagName("console")[0])
return hostdev
+ def add_graphics(self, params, domain):
+ graphics = xmlgenerator.graphics_xml(params)
+ graphics_node = domain.importNode(graphics.childNodes[0], True)
+ domain.getElementsByTagName("devices")[0].insertBefore(
+ graphics_node, domain.getElementsByTagName("console")[0])
+ return graphics
+
def build_domain_install(self, params):
domain = xmlgenerator.domain_xml(params, True)
self.add_disk(params, domain)
@@ -151,6 +158,12 @@ class XmlBuilder:
self.write_toxml(hostdev)
return hostdev.toxml()
+ def build_graphics(self, params):
+ graphics = xmlgenerator.graphics_xml(params)
+ if __DEBUG__:
+ self.write_toxml(graphics)
+ return graphics.toxml()
+
def build_pool(self, params):
pool = xmlgenerator.pool_xml(params)
if __DEBUG__:
@@ -242,6 +255,20 @@ if __name__ == "__main__":
interfacexml = xmlobj.build_interface(params)
+ #--------------------------
+ # get graphics xml string
+ #--------------------------
+ print '=' * 30, 'graphics xml', '=' * 30
+ params['graphtype'] = 'spice'
+ params['image'] = 'auto_glz'
+ params['jpeg'] = 'auto'
+ params['zlib'] = 'auto'
+ params['playback'] = 'on'
+ params['streaming'] = 'filter'
+ params['clipboard'] = 'no'
+
+ graphicsxml = xmlobj.build_graphics(params)
+
#---------------------
# get pool xml string
#---------------------
@@ -297,6 +324,13 @@ if __name__ == "__main__":
params['memory'] = '1048576'
params['vcpu'] = '2'
params['inputbus'] = 'usb'
+ params['graphtype'] = 'spice'
+ params['image'] = 'auto_glz'
+ params['jpeg'] = 'auto'
+ params['zlib'] = 'auto'
+ params['playback'] = 'on'
+ params['streaming'] = 'filter'
+ params['clipboard'] = 'no'
params['sound'] = 'ac97'
params['bootcd'] = '/iso/rhel5.iso'
@@ -367,7 +401,7 @@ if __name__ == "__main__":
#----------------------------------------
# get domain snapshot xml string
#----------------------------------------
- params['name'] = 'hello'
+ params['snapshotname'] = 'hello'
params['description'] = 'hello snapshot'
snapshot_xml = xmlobj.build_domain_snapshot(params)
diff --git a/utils/Python/xmlgenerator.py b/utils/Python/xmlgenerator.py
index d57dd33..460f2e5 100644
--- a/utils/Python/xmlgenerator.py
+++ b/utils/Python/xmlgenerator.py
@@ -233,12 +233,6 @@ def domain_xml(params, install = False):
input_element.setAttribute('bus', 'ps2')
devices_element.appendChild(input_element)
- # <graphics>
- graphics_element = domain.createElement('graphics')
- graphics_element.setAttribute('type', 'vnc')
- graphics_element.setAttribute('port', '-1')
- graphics_element.setAttribute('keymap', 'en-us')
- devices_element.appendChild(graphics_element)
domain_element.appendChild(devices_element)
# <sound>
@@ -253,6 +247,62 @@ def domain_xml(params, install = False):
return domain
+def graphics_xml(params):
+ graphics = xml.dom.minidom.Document()
+ # <graphics>
+ graphics_element = graphics.createElement('graphics')
+ if not params.has_key('graphtype'):
+ params['graphtype'] == 'vnc'
+
+ graphics_element.setAttribute('type', params['graphtype'])
+ graphics.appendChild(graphics_element)
+
+ if params['graphtype'] == 'vnc':
+ graphics_element.setAttribute('port', '-1')
+ graphics_element.setAttribute('keymap', 'en-us')
+ elif params['graphtype'] == 'spice':
+ graphics_element.setAttribute('autoport', 'yes')
+ if params.has_key('image'):
+ image_element = graphics.createElement('image')
+ # image to set image compression (accepts
+ # auto_glz, auto_lz, quic, glz, lz, off)
+ image_element.setAttribute('compression', params['image'])
+ graphics_element.appendChild(image_element)
+ if params.has_key('jpeg'):
+ jpeg_element = graphics.createElement('jpeg')
+ # jpeg for JPEG compression for images over wan (accepts
+ # auto, never, always)
+ jpeg_element.setAttribute('compression', params['jpeg'])
+ graphics_element.appendChild(jpeg_element)
+ if params.has_key('zlib'):
+ zlib_element = graphics.createElement('zlib')
+ # zlib for configuring wan image compression (accepts
+ # auto, never, always)
+ zlib_element.setAttribute('compression', params['zlib'])
+ graphics_element.appendChild(zlib_element)
+ if params.has_key('playback'):
+ playback_element = graphics.createElement('playback')
+ # playback for enabling audio stream compression (accepts on or off)
+ playback_element.setAttribute('compression', params['playback'])
+ graphics_element.appendChild(playback_element)
+ if params.has_key('streaming'):
+ streaming_element = graphics.createElement('streaming')
+ # streamming for settings it's mode attribute to one of
+ # filter, all or off
+ streaming_element.setAttribute('mode', params['streaming'])
+ graphics_element.appendChild(streaming_element)
+ if params.has_key('clipboard'):
+ clipboard_element = graphics.createElement('clipboard')
+ # Copy & Paste functionality is enabled by default, and can
+ # be disabled by setting the copypaste property to no
+ clipboard_element.setAttribute('copypaste', params['clipboard'])
+ graphics_element.appendChild(clipboard_element)
+ else:
+ print 'Wrong graphics type was specified.'
+ sys.exit(1)
+
+ return graphics
+
def disk_xml(params, cdrom = False):
disk = xml.dom.minidom.Document()
# <disk> -- START
--
1.7.4.4
12 years, 11 months
[libvirt] [test-API][PATCH] Add test case set_cpu_shares.py for setting cpu scheduler info
by Nan Zhang
* repos/domain/set_cpu_shares.py: set the value of cpu_shares
property of the guest.
---
repos/domain/set_cpu_shares.py | 111 ++++++++++++++++++++++++++++++++++++++++
1 files changed, 111 insertions(+), 0 deletions(-)
create mode 100644 repos/domain/set_cpu_shares.py
diff --git a/repos/domain/set_cpu_shares.py b/repos/domain/set_cpu_shares.py
new file mode 100644
index 0000000..4560809
--- /dev/null
+++ b/repos/domain/set_cpu_shares.py
@@ -0,0 +1,111 @@
+#!/usr/bin/env python
+"""Set the value of cpu_shares property of the guest
+ domain:set_cpu_shares
+ guestname
+ xxx
+ flags
+ 0|1|2
+"""
+
+__author__ = 'Nan Zhang: nzhang(a)redhat.com'
+__date__ = 'Tue Sep 27, 2011'
+__version__ = '0.1.0'
+__credits__ = 'Copyright (C) 2011 Red Hat, Inc.'
+__all__ = ['check_params', 'check_cpu_shares', 'set_cpu_shares']
+
+import os
+import re
+import sys
+import time
+from xml.dom import minidom
+
+
+def append_path(path):
+ """Append root path of package"""
+ if path in sys.path:
+ pass
+ else:
+ sys.path.append(path)
+
+pwd = os.getcwd()
+result = re.search('(.*)libvirt-test-API', pwd)
+append_path(result.group(0))
+
+from lib import connectAPI
+from lib import domainAPI
+from utils.Python import utils
+from utils.Python import xmlbuilder
+from exception import LibvirtAPI
+
+def check_params(params):
+ """Verify inputing parameter dictionary"""
+ logger = params['logger']
+ keys = ['guestname', 'flags']
+ for key in keys:
+ if key not in params:
+ logger.error("%s is required" %key)
+ return 1
+ return 0
+
+def check_cpu_shares(params, util, guestname, cpu_shares):
+ """Check the value of cpu_shares"""
+ logger = params['logger']
+ cmd = "cat /cgroup/cpu/libvirt/qemu/%s/cpu.shares" % guestname
+ ret, out = util.exec_cmd(cmd, shell=True)
+ if ret:
+ logger.error("fail to set the value of cpu_shares: %s" % out[0])
+ return 0
+ else:
+ logger.info("from cgroup, the value of cpu_shares is %s" % out[0])
+
+ if cmp(int(out[0]), cpu_shares):
+ return 1
+ else:
+ logger.info("the value of cpu_shares does match the original \
+cpu scheduler information.")
+ return 0
+
+def set_cpu_shares(params):
+ """Get the cpu scheduler information"""
+ # Initiate and check parameters
+ params_check_result = check_params(params)
+ if params_check_result:
+ return 1
+ logger = params['logger']
+ guestname = params['guestname']
+ flags = params['flags']
+ schedinfo = {}
+ schedinfo['cpu_shares'] = 2048
+ cpu_shares = schedinfo['cpu_shares']
+
+ # Connect to local hypervisor connection URI
+ util = utils.Utils()
+ uri = util.get_uri('127.0.0.1')
+ conn = connectAPI.ConnectAPI()
+ virconn = conn.open(uri)
+
+ caps = conn.get_caps()
+ logger.debug(caps)
+
+ domobj = domainAPI.DomainAPI(virconn)
+ try:
+ domobj.set_sched_params_flags(guestname, schedinfo, int(flags))
+ logger.debug("set the value of cpu_shares with %s" % cpu_shares)
+ except LibvirtAPI, e:
+ logger.error("API error message: %s, error code is %s" %
+ (e.response()['message'], e.response()['code']))
+ return 1
+
+ check_result = check_cpu_shares(params, util, guestname, cpu_shares)
+ if check_result:
+ logger.error("cpu_shares does not match.")
+ conn.close()
+ return 1
+
+ logger.info("success to set scheduler parameters.")
+ conn.close()
+ return 0
+
+def set_cpu_shares_clean():
+ """Clean testing environment"""
+ pass
--
1.7.4.4
12 years, 11 months
Re: [libvirt] macvtap not working on kvm
by Amit Tewari
Hi all,
In same previous scenario of macvtap ,Im not able to assign dhcp address
to guest interface, and if I assign static ip of my lan to guest
interface , than also it is not able to connect to the network..
________________________________
From: libvir-list-bounces(a)redhat.com
[mailto:libvir-list-bounces@redhat.com] On Behalf Of Amit Tewari
Sent: Tuesday, December 13, 2011 5:11 PM
To: xhu
Cc: libvir-list(a)redhat.com
Subject: Re: [libvirt] macvtap not working on kvm
Hi,
Actually udev renames the eth0 interface in guest to eth1 because
macvtap0 has different mac address then eth0 on host..
Is there some way so that we can prevent changing this rename of eth0 to
eth1 on guest. I don't want to change udev files.
________________________________
From: xhu [mailto:xhu@redhat.com]
Sent: Tuesday, December 13, 2011 3:01 PM
To: Amit Tewari
Cc: libvir-list(a)redhat.com
Subject: Re: [libvirt] macvtap not working on kvm
On 12/13/2011 12:59 PM, Amit Tewari wrote:
Hi all,
My test environment
Host os=rhel6.1
Guest os = rhel6.1
Libvirt=0.9.8
Kvm hypervisor
I have made this entry in guest xml file
<interface type='direct'>
<source dev='eth0' mode='bridge'/>
<model type='virtio'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x03'
function='0x0'/>
</interface>
Now when I start the guest
#virsh start guest
Following macvtap0 is created on host and is shown below
#ip link show macvtap0
51: macvtap0@eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq
state UNKNOWN qlen 500
link/ether 52:54:00:55:AE:B5brd ff:ff:ff:ff:ff:ff
but when the guest is up and I try to perform
# ifup eth0
It gives following message
"Bringing up interface eth0: Device eth0 does not seem to be present,
delaying initialization.
[FAILED]"
The interface name may change to ethN(N may be 1 or 2 ...) due to the
change of guest interface mac address.
You can log in guest and use command: "ip link show" to get the value
of N.
Then create ifcfg-ethN for it and use "ifup ethN" to start it in guest.
Guest eth interface does not create. What is the problem?
When I checked dmesg on host it gives following message-"macvatp0: ipv6
routers not present"
Please let me know how macvtap work on kvm.
DISCLAIMER:
------------------------------------------------------------------------
-----------------------------------------------
The contents of this e-mail and any attachment(s) are confidential and
intended
for the named recipient(s) only.
It shall not attach any liability on the originator or NECHCL or its
affiliates. Any views or opinions presented in
this email are solely those of the author and may not necessarily
reflect the
opinions of NECHCL or its affiliates.
Any form of reproduction, dissemination, copying, disclosure,
modification,
distribution and / or publication of
this message without the prior written consent of the author of this
e-mail is
strictly prohibited. If you have
received this email in error please delete it and notify the sender
immediately. .
------------------------------------------------------------------------
-----------------------------------------------
--
libvir-list mailing list
libvir-list(a)redhat.com
https://www.redhat.com/mailman/listinfo/libvir-list
DISCLAIMER:
------------------------------------------------------------------------
-----------------------------------------------
The contents of this e-mail and any attachment(s) are confidential and
intended
for the named recipient(s) only.
It shall not attach any liability on the originator or NECHCL or its
affiliates. Any views or opinions presented in
this email are solely those of the author and may not necessarily
reflect the
opinions of NECHCL or its affiliates.
Any form of reproduction, dissemination, copying, disclosure,
modification,
distribution and / or publication of
this message without the prior written consent of the author of this
e-mail is
strictly prohibited. If you have
received this email in error please delete it and notify the sender
immediately. .
------------------------------------------------------------------------
-----------------------------------------------
DISCLAIMER:
-----------------------------------------------------------------------------------------------------------------------
The contents of this e-mail and any attachment(s) are confidential and
intended
for the named recipient(s) only.
It shall not attach any liability on the originator or NECHCL or its
affiliates. Any views or opinions presented in
this email are solely those of the author and may not necessarily reflect the
opinions of NECHCL or its affiliates.
Any form of reproduction, dissemination, copying, disclosure, modification,
distribution and / or publication of
this message without the prior written consent of the author of this e-mail is
strictly prohibited. If you have
received this email in error please delete it and notify the sender
immediately. .
-----------------------------------------------------------------------------------------------------------------------
12 years, 11 months
[libvirt] [PATCH] python: Expose memoryPeek to Python binding
by Osier Yang
---
python/libvirt-override-api.xml | 8 ++++++++
python/libvirt-override.c | 33 +++++++++++++++++++++++++++++++++
2 files changed, 41 insertions(+), 0 deletions(-)
diff --git a/python/libvirt-override-api.xml b/python/libvirt-override-api.xml
index 87db67b..2e0c7cb 100644
--- a/python/libvirt-override-api.xml
+++ b/python/libvirt-override-api.xml
@@ -414,5 +414,13 @@
<arg name='flags' type='unsigned int' info='unused, always passed 0'/>
<return type='char *' info='the returned buffer or None in case of error'/>
</function>
+ <function name='virDomainMemoryPeek' file='python'>
+ <info>Read the contents of domain's memory</info>
+ <arg name='dom' type='virDomainPtr' info='pointer to the domain'/>
+ <arg name='start' type='unsigned long long' info='start of memory to peek'/>
+ <arg name='size' type='size_t' info='size of memory to peek'/>
+ <arg name='flags' type='unsigned int' info='an OR'ed set of virDomainMemoryFlags'/>
+ <return type='char *' info='the returned buffer or None in case of error'/>
+ </function>
</symbols>
</api>
diff --git a/python/libvirt-override.c b/python/libvirt-override.c
index 4839e08..032cc47 100644
--- a/python/libvirt-override.c
+++ b/python/libvirt-override.c
@@ -5051,6 +5051,38 @@ libvirt_virDomainBlockPeek(PyObject *self ATTRIBUTE_UNUSED,
return py_retval;
}
+static PyObject *
+libvirt_virDomainMemoryPeek(PyObject *self ATTRIBUTE_UNUSED,
+ PyObject *args) {
+ PyObject *py_retval;
+ int c_retval;
+ virDomainPtr domain;
+ PyObject *pyobj_domain;
+ unsigned long long start;
+ size_t size;
+ char *buf;
+ unsigned int flags;
+
+ if (!PyArg_ParseTuple(args, (char *)"OLni:virDomainMemoryPeek", &pyobj_domain,
+ &start, &size, &flags))
+ return(NULL);
+
+ domain = (virDomainPtr) PyvirDomain_Get(pyobj_domain);
+
+ if ((buf = malloc(size)) == NULL)
+ return VIR_PY_NONE;
+
+ LIBVIRT_BEGIN_ALLOW_THREADS;
+ c_retval = virDomainMemoryPeek(domain, start, size, buf, flags);
+ LIBVIRT_END_ALLOW_THREADS;
+
+ if (c_retval < 0)
+ return VIR_PY_NONE;
+
+ py_retval = libvirt_charPtrWrap(buf);
+ return py_retval;
+}
+
/************************************************************************
* *
* The registration stuff *
@@ -5147,6 +5179,7 @@ static PyMethodDef libvirtMethods[] = {
{(char *) "virDomainSendKey", libvirt_virDomainSendKey, METH_VARARGS, NULL},
{(char *) "virDomainMigrateGetMaxSpeed", libvirt_virDomainMigrateGetMaxSpeed, METH_VARARGS, NULL},
{(char *) "virDomainBlockPeek", libvirt_virDomainBlockPeek, METH_VARARGS, NULL},
+ {(char *) "virDomainMemoryPeek", libvirt_virDomainMemoryPeek, METH_VARARGS, NULL},
{NULL, NULL, 0, NULL}
};
--
1.7.1
12 years, 11 months
[libvirt] [PATCH 0/2] virsh: allow to specify pci address with attach-interface
by KAMEZAWA Hiroyuki
Now, at using attach-interface, pci address of the device is determined
automatically. This is nice. But in some situation, users may want to
specify pci address by hand. For example, when users want to use multifunction
pci device, he need to specify pci slot.
This patch allows to specify pci address with attach-interface.
Then, this kind of script can run to assign multiple nics in a slot.
==
#!/bin/bash -x
DOM=$1
SLOT=$2
NUM=$3
for i in `seq 0 $NUM`; do
virsh attach-interface --domain $DOM --type='network' --source=default\
--persistent --model=virtio --address=pci:0000:00:$SLOT:0$i --multifunction;
done
==
Because I moved placement of some functions in virsh.c, diffstat seems big...
virsh.c | 948 ++++++++++++++++++++++++++++++++------------------------------
virsh.pod | 4
2 files changed, 495 insertions(+), 457 deletions(-)
Thanks,
-Kame
12 years, 11 months
[libvirt] [PATCH] virsh: support multifunction in attach-disk
by KAMEZAWA Hiroyuki
From: KAMEZAWA Hiroyuki <kamezawa.hiroyu(a)jp.fujitsu.com>
PCI <address...> can be specified by attach-disk but multifunction cannot
be specified. add --multifunction support.
---
tools/virsh.c | 7 ++++++-
tools/virsh.pod | 3 ++-
2 files changed, 8 insertions(+), 2 deletions(-)
diff --git a/tools/virsh.c b/tools/virsh.c
index d58b827..346b440 100644
--- a/tools/virsh.c
+++ b/tools/virsh.c
@@ -12661,6 +12661,7 @@ static const vshCmdOptDef opts_attach_disk[] = {
{"serial", VSH_OT_STRING, 0, N_("serial of disk device")},
{"shareable", VSH_OT_BOOL, 0, N_("shareable between domains")},
{"address", VSH_OT_STRING, 0, N_("address of disk device")},
+ {"multifunction", VSH_OT_BOOL, 0, N_("use multifunction pci under specified address")},
{NULL, 0, 0, NULL}
};
@@ -12916,9 +12917,13 @@ cmdAttachDisk(vshControl *ctl, const vshCmd *cmd)
if (diskAddr.type == DISK_ADDR_TYPE_PCI) {
virBufferAsprintf(&buf,
" <address type='pci' domain='0x%04x'"
- " bus ='0x%02x' slot='0x%02x' function='0x%0x' />\n",
+ " bus ='0x%02x' slot='0x%02x' function='0x%0x'",
diskAddr.addr.pci.domain, diskAddr.addr.pci.bus,
diskAddr.addr.pci.slot, diskAddr.addr.pci.function);
+ if (vshCommandOptBool(cmd, "multifunction"))
+ virBufferAsprintf(&buf, " multifunction='on' />\n");
+ else
+ virBufferAsprintf(&buf, " />\n");
} else {
vshError(ctl, "%s", _("expecting a pci:0000.00.00.00 address."));
goto cleanup;
diff --git a/tools/virsh.pod b/tools/virsh.pod
index fe92714..1a778f9 100644
--- a/tools/virsh.pod
+++ b/tools/virsh.pod
@@ -1273,7 +1273,7 @@ the device does not use managed mode.
=item B<attach-disk> I<domain-id> I<source> I<target>
[I<--driver driver>] [I<--subdriver subdriver>] [I<--cache cache>]
[I<--type type>] [I<--mode mode>] [I<--persistent>] [I<--sourcetype soucetype>]
-[I<--serial serial>] [I<--shareable>] [I<--address address>]
+[I<--serial serial>] [I<--shareable>] [I<--address address>] [I<--multifunction>]
Attach a new disk device to the domain.
I<source> and I<target> are paths for the files and devices.
@@ -1291,6 +1291,7 @@ I<serial> is the serial of disk device. I<shareable> indicates the disk device
is shareable between domains.
I<address> is the address of disk device in the form of pci:domain.bus.slot.function,
scsi:controller.bus.unit or ide:controller.bus.unit.
+I<multifunction> indicates specified pci address is a multifunction pci device address.
=item B<attach-interface> I<domain-id> I<type> I<source>
[I<--target target>] [I<--mac mac>] [I<--script script>] [I<--model model>]
--
1.7.4.1
12 years, 11 months
[libvirt] [PATCH v3] virsh: Add option to undefine storage with domains
by Peter Krempa
Add an option for virsh undefine command, to remove associated storage
volumes while undefining a domain. This patch alows the user to remove
associated (libvirt managed ) storage volumes while undefining a domain.
The new option --storage for the undefine command takes a string
argument that consists of comma separated list of target or source path
of volumes to be undefined. Volumes are removed after the domain has
been successfuly undefined,
If a volume is not part of a storage pool, the user is warned to remove
the volume in question himself.
Option --wipe-storage may be specified along with this, that ensures
the image is wiped before removing.
Option --remove-all-storage enables the user to remove all storage. The
name is chosen long as the users shoudl be aware what they're about to
do.
---
Changes to v2 based on reveiw by Eric:
( http://www.redhat.com/archives/libvir-list/2011-September/msg00481.html )
- First undefine the domain, then undefine volumes
- Add option to remove all volumes
- Fix typos and strange wordings
- Change retur value to failure if some operations on volumes fail.
tools/virsh.c | 143 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
tools/virsh.pod | 18 +++++++
2 files changed, 161 insertions(+), 0 deletions(-)
diff --git a/tools/virsh.c b/tools/virsh.c
index d58b827..281ffa9 100644
--- a/tools/virsh.c
+++ b/tools/virsh.c
@@ -1924,6 +1924,13 @@ static const vshCmdInfo info_undefine[] = {
static const vshCmdOptDef opts_undefine[] = {
{"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name or uuid")},
{"managed-save", VSH_OT_BOOL, 0, N_("remove domain managed state file")},
+ {"storage", VSH_OT_DATA, VSH_OFLAG_NONE,
+ N_("remove associated storage volumes (comma separated list of targets "
+ "or source paths) (see domblklist)")},
+ {"remove-all-storage", VSH_OT_BOOL, 0,
+ N_("remove all associated storage volumes (use with caution)")},
+ {"wipe-storage", VSH_OT_BOOL, VSH_OFLAG_NONE,
+ N_("wipe data on the removed volumes")},
{"snapshots-metadata", VSH_OT_BOOL, 0,
N_("remove all domain snapshot metadata, if inactive")},
{NULL, 0, 0, NULL}
@@ -1940,6 +1947,9 @@ cmdUndefine(vshControl *ctl, const vshCmd *cmd)
/* User-requested actions. */
bool managed_save = vshCommandOptBool(cmd, "managed-save");
bool snapshots_metadata = vshCommandOptBool(cmd, "snapshots-metadata");
+ bool wipe_storage = vshCommandOptBool(cmd, "wipe-storage");
+ bool remove_storage = false;
+ bool remove_all_storage = vshCommandOptBool(cmd, "remove-all-storage");
/* Positive if these items exist. */
int has_managed_save = 0;
int has_snapshots_metadata = 0;
@@ -1949,6 +1959,22 @@ cmdUndefine(vshControl *ctl, const vshCmd *cmd)
bool snapshots_safe = false;
int rc = -1;
int running;
+ /* list of volumes to remove along with this domain */
+ char *volumes = NULL;
+ const char *volume;
+ char *saveptr = NULL;
+ char *def = NULL;
+ char *source = NULL;
+ char *target = NULL;
+ int i;
+
+ xmlNodePtr *vol_nodes = NULL;
+ int nvolumes = 0;
+ xmlXPathContextPtr ctxt = NULL;
+ xmlDocPtr doc = NULL;
+ virStorageVolPtr vol = NULL;
+ bool vol_del_failed = false;
+
if (managed_save) {
flags |= VIR_DOMAIN_UNDEFINE_MANAGED_SAVE;
@@ -1965,6 +1991,18 @@ cmdUndefine(vshControl *ctl, const vshCmd *cmd)
if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
return false;
+ /* check if a string that should contain list of volumes to remove is present */
+ if (vshCommandOptString(cmd, "storage", (const char **)&volumes) > 0) {
+ /* caution volumes has to be re-assigned as it would be doulbe freed */
+ volumes = vshStrdup(ctl, volumes);
+
+ if (remove_all_storage) {
+ vshError(ctl, _("Specified both --storage and --remove-all-storage"));
+ goto cleanup;
+ }
+ remove_storage = true;
+ }
+
/* Do some flag manipulation. The goal here is to disable bits
* from flags to reduce the likelihood of a server rejecting
* unknown flag bits, as well as to track conditions which are
@@ -2027,6 +2065,20 @@ cmdUndefine(vshControl *ctl, const vshCmd *cmd)
snapshots_safe = true;
}
+ /* Stash domain description for later use */
+ if (remove_storage || remove_all_storage) {
+ if (running) {
+ vshError(ctl, _("Storage volume deletion is supported only on stopped domains"));
+ goto cleanup;
+ }
+
+ if (!(def = virDomainGetXMLDesc(dom, 0))) {
+ vshError(ctl, _("Could not retrieve domain XML description"));
+ goto cleanup;
+ }
+ }
+
+
/* Generally we want to try the new API first. However, while
* virDomainUndefineFlags was introduced at the same time as
* VIR_DOMAIN_UNDEFINE_MANAGED_SAVE in 0.9.4, the
@@ -2076,9 +2128,100 @@ out:
ret = true;
} else {
vshError(ctl, _("Failed to undefine domain %s"), name);
+ goto cleanup;
+ }
+
+ /* try to undefine storage volumes associated with this domain, if it's requested */
+ if (remove_storage || remove_all_storage) {
+ ret = false;
+ doc = virXMLParseStringCtxt(def, _("(domain_definition)"), &ctxt);
+ if (!doc)
+ goto cleanup;
+
+ nvolumes = virXPathNodeSet("./devices/disk", ctxt, &vol_nodes);
+
+ if (nvolumes < 0)
+ goto cleanup;
+
+ for (i = 0; i < nvolumes; i++) {
+ ctxt->node = vol_nodes[i];
+ VIR_FREE(target);
+ VIR_FREE(source);
+ if (vol) {
+ virStorageVolFree(vol);
+ vol = NULL;
+ }
+
+ /* get volume source and target paths */
+ if (!(target = virXPathString("string(./target/@dev)", ctxt))) {
+ vshError(ctl, _("Failed to enumerate devices"));
+ goto cleanup;
+ }
+
+ if (!(source = virXPathString("string("
+ "./source/@file|"
+ "./source/@dir|"
+ "./source/@name|"
+ "./source/@dev)", ctxt)) &&
+ virGetLastError())
+ goto cleanup;
+
+ if (volumes) {
+ volume = strtok_r(volumes, ",", &saveptr);
+ while (volume) {
+ if (STREQ_NULLABLE(volume, target) ||
+ STREQ_NULLABLE(volume, source))
+ break;
+ volume = strtok_r(NULL, ",", &saveptr);
+ }
+ if (!volume)
+ continue;
+ }
+
+ if (!source)
+ continue;
+
+ if (!(vol = virStorageVolLookupByPath(ctl->conn, source))) {
+ vshPrint(ctl,
+ _("Storage volume '%s' is not managed by libvirt. "
+ "Remove it manually.\n"), source);
+ virResetLastError();
+ continue;
+ }
+
+ if (wipe_storage) {
+ vshPrint(ctl, _("Wiping volume '%s' ... "), source);
+ fflush(stdout);
+ if (virStorageVolWipe(vol, 0) < 0) {
+ vshError(ctl, _("Failed! Volume not removed."));
+ vol_del_failed = true;
+ continue;
+ } else {
+ vshPrint(ctl, _("Done.\n"));
+ }
+ }
+
+ /* delete the volume */
+ if (virStorageVolDelete(vol, 0) < 0) {
+ vshError(ctl, _("Failed to remove storage volume '%s'"),
+ source);
+ vol_del_failed = true;
+ }
+ vshPrint(ctl, _("Volume '%s' removed.\n"), source);
+ }
+
+ if (!vol_del_failed)
+ ret = true;
}
cleanup:
+ VIR_FREE(source);
+ VIR_FREE(target);
+ VIR_FREE(volumes);
+ VIR_FREE(def);
+ VIR_FREE(vol_nodes);
+ xmlFreeDoc(doc);
+ xmlXPathFreeContext(ctxt);
virDomainFree(dom);
return ret;
}
diff --git a/tools/virsh.pod b/tools/virsh.pod
index fe92714..c0b55bd 100644
--- a/tools/virsh.pod
+++ b/tools/virsh.pod
@@ -1179,6 +1179,7 @@ Output the device used for the TTY console of the domain. If the information
is not available the processes will provide an exit code of 1.
=item B<undefine> I<domain-id> [I<--managed-save>] [I<--snapshots-metadata>]
+[I<--storage> B<volumes> I<--wipe-storage> I<--remove-all-storage>]
Undefine a domain. If the domain is running, this converts it to a
transient domain, without stopping it. If the domain is inactive,
@@ -1194,6 +1195,23 @@ domain. Without the flag, attempts to undefine an inactive domain with
snapshot metadata will fail. If the domain is active, this flag is
ignored.
+The I<--storage> flag takes a parameter B<volumes>, that is a comma separated
+list of volume target names or source paths of storage volumes to be removed
+along with the undefined domain. Volumes can be undefined and thus removed only
+on inactive domains. Volume deletion is only attempted after the domain is
+undefined; if not all of the requested volumes could be deleted, the the
+error message indicates what still remains behind. If a volume path is not
+found in the domain definition, it's treated as if the volume was successfuly
+deleteted.
+(See B<domblklist> for list of target names associated to a domain).
+Example: --storage vda,vdb,vdc
+
+The I<--remove-all-storage> flag specifies, that all domain's storage volumes
+should be deleted.
+
+The flag I<--wipe-storage> specifies that the storage volumes should be
+wiped before removal.
+
NOTE: For an inactive domain, the domain name or UUID must be used as the
I<domain-id>.
--
1.7.3.4
12 years, 11 months
[libvirt] [PATCH] virsh: Print error message if argument parsing fails for cmdNodesuspend
by Peter Krempa
If parsing of arguments failed, virsh did silently exit returning and
error state, but not specifying the possible problem.
* tools/virsh: cmdNodesuspend: - error handling added
---
tools/virsh.c | 12 +++++++++---
1 files changed, 9 insertions(+), 3 deletions(-)
diff --git a/tools/virsh.c b/tools/virsh.c
index d58b827..ebda248 100644
--- a/tools/virsh.c
+++ b/tools/virsh.c
@@ -5270,14 +5270,20 @@ cmdNodeSuspend(vshControl *ctl, const vshCmd *cmd)
if (!vshConnectionUsability(ctl, ctl->conn))
return false;
- if (vshCommandOptString(cmd, "target", &target) < 0)
+ if (vshCommandOptString(cmd, "target", &target) < 0) {
+ vshError(ctl, _("Invalid suspend target argument"));
return false;
+ }
- if (vshCommandOptLongLong(cmd, "duration", &duration) < 0)
+ if (vshCommandOptLongLong(cmd, "duration", &duration) < 0) {
+ vshError(ctl, _("Invalid suspend duration argument"));
return false;
+ }
- if (vshCommandOptUInt(cmd, "flags", &flags) < 0)
+ if (vshCommandOptUInt(cmd, "flags", &flags) < 0) {
+ vshError(ctl, _("Invalid suspend flags argument"));
return false;
+ }
if (STREQ(target, "mem"))
suspendTarget = VIR_NODE_SUSPEND_TARGET_MEM;
--
1.7.3.4
12 years, 11 months
[libvirt] [PATCH] docs: tweak 'virsh edit' wording
by Eric Blake
I was wondering why 'virsh edit' didn't support the same
'--inactive' option as 'virsh dumpxml'; reading the source
code showed that --inactive was already implied, and that
the only way to alter a running guest rather than affecting
next boot is by hot-plugging individual devices, or by
something complex like saving the guest and modifying the
save image.
* tools/virsh.pod (define, edit): Mention behavior when guest is
already running.
---
tools/virsh.pod | 8 +++++---
1 files changed, 5 insertions(+), 3 deletions(-)
diff --git a/tools/virsh.pod b/tools/virsh.pod
index fe92714..965b215 100644
--- a/tools/virsh.pod
+++ b/tools/virsh.pod
@@ -497,7 +497,8 @@ B<Example>
=item B<define> I<FILE>
Define a domain from an XML <file>. The domain definition is registered
-but not started.
+but not started. If domain is already running, the changes will take
+effect on the next boot.
=item B<destroy> I<domain-id>
@@ -717,11 +718,12 @@ specified, then the output will be escaped for use in XML.
=item B<edit> I<domain-id>
-Edit the XML configuration file for a domain.
+Edit the XML configuration file for a domain, which will affect the
+next boot of the guest.
This is equivalent to:
- virsh dumpxml domain > domain.xml
+ virsh dumpxml --inactive domain > domain.xml
vi domain.xml (or make changes with your other text editor)
virsh define domain.xml
--
1.7.7.4
12 years, 11 months
[libvirt] [PATCH] python: Fix export of virDomainSnapshotListChildrenNames
by Peter Krempa
Commit f2013c9dd1ce468b8620ee35c232a93ef7026fb0 added implementation of
virDomainSnapshotListChildrenNames override export, but registration of
the newly exported function was not added.
*python/libvirt-override.c: - register export of function
---
python/libvirt-override.c | 1 +
1 files changed, 1 insertions(+), 0 deletions(-)
diff --git a/python/libvirt-override.c b/python/libvirt-override.c
index 9e98918..a16a7d1 100644
--- a/python/libvirt-override.c
+++ b/python/libvirt-override.c
@@ -5106,6 +5106,7 @@ static PyMethodDef libvirtMethods[] = {
{(char *) "virConnectBaselineCPU", libvirt_virConnectBaselineCPU, METH_VARARGS, NULL},
{(char *) "virDomainGetJobInfo", libvirt_virDomainGetJobInfo, METH_VARARGS, NULL},
{(char *) "virDomainSnapshotListNames", libvirt_virDomainSnapshotListNames, METH_VARARGS, NULL},
+ {(char *) "virDomainSnapshotListChildrenNames", libvirt_virDomainSnapshotListChildrenNames, METH_VARARGS, NULL},
{(char *) "virDomainRevertToSnapshot", libvirt_virDomainRevertToSnapshot, METH_VARARGS, NULL},
{(char *) "virDomainGetBlockJobInfo", libvirt_virDomainGetBlockJobInfo, METH_VARARGS, NULL},
{(char *) "virDomainSetBlockIoTune", libvirt_virDomainSetBlockIoTune, METH_VARARGS, NULL},
--
1.7.3.4
12 years, 11 months