[libvirt] [test-API][PATCH v2] Add test case for start domain on nfs storage
by Wayne Sun
* This test is for start a domain with img file on nfs storage.
Under SElinux boolean virt_use_nfs on or off, combine with
setting the dynamic_ownership in /etc/libvirt/qemu.conf,
check whether the guest can be started or not. The nfs could
be root_squash or no_root_squash. SElinux should be enabled
and enforcing on host.
---
repos/sVirt/domain_nfs_start.py | 485 +++++++++++++++++++++++++++++++++++++++
1 files changed, 485 insertions(+), 0 deletions(-)
create mode 100644 repos/sVirt/__init__.py
create mode 100644 repos/sVirt/domain_nfs_start.py
diff --git a/repos/sVirt/__init__.py b/repos/sVirt/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/repos/sVirt/domain_nfs_start.py b/repos/sVirt/domain_nfs_start.py
new file mode 100644
index 0000000..e4bc867
--- /dev/null
+++ b/repos/sVirt/domain_nfs_start.py
@@ -0,0 +1,485 @@
+#!/usr/bin/env python
+"""This test is for start a guest with img file on nfs storage.
+ Under SElinux boolean virt_use_nfs on or off, combine with
+ setting the dynamic_ownership in /etc/libvirt/qemu.conf,
+ check whether the guest can be started or not. The nfs could
+ be root_squash or no_root_squash. SElinux should be enabled
+ and enforcing on host.
+ sVirt:domain_nfs_start
+ guestname
+ #GUESTNAME#
+ dynamic_ownership
+ enable|disable
+ virt_use_nfs
+ on|off
+ root_squash
+ yes|no
+"""
+
+__author__ = 'Wayne Sun: gsun(a)redhat.com'
+__date__ = 'Mon Sep 2, 2011'
+__version__ = '0.1.0'
+__credits__ = 'Copyright (C) 2011 Red Hat, Inc.'
+__all__ = ['domain_nfs_start']
+
+import os
+import re
+import sys
+
+QEMU_CONF = "/etc/libvirt/qemu.conf"
+
+def append_path(path):
+ """Append root path of package"""
+ if path not in sys.path:
+ sys.path.append(path)
+
+from lib import connectAPI
+from lib import domainAPI
+from utils.Python import utils
+from exception import LibvirtAPI
+from shutil import copy
+
+pwd = os.getcwd()
+result = re.search('(.*)libvirt-test-API', pwd)
+append_path(result.group(0))
+
+def return_close(conn, logger, ret):
+ """close hypervisor connection and return the given value"""
+ conn.close()
+ logger.info("closed hypervisor connection")
+ return ret
+
+def check_params(params):
+ """Verify inputing parameter dictionary"""
+ logger = params['logger']
+ keys = ['guestname', 'dynamic_ownership', 'virt_use_nfs', 'root_squash']
+ for key in keys:
+ if key not in params:
+ logger.error("%s is required" %key)
+ return 1
+ return 0
+
+def nfs_setup(util, root_squash, logger):
+ """setup nfs on localhost
+ """
+ logger.info("set nfs service")
+ if root_squash == "yes":
+ option = "root_squash"
+ elif root_squash == "no":
+ option = "no_root_squash"
+ else:
+ logger.error("wrong root_squash value")
+ return 1
+
+ cmd = "echo /tmp *\(rw,%s\) >> /etc/exports" % option
+ ret, out = util.exec_cmd(cmd, shell=True)
+ if ret:
+ logger.error("failed to config nfs export")
+ return 1
+
+ logger.info("restart nfs service")
+ cmd = "service nfs restart"
+ ret, out = util.exec_cmd(cmd, shell=True)
+ if ret:
+ logger.error("failed to restart nfs service")
+ return 1
+ else:
+ for i in range(len(out)):
+ logger.info(out[i])
+
+ return 0
+
+def prepare_env(util, d_ownership, virt_use_nfs, guestname, root_squash, \
+ disk_file, img_dir, logger):
+ """set virt_use_nfs SElinux boolean, configure
+ dynamic_ownership in /etc/libvirt/qemu.conf
+ """
+ logger.info("set virt_use_nfs selinux boolean")
+ cmd = "setsebool virt_use_nfs %s" % virt_use_nfs
+ ret, out = util.exec_cmd(cmd, shell=True)
+ if ret:
+ logger.error("failed to set virt_use_nfs SElinux boolean")
+ return 1
+
+ logger.info("set the dynamic ownership in %s as %s" % \
+ (QEMU_CONF, d_ownership))
+ if d_ownership == "enable":
+ option = 1
+ elif d_ownership == "disable":
+ option = 0
+ else:
+ logger.error("wrong dynamic_ownership value")
+ return 1
+
+ set_cmd = "echo dynamic_ownership = %s >> %s" % \
+ (option, QEMU_CONF)
+ ret, out = util.exec_cmd(set_cmd, shell=True)
+ if ret:
+ logger.error("failed to set dynamic ownership")
+ return 1
+
+ logger.info("restart libvirtd")
+ restart_cmd = "service libvirtd restart"
+ ret, out = util.exec_cmd(restart_cmd, shell=True)
+ if ret:
+ logger.error("failed to restart libvirtd")
+ for i in range(len(out)):
+ logger.info(out[i])
+ return 1
+ else:
+ for i in range(len(out)):
+ logger.info(out[i])
+
+ file_name = os.path.basename(disk_file)
+ filepath = "/tmp/%s" % file_name
+ if os.path.exists(filepath):
+ os.remove(filepath)
+
+ logger.info("copy %s img file to nfs path" % guestname)
+ copy(disk_file, "/tmp")
+
+ logger.info("set up nfs service on localhost")
+ ret = nfs_setup(util, root_squash, logger)
+ if ret:
+ return 1
+
+ logger.info("mount nfs to img dir path")
+ mount_cmd = "mount -o vers=3 127.0.0.1:/tmp %s" % img_dir
+ ret, out = util.exec_cmd(mount_cmd, shell=True)
+ if ret:
+ logger.error("Failed to mount the nfs path")
+ for i in range(len(out)):
+ logger.info(out[i])
+ return 1
+
+ return 0
+
+def domain_nfs_start(params):
+ """start domain with img on nfs"""
+ # Initiate and check parameters
+ params_check_result = check_params(params)
+ if params_check_result:
+ return 1
+
+ logger = params['logger']
+ guestname = params['guestname']
+ dynamic_ownership = params['dynamic_ownership']
+ virt_use_nfs = params['virt_use_nfs']
+ root_squash = params['root_squash']
+
+ util = utils.Utils()
+
+ # Connect to local hypervisor connection URI
+ uri = util.get_uri('127.0.0.1')
+ conn = connectAPI.ConnectAPI()
+ virconn = conn.open(uri)
+ domobj = domainAPI.DomainAPI(virconn)
+
+ logger.info("get the domain state")
+ try:
+ state = domobj.get_state(guestname)
+ logger.info("domain %s is %s" % (guestname, state))
+ except LibvirtAPI, e:
+ logger.error("API error message: %s, error code is %s" % \
+ (e.response()['message'], e.response()['code']))
+ logger.error("Error: fail to get domain %s state" % guestname)
+ return return_close(conn, logger, 1)
+
+ if state != "shutoff":
+ logger.info("shut down the domain %s" % guestname)
+ try:
+ domobj.destroy(guestname)
+ except LibvirtAPI, e:
+ logger.error("API error message: %s, error code is %s" % \
+ (e.response()['message'], e.response()['code']))
+ logger.error("Error: fail to destroy domain %s" % guestname)
+ return return_close(conn, logger, 1)
+
+ logger.info("get guest img file path")
+ try:
+ dom_xml = domobj.get_xml_desc(guestname)
+ disk_file = util.get_disk_path(dom_xml)
+ logger.info("%s disk file path is %s" % (guestname, disk_file))
+ img_dir = os.path.dirname(disk_file)
+ except LibvirtAPI, e:
+ logger.error("API error message: %s, error code is %s" % \
+ (e.response()['message'], e.response()['code']))
+ logger.error("Error: fail to get domain %s xml" % guestname)
+ return return_close(conn, logger, 1)
+
+ # close connection before restart libvirtd
+ conn.close()
+
+ # set env
+ logger.info("prepare the environment")
+ ret = prepare_env(util, dynamic_ownership, virt_use_nfs, guestname,\
+ root_squash, disk_file, img_dir, logger)
+ if ret:
+ logger.error("failed to prepare the environment")
+ return return_close(conn, logger, 1)
+
+ # reconnect
+ conn = connectAPI.ConnectAPI()
+ virconn = conn.open(uri)
+ domobj = domainAPI.DomainAPI(virconn)
+
+ logger.info("begin to test start domain from nfs storage")
+ logger.info("First, start the domain without chown the img file to qemu")
+ logger.info("start domain %s" % guestname)
+ if root_squash == "yes":
+ if virt_use_nfs == "on":
+ if dynamic_ownership == "enable":
+ try:
+ domobj.start(guestname)
+ logger.error("Domain %s started, this is not expected" % \
+ guestname)
+ return return_close(conn, logger, 1)
+ except LibvirtAPI, e:
+ logger.info("API error message: %s, error code is %s" % \
+ (e.response()['message'], e.response()['code']))
+ logger.info("Fail to start domain %s, this is expected" % \
+ guestname)
+
+ elif dynamic_ownership == "disable":
+ try:
+ domobj.start(guestname)
+ logger.error("Domain %s started, this is not expected" % \
+ guestname)
+ return return_close(conn, logger, 1)
+ except LibvirtAPI, e:
+ logger.info("API error message: %s, error code is %s" % \
+ (e.response()['message'], e.response()['code']))
+ logger.info("Fail to start domain %s, this is expected" % \
+ guestname)
+ elif virt_use_nfs == "off":
+ if dynamic_ownership == "enable":
+ try:
+ domobj.start(guestname)
+ logger.error("Domain %s started, this is not expected" % \
+ guestname)
+ return return_close(conn, logger, 1)
+ except LibvirtAPI, e:
+ logger.info("API error message: %s, error code is %s" % \
+ (e.response()['message'], e.response()['code']))
+ logger.info("Fail to start domain %s, this is expected" % \
+ guestname)
+
+ elif dynamic_ownership == "disable":
+ try:
+ domobj.start(guestname)
+ logger.error("Domain %s started, this is not expected" % \
+ guestname)
+ return return_close(conn, logger, 1)
+ except LibvirtAPI, e:
+ logger.info("API error message: %s, error code is %s" % \
+ (e.response()['message'], e.response()['code']))
+ logger.info("Fail to start domain %s, this is expected" % \
+ guestname)
+ elif root_squash == "no":
+ if virt_use_nfs == "on":
+ if dynamic_ownership == "enable":
+ try:
+ domobj.start(guestname)
+ logger.info("Success start domain %s" % guestname)
+ except LibvirtAPI, e:
+ logger.error("API error message: %s, error code is %s" % \
+ (e.response()['message'], e.response()['code']))
+ logger.error("Fail to start domain %s" % guestname)
+ return return_close(conn, logger, 1)
+
+ elif dynamic_ownership == "disable":
+ try:
+ domobj.start(guestname)
+ logger.error("Domain %s started, this is not expected" % \
+ guestname)
+ return return_close(conn, logger, 1)
+ except LibvirtAPI, e:
+ logger.info("API error message: %s, error code is %s" % \
+ (e.response()['message'], e.response()['code']))
+ logger.info("Fail to start domain %s, this is expected" % \
+ guestname)
+ elif virt_use_nfs == "off":
+ if dynamic_ownership == "enable":
+ try:
+ domobj.start(guestname)
+ logger.error("Domain %s started, this is not expected" % \
+ guestname)
+ return return_close(conn, logger, 1)
+ except LibvirtAPI, e:
+ logger.info("API error message: %s, error code is %s" % \
+ (e.response()['message'], e.response()['code']))
+ logger.info("Fail to start domain %s, this is expected" % \
+ guestname)
+
+ elif dynamic_ownership == "disable":
+ try:
+ domobj.start(guestname)
+ logger.error("Domain %s started, this is not expected" % \
+ guestname)
+ return return_close(conn, logger, 1)
+ except LibvirtAPI, e:
+ logger.info("API error message: %s, error code is %s" % \
+ (e.response()['message'], e.response()['code']))
+ logger.info("Fail to start domain %s, this is expected" % \
+ guestname)
+
+ logger.info("get the domain state")
+ try:
+ state = domobj.get_state(guestname)
+ logger.info("domain %s is %s" % (guestname, state))
+ except LibvirtAPI, e:
+ logger.error("API error message: %s, error code is %s" % \
+ (e.response()['message'], e.response()['code']))
+ logger.error("Error: fail to get domain %s state" % guestname)
+ return return_close(conn, logger, 1)
+
+ if state != "shutoff":
+ logger.info("shut down the domain %s" % guestname)
+ try:
+ domobj.destroy(guestname)
+ except LibvirtAPI, e:
+ logger.error("API error message: %s, error code is %s" % \
+ (e.response()['message'], e.response()['code']))
+ logger.error("Error: fail to destroy domain %s" % guestname)
+ return return_close(conn, logger, 1)
+
+ logger.info("Second, start the domain after chown the img file to qemu")
+
+ file_name = os.path.basename(disk_file)
+ filepath = "/tmp/%s" % file_name
+ logger.info("set chown of %s as 107:107" % filepath)
+ chown_cmd = "chown 107:107 %s" % filepath
+ ret, out = util.exec_cmd(chown_cmd, shell=True)
+ if ret:
+ logger.error("failed to chown %s to qemu:qemu" % filepath)
+ return return_close(conn, logger, 1)
+
+ logger.info("start domain %s" % guestname)
+ if root_squash == "yes":
+ if virt_use_nfs == "on":
+ if dynamic_ownership == "enable":
+ try:
+ domobj.start(guestname)
+ logger.info("Success start domain %s" % guestname)
+ except LibvirtAPI, e:
+ logger.error("API error message: %s, error code is %s" % \
+ (e.response()['message'], e.response()['code']))
+ logger.error("Fail to start domain %s" % guestname)
+ return return_close(conn, logger, 1)
+
+ elif dynamic_ownership == "disable":
+ try:
+ domobj.start(guestname)
+ logger.info("Success start domain %s" % guestname)
+ except LibvirtAPI, e:
+ logger.error("API error message: %s, error code is %s" % \
+ (e.response()['message'], e.response()['code']))
+ logger.error("Fail to start domain %s" % guestname)
+ return return_close(conn, logger, 1)
+
+ elif virt_use_nfs == "off":
+ if dynamic_ownership == "enable":
+ try:
+ domobj.start(guestname)
+ logger.error("Domain %s started, this is not expected" % \
+ guestname)
+ return return_close(conn, logger, 1)
+ except LibvirtAPI, e:
+ logger.info("API error message: %s, error code is %s" % \
+ (e.response()['message'], e.response()['code']))
+ logger.info("Fail to start domain %s, this is expected" % \
+ guestname)
+
+ elif dynamic_ownership == "disable":
+ try:
+ domobj.start(guestname)
+ logger.error("Domain %s started, this is not expected" % \
+ guestname)
+ return return_close(conn, logger, 1)
+ except LibvirtAPI, e:
+ logger.info("API error message: %s, error code is %s" % \
+ (e.response()['message'], e.response()['code']))
+ logger.info("Fail to start domain %s, this is expected" % \
+ guestname)
+ elif root_squash == "no":
+ if virt_use_nfs == "on":
+ if dynamic_ownership == "enable":
+ try:
+ domobj.start(guestname)
+ logger.info("Success start domain %s" % guestname)
+ except LibvirtAPI, e:
+ logger.error("API error message: %s, error code is %s" % \
+ (e.response()['message'], e.response()['code']))
+ logger.error("Fail to start domain %s" % guestname)
+ return return_close(conn, logger, 1)
+
+ elif dynamic_ownership == "disable":
+ try:
+ domobj.start(guestname)
+ logger.info("Success start Domain %s" % guestname)
+ except LibvirtAPI, e:
+ logger.error("API error message: %s, error code is %s" % \
+ (e.response()['message'], e.response()['code']))
+ logger.error("Fail to start domain %s" % guestname)
+ return return_close(conn, logger, 1)
+
+ elif virt_use_nfs == "off":
+ if dynamic_ownership == "enable":
+ try:
+ domobj.start(guestname)
+ logger.error("Domain %s started, this is not expected" % \
+ guestname)
+ return return_close(conn, logger, 1)
+ except LibvirtAPI, e:
+ logger.info("API error message: %s, error code is %s" % \
+ (e.response()['message'], e.response()['code']))
+ logger.info("Fail to start domain %s, this is expected" % \
+ guestname)
+
+ elif dynamic_ownership == "disable":
+ try:
+ domobj.start(guestname)
+ logger.error("Domain %s started, this is not expected" % \
+ guestname)
+ return return_close(conn, logger, 1)
+ except LibvirtAPI, e:
+ logger.info("API error message: %s, error code is %s" % \
+ (e.response()['message'], e.response()['code']))
+ logger.info("Fail to start domain %s, this is expected" % \
+ guestname)
+
+ return return_close(conn, logger, 0)
+
+def domain_nfs_start_clean(params):
+ """clean testing environment"""
+ logger = params['logger']
+ guestname = params['guestname']
+
+ util = utils.Utils()
+
+ # Connect to local hypervisor connection URI
+ uri = util.get_uri('127.0.0.1')
+ conn = connectAPI.ConnectAPI()
+ virconn = conn.open(uri)
+ domobj = domainAPI.DomainAPI(virconn)
+
+ if domobj.get_state(guestname) != "shutoff":
+ domobj.destroy(guestname)
+
+ dom_xml = domobj.get_xml_desc(guestname)
+ disk_file = util.get_disk_path(dom_xml)
+ img_dir = os.path.dirname(disk_file)
+ file_name = os.path.basename(disk_file)
+ temp_file = "/tmp/%s" % file_name
+
+ if os.path.ismount(img_dir):
+ umount_cmd = "umount -f %s" % img_dir
+ ret, out = util.exec_cmd(umount_cmd, shell=True)
+ if ret:
+ logger.error("failed to umount %s" % img_dir)
+
+ if os.path.exists(temp_file):
+ os.remove(temp_file)
+
+ conn.close()
+
--
1.7.1
13 years, 2 months
[libvirt] [test-API][PATCH v2 1/4] Add init module under libvirtd
by Wayne Sun
* repos/libvirtd/__init__.py
---
0 files changed, 0 insertions(+), 0 deletions(-)
create mode 100644 repos/libvirtd/__init__.py
diff --git a/repos/libvirtd/__init__.py b/repos/libvirtd/__init__.py
new file mode 100644
index 0000000..e69de29
--
1.7.1
13 years, 2 months
[libvirt] [PATCH] Fix build after commit 829bce17
by Jim Fehlig
Pushing under build-breaker rule.
---
src/qemu/qemu_driver.c | 4 +++-
1 files changed, 3 insertions(+), 1 deletions(-)
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index 0ecd257..d2626ff 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -8881,6 +8881,7 @@ qemuDomainMigrateGetMaxSpeed(virDomainPtr dom,
{
struct qemud_driver *driver = dom->conn->privateData;
virDomainObjPtr vm;
+ qemuDomainObjPrivatePtr priv;
int ret = -1;
virCheckFlags(0, -1);
@@ -8897,7 +8898,8 @@ qemuDomainMigrateGetMaxSpeed(virDomainPtr dom,
goto cleanup;
}
- *bandwidth = vm->privateData->migMaxBandwidth;
+ priv = vm->privateData;
+ *bandwidth = priv->migMaxBandwidth;
ret = 0;
cleanup:
--
1.7.5.4
13 years, 2 months
[libvirt] [PATCH 0/5] Implement virDomainMigrateGetMaxSpeed in qemu driver
by Jim Fehlig
This is a rebase of uncommitted patches in the series originally posted
here
https://www.redhat.com/archives/libvir-list/2011-August/msg01373.html
Jim Fehlig (5):
Store max migration bandwidth in qemuDomainObjPrivate struct
Impl virDomainMigrateGetMaxSpeed in qemu driver
Save migration speed in qemuDomainMigrateSetMaxSpeed
Set qemu migration speed unlimited when migrating to file
Use max bandwidth from qemuDomainObjPrivate struct when migrating
src/qemu/qemu_domain.c | 2 +
src/qemu/qemu_domain.h | 5 +++
src/qemu/qemu_driver.c | 65 +++++++++++++++++++++++++++++++-------------
src/qemu/qemu_migration.c | 21 +++++++++++++-
4 files changed, 72 insertions(+), 21 deletions(-)
--
1.7.5.4
13 years, 2 months
[libvirt] [PATCH] conf: add missing break in virDomainAuditRedirdev
by Marc-André Lureau
Also initialize to NULL a few variables that might get
free before being set.
---
src/conf/domain_audit.c | 9 +++++----
1 files changed, 5 insertions(+), 4 deletions(-)
diff --git a/src/conf/domain_audit.c b/src/conf/domain_audit.c
index ef0f47a..c9c6003 100644
--- a/src/conf/domain_audit.c
+++ b/src/conf/domain_audit.c
@@ -250,8 +250,8 @@ virDomainAuditHostdev(virDomainObjPtr vm, virDomainHostdevDefPtr hostdev,
{
char uuidstr[VIR_UUID_STRING_BUFLEN];
char *vmname;
- char *address;
- char *device;
+ char *address = NULL;
+ char *device = NULL;
const char *virt;
virUUIDFormat(vm->def->uuid, uuidstr);
@@ -323,8 +323,8 @@ virDomainAuditRedirdev(virDomainObjPtr vm, virDomainRedirdevDefPtr redirdev,
{
char uuidstr[VIR_UUID_STRING_BUFLEN];
char *vmname;
- char *address;
- char *device;
+ char *address = NULL;
+ char *device = NULL;
const char *virt;
virUUIDFormat(vm->def->uuid, uuidstr);
@@ -344,6 +344,7 @@ virDomainAuditRedirdev(virDomainObjPtr vm, virDomainRedirdevDefPtr redirdev,
VIR_WARN("OOM while encoding audit message");
goto cleanup;
}
+ break;
default:
VIR_WARN("Unexpected redirdev bus while encoding audit message: %d",
redirdev->bus);
--
1.7.6
13 years, 2 months
[libvirt] [PATCH] network: add missing exports
by Eric Blake
Commit c246b025 added new functions, but forgot to export them,
resulting in a build failure when using modules.
* src/libvirt_private.syms (network.h): Export new functions.
---
Pushing under the build-breaker rule.
src/libvirt_private.syms | 2 ++
1 files changed, 2 insertions(+), 0 deletions(-)
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index 0780f2d..e45965e 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -742,6 +742,7 @@ virBandwidthDefFree;
virBandwidthDefParseNode;
virBandwidthDisable;
virBandwidthEnable;
+virBandwidthEqual;
virSocketAddrBroadcast;
virSocketAddrBroadcastByPrefix;
virSocketAddrIsNetmask;
@@ -757,6 +758,7 @@ virSocketParseAddr;
virSocketParseIpv4Addr;
virSocketParseIpv6Addr;
virSocketSetPort;
+virVirtualPortProfileEqual;
virVirtualPortProfileFormat;
virVirtualPortProfileParseXML;
--
1.7.4.4
13 years, 2 months
[libvirt] [PATCH] snapshot: ABI stability must include memory sizing
by Eric Blake
Commit 973fcd8f introduced the ability for qemu to reject snapshot
reversion on an ABI incompatibility; but the very example that was
first proposed on-list[1] as a demonstration of an ABI incompatibility,
namely that of changing the max memory allocation, was not being
checked for, resulting in a cryptic failure when running with larger
max mem than what the snapshot was created with:
error: operation failed: Error -22 while loading VM state
This commit merely protects the three variables within mem that are
referenced by qemu_command.c, rather than all 7 (the other 4 variables
affect cgroup handling, but have no visible effect to the qemu guest).
[1] https://www.redhat.com/archives/libvir-list/2010-December/msg00331.html
* src/conf/domain_conf.c (virDomainDefCheckABIStability): Add
memory sizing checks.
---
See also https://bugzilla.redhat.com/show_bug.cgi?id=735553#c5
src/conf/domain_conf.c | 20 ++++++++++++++++++++
1 files changed, 20 insertions(+), 0 deletions(-)
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index 11755fe..996e0d5 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -8229,6 +8229,26 @@ bool virDomainDefCheckABIStability(virDomainDefPtr src,
goto cleanup;
}
+ if (src->mem.max_balloon != dst->mem.max_balloon) {
+ virDomainReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ _("Target domain max memory %ld does not match source %ld"),
+ dst->mem.max_balloon, src->mem.max_balloon);
+ goto cleanup;
+ }
+ if (src->mem.cur_balloon != dst->mem.cur_balloon) {
+ virDomainReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ _("Target domain current memory %ld does not match source %ld"),
+ dst->mem.cur_balloon, src->mem.cur_balloon);
+ goto cleanup;
+ }
+ if (src->mem.hugepage_backed != dst->mem.hugepage_backed) {
+ virDomainReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ _("Target domain huge page backing %ld does not match source %ld"),
+ dst->mem.hugepage_backed,
+ src->mem.hugepage_backed);
+ goto cleanup;
+ }
+
if (src->vcpus != dst->vcpus) {
virDomainReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("Target domain vpu count %d does not match source %d"),
--
1.7.4.4
13 years, 2 months
[libvirt] [PATCH v5] virsh: Increase device-detach intelligence
by Michal Privoznik
From: Michal Prívozník <mprivozn(a)redhat.com>
Up to now users have to give a full XML description on input when
device-detaching. If they omitted something it lead to unclear
error messages (like generated MAC wasn't found, etc.).
With this patch users can specify only those information which
specify one device sufficiently precise. Remaining information is
completed from domain.
---
diff to v4:
-Eric's review suggestions
tools/virsh.c | 284 +++++++++++++++++++++++++++++++++++++++++++++++++++++---
1 files changed, 268 insertions(+), 16 deletions(-)
diff --git a/tools/virsh.c b/tools/virsh.c
index 430168c..0caa360 100644
--- a/tools/virsh.c
+++ b/tools/virsh.c
@@ -59,6 +59,7 @@
#include "threads.h"
#include "command.h"
#include "virkeycode.h"
+#include "util/bitmap.h"
static char *progname;
@@ -10986,6 +10987,243 @@ cmdAttachDevice(vshControl *ctl, const vshCmd *cmd)
return true;
}
+/**
+ * Check if n1 is superset of n2, meaning n1 contains all elements and
+ * attributes as n2 at least. Including children.
+ * @n1 first node
+ * @n2 second node
+ * return 1 in case n1 covers n2, 0 otherwise.
+ */
+static bool
+vshNodeIsSuperset(xmlNodePtr n1, xmlNodePtr n2) {
+ xmlNodePtr child1, child2;
+ xmlAttrPtr attr;
+ char *prop1, *prop2;
+ bool found;
+ bool visited;
+ bool ret = false;
+ unsigned long n1_child_size, n1_iter;
+ virBitmapPtr bitmap;
+
+ if (!n1 && !n2)
+ return true;
+
+ if (!n1 || !n2)
+ return false;
+
+ if (!xmlStrEqual(n1->name, n2->name))
+ return false;
+
+ /* Iterate over n2 attributes and check if n1 contains them*/
+ attr = n2->properties;
+ while (attr) {
+ if (attr->type == XML_ATTRIBUTE_NODE) {
+ prop1 = virXMLPropString(n1, (const char *) attr->name);
+ prop2 = virXMLPropString(n2, (const char *) attr->name);
+ if (STRNEQ_NULLABLE(prop1, prop2)) {
+ xmlFree(prop1);
+ xmlFree(prop2);
+ return false;
+ }
+ xmlFree(prop1);
+ xmlFree(prop2);
+ }
+ attr = attr->next;
+ }
+
+ n1_child_size = xmlChildElementCount(n1);
+ if (!n1_child_size) {
+ return !xmlChildElementCount(n2);
+ }
+
+ if (!(bitmap = virBitmapAlloc(n1_child_size))) {
+ virReportOOMError();
+ return false;
+ }
+
+ child2 = n2->children;
+ while (child2) {
+ if (child2->type != XML_ELEMENT_NODE) {
+ child2 = child2->next;
+ continue;
+ }
+
+ child1 = n1->children;
+ n1_iter = 0;
+ found = false;
+ while (child1) {
+ if (child1->type != XML_ELEMENT_NODE) {
+ child1 = child1->next;
+ continue;
+ }
+
+ if (virBitmapGetBit(bitmap, n1_iter, &visited) < 0) {
+ vshError(NULL, "%s", _("Bad child elements counting."));
+ goto cleanup;
+ }
+
+ if (visited) {
+ child1 = child1->next;
+ n1_iter++;
+ continue;
+ }
+
+ if (xmlStrEqual(child1->name, child2->name)) {
+ found = true;
+ if (virBitmapSetBit(bitmap, n1_iter) < 0) {
+ vshError(NULL, "%s", _("Bad child elements counting."));
+ goto cleanup;
+ }
+
+ if (!vshNodeIsSuperset(child1, child2))
+ goto cleanup;
+
+ break;
+ }
+
+ child1 = child1->next;
+ n1_iter++;
+ }
+
+ if (!found)
+ goto cleanup;
+
+ child2 = child2->next;
+ }
+
+ ret = true;
+
+cleanup:
+ virBitmapFree(bitmap);
+ return ret;
+}
+
+/**
+ * vshCompleteXMLFromDomain:
+ * @ctl vshControl for error messages printing
+ * @dom domain
+ * @oldXML device XML before
+ * @newXML and after completion
+ *
+ * For given domain and (probably incomplete) device XML specification try to
+ * find such device in domain and complete missing parts. This is however
+ * possible only when given device XML is sufficiently precise so it addresses
+ * only one device.
+ *
+ * Returns -2 when no such device exists in domain, -3 when given XML selects many
+ * (is too ambiguous), 0 in case of success. Otherwise returns -1. @newXML
+ * is touched only in case of success.
+ */
+static int
+vshCompleteXMLFromDomain(vshControl *ctl, virDomainPtr dom, char *oldXML,
+ char **newXML) {
+ int funcRet = -1;
+ char *domXML = NULL;
+ xmlDocPtr domDoc = NULL, devDoc = NULL;
+ xmlNodePtr node = NULL;
+ xmlXPathContextPtr domCtxt = NULL, devCtxt = NULL;
+ xmlNodePtr *devices = NULL;
+ xmlSaveCtxtPtr sctxt = NULL;
+ int devices_size;
+ char *xpath;
+ xmlBufferPtr buf = NULL;
+ int i = 0;
+ int indx = -1;
+
+ if (!(domXML = virDomainGetXMLDesc(dom, 0))) {
+ vshError(ctl, _("couldn't get XML description of domain %s"),
+ virDomainGetName(dom));
+ goto cleanup;
+ }
+
+ domDoc = virXMLParseStringCtxt(domXML, _("(domain_definition)"), &domCtxt);
+ if (!domDoc) {
+ vshError(ctl, _("Failed to parse domain definition xml"));
+ goto cleanup;
+ }
+
+ devDoc = virXMLParseStringCtxt(oldXML, _("(device_definition)"), &devCtxt);
+ if (!devDoc) {
+ vshError(ctl, _("Failed to parse device definition xml"));
+ goto cleanup;
+ }
+
+ node = xmlDocGetRootElement(devDoc);
+
+ buf = xmlBufferCreate();
+ if (!buf) {
+ vshError(ctl, "%s", _("out of memory"));
+ goto cleanup;
+ }
+
+ /* Get all possible devices */
+ virAsprintf(xpath, "/domain/devices/%s", node->name);
+ if (!xpath) {
+ virReportOOMError();
+ goto cleanup;
+ }
+ devices_size = virXPathNodeSet(xpath, domCtxt, &devices);
+
+ if (devices_size < 0) {
+ /* error */
+ vshError(ctl, "%s", _("error when selecting nodes"));
+ goto cleanup;
+ } else if (devices_size == 0) {
+ /* no such device */
+ funcRet = -2;
+ goto cleanup;
+ }
+
+ /* and refine */
+ for (i = 0; i < devices_size; i++) {
+ if (vshNodeIsSuperset(devices[i], node)) {
+ if (indx >= 0) {
+ funcRet = -3; /* ambiguous */
+ goto cleanup;
+ }
+ indx = i;
+ }
+ }
+
+ if (indx < 0) {
+ funcRet = -2; /* no such device */
+ goto cleanup;
+ }
+
+ vshDebug(ctl, VSH_ERR_DEBUG, "Found device at pos %d\n", indx);
+
+ if (newXML) {
+ sctxt = xmlSaveToBuffer(buf, NULL, 0);
+ if (!sctxt) {
+ vshError(ctl, "%s", _("failed to create document saving context"));
+ goto cleanup;
+ }
+
+ xmlSaveTree(sctxt, devices[indx]);
+ xmlSaveClose(sctxt);
+ *newXML = (char *) xmlBufferContent(buf);
+ if (!*newXML) {
+ virReportOOMError();
+ goto cleanup;
+ }
+ buf->content = NULL;
+ }
+
+ vshDebug(ctl, VSH_ERR_DEBUG, "Old xml:\n%s\nNew xml:\n%s\n", oldXML,
+ newXML ? NULLSTR(*newXML) : "(null)");
+
+ funcRet = 0;
+
+cleanup:
+ xmlBufferFree(buf);
+ VIR_FREE(devices);
+ xmlXPathFreeContext(devCtxt);
+ xmlXPathFreeContext(domCtxt);
+ xmlFreeDoc(devDoc);
+ xmlFreeDoc(domDoc);
+ VIR_FREE(domXML);
+ return funcRet;
+}
/*
* "detach-device" command
@@ -11006,10 +11244,11 @@ static const vshCmdOptDef opts_detach_device[] = {
static bool
cmdDetachDevice(vshControl *ctl, const vshCmd *cmd)
{
- virDomainPtr dom;
+ virDomainPtr dom = NULL;
const char *from = NULL;
- char *buffer;
+ char *buffer = NULL, *new_buffer = NULL;
int ret;
+ bool funcRet = false;
unsigned int flags;
if (!vshConnectionUsability(ctl, ctl->conn))
@@ -11018,37 +11257,50 @@ cmdDetachDevice(vshControl *ctl, const vshCmd *cmd)
if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
return false;
- if (vshCommandOptString(cmd, "file", &from) <= 0) {
- virDomainFree(dom);
- return false;
- }
+ if (vshCommandOptString(cmd, "file", &from) <= 0)
+ goto cleanup;
if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0) {
virshReportError(ctl);
- virDomainFree(dom);
- return false;
+ goto cleanup;
+ }
+
+ ret = vshCompleteXMLFromDomain(ctl, dom, buffer, &new_buffer);
+ if (ret < 0) {
+ if (ret == -2) {
+ vshError(ctl, _("no such device in %s"), virDomainGetName(dom));
+ } else if (ret == -3) {
+ vshError(ctl, "%s", _("given XML selects too many devices. "
+ "Please, be more specific"));
+ } else {
+ /* vshCompleteXMLFromDomain() already printed error message,
+ * so nothing to do here. */
+ }
+ goto cleanup;
}
if (vshCommandOptBool(cmd, "persistent")) {
flags = VIR_DOMAIN_AFFECT_CONFIG;
if (virDomainIsActive(dom) == 1)
flags |= VIR_DOMAIN_AFFECT_LIVE;
- ret = virDomainDetachDeviceFlags(dom, buffer, flags);
+ ret = virDomainDetachDeviceFlags(dom, new_buffer, flags);
} else {
- ret = virDomainDetachDevice(dom, buffer);
+ ret = virDomainDetachDevice(dom, new_buffer);
}
- VIR_FREE(buffer);
if (ret < 0) {
vshError(ctl, _("Failed to detach device from %s"), from);
- virDomainFree(dom);
- return false;
- } else {
- vshPrint(ctl, "%s", _("Device detached successfully\n"));
+ goto cleanup;
}
+ vshPrint(ctl, "%s", _("Device detached successfully\n"));
+ funcRet = true;
+
+cleanup:
+ VIR_FREE(new_buffer);
+ VIR_FREE(buffer);
virDomainFree(dom);
- return true;
+ return funcRet;
}
--
1.7.3.4
13 years, 2 months
[libvirt] [PATCH v2] xml: Clean up rest of virtual XML document names for XML strings
by Peter Krempa
Commit 498d783 cleans up some of virtual file names for parsing strings
in memory. This patch cleans up (hopefuly) the rest forgotten by the
first patch.
This patch also changes all of the previously modified "filenames" to
valid URI's replacing spaces for underscores.
Changes to v1:
- Replace all spaces for underscores, so that the strings form valid
URI's
- Replace spaces in places changed by commit 498d783
---
src/conf/domain_conf.c | 6 +++---
src/conf/interface_conf.c | 2 +-
src/conf/network_conf.c | 2 +-
src/conf/node_device_conf.c | 2 +-
src/conf/nwfilter_conf.c | 2 +-
src/conf/secret_conf.c | 2 +-
src/conf/storage_conf.c | 6 +++---
src/cpu/cpu.c | 4 ++--
src/esx/esx_vi.c | 2 +-
src/qemu/qemu_migration.c | 2 +-
src/security/virt-aa-helper.c | 2 +-
tools/virsh.c | 20 ++++++++++----------
12 files changed, 26 insertions(+), 26 deletions(-)
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index 11755fe..c4fbdd5 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -5739,7 +5739,7 @@ virDomainDeviceDefPtr virDomainDeviceDefParse(virCapsPtr caps,
xmlXPathContextPtr ctxt = NULL;
virDomainDeviceDefPtr dev = NULL;
- if (!(xml = virXMLParseStringCtxt(xmlStr, "device.xml", &ctxt))) {
+ if (!(xml = virXMLParseStringCtxt(xmlStr, _("(device_definition)"), &ctxt))) {
goto error;
}
node = ctxt->node;
@@ -7461,7 +7461,7 @@ virDomainDefParse(const char *xmlStr,
xmlDocPtr xml;
virDomainDefPtr def = NULL;
- if ((xml = virXMLParse(filename, xmlStr, _("(domain definition)")))) {
+ if ((xml = virXMLParse(filename, xmlStr, _("(domain_definition)")))) {
def = virDomainDefParseNode(caps, xml, xmlDocGetRootElement(xml),
expectedVirtTypes, flags);
xmlFreeDoc(xml);
@@ -11573,7 +11573,7 @@ virDomainSnapshotDefParseString(const char *xmlStr,
int active;
char *tmp;
- xml = virXMLParseCtxt(NULL, xmlStr, "domainsnapshot.xml", &ctxt);
+ xml = virXMLParseCtxt(NULL, xmlStr, _("(domain_snapshot)"), &ctxt);
if (!xml) {
return NULL;
}
diff --git a/src/conf/interface_conf.c b/src/conf/interface_conf.c
index 10377e7..fd8d1ae 100644
--- a/src/conf/interface_conf.c
+++ b/src/conf/interface_conf.c
@@ -864,7 +864,7 @@ virInterfaceDefParse(const char *xmlStr,
xmlDocPtr xml;
virInterfaceDefPtr def = NULL;
- if ((xml = virXMLParse(filename, xmlStr, "interface.xml"))) {
+ if ((xml = virXMLParse(filename, xmlStr, _("(interface_definition)")))) {
def = virInterfaceDefParseNode(xml, xmlDocGetRootElement(xml));
xmlFreeDoc(xml);
}
diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c
index e055094..b98ffad 100644
--- a/src/conf/network_conf.c
+++ b/src/conf/network_conf.c
@@ -1084,7 +1084,7 @@ virNetworkDefParse(const char *xmlStr,
xmlDocPtr xml;
virNetworkDefPtr def = NULL;
- if ((xml = virXMLParse(filename, xmlStr, "network.xml"))) {
+ if ((xml = virXMLParse(filename, xmlStr, _("(network_definition)")))) {
def = virNetworkDefParseNode(xml, xmlDocGetRootElement(xml));
xmlFreeDoc(xml);
}
diff --git a/src/conf/node_device_conf.c b/src/conf/node_device_conf.c
index 6b0ef50..fc284e0 100644
--- a/src/conf/node_device_conf.c
+++ b/src/conf/node_device_conf.c
@@ -1231,7 +1231,7 @@ virNodeDeviceDefParse(const char *str,
xmlDocPtr xml;
virNodeDeviceDefPtr def = NULL;
- if ((xml = virXMLParse(filename, str, "device.xml"))) {
+ if ((xml = virXMLParse(filename, str, _("(node_device_definition)")))) {
def = virNodeDeviceDefParseNode(xml, xmlDocGetRootElement(xml), create);
xmlFreeDoc(xml);
}
diff --git a/src/conf/nwfilter_conf.c b/src/conf/nwfilter_conf.c
index 04bfa22..08ede48 100644
--- a/src/conf/nwfilter_conf.c
+++ b/src/conf/nwfilter_conf.c
@@ -2119,7 +2119,7 @@ virNWFilterDefParse(virConnectPtr conn ATTRIBUTE_UNUSED,
virNWFilterDefPtr def = NULL;
xmlDocPtr xml;
- if ((xml = virXMLParse(filename, xmlStr, "nwfilter.xml"))) {
+ if ((xml = virXMLParse(filename, xmlStr, _("(nwfilter_definition")))) {
def = virNWFilterDefParseNode(xml, xmlDocGetRootElement(xml));
xmlFreeDoc(xml);
}
diff --git a/src/conf/secret_conf.c b/src/conf/secret_conf.c
index 105afbe..b33ce98 100644
--- a/src/conf/secret_conf.c
+++ b/src/conf/secret_conf.c
@@ -195,7 +195,7 @@ virSecretDefParse(const char *xmlStr,
xmlDocPtr xml;
virSecretDefPtr ret = NULL;
- if ((xml = virXMLParse(filename, xmlStr, "secret.xml"))) {
+ if ((xml = virXMLParse(filename, xmlStr, _("(definition_of_secret)")))) {
ret = secretXMLParseNode(xml, xmlDocGetRootElement(xml));
xmlFreeDoc(xml);
}
diff --git a/src/conf/storage_conf.c b/src/conf/storage_conf.c
index 1e7da69..e893b2d 100644
--- a/src/conf/storage_conf.c
+++ b/src/conf/storage_conf.c
@@ -505,7 +505,7 @@ virStoragePoolDefParseSourceString(const char *srcSpec,
xmlXPathContextPtr xpath_ctxt = NULL;
virStoragePoolSourcePtr def = NULL, ret = NULL;
- if (!(doc = virXMLParseStringCtxt(srcSpec, "srcSpec.xml", &xpath_ctxt))) {
+ if (!(doc = virXMLParseStringCtxt(srcSpec, _("(storage_source_specification)"), &xpath_ctxt))) {
goto cleanup;
}
@@ -765,7 +765,7 @@ virStoragePoolDefParse(const char *xmlStr,
virStoragePoolDefPtr ret = NULL;
xmlDocPtr xml;
- if ((xml = virXMLParse(filename, xmlStr, "storage.xml"))) {
+ if ((xml = virXMLParse(filename, xmlStr, _("(storage_pool_definition)")))) {
ret = virStoragePoolDefParseNode(xml, xmlDocGetRootElement(xml));
xmlFreeDoc(xml);
}
@@ -1146,7 +1146,7 @@ virStorageVolDefParse(virStoragePoolDefPtr pool,
virStorageVolDefPtr ret = NULL;
xmlDocPtr xml;
- if ((xml = virXMLParse(filename, xmlStr, "storage.xml"))) {
+ if ((xml = virXMLParse(filename, xmlStr, _("(storage_volume_definition)")))) {
ret = virStorageVolDefParseNode(pool, xml, xmlDocGetRootElement(xml));
xmlFreeDoc(xml);
}
diff --git a/src/cpu/cpu.c b/src/cpu/cpu.c
index b47f078..2906be9 100644
--- a/src/cpu/cpu.c
+++ b/src/cpu/cpu.c
@@ -76,7 +76,7 @@ cpuCompareXML(virCPUDefPtr host,
VIR_DEBUG("host=%p, xml=%s", host, NULLSTR(xml));
- if (!(doc = virXMLParseStringCtxt(xml, "cpu.xml", &ctxt)))
+ if (!(doc = virXMLParseStringCtxt(xml, _("(CPU_definition)"), &ctxt)))
goto cleanup;
cpu = virCPUDefParseXML(ctxt->node, ctxt, VIR_CPU_TYPE_AUTO);
@@ -304,7 +304,7 @@ cpuBaselineXML(const char **xmlCPUs,
goto no_memory;
for (i = 0; i < ncpus; i++) {
- if (!(doc = virXMLParseStringCtxt(xmlCPUs[i], "cpu.xml", &ctxt)))
+ if (!(doc = virXMLParseStringCtxt(xmlCPUs[i], _("(CPU_definition)"), &ctxt)))
goto error;
cpus[i] = virCPUDefParseXML(ctxt->node, ctxt, VIR_CPU_TYPE_HOST);
diff --git a/src/esx/esx_vi.c b/src/esx/esx_vi.c
index f4033eb..2d9890c 100644
--- a/src/esx/esx_vi.c
+++ b/src/esx/esx_vi.c
@@ -911,7 +911,7 @@ esxVI_Context_Execute(esxVI_Context *ctx, const char *methodName,
if ((*response)->responseCode == 500 || (*response)->responseCode == 200) {
(*response)->document = virXMLParseStringCtxt((*response)->content,
- "esx.xml",
+ _("(esx execute response)"),
&xpathContext);
if ((*response)->document == NULL) {
diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c
index f849d05..7fd4192 100644
--- a/src/qemu/qemu_migration.c
+++ b/src/qemu/qemu_migration.c
@@ -603,7 +603,7 @@ qemuMigrationCookieXMLParseStr(qemuMigrationCookiePtr mig,
VIR_DEBUG("xml=%s", NULLSTR(xml));
- if (!(doc = virXMLParseStringCtxt(xml, "qemumigration.xml", &ctxt)))
+ if (!(doc = virXMLParseStringCtxt(xml, _("(qemu_migration_cookie)"), &ctxt)))
goto cleanup;
ret = qemuMigrationCookieXMLParse(mig, ctxt, flags);
diff --git a/src/security/virt-aa-helper.c b/src/security/virt-aa-helper.c
index b9e551a..66608f8 100644
--- a/src/security/virt-aa-helper.c
+++ b/src/security/virt-aa-helper.c
@@ -641,7 +641,7 @@ caps_mockup(vahControl * ctl, const char *xmlStr)
xmlDocPtr xml = NULL;
xmlXPathContextPtr ctxt = NULL;
- if (!(xml = virXMLParseStringCtxt(xmlStr, _("(domain definition)"),
+ if (!(xml = virXMLParseStringCtxt(xmlStr, _("(domain_definition)"),
&ctxt))) {
goto cleanup;
}
diff --git a/tools/virsh.c b/tools/virsh.c
index 430168c..3c6e65a 100644
--- a/tools/virsh.c
+++ b/tools/virsh.c
@@ -1307,7 +1307,7 @@ cmdDomIfSetLink (vshControl *ctl, const vshCmd *cmd)
flags = VIR_DOMAIN_AFFECT_CONFIG;
/* extract current network device description */
- xml = virXMLParseStringCtxt(desc, _("(domain definition)"), &ctxt);
+ xml = virXMLParseStringCtxt(desc, _("(domain_definition)"), &ctxt);
VIR_FREE(desc);
if (!xml) {
vshError(ctl, _("Failed to parse domain description xml"));
@@ -1454,7 +1454,7 @@ cmdDomIfGetLink (vshControl *ctl, const vshCmd *cmd)
goto cleanup;
}
- xml = virXMLParseStringCtxt(desc, _("(domain definition)"), &ctxt);
+ xml = virXMLParseStringCtxt(desc, _("(domain_definition)"), &ctxt);
VIR_FREE(desc);
if (!xml) {
vshError(ctl, _("Failed to parse domain description xml"));
@@ -1663,7 +1663,7 @@ cmdDomblklist(vshControl *ctl, const vshCmd *cmd)
if (!xml)
goto cleanup;
- xmldoc = virXMLParseStringCtxt(xml, _("(domain definition)"), &ctxt);
+ xmldoc = virXMLParseStringCtxt(xml, _("(domain_definition)"), &ctxt);
if (!xmldoc)
goto cleanup;
@@ -9042,7 +9042,7 @@ makeCloneXML(const char *origxml, const char *newname)
xmlChar *newxml = NULL;
int size;
- doc = virXMLParseStringCtxt(origxml, _("(volume definition)"), &ctxt);
+ doc = virXMLParseStringCtxt(origxml, _("(volume_definition)"), &ctxt);
if (!doc)
goto cleanup;
@@ -10833,7 +10833,7 @@ cmdVNCDisplay(vshControl *ctl, const vshCmd *cmd)
if (!doc)
goto cleanup;
- xml = virXMLParseStringCtxt(doc, _("(domain definition)"), &ctxt);
+ xml = virXMLParseStringCtxt(doc, _("(domain_definition)"), &ctxt);
VIR_FREE(doc);
if (!xml)
goto cleanup;
@@ -10901,7 +10901,7 @@ cmdTTYConsole(vshControl *ctl, const vshCmd *cmd)
if (!doc)
goto cleanup;
- xml = virXMLParseStringCtxt(doc, _("(domain definition)"), &ctxt);
+ xml = virXMLParseStringCtxt(doc, _("(domain_definition)"), &ctxt);
VIR_FREE(doc);
if (!xml)
goto cleanup;
@@ -11288,7 +11288,7 @@ cmdDetachInterface(vshControl *ctl, const vshCmd *cmd)
if (!doc)
goto cleanup;
- xml = virXMLParseStringCtxt(doc, _("(domain definition)"), &ctxt);
+ xml = virXMLParseStringCtxt(doc, _("(domain_definition)"), &ctxt);
VIR_FREE(doc);
if (!xml) {
vshError(ctl, "%s", _("Failed to get interface information"));
@@ -11755,7 +11755,7 @@ cmdDetachDisk(vshControl *ctl, const vshCmd *cmd)
if (!doc)
goto cleanup;
- xml = virXMLParseStringCtxt(doc, _("(domain definition)"), &ctxt);
+ xml = virXMLParseStringCtxt(doc, _("(domain_definition)"), &ctxt);
VIR_FREE(doc);
if (!xml) {
vshError(ctl, "%s", _("Failed to get disk information"));
@@ -13025,7 +13025,7 @@ cmdSnapshotList(vshControl *ctl, const vshCmd *cmd)
if (!doc)
continue;
- xml = virXMLParseStringCtxt(doc, _("(domain snapshot)"), &ctxt);
+ xml = virXMLParseStringCtxt(doc, _("(domain_snapshot)"), &ctxt);
if (!xml)
continue;
@@ -13186,7 +13186,7 @@ cmdSnapshotParent(vshControl *ctl, const vshCmd *cmd)
if (!xml)
goto cleanup;
- xmldoc = virXMLParseStringCtxt(xml, _("(domain snapshot)"), &ctxt);
+ xmldoc = virXMLParseStringCtxt(xml, _("(domain_snapshot)"), &ctxt);
if (!xmldoc)
goto cleanup;
--
1.7.3.4
13 years, 2 months
[libvirt] [PATCH v4] virsh: Increase device-detach intelligence
by Michal Privoznik
From: Michal Prívozník <mprivozn(a)redhat.com>
Up to now users have to give a full XML description on input when
device-detaching. If they omitted something it lead to unclear
error messages (like generated MAC wasn't found, etc.).
With this patch users can specify only those information which
specify one device sufficiently precise. Remaining information is
completed from domain.
---
diff to v3:
-change the superset search
-tweak little snippets acording to Eric's review
tools/virsh.c | 278 +++++++++++++++++++++++++++++++++++++++++++++++++++++----
1 files changed, 262 insertions(+), 16 deletions(-)
diff --git a/tools/virsh.c b/tools/virsh.c
index 430168c..3e87dbb 100644
--- a/tools/virsh.c
+++ b/tools/virsh.c
@@ -59,6 +59,7 @@
#include "threads.h"
#include "command.h"
#include "virkeycode.h"
+#include "util/bitmap.h"
static char *progname;
@@ -10986,6 +10987,237 @@ cmdAttachDevice(vshControl *ctl, const vshCmd *cmd)
return true;
}
+/**
+ * Check if n1 is superset of n2, meaning n1 contains all elements and
+ * attributes as n2 at lest. Including children.
+ * @n1 first node
+ * @n2 second node
+ * return 1 in case n1 covers n2, 0 otherwise.
+ */
+static bool
+vshNodeIsSuperset(xmlNodePtr n1, xmlNodePtr n2) {
+ xmlNodePtr child1, child2;
+ xmlAttrPtr attr;
+ char *prop1, *prop2;
+ bool found;
+ bool visited;
+ bool ret = false;
+ unsigned long n1_child_size, n1_iter;
+ virBitmapPtr bitmap;
+
+ if (!n1 && !n2)
+ return true;
+
+ if (!n1 || !n2)
+ return false;
+
+ if (!xmlStrEqual(n1->name, n2->name))
+ return true;
+
+ /* Iterate over n2 attributes and check if n1 contains them*/
+ attr = n2->properties;
+ while (attr) {
+ if (attr->type == XML_ATTRIBUTE_NODE) {
+ prop1 = virXMLPropString(n1, (const char *) attr->name);
+ prop2 = virXMLPropString(n2, (const char *) attr->name);
+ if (STRNEQ_NULLABLE(prop1, prop2)){
+ xmlFree(prop1);
+ xmlFree(prop2);
+ return false;
+ }
+ xmlFree(prop1);
+ xmlFree(prop2);
+ }
+ attr = attr->next;
+ }
+
+ n1_child_size = xmlChildElementCount(n1);
+ if (!n1_child_size) {
+ return (n1->type == XML_ELEMENT_NODE) ? true : false;
+ }
+
+ if (!(bitmap = virBitmapAlloc(n1_child_size))) {
+ virReportOOMError();
+ return false;
+ }
+
+ child2 = n2->children;
+ while (child2) {
+ if (child2->type != XML_ELEMENT_NODE) {
+ child2 = child2->next;
+ continue;
+ }
+
+ child1 = n1->children;
+ n1_iter = 0;
+ found = false;
+ while (child1) {
+ if (child1->type != XML_ELEMENT_NODE) {
+ child1 = child1->next;
+ continue;
+ }
+
+ if (virBitmapGetBit(bitmap, n1_iter, &visited) < 0) {
+ vshError(NULL, "%s", _("Bad child elements counting."));
+ goto cleanup;
+ }
+
+ if (visited) {
+ child1 = child1->next;
+ n1_iter++;
+ continue;
+ }
+
+ if (xmlStrEqual(child1->name, child2->name)) {
+ found = true;
+ if (virBitmapSetBit(bitmap, n1_iter) < 0) {
+ vshError(NULL, "%s", _("Bad child elements counting."));
+ goto cleanup;
+ }
+
+ if (!vshNodeIsSuperset(child1, child2))
+ goto cleanup;
+
+ break;
+ }
+
+ child1 = child1->next;
+ n1_iter++;
+ }
+
+ if (!found)
+ goto cleanup;
+
+ child2 = child2->next;
+ }
+
+ ret = true;
+
+cleanup:
+ virBitmapFree(bitmap);
+ return ret;
+}
+
+/**
+ * vshCompleteXMLFromDomain:
+ * @ctl vshControl for error messages printing
+ * @dom domain
+ * @oldXML device XML before
+ * @newXML and after completion
+ *
+ * To given domain and (probably incomplete) device XML specification try to
+ * find such device in domain and complete missing parts. This is however
+ * possible only when given device XML is sufficiently precise so it addresses
+ * only one device.
+ *
+ * Returns -2 when no such device exists in domain, -3 when given XML selects many
+ * (is too ambiguous), 0 in case of success. Otherwise returns -1. @newXML
+ * is touched only in case of success.
+ */
+static int
+vshCompleteXMLFromDomain(vshControl *ctl, virDomainPtr dom, char *oldXML,
+ char **newXML) {
+ int funcRet = -1;
+ char *domXML = NULL;
+ xmlDocPtr domDoc = NULL, devDoc = NULL;
+ xmlNodePtr node = NULL;
+ xmlXPathContextPtr domCtxt = NULL, devCtxt = NULL;
+ xmlNodePtr *devices = NULL;
+ xmlSaveCtxtPtr sctxt = NULL;
+ int devices_size;
+ char *xpath;
+ xmlBufferPtr buf = NULL;
+
+ if (!(domXML = virDomainGetXMLDesc(dom, 0))) {
+ vshError(ctl, _("couldn't get XML description of domain %s"),
+ virDomainGetName(dom));
+ goto cleanup;
+ }
+
+ domDoc = virXMLParseStringCtxt(domXML, _("(domain definition)"), &domCtxt);
+ if (!domDoc) {
+ vshError(ctl, _("Failed to parse domain definition xml"));
+ goto cleanup;
+ }
+
+ devDoc = virXMLParseStringCtxt(oldXML, _("(device definition)"), &devCtxt);
+ if (!devDoc) {
+ vshError(ctl, _("Failed to parse device definition xml"));
+ goto cleanup;
+ }
+
+ node = xmlDocGetRootElement(devDoc);
+
+ buf = xmlBufferCreate();
+ if (!buf) {
+ vshError(ctl, "%s", _("out of memory"));
+ goto cleanup;
+ }
+
+ xmlBufferCat(buf, BAD_CAST "/domain/devices/");
+ xmlBufferCat(buf, node->name);
+ xpath = (char *) xmlBufferContent(buf);
+ /* Get all possible devices */
+ devices_size = virXPathNodeSet(xpath, domCtxt, &devices);
+ xmlBufferEmpty(buf);
+
+ if (devices_size < 0) {
+ /* error */
+ vshError(ctl, "%s", _("error when selecting nodes"));
+ goto cleanup;
+ } else if (devices_size == 0) {
+ /* no such device */
+ funcRet = -2;
+ goto cleanup;
+ }
+
+ /* and refine */
+ int i = 0;
+ int indx = -1;
+ for (i = 0; i < devices_size; i++) {
+ if (vshNodeIsSuperset(devices[i], node)) {
+ if (indx >= 0) {
+ funcRet = -3; /* ambiguous */
+ goto cleanup;
+ }
+ indx = i;
+ }
+ }
+
+ if (indx < 0) {
+ funcRet = -2; /* no such device */
+ goto cleanup;
+ }
+
+ vshDebug(ctl, VSH_ERR_DEBUG, "Found device at pos %d\n", indx);
+
+ if (newXML) {
+ sctxt = xmlSaveToBuffer(buf, NULL, 0);
+ if (!sctxt) {
+ vshError(ctl, "%s", _("failed to create document saving context"));
+ goto cleanup;
+ }
+
+ xmlSaveTree(sctxt, devices[indx]);
+ xmlSaveClose(sctxt);
+ *newXML = (char *) xmlBufferContent(buf);
+ buf->content = NULL;
+ }
+
+ vshDebug(ctl, VSH_ERR_DEBUG, "Old xml:\n%s\nNew xml:\n%s\n", oldXML, *newXML);
+
+ funcRet = 0;
+
+cleanup:
+ xmlBufferFree(buf);
+ VIR_FREE(devices);
+ xmlXPathFreeContext(devCtxt);
+ xmlXPathFreeContext(domCtxt);
+ xmlFreeDoc(devDoc);
+ xmlFreeDoc(domDoc);
+ VIR_FREE(domXML);
+ return funcRet;
+}
/*
* "detach-device" command
@@ -11006,10 +11238,11 @@ static const vshCmdOptDef opts_detach_device[] = {
static bool
cmdDetachDevice(vshControl *ctl, const vshCmd *cmd)
{
- virDomainPtr dom;
+ virDomainPtr dom = NULL;
const char *from = NULL;
- char *buffer;
+ char *buffer = NULL, *new_buffer = NULL;
int ret;
+ bool funcRet = false;
unsigned int flags;
if (!vshConnectionUsability(ctl, ctl->conn))
@@ -11018,37 +11251,50 @@ cmdDetachDevice(vshControl *ctl, const vshCmd *cmd)
if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
return false;
- if (vshCommandOptString(cmd, "file", &from) <= 0) {
- virDomainFree(dom);
- return false;
- }
+ if (vshCommandOptString(cmd, "file", &from) <= 0)
+ goto cleanup;
if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0) {
virshReportError(ctl);
- virDomainFree(dom);
- return false;
+ goto cleanup;
+ }
+
+ ret = vshCompleteXMLFromDomain(ctl, dom, buffer, &new_buffer);
+ if (ret < 0) {
+ if (ret == -2) {
+ vshError(ctl, _("no such device in %s"), virDomainGetName(dom));
+ } else if (ret == -3) {
+ vshError(ctl, "%s", _("given XML selects too many devices. "
+ "Please, be more specific"));
+ } else {
+ /* vshCompleteXMLFromDomain() already printed error message,
+ * so nothing to do here. */
+ }
+ goto cleanup;
}
if (vshCommandOptBool(cmd, "persistent")) {
flags = VIR_DOMAIN_AFFECT_CONFIG;
if (virDomainIsActive(dom) == 1)
flags |= VIR_DOMAIN_AFFECT_LIVE;
- ret = virDomainDetachDeviceFlags(dom, buffer, flags);
+ ret = virDomainDetachDeviceFlags(dom, new_buffer, flags);
} else {
- ret = virDomainDetachDevice(dom, buffer);
+ ret = virDomainDetachDevice(dom, new_buffer);
}
- VIR_FREE(buffer);
if (ret < 0) {
vshError(ctl, _("Failed to detach device from %s"), from);
- virDomainFree(dom);
- return false;
- } else {
- vshPrint(ctl, "%s", _("Device detached successfully\n"));
+ goto cleanup;
}
+ vshPrint(ctl, "%s", _("Device detached successfully\n"));
+ funcRet = true;
+
+cleanup:
+ VIR_FREE(new_buffer);
+ VIR_FREE(buffer);
virDomainFree(dom);
- return true;
+ return funcRet;
}
--
1.7.3.4
13 years, 2 months