[libvirt] [RFC PATCHv2 0/n] snapshot: add revert-and-create branching
by Eric Blake
v1 was here:
https://www.redhat.com/archives/libvir-list/2012-November/msg00484.html
Since then, I've folded in review comments on 1; 2-4 are new.
Documentation is still pending (split out of 1 based on the v1
review). Also, I'm still writing and testing the actual qemu use
of the new snapshot_conf.c code, so this series is not quite ready
for committing without that being done.
In my review of Peter's series, I promised to post my patch for
making it easier to clone the in-memory view of a domain when
creating or reverting to a snapshot; that's patch 3.
Eric Blake (4):
snapshot: add revert-and-create branching of external snapshots
snapshot: prepare to parse new XML
snapshot: make cloning of domain definition easier
snapshot: actually compute branch definition from XML
docs/formatsnapshot.html.in | 16 +++++--
docs/schemas/domainsnapshot.rng | 5 +++
include/libvirt/libvirt.h.in | 3 ++
src/conf/domain_conf.c | 37 ++++++++++------
src/conf/domain_conf.h | 2 +
src/conf/snapshot_conf.c | 93 ++++++++++++++++++++++++++++++++++++++-
src/conf/snapshot_conf.h | 2 +
src/esx/esx_driver.c | 2 +-
src/libvirt.c | 27 +++++++++---
src/libvirt_private.syms | 1 +
src/qemu/qemu_driver.c | 17 ++-----
src/vbox/vbox_tmpl.c | 2 +-
tests/domainsnapshotxml2xmltest.c | 2 +-
tools/virsh-snapshot.c | 40 +++++++++++++----
tools/virsh.pod | 16 ++++++-
15 files changed, 215 insertions(+), 50 deletions(-)
--
1.7.11.7
12 years
[libvirt] [PATCH] Log an audit message with the LXC init pid
by Daniel P. Berrange
From: "Daniel P. Berrange" <berrange(a)redhat.com>
Currently the LXC driver logs audit messages when a container
is started or stopped. These audit messages, however, contain
the PID of the libvirt_lxc supervisor process. To enable
sysadmins to correlate with audit messages generated by
processes /inside/ the container, we need to include the
container init process PID.
We can't do this in the main 'start' audit message, since
the init PID is not available at that point. Instead we output
a completely new audit record, that lists both PIDs.
type=VIRT_CONTROL msg=audit(1353433750.071:363): pid=20180 uid=0 auid=501 ses=3 subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 msg='virt=lxc op=init vm="busy" uuid=dda7b947-0846-1759-2873-0f375df7d7eb vm-pid=20371 init-pid=20372 exe="/home/berrange/src/virt/libvirt/daemon/.libs/lt-libvirtd" hostname=? addr=? terminal=pts/6 res=success'
---
src/conf/domain_audit.c | 26 ++++++++++++++++++++++++++
src/conf/domain_audit.h | 3 +++
src/libvirt_private.syms | 1 +
src/lxc/lxc_controller.c | 31 ++++++++++++++++++++++++++++++-
src/lxc/lxc_monitor.c | 23 +++++++++++++++++++++++
src/lxc/lxc_monitor.h | 5 +++++
src/lxc/lxc_process.c | 8 ++++++++
src/lxc/lxc_protocol.x | 7 ++++++-
8 files changed, 102 insertions(+), 2 deletions(-)
diff --git a/src/conf/domain_audit.c b/src/conf/domain_audit.c
index 0f3924a..34cd92e 100644
--- a/src/conf/domain_audit.c
+++ b/src/conf/domain_audit.c
@@ -605,6 +605,32 @@ virDomainAuditStart(virDomainObjPtr vm, const char *reason, bool success)
virDomainAuditLifecycle(vm, "start", reason, success);
}
+void
+virDomainAuditInit(virDomainObjPtr vm,
+ unsigned long long initpid)
+{
+ char uuidstr[VIR_UUID_STRING_BUFLEN];
+ char *vmname;
+ const char *virt;
+
+ virUUIDFormat(vm->def->uuid, uuidstr);
+
+ if (!(vmname = virAuditEncode("vm", vm->def->name))) {
+ VIR_WARN("OOM while encoding audit message");
+ return;
+ }
+
+ if (!(virt = virDomainVirtTypeToString(vm->def->virtType))) {
+ VIR_WARN("Unexpected virt type %d while encoding audit message", vm->def->virtType);
+ virt = "?";
+ }
+
+ VIR_AUDIT(VIR_AUDIT_RECORD_MACHINE_CONTROL, true,
+ "virt=%s op=init %s uuid=%s vm-pid=%lld init-pid=%lld",
+ virt, vmname, uuidstr, (long long)vm->pid, initpid);
+
+ VIR_FREE(vmname);
+}
void
virDomainAuditStop(virDomainObjPtr vm, const char *reason)
diff --git a/src/conf/domain_audit.h b/src/conf/domain_audit.h
index db35d09..94b1198 100644
--- a/src/conf/domain_audit.h
+++ b/src/conf/domain_audit.h
@@ -31,6 +31,9 @@ void virDomainAuditStart(virDomainObjPtr vm,
const char *reason,
bool success)
ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2);
+void virDomainAuditInit(virDomainObjPtr vm,
+ unsigned long long pid)
+ ATTRIBUTE_NONNULL(1);
void virDomainAuditStop(virDomainObjPtr vm,
const char *reason)
ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2);
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index 5a07139..23369e7 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -258,6 +258,7 @@ virDomainAuditCgroupPath;
virDomainAuditDisk;
virDomainAuditFS;
virDomainAuditHostdev;
+virDomainAuditInit;
virDomainAuditMemory;
virDomainAuditNet;
virDomainAuditNetDevice;
diff --git a/src/lxc/lxc_controller.c b/src/lxc/lxc_controller.c
index 4746897..65d117a 100644
--- a/src/lxc/lxc_controller.c
+++ b/src/lxc/lxc_controller.c
@@ -123,6 +123,7 @@ struct _virLXCController {
/* Server socket */
virNetServerPtr server;
+ bool firstClient;
virNetServerClientPtr client;
virNetServerProgramPtr prog;
bool inShutdown;
@@ -132,6 +133,8 @@ struct _virLXCController {
#include "lxc_controller_dispatch.h"
static void virLXCControllerFree(virLXCControllerPtr ctrl);
+static int virLXCControllerEventSendInit(virLXCControllerPtr ctrl,
+ pid_t initpid);
static void virLXCControllerQuitTimer(int timer ATTRIBUTE_UNUSED, void *opaque)
{
@@ -152,6 +155,7 @@ static virLXCControllerPtr virLXCControllerNew(const char *name)
goto no_memory;
ctrl->timerShutdown = -1;
+ ctrl->firstClient = true;
if (!(ctrl->name = strdup(name)))
goto no_memory;
@@ -588,6 +592,11 @@ static void *virLXCControllerClientPrivateNew(virNetServerClientPtr client,
virNetServerClientSetCloseHook(client, virLXCControllerClientCloseHook);
VIR_DEBUG("Got new client %p", client);
ctrl->client = client;
+
+ if (ctrl->initpid && ctrl->firstClient)
+ virLXCControllerEventSendInit(ctrl, ctrl->initpid);
+ ctrl->firstClient = false;
+
return dummy;
}
@@ -1283,8 +1292,10 @@ virLXCControllerEventSend(virLXCControllerPtr ctrl,
{
virNetMessagePtr msg;
- if (!ctrl->client)
+ if (!ctrl->client) {
+ VIR_WARN("Dropping event %d becuase libvirtd is not connected", procnr);
return;
+ }
VIR_DEBUG("Send event %d client=%p", procnr, ctrl->client);
if (!(msg = virNetMessageNew(false)))
@@ -1352,6 +1363,24 @@ virLXCControllerEventSendExit(virLXCControllerPtr ctrl,
static int
+virLXCControllerEventSendInit(virLXCControllerPtr ctrl,
+ pid_t initpid)
+{
+ virLXCProtocolInitEventMsg msg;
+
+ VIR_DEBUG("Init pid %llu", (unsigned long long)initpid);
+ memset(&msg, 0, sizeof(msg));
+ msg.initpid = initpid;
+
+ virLXCControllerEventSend(ctrl,
+ VIR_LXC_PROTOCOL_PROC_INIT_EVENT,
+ (xdrproc_t)xdr_virLXCProtocolInitEventMsg,
+ (void*)&msg);
+ return 0;
+}
+
+
+static int
virLXCControllerRun(virLXCControllerPtr ctrl)
{
int rc = -1;
diff --git a/src/lxc/lxc_monitor.c b/src/lxc/lxc_monitor.c
index 3e00751..97ff828 100644
--- a/src/lxc/lxc_monitor.c
+++ b/src/lxc/lxc_monitor.c
@@ -65,12 +65,20 @@ static void
virLXCMonitorHandleEventExit(virNetClientProgramPtr prog,
virNetClientPtr client,
void *evdata, void *opaque);
+static void
+virLXCMonitorHandleEventInit(virNetClientProgramPtr prog,
+ virNetClientPtr client,
+ void *evdata, void *opaque);
static virNetClientProgramEvent virLXCProtocolEvents[] = {
{ VIR_LXC_PROTOCOL_PROC_EXIT_EVENT,
virLXCMonitorHandleEventExit,
sizeof(virLXCProtocolExitEventMsg),
(xdrproc_t)xdr_virLXCProtocolExitEventMsg },
+ { VIR_LXC_PROTOCOL_PROC_INIT_EVENT,
+ virLXCMonitorHandleEventInit,
+ sizeof(virLXCProtocolInitEventMsg),
+ (xdrproc_t)xdr_virLXCProtocolInitEventMsg },
};
@@ -88,6 +96,21 @@ virLXCMonitorHandleEventExit(virNetClientProgramPtr prog ATTRIBUTE_UNUSED,
}
+static void
+virLXCMonitorHandleEventInit(virNetClientProgramPtr prog ATTRIBUTE_UNUSED,
+ virNetClientPtr client ATTRIBUTE_UNUSED,
+ void *evdata, void *opaque)
+{
+ virLXCMonitorPtr mon = opaque;
+ virLXCProtocolInitEventMsg *msg = evdata;
+
+ VIR_DEBUG("Event init %llu",
+ (unsigned long long)msg->initpid);
+ if (mon->cb.initNotify)
+ mon->cb.initNotify(mon, msg->initpid, mon->vm);
+}
+
+
static void virLXCMonitorEOFNotify(virNetClientPtr client ATTRIBUTE_UNUSED,
int reason ATTRIBUTE_UNUSED,
void *opaque)
diff --git a/src/lxc/lxc_monitor.h b/src/lxc/lxc_monitor.h
index bb8349a..fd8efd9 100644
--- a/src/lxc/lxc_monitor.h
+++ b/src/lxc/lxc_monitor.h
@@ -40,10 +40,15 @@ typedef void (*virLXCMonitorCallbackExitNotify)(virLXCMonitorPtr mon,
virLXCProtocolExitStatus status,
virDomainObjPtr vm);
+typedef void (*virLXCMonitorCallbackInitNotify)(virLXCMonitorPtr mon,
+ unsigned long long pid,
+ virDomainObjPtr vm);
+
struct _virLXCMonitorCallbacks {
virLXCMonitorCallbackDestroy destroy;
virLXCMonitorCallbackEOFNotify eofNotify;
virLXCMonitorCallbackExitNotify exitNotify;
+ virLXCMonitorCallbackInitNotify initNotify;
};
virLXCMonitorPtr virLXCMonitorNew(virDomainObjPtr vm,
diff --git a/src/lxc/lxc_process.c b/src/lxc/lxc_process.c
index 079bc3a..c1ef2bd 100644
--- a/src/lxc/lxc_process.c
+++ b/src/lxc/lxc_process.c
@@ -637,9 +637,17 @@ static void virLXCProcessMonitorExitNotify(virLXCMonitorPtr mon ATTRIBUTE_UNUSED
priv->stopReason, status);
}
+static void virLXCProcessMonitorInitNotify(virLXCMonitorPtr mon ATTRIBUTE_UNUSED,
+ unsigned long long initpid,
+ virDomainObjPtr vm)
+{
+ virDomainAuditInit(vm, initpid);
+}
+
static virLXCMonitorCallbacks monitorCallbacks = {
.eofNotify = virLXCProcessMonitorEOFNotify,
.exitNotify = virLXCProcessMonitorExitNotify,
+ .initNotify = virLXCProcessMonitorInitNotify,
};
diff --git a/src/lxc/lxc_protocol.x b/src/lxc/lxc_protocol.x
index e437217..0f041f6 100644
--- a/src/lxc/lxc_protocol.x
+++ b/src/lxc/lxc_protocol.x
@@ -14,9 +14,14 @@ struct virLXCProtocolExitEventMsg {
enum virLXCProtocolExitStatus status;
};
+struct virLXCProtocolInitEventMsg {
+ unsigned hyper initpid;
+};
+
const VIR_LXC_PROTOCOL_PROGRAM = 0x12341234;
const VIR_LXC_PROTOCOL_PROGRAM_VERSION = 1;
enum virLXCProtocolProcedure {
- VIR_LXC_PROTOCOL_PROC_EXIT_EVENT = 1 /* skipgen skipgen */
+ VIR_LXC_PROTOCOL_PROC_EXIT_EVENT = 1, /* skipgen skipgen */
+ VIR_LXC_PROTOCOL_PROC_INIT_EVENT = 2 /* skipgen skipgen */
};
--
1.7.11.7
12 years
[libvirt] [PATCH] conf: Report sensible error for invalid disk name
by Martin Kletzander
The error "... but the cause is unknown" appeared for XMLs similar to
this:
<disk type='file' device='cdrom'>
<driver name='qemu' type='raw'/>
<source file='/dev/zero'/>
<target dev='sr0'/>
</disk>
Notice unsupported disk type (for the driver), but also no address
specified. The first part is not a problem and we should not abort
immediately because of that, but the combination with the address
unknown was causing an unspecified error.
---
src/util/util.c | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/src/util/util.c b/src/util/util.c
index 75b18c1..d5b2c97 100644
--- a/src/util/util.c
+++ b/src/util/util.c
@@ -2189,8 +2189,12 @@ int virDiskNameToIndex(const char *name) {
}
}
- if (!ptr)
+ if (!ptr) {
+ virReportError(VIR_ERR_XML_ERROR,
+ _("Unknown disk name '%s' and no address specified"),
+ name);
return -1;
+ }
for (i = 0; *ptr; i++) {
idx = (idx + (i < 1 ? 0 : 1)) * 26;
--
1.8.0
12 years
[libvirt] Report guest IPs as XML or struct?
by Michal Privoznik
I've proposed both approaches in the past. However, none of them was
accepted as we ought to agree on $subj first. Frankly, both makes sense
to me, both has advantages and disadvantages.
XML - keeps things more consistent as libvirt is tied to XML, right?
Pros:
- can add items over the time
Cons:
- more complicated to parse and dump info (apps would have to query
multiple XPATHs to get answer - at least with my implementation I've sent)
struct - it would be a linked list or array of structs in fact
Pros:
- easier to dump useful data
Cons:
- once struct is released we cannot change it if we don't want to
break ABI.
What things do we want to expose for now?
qemu guest agent reports:
- type of IP address (v4/v6)
- prefix length
- actual address (string)
(these can repeat multiple times, since there's 1:* relationship between
interface an IP addresses)
- hw address
- name as seen within guest
(HW address can be omitted - not all interfaces must have one).
The only element that will be there for sure is name then.
The whole QAPI schema can be found in qapi-schema-guest.json [1].
Suggestions are welcome.
Michal
1:
http://git.qemu.org/?p=qemu.git;a=blob;f=qapi-schema-guest.json;h=ed0eb69...
12 years
[libvirt] [test-API][PATCH] Add test case of set vcpus with flags
by Wayne Sun
Use setVcpusFlags API to set domain vcpu with flags, domain could
be active or not. Flags could be '0', 'live', 'config', 'maximum'
and their combinations, use '|' between flags for combinations. A
sample conf file also added.
Signed-off-by: Wayne Sun <gsun(a)redhat.com>
---
cases/set_vcpus_flags.conf | 56 ++++++++
repos/domain/set_vcpus_flags.py | 283 +++++++++++++++++++++++++++++++++++++++
2 files changed, 339 insertions(+), 0 deletions(-)
create mode 100644 cases/set_vcpus_flags.conf
create mode 100644 repos/domain/set_vcpus_flags.py
diff --git a/cases/set_vcpus_flags.conf b/cases/set_vcpus_flags.conf
new file mode 100644
index 0000000..7da13a2
--- /dev/null
+++ b/cases/set_vcpus_flags.conf
@@ -0,0 +1,56 @@
+domain:install_linux_cdrom
+ guestname
+ $defaultname
+ guestos
+ $defaultos
+ guestarch
+ $defaultarch
+ vcpu
+ $defaultvcpu
+ memory
+ $defaultmem
+ hddriver
+ $defaulthd
+ nicdriver
+ $defaultnic
+ imageformat
+ qcow2
+
+domain:set_vcpus_flags
+ guestname
+ $defaultname
+ vcpu
+ 4
+ flags
+ 0|config|live
+ username
+ $username
+ password
+ $password
+
+domain:destroy
+ guestname
+ $defaultname
+
+domain:set_vcpus_flags
+ guestname
+ $defaultname
+ vcpu
+ 5
+ flags
+ 0|config
+
+domain:set_vcpus_flags
+ guestname
+ $defaultname
+ vcpu
+ 6
+ flags
+ maximum
+
+domain:undefine
+ guestname
+ $defaultname
+
+options cleanup=enable
+
diff --git a/repos/domain/set_vcpus_flags.py b/repos/domain/set_vcpus_flags.py
new file mode 100644
index 0000000..ca614cc
--- /dev/null
+++ b/repos/domain/set_vcpus_flags.py
@@ -0,0 +1,283 @@
+#!/usr/bin/env python
+# Test set domain vcpu with flags. Flags could be 0, live, config,
+# maximum and their combinations, use '|' for combinations. If
+# domain is active, username and password should be provided, else
+# not.
+
+import time
+import commands
+from xml.dom import minidom
+
+import libvirt
+from libvirt import libvirtError
+
+from src import sharedmod
+from utils import utils
+
+required_params = ('guestname', 'vcpu', 'flags', )
+optional_params = {
+ 'username': 'root',
+ 'password': '',
+ }
+
+def check_domain_running(conn, guestname):
+ """ check if the domain exists, may or may not be active """
+ guest_names = []
+ ids = conn.listDomainsID()
+ for id in ids:
+ obj = conn.lookupByID(id)
+ guest_names.append(obj.name())
+
+ if guestname not in guest_names:
+ logger.info("%s is not running" % guestname)
+ return 1
+ else:
+ return 0
+
+def redefine_vcpu_number(domobj, guestname, vcpu):
+ """dump domain xml description to change the vcpu number,
+ then, define the domain again
+ """
+ guestxml = domobj.XMLDesc(0)
+ logger.debug('''original guest %s xml :\n%s''' %(guestname, guestxml))
+
+ doc = minidom.parseString(guestxml)
+
+ newvcpu = doc.createElement('vcpu')
+ newvcpuval = doc.createTextNode(str(vcpu))
+ newvcpu.appendChild(newvcpuval)
+ newvcpu.setAttribute('current', '1')
+
+ domain = doc.getElementsByTagName('domain')[0]
+ oldvcpu = doc.getElementsByTagName('vcpu')[0]
+
+ domain.replaceChild(newvcpu, oldvcpu)
+
+ return doc.toxml()
+
+def check_current_vcpu(domobj, state, flag, username, password):
+ """dump domain xml description to get current vcpu number and
+ check vcpu in domain if domain is active
+ """
+ if len(flag) == 1 and flag[0] == '0':
+ if state:
+ flag.append('config')
+ else:
+ flag.append('live')
+
+ if len(flag) == 1 and flag[0] == 'maximum':
+ if state:
+ flag.append('config')
+ else:
+ logger.error("'maximum' on live domain is not supported'")
+ return False
+
+ if 'live' in flag:
+ if 'maximum' in flag:
+ logger.error("'live' with 'maximum' is not supported'")
+ return False
+
+ guestxml = domobj.XMLDesc(1)
+ logger.debug("domain %s xml is :\n%s" %(domobj.name(), guestxml))
+
+ xml = minidom.parseString(guestxml)
+ vcpu = xml.getElementsByTagName('vcpu')[0]
+ if vcpu.hasAttribute('current'):
+ attr = vcpu.getAttributeNode('current')
+ current_vcpu = int(attr.nodeValue)
+ else:
+ logger.info("no 'current' attribute in vcpu element")
+ return False
+
+ if not state:
+ if password == '' and username == 'root':
+ logger.error("check will fail with empty root password")
+ return False
+
+ logger.info("check cpu number in domain")
+ ip = utils.mac_to_ip(mac, 180)
+
+ cmd = "cat /proc/cpuinfo | grep processor | wc -l"
+ ret, output = utils.remote_exec_pexpect(ip, username, password, cmd)
+ if not ret:
+ logger.info("cpu number in domain is %s" % output)
+ if int(output) == current_vcpu:
+ logger.info("cpu in domain is equal to current vcpu value")
+ else:
+ logger.error("current vcpu is not equal as check in domain")
+ return False
+ else:
+ logger.error("check in domain fail")
+ return False
+
+ if 'config' in flag:
+ guestxml = domobj.XMLDesc(2)
+ logger.debug("domain %s xml is :\n%s" %(domobj.name(), guestxml))
+
+ xml = minidom.parseString(guestxml)
+ vcpu = xml.getElementsByTagName('vcpu')[0]
+ if 'maximum' in flag:
+ current_vcpu = int(vcpu.childNodes[0].data)
+ else:
+ if vcpu.hasAttribute('current'):
+ attr = vcpu.getAttributeNode('current')
+ conf_vcpu = int(attr.nodeValue)
+ if 'live' in flag:
+ if not current_vcpu == conf_vcpu:
+ return False
+ else:
+ current_vcpu = conf_vcpu
+ else:
+ logger.info("no 'current' attribute in vcpu element")
+ return False
+
+ return current_vcpu
+
+def set_vcpus_offline(domobj, guestname, vcpu):
+ """offline set the domain vcpu as given and current value as 1,
+ then boot up the domain
+ """
+ timeout = 60
+ logger.info('destroy domain')
+
+ try:
+ domobj.destroy()
+ except libvirtError, e:
+ logger.error("libvirt call failed: " + str(e))
+ logger.error("fail to destroy domain")
+ return 1
+
+ newguestxml = redefine_vcpu_number(domobj, guestname, vcpu)
+ logger.debug('''new guest %s xml :\n%s''' %(guestname, newguestxml))
+
+ logger.info("undefine the original guest")
+ try:
+ domobj.undefine()
+ except libvirtError, e:
+ logger.error("libvirt call failed: " + str(e))
+ logger.error("fail to undefine guest %" % guestname)
+ return 1
+
+ logger.info("define guest with new xml")
+ try:
+ conn = domobj._conn
+ conn.defineXML(newguestxml)
+ except libvirtError, e:
+ logger.error("libvirt call failed: " + str(e))
+ logger.error("fail to define guest %s" % guestname)
+ return 1
+
+ try:
+ logger.info('boot guest up ...')
+ domobj.create()
+ except libvirtError, e:
+ logger.error("libvirt call failed: " + str(e))
+ logger.error("fail to start domain %s" % guestname)
+ return 1
+
+ timeout = 600
+
+ while timeout:
+ time.sleep(10)
+ timeout -= 10
+
+ logger.debug("get ip by mac address")
+ ip = utils.mac_to_ip(mac, 180)
+ logger.debug("the ip address of vm %s is %s" % (guestname, ip))
+
+ if not ip:
+ logger.info(str(timeout) + "s left")
+ else:
+ logger.info("vm %s power on successfully" % guestname)
+ logger.info("the ip address of vm %s is %s" % (guestname, ip))
+ break
+
+ if timeout <= 0:
+ logger.info("fail to power on vm %s" % guestname)
+ return 1
+
+ return 0
+
+def set_vcpus_flags(params):
+ """set domain vcpu with flags and check
+ """
+ global logger
+ logger = params['logger']
+ params.pop('logger')
+ guestname = params['guestname']
+ vcpu = int(params['vcpu'])
+ flags = params['flags']
+ username = params.get('username', 'root')
+ password = params.get('password', '')
+
+ logger.info("the name of virtual machine is %s" % guestname)
+ logger.info("the vcpu given is %s" % vcpu)
+
+ logger.info("the flags is %s" % flags)
+ flags_string = flags.split("|")
+
+ flags = 0
+ for flag in flags_string:
+ if flag == '0':
+ flags |= 0
+ elif flag == 'live':
+ flags |= libvirt.VIR_DOMAIN_AFFECT_LIVE
+ elif flag == 'config':
+ flags |= libvirt.VIR_DOMAIN_AFFECT_CONFIG
+ elif flag == 'maximum':
+ flags |= libvirt.VIR_DOMAIN_VCPU_MAXIMUM
+ else:
+ logger.error("unknown flag")
+ return 1
+
+ conn = sharedmod.libvirtobj['conn']
+
+ domobj = conn.lookupByName(guestname)
+ logger.debug("get the mac address of vm %s" % guestname)
+ global mac
+ mac = utils.get_dom_mac_addr(guestname)
+ logger.debug("the mac address of vm %s is %s" % (guestname, mac))
+
+ num = vcpu + 1
+ try:
+ max_vcpus = int(conn.getMaxVcpus('kvm'))
+ logger.debug("hypervisor supported max vcpu is %s" % max_vcpus)
+ except libvirtError, e:
+ logger.error("libvirt call failed: " + str(e))
+ return 1
+
+ if num > max_vcpus:
+ logger.error("vcpu must smaller than max number: %s" % max_vcpus)
+ return 1
+
+ state = check_domain_running(conn, guestname)
+
+ if state:
+ try:
+ logger.info("set domain maximum vcpu as: %s" % num)
+ domobj.setVcpusFlags(num, libvirt.VIR_DOMAIN_VCPU_MAXIMUM)
+ except libvirtError, e:
+ logger.error("libvirt call failed: " + str(e))
+ return 1
+ else:
+ logger.info("set domain vcpu to %s and restart with current cpu as 1" %
+ num)
+ ret = set_vcpus_offline(domobj, guestname, num)
+ if ret != 0:
+ return 1
+
+ try:
+ logger.info("set vcpus to %s with flag: %s" % (vcpu, flags))
+ domobj.setVcpusFlags(vcpu, flags)
+ logger.info("set vcpu with flag succeed")
+ except libvirtError, e:
+ logger.error("libvirt call failed: " + str(e))
+ return 1
+
+ ret = check_current_vcpu(domobj, state, flags_string, username, password)
+ if ret == 'False':
+ logger.error("check set vcpu failed")
+ return 1
+ elif ret == vcpu:
+ logger.info("check set vcpu succeed")
+ return 0
--
1.7.1
12 years
[libvirt] [PATCHv2 0/4] add more snapshot-list filters
by Eric Blake
v1 was here:
https://www.redhat.com/archives/libvir-list/2012-November/msg00604.html
Diff in v2: Prefer active/inactive over online/offline; use XPath rather
than strstr for parsing XML, rebase to latest
Eric Blake (4):
snapshot: add two more filter sets to API
snapshot: add virsh back-compat support for new filters
snapshot: implement new filter sets
snapshot: expose location through virsh snapshot-info
include/libvirt/libvirt.h.in | 19 ++++
src/conf/snapshot_conf.c | 30 +++++-
src/conf/snapshot_conf.h | 13 ++-
src/libvirt.c | 60 ++++++++++++
tools/virsh-snapshot.c | 214 +++++++++++++++++++++++++++++++++++++------
tools/virsh.pod | 17 +++-
6 files changed, 322 insertions(+), 31 deletions(-)
--
1.7.11.7
12 years
[libvirt] Plan A or Plan B?
by Gene Czarcinski
Plan A:
This consists of the set of patches I have submitted for conf-file,
DHCPv6, etc. The response has been a resounding SILENCE. Thus, I am
assuming that there is some reluctance to adopting these changes in
their current form. This especially includes the conf-file changes.
While I believe that changing dnsmasq parameters from the command-line
to a configuration file makes a great deal of sense and might have been
the approach if this was being done today, there is resistance to change.
OK, so here is Plan B:
I am removing the conf-file and related changes. They will be
repackaged and resubmitted at some later time. The new multi-file patch
will focus on "IPV6 Enhanced Support" which will consist of the following:
1. In a manner similar to what is done for IPV6, add ip6tables rules to
permit virtual systems to communicate via a defined virtual interface
which has no gateway addresses defined. This does mean that virtual
systems will not be able to communicate with the host via this interface
... only with each other. Also, the following must be:
net.ipv6.conf.virbr19.disable_ipv6 = 1
so that the kernel does not start anything.
This implements IPv6 functionality currently available for IPv4.
Documentation will be added to explain the functionality for both IPv4
and IPv6.
[BTW, the only place I have found to add documentaiotn is in the
"docs/formatnetwork.html.in" file. If there are other files I should be
updating, please enlighten me.]
2. Add code to get the dnsmasq version and save that information. The
added code described below will require a dnsmasq version greater than
or equal to 2.64. Documentation will be updated to state that dnsmasq
>= 2.64 is required for DHCPv6.
3. Implement support for DHCPv6. Most of this is already done with the
existing patch. However, this refits the code to work with command-line
parameters AND adds a check for dnsmasq >= 2.64. Naturally, tests and
documentation will be updated.
4. If dnsmasq >= 2.64, do not use radvd but instead use dnsmasq to
support the RA service for both state-full and state-less IPV6.
OPTIONS:
========
a) If dnsmasq < 2.64, just ignore dhcp-range or dhcp-host definitions
OR
b) issue error message and stop dnsmasq startup if dhcp-range or
dhcp-host is specified.
========
c) Currently, tests for valid dhcp specifications is only done when a
network is started (all significant changes are to
"network/bridge_driver.c". This situation could continue. Thus, virsh
could be used to specify dhcp-range and dhcp-host but it would not work
if the network was started.
OR
d) Move the dnsmasq version checks back into when the network is
defined. This would be a bit "trickier" to implement properly since the
same code is used by multiple network types and not just that supported
by "network/bridge_driver.c". If this is the approach, I will need some
guidance as to modifying "conf/network.c".
OK folks. I need some input here. I realize that all of you are very
busy working your own interests but I need a little time from someone
with "commit" authority to say "go ahead" or "get lost".
Gene
12 years
[libvirt] Use remote connect with credetial via Java code
by Yaniv Hadad
Hello all,
Someone can explain me how can I generate connection with credential like
user and pass (ex: root\passw0rd)
I use Java API, and use this following rows for connection with local
host :
conn = new Connect("qemu:///system", false);
Thanks in advance
Yaniv Hadad
Servers & Network Group, IBM R&D Labs in Israel
Software Devloper
yanivh(a)il.ibm.com, +972-4-829-65947
Fax: +972-4-829-6111, Cell: +972-50-4078908
12 years
[libvirt] [PATCH 0/4] add more snapshot-list filters
by Eric Blake
Depends on this patch being applied first:
https://www.redhat.com/archives/libvir-list/2012-November/msg00598.html
Adds the ability for virsh to filter on whether a snapshot was
offline, online, or disk-only; and whether it was internal or external.
Eric Blake (4):
snapshot: add two more filter sets to API
snapshot: add virsh back-compat support for new filters
snapshot: expose location through virsh snapshot-info
snapshot: implement new filter sets
include/libvirt/libvirt.h.in | 19 +++++
src/conf/snapshot_conf.c | 30 ++++++-
src/conf/snapshot_conf.h | 13 ++-
src/libvirt.c | 60 ++++++++++++++
tools/virsh-snapshot.c | 187 +++++++++++++++++++++++++++++++++++++------
tools/virsh.pod | 17 +++-
6 files changed, 298 insertions(+), 28 deletions(-)
--
1.7.11.7
12 years
[libvirt] [PATCH v14] support offline migration
by liguang
original migration did not aware of offline case,
so, try to support offline migration quietly
(did not disturb original migration) by pass
VIR_MIGRATE_OFFLINE flag to migration APIs if only
the domain is really inactive, and
migration process will not puzzled by domain
offline and exit unexpectedly.
these changes did not take care of disk images the
domain required, for them could be transferred by
other APIs as suggested, then VIR_MIGRATE_OFFLINE
must not combined with VIR_MIGRATE_NON_SHARED_*.
and you must do a persistent migration at same time,
do "virsh migrate --offline --persistent ...".
Signed-off-by: liguang <lig.fnst(a)cn.fujitsu.com>
---
include/libvirt/libvirt.h.in | 1 +
src/qemu/qemu_driver.c | 16 +++---
src/qemu/qemu_migration.c | 110 ++++++++++++++++++++++++++++--------------
src/qemu/qemu_migration.h | 9 ++-
tools/virsh-domain.c | 5 ++
tools/virsh.pod | 5 +-
6 files changed, 97 insertions(+), 49 deletions(-)
diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in
index fe58c08..1e0500d 100644
--- a/include/libvirt/libvirt.h.in
+++ b/include/libvirt/libvirt.h.in
@@ -1090,6 +1090,7 @@ typedef enum {
* whole migration process; this will be used automatically
* when supported */
VIR_MIGRATE_UNSAFE = (1 << 9), /* force migration even if it is considered unsafe */
+ VIR_MIGRATE_OFFLINE = (1 << 10), /* offline migrate */
} virDomainMigrateFlags;
/* Domain migration. */
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index 978af57..5f91688 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -9594,7 +9594,7 @@ qemudDomainMigratePrepareTunnel(virConnectPtr dconn,
ret = qemuMigrationPrepareTunnel(driver, dconn,
NULL, 0, NULL, NULL, /* No cookies in v2 */
- st, dname, dom_xml);
+ st, dname, dom_xml, flags);
cleanup:
qemuDriverUnlock(driver);
@@ -9654,7 +9654,7 @@ qemudDomainMigratePrepare2(virConnectPtr dconn,
ret = qemuMigrationPrepareDirect(driver, dconn,
NULL, 0, NULL, NULL, /* No cookies */
uri_in, uri_out,
- dname, dom_xml);
+ dname, dom_xml, flags);
cleanup:
qemuDriverUnlock(driver);
@@ -9796,7 +9796,7 @@ qemuDomainMigrateBegin3(virDomainPtr domain,
asyncJob = QEMU_ASYNC_JOB_NONE;
}
- if (!virDomainObjIsActive(vm)) {
+ if (!virDomainObjIsActive(vm) && !(flags & VIR_MIGRATE_OFFLINE)) {
virReportError(VIR_ERR_OPERATION_INVALID,
"%s", _("domain is not running"));
goto endjob;
@@ -9805,9 +9805,9 @@ qemuDomainMigrateBegin3(virDomainPtr domain,
/* Check if there is any ejected media.
* We don't want to require them on the destination.
*/
-
- if (qemuDomainCheckEjectableMedia(driver, vm, asyncJob) < 0)
- goto endjob;
+ if (virDomainObjIsActive(vm) && (flags & VIR_MIGRATE_OFFLINE))
+ if (qemuDomainCheckEjectableMedia(driver, vm, asyncJob) < 0)
+ goto endjob;
if (!(xml = qemuMigrationBegin(driver, vm, xmlin, dname,
cookieout, cookieoutlen,
@@ -9891,7 +9891,7 @@ qemuDomainMigratePrepare3(virConnectPtr dconn,
cookiein, cookieinlen,
cookieout, cookieoutlen,
uri_in, uri_out,
- dname, dom_xml);
+ dname, dom_xml, flags);
cleanup:
qemuDriverUnlock(driver);
@@ -9936,7 +9936,7 @@ qemuDomainMigratePrepareTunnel3(virConnectPtr dconn,
ret = qemuMigrationPrepareTunnel(driver, dconn,
cookiein, cookieinlen,
cookieout, cookieoutlen,
- st, dname, dom_xml);
+ st, dname, dom_xml, flags);
qemuDriverUnlock(driver);
cleanup:
diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c
index 5f8a9c5..2479114 100644
--- a/src/qemu/qemu_migration.c
+++ b/src/qemu/qemu_migration.c
@@ -1443,6 +1443,20 @@ char *qemuMigrationBegin(struct qemud_driver *driver,
QEMU_MIGRATION_COOKIE_LOCKSTATE) < 0)
goto cleanup;
+ if (flags & VIR_MIGRATE_OFFLINE) {
+ if (flags & (VIR_MIGRATE_NON_SHARED_DISK|
+ VIR_MIGRATE_NON_SHARED_INC)) {
+ virReportError(VIR_ERR_OPERATION_INVALID,
+ "%s", _("offline migration cannot handle non-shared storage"));
+ goto cleanup;
+ }
+ if (!(flags & VIR_MIGRATE_PERSIST_DEST)) {
+ virReportError(VIR_ERR_OPERATION_INVALID,
+ "%s", _("offline migration must be specified with the persistent flag set"));
+ goto cleanup;
+ }
+ }
+
if (xmlin) {
if (!(def = virDomainDefParseString(driver->caps, xmlin,
QEMU_EXPECTED_VIRT_TYPES,
@@ -1500,7 +1514,8 @@ qemuMigrationPrepareAny(struct qemud_driver *driver,
const char *dname,
const char *dom_xml,
const char *migrateFrom,
- virStreamPtr st)
+ virStreamPtr st,
+ unsigned long flags)
{
virDomainDefPtr def = NULL;
virDomainObjPtr vm = NULL;
@@ -1610,15 +1625,17 @@ qemuMigrationPrepareAny(struct qemud_driver *driver,
/* Start the QEMU daemon, with the same command-line arguments plus
* -incoming $migrateFrom
*/
- if (qemuProcessStart(dconn, driver, vm, migrateFrom, dataFD[0], NULL, NULL,
- VIR_NETDEV_VPORT_PROFILE_OP_MIGRATE_IN_START,
- VIR_QEMU_PROCESS_START_PAUSED |
- VIR_QEMU_PROCESS_START_AUTODESROY) < 0) {
- virDomainAuditStart(vm, "migrated", false);
- /* Note that we don't set an error here because qemuProcessStart
- * should have already done that.
- */
- goto endjob;
+ if (!(flags & VIR_MIGRATE_OFFLINE)) {
+ if (qemuProcessStart(dconn, driver, vm, migrateFrom, dataFD[0], NULL, NULL,
+ VIR_NETDEV_VPORT_PROFILE_OP_MIGRATE_IN_START,
+ VIR_QEMU_PROCESS_START_PAUSED |
+ VIR_QEMU_PROCESS_START_AUTODESROY) < 0) {
+ virDomainAuditStart(vm, "migrated", false);
+ /* Note that we don't set an error here because qemuProcessStart
+ * should have already done that.
+ */
+ goto endjob;
+ }
}
if (tunnel) {
@@ -1626,7 +1643,8 @@ qemuMigrationPrepareAny(struct qemud_driver *driver,
virReportSystemError(errno, "%s",
_("cannot pass pipe for tunnelled migration"));
virDomainAuditStart(vm, "migrated", false);
- qemuProcessStop(driver, vm, VIR_DOMAIN_SHUTOFF_FAILED, 0);
+ if (!(flags & VIR_MIGRATE_OFFLINE))
+ qemuProcessStop(driver, vm, VIR_DOMAIN_SHUTOFF_FAILED, 0);
goto endjob;
}
dataFD[1] = -1; /* 'st' owns the FD now & will close it */
@@ -1709,7 +1727,8 @@ qemuMigrationPrepareTunnel(struct qemud_driver *driver,
int *cookieoutlen,
virStreamPtr st,
const char *dname,
- const char *dom_xml)
+ const char *dom_xml,
+ unsigned long flags)
{
int ret;
@@ -1723,7 +1742,7 @@ qemuMigrationPrepareTunnel(struct qemud_driver *driver,
*/
ret = qemuMigrationPrepareAny(driver, dconn, cookiein, cookieinlen,
cookieout, cookieoutlen, dname, dom_xml,
- "stdio", st);
+ "stdio", st, flags);
return ret;
}
@@ -1738,7 +1757,8 @@ qemuMigrationPrepareDirect(struct qemud_driver *driver,
const char *uri_in,
char **uri_out,
const char *dname,
- const char *dom_xml)
+ const char *dom_xml,
+ unsigned long flags)
{
static int port = 0;
int this_port;
@@ -1834,7 +1854,7 @@ qemuMigrationPrepareDirect(struct qemud_driver *driver,
ret = qemuMigrationPrepareAny(driver, dconn, cookiein, cookieinlen,
cookieout, cookieoutlen, dname, dom_xml,
- migrateFrom, NULL);
+ migrateFrom, NULL, flags);
cleanup:
VIR_FREE(hostname);
if (ret != 0)
@@ -2280,7 +2300,7 @@ cleanup:
orig_err = virSaveLastError();
if (spec->fwdType != MIGRATION_FWD_DIRECT) {
- if (iothread && qemuMigrationStopTunnel(iothread, ret < 0) < 0)
+ if (iothread && qemuMigrationStopTunnel(iothread, ret < 0) < 0)
ret = -1;
VIR_FORCE_CLOSE(fd);
}
@@ -2353,6 +2373,9 @@ static int doNativeMigrate(struct qemud_driver *driver,
if (!uribits)
return -1;
+ if (flags & VIR_MIGRATE_OFFLINE)
+ return 0;
+
if (qemuCapsGet(priv->caps, QEMU_CAPS_MIGRATE_QEMU_FD))
spec.destType = MIGRATION_DEST_CONNECT_HOST;
else
@@ -2665,10 +2688,18 @@ static int doPeer2PeerMigrate3(struct qemud_driver *driver,
uri, &uri_out, flags, dname, resource, dom_xml);
qemuDomainObjExitRemoteWithDriver(driver, vm);
}
+
VIR_FREE(dom_xml);
+
if (ret == -1)
goto cleanup;
+ if (flags & VIR_MIGRATE_OFFLINE) {
+ cookieout = NULL;
+ cookieoutlen = 0;
+ goto finish;
+ }
+
if (!(flags & VIR_MIGRATE_TUNNELLED) &&
(uri_out == NULL)) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
@@ -2771,7 +2802,7 @@ finish:
vm->def->name);
cleanup:
- if (ddomain) {
+ if (ddomain || (flags & VIR_MIGRATE_OFFLINE)) {
virObjectUnref(ddomain);
ret = 0;
} else {
@@ -2848,7 +2879,7 @@ static int doPeer2PeerMigrate(struct qemud_driver *driver,
}
/* domain may have been stopped while we were talking to remote daemon */
- if (!virDomainObjIsActive(vm)) {
+ if (!virDomainObjIsActive(vm) && !(flags & VIR_MIGRATE_OFFLINE)) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("guest unexpectedly quit"));
goto cleanup;
@@ -2911,7 +2942,7 @@ qemuMigrationPerformJob(struct qemud_driver *driver,
if (qemuMigrationJobStart(driver, vm, QEMU_ASYNC_JOB_MIGRATION_OUT) < 0)
goto cleanup;
- if (!virDomainObjIsActive(vm)) {
+ if (!virDomainObjIsActive(vm) && !(flags & VIR_MIGRATE_OFFLINE)) {
virReportError(VIR_ERR_OPERATION_INVALID,
"%s", _("domain is not running"));
goto endjob;
@@ -3235,26 +3266,27 @@ qemuMigrationFinish(struct qemud_driver *driver,
* object, but if no, clean up the empty qemu process.
*/
if (retcode == 0) {
- if (!virDomainObjIsActive(vm)) {
+ if (!virDomainObjIsActive(vm) && !(flags & VIR_MIGRATE_OFFLINE)) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("guest unexpectedly quit"));
goto endjob;
}
- if (qemuMigrationVPAssociatePortProfiles(vm->def) < 0) {
- qemuProcessStop(driver, vm, VIR_DOMAIN_SHUTOFF_FAILED,
- VIR_QEMU_PROCESS_STOP_MIGRATED);
- virDomainAuditStop(vm, "failed");
- event = virDomainEventNewFromObj(vm,
- VIR_DOMAIN_EVENT_STOPPED,
- VIR_DOMAIN_EVENT_STOPPED_FAILED);
- goto endjob;
+ if (!(flags & VIR_MIGRATE_OFFLINE)) {
+ if (qemuMigrationVPAssociatePortProfiles(vm->def) < 0) {
+ qemuProcessStop(driver, vm, VIR_DOMAIN_SHUTOFF_FAILED,
+ VIR_QEMU_PROCESS_STOP_MIGRATED);
+ virDomainAuditStop(vm, "failed");
+ event = virDomainEventNewFromObj(vm,
+ VIR_DOMAIN_EVENT_STOPPED,
+ VIR_DOMAIN_EVENT_STOPPED_FAILED);
+ goto endjob;
+ }
+ if (mig->network)
+ if (qemuDomainMigrateOPDRelocate(driver, vm, mig) < 0)
+ VIR_WARN("unable to provide network data for relocation");
}
- if (mig->network)
- if (qemuDomainMigrateOPDRelocate(driver, vm, mig) < 0)
- VIR_WARN("unable to provide network data for relocation");
-
if (flags & VIR_MIGRATE_PERSIST_DEST) {
virDomainDefPtr vmdef;
if (vm->persistent)
@@ -3302,7 +3334,7 @@ qemuMigrationFinish(struct qemud_driver *driver,
event = NULL;
}
- if (!(flags & VIR_MIGRATE_PAUSED)) {
+ if (!(flags & VIR_MIGRATE_PAUSED) && !(flags & VIR_MIGRATE_OFFLINE)) {
/* run 'cont' on the destination, which allows migration on qemu
* >= 0.10.6 to work properly. This isn't strictly necessary on
* older qemu's, but it also doesn't hurt anything there
@@ -3351,9 +3383,11 @@ qemuMigrationFinish(struct qemud_driver *driver,
VIR_DOMAIN_EVENT_SUSPENDED,
VIR_DOMAIN_EVENT_SUSPENDED_PAUSED);
}
- if (virDomainSaveStatus(driver->caps, driver->stateDir, vm) < 0) {
- VIR_WARN("Failed to save status on vm %s", vm->def->name);
- goto endjob;
+ if (virDomainObjIsActive(vm)) {
+ if (virDomainSaveStatus(driver->caps, driver->stateDir, vm) < 0) {
+ VIR_WARN("Failed to save status on vm %s", vm->def->name);
+ goto endjob;
+ }
}
/* Guest is successfully running, so cancel previous auto destroy */
@@ -3420,6 +3454,9 @@ int qemuMigrationConfirm(struct qemud_driver *driver,
if (!(mig = qemuMigrationEatCookie(driver, vm, cookiein, cookieinlen, 0)))
return -1;
+ if (flags & VIR_MIGRATE_OFFLINE)
+ goto done;
+
/* Did the migration go as planned? If yes, kill off the
* domain object, but if no, resume CPUs
*/
@@ -3455,6 +3492,7 @@ int qemuMigrationConfirm(struct qemud_driver *driver,
}
}
+done:
qemuMigrationCookieFree(mig);
rv = 0;
diff --git a/src/qemu/qemu_migration.h b/src/qemu/qemu_migration.h
index 7a2269a..f2dc5aa 100644
--- a/src/qemu/qemu_migration.h
+++ b/src/qemu/qemu_migration.h
@@ -36,7 +36,8 @@
VIR_MIGRATE_NON_SHARED_DISK | \
VIR_MIGRATE_NON_SHARED_INC | \
VIR_MIGRATE_CHANGE_PROTECTION | \
- VIR_MIGRATE_UNSAFE)
+ VIR_MIGRATE_UNSAFE | \
+ VIR_MIGRATE_OFFLINE)
enum qemuMigrationJobPhase {
QEMU_MIGRATION_PHASE_NONE = 0,
@@ -97,7 +98,8 @@ int qemuMigrationPrepareTunnel(struct qemud_driver *driver,
int *cookieoutlen,
virStreamPtr st,
const char *dname,
- const char *dom_xml);
+ const char *dom_xml,
+ unsigned long flags);
int qemuMigrationPrepareDirect(struct qemud_driver *driver,
virConnectPtr dconn,
@@ -108,7 +110,8 @@ int qemuMigrationPrepareDirect(struct qemud_driver *driver,
const char *uri_in,
char **uri_out,
const char *dname,
- const char *dom_xml);
+ const char *dom_xml,
+ unsigned long flags);
int qemuMigrationPerform(struct qemud_driver *driver,
virConnectPtr conn,
diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c
index 393b67b..39674ba 100644
--- a/tools/virsh-domain.c
+++ b/tools/virsh-domain.c
@@ -6644,6 +6644,7 @@ static const vshCmdInfo info_migrate[] = {
static const vshCmdOptDef opts_migrate[] = {
{"live", VSH_OT_BOOL, 0, N_("live migration")},
+ {"offline", VSH_OT_BOOL, 0, N_("offline (domain's inactive) migration")},
{"p2p", VSH_OT_BOOL, 0, N_("peer-2-peer migration")},
{"direct", VSH_OT_BOOL, 0, N_("direct migration")},
{"tunneled", VSH_OT_ALIAS, 0, "tunnelled"},
@@ -6729,6 +6730,10 @@ doMigrate(void *opaque)
if (vshCommandOptBool(cmd, "unsafe"))
flags |= VIR_MIGRATE_UNSAFE;
+ if (vshCommandOptBool(cmd, "offline")) {
+ flags |= VIR_MIGRATE_OFFLINE;
+ }
+
if (xmlfile &&
virFileReadAll(xmlfile, 8192, &xml) < 0) {
vshError(ctl, _("file '%s' doesn't exist"), xmlfile);
diff --git a/tools/virsh.pod b/tools/virsh.pod
index e0c6b42..2545455 100644
--- a/tools/virsh.pod
+++ b/tools/virsh.pod
@@ -1026,13 +1026,14 @@ I<--total> for only the total stats, I<start> for only the per-cpu
stats of the CPUs from I<start>, I<count> for only I<count> CPUs'
stats.
-=item B<migrate> [I<--live>] [I<--direct>] [I<--p2p> [I<--tunnelled>]]
+=item B<migrate> [I<--live>] [I<--offline>] [I<--direct>] [I<--p2p> [I<--tunnelled>]]
[I<--persistent>] [I<--undefinesource>] [I<--suspend>] [I<--copy-storage-all>]
[I<--copy-storage-inc>] [I<--change-protection>] [I<--unsafe>] [I<--verbose>]
I<domain> I<desturi> [I<migrateuri>] [I<dname>]
[I<--timeout> B<seconds>] [I<--xml> B<file>]
-Migrate domain to another host. Add I<--live> for live migration; I<--p2p>
+Migrate domain to another host. Add I<--live> for live migration;
+I<--offline> for offline (domain's inactive) migration; <--p2p>
for peer-2-peer migration; I<--direct> for direct migration; or I<--tunnelled>
for tunnelled migration. I<--persistent> leaves the domain persistent on
destination host, I<--undefinesource> undefines the domain on the source host,
--
1.7.1
12 years