mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2024-12-27 16:15:23 +00:00
55ea7be7d9
For stateless, client side drivers, it is never correct to probe for secondary drivers. It is only ever appropriate to use the secondary driver that is associated with the hypervisor in question. As a result the ESX & HyperV drivers have both been forced to do hacks where they register no-op drivers for the ones they don't implement. For stateful, server side drivers, we always just want to use the same built-in shared driver. The exception is virtualbox which is really a stateless driver and so wants to use its own server side secondary drivers. To deal with this virtualbox has to be built as 3 separate loadable modules to allow registration to work in the right order. This can all be simplified by introducing a new struct recording the precise set of secondary drivers each hypervisor driver wants struct _virConnectDriver { virHypervisorDriverPtr hypervisorDriver; virInterfaceDriverPtr interfaceDriver; virNetworkDriverPtr networkDriver; virNodeDeviceDriverPtr nodeDeviceDriver; virNWFilterDriverPtr nwfilterDriver; virSecretDriverPtr secretDriver; virStorageDriverPtr storageDriver; }; Instead of registering the hypervisor driver, we now just register a virConnectDriver instead. This allows us to remove all probing of secondary drivers. Once we have chosen the primary driver, we immediately know the correct secondary drivers to use. Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
548 lines
14 KiB
C
548 lines
14 KiB
C
/*
|
|
* esx_storage_driver.c: storage driver functions for managing VMware ESX
|
|
* host storage
|
|
*
|
|
* Copyright (C) 2010-2011 Red Hat, Inc.
|
|
* Copyright (C) 2010-2012 Matthias Bolte <matthias.bolte@googlemail.com>
|
|
* Copyright (C) 2012 Ata E Husain Bohra <ata.husain@hotmail.com>
|
|
*
|
|
* 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 "viruuid.h"
|
|
#include "viralloc.h"
|
|
#include "storage_conf.h"
|
|
#include "esx_private.h"
|
|
#include "esx_storage_driver.h"
|
|
#include "esx_storage_backend_vmfs.h"
|
|
#include "esx_storage_backend_iscsi.h"
|
|
|
|
#define VIR_FROM_THIS VIR_FROM_ESX
|
|
|
|
/*
|
|
* ESX storage driver implements a facade pattern;
|
|
* the driver exposes the routines supported by libvirt
|
|
* public interface to manage ESX storage devices. Internally
|
|
* it uses backend drivers to perform the required task.
|
|
*/
|
|
enum {
|
|
VMFS = 0,
|
|
ISCSI,
|
|
LAST_BACKEND
|
|
};
|
|
|
|
static virStorageDriverPtr backends[] = {
|
|
&esxStorageBackendVMFS,
|
|
&esxStorageBackendISCSI
|
|
};
|
|
|
|
|
|
static int
|
|
esxConnectNumOfStoragePools(virConnectPtr conn)
|
|
{
|
|
int count = 0;
|
|
esxPrivate *priv = conn->privateData;
|
|
size_t i;
|
|
int tmp;
|
|
|
|
if (esxVI_EnsureSession(priv->primary) < 0)
|
|
return -1;
|
|
|
|
for (i = 0; i < LAST_BACKEND; ++i) {
|
|
tmp = backends[i]->connectNumOfStoragePools(conn);
|
|
|
|
if (tmp < 0)
|
|
return -1;
|
|
|
|
count += tmp;
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
esxConnectListStoragePools(virConnectPtr conn, char **const names, int maxnames)
|
|
{
|
|
bool success = false;
|
|
esxPrivate *priv = conn->privateData;
|
|
int count = 0;
|
|
size_t i;
|
|
int tmp;
|
|
|
|
if (maxnames == 0)
|
|
return 0;
|
|
|
|
if (esxVI_EnsureSession(priv->primary) < 0)
|
|
return -1;
|
|
|
|
for (i = 0; i < LAST_BACKEND; ++i) {
|
|
tmp = backends[i]->connectListStoragePools(conn, &names[count], maxnames - count);
|
|
|
|
if (tmp < 0)
|
|
goto cleanup;
|
|
|
|
count += tmp;
|
|
}
|
|
|
|
success = true;
|
|
|
|
cleanup:
|
|
if (! success) {
|
|
for (i = 0; i < count; ++i)
|
|
VIR_FREE(names[i]);
|
|
|
|
count = -1;
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
esxConnectNumOfDefinedStoragePools(virConnectPtr conn ATTRIBUTE_UNUSED)
|
|
{
|
|
/* ESX storage pools are always active */
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
esxConnectListDefinedStoragePools(virConnectPtr conn ATTRIBUTE_UNUSED,
|
|
char **const names ATTRIBUTE_UNUSED,
|
|
int maxnames ATTRIBUTE_UNUSED)
|
|
{
|
|
/* ESX storage pools are always active */
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
static virStoragePoolPtr
|
|
esxStoragePoolLookupByName(virConnectPtr conn, const char *name)
|
|
{
|
|
esxPrivate *priv = conn->privateData;
|
|
size_t i;
|
|
virStoragePoolPtr pool;
|
|
|
|
virCheckNonNullArgReturn(name, NULL);
|
|
|
|
if (esxVI_EnsureSession(priv->primary) < 0)
|
|
return NULL;
|
|
|
|
for (i = 0; i < LAST_BACKEND; ++i) {
|
|
pool = backends[i]->storagePoolLookupByName(conn, name);
|
|
|
|
if (pool)
|
|
return pool;
|
|
}
|
|
|
|
virReportError(VIR_ERR_NO_STORAGE_POOL,
|
|
_("Could not find storage pool with name '%s'"), name);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
|
|
static virStoragePoolPtr
|
|
esxStoragePoolLookupByUUID(virConnectPtr conn, const unsigned char *uuid)
|
|
{
|
|
esxPrivate *priv = conn->privateData;
|
|
size_t i;
|
|
virStoragePoolPtr pool;
|
|
char uuid_string[VIR_UUID_STRING_BUFLEN] = "";
|
|
|
|
if (esxVI_EnsureSession(priv->primary) < 0)
|
|
return NULL;
|
|
|
|
/* invoke backend drive method to search all known pools */
|
|
for (i = 0; i < LAST_BACKEND; ++i) {
|
|
pool = backends[i]->storagePoolLookupByUUID(conn, uuid);
|
|
|
|
if (pool)
|
|
return pool;
|
|
}
|
|
|
|
virUUIDFormat(uuid, uuid_string);
|
|
virReportError(VIR_ERR_NO_STORAGE_POOL,
|
|
_("Could not find storage pool with uuid '%s'"),
|
|
uuid_string);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
|
|
static virStoragePoolPtr
|
|
esxStoragePoolLookupByVolume(virStorageVolPtr volume)
|
|
{
|
|
return esxStoragePoolLookupByName(volume->conn, volume->pool);
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
esxStoragePoolRefresh(virStoragePoolPtr pool, unsigned int flags)
|
|
{
|
|
esxPrivate *priv = pool->conn->privateData;
|
|
virStorageDriverPtr backend = pool->privateData;
|
|
|
|
virCheckNonNullArgReturn(pool->privateData, -1);
|
|
|
|
if (esxVI_EnsureSession(priv->primary) < 0)
|
|
return -1;
|
|
|
|
return backend->storagePoolRefresh(pool, flags);
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
esxStoragePoolGetInfo(virStoragePoolPtr pool, virStoragePoolInfoPtr info)
|
|
{
|
|
esxPrivate *priv = pool->conn->privateData;
|
|
virStorageDriverPtr backend = pool->privateData;
|
|
|
|
virCheckNonNullArgReturn(pool->privateData, -1);
|
|
|
|
memset(info, 0, sizeof(*info));
|
|
|
|
if (esxVI_EnsureSession(priv->primary) < 0)
|
|
return -1;
|
|
|
|
return backend->storagePoolGetInfo(pool, info);
|
|
}
|
|
|
|
|
|
|
|
static char *
|
|
esxStoragePoolGetXMLDesc(virStoragePoolPtr pool, unsigned int flags)
|
|
{
|
|
esxPrivate *priv = pool->conn->privateData;
|
|
virStorageDriverPtr backend = pool->privateData;
|
|
|
|
virCheckNonNullArgReturn(pool->privateData, NULL);
|
|
|
|
if (esxVI_EnsureSession(priv->primary) < 0)
|
|
return NULL;
|
|
|
|
return backend->storagePoolGetXMLDesc(pool, flags);
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
esxStoragePoolGetAutostart(virStoragePoolPtr pool ATTRIBUTE_UNUSED,
|
|
int *autostart)
|
|
{
|
|
/* ESX storage pools are always active */
|
|
*autostart = 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
esxStoragePoolSetAutostart(virStoragePoolPtr pool ATTRIBUTE_UNUSED,
|
|
int autostart)
|
|
{
|
|
/* Just accept autostart activation, but fail on autostart deactivation */
|
|
autostart = (autostart != 0);
|
|
|
|
if (! autostart) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("Cannot deactivate storage pool autostart"));
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
esxStoragePoolNumOfVolumes(virStoragePoolPtr pool)
|
|
{
|
|
esxPrivate *priv = pool->conn->privateData;
|
|
virStorageDriverPtr backend = pool->privateData;
|
|
|
|
virCheckNonNullArgReturn(pool->privateData, -1);
|
|
|
|
if (esxVI_EnsureSession(priv->primary) < 0)
|
|
return -1;
|
|
|
|
return backend->storagePoolNumOfVolumes(pool);
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
esxStoragePoolListVolumes(virStoragePoolPtr pool, char **const names,
|
|
int maxnames)
|
|
{
|
|
esxPrivate *priv = pool->conn->privateData;
|
|
virStorageDriverPtr backend = pool->privateData;
|
|
|
|
virCheckNonNullArgReturn(pool->privateData, -1);
|
|
|
|
if (esxVI_EnsureSession(priv->primary) < 0)
|
|
return -1;
|
|
|
|
return backend->storagePoolListVolumes(pool, names, maxnames);
|
|
}
|
|
|
|
|
|
|
|
static virStorageVolPtr
|
|
esxStorageVolLookupByName(virStoragePoolPtr pool, const char *name)
|
|
{
|
|
esxPrivate *priv = pool->conn->privateData;
|
|
virStorageDriverPtr backend = pool->privateData;
|
|
|
|
virCheckNonNullArgReturn(pool->privateData, NULL);
|
|
|
|
if (esxVI_EnsureSession(priv->primary) < 0)
|
|
return NULL;
|
|
|
|
return backend->storageVolLookupByName(pool, name);
|
|
}
|
|
|
|
|
|
|
|
static virStorageVolPtr
|
|
esxStorageVolLookupByPath(virConnectPtr conn, const char *path)
|
|
{
|
|
esxPrivate *priv = conn->privateData;
|
|
|
|
if (esxVI_EnsureSession(priv->primary) < 0)
|
|
return NULL;
|
|
|
|
/*
|
|
* FIXME: calling backends blindly may set unwanted error codes
|
|
*
|
|
* VMFS Datastore path follows canonical format i.e.:
|
|
* [<datastore_name>] <file_path>
|
|
* WHEREAS
|
|
* iSCSI LUNs device path follows normal linux path convention
|
|
*/
|
|
if (STRPREFIX(path, "[")) {
|
|
return backends[VMFS]->storageVolLookupByPath(conn, path);
|
|
} else if (STRPREFIX(path, "/")) {
|
|
return backends[ISCSI]->storageVolLookupByPath(conn, path);
|
|
} else {
|
|
virReportError(VIR_ERR_INVALID_ARG,
|
|
_("Unexpected volume path format: %s"), path);
|
|
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static virStorageVolPtr
|
|
esxStorageVolLookupByKey(virConnectPtr conn, const char *key)
|
|
{
|
|
virStorageVolPtr volume;
|
|
esxPrivate *priv = conn->privateData;
|
|
size_t i;
|
|
|
|
if (esxVI_EnsureSession(priv->primary) < 0)
|
|
return NULL;
|
|
|
|
for (i = 0; i < LAST_BACKEND; ++i) {
|
|
volume = backends[i]->storageVolLookupByKey(conn, key);
|
|
|
|
if (volume)
|
|
return volume;
|
|
}
|
|
|
|
virReportError(VIR_ERR_NO_STORAGE_VOL,
|
|
_("Could not find storage volume with key '%s'"),
|
|
key);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
|
|
static virStorageVolPtr
|
|
esxStorageVolCreateXML(virStoragePoolPtr pool, const char *xmldesc,
|
|
unsigned int flags)
|
|
{
|
|
esxPrivate *priv = pool->conn->privateData;
|
|
virStorageDriverPtr backend = pool->privateData;
|
|
|
|
virCheckNonNullArgReturn(pool->privateData, NULL);
|
|
|
|
if (esxVI_EnsureSession(priv->primary) < 0)
|
|
return NULL;
|
|
|
|
return backend->storageVolCreateXML(pool, xmldesc, flags);
|
|
}
|
|
|
|
|
|
|
|
static virStorageVolPtr
|
|
esxStorageVolCreateXMLFrom(virStoragePoolPtr pool, const char *xmldesc,
|
|
virStorageVolPtr sourceVolume, unsigned int flags)
|
|
{
|
|
esxPrivate *priv = pool->conn->privateData;
|
|
virStorageDriverPtr backend = pool->privateData;
|
|
|
|
virCheckNonNullArgReturn(pool->privateData, NULL);
|
|
|
|
if (esxVI_EnsureSession(priv->primary) < 0)
|
|
return NULL;
|
|
|
|
return backend->storageVolCreateXMLFrom(pool, xmldesc, sourceVolume, flags);
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
esxStorageVolDelete(virStorageVolPtr volume, unsigned int flags)
|
|
{
|
|
esxPrivate *priv = volume->conn->privateData;
|
|
virStorageDriverPtr backend = volume->privateData;
|
|
|
|
virCheckNonNullArgReturn(volume->privateData, -1);
|
|
|
|
if (esxVI_EnsureSession(priv->primary) < 0)
|
|
return -1;
|
|
|
|
return backend->storageVolDelete(volume, flags);
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
esxStorageVolWipe(virStorageVolPtr volume, unsigned int flags)
|
|
{
|
|
esxPrivate *priv = volume->conn->privateData;
|
|
virStorageDriverPtr backend = volume->privateData;
|
|
|
|
virCheckNonNullArgReturn(volume->privateData, -1);
|
|
|
|
if (esxVI_EnsureSession(priv->primary) < 0)
|
|
return -1;
|
|
|
|
return backend->storageVolWipe(volume, flags);
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
esxStorageVolGetInfo(virStorageVolPtr volume, virStorageVolInfoPtr info)
|
|
{
|
|
esxPrivate *priv = volume->conn->privateData;
|
|
virStorageDriverPtr backend = volume->privateData;
|
|
|
|
virCheckNonNullArgReturn(volume->privateData, -1);
|
|
|
|
if (esxVI_EnsureSession(priv->primary) < 0)
|
|
return -1;
|
|
|
|
return backend->storageVolGetInfo(volume, info);
|
|
}
|
|
|
|
|
|
|
|
static char *
|
|
esxStorageVolGetXMLDesc(virStorageVolPtr volume, unsigned int flags)
|
|
{
|
|
esxPrivate *priv = volume->conn->privateData;
|
|
virStorageDriverPtr backend = volume->privateData;
|
|
|
|
virCheckNonNullArgReturn(volume->privateData, NULL);
|
|
|
|
if (esxVI_EnsureSession(priv->primary) < 0)
|
|
return NULL;
|
|
|
|
return backend->storageVolGetXMLDesc(volume, flags);
|
|
}
|
|
|
|
|
|
|
|
static char *
|
|
esxStorageVolGetPath(virStorageVolPtr volume)
|
|
{
|
|
esxPrivate *priv = volume->conn->privateData;
|
|
virStorageDriverPtr backend = volume->privateData;
|
|
|
|
virCheckNonNullArgReturn(volume->privateData, NULL);
|
|
|
|
if (esxVI_EnsureSession(priv->primary) < 0)
|
|
return NULL;
|
|
|
|
return backend->storageVolGetPath(volume);
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
esxStoragePoolIsActive(virStoragePoolPtr pool ATTRIBUTE_UNUSED)
|
|
{
|
|
/* ESX storage pools are always active */
|
|
return 1;
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
esxStoragePoolIsPersistent(virStoragePoolPtr pool ATTRIBUTE_UNUSED)
|
|
{
|
|
/* ESX has no concept of transient pools, so all of them are persistent */
|
|
return 1;
|
|
}
|
|
|
|
|
|
|
|
virStorageDriver esxStorageDriver = {
|
|
.connectNumOfStoragePools = esxConnectNumOfStoragePools, /* 0.8.2 */
|
|
.connectListStoragePools = esxConnectListStoragePools, /* 0.8.2 */
|
|
.connectNumOfDefinedStoragePools = esxConnectNumOfDefinedStoragePools, /* 0.8.2 */
|
|
.connectListDefinedStoragePools = esxConnectListDefinedStoragePools, /* 0.8.2 */
|
|
.storagePoolLookupByName = esxStoragePoolLookupByName, /* 0.8.2 */
|
|
.storagePoolLookupByUUID = esxStoragePoolLookupByUUID, /* 0.8.2 */
|
|
.storagePoolLookupByVolume = esxStoragePoolLookupByVolume, /* 0.8.4 */
|
|
.storagePoolRefresh = esxStoragePoolRefresh, /* 0.8.2 */
|
|
.storagePoolGetInfo = esxStoragePoolGetInfo, /* 0.8.2 */
|
|
.storagePoolGetXMLDesc = esxStoragePoolGetXMLDesc, /* 0.8.2 */
|
|
.storagePoolGetAutostart = esxStoragePoolGetAutostart, /* 0.8.2 */
|
|
.storagePoolSetAutostart = esxStoragePoolSetAutostart, /* 0.8.2 */
|
|
.storagePoolNumOfVolumes = esxStoragePoolNumOfVolumes, /* 0.8.4 */
|
|
.storagePoolListVolumes = esxStoragePoolListVolumes, /* 0.8.4 */
|
|
.storageVolLookupByName = esxStorageVolLookupByName, /* 0.8.4 */
|
|
.storageVolLookupByPath = esxStorageVolLookupByPath, /* 0.8.4 */
|
|
.storageVolLookupByKey = esxStorageVolLookupByKey, /* 0.8.4 */
|
|
.storageVolCreateXML = esxStorageVolCreateXML, /* 0.8.4 */
|
|
.storageVolCreateXMLFrom = esxStorageVolCreateXMLFrom, /* 0.8.7 */
|
|
.storageVolDelete = esxStorageVolDelete, /* 0.8.7 */
|
|
.storageVolWipe = esxStorageVolWipe, /* 0.8.7 */
|
|
.storageVolGetInfo = esxStorageVolGetInfo, /* 0.8.4 */
|
|
.storageVolGetXMLDesc = esxStorageVolGetXMLDesc, /* 0.8.4 */
|
|
.storageVolGetPath = esxStorageVolGetPath, /* 0.8.4 */
|
|
.storagePoolIsActive = esxStoragePoolIsActive, /* 0.8.2 */
|
|
.storagePoolIsPersistent = esxStoragePoolIsPersistent, /* 0.8.2 */
|
|
};
|