This patch provides the general purpose code for the storage driver. The storage_driver.{c,h} file implements the libvirt internal storage driver API. The storage_conf.{c,h} file takes care of parsing and formatting the XML for pools and volumes. This should be reusable by the test driver's impl of the storage APIs in the future. The final storage_backend.{c,h} file provides a 2nd level internal driver API. This allows the main storage driver to call into storage pool specific impls for iSCSI, disk, filesystem, LVM and more. The storage_backend.h definitions allow the specific drivers to declare particular parts of the generic pool XML which are mandatory. For example, a disk pool always requires a source device element. This patch adds AC_SYS_LARGEFILE to turn on large file support inside libvirt globally, allowing us to manage files > 2GB. b/src/storage_backend.c | 80 +++ b/src/storage_backend.h | 121 ++++ b/src/storage_conf.c | 1138 ++++++++++++++++++++++++++++++++++++++++++ b/src/storage_conf.h | 313 +++++++++++ b/src/storage_driver.c | 1169 ++++++++++++++++++++++++++++++++++++++++++++ b/src/storage_driver.h | 45 + configure.in | 2 include/libvirt/virterror.h | 2 src/Makefile.am | 3 src/libvirt.c | 2 src/virterror.c | 12 11 files changed, 2887 insertions(+) diff -r 77cf7f42edd4 configure.in --- a/configure.in Thu Feb 07 12:33:33 2008 -0500 +++ b/configure.in Thu Feb 07 12:59:40 2008 -0500 @@ -689,6 +689,8 @@ AC_SUBST(CYGWIN_EXTRA_PYTHON_LIBADD) AC_SUBST(CYGWIN_EXTRA_PYTHON_LIBADD) AC_SUBST(MINGW_EXTRA_LDFLAGS) +AC_SYS_LARGEFILE + # very annoying rm -f COPYING cp COPYING.LIB COPYING diff -r 77cf7f42edd4 include/libvirt/virterror.h --- a/include/libvirt/virterror.h Thu Feb 07 12:33:33 2008 -0500 +++ b/include/libvirt/virterror.h Thu Feb 07 12:59:40 2008 -0500 @@ -136,6 +136,8 @@ typedef enum { VIR_ERR_INVALID_STORAGE_POOL, /* invalid storage pool object */ VIR_ERR_INVALID_STORAGE_VOL, /* invalid storage vol object */ VIR_WAR_NO_STORAGE, /* failed to start storage */ + VIR_ERR_NO_STORAGE_POOL, /* storage pool not found */ + VIR_ERR_NO_STORAGE_VOL, /* storage pool not found */ } virErrorNumber; /** diff -r 77cf7f42edd4 src/Makefile.am --- a/src/Makefile.am Thu Feb 07 12:33:33 2008 -0500 +++ b/src/Makefile.am Thu Feb 07 12:59:40 2008 -0500 @@ -59,6 +59,9 @@ CLIENT_SOURCES = \ openvz_conf.c openvz_conf.h \ openvz_driver.c openvz_driver.h \ nodeinfo.h nodeinfo.c \ + storage_conf.h storage_conf.c \ + storage_driver.h storage_driver.c \ + storage_backend.h storage_backend.c \ util.c util.h SERVER_SOURCES = \ diff -r 77cf7f42edd4 src/libvirt.c --- a/src/libvirt.c Thu Feb 07 12:33:33 2008 -0500 +++ b/src/libvirt.c Thu Feb 07 12:59:40 2008 -0500 @@ -38,6 +38,7 @@ #include "xen_unified.h" #include "remote_internal.h" #include "qemu_driver.h" +#include "storage_driver.h" #ifdef WITH_OPENVZ #include "openvz_driver.h" #endif @@ -216,6 +217,7 @@ virInitialize(void) #ifdef WITH_OPENVZ if (openvzRegister() == -1) return -1; #endif + if (storageRegister() == -1) return -1; #ifdef WITH_REMOTE if (remoteRegister () == -1) return -1; #endif diff -r 77cf7f42edd4 src/storage_backend.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/storage_backend.c Thu Feb 07 12:59:40 2008 -0500 @@ -0,0 +1,80 @@ +/* + * storage_backend.h: internal storage driver backend contract + * + * Copyright (C) 2007-2008 Red Hat, Inc. + * Copyright (C) 2007-2008 Daniel P. Berrange + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Daniel P. Berrange <berrange@xxxxxxxxxx> + */ + +#include <config.h> + +#include <string.h> +#include <regex.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <unistd.h> + +#include "util.h" + +#include "storage_backend.h" + + +virStorageBackendPtr virStorageBackendForType(int type) { + virStorageReportError(NULL, VIR_ERR_INTERNAL_ERROR, "missing backend for pool type %d", type); + return NULL; +} + +virStorageBackendPoolOptionsPtr virStorageBackendPoolOptionsForType(int type) { + virStorageBackendPtr backend = virStorageBackendForType(type); + if (backend == NULL) + return NULL; + return &backend->poolOptions; +} + +virStorageBackendVolOptionsPtr virStorageBackendVolOptionsForType(int type) { + virStorageBackendPtr backend = virStorageBackendForType(type); + if (backend == NULL) + return NULL; + return &backend->volOptions; +} + + +int virStorageBackendFromString(const char *type) { + virStorageReportError(NULL, VIR_ERR_INTERNAL_ERROR, "unknown storage backend type %s", type); + return -1; +} + +const char *virStorageBackendToString(int type) { + virStorageReportError(NULL, VIR_ERR_INTERNAL_ERROR, "unknown storage backend type %d", type); + return NULL; +} + + +/* + * vim: set tabstop=4: + * vim: set shiftwidth=4: + * vim: set expandtab: + */ +/* + * Local variables: + * indent-tabs-mode: nil + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 4 + * End: + */ diff -r 77cf7f42edd4 src/storage_backend.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/storage_backend.h Thu Feb 07 12:59:40 2008 -0500 @@ -0,0 +1,121 @@ +/* + * storage_backend.h: internal storage driver backend contract + * + * Copyright (C) 2007-2008 Red Hat, Inc. + * Copyright (C) 2007-2008 Daniel P. Berrange + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Daniel P. Berrange <berrange@xxxxxxxxxx> + */ + +#ifndef __VIR_STORAGE_BACKEND_H__ +#define __VIR_STORAGE_BACKEND_H__ + +#include <libvirt/libvirt.h> +#include "storage_conf.h" + + +typedef const char *(*virStorageVolFormatToString)(virConnectPtr conn, + int format); +typedef int (*virStorageVolFormatFromString)(virConnectPtr conn, + const char *format); + +typedef const char *(*virStoragePoolFormatToString)(virConnectPtr conn, + int format); +typedef int (*virStoragePoolFormatFromString)(virConnectPtr conn, + const char *format); + + +typedef struct _virStorageBackendVolOptions virStorageBackendVolOptions; +typedef virStorageBackendVolOptions *virStorageBackendVolOptionsPtr; +struct _virStorageBackendVolOptions { + virStorageVolFormatToString formatToString; + virStorageVolFormatFromString formatFromString; +}; + + +/* Flags to indicate mandatory components in the pool source */ +enum { + VIR_STORAGE_BACKEND_POOL_SOURCE_HOST = (1<<0), + VIR_STORAGE_BACKEND_POOL_SOURCE_DEVICE = (1<<1), + VIR_STORAGE_BACKEND_POOL_SOURCE_DIR = (1<<3), + VIR_STORAGE_BACKEND_POOL_SOURCE_ADAPTER = (1<<4), +}; + +typedef struct _virStorageBackendPoolOptions virStorageBackendPoolOptions; +typedef virStorageBackendPoolOptions *virStorageBackendPoolOptionsPtr; +struct _virStorageBackendPoolOptions { + int flags; + virStoragePoolFormatToString formatToString; + virStoragePoolFormatFromString formatFromString; +}; + +typedef int (*virStorageBackendStartPool)(virConnectPtr conn, virStoragePoolObjPtr pool); +typedef int (*virStorageBackendBuildPool)(virConnectPtr conn, virStoragePoolObjPtr pool, unsigned int flags); +typedef int (*virStorageBackendRefreshPool)(virConnectPtr conn, virStoragePoolObjPtr pool); +typedef int (*virStorageBackendStopPool)(virConnectPtr conn, virStoragePoolObjPtr pool); +typedef int (*virStorageBackendDeletePool)(virConnectPtr conn, virStoragePoolObjPtr pool, unsigned int flags); + +typedef int (*virStorageBackendCreateVol)(virConnectPtr conn, virStoragePoolObjPtr pool, virStorageVolDefPtr vol); +typedef int (*virStorageBackendRefreshVol)(virConnectPtr conn, virStoragePoolObjPtr pool, virStorageVolDefPtr vol); +typedef int (*virStorageBackendDeleteVol)(virConnectPtr conn, virStoragePoolObjPtr pool, virStorageVolDefPtr vol, unsigned int flags); + + +typedef struct _virStorageBackend virStorageBackend; +typedef virStorageBackend *virStorageBackendPtr; + +struct _virStorageBackend { + int type; + + virStorageBackendStartPool startPool; + virStorageBackendBuildPool buildPool; + virStorageBackendRefreshPool refreshPool; + virStorageBackendStopPool stopPool; + virStorageBackendDeletePool deletePool; + + virStorageBackendCreateVol createVol; + virStorageBackendRefreshVol refreshVol; + virStorageBackendDeleteVol deleteVol; + + virStorageBackendPoolOptions poolOptions; + virStorageBackendVolOptions volOptions; + + int volType; +}; + + +virStorageBackendPtr virStorageBackendForType(int type); +virStorageBackendPoolOptionsPtr virStorageBackendPoolOptionsForType(int type); +virStorageBackendVolOptionsPtr virStorageBackendVolOptionsForType(int type); +int virStorageBackendFromString(const char *type); +const char *virStorageBackendToString(int type); + + +#endif /* __VIR_STORAGE_BACKEND_H__ */ + +/* + * vim: set tabstop=4: + * vim: set shiftwidth=4: + * vim: set expandtab: + */ +/* + * Local variables: + * indent-tabs-mode: nil + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 4 + * End: + */ diff -r 77cf7f42edd4 src/storage_conf.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/storage_conf.c Thu Feb 07 12:59:40 2008 -0500 @@ -0,0 +1,1138 @@ +/* + * storage_conf.c: config handling for storage driver + * + * Copyright (C) 2006-2008 Red Hat, Inc. + * Copyright (C) 2006-2008 Daniel P. Berrange + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Daniel P. Berrange <berrange@xxxxxxxxxx> + */ + +#include <config.h> + +#include <libvirt/libvirt.h> +#include <libvirt/virterror.h> + +#include <libxml/parser.h> +#include <libxml/tree.h> +#include <libxml/xpath.h> +#include <libxml/uri.h> + +#include <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <dirent.h> +#include <unistd.h> +#include <errno.h> +#include <stdio.h> +#include <fcntl.h> +#include <string.h> + +#include "storage_conf.h" +#include "storage_backend.h" +#include "xml.h" +#include "uuid.h" +#include "buf.h" +#include "util.h" + +#define virStorageLog(msg...) fprintf(stderr, msg) + +void +virStorageReportError(virConnectPtr conn, int code, const char *fmt, ...) { + va_list args; + char errorMessage[1024]; + + if (fmt) { + va_start(args, fmt); + vsnprintf(errorMessage, sizeof(errorMessage)-1, fmt, args); + va_end(args); + } else { + errorMessage[0] = '\0'; + } + virStorageLog("%s", errorMessage); + __virRaiseError(conn, NULL, NULL, VIR_FROM_STORAGE, code, VIR_ERR_ERROR, + NULL, NULL, NULL, -1, -1, "%s", errorMessage); +} + + + +void virStorageVolDefFree(virStorageVolDefPtr def) { + int i; + free(def->name); + free(def->key); + + for (i = 0 ; i < def->source.nextent ; i++) { + free(def->source.extents[i].path); + } + free(def->source.extents); + + free(def->target.path); + free(def->target.perms.label); + free(def); +} + +void virStoragePoolDefFree(virStoragePoolDefPtr def) { + int i; + + free(def->name); + free(def->source.host.name); + for (i = 0 ; i < def->source.ndevice ; i++) { + free(def->source.devices[i].freeExtents); + free(def->source.devices[i].path); + } + free(def->source.devices); + free(def->source.dir); + + if (def->source.authType == VIR_STORAGE_POOL_AUTH_CHAP) { + free(def->source.auth.chap.login); + free(def->source.auth.chap.passwd); + } + + free(def->target.path); + free(def->target.perms.label); + free(def); +} + + +void virStoragePoolObjFree(virStoragePoolObjPtr obj) { + if (obj->def) + virStoragePoolDefFree(obj->def); + if (obj->newDef) + virStoragePoolDefFree(obj->newDef); + + free(obj->configFile); + free(obj->autostartLink); + free(obj); +} + +void virStoragePoolObjRemove(virStorageDriverStatePtr driver, + virStoragePoolObjPtr pool) +{ + virStoragePoolObjPtr prev = NULL, curr; + + curr = driver->pools; + while (curr != pool) { + prev = curr; + curr = curr->next; + } + + if (curr) { + if (prev) + prev->next = curr->next; + else + driver->pools = curr->next; + + driver->ninactivePools--; + } + + virStoragePoolObjFree(pool); +} + + +static int virStoragePoolDefParseAuthChap(virConnectPtr conn, xmlXPathContextPtr ctxt, virStoragePoolAuthChapPtr auth) { + auth->login = virXPathString("string(/pool/source/auth/@login)", ctxt); + if (auth->login == NULL) { + virStorageReportError(conn, VIR_ERR_XML_ERROR, "missing auth host attribute"); + return -1; + } + + auth->passwd = virXPathString("string(/pool/source/auth/@passwd)", ctxt); + if (auth->passwd == NULL) { + virStorageReportError(conn, VIR_ERR_XML_ERROR, "missing auth passwd attribute"); + return -1; + } + + return 0; +} + + +static int virStoragePoolDefParsePerms(virConnectPtr conn, xmlXPathContextPtr ctxt, virStoragePermsPtr perms) { + char *mode; + long v; + + mode = virXPathString("string(/pool/permissions/mode)", ctxt); + if (!mode) { + perms->mode = 0700; + } else { + char *end; + perms->mode = strtol(mode, &end, 8); + if (end && *end) { + virStorageReportError(conn, VIR_ERR_XML_ERROR, "malformed octal mode"); + return -1; + } + } + + if (virXPathNode("/pool/permissions/owner", ctxt) == NULL) { + perms->uid = getuid(); + } else { + if (virXPathLong("number(/pool/permissions/owner)", ctxt, &v) < 0) { + virStorageReportError(conn, VIR_ERR_XML_ERROR, "malformed owner element"); + return -1; + } + perms->uid = (int)v; + } + + if (virXPathNode("/pool/permissions/group", ctxt) == NULL) { + perms->uid = getgid(); + } else { + if (virXPathLong("number(/pool/permissions/group)", ctxt, &v) < 0) { + virStorageReportError(conn, VIR_ERR_XML_ERROR, "malformed group element"); + return -1; + } + perms->gid = (int)v; + } + + /* NB, we're ignoring missing labels here - they'll simply inherit */ + perms->label = virXPathString("string(/pool/permissions/label)", ctxt); + + return 0; +} + + +static virStoragePoolDefPtr virStoragePoolDefParseDoc(virConnectPtr conn, + xmlXPathContextPtr ctxt, xmlNodePtr root) { + virStorageBackendPoolOptionsPtr options; + virStoragePoolDefPtr ret; + xmlChar *type = NULL; + char *uuid = NULL; + char *authType = NULL; + + if ((ret = calloc(1, sizeof(virStoragePoolDef))) == NULL) + return NULL; + + if (STRNEQ((const char *)root->name, "pool")) { + virStorageReportError(conn, VIR_ERR_XML_ERROR, "unknown root element"); + goto cleanup; + } + + type = xmlGetProp(root, BAD_CAST "type"); + if ((ret->type = virStorageBackendFromString((const char *)type)) < 0) + goto cleanup; + xmlFree(type); + type = NULL; + + if ((options = virStorageBackendPoolOptionsForType(ret->type)) == NULL) { + goto cleanup; + } + + if ((ret->name = virXPathString("string(/pool/name)", ctxt)) == NULL) { + virStorageReportError(conn, VIR_ERR_XML_ERROR, "missing name element"); + goto cleanup; + } + + uuid = virXPathString("string(/pool/uuid)", ctxt); + if (uuid == NULL) { + if (virUUIDGenerate(ret->uuid) < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "unable to generate uuid"); + goto cleanup; + } + } else { + if (virUUIDParse(uuid, ret->uuid) < 0) { + virStorageReportError(conn, VIR_ERR_XML_ERROR, "malformed uuid element"); + goto cleanup; + } + free(uuid); + uuid = NULL; + } + + if (options->formatFromString) { + char *format = virXPathString("string(/pool/source/format/@type)", ctxt); + if ((ret->source.format = (options->formatFromString)(conn, format)) < 0) { + free(format); + goto cleanup; + } + free(format); + } + + if (options->flags & VIR_STORAGE_BACKEND_POOL_SOURCE_HOST) { + if ((ret->source.host.name = virXPathString("string(/pool/source/host/@name)", ctxt)) == NULL) { + virStorageReportError(conn, VIR_ERR_XML_ERROR, "missing source host name"); + goto cleanup; + } + } + if (options->flags & VIR_STORAGE_BACKEND_POOL_SOURCE_DEVICE) { + xmlNodePtr *nodeset = NULL; + int nsource, i; + + if ((nsource = virXPathNodeSet("/pool/source/device", ctxt, &nodeset)) <= 0) { + virStorageReportError(conn, VIR_ERR_XML_ERROR, "cannot extract source devices"); + goto cleanup; + } + if ((ret->source.devices = calloc(nsource, sizeof(*ret->source.devices))) == NULL) { + free(nodeset); + virStorageReportError(conn, VIR_ERR_NO_MEMORY, "device"); + goto cleanup; + } + for (i = 0 ; i < nsource ; i++) { + xmlChar *path = xmlGetProp(nodeset[i], BAD_CAST "path"); + if (path == NULL) { + virStorageReportError(conn, VIR_ERR_XML_ERROR, "missing source device path"); + goto cleanup; + } + ret->source.devices[i].path = (char *)path; + } + free(nodeset); + ret->source.ndevice = nsource; + } + if (options->flags & VIR_STORAGE_BACKEND_POOL_SOURCE_DIR) { + if ((ret->source.dir = virXPathString("string(/pool/source/dir/@path)", ctxt)) == NULL) { + virStorageReportError(conn, VIR_ERR_XML_ERROR, "missing source path"); + goto cleanup; + } + } + + + authType = virXPathString("string(/pool/source/auth/@type)", ctxt); + if (authType == NULL) { + ret->source.authType = VIR_STORAGE_POOL_AUTH_NONE; + } else { + if (STREQ(authType, "chap")) { + ret->source.authType = VIR_STORAGE_POOL_AUTH_CHAP; + } else { + virStorageReportError(conn, VIR_ERR_XML_ERROR, "unknown auth type '%s'", (const char *)authType); + goto cleanup; + } + free(authType); + authType = NULL; + } + + if (ret->source.authType == VIR_STORAGE_POOL_AUTH_CHAP) { + if (virStoragePoolDefParseAuthChap(conn, ctxt, &ret->source.auth.chap) < 0) + goto cleanup; + } + + if ((ret->target.path = virXPathString("string(/pool/target/path)", ctxt)) == NULL) { + virStorageReportError(conn, VIR_ERR_XML_ERROR, "missing target path"); + goto cleanup; + } + + if (virStoragePoolDefParsePerms(conn, ctxt, &ret->target.perms) < 0) + goto cleanup; + + return ret; + + cleanup: + free(uuid); + if (type) + xmlFree(type); + if (authType) + xmlFree(authType); + virStoragePoolDefFree(ret); + return NULL; +} + +virStoragePoolDefPtr virStoragePoolDefParse(virConnectPtr conn, + const char *xmlStr, const char *filename) { + virStoragePoolDefPtr ret = NULL; + xmlDocPtr xml = NULL; + xmlNodePtr node = NULL; + xmlXPathContextPtr ctxt = NULL; + + if (!(xml = xmlReadDoc(BAD_CAST xmlStr, filename ? filename : "storage.xml", NULL, + XML_PARSE_NOENT | XML_PARSE_NONET | + XML_PARSE_NOERROR | XML_PARSE_NOWARNING))) { + virStorageReportError(conn, VIR_ERR_XML_ERROR, "malformed xml document"); + goto cleanup; + } + + ctxt = xmlXPathNewContext(xml); + if (ctxt == NULL) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, "xmlXPathContext"); + goto cleanup; + } + + node = xmlDocGetRootElement(xml); + if (node == NULL) { + virStorageReportError(conn, VIR_ERR_XML_ERROR, "missing root element"); + goto cleanup; + } + + ret = virStoragePoolDefParseDoc(conn, ctxt, node); + + xmlXPathFreeContext(ctxt); + xmlFreeDoc(xml); + + return ret; + + cleanup: + xmlXPathFreeContext(ctxt); + if (xml) + xmlFreeDoc(xml); + return NULL; +} + + +char *virStoragePoolDefFormat(virConnectPtr conn, + virStoragePoolDefPtr def) { + virStorageBackendPoolOptionsPtr options; + virBufferPtr buf; + const char *type; + char uuid[VIR_UUID_STRING_BUFLEN]; + int i; + + options = virStorageBackendPoolOptionsForType(def->type); + if (options == NULL) + return NULL; + + if ((buf = virBufferNew(8192)) == NULL) + goto no_memory; + + type = virStorageBackendToString(def->type); + if (!type) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "unexpected pool type"); + goto cleanup; + } + if (virBufferVSprintf(buf, "<pool type='%s'>\n", type) < 0) + goto no_memory; + + if (virBufferVSprintf(buf," <name>%s</name>\n", def->name) < 0) + goto no_memory; + + virUUIDFormat(def->uuid, uuid); + if (virBufferVSprintf(buf," <uuid>%s</uuid>\n", uuid) < 0) + goto no_memory; + + if (virBufferVSprintf(buf," <capacity>%llu</capacity>\n", def->capacity) < 0) + goto no_memory; + if (virBufferVSprintf(buf," <allocation>%llu</allocation>\n", def->allocation) < 0) + goto no_memory; + if (virBufferVSprintf(buf," <available>%llu</available>\n", def->available) < 0) + goto no_memory; + + + if (virBufferAddLit(buf," <source>\n") < 0) + goto no_memory; + if ((options->flags & VIR_STORAGE_BACKEND_POOL_SOURCE_HOST) && + def->source.host.name && + virBufferVSprintf(buf," <host name='%s'/>\n", def->source.host.name) < 0) + goto no_memory; + if ((options->flags & VIR_STORAGE_BACKEND_POOL_SOURCE_DEVICE) && + def->source.ndevice) { + for (i = 0 ; i < def->source.ndevice ; i++) { + if (virBufferVSprintf(buf," <device path='%s'>\n", def->source.devices[i].path) < 0) + goto no_memory; + if (def->source.devices[i].nfreeExtent) { + int j; + for (j = 0 ; j < def->source.devices[i].nfreeExtent ; j++) { + if (virBufferVSprintf(buf, " <freeExtent start='%llu' end='%llu'/>\n", + def->source.devices[i].freeExtents[j].start, + def->source.devices[i].freeExtents[j].end) < 0) + goto no_memory; + } + } + if (virBufferAddLit(buf," </device>\n") < 0) + goto no_memory; + } + } + if ((options->flags & VIR_STORAGE_BACKEND_POOL_SOURCE_DIR) && + def->source.dir && + virBufferVSprintf(buf," <directory path='%s'/>\n", def->source.dir) < 0) + goto no_memory; + if ((options->flags & VIR_STORAGE_BACKEND_POOL_SOURCE_ADAPTER) && + def->source.adapter && + virBufferVSprintf(buf," <adapter name='%s'/>\n", def->source.adapter) < 0) + goto no_memory; + + if (options->formatToString) { + const char *format = (options->formatToString)(conn, def->source.format); + if (!format) + goto cleanup; + if (virBufferVSprintf(buf," <format type='%s'/>\n", format) < 0) + goto no_memory; + } + + + if (def->source.authType == VIR_STORAGE_POOL_AUTH_CHAP && + virBufferVSprintf(buf," <auth type='chap' login='%s' passwd='%s'>\n", + def->source.auth.chap.login, + def->source.auth.chap.passwd) < 0) + goto no_memory; + if (virBufferAddLit(buf," </source>\n") < 0) + goto no_memory; + + + + if (virBufferAddLit(buf," <target>\n") < 0) + goto no_memory; + + if (def->target.path && + virBufferVSprintf(buf," <path>%s</path>\n", def->target.path) < 0) + goto no_memory; + + if (virBufferAddLit(buf," <permissions>\n") < 0) + goto no_memory; + if (virBufferVSprintf(buf," <mode>0%o</mode>\n", def->target.perms.mode) < 0) + goto no_memory; + if (virBufferVSprintf(buf," <owner>%d</owner>\n", def->target.perms.uid) < 0) + goto no_memory; + if (virBufferVSprintf(buf," <group>%d</group>\n", def->target.perms.gid) < 0) + goto no_memory; + + if (def->target.perms.label) { + if (virBufferVSprintf(buf," <label>%s</label>\n", def->target.perms.label) < 0) + goto no_memory; + } + if (virBufferAddLit(buf," </permissions>\n") < 0) + goto no_memory; + if (virBufferAddLit(buf," </target>\n") < 0) + goto no_memory; + + if (virBufferAddLit(buf,"</pool>\n") < 0) + goto no_memory; + + return virBufferContentAndFree(buf); + + no_memory: + virStorageReportError(conn, VIR_ERR_NO_MEMORY, "xml"); + cleanup: + if (buf) virBufferFree(buf); + return NULL; +} + + +static int virStorageVolDefParsePerms(virConnectPtr conn, xmlXPathContextPtr ctxt, virStoragePermsPtr perms) { + char *mode; + long v; + + mode = virXPathString("string(/volume/permissions/mode)", ctxt); + if (!mode) { + perms->mode = 0600; + } else { + char *end; + perms->mode = strtol(mode, &end, 8); + if (end && *end) { + virStorageReportError(conn, VIR_ERR_XML_ERROR, "malformed octal mode"); + return -1; + } + } + + if (virXPathNode("/volume/permissions/owner", ctxt) == NULL) { + perms->uid = getuid(); + } else { + if (virXPathLong("number(/volume/permissions/owner)", ctxt, &v) < 0) { + virStorageReportError(conn, VIR_ERR_XML_ERROR, "missing owner element"); + return -1; + } + perms->uid = (int)v; + } + if (virXPathNode("/volume/permissions/group", ctxt) == NULL) { + perms->gid = getgid(); + } else { + if (virXPathLong("number(/volume/permissions/group)", ctxt, &v) < 0) { + virStorageReportError(conn, VIR_ERR_XML_ERROR, "missing owner element"); + return -1; + } + perms->gid = (int)v; + } + + /* NB, we're ignoring missing labels here - they'll simply inherit */ + perms->label = virXPathString("string(/volume/permissions/label)", ctxt); + + return 0; +} + + +static int virStorageSize(virConnectPtr conn, + const char *unit, + const char *val, + unsigned long long *ret) { + unsigned long long mult; + char *end; + + if (!unit) { + mult = 1; + } else { + switch (unit[0]) { + case 'k': + case 'K': + mult = 1024ull; + break; + + case 'm': + case 'M': + mult = 1024ull * 1024ull; + break; + + case 'g': + case 'G': + mult = 1024ull * 1024ull * 1024ull; + break; + + case 't': + case 'T': + mult = 1024ull * 1024ull * 1024ull * 1024ull; + break; + + default: + virStorageReportError(conn, VIR_ERR_XML_ERROR, "Unknown size units %s", unit); + return -1; + } + } + + *ret = strtoull(val, &end, 10); + if (end && *end) { + virStorageReportError(conn, VIR_ERR_XML_ERROR, "malformed capacity element"); + return -1; + } + *ret *= mult; + + return 0; +} + +static virStorageVolDefPtr virStorageVolDefParseDoc(virConnectPtr conn, + virStoragePoolDefPtr pool, + xmlXPathContextPtr ctxt, xmlNodePtr root) { + virStorageVolDefPtr ret; + virStorageBackendVolOptionsPtr options; + char *allocation = NULL; + char *capacity = NULL; + char *unit = NULL; + + options = virStorageBackendVolOptionsForType(pool->type); + if (options == NULL) + return NULL; + + if ((ret = calloc(1, sizeof(virStorageVolDef))) == NULL) + return NULL; + + if (STRNEQ((const char *)root->name, "volume")) { + virStorageReportError(conn, VIR_ERR_XML_ERROR, "unknown root element"); + goto cleanup; + } + + ret->name = virXPathString("string(/volume/name)", ctxt); + if (ret->name == NULL) { + virStorageReportError(conn, VIR_ERR_XML_ERROR, "missing name element"); + goto cleanup; + } + + /* Auto-generated so delibrately ignore */ + /*ret->key = virXPathString("string(/volume/key)", ctxt);*/ + + capacity = virXPathString("string(/volume/capacity)", ctxt); + unit = virXPathString("string(/volume/capacity/@unit)", ctxt); + if (capacity == NULL) { + virStorageReportError(conn, VIR_ERR_XML_ERROR, "missing capacity element"); + goto cleanup; + } + if (virStorageSize(conn, unit, capacity, &ret->capacity) < 0) + goto cleanup; + free(capacity); + capacity = NULL; + free(unit); + unit = NULL; + + allocation = virXPathString("string(/volume/allocation)", ctxt); + if (allocation) { + unit = virXPathString("string(/volume/allocation/@unit)", ctxt); + if (virStorageSize(conn, unit, allocation, &ret->allocation) < 0) + goto cleanup; + free(allocation); + allocation = NULL; + free(unit); + unit = NULL; + } else { + ret->allocation = ret->capacity; + } + + ret->target.path = virXPathString("string(/volume/target/path)", ctxt); + if (options->formatFromString) { + char *format = virXPathString("string(/volume/target/format/@type)", ctxt); + if ((ret->target.format = (options->formatFromString)(conn, format)) < 0) { + free(format); + goto cleanup; + } + free(format); + } + + if (virStorageVolDefParsePerms(conn, ctxt, &ret->target.perms) < 0) + goto cleanup; + + return ret; + + cleanup: + free(allocation); + free(capacity); + free(unit); + virStorageVolDefFree(ret); + return NULL; +} + + +virStorageVolDefPtr virStorageVolDefParse(virConnectPtr conn, + virStoragePoolDefPtr pool, + const char *xmlStr, const char *filename) { + virStorageVolDefPtr ret = NULL; + xmlDocPtr xml = NULL; + xmlNodePtr node = NULL; + xmlXPathContextPtr ctxt = NULL; + + if (!(xml = xmlReadDoc(BAD_CAST xmlStr, filename ? filename : "storage.xml", NULL, + XML_PARSE_NOENT | XML_PARSE_NONET | + XML_PARSE_NOERROR | XML_PARSE_NOWARNING))) { + virStorageReportError(conn, VIR_ERR_XML_ERROR, "malformed xml document"); + goto cleanup; + } + + ctxt = xmlXPathNewContext(xml); + if (ctxt == NULL) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, "xmlXPathContext"); + goto cleanup; + } + + node = xmlDocGetRootElement(xml); + if (node == NULL) { + virStorageReportError(conn, VIR_ERR_XML_ERROR, "missing root element"); + goto cleanup; + } + + ret = virStorageVolDefParseDoc(conn, pool, ctxt, node); + + xmlXPathFreeContext(ctxt); + xmlFreeDoc(xml); + + return ret; + + cleanup: + xmlXPathFreeContext(ctxt); + if (xml) + xmlFreeDoc(xml); + return NULL; +} + + + +char *virStorageVolDefFormat(virConnectPtr conn, + virStoragePoolDefPtr pool, + virStorageVolDefPtr def) { + virStorageBackendVolOptionsPtr options; + virBufferPtr buf = virBufferNew(8192); + + options = virStorageBackendVolOptionsForType(pool->type); + if (options == NULL) + return NULL; + + if (!buf) + goto no_memory; + + if (virBufferAddLit(buf, "<volume>\n") < 0) + goto no_memory; + + if (virBufferVSprintf(buf," <name>%s</name>\n", def->name) < 0) + goto no_memory; + + if (virBufferVSprintf(buf," <key>%s</key>\n", def->key) < 0) + goto no_memory; + + if (virBufferAddLit(buf, " <source>\n") < 0) + goto no_memory; + if (def->source.nextent) { + int i; + const char *thispath = NULL; + for (i = 0 ; i < def->source.nextent ; i++) { + if (thispath == NULL || + STRNEQ(thispath, def->source.extents[i].path)) { + if (thispath != NULL) + if (virBufferAddLit(buf, " </device>\n") < 0) + goto no_memory; + if (virBufferVSprintf(buf, " <device path='%s'>\n", def->source.extents[i].path) < 0) + goto no_memory; + } + + if (virBufferVSprintf(buf, " <extent start='%llu' end='%llu'/>\n", + def->source.extents[i].start, + def->source.extents[i].end) < 0) + goto no_memory; + thispath = def->source.extents[i].path; + } + if (thispath != NULL) + if (virBufferAddLit(buf, " </device>\n") < 0) + goto no_memory; + } + if (virBufferAddLit(buf, " </source>\n") < 0) + goto no_memory; + + if (virBufferVSprintf(buf," <capacity>%llu</capacity>\n", def->capacity) < 0) + goto no_memory; + if (virBufferVSprintf(buf," <allocation>%llu</allocation>\n", def->allocation) < 0) + goto no_memory; + + if (virBufferAddLit(buf, " <target>\n") < 0) + goto no_memory; + + if (def->target.path && + virBufferVSprintf(buf," <path>%s</path>\n", def->target.path) < 0) + goto no_memory; + + if (options->formatToString) { + const char *format = (options->formatToString)(conn, def->target.format); + if (!format) + goto cleanup; + if (virBufferVSprintf(buf," <format type='%s'/>\n", format) < 0) + goto no_memory; + } + + if (virBufferAddLit(buf," <permissions>\n") < 0) + goto no_memory; + if (virBufferVSprintf(buf," <mode>0%o</mode>\n", def->target.perms.mode) < 0) + goto no_memory; + if (virBufferVSprintf(buf," <owner>%d</owner>\n", def->target.perms.uid) < 0) + goto no_memory; + if (virBufferVSprintf(buf," <group>%d</group>\n", def->target.perms.gid) < 0) + goto no_memory; + + if (def->target.perms.label && + virBufferVSprintf(buf," <label>%s</label>\n", def->target.perms.label) < 0) + goto no_memory; + if (virBufferAddLit(buf," </permissions>\n") < 0) + goto no_memory; + + if (virBufferAddLit(buf, " </target>\n") < 0) + goto no_memory; + + if (virBufferAddLit(buf,"</volume>\n") < 0) + goto no_memory; + + return virBufferContentAndFree(buf); + + no_memory: + virStorageReportError(conn, VIR_ERR_NO_MEMORY, "xml"); + cleanup: + if (buf) virBufferFree(buf); + return NULL; +} + + +virStoragePoolObjPtr virStoragePoolObjFindByUUID(virStorageDriverStatePtr driver, + const unsigned char *uuid) { + virStoragePoolObjPtr pool = driver->pools; + + while (pool) { + if (!memcmp(pool->def->uuid, uuid, VIR_UUID_BUFLEN)) + return pool; + pool = pool->next; + } + + return NULL; +} + +virStoragePoolObjPtr virStoragePoolObjFindByName(virStorageDriverStatePtr driver, + const char *name) { + virStoragePoolObjPtr pool = driver->pools; + + while (pool) { + if (STREQ(pool->def->name, name)) + return pool; + pool = pool->next; + } + + return NULL; +} + +void virStoragePoolObjClearVols(virStoragePoolObjPtr pool) +{ + virStorageVolDefPtr vol = pool->volumes; + while (vol) { + virStorageVolDefPtr next = vol->next; + virStorageVolDefFree(vol); + vol = next; + } + pool->volumes = NULL; + pool->nvolumes = 0; +} + + + +virStorageVolDefPtr virStorageVolDefFindByKey(virStoragePoolObjPtr pool, + const char *key) { + virStorageVolDefPtr vol = pool->volumes; + + while (vol) { + if (STREQ(vol->key, key)) + return vol; + vol = vol->next; + } + + return NULL; +} +virStorageVolDefPtr virStorageVolDefFindByPath(virStoragePoolObjPtr pool, + const char *path) { + virStorageVolDefPtr vol = pool->volumes; + + while (vol) { + if (STREQ(vol->target.path, path)) + return vol; + vol = vol->next; + } + + return NULL; +} + +virStorageVolDefPtr virStorageVolDefFindByName(virStoragePoolObjPtr pool, + const char *name) { + virStorageVolDefPtr vol = pool->volumes; + + while (vol) { + if (STREQ(vol->name, name)) + return vol; + vol = vol->next; + } + + return NULL; +} + +virStoragePoolObjPtr virStoragePoolObjAssignDef(virConnectPtr conn, + virStorageDriverStatePtr driver, + virStoragePoolDefPtr def) { + virStoragePoolObjPtr pool; + + if ((pool = virStoragePoolObjFindByName(driver, def->name))) { + if (!virStoragePoolObjIsActive(pool)) { + virStoragePoolDefFree(pool->def); + pool->def = def; + } else { + if (pool->newDef) + virStoragePoolDefFree(pool->newDef); + pool->newDef = def; + } + return pool; + } + + if (!(pool = calloc(1, sizeof(virStoragePoolObj)))) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, "pool"); + return NULL; + } + + pool->active = 0; + pool->def = def; + pool->next = driver->pools; + + driver->pools = pool; + driver->ninactivePools++; + + return pool; +} + +static virStoragePoolObjPtr +virStoragePoolObjLoad(virStorageDriverStatePtr driver, + const char *file, + const char *path, + const char *xml, + const char *autostartLink) { + virStoragePoolDefPtr def; + virStoragePoolObjPtr pool; + + if (!(def = virStoragePoolDefParse(NULL, xml, file))) { + virErrorPtr err = virGetLastError(); + virStorageLog("Error parsing storage pool config '%s' : %s", + path, err->message); + return NULL; + } + + if (!virFileMatchesNameSuffix(file, def->name, ".xml")) { + virStorageLog("Storage Pool config filename '%s' does not match pool name '%s'", + path, def->name); + virStoragePoolDefFree(def); + return NULL; + } + + if (!(pool = virStoragePoolObjAssignDef(NULL, driver, def))) { + virStorageLog("Failed to load storage pool config '%s': out of memory", path); + virStoragePoolDefFree(def); + return NULL; + } + + pool->configFile = strdup(path); + if (pool->configFile == NULL) { + virStorageLog("Failed to load storage pool config '%s': out of memory", path); + virStoragePoolDefFree(def); + return NULL; + } + pool->autostartLink = strdup(autostartLink); + if (pool->autostartLink == NULL) { + virStorageLog("Failed to load storage pool config '%s': out of memory", path); + virStoragePoolDefFree(def); + return NULL; + } + + pool->autostart = virFileLinkPointsTo(pool->autostartLink, pool->configFile); + + return pool; +} + + +int virStoragePoolObjScanConfigs(virStorageDriverStatePtr driver) { + DIR *dir; + struct dirent *entry; + + if (!(dir = opendir(driver->configDir))) { + if (errno == ENOENT) + return 0; + virStorageLog("Failed to open dir '%s': %s", + driver->configDir, strerror(errno)); + return -1; + } + + while ((entry = readdir(dir))) { + char *xml = NULL; + char path[PATH_MAX]; + char autostartLink[PATH_MAX]; + + if (entry->d_name[0] == '.') + continue; + + if (!virFileHasSuffix(entry->d_name, ".xml")) + continue; + + if (virFileBuildPath(driver->configDir, entry->d_name, NULL, path, PATH_MAX) < 0) { + virStorageLog("Config filename '%s/%s' is too long", + driver->configDir, entry->d_name); + continue; + } + + if (virFileBuildPath(driver->autostartDir, entry->d_name, NULL, autostartLink, PATH_MAX) < 0) { + virStorageLog("Autostart link path '%s/%s' is too long", + driver->autostartDir, entry->d_name); + continue; + } + + if (virFileReadAll(path, 8192, &xml) < 0) + continue; + + virStoragePoolObjLoad(driver, entry->d_name, path, xml, autostartLink); + + free(xml); + } + + closedir(dir); + + return 0; +} + +int virStoragePoolObjSaveDef(virConnectPtr conn, + virStorageDriverStatePtr driver, + virStoragePoolObjPtr pool, + virStoragePoolDefPtr def) { + char *xml; + int fd = -1, ret = -1; + int towrite; + + if (!pool->configFile) { + int err; + char path[PATH_MAX]; + + if ((err = virFileMakePath(driver->configDir))) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + "cannot create config directory %s: %s", + driver->configDir, strerror(err)); + return -1; + } + + if (virFileBuildPath(driver->configDir, def->name, ".xml", + path, sizeof(path)) < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + "cannot construct config file path"); + return -1; + } + if (!(pool->configFile = strdup(path))) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, "configFile"); + return -1; + } + + if (virFileBuildPath(driver->autostartDir, def->name, ".xml", + path, sizeof(path)) < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + "cannot construct autostart link path"); + free(pool->configFile); + pool->configFile = NULL; + return -1; + } + if (!(pool->autostartLink = strdup(path))) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, "configFile"); + free(pool->configFile); + pool->configFile = NULL; + return -1; + } + } + + if (!(xml = virStoragePoolDefFormat(conn, def))) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + "failed to generate XML"); + return -1; + } + + if ((fd = open(pool->configFile, + O_WRONLY | O_CREAT | O_TRUNC, + S_IRUSR | S_IWUSR )) < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + "cannot create config file %s: %s", + pool->configFile, strerror(errno)); + goto cleanup; + } + + towrite = strlen(xml); + if (write(fd, xml, towrite) != towrite) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + "cannot write config file %s: %s", + pool->configFile, strerror(errno)); + goto cleanup; + } + + if (close(fd) < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + "cannot save config file %s: %s", + pool->configFile, strerror(errno)); + goto cleanup; + } + + ret = 0; + + cleanup: + if (fd != -1) + close(fd); + + free(xml); + + return ret; +} + +int virStoragePoolObjDeleteDef(virConnectPtr conn, virStoragePoolObjPtr pool) { + if (!pool->configFile) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "no config file for %s", pool->def->name); + return -1; + } + + if (unlink(pool->configFile) < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "cannot remove config for %s", pool->def->name); + return -1; + } + + return 0; +} + +/* + * vim: set tabstop=4: + * vim: set shiftwidth=4: + * vim: set expandtab: + */ +/* + * Local variables: + * indent-tabs-mode: nil + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 4 + * End: + */ diff -r 77cf7f42edd4 src/storage_conf.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/storage_conf.h Thu Feb 07 12:59:40 2008 -0500 @@ -0,0 +1,313 @@ +/* + * storage_conf.h: config handling for storage driver + * + * Copyright (C) 2006-2008 Red Hat, Inc. + * Copyright (C) 2006-2008 Daniel P. Berrange + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Daniel P. Berrange <berrange@xxxxxxxxxx> + */ + +#ifndef __VIR_STORAGE_CONF_H__ +#define __VIR_STORAGE_CONF_H__ + +#include <libvirt/libvirt.h> +#include "internal.h" + +/* Shared structs */ + + + +typedef struct _virStoragePerms virStoragePerms; +typedef virStoragePerms *virStoragePermsPtr; +struct _virStoragePerms { + int mode; + int uid; + int gid; + char *label; +}; + +/* Storage volumes */ + + +/* + * How the volume's data is stored on underlying + * physical devices - can potentially span many + * devices in LVM case. + */ +typedef struct _virStorageVolSourceExtent virStorageVolSourceExtent; +typedef virStorageVolSourceExtent *virStorageVolSourceExtentPtr; +struct _virStorageVolSourceExtent { + char *path; + unsigned long long start; + unsigned long long end; +}; + +typedef struct _virStorageVolSource virStorageVolSource; +typedef virStorageVolSource *virStorageVolSourcePtr; +struct _virStorageVolSource { + int nextent; + virStorageVolSourceExtentPtr extents; +}; + + +/* + * How the volume appears on the host + */ +typedef struct _virStorageVolTarget virStorageVolTarget; +typedef virStorageVolTarget *virStorageVolTargetPtr; +struct _virStorageVolTarget { + char *path; + int format; + virStoragePerms perms; +}; + + +typedef struct _virStorageVolDef virStorageVolDef; +typedef virStorageVolDef *virStorageVolDefPtr; +struct _virStorageVolDef { + char *name; + char *key; + + unsigned long long allocation; + unsigned long long capacity; + + virStorageVolSource source; + virStorageVolTarget target; + + virStorageVolDefPtr next; +}; + + + + +/* Storage pools */ + +enum virStoragePoolType { + VIR_STORAGE_POOL_DIR = 1, /* Local directory */ + VIR_STORAGE_POOL_FS, /* Local filesystem */ + VIR_STORAGE_POOL_NETFS, /* Networked filesystem - eg NFS, GFS, etc */ + VIR_STORAGE_POOL_LOGICAL, /* Logical volume groups / volumes */ + VIR_STORAGE_POOL_DISK, /* Disk partitions */ + VIR_STORAGE_POOL_ISCSI, /* iSCSI targets */ + VIR_STORAGE_POOL_SCSI, /* SCSI HBA */ +}; + + +enum virStoragePoolAuthType { + VIR_STORAGE_POOL_AUTH_NONE, + VIR_STORAGE_POOL_AUTH_CHAP, +}; + +typedef struct _virStoragePoolAuthChap virStoragePoolAuthChap; +typedef virStoragePoolAuthChap *virStoragePoolAuthChapPtr; +struct _virStoragePoolAuthChap { + char *login; + char *passwd; +}; + + +/* + * For remote pools, info on how to reach the host + */ +typedef struct _virStoragePoolSourceHost virStoragePoolSourceHost; +typedef virStoragePoolSourceHost *virStoragePoolSourceHostPtr; +struct _virStoragePoolSourceHost { + char *name; + int port; + int protocol; +}; + + +/* + * Available extents on the underlying storage + */ +typedef struct _virStoragePoolSourceDeviceExtent virStoragePoolSourceDeviceExtent; +typedef virStoragePoolSourceDeviceExtent *virStoragePoolSourceDeviceExtentPtr; +struct _virStoragePoolSourceDeviceExtent { + unsigned long long start; + unsigned long long end; +}; + + +/* + * Pools can be backed by one or more devices, and some + * allow us to track free space on underlying devices. + */ +typedef struct _virStoragePoolSourceDevice virStoragePoolSourceDevice; +typedef virStoragePoolSourceDevice *virStoragePoolSourceDevicePtr; +struct _virStoragePoolSourceDevice { + int nfreeExtent; + virStoragePoolSourceDeviceExtentPtr freeExtents; + char *path; + int format; /* Pool specific source format */ +}; + + + +typedef struct _virStoragePoolSource virStoragePoolSource; +typedef virStoragePoolSource *virStoragePoolSourcePtr; +struct _virStoragePoolSource { + /* An optional host */ + virStoragePoolSourceHost host; + + /* And either one or more devices ... */ + int ndevice; + virStoragePoolSourceDevicePtr devices; + + /* Or a directory */ + char *dir; + + /* Or an adapter */ + char *adapter; + + int authType; /* virStoragePoolAuthType */ + union { + virStoragePoolAuthChap chap; + } auth; + + int format; /* Pool type specific format such as filesystem type, or lvm version, etc */ +}; + + +typedef struct _virStoragePoolTarget virStoragePoolTarget; +typedef virStoragePoolTarget *virStoragePoolTargetPtr; +struct _virStoragePoolTarget { + char *path; /* Optional local filesystem mapping */ + virStoragePerms perms; /* Default permissions for volumes */ +}; + + +typedef struct _virStoragePoolDef virStoragePoolDef; +typedef virStoragePoolDef *virStoragePoolDefPtr; +struct _virStoragePoolDef { + /* General metadata */ + char *name; + unsigned char uuid[VIR_UUID_BUFLEN]; + int type; /* virStoragePoolType */ + + unsigned long long allocation; + unsigned long long capacity; + unsigned long long available; + + virStoragePoolSource source; + virStoragePoolTarget target; +}; + +typedef struct _virStoragePoolObj virStoragePoolObj; +typedef virStoragePoolObj *virStoragePoolObjPtr; + +struct _virStoragePoolObj { + char *configFile; + char *autostartLink; + int active; + int autostart; + + virStoragePoolDefPtr def; + virStoragePoolDefPtr newDef; + + int nvolumes; + virStorageVolDefPtr volumes; + + virStoragePoolObjPtr next; +}; + + + + +typedef struct _virStorageDriverState virStorageDriverState; +typedef virStorageDriverState *virStorageDriverStatePtr; + +struct _virStorageDriverState { + int nactivePools; + int ninactivePools; + virStoragePoolObjPtr pools; + char *configDir; + char *autostartDir; +}; + + +static inline int virStoragePoolObjIsActive(virStoragePoolObjPtr pool) { + return pool->active; +} + +void virStorageReportError(virConnectPtr conn, + int code, + const char *fmt, ...); + +int virStoragePoolObjScanConfigs(virStorageDriverStatePtr driver); + +virStoragePoolObjPtr virStoragePoolObjFindByUUID(virStorageDriverStatePtr driver, + const unsigned char *uuid); +virStoragePoolObjPtr virStoragePoolObjFindByName(virStorageDriverStatePtr driver, + const char *name); + +virStorageVolDefPtr virStorageVolDefFindByKey(virStoragePoolObjPtr pool, + const char *key); +virStorageVolDefPtr virStorageVolDefFindByPath(virStoragePoolObjPtr pool, + const char *path); +virStorageVolDefPtr virStorageVolDefFindByName(virStoragePoolObjPtr pool, + const char *name); + +void virStoragePoolObjClearVols(virStoragePoolObjPtr pool); + +virStoragePoolDefPtr virStoragePoolDefParse(virConnectPtr conn, + const char *xml, + const char *filename); +char *virStoragePoolDefFormat(virConnectPtr conn, + virStoragePoolDefPtr def); + +virStorageVolDefPtr virStorageVolDefParse(virConnectPtr conn, + virStoragePoolDefPtr pool, + const char *xml, + const char *filename); +char *virStorageVolDefFormat(virConnectPtr conn, + virStoragePoolDefPtr pool, + virStorageVolDefPtr def); + +virStoragePoolObjPtr virStoragePoolObjAssignDef(virConnectPtr conn, + virStorageDriverStatePtr driver, + virStoragePoolDefPtr def); + +int virStoragePoolObjSaveDef(virConnectPtr conn, + virStorageDriverStatePtr driver, + virStoragePoolObjPtr pool, + virStoragePoolDefPtr def); +int virStoragePoolObjDeleteDef(virConnectPtr conn, + virStoragePoolObjPtr pool); + +void virStorageVolDefFree(virStorageVolDefPtr def); +void virStoragePoolDefFree(virStoragePoolDefPtr def); +void virStoragePoolObjFree(virStoragePoolObjPtr pool); +void virStoragePoolObjRemove(virStorageDriverStatePtr driver, + virStoragePoolObjPtr pool); + +#endif /* __VIR_STORAGE_DRIVER_H__ */ + + +/* + * vim: set tabstop=4: + * vim: set shiftwidth=4: + * vim: set expandtab: + */ +/* + * Local variables: + * indent-tabs-mode: nil + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 4 + * End: + */ diff -r 77cf7f42edd4 src/storage_driver.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/storage_driver.c Thu Feb 07 12:59:40 2008 -0500 @@ -0,0 +1,1169 @@ +/* + * storage_driver.c: core driver for storage APIs + * + * Copyright (C) 2006-2008 Red Hat, Inc. + * Copyright (C) 2006-2008 Daniel P. Berrange + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Daniel P. Berrange <berrange@xxxxxxxxxx> + */ + +#include <config.h> + +#include <stdio.h> +#include <unistd.h> +#include <sys/types.h> +#include <pwd.h> +#include <errno.h> +#include <string.h> + +#include "driver.h" +#include "util.h" +#include "storage_driver.h" +#include "storage_conf.h" + +#include "storage_backend.h" + +#define storageLog(msg...) fprintf(stderr, msg) + +static virStorageDriverStatePtr driverState; + +static int storageDriverShutdown(void); + + +static void storageDriverAutostart(virStorageDriverStatePtr driver) { + virStoragePoolObjPtr pool; + + pool = driver->pools; + while (pool != NULL) { + virStoragePoolObjPtr next = pool->next; + + if (pool->autostart && + !virStoragePoolObjIsActive(pool)) { + virStorageBackendPtr backend; + if ((backend = virStorageBackendForType(pool->def->type)) == NULL) { + storageLog("Missing backend %d", + pool->def->type); + pool = next; + continue; + } + + if (backend->startPool && + backend->startPool(NULL, pool) < 0) { + virErrorPtr err = virGetLastError(); + storageLog("Failed to autostart storage pool '%s': %s", + pool->def->name, err ? err->message : NULL); + pool = next; + continue; + } + + if (backend->refreshPool(NULL, pool) < 0) { + virErrorPtr err = virGetLastError(); + if (backend->stopPool) + backend->stopPool(NULL, pool); + storageLog("Failed to autostart storage pool '%s': %s", + pool->def->name, err ? err->message : NULL); + pool = next; + continue; + } + pool->active = 1; + driver->nactivePools++; + driver->ninactivePools--; + } + + pool = next; + } +} + +/** + * virStorageStartup: + * + * Initialization function for the QEmu daemon + */ +static int +storageDriverStartup(void) { + uid_t uid = geteuid(); + struct passwd *pw; + char *base = NULL; + char driverConf[PATH_MAX]; + + if (!(driverState = calloc(1, sizeof(virStorageDriverState)))) { + return -1; + } + + if (!uid) { + if ((base = strdup (SYSCONF_DIR "/libvirt")) == NULL) + goto out_of_memory; + } else { + if (!(pw = getpwuid(uid))) { + storageLog("Failed to find user record for uid '%d': %s", + uid, strerror(errno)); + goto out_of_memory; + } + + if (asprintf (&base, "%s/.libvirt", pw->pw_dir) == -1) { + storageLog("out of memory in asprintf"); + goto out_of_memory; + } + } + + /* Configuration paths are either ~/.libvirt/storage/... (session) or + * /etc/libvirt/storage/... (system). + */ + if (snprintf (driverConf, sizeof(driverConf), "%s/storage.conf", base) == -1) + goto out_of_memory; + driverConf[sizeof(driverConf)-1] = '\0'; + + if (asprintf (&driverState->configDir, "%s/storage", base) == -1) + goto out_of_memory; + + if (asprintf (&driverState->autostartDir, "%s/storage/autostart", base) == -1) + goto out_of_memory; + + free(base); + base = NULL; + + /* + if (virStorageLoadDriverConfig(driver, driverConf) < 0) { + virStorageDriverShutdown(); + return -1; + } + */ + + if (virStoragePoolObjScanConfigs(driverState) < 0) { + storageDriverShutdown(); + return -1; + } + storageDriverAutostart(driverState); + + return 0; + + out_of_memory: + storageLog("virStorageStartup: out of memory"); + free(base); + free(driverState); + driverState = NULL; + return -1; +} + +/** + * virStorageReload: + * + * Function to restart the storage driver, it will recheck the configuration + * files and update its state + */ +static int +storageDriverReload(void) { + virStoragePoolObjScanConfigs(driverState); + storageDriverAutostart(driverState); + + return 0; +} + +/** + * virStorageActive: + * + * Checks if the storage driver is active, i.e. has an active pool + * + * Returns 1 if active, 0 otherwise + */ +static int +storageDriverActive(void) { + /* If we've any active networks or guests, then we + * mark this driver as active + */ + if (driverState->nactivePools) + return 1; + + /* Otherwise we're happy to deal with a shutdown */ + return 0; +} + +/** + * virStorageShutdown: + * + * Shutdown the storage driver, it will stop all active storage pools + */ +static int +storageDriverShutdown(void) { + virStoragePoolObjPtr pool; + + if (!driverState) + return -1; + + /* shutdown active pools */ + pool = driverState->pools; + while (pool) { + virStoragePoolObjPtr next = pool->next; + if (virStoragePoolObjIsActive(pool)) { + virStorageBackendPtr backend; + if ((backend = virStorageBackendForType(pool->def->type)) == NULL) { + storageLog("Missing backend"); + continue; + } + + if (backend->stopPool && + backend->stopPool(NULL, pool) < 0) { + virErrorPtr err = virGetLastError(); + storageLog("Failed to stop storage pool '%s': %s", + pool->def->name, err->message); + } + virStoragePoolObjClearVols(pool); + } + pool = next; + } + + /* free inactive pools */ + pool = driverState->pools; + while (pool) { + virStoragePoolObjPtr next = pool->next; + virStoragePoolObjFree(pool); + pool = next; + } + driverState->pools = NULL; + driverState->nactivePools = 0; + driverState->ninactivePools = 0; + + free(driverState->configDir); + free(driverState->autostartDir); + free(driverState); + driverState = NULL; + + return 0; +} + + + +static virStoragePoolPtr storagePoolLookupByUUID(virConnectPtr conn, + const unsigned char *uuid) { + virStorageDriverStatePtr driver = (virStorageDriverStatePtr)conn->storagePrivateData; + virStoragePoolObjPtr pool = virStoragePoolObjFindByUUID(driver, uuid); + virStoragePoolPtr ret; + + if (!pool) { + virStorageReportError(conn, VIR_ERR_NO_STORAGE_POOL, "no pool with matching uuid"); + return NULL; + } + + ret = virGetStoragePool(conn, pool->def->name, pool->def->uuid); + return ret; +} + +static virStoragePoolPtr storagePoolLookupByName(virConnectPtr conn, + const char *name) { + virStorageDriverStatePtr driver = (virStorageDriverStatePtr)conn->storagePrivateData; + virStoragePoolObjPtr pool = virStoragePoolObjFindByName(driver, name); + virStoragePoolPtr ret; + + if (!pool) { + virStorageReportError(conn, VIR_ERR_NO_STORAGE_POOL, "no pool with matching name"); + return NULL; + } + + ret = virGetStoragePool(conn, pool->def->name, pool->def->uuid); + return ret; +} + +static virStoragePoolPtr storagePoolLookupByVolume(virStorageVolPtr vol) { + return storagePoolLookupByName(vol->conn, vol->pool); +} + +static virDrvOpenStatus storageOpen(virConnectPtr conn, + xmlURIPtr uri ATTRIBUTE_UNUSED, + virConnectAuthPtr auth ATTRIBUTE_UNUSED, + int flags ATTRIBUTE_UNUSED) { + if (!driverState) + return VIR_DRV_OPEN_DECLINED; + + conn->storagePrivateData = driverState; + return VIR_DRV_OPEN_SUCCESS; +} + +static int storageClose(virConnectPtr conn) { + conn->storagePrivateData = NULL; + return 0; +} + +static int storageNumPools(virConnectPtr conn) { + virStorageDriverStatePtr driver = (virStorageDriverStatePtr)conn->storagePrivateData; + return driver->nactivePools; +} + +static int storageListPools(virConnectPtr conn, char **const names, int nnames) { + virStorageDriverStatePtr driver = (virStorageDriverStatePtr)conn->storagePrivateData; + virStoragePoolObjPtr pool = driver->pools; + int got = 0, i; + while (pool && got < nnames) { + if (virStoragePoolObjIsActive(pool)) { + if (!(names[got] = strdup(pool->def->name))) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, "names"); + goto cleanup; + } + got++; + } + pool = pool->next; + } + return got; + + cleanup: + for (i = 0 ; i < got ; i++) { + free(names[i]); + names[i] = NULL; + } + return -1; +} + +static int storageNumDefinedPools(virConnectPtr conn) { + virStorageDriverStatePtr driver = (virStorageDriverStatePtr)conn->storagePrivateData; + return driver->ninactivePools; +} + +static int storageListDefinedPools(virConnectPtr conn, char **const names, int nnames) { + virStorageDriverStatePtr driver = (virStorageDriverStatePtr)conn->storagePrivateData; + virStoragePoolObjPtr pool = driver->pools; + int got = 0, i; + while (pool && got < nnames) { + if (!virStoragePoolObjIsActive(pool)) { + if (!(names[got] = strdup(pool->def->name))) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, "names"); + goto cleanup; + } + got++; + } + pool = pool->next; + } + return got; + + cleanup: + for (i = 0 ; i < got ; i++) { + free(names[i]); + names[i] = NULL; + } + return -1; +} + +static int storageDiscoverPools(virConnectPtr conn ATTRIBUTE_UNUSED, + const char *hostname ATTRIBUTE_UNUSED, + const char *type ATTRIBUTE_UNUSED, + unsigned int flags ATTRIBUTE_UNUSED, + char ***xmlDesc ATTRIBUTE_UNUSED) { + return -1; +} + +static virStoragePoolPtr storagePoolCreate(virConnectPtr conn, const char *xml) { + virStorageDriverStatePtr driver = (virStorageDriverStatePtr )conn->storagePrivateData; + virStoragePoolDefPtr def; + virStoragePoolObjPtr pool; + virStoragePoolPtr ret; + virStorageBackendPtr backend; + + if (!(def = virStoragePoolDefParse(conn, xml, NULL))) + return NULL; + + if (virStoragePoolObjFindByUUID(driver, def->uuid) || + virStoragePoolObjFindByName(driver, def->name)) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "storage pool already exists"); + virStoragePoolDefFree(def); + return NULL; + } + + if ((backend = virStorageBackendForType(def->type)) == NULL) { + virStoragePoolDefFree(def); + return NULL; + } + + if (!(pool = virStoragePoolObjAssignDef(conn, driver, def))) { + virStoragePoolDefFree(def); + return NULL; + } + + if (backend->startPool(conn, pool) < 0) { + virStoragePoolObjRemove(driver, pool); + return NULL; + } + pool->active = 1; + driver->nactivePools++; + driver->ninactivePools--; + + ret = virGetStoragePool(conn, pool->def->name, pool->def->uuid); + + return ret; +} + +static virStoragePoolPtr storagePoolDefine(virConnectPtr conn, const char *xml) { + virStorageDriverStatePtr driver = (virStorageDriverStatePtr )conn->storagePrivateData; + virStoragePoolDefPtr def; + virStoragePoolObjPtr pool; + virStoragePoolPtr ret; + virStorageBackendPtr backend; + + if (!(def = virStoragePoolDefParse(conn, xml, NULL))) + return NULL; + + if ((backend = virStorageBackendForType(def->type)) == NULL) { + virStoragePoolDefFree(def); + return NULL; + } + + if (!(pool = virStoragePoolObjAssignDef(conn, driver, def))) { + virStoragePoolDefFree(def); + return NULL; + } + + if (virStoragePoolObjSaveDef(conn, driver, pool, def) < 0) { + virStoragePoolObjRemove(driver, pool); + return NULL; + } + + ret = virGetStoragePool(conn, pool->def->name, pool->def->uuid); + return ret; +} + +static int storagePoolUndefine(virStoragePoolPtr obj) { + virStorageDriverStatePtr driver = (virStorageDriverStatePtr)obj->conn->storagePrivateData; + virStoragePoolObjPtr pool = virStoragePoolObjFindByUUID(driver, obj->uuid); + + if (!pool) { + virStorageReportError(obj->conn, VIR_ERR_INVALID_STORAGE_POOL, + "no storage pool with matching uuid"); + return -1; + } + + if (virStoragePoolObjIsActive(pool)) { + virStorageReportError(obj->conn, VIR_ERR_INTERNAL_ERROR, "pool is still active"); + return -1; + } + + if (virStoragePoolObjDeleteDef(obj->conn, pool) < 0) + return -1; + + if (unlink(pool->autostartLink) < 0 && errno != ENOENT && errno != ENOTDIR) + storageLog("Failed to delete autostart link '%s': %s", + pool->autostartLink, strerror(errno)); + + free(pool->configFile); + pool->configFile = NULL; + free(pool->autostartLink); + pool->autostartLink = NULL; + + virStoragePoolObjRemove(driver, pool); + + return 0; +} + +static int storagePoolStart(virStoragePoolPtr obj) { + virStorageDriverStatePtr driver = (virStorageDriverStatePtr)obj->conn->storagePrivateData; + virStoragePoolObjPtr pool = virStoragePoolObjFindByUUID(driver, obj->uuid); + virStorageBackendPtr backend; + + if (!pool) { + virStorageReportError(obj->conn, VIR_ERR_INVALID_STORAGE_POOL, + "no storage pool with matching uuid"); + return -1; + } + + if ((backend = virStorageBackendForType(pool->def->type)) == NULL) { + return -1; + } + + if (virStoragePoolObjIsActive(pool)) { + virStorageReportError(obj->conn, VIR_ERR_INTERNAL_ERROR, "pool already active"); + return -1; + } + if (backend->startPool && + backend->startPool(obj->conn, pool) < 0) + return -1; + if (backend->refreshPool(obj->conn, pool) < 0) { + if (backend->stopPool) + backend->stopPool(obj->conn, pool); + return -1; + } + + pool->active = 1; + driver->nactivePools++; + driver->ninactivePools--; + + return 0; +} + +static int storagePoolBuild(virStoragePoolPtr obj, + unsigned int flags) { + virStorageDriverStatePtr driver = (virStorageDriverStatePtr)obj->conn->storagePrivateData; + virStoragePoolObjPtr pool = virStoragePoolObjFindByUUID(driver, obj->uuid); + virStorageBackendPtr backend; + + if (!pool) { + virStorageReportError(obj->conn, VIR_ERR_INVALID_STORAGE_POOL, + "no storage pool with matching uuid"); + return -1; + } + + if ((backend = virStorageBackendForType(pool->def->type)) == NULL) { + return -1; + } + + if (virStoragePoolObjIsActive(pool)) { + virStorageReportError(obj->conn, VIR_ERR_INTERNAL_ERROR, + "storage pool is already active"); + return -1; + } + + if (backend->buildPool && + backend->buildPool(obj->conn, pool, flags) < 0) + return -1; + + return 0; +} + + +static int storagePoolDestroy(virStoragePoolPtr obj) { + virStorageDriverStatePtr driver = (virStorageDriverStatePtr)obj->conn->storagePrivateData; + virStoragePoolObjPtr pool = virStoragePoolObjFindByUUID(driver, obj->uuid); + virStorageBackendPtr backend; + + if (!pool) { + virStorageReportError(obj->conn, VIR_ERR_INVALID_STORAGE_POOL, + "no storage pool with matching uuid"); + return -1; + } + + if ((backend = virStorageBackendForType(pool->def->type)) == NULL) { + return -1; + } + + if (!virStoragePoolObjIsActive(pool)) { + virStorageReportError(obj->conn, VIR_ERR_INTERNAL_ERROR, + "storage pool is not active"); + return -1; + } + + if (backend->stopPool && + backend->stopPool(obj->conn, pool) < 0) + return -1; + + virStoragePoolObjClearVols(pool); + + pool->active = 0; + driver->nactivePools--; + driver->ninactivePools++; + + if (pool->configFile == NULL) + virStoragePoolObjRemove(driver, pool); + + return 0; +} + + +static int storagePoolDelete(virStoragePoolPtr obj, + unsigned int flags) { + virStorageDriverStatePtr driver = (virStorageDriverStatePtr)obj->conn->storagePrivateData; + virStoragePoolObjPtr pool = virStoragePoolObjFindByUUID(driver, obj->uuid); + virStorageBackendPtr backend; + + if (!pool) { + virStorageReportError(obj->conn, VIR_ERR_INVALID_STORAGE_POOL, + "no storage pool with matching uuid"); + return -1; + } + + if ((backend = virStorageBackendForType(pool->def->type)) == NULL) { + return -1; + } + + if (virStoragePoolObjIsActive(pool)) { + virStorageReportError(obj->conn, VIR_ERR_INTERNAL_ERROR, + "storage pool is still active"); + return -1; + } + + if (backend->deletePool && + backend->deletePool(obj->conn, pool, flags) < 0) + return -1; + + return 0; +} + + +static int storagePoolRefresh(virStoragePoolPtr obj, + unsigned int flags ATTRIBUTE_UNUSED) { + virStorageDriverStatePtr driver = (virStorageDriverStatePtr)obj->conn->storagePrivateData; + virStoragePoolObjPtr pool = virStoragePoolObjFindByUUID(driver, obj->uuid); + virStorageBackendPtr backend; + int ret = 0; + + if (!pool) { + virStorageReportError(obj->conn, VIR_ERR_INVALID_STORAGE_POOL, + "no storage pool with matching uuid"); + return -1; + } + + if ((backend = virStorageBackendForType(pool->def->type)) == NULL) { + return -1; + } + + if (!virStoragePoolObjIsActive(pool)) { + virStorageReportError(obj->conn, VIR_ERR_INTERNAL_ERROR, + "storage pool is not active"); + return -1; + } + + virStoragePoolObjClearVols(pool); + if ((ret = backend->refreshPool(obj->conn, pool)) < 0) { + if (backend->stopPool) + backend->stopPool(obj->conn, pool); + + pool->active = 0; + driver->nactivePools--; + driver->ninactivePools++; + + if (pool->configFile == NULL) + virStoragePoolObjRemove(driver, pool); + } + + return ret; +} + + +static int storagePoolGetInfo(virStoragePoolPtr obj, virStoragePoolInfoPtr info) { + virStorageDriverStatePtr driver = (virStorageDriverStatePtr)obj->conn->storagePrivateData; + virStoragePoolObjPtr pool = virStoragePoolObjFindByUUID(driver, obj->uuid); + virStorageBackendPtr backend; + + if (!pool) { + virStorageReportError(obj->conn, VIR_ERR_INVALID_STORAGE_POOL, + "no storage pool with matching uuid"); + return -1; + } + + if ((backend = virStorageBackendForType(pool->def->type)) == NULL) { + return -1; + } + + memset(info, 0, sizeof(virStoragePoolInfo)); + if (pool->active) + info->state = VIR_STORAGE_POOL_RUNNING; + else + info->state = VIR_STORAGE_POOL_INACTIVE; + info->capacity = pool->def->capacity; + info->allocation = pool->def->allocation; + info->available = pool->def->available; + + return 0; +} + +static char *storagePoolDumpXML(virStoragePoolPtr obj, + unsigned int flags ATTRIBUTE_UNUSED) { + virStorageDriverStatePtr driver = (virStorageDriverStatePtr)obj->conn->storagePrivateData; + virStoragePoolObjPtr pool = virStoragePoolObjFindByUUID(driver, obj->uuid); + + if (!pool) { + virStorageReportError(obj->conn, VIR_ERR_INVALID_STORAGE_POOL, + "no storage pool with matching uuid"); + return NULL; + } + + return virStoragePoolDefFormat(obj->conn, pool->def); +} + +static int storagePoolGetAutostart(virStoragePoolPtr obj, + int *autostart) { + virStorageDriverStatePtr driver = (virStorageDriverStatePtr)obj->conn->storagePrivateData; + virStoragePoolObjPtr pool = virStoragePoolObjFindByUUID(driver, obj->uuid); + + if (!pool) { + virStorageReportError(obj->conn, VIR_ERR_INVALID_STORAGE_POOL, + "no pool with matching uuid"); + return -1; + } + + if (!pool->configFile) { + *autostart = 0; + } else { + *autostart = pool->autostart; + } + + return 0; +} + +static int storagePoolSetAutostart(virStoragePoolPtr obj, + int autostart) { + virStorageDriverStatePtr driver = (virStorageDriverStatePtr)obj->conn->storagePrivateData; + virStoragePoolObjPtr pool = virStoragePoolObjFindByUUID(driver, obj->uuid); + + if (!pool) { + virStorageReportError(obj->conn, VIR_ERR_INVALID_STORAGE_POOL, + "no pool with matching uuid"); + return -1; + } + + if (!pool->configFile) { + virStorageReportError(obj->conn, VIR_ERR_INVALID_ARG, + "pool has no config file"); + return -1; + } + + autostart = (autostart != 0); + + if (pool->autostart == autostart) + return 0; + + if (autostart) { + int err; + + if ((err = virFileMakePath(driver->autostartDir))) { + virStorageReportError(obj->conn, VIR_ERR_INTERNAL_ERROR, + "cannot create autostart directory %s: %s", + driver->autostartDir, strerror(err)); + return -1; + } + + if (symlink(pool->configFile, pool->autostartLink) < 0) { + virStorageReportError(obj->conn, VIR_ERR_INTERNAL_ERROR, + "Failed to create symlink '%s' to '%s': %s", + pool->autostartLink, pool->configFile, strerror(errno)); + return -1; + } + } else { + if (unlink(pool->autostartLink) < 0 && errno != ENOENT && errno != ENOTDIR) { + virStorageReportError(obj->conn, VIR_ERR_INTERNAL_ERROR, + "Failed to delete symlink '%s': %s", + pool->autostartLink, strerror(errno)); + return -1; + } + } + + pool->autostart = autostart; + + return 0; +} + + +static int storagePoolNumVolumes(virStoragePoolPtr obj) { + virStorageDriverStatePtr driver = (virStorageDriverStatePtr)obj->conn->storagePrivateData; + virStoragePoolObjPtr pool = virStoragePoolObjFindByUUID(driver, obj->uuid); + + if (!pool) { + virStorageReportError(obj->conn, VIR_ERR_INVALID_STORAGE_POOL, + "no storage pool with matching uuid"); + return -1; + } + + if (!virStoragePoolObjIsActive(pool)) { + virStorageReportError(obj->conn, VIR_ERR_INTERNAL_ERROR, + "storage pool is not active"); + return -1; + } + + return pool->nvolumes; +} + +static int storagePoolListVolumes(virStoragePoolPtr obj, char **const names, int maxnames) { + virStorageDriverStatePtr driver = (virStorageDriverStatePtr)obj->conn->storagePrivateData; + virStoragePoolObjPtr pool = virStoragePoolObjFindByUUID(driver, obj->uuid); + int i = 0; + virStorageVolDefPtr vol; + + if (!pool) { + virStorageReportError(obj->conn, VIR_ERR_INVALID_STORAGE_POOL, + "no storage pool with matching uuid"); + return -1; + } + + if (!virStoragePoolObjIsActive(pool)) { + virStorageReportError(obj->conn, VIR_ERR_INTERNAL_ERROR, + "storage pool is not active"); + return -1; + } + + memset(names, 0, maxnames); + vol = pool->volumes; + while (vol && i < maxnames) { + names[i] = strdup(vol->name); + if (names[i] == NULL) { + virStorageReportError(obj->conn, VIR_ERR_NO_MEMORY, "name"); + goto cleanup; + } + vol = vol->next; + i++; + } + + return i; + + cleanup: + for (i = 0 ; i < maxnames ; i++) { + free(names[i]); + names[i] = NULL; + } + return -1; +} + + +static virStorageVolPtr storageVolumeLookupByName(virStoragePoolPtr obj, const char *name) { + virStorageDriverStatePtr driver = (virStorageDriverStatePtr)obj->conn->storagePrivateData; + virStoragePoolObjPtr pool = virStoragePoolObjFindByUUID(driver, obj->uuid); + virStorageVolDefPtr vol; + + if (!pool) { + virStorageReportError(obj->conn, VIR_ERR_INVALID_STORAGE_POOL, + "no storage pool with matching uuid"); + return NULL; + } + + if (!virStoragePoolObjIsActive(pool)) { + virStorageReportError(obj->conn, VIR_ERR_INTERNAL_ERROR, + "storage pool is not active"); + return NULL; + } + + vol = virStorageVolDefFindByName(pool, name); + + if (!vol) { + virStorageReportError(obj->conn, VIR_ERR_INVALID_STORAGE_POOL, + "no storage vol with matching name"); + return NULL; + } + + return virGetStorageVol(obj->conn, pool->def->name, vol->name, vol->key); +} + + +static virStorageVolPtr storageVolumeLookupByKey(virConnectPtr conn, const char *key) { + virStorageDriverStatePtr driver = (virStorageDriverStatePtr)conn->storagePrivateData; + virStoragePoolObjPtr pool = driver->pools; + + while (pool) { + if (virStoragePoolObjIsActive(pool)) { + virStorageVolDefPtr vol = virStorageVolDefFindByKey(pool, key); + + if (vol) + return virGetStorageVol(conn, pool->def->name, vol->name, vol->key); + } + pool = pool->next; + } + + virStorageReportError(conn, VIR_ERR_INVALID_STORAGE_VOL, + "no storage vol with matching key"); + return NULL; +} + +static virStorageVolPtr storageVolumeLookupByPath(virConnectPtr conn, const char *path) { + virStorageDriverStatePtr driver = (virStorageDriverStatePtr)conn->storagePrivateData; + virStoragePoolObjPtr pool = driver->pools; + + while (pool) { + if (virStoragePoolObjIsActive(pool)) { + virStorageVolDefPtr vol = virStorageVolDefFindByPath(pool, path); + + if (vol) + return virGetStorageVol(conn, pool->def->name, vol->name, vol->key); + } + pool = pool->next; + } + + virStorageReportError(conn, VIR_ERR_INVALID_STORAGE_VOL, + "no storage vol with matching path"); + return NULL; +} + +static virStorageVolPtr storageVolumeCreateXML(virStoragePoolPtr obj, + const char *xmldesc, + unsigned int flags ATTRIBUTE_UNUSED) { + virStorageDriverStatePtr driver = (virStorageDriverStatePtr)obj->conn->storagePrivateData; + virStoragePoolObjPtr pool = virStoragePoolObjFindByUUID(driver, obj->uuid); + virStorageBackendPtr backend; + virStorageVolDefPtr vol; + + if (!pool) { + virStorageReportError(obj->conn, VIR_ERR_INVALID_STORAGE_POOL, + "no storage pool with matching uuid"); + return NULL; + } + + if (!virStoragePoolObjIsActive(pool)) { + virStorageReportError(obj->conn, VIR_ERR_INTERNAL_ERROR, + "storage pool is not active"); + return NULL; + } + + if ((backend = virStorageBackendForType(pool->def->type)) == NULL) + return NULL; + + vol = virStorageVolDefParse(obj->conn, pool->def, xmldesc, NULL); + if (vol == NULL) + return NULL; + + if (virStorageVolDefFindByName(pool, vol->name)) { + virStorageReportError(obj->conn, VIR_ERR_INVALID_STORAGE_POOL, + "storage vol already exists"); + virStorageVolDefFree(vol); + return NULL; + } + + if (!backend->createVol) { + virStorageReportError(obj->conn, VIR_ERR_NO_SUPPORT, + "storage pool does not support volume creation"); + virStorageVolDefFree(vol); + return NULL; + } + + if (backend->createVol(obj->conn, pool, vol) < 0) { + virStorageVolDefFree(vol); + return NULL; + } + + vol->next = pool->volumes; + pool->volumes = vol; + pool->nvolumes++; + + return virGetStorageVol(obj->conn, pool->def->name, vol->name, vol->key); +} + +static int storageVolumeDelete(virStorageVolPtr obj, + unsigned int flags) { + virStorageDriverStatePtr driver = (virStorageDriverStatePtr)obj->conn->storagePrivateData; + virStoragePoolObjPtr pool = virStoragePoolObjFindByName(driver, obj->pool); + virStorageBackendPtr backend; + virStorageVolDefPtr vol, tmp, prev; + + if (!pool) { + virStorageReportError(obj->conn, VIR_ERR_INVALID_STORAGE_POOL, + "no storage pool with matching uuid"); + return -1; + } + + if (!virStoragePoolObjIsActive(pool)) { + virStorageReportError(obj->conn, VIR_ERR_INTERNAL_ERROR, + "storage pool is not active"); + return -1; + } + + if ((backend = virStorageBackendForType(pool->def->type)) == NULL) + return -1; + + vol = virStorageVolDefFindByName(pool, obj->name); + + if (!vol) { + virStorageReportError(obj->conn, VIR_ERR_INVALID_STORAGE_POOL, + "no storage vol with matching name"); + return -1; + } + + if (!backend->deleteVol) { + virStorageReportError(obj->conn, VIR_ERR_NO_SUPPORT, + "storage pool does not support volume deletion"); + virStorageVolDefFree(vol); + return -1; + } + + if (backend->deleteVol(obj->conn, pool, vol, flags) < 0) { + return -1; + } + + prev = NULL; + tmp = pool->volumes; + while (tmp) { + if (tmp == vol) { + break; + } + prev = tmp; + tmp = tmp->next; + } + if (prev) { + prev->next = vol->next; + } else { + pool->volumes = vol->next; + } + pool->nvolumes--; + virStorageVolDefFree(vol); + + return 0; +} + +static int storageVolumeGetInfo(virStorageVolPtr obj, virStorageVolInfoPtr info) { + virStorageDriverStatePtr driver = (virStorageDriverStatePtr)obj->conn->storagePrivateData; + virStoragePoolObjPtr pool = virStoragePoolObjFindByName(driver, obj->pool); + virStorageBackendPtr backend; + virStorageVolDefPtr vol; + + if (!pool) { + virStorageReportError(obj->conn, VIR_ERR_INVALID_STORAGE_POOL, + "no storage pool with matching uuid"); + return -1; + } + + if (!virStoragePoolObjIsActive(pool)) { + virStorageReportError(obj->conn, VIR_ERR_INTERNAL_ERROR, + "storage pool is not active"); + return -1; + } + + vol = virStorageVolDefFindByName(pool, obj->name); + + if (!vol) { + virStorageReportError(obj->conn, VIR_ERR_INVALID_STORAGE_POOL, + "no storage vol with matching name"); + return -1; + } + + if ((backend = virStorageBackendForType(pool->def->type)) == NULL) + return -1; + + if (backend->refreshVol && + backend->refreshVol(obj->conn, pool, vol) < 0) + return -1; + + memset(info, 0, sizeof(*info)); + info->type = backend->volType; + info->capacity = vol->capacity; + info->allocation = vol->allocation; + + return 0; +} + +static char *storageVolumeGetXMLDesc(virStorageVolPtr obj, + unsigned int flags ATTRIBUTE_UNUSED) { + virStorageDriverStatePtr driver = (virStorageDriverStatePtr)obj->conn->storagePrivateData; + virStoragePoolObjPtr pool = virStoragePoolObjFindByName(driver, obj->pool); + virStorageBackendPtr backend; + virStorageVolDefPtr vol; + + if (!pool) { + virStorageReportError(obj->conn, VIR_ERR_INVALID_STORAGE_POOL, + "no storage pool with matching uuid"); + return NULL; + } + + if (!virStoragePoolObjIsActive(pool)) { + virStorageReportError(obj->conn, VIR_ERR_INTERNAL_ERROR, + "storage pool is not active"); + return NULL; + } + + vol = virStorageVolDefFindByName(pool, obj->name); + + if (!vol) { + virStorageReportError(obj->conn, VIR_ERR_INVALID_STORAGE_POOL, + "no storage vol with matching name"); + return NULL; + } + + if ((backend = virStorageBackendForType(pool->def->type)) == NULL) + return NULL; + + return virStorageVolDefFormat(obj->conn, pool->def, vol); +} + +static char *storageVolumeGetPath(virStorageVolPtr obj) { + virStorageDriverStatePtr driver = (virStorageDriverStatePtr)obj->conn->storagePrivateData; + virStoragePoolObjPtr pool = virStoragePoolObjFindByName(driver, obj->pool); + virStorageVolDefPtr vol; + char *ret; + + if (!pool) { + virStorageReportError(obj->conn, VIR_ERR_INVALID_STORAGE_POOL, + "no storage pool with matching uuid"); + return NULL; + } + + if (!virStoragePoolObjIsActive(pool)) { + virStorageReportError(obj->conn, VIR_ERR_INTERNAL_ERROR, + "storage pool is not active"); + return NULL; + } + + vol = virStorageVolDefFindByName(pool, obj->name); + + if (!vol) { + virStorageReportError(obj->conn, VIR_ERR_INVALID_STORAGE_POOL, + "no storage vol with matching name"); + return NULL; + } + + ret = strdup(vol->target.path); + if (ret == NULL) { + virStorageReportError(obj->conn, VIR_ERR_NO_MEMORY, "path"); + return NULL; + } + return ret; +} + + + + + +static virStorageDriver storageDriver = { + "storage", + storageOpen, + storageClose, + storageNumPools, + storageListPools, + storageNumDefinedPools, + storageListDefinedPools, + storageDiscoverPools, + storagePoolLookupByName, + storagePoolLookupByUUID, + storagePoolLookupByVolume, + storagePoolCreate, + storagePoolDefine, + storagePoolBuild, + storagePoolUndefine, + storagePoolStart, + storagePoolDestroy, + storagePoolDelete, + storagePoolRefresh, + storagePoolGetInfo, + storagePoolDumpXML, + storagePoolGetAutostart, + storagePoolSetAutostart, + storagePoolNumVolumes, + storagePoolListVolumes, + storageVolumeLookupByName, + storageVolumeLookupByKey, + storageVolumeLookupByPath, + storageVolumeCreateXML, + storageVolumeDelete, + storageVolumeGetInfo, + storageVolumeGetXMLDesc, + storageVolumeGetPath +}; + + +static virStateDriver stateDriver = { + storageDriverStartup, + storageDriverShutdown, + storageDriverReload, + storageDriverActive, +}; + +int storageRegister(void) { + virRegisterStorageDriver(&storageDriver); + virRegisterStateDriver(&stateDriver); + return 0; +} + +/* + * vim: set tabstop=4: + * vim: set shiftwidth=4: + * vim: set expandtab: + */ +/* + * Local variables: + * indent-tabs-mode: nil + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 4 + * End: + */ diff -r 77cf7f42edd4 src/storage_driver.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/storage_driver.h Thu Feb 07 12:59:40 2008 -0500 @@ -0,0 +1,45 @@ +/* + * storage_driver.h: core driver for storage APIs + * + * Copyright (C) 2006-2008 Red Hat, Inc. + * Copyright (C) 2006-2008 Daniel P. Berrange + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Daniel P. Berrange <berrange@xxxxxxxxxx> + */ + +#ifndef __VIR_STORAGE_DRIVER_H__ +#define __VIR_STORAGE_DRIVER_H__ + +#include "storage_conf.h" + +int storageRegister(void); + +#endif /* __VIR_STORAGE_DRIVER_H__ */ + +/* + * vim: set tabstop=4: + * vim: set shiftwidth=4: + * vim: set expandtab: + */ +/* + * Local variables: + * indent-tabs-mode: nil + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 4 + * End: + */ diff -r 77cf7f42edd4 src/virterror.c --- a/src/virterror.c Thu Feb 07 12:33:33 2008 -0500 +++ b/src/virterror.c Thu Feb 07 12:59:40 2008 -0500 @@ -668,6 +668,18 @@ __virErrorMsg(virErrorNumber error, cons else errmsg = _("authentication failed: %s"); break; + case VIR_ERR_NO_STORAGE_POOL: + if (info == NULL) + errmsg = _("Storage pool not found"); + else + errmsg = _("Storage pool not found: %s"); + break; + case VIR_ERR_NO_STORAGE_VOL: + if (info == NULL) + errmsg = _("Storage volume not found"); + else + errmsg = _("Storage volume not found: %s"); + break; case VIR_ERR_INVALID_STORAGE_POOL: if (info == NULL) errmsg = _("invalid storage pool pointer in"); -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=| -- Libvir-list mailing list Libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list