[libvirt] [PATCH 0/3 v3] cleanup: About header including
by Osier Yang
These 3 patches are the left ones in v2. 6/10 ~ 9/10 in v2 are merged
into one single patch.
Osier Yang (3):
syntax-check: Don't include public headers in internal source
syntax-check: Only allows to include public headers in external tools
docs: Update HACKING
cfg.mk | 21 +++++++++++++++++++++
daemon/remote.c | 2 --
docs/hacking.html.in | 8 ++++----
include/libvirt/libvirt-lxc.h | 2 +-
include/libvirt/libvirt-qemu.h | 2 +-
python/libvirt-lxc-override.c | 4 ++--
python/libvirt-override.c | 4 ++--
python/libvirt-qemu-override.c | 4 ++--
python/typewrappers.h | 4 ++--
src/libvirt-qemu.c | 1 -
tests/shunloadhelper.c | 5 ++---
tools/virsh.c | 4 ++--
12 files changed, 39 insertions(+), 22 deletions(-)
--
1.8.1.4
11 years, 7 months
[libvirt] Got invaliProperty error when retrieve properties from ESXi server
by Dennis Zou (yunzou)
Hi team,
When I tried to retrieve the property of storage.perDatastoreUsage under specify virtual machine and got a invalidProperty error but other properties(eg. summary.storage, guest) under the same virtual machine are ok.
Then I write a same program use vsphere web service sdk in java and catch the http package by wireshark.
By comparing, I found that we lost soapAction field in http request header, it seems to like this: SOAPAction: "urn:vim25/5.0".
I checked the vsphere development document and found some key description as following:
When a client application connects to a Web service running on an vSphere server (ESX/ESXi or vCenter Server system), the server detects the version of the API that was used to develop the client and makes available only those operations supported by the client.
Client applications convey information about the API version used in the SOAP messages that they send to a vSphere server. These SOAP messages include a versionID in the soapAction attribute. The details are handled transparently by the SOAP toolkit and the client proxy code. The server adjusts its behavior based on the client's version information, exposing the API version that the client supports to the client.
If you are developing a client application that must support multiple server versions at the same time (ESX/ESXi 5.0 and ESX/ESXi 3.x, for example), you must obtain information about the API versions that are supported on the server and provide logic in your code to use or not use features, based upon the version information.
Finally, I added the soapAction field into http request header and got a correct result. So I added some lines in function esxVI_Context_Connect() at src/esx/esx_vi.c
The code change is bold line as follwing:
int esxVI_Context_Connect(esxVI_Context *ctx, const char *url,
const char *ipAddress, const char *username, const char *password,
esxUtil_ParsedUri *parsedUri) {
char * soapAction = NULL;
int result = -1;
if (ctx == NULL || url == NULL || ipAddress == NULL || username == NULL
|| password == NULL || ctx->url != NULL || ctx->service != NULL
|| ctx->curl != NULL) {
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR, "%s", _("Invalid argument"));
return -1;
}
if (esxVI_CURL_Alloc(&ctx->curl) < 0
|| esxVI_CURL_Connect(ctx->curl, parsedUri) < 0
|| esxVI_String_DeepCopyValue(&ctx->url, url) < 0
|| esxVI_String_DeepCopyValue(&ctx->ipAddress, ipAddress) < 0
|| esxVI_String_DeepCopyValue(&ctx->username, username) < 0
|| esxVI_String_DeepCopyValue(&ctx->password, password) < 0) {
return -1;
}
if (VIR_ALLOC(ctx->sessionLock) < 0) {
virReportOOMError();
return -1;
}
if (virMutexInit(ctx->sessionLock) < 0) {
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR,
"%s", _("Could not initialize session mutex"));
return -1;
}
if (esxVI_RetrieveServiceContent(ctx, &ctx->service) < 0) {
return -1;
}
if (virAsprintf(&soapAction, "SOAPAction: \"urn:vim25/%s\"",
ctx->service->about->apiVersion) < 0) {
virReportOOMError();
goto cleanup;
}
ctx->curl->headers = curl_slist_append(ctx->curl->headers, soapAction);
if (STREQ(ctx->service->about->apiType, "HostAgent")
|| STREQ(ctx->service->about->apiType, "VirtualCenter")) {
if (STRPREFIX(ctx->service->about->apiVersion, "2.5")) {
ctx->apiVersion = esxVI_APIVersion_25;
} else if (STRPREFIX(ctx->service->about->apiVersion, "4.0")) {
ctx->apiVersion = esxVI_APIVersion_40;
} else if (STRPREFIX(ctx->service->about->apiVersion, "4.1")) {
ctx->apiVersion = esxVI_APIVersion_41;
} else if (STRPREFIX(ctx->service->about->apiVersion, "4.")) {
ctx->apiVersion = esxVI_APIVersion_4x;
VIR_WARN(
"Found untested VI API major/minor version '%s'", ctx->service->about->apiVersion);
} else if (STRPREFIX(ctx->service->about->apiVersion, "5.0")) {
ctx->apiVersion = esxVI_APIVersion_50;
} else if (STRPREFIX(ctx->service->about->apiVersion, "5.1")) {
ctx->apiVersion = esxVI_APIVersion_51;
}else if (STRPREFIX(ctx->service->about->apiVersion, "5.")) {
ctx->apiVersion = esxVI_APIVersion_5x;
VIR_WARN(
"Found untested VI API major/minor version '%s'", ctx->service->about->apiVersion);
} else {
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR,
_("Expecting VI API major/minor version '2.5', '4.x' or "
"'5.x' but found '%s'"), ctx->service->about->apiVersion);
return -1;
}
if (STREQ(ctx->service->about->productLineId, "gsx")) {
if (STRPREFIX(ctx->service->about->version, "2.0")) {
ctx->productVersion = esxVI_ProductVersion_GSX20;
} else {
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR,
_("Expecting GSX major/minor version '2.0' but "
"found '%s'"), ctx->service->about->version);
return -1;
}
} else if (STREQ(ctx->service->about->productLineId, "esx")
|| STREQ(ctx->service->about->productLineId, "embeddedEsx")) {
if (STRPREFIX(ctx->service->about->version, "3.5")) {
ctx->productVersion = esxVI_ProductVersion_ESX35;
} else if (STRPREFIX(ctx->service->about->version, "4.0")) {
ctx->productVersion = esxVI_ProductVersion_ESX40;
} else if (STRPREFIX(ctx->service->about->version, "4.1")) {
ctx->productVersion = esxVI_ProductVersion_ESX41;
} else if (STRPREFIX(ctx->service->about->version, "4.")) {
ctx->productVersion = esxVI_ProductVersion_ESX4x;
VIR_WARN(
"Found untested ESX major/minor version '%s'", ctx->service->about->version);
} else if (STRPREFIX(ctx->service->about->version, "5.0")) {
ctx->productVersion = esxVI_ProductVersion_ESX50;
} else if (STRPREFIX(ctx->service->about->version, "5.1")) {
ctx->productVersion = esxVI_ProductVersion_ESX51;
}else if (STRPREFIX(ctx->service->about->version, "5.")) {
ctx->productVersion = esxVI_ProductVersion_ESX5x;
VIR_WARN(
"Found untested ESX major/minor version '%s'", ctx->service->about->version);
} else {
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR,
_("Expecting ESX major/minor version '3.5', "
"'4.x' or '5.x' but found '%s'"), ctx->service->about->version);
return -1;
}
} else if (STREQ(ctx->service->about->productLineId, "vpx")) {
if (STRPREFIX(ctx->service->about->version, "2.5")) {
ctx->productVersion = esxVI_ProductVersion_VPX25;
} else if (STRPREFIX(ctx->service->about->version, "4.0")) {
ctx->productVersion = esxVI_ProductVersion_VPX40;
} else if (STRPREFIX(ctx->service->about->version, "4.1")) {
ctx->productVersion = esxVI_ProductVersion_VPX41;
} else if (STRPREFIX(ctx->service->about->version, "4.")) {
ctx->productVersion = esxVI_ProductVersion_VPX4x;
VIR_WARN(
"Found untested VPX major/minor version '%s'", ctx->service->about->version);
} else if (STRPREFIX(ctx->service->about->version, "5.0")) {
ctx->productVersion = esxVI_ProductVersion_VPX50;
} else if (STRPREFIX(ctx->service->about->version, "5.1")) {
ctx->productVersion = esxVI_ProductVersion_VPX51;
}else if (STRPREFIX(ctx->service->about->version, "5.")) {
ctx->productVersion = esxVI_ProductVersion_VPX5x;
VIR_WARN(
"Found untested VPX major/minor version '%s'", ctx->service->about->version);
} else {
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR,
_("Expecting VPX major/minor version '2.5', '4.x' "
"or '5.x' but found '%s'"), ctx->service->about->version);
return -1;
}
} else {
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR,
_("Expecting product 'gsx' or 'esx' or 'embeddedEsx' "
"or 'vpx' but found '%s'"), ctx->service->about->productLineId);
return -1;
}
} else {
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR,
_("Expecting VI API type 'HostAgent' or 'VirtualCenter' "
"but found '%s'"), ctx->service->about->apiType);
return -1;
}
if (ctx->productVersion & esxVI_ProductVersion_ESX) {
/*
* FIXME: Actually this should be detected by really calling
* QueryVirtualDiskUuid and checking if a NotImplemented fault is
* returned. But currently we don't deserialized the details of a
* possbile fault and therefore we don't know if the fault was a
* NotImplemented fault or not.
*/
ctx->hasQueryVirtualDiskUuid = true;
}
if (ctx->productVersion & esxVI_ProductVersion_VPX) {
ctx->hasSessionIsActive = true;
}
if (esxVI_Login(ctx, username, password, NULL, &ctx->session) < 0
|| esxVI_BuildSelectSetCollection(ctx) < 0) {
return -1;
}
result = 0;
cleanup: VIR_FREE(soapAction);
return result;
}
int esxVI_Context_LookupManagedObjects(esxVI_Context *ctx) {
/* Lookup Datacenter */
if (esxVI_LookupDatacenter(ctx, NULL, ctx->service->rootFolder, NULL,
&ctx->datacenter, esxVI_Occurrence_RequiredItem) < 0) {
return -1;
}
ctx->datacenterPath = strdup(ctx->datacenter->name);
if (ctx->datacenterPath == NULL) {
virReportOOMError();
return -1;
}
/* Lookup (Cluster)ComputeResource */
if (esxVI_LookupComputeResource(ctx, NULL, ctx->datacenter->hostFolder,
NULL, &ctx->computeResource, esxVI_Occurrence_RequiredItem) < 0) {
return -1;
}
if (ctx->computeResource->resourcePool == NULL) {
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR,
"%s", _("Could not retrieve resource pool"));
return -1;
}
ctx->computeResourcePath = strdup(ctx->computeResource->name);
if (ctx->computeResourcePath == NULL) {
virReportOOMError();
return -1;
}
if (STRCASEEQ(ctx->computeResource->_reference->type, "ClusterComputeResource")) {
ctx->computeResourceType = esxVI_ComputeResourceType_Cluster;
} else {
ctx->computeResourceType = esxVI_ComputeResourceType_Basic;
}
/* Lookup HostSystem */
if (esxVI_LookupHostSystem(ctx, NULL, ctx->computeResource->_reference,
NULL, &ctx->hostSystem, esxVI_Occurrence_RequiredItem) < 0) {
return -1;
}
ctx->hostSystemName = strdup(ctx->hostSystem->name);
if (ctx->hostSystemName == NULL) {
virReportOOMError();
return -1;
}
return 0;
}
int esxVI_Context_LookupHostManagedObjects(esxVI_Context *ctx,
virHostNameList *hostNameListPtr) {
int result = -1;
esxVI_Datacenter *datacenters = NULL;
esxVI_Datacenter *candidateDatacenter = NULL;
esxVI_ComputeResource *computeResources = NULL;
esxVI_ComputeResource *candidateComputeResource = NULL;
esxVI_HostSystem *hostSystems = NULL;
esxVI_HostSystem *candidateHostSystem = NULL;
esxVI_HostContext *current = NULL;
virHostNameEntry *hostNameEntry = NULL;
char *hostName = NULL;
/* create hash table for host contexts*/
ctx->hostCtxs = virHashCreate(500, esxVI_HostContext_HashDataFree);
/* list Datacenter */
if (esxVI_ListDatacenter(ctx, NULL, ctx->service->rootFolder, NULL,
&datacenters, esxVI_Occurrence_OptionalList) < 0) {
goto cleanup;
}
for (candidateDatacenter = datacenters; NULL != candidateDatacenter;
candidateDatacenter = candidateDatacenter->_next) {
/* list (Cluster)ComputeResource */
if (esxVI_ListComputeResource(ctx, NULL,
candidateDatacenter->hostFolder, NULL, &computeResources,
esxVI_Occurrence_OptionalList) < 0) {
goto cleanup;
}
for (candidateComputeResource = computeResources;
NULL != candidateComputeResource; candidateComputeResource =
candidateComputeResource->_next) {
/* list HostSystem */
if (esxVI_ListHostSystem(ctx, NULL,
candidateComputeResource->_reference, NULL, &hostSystems,
esxVI_Occurrence_OptionalList) < 0) {
goto cleanup;
}
for (candidateHostSystem = hostSystems; NULL != candidateHostSystem;
) {
/* Allocate a HostContext entry */
if (esxVI_HostContext_Alloc(¤t) < 0
|| esxVI_Datacenter_Alloc(¤t->datacenter) < 0
|| esxVI_ComputeResource_Alloc(
¤t->computeResource) < 0) {
goto cleanup;
}
/* Fill the HostContext entry */
esxVI_ManagedObjectReference_DeepCopy(
¤t->datacenter->_reference,
candidateDatacenter->_reference);
esxVI_ManagedObjectReference_DeepCopy(
¤t->datacenter->hostFolder,
candidateDatacenter->hostFolder);
esxVI_ManagedObjectReference_DeepCopy(
¤t->datacenter->vmFolder,
candidateDatacenter->vmFolder);
current->datacenter->name = strdup(candidateDatacenter->name);
current->datacenterPath = strdup(candidateDatacenter->name);
esxVI_ManagedObjectReference_DeepCopy(
¤t->computeResource->_reference,
candidateComputeResource->_reference);
esxVI_ManagedObjectReference_DeepCopy(
¤t->computeResource->host,
candidateComputeResource->host);
esxVI_ManagedObjectReference_DeepCopy(
¤t->computeResource->resourcePool,
candidateComputeResource->resourcePool);
current->computeResource->name = strdup(
candidateComputeResource->name);
current->computeResourcePath = strdup(
candidateComputeResource->name);
if (STRCASEEQ(candidateComputeResource->_reference->type, "ClusterComputeResource")) {
current->computeResourceType =
esxVI_ComputeResourceType_Cluster;
} else {
current->computeResourceType =
esxVI_ComputeResourceType_Basic;
}
current->hostSystem = candidateHostSystem;
candidateHostSystem = candidateHostSystem->_next;
current->hostSystem->_next = NULL;
current->hostSystemName = strdup(current->hostSystem->name);
hostName = strdup(current->hostSystem->name);
current->maxVcpus = -1;
current->supportsVMotion = esxVI_Boolean_Undefined;
current->supportsLongMode = esxVI_Boolean_Undefined;
current->usedCpuTimeCounterId = -1;
/* update hash table */
if (virHashAddEntry(ctx->hostCtxs, hostName, current) < 0) {
goto cleanup;
}
current = NULL;
if (NULL != hostNameListPtr) {
/* fill the hostNameList */
if (VIR_ALLOC(hostNameEntry) < 0) {
virReportOOMError();
goto cleanup;
}
hostNameEntry->hostName = strdup(hostName);
hostNameEntry->next = *hostNameListPtr;
*hostNameListPtr = hostNameEntry;
hostNameEntry = NULL;
}
free(hostName);
}
hostSystems = NULL;
}
esxVI_ComputeResource_Free(&computeResources);
}
result = 0;
cleanup: esxVI_Datacenter_Free(&datacenters);
esxVI_ComputeResource_Free(&computeResources);
return result;
}
Regards,
Dennis
11 years, 7 months
[libvirt] [PATCH] conf: fix comment about parsing graphics listen address
by Ján Tomko
---
Pushed under the trivial rule.
src/conf/domain_conf.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index 548368e..1643f30 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -7291,7 +7291,7 @@ virDomainGraphicsListenDefParseXML(virDomainGraphicsListenDefPtr def,
if (network && network[0]) {
if (def->type != VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_NETWORK) {
/* network='xxx' never makes sense with anything except
- * type='address' */
+ * type='network' */
virReportError(VIR_ERR_XML_ERROR, "%s",
_("network attribute not allowed when listen type is not network"));
goto error;
--
1.8.1.5
11 years, 7 months
[libvirt] [PATCH] Fix typo in ocaml_libvirt_storage_vol_get_info
by David Scott
The info.capacity was being overwritten with the info.allocation.
Signed-off-by: David Scott <dave.scott(a)eu.citrix.com>
---
libvirt/libvirt_c_oneoffs.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/libvirt/libvirt_c_oneoffs.c b/libvirt/libvirt_c_oneoffs.c
index 80e1c03..01985f5 100644
--- a/libvirt/libvirt_c_oneoffs.c
+++ b/libvirt/libvirt_c_oneoffs.c
@@ -1418,7 +1418,7 @@ ocaml_libvirt_storage_vol_get_info (value volv)
rv = caml_alloc (3, 0);
Store_field (rv, 0, Val_int (info.type));
v = caml_copy_int64 (info.capacity); Store_field (rv, 1, v);
- v = caml_copy_int64 (info.allocation); Store_field (rv, 1, v);
+ v = caml_copy_int64 (info.allocation); Store_field (rv, 2, v);
CAMLreturn (rv);
#else
--
1.8.1.2
11 years, 7 months
[libvirt] [PATCH 0/7] qemu: PCI bridge support
by Ján Tomko
This series allows PCI bridges to be added and used.
After patch 6/7 more buses are usable if the bridges
and addresses are explicitly specified in the XML.
After 7/7 bridges are auto-added and the new buses
are used automatically. (This only works if there is
enough space on bus 0 for the bridges, otherwise they
need to be specified manually).
(Documentation, tests and bridge hotplug is still missing)
Contains slightly modified li guang's patches 1-3:
https://www.redhat.com/archives/libvir-list/2013-February/msg00981.html
and reworked address allocation for multiple buses
https://www.redhat.com/archives/libvir-list/2013-February/msg00793.html
Ján Tomko (5):
qemu: QEMU_PCI constant consistency
qemu: move PCI address check out of qemuPCIAddressAsString
qemu: switch PCI address set from hash table to an array
qemu: Add support for plugging devices into PCI bridges
qemu: auto-add and use bridges
liguang (2):
add pci-bridge controller type
qemu: build command line for pci-bridge device
docs/schemas/domaincommon.rng | 1 +
src/conf/domain_conf.c | 3 +-
src/conf/domain_conf.h | 1 +
src/qemu/qemu_capabilities.c | 2 +
src/qemu/qemu_capabilities.h | 1 +
src/qemu/qemu_command.c | 551 ++++++++++++++++++++++++++++++------------
src/qemu/qemu_command.h | 4 +-
tests/qemuhelptest.c | 21 +-
8 files changed, 421 insertions(+), 163 deletions(-)
--
1.8.1.5
11 years, 7 months
[libvirt] [PATCH] Partially revert "cleanup: Don't include libvirt/libvirt.h"
by Eric Blake
This reverts the changes to remote_protocol.x made in commit
2d25fd4f410f65b40ef1c3d65124775a944f4025. It turns out that
if 'dwarves' is installed, 'make check' fails if any *_LAST
enum values are visible from within the .o file compiled from
a .x file; including <libvirt/libvirt.h> prior to "internal.h"
is the documented way to avoid exposing *_LAST enum elements.
---
Pushing under the build-breaker rule.
src/remote/remote_protocol.x | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x
index d384225..b957b8e 100644
--- a/src/remote/remote_protocol.x
+++ b/src/remote/remote_protocol.x
@@ -36,6 +36,7 @@
* 'REMOTE_'. This makes names quite long.
*/
+%#include <libvirt/libvirt.h>
%#include "internal.h"
%#include <arpa/inet.h>
--
1.8.1.4
11 years, 7 months
[libvirt] [PATCH 00/10 v2] Cleanup: About header including
by Osier Yang
The public headers libvirt/libvirt.h, libvirt/virterror.h,
libvirt/libvirt-qemu.h, and libvirt/libvirt-lxc.h shouldn't
be included in internal source directly.
Osier Yang (10):
cleanup: Remove the duplicate header
cleanup: Only include testutils.h once
cleanup: Don't include libvirt/libvirt.h
cleanup: Don't include libvirt/virterror.h
syntax-check: Don't include duplicate header
syntax-check: Don't include "libvirt.h" and "virterror.h" in "" form
syntax-check: Include libvirt.h and virterror.h in <> form only in
external tools
syntax-check: Don't include libvirt-{qemu,lxc}.h internally
syntax-check: Include libvirt-{qemu,lxc}.h in <> form only in external
tools
docs: Update hacking
cfg.mk | 106 ++++++++++++++++++++++++++++++++++++++
daemon/libvirtd.c | 1 -
daemon/remote.c | 2 -
docs/hacking.html.in | 8 +--
include/libvirt/libvirt-lxc.h | 2 +-
include/libvirt/libvirt-qemu.h | 2 +-
python/libvirt-lxc-override.c | 4 +-
python/libvirt-override.c | 4 +-
python/libvirt-qemu-override.c | 4 +-
python/typewrappers.h | 4 +-
src/conf/node_device_conf.c | 1 -
src/libvirt-qemu.c | 1 -
src/network/bridge_driver.c | 1 -
src/nodeinfo.h | 1 -
src/openvz/openvz_conf.c | 1 -
src/openvz/openvz_driver.c | 1 -
src/parallels/parallels_driver.c | 1 -
src/phyp/phyp_driver.c | 2 -
src/qemu/qemu_capabilities.h | 1 -
src/qemu/qemu_driver.c | 1 -
src/remote/remote_driver.h | 2 -
src/remote/remote_protocol.x | 1 -
src/security/security_selinux.c | 1 -
src/uml/uml_driver.c | 1 -
src/util/virkeycode.h | 1 -
src/util/virnetdevtap.c | 1 -
src/xen/xen_hypervisor.c | 1 -
tests/domainsnapshotxml2xmltest.c | 4 +-
tests/esxutilstest.c | 4 +-
tests/lxcxml2xmltest.c | 4 +-
tests/openvzutilstest.c | 4 +-
tests/qemuargv2xmltest.c | 4 +-
tests/qemuhelptest.c | 4 +-
tests/qemumonitortest.c | 4 +-
tests/qemuxml2argvtest.c | 4 +-
tests/qemuxml2xmltest.c | 4 +-
tests/qemuxmlnstest.c | 4 +-
tests/shunloadhelper.c | 2 -
tests/shunloadtest.c | 4 +-
tests/vmx2xmltest.c | 4 +-
tests/xml2vmxtest.c | 4 +-
tools/virsh.c | 4 +-
42 files changed, 148 insertions(+), 66 deletions(-)
--
1.8.1.4
11 years, 7 months
[libvirt] [PATCH] qemu: Allow the disk wwn to have "0x" prefix
by Osier Yang
The recent qemu requires "0x" prefix for the disk wwn, this patch
changes virValidateWWN to allow the prefix, and prepend "0x" if
it's not specified. E.g.
qemu-kvm: -device scsi-hd,bus=scsi0.0,channel=0,scsi-id=0,lun=0,\
drive=drive-scsi0-0-0-0,id=scsi0-0-0-0,wwn=6000c60016ea71ad:
Property 'scsi-hd.wwn' doesn't take value '6000c60016ea71ad'
---
docs/schemas/basictypes.rng | 2 +-
src/qemu/qemu_command.c | 8 ++++++--
src/util/virutil.c | 12 +++++++++---
tests/qemuxml2argvdata/qemuxml2argv-disk-ide-wwn.args | 2 +-
tests/qemuxml2argvdata/qemuxml2argv-disk-scsi-disk-wwn.args | 4 ++--
tests/qemuxml2argvdata/qemuxml2argv-disk-scsi-disk-wwn.xml | 2 +-
6 files changed, 20 insertions(+), 10 deletions(-)
diff --git a/docs/schemas/basictypes.rng b/docs/schemas/basictypes.rng
index adaedd8..34c2254 100644
--- a/docs/schemas/basictypes.rng
+++ b/docs/schemas/basictypes.rng
@@ -280,7 +280,7 @@
<define name='wwn'>
<data type='string'>
- <param name='pattern'>[0-9a-fA-F]{16}</param>
+ <param name='pattern'>(0x)?[0-9a-fA-F]{16}</param>
</data>
</define>
diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c
index 63b9350..009d42d 100644
--- a/src/qemu/qemu_command.c
+++ b/src/qemu/qemu_command.c
@@ -3358,8 +3358,12 @@ qemuBuildDriveDevStr(virDomainDefPtr def,
disk->blockio.physical_block_size);
}
- if (disk->wwn)
- virBufferAsprintf(&opt, ",wwn=%s", disk->wwn);
+ if (disk->wwn) {
+ if (STRPREFIX(disk->wwn, "0x"))
+ virBufferAsprintf(&opt, ",wwn=%s", disk->wwn);
+ else
+ virBufferAsprintf(&opt, ",wwn=0x%s", disk->wwn);
+ }
if (disk->vendor)
virBufferAsprintf(&opt, ",vendor=%s", disk->vendor);
diff --git a/src/util/virutil.c b/src/util/virutil.c
index 0e4063b..6890362 100644
--- a/src/util/virutil.c
+++ b/src/util/virutil.c
@@ -3228,12 +3228,18 @@ bool virIsDevMapperDevice(const char *dev_name ATTRIBUTE_UNUSED)
bool
virValidateWWN(const char *wwn) {
int i;
+ const char *p = wwn;
- for (i = 0; wwn[i]; i++)
- if (!c_isxdigit(wwn[i]))
+ if (STRPREFIX(wwn, "0x")) {
+ p += 2;
+ }
+
+ for (i = 0; p[i]; i++) {
+ if (!c_isxdigit(p[i]))
break;
+ }
- if (i != 16 || wwn[i]) {
+ if (i != 16 || p[i]) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Malformed wwn: %s"));
return false;
diff --git a/tests/qemuxml2argvdata/qemuxml2argv-disk-ide-wwn.args b/tests/qemuxml2argvdata/qemuxml2argv-disk-ide-wwn.args
index 1633d29..3b9693c 100644
--- a/tests/qemuxml2argvdata/qemuxml2argv-disk-ide-wwn.args
+++ b/tests/qemuxml2argvdata/qemuxml2argv-disk-ide-wwn.args
@@ -2,5 +2,5 @@ LC_ALL=C PATH=/bin HOME=/home/test USER=test LOGNAME=test \
/usr/bin/qemu -S -M pc -m 214 -smp 1 -nographic -nodefaults \
-monitor unix:/tmp/test-monitor,server,nowait -no-acpi -boot c -usb \
-drive file=/dev/HostVG/QEMUGuest1,if=none,id=drive-ide0-0-1,serial=WD-WMAP9A966149 \
--device ide-hd,bus=ide.0,unit=1,drive=drive-ide0-0-1,id=ide0-0-1,wwn=5000c50015ea71ad \
+-device ide-hd,bus=ide.0,unit=1,drive=drive-ide0-0-1,id=ide0-0-1,wwn=0x5000c50015ea71ad \
-device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x3
diff --git a/tests/qemuxml2argvdata/qemuxml2argv-disk-scsi-disk-wwn.args b/tests/qemuxml2argvdata/qemuxml2argv-disk-scsi-disk-wwn.args
index 0393640..0dd2aa9 100644
--- a/tests/qemuxml2argvdata/qemuxml2argv-disk-scsi-disk-wwn.args
+++ b/tests/qemuxml2argvdata/qemuxml2argv-disk-scsi-disk-wwn.args
@@ -5,7 +5,7 @@ LC_ALL=C PATH=/bin HOME=/home/test USER=test LOGNAME=test \
-device lsi,id=scsi1,bus=pci.0,addr=0x4 \
-usb \
-drive file=/dev/HostVG/QEMUGuest1,if=none,id=drive-scsi0-0-1-0 \
--device scsi-cd,bus=scsi0.0,channel=0,scsi-id=1,lun=0,drive=drive-scsi0-0-1-0,id=scsi0-0-1-0,wwn=5000c50015ea71ac \
+-device scsi-cd,bus=scsi0.0,channel=0,scsi-id=1,lun=0,drive=drive-scsi0-0-1-0,id=scsi0-0-1-0,wwn=0x5000c50015ea71ac \
-drive file=/dev/HostVG/QEMUGuest2,if=none,id=drive-scsi0-0-0-0 \
--device scsi-hd,bus=scsi0.0,channel=0,scsi-id=0,lun=0,drive=drive-scsi0-0-0-0,id=scsi0-0-0-0,wwn=5000c50015ea71ad \
+-device scsi-hd,bus=scsi0.0,channel=0,scsi-id=0,lun=0,drive=drive-scsi0-0-0-0,id=scsi0-0-0-0,wwn=0x5000c50015ea71ad \
-device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x5
diff --git a/tests/qemuxml2argvdata/qemuxml2argv-disk-scsi-disk-wwn.xml b/tests/qemuxml2argvdata/qemuxml2argv-disk-scsi-disk-wwn.xml
index dc35548..caf957b 100644
--- a/tests/qemuxml2argvdata/qemuxml2argv-disk-scsi-disk-wwn.xml
+++ b/tests/qemuxml2argvdata/qemuxml2argv-disk-scsi-disk-wwn.xml
@@ -25,7 +25,7 @@
<source dev='/dev/HostVG/QEMUGuest2'/>
<target dev='sdb' bus='scsi'/>
<address type='drive' controller='0' bus='0' target='0' unit='0'/>
- <wwn>5000c50015ea71ad</wwn>
+ <wwn>0x5000c50015ea71ad</wwn>
</disk>
<controller type='usb' index='0'/>
<controller type='scsi' index='0' model='virtio-scsi'/>
--
1.8.1.4
11 years, 7 months
[libvirt] [PATCH] docs: Add detailed notes snapshots, blockcommit, blockpull
by Kashyap Chamarthy
More elaborate notes on snapshots, blockpull, blockcommit. Much of this
is derived from various dicussions with Eric Blake, Jeff Cody, Kevin Wolf
(thanks a lot!) & several others on IRC and mailing lists and a lot of adhoc
testing. I didn't wanted this to get lost.
I also plan to add notes for 'blockcopy' once I complete testing with upstream
libvirt/qemu git.
NOTE: This document is formatted using reStructuredText. And can be trivially
converted to HTML using:
# rst2html snapshots-blockcommit-blockpull.rst > snapshots-blockcommit-blockpull.html
('rst2html' is part of python-docutils package.)
I didn't send an html PATCH directly, as I thought, this'd be more readable.
Any comments, criticisms more than welcome.
---
docs/snapshots-blockcommit-blockpull.rst | 646 ++++++++++++++++++++++++++++++
1 files changed, 646 insertions(+), 0 deletions(-)
create mode 100644 docs/snapshots-blockcommit-blockpull.rst
diff --git a/docs/snapshots-blockcommit-blockpull.rst b/docs/snapshots-blockcommit-blockpull.rst
new file mode 100644
index 0000000000000000000000000000000000000000..99c30223a004ee5291e2914b788ac7fe04eee3c8
--- /dev/null
+++ b/docs/snapshots-blockcommit-blockpull.rst
@@ -0,0 +1,646 @@
+.. ----------------------------------------------------------------------
+ Note: All these tests were performed with latest qemu-git,libvirt-git (as of
+ 20-Oct-2012 on a Fedora-18 alpha machine
+.. ----------------------------------------------------------------------
+
+
+Introduction
+============
+
+A virtual machine snapshot is a view of a virtual machine(its OS & all its
+applications) at a given point in time. So that, one can revert to a known sane
+state, or take backups while the guest is running live. So, before we dive into
+snapshots, let's have an understanding of backing files and overlays.
+
+
+
+QCOW2 backing files & overlays
+------------------------------
+
+In essence, QCOW2(Qemu Copy-On-Write) gives you an ability to create a base-image,
+and create several 'disposable' copy-on-write overlay disk images on top of the
+base image(also called backing file). Backing files and overlays are
+extremely useful to rapidly instantiate thin-privisoned virtual machines(more on
+it below). Especially quite useful in development & test environments, so that
+one could quickly revert to a known state & discard the overlay.
+
+**Figure-1**
+
+::
+
+ .--------------. .-------------. .-------------. .-------------.
+ | | | | | | | |
+ | RootBase |<---| Overlay-1 |<---| Overlay-1A <--- | Overlay-1B |
+ | (raw/qcow2) | | (qcow2) | | (qcow2) | | (qcow2) |
+ '--------------' '-------------' '-------------' '-------------'
+
+The above figure illustrates - RootBase is the backing file for Overlay-1, which
+in turn is backing file for Overlay-2, which in turn is backing file for
+Overlay-3.
+
+**Figure-2**
+::
+
+ .-----------. .-----------. .------------. .------------. .------------.
+ | | | | | | | | | |
+ | RootBase |<--- Overlay-1 |<--- Overlay-1A <--- Overlay-1B <--- Overlay-1C |
+ | | | | | | | | | (Active) |
+ '-----------' '-----------' '------------' '------------' '------------'
+ ^ ^
+ | |
+ | | .-----------. .------------.
+ | | | | | |
+ | '-------| Overlay-2 |<---| Overlay-2A |
+ | | | | (Active) |
+ | '-----------' '------------'
+ |
+ |
+ | .-----------. .------------.
+ | | | | |
+ '------------| Overlay-3 |<---| Overlay-3A |
+ | | | (Active) |
+ '-----------' '------------'
+
+The above figure is just another representation which indicates, we can use a
+'single' backing file, and create several overlays -- which can be used further,
+to create overlays on top of them.
+
+
+**NOTE**: Backing files are always opened **read-only**. In other words, once
+ an overlay is created, its backing file should not be modified(as the
+ overlay depends on a particular state of the backing file). Refer
+ below ('blockcommit' section) for relevant info on this.
+
+
+**Example** :
+
+::
+
+ [FedoraBase.img] ----- <- [Fedora-guest-1.qcow2] <- [Fed-w-updates.qcow2] <- [Fedora-guest-with-updates-1A]
+ \
+ \--- <- [Fedora-guest-2.qcow2] <- [Fed-w-updates.qcow2] <- [Fedora-guest-with-updates-2A]
+
+(Arrow to be read as Fed-w-updates.qcow2 has Fedora-guest-1.qcow2 as its backing file.)
+
+In the above example, say, *FedoraBase.img* has a freshly installed Fedora-17 OS on it,
+and let's establish it as our backing file. Now, FedoraBase can be used as a
+read-only 'template' to quickly instantiate two(or more) thinly provisioned
+Fedora-17 guests(say Fedora-guest-1.qcow2, Fedora-guest-2.qcow2) by creating
+QCOW2 overlay files pointing to our backing file. Also, the example & *Figure-2*
+above illustrate that a single root-base image(FedoraBase.img) can be used
+to create multiple overlays -- which can subsequently have their own overlays.
+
+
+ To create two thinly-provisioned Fedora clones(or overlays) using a single
+ backing file, we can invoke qemu-img as below: ::
+
+
+ # qemu-img create -b /export/vmimages/RootBase.img -f qcow2 \
+ /export/vmimages/Fedora-guest-1.qcow2
+
+ # qemu-img create -b /export/vmimages/RootBase.img -f qcow2 \
+ /export/vmimages/Fedora-guest-2.qcow2
+
+ Now, both the above images *Fedora-guest-1* & *Fedora-guest-2* are ready to
+ boot. Continuting with our example, say, now you want to instantiate a
+ Fedora-17 guest, but this time, with full Fedora updates. This can be
+ accomplished by creating another overlay(Fedora-guest-with-updates-1A) - but
+ this overly would point to 'Fed-w-updates.qcow2' as its backing file (which
+ has the full Fedora updates) ::
+
+ # qemu-img create -b /export/vmimages/Fed-w-updates.qcow2 -f qcow2 \
+ /export/vmimages/Fedora-guest-with-updates-1A.qcow2
+
+
+ Information about a disk image, like virtual size, disk size, backing file(if it
+ exists) can be obtained by using 'qemu-img' as below:
+ ::
+
+ # qemu-img info /export/vmimages/Fedora-guest-with-updates-1A.qcow2
+
+ NOTE: With latest qemu, an entire backing chain can be recursively
+ enumerated by doing:
+ ::
+
+ # qemu-img info --backing-chain /export/vmimages/Fedora-guest-with-updates-1A.qcow2
+
+
+
+Snapshot Terminology:
+---------------------
+
+ - **Internal Snapshots** -- A single qcow2 image file holds both the saved state
+ & the delta since that saved point. This can be further classified as :-
+
+ (1) **Internal disk snapshot**: The state of the virtual disk at a given
+ point in time. Both the snapshot & delta since the snapshot are
+ stored in the same qcow2 file. Can be taken when the guest is 'live'
+ or 'offline'.
+
+ - Libvirt uses QEMU's 'qemu-img' command when the guest is 'offline'.
+ - Libvirt uses QEMU's 'savevm' command when the guest is 'live'.
+
+ (2) **Internal system checkpoint**: RAM state, device state & the
+ disk-state of a running guest, are all stored in the same originial
+ qcow2 file. Can be taken when the guest is running 'live'.
+
+ - Libvirt uses QEMU's 'savevm' command when the guest is 'live'
+
+
+ - **External Snapshots** -- Here, when a snapshot is taken, the saved state will
+ be stored in one file(from that point, it becomes a read-only backing
+ file) & a new file(overlay) will track the deltas from that saved state.
+ This can be further classified as :-
+
+ (1) **External disk snapshot**: The snapshot of the disk is saved in one
+ file, and the delta since the snapshot is tracked in a new qcow2
+ file. Can be taken when the guest is 'live' or 'offline'.
+
+ - Libvirt uses QEMU's 'transaction' cmd under the hood, when the
+ guest is 'live'.
+
+ - Libvirt uses QEMU's 'qemu-img' cmd under the hood when the
+ guest is 'offline'(this implementation is in progress, as of
+ writing this).
+
+ (2) **External system checkpoint**: Here, the guest's disk-state will be
+ saved in one file, its RAM & device-state will be saved in another
+ new file (This implementation is in progress upstream libvirt, as of
+ writing this).
+
+
+
+ - **VM State**: Saves the RAM & device state of a running guest(not 'disk-state') to
+ a file, so that it can be restored later. This simliar to doing hibernate
+ of the system. (NOTE: The disk-state should be unmodified at the time of
+ restoration.)
+
+ - Libvirt uses QEMU's 'migrate' (to file) cmd under the hood.
+
+
+
+Creating snapshots
+==================
+ - Whenever an 'external' snapshot is issued, a /new/ overlay image is
+ created to facilitate guest writes, and the previous image becomes a
+ snapshot.
+
+ - **Create a disk-only internal snapshot**
+
+ (1) If I have a guest named 'f17vm1', to create an offline or online
+ 'internal' snapshot called 'snap1' with description 'snap1-desc' ::
+
+ # virsh snapshot-create-as f17vm1 snap1 snap1-desc
+
+ (2) List the snapshot ; and query using *qemu-img* tool to view
+ the image info & its internal snapshot details ::
+
+ # virsh snapshot-list f17vm1
+ # qemu-img info /home/kashyap/vmimages/f17vm1.qcow2
+
+
+
+ - **Create a disk-only external snapshot** :
+
+ (1) List the block device associated with the guest. ::
+
+ # virsh domblklist f17-base
+ Target Source
+ ---------------------------------------------
+ vda /export/vmimages/f17-base.qcow2
+
+ #
+
+ (2) Create external disk-only snapshot (while the guest is *running*). ::
+
+ # virsh snapshot-create-as --domain f17-base snap1 snap1-desc \
+ --disk-only --diskspec vda,snapshot=external,file=/export/vmimages/sn1-of-f17-base.qcow2 \
+ --atomic
+ Domain snapshot snap1 created
+ #
+
+ * Once the above command is issued, the original disk-image
+ of f17-base will become the backing_file & a new overlay
+ image is created to track the new changes. Here on, libvirt
+ will use this overlay for further write operations(while
+ using the original image as a read-only backing_file).
+
+ (3) Now, list the block device associated(use cmd from step-1, above)
+ with the guest,again, to ensure it reflects the new overlay image as
+ the current block device in use. ::
+
+ # virsh domblklist f17-base
+ Target Source
+ ----------------------------------------------------
+ vda /export/vmimages/sn1-of-f17-base.qcow2
+
+ #
+
+
+
+
+Reverting to snapshots
+======================
+As of writing this, reverting to 'Internal Snapshots'(system checkpoint or
+disk-only) is possible.
+
+ To revert to a snapshot named 'snap1' of domain f17vm1 ::
+
+ # virsh snapshot-revert --domain f17vm1 snap1
+
+Reverting to 'external disk snapshots' using *snapshot-revert* is a little more
+tricky, as it involves slightly complicated process of dealing with additional
+snapshot files - whether to merge 'base' images into 'top' or to merge other way
+round ('top' into 'base').
+
+That said, there are a couple of ways to deal with external snapshot files by
+merging them to reduce the external snapshot disk image chain by performing
+either a **blockpull** or **blockcommit** (more on this below).
+
+Further improvements on this front is in work upstream libvirt as of writing
+this.
+
+
+
+Merging snapshot files
+======================
+External snapshots are incredibly useful. But, with plenty of external snapshot
+files, there comes a problem of maintaining and tracking all these inidivdual
+files. At a later point in time, we might want to 'merge' some of these snapshot
+files (either backing_files into overlays or vice-versa) to reduce the length of
+the image chain. To accomplish that, there are two mechanisms:
+
+ + blockcommit: merges data from **top** into **base** (in other
+ words, merge overlays into backing files).
+
+
+ + blockpull: Populates a disk image with data from its backing file. Or
+ merges data from **base** into **top** (in other words, merge backing files
+ into overlays).
+
+
+blockcommit
+-----------
+
+Block Commit allows you to merge from a 'top' image(within a disk backing file
+chain) into a lower-level 'base' image. To rephrase, it allows you to
+merge overlays into backing files. Once the **blockcommit** operation is finished,
+any portion that depends on the 'top' image, will now be pointing to the 'base'.
+
+This is useful in flattening(or collapsing or reducing) backing file chain
+length after taking several external snapshots.
+
+
+Let's understand with an illustration below:
+
+We have a base image called 'RootBase', which has a disk image chain with 4
+external snapshots. With 'Active' as the current active-layer, where 'live' guest
+writes happen. There are a few possibilities of resulting image chains that we
+can end up with, using 'blockcommit' :
+
+ (1) Data from Snap-1, Snap-2 and Snap-3 can be merged into 'RootBase'
+ (resulting in RootBase becoming the backing_file of 'Active', and thus
+ invalidating Snap-1, Snap-2, & Snap-3).
+
+ (2) Data from Snap-1 and Snap-2 can be merged into RootBase(resulting in
+ Rootbase becoming the backing_file of Snap-3, and thus invalidating
+ Snap-1 & Snap-2).
+
+ (3) Data from Snap-1 can be merged into RootBase(resulting in RootBase
+ becoming the backing_file of Snap-2, and thus invalidating Snap-1).
+
+ (4) Data from Snap-2 can be merged into Snap-1(resulting in Snap-1 becoming
+ the backing_file of Snap-3, and thus invalidating Snap-2).
+
+ (5) Data from Snap-3 can be merged into Snap-2(resulting in Snap-2 becoming
+ the backing_file for 'Active', and thus invalidating Snap-3).
+
+ (6) Data from Snap-2 and Snap-3 can be merged into Snap-1(resulting in
+ Snap-1 becoming the backing_file of 'Active', and thus invalidating
+ Snap-2 & Snap-3).
+
+ NOTE: Eventually(not supported in qemu as of writing this), we can also
+ merge down the 'Active' layer(the top-most overlay) into its
+ backing_files. Once it is supported, the 'top' argument can become
+ optional, and default to active layer.
+
+
+(The below figure illustrates case (6) from the above)
+
+**Figure-3**
+::
+
+ .------------. .------------. .------------. .------------. .------------.
+ | | | | | | | | | |
+ | RootBase <--- Snap-1 <--- Snap-2 <--- Snap-3 <--- Snap-4 |
+ | | | | | | | | | (Active) |
+ '------------' '------------' '------------' '------------' '------------'
+ / |
+ / |
+ / commit data |
+ / |
+ / |
+ / |
+ v commit data |
+ .------------. .------------. <--------------------' .------------.
+ | | | | | |
+ | RootBase <--- Snap-1 |<---------------------------------| Snap-4 |
+ | | | | Backing File | (Active) |
+ '------------' '------------' '------------'
+
+For instance, if we have the below scenario:
+
+ Actual: [base] <- sn1 <- sn2 <- sn3 <- sn4(this is active)
+
+ Desired: [base] <- sn1 <- sn4 (thus invalidating sn2,sn3)
+
+ Any of the below two methods is valid (as of 17-Oct-2012 qemu-git). With
+ method-a, operation will be faster & correct if we don't care about
+ sn2(because, it'll be invalidated). Note that, method-b is slower, but sn2
+ will remain valid. (Also note that, the guest is 'live' in all these cases).
+
+ **(method-a)**:
+ ::
+
+ # virsh blockcommit --domain f17 vda --base /export/vmimages/sn1.qcow2 --top /export/vmimages/sn3.qcow2 --wait --verbose
+
+ [OR]
+
+ **(method-b)**:
+ ::
+
+ # virsh blockcommit --domain f17 vda --base /export/vmimages/sn2.qcow2 --top /export/vmimages/sn3.qcow2 --wait --verbose
+ # virsh blockcommit --domain f17 vda --base /export/vmimages/sn1.qcow2 --top /export/vmimages/sn2.qcow2 --wait --verbose
+
+ NOTE: If we had to do manually with *qemu-img* cmd, we can only do method-b at the moment.
+
+
+**Figure-4**
+::
+
+ .------------. .------------. .------------. .------------. .------------.
+ | | | | | | | | | |
+ | RootBase <--- Snap-1 <--- Snap-2 <--- Snap-3 <--- Snap-4 |
+ | | | | | | | | | (Active) |
+ '------------' '------------' '------------' '------------' '------------'
+ / | |
+ / | |
+ / | |
+ commit data / commit data | |
+ / | |
+ / | commit data |
+ v | |
+ .------------.<----------------------|-------------' .------------.
+ | |<----------------------' | |
+ | RootBase | | Snap-4 |
+ | |<-------------------------------------------------| (Active) |
+ '------------' Backing File '------------'
+
+
+The above figure is another representation of reducing the disk image chain
+using blockcommit. Data from Snap-1, Snap-2, Snap-3 are merged(/committed)
+into RootBase, & now the current 'Active' image now pointing to 'RootBase' as its
+backing file(instead of Snap-3, which was the case *before* blockcommit). Note
+that, now intermediate images Snap-1, Snap-1, Snap-3 will be invalidated(as they were
+dependent on a particular state of RootBase).
+
+blockpull
+---------
+Block Pull(also called 'Block Stream' in QEMU's paralance) allows you to merge
+into 'base' from a 'top' image(within a disk backing file chain). To rephrase it
+allows merging backing files into an overlay(active). This works in the
+opposite side of 'blockcommit' to flatten the snapshot chain. At the moment,
+**blockpull** can pull only into the active layer(the top-most image). It's
+worth noting here that, intermediate images are not invalidated once a blockpull
+operation is complete (while blockcommit, invalidates them).
+
+
+Consider the below illustration:
+
+**Figure-5**
+::
+
+ .------------. .------------. .------------. .------------. .------------.
+ | | | | | | | | | |
+ | RootBase <--- Snap-1 <--- Snap-2 <--- Snap-3 <--- Snap-4 |
+ | | | | | | | | | (Active) |
+ '------------' '------------' '------------' '------------' '------------'
+ | | \
+ | | \
+ | | \
+ | | \ stream data
+ | | stream data \
+ | stream data | \
+ | | v
+ .------------. | '---------------> .------------.
+ | | '---------------------------------> | |
+ | RootBase | | Snap-4 |
+ | | <---------------------------------------- | (Active) |
+ '------------' Backing File '------------'
+
+
+
+The above figure illustrates that, using block-copy we can pull data from
+Snap-1, Snap-2 and Snap-3 into the 'Active' layer, resulting in 'RootBase'
+becoming the backing file for the 'Active' image (instead of 'Snap-3', which was
+the case before doing the blockpull operation).
+
+The command flow would be:
+ (1) Assuming a external disk-only snapshot was created as mentioned in
+ *Creating Snapshots* section:
+
+ (2) A blockpull operation can be issued this way, to achieve the desired
+ state of *Figure-5*-- [RootBase] <- [Active]. ::
+
+ # virsh blockpull --domain RootBase --path var/lib/libvirt/images/active.qcow2 --base /var/lib/libvirt/images/RootBase.qcow2 --wait --verbose
+
+
+ As a follow up, we can do the below to clean-up the snapshot *tracking*
+ metadata by libvirt (note: the below does not 'remove' the files, it
+ just cleans up the snapshot tracking metadata). ::
+
+ # virsh snapshot-delete --domain RootBase Snap-3 --metadata
+ # virsh snapshot-delete --domain RootBase Snap-2 --metadata
+ # virsh snapshot-delete --domain RootBase Snap-1 --metadata
+
+
+
+
+**Figure-6**
+::
+
+ .------------. .------------. .------------. .------------. .------------.
+ | | | | | | | | | |
+ | RootBase <--- Snap-1 <--- Snap-2 <--- Snap-3 <--- Snap-4 |
+ | | | | | | | | | (Active) |
+ '------------' '------------' '------------' '------------' '------------'
+ | | | \
+ | | | \
+ | | | \ stream data
+ | | | stream data \
+ | | | \
+ | | stream data | \
+ | stream data | '------------------> v
+ | | .--------------.
+ | '---------------------------------> | |
+ | | Snap-4 |
+ '----------------------------------------------------> | (Active) |
+ '--------------'
+ 'Standalone'
+ (w/o backing
+ file)
+
+The above figure illustrates, once blockpull operation is complete, by
+pulling/streaming data from RootBase, Snap-1, Snap-2, Snap-3 into 'Active', all
+the backing files can be discarded and 'Active' now will be a standalone image
+without any backing files.
+
+Command flow would be:
+ (0) Assuming 4 external disk-only (live) snapshots were created as
+ mentioned in *Creating Snapshots* section,
+
+ (1) Let's check the snapshot overlay images size *before* blockpull operation (note the image of 'Active'):
+ ::
+
+ # ls -lash /var/lib/libvirt/images/RootBase.img
+ 608M -rw-r--r--. 1 qemu qemu 1.0G Oct 11 17:54 /var/lib/libvirt/images/RootBase.img
+
+ # ls -lash /var/lib/libvirt/images/*Snap*
+ 840K -rw-------. 1 qemu qemu 896K Oct 11 17:56 /var/lib/libvirt/images/Snap-1.qcow2
+ 392K -rw-------. 1 qemu qemu 448K Oct 11 17:56 /var/lib/libvirt/images/Snap-2.qcow2
+ 456K -rw-------. 1 qemu qemu 512K Oct 11 17:56 /var/lib/libvirt/images/Snap-3.qcow2
+ 2.9M -rw-------. 1 qemu qemu 3.0M Oct 11 18:10 /var/lib/libvirt/images/Active.qcow2
+
+ (2) Also, check the disk image information of 'Active'. It can noticed that
+ 'Active' has Snap-3 as its backing file. ::
+
+ # qemu-img info /var/lib/libvirt/images/Active.qcow2
+ image: /var/lib/libvirt/images/Active.qcow2
+ file format: qcow2
+ virtual size: 1.0G (1073741824 bytes)
+ disk size: 2.9M
+ cluster_size: 65536
+ backing file: /var/lib/libvirt/images/Snap-3.qcow2
+
+ (3) Do the **blockpull** operation. ::
+
+ # virsh blockpull --domain ptest2-base --path /var/lib/libvirt/images/Active.qcow2 --wait --verbose
+ Block Pull: [100 %]
+ Pull complete
+
+ (4) Let's again check the snapshot overlay images size *after*
+ blockpull operation. It can be noticed, 'Active' is now considerably larger. ::
+
+ # ls -lash /var/lib/libvirt/images/*Snap*
+ 840K -rw-------. 1 qemu qemu 896K Oct 11 17:56 /var/lib/libvirt/images/Snap-1.qcow2
+ 392K -rw-------. 1 qemu qemu 448K Oct 11 17:56 /var/lib/libvirt/images/Snap-2.qcow2
+ 456K -rw-------. 1 qemu qemu 512K Oct 11 17:56 /var/lib/libvirt/images/Snap-3.qcow2
+ 1011M -rw-------. 1 qemu qemu 3.0M Oct 11 18:29 /var/lib/libvirt/images/Active.qcow2
+
+
+ (5) Also, check the disk image information of 'Active'. It can now be
+ noticed that 'Active' is a standalone image without any backing file -
+ which is the desired state of *Figure-6*.::
+
+ # qemu-img info /var/lib/libvirt/images/Active.qcow2
+ image: /var/lib/libvirt/images/Active.qcow2
+ file format: qcow2
+ virtual size: 1.0G (1073741824 bytes)
+ disk size: 1.0G
+ cluster_size: 65536
+
+ (6) We can now clean-up the snapshot tracking metadata by libvirt to
+ reflect the new reality ::
+
+ # virsh snapshot-delete --domain RootBase Snap-3 --metadata
+
+ (7) Optionally, one can check, the guest disk contents by invoking
+ *guestfish* tool(part of *libguestfs*) **READ-ONLY** (*--ro* option
+ below does it) as below ::
+
+ # guestfish --ro -i -a /var/lib/libvirt/images/Active.qcow2
+
+
+Deleting snapshots (and 'offline commit')
+=========================================
+
+Deleting (live/offline) *Internal Snapshots* (where the originial & all the named snapshots
+are stored in a single QCOW2 file), is quite straight forward. ::
+
+ # virsh snapshot-delete --domain f17vm --snapshotname snap6
+
+ [OR]
+
+ # virsh snapshot-delete f17vm snap6
+
+Deleting External snapshots (offline), Libvirt has not acquired the capability.
+But, it can be done via *qemu-img* manipulation.
+
+Say, we have this image chain(the guest is *offline* here): **base <- sn1 <- sn2 <- sn3**
+(arrow to be read as 'sn3 has sn2 as its backing file').
+
+
+And, we want to delete the second snapshot(sn2). It's possible to do it in two
+ways:
+
+
+ - **Method (1)**: **base <- sn1 <- sn3** (by copying sn2 into sn1)
+ - **Method (2)**: **base <- sn1 <- sn3** (by copying sn2 into sn3)
+
+Method (1)
+----------
+To end up with this image chain : **base <- sn1 <- sn3** (by copying *sn2* into *sn1*)
+
+**NOTE**: This is only possible *if* sn1 isn't used by more images as their backing
+file, or they'd get corrupted!!
+
+ (a) We're doing an *offline commit* (similar to what *blockcommit* can do
+ to an *online* guest). ::
+
+ # qemu-img commit sn2.qcow2
+
+ - This will *commit* the changes from sn2 into its backing file(which is
+ sn1).
+
+ (b) Now that we've comitted changes from sn2 into sn1, let's change the
+ backing file link in sn3 to point to sn1. ::
+
+ # qemu-img rebase -u -b sn1.qcow2 sn3.qcow2
+
+ - **NOTE**: This is 'Unsafe mode' -- in this mode, only the backing file
+ name is changed w/o any checks on the file contents. The user must
+ take care of specifying the correct new backing file, or the
+ guest-visible. This mode is useful for renaming or moving the
+ backing file to somewhere else. It can be used without an
+ accessible old backing file, i.e. you can use it to fix an image
+ whose backing file has already been moved/renamed.
+
+
+ (c) Now, we can delete the sn2 disk image(as the changes are now committed
+ to sn1). ::
+
+ # rm sn2.qcow2
+
+
+Method (2)
+----------
+To end up with this image chain : **base <- sn1 <- sn3** (by copying *sn2* into *sn3*)
+
+ (a) Copy contents of sn2(the old backing file) into sn3, and change the backing file link of sn3 to sn1::
+
+ # qemu-img rebase -b sn1.qcow2 sn3.qcow2
+
+ - Apart from changing backing file link of sn3 to sn1, the above cmd
+ will it also /copy/ the contents from sn2 into sn3).
+
+ - In other words: This is 'Safe mode', which is the default --
+ any clusters that differ between the new backing_file(in this
+ case, sn1) and the old backing file(in this case, sn2) of
+ filename(in this case, sn3) are merged into filename(sn3), before
+ actually changing the backing file.
+
+ (b) Now, we can delete the sn2 disk image(as the changes are now committed to
+ sn1). ::
+
+ # rm sn2.qcow2
+
--
1.7.7.6
11 years, 7 months
[libvirt] [libvirt-php] Allow block devices in libvirt_domain_disk_add
by Martijn Otto
I have written a patch to the libvirt_domain_disk_add function which
does the following things:
1. Allow adding block devices as disks with libvirt_domain_disk_add
2. Removed the xflags parameter (not relevant as no xml is returned
anyways)
3. Added the cache option to control cache mode (either default,
none, writeback or writethrough)
4. Added the io option to control the used io mode (either default,
native or threads)
Besides this, I have rewritten it so it now uses the
virDomainAttachDevice function, which is meant for these things, instead
of pasting the device XML by hand into the domain XML.
I have uploaded this to my github clone at
https://github.com/martijnotto/libvirt-php
It would be nice if this could be included in a future release of
lbvirt-php. If the code does not meet a coding standard for libvirt-php
or if there is some other concern, please let me know and I will do what
I can to improve it.
With Regards,
Martijn Otto
Copernica B.V.
11 years, 7 months