2017-03-13 11:46:18 +00:00
|
|
|
/*
|
|
|
|
* qemu_block.c: helper functions for QEMU block subsystem
|
|
|
|
*
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This library is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* Lesser General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
|
|
* License along with this library. If not, see
|
|
|
|
* <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <config.h>
|
|
|
|
|
|
|
|
#include "qemu_block.h"
|
2018-07-03 12:13:29 +00:00
|
|
|
#include "qemu_command.h"
|
2017-03-13 11:46:18 +00:00
|
|
|
#include "qemu_domain.h"
|
2017-09-25 09:44:00 +00:00
|
|
|
#include "qemu_alias.h"
|
2019-11-19 07:43:58 +00:00
|
|
|
#include "qemu_security.h"
|
2017-03-13 11:46:18 +00:00
|
|
|
|
|
|
|
#include "viralloc.h"
|
|
|
|
#include "virstring.h"
|
2018-05-16 11:39:22 +00:00
|
|
|
#include "virlog.h"
|
2017-03-13 11:46:18 +00:00
|
|
|
|
|
|
|
#define VIR_FROM_THIS VIR_FROM_QEMU
|
|
|
|
|
2018-05-16 11:39:22 +00:00
|
|
|
VIR_LOG_INIT("qemu.qemu_block");
|
|
|
|
|
2018-03-28 07:04:49 +00:00
|
|
|
/* qemu declares the buffer for node names as a 32 byte array */
|
|
|
|
static const size_t qemuBlockNodeNameBufSize = 32;
|
|
|
|
|
|
|
|
static int
|
|
|
|
qemuBlockNodeNameValidate(const char *nn)
|
|
|
|
{
|
|
|
|
if (!nn)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (strlen(nn) >= qemuBlockNodeNameBufSize) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("node-name '%s' too long for qemu"), nn);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-03-13 11:46:18 +00:00
|
|
|
|
2017-07-25 15:32:38 +00:00
|
|
|
static int
|
2019-10-14 12:45:33 +00:00
|
|
|
qemuBlockNamedNodesArrayToHash(size_t pos G_GNUC_UNUSED,
|
2017-07-25 15:32:38 +00:00
|
|
|
virJSONValuePtr item,
|
|
|
|
void *opaque)
|
|
|
|
{
|
|
|
|
virHashTablePtr table = opaque;
|
|
|
|
const char *name;
|
|
|
|
|
|
|
|
if (!(name = virJSONValueObjectGetString(item, "node-name")))
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
if (virHashAddEntry(table, name, item) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-03-13 11:46:18 +00:00
|
|
|
static void
|
|
|
|
qemuBlockNodeNameBackingChainDataFree(qemuBlockNodeNameBackingChainDataPtr data)
|
|
|
|
{
|
|
|
|
if (!data)
|
|
|
|
return;
|
|
|
|
|
|
|
|
VIR_FREE(data->nodeformat);
|
|
|
|
VIR_FREE(data->nodestorage);
|
|
|
|
|
|
|
|
VIR_FREE(data->qemufilename);
|
2017-07-26 07:36:21 +00:00
|
|
|
|
2017-07-25 17:10:27 +00:00
|
|
|
VIR_FREE(data->drvformat);
|
|
|
|
VIR_FREE(data->drvstorage);
|
|
|
|
|
2017-07-26 07:36:21 +00:00
|
|
|
qemuBlockNodeNameBackingChainDataFree(data->backing);
|
2017-03-13 11:46:18 +00:00
|
|
|
|
|
|
|
VIR_FREE(data);
|
|
|
|
}
|
|
|
|
|
2019-10-15 12:47:50 +00:00
|
|
|
G_DEFINE_AUTOPTR_CLEANUP_FUNC(qemuBlockNodeNameBackingChainData,
|
2019-04-04 09:18:07 +00:00
|
|
|
qemuBlockNodeNameBackingChainDataFree);
|
|
|
|
|
2017-03-13 11:46:18 +00:00
|
|
|
|
|
|
|
static void
|
2019-11-21 19:27:58 +00:00
|
|
|
qemuBlockNodeNameBackingChainDataHashEntryFree(void *opaque)
|
2017-03-13 11:46:18 +00:00
|
|
|
{
|
|
|
|
qemuBlockNodeNameBackingChainDataFree(opaque);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-07-26 07:36:21 +00:00
|
|
|
/* list of driver names of layers that qemu automatically adds into the
|
|
|
|
* backing chain */
|
|
|
|
static const char *qemuBlockDriversBlockjob[] = {
|
|
|
|
"mirror_top", "commit_top", NULL };
|
2017-03-13 11:46:18 +00:00
|
|
|
|
|
|
|
static bool
|
|
|
|
qemuBlockDriverMatch(const char *drvname,
|
|
|
|
const char **drivers)
|
|
|
|
{
|
|
|
|
while (*drivers) {
|
|
|
|
if (STREQ(drvname, *drivers))
|
|
|
|
return true;
|
|
|
|
|
|
|
|
drivers++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-07-26 07:36:21 +00:00
|
|
|
struct qemuBlockNodeNameGetBackingChainData {
|
|
|
|
virHashTablePtr nodenamestable;
|
|
|
|
virHashTablePtr disks;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2017-03-13 11:46:18 +00:00
|
|
|
static int
|
2017-07-26 07:36:21 +00:00
|
|
|
qemuBlockNodeNameGetBackingChainBacking(virJSONValuePtr next,
|
|
|
|
virHashTablePtr nodenamestable,
|
|
|
|
qemuBlockNodeNameBackingChainDataPtr *nodenamedata)
|
2017-03-13 11:46:18 +00:00
|
|
|
{
|
2019-10-15 12:47:50 +00:00
|
|
|
g_autoptr(qemuBlockNodeNameBackingChainData) data = NULL;
|
2017-07-26 07:36:21 +00:00
|
|
|
qemuBlockNodeNameBackingChainDataPtr backingdata = NULL;
|
|
|
|
virJSONValuePtr backing = virJSONValueObjectGetObject(next, "backing");
|
|
|
|
virJSONValuePtr parent = virJSONValueObjectGetObject(next, "parent");
|
|
|
|
virJSONValuePtr parentnodedata;
|
|
|
|
virJSONValuePtr nodedata;
|
|
|
|
const char *nodename = virJSONValueObjectGetString(next, "node-name");
|
2017-07-25 17:10:27 +00:00
|
|
|
const char *drvname = NULL;
|
|
|
|
const char *drvparent = NULL;
|
2017-07-26 07:36:21 +00:00
|
|
|
const char *parentnodename = NULL;
|
|
|
|
const char *filename = NULL;
|
2017-03-13 11:46:18 +00:00
|
|
|
|
2017-07-26 07:36:21 +00:00
|
|
|
if (!nodename)
|
2017-03-13 11:46:18 +00:00
|
|
|
return 0;
|
|
|
|
|
2017-07-26 07:36:21 +00:00
|
|
|
if ((nodedata = virHashLookup(nodenamestable, nodename)) &&
|
|
|
|
(drvname = virJSONValueObjectGetString(nodedata, "drv"))) {
|
|
|
|
|
|
|
|
/* qemu 2.9 reports layers in the backing chain which don't correspond
|
|
|
|
* to files. skip them */
|
|
|
|
if (qemuBlockDriverMatch(drvname, qemuBlockDriversBlockjob)) {
|
|
|
|
if (backing) {
|
|
|
|
return qemuBlockNodeNameGetBackingChainBacking(backing,
|
|
|
|
nodenamestable,
|
|
|
|
nodenamedata);
|
|
|
|
} else {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-03-13 11:46:18 +00:00
|
|
|
|
2017-07-26 07:36:21 +00:00
|
|
|
if (parent &&
|
|
|
|
(parentnodename = virJSONValueObjectGetString(parent, "node-name"))) {
|
2017-07-25 17:10:27 +00:00
|
|
|
if ((parentnodedata = virHashLookup(nodenamestable, parentnodename))) {
|
2017-07-26 07:36:21 +00:00
|
|
|
filename = virJSONValueObjectGetString(parentnodedata, "file");
|
2017-07-25 17:10:27 +00:00
|
|
|
drvparent = virJSONValueObjectGetString(parentnodedata, "drv");
|
|
|
|
}
|
2017-07-26 07:36:21 +00:00
|
|
|
}
|
2017-03-13 11:46:18 +00:00
|
|
|
|
2017-07-26 07:36:21 +00:00
|
|
|
if (VIR_ALLOC(data) < 0)
|
2019-04-04 09:31:05 +00:00
|
|
|
return -1;
|
2017-03-13 11:46:18 +00:00
|
|
|
|
2019-10-20 11:49:46 +00:00
|
|
|
data->nodeformat = g_strdup(nodename);
|
|
|
|
data->nodestorage = g_strdup(parentnodename);
|
|
|
|
data->qemufilename = g_strdup(filename);
|
|
|
|
data->drvformat = g_strdup(drvname);
|
|
|
|
data->drvstorage = g_strdup(drvparent);
|
2017-03-13 11:46:18 +00:00
|
|
|
|
2017-07-26 07:36:21 +00:00
|
|
|
if (backing &&
|
|
|
|
qemuBlockNodeNameGetBackingChainBacking(backing, nodenamestable,
|
|
|
|
&backingdata) < 0)
|
2019-04-04 09:31:05 +00:00
|
|
|
return -1;
|
2017-03-13 11:46:18 +00:00
|
|
|
|
2019-10-16 11:43:18 +00:00
|
|
|
data->backing = g_steal_pointer(&backingdata);
|
|
|
|
*nodenamedata = g_steal_pointer(&data);
|
2017-03-13 11:46:18 +00:00
|
|
|
|
2019-04-04 09:31:05 +00:00
|
|
|
return 0;
|
2017-03-13 11:46:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
2019-10-14 12:45:33 +00:00
|
|
|
qemuBlockNodeNameGetBackingChainDisk(size_t pos G_GNUC_UNUSED,
|
2017-07-26 07:36:21 +00:00
|
|
|
virJSONValuePtr item,
|
|
|
|
void *opaque)
|
2017-03-13 11:46:18 +00:00
|
|
|
{
|
2017-07-26 07:36:21 +00:00
|
|
|
struct qemuBlockNodeNameGetBackingChainData *data = opaque;
|
|
|
|
const char *device = virJSONValueObjectGetString(item, "device");
|
2019-10-15 12:47:50 +00:00
|
|
|
g_autoptr(qemuBlockNodeNameBackingChainData) devicedata = NULL;
|
2017-03-13 11:46:18 +00:00
|
|
|
|
2017-07-26 07:36:21 +00:00
|
|
|
if (qemuBlockNodeNameGetBackingChainBacking(item, data->nodenamestable,
|
|
|
|
&devicedata) < 0)
|
2019-04-04 09:31:05 +00:00
|
|
|
return -1;
|
2017-03-13 11:46:18 +00:00
|
|
|
|
2017-07-26 07:36:21 +00:00
|
|
|
if (devicedata &&
|
|
|
|
virHashAddEntry(data->disks, device, devicedata) < 0)
|
2019-04-04 09:31:05 +00:00
|
|
|
return -1;
|
2017-03-13 11:46:18 +00:00
|
|
|
|
2017-07-26 07:36:21 +00:00
|
|
|
devicedata = NULL;
|
2019-04-04 09:31:05 +00:00
|
|
|
return 1; /* we don't really want to steal @item */
|
2017-03-13 11:46:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* qemuBlockNodeNameGetBackingChain:
|
2017-07-26 07:36:21 +00:00
|
|
|
* @namednodes: JSON array of data returned from 'query-named-block-nodes'
|
|
|
|
* @blockstats: JSON array of data returned from 'query-blockstats'
|
2017-03-13 11:46:18 +00:00
|
|
|
*
|
|
|
|
* Tries to reconstruct the backing chain from @json to allow detection of
|
|
|
|
* node names that were auto-assigned by qemu. This is a best-effort operation
|
|
|
|
* and may not be successful. The returned hash table contains the entries as
|
|
|
|
* qemuBlockNodeNameBackingChainDataPtr accessible by the node name. The fields
|
|
|
|
* then can be used to recover the full backing chain.
|
|
|
|
*
|
|
|
|
* Returns a hash table on success and NULL on failure.
|
|
|
|
*/
|
|
|
|
virHashTablePtr
|
2017-07-26 07:36:21 +00:00
|
|
|
qemuBlockNodeNameGetBackingChain(virJSONValuePtr namednodes,
|
|
|
|
virJSONValuePtr blockstats)
|
2017-03-13 11:46:18 +00:00
|
|
|
{
|
|
|
|
struct qemuBlockNodeNameGetBackingChainData data;
|
2019-10-15 12:47:50 +00:00
|
|
|
g_autoptr(virHashTable) namednodestable = NULL;
|
|
|
|
g_autoptr(virHashTable) disks = NULL;
|
2017-03-13 11:46:18 +00:00
|
|
|
|
|
|
|
memset(&data, 0, sizeof(data));
|
|
|
|
|
2017-07-26 07:36:21 +00:00
|
|
|
if (!(namednodestable = virHashCreate(50, virJSONValueHashFree)))
|
2019-04-04 09:31:05 +00:00
|
|
|
return NULL;
|
2017-03-13 11:46:18 +00:00
|
|
|
|
2017-07-26 07:36:21 +00:00
|
|
|
if (virJSONValueArrayForeachSteal(namednodes,
|
|
|
|
qemuBlockNamedNodesArrayToHash,
|
|
|
|
namednodestable) < 0)
|
2019-04-04 09:31:05 +00:00
|
|
|
return NULL;
|
2017-03-13 11:46:18 +00:00
|
|
|
|
2017-07-26 07:36:21 +00:00
|
|
|
if (!(disks = virHashCreate(50, qemuBlockNodeNameBackingChainDataHashEntryFree)))
|
2019-04-04 09:31:05 +00:00
|
|
|
return NULL;
|
2017-03-13 11:46:18 +00:00
|
|
|
|
2017-07-26 07:36:21 +00:00
|
|
|
data.nodenamestable = namednodestable;
|
|
|
|
data.disks = disks;
|
2017-03-13 11:46:18 +00:00
|
|
|
|
2017-07-26 07:36:21 +00:00
|
|
|
if (virJSONValueArrayForeachSteal(blockstats,
|
|
|
|
qemuBlockNodeNameGetBackingChainDisk,
|
|
|
|
&data) < 0)
|
2019-04-04 09:31:05 +00:00
|
|
|
return NULL;
|
2017-03-13 11:46:18 +00:00
|
|
|
|
2019-10-16 11:35:54 +00:00
|
|
|
return g_steal_pointer(&disks);
|
2017-03-13 11:46:18 +00:00
|
|
|
}
|
2017-03-15 12:03:21 +00:00
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
qemuBlockDiskClearDetectedNodes(virDomainDiskDefPtr disk)
|
|
|
|
{
|
|
|
|
virStorageSourcePtr next = disk->src;
|
|
|
|
|
2017-10-12 17:27:40 +00:00
|
|
|
while (virStorageSourceIsBacking(next)) {
|
2017-03-15 12:03:21 +00:00
|
|
|
VIR_FREE(next->nodeformat);
|
2017-07-25 16:11:58 +00:00
|
|
|
VIR_FREE(next->nodestorage);
|
2017-03-15 12:03:21 +00:00
|
|
|
|
|
|
|
next = next->backingStore;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
qemuBlockDiskDetectNodes(virDomainDiskDefPtr disk,
|
2017-07-26 07:36:21 +00:00
|
|
|
virHashTablePtr disktable)
|
2017-03-15 12:03:21 +00:00
|
|
|
{
|
|
|
|
qemuBlockNodeNameBackingChainDataPtr entry = NULL;
|
|
|
|
virStorageSourcePtr src = disk->src;
|
2019-10-15 13:16:31 +00:00
|
|
|
g_autofree char *alias = NULL;
|
2017-09-25 09:44:00 +00:00
|
|
|
int ret = -1;
|
2017-03-15 12:03:21 +00:00
|
|
|
|
2017-07-26 07:36:21 +00:00
|
|
|
/* don't attempt the detection if the top level already has node names */
|
|
|
|
if (src->nodeformat || src->nodestorage)
|
|
|
|
return 0;
|
2017-03-15 12:03:21 +00:00
|
|
|
|
2018-05-31 09:55:24 +00:00
|
|
|
if (!(alias = qemuAliasDiskDriveFromDisk(disk)))
|
2017-09-25 09:44:00 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (!(entry = virHashLookup(disktable, alias))) {
|
|
|
|
ret = 0;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
2017-09-25 09:39:55 +00:00
|
|
|
|
2017-10-12 17:27:40 +00:00
|
|
|
while (virStorageSourceIsBacking(src) && entry) {
|
2017-07-25 16:11:58 +00:00
|
|
|
if (src->nodeformat || src->nodestorage) {
|
2017-03-15 12:03:21 +00:00
|
|
|
if (STRNEQ_NULLABLE(src->nodeformat, entry->nodeformat) ||
|
2017-07-25 16:11:58 +00:00
|
|
|
STRNEQ_NULLABLE(src->nodestorage, entry->nodestorage))
|
2017-09-25 09:44:00 +00:00
|
|
|
goto cleanup;
|
2017-03-15 12:03:21 +00:00
|
|
|
|
|
|
|
break;
|
|
|
|
} else {
|
2019-10-20 11:49:46 +00:00
|
|
|
src->nodeformat = g_strdup(entry->nodeformat);
|
|
|
|
src->nodestorage = g_strdup(entry->nodestorage);
|
2017-03-15 12:03:21 +00:00
|
|
|
}
|
|
|
|
|
2017-07-26 07:36:21 +00:00
|
|
|
entry = entry->backing;
|
2017-03-15 12:03:21 +00:00
|
|
|
src = src->backingStore;
|
|
|
|
}
|
|
|
|
|
2017-09-25 09:44:00 +00:00
|
|
|
ret = 0;
|
2017-03-15 12:03:21 +00:00
|
|
|
|
2017-09-25 09:44:00 +00:00
|
|
|
cleanup:
|
|
|
|
if (ret < 0)
|
|
|
|
qemuBlockDiskClearDetectedNodes(disk);
|
|
|
|
|
|
|
|
return ret;
|
2017-03-15 12:03:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
qemuBlockNodeNamesDetect(virQEMUDriverPtr driver,
|
2017-04-07 11:06:24 +00:00
|
|
|
virDomainObjPtr vm,
|
|
|
|
qemuDomainAsyncJob asyncJob)
|
2017-03-15 12:03:21 +00:00
|
|
|
{
|
|
|
|
qemuDomainObjPrivatePtr priv = vm->privateData;
|
2019-10-15 12:47:50 +00:00
|
|
|
g_autoptr(virHashTable) disktable = NULL;
|
|
|
|
g_autoptr(virJSONValue) data = NULL;
|
|
|
|
g_autoptr(virJSONValue) blockstats = NULL;
|
2017-03-15 12:03:21 +00:00
|
|
|
virDomainDiskDefPtr disk;
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
if (!virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_QUERY_NAMED_BLOCK_NODES))
|
|
|
|
return 0;
|
|
|
|
|
2017-04-07 11:06:24 +00:00
|
|
|
if (qemuDomainObjEnterMonitorAsync(driver, vm, asyncJob) < 0)
|
|
|
|
return -1;
|
2017-03-15 12:03:21 +00:00
|
|
|
|
|
|
|
data = qemuMonitorQueryNamedBlockNodes(qemuDomainGetMonitor(vm));
|
2018-07-30 15:08:37 +00:00
|
|
|
blockstats = qemuMonitorQueryBlockstats(qemuDomainGetMonitor(vm));
|
2017-03-15 12:03:21 +00:00
|
|
|
|
2017-07-26 07:36:21 +00:00
|
|
|
if (qemuDomainObjExitMonitor(driver, vm) < 0 || !data || !blockstats)
|
2019-04-04 09:31:05 +00:00
|
|
|
return -1;
|
2017-03-15 12:03:21 +00:00
|
|
|
|
2017-07-26 07:36:21 +00:00
|
|
|
if (!(disktable = qemuBlockNodeNameGetBackingChain(data, blockstats)))
|
2019-04-04 09:31:05 +00:00
|
|
|
return -1;
|
2017-03-15 12:03:21 +00:00
|
|
|
|
|
|
|
for (i = 0; i < vm->def->ndisks; i++) {
|
|
|
|
disk = vm->def->disks[i];
|
|
|
|
|
2017-07-26 07:36:21 +00:00
|
|
|
if (qemuBlockDiskDetectNodes(disk, disktable) < 0)
|
2019-04-04 09:31:05 +00:00
|
|
|
return -1;
|
2017-03-15 12:03:21 +00:00
|
|
|
}
|
|
|
|
|
2019-04-04 09:31:05 +00:00
|
|
|
return 0;
|
2017-03-15 12:03:21 +00:00
|
|
|
}
|
2017-03-16 11:30:16 +00:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* qemuBlockGetNodeData:
|
|
|
|
* @data: JSON object returned from query-named-block-nodes
|
|
|
|
*
|
|
|
|
* Returns a hash table organized by the node name of the JSON value objects of
|
|
|
|
* data for given qemu block nodes.
|
|
|
|
*
|
|
|
|
* Returns a filled virHashTablePtr on success NULL on error.
|
|
|
|
*/
|
|
|
|
virHashTablePtr
|
|
|
|
qemuBlockGetNodeData(virJSONValuePtr data)
|
|
|
|
{
|
2019-10-15 12:47:50 +00:00
|
|
|
g_autoptr(virHashTable) nodedata = NULL;
|
2017-03-16 11:30:16 +00:00
|
|
|
|
2019-04-04 09:03:24 +00:00
|
|
|
if (!(nodedata = virHashCreate(50, virJSONValueHashFree)))
|
2017-03-16 11:30:16 +00:00
|
|
|
return NULL;
|
|
|
|
|
2017-07-25 15:32:38 +00:00
|
|
|
if (virJSONValueArrayForeachSteal(data,
|
2019-04-04 09:03:24 +00:00
|
|
|
qemuBlockNamedNodesArrayToHash, nodedata) < 0)
|
2019-04-04 09:31:05 +00:00
|
|
|
return NULL;
|
2017-03-16 11:30:16 +00:00
|
|
|
|
2019-10-16 11:35:54 +00:00
|
|
|
return g_steal_pointer(&nodedata);
|
2017-03-16 11:30:16 +00:00
|
|
|
}
|
2017-07-07 14:29:01 +00:00
|
|
|
|
|
|
|
|
2017-11-14 14:34:46 +00:00
|
|
|
/**
|
|
|
|
* qemuBlockStorageSourceSupportsConcurrentAccess:
|
|
|
|
* @src: disk storage source
|
|
|
|
*
|
|
|
|
* Returns true if the given storage format supports concurrent access from two
|
|
|
|
* separate processes.
|
|
|
|
*/
|
|
|
|
bool
|
|
|
|
qemuBlockStorageSourceSupportsConcurrentAccess(virStorageSourcePtr src)
|
|
|
|
{
|
|
|
|
/* no need to check in backing chain since only RAW storage supports this */
|
|
|
|
return src->format == VIR_STORAGE_FILE_RAW;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-07-13 13:38:50 +00:00
|
|
|
/**
|
|
|
|
* qemuBlockStorageSourceGetURI:
|
|
|
|
* @src: disk storage source
|
|
|
|
*
|
|
|
|
* Formats a URI from a virStorageSource.
|
|
|
|
*/
|
|
|
|
virURIPtr
|
|
|
|
qemuBlockStorageSourceGetURI(virStorageSourcePtr src)
|
|
|
|
{
|
2019-10-15 12:47:50 +00:00
|
|
|
g_autoptr(virURI) uri = NULL;
|
2017-07-13 13:38:50 +00:00
|
|
|
|
|
|
|
if (src->nhosts != 1) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("protocol '%s' accepts only one host"),
|
|
|
|
virStorageNetProtocolTypeToString(src->protocol));
|
2019-04-04 09:31:05 +00:00
|
|
|
return NULL;
|
2017-07-13 13:38:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (VIR_ALLOC(uri) < 0)
|
2019-04-04 09:31:05 +00:00
|
|
|
return NULL;
|
2017-07-13 13:38:50 +00:00
|
|
|
|
|
|
|
if (src->hosts->transport == VIR_STORAGE_NET_HOST_TRANS_TCP) {
|
|
|
|
uri->port = src->hosts->port;
|
|
|
|
|
2019-10-20 11:49:46 +00:00
|
|
|
uri->scheme = g_strdup(virStorageNetProtocolTypeToString(src->protocol));
|
2017-07-13 13:38:50 +00:00
|
|
|
} else {
|
2019-10-22 13:26:14 +00:00
|
|
|
uri->scheme = g_strdup_printf("%s+%s",
|
|
|
|
virStorageNetProtocolTypeToString(src->protocol),
|
|
|
|
virStorageNetHostTransportTypeToString(src->hosts->transport));
|
2017-07-13 13:38:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (src->path) {
|
|
|
|
if (src->volume) {
|
2019-10-22 13:26:14 +00:00
|
|
|
uri->path = g_strdup_printf("/%s/%s", src->volume, src->path);
|
2017-07-13 13:38:50 +00:00
|
|
|
} else {
|
2019-10-22 13:26:14 +00:00
|
|
|
uri->path = g_strdup_printf("%s%s", src->path[0] == '/' ? "" : "/",
|
|
|
|
src->path);
|
2017-07-13 13:38:50 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-20 11:49:46 +00:00
|
|
|
uri->server = g_strdup(src->hosts->name);
|
2017-07-13 13:38:50 +00:00
|
|
|
|
2019-10-16 11:35:54 +00:00
|
|
|
return g_steal_pointer(&uri);
|
2017-07-13 13:38:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-09-12 11:43:31 +00:00
|
|
|
/**
|
|
|
|
* qemuBlockStorageSourceBuildJSONSocketAddress
|
|
|
|
* @host: the virStorageNetHostDefPtr definition to build
|
2018-02-12 14:44:11 +00:00
|
|
|
* @legacy: use old field names/values
|
2017-09-12 11:43:31 +00:00
|
|
|
*
|
|
|
|
* Formats @hosts into a json object conforming to the 'SocketAddress' type
|
|
|
|
* in qemu.
|
|
|
|
*
|
2018-02-12 14:44:11 +00:00
|
|
|
* For compatibility with old approach used in the gluster driver of old qemus
|
|
|
|
* use the old spelling for TCP transport and, the path field of the unix socket.
|
|
|
|
*
|
2017-09-12 11:43:31 +00:00
|
|
|
* Returns a virJSONValuePtr for a single server.
|
|
|
|
*/
|
|
|
|
static virJSONValuePtr
|
|
|
|
qemuBlockStorageSourceBuildJSONSocketAddress(virStorageNetHostDefPtr host,
|
|
|
|
bool legacy)
|
|
|
|
{
|
2019-10-15 12:47:50 +00:00
|
|
|
g_autoptr(virJSONValue) server = NULL;
|
2017-09-12 11:43:31 +00:00
|
|
|
const char *transport;
|
2018-02-12 14:44:11 +00:00
|
|
|
const char *field;
|
2019-10-15 13:16:31 +00:00
|
|
|
g_autofree char *port = NULL;
|
2017-09-12 11:43:31 +00:00
|
|
|
|
|
|
|
switch ((virStorageNetHostTransport) host->transport) {
|
|
|
|
case VIR_STORAGE_NET_HOST_TRANS_TCP:
|
|
|
|
if (legacy)
|
|
|
|
transport = "tcp";
|
|
|
|
else
|
|
|
|
transport = "inet";
|
|
|
|
|
2019-10-22 13:26:14 +00:00
|
|
|
port = g_strdup_printf("%u", host->port);
|
2017-09-12 11:43:31 +00:00
|
|
|
|
|
|
|
if (virJSONValueObjectCreate(&server,
|
|
|
|
"s:type", transport,
|
|
|
|
"s:host", host->name,
|
|
|
|
"s:port", port,
|
|
|
|
NULL) < 0)
|
2019-04-04 09:31:05 +00:00
|
|
|
return NULL;
|
2017-09-12 11:43:31 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case VIR_STORAGE_NET_HOST_TRANS_UNIX:
|
2018-02-12 14:44:11 +00:00
|
|
|
if (legacy)
|
|
|
|
field = "s:socket";
|
|
|
|
else
|
|
|
|
field = "s:path";
|
|
|
|
|
2017-09-12 11:43:31 +00:00
|
|
|
if (virJSONValueObjectCreate(&server,
|
|
|
|
"s:type", "unix",
|
2018-02-12 14:44:11 +00:00
|
|
|
field, host->socket,
|
2017-09-12 11:43:31 +00:00
|
|
|
NULL) < 0)
|
2019-04-04 09:31:05 +00:00
|
|
|
return NULL;
|
2017-09-12 11:43:31 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case VIR_STORAGE_NET_HOST_TRANS_RDMA:
|
|
|
|
case VIR_STORAGE_NET_HOST_TRANS_LAST:
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("transport protocol '%s' is not yet supported"),
|
|
|
|
virStorageNetHostTransportTypeToString(host->transport));
|
2019-04-04 09:31:05 +00:00
|
|
|
return NULL;
|
2017-09-12 11:43:31 +00:00
|
|
|
}
|
|
|
|
|
2019-10-16 11:35:54 +00:00
|
|
|
return g_steal_pointer(&server);
|
2017-09-12 11:43:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-07-07 15:55:04 +00:00
|
|
|
/**
|
|
|
|
* qemuBlockStorageSourceBuildHostsJSONSocketAddress:
|
|
|
|
* @src: disk storage source
|
|
|
|
* @legacy: use 'tcp' instead of 'inet' for compatibility reasons
|
|
|
|
*
|
|
|
|
* Formats src->hosts into a json object conforming to the 'SocketAddress' type
|
|
|
|
* in qemu.
|
|
|
|
*/
|
2017-07-07 14:29:01 +00:00
|
|
|
static virJSONValuePtr
|
2017-07-07 15:55:04 +00:00
|
|
|
qemuBlockStorageSourceBuildHostsJSONSocketAddress(virStorageSourcePtr src,
|
|
|
|
bool legacy)
|
2017-07-07 14:29:01 +00:00
|
|
|
{
|
2019-10-15 12:47:50 +00:00
|
|
|
g_autoptr(virJSONValue) servers = NULL;
|
|
|
|
g_autoptr(virJSONValue) server = NULL;
|
2017-07-07 14:29:01 +00:00
|
|
|
virStorageNetHostDefPtr host;
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
if (!(servers = virJSONValueNewArray()))
|
2019-04-04 09:31:05 +00:00
|
|
|
return NULL;
|
2017-07-07 14:29:01 +00:00
|
|
|
|
|
|
|
for (i = 0; i < src->nhosts; i++) {
|
|
|
|
host = src->hosts + i;
|
|
|
|
|
2017-09-12 11:43:31 +00:00
|
|
|
if (!(server = qemuBlockStorageSourceBuildJSONSocketAddress(host, legacy)))
|
2019-04-04 09:31:05 +00:00
|
|
|
return NULL;
|
2017-07-07 14:29:01 +00:00
|
|
|
|
|
|
|
if (virJSONValueArrayAppend(servers, server) < 0)
|
2019-04-04 09:31:05 +00:00
|
|
|
return NULL;
|
2017-07-07 14:29:01 +00:00
|
|
|
|
|
|
|
server = NULL;
|
|
|
|
}
|
|
|
|
|
2019-10-16 11:35:54 +00:00
|
|
|
return g_steal_pointer(&servers);
|
2017-07-07 14:29:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-10-23 16:02:28 +00:00
|
|
|
/**
|
|
|
|
* qemuBlockStorageSourceBuildJSONInetSocketAddress
|
|
|
|
* @host: the virStorageNetHostDefPtr definition to build
|
|
|
|
*
|
|
|
|
* Formats @hosts into a json object conforming to the 'InetSocketAddress' type
|
|
|
|
* in qemu.
|
|
|
|
*
|
|
|
|
* Returns a virJSONValuePtr for a single server.
|
|
|
|
*/
|
|
|
|
static virJSONValuePtr
|
|
|
|
qemuBlockStorageSourceBuildJSONInetSocketAddress(virStorageNetHostDefPtr host)
|
|
|
|
{
|
|
|
|
virJSONValuePtr ret = NULL;
|
2019-10-15 13:16:31 +00:00
|
|
|
g_autofree char *port = NULL;
|
2017-10-23 16:02:28 +00:00
|
|
|
|
|
|
|
if (host->transport != VIR_STORAGE_NET_HOST_TRANS_TCP) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("only TCP protocol can be converted to InetSocketAddress"));
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2019-10-22 13:26:14 +00:00
|
|
|
port = g_strdup_printf("%u", host->port);
|
2017-10-23 16:02:28 +00:00
|
|
|
|
|
|
|
ignore_value(virJSONValueObjectCreate(&ret,
|
|
|
|
"s:host", host->name,
|
|
|
|
"s:port", port,
|
|
|
|
NULL));
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-10-23 16:16:53 +00:00
|
|
|
/**
|
|
|
|
* qemuBlockStorageSourceBuildHostsJSONInetSocketAddress:
|
|
|
|
* @src: disk storage source
|
|
|
|
*
|
|
|
|
* Formats src->hosts into a json object conforming to the 'InetSocketAddress'
|
|
|
|
* type in qemu.
|
|
|
|
*/
|
|
|
|
static virJSONValuePtr
|
|
|
|
qemuBlockStorageSourceBuildHostsJSONInetSocketAddress(virStorageSourcePtr src)
|
|
|
|
{
|
2019-10-15 12:47:50 +00:00
|
|
|
g_autoptr(virJSONValue) servers = NULL;
|
|
|
|
g_autoptr(virJSONValue) server = NULL;
|
2017-10-23 16:16:53 +00:00
|
|
|
virStorageNetHostDefPtr host;
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
if (!(servers = virJSONValueNewArray()))
|
2019-04-04 09:31:05 +00:00
|
|
|
return NULL;
|
2017-10-23 16:16:53 +00:00
|
|
|
|
|
|
|
for (i = 0; i < src->nhosts; i++) {
|
|
|
|
host = src->hosts + i;
|
|
|
|
|
|
|
|
if (!(server = qemuBlockStorageSourceBuildJSONInetSocketAddress(host)))
|
2019-04-04 09:31:05 +00:00
|
|
|
return NULL;
|
2017-10-23 16:16:53 +00:00
|
|
|
|
|
|
|
if (virJSONValueArrayAppend(servers, server) < 0)
|
2019-04-04 09:31:05 +00:00
|
|
|
return NULL;
|
2017-10-23 16:16:53 +00:00
|
|
|
|
|
|
|
server = NULL;
|
|
|
|
}
|
|
|
|
|
2019-10-16 11:35:54 +00:00
|
|
|
return g_steal_pointer(&servers);
|
2017-10-23 16:16:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-07-07 14:29:01 +00:00
|
|
|
static virJSONValuePtr
|
2018-03-22 15:42:47 +00:00
|
|
|
qemuBlockStorageSourceGetGlusterProps(virStorageSourcePtr src,
|
2018-09-04 16:52:02 +00:00
|
|
|
bool legacy,
|
|
|
|
bool onlytarget)
|
2017-07-07 14:29:01 +00:00
|
|
|
{
|
2019-10-15 12:47:50 +00:00
|
|
|
g_autoptr(virJSONValue) servers = NULL;
|
|
|
|
g_autoptr(virJSONValue) props = NULL;
|
2017-07-07 14:29:01 +00:00
|
|
|
|
2018-03-22 15:42:47 +00:00
|
|
|
if (!(servers = qemuBlockStorageSourceBuildHostsJSONSocketAddress(src, legacy)))
|
2017-07-07 14:29:01 +00:00
|
|
|
return NULL;
|
|
|
|
|
|
|
|
/* { driver:"gluster",
|
|
|
|
* volume:"testvol",
|
|
|
|
* path:"/a.img",
|
|
|
|
* server :[{type:"tcp", host:"1.2.3.4", port:24007},
|
|
|
|
* {type:"unix", socket:"/tmp/glusterd.socket"}, ...]}
|
|
|
|
*/
|
2017-11-23 15:07:47 +00:00
|
|
|
if (virJSONValueObjectCreate(&props,
|
2017-07-07 14:29:01 +00:00
|
|
|
"s:volume", src->volume,
|
|
|
|
"s:path", src->path,
|
2018-03-30 09:12:57 +00:00
|
|
|
"a:server", &servers, NULL) < 0)
|
2019-04-04 09:31:05 +00:00
|
|
|
return NULL;
|
2017-11-23 15:07:47 +00:00
|
|
|
|
2018-09-04 16:52:02 +00:00
|
|
|
if (!onlytarget &&
|
|
|
|
src->debug &&
|
2017-11-23 15:07:47 +00:00
|
|
|
virJSONValueObjectAdd(props, "u:debug", src->debugLevel, NULL) < 0)
|
2019-04-04 09:31:05 +00:00
|
|
|
return NULL;
|
2017-11-23 15:07:47 +00:00
|
|
|
|
2019-10-16 11:35:54 +00:00
|
|
|
return g_steal_pointer(&props);
|
2017-07-07 14:29:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
qemu: Add qemu command line generation for a VxHS block device
The VxHS block device will only use the newer formatting options and
avoid the legacy URI syntax.
An excerpt for a sample QEMU command line is:
-drive file.driver=vxhs,file.vdisk-id=eb90327c-8302-4725-9e1b-4e85ed4dc251,\
file.server.type=tcp,file.server.host=192.168.0.1,\
file.server.port=9999,format=raw,if=none,id=drive-virtio-disk0,cache=none \
-device virtio-blk-pci,bus=pci.0,addr=0x4,drive=drive-virtio-disk0,\
id=virtio-disk0
Update qemuxml2argvtest with a simple test.
Signed-off-by: Ashish Mittal <Ashish.Mittal@veritas.com>
Signed-off-by: John Ferlan <jferlan@redhat.com>
2017-08-30 13:46:53 +00:00
|
|
|
static virJSONValuePtr
|
2018-09-04 16:52:02 +00:00
|
|
|
qemuBlockStorageSourceGetVxHSProps(virStorageSourcePtr src,
|
|
|
|
bool onlytarget)
|
qemu: Add qemu command line generation for a VxHS block device
The VxHS block device will only use the newer formatting options and
avoid the legacy URI syntax.
An excerpt for a sample QEMU command line is:
-drive file.driver=vxhs,file.vdisk-id=eb90327c-8302-4725-9e1b-4e85ed4dc251,\
file.server.type=tcp,file.server.host=192.168.0.1,\
file.server.port=9999,format=raw,if=none,id=drive-virtio-disk0,cache=none \
-device virtio-blk-pci,bus=pci.0,addr=0x4,drive=drive-virtio-disk0,\
id=virtio-disk0
Update qemuxml2argvtest with a simple test.
Signed-off-by: Ashish Mittal <Ashish.Mittal@veritas.com>
Signed-off-by: John Ferlan <jferlan@redhat.com>
2017-08-30 13:46:53 +00:00
|
|
|
{
|
2019-10-15 12:47:50 +00:00
|
|
|
g_autoptr(virJSONValue) server = NULL;
|
2018-09-04 16:52:02 +00:00
|
|
|
const char *tlsAlias = src->tlsAlias;
|
qemu: Add qemu command line generation for a VxHS block device
The VxHS block device will only use the newer formatting options and
avoid the legacy URI syntax.
An excerpt for a sample QEMU command line is:
-drive file.driver=vxhs,file.vdisk-id=eb90327c-8302-4725-9e1b-4e85ed4dc251,\
file.server.type=tcp,file.server.host=192.168.0.1,\
file.server.port=9999,format=raw,if=none,id=drive-virtio-disk0,cache=none \
-device virtio-blk-pci,bus=pci.0,addr=0x4,drive=drive-virtio-disk0,\
id=virtio-disk0
Update qemuxml2argvtest with a simple test.
Signed-off-by: Ashish Mittal <Ashish.Mittal@veritas.com>
Signed-off-by: John Ferlan <jferlan@redhat.com>
2017-08-30 13:46:53 +00:00
|
|
|
virJSONValuePtr ret = NULL;
|
|
|
|
|
|
|
|
if (src->nhosts != 1) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("VxHS protocol accepts only one host"));
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2017-10-23 16:02:28 +00:00
|
|
|
if (!(server = qemuBlockStorageSourceBuildJSONInetSocketAddress(&src->hosts[0])))
|
qemu: Add qemu command line generation for a VxHS block device
The VxHS block device will only use the newer formatting options and
avoid the legacy URI syntax.
An excerpt for a sample QEMU command line is:
-drive file.driver=vxhs,file.vdisk-id=eb90327c-8302-4725-9e1b-4e85ed4dc251,\
file.server.type=tcp,file.server.host=192.168.0.1,\
file.server.port=9999,format=raw,if=none,id=drive-virtio-disk0,cache=none \
-device virtio-blk-pci,bus=pci.0,addr=0x4,drive=drive-virtio-disk0,\
id=virtio-disk0
Update qemuxml2argvtest with a simple test.
Signed-off-by: Ashish Mittal <Ashish.Mittal@veritas.com>
Signed-off-by: John Ferlan <jferlan@redhat.com>
2017-08-30 13:46:53 +00:00
|
|
|
return NULL;
|
|
|
|
|
2018-09-04 16:52:02 +00:00
|
|
|
if (onlytarget)
|
|
|
|
tlsAlias = NULL;
|
|
|
|
|
qemu: Add qemu command line generation for a VxHS block device
The VxHS block device will only use the newer formatting options and
avoid the legacy URI syntax.
An excerpt for a sample QEMU command line is:
-drive file.driver=vxhs,file.vdisk-id=eb90327c-8302-4725-9e1b-4e85ed4dc251,\
file.server.type=tcp,file.server.host=192.168.0.1,\
file.server.port=9999,format=raw,if=none,id=drive-virtio-disk0,cache=none \
-device virtio-blk-pci,bus=pci.0,addr=0x4,drive=drive-virtio-disk0,\
id=virtio-disk0
Update qemuxml2argvtest with a simple test.
Signed-off-by: Ashish Mittal <Ashish.Mittal@veritas.com>
Signed-off-by: John Ferlan <jferlan@redhat.com>
2017-08-30 13:46:53 +00:00
|
|
|
/* VxHS disk specification example:
|
|
|
|
* { driver:"vxhs",
|
qemu: Add TLS support for Veritas HyperScale (VxHS)
Alter qemu command line generation in order to possibly add TLS for
a suitably configured domain.
Sample TLS args generated by libvirt -
-object tls-creds-x509,id=objvirtio-disk0_tls0,dir=/etc/pki/qemu,\
endpoint=client,verify-peer=yes \
-drive file.driver=vxhs,file.tls-creds=objvirtio-disk0_tls0,\
file.vdisk-id=eb90327c-8302-4725-9e1b-4e85ed4dc251,\
file.server.type=tcp,file.server.host=192.168.0.1,\
file.server.port=9999,format=raw,if=none,\
id=drive-virtio-disk0,cache=none \
-device virtio-blk-pci,bus=pci.0,addr=0x4,drive=drive-virtio-disk0,\
id=virtio-disk0
Update the qemuxml2argvtest with a couple of examples. One for a
simple case and the other a bit more complex where multiple VxHS disks
are added where at least one uses a VxHS that doesn't require TLS
credentials and thus sets the domain disk source attribute "tls = 'no'".
Update the hotplug to be able to handle processing the tlsAlias whether
it's to add the TLS object when hotplugging a disk or to remove the TLS
object when hot unplugging a disk. The hot plug/unplug code is largely
generic, but the addition code does make the VXHS specific checks only
because it needs to grab the correct config directory and generate the
object as the command line would do.
Signed-off-by: Ashish Mittal <Ashish.Mittal@veritas.com>
Signed-off-by: John Ferlan <jferlan@redhat.com>
2017-08-30 15:06:00 +00:00
|
|
|
* tls-creds:"objvirtio-disk0_tls0",
|
qemu: Add qemu command line generation for a VxHS block device
The VxHS block device will only use the newer formatting options and
avoid the legacy URI syntax.
An excerpt for a sample QEMU command line is:
-drive file.driver=vxhs,file.vdisk-id=eb90327c-8302-4725-9e1b-4e85ed4dc251,\
file.server.type=tcp,file.server.host=192.168.0.1,\
file.server.port=9999,format=raw,if=none,id=drive-virtio-disk0,cache=none \
-device virtio-blk-pci,bus=pci.0,addr=0x4,drive=drive-virtio-disk0,\
id=virtio-disk0
Update qemuxml2argvtest with a simple test.
Signed-off-by: Ashish Mittal <Ashish.Mittal@veritas.com>
Signed-off-by: John Ferlan <jferlan@redhat.com>
2017-08-30 13:46:53 +00:00
|
|
|
* vdisk-id:"eb90327c-8302-4725-4e85ed4dc251",
|
|
|
|
* server:{type:"tcp", host:"1.2.3.4", port:9999}}
|
|
|
|
*/
|
2019-04-04 08:55:32 +00:00
|
|
|
ignore_value(virJSONValueObjectCreate(&ret,
|
2018-09-04 16:52:02 +00:00
|
|
|
"S:tls-creds", tlsAlias,
|
2019-04-04 08:55:32 +00:00
|
|
|
"s:vdisk-id", src->path,
|
|
|
|
"a:server", &server, NULL));
|
qemu: Add qemu command line generation for a VxHS block device
The VxHS block device will only use the newer formatting options and
avoid the legacy URI syntax.
An excerpt for a sample QEMU command line is:
-drive file.driver=vxhs,file.vdisk-id=eb90327c-8302-4725-9e1b-4e85ed4dc251,\
file.server.type=tcp,file.server.host=192.168.0.1,\
file.server.port=9999,format=raw,if=none,id=drive-virtio-disk0,cache=none \
-device virtio-blk-pci,bus=pci.0,addr=0x4,drive=drive-virtio-disk0,\
id=virtio-disk0
Update qemuxml2argvtest with a simple test.
Signed-off-by: Ashish Mittal <Ashish.Mittal@veritas.com>
Signed-off-by: John Ferlan <jferlan@redhat.com>
2017-08-30 13:46:53 +00:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-07-13 13:48:06 +00:00
|
|
|
static virJSONValuePtr
|
2018-09-04 16:52:02 +00:00
|
|
|
qemuBlockStorageSourceGetCURLProps(virStorageSourcePtr src,
|
|
|
|
bool onlytarget)
|
2017-07-13 13:48:06 +00:00
|
|
|
{
|
|
|
|
qemuDomainStorageSourcePrivatePtr srcPriv = QEMU_DOMAIN_STORAGE_SOURCE_PRIVATE(src);
|
|
|
|
const char *passwordalias = NULL;
|
|
|
|
const char *username = NULL;
|
|
|
|
virJSONValuePtr ret = NULL;
|
2019-10-15 12:47:50 +00:00
|
|
|
g_autoptr(virURI) uri = NULL;
|
2019-10-15 13:16:31 +00:00
|
|
|
g_autofree char *uristr = NULL;
|
2017-07-13 13:48:06 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Common options:
|
|
|
|
* url, readahead, timeout, username, password-secret, proxy-username,
|
|
|
|
* proxy-password-secret
|
|
|
|
*
|
|
|
|
* Options for http transport:
|
|
|
|
* cookie, cookie-secret
|
|
|
|
*
|
|
|
|
* Options for secure transport (ftps, https):
|
|
|
|
* sslverify
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
if (!(uri = qemuBlockStorageSourceGetURI(src)))
|
2019-04-04 09:31:05 +00:00
|
|
|
return NULL;
|
2017-07-13 13:48:06 +00:00
|
|
|
|
|
|
|
if (!(uristr = virURIFormat(uri)))
|
2019-04-04 09:31:05 +00:00
|
|
|
return NULL;
|
2017-07-13 13:48:06 +00:00
|
|
|
|
2018-09-04 16:52:02 +00:00
|
|
|
if (!onlytarget && src->auth) {
|
2017-07-13 13:48:06 +00:00
|
|
|
username = src->auth->username;
|
|
|
|
passwordalias = srcPriv->secinfo->s.aes.alias;
|
|
|
|
}
|
|
|
|
|
|
|
|
ignore_value(virJSONValueObjectCreate(&ret,
|
|
|
|
"s:url", uristr,
|
|
|
|
"S:username", username,
|
|
|
|
"S:password-secret", passwordalias,
|
|
|
|
NULL));
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-10-19 15:44:57 +00:00
|
|
|
static virJSONValuePtr
|
2018-09-04 16:52:02 +00:00
|
|
|
qemuBlockStorageSourceGetISCSIProps(virStorageSourcePtr src,
|
|
|
|
bool onlytarget)
|
2017-10-19 15:44:57 +00:00
|
|
|
{
|
|
|
|
qemuDomainStorageSourcePrivatePtr srcPriv = QEMU_DOMAIN_STORAGE_SOURCE_PRIVATE(src);
|
2019-10-15 13:16:31 +00:00
|
|
|
g_autofree char *target = NULL;
|
2017-10-19 15:44:57 +00:00
|
|
|
char *lunStr = NULL;
|
|
|
|
char *username = NULL;
|
|
|
|
char *objalias = NULL;
|
2019-10-15 13:16:31 +00:00
|
|
|
g_autofree char *portal = NULL;
|
2017-10-19 15:44:57 +00:00
|
|
|
unsigned int lun = 0;
|
|
|
|
virJSONValuePtr ret = NULL;
|
|
|
|
|
|
|
|
/* { driver:"iscsi",
|
|
|
|
* transport:"tcp", ("iser" also possible)
|
|
|
|
* portal:"example.com",
|
|
|
|
* target:"iqn.2017-04.com.example:iscsi-disks",
|
|
|
|
* lun:1,
|
|
|
|
* user:"username",
|
|
|
|
* password-secret:"secret-alias",
|
2018-08-07 12:57:43 +00:00
|
|
|
* initiator-name:"iqn.2017-04.com.example:client"
|
2017-10-19 15:44:57 +00:00
|
|
|
* }
|
|
|
|
*/
|
|
|
|
|
2019-10-20 11:49:46 +00:00
|
|
|
target = g_strdup(src->path);
|
2017-10-19 15:44:57 +00:00
|
|
|
|
|
|
|
/* Separate the target and lun */
|
|
|
|
if ((lunStr = strchr(target, '/'))) {
|
|
|
|
*(lunStr++) = '\0';
|
|
|
|
if (virStrToLong_ui(lunStr, NULL, 10, &lun) < 0) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("cannot parse target for lunStr '%s'"),
|
|
|
|
target);
|
2019-04-04 09:31:05 +00:00
|
|
|
return NULL;
|
2017-10-19 15:44:57 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* combine host and port into portal */
|
|
|
|
if (virSocketAddrNumericFamily(src->hosts[0].name) == AF_INET6) {
|
2019-10-22 13:26:14 +00:00
|
|
|
portal = g_strdup_printf("[%s]:%u", src->hosts[0].name,
|
|
|
|
src->hosts[0].port);
|
2017-10-19 15:44:57 +00:00
|
|
|
} else {
|
2019-10-22 13:26:14 +00:00
|
|
|
portal = g_strdup_printf("%s:%u", src->hosts[0].name, src->hosts[0].port);
|
2017-10-19 15:44:57 +00:00
|
|
|
}
|
|
|
|
|
2018-09-04 16:52:02 +00:00
|
|
|
if (!onlytarget && src->auth) {
|
2017-10-19 15:44:57 +00:00
|
|
|
username = src->auth->username;
|
|
|
|
objalias = srcPriv->secinfo->s.aes.alias;
|
|
|
|
}
|
|
|
|
|
|
|
|
ignore_value(virJSONValueObjectCreate(&ret,
|
|
|
|
"s:portal", portal,
|
|
|
|
"s:target", target,
|
|
|
|
"u:lun", lun,
|
|
|
|
"s:transport", "tcp",
|
|
|
|
"S:user", username,
|
|
|
|
"S:password-secret", objalias,
|
2018-08-07 12:57:43 +00:00
|
|
|
"S:initiator-name", src->initiator.iqn,
|
2017-10-19 15:44:57 +00:00
|
|
|
NULL));
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-07-13 13:48:06 +00:00
|
|
|
static virJSONValuePtr
|
2018-09-04 16:52:02 +00:00
|
|
|
qemuBlockStorageSourceGetNBDProps(virStorageSourcePtr src,
|
|
|
|
bool onlytarget)
|
2017-07-13 13:48:06 +00:00
|
|
|
{
|
2019-10-15 12:47:50 +00:00
|
|
|
g_autoptr(virJSONValue) serverprops = NULL;
|
2018-09-04 16:52:02 +00:00
|
|
|
const char *tlsAlias = src->tlsAlias;
|
2017-07-13 13:48:06 +00:00
|
|
|
virJSONValuePtr ret = NULL;
|
|
|
|
|
|
|
|
if (src->nhosts != 1) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("nbd protocol accepts only one host"));
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
serverprops = qemuBlockStorageSourceBuildJSONSocketAddress(&src->hosts[0],
|
|
|
|
false);
|
|
|
|
if (!serverprops)
|
|
|
|
return NULL;
|
|
|
|
|
2018-09-04 16:52:02 +00:00
|
|
|
if (onlytarget)
|
|
|
|
tlsAlias = NULL;
|
|
|
|
|
2017-11-09 09:29:53 +00:00
|
|
|
if (virJSONValueObjectCreate(&ret,
|
2018-03-30 09:12:57 +00:00
|
|
|
"a:server", &serverprops,
|
2017-11-09 09:29:53 +00:00
|
|
|
"S:export", src->path,
|
2018-09-04 16:52:02 +00:00
|
|
|
"S:tls-creds", tlsAlias,
|
2017-11-09 09:29:53 +00:00
|
|
|
NULL) < 0)
|
2019-04-04 09:31:05 +00:00
|
|
|
return NULL;
|
2017-11-09 09:29:53 +00:00
|
|
|
|
2017-07-13 13:48:06 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-10-23 16:16:53 +00:00
|
|
|
static virJSONValuePtr
|
2018-09-04 16:52:02 +00:00
|
|
|
qemuBlockStorageSourceGetRBDProps(virStorageSourcePtr src,
|
|
|
|
bool onlytarget)
|
2017-10-23 16:16:53 +00:00
|
|
|
{
|
|
|
|
qemuDomainStorageSourcePrivatePtr srcPriv = QEMU_DOMAIN_STORAGE_SOURCE_PRIVATE(src);
|
2019-10-15 12:47:50 +00:00
|
|
|
g_autoptr(virJSONValue) servers = NULL;
|
2017-10-23 16:16:53 +00:00
|
|
|
virJSONValuePtr ret = NULL;
|
|
|
|
const char *username = NULL;
|
2019-10-15 12:47:50 +00:00
|
|
|
g_autoptr(virJSONValue) authmodes = NULL;
|
|
|
|
g_autoptr(virJSONValue) mode = NULL;
|
2018-07-09 14:05:16 +00:00
|
|
|
const char *keysecret = NULL;
|
2017-10-23 16:16:53 +00:00
|
|
|
|
|
|
|
if (src->nhosts > 0 &&
|
|
|
|
!(servers = qemuBlockStorageSourceBuildHostsJSONInetSocketAddress(src)))
|
|
|
|
return NULL;
|
|
|
|
|
2018-09-04 16:52:02 +00:00
|
|
|
if (!onlytarget && src->auth) {
|
2017-10-23 16:16:53 +00:00
|
|
|
username = srcPriv->secinfo->s.aes.username;
|
2018-07-09 14:05:16 +00:00
|
|
|
keysecret = srcPriv->secinfo->s.aes.alias;
|
|
|
|
/* the auth modes are modelled after our old command line generator */
|
|
|
|
if (!(authmodes = virJSONValueNewArray()))
|
2019-04-04 09:31:05 +00:00
|
|
|
return NULL;
|
2018-07-09 14:05:16 +00:00
|
|
|
|
|
|
|
if (!(mode = virJSONValueNewString("cephx")) ||
|
|
|
|
virJSONValueArrayAppend(authmodes, mode) < 0)
|
2019-04-04 09:31:05 +00:00
|
|
|
return NULL;
|
2018-07-09 14:05:16 +00:00
|
|
|
|
|
|
|
mode = NULL;
|
|
|
|
|
|
|
|
if (!(mode = virJSONValueNewString("none")) ||
|
|
|
|
virJSONValueArrayAppend(authmodes, mode) < 0)
|
2019-04-04 09:31:05 +00:00
|
|
|
return NULL;
|
2018-07-09 14:05:16 +00:00
|
|
|
|
|
|
|
mode = NULL;
|
|
|
|
}
|
2017-10-23 16:16:53 +00:00
|
|
|
|
2017-11-09 09:29:53 +00:00
|
|
|
if (virJSONValueObjectCreate(&ret,
|
|
|
|
"s:pool", src->volume,
|
|
|
|
"s:image", src->path,
|
|
|
|
"S:snapshot", src->snapshot,
|
|
|
|
"S:conf", src->configFile,
|
2018-03-30 09:12:57 +00:00
|
|
|
"A:server", &servers,
|
2017-11-09 09:29:53 +00:00
|
|
|
"S:user", username,
|
2018-07-09 14:05:16 +00:00
|
|
|
"A:auth-client-required", &authmodes,
|
|
|
|
"S:key-secret", keysecret,
|
2017-11-09 09:29:53 +00:00
|
|
|
NULL) < 0)
|
2019-04-04 09:31:05 +00:00
|
|
|
return NULL;
|
2017-10-23 16:16:53 +00:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-10-23 16:44:35 +00:00
|
|
|
static virJSONValuePtr
|
|
|
|
qemuBlockStorageSourceGetSheepdogProps(virStorageSourcePtr src)
|
|
|
|
{
|
2019-10-15 12:47:50 +00:00
|
|
|
g_autoptr(virJSONValue) serverprops = NULL;
|
2017-10-23 16:44:35 +00:00
|
|
|
virJSONValuePtr ret = NULL;
|
|
|
|
|
|
|
|
if (src->nhosts != 1) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("sheepdog protocol accepts only one host"));
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
serverprops = qemuBlockStorageSourceBuildJSONSocketAddress(&src->hosts[0],
|
|
|
|
false);
|
|
|
|
if (!serverprops)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
/* libvirt does not support the 'snap-id' and 'tag' properties */
|
2017-11-09 09:29:53 +00:00
|
|
|
if (virJSONValueObjectCreate(&ret,
|
2018-03-30 09:12:57 +00:00
|
|
|
"a:server", &serverprops,
|
2017-11-09 09:29:53 +00:00
|
|
|
"s:vdi", src->path,
|
|
|
|
NULL) < 0)
|
2019-04-04 09:31:05 +00:00
|
|
|
return NULL;
|
2017-10-23 16:44:35 +00:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-10-23 16:54:12 +00:00
|
|
|
|
|
|
|
static virJSONValuePtr
|
|
|
|
qemuBlockStorageSourceGetSshProps(virStorageSourcePtr src)
|
|
|
|
{
|
2019-10-15 12:47:50 +00:00
|
|
|
g_autoptr(virJSONValue) serverprops = NULL;
|
2017-10-23 16:54:12 +00:00
|
|
|
virJSONValuePtr ret = NULL;
|
|
|
|
const char *username = NULL;
|
|
|
|
|
|
|
|
if (src->nhosts != 1) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("sheepdog protocol accepts only one host"));
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
serverprops = qemuBlockStorageSourceBuildJSONInetSocketAddress(&src->hosts[0]);
|
|
|
|
if (!serverprops)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (src->auth)
|
|
|
|
username = src->auth->username;
|
|
|
|
|
2017-11-09 09:29:53 +00:00
|
|
|
if (virJSONValueObjectCreate(&ret,
|
|
|
|
"s:path", src->path,
|
2018-03-30 09:12:57 +00:00
|
|
|
"a:server", &serverprops,
|
2017-11-09 09:29:53 +00:00
|
|
|
"S:user", username,
|
|
|
|
NULL) < 0)
|
2019-04-04 09:31:05 +00:00
|
|
|
return NULL;
|
2017-11-09 09:29:53 +00:00
|
|
|
|
2017-10-23 16:54:12 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-05 16:02:28 +00:00
|
|
|
static virJSONValuePtr
|
2018-09-04 16:52:02 +00:00
|
|
|
qemuBlockStorageSourceGetFileProps(virStorageSourcePtr src,
|
|
|
|
bool onlytarget)
|
2017-12-05 16:02:28 +00:00
|
|
|
{
|
2017-12-05 16:05:12 +00:00
|
|
|
const char *iomode = NULL;
|
2018-05-30 13:47:13 +00:00
|
|
|
const char *prManagerAlias = NULL;
|
2017-12-05 16:02:28 +00:00
|
|
|
virJSONValuePtr ret = NULL;
|
|
|
|
|
2018-09-04 16:52:02 +00:00
|
|
|
if (!onlytarget) {
|
|
|
|
if (src->pr)
|
|
|
|
prManagerAlias = src->pr->mgralias;
|
|
|
|
|
|
|
|
if (src->iomode != VIR_DOMAIN_DISK_IO_DEFAULT)
|
|
|
|
iomode = virDomainDiskIoTypeToString(src->iomode);
|
|
|
|
}
|
2018-05-30 13:47:13 +00:00
|
|
|
|
2017-12-05 16:02:28 +00:00
|
|
|
ignore_value(virJSONValueObjectCreate(&ret,
|
2017-12-05 16:05:12 +00:00
|
|
|
"s:filename", src->path,
|
|
|
|
"S:aio", iomode,
|
2018-05-30 13:47:13 +00:00
|
|
|
"S:pr-manager", prManagerAlias,
|
2017-12-05 16:05:12 +00:00
|
|
|
NULL) < 0);
|
2017-12-05 16:02:28 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-03-29 06:44:02 +00:00
|
|
|
static virJSONValuePtr
|
2018-09-04 16:52:02 +00:00
|
|
|
qemuBlockStorageSourceGetVvfatProps(virStorageSourcePtr src,
|
|
|
|
bool onlytarget)
|
2018-03-29 06:44:02 +00:00
|
|
|
{
|
2019-10-15 12:47:50 +00:00
|
|
|
g_autoptr(virJSONValue) ret = NULL;
|
2018-03-29 06:44:02 +00:00
|
|
|
|
|
|
|
/* libvirt currently does not handle the following attributes:
|
|
|
|
* '*fat-type': 'int'
|
|
|
|
* '*label': 'str'
|
|
|
|
*/
|
2018-09-04 16:52:02 +00:00
|
|
|
if (virJSONValueObjectCreate(&ret,
|
|
|
|
"s:driver", "vvfat",
|
|
|
|
"s:dir", src->path,
|
|
|
|
"b:floppy", src->floppyimg, NULL) < 0)
|
|
|
|
return NULL;
|
2018-03-29 06:44:02 +00:00
|
|
|
|
2018-09-04 16:52:02 +00:00
|
|
|
if (!onlytarget &&
|
|
|
|
virJSONValueObjectAdd(ret, "b:rw", !src->readonly, NULL) < 0)
|
|
|
|
return NULL;
|
|
|
|
|
2019-10-16 11:35:54 +00:00
|
|
|
return g_steal_pointer(&ret);
|
2018-03-29 06:44:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-06-21 13:07:22 +00:00
|
|
|
static virJSONValuePtr
|
|
|
|
qemuBlockStorageSourceGetNVMeProps(virStorageSourcePtr src)
|
|
|
|
{
|
|
|
|
const virStorageSourceNVMeDef *nvme = src->nvme;
|
|
|
|
g_autofree char *pciAddr = NULL;
|
|
|
|
virJSONValuePtr ret = NULL;
|
|
|
|
|
|
|
|
if (!(pciAddr = virPCIDeviceAddressAsString(&nvme->pciAddr)))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
ignore_value(virJSONValueObjectCreate(&ret,
|
|
|
|
"s:driver", "nvme",
|
|
|
|
"s:device", pciAddr,
|
|
|
|
"U:namespace", nvme->namespace,
|
|
|
|
NULL));
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-04-25 15:04:35 +00:00
|
|
|
static int
|
|
|
|
qemuBlockStorageSourceGetBlockdevGetCacheProps(virStorageSourcePtr src,
|
|
|
|
virJSONValuePtr props)
|
|
|
|
{
|
2019-10-15 12:47:50 +00:00
|
|
|
g_autoptr(virJSONValue) cacheobj = NULL;
|
2018-04-25 15:04:35 +00:00
|
|
|
bool direct = false;
|
|
|
|
bool noflush = false;
|
|
|
|
|
|
|
|
if (src->cachemode == VIR_DOMAIN_DISK_CACHE_DEFAULT)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (qemuDomainDiskCachemodeFlags(src->cachemode, NULL, &direct, &noflush) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (virJSONValueObjectCreate(&cacheobj,
|
|
|
|
"b:direct", direct,
|
|
|
|
"b:no-flush", noflush,
|
|
|
|
NULL) < 0)
|
|
|
|
return -1;
|
|
|
|
|
2019-04-04 08:55:32 +00:00
|
|
|
if (virJSONValueObjectAppend(props, "cache", cacheobj) < 0)
|
2018-04-25 15:04:35 +00:00
|
|
|
return -1;
|
2019-04-04 08:55:32 +00:00
|
|
|
cacheobj = NULL;
|
2018-04-25 15:04:35 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-07-07 15:37:42 +00:00
|
|
|
/**
|
|
|
|
* qemuBlockStorageSourceGetBackendProps:
|
|
|
|
* @src: disk source
|
2018-03-22 15:42:47 +00:00
|
|
|
* @legacy: use legacy formatting of attributes (for -drive / old qemus)
|
2018-09-04 16:52:02 +00:00
|
|
|
* @onlytarget: omit any data which does not identify the image itself
|
2018-09-04 17:29:10 +00:00
|
|
|
* @autoreadonly: use the auto-read-only feature of qemu
|
2017-07-07 15:37:42 +00:00
|
|
|
*
|
|
|
|
* Creates a JSON object describing the underlying storage or protocol of a
|
|
|
|
* storage source. Returns NULL on error and reports an appropriate error message.
|
|
|
|
*/
|
|
|
|
virJSONValuePtr
|
2018-03-22 15:42:47 +00:00
|
|
|
qemuBlockStorageSourceGetBackendProps(virStorageSourcePtr src,
|
2018-09-04 16:52:02 +00:00
|
|
|
bool legacy,
|
2018-09-04 17:29:10 +00:00
|
|
|
bool onlytarget,
|
|
|
|
bool autoreadonly)
|
2017-07-07 14:29:01 +00:00
|
|
|
{
|
|
|
|
int actualType = virStorageSourceGetActualType(src);
|
2019-10-15 12:47:50 +00:00
|
|
|
g_autoptr(virJSONValue) fileprops = NULL;
|
2019-02-05 15:03:39 +00:00
|
|
|
const char *driver = NULL;
|
2018-09-04 17:29:10 +00:00
|
|
|
virTristateBool aro = VIR_TRISTATE_BOOL_ABSENT;
|
|
|
|
virTristateBool ro = VIR_TRISTATE_BOOL_ABSENT;
|
|
|
|
|
|
|
|
if (autoreadonly) {
|
|
|
|
aro = VIR_TRISTATE_BOOL_YES;
|
|
|
|
} else {
|
|
|
|
if (src->readonly)
|
|
|
|
ro = VIR_TRISTATE_BOOL_YES;
|
|
|
|
else
|
|
|
|
ro = VIR_TRISTATE_BOOL_NO;
|
|
|
|
}
|
2017-07-07 14:29:01 +00:00
|
|
|
|
2018-04-25 12:42:34 +00:00
|
|
|
switch ((virStorageType)actualType) {
|
2017-07-07 14:29:01 +00:00
|
|
|
case VIR_STORAGE_TYPE_BLOCK:
|
|
|
|
case VIR_STORAGE_TYPE_FILE:
|
2019-02-05 15:03:39 +00:00
|
|
|
if (virStorageSourceIsBlockLocal(src)) {
|
|
|
|
if (src->hostcdrom)
|
|
|
|
driver = "host_cdrom";
|
|
|
|
else
|
|
|
|
driver = "host_device";
|
|
|
|
} else {
|
|
|
|
driver = "file";
|
|
|
|
}
|
|
|
|
|
2018-09-04 16:52:02 +00:00
|
|
|
if (!(fileprops = qemuBlockStorageSourceGetFileProps(src, onlytarget)))
|
2017-07-13 07:48:14 +00:00
|
|
|
return NULL;
|
|
|
|
break;
|
2018-03-29 06:44:02 +00:00
|
|
|
|
|
|
|
case VIR_STORAGE_TYPE_DIR:
|
|
|
|
/* qemu handles directories by exposing them as a device with emulated
|
|
|
|
* FAT filesystem */
|
2018-09-04 16:52:02 +00:00
|
|
|
if (!(fileprops = qemuBlockStorageSourceGetVvfatProps(src, onlytarget)))
|
2018-03-29 06:44:02 +00:00
|
|
|
return NULL;
|
|
|
|
break;
|
2017-07-13 07:48:14 +00:00
|
|
|
|
2019-06-03 15:31:13 +00:00
|
|
|
case VIR_STORAGE_TYPE_NVME:
|
2019-06-21 13:07:22 +00:00
|
|
|
if (!(fileprops = qemuBlockStorageSourceGetNVMeProps(src)))
|
|
|
|
return NULL;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case VIR_STORAGE_TYPE_VOLUME:
|
2017-07-07 14:29:01 +00:00
|
|
|
case VIR_STORAGE_TYPE_NONE:
|
|
|
|
case VIR_STORAGE_TYPE_LAST:
|
2017-11-09 09:13:26 +00:00
|
|
|
return NULL;
|
2017-07-07 14:29:01 +00:00
|
|
|
|
|
|
|
case VIR_STORAGE_TYPE_NETWORK:
|
2017-07-07 15:37:42 +00:00
|
|
|
switch ((virStorageNetProtocol) src->protocol) {
|
|
|
|
case VIR_STORAGE_NET_PROTOCOL_GLUSTER:
|
2019-02-05 15:03:39 +00:00
|
|
|
driver = "gluster";
|
2018-09-04 16:52:02 +00:00
|
|
|
if (!(fileprops = qemuBlockStorageSourceGetGlusterProps(src, legacy, onlytarget)))
|
2017-10-10 06:23:12 +00:00
|
|
|
return NULL;
|
2017-07-07 15:37:42 +00:00
|
|
|
break;
|
|
|
|
|
qemu: Add qemu command line generation for a VxHS block device
The VxHS block device will only use the newer formatting options and
avoid the legacy URI syntax.
An excerpt for a sample QEMU command line is:
-drive file.driver=vxhs,file.vdisk-id=eb90327c-8302-4725-9e1b-4e85ed4dc251,\
file.server.type=tcp,file.server.host=192.168.0.1,\
file.server.port=9999,format=raw,if=none,id=drive-virtio-disk0,cache=none \
-device virtio-blk-pci,bus=pci.0,addr=0x4,drive=drive-virtio-disk0,\
id=virtio-disk0
Update qemuxml2argvtest with a simple test.
Signed-off-by: Ashish Mittal <Ashish.Mittal@veritas.com>
Signed-off-by: John Ferlan <jferlan@redhat.com>
2017-08-30 13:46:53 +00:00
|
|
|
case VIR_STORAGE_NET_PROTOCOL_VXHS:
|
2019-02-05 15:03:39 +00:00
|
|
|
driver = "vxhs";
|
2018-09-04 16:52:02 +00:00
|
|
|
if (!(fileprops = qemuBlockStorageSourceGetVxHSProps(src, onlytarget)))
|
2017-10-10 06:23:12 +00:00
|
|
|
return NULL;
|
qemu: Add qemu command line generation for a VxHS block device
The VxHS block device will only use the newer formatting options and
avoid the legacy URI syntax.
An excerpt for a sample QEMU command line is:
-drive file.driver=vxhs,file.vdisk-id=eb90327c-8302-4725-9e1b-4e85ed4dc251,\
file.server.type=tcp,file.server.host=192.168.0.1,\
file.server.port=9999,format=raw,if=none,id=drive-virtio-disk0,cache=none \
-device virtio-blk-pci,bus=pci.0,addr=0x4,drive=drive-virtio-disk0,\
id=virtio-disk0
Update qemuxml2argvtest with a simple test.
Signed-off-by: Ashish Mittal <Ashish.Mittal@veritas.com>
Signed-off-by: John Ferlan <jferlan@redhat.com>
2017-08-30 13:46:53 +00:00
|
|
|
break;
|
|
|
|
|
2017-07-07 15:37:42 +00:00
|
|
|
case VIR_STORAGE_NET_PROTOCOL_HTTP:
|
|
|
|
case VIR_STORAGE_NET_PROTOCOL_HTTPS:
|
|
|
|
case VIR_STORAGE_NET_PROTOCOL_FTP:
|
|
|
|
case VIR_STORAGE_NET_PROTOCOL_FTPS:
|
|
|
|
case VIR_STORAGE_NET_PROTOCOL_TFTP:
|
2019-02-05 15:03:39 +00:00
|
|
|
driver = virStorageNetProtocolTypeToString(src->protocol);
|
2018-09-04 16:52:02 +00:00
|
|
|
if (!(fileprops = qemuBlockStorageSourceGetCURLProps(src, onlytarget)))
|
2017-07-13 13:48:06 +00:00
|
|
|
return NULL;
|
|
|
|
break;
|
|
|
|
|
2017-10-19 15:44:57 +00:00
|
|
|
case VIR_STORAGE_NET_PROTOCOL_ISCSI:
|
2019-02-05 15:03:39 +00:00
|
|
|
driver = "iscsi";
|
2018-09-04 16:52:02 +00:00
|
|
|
if (!(fileprops = qemuBlockStorageSourceGetISCSIProps(src, onlytarget)))
|
2017-10-19 15:44:57 +00:00
|
|
|
return NULL;
|
|
|
|
break;
|
|
|
|
|
2017-07-13 13:48:06 +00:00
|
|
|
case VIR_STORAGE_NET_PROTOCOL_NBD:
|
2019-02-05 15:03:39 +00:00
|
|
|
driver = "nbd";
|
2018-09-04 16:52:02 +00:00
|
|
|
if (!(fileprops = qemuBlockStorageSourceGetNBDProps(src, onlytarget)))
|
2017-07-13 13:48:06 +00:00
|
|
|
return NULL;
|
|
|
|
break;
|
|
|
|
|
2017-07-13 13:48:06 +00:00
|
|
|
case VIR_STORAGE_NET_PROTOCOL_RBD:
|
2019-02-05 15:03:39 +00:00
|
|
|
driver = "rbd";
|
2018-09-04 16:52:02 +00:00
|
|
|
if (!(fileprops = qemuBlockStorageSourceGetRBDProps(src, onlytarget)))
|
2017-10-23 16:16:53 +00:00
|
|
|
return NULL;
|
|
|
|
break;
|
|
|
|
|
2017-07-13 13:48:06 +00:00
|
|
|
case VIR_STORAGE_NET_PROTOCOL_SHEEPDOG:
|
2019-02-05 15:03:39 +00:00
|
|
|
driver = "sheepdog";
|
2017-10-23 16:44:35 +00:00
|
|
|
if (!(fileprops = qemuBlockStorageSourceGetSheepdogProps(src)))
|
|
|
|
return NULL;
|
|
|
|
break;
|
|
|
|
|
2017-07-07 15:37:42 +00:00
|
|
|
case VIR_STORAGE_NET_PROTOCOL_SSH:
|
2019-02-05 15:03:39 +00:00
|
|
|
driver = "ssh";
|
2017-10-23 16:54:12 +00:00
|
|
|
if (!(fileprops = qemuBlockStorageSourceGetSshProps(src)))
|
|
|
|
return NULL;
|
|
|
|
break;
|
|
|
|
|
2017-07-07 15:37:42 +00:00
|
|
|
case VIR_STORAGE_NET_PROTOCOL_NONE:
|
|
|
|
case VIR_STORAGE_NET_PROTOCOL_LAST:
|
2017-11-09 09:13:26 +00:00
|
|
|
return NULL;
|
2017-07-07 14:29:01 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2019-02-05 15:03:39 +00:00
|
|
|
if (driver && virJSONValueObjectPrependString(fileprops, "driver", driver) < 0)
|
|
|
|
return NULL;
|
|
|
|
|
2018-09-04 16:52:02 +00:00
|
|
|
if (!onlytarget) {
|
|
|
|
if (qemuBlockNodeNameValidate(src->nodestorage) < 0 ||
|
|
|
|
virJSONValueObjectAdd(fileprops, "S:node-name", src->nodestorage, NULL) < 0)
|
2019-04-04 09:31:05 +00:00
|
|
|
return NULL;
|
2018-04-06 04:40:23 +00:00
|
|
|
|
2018-09-04 16:52:02 +00:00
|
|
|
if (!legacy) {
|
|
|
|
if (qemuBlockStorageSourceGetBlockdevGetCacheProps(src, fileprops) < 0)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (virJSONValueObjectAdd(fileprops,
|
2018-09-04 17:29:10 +00:00
|
|
|
"T:read-only", ro,
|
|
|
|
"T:auto-read-only", aro,
|
2018-09-04 16:52:02 +00:00
|
|
|
"s:discard", "unmap",
|
|
|
|
NULL) < 0)
|
|
|
|
return NULL;
|
|
|
|
}
|
2017-10-09 06:55:15 +00:00
|
|
|
}
|
|
|
|
|
2019-10-16 11:35:54 +00:00
|
|
|
return g_steal_pointer(&fileprops);
|
2017-07-07 14:29:01 +00:00
|
|
|
}
|
2017-10-23 10:26:10 +00:00
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
qemuBlockStorageSourceGetFormatRawProps(virStorageSourcePtr src,
|
|
|
|
virJSONValuePtr props)
|
|
|
|
{
|
|
|
|
qemuDomainStorageSourcePrivatePtr srcPriv = QEMU_DOMAIN_STORAGE_SOURCE_PRIVATE(src);
|
|
|
|
const char *driver = "raw";
|
|
|
|
const char *secretalias = NULL;
|
|
|
|
|
|
|
|
if (src->encryption &&
|
|
|
|
src->encryption->format == VIR_STORAGE_ENCRYPTION_FORMAT_LUKS &&
|
|
|
|
srcPriv &&
|
|
|
|
srcPriv->encinfo) {
|
|
|
|
driver = "luks";
|
|
|
|
secretalias = srcPriv->encinfo->s.aes.alias;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* currently unhandled properties for the 'raw' driver:
|
|
|
|
* 'offset'
|
|
|
|
* 'size'
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (virJSONValueObjectAdd(props,
|
|
|
|
"s:driver", driver,
|
|
|
|
"S:key-secret", secretalias, NULL) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
qemuBlockStorageSourceGetCryptoProps(virStorageSourcePtr src,
|
|
|
|
virJSONValuePtr *encprops)
|
|
|
|
{
|
|
|
|
qemuDomainStorageSourcePrivatePtr srcpriv = QEMU_DOMAIN_STORAGE_SOURCE_PRIVATE(src);
|
|
|
|
const char *encformat = NULL;
|
|
|
|
|
|
|
|
*encprops = NULL;
|
|
|
|
|
|
|
|
/* qemu requires encrypted secrets regardless of encryption method used when
|
|
|
|
* passed using the blockdev infrastructure, thus only
|
|
|
|
* VIR_DOMAIN_SECRET_INFO_TYPE_AES works here. The correct type needs to be
|
|
|
|
* instantiated elsewhere. */
|
|
|
|
if (!src->encryption ||
|
|
|
|
!srcpriv ||
|
|
|
|
!srcpriv->encinfo ||
|
|
|
|
srcpriv->encinfo->type != VIR_DOMAIN_SECRET_INFO_TYPE_AES)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
switch ((virStorageEncryptionFormatType) src->encryption->format) {
|
|
|
|
case VIR_STORAGE_ENCRYPTION_FORMAT_QCOW:
|
|
|
|
encformat = "aes";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case VIR_STORAGE_ENCRYPTION_FORMAT_LUKS:
|
|
|
|
encformat = "luks";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case VIR_STORAGE_ENCRYPTION_FORMAT_DEFAULT:
|
|
|
|
case VIR_STORAGE_ENCRYPTION_FORMAT_LAST:
|
|
|
|
default:
|
|
|
|
virReportEnumRangeError(virStorageEncryptionFormatType,
|
|
|
|
src->encryption->format);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return virJSONValueObjectCreate(encprops,
|
|
|
|
"s:format", encformat,
|
|
|
|
"s:key-secret", srcpriv->encinfo->s.aes.alias,
|
|
|
|
NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
qemuBlockStorageSourceGetFormatQcowGenericProps(virStorageSourcePtr src,
|
|
|
|
const char *format,
|
|
|
|
virJSONValuePtr props)
|
|
|
|
{
|
2019-10-15 12:47:50 +00:00
|
|
|
g_autoptr(virJSONValue) encprops = NULL;
|
2017-10-23 10:26:10 +00:00
|
|
|
|
|
|
|
if (qemuBlockStorageSourceGetCryptoProps(src, &encprops) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (virJSONValueObjectAdd(props,
|
|
|
|
"s:driver", format,
|
|
|
|
"A:encrypt", &encprops, NULL) < 0)
|
2019-04-04 09:31:05 +00:00
|
|
|
return -1;
|
2017-10-23 10:26:10 +00:00
|
|
|
|
2019-04-04 09:31:05 +00:00
|
|
|
return 0;
|
2017-10-23 10:26:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
qemuBlockStorageSourceGetFormatQcow2Props(virStorageSourcePtr src,
|
|
|
|
virJSONValuePtr props)
|
|
|
|
{
|
|
|
|
/* currently unhandled qcow2 props:
|
|
|
|
*
|
|
|
|
* 'lazy-refcounts'
|
|
|
|
* 'pass-discard-request'
|
|
|
|
* 'pass-discard-snapshot'
|
|
|
|
* 'pass-discard-other'
|
|
|
|
* 'overlap-check'
|
|
|
|
* 'l2-cache-size'
|
|
|
|
* 'l2-cache-entry-size'
|
|
|
|
* 'refcount-cache-size'
|
|
|
|
* 'cache-clean-interval'
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (qemuBlockStorageSourceGetFormatQcowGenericProps(src, "qcow2", props) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static virJSONValuePtr
|
|
|
|
qemuBlockStorageSourceGetBlockdevFormatCommonProps(virStorageSourcePtr src)
|
|
|
|
{
|
|
|
|
const char *detectZeroes = NULL;
|
|
|
|
const char *discard = NULL;
|
|
|
|
int detectZeroesMode = virDomainDiskGetDetectZeroesMode(src->discard,
|
|
|
|
src->detect_zeroes);
|
2019-10-15 12:47:50 +00:00
|
|
|
g_autoptr(virJSONValue) props = NULL;
|
2017-10-23 10:26:10 +00:00
|
|
|
|
|
|
|
if (qemuBlockNodeNameValidate(src->nodeformat) < 0)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (src->discard)
|
|
|
|
discard = virDomainDiskDiscardTypeToString(src->discard);
|
|
|
|
|
|
|
|
if (detectZeroesMode)
|
|
|
|
detectZeroes = virDomainDiskDetectZeroesTypeToString(detectZeroesMode);
|
|
|
|
|
|
|
|
/* currently unhandled global properties:
|
|
|
|
* '*force-share': 'bool'
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (virJSONValueObjectCreate(&props,
|
|
|
|
"s:node-name", src->nodeformat,
|
|
|
|
"b:read-only", src->readonly,
|
|
|
|
"S:discard", discard,
|
|
|
|
"S:detect-zeroes", detectZeroes,
|
|
|
|
NULL) < 0)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (qemuBlockStorageSourceGetBlockdevGetCacheProps(src, props) < 0)
|
2019-04-04 09:31:05 +00:00
|
|
|
return NULL;
|
2017-10-23 10:26:10 +00:00
|
|
|
|
2019-10-16 11:35:54 +00:00
|
|
|
return g_steal_pointer(&props);
|
2017-10-23 10:26:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static virJSONValuePtr
|
|
|
|
qemuBlockStorageSourceGetBlockdevFormatProps(virStorageSourcePtr src)
|
|
|
|
{
|
|
|
|
const char *driver = NULL;
|
2019-10-15 12:47:50 +00:00
|
|
|
g_autoptr(virJSONValue) props = NULL;
|
2017-10-23 10:26:10 +00:00
|
|
|
|
|
|
|
if (!(props = qemuBlockStorageSourceGetBlockdevFormatCommonProps(src)))
|
2019-04-04 09:31:05 +00:00
|
|
|
return NULL;
|
2017-10-23 10:26:10 +00:00
|
|
|
|
|
|
|
switch ((virStorageFileFormat) src->format) {
|
|
|
|
case VIR_STORAGE_FILE_FAT:
|
|
|
|
/* The fat layer is emulated by the storage access layer, so we need to
|
|
|
|
* put a raw layer on top */
|
|
|
|
case VIR_STORAGE_FILE_RAW:
|
|
|
|
if (qemuBlockStorageSourceGetFormatRawProps(src, props) < 0)
|
2019-04-04 09:31:05 +00:00
|
|
|
return NULL;
|
2017-10-23 10:26:10 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case VIR_STORAGE_FILE_QCOW2:
|
|
|
|
if (qemuBlockStorageSourceGetFormatQcow2Props(src, props) < 0)
|
2019-04-04 09:31:05 +00:00
|
|
|
return NULL;
|
2017-10-23 10:26:10 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case VIR_STORAGE_FILE_QCOW:
|
|
|
|
if (qemuBlockStorageSourceGetFormatQcowGenericProps(src, "qcow", props) < 0)
|
2019-04-04 09:31:05 +00:00
|
|
|
return NULL;
|
2017-10-23 10:26:10 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
/* formats without any special parameters */
|
|
|
|
case VIR_STORAGE_FILE_PLOOP:
|
|
|
|
driver = "parallels";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case VIR_STORAGE_FILE_VHD:
|
|
|
|
driver = "vhdx";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case VIR_STORAGE_FILE_BOCHS:
|
|
|
|
case VIR_STORAGE_FILE_CLOOP:
|
|
|
|
case VIR_STORAGE_FILE_DMG:
|
|
|
|
case VIR_STORAGE_FILE_VDI:
|
|
|
|
case VIR_STORAGE_FILE_VPC:
|
|
|
|
case VIR_STORAGE_FILE_QED:
|
|
|
|
case VIR_STORAGE_FILE_VMDK:
|
|
|
|
driver = virStorageFileFormatTypeToString(src->format);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case VIR_STORAGE_FILE_AUTO_SAFE:
|
|
|
|
case VIR_STORAGE_FILE_AUTO:
|
|
|
|
case VIR_STORAGE_FILE_NONE:
|
|
|
|
case VIR_STORAGE_FILE_COW:
|
|
|
|
case VIR_STORAGE_FILE_ISO:
|
|
|
|
case VIR_STORAGE_FILE_DIR:
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("mishandled storage format '%s'"),
|
|
|
|
virStorageFileFormatTypeToString(src->format));
|
2019-04-04 09:31:05 +00:00
|
|
|
return NULL;
|
2017-10-23 10:26:10 +00:00
|
|
|
|
|
|
|
case VIR_STORAGE_FILE_LAST:
|
|
|
|
default:
|
|
|
|
virReportEnumRangeError(virStorageFileFormat, src->format);
|
2019-04-04 09:31:05 +00:00
|
|
|
return NULL;
|
2017-10-23 10:26:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (driver &&
|
|
|
|
virJSONValueObjectAdd(props, "s:driver", driver, NULL) < 0)
|
2019-04-04 09:31:05 +00:00
|
|
|
return NULL;
|
2017-10-23 10:26:10 +00:00
|
|
|
|
2019-10-16 11:35:54 +00:00
|
|
|
return g_steal_pointer(&props);
|
2017-10-23 10:26:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* qemuBlockStorageSourceGetBlockdevProps:
|
|
|
|
*
|
|
|
|
* @src: storage source to format
|
2019-09-03 11:27:52 +00:00
|
|
|
* @backingStore: a storage source to use as backing of @src
|
2017-10-23 10:26:10 +00:00
|
|
|
*
|
|
|
|
* Formats @src into a JSON object which can be used with blockdev-add or
|
|
|
|
* -blockdev. The formatted object contains both the storage and format layer
|
|
|
|
* in nested form including link to the backing chain layer if necessary.
|
|
|
|
*/
|
|
|
|
virJSONValuePtr
|
2019-09-03 11:27:52 +00:00
|
|
|
qemuBlockStorageSourceGetBlockdevProps(virStorageSourcePtr src,
|
|
|
|
virStorageSourcePtr backingStore)
|
2017-10-23 10:26:10 +00:00
|
|
|
{
|
2019-10-15 12:47:50 +00:00
|
|
|
g_autoptr(virJSONValue) props = NULL;
|
2017-10-23 10:26:10 +00:00
|
|
|
|
|
|
|
if (!(props = qemuBlockStorageSourceGetBlockdevFormatProps(src)))
|
2019-04-04 09:31:05 +00:00
|
|
|
return NULL;
|
2017-10-23 10:26:10 +00:00
|
|
|
|
2018-05-14 11:54:12 +00:00
|
|
|
if (virJSONValueObjectAppendString(props, "file", src->nodestorage) < 0)
|
2019-04-04 09:31:05 +00:00
|
|
|
return NULL;
|
2017-10-23 10:26:10 +00:00
|
|
|
|
2019-09-03 11:16:39 +00:00
|
|
|
if (backingStore) {
|
|
|
|
if (src->format >= VIR_STORAGE_FILE_BACKING) {
|
|
|
|
if (virStorageSourceIsBacking(backingStore)) {
|
|
|
|
if (virJSONValueObjectAppendString(props, "backing",
|
|
|
|
backingStore->nodeformat) < 0)
|
|
|
|
return NULL;
|
|
|
|
} else {
|
|
|
|
/* chain is terminated, indicate that no detection should happen
|
|
|
|
* in qemu */
|
|
|
|
if (virJSONValueObjectAppendNull(props, "backing") < 0)
|
|
|
|
return NULL;
|
|
|
|
}
|
2017-10-23 10:26:10 +00:00
|
|
|
} else {
|
2019-09-03 11:16:39 +00:00
|
|
|
if (virStorageSourceIsBacking(backingStore)) {
|
|
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
|
|
_("storage format '%s' does not support backing store"),
|
|
|
|
virStorageFileFormatTypeToString(src->format));
|
2019-04-04 09:31:05 +00:00
|
|
|
return NULL;
|
2019-09-03 11:16:39 +00:00
|
|
|
}
|
2017-10-23 10:26:10 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-16 11:35:54 +00:00
|
|
|
return g_steal_pointer(&props);
|
2017-10-23 10:26:10 +00:00
|
|
|
}
|
2018-02-23 11:59:13 +00:00
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
qemuBlockStorageSourceAttachDataFree(qemuBlockStorageSourceAttachDataPtr data)
|
|
|
|
{
|
|
|
|
if (!data)
|
|
|
|
return;
|
|
|
|
|
|
|
|
virJSONValueFree(data->storageProps);
|
|
|
|
virJSONValueFree(data->formatProps);
|
2018-06-01 13:56:47 +00:00
|
|
|
virJSONValueFree(data->prmgrProps);
|
2018-05-17 08:32:32 +00:00
|
|
|
virJSONValueFree(data->authsecretProps);
|
|
|
|
virJSONValueFree(data->encryptsecretProps);
|
2018-06-01 10:11:06 +00:00
|
|
|
virJSONValueFree(data->tlsProps);
|
|
|
|
VIR_FREE(data->tlsAlias);
|
2018-05-17 08:32:32 +00:00
|
|
|
VIR_FREE(data->authsecretAlias);
|
|
|
|
VIR_FREE(data->encryptsecretAlias);
|
2018-05-16 11:39:22 +00:00
|
|
|
VIR_FREE(data->driveCmd);
|
|
|
|
VIR_FREE(data->driveAlias);
|
2018-02-23 11:59:13 +00:00
|
|
|
VIR_FREE(data);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* qemuBlockStorageSourceAttachPrepareBlockdev:
|
|
|
|
* @src: storage source to prepare data from
|
2019-09-03 11:58:34 +00:00
|
|
|
* @backingStore: storage source to use as backing of @src
|
2018-09-04 17:29:10 +00:00
|
|
|
* @autoreadonly: use 'auto-read-only' feature of qemu
|
2018-02-23 11:59:13 +00:00
|
|
|
*
|
|
|
|
* Creates a qemuBlockStorageSourceAttachData structure containing data to attach
|
|
|
|
* @src to a VM using the blockdev-add approach. Note that this function only
|
|
|
|
* creates the data for the storage source itself, any other related
|
|
|
|
* authentication/encryption/... objects need to be prepared separately.
|
|
|
|
*
|
|
|
|
* The changes are then applied using qemuBlockStorageSourceAttachApply.
|
|
|
|
*
|
|
|
|
* Returns the filled data structure on success or NULL on error and a libvirt
|
|
|
|
* error is reported
|
|
|
|
*/
|
|
|
|
qemuBlockStorageSourceAttachDataPtr
|
2018-09-04 17:29:10 +00:00
|
|
|
qemuBlockStorageSourceAttachPrepareBlockdev(virStorageSourcePtr src,
|
2019-09-03 11:58:34 +00:00
|
|
|
virStorageSourcePtr backingStore,
|
2018-09-04 17:29:10 +00:00
|
|
|
bool autoreadonly)
|
2018-02-23 11:59:13 +00:00
|
|
|
{
|
2019-10-15 12:47:50 +00:00
|
|
|
g_autoptr(qemuBlockStorageSourceAttachData) data = NULL;
|
2018-02-23 11:59:13 +00:00
|
|
|
|
|
|
|
if (VIR_ALLOC(data) < 0)
|
|
|
|
return NULL;
|
|
|
|
|
2019-09-03 11:27:52 +00:00
|
|
|
if (!(data->formatProps = qemuBlockStorageSourceGetBlockdevProps(src,
|
2019-09-03 11:58:34 +00:00
|
|
|
backingStore)) ||
|
2018-09-04 17:29:10 +00:00
|
|
|
!(data->storageProps = qemuBlockStorageSourceGetBackendProps(src, false,
|
|
|
|
false,
|
|
|
|
autoreadonly)))
|
2019-04-04 09:31:05 +00:00
|
|
|
return NULL;
|
2018-02-23 11:59:13 +00:00
|
|
|
|
|
|
|
data->storageNodeName = src->nodestorage;
|
|
|
|
data->formatNodeName = src->nodeformat;
|
|
|
|
|
2019-10-16 11:35:54 +00:00
|
|
|
return g_steal_pointer(&data);
|
2018-02-23 11:59:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-05-27 15:30:12 +00:00
|
|
|
static int
|
|
|
|
qemuBlockStorageSourceAttachApplyStorageDeps(qemuMonitorPtr mon,
|
|
|
|
qemuBlockStorageSourceAttachDataPtr data)
|
2018-02-23 11:59:13 +00:00
|
|
|
{
|
2018-06-01 13:56:47 +00:00
|
|
|
if (data->prmgrProps &&
|
|
|
|
qemuMonitorAddObject(mon, &data->prmgrProps, &data->prmgrAlias) < 0)
|
|
|
|
return -1;
|
|
|
|
|
2018-05-17 08:32:32 +00:00
|
|
|
if (data->authsecretProps &&
|
|
|
|
qemuMonitorAddObject(mon, &data->authsecretProps,
|
|
|
|
&data->authsecretAlias) < 0)
|
|
|
|
return -1;
|
|
|
|
|
2018-06-01 10:11:06 +00:00
|
|
|
if (data->tlsProps &&
|
|
|
|
qemuMonitorAddObject(mon, &data->tlsProps, &data->tlsAlias) < 0)
|
|
|
|
return -1;
|
|
|
|
|
2019-05-27 15:30:12 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
qemuBlockStorageSourceAttachApplyStorage(qemuMonitorPtr mon,
|
|
|
|
qemuBlockStorageSourceAttachDataPtr data)
|
|
|
|
{
|
|
|
|
int rv;
|
|
|
|
|
2018-02-23 11:59:13 +00:00
|
|
|
if (data->storageProps) {
|
|
|
|
rv = qemuMonitorBlockdevAdd(mon, data->storageProps);
|
|
|
|
data->storageProps = NULL;
|
|
|
|
|
|
|
|
if (rv < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
data->storageAttached = true;
|
|
|
|
}
|
|
|
|
|
2019-05-27 15:30:12 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
qemuBlockStorageSourceAttachApplyFormatDeps(qemuMonitorPtr mon,
|
|
|
|
qemuBlockStorageSourceAttachDataPtr data)
|
|
|
|
{
|
|
|
|
if (data->encryptsecretProps &&
|
|
|
|
qemuMonitorAddObject(mon, &data->encryptsecretProps,
|
|
|
|
&data->encryptsecretAlias) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
qemuBlockStorageSourceAttachApplyFormat(qemuMonitorPtr mon,
|
|
|
|
qemuBlockStorageSourceAttachDataPtr data)
|
|
|
|
{
|
|
|
|
int rv;
|
|
|
|
|
2018-02-23 11:59:13 +00:00
|
|
|
if (data->formatProps) {
|
|
|
|
rv = qemuMonitorBlockdevAdd(mon, data->formatProps);
|
|
|
|
data->formatProps = NULL;
|
|
|
|
|
|
|
|
if (rv < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
data->formatAttached = true;
|
|
|
|
}
|
|
|
|
|
2019-05-27 15:30:12 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* qemuBlockStorageSourceAttachApply:
|
|
|
|
* @mon: monitor object
|
|
|
|
* @data: structure holding data of block device to apply
|
|
|
|
*
|
|
|
|
* Attaches a virStorageSource definition converted to
|
|
|
|
* qemuBlockStorageSourceAttachData to a running VM. This function expects being
|
|
|
|
* called after the monitor was entered.
|
|
|
|
*
|
|
|
|
* Returns 0 on success and -1 on error with a libvirt error reported. If an
|
|
|
|
* error occurred, changes which were already applied need to be rolled back by
|
|
|
|
* calling qemuBlockStorageSourceAttachRollback.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
qemuBlockStorageSourceAttachApply(qemuMonitorPtr mon,
|
|
|
|
qemuBlockStorageSourceAttachDataPtr data)
|
|
|
|
{
|
|
|
|
if (qemuBlockStorageSourceAttachApplyStorageDeps(mon, data) < 0 ||
|
|
|
|
qemuBlockStorageSourceAttachApplyStorage(mon, data) < 0 ||
|
|
|
|
qemuBlockStorageSourceAttachApplyFormatDeps(mon, data) < 0 ||
|
|
|
|
qemuBlockStorageSourceAttachApplyFormat(mon, data) < 0)
|
|
|
|
return -1;
|
|
|
|
|
2018-05-16 11:39:22 +00:00
|
|
|
if (data->driveCmd) {
|
|
|
|
if (qemuMonitorAddDrive(mon, data->driveCmd) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
data->driveAdded = true;
|
|
|
|
}
|
|
|
|
|
2018-02-23 11:59:13 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* qemuBlockStorageSourceAttachRollback:
|
|
|
|
* @mon: monitor object
|
|
|
|
* @data: structure holding data of block device to roll back
|
|
|
|
*
|
|
|
|
* Attempts a best effort rollback of changes which were made to a running VM by
|
|
|
|
* qemuBlockStorageSourceAttachApply. Preserves any existing errors.
|
|
|
|
*
|
|
|
|
* This function expects being called after the monitor was entered.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
qemuBlockStorageSourceAttachRollback(qemuMonitorPtr mon,
|
|
|
|
qemuBlockStorageSourceAttachDataPtr data)
|
|
|
|
{
|
|
|
|
virErrorPtr orig_err;
|
|
|
|
|
|
|
|
virErrorPreserveLast(&orig_err);
|
|
|
|
|
2018-05-16 11:39:22 +00:00
|
|
|
if (data->driveAdded) {
|
|
|
|
if (qemuMonitorDriveDel(mon, data->driveAlias) < 0)
|
|
|
|
VIR_WARN("Unable to remove drive %s (%s) after failed "
|
|
|
|
"qemuMonitorAddDevice", data->driveAlias, data->driveCmd);
|
|
|
|
}
|
|
|
|
|
2018-02-23 11:59:13 +00:00
|
|
|
if (data->formatAttached)
|
|
|
|
ignore_value(qemuMonitorBlockdevDel(mon, data->formatNodeName));
|
|
|
|
|
|
|
|
if (data->storageAttached)
|
|
|
|
ignore_value(qemuMonitorBlockdevDel(mon, data->storageNodeName));
|
|
|
|
|
2018-06-01 13:56:47 +00:00
|
|
|
if (data->prmgrAlias)
|
|
|
|
ignore_value(qemuMonitorDelObject(mon, data->prmgrAlias));
|
|
|
|
|
2018-05-17 08:32:32 +00:00
|
|
|
if (data->authsecretAlias)
|
|
|
|
ignore_value(qemuMonitorDelObject(mon, data->authsecretAlias));
|
|
|
|
|
|
|
|
if (data->encryptsecretAlias)
|
|
|
|
ignore_value(qemuMonitorDelObject(mon, data->encryptsecretAlias));
|
|
|
|
|
2018-06-01 10:11:06 +00:00
|
|
|
if (data->tlsAlias)
|
|
|
|
ignore_value(qemuMonitorDelObject(mon, data->tlsAlias));
|
|
|
|
|
2018-05-17 08:32:32 +00:00
|
|
|
|
2018-02-23 11:59:13 +00:00
|
|
|
virErrorRestore(&orig_err);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-10-10 13:30:24 +00:00
|
|
|
/**
|
|
|
|
* qemuBlockStorageSourceDetachPrepare:
|
|
|
|
* @src: disk source structure
|
|
|
|
* @driveAlias: Alias of the -drive backend, the pointer is always consumed
|
|
|
|
*
|
|
|
|
* Prepare qemuBlockStorageSourceAttachDataPtr for detaching a single source
|
|
|
|
* from a VM. If @driveAlias is NULL -blockdev is assumed.
|
|
|
|
*/
|
|
|
|
qemuBlockStorageSourceAttachDataPtr
|
|
|
|
qemuBlockStorageSourceDetachPrepare(virStorageSourcePtr src,
|
|
|
|
char *driveAlias)
|
|
|
|
{
|
|
|
|
qemuDomainStorageSourcePrivatePtr srcpriv = QEMU_DOMAIN_STORAGE_SOURCE_PRIVATE(src);
|
2019-10-15 12:47:50 +00:00
|
|
|
g_autoptr(qemuBlockStorageSourceAttachData) data = NULL;
|
2018-10-10 13:30:24 +00:00
|
|
|
qemuBlockStorageSourceAttachDataPtr ret = NULL;
|
|
|
|
|
|
|
|
if (VIR_ALLOC(data) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (driveAlias) {
|
2019-10-16 11:43:18 +00:00
|
|
|
data->driveAlias = g_steal_pointer(&driveAlias);
|
2018-10-10 13:30:24 +00:00
|
|
|
data->driveAdded = true;
|
|
|
|
} else {
|
|
|
|
data->formatNodeName = src->nodeformat;
|
|
|
|
data->formatAttached = true;
|
|
|
|
data->storageNodeName = src->nodestorage;
|
|
|
|
data->storageAttached = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (src->pr &&
|
2019-10-20 11:49:46 +00:00
|
|
|
!virStoragePRDefIsManaged(src->pr))
|
|
|
|
data->prmgrAlias = g_strdup(src->pr->mgralias);
|
2018-10-10 13:30:24 +00:00
|
|
|
|
2019-10-20 11:49:46 +00:00
|
|
|
data->tlsAlias = g_strdup(src->tlsAlias);
|
2018-10-10 13:30:24 +00:00
|
|
|
|
|
|
|
if (srcpriv) {
|
2019-10-20 11:49:46 +00:00
|
|
|
if (srcpriv->secinfo && srcpriv->secinfo->type == VIR_DOMAIN_SECRET_INFO_TYPE_AES)
|
|
|
|
data->authsecretAlias = g_strdup(srcpriv->secinfo->s.aes.alias);
|
|
|
|
|
|
|
|
if (srcpriv->encinfo && srcpriv->encinfo->type == VIR_DOMAIN_SECRET_INFO_TYPE_AES)
|
|
|
|
data->encryptsecretAlias = g_strdup(srcpriv->encinfo->s.aes.alias);
|
2018-10-10 13:30:24 +00:00
|
|
|
}
|
|
|
|
|
2019-10-16 11:43:18 +00:00
|
|
|
ret = g_steal_pointer(&data);
|
2018-10-10 13:30:24 +00:00
|
|
|
|
|
|
|
cleanup:
|
|
|
|
VIR_FREE(driveAlias);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-04-04 14:50:49 +00:00
|
|
|
void
|
|
|
|
qemuBlockStorageSourceChainDataFree(qemuBlockStorageSourceChainDataPtr data)
|
|
|
|
{
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
if (!data)
|
|
|
|
return;
|
|
|
|
|
|
|
|
for (i = 0; i < data->nsrcdata; i++)
|
|
|
|
qemuBlockStorageSourceAttachDataFree(data->srcdata[i]);
|
|
|
|
|
|
|
|
VIR_FREE(data->srcdata);
|
|
|
|
VIR_FREE(data);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* qemuBlockStorageSourceChainDetachPrepareBlockdev
|
|
|
|
* @src: storage source chain to remove
|
|
|
|
*
|
|
|
|
* Prepares qemuBlockStorageSourceChainDataPtr for detaching @src and its
|
|
|
|
* backingStore if -blockdev was used.
|
|
|
|
*/
|
|
|
|
qemuBlockStorageSourceChainDataPtr
|
|
|
|
qemuBlockStorageSourceChainDetachPrepareBlockdev(virStorageSourcePtr src)
|
|
|
|
{
|
2019-10-15 12:47:50 +00:00
|
|
|
g_autoptr(qemuBlockStorageSourceAttachData) backend = NULL;
|
|
|
|
g_autoptr(qemuBlockStorageSourceChainData) data = NULL;
|
2019-04-04 14:50:49 +00:00
|
|
|
virStorageSourcePtr n;
|
|
|
|
|
|
|
|
if (VIR_ALLOC(data) < 0)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
for (n = src; virStorageSourceIsBacking(n); n = n->backingStore) {
|
|
|
|
if (!(backend = qemuBlockStorageSourceDetachPrepare(n, NULL)))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (VIR_APPEND_ELEMENT(data->srcdata, data->nsrcdata, backend) < 0)
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2019-10-16 11:35:54 +00:00
|
|
|
return g_steal_pointer(&data);
|
2019-04-04 14:50:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* qemuBlockStorageSourceChainDetachPrepareLegacy
|
|
|
|
* @src: storage source chain to remove
|
|
|
|
* @driveAlias: Alias of the 'drive' backend (always consumed)
|
|
|
|
*
|
|
|
|
* Prepares qemuBlockStorageSourceChainDataPtr for detaching @src and its
|
|
|
|
* backingStore if -drive was used.
|
|
|
|
*/
|
|
|
|
qemuBlockStorageSourceChainDataPtr
|
|
|
|
qemuBlockStorageSourceChainDetachPrepareDrive(virStorageSourcePtr src,
|
|
|
|
char *driveAlias)
|
|
|
|
{
|
2019-10-15 12:47:50 +00:00
|
|
|
g_autoptr(qemuBlockStorageSourceAttachData) backend = NULL;
|
|
|
|
g_autoptr(qemuBlockStorageSourceChainData) data = NULL;
|
2019-04-04 14:50:49 +00:00
|
|
|
|
|
|
|
if (VIR_ALLOC(data) < 0)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (!(backend = qemuBlockStorageSourceDetachPrepare(src, driveAlias)))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (VIR_APPEND_ELEMENT(data->srcdata, data->nsrcdata, backend) < 0)
|
|
|
|
return NULL;
|
|
|
|
|
2019-10-16 11:35:54 +00:00
|
|
|
return g_steal_pointer(&data);
|
2019-04-04 14:50:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* qemuBlockStorageSourceChainAttach:
|
|
|
|
* @mon: monitor object
|
|
|
|
* @data: storage source chain data
|
|
|
|
*
|
|
|
|
* Attach a storage source including its backing chain and supporting objects.
|
|
|
|
* Caller must enter @mon prior calling this function. In case of error this
|
|
|
|
* function returns -1. @data is updated so that qemuBlockStorageSourceChainDetach
|
|
|
|
* can be used to roll-back the changes.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
qemuBlockStorageSourceChainAttach(qemuMonitorPtr mon,
|
|
|
|
qemuBlockStorageSourceChainDataPtr data)
|
|
|
|
{
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
for (i = data->nsrcdata; i > 0; i--) {
|
|
|
|
if (qemuBlockStorageSourceAttachApply(mon, data->srcdata[i - 1]) < 0)
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* qemuBlockStorageSourceChainDetach:
|
|
|
|
* @mon: monitor object
|
|
|
|
* @data: storage source chain data
|
|
|
|
*
|
|
|
|
* Detach a unused storage source including all its backing chain and related
|
|
|
|
* objects described by @data.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
qemuBlockStorageSourceChainDetach(qemuMonitorPtr mon,
|
|
|
|
qemuBlockStorageSourceChainDataPtr data)
|
|
|
|
{
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
for (i = 0; i < data->nsrcdata; i++)
|
|
|
|
qemuBlockStorageSourceAttachRollback(mon, data->srcdata[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-02-23 11:59:13 +00:00
|
|
|
/**
|
|
|
|
* qemuBlockStorageSourceDetachOneBlockdev:
|
|
|
|
* @driver: qemu driver object
|
|
|
|
* @vm: domain object
|
|
|
|
* @asyncJob: currently running async job
|
|
|
|
* @src: storage source to detach
|
|
|
|
*
|
|
|
|
* Detaches one virStorageSource using blockdev-del. Note that this does not
|
|
|
|
* detach any authentication/encryption objects. This function enters the
|
|
|
|
* monitor internally.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
qemuBlockStorageSourceDetachOneBlockdev(virQEMUDriverPtr driver,
|
|
|
|
virDomainObjPtr vm,
|
|
|
|
qemuDomainAsyncJob asyncJob,
|
|
|
|
virStorageSourcePtr src)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (qemuDomainObjEnterMonitorAsync(driver, vm, asyncJob) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
ret = qemuMonitorBlockdevDel(qemuDomainGetMonitor(vm), src->nodeformat);
|
|
|
|
|
|
|
|
if (ret == 0)
|
|
|
|
ret = qemuMonitorBlockdevDel(qemuDomainGetMonitor(vm), src->nodestorage);
|
|
|
|
|
|
|
|
if (qemuDomainObjExitMonitor(driver, vm) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
2018-07-03 12:13:29 +00:00
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
qemuBlockSnapshotAddLegacy(virJSONValuePtr actions,
|
|
|
|
virDomainDiskDefPtr disk,
|
|
|
|
virStorageSourcePtr newsrc,
|
|
|
|
bool reuse)
|
|
|
|
{
|
|
|
|
const char *format = virStorageFileFormatTypeToString(newsrc->format);
|
2019-10-15 13:16:31 +00:00
|
|
|
g_autofree char *device = NULL;
|
|
|
|
g_autofree char *source = NULL;
|
2018-07-03 12:13:29 +00:00
|
|
|
|
|
|
|
if (!(device = qemuAliasDiskDriveFromDisk(disk)))
|
2019-04-04 09:31:05 +00:00
|
|
|
return -1;
|
2018-07-03 12:13:29 +00:00
|
|
|
|
|
|
|
if (qemuGetDriveSourceString(newsrc, NULL, &source) < 0)
|
2019-04-04 09:31:05 +00:00
|
|
|
return -1;
|
2018-07-03 12:13:29 +00:00
|
|
|
|
2019-09-26 14:33:43 +00:00
|
|
|
return qemuMonitorTransactionSnapshotLegacy(actions, device, source, format, reuse);
|
2018-07-03 12:13:29 +00:00
|
|
|
}
|
2018-06-11 11:25:20 +00:00
|
|
|
|
|
|
|
|
2018-09-07 09:33:54 +00:00
|
|
|
int
|
|
|
|
qemuBlockSnapshotAddBlockdev(virJSONValuePtr actions,
|
|
|
|
virDomainDiskDefPtr disk,
|
|
|
|
virStorageSourcePtr newsrc)
|
|
|
|
{
|
2019-09-26 14:33:43 +00:00
|
|
|
return qemuMonitorTransactionSnapshotBlockdev(actions,
|
|
|
|
disk->src->nodeformat,
|
|
|
|
newsrc->nodeformat);
|
2018-09-07 09:33:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-06-11 11:25:20 +00:00
|
|
|
/**
|
|
|
|
* qemuBlockStorageGetCopyOnReadProps:
|
|
|
|
* @disk: disk with copy-on-read enabled
|
|
|
|
*
|
|
|
|
* Creates blockdev properties for a disk copy-on-read layer.
|
|
|
|
*/
|
|
|
|
virJSONValuePtr
|
|
|
|
qemuBlockStorageGetCopyOnReadProps(virDomainDiskDefPtr disk)
|
|
|
|
{
|
|
|
|
qemuDomainDiskPrivatePtr priv = QEMU_DOMAIN_DISK_PRIVATE(disk);
|
|
|
|
virJSONValuePtr ret = NULL;
|
|
|
|
|
|
|
|
ignore_value(virJSONValueObjectCreate(&ret,
|
|
|
|
"s:driver", "copy-on-read",
|
|
|
|
"s:node-name", priv->nodeCopyOnRead,
|
|
|
|
"s:file", disk->src->nodeformat,
|
|
|
|
NULL));
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
2018-09-03 18:08:07 +00:00
|
|
|
|
|
|
|
|
2019-07-22 15:36:05 +00:00
|
|
|
/**
|
|
|
|
* qemuBlockGetBackingStoreString:
|
|
|
|
* @src: storage source to get the string for
|
|
|
|
*
|
|
|
|
* Formats a string used in the backing store field of a disk image which
|
|
|
|
* supports backing store. Non-local storage may result in use of the json:
|
|
|
|
* pseudo protocol for any complex configuration.
|
|
|
|
*/
|
|
|
|
char *
|
|
|
|
qemuBlockGetBackingStoreString(virStorageSourcePtr src)
|
|
|
|
{
|
|
|
|
int actualType = virStorageSourceGetActualType(src);
|
2019-10-15 12:47:50 +00:00
|
|
|
g_autoptr(virJSONValue) backingProps = NULL;
|
|
|
|
g_autoptr(virURI) uri = NULL;
|
2019-10-15 13:16:31 +00:00
|
|
|
g_autofree char *backingJSON = NULL;
|
2019-07-22 15:36:05 +00:00
|
|
|
char *ret = NULL;
|
|
|
|
|
|
|
|
if (virStorageSourceIsLocalStorage(src)) {
|
2019-10-18 11:27:03 +00:00
|
|
|
ret = g_strdup(src->path);
|
2019-07-22 15:36:05 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* generate simplified URIs for the easy cases */
|
|
|
|
if (actualType == VIR_STORAGE_TYPE_NETWORK &&
|
|
|
|
src->nhosts == 1 &&
|
|
|
|
src->hosts->transport == VIR_STORAGE_NET_HOST_TRANS_TCP) {
|
|
|
|
|
|
|
|
switch ((virStorageNetProtocol) src->protocol) {
|
|
|
|
case VIR_STORAGE_NET_PROTOCOL_NBD:
|
|
|
|
case VIR_STORAGE_NET_PROTOCOL_HTTP:
|
|
|
|
case VIR_STORAGE_NET_PROTOCOL_HTTPS:
|
|
|
|
case VIR_STORAGE_NET_PROTOCOL_FTP:
|
|
|
|
case VIR_STORAGE_NET_PROTOCOL_FTPS:
|
|
|
|
case VIR_STORAGE_NET_PROTOCOL_TFTP:
|
|
|
|
case VIR_STORAGE_NET_PROTOCOL_ISCSI:
|
|
|
|
case VIR_STORAGE_NET_PROTOCOL_GLUSTER:
|
|
|
|
if (!(uri = qemuBlockStorageSourceGetURI(src)))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (!(ret = virURIFormat(uri)))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
case VIR_STORAGE_NET_PROTOCOL_SHEEPDOG:
|
|
|
|
case VIR_STORAGE_NET_PROTOCOL_RBD:
|
|
|
|
case VIR_STORAGE_NET_PROTOCOL_VXHS:
|
|
|
|
case VIR_STORAGE_NET_PROTOCOL_SSH:
|
|
|
|
case VIR_STORAGE_NET_PROTOCOL_LAST:
|
|
|
|
case VIR_STORAGE_NET_PROTOCOL_NONE:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* use json: pseudo protocol otherwise */
|
|
|
|
if (!(backingProps = qemuBlockStorageSourceGetBackendProps(src, false, true, false)))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (!(backingJSON = virJSONValueToString(backingProps, false)))
|
|
|
|
return NULL;
|
|
|
|
|
2019-10-22 13:26:14 +00:00
|
|
|
ret = g_strdup_printf("json:%s", backingJSON);
|
2019-07-22 15:36:05 +00:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-09-03 18:08:07 +00:00
|
|
|
static int
|
|
|
|
qemuBlockStorageSourceCreateAddBacking(virStorageSourcePtr backing,
|
|
|
|
virJSONValuePtr props,
|
|
|
|
bool format)
|
|
|
|
{
|
2019-10-15 13:16:31 +00:00
|
|
|
g_autofree char *backingFileStr = NULL;
|
2018-09-03 18:08:07 +00:00
|
|
|
const char *backingFormatStr = NULL;
|
|
|
|
|
|
|
|
if (!virStorageSourceIsBacking(backing))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (format) {
|
|
|
|
if (backing->encryption &&
|
|
|
|
backing->encryption->format == VIR_STORAGE_ENCRYPTION_FORMAT_LUKS)
|
|
|
|
backingFormatStr = "luks";
|
|
|
|
else
|
|
|
|
backingFormatStr = virStorageFileFormatTypeToString(backing->format);
|
|
|
|
}
|
|
|
|
|
2019-07-22 15:36:05 +00:00
|
|
|
if (!(backingFileStr = qemuBlockGetBackingStoreString(backing)))
|
|
|
|
return -1;
|
2018-09-03 18:08:07 +00:00
|
|
|
|
|
|
|
if (virJSONValueObjectAdd(props,
|
|
|
|
"S:backing-file", backingFileStr,
|
|
|
|
"S:backing-fmt", backingFormatStr,
|
|
|
|
NULL) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
qemuBlockStorageSourceCreateGetFormatPropsGeneric(virStorageSourcePtr src,
|
|
|
|
const char *driver,
|
|
|
|
virJSONValuePtr *retprops,
|
|
|
|
virStorageSourcePtr backing)
|
|
|
|
{
|
2019-10-15 12:47:50 +00:00
|
|
|
g_autoptr(virJSONValue) props = NULL;
|
2018-09-03 18:08:07 +00:00
|
|
|
|
|
|
|
if (virJSONValueObjectCreate(&props,
|
|
|
|
"s:driver", driver,
|
|
|
|
"s:file", src->nodestorage,
|
2019-08-30 14:33:48 +00:00
|
|
|
"U:size", src->capacity,
|
2018-09-03 18:08:07 +00:00
|
|
|
NULL) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (backing &&
|
|
|
|
qemuBlockStorageSourceCreateAddBacking(backing, props, false) < 0)
|
|
|
|
return -1;
|
|
|
|
|
2019-10-16 11:43:18 +00:00
|
|
|
*retprops = g_steal_pointer(&props);
|
2018-09-03 18:08:07 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
qemuBlockStorageSourceCreateGetEncryptionLUKS(virStorageSourcePtr src,
|
|
|
|
virJSONValuePtr *luksProps)
|
|
|
|
{
|
|
|
|
qemuDomainStorageSourcePrivatePtr srcpriv = QEMU_DOMAIN_STORAGE_SOURCE_PRIVATE(src);
|
2019-10-15 12:47:50 +00:00
|
|
|
g_autoptr(virJSONValue) props = NULL;
|
2019-10-15 13:16:31 +00:00
|
|
|
g_autofree char *cipheralg = NULL;
|
2018-09-03 18:08:07 +00:00
|
|
|
const char *keysecret = NULL;
|
|
|
|
|
|
|
|
if (srcpriv &&
|
|
|
|
srcpriv->encinfo &&
|
|
|
|
srcpriv->encinfo->type == VIR_DOMAIN_SECRET_INFO_TYPE_AES)
|
|
|
|
keysecret = srcpriv->encinfo->s.aes.alias;
|
|
|
|
|
|
|
|
if (virJSONValueObjectCreate(&props,
|
|
|
|
"s:key-secret", keysecret,
|
|
|
|
NULL) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (src->encryption) {
|
2019-10-22 13:26:14 +00:00
|
|
|
if (src->encryption->encinfo.cipher_name) {
|
|
|
|
cipheralg = g_strdup_printf("%s-%u",
|
|
|
|
src->encryption->encinfo.cipher_name,
|
|
|
|
src->encryption->encinfo.cipher_size);
|
|
|
|
}
|
2018-09-03 18:08:07 +00:00
|
|
|
|
|
|
|
if (virJSONValueObjectAdd(props,
|
|
|
|
"S:cipher-alg", cipheralg,
|
|
|
|
"S:cipher-mode", src->encryption->encinfo.cipher_mode,
|
|
|
|
"S:hash-alg", src->encryption->encinfo.cipher_hash,
|
|
|
|
"S:ivgen-alg", src->encryption->encinfo.ivgen_name,
|
|
|
|
"S:ivgen-hash-alg", src->encryption->encinfo.ivgen_hash,
|
|
|
|
NULL) < 0)
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2019-10-16 11:43:18 +00:00
|
|
|
*luksProps = g_steal_pointer(&props);
|
2018-09-03 18:08:07 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
qemuBlockStorageSourceCreateGetFormatPropsLUKS(virStorageSourcePtr src,
|
|
|
|
virJSONValuePtr *props)
|
|
|
|
{
|
2019-10-15 12:47:50 +00:00
|
|
|
g_autoptr(virJSONValue) luksprops = NULL;
|
2018-09-03 18:08:07 +00:00
|
|
|
|
|
|
|
if (qemuBlockStorageSourceCreateGetEncryptionLUKS(src, &luksprops) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (virJSONValueObjectAdd(luksprops,
|
|
|
|
"s:driver", "luks",
|
|
|
|
"s:file", src->nodestorage,
|
2019-08-30 14:33:48 +00:00
|
|
|
"U:size", src->capacity,
|
2018-09-03 18:08:07 +00:00
|
|
|
NULL) < 0)
|
|
|
|
return -1;
|
|
|
|
|
2019-10-16 11:43:18 +00:00
|
|
|
*props = g_steal_pointer(&luksprops);
|
2018-09-03 18:08:07 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
qemuBlockStorageSourceCreateAddEncryptionQcow(virStorageSourcePtr src,
|
|
|
|
virJSONValuePtr props)
|
|
|
|
{
|
2019-10-15 12:47:50 +00:00
|
|
|
g_autoptr(virJSONValue) encryptProps = NULL;
|
2018-09-03 18:08:07 +00:00
|
|
|
|
|
|
|
if (!src->encryption)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (src->encryption->format != VIR_STORAGE_ENCRYPTION_FORMAT_LUKS) {
|
|
|
|
virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s",
|
|
|
|
_("creation of qcow/qcow2 files supports only 'luks' encryption"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (qemuBlockStorageSourceCreateGetEncryptionLUKS(src, &encryptProps) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (virJSONValueObjectAdd(encryptProps, "s:format", "luks", NULL) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (virJSONValueObjectAdd(props, "a:encrypt", &encryptProps, NULL) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
qemuBlockStorageSourceCreateGetFormatPropsQcow2(virStorageSourcePtr src,
|
|
|
|
virStorageSourcePtr backing,
|
|
|
|
virJSONValuePtr *props)
|
|
|
|
{
|
2019-10-15 12:47:50 +00:00
|
|
|
g_autoptr(virJSONValue) qcow2props = NULL;
|
2018-09-03 18:08:07 +00:00
|
|
|
const char *qcow2version = NULL;
|
|
|
|
|
|
|
|
if (STREQ_NULLABLE(src->compat, "0.10"))
|
|
|
|
qcow2version = "v2";
|
|
|
|
else if (STREQ_NULLABLE(src->compat, "1.1"))
|
|
|
|
qcow2version = "v3";
|
|
|
|
|
|
|
|
if (virJSONValueObjectCreate(&qcow2props,
|
|
|
|
"s:driver", "qcow2",
|
|
|
|
"s:file", src->nodestorage,
|
2019-08-30 14:33:48 +00:00
|
|
|
"U:size", src->capacity,
|
2018-09-03 18:08:07 +00:00
|
|
|
"S:version", qcow2version,
|
|
|
|
NULL) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (qemuBlockStorageSourceCreateAddBacking(backing, qcow2props, true) < 0 ||
|
|
|
|
qemuBlockStorageSourceCreateAddEncryptionQcow(src, qcow2props) < 0)
|
|
|
|
return -1;
|
|
|
|
|
2019-10-16 11:43:18 +00:00
|
|
|
*props = g_steal_pointer(&qcow2props);
|
2018-09-03 18:08:07 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
qemuBlockStorageSourceCreateGetFormatPropsQcow(virStorageSourcePtr src,
|
|
|
|
virStorageSourcePtr backing,
|
|
|
|
virJSONValuePtr *props)
|
|
|
|
{
|
2019-10-15 12:47:50 +00:00
|
|
|
g_autoptr(virJSONValue) qcowprops = NULL;
|
2018-09-03 18:08:07 +00:00
|
|
|
|
|
|
|
if (virJSONValueObjectCreate(&qcowprops,
|
|
|
|
"s:driver", "qcow",
|
|
|
|
"s:file", src->nodestorage,
|
2019-08-30 14:33:48 +00:00
|
|
|
"U:size", src->capacity,
|
2018-09-03 18:08:07 +00:00
|
|
|
NULL) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (qemuBlockStorageSourceCreateAddBacking(backing, qcowprops, false) < 0 ||
|
|
|
|
qemuBlockStorageSourceCreateAddEncryptionQcow(src, qcowprops) < 0)
|
|
|
|
return -1;
|
|
|
|
|
2019-10-16 11:43:18 +00:00
|
|
|
*props = g_steal_pointer(&qcowprops);
|
2018-09-03 18:08:07 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
qemuBlockStorageSourceCreateGetFormatPropsQed(virStorageSourcePtr src,
|
|
|
|
virStorageSourcePtr backing,
|
|
|
|
virJSONValuePtr *props)
|
|
|
|
{
|
2019-10-15 12:47:50 +00:00
|
|
|
g_autoptr(virJSONValue) qedprops = NULL;
|
2018-09-03 18:08:07 +00:00
|
|
|
|
|
|
|
if (virJSONValueObjectCreate(&qedprops,
|
|
|
|
"s:driver", "qed",
|
|
|
|
"s:file", src->nodestorage,
|
2019-08-30 14:33:48 +00:00
|
|
|
"U:size", src->capacity,
|
2018-09-03 18:08:07 +00:00
|
|
|
NULL) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (qemuBlockStorageSourceCreateAddBacking(backing, qedprops, true) < 0)
|
|
|
|
return -1;
|
|
|
|
|
2019-10-16 11:43:18 +00:00
|
|
|
*props = g_steal_pointer(&qedprops);
|
2018-09-03 18:08:07 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* qemuBlockStorageSourceCreateGetFormatProps:
|
|
|
|
* @src: storage source to format
|
|
|
|
* @backing: storage source describing backing image of @src (if necessary)
|
|
|
|
* @props: filled with props to be used with 'blockdev-create' to format @src
|
|
|
|
*
|
|
|
|
* @src must be properly initialized to contain node-names of the protocol layer
|
|
|
|
* which should be formatted. @props may be NULL with success returned in which
|
|
|
|
* case creation of given storage format is not supported. Note that creation
|
|
|
|
* of 'raw' storage is also returns NULL as there is nothing to do.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
qemuBlockStorageSourceCreateGetFormatProps(virStorageSourcePtr src,
|
|
|
|
virStorageSourcePtr backing,
|
|
|
|
virJSONValuePtr *props)
|
|
|
|
{
|
|
|
|
switch ((virStorageFileFormat) src->format) {
|
|
|
|
case VIR_STORAGE_FILE_RAW:
|
|
|
|
if (!src->encryption ||
|
|
|
|
src->encryption->format != VIR_STORAGE_ENCRYPTION_FORMAT_LUKS)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
return qemuBlockStorageSourceCreateGetFormatPropsLUKS(src, props);
|
|
|
|
|
|
|
|
case VIR_STORAGE_FILE_QCOW2:
|
|
|
|
return qemuBlockStorageSourceCreateGetFormatPropsQcow2(src, backing, props);
|
|
|
|
|
|
|
|
case VIR_STORAGE_FILE_QCOW:
|
|
|
|
return qemuBlockStorageSourceCreateGetFormatPropsQcow(src, backing, props);
|
|
|
|
|
|
|
|
case VIR_STORAGE_FILE_QED:
|
|
|
|
return qemuBlockStorageSourceCreateGetFormatPropsQed(src, backing, props);
|
|
|
|
|
|
|
|
case VIR_STORAGE_FILE_VPC:
|
|
|
|
return qemuBlockStorageSourceCreateGetFormatPropsGeneric(src, "vpc",
|
|
|
|
props, NULL);
|
|
|
|
|
|
|
|
case VIR_STORAGE_FILE_PLOOP:
|
|
|
|
return qemuBlockStorageSourceCreateGetFormatPropsGeneric(src, "parallels",
|
|
|
|
props, NULL);
|
|
|
|
|
|
|
|
case VIR_STORAGE_FILE_VDI:
|
|
|
|
return qemuBlockStorageSourceCreateGetFormatPropsGeneric(src, "vdi",
|
|
|
|
props, NULL);
|
|
|
|
|
|
|
|
case VIR_STORAGE_FILE_VHD:
|
|
|
|
return qemuBlockStorageSourceCreateGetFormatPropsGeneric(src, "vhdx",
|
|
|
|
props, NULL);
|
|
|
|
|
|
|
|
case VIR_STORAGE_FILE_VMDK:
|
|
|
|
return qemuBlockStorageSourceCreateGetFormatPropsGeneric(src, "vmdk",
|
|
|
|
props, backing);
|
|
|
|
|
|
|
|
/* unsupported by qemu / impossible */
|
|
|
|
case VIR_STORAGE_FILE_FAT:
|
|
|
|
case VIR_STORAGE_FILE_BOCHS:
|
|
|
|
case VIR_STORAGE_FILE_CLOOP:
|
|
|
|
case VIR_STORAGE_FILE_DMG:
|
|
|
|
case VIR_STORAGE_FILE_COW:
|
|
|
|
case VIR_STORAGE_FILE_ISO:
|
|
|
|
case VIR_STORAGE_FILE_DIR:
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
case VIR_STORAGE_FILE_AUTO_SAFE:
|
|
|
|
case VIR_STORAGE_FILE_AUTO:
|
|
|
|
case VIR_STORAGE_FILE_NONE:
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("mishandled storage format '%s'"),
|
|
|
|
virStorageFileFormatTypeToString(src->format));
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
case VIR_STORAGE_FILE_LAST:
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
virReportEnumRangeError(virStorageFileFormat, src->format);
|
|
|
|
return -1;
|
|
|
|
}
|
2018-09-06 07:09:55 +00:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* qemuBlockStorageSourceCreateGetStorageProps:
|
|
|
|
* @src: storage source to create
|
|
|
|
* @props: filled with props to be used with 'blockdev-create' to create @src
|
|
|
|
*
|
|
|
|
* This function should be used only if @src->type is VIR_STORAGE_TYPE_NETWORK.
|
|
|
|
* Note that @props may be NULL if qemu does not support creation storage
|
|
|
|
* on given protocol. @src->physical is used as size for the storage.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
qemuBlockStorageSourceCreateGetStorageProps(virStorageSourcePtr src,
|
|
|
|
virJSONValuePtr *props)
|
|
|
|
{
|
|
|
|
int actualType = virStorageSourceGetActualType(src);
|
2019-10-15 12:47:50 +00:00
|
|
|
g_autoptr(virJSONValue) location = NULL;
|
2018-09-06 07:09:55 +00:00
|
|
|
const char *driver = NULL;
|
|
|
|
const char *filename = NULL;
|
|
|
|
|
|
|
|
switch ((virStorageType) actualType) {
|
|
|
|
case VIR_STORAGE_TYPE_FILE:
|
|
|
|
driver = "file";
|
|
|
|
filename = src->path;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case VIR_STORAGE_TYPE_NETWORK:
|
|
|
|
switch ((virStorageNetProtocol) src->protocol) {
|
|
|
|
case VIR_STORAGE_NET_PROTOCOL_GLUSTER:
|
|
|
|
driver = "gluster";
|
|
|
|
if (!(location = qemuBlockStorageSourceGetGlusterProps(src, false, false)))
|
|
|
|
return -1;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case VIR_STORAGE_NET_PROTOCOL_RBD:
|
|
|
|
driver = "rbd";
|
|
|
|
if (!(location = qemuBlockStorageSourceGetRBDProps(src, false)))
|
|
|
|
return -1;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case VIR_STORAGE_NET_PROTOCOL_SHEEPDOG:
|
|
|
|
driver = "sheepdog";
|
|
|
|
if (!(location = qemuBlockStorageSourceGetSheepdogProps(src)))
|
|
|
|
return -1;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case VIR_STORAGE_NET_PROTOCOL_SSH:
|
|
|
|
driver = "ssh";
|
|
|
|
if (!(location = qemuBlockStorageSourceGetSshProps(src)))
|
|
|
|
return -1;
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* unsupported/impossible */
|
|
|
|
case VIR_STORAGE_NET_PROTOCOL_NBD:
|
|
|
|
case VIR_STORAGE_NET_PROTOCOL_ISCSI:
|
|
|
|
case VIR_STORAGE_NET_PROTOCOL_VXHS:
|
|
|
|
case VIR_STORAGE_NET_PROTOCOL_HTTP:
|
|
|
|
case VIR_STORAGE_NET_PROTOCOL_HTTPS:
|
|
|
|
case VIR_STORAGE_NET_PROTOCOL_FTP:
|
|
|
|
case VIR_STORAGE_NET_PROTOCOL_FTPS:
|
|
|
|
case VIR_STORAGE_NET_PROTOCOL_TFTP:
|
|
|
|
case VIR_STORAGE_NET_PROTOCOL_NONE:
|
|
|
|
case VIR_STORAGE_NET_PROTOCOL_LAST:
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case VIR_STORAGE_TYPE_BLOCK:
|
|
|
|
case VIR_STORAGE_TYPE_DIR:
|
|
|
|
case VIR_STORAGE_TYPE_VOLUME:
|
2019-06-03 15:31:13 +00:00
|
|
|
case VIR_STORAGE_TYPE_NVME:
|
2018-09-06 07:09:55 +00:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
case VIR_STORAGE_TYPE_NONE:
|
|
|
|
case VIR_STORAGE_TYPE_LAST:
|
|
|
|
virReportEnumRangeError(virStorageType, actualType);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virJSONValueObjectCreate(props,
|
|
|
|
"s:driver", driver,
|
|
|
|
"S:filename", filename,
|
|
|
|
"A:location", &location,
|
2019-08-30 14:33:48 +00:00
|
|
|
"U:size", src->physical,
|
2018-09-06 07:09:55 +00:00
|
|
|
NULL) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2019-06-10 16:13:09 +00:00
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
qemuBlockStorageSourceCreateGeneric(virDomainObjPtr vm,
|
|
|
|
virJSONValuePtr createProps,
|
|
|
|
virStorageSourcePtr src,
|
|
|
|
virStorageSourcePtr chain,
|
|
|
|
bool storageCreate,
|
|
|
|
qemuDomainAsyncJob asyncJob)
|
|
|
|
{
|
2019-10-15 12:47:50 +00:00
|
|
|
g_autoptr(virJSONValue) props = createProps;
|
2019-06-10 16:13:09 +00:00
|
|
|
qemuDomainObjPrivatePtr priv = vm->privateData;
|
|
|
|
qemuBlockJobDataPtr job = NULL;
|
|
|
|
int ret = -1;
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
if (!(job = qemuBlockJobNewCreate(vm, src, chain, storageCreate)))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
qemuBlockJobSyncBegin(job);
|
|
|
|
|
|
|
|
if (qemuDomainObjEnterMonitorAsync(priv->driver, vm, asyncJob) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
rc = qemuMonitorBlockdevCreate(priv->mon, job->name, props);
|
|
|
|
props = NULL;
|
|
|
|
|
|
|
|
if (qemuDomainObjExitMonitor(priv->driver, vm) < 0 || rc < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
qemuBlockJobStarted(job, vm);
|
|
|
|
|
|
|
|
qemuBlockJobUpdate(vm, job, QEMU_ASYNC_JOB_NONE);
|
|
|
|
while (qemuBlockJobIsRunning(job)) {
|
|
|
|
if (virDomainObjWait(vm) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
qemuBlockJobUpdate(vm, job, QEMU_ASYNC_JOB_NONE);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (job->state == QEMU_BLOCKJOB_STATE_FAILED ||
|
|
|
|
job->state == QEMU_BLOCKJOB_STATE_CANCELLED) {
|
|
|
|
if (job->state == QEMU_BLOCKJOB_STATE_CANCELLED && !job->errmsg) {
|
|
|
|
virReportError(VIR_ERR_OPERATION_FAILED, "%s",
|
|
|
|
_("blockdev-create job was cancelled"));
|
|
|
|
} else {
|
|
|
|
virReportError(VIR_ERR_OPERATION_FAILED,
|
|
|
|
_("failed to format image: '%s'"), NULLSTR(job->errmsg));
|
|
|
|
}
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
qemuBlockJobStartupFinalize(vm, job);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
qemuBlockStorageSourceCreateStorage(virDomainObjPtr vm,
|
|
|
|
virStorageSourcePtr src,
|
|
|
|
virStorageSourcePtr chain,
|
|
|
|
qemuDomainAsyncJob asyncJob)
|
|
|
|
{
|
|
|
|
int actualType = virStorageSourceGetActualType(src);
|
2019-10-15 12:47:50 +00:00
|
|
|
g_autoptr(virJSONValue) createstorageprops = NULL;
|
2019-06-10 16:13:09 +00:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
/* We create local files directly to be able to apply security labels
|
|
|
|
* properly. This is enough for formats which store the capacity of the image
|
|
|
|
* in the metadata as they will grow. We must create a correctly sized
|
|
|
|
* image for 'raw' and 'luks' though as the image size influences the
|
|
|
|
* capacity.
|
|
|
|
*/
|
|
|
|
if (actualType != VIR_STORAGE_TYPE_NETWORK &&
|
|
|
|
!(actualType == VIR_STORAGE_TYPE_FILE && src->format == VIR_STORAGE_FILE_RAW))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (qemuBlockStorageSourceCreateGetStorageProps(src, &createstorageprops) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (!createstorageprops) {
|
|
|
|
/* we can always try opening it to see whether it was existing */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = qemuBlockStorageSourceCreateGeneric(vm, createstorageprops, src, chain,
|
|
|
|
true, asyncJob);
|
|
|
|
createstorageprops = NULL;
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
qemuBlockStorageSourceCreateFormat(virDomainObjPtr vm,
|
|
|
|
virStorageSourcePtr src,
|
|
|
|
virStorageSourcePtr backingStore,
|
|
|
|
virStorageSourcePtr chain,
|
|
|
|
qemuDomainAsyncJob asyncJob)
|
|
|
|
{
|
2019-10-15 12:47:50 +00:00
|
|
|
g_autoptr(virJSONValue) createformatprops = NULL;
|
2019-06-10 16:13:09 +00:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (src->format == VIR_STORAGE_FILE_RAW)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (qemuBlockStorageSourceCreateGetFormatProps(src, backingStore,
|
|
|
|
&createformatprops) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (!createformatprops) {
|
|
|
|
virReportError(VIR_ERR_OPERATION_UNSUPPORTED,
|
|
|
|
_("can't create storage format '%s'"),
|
|
|
|
virStorageFileFormatTypeToString(src->format));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = qemuBlockStorageSourceCreateGeneric(vm, createformatprops, src, chain,
|
|
|
|
false, asyncJob);
|
|
|
|
createformatprops = NULL;
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* qemuBlockStorageSourceCreate:
|
|
|
|
* @vm: domain object
|
|
|
|
* @src: storage source definition to create
|
|
|
|
* @backingStore: backingStore of the new image (used only in image metadata)
|
|
|
|
* @chain: backing chain to unplug in case of a long-running job failure
|
|
|
|
* @data: qemuBlockStorageSourceAttachData for @src so that it can be attached
|
|
|
|
* @asyncJob: qemu asynchronous job type
|
|
|
|
*
|
|
|
|
* Creates and formats a storage volume according to @src and attaches it to @vm.
|
|
|
|
* @data must provide attachment data as if @src was existing. @src is attached
|
|
|
|
* after successful return of this function. If libvirtd is restarted during
|
|
|
|
* the create job @chain is unplugged, otherwise it's left for the caller.
|
|
|
|
* If @backingStore is provided, the new image will refer to it as its backing
|
|
|
|
* store.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
qemuBlockStorageSourceCreate(virDomainObjPtr vm,
|
|
|
|
virStorageSourcePtr src,
|
|
|
|
virStorageSourcePtr backingStore,
|
|
|
|
virStorageSourcePtr chain,
|
|
|
|
qemuBlockStorageSourceAttachDataPtr data,
|
|
|
|
qemuDomainAsyncJob asyncJob)
|
|
|
|
{
|
|
|
|
qemuDomainObjPrivatePtr priv = vm->privateData;
|
|
|
|
int ret = -1;
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
if (qemuDomainObjEnterMonitorAsync(priv->driver, vm, asyncJob) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
rc = qemuBlockStorageSourceAttachApplyStorageDeps(priv->mon, data);
|
|
|
|
|
|
|
|
if (qemuDomainObjExitMonitor(priv->driver, vm) < 0 || rc < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (qemuBlockStorageSourceCreateStorage(vm, src, chain, asyncJob) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (qemuDomainObjEnterMonitorAsync(priv->driver, vm, asyncJob) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
rc = qemuBlockStorageSourceAttachApplyStorage(priv->mon, data);
|
|
|
|
|
|
|
|
if (rc == 0)
|
|
|
|
rc = qemuBlockStorageSourceAttachApplyFormatDeps(priv->mon, data);
|
|
|
|
|
|
|
|
if (qemuDomainObjExitMonitor(priv->driver, vm) < 0 || rc < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (qemuBlockStorageSourceCreateFormat(vm, src, backingStore, chain,
|
|
|
|
asyncJob) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (qemuDomainObjEnterMonitorAsync(priv->driver, vm, asyncJob) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
rc = qemuBlockStorageSourceAttachApplyFormat(priv->mon, data);
|
|
|
|
|
|
|
|
if (qemuDomainObjExitMonitor(priv->driver, vm) < 0 || rc < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
if (ret < 0 &&
|
|
|
|
virDomainObjIsActive(vm) &&
|
|
|
|
qemuDomainObjEnterMonitorAsync(priv->driver, vm, asyncJob) == 0) {
|
|
|
|
|
|
|
|
qemuBlockStorageSourceAttachRollback(priv->mon, data);
|
|
|
|
ignore_value(qemuDomainObjExitMonitor(priv->driver, vm));
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* qemuBlockStorageSourceCreateDetectSize:
|
2019-10-10 13:02:00 +00:00
|
|
|
* @blockNamedNodeData: hash table filled with qemuBlockNamedNodeData
|
2019-06-10 16:13:09 +00:00
|
|
|
* @src: storage source to update size/capacity on
|
|
|
|
* @templ: storage source template
|
|
|
|
*
|
|
|
|
* When creating a storage source via blockdev-create we need to know the size
|
|
|
|
* and capacity of the original volume (e.g. when creating a snapshot or copy).
|
|
|
|
* This function updates @src's 'capacity' and 'physical' attributes according
|
|
|
|
* to the detected sizes from @templ.
|
|
|
|
*/
|
|
|
|
int
|
2019-10-10 13:02:00 +00:00
|
|
|
qemuBlockStorageSourceCreateDetectSize(virHashTablePtr blockNamedNodeData,
|
2019-06-10 16:13:09 +00:00
|
|
|
virStorageSourcePtr src,
|
2019-10-10 13:02:00 +00:00
|
|
|
virStorageSourcePtr templ)
|
2019-06-10 16:13:09 +00:00
|
|
|
{
|
2019-10-10 13:02:00 +00:00
|
|
|
qemuBlockNamedNodeDataPtr entry;
|
2019-06-10 16:13:09 +00:00
|
|
|
|
2019-10-10 13:02:00 +00:00
|
|
|
if (!(entry = virHashLookup(blockNamedNodeData, templ->nodeformat))) {
|
2019-06-10 16:13:09 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("failed to update capacity data for block node '%s'"),
|
|
|
|
templ->nodeformat);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (src->format == VIR_STORAGE_FILE_RAW) {
|
|
|
|
src->physical = entry->capacity;
|
|
|
|
} else {
|
|
|
|
src->physical = entry->physical;
|
|
|
|
}
|
|
|
|
|
|
|
|
src->capacity = entry->capacity;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2019-11-19 07:43:58 +00:00
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
qemuBlockRemoveImageMetadata(virQEMUDriverPtr driver,
|
|
|
|
virDomainObjPtr vm,
|
|
|
|
const char *diskTarget,
|
|
|
|
virStorageSourcePtr src)
|
|
|
|
{
|
|
|
|
virStorageSourcePtr n;
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
for (n = src; virStorageSourceIsBacking(n); n = n->backingStore) {
|
|
|
|
if (qemuSecurityMoveImageMetadata(driver, vm, n, NULL) < 0) {
|
|
|
|
VIR_WARN("Unable to remove disk metadata on "
|
|
|
|
"vm %s from %s (disk target %s)",
|
|
|
|
vm->def->name,
|
|
|
|
NULLSTR(n->path),
|
|
|
|
diskTarget);
|
|
|
|
ret = -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
2019-12-05 14:57:30 +00:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* qemuBlockNamedNodeDataGetBitmapByName:
|
|
|
|
* @blockNamedNodeData: hash table returned by qemuMonitorBlockGetNamedNodeData
|
|
|
|
* @src: disk source to find the bitmap for
|
|
|
|
* @bitmap: name of the bitmap to find
|
|
|
|
*
|
|
|
|
* Looks up a bitmap named @bitmap of the @src image.
|
|
|
|
*/
|
|
|
|
qemuBlockNamedNodeDataBitmapPtr
|
|
|
|
qemuBlockNamedNodeDataGetBitmapByName(virHashTablePtr blockNamedNodeData,
|
|
|
|
virStorageSourcePtr src,
|
|
|
|
const char *bitmap)
|
|
|
|
{
|
|
|
|
qemuBlockNamedNodeDataPtr nodedata;
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
if (!(nodedata = virHashLookup(blockNamedNodeData, src->nodeformat)))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
for (i = 0; i < nodedata->nbitmaps; i++) {
|
|
|
|
qemuBlockNamedNodeDataBitmapPtr bitmapdata = nodedata->bitmaps[i];
|
|
|
|
|
|
|
|
if (STRNEQ(bitmapdata->name, bitmap))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
return bitmapdata;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|