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"
|
2018-07-03 12:13:29 +00:00
|
|
|
#include "qemu_monitor_json.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
|
|
|
|
qemuBlockNamedNodesArrayToHash(size_t pos ATTRIBUTE_UNUSED,
|
|
|
|
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-04-04 09:18:07 +00:00
|
|
|
VIR_DEFINE_AUTOPTR_FUNC(qemuBlockNodeNameBackingChainData,
|
|
|
|
qemuBlockNodeNameBackingChainDataFree);
|
|
|
|
|
2017-03-13 11:46:18 +00:00
|
|
|
|
|
|
|
static void
|
|
|
|
qemuBlockNodeNameBackingChainDataHashEntryFree(void *opaque,
|
|
|
|
const void *name ATTRIBUTE_UNUSED)
|
|
|
|
{
|
|
|
|
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-04-04 09:18:07 +00:00
|
|
|
VIR_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
|
|
|
|
2017-07-26 07:36:21 +00:00
|
|
|
if (VIR_STRDUP(data->nodeformat, nodename) < 0 ||
|
|
|
|
VIR_STRDUP(data->nodestorage, parentnodename) < 0 ||
|
2017-07-25 17:10:27 +00:00
|
|
|
VIR_STRDUP(data->qemufilename, filename) < 0 ||
|
|
|
|
VIR_STRDUP(data->drvformat, drvname) < 0 ||
|
|
|
|
VIR_STRDUP(data->drvstorage, drvparent) < 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 (backing &&
|
|
|
|
qemuBlockNodeNameGetBackingChainBacking(backing, nodenamestable,
|
|
|
|
&backingdata) < 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
|
|
|
VIR_STEAL_PTR(data->backing, backingdata);
|
|
|
|
VIR_STEAL_PTR(*nodenamedata, 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
|
2017-07-26 07:36:21 +00:00
|
|
|
qemuBlockNodeNameGetBackingChainDisk(size_t pos ATTRIBUTE_UNUSED,
|
|
|
|
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-04-04 09:18:07 +00:00
|
|
|
VIR_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-04-04 09:03:24 +00:00
|
|
|
VIR_AUTOPTR(virHashTable) namednodestable = NULL;
|
|
|
|
VIR_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-04-05 15:52:17 +00:00
|
|
|
VIR_RETURN_PTR(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-04-04 09:13:36 +00:00
|
|
|
VIR_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 {
|
|
|
|
if (VIR_STRDUP(src->nodeformat, entry->nodeformat) < 0 ||
|
2017-07-25 16:11:58 +00:00
|
|
|
VIR_STRDUP(src->nodestorage, entry->nodestorage) < 0)
|
2017-09-25 09:44:00 +00:00
|
|
|
goto cleanup;
|
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-04-04 09:03:24 +00:00
|
|
|
VIR_AUTOPTR(virHashTable) disktable = NULL;
|
2019-04-04 08:55:32 +00:00
|
|
|
VIR_AUTOPTR(virJSONValue) data = NULL;
|
|
|
|
VIR_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-04-04 09:03:24 +00:00
|
|
|
VIR_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-04-05 15:52:17 +00:00
|
|
|
VIR_RETURN_PTR(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-04-04 09:03:24 +00:00
|
|
|
VIR_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;
|
|
|
|
|
|
|
|
if (VIR_STRDUP(uri->scheme,
|
|
|
|
virStorageNetProtocolTypeToString(src->protocol)) < 0)
|
2019-04-04 09:31:05 +00:00
|
|
|
return NULL;
|
2017-07-13 13:38:50 +00:00
|
|
|
} else {
|
|
|
|
if (virAsprintf(&uri->scheme, "%s+%s",
|
|
|
|
virStorageNetProtocolTypeToString(src->protocol),
|
|
|
|
virStorageNetHostTransportTypeToString(src->hosts->transport)) < 0)
|
2019-04-04 09:31:05 +00:00
|
|
|
return NULL;
|
2017-07-13 13:38:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (src->path) {
|
|
|
|
if (src->volume) {
|
2017-10-23 14:39:49 +00:00
|
|
|
if (virAsprintf(&uri->path, "/%s/%s",
|
2017-07-13 13:38:50 +00:00
|
|
|
src->volume, src->path) < 0)
|
2019-04-04 09:31:05 +00:00
|
|
|
return NULL;
|
2017-07-13 13:38:50 +00:00
|
|
|
} else {
|
|
|
|
if (virAsprintf(&uri->path, "%s%s",
|
|
|
|
src->path[0] == '/' ? "" : "/",
|
|
|
|
src->path) < 0)
|
2019-04-04 09:31:05 +00:00
|
|
|
return NULL;
|
2017-07-13 13:38:50 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (VIR_STRDUP(uri->server, src->hosts->name) < 0)
|
2019-04-04 09:31:05 +00:00
|
|
|
return NULL;
|
2017-07-13 13:38:50 +00:00
|
|
|
|
2019-04-05 15:52:17 +00:00
|
|
|
VIR_RETURN_PTR(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-04-04 08:55:32 +00:00
|
|
|
VIR_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-04-04 09:13:36 +00:00
|
|
|
VIR_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";
|
|
|
|
|
|
|
|
if (virAsprintf(&port, "%u", host->port) < 0)
|
2019-04-04 09:31:05 +00:00
|
|
|
return NULL;
|
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-04-05 15:52:17 +00:00
|
|
|
VIR_RETURN_PTR(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-04-04 08:55:32 +00:00
|
|
|
VIR_AUTOPTR(virJSONValue) servers = NULL;
|
|
|
|
VIR_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-04-05 15:52:17 +00:00
|
|
|
VIR_RETURN_PTR(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-04-04 09:13:36 +00:00
|
|
|
VIR_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;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virAsprintf(&port, "%u", host->port) < 0)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
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-04-04 08:55:32 +00:00
|
|
|
VIR_AUTOPTR(virJSONValue) servers = NULL;
|
|
|
|
VIR_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-04-05 15:52:17 +00:00
|
|
|
VIR_RETURN_PTR(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,
|
|
|
|
bool legacy)
|
2017-07-07 14:29:01 +00:00
|
|
|
{
|
2019-04-04 08:55:32 +00:00
|
|
|
VIR_AUTOPTR(virJSONValue) servers = NULL;
|
|
|
|
VIR_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 16:00:04 +00:00
|
|
|
"s:driver", "gluster",
|
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
|
|
|
|
|
|
|
if (src->debug &&
|
|
|
|
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-04-05 15:52:17 +00:00
|
|
|
VIR_RETURN_PTR(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
|
|
|
|
qemuBlockStorageSourceGetVxHSProps(virStorageSourcePtr src)
|
|
|
|
{
|
|
|
|
const char *protocol = virStorageNetProtocolTypeToString(src->protocol);
|
2019-04-04 08:55:32 +00:00
|
|
|
VIR_AUTOPTR(virJSONValue) 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
|
|
|
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;
|
|
|
|
|
|
|
|
/* 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,
|
|
|
|
"s:driver", protocol,
|
|
|
|
"S:tls-creds", src->tlsAlias,
|
|
|
|
"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
|
|
|
|
qemuBlockStorageSourceGetCURLProps(virStorageSourcePtr src)
|
|
|
|
{
|
|
|
|
qemuDomainStorageSourcePrivatePtr srcPriv = QEMU_DOMAIN_STORAGE_SOURCE_PRIVATE(src);
|
|
|
|
const char *passwordalias = NULL;
|
|
|
|
const char *username = NULL;
|
|
|
|
virJSONValuePtr ret = NULL;
|
2019-04-04 09:03:24 +00:00
|
|
|
VIR_AUTOPTR(virURI) uri = NULL;
|
2019-04-04 09:13:36 +00:00
|
|
|
VIR_AUTOFREE(char *) uristr = NULL;
|
2017-07-13 13:48:06 +00:00
|
|
|
const char *driver;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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
|
|
|
|
*/
|
|
|
|
|
|
|
|
driver = virStorageNetProtocolTypeToString(src->protocol);
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
if (src->auth) {
|
|
|
|
username = src->auth->username;
|
|
|
|
passwordalias = srcPriv->secinfo->s.aes.alias;
|
|
|
|
}
|
|
|
|
|
|
|
|
ignore_value(virJSONValueObjectCreate(&ret,
|
|
|
|
"s:driver", driver,
|
|
|
|
"s:url", uristr,
|
|
|
|
"S:username", username,
|
|
|
|
"S:password-secret", passwordalias,
|
|
|
|
NULL));
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-10-19 15:44:57 +00:00
|
|
|
static virJSONValuePtr
|
|
|
|
qemuBlockStorageSourceGetISCSIProps(virStorageSourcePtr src)
|
|
|
|
{
|
|
|
|
qemuDomainStorageSourcePrivatePtr srcPriv = QEMU_DOMAIN_STORAGE_SOURCE_PRIVATE(src);
|
|
|
|
const char *protocol = virStorageNetProtocolTypeToString(src->protocol);
|
2019-04-04 09:13:36 +00:00
|
|
|
VIR_AUTOFREE(char *) target = NULL;
|
2017-10-19 15:44:57 +00:00
|
|
|
char *lunStr = NULL;
|
|
|
|
char *username = NULL;
|
|
|
|
char *objalias = NULL;
|
2019-04-04 09:13:36 +00:00
|
|
|
VIR_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
|
|
|
* }
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (VIR_STRDUP(target, src->path) < 0)
|
2019-04-04 09:31:05 +00:00
|
|
|
return NULL;
|
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) {
|
|
|
|
if (virAsprintf(&portal, "[%s]:%u",
|
|
|
|
src->hosts[0].name, src->hosts[0].port) < 0)
|
2019-04-04 09:31:05 +00:00
|
|
|
return NULL;
|
2017-10-19 15:44:57 +00:00
|
|
|
} else {
|
|
|
|
if (virAsprintf(&portal, "%s:%u",
|
|
|
|
src->hosts[0].name, src->hosts[0].port) < 0)
|
2019-04-04 09:31:05 +00:00
|
|
|
return NULL;
|
2017-10-19 15:44:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (src->auth) {
|
|
|
|
username = src->auth->username;
|
|
|
|
objalias = srcPriv->secinfo->s.aes.alias;
|
|
|
|
}
|
|
|
|
|
|
|
|
ignore_value(virJSONValueObjectCreate(&ret,
|
|
|
|
"s:driver", protocol,
|
|
|
|
"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
|
|
|
|
qemuBlockStorageSourceGetNBDProps(virStorageSourcePtr src)
|
|
|
|
{
|
2019-04-04 08:55:32 +00:00
|
|
|
VIR_AUTOPTR(virJSONValue) serverprops = NULL;
|
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;
|
|
|
|
|
2017-11-09 09:29:53 +00:00
|
|
|
if (virJSONValueObjectCreate(&ret,
|
|
|
|
"s:driver", "nbd",
|
2018-03-30 09:12:57 +00:00
|
|
|
"a:server", &serverprops,
|
2017-11-09 09:29:53 +00:00
|
|
|
"S:export", src->path,
|
|
|
|
"S:tls-creds", src->tlsAlias,
|
|
|
|
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
|
|
|
|
qemuBlockStorageSourceGetRBDProps(virStorageSourcePtr src)
|
|
|
|
{
|
|
|
|
qemuDomainStorageSourcePrivatePtr srcPriv = QEMU_DOMAIN_STORAGE_SOURCE_PRIVATE(src);
|
2019-04-04 08:55:32 +00:00
|
|
|
VIR_AUTOPTR(virJSONValue) servers = NULL;
|
2017-10-23 16:16:53 +00:00
|
|
|
virJSONValuePtr ret = NULL;
|
|
|
|
const char *username = NULL;
|
2019-04-04 08:55:32 +00:00
|
|
|
VIR_AUTOPTR(virJSONValue) authmodes = NULL;
|
|
|
|
VIR_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-07-09 14:05:16 +00:00
|
|
|
if (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:driver", "rbd",
|
|
|
|
"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-04-04 08:55:32 +00:00
|
|
|
VIR_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,
|
|
|
|
"s:driver", "sheepdog",
|
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-04-04 08:55:32 +00:00
|
|
|
VIR_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:driver", "ssh",
|
|
|
|
"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
|
|
|
|
qemuBlockStorageSourceGetFileProps(virStorageSourcePtr src)
|
|
|
|
{
|
2018-04-24 15:12:19 +00:00
|
|
|
const char *driver = "file";
|
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;
|
|
|
|
|
2017-12-05 16:05:12 +00:00
|
|
|
if (src->iomode != VIR_DOMAIN_DISK_IO_DEFAULT)
|
|
|
|
iomode = virDomainDiskIoTypeToString(src->iomode);
|
|
|
|
|
2018-04-24 15:12:19 +00:00
|
|
|
if (virStorageSourceIsBlockLocal(src)) {
|
|
|
|
if (src->hostcdrom)
|
|
|
|
driver = "host_cdrom";
|
|
|
|
else
|
|
|
|
driver = "host_device";
|
|
|
|
}
|
|
|
|
|
2018-05-30 13:47:13 +00:00
|
|
|
if (src->pr)
|
|
|
|
prManagerAlias = src->pr->mgralias;
|
|
|
|
|
2017-12-05 16:02:28 +00:00
|
|
|
ignore_value(virJSONValueObjectCreate(&ret,
|
2018-04-24 15:12:19 +00:00
|
|
|
"s:driver", driver,
|
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
|
|
|
|
qemuBlockStorageSourceGetVvfatProps(virStorageSourcePtr src)
|
|
|
|
{
|
|
|
|
virJSONValuePtr ret = NULL;
|
|
|
|
|
|
|
|
/* libvirt currently does not handle the following attributes:
|
|
|
|
* '*fat-type': 'int'
|
|
|
|
* '*label': 'str'
|
|
|
|
*/
|
|
|
|
ignore_value(virJSONValueObjectCreate(&ret,
|
|
|
|
"s:driver", "vvfat",
|
|
|
|
"s:dir", src->path,
|
|
|
|
"b:floppy", src->floppyimg,
|
|
|
|
"b:rw", !src->readonly, NULL));
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-04-25 15:04:35 +00:00
|
|
|
static int
|
|
|
|
qemuBlockStorageSourceGetBlockdevGetCacheProps(virStorageSourcePtr src,
|
|
|
|
virJSONValuePtr props)
|
|
|
|
{
|
2019-04-04 08:55:32 +00:00
|
|
|
VIR_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)
|
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,
|
|
|
|
bool legacy)
|
2017-07-07 14:29:01 +00:00
|
|
|
{
|
|
|
|
int actualType = virStorageSourceGetActualType(src);
|
2019-04-04 08:55:32 +00:00
|
|
|
VIR_AUTOPTR(virJSONValue) fileprops = NULL;
|
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:
|
2017-12-05 16:02:28 +00:00
|
|
|
if (!(fileprops = qemuBlockStorageSourceGetFileProps(src)))
|
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 */
|
|
|
|
if (!(fileprops = qemuBlockStorageSourceGetVvfatProps(src)))
|
|
|
|
return NULL;
|
|
|
|
break;
|
2017-07-13 07:48:14 +00:00
|
|
|
|
2017-07-07 14:29:01 +00:00
|
|
|
case VIR_STORAGE_TYPE_VOLUME:
|
|
|
|
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:
|
2018-03-22 15:42:47 +00:00
|
|
|
if (!(fileprops = qemuBlockStorageSourceGetGlusterProps(src, legacy)))
|
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:
|
|
|
|
if (!(fileprops = qemuBlockStorageSourceGetVxHSProps(src)))
|
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:
|
2017-07-13 13:48:06 +00:00
|
|
|
if (!(fileprops = qemuBlockStorageSourceGetCURLProps(src)))
|
|
|
|
return NULL;
|
|
|
|
break;
|
|
|
|
|
2017-10-19 15:44:57 +00:00
|
|
|
case VIR_STORAGE_NET_PROTOCOL_ISCSI:
|
|
|
|
if (!(fileprops = qemuBlockStorageSourceGetISCSIProps(src)))
|
|
|
|
return NULL;
|
|
|
|
break;
|
|
|
|
|
2017-07-13 13:48:06 +00:00
|
|
|
case VIR_STORAGE_NET_PROTOCOL_NBD:
|
2017-07-13 13:48:06 +00:00
|
|
|
if (!(fileprops = qemuBlockStorageSourceGetNBDProps(src)))
|
|
|
|
return NULL;
|
|
|
|
break;
|
|
|
|
|
2017-07-13 13:48:06 +00:00
|
|
|
case VIR_STORAGE_NET_PROTOCOL_RBD:
|
2017-10-23 16:16:53 +00:00
|
|
|
if (!(fileprops = qemuBlockStorageSourceGetRBDProps(src)))
|
|
|
|
return NULL;
|
|
|
|
break;
|
|
|
|
|
2017-07-13 13:48:06 +00:00
|
|
|
case VIR_STORAGE_NET_PROTOCOL_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:
|
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;
|
|
|
|
}
|
|
|
|
|
2018-03-28 07:04:49 +00:00
|
|
|
if (qemuBlockNodeNameValidate(src->nodestorage) < 0 ||
|
2018-04-25 15:04:35 +00:00
|
|
|
virJSONValueObjectAdd(fileprops, "S:node-name", src->nodestorage, NULL) < 0)
|
2019-04-04 09:31:05 +00:00
|
|
|
return NULL;
|
2018-04-25 15:04:35 +00:00
|
|
|
|
|
|
|
if (!legacy) {
|
|
|
|
if (qemuBlockStorageSourceGetBlockdevGetCacheProps(src, fileprops) < 0)
|
2019-04-04 09:31:05 +00:00
|
|
|
return NULL;
|
2018-04-06 04:40:23 +00:00
|
|
|
|
2018-04-19 13:44:36 +00:00
|
|
|
if (virJSONValueObjectAdd(fileprops,
|
|
|
|
"b:read-only", src->readonly,
|
|
|
|
"s:discard", "unmap",
|
|
|
|
NULL) < 0)
|
2019-04-04 09:31:05 +00:00
|
|
|
return NULL;
|
2017-10-09 06:55:15 +00:00
|
|
|
}
|
|
|
|
|
2019-04-05 15:52:17 +00:00
|
|
|
VIR_RETURN_PTR(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-04-04 08:55:32 +00:00
|
|
|
VIR_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-04-04 08:55:32 +00:00
|
|
|
VIR_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-04-05 15:52:17 +00:00
|
|
|
VIR_RETURN_PTR(props);
|
2017-10-23 10:26:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static virJSONValuePtr
|
|
|
|
qemuBlockStorageSourceGetBlockdevFormatProps(virStorageSourcePtr src)
|
|
|
|
{
|
|
|
|
const char *driver = NULL;
|
2019-04-04 08:55:32 +00:00
|
|
|
VIR_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-04-05 15:52:17 +00:00
|
|
|
VIR_RETURN_PTR(props);
|
2017-10-23 10:26:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* qemuBlockStorageSourceGetBlockdevProps:
|
|
|
|
*
|
|
|
|
* @src: storage source to format
|
|
|
|
*
|
|
|
|
* 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
|
|
|
|
qemuBlockStorageSourceGetBlockdevProps(virStorageSourcePtr src)
|
|
|
|
{
|
|
|
|
bool backingSupported = src->format >= VIR_STORAGE_FILE_BACKING;
|
2019-04-04 08:55:32 +00:00
|
|
|
VIR_AUTOPTR(virJSONValue) props = NULL;
|
2017-10-23 10:26:10 +00:00
|
|
|
|
|
|
|
if (virStorageSourceHasBacking(src) && !backingSupported) {
|
|
|
|
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;
|
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
|
|
|
|
|
|
|
if (src->backingStore && backingSupported) {
|
|
|
|
if (virStorageSourceHasBacking(src)) {
|
|
|
|
if (virJSONValueObjectAppendString(props, "backing",
|
|
|
|
src->backingStore->nodeformat) < 0)
|
2019-04-04 09:31:05 +00:00
|
|
|
return NULL;
|
2017-10-23 10:26:10 +00:00
|
|
|
} else {
|
|
|
|
/* chain is terminated, indicate that no detection should happen
|
|
|
|
* in qemu */
|
|
|
|
if (virJSONValueObjectAppendNull(props, "backing") < 0)
|
2019-04-04 09:31:05 +00:00
|
|
|
return NULL;
|
2017-10-23 10:26:10 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-05 15:52:17 +00:00
|
|
|
VIR_RETURN_PTR(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
|
|
|
|
*
|
|
|
|
* 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
|
|
|
|
qemuBlockStorageSourceAttachPrepareBlockdev(virStorageSourcePtr src)
|
|
|
|
{
|
2019-04-04 08:46:21 +00:00
|
|
|
VIR_AUTOPTR(qemuBlockStorageSourceAttachData) data = NULL;
|
2018-02-23 11:59:13 +00:00
|
|
|
|
|
|
|
if (VIR_ALLOC(data) < 0)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (!(data->formatProps = qemuBlockStorageSourceGetBlockdevProps(src)) ||
|
|
|
|
!(data->storageProps = qemuBlockStorageSourceGetBackendProps(src, false)))
|
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-04-05 15:52:17 +00:00
|
|
|
VIR_RETURN_PTR(data);
|
2018-02-23 11:59:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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
|
2018-12-04 17:08:14 +00:00
|
|
|
* error occurred, changes which were already applied need to be rolled back by
|
2018-02-23 11:59:13 +00:00
|
|
|
* calling qemuBlockStorageSourceAttachRollback.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
qemuBlockStorageSourceAttachApply(qemuMonitorPtr mon,
|
|
|
|
qemuBlockStorageSourceAttachDataPtr data)
|
|
|
|
{
|
|
|
|
int rv;
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
if (data->encryptsecretProps &&
|
|
|
|
qemuMonitorAddObject(mon, &data->encryptsecretProps,
|
|
|
|
&data->encryptsecretAlias) < 0)
|
|
|
|
return -1;
|
|
|
|
|
2018-06-01 10:11:06 +00:00
|
|
|
if (data->tlsProps &&
|
|
|
|
qemuMonitorAddObject(mon, &data->tlsProps, &data->tlsAlias) < 0)
|
|
|
|
return -1;
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (data->formatProps) {
|
|
|
|
rv = qemuMonitorBlockdevAdd(mon, data->formatProps);
|
|
|
|
data->formatProps = NULL;
|
|
|
|
|
|
|
|
if (rv < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
data->formatAttached = true;
|
|
|
|
}
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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-04-04 09:13:36 +00:00
|
|
|
VIR_AUTOFREE(char *) device = NULL;
|
|
|
|
VIR_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
|
|
|
|
|
|
|
if (qemuMonitorJSONTransactionAdd(actions, "blockdev-snapshot-sync",
|
|
|
|
"s:device", device,
|
|
|
|
"s:snapshot-file", source,
|
|
|
|
"s:format", format,
|
|
|
|
"S:mode", reuse ? "existing" : NULL,
|
|
|
|
NULL) < 0)
|
2019-04-04 09:31:05 +00:00
|
|
|
return -1;
|
2018-07-03 12:13:29 +00:00
|
|
|
|
2019-04-04 09:31:05 +00:00
|
|
|
return 0;
|
2018-07-03 12:13:29 +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;
|
|
|
|
}
|