diff --git a/ChangeLog b/ChangeLog index 132032c95e..179174ca6f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,17 @@ +Wed Feb 20 10:26:27 EST 2008 Daniel P. Berrange + + * Makefile.maint: Add virStorageReportError to locale check rule + * configure.in: Turn on large file support + * po/POTFILES.in: Add storage driver files + * include/libvirt/virterror.h, src/virterror.c: Add more error codes + * src/storage_driver.c, src/storage_driver.h: Add impl of all the + storage APIs + * src/storage_conf.c, src/storage_conf.h: Support routines for + parsing and formatting XML, and persisting storage pool configs + * src/storage_backend.c, src/storage_backend.h: Contract for + internal storage backends to interface with driver + * src/Makefile.am: Add new storage source files to library build + Wed Feb 20 10:23:27 EST 2008 Daniel P. Berrange * src/virsh.c: Added convenience methods for creating pools diff --git a/Makefile.maint b/Makefile.maint index 4b54bafc9a..39320df088 100644 --- a/Makefile.maint +++ b/Makefile.maint @@ -289,7 +289,7 @@ sc_two_space_separator_in_usage: 1>&2; exit 1; } || : err_func_re = \ -(DISABLE_fprintf|qemudLog|(xmlRpc|vir(Xend|XML|Hash|Conf|Test|LibConn))Error) +(DISABLE_fprintf|qemudLog|(xmlRpc|vir(Xend|XML|Hash|Conf|Test|LibConn|StorageReport))Error) # Look for diagnostics that aren't marked for translation. # This won't find any for which error's format string is on a separate line. diff --git a/configure.in b/configure.in index 3c30a8ae7b..9155c77fcc 100644 --- a/configure.in +++ b/configure.in @@ -689,6 +689,8 @@ AC_SUBST(CYGWIN_EXTRA_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 --git a/include/libvirt/virterror.h b/include/libvirt/virterror.h index fa9457796d..f28ce50ba5 100644 --- a/include/libvirt/virterror.h +++ b/include/libvirt/virterror.h @@ -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 --git a/po/POTFILES.in b/po/POTFILES.in index a1e78fa8f9..f6c4dd8fa1 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -10,6 +10,9 @@ src/proxy_internal.c src/qemu_conf.c src/qemu_driver.c src/remote_internal.c +src/storage_backend.c +src/storage_conf.c +src/storage_driver.c src/sexpr.c src/test.c src/uuid.c diff --git a/src/Makefile.am b/src/Makefile.am index d058465abf..7c06f0363a 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -58,6 +58,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 --git a/src/libvirt.c b/src/libvirt.c index 7c9c297c13..9cd70bb399 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -37,6 +37,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 @@ -215,6 +216,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 --git a/src/storage_backend.c b/src/storage_backend.c new file mode 100644 index 0000000000..254d37f43d --- /dev/null +++ b/src/storage_backend.c @@ -0,0 +1,88 @@ +/* + * 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 + */ + +#include + +#include +#include +#include +#include +#include + +#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 --git a/src/storage_backend.h b/src/storage_backend.h new file mode 100644 index 0000000000..0ad51526c1 --- /dev/null +++ b/src/storage_backend.h @@ -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 + */ + +#ifndef __VIR_STORAGE_BACKEND_H__ +#define __VIR_STORAGE_BACKEND_H__ + +#include +#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<<2), + VIR_STORAGE_BACKEND_POOL_SOURCE_ADAPTER = (1<<3), +}; + +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 --git a/src/storage_conf.c b/src/storage_conf.c new file mode 100644 index 0000000000..8dfcde7409 --- /dev/null +++ b/src/storage_conf.c @@ -0,0 +1,1251 @@ +/* + * 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 + */ + +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 || perms->mode < 0 || perms->mode > 0777) { + 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) { + free(nodeset); + 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); + free(authType); + authType = NULL; + 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); + 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); + 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, "\n", type) < 0) + goto no_memory; + + if (virBufferVSprintf(buf," %s\n", def->name) < 0) + goto no_memory; + + virUUIDFormat(def->uuid, uuid); + if (virBufferVSprintf(buf," %s\n", uuid) < 0) + goto no_memory; + + if (virBufferVSprintf(buf," %llu\n", + def->capacity) < 0) + goto no_memory; + if (virBufferVSprintf(buf," %llu\n", + def->allocation) < 0) + goto no_memory; + if (virBufferVSprintf(buf," %llu\n", + def->available) < 0) + goto no_memory; + + + if (virBufferAddLit(buf," \n") < 0) + goto no_memory; + if ((options->flags & VIR_STORAGE_BACKEND_POOL_SOURCE_HOST) && + def->source.host.name && + virBufferVSprintf(buf," \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," \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, " \n", + def->source.devices[i].freeExtents[j].start, + def->source.devices[i].freeExtents[j].end) < 0) + goto no_memory; + } + } + if (virBufferAddLit(buf," \n") < 0) + goto no_memory; + } + } + if ((options->flags & VIR_STORAGE_BACKEND_POOL_SOURCE_DIR) && + def->source.dir && + virBufferVSprintf(buf," \n", def->source.dir) < 0) + goto no_memory; + if ((options->flags & VIR_STORAGE_BACKEND_POOL_SOURCE_ADAPTER) && + def->source.adapter && + virBufferVSprintf(buf," \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," \n", format) < 0) + goto no_memory; + } + + + if (def->source.authType == VIR_STORAGE_POOL_AUTH_CHAP && + virBufferVSprintf(buf," \n", + def->source.auth.chap.login, + def->source.auth.chap.passwd) < 0) + goto no_memory; + if (virBufferAddLit(buf," \n") < 0) + goto no_memory; + + + + if (virBufferAddLit(buf," \n") < 0) + goto no_memory; + + if (def->target.path && + virBufferVSprintf(buf," %s\n", def->target.path) < 0) + goto no_memory; + + if (virBufferAddLit(buf," \n") < 0) + goto no_memory; + if (virBufferVSprintf(buf," 0%o\n", + def->target.perms.mode) < 0) + goto no_memory; + if (virBufferVSprintf(buf," %d\n", + def->target.perms.uid) < 0) + goto no_memory; + if (virBufferVSprintf(buf," %d\n", + def->target.perms.gid) < 0) + goto no_memory; + + if (def->target.perms.label) { + if (virBufferVSprintf(buf," \n", + def->target.perms.label) < 0) + goto no_memory; + } + if (virBufferAddLit(buf," \n") < 0) + goto no_memory; + if (virBufferAddLit(buf," \n") < 0) + goto no_memory; + + if (virBufferAddLit(buf,"\n") < 0) + goto no_memory; + + return virBufferContentAndFree(buf); + + no_memory: + virStorageReportError(conn, VIR_ERR_NO_MEMORY, _("xml")); + cleanup: + 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 = NULL; + perms->mode = strtol(mode, &end, 8); + if (*end || perms->mode < 0 || perms->mode > 0777) { + 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; + + case 'p': + case 'P': + mult = 1024ull * 1024ull * 1024ull * 1024ull * 1024ull; + break; + + case 'y': + case 'Y': + mult = 1024ull * 1024ull * 1024ull * 1024ull * 1024ull * + 1024ull; + break; + + case 'z': + case 'Z': + mult = 1024ull * 1024ull * 1024ull * 1024ull * 1024ull * + 1024ull * 1024ull; + break; + + default: + virStorageReportError(conn, VIR_ERR_XML_ERROR, + _("unknown size units '%s'"), unit); + return -1; + } + } + + if (virStrToLong_ull (val, &end, 10, ret) < 0) { + virStorageReportError(conn, VIR_ERR_XML_ERROR, + _("malformed capacity element")); + return -1; + } + if (*ret > (ULLONG_MAX / mult)) { + virStorageReportError(conn, VIR_ERR_XML_ERROR, + _("capacity element value too large")); + 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); + 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, "\n") < 0) + goto no_memory; + + if (virBufferVSprintf(buf," %s\n", def->name) < 0) + goto no_memory; + + if (virBufferVSprintf(buf," %s\n", def->key) < 0) + goto no_memory; + + if (virBufferAddLit(buf, " \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, " \n") < 0) + goto no_memory; + if (virBufferVSprintf(buf, " \n", + def->source.extents[i].path) < 0) + goto no_memory; + } + + if (virBufferVSprintf(buf, + " \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, " \n") < 0) + goto no_memory; + } + if (virBufferAddLit(buf, " \n") < 0) + goto no_memory; + + if (virBufferVSprintf(buf," %llu\n", + def->capacity) < 0) + goto no_memory; + if (virBufferVSprintf(buf," %llu\n", + def->allocation) < 0) + goto no_memory; + + if (virBufferAddLit(buf, " \n") < 0) + goto no_memory; + + if (def->target.path && + virBufferVSprintf(buf," %s\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," \n", format) < 0) + goto no_memory; + } + + if (virBufferAddLit(buf," \n") < 0) + goto no_memory; + if (virBufferVSprintf(buf," 0%o\n", + def->target.perms.mode) < 0) + goto no_memory; + if (virBufferVSprintf(buf," %d\n", + def->target.perms.uid) < 0) + goto no_memory; + if (virBufferVSprintf(buf," %d\n", + def->target.perms.gid) < 0) + goto no_memory; + + if (def->target.perms.label && + virBufferVSprintf(buf," \n", + def->target.perms.label) < 0) + goto no_memory; + if (virBufferAddLit(buf," \n") < 0) + goto no_memory; + + if (virBufferAddLit(buf, " \n") < 0) + goto no_memory; + + if (virBufferAddLit(buf,"\n") < 0) + goto no_memory; + + return virBufferContentAndFree(buf); + + no_memory: + virStorageReportError(conn, VIR_ERR_NO_MEMORY, _("xml")); + cleanup: + 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; + ssize_t 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, + _("config file")); + 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 (safewrite(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 --git a/src/storage_conf.h b/src/storage_conf.h new file mode 100644 index 0000000000..9ed5e9facd --- /dev/null +++ b/src/storage_conf.h @@ -0,0 +1,314 @@ +/* + * 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 + */ + +#ifndef __VIR_STORAGE_CONF_H__ +#define __VIR_STORAGE_CONF_H__ + +#include +#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, ...) + ATTRIBUTE_FORMAT(printf, 3, 4); + +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 --git a/src/storage_driver.c b/src/storage_driver.c new file mode 100644 index 0000000000..f97f706273 --- /dev/null +++ b/src/storage_driver.c @@ -0,0 +1,1261 @@ +/* + * 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 + */ + +#include + +#include +#include +#include +#include +#include +#include + +#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; + } + memset(names, 0, nnames); + 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; + } + memset(names, 0, nnames); + return -1; +} + +static virStoragePoolPtr +storagePoolCreate(virConnectPtr conn, + const char *xml, + unsigned int flags ATTRIBUTE_UNUSED) { + 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, + unsigned int flags ATTRIBUTE_UNUSED) { + 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, + unsigned int flags ATTRIBUTE_UNUSED) { + 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) { + virStorageReportError(obj->conn, VIR_ERR_NO_SUPPORT, + _("pool does not support volume delete")); + return -1; + } + if (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; + } + memset(names, 0, maxnames); + 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 vol 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, + 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 --git a/src/storage_driver.h b/src/storage_driver.h new file mode 100644 index 0000000000..9c332bbda7 --- /dev/null +++ b/src/storage_driver.h @@ -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 + */ + +#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 --git a/src/virterror.c b/src/virterror.c index 2629c37fc9..b2f860d475 100644 --- a/src/virterror.c +++ b/src/virterror.c @@ -678,6 +678,18 @@ __virErrorMsg(virErrorNumber error, const char *info) 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");