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
|
|
|
|
2021-01-22 09:36:21 +00:00
|
|
|
#include "storage_source.h"
|
2017-03-13 11:46:18 +00:00
|
|
|
#include "viralloc.h"
|
2021-01-21 14:34:45 +00:00
|
|
|
#include "virstoragefile.h"
|
2017-03-13 11:46:18 +00:00
|
|
|
#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,
|
2021-03-11 07:16:13 +00:00
|
|
|
virJSONValue *item,
|
2017-07-25 15:32:38 +00:00
|
|
|
void *opaque)
|
|
|
|
{
|
2020-10-22 17:04:18 +00:00
|
|
|
GHashTable *table = opaque;
|
2017-07-25 15:32:38 +00:00
|
|
|
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
|
2021-03-11 07:16:13 +00:00
|
|
|
qemuBlockNodeNameBackingChainDataFree(qemuBlockNodeNameBackingChainData *data)
|
2017-03-13 11:46:18 +00:00
|
|
|
{
|
|
|
|
if (!data)
|
|
|
|
return;
|
|
|
|
|
2021-02-03 19:36:01 +00:00
|
|
|
g_free(data->nodeformat);
|
|
|
|
g_free(data->nodestorage);
|
2017-03-13 11:46:18 +00:00
|
|
|
|
2021-02-03 19:36:01 +00:00
|
|
|
g_free(data->qemufilename);
|
2017-07-26 07:36:21 +00:00
|
|
|
|
2021-02-03 19:36:01 +00:00
|
|
|
g_free(data->drvformat);
|
|
|
|
g_free(data->drvstorage);
|
2017-07-25 17:10:27 +00:00
|
|
|
|
2017-07-26 07:36:21 +00:00
|
|
|
qemuBlockNodeNameBackingChainDataFree(data->backing);
|
2017-03-13 11:46:18 +00:00
|
|
|
|
2021-02-03 19:36:01 +00:00
|
|
|
g_free(data);
|
2017-03-13 11:46:18 +00:00
|
|
|
}
|
|
|
|
|
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 {
|
2020-10-22 17:04:18 +00:00
|
|
|
GHashTable *nodenamestable;
|
|
|
|
GHashTable *disks;
|
2017-07-26 07:36:21 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
2017-03-13 11:46:18 +00:00
|
|
|
static int
|
2021-03-11 07:16:13 +00:00
|
|
|
qemuBlockNodeNameGetBackingChainBacking(virJSONValue *next,
|
2020-10-22 17:04:18 +00:00
|
|
|
GHashTable *nodenamestable,
|
2021-03-11 07:16:13 +00:00
|
|
|
qemuBlockNodeNameBackingChainData **nodenamedata)
|
2017-03-13 11:46:18 +00:00
|
|
|
{
|
2019-10-15 12:47:50 +00:00
|
|
|
g_autoptr(qemuBlockNodeNameBackingChainData) data = NULL;
|
2021-03-11 07:16:13 +00:00
|
|
|
qemuBlockNodeNameBackingChainData *backingdata = NULL;
|
|
|
|
virJSONValue *backing = virJSONValueObjectGetObject(next, "backing");
|
|
|
|
virJSONValue *parent = virJSONValueObjectGetObject(next, "parent");
|
|
|
|
virJSONValue *parentnodedata;
|
|
|
|
virJSONValue *nodedata;
|
2017-07-26 07:36:21 +00:00
|
|
|
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
|
|
|
|
2020-10-05 10:28:26 +00:00
|
|
|
data = g_new0(qemuBlockNodeNameBackingChainData, 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,
|
2021-03-11 07:16:13 +00:00
|
|
|
virJSONValue *item,
|
2017-07-26 07:36:21 +00:00
|
|
|
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
|
2021-03-11 07:16:13 +00:00
|
|
|
* qemuBlockNodeNameBackingChainData *accessible by the node name. The fields
|
2017-03-13 11:46:18 +00:00
|
|
|
* then can be used to recover the full backing chain.
|
|
|
|
*
|
|
|
|
* Returns a hash table on success and NULL on failure.
|
|
|
|
*/
|
2020-10-22 17:04:18 +00:00
|
|
|
GHashTable *
|
2021-03-11 07:16:13 +00:00
|
|
|
qemuBlockNodeNameGetBackingChain(virJSONValue *namednodes,
|
|
|
|
virJSONValue *blockstats)
|
2017-03-13 11:46:18 +00:00
|
|
|
{
|
|
|
|
struct qemuBlockNodeNameGetBackingChainData data;
|
2020-10-22 17:04:18 +00:00
|
|
|
g_autoptr(GHashTable) namednodestable = NULL;
|
|
|
|
g_autoptr(GHashTable) disks = NULL;
|
2017-03-13 11:46:18 +00:00
|
|
|
|
|
|
|
memset(&data, 0, sizeof(data));
|
|
|
|
|
2020-10-20 16:43:20 +00:00
|
|
|
if (!(namednodestable = virHashNew(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
|
|
|
|
2020-10-20 16:43:20 +00:00
|
|
|
if (!(disks = virHashNew(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
|
2021-03-11 07:16:13 +00:00
|
|
|
qemuBlockDiskClearDetectedNodes(virDomainDiskDef *disk)
|
2017-03-15 12:03:21 +00:00
|
|
|
{
|
2021-03-11 07:16:13 +00:00
|
|
|
virStorageSource *next = disk->src;
|
2017-03-15 12:03:21 +00:00
|
|
|
|
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
|
2021-03-11 07:16:13 +00:00
|
|
|
qemuBlockDiskDetectNodes(virDomainDiskDef *disk,
|
2020-10-22 17:04:18 +00:00
|
|
|
GHashTable *disktable)
|
2017-03-15 12:03:21 +00:00
|
|
|
{
|
2021-03-11 07:16:13 +00:00
|
|
|
qemuBlockNodeNameBackingChainData *entry = NULL;
|
|
|
|
virStorageSource *src = disk->src;
|
2019-10-15 13:16:31 +00:00
|
|
|
g_autofree char *alias = NULL;
|
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)))
|
2021-02-18 02:43:33 +00:00
|
|
|
return -1;
|
2017-09-25 09:44:00 +00:00
|
|
|
|
2021-02-18 02:43:33 +00:00
|
|
|
if (!(entry = virHashLookup(disktable, alias)))
|
|
|
|
return 0;
|
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))
|
2021-02-18 02:43:33 +00:00
|
|
|
goto error;
|
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;
|
|
|
|
}
|
|
|
|
|
2021-02-18 02:43:33 +00:00
|
|
|
return 0;
|
2017-09-25 09:44:00 +00:00
|
|
|
|
2021-02-18 02:43:33 +00:00
|
|
|
error:
|
|
|
|
qemuBlockDiskClearDetectedNodes(disk);
|
|
|
|
return -1;
|
2017-03-15 12:03:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
2021-03-11 07:16:13 +00:00
|
|
|
qemuBlockNodeNamesDetect(virQEMUDriver *driver,
|
|
|
|
virDomainObj *vm,
|
2017-04-07 11:06:24 +00:00
|
|
|
qemuDomainAsyncJob asyncJob)
|
2017-03-15 12:03:21 +00:00
|
|
|
{
|
2021-03-11 07:16:13 +00:00
|
|
|
qemuDomainObjPrivate *priv = vm->privateData;
|
2020-10-22 17:04:18 +00:00
|
|
|
g_autoptr(GHashTable) disktable = NULL;
|
2019-10-15 12:47:50 +00:00
|
|
|
g_autoptr(virJSONValue) data = NULL;
|
|
|
|
g_autoptr(virJSONValue) blockstats = NULL;
|
2021-03-11 07:16:13 +00:00
|
|
|
virDomainDiskDef *disk;
|
2017-03-15 12:03:21 +00:00
|
|
|
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.
|
|
|
|
*
|
2020-10-22 17:04:18 +00:00
|
|
|
* Returns a filled GHashTable *on success NULL on error.
|
2017-03-16 11:30:16 +00:00
|
|
|
*/
|
2020-10-22 17:04:18 +00:00
|
|
|
GHashTable *
|
2021-03-11 07:16:13 +00:00
|
|
|
qemuBlockGetNodeData(virJSONValue *data)
|
2017-03-16 11:30:16 +00:00
|
|
|
{
|
2020-10-22 17:04:18 +00:00
|
|
|
g_autoptr(GHashTable) nodedata = NULL;
|
2017-03-16 11:30:16 +00:00
|
|
|
|
2020-10-20 16:43:20 +00:00
|
|
|
if (!(nodedata = virHashNew(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
|
2021-03-11 07:16:13 +00:00
|
|
|
qemuBlockStorageSourceSupportsConcurrentAccess(virStorageSource *src)
|
2017-11-14 14:34:46 +00:00
|
|
|
{
|
|
|
|
/* 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.
|
|
|
|
*/
|
2021-03-11 07:16:13 +00:00
|
|
|
virURI *
|
|
|
|
qemuBlockStorageSourceGetURI(virStorageSource *src)
|
2017-07-13 13:38:50 +00:00
|
|
|
{
|
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
|
|
|
}
|
|
|
|
|
2020-10-05 10:28:26 +00:00
|
|
|
uri = g_new0(virURI, 1);
|
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 {
|
2021-04-20 04:44:12 +00:00
|
|
|
uri->path = g_strdup_printf("%s%s",
|
|
|
|
g_path_is_absolute(src->path) ? "" : "/",
|
2019-10-22 13:26:14 +00:00
|
|
|
src->path);
|
2017-07-13 13:38:50 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-27 15:34:37 +00:00
|
|
|
uri->query = g_strdup(src->query);
|
|
|
|
|
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
|
2021-03-11 07:16:13 +00:00
|
|
|
* @host: the virStorageNetHostDef * 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.
|
|
|
|
*
|
2021-03-11 07:16:13 +00:00
|
|
|
* Returns a virJSONValue * for a single server.
|
2017-09-12 11:43:31 +00:00
|
|
|
*/
|
2021-03-11 07:16:13 +00:00
|
|
|
static virJSONValue *
|
|
|
|
qemuBlockStorageSourceBuildJSONSocketAddress(virStorageNetHostDef *host,
|
2017-09-12 11:43:31 +00:00
|
|
|
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.
|
|
|
|
*/
|
2021-03-11 07:16:13 +00:00
|
|
|
static virJSONValue *
|
|
|
|
qemuBlockStorageSourceBuildHostsJSONSocketAddress(virStorageSource *src,
|
2017-07-07 15:55:04 +00:00
|
|
|
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;
|
2021-03-11 07:16:13 +00:00
|
|
|
virStorageNetHostDef *host;
|
2017-07-07 14:29:01 +00:00
|
|
|
size_t i;
|
|
|
|
|
2020-01-31 07:18:36 +00:00
|
|
|
servers = virJSONValueNewArray();
|
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
|
|
|
|
2021-02-11 16:57:45 +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
|
|
|
}
|
|
|
|
|
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
|
2021-03-11 07:16:13 +00:00
|
|
|
* @host: the virStorageNetHostDef * definition to build
|
2017-10-23 16:02:28 +00:00
|
|
|
*
|
|
|
|
* Formats @hosts into a json object conforming to the 'InetSocketAddress' type
|
|
|
|
* in qemu.
|
|
|
|
*
|
2021-03-11 07:16:13 +00:00
|
|
|
* Returns a virJSONValue *for a single server.
|
2017-10-23 16:02:28 +00:00
|
|
|
*/
|
2021-03-11 07:16:13 +00:00
|
|
|
static virJSONValue *
|
|
|
|
qemuBlockStorageSourceBuildJSONInetSocketAddress(virStorageNetHostDef *host)
|
2017-10-23 16:02:28 +00:00
|
|
|
{
|
2021-03-11 07:16:13 +00:00
|
|
|
virJSONValue *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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-01-06 21:32:30 +00:00
|
|
|
/**
|
2021-03-11 07:16:13 +00:00
|
|
|
* qemuBlockStorageSourceBuildJSONNFSServer(virStorageNetHostDef *host)
|
|
|
|
* @host: the virStorageNetHostDef * definition to build
|
2021-01-06 21:32:30 +00:00
|
|
|
*
|
|
|
|
* Formats @hosts into a json object conforming to the 'NFSServer' type
|
|
|
|
* in qemu.
|
|
|
|
*
|
2021-03-11 07:16:13 +00:00
|
|
|
* Returns a virJSONValue *for a single server.
|
2021-01-06 21:32:30 +00:00
|
|
|
*/
|
2021-03-11 07:16:13 +00:00
|
|
|
static virJSONValue *
|
|
|
|
qemuBlockStorageSourceBuildJSONNFSServer(virStorageNetHostDef *host)
|
2021-01-06 21:32:30 +00:00
|
|
|
{
|
2021-03-11 07:16:13 +00:00
|
|
|
virJSONValue *ret = NULL;
|
2021-01-06 21:32:30 +00:00
|
|
|
|
|
|
|
ignore_value(virJSONValueObjectCreate(&ret,
|
|
|
|
"s:host", host->name,
|
|
|
|
"s:type", "inet",
|
|
|
|
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.
|
|
|
|
*/
|
2021-03-11 07:16:13 +00:00
|
|
|
static virJSONValue *
|
|
|
|
qemuBlockStorageSourceBuildHostsJSONInetSocketAddress(virStorageSource *src)
|
2017-10-23 16:16:53 +00:00
|
|
|
{
|
2019-10-15 12:47:50 +00:00
|
|
|
g_autoptr(virJSONValue) servers = NULL;
|
|
|
|
g_autoptr(virJSONValue) server = NULL;
|
2021-03-11 07:16:13 +00:00
|
|
|
virStorageNetHostDef *host;
|
2017-10-23 16:16:53 +00:00
|
|
|
size_t i;
|
|
|
|
|
2020-01-31 07:18:36 +00:00
|
|
|
servers = virJSONValueNewArray();
|
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
|
|
|
|
2021-02-11 16:57:45 +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
|
|
|
}
|
|
|
|
|
2019-10-16 11:35:54 +00:00
|
|
|
return g_steal_pointer(&servers);
|
2017-10-23 16:16:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-03-11 07:16:13 +00:00
|
|
|
static virJSONValue *
|
|
|
|
qemuBlockStorageSourceGetGlusterProps(virStorageSource *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
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-03-11 07:16:13 +00:00
|
|
|
static virJSONValue *
|
|
|
|
qemuBlockStorageSourceGetVxHSProps(virStorageSource *src,
|
2018-09-04 16:52:02 +00:00
|
|
|
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;
|
2021-03-11 07:16:13 +00:00
|
|
|
virJSONValue *ret = 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
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-03-11 07:16:13 +00:00
|
|
|
static virJSONValue *
|
|
|
|
qemuBlockStorageSourceGetNFSProps(virStorageSource *src)
|
2021-01-06 21:32:30 +00:00
|
|
|
{
|
|
|
|
g_autoptr(virJSONValue) server = NULL;
|
2021-03-11 07:16:13 +00:00
|
|
|
virJSONValue *ret = NULL;
|
2021-01-06 21:32:30 +00:00
|
|
|
|
|
|
|
if (!(server = qemuBlockStorageSourceBuildJSONNFSServer(&src->hosts[0])))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
/* NFS disk specification example:
|
|
|
|
* { driver:"nfs",
|
|
|
|
* user: "0",
|
|
|
|
* group: "0",
|
|
|
|
* path: "/foo/bar/baz",
|
|
|
|
* server: {type:"tcp", host:"1.2.3.4"}}
|
|
|
|
*/
|
|
|
|
if (virJSONValueObjectCreate(&ret,
|
|
|
|
"a:server", &server,
|
|
|
|
"S:path", src->path, NULL) < 0)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (src->nfs_uid != -1 &&
|
|
|
|
virJSONValueObjectAdd(ret, "i:user", src->nfs_uid, NULL) < 0)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (src->nfs_gid != -1 &&
|
|
|
|
virJSONValueObjectAdd(ret, "i:group", src->nfs_gid, NULL) < 0)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-03-11 07:16:13 +00:00
|
|
|
static virJSONValue *
|
|
|
|
qemuBlockStorageSourceGetCURLProps(virStorageSource *src,
|
2018-09-04 16:52:02 +00:00
|
|
|
bool onlytarget)
|
2017-07-13 13:48:06 +00:00
|
|
|
{
|
2021-03-11 07:16:13 +00:00
|
|
|
qemuDomainStorageSourcePrivate *srcPriv = QEMU_DOMAIN_STORAGE_SOURCE_PRIVATE(src);
|
2017-07-13 13:48:06 +00:00
|
|
|
const char *passwordalias = NULL;
|
2017-05-04 10:55:49 +00:00
|
|
|
const char *cookiealias = NULL;
|
2017-07-13 13:48:06 +00:00
|
|
|
const char *username = NULL;
|
2021-03-11 07:16:13 +00:00
|
|
|
virJSONValue *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;
|
2020-03-23 15:36:22 +00:00
|
|
|
g_autofree char *cookiestr = 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
|
|
|
|
2017-05-04 10:55:49 +00:00
|
|
|
if (!onlytarget) {
|
|
|
|
if (src->auth) {
|
|
|
|
username = src->auth->username;
|
|
|
|
passwordalias = srcPriv->secinfo->s.aes.alias;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (srcPriv &&
|
|
|
|
srcPriv->httpcookie)
|
|
|
|
cookiealias = srcPriv->httpcookie->s.aes.alias;
|
2020-03-23 15:36:22 +00:00
|
|
|
} else {
|
|
|
|
/* format target string along with cookies */
|
|
|
|
cookiestr = qemuBlockStorageSourceGetCookieString(src);
|
2017-07-13 13:48:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ignore_value(virJSONValueObjectCreate(&ret,
|
|
|
|
"s:url", uristr,
|
|
|
|
"S:username", username,
|
|
|
|
"S:password-secret", passwordalias,
|
2017-04-28 10:58:17 +00:00
|
|
|
"T:sslverify", src->sslverify,
|
2020-03-23 15:36:22 +00:00
|
|
|
"S:cookie", cookiestr,
|
2017-05-04 10:55:49 +00:00
|
|
|
"S:cookie-secret", cookiealias,
|
2020-03-06 06:09:22 +00:00
|
|
|
"P:timeout", src->timeout,
|
|
|
|
"P:readahead", src->readahead,
|
2017-07-13 13:48:06 +00:00
|
|
|
NULL));
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-03-11 07:16:13 +00:00
|
|
|
static virJSONValue *
|
|
|
|
qemuBlockStorageSourceGetISCSIProps(virStorageSource *src,
|
2018-09-04 16:52:02 +00:00
|
|
|
bool onlytarget)
|
2017-10-19 15:44:57 +00:00
|
|
|
{
|
2021-03-11 07:16:13 +00:00
|
|
|
qemuDomainStorageSourcePrivate *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;
|
2021-03-11 07:16:13 +00:00
|
|
|
virJSONValue *ret = NULL;
|
2017-10-19 15:44:57 +00:00
|
|
|
|
|
|
|
/* { 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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-03-11 07:16:13 +00:00
|
|
|
static virJSONValue *
|
|
|
|
qemuBlockStorageSourceGetNBDProps(virStorageSource *src,
|
2018-09-04 16:52:02 +00:00
|
|
|
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;
|
2021-03-11 07:16:13 +00:00
|
|
|
virJSONValue *ret = NULL;
|
2017-07-13 13:48:06 +00:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-03-11 07:16:13 +00:00
|
|
|
static virJSONValue *
|
|
|
|
qemuBlockStorageSourceGetRBDProps(virStorageSource *src,
|
2018-09-04 16:52:02 +00:00
|
|
|
bool onlytarget)
|
2017-10-23 16:16:53 +00:00
|
|
|
{
|
2021-03-11 07:16:13 +00:00
|
|
|
qemuDomainStorageSourcePrivate *srcPriv = QEMU_DOMAIN_STORAGE_SOURCE_PRIVATE(src);
|
2019-10-15 12:47:50 +00:00
|
|
|
g_autoptr(virJSONValue) servers = NULL;
|
2021-03-11 07:16:13 +00:00
|
|
|
virJSONValue *ret = NULL;
|
2017-10-23 16:16:53 +00:00
|
|
|
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 */
|
2020-01-31 07:18:36 +00:00
|
|
|
authmodes = virJSONValueNewArray();
|
2018-07-09 14:05:16 +00:00
|
|
|
|
|
|
|
if (!(mode = virJSONValueNewString("cephx")) ||
|
2021-02-11 16:57:45 +00:00
|
|
|
virJSONValueArrayAppend(authmodes, &mode) < 0)
|
2019-04-04 09:31:05 +00:00
|
|
|
return NULL;
|
2018-07-09 14:05:16 +00:00
|
|
|
|
|
|
|
if (!(mode = virJSONValueNewString("none")) ||
|
2021-02-11 16:57:45 +00:00
|
|
|
virJSONValueArrayAppend(authmodes, &mode) < 0)
|
2019-04-04 09:31:05 +00:00
|
|
|
return NULL;
|
2018-07-09 14:05:16 +00:00
|
|
|
}
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-03-11 07:16:13 +00:00
|
|
|
static virJSONValue *
|
|
|
|
qemuBlockStorageSourceGetSheepdogProps(virStorageSource *src)
|
2017-10-23 16:44:35 +00:00
|
|
|
{
|
2019-10-15 12:47:50 +00:00
|
|
|
g_autoptr(virJSONValue) serverprops = NULL;
|
2021-03-11 07:16:13 +00:00
|
|
|
virJSONValue *ret = NULL;
|
2017-10-23 16:44:35 +00:00
|
|
|
|
|
|
|
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
|
|
|
|
2021-03-11 07:16:13 +00:00
|
|
|
static virJSONValue *
|
|
|
|
qemuBlockStorageSourceGetSshProps(virStorageSource *src)
|
2017-10-23 16:54:12 +00:00
|
|
|
{
|
2019-10-15 12:47:50 +00:00
|
|
|
g_autoptr(virJSONValue) serverprops = NULL;
|
2021-03-11 07:16:13 +00:00
|
|
|
virJSONValue *ret = NULL;
|
2017-10-23 16:54:12 +00:00
|
|
|
const char *username = NULL;
|
2020-03-09 14:05:58 +00:00
|
|
|
g_autoptr(virJSONValue) host_key_check = NULL;
|
2017-10-23 16:54:12 +00:00
|
|
|
|
|
|
|
if (src->nhosts != 1) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
2021-04-19 10:28:17 +00:00
|
|
|
_("ssh protocol accepts only one host"));
|
2017-10-23 16:54:12 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
serverprops = qemuBlockStorageSourceBuildJSONInetSocketAddress(&src->hosts[0]);
|
|
|
|
if (!serverprops)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (src->auth)
|
|
|
|
username = src->auth->username;
|
2020-03-09 14:05:58 +00:00
|
|
|
else if (src->ssh_user)
|
|
|
|
username = src->ssh_user;
|
|
|
|
|
|
|
|
if (src->ssh_host_key_check_disabled &&
|
|
|
|
virJSONValueObjectCreate(&host_key_check,
|
|
|
|
"s:mode", "none",
|
|
|
|
NULL) < 0)
|
|
|
|
return NULL;
|
2017-10-23 16:54:12 +00:00
|
|
|
|
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,
|
2020-03-09 14:05:58 +00:00
|
|
|
"A:host-key-check", &host_key_check,
|
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-10-23 16:54:12 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-03-11 07:16:13 +00:00
|
|
|
static virJSONValue *
|
|
|
|
qemuBlockStorageSourceGetFileProps(virStorageSource *src,
|
2018-09-04 16:52:02 +00:00
|
|
|
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;
|
2021-03-11 07:16:13 +00:00
|
|
|
virJSONValue *ret = NULL;
|
2017-12-05 16:02:28 +00:00
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-03-11 07:16:13 +00:00
|
|
|
static virJSONValue *
|
|
|
|
qemuBlockStorageSourceGetVvfatProps(virStorageSource *src,
|
2018-09-04 16:52:02 +00:00
|
|
|
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
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-03-11 07:16:13 +00:00
|
|
|
static virJSONValue *
|
|
|
|
qemuBlockStorageSourceGetNVMeProps(virStorageSource *src)
|
2019-06-21 13:07:22 +00:00
|
|
|
{
|
|
|
|
const virStorageSourceNVMeDef *nvme = src->nvme;
|
|
|
|
g_autofree char *pciAddr = NULL;
|
2021-03-11 07:16:13 +00:00
|
|
|
virJSONValue *ret = NULL;
|
2019-06-21 13:07:22 +00:00
|
|
|
|
|
|
|
if (!(pciAddr = virPCIDeviceAddressAsString(&nvme->pciAddr)))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
ignore_value(virJSONValueObjectCreate(&ret,
|
|
|
|
"s:driver", "nvme",
|
|
|
|
"s:device", pciAddr,
|
2020-03-23 09:57:49 +00:00
|
|
|
"U:namespace", nvme->namespc,
|
2019-06-21 13:07:22 +00:00
|
|
|
NULL));
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-04-25 15:04:35 +00:00
|
|
|
static int
|
2021-03-11 07:16:13 +00:00
|
|
|
qemuBlockStorageSourceGetBlockdevGetCacheProps(virStorageSource *src,
|
|
|
|
virJSONValue *props)
|
2018-04-25 15:04:35 +00:00
|
|
|
{
|
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;
|
|
|
|
|
2021-02-11 16:57:45 +00:00
|
|
|
if (virJSONValueObjectAppend(props, "cache", &cacheobj) < 0)
|
2018-04-25 15:04:35 +00:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-07-07 15:37:42 +00:00
|
|
|
/**
|
|
|
|
* qemuBlockStorageSourceGetBackendProps:
|
|
|
|
* @src: disk source
|
2020-06-19 14:28:12 +00:00
|
|
|
* @flags: bitwise-or of qemuBlockStorageSourceBackendPropsFlags
|
|
|
|
*
|
|
|
|
* Flags:
|
|
|
|
* QEMU_BLOCK_STORAGE_SOURCE_BACKEND_PROPS_LEGACY:
|
|
|
|
* use legacy formatting of attributes (for -drive / old qemus)
|
|
|
|
* QEMU_BLOCK_STORAGE_SOURCE_BACKEND_PROPS_TARGET_ONLY:
|
|
|
|
* omit any data which does not identify the image itself
|
|
|
|
* QEMU_BLOCK_STORAGE_SOURCE_BACKEND_PROPS_AUTO_READONLY:
|
|
|
|
* use the auto-read-only feature of qemu
|
2020-06-19 14:37:43 +00:00
|
|
|
* QEMU_BLOCK_STORAGE_SOURCE_BACKEND_PROPS_SKIP_UNMAP:
|
|
|
|
* don't enable 'discard:unmap' option for passing through discards
|
|
|
|
* (note that this is disabled also for _LEGACY and _TARGET_ONLY options)
|
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.
|
|
|
|
*/
|
2021-03-11 07:16:13 +00:00
|
|
|
virJSONValue *
|
|
|
|
qemuBlockStorageSourceGetBackendProps(virStorageSource *src,
|
2020-06-19 14:28:12 +00:00
|
|
|
unsigned int flags)
|
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;
|
2020-06-19 14:28:12 +00:00
|
|
|
bool onlytarget = flags & QEMU_BLOCK_STORAGE_SOURCE_BACKEND_PROPS_TARGET_ONLY;
|
|
|
|
bool legacy = flags & QEMU_BLOCK_STORAGE_SOURCE_BACKEND_PROPS_LEGACY;
|
2018-09-04 17:29:10 +00:00
|
|
|
|
2020-06-19 14:28:12 +00:00
|
|
|
if (flags & QEMU_BLOCK_STORAGE_SOURCE_BACKEND_PROPS_AUTO_READONLY) {
|
2018-09-04 17:29:10 +00:00
|
|
|
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;
|
|
|
|
|
2021-01-25 17:13:29 +00:00
|
|
|
case VIR_STORAGE_TYPE_VHOST_USER:
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("unable to create blockdev props for vhostuser disk type"));
|
|
|
|
return NULL;
|
|
|
|
|
2019-06-21 13:07:22 +00:00
|
|
|
case VIR_STORAGE_TYPE_VOLUME:
|
2020-02-06 09:07:44 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("storage source pool '%s' volume '%s' is not translated"),
|
|
|
|
src->srcpool->pool, src->srcpool->volume);
|
|
|
|
return NULL;
|
|
|
|
|
2017-07-07 14:29:01 +00:00
|
|
|
case VIR_STORAGE_TYPE_NONE:
|
|
|
|
case VIR_STORAGE_TYPE_LAST:
|
2020-02-06 09:07:44 +00:00
|
|
|
virReportEnumRangeError(virStorageType, actualType);
|
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;
|
|
|
|
|
2021-01-06 21:32:26 +00:00
|
|
|
case VIR_STORAGE_NET_PROTOCOL_NFS:
|
2021-01-06 21:32:30 +00:00
|
|
|
driver = "nfs";
|
|
|
|
if (!(fileprops = qemuBlockStorageSourceGetNFSProps(src)))
|
|
|
|
return NULL;
|
|
|
|
break;
|
|
|
|
|
2017-07-07 15:37:42 +00:00
|
|
|
case VIR_STORAGE_NET_PROTOCOL_NONE:
|
|
|
|
case VIR_STORAGE_NET_PROTOCOL_LAST:
|
2020-02-06 09:07:44 +00:00
|
|
|
virReportEnumRangeError(virStorageNetProtocol, src->protocol);
|
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,
|
2020-06-19 14:37:43 +00:00
|
|
|
NULL) < 0)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (!(flags & QEMU_BLOCK_STORAGE_SOURCE_BACKEND_PROPS_SKIP_UNMAP) &&
|
|
|
|
virJSONValueObjectAdd(fileprops,
|
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
|
2021-03-11 07:16:13 +00:00
|
|
|
qemuBlockStorageSourceGetFormatLUKSProps(virStorageSource *src,
|
|
|
|
virJSONValue *props)
|
2017-10-23 10:26:10 +00:00
|
|
|
{
|
2021-03-11 07:16:13 +00:00
|
|
|
qemuDomainStorageSourcePrivate *srcPriv = QEMU_DOMAIN_STORAGE_SOURCE_PRIVATE(src);
|
2020-03-19 16:23:33 +00:00
|
|
|
|
|
|
|
if (!srcPriv || !srcPriv->encinfo || !srcPriv->encinfo->s.aes.alias) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("missing secret info for 'luks' driver"));
|
|
|
|
return -1;
|
2017-10-23 10:26:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (virJSONValueObjectAdd(props,
|
2020-03-19 16:23:33 +00:00
|
|
|
"s:driver", "luks",
|
|
|
|
"s:key-secret", srcPriv->encinfo->s.aes.alias,
|
|
|
|
NULL) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
2021-03-11 07:16:13 +00:00
|
|
|
qemuBlockStorageSourceGetFormatRawProps(virStorageSource *src,
|
|
|
|
virJSONValue *props)
|
2020-03-19 16:23:33 +00:00
|
|
|
{
|
|
|
|
if (virJSONValueObjectAdd(props, "s:driver", "raw", NULL) < 0)
|
2017-10-23 10:26:10 +00:00
|
|
|
return -1;
|
|
|
|
|
2020-02-05 17:08:55 +00:00
|
|
|
/* Currently only storage slices are supported. We'll have to calculate
|
|
|
|
* the union of the slices here if we don't want to be adding needless
|
|
|
|
* 'raw' nodes. */
|
|
|
|
if (src->sliceStorage &&
|
|
|
|
virJSONValueObjectAdd(props,
|
|
|
|
"U:offset", src->sliceStorage->offset,
|
|
|
|
"U:size", src->sliceStorage->size,
|
|
|
|
NULL) < 0)
|
|
|
|
return -1;
|
|
|
|
|
2017-10-23 10:26:10 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
2021-03-11 07:16:13 +00:00
|
|
|
qemuBlockStorageSourceGetCryptoProps(virStorageSource *src,
|
|
|
|
virJSONValue **encprops)
|
2017-10-23 10:26:10 +00:00
|
|
|
{
|
2021-03-11 07:16:13 +00:00
|
|
|
qemuDomainStorageSourcePrivate *srcpriv = QEMU_DOMAIN_STORAGE_SOURCE_PRIVATE(src);
|
2017-10-23 10:26:10 +00:00
|
|
|
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
|
2021-03-11 07:16:13 +00:00
|
|
|
qemuBlockStorageSourceGetFormatQcowGenericProps(virStorageSource *src,
|
2017-10-23 10:26:10 +00:00
|
|
|
const char *format,
|
2021-03-11 07:16:13 +00:00
|
|
|
virJSONValue *props)
|
2017-10-23 10:26:10 +00:00
|
|
|
{
|
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
|
2021-03-11 07:16:13 +00:00
|
|
|
qemuBlockStorageSourceGetFormatQcow2Props(virStorageSource *src,
|
|
|
|
virJSONValue *props)
|
2017-10-23 10:26:10 +00:00
|
|
|
{
|
|
|
|
/* 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;
|
|
|
|
|
2021-01-06 17:20:29 +00:00
|
|
|
/* 'cache-size' controls the maximum size of L2 and refcount caches.
|
|
|
|
* see: qemu.git/docs/qcow2-cache.txt
|
|
|
|
* https://git.qemu.org/?p=qemu.git;a=blob;f=docs/qcow2-cache.txt
|
|
|
|
*/
|
|
|
|
if (src->metadataCacheMaxSize > 0) {
|
|
|
|
if (virJSONValueObjectAdd(props,
|
|
|
|
"U:cache-size", src->metadataCacheMaxSize,
|
|
|
|
NULL) < 0)
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2017-10-23 10:26:10 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-03-11 07:16:13 +00:00
|
|
|
static virJSONValue *
|
|
|
|
qemuBlockStorageSourceGetBlockdevFormatCommonProps(virStorageSource *src)
|
2017-10-23 10:26:10 +00:00
|
|
|
{
|
|
|
|
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
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-03-11 07:16:13 +00:00
|
|
|
static virJSONValue *
|
|
|
|
qemuBlockStorageSourceGetBlockdevFormatProps(virStorageSource *src)
|
2017-10-23 10:26:10 +00:00
|
|
|
{
|
|
|
|
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:
|
2020-03-19 16:23:33 +00:00
|
|
|
if (src->encryption &&
|
|
|
|
src->encryption->format == VIR_STORAGE_ENCRYPTION_FORMAT_LUKS) {
|
|
|
|
if (qemuBlockStorageSourceGetFormatLUKSProps(src, props) < 0)
|
|
|
|
return NULL;
|
|
|
|
} else {
|
|
|
|
if (qemuBlockStorageSourceGetFormatRawProps(src, props) < 0)
|
|
|
|
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.
|
|
|
|
*/
|
2021-03-11 07:16:13 +00:00
|
|
|
virJSONValue *
|
|
|
|
qemuBlockStorageSourceGetBlockdevProps(virStorageSource *src,
|
|
|
|
virStorageSource *backingStore)
|
2017-10-23 10:26:10 +00:00
|
|
|
{
|
2019-10-15 12:47:50 +00:00
|
|
|
g_autoptr(virJSONValue) props = NULL;
|
2020-02-10 13:37:14 +00:00
|
|
|
const char *storagenode = src->nodestorage;
|
|
|
|
|
2020-03-19 15:43:49 +00:00
|
|
|
if (qemuBlockStorageSourceNeedsStorageSliceLayer(src))
|
2020-02-10 13:37:14 +00:00
|
|
|
storagenode = src->sliceStorage->nodename;
|
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
|
|
|
|
2020-02-10 13:37:14 +00:00
|
|
|
if (virJSONValueObjectAppendString(props, "file", storagenode) < 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
|
|
|
|
|
|
|
|
2021-03-11 07:16:13 +00:00
|
|
|
static virJSONValue *
|
|
|
|
qemuBlockStorageSourceGetBlockdevStorageSliceProps(virStorageSource *src)
|
2020-02-10 13:37:14 +00:00
|
|
|
{
|
|
|
|
g_autoptr(virJSONValue) props = NULL;
|
|
|
|
|
|
|
|
if (qemuBlockNodeNameValidate(src->sliceStorage->nodename) < 0)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (virJSONValueObjectCreate(&props,
|
|
|
|
"s:driver", "raw",
|
|
|
|
"s:node-name", src->sliceStorage->nodename,
|
|
|
|
"U:offset", src->sliceStorage->offset,
|
|
|
|
"U:size", src->sliceStorage->size,
|
|
|
|
"s:file", src->nodestorage,
|
|
|
|
"b:auto-read-only", true,
|
|
|
|
"s:discard", "unmap",
|
|
|
|
NULL) < 0)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (qemuBlockStorageSourceGetBlockdevGetCacheProps(src, props) < 0)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
return g_steal_pointer(&props);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-02-23 11:59:13 +00:00
|
|
|
void
|
2021-03-11 07:16:13 +00:00
|
|
|
qemuBlockStorageSourceAttachDataFree(qemuBlockStorageSourceAttachData *data)
|
2018-02-23 11:59:13 +00:00
|
|
|
{
|
|
|
|
if (!data)
|
|
|
|
return;
|
|
|
|
|
|
|
|
virJSONValueFree(data->storageProps);
|
2020-02-10 13:37:14 +00:00
|
|
|
virJSONValueFree(data->storageSliceProps);
|
2018-02-23 11:59:13 +00:00
|
|
|
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);
|
2020-03-09 08:14:07 +00:00
|
|
|
virJSONValueFree(data->httpcookiesecretProps);
|
2018-05-17 08:32:32 +00:00
|
|
|
virJSONValueFree(data->encryptsecretProps);
|
2018-06-01 10:11:06 +00:00
|
|
|
virJSONValueFree(data->tlsProps);
|
2020-06-29 13:11:00 +00:00
|
|
|
virJSONValueFree(data->tlsKeySecretProps);
|
2021-02-03 19:36:01 +00:00
|
|
|
g_free(data->tlsAlias);
|
|
|
|
g_free(data->tlsKeySecretAlias);
|
|
|
|
g_free(data->authsecretAlias);
|
|
|
|
g_free(data->encryptsecretAlias);
|
|
|
|
g_free(data->httpcookiesecretAlias);
|
|
|
|
g_free(data->driveCmd);
|
|
|
|
g_free(data->driveAlias);
|
|
|
|
g_free(data->chardevAlias);
|
|
|
|
g_free(data->chardevCmd);
|
|
|
|
g_free(data);
|
2018-02-23 11:59:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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
|
|
|
|
*/
|
2021-03-11 07:16:13 +00:00
|
|
|
qemuBlockStorageSourceAttachData *
|
|
|
|
qemuBlockStorageSourceAttachPrepareBlockdev(virStorageSource *src,
|
|
|
|
virStorageSource *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;
|
2020-06-19 14:28:12 +00:00
|
|
|
unsigned int backendpropsflags = 0;
|
|
|
|
|
|
|
|
if (autoreadonly)
|
|
|
|
backendpropsflags |= QEMU_BLOCK_STORAGE_SOURCE_BACKEND_PROPS_AUTO_READONLY;
|
2018-02-23 11:59:13 +00:00
|
|
|
|
2020-10-05 10:28:26 +00:00
|
|
|
data = g_new0(qemuBlockStorageSourceAttachData, 1);
|
2018-02-23 11:59:13 +00:00
|
|
|
|
2019-09-03 11:27:52 +00:00
|
|
|
if (!(data->formatProps = qemuBlockStorageSourceGetBlockdevProps(src,
|
2019-09-03 11:58:34 +00:00
|
|
|
backingStore)) ||
|
2020-06-19 14:28:12 +00:00
|
|
|
!(data->storageProps = qemuBlockStorageSourceGetBackendProps(src,
|
|
|
|
backendpropsflags)))
|
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;
|
|
|
|
|
2020-03-19 15:43:49 +00:00
|
|
|
if (qemuBlockStorageSourceNeedsStorageSliceLayer(src)) {
|
2020-02-10 13:37:14 +00:00
|
|
|
if (!(data->storageSliceProps = qemuBlockStorageSourceGetBlockdevStorageSliceProps(src)))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
data->storageSliceNodeName = src->sliceStorage->nodename;
|
|
|
|
}
|
|
|
|
|
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
|
2021-03-11 07:16:13 +00:00
|
|
|
qemuBlockStorageSourceAttachApplyStorageDeps(qemuMonitor *mon,
|
|
|
|
qemuBlockStorageSourceAttachData *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;
|
|
|
|
|
2020-03-09 08:14:07 +00:00
|
|
|
if (data->httpcookiesecretProps &&
|
|
|
|
qemuMonitorAddObject(mon, &data->httpcookiesecretProps,
|
|
|
|
&data->httpcookiesecretAlias) < 0)
|
|
|
|
return -1;
|
|
|
|
|
2020-06-29 13:11:00 +00:00
|
|
|
if (data->tlsKeySecretProps &&
|
|
|
|
qemuMonitorAddObject(mon, &data->tlsKeySecretProps,
|
|
|
|
&data->tlsKeySecretAlias) < 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
|
2021-03-11 07:16:13 +00:00
|
|
|
qemuBlockStorageSourceAttachApplyStorage(qemuMonitor *mon,
|
|
|
|
qemuBlockStorageSourceAttachData *data)
|
2019-05-27 15:30:12 +00:00
|
|
|
{
|
2018-02-23 11:59:13 +00:00
|
|
|
if (data->storageProps) {
|
2020-02-06 12:13:32 +00:00
|
|
|
if (qemuMonitorBlockdevAdd(mon, &data->storageProps) < 0)
|
2018-02-23 11:59:13 +00:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
data->storageAttached = true;
|
|
|
|
}
|
|
|
|
|
2019-05-27 15:30:12 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
2021-03-11 07:16:13 +00:00
|
|
|
qemuBlockStorageSourceAttachApplyFormatDeps(qemuMonitor *mon,
|
|
|
|
qemuBlockStorageSourceAttachData *data)
|
2019-05-27 15:30:12 +00:00
|
|
|
{
|
|
|
|
if (data->encryptsecretProps &&
|
|
|
|
qemuMonitorAddObject(mon, &data->encryptsecretProps,
|
|
|
|
&data->encryptsecretAlias) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
2021-03-11 07:16:13 +00:00
|
|
|
qemuBlockStorageSourceAttachApplyFormat(qemuMonitor *mon,
|
|
|
|
qemuBlockStorageSourceAttachData *data)
|
2019-05-27 15:30:12 +00:00
|
|
|
{
|
2018-02-23 11:59:13 +00:00
|
|
|
if (data->formatProps) {
|
2020-02-06 12:13:32 +00:00
|
|
|
if (qemuMonitorBlockdevAdd(mon, &data->formatProps) < 0)
|
2018-02-23 11:59:13 +00:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
data->formatAttached = true;
|
|
|
|
}
|
|
|
|
|
2019-05-27 15:30:12 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-02-10 13:37:14 +00:00
|
|
|
static int
|
2021-03-11 07:16:13 +00:00
|
|
|
qemuBlockStorageSourceAttachApplyStorageSlice(qemuMonitor *mon,
|
|
|
|
qemuBlockStorageSourceAttachData *data)
|
2020-02-10 13:37:14 +00:00
|
|
|
{
|
|
|
|
if (data->storageSliceProps) {
|
|
|
|
if (qemuMonitorBlockdevAdd(mon, &data->storageSliceProps) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
data->storageSliceAttached = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-05-27 15:30:12 +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
|
|
|
|
* error occurred, changes which were already applied need to be rolled back by
|
|
|
|
* calling qemuBlockStorageSourceAttachRollback.
|
|
|
|
*/
|
|
|
|
int
|
2021-03-11 07:16:13 +00:00
|
|
|
qemuBlockStorageSourceAttachApply(qemuMonitor *mon,
|
|
|
|
qemuBlockStorageSourceAttachData *data)
|
2019-05-27 15:30:12 +00:00
|
|
|
{
|
|
|
|
if (qemuBlockStorageSourceAttachApplyStorageDeps(mon, data) < 0 ||
|
|
|
|
qemuBlockStorageSourceAttachApplyStorage(mon, data) < 0 ||
|
2020-02-10 13:37:14 +00:00
|
|
|
qemuBlockStorageSourceAttachApplyStorageSlice(mon, data) < 0 ||
|
2019-05-27 15:30:12 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2021-02-01 11:00:35 +00:00
|
|
|
if (data->chardevDef) {
|
|
|
|
if (qemuMonitorAttachCharDev(mon, data->chardevAlias, data->chardevDef) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
data->chardevAdded = 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
|
2021-03-11 07:16:13 +00:00
|
|
|
qemuBlockStorageSourceAttachRollback(qemuMonitor *mon,
|
|
|
|
qemuBlockStorageSourceAttachData *data)
|
2018-02-23 11:59:13 +00:00
|
|
|
{
|
|
|
|
virErrorPtr orig_err;
|
|
|
|
|
|
|
|
virErrorPreserveLast(&orig_err);
|
|
|
|
|
2021-02-01 11:00:35 +00:00
|
|
|
if (data->chardevAdded) {
|
|
|
|
if (qemuMonitorDetachCharDev(mon, data->chardevAlias) < 0) {
|
|
|
|
VIR_WARN("Unable to remove chardev %s after failed " "qemuMonitorAddDevice",
|
|
|
|
data->chardevAlias);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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));
|
|
|
|
|
2020-02-10 13:37:14 +00:00
|
|
|
if (data->storageSliceAttached)
|
|
|
|
ignore_value(qemuMonitorBlockdevDel(mon, data->storageSliceNodeName));
|
|
|
|
|
2018-02-23 11:59:13 +00:00
|
|
|
if (data->storageAttached)
|
|
|
|
ignore_value(qemuMonitorBlockdevDel(mon, data->storageNodeName));
|
|
|
|
|
2018-06-01 13:56:47 +00:00
|
|
|
if (data->prmgrAlias)
|
2020-03-18 11:24:40 +00:00
|
|
|
ignore_value(qemuMonitorDelObject(mon, data->prmgrAlias, false));
|
2018-06-01 13:56:47 +00:00
|
|
|
|
2018-05-17 08:32:32 +00:00
|
|
|
if (data->authsecretAlias)
|
2020-03-18 11:24:40 +00:00
|
|
|
ignore_value(qemuMonitorDelObject(mon, data->authsecretAlias, false));
|
2018-05-17 08:32:32 +00:00
|
|
|
|
|
|
|
if (data->encryptsecretAlias)
|
2020-03-18 11:24:40 +00:00
|
|
|
ignore_value(qemuMonitorDelObject(mon, data->encryptsecretAlias, false));
|
2018-05-17 08:32:32 +00:00
|
|
|
|
2020-03-09 08:14:07 +00:00
|
|
|
if (data->httpcookiesecretAlias)
|
2020-03-18 11:24:40 +00:00
|
|
|
ignore_value(qemuMonitorDelObject(mon, data->httpcookiesecretAlias, false));
|
2020-03-09 08:14:07 +00:00
|
|
|
|
2018-06-01 10:11:06 +00:00
|
|
|
if (data->tlsAlias)
|
2020-03-18 11:24:40 +00:00
|
|
|
ignore_value(qemuMonitorDelObject(mon, data->tlsAlias, false));
|
2018-06-01 10:11:06 +00:00
|
|
|
|
2020-06-29 13:11:00 +00:00
|
|
|
if (data->tlsKeySecretAlias)
|
|
|
|
ignore_value(qemuMonitorDelObject(mon, data->tlsKeySecretAlias, false));
|
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
|
|
|
|
*
|
2021-03-11 07:16:13 +00:00
|
|
|
* Prepare qemuBlockStorageSourceAttachData *for detaching a single source
|
2018-10-10 13:30:24 +00:00
|
|
|
* from a VM. If @driveAlias is NULL -blockdev is assumed.
|
|
|
|
*/
|
2021-03-11 07:16:13 +00:00
|
|
|
qemuBlockStorageSourceAttachData *
|
|
|
|
qemuBlockStorageSourceDetachPrepare(virStorageSource *src,
|
2018-10-10 13:30:24 +00:00
|
|
|
char *driveAlias)
|
|
|
|
{
|
2021-03-11 07:16:13 +00:00
|
|
|
qemuDomainStorageSourcePrivate *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
|
|
|
|
2020-03-06 08:45:45 +00:00
|
|
|
data = g_new0(qemuBlockStorageSourceAttachData, 1);
|
2018-10-10 13:30:24 +00:00
|
|
|
|
|
|
|
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;
|
2020-02-10 13:37:14 +00:00
|
|
|
|
|
|
|
/* 'raw' format doesn't need the extra 'raw' layer when slicing, thus
|
|
|
|
* the nodename is NULL */
|
|
|
|
if (src->sliceStorage &&
|
|
|
|
src->sliceStorage->nodename) {
|
|
|
|
data->storageSliceNodeName = src->sliceStorage->nodename;
|
|
|
|
data->storageSliceAttached = true;
|
|
|
|
}
|
2018-10-10 13:30:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
2020-03-09 08:14:07 +00:00
|
|
|
|
|
|
|
if (srcpriv->httpcookie)
|
|
|
|
data->httpcookiesecretAlias = g_strdup(srcpriv->httpcookie->s.aes.alias);
|
2020-06-29 13:11:00 +00:00
|
|
|
|
|
|
|
if (srcpriv->tlsKeySecret)
|
|
|
|
data->tlsKeySecretAlias = g_strdup(srcpriv->tlsKeySecret->s.aes.alias);
|
2018-10-10 13:30:24 +00:00
|
|
|
}
|
|
|
|
|
2020-03-06 08:45:45 +00:00
|
|
|
return g_steal_pointer(&data);
|
2018-10-10 13:30:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-04-04 14:50:49 +00:00
|
|
|
void
|
2021-03-11 07:16:13 +00:00
|
|
|
qemuBlockStorageSourceChainDataFree(qemuBlockStorageSourceChainData *data)
|
2019-04-04 14:50:49 +00:00
|
|
|
{
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
if (!data)
|
|
|
|
return;
|
|
|
|
|
|
|
|
for (i = 0; i < data->nsrcdata; i++)
|
|
|
|
qemuBlockStorageSourceAttachDataFree(data->srcdata[i]);
|
|
|
|
|
2021-02-03 19:36:01 +00:00
|
|
|
g_free(data->srcdata);
|
|
|
|
g_free(data);
|
2019-04-04 14:50:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* qemuBlockStorageSourceChainDetachPrepareBlockdev
|
|
|
|
* @src: storage source chain to remove
|
|
|
|
*
|
2021-03-11 07:16:13 +00:00
|
|
|
* Prepares qemuBlockStorageSourceChainData *for detaching @src and its
|
2019-04-04 14:50:49 +00:00
|
|
|
* backingStore if -blockdev was used.
|
|
|
|
*/
|
2021-03-11 07:16:13 +00:00
|
|
|
qemuBlockStorageSourceChainData *
|
|
|
|
qemuBlockStorageSourceChainDetachPrepareBlockdev(virStorageSource *src)
|
2019-04-04 14:50:49 +00:00
|
|
|
{
|
2019-10-15 12:47:50 +00:00
|
|
|
g_autoptr(qemuBlockStorageSourceAttachData) backend = NULL;
|
|
|
|
g_autoptr(qemuBlockStorageSourceChainData) data = NULL;
|
2021-03-11 07:16:13 +00:00
|
|
|
virStorageSource *n;
|
2019-04-04 14:50:49 +00:00
|
|
|
|
2020-10-05 10:28:26 +00:00
|
|
|
data = g_new0(qemuBlockStorageSourceChainData, 1);
|
2019-04-04 14:50:49 +00:00
|
|
|
|
|
|
|
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)
|
|
|
|
*
|
2021-03-11 07:16:13 +00:00
|
|
|
* Prepares qemuBlockStorageSourceChainData *for detaching @src and its
|
2019-04-04 14:50:49 +00:00
|
|
|
* backingStore if -drive was used.
|
|
|
|
*/
|
2021-03-11 07:16:13 +00:00
|
|
|
qemuBlockStorageSourceChainData *
|
|
|
|
qemuBlockStorageSourceChainDetachPrepareDrive(virStorageSource *src,
|
2019-04-04 14:50:49 +00:00
|
|
|
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
|
|
|
|
2020-10-05 10:28:26 +00:00
|
|
|
data = g_new0(qemuBlockStorageSourceChainData, 1);
|
2019-04-04 14:50:49 +00:00
|
|
|
|
|
|
|
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
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-02-01 11:00:35 +00:00
|
|
|
/**
|
|
|
|
* qemuBlockStorageSourceChainDetachPrepareChardev
|
|
|
|
* @src: storage source chain to remove
|
|
|
|
*
|
2021-03-11 07:16:13 +00:00
|
|
|
* Prepares qemuBlockStorageSourceChainData *for detaching @src and its
|
2021-02-01 11:00:35 +00:00
|
|
|
* backingStore if -chardev was used.
|
|
|
|
*/
|
2021-03-11 07:16:13 +00:00
|
|
|
qemuBlockStorageSourceChainData *
|
2021-02-01 11:00:35 +00:00
|
|
|
qemuBlockStorageSourceChainDetachPrepareChardev(char *chardevAlias)
|
|
|
|
{
|
|
|
|
g_autoptr(qemuBlockStorageSourceAttachData) backend = NULL;
|
|
|
|
g_autoptr(qemuBlockStorageSourceChainData) data = NULL;
|
|
|
|
|
|
|
|
data = g_new0(qemuBlockStorageSourceChainData, 1);
|
|
|
|
backend = g_new0(qemuBlockStorageSourceAttachData, 1);
|
|
|
|
|
|
|
|
backend->chardevAlias = chardevAlias;
|
|
|
|
backend->chardevAdded = true;
|
|
|
|
|
|
|
|
if (VIR_APPEND_ELEMENT(data->srcdata, data->nsrcdata, backend) < 0)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
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
|
2021-03-11 07:16:13 +00:00
|
|
|
qemuBlockStorageSourceChainAttach(qemuMonitor *mon,
|
|
|
|
qemuBlockStorageSourceChainData *data)
|
2019-04-04 14:50:49 +00:00
|
|
|
{
|
|
|
|
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
|
2021-03-11 07:16:13 +00:00
|
|
|
qemuBlockStorageSourceChainDetach(qemuMonitor *mon,
|
|
|
|
qemuBlockStorageSourceChainData *data)
|
2019-04-04 14:50:49 +00:00
|
|
|
{
|
|
|
|
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
|
2021-03-11 07:16:13 +00:00
|
|
|
qemuBlockStorageSourceDetachOneBlockdev(virQEMUDriver *driver,
|
|
|
|
virDomainObj *vm,
|
2018-02-23 11:59:13 +00:00
|
|
|
qemuDomainAsyncJob asyncJob,
|
2021-03-11 07:16:13 +00:00
|
|
|
virStorageSource *src)
|
2018-02-23 11:59:13 +00:00
|
|
|
{
|
|
|
|
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
|
2021-03-11 07:16:13 +00:00
|
|
|
qemuBlockSnapshotAddLegacy(virJSONValue *actions,
|
|
|
|
virDomainDiskDef *disk,
|
|
|
|
virStorageSource *newsrc,
|
2018-07-03 12:13:29 +00:00
|
|
|
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
|
2021-03-11 07:16:13 +00:00
|
|
|
qemuBlockSnapshotAddBlockdev(virJSONValue *actions,
|
|
|
|
virDomainDiskDef *disk,
|
|
|
|
virStorageSource *newsrc)
|
2018-09-07 09:33:54 +00:00
|
|
|
{
|
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.
|
|
|
|
*/
|
2021-03-11 07:16:13 +00:00
|
|
|
virJSONValue *
|
|
|
|
qemuBlockStorageGetCopyOnReadProps(virDomainDiskDef *disk)
|
2018-06-11 11:25:20 +00:00
|
|
|
{
|
2021-03-11 07:16:13 +00:00
|
|
|
qemuDomainDiskPrivate *priv = QEMU_DOMAIN_DISK_PRIVATE(disk);
|
|
|
|
virJSONValue *ret = NULL;
|
2018-06-11 11:25:20 +00:00
|
|
|
|
|
|
|
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
|
2020-03-23 10:48:10 +00:00
|
|
|
* @pretty: pretty-print the JSON (if applicable, used by tests)
|
2019-07-22 15:36:05 +00:00
|
|
|
*
|
|
|
|
* 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 *
|
2021-03-11 07:16:13 +00:00
|
|
|
qemuBlockGetBackingStoreString(virStorageSource *src,
|
2020-03-23 10:48:10 +00:00
|
|
|
bool pretty)
|
2019-07-22 15:36:05 +00:00
|
|
|
{
|
|
|
|
int actualType = virStorageSourceGetActualType(src);
|
2019-10-15 12:47:50 +00:00
|
|
|
g_autoptr(virJSONValue) backingProps = NULL;
|
2020-02-12 16:25:12 +00:00
|
|
|
g_autoptr(virJSONValue) sliceProps = NULL;
|
2021-03-11 07:16:13 +00:00
|
|
|
virJSONValue *props = 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 *backingJSON = NULL;
|
2019-07-22 15:36:05 +00:00
|
|
|
|
2020-02-12 16:25:12 +00:00
|
|
|
if (!src->sliceStorage) {
|
2020-03-23 15:04:19 +00:00
|
|
|
if (virStorageSourceIsLocalStorage(src)) {
|
|
|
|
if (src->type == VIR_STORAGE_TYPE_DIR &&
|
|
|
|
src->format == VIR_STORAGE_FILE_FAT)
|
|
|
|
return g_strdup_printf("fat:%s", src->path);
|
|
|
|
|
2020-03-23 14:29:56 +00:00
|
|
|
return g_strdup(src->path);
|
2020-03-23 15:04:19 +00:00
|
|
|
}
|
2019-07-22 15:36:05 +00:00
|
|
|
|
2020-02-12 16:25:12 +00:00
|
|
|
/* generate simplified URIs for the easy cases */
|
|
|
|
if (actualType == VIR_STORAGE_TYPE_NETWORK &&
|
|
|
|
src->nhosts == 1 &&
|
2020-03-23 15:36:22 +00:00
|
|
|
src->hosts->transport == VIR_STORAGE_NET_HOST_TRANS_TCP &&
|
|
|
|
src->timeout == 0 &&
|
|
|
|
src->ncookies == 0 &&
|
|
|
|
src->sslverify == VIR_TRISTATE_BOOL_ABSENT &&
|
|
|
|
src->timeout == 0 &&
|
|
|
|
src->readahead == 0) {
|
2020-02-12 16:25:12 +00:00
|
|
|
|
|
|
|
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;
|
2019-07-22 15:36:05 +00:00
|
|
|
|
2020-03-23 14:29:56 +00:00
|
|
|
return virURIFormat(uri);
|
2019-07-22 15:36:05 +00:00
|
|
|
|
2020-02-12 16:25:12 +00:00
|
|
|
case VIR_STORAGE_NET_PROTOCOL_SHEEPDOG:
|
|
|
|
case VIR_STORAGE_NET_PROTOCOL_RBD:
|
|
|
|
case VIR_STORAGE_NET_PROTOCOL_VXHS:
|
2021-01-06 21:32:26 +00:00
|
|
|
case VIR_STORAGE_NET_PROTOCOL_NFS:
|
2020-02-12 16:25:12 +00:00
|
|
|
case VIR_STORAGE_NET_PROTOCOL_SSH:
|
|
|
|
case VIR_STORAGE_NET_PROTOCOL_LAST:
|
|
|
|
case VIR_STORAGE_NET_PROTOCOL_NONE:
|
|
|
|
break;
|
|
|
|
}
|
2019-07-22 15:36:05 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* use json: pseudo protocol otherwise */
|
2020-06-19 14:28:12 +00:00
|
|
|
if (!(backingProps = qemuBlockStorageSourceGetBackendProps(src,
|
|
|
|
QEMU_BLOCK_STORAGE_SOURCE_BACKEND_PROPS_TARGET_ONLY)))
|
2019-07-22 15:36:05 +00:00
|
|
|
return NULL;
|
|
|
|
|
2020-02-12 16:25:12 +00:00
|
|
|
props = backingProps;
|
|
|
|
|
|
|
|
if (src->sliceStorage) {
|
|
|
|
if (virJSONValueObjectCreate(&sliceProps,
|
|
|
|
"s:driver", "raw",
|
|
|
|
"U:offset", src->sliceStorage->offset,
|
|
|
|
"U:size", src->sliceStorage->size,
|
|
|
|
"a:file", &backingProps,
|
|
|
|
NULL) < 0)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
props = sliceProps;
|
|
|
|
}
|
|
|
|
|
2020-03-23 10:48:10 +00:00
|
|
|
if (!(backingJSON = virJSONValueToString(props, pretty)))
|
2019-07-22 15:36:05 +00:00
|
|
|
return NULL;
|
|
|
|
|
2020-03-23 15:09:31 +00:00
|
|
|
return g_strdup_printf("json:{\"file\":%s}", backingJSON);
|
2019-07-22 15:36:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-09-03 18:08:07 +00:00
|
|
|
static int
|
2021-03-11 07:16:13 +00:00
|
|
|
qemuBlockStorageSourceCreateAddBacking(virStorageSource *backing,
|
|
|
|
virJSONValue *props,
|
2018-09-03 18:08:07 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2020-03-23 10:48:10 +00:00
|
|
|
if (!(backingFileStr = qemuBlockGetBackingStoreString(backing, false)))
|
2019-07-22 15:36:05 +00:00
|
|
|
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
|
2021-03-11 07:16:13 +00:00
|
|
|
qemuBlockStorageSourceCreateGetFormatPropsGeneric(virStorageSource *src,
|
2018-09-03 18:08:07 +00:00
|
|
|
const char *driver,
|
2021-03-11 07:16:13 +00:00
|
|
|
virJSONValue **retprops,
|
|
|
|
virStorageSource *backing)
|
2018-09-03 18:08:07 +00:00
|
|
|
{
|
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
|
2021-03-11 07:16:13 +00:00
|
|
|
qemuBlockStorageSourceCreateGetEncryptionLUKS(virStorageSource *src,
|
|
|
|
virJSONValue **luksProps)
|
2018-09-03 18:08:07 +00:00
|
|
|
{
|
2021-03-11 07:16:13 +00:00
|
|
|
qemuDomainStorageSourcePrivate *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
|
2021-03-11 07:16:13 +00:00
|
|
|
qemuBlockStorageSourceCreateGetFormatPropsLUKS(virStorageSource *src,
|
|
|
|
virJSONValue **props)
|
2018-09-03 18:08:07 +00:00
|
|
|
{
|
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
|
2021-03-11 07:16:13 +00:00
|
|
|
qemuBlockStorageSourceCreateAddEncryptionQcow(virStorageSource *src,
|
|
|
|
virJSONValue *props)
|
2018-09-03 18:08:07 +00:00
|
|
|
{
|
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
|
2021-03-11 07:16:13 +00:00
|
|
|
qemuBlockStorageSourceCreateGetFormatPropsQcow2(virStorageSource *src,
|
|
|
|
virStorageSource *backing,
|
|
|
|
virJSONValue **props)
|
2018-09-03 18:08:07 +00:00
|
|
|
{
|
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,
|
2020-08-26 14:43:29 +00:00
|
|
|
"P:cluster-size", src->clusterSize,
|
2018-09-03 18:08:07 +00:00
|
|
|
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
|
2021-03-11 07:16:13 +00:00
|
|
|
qemuBlockStorageSourceCreateGetFormatPropsQcow(virStorageSource *src,
|
|
|
|
virStorageSource *backing,
|
|
|
|
virJSONValue **props)
|
2018-09-03 18:08:07 +00:00
|
|
|
{
|
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
|
2021-03-11 07:16:13 +00:00
|
|
|
qemuBlockStorageSourceCreateGetFormatPropsQed(virStorageSource *src,
|
|
|
|
virStorageSource *backing,
|
|
|
|
virJSONValue **props)
|
2018-09-03 18:08:07 +00:00
|
|
|
{
|
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
|
2021-03-11 07:16:13 +00:00
|
|
|
qemuBlockStorageSourceCreateGetFormatProps(virStorageSource *src,
|
|
|
|
virStorageSource *backing,
|
|
|
|
virJSONValue **props)
|
2018-09-03 18:08:07 +00:00
|
|
|
{
|
|
|
|
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
|
2021-03-11 07:16:13 +00:00
|
|
|
qemuBlockStorageSourceCreateGetStorageProps(virStorageSource *src,
|
|
|
|
virJSONValue **props)
|
2018-09-06 07:09:55 +00:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
|
2021-01-06 21:32:30 +00:00
|
|
|
case VIR_STORAGE_NET_PROTOCOL_NFS:
|
|
|
|
driver = "nfs";
|
|
|
|
if (!(location = qemuBlockStorageSourceGetNFSProps(src)))
|
|
|
|
return -1;
|
|
|
|
break;
|
|
|
|
|
2018-09-06 07:09:55 +00:00
|
|
|
/* 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:
|
2021-01-25 17:13:29 +00:00
|
|
|
case VIR_STORAGE_TYPE_VHOST_USER:
|
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
|
2021-03-11 07:16:13 +00:00
|
|
|
qemuBlockStorageSourceCreateGeneric(virDomainObj *vm,
|
|
|
|
virJSONValue *createProps,
|
|
|
|
virStorageSource *src,
|
|
|
|
virStorageSource *chain,
|
2019-06-10 16:13:09 +00:00
|
|
|
bool storageCreate,
|
|
|
|
qemuDomainAsyncJob asyncJob)
|
|
|
|
{
|
2019-10-15 12:47:50 +00:00
|
|
|
g_autoptr(virJSONValue) props = createProps;
|
2021-03-11 07:16:13 +00:00
|
|
|
qemuDomainObjPrivate *priv = vm->privateData;
|
|
|
|
qemuBlockJobData *job = NULL;
|
2019-06-10 16:13:09 +00:00
|
|
|
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);
|
|
|
|
|
2019-12-20 08:30:28 +00:00
|
|
|
qemuBlockJobUpdate(vm, job, asyncJob);
|
2019-06-10 16:13:09 +00:00
|
|
|
while (qemuBlockJobIsRunning(job)) {
|
|
|
|
if (virDomainObjWait(vm) < 0)
|
|
|
|
goto cleanup;
|
2019-12-20 08:30:28 +00:00
|
|
|
qemuBlockJobUpdate(vm, job, asyncJob);
|
2019-06-10 16:13:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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
|
2021-03-11 07:16:13 +00:00
|
|
|
qemuBlockStorageSourceCreateStorage(virDomainObj *vm,
|
|
|
|
virStorageSource *src,
|
|
|
|
virStorageSource *chain,
|
2019-06-10 16:13:09 +00:00
|
|
|
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
|
2021-03-11 07:16:13 +00:00
|
|
|
qemuBlockStorageSourceCreateFormat(virDomainObj *vm,
|
|
|
|
virStorageSource *src,
|
|
|
|
virStorageSource *backingStore,
|
|
|
|
virStorageSource *chain,
|
2019-06-10 16:13:09 +00:00
|
|
|
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;
|
|
|
|
|
2020-01-20 12:02:58 +00:00
|
|
|
if (src->format == VIR_STORAGE_FILE_RAW &&
|
|
|
|
!src->encryption)
|
2019-06-10 16:13:09 +00:00
|
|
|
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
|
2021-03-11 07:16:13 +00:00
|
|
|
qemuBlockStorageSourceCreate(virDomainObj *vm,
|
|
|
|
virStorageSource *src,
|
|
|
|
virStorageSource *backingStore,
|
|
|
|
virStorageSource *chain,
|
|
|
|
qemuBlockStorageSourceAttachData *data,
|
2019-06-10 16:13:09 +00:00
|
|
|
qemuDomainAsyncJob asyncJob)
|
|
|
|
{
|
2021-03-11 07:16:13 +00:00
|
|
|
qemuDomainObjPrivate *priv = vm->privateData;
|
2019-06-10 16:13:09 +00:00
|
|
|
int ret = -1;
|
|
|
|
int rc;
|
|
|
|
|
2020-02-12 16:26:47 +00:00
|
|
|
if (src->sliceStorage) {
|
|
|
|
virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s",
|
|
|
|
_("creation of images with slice type='storage' is not supported"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2020-05-11 12:23:13 +00:00
|
|
|
/* grant write access to read-only images during formatting */
|
|
|
|
if (src->readonly &&
|
|
|
|
qemuDomainStorageSourceAccessAllow(priv->driver, vm, src, false,
|
|
|
|
false, true) < 0)
|
|
|
|
return -1;
|
|
|
|
|
2019-06-10 16:13:09 +00:00
|
|
|
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;
|
|
|
|
|
2020-05-11 12:23:13 +00:00
|
|
|
/* revoke write access to read-only images during formatting */
|
|
|
|
if (src->readonly &&
|
|
|
|
qemuDomainStorageSourceAccessAllow(priv->driver, vm, src, true,
|
|
|
|
false, true) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
2019-06-10 16:13:09 +00:00
|
|
|
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
|
2020-10-22 17:04:18 +00:00
|
|
|
qemuBlockStorageSourceCreateDetectSize(GHashTable *blockNamedNodeData,
|
2021-03-11 07:16:13 +00:00
|
|
|
virStorageSource *src,
|
|
|
|
virStorageSource *templ)
|
2019-06-10 16:13:09 +00:00
|
|
|
{
|
2021-03-11 07:16:13 +00:00
|
|
|
qemuBlockNamedNodeData *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;
|
|
|
|
}
|
|
|
|
|
2020-08-26 14:45:51 +00:00
|
|
|
/* propagate cluster size if the images are compatible */
|
|
|
|
if (templ->format == VIR_STORAGE_FILE_QCOW2 &&
|
|
|
|
src->format == VIR_STORAGE_FILE_QCOW2 &&
|
|
|
|
src->clusterSize == 0)
|
|
|
|
src->clusterSize = entry->clusterSize;
|
|
|
|
|
2019-06-10 16:13:09 +00:00
|
|
|
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
|
2021-03-11 07:16:13 +00:00
|
|
|
qemuBlockRemoveImageMetadata(virQEMUDriver *driver,
|
|
|
|
virDomainObj *vm,
|
2019-11-19 07:43:58 +00:00
|
|
|
const char *diskTarget,
|
2021-03-11 07:16:13 +00:00
|
|
|
virStorageSource *src)
|
2019-11-19 07:43:58 +00:00
|
|
|
{
|
2021-03-11 07:16:13 +00:00
|
|
|
virStorageSource *n;
|
2019-11-19 07:43:58 +00:00
|
|
|
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.
|
|
|
|
*/
|
2021-03-11 07:16:13 +00:00
|
|
|
qemuBlockNamedNodeDataBitmap *
|
2020-10-22 17:04:18 +00:00
|
|
|
qemuBlockNamedNodeDataGetBitmapByName(GHashTable *blockNamedNodeData,
|
2021-03-11 07:16:13 +00:00
|
|
|
virStorageSource *src,
|
2019-12-05 14:57:30 +00:00
|
|
|
const char *bitmap)
|
|
|
|
{
|
2021-03-11 07:16:13 +00:00
|
|
|
qemuBlockNamedNodeData *nodedata;
|
2019-12-05 14:57:30 +00:00
|
|
|
size_t i;
|
|
|
|
|
|
|
|
if (!(nodedata = virHashLookup(blockNamedNodeData, src->nodeformat)))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
for (i = 0; i < nodedata->nbitmaps; i++) {
|
2021-03-11 07:16:13 +00:00
|
|
|
qemuBlockNamedNodeDataBitmap *bitmapdata = nodedata->bitmaps[i];
|
2019-12-05 14:57:30 +00:00
|
|
|
|
|
|
|
if (STRNEQ(bitmapdata->name, bitmap))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
return bitmapdata;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
2020-01-21 14:10:27 +00:00
|
|
|
|
|
|
|
|
2020-10-22 17:04:18 +00:00
|
|
|
GHashTable *
|
2021-03-11 07:16:13 +00:00
|
|
|
qemuBlockGetNamedNodeData(virDomainObj *vm,
|
2020-01-21 14:10:27 +00:00
|
|
|
qemuDomainAsyncJob asyncJob)
|
|
|
|
{
|
2021-03-11 07:16:13 +00:00
|
|
|
qemuDomainObjPrivate *priv = vm->privateData;
|
|
|
|
virQEMUDriver *driver = priv->driver;
|
2020-10-22 17:04:18 +00:00
|
|
|
g_autoptr(GHashTable) blockNamedNodeData = NULL;
|
2020-01-21 15:51:40 +00:00
|
|
|
bool supports_flat = virQEMUCapsGet(priv->qemuCaps,
|
|
|
|
QEMU_CAPS_QMP_QUERY_NAMED_BLOCK_NODES_FLAT);
|
2020-01-21 14:10:27 +00:00
|
|
|
|
|
|
|
if (qemuDomainObjEnterMonitorAsync(driver, vm, asyncJob) < 0)
|
|
|
|
return NULL;
|
|
|
|
|
2020-01-21 15:51:40 +00:00
|
|
|
blockNamedNodeData = qemuMonitorBlockGetNamedNodeData(priv->mon, supports_flat);
|
2020-01-21 14:10:27 +00:00
|
|
|
|
|
|
|
if (qemuDomainObjExitMonitor(driver, vm) < 0 || !blockNamedNodeData)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
return g_steal_pointer(&blockNamedNodeData);
|
|
|
|
}
|
2020-01-29 11:08:50 +00:00
|
|
|
|
|
|
|
|
2020-06-01 10:05:49 +00:00
|
|
|
/**
|
|
|
|
* qemuBlockGetBitmapMergeActionsGetBitmaps:
|
|
|
|
*
|
|
|
|
* Collect a list of bitmaps which need to be handled in
|
|
|
|
* qemuBlockGetBitmapMergeActions. The list contains only valid bitmaps in the
|
|
|
|
* sub-chain which is being processed.
|
|
|
|
*
|
|
|
|
* Note that the returned GSList contains bitmap names string pointers borrowed
|
|
|
|
* from @blockNamedNodeData so they must not be freed.
|
|
|
|
*/
|
|
|
|
static GSList *
|
2021-03-11 07:16:13 +00:00
|
|
|
qemuBlockGetBitmapMergeActionsGetBitmaps(virStorageSource *topsrc,
|
2020-06-01 10:05:49 +00:00
|
|
|
const char *bitmapname,
|
2020-10-22 17:04:18 +00:00
|
|
|
GHashTable *blockNamedNodeData)
|
2020-06-01 10:05:49 +00:00
|
|
|
{
|
|
|
|
g_autoptr(GSList) ret = NULL;
|
2021-03-11 07:16:13 +00:00
|
|
|
qemuBlockNamedNodeData *entry;
|
2020-06-01 10:05:49 +00:00
|
|
|
size_t i;
|
|
|
|
|
|
|
|
/* for now it doesn't make sense to consider bitmaps which are not present
|
|
|
|
* in @topsrc as we can't recreate a bitmap for a layer if it's missing */
|
|
|
|
|
|
|
|
if (!(entry = virHashLookup(blockNamedNodeData, topsrc->nodeformat)))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
for (i = 0; i < entry->nbitmaps; i++) {
|
2021-03-11 07:16:13 +00:00
|
|
|
qemuBlockNamedNodeDataBitmap *bitmap = entry->bitmaps[i];
|
2020-06-01 10:05:49 +00:00
|
|
|
|
|
|
|
if (bitmapname &&
|
|
|
|
STRNEQ(bitmapname, bitmap->name))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (!qemuBlockBitmapChainIsValid(topsrc, bitmap->name, blockNamedNodeData))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
ret = g_slist_prepend(ret, bitmap->name);
|
|
|
|
}
|
|
|
|
|
|
|
|
return g_steal_pointer(&ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* qemuBlockGetBitmapMergeActions:
|
|
|
|
* @topsrc: top of the chain to merge bitmaps in
|
|
|
|
* @basesrc: bottom of the chain to merge bitmaps in (NULL for full chain)
|
|
|
|
* @target: destination storage source of the merge (may be part of original chain)
|
|
|
|
* @bitmapname: name of bitmap to perform the merge (NULL for all bitmaps)
|
|
|
|
* @dstbitmapname: name of destination bitmap of the merge (see below for caveats)
|
|
|
|
* @writebitmapsrc: storage source corresponding to the node containing the write temporary bitmap
|
|
|
|
* @actions: returns actions for a 'transaction' QMP command for executing the merge
|
|
|
|
* @blockNamedNodeData: hash table filled with qemuBlockNamedNodeData
|
|
|
|
*
|
|
|
|
* Calculate handling of dirty block bitmaps between @topsrc and @basesrc. If
|
|
|
|
* @basesrc is NULL the end of the chain is considered. @target is the destination
|
|
|
|
* storage source definition of the merge and may or may not be part of the
|
|
|
|
* merged chain.
|
|
|
|
*
|
|
|
|
* Specifically the merging algorithm ensures that each considered bitmap is
|
|
|
|
* merged with the appropriate bitmaps so that it properly describes
|
|
|
|
* the state of dirty blocks when looked at from @topsrc based on the depth
|
|
|
|
* of the backing chain where the bitmap is placed.
|
|
|
|
*
|
|
|
|
* If @bitmapname is non-NULL only bitmaps with that name are handled, otherwise
|
|
|
|
* all bitmaps are considered.
|
|
|
|
*
|
|
|
|
* If @dstbitmap is non-NULL everything is merged into a bitmap with that name,
|
|
|
|
* otherwise each bitmap is merged into a bitmap with the same name into @target.
|
|
|
|
* Additionally if @dstbitmap is non-NULL the target bitmap is created as 'inactive'
|
|
|
|
* and 'transient' as a special case for the backup operation.
|
|
|
|
*
|
|
|
|
* If @writebitmapsrc is non-NULL, the 'libvirt-tmp-activewrite' bitmap from
|
|
|
|
* given node is merged along with others. This bitmap corresponds to the writes
|
|
|
|
* which occurred between an active layer job finished and the rest of the bitmap
|
|
|
|
* merging.
|
|
|
|
*
|
|
|
|
* If the bitmap is not valid somehow (see qemuBlockBitmapChainIsValid) given
|
|
|
|
* bitmap is silently skipped, so callers must ensure that given bitmap is valid
|
|
|
|
* if they care about it.
|
|
|
|
*
|
|
|
|
* The resulting 'transaction' QMP command actions are filled in and returned via
|
|
|
|
* @actions.
|
|
|
|
*
|
|
|
|
* Note that @actions may be NULL if no merging is required.
|
|
|
|
*/
|
|
|
|
int
|
2021-03-11 07:16:13 +00:00
|
|
|
qemuBlockGetBitmapMergeActions(virStorageSource *topsrc,
|
|
|
|
virStorageSource *basesrc,
|
|
|
|
virStorageSource *target,
|
2020-06-01 10:05:49 +00:00
|
|
|
const char *bitmapname,
|
|
|
|
const char *dstbitmapname,
|
2021-03-11 07:16:13 +00:00
|
|
|
virStorageSource *writebitmapsrc,
|
|
|
|
virJSONValue **actions,
|
2020-10-22 17:04:18 +00:00
|
|
|
GHashTable *blockNamedNodeData)
|
2020-06-01 10:05:49 +00:00
|
|
|
{
|
|
|
|
g_autoptr(virJSONValue) act = virJSONValueNewArray();
|
2021-03-11 07:16:13 +00:00
|
|
|
virStorageSource *n;
|
2020-06-01 10:05:49 +00:00
|
|
|
|
|
|
|
g_autoptr(GSList) bitmaps = NULL;
|
|
|
|
GSList *next;
|
|
|
|
|
|
|
|
if (!(bitmaps = qemuBlockGetBitmapMergeActionsGetBitmaps(topsrc, bitmapname,
|
|
|
|
blockNamedNodeData)))
|
2020-07-16 12:46:43 +00:00
|
|
|
goto done;
|
2020-06-01 10:05:49 +00:00
|
|
|
|
|
|
|
for (next = bitmaps; next; next = next->next) {
|
|
|
|
const char *curbitmap = next->data;
|
|
|
|
const char *mergebitmapname = dstbitmapname;
|
|
|
|
bool mergebitmappersistent = false;
|
|
|
|
bool mergebitmapdisabled = true;
|
|
|
|
g_autoptr(virJSONValue) merge = virJSONValueNewArray();
|
|
|
|
unsigned long long granularity = 0;
|
2021-03-11 07:16:13 +00:00
|
|
|
qemuBlockNamedNodeDataBitmap *bitmap;
|
2020-06-01 10:05:49 +00:00
|
|
|
|
|
|
|
/* explicitly named destinations mean that we want a temporary
|
|
|
|
* disabled bitmap only, so undo the default for non-explicit cases */
|
|
|
|
if (!mergebitmapname) {
|
|
|
|
mergebitmapname = curbitmap;
|
|
|
|
mergebitmappersistent = true;
|
|
|
|
mergebitmapdisabled = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (n = topsrc; virStorageSourceIsBacking(n) && n != basesrc; n = n->backingStore) {
|
|
|
|
if (!(bitmap = qemuBlockNamedNodeDataGetBitmapByName(blockNamedNodeData,
|
|
|
|
n, curbitmap)))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (granularity == 0)
|
|
|
|
granularity = bitmap->granularity;
|
|
|
|
|
|
|
|
if (qemuMonitorTransactionBitmapMergeSourceAddBitmap(merge,
|
|
|
|
n->nodeformat,
|
|
|
|
bitmap->name) < 0)
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dstbitmapname ||
|
|
|
|
!(bitmap = qemuBlockNamedNodeDataGetBitmapByName(blockNamedNodeData,
|
|
|
|
target, curbitmap))) {
|
|
|
|
|
|
|
|
if (qemuMonitorTransactionBitmapAdd(act,
|
|
|
|
target->nodeformat,
|
|
|
|
mergebitmapname,
|
|
|
|
mergebitmappersistent,
|
|
|
|
mergebitmapdisabled,
|
|
|
|
granularity) < 0)
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (writebitmapsrc &&
|
|
|
|
qemuMonitorTransactionBitmapMergeSourceAddBitmap(merge,
|
|
|
|
writebitmapsrc->nodeformat,
|
|
|
|
"libvirt-tmp-activewrite") < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (qemuMonitorTransactionBitmapMerge(act, target->nodeformat,
|
|
|
|
mergebitmapname, &merge) < 0)
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2020-07-16 12:46:43 +00:00
|
|
|
done:
|
2020-06-01 10:05:49 +00:00
|
|
|
if (writebitmapsrc &&
|
|
|
|
qemuMonitorTransactionBitmapRemove(act, writebitmapsrc->nodeformat,
|
|
|
|
"libvirt-tmp-activewrite") < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (virJSONValueArraySize(act) > 0)
|
|
|
|
*actions = g_steal_pointer(&act);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-01-29 11:08:50 +00:00
|
|
|
/**
|
|
|
|
* qemuBlockBitmapChainIsValid:
|
|
|
|
*
|
2020-04-21 08:25:09 +00:00
|
|
|
* Validates that the backing chain of @src contains bitmaps which libvirt will
|
|
|
|
* consider as properly corresponding to a checkpoint named @bitmapname.
|
2020-01-29 11:08:50 +00:00
|
|
|
*
|
2020-04-21 08:25:09 +00:00
|
|
|
* The bitmaps need to:
|
|
|
|
* 1) start from the top image @src
|
|
|
|
* 2) must be present in consecutive layers
|
|
|
|
* 3) all must be active, persistent and not inconsistent
|
2020-01-29 11:08:50 +00:00
|
|
|
*/
|
|
|
|
bool
|
2021-03-11 07:16:13 +00:00
|
|
|
qemuBlockBitmapChainIsValid(virStorageSource *src,
|
2020-01-29 11:08:50 +00:00
|
|
|
const char *bitmapname,
|
2020-10-22 17:04:18 +00:00
|
|
|
GHashTable *blockNamedNodeData)
|
2020-01-29 11:08:50 +00:00
|
|
|
{
|
2021-03-11 07:16:13 +00:00
|
|
|
virStorageSource *n;
|
2020-04-21 08:25:09 +00:00
|
|
|
bool found = false;
|
2020-01-29 11:08:50 +00:00
|
|
|
bool chain_ended = false;
|
|
|
|
|
2020-04-21 08:25:09 +00:00
|
|
|
for (n = src; virStorageSourceIsBacking(n); n = n->backingStore) {
|
2021-03-11 07:16:13 +00:00
|
|
|
qemuBlockNamedNodeDataBitmap *bitmap;
|
2020-04-21 08:25:09 +00:00
|
|
|
|
|
|
|
if (!(bitmap = qemuBlockNamedNodeDataGetBitmapByName(blockNamedNodeData,
|
|
|
|
n, bitmapname))) {
|
|
|
|
/* rule 1, must start from top */
|
|
|
|
if (!found)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
chain_ended = true;
|
2020-01-29 11:08:50 +00:00
|
|
|
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2020-04-21 08:25:09 +00:00
|
|
|
/* rule 2, no-gaps */
|
2020-01-29 11:08:50 +00:00
|
|
|
if (chain_ended)
|
|
|
|
return false;
|
|
|
|
|
2020-04-21 08:25:09 +00:00
|
|
|
/* rule 3 */
|
|
|
|
if (bitmap->inconsistent || !bitmap->persistent || !bitmap->recording)
|
2020-01-29 11:08:50 +00:00
|
|
|
return false;
|
2020-04-21 08:25:09 +00:00
|
|
|
|
|
|
|
found = true;
|
2020-01-29 11:08:50 +00:00
|
|
|
}
|
|
|
|
|
2020-04-21 08:25:09 +00:00
|
|
|
return found;
|
2020-01-29 11:08:50 +00:00
|
|
|
}
|
2020-01-29 11:16:12 +00:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* qemuBlockBitmapsHandleBlockcopy:
|
|
|
|
* @src: disk source
|
|
|
|
* @mirror: mirror source
|
|
|
|
* @blockNamedNodeData: hash table containing data about bitmaps
|
|
|
|
* @shallow: whether shallow copy is requested
|
|
|
|
* @actions: filled with arguments for a 'transaction' command
|
|
|
|
*
|
|
|
|
* Calculates which bitmaps to copy and merge during a virDomainBlockCopy job.
|
|
|
|
* This is designed to be called when the job is already synchronised as it
|
|
|
|
* may result in active bitmaps being created.
|
|
|
|
*
|
|
|
|
* Returns 0 on success and -1 on error. If @actions is NULL when 0 is returned
|
|
|
|
* there are no actions to perform for the given job.
|
|
|
|
*/
|
|
|
|
int
|
2021-03-11 07:16:13 +00:00
|
|
|
qemuBlockBitmapsHandleBlockcopy(virStorageSource *src,
|
|
|
|
virStorageSource *mirror,
|
2020-10-22 17:04:18 +00:00
|
|
|
GHashTable *blockNamedNodeData,
|
2020-01-29 11:16:12 +00:00
|
|
|
bool shallow,
|
2021-03-11 07:16:13 +00:00
|
|
|
virJSONValue **actions)
|
2020-01-29 11:16:12 +00:00
|
|
|
{
|
2021-03-11 07:16:13 +00:00
|
|
|
virStorageSource *base = NULL;
|
2020-01-29 11:16:12 +00:00
|
|
|
|
2020-05-29 08:34:11 +00:00
|
|
|
if (shallow)
|
|
|
|
base = src->backingStore;
|
2020-01-29 11:16:12 +00:00
|
|
|
|
2020-05-29 08:34:11 +00:00
|
|
|
if (qemuBlockGetBitmapMergeActions(src, base, mirror, NULL, NULL, mirror, actions,
|
|
|
|
blockNamedNodeData) < 0)
|
2020-01-29 11:16:12 +00:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2020-02-13 08:24:36 +00:00
|
|
|
|
|
|
|
|
2020-03-02 13:40:18 +00:00
|
|
|
/**
|
|
|
|
* @topsrc: virStorageSource representing 'top' of the job
|
|
|
|
* @basesrc: virStorageSource representing 'base' of the job
|
2020-04-17 07:51:40 +00:00
|
|
|
* @active: commit job is an active layer block-commit
|
2020-03-02 13:40:18 +00:00
|
|
|
* @blockNamedNodeData: hash table containing data about bitmaps
|
|
|
|
* @actions: filled with arguments for a 'transaction' command
|
|
|
|
* @disabledBitmapsBase: bitmap names which were disabled
|
|
|
|
*
|
|
|
|
* Calculates the necessary bitmap merges/additions/enablements to properly
|
|
|
|
* handle commit of images from 'top' into 'base'. The necessary operations
|
|
|
|
* in the form of arguments of the 'transaction' command are filled into
|
|
|
|
* 'actions' if there is anything to do. Otherwise NULL is returned.
|
|
|
|
*/
|
|
|
|
int
|
2021-03-11 07:16:13 +00:00
|
|
|
qemuBlockBitmapsHandleCommitFinish(virStorageSource *topsrc,
|
|
|
|
virStorageSource *basesrc,
|
2020-04-17 07:51:40 +00:00
|
|
|
bool active,
|
2020-10-22 17:04:18 +00:00
|
|
|
GHashTable *blockNamedNodeData,
|
2021-03-11 07:16:13 +00:00
|
|
|
virJSONValue **actions)
|
2020-03-02 13:40:18 +00:00
|
|
|
{
|
2021-03-11 07:16:13 +00:00
|
|
|
virStorageSource *writebitmapsrc = NULL;
|
2020-03-02 13:40:18 +00:00
|
|
|
|
2020-04-17 07:51:40 +00:00
|
|
|
if (active)
|
|
|
|
writebitmapsrc = basesrc;
|
2020-03-02 13:40:18 +00:00
|
|
|
|
2020-04-17 07:51:40 +00:00
|
|
|
if (qemuBlockGetBitmapMergeActions(topsrc, basesrc, basesrc, NULL, NULL,
|
|
|
|
writebitmapsrc, actions,
|
|
|
|
blockNamedNodeData) < 0)
|
2020-03-02 13:40:18 +00:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-02-13 08:24:36 +00:00
|
|
|
/**
|
|
|
|
* qemuBlockReopenFormat:
|
|
|
|
* @vm: domain object
|
|
|
|
* @src: storage source to reopen
|
|
|
|
* @asyncJob: qemu async job type
|
|
|
|
*
|
|
|
|
* Invokes the 'blockdev-reopen' command on the format layer of @src. This means
|
|
|
|
* that @src must be already properly configured for the desired outcome. The
|
|
|
|
* nodenames of @src are used to identify the specific image in qemu.
|
|
|
|
*/
|
|
|
|
static int
|
2021-03-11 07:16:13 +00:00
|
|
|
qemuBlockReopenFormat(virDomainObj *vm,
|
|
|
|
virStorageSource *src,
|
2020-02-13 08:24:36 +00:00
|
|
|
qemuDomainAsyncJob asyncJob)
|
|
|
|
{
|
2021-03-11 07:16:13 +00:00
|
|
|
qemuDomainObjPrivate *priv = vm->privateData;
|
|
|
|
virQEMUDriver *driver = priv->driver;
|
2020-02-13 08:24:36 +00:00
|
|
|
g_autoptr(virJSONValue) reopenprops = NULL;
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
/* If we are lacking the object here, qemu might have opened an image with
|
|
|
|
* a node name unknown to us */
|
|
|
|
if (!src->backingStore) {
|
|
|
|
virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s",
|
|
|
|
_("can't reopen image with unknown presence of backing store"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(reopenprops = qemuBlockStorageSourceGetBlockdevProps(src, src->backingStore)))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (qemuDomainObjEnterMonitorAsync(driver, vm, asyncJob) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
rc = qemuMonitorBlockdevReopen(priv->mon, &reopenprops);
|
|
|
|
|
|
|
|
if (qemuDomainObjExitMonitor(driver, vm) < 0 || rc < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* qemuBlockReopenReadWrite:
|
|
|
|
* @vm: domain object
|
|
|
|
* @src: storage source to reopen
|
|
|
|
* @asyncJob: qemu async job type
|
|
|
|
*
|
|
|
|
* Wrapper that reopens @src read-write. We currently depend on qemu
|
|
|
|
* reopening the storage with 'auto-read-only' enabled for us.
|
|
|
|
* After successful reopen @src's 'readonly' flag is modified. Does nothing
|
|
|
|
* if @src is already read-write.
|
|
|
|
*/
|
|
|
|
int
|
2021-03-11 07:16:13 +00:00
|
|
|
qemuBlockReopenReadWrite(virDomainObj *vm,
|
|
|
|
virStorageSource *src,
|
2020-02-13 08:24:36 +00:00
|
|
|
qemuDomainAsyncJob asyncJob)
|
|
|
|
{
|
|
|
|
if (!src->readonly)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
src->readonly = false;
|
|
|
|
if (qemuBlockReopenFormat(vm, src, asyncJob) < 0) {
|
|
|
|
src->readonly = true;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* qemuBlockReopenReadOnly:
|
|
|
|
* @vm: domain object
|
|
|
|
* @src: storage source to reopen
|
|
|
|
* @asyncJob: qemu async job type
|
|
|
|
*
|
|
|
|
* Wrapper that reopens @src read-only. We currently depend on qemu
|
|
|
|
* reopening the storage with 'auto-read-only' enabled for us.
|
|
|
|
* After successful reopen @src's 'readonly' flag is modified. Does nothing
|
|
|
|
* if @src is already read-only.
|
|
|
|
*/
|
|
|
|
int
|
2021-03-11 07:16:13 +00:00
|
|
|
qemuBlockReopenReadOnly(virDomainObj *vm,
|
|
|
|
virStorageSource *src,
|
2020-02-13 08:24:36 +00:00
|
|
|
qemuDomainAsyncJob asyncJob)
|
|
|
|
{
|
|
|
|
if (src->readonly)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
src->readonly = true;
|
|
|
|
if (qemuBlockReopenFormat(vm, src, asyncJob) < 0) {
|
|
|
|
src->readonly = false;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2020-03-19 15:43:49 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* qemuBlockStorageSourceNeedSliceLayer:
|
|
|
|
* @src: source to inspect
|
|
|
|
*
|
|
|
|
* Returns true if @src requires an extra 'raw' layer for handling of the storage
|
|
|
|
* slice.
|
|
|
|
*/
|
|
|
|
bool
|
|
|
|
qemuBlockStorageSourceNeedsStorageSliceLayer(const virStorageSource *src)
|
|
|
|
{
|
|
|
|
if (!src->sliceStorage)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (src->format != VIR_STORAGE_FILE_RAW)
|
|
|
|
return true;
|
|
|
|
|
2020-03-19 15:54:52 +00:00
|
|
|
if (src->encryption &&
|
|
|
|
src->encryption->format == VIR_STORAGE_ENCRYPTION_FORMAT_LUKS)
|
|
|
|
return true;
|
|
|
|
|
2020-03-19 15:43:49 +00:00
|
|
|
return false;
|
|
|
|
}
|
2020-03-23 15:31:19 +00:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* qemuBlockStorageSourceGetCookieString:
|
|
|
|
* @src: storage source
|
|
|
|
*
|
|
|
|
* Returns a properly formatted string representing cookies of @src in format
|
|
|
|
* accepted by qemu.
|
|
|
|
*/
|
|
|
|
char *
|
2021-03-11 07:16:13 +00:00
|
|
|
qemuBlockStorageSourceGetCookieString(virStorageSource *src)
|
2020-03-23 15:31:19 +00:00
|
|
|
{
|
2020-07-02 22:26:41 +00:00
|
|
|
g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
|
2020-03-23 15:31:19 +00:00
|
|
|
size_t i;
|
|
|
|
|
|
|
|
for (i = 0; i < src->ncookies; i++) {
|
2021-03-11 07:16:13 +00:00
|
|
|
virStorageNetCookieDef *cookie = src->cookies[i];
|
2020-03-23 15:31:19 +00:00
|
|
|
|
|
|
|
virBufferAsprintf(&buf, "%s=%s; ", cookie->name, cookie->value);
|
|
|
|
}
|
|
|
|
|
|
|
|
virBufferTrim(&buf, "; ");
|
|
|
|
|
|
|
|
return virBufferContentAndReset(&buf);
|
|
|
|
}
|
2020-03-30 09:18:32 +00:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* qemuBlockUpdateRelativeBacking:
|
|
|
|
* @vm: domain object
|
|
|
|
* @src: starting point of the update
|
|
|
|
* @topsrc: top level image in the backing chain (used to get security label)
|
|
|
|
*
|
|
|
|
* Reload data necessary for keeping backing store links starting from @src
|
|
|
|
* relative.
|
|
|
|
*/
|
|
|
|
int
|
2021-03-11 07:16:13 +00:00
|
|
|
qemuBlockUpdateRelativeBacking(virDomainObj *vm,
|
|
|
|
virStorageSource *src,
|
|
|
|
virStorageSource *topsrc)
|
2020-03-30 09:18:32 +00:00
|
|
|
{
|
2021-03-11 07:16:13 +00:00
|
|
|
qemuDomainObjPrivate *priv = vm->privateData;
|
|
|
|
virQEMUDriver *driver = priv->driver;
|
|
|
|
virStorageSource *n;
|
2020-03-30 09:18:32 +00:00
|
|
|
|
|
|
|
for (n = src; virStorageSourceHasBacking(n); n = n->backingStore) {
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
if (n->backingStore->relPath)
|
|
|
|
break;
|
|
|
|
|
2021-01-21 15:46:14 +00:00
|
|
|
if (!virStorageSourceSupportsBackingChainTraversal(n))
|
2020-03-30 09:18:32 +00:00
|
|
|
continue;
|
|
|
|
|
|
|
|
if (qemuDomainStorageFileInit(driver, vm, n, topsrc) < 0)
|
|
|
|
return -1;
|
|
|
|
|
2021-01-22 14:08:23 +00:00
|
|
|
rc = virStorageSourceFetchRelativeBackingPath(n, &n->backingStore->relPath);
|
2020-03-30 09:18:32 +00:00
|
|
|
|
2021-01-21 15:46:14 +00:00
|
|
|
virStorageSourceDeinit(n);
|
2020-03-30 09:18:32 +00:00
|
|
|
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2020-10-14 09:12:19 +00:00
|
|
|
|
|
|
|
|
2021-03-11 07:16:13 +00:00
|
|
|
virJSONValue *
|
2020-10-14 09:33:06 +00:00
|
|
|
qemuBlockExportGetNBDProps(const char *nodename,
|
|
|
|
const char *exportname,
|
|
|
|
bool writable,
|
2020-10-26 12:06:17 +00:00
|
|
|
const char **bitmaps)
|
2020-10-14 09:33:06 +00:00
|
|
|
{
|
|
|
|
g_autofree char *exportid = NULL;
|
2020-10-26 12:06:17 +00:00
|
|
|
g_autoptr(virJSONValue) bitmapsarr = NULL;
|
2021-03-11 07:16:13 +00:00
|
|
|
virJSONValue *ret = NULL;
|
2020-10-14 09:33:06 +00:00
|
|
|
|
|
|
|
exportid = g_strdup_printf("libvirt-nbd-%s", nodename);
|
|
|
|
|
2020-10-26 12:06:17 +00:00
|
|
|
if (bitmaps && *bitmaps) {
|
|
|
|
bitmapsarr = virJSONValueNewArray();
|
|
|
|
|
|
|
|
while (*bitmaps) {
|
|
|
|
if (virJSONValueArrayAppendString(bitmapsarr, *(bitmaps++)) < 0)
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-14 09:33:06 +00:00
|
|
|
if (virJSONValueObjectCreate(&ret,
|
|
|
|
"s:type", "nbd",
|
|
|
|
"s:id", exportid,
|
|
|
|
"s:node-name", nodename,
|
|
|
|
"b:writable", writable,
|
|
|
|
"s:name", exportname,
|
2020-10-26 12:06:17 +00:00
|
|
|
"A:bitmaps", &bitmapsarr,
|
2020-10-14 09:33:06 +00:00
|
|
|
NULL) < 0)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-10-14 09:12:19 +00:00
|
|
|
/**
|
|
|
|
* qemuBlockExportAddNBD:
|
|
|
|
* @vm: domain object
|
|
|
|
* @drivealias: (optional) alias of -drive to export in pre-blockdev configurations
|
|
|
|
* @src: disk source to export
|
|
|
|
* @exportname: name for the export
|
|
|
|
* @writable: whether the NBD export allows writes
|
|
|
|
* @bitmap: (optional) block dirty bitmap to export along
|
|
|
|
*
|
|
|
|
* This function automatically selects the proper invocation of exporting a
|
|
|
|
* block backend via NBD in qemu. This includes use of nodename for blockdev
|
|
|
|
* and proper configuration for the exportname for older qemus.
|
|
|
|
*
|
|
|
|
* This function must be called while in the monitor context.
|
|
|
|
*/
|
|
|
|
int
|
2021-03-11 07:16:13 +00:00
|
|
|
qemuBlockExportAddNBD(virDomainObj *vm,
|
2020-10-14 09:12:19 +00:00
|
|
|
const char *drivealias,
|
2021-03-11 07:16:13 +00:00
|
|
|
virStorageSource *src,
|
2020-10-14 09:12:19 +00:00
|
|
|
const char *exportname,
|
|
|
|
bool writable,
|
|
|
|
const char *bitmap)
|
|
|
|
{
|
2021-03-11 07:16:13 +00:00
|
|
|
qemuDomainObjPrivate *priv = vm->privateData;
|
2020-10-14 09:12:19 +00:00
|
|
|
|
|
|
|
if (virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_BLOCKDEV)) {
|
2020-10-14 09:49:50 +00:00
|
|
|
if (virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_BLOCK_EXPORT_ADD)) {
|
|
|
|
g_autoptr(virJSONValue) nbdprops = NULL;
|
2020-10-26 12:06:17 +00:00
|
|
|
const char *bitmaps[2] = { bitmap, NULL };
|
2020-10-14 09:49:50 +00:00
|
|
|
|
|
|
|
if (!(nbdprops = qemuBlockExportGetNBDProps(src->nodeformat,
|
|
|
|
exportname,
|
|
|
|
writable,
|
2020-10-26 12:06:17 +00:00
|
|
|
bitmaps)))
|
2020-10-14 09:49:50 +00:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
return qemuMonitorBlockExportAdd(priv->mon, &nbdprops);
|
|
|
|
} else {
|
|
|
|
return qemuMonitorNBDServerAdd(priv->mon, src->nodeformat,
|
|
|
|
exportname, writable, bitmap);
|
|
|
|
}
|
2020-10-14 09:12:19 +00:00
|
|
|
} else {
|
|
|
|
/* older qemu versions didn't support configuring the exportname and
|
|
|
|
* took the 'drivealias' as the export name */
|
2020-10-14 09:49:50 +00:00
|
|
|
return qemuMonitorNBDServerAdd(priv->mon, drivealias, NULL, writable, NULL);
|
2020-10-14 09:12:19 +00:00
|
|
|
}
|
|
|
|
|
2020-10-14 09:49:50 +00:00
|
|
|
return 0;
|
2020-10-14 09:12:19 +00:00
|
|
|
}
|