[libvirt] [PATCH rfc v2 0/8] fspool: backend directory

Hi everyone, we would like to propose the first implementation of fspool with directory backend. Filesystem pools is a facility to manage filesystems resources similar to how storage pools manages volume resources. Furthermore new API follows storage API closely where it makes sense. Uploading/downloading operations are not defined yet as it is not obvious how to make it properly. I guess we can use some kind of tar to make a stream from a filesystem. Please share you thoughts on this particular issue. The patchset provides 'dir' backend which simply expose directories in some directory in host filesystem. The virsh commands are provided too. So it is ready to play with, just replace 'pool' in xml descriptions and virsh commands to 'fspool' and 'volume' to 'item'. Examle and usage: Define: virsh -c qemu:///system fspool-define-as fs_pool_name dir --target /path/on/host Build virsh -c qemu:///system fspool-build fs_pool_name Start virsh -c qemu:///system fspool-start fs_pool_name Look inside virsh -c qemu:///system fspool-list (--all) fspool_name Fspool called POOL, on the host fs uses /fs_driver to hold items. virsh -c qemu:///system fspool-dumpxml POOL <fspool type='dir'> <name>POOL</name> <uuid>c57c9d7c-b1d5-4c45-ba9c-67f03d4da160</uuid> <capacity unit='bytes'>733722615808</capacity> <allocation unit='bytes'>1331486720</allocation> <available unit='bytes'>534810800128</available> <source> </source> <target> <path>/fs_driver</path> <permissions> <mode>0755</mode> <owner>0</owner> <group>0</group> </permissions> </target> </fspool> virsh -c qemu:///system fspool-info POOL Name: POOL UUID: c57c9d7c-b1d5-4c45-ba9c-67f03d4da160 State: running Persistent: yes Autostart: no autostart Capacity: 683.33 GiB Allocation: 1.24 GiB Available: 498.08 GiB virsh -c qemu+unix:///system item-list POOL Name Path ------------------------------------------------------------------------------ item1 /fs_driver/item1 item10 /fs_driver/item10 item11 /fs_driver/item11 item12 /fs_driver/item12 item15 /fs_driver/item15 Fspool of directory type is some directory on host fs that holds items (subdirs). Example of usage for items: virsh -c vz+unix:///system item-create-as POOL item1 1g - create item virsh -c qemu+unix:///system item-dumpxml item1 POOL <fsitem> <name>item1</name> <key>/fs_driver/item1</key> <source> </source> <capacity unit='bytes'>0</capacity> <allocation unit='bytes'>0</allocation> <target> <format type='dir'/> </target> </fsitem> virsh -c qemu+unix:///system item-info item1 POOL Name: item1 Type: dir Capacity: 683.33 GiB Allocation: 634.87 MiB Autostart: no autostart Capacity: 683.33 GiB Allocation: 1.24 GiB Available: 498.08 GiB virsh -c qemu+unix:///system item-list POOL Name Path ------------------------------------------------------------------------------ item1 /fs_driver/item1 item10 /fs_driver/item10 item11 /fs_driver/item11 item12 /fs_driver/item12 item15 /fs_driver/item15 v2: - renamed Fs to FS - in configure.ac script macro m4 is used - updates docs - created simple tests - updated virsh.pod - added information abot fspool in fotmatfs.html Olga Krishtal (8): fspool: introduce filesystem pools API fspool: usual driver based implementation of filesystem pools API fspools: configuration and internal representation fspools: acl support for filesystem pools remote: filesystem pools driver implementation fspool: default implementation of filesystem pools virsh: filesystem pools commands fspools: docs and tests for fspool directory backend configure.ac | 38 + daemon/Makefile.am | 4 + daemon/libvirtd.c | 10 + daemon/remote.c | 35 + docs/formatfs.html.in | 208 ++ docs/fspool.html.in | 41 + docs/schemas/fsitem.rng | 66 + docs/schemas/fspool.rng | 82 + docs/sitemap.html.in | 4 + include/libvirt/libvirt-fs.h | 260 +++ include/libvirt/libvirt.h | 1 + include/libvirt/virterror.h | 8 + m4/virt-driver-fspool.m4 | 52 + po/POTFILES.in | 6 + src/Makefile.am | 46 + src/access/viraccessdriver.h | 12 + src/access/viraccessdrivernop.c | 19 + src/access/viraccessdriverpolkit.c | 47 + src/access/viraccessdriverstack.c | 49 + src/access/viraccessmanager.c | 31 + src/access/viraccessmanager.h | 11 + src/access/viraccessperm.c | 15 +- src/access/viraccessperm.h | 124 ++ src/check-driverimpls.pl | 2 + src/conf/fs_conf.c | 1637 ++++++++++++++++ src/conf/fs_conf.h | 323 +++ src/datatypes.c | 154 ++ src/datatypes.h | 94 + src/driver-fs.h | 192 ++ src/driver.h | 3 + src/fs/fs_backend.h | 107 + src/fs/fs_backend_dir.c | 355 ++++ src/fs/fs_backend_dir.h | 8 + src/fs/fs_driver.c | 2058 ++++++++++++++++++++ src/fs/fs_driver.h | 10 + src/libvirt-fs.c | 1556 +++++++++++++++ src/libvirt.c | 28 + src/libvirt_private.syms | 53 + src/libvirt_public.syms | 42 + src/remote/remote_driver.c | 66 + src/remote/remote_protocol.x | 466 ++++- src/remote_protocol-structs | 165 ++ src/rpc/gendispatch.pl | 23 +- src/util/virerror.c | 37 + tests/Makefile.am | 12 + tests/fsitemxml2xmlin/item.xml | 13 + tests/fsitemxml2xmlout/item.xml | 13 + tests/fsitemxml2xmltest.c | 105 + .../dir-missing-target-path-invalid.xml | 12 + tests/fspoolxml2xmlin/fspool-dir.xml | 16 + tests/fspoolxml2xmlout/fspool-dir.xml | 16 + tests/fspoolxml2xmltest.c | 81 + tools/Makefile.am | 2 + tools/virsh-fsitem.c | 1292 ++++++++++++ tools/virsh-fsitem.h | 39 + tools/virsh-fspool.c | 1586 +++++++++++++++ tools/virsh-fspool.h | 38 + tools/virsh.c | 4 + tools/virsh.h | 9 + tools/virsh.pod | 252 ++- 60 files changed, 12028 insertions(+), 10 deletions(-) create mode 100644 docs/formatfs.html.in create mode 100644 docs/fspool.html.in create mode 100644 docs/schemas/fsitem.rng create mode 100644 docs/schemas/fspool.rng create mode 100644 include/libvirt/libvirt-fs.h create mode 100644 m4/virt-driver-fspool.m4 create mode 100644 src/conf/fs_conf.c create mode 100644 src/conf/fs_conf.h create mode 100644 src/driver-fs.h create mode 100644 src/fs/fs_backend.h create mode 100644 src/fs/fs_backend_dir.c create mode 100644 src/fs/fs_backend_dir.h create mode 100644 src/fs/fs_driver.c create mode 100644 src/fs/fs_driver.h create mode 100644 src/libvirt-fs.c create mode 100644 tests/fsitemxml2xmlin/item.xml create mode 100644 tests/fsitemxml2xmlout/item.xml create mode 100644 tests/fsitemxml2xmltest.c create mode 100644 tests/fspoolschemadata/dir-missing-target-path-invalid.xml create mode 100644 tests/fspoolxml2xmlin/fspool-dir.xml create mode 100644 tests/fspoolxml2xmlout/fspool-dir.xml create mode 100644 tests/fspoolxml2xmltest.c create mode 100644 tools/virsh-fsitem.c create mode 100644 tools/virsh-fsitem.h create mode 100644 tools/virsh-fspool.c create mode 100644 tools/virsh-fspool.h -- 1.8.3.1

API follows the API of storage pools closely where it doesn't differ for filesystems. Signed-off-by: Nikolay Shirokovskiy <nshirokovskiy@virtuozzo.com> Signed-off-by: Maxim Nestratov <mnestratov@virtuozzo.com> --- include/libvirt/libvirt-fs.h | 260 +++++++++++++++++++++++++++++++++++++++++++ include/libvirt/libvirt.h | 1 + 2 files changed, 261 insertions(+) create mode 100644 include/libvirt/libvirt-fs.h diff --git a/include/libvirt/libvirt-fs.h b/include/libvirt/libvirt-fs.h new file mode 100644 index 0000000..53ff766 --- /dev/null +++ b/include/libvirt/libvirt-fs.h @@ -0,0 +1,260 @@ +/* libvirt-fs.h + * Summary: APIs for management of filesystem pools and items + * Description: Provides APIs for the management of filesystem pools and items + * Author: Olga Krishtal <okrishtal@virtuozzo.com> + * + * Copyright (C) 2016 Parallels IP Holdings GmbH + * + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + */ + +#ifndef __VIR_LIBVIRT_FS_H__ +# define __VIR_LIBVIRT_FS_H__ + +# ifndef __VIR_LIBVIRT_H_INCLUDES__ +# error "Don't include this file directly, only use libvirt/libvirt.h" +# endif + +typedef enum { + VIR_FSPOOL_CREATE_NORMAL = 0, + + /* Create the fspool and perform fspool build without any flags */ + VIR_FSPOOL_CREATE_WITH_BUILD = 1 << 0, + + /* Create the fspool and perform fspool build using the + * exclusive to VIR_FSPOOL_CREATE_WITH_BUILD_NO_OVERWRITE */ + VIR_FSPOOL_CREATE_WITH_BUILD_OVERWRITE = 1 << 1, + + /* Create the pool and perform pool build using the + * VIR_FSPOOL_BUILD_NO_OVERWRITE flag. This is mutually + * exclusive to VIR_FSPOOL_CREATE_WITH_BUILD_OVERWRITE */ + VIR_FSPOOL_CREATE_WITH_BUILD_NO_OVERWRITE = 1 << 2, +} virFSPoolCreateFlags; + +typedef enum { + VIR_FSPOOL_BUILD_NEW = 0, /* Regular build from scratch */ + VIR_FSPOOL_BUILD_NO_OVERWRITE = (1 << 2), /* Do not overwrite existing pool */ + VIR_FSPOOL_BUILD_OVERWRITE = (1 << 3), /* Overwrite data */ +} virFSPoolBuildFlags; + +/** + * virFSPool: + * + * a virFSPool is a private structure representing a fspool + */ +typedef struct _virFSPool virFSPool; + +/** + * virFSPoolPtr: + * + * a virFSPoolPtr is pointer to a virFSPool private structure, this is the + * type used to reference a fspool in the API. + */ +typedef virFSPool *virFSPoolPtr; + +typedef enum { + VIR_FSPOOL_INACTIVE = 0, + VIR_FSPOOL_BUILDING = 1, + VIR_FSPOOL_RUNNING = 2, + +# ifdef VIR_ENUM_SENTINELS + VIR_FSPOOL_STATE_LAST +# endif +} virFSPoolState; + +typedef struct _virFSPoolInfo virFSPoolInfo; + +struct _virFSPoolInfo { + int state; /* virFSPoolState flags */ + unsigned long long capacity; /* Logical size bytes */ + unsigned long long allocation; /* Current allocation bytes */ + unsigned long long available; /* Remaining free space bytes */ +}; + +typedef virFSPoolInfo *virFSPoolInfoPtr; + +/** + * virFSItem: + * + * a virFSItem is a private structure representing a fspool item + */ +typedef struct _virFSItem virFSItem; + +/** + * virFSItemPtr: + * + * a virFSItemPtr is pointer to a virFSItem private structure, this is the + * type used to reference a fspool item in the API. + */ +typedef virFSItem *virFSItemPtr; + +typedef struct _virFSItemInfo virFSItemInfo; + +typedef enum { + VIR_FSITEM_DIR = 0, + VIR_FSITEM_LAST +} virFSItemType; + +struct _virFSItemInfo { + int type; /* virFSItemType flags */ + unsigned long long capacity; /* Logical size bytes */ + unsigned long long allocation; /* Current allocation bytes */ +}; + +typedef virFSItemInfo *virFSItemInfoPtr; +/* + * Get connection from fspool. + */ +virConnectPtr virFSPoolGetConnect(virFSPoolPtr fspool); + + +/* + * virConnectListAllFSPoolsFlags: + * + * Flags used to tune fspools returned by virConnectListAllFSPools(). + * Note that these flags come in groups; if all bits from a group are 0, + * then that group is not used to filter results. + */ +typedef enum { + VIR_CONNECT_LIST_FSPOOLS_INACTIVE = 1 << 0, + VIR_CONNECT_LIST_FSPOOLS_ACTIVE = 1 << 1, + + VIR_CONNECT_LIST_FSPOOLS_PERSISTENT = 1 << 2, + VIR_CONNECT_LIST_FSPOOLS_TRANSIENT = 1 << 3, + + VIR_CONNECT_LIST_FSPOOLS_AUTOSTART = 1 << 4, + VIR_CONNECT_LIST_FSPOOLS_NO_AUTOSTART = 1 << 5, + + /* List fspools by type */ + VIR_CONNECT_LIST_FSPOOLS_DIR = 1 << 6, +} virConnectListAllFSPoolsFlags; + +typedef enum { + VIR_FS_XML_INACTIVE = (1 << 0), /* dump inactive fspool/item information */ +} virFsXMLFlags; + + +int virConnectListAllFSPools(virConnectPtr conn, + virFSPoolPtr **fspools, + unsigned int flags); + +/* + * Lookup fspool by name or uuid + */ + +virFSPoolPtr virFSPoolLookupByName(virConnectPtr conn, + const char *name); +virFSPoolPtr virFSPoolLookupByUUID(virConnectPtr conn, + const unsigned char *uuid); +virFSPoolPtr virFSPoolLookupByUUIDString(virConnectPtr conn, + const char *uuid); +virFSPoolPtr virFSPoolLookupByItem(virFSItemPtr item); + + +/* + * Creating/destroying fspools + */ +virFSPoolPtr virFSPoolCreateXML(virConnectPtr conn, + const char *xmlDesc, + unsigned int flags); +virFSPoolPtr virFSPoolDefineXML(virConnectPtr conn, + const char *xmlDesc, + unsigned int flags); +int virFSPoolBuild(virFSPoolPtr fspool, + unsigned int flags); +int virFSPoolRefresh(virFSPoolPtr fspool, + unsigned int flags); +int virFSPoolUndefine(virFSPoolPtr fspool); +int virFSPoolCreate(virFSPoolPtr fspool, + unsigned int flags); +int virFSPoolDestroy(virFSPoolPtr fspool); +int virFSPoolDelete(virFSPoolPtr fspool, + unsigned int flags); +int virFSPoolRefresh(virFSPoolPtr fspool, + unsigned int flags); +int virFSPoolRef(virFSPoolPtr fspool); +int virFSPoolFree(virFSPoolPtr fspool); + +/* + * FSPool information + */ +const char * virFSPoolGetName(virFSPoolPtr fspool); +int virFSPoolGetUUID(virFSPoolPtr fspool, + unsigned char *uuid); +int virFSPoolGetUUIDString(virFSPoolPtr fspool, + char *buf); + +int virFSPoolGetInfo(virFSPoolPtr pool, + virFSPoolInfoPtr info); + +char * virFSPoolGetXMLDesc(virFSPoolPtr fspool, + unsigned int flags); +int virFSPoolGetAutostart(virFSPoolPtr fspool, + int *autostart); +int virFSPoolSetAutostart(virFSPoolPtr fspool, + int autostart); + + +/* + * List/lookup fs items within a fspool + */ +int virFSPoolNumOfItems(virFSPoolPtr fspool); +int virFSPoolListItems(virFSPoolPtr fspool, + char **const names, + int maxnames); +int virFSPoolListAllItems(virFSPoolPtr fspool, + virFSItemPtr **items, + unsigned int flags); + +virConnectPtr virFSItemGetConnect(virFSItemPtr item); + +/* + * Lookup itemumes based on various attributes + */ +virFSItemPtr virFSItemLookupByName(virFSPoolPtr fspool, + const char *name); +virFSItemPtr virFSItemLookupByKey(virConnectPtr conn, + const char *key); +virFSItemPtr virFSItemLookupByPath(virConnectPtr conn, + const char *path); + + +const char * virFSItemGetName(virFSItemPtr item); +const char * virFSItemGetKey(virFSItemPtr item); + +virFSItemPtr virFSItemCreateXML(virFSPoolPtr fspool, + const char *xmldesc, + unsigned int flags); +virFSItemPtr virFSItemCreateXMLFrom(virFSPoolPtr fspool, + const char *xmldesc, + virFSItemPtr cloneitem, + unsigned int flags); + +int virFSItemDelete(virFSItemPtr item, + unsigned int flags); +int virFSItemRef(virFSItemPtr item); +int virFSItemFree(virFSItemPtr item); + +int virFSItemGetInfo(virFSItemPtr item, + virFSItemInfoPtr info); +char * virFSItemGetXMLDesc(virFSItemPtr item, + unsigned int flags); + +char * virFSItemGetPath(virFSItemPtr item); + +int virFSPoolIsActive(virFSPoolPtr fspool); +int virFSPoolIsPersistent(virFSPoolPtr fspool); + + + +#endif /* __VIR_LIBVIRT_FS_H__ */ diff --git a/include/libvirt/libvirt.h b/include/libvirt/libvirt.h index 36f6d60..665414b 100644 --- a/include/libvirt/libvirt.h +++ b/include/libvirt/libvirt.h @@ -45,6 +45,7 @@ extern "C" { # include <libvirt/libvirt-secret.h> # include <libvirt/libvirt-storage.h> # include <libvirt/libvirt-stream.h> +# include <libvirt/libvirt-fs.h> # undef __VIR_LIBVIRT_H_INCLUDES__ # ifdef __cplusplus -- 1.8.3.1

Signed-off-by: Nikolay Shirokovskiy <nshirokovskiy@virtuozzo.com> Signed-off-by: Maxim Nestratov <mnestratov@virtuozzo.com> --- include/libvirt/virterror.h | 8 + po/POTFILES.in | 1 + src/Makefile.am | 3 + src/datatypes.c | 154 +++++ src/datatypes.h | 94 +++ src/driver-fs.h | 192 ++++++ src/driver.h | 1 + src/libvirt-fs.c | 1556 +++++++++++++++++++++++++++++++++++++++++++ src/libvirt_private.syms | 4 + src/libvirt_public.syms | 42 ++ src/util/virerror.c | 37 + 11 files changed, 2092 insertions(+) create mode 100644 src/driver-fs.h create mode 100644 src/libvirt-fs.c diff --git a/include/libvirt/virterror.h b/include/libvirt/virterror.h index efe83aa..6c9822d 100644 --- a/include/libvirt/virterror.h +++ b/include/libvirt/virterror.h @@ -132,6 +132,8 @@ typedef enum { VIR_FROM_PERF = 65, /* Error from perf */ + VIR_FROM_FSPOOL = 66, /* Error from fs pool */ + # ifdef VIR_ENUM_SENTINELS VIR_ERR_DOMAIN_LAST # endif @@ -317,6 +319,12 @@ typedef enum { VIR_ERR_NO_CLIENT = 96, /* Client was not found */ VIR_ERR_AGENT_UNSYNCED = 97, /* guest agent replies with wrong id to guest-sync command */ + VIR_ERR_INVALID_FSPOOL = 98, /* invalid fspool object */ + VIR_ERR_INVALID_FSITEM = 99, /* invalid fspool object */ + VIR_WAR_NO_FSPOOL = 100, /* failed to start fspool */ + VIR_ERR_NO_FSPOOL = 101, /* fspool not found */ + VIR_ERR_NO_FSITEM = 102, /* fstem not found */ + VIR_ERR_FSITEM_EXIST = 103, /* fspool item already exists */ } virErrorNumber; /** diff --git a/po/POTFILES.in b/po/POTFILES.in index 25dbc84..af00155 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -67,6 +67,7 @@ src/internal.h src/libvirt-admin.c src/libvirt-domain-snapshot.c src/libvirt-domain.c +src/libvirt-fs.c src/libvirt-host.c src/libvirt-lxc.c src/libvirt-network.c diff --git a/src/Makefile.am b/src/Makefile.am index 8ee5567..7b911bd 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -211,6 +211,7 @@ DRIVER_SOURCES = \ driver-secret.h \ driver-state.h \ driver-storage.h \ + driver-fs.h \ driver-stream.h \ internal.h \ $(DATATYPES_SOURCES) \ @@ -227,6 +228,7 @@ DRIVER_SOURCES = \ libvirt-secret.c \ libvirt-storage.c \ libvirt-stream.c \ + libvirt-fs.c \ locking/lock_manager.c locking/lock_manager.h \ locking/lock_driver.h \ locking/lock_driver_nop.h locking/lock_driver_nop.c \ @@ -2399,6 +2401,7 @@ libvirt_setuid_rpc_client_la_SOURCES = \ libvirt-storage.c \ libvirt-stream.c \ libvirt-lxc.c \ + libvirt-fs.c \ $(NULL) libvirt_setuid_rpc_client_la_LDFLAGS = \ diff --git a/src/datatypes.c b/src/datatypes.c index ff0c46f..03585f2 100644 --- a/src/datatypes.c +++ b/src/datatypes.c @@ -45,6 +45,8 @@ virClassPtr virSecretClass; virClassPtr virStreamClass; virClassPtr virStorageVolClass; virClassPtr virStoragePoolClass; +virClassPtr virFSPoolClass; +virClassPtr virFSItemClass; static void virConnectDispose(void *obj); static void virConnectCloseCallbackDataDispose(void *obj); @@ -58,6 +60,8 @@ static void virSecretDispose(void *obj); static void virStreamDispose(void *obj); static void virStorageVolDispose(void *obj); static void virStoragePoolDispose(void *obj); +static void virFSItemDispose(void *obj); +static void virFSPoolDispose(void *obj); virClassPtr virAdmConnectClass; virClassPtr virAdmConnectCloseCallbackDataClass; @@ -96,6 +100,8 @@ virDataTypesOnceInit(void) DECLARE_CLASS(virStream); DECLARE_CLASS(virStorageVol); DECLARE_CLASS(virStoragePool); + DECLARE_CLASS(virFSItem); + DECLARE_CLASS(virFSPool); DECLARE_CLASS_LOCKABLE(virAdmConnect); DECLARE_CLASS_LOCKABLE(virAdmConnectCloseCallbackData); @@ -597,7 +603,155 @@ virStorageVolDispose(void *obj) virObjectUnref(vol->conn); } +/** + * virGetFSPool: + * @conn: the hypervisor connection + * @name: pointer to the fs pool name + * @uuid: pointer to the uuid + * @privateData: pointer to driver specific private data + * @freeFunc: private data cleanup function pointer specific to driver + * + * Allocates a new storage pool object. When the object is no longer needed, + * virObjectUnref() must be called in order to not leak data. + * + * Returns a pointer to the storage pool object, or NULL on error. + */ +virFSPoolPtr +virGetFSPool(virConnectPtr conn, const char *name, + const unsigned char *uuid, + void *privateData, virFreeCallback freeFunc) +{ + virFSPoolPtr ret = NULL; + + if (virDataTypesInitialize() < 0) + return NULL; + + virCheckConnectGoto(conn, error); + virCheckNonNullArgGoto(name, error); + virCheckNonNullArgGoto(uuid, error); + + if (!(ret = virObjectNew(virFSPoolClass))) + goto error; + + if (VIR_STRDUP(ret->name, name) < 0) + goto error; + + ret->conn = virObjectRef(conn); + memcpy(&(ret->uuid[0]), uuid, VIR_UUID_BUFLEN); + + /* set the driver specific data */ + ret->privateData = privateData; + ret->privateDataFreeFunc = freeFunc; + + return ret; + + error: + virObjectUnref(ret); + return NULL; +} + + +/** + * virFSPoolDispose: + * @obj: the fs pool to release + * + * Unconditionally release all memory associated with fs pool. + * The pool object must not be used once this method returns. + * + * It will also unreference the associated connection object, + * which may also be released if its ref count hits zero. + */ +static void +virFSPoolDispose(void *obj) +{ + virFSPoolPtr fspool = obj; + char uuidstr[VIR_UUID_STRING_BUFLEN]; + + virUUIDFormat(fspool->uuid, uuidstr); + VIR_DEBUG("release fspool %p %s %s", fspool, fspool->name, uuidstr); + + if (fspool->privateDataFreeFunc) + fspool->privateDataFreeFunc(fspool->privateData); + VIR_FREE(fspool->name); + virObjectUnref(fspool->conn); +} + + +/** + * virGetFSItem: + * @conn: the hypervisor connection + * @fspool: fspool owning the item + * @name: pointer to the fsitem name + * @key: pointer to unique key of the fsitem + * @privateData: pointer to driver specific private data + * @freeFunc: private data cleanup function pointer specific to driver + * + * Allocates a new fsitem object. When the object is no longer needed, + * virObjectUnref() must be called in order to not leak data. + * + * Returns a pointer to the fsitem object, or NULL on error. + */ +virFSItemPtr +virGetFSItem(virConnectPtr conn, const char *fspool, const char *name, + const char *key, void *privateData, virFreeCallback freeFunc) +{ + virFSItemPtr ret = NULL; + + if (virDataTypesInitialize() < 0) + return NULL; + + virCheckConnectGoto(conn, error); + virCheckNonNullArgGoto(fspool, error); + virCheckNonNullArgGoto(name, error); + virCheckNonNullArgGoto(key, error); + + if (!(ret = virObjectNew(virFSItemClass))) + goto error; + + if (VIR_STRDUP(ret->fspool, fspool) < 0 || + VIR_STRDUP(ret->name, name) < 0 || + VIR_STRDUP(ret->key, key) < 0) + goto error; + + ret->conn = virObjectRef(conn); + + /* set driver specific data */ + ret->privateData = privateData; + ret->privateDataFreeFunc = freeFunc; + + return ret; + + error: + virObjectUnref(ret); + return NULL; +} + + +/** + * virFSItemDispose: + * @obj: the fsitem to release + * + * Unconditionally release all memory associated with a fsitem. + * The fsitem object must not be used once this method returns. + * + * It will also unreference the associated connection object, + * which may also be released if its ref count hits zero. + */ +static void +virFSItemDispose(void *obj) +{ + virFSItemPtr fsitem = obj; + VIR_DEBUG("release item %p %s", fsitem, fsitem->name); + + if (fsitem->privateDataFreeFunc) + fsitem->privateDataFreeFunc(fsitem->privateData); + + VIR_FREE(fsitem->key); + VIR_FREE(fsitem->name); + VIR_FREE(fsitem->fspool); + virObjectUnref(fsitem->conn); +} /** * virGetNodeDevice: * @conn: the hypervisor connection diff --git a/src/datatypes.h b/src/datatypes.h index 2b6adb4..ad5f9d3 100644 --- a/src/datatypes.h +++ b/src/datatypes.h @@ -40,6 +40,8 @@ extern virClassPtr virSecretClass; extern virClassPtr virStreamClass; extern virClassPtr virStorageVolClass; extern virClassPtr virStoragePoolClass; +extern virClassPtr virFSItemClass; +extern virClassPtr virFSPoolClass; extern virClassPtr virAdmConnectClass; extern virClassPtr virAdmServerClass; @@ -182,6 +184,46 @@ extern virClassPtr virAdmClientClass; } \ } while (0) +# define virCheckFSPoolReturn(obj, retval) \ + do { \ + virFSPoolPtr _pool = (obj); \ + if (!virObjectIsClass(_pool, virFSPoolClass) || \ + !virObjectIsClass(_pool->conn, virConnectClass)) { \ + virReportErrorHelper(VIR_FROM_FSPOOL, \ + VIR_ERR_INVALID_FSPOOL, \ + __FILE__, __FUNCTION__, __LINE__, \ + __FUNCTION__); \ + virDispatchError(NULL); \ + return retval; \ + } \ + } while (0) + +# define virCheckFSItemReturn(obj, retval) \ + do { \ + virFSItemPtr _item = (obj); \ + if (!virObjectIsClass(_item, virFSItemClass) || \ + !virObjectIsClass(_item->conn, virConnectClass)) { \ + virReportErrorHelper(VIR_FROM_FSPOOL, \ + VIR_ERR_INVALID_FSITEM, \ + __FILE__, __FUNCTION__, __LINE__, \ + __FUNCTION__); \ + virDispatchError(NULL); \ + return retval; \ + } \ + } while (0) +# define virCheckFSItemGoto(obj, label) \ + do { \ + virFSItemPtr _item = (obj); \ + if (!virObjectIsClass(_item, virFSItemClass) || \ + !virObjectIsClass(_item->conn, virConnectClass)) { \ + virReportErrorHelper(VIR_FROM_FSPOOL, \ + VIR_ERR_INVALID_FSITEM, \ + __FILE__, __FUNCTION__, __LINE__, \ + __FUNCTION__); \ + goto label; \ + } \ + } while (0) + # define virCheckNodeDeviceReturn(obj, retval) \ do { \ virNodeDevicePtr _node = (obj); \ @@ -457,6 +499,7 @@ struct _virConnect { virNodeDeviceDriverPtr nodeDeviceDriver; virSecretDriverPtr secretDriver; virNWFilterDriverPtr nwfilterDriver; + virFSDriverPtr fsDriver; /* Private data pointer which can be used by driver and * network driver as they wish. @@ -596,6 +639,45 @@ struct _virStorageVol { }; /** +* _virFSPool: +* +* Internal structure associated to a fs pool +*/ +struct _virFSPool { + virObject object; + virConnectPtr conn; /* pointer back to the connection */ + char *name; /* the storage pool external name */ + unsigned char uuid[VIR_UUID_BUFLEN]; /* the storage pool unique identifier */ + + /* Private data pointer which can be used by driver as they wish. + * Cleanup function pointer can be hooked to provide custom cleanup + * operation. + */ + void *privateData; + virFreeCallback privateDataFreeFunc; +}; + +/** +* _virFSItem: +* +* Internal structure associated to a fs pool item +*/ +struct _virFSItem { + virObject object; + virConnectPtr conn; /* pointer back to the connection */ + char *fspool; /* Pool name of owner */ + char *name; /* the storage vol external name */ + char *key; /* unique key for storage vol */ + + /* Private data pointer which can be used by driver as they wish. + * Cleanup function pointer can be hooked to provide custom cleanup + * operation. + */ + void *privateData; + virFreeCallback privateDataFreeFunc; +}; + +/** * _virNodeDevice: * * Internal structure associated with a node device @@ -687,6 +769,18 @@ virStorageVolPtr virGetStorageVol(virConnectPtr conn, const char *key, void *privateData, virFreeCallback freeFunc); +virFSPoolPtr virGetFSPool(virConnectPtr conn, + const char *name, + const unsigned char *uuid, + void *privateData, + virFreeCallback freeFunc); +virFSItemPtr virGetFSItem(virConnectPtr conn, + const char *pool, + const char *name, + const char *key, + void *privateData, + virFreeCallback freeFunc); + virNodeDevicePtr virGetNodeDevice(virConnectPtr conn, const char *name); virSecretPtr virGetSecret(virConnectPtr conn, diff --git a/src/driver-fs.h b/src/driver-fs.h new file mode 100644 index 0000000..37f6cb8 --- /dev/null +++ b/src/driver-fs.h @@ -0,0 +1,192 @@ +/* + * driver-fs.h: entry points for fs drivers + * Author: Olga Krishtal <okrishtal@virtuozzo.com> + * + * Copyright (C) 2016 Parallels IP Holdings GmbH + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + */ + +#ifndef __VIR_DRIVER_FS_H__ +# define __VIR_DRIVER_FS_H__ + +# ifndef __VIR_DRIVER_H_INCLUDES___ +# error "Don't include this file directly, only use driver.h" +# endif + +typedef int +(*virDrvConnectListAllFSPools)(virConnectPtr conn, + virFSPoolPtr **pools, + unsigned int flags); + +typedef virFSPoolPtr +(*virDrvFSPoolLookupByName)(virConnectPtr conn, + const char *name); + +typedef virFSPoolPtr +(*virDrvFSPoolLookupByUUID)(virConnectPtr conn, + const unsigned char *uuid); +typedef virFSPoolPtr +(*virDrvFSPoolLookupByItem)(virFSItemPtr item); + +typedef virFSPoolPtr +(*virDrvFSPoolCreateXML)(virConnectPtr conn, + const char *xmlDesc, + unsigned int flags); + +typedef virFSPoolPtr +(*virDrvFSPoolDefineXML)(virConnectPtr conn, + const char *xmlDesc, + unsigned int flags); + +typedef int +(*virDrvFSPoolUndefine)(virFSPoolPtr fspool); + +typedef int +(*virDrvFSPoolBuild)(virFSPoolPtr fspool, + unsigned int flags); + +typedef int +(*virDrvFSPoolCreate)(virFSPoolPtr fspool, + unsigned int flags); +typedef int +(*virDrvFSPoolDestroy)(virFSPoolPtr fspool); +typedef int +(*virDrvFSPoolRefresh)(virFSPoolPtr fspool, + unsigned int flags); + +typedef int +(*virDrvFSPoolDelete)(virFSPoolPtr fspool, + unsigned int flags); + +typedef int +(*virDrvFSPoolGetInfo)(virFSPoolPtr fspool, + virFSPoolInfoPtr info); + +typedef char * +(*virDrvFSPoolGetXMLDesc)(virFSPoolPtr fspool, + unsigned int flags); +typedef int +(*virDrvFSPoolGetAutostart)(virFSPoolPtr fspool, + int *autostart); +typedef int +(*virDrvFSPoolSetAutostart)(virFSPoolPtr fspool, + int autostart); + +typedef int +(*virDrvFSPoolNumOfItems)(virFSPoolPtr fspool); + +typedef int +(*virDrvFSPoolListItems)(virFSPoolPtr fspool, + char **const names, + int maxnames); + +typedef int +(*virDrvFSPoolListAllItems)(virFSPoolPtr fspool, + virFSItemPtr **items, + unsigned int flags); + +typedef virFSItemPtr +(*virDrvFSItemLookupByName)(virFSPoolPtr fspool, + const char *name); + +typedef virFSItemPtr +(*virDrvFSItemLookupByKey)(virConnectPtr fspool, + const char *key); + +typedef virFSItemPtr +(*virDrvFSItemLookupByPath)(virConnectPtr fspool, + const char *path); + +typedef virFSItemPtr +(*virDrvFSItemCreateXML)(virFSPoolPtr fspool, + const char *xmldesc, + unsigned int flags); + +typedef int +(*virDrvFSItemDelete)(virFSItemPtr item, + unsigned int flags); + + +typedef int +(*virDrvFSItemGetInfo)(virFSItemPtr item, + virFSItemInfoPtr info); + +typedef char * +(*virDrvFSItemGetXMLDesc)(virFSItemPtr fspool, + unsigned int flags); + +typedef char * +(*virDrvFSItemGetPath)(virFSItemPtr item); + +typedef virFSItemPtr +(*virDrvFSItemCreateXMLFrom)(virFSPoolPtr fspool, + const char *xmldesc, + virFSItemPtr cloneitem, + unsigned int flags); + +typedef struct _virFSDriver virFSDriver; +typedef virFSDriver *virFSDriverPtr; + +typedef int +(*virDrvFSPoolIsActive)(virFSPoolPtr fspool); + +typedef int +(*virDrvFSPoolIsPersistent)(virFSPoolPtr fspool); + + + +/** + * _virFSDriver: + * + * Structure associated to a storage driver, defining the various + * entry points for it. + */ +struct _virFSDriver { + const char *name; /* the name of the driver */ + virDrvConnectListAllFSPools connectListAllFSPools; + virDrvFSPoolLookupByName fsPoolLookupByName; + virDrvFSPoolLookupByUUID fsPoolLookupByUUID; + virDrvFSPoolLookupByItem fsPoolLookupByItem; + virDrvFSPoolCreateXML fsPoolCreateXML; + virDrvFSPoolDefineXML fsPoolDefineXML; + virDrvFSPoolBuild fsPoolBuild; + virDrvFSPoolUndefine fsPoolUndefine; + virDrvFSPoolCreate fsPoolCreate; + virDrvFSPoolDestroy fsPoolDestroy; + virDrvFSPoolDelete fsPoolDelete; + virDrvFSPoolRefresh fsPoolRefresh; + virDrvFSPoolGetInfo fsPoolGetInfo; + virDrvFSPoolGetXMLDesc fsPoolGetXMLDesc; + virDrvFSPoolGetAutostart fsPoolGetAutostart; + virDrvFSPoolSetAutostart fsPoolSetAutostart; + virDrvFSPoolNumOfItems fsPoolNumOfItems; + virDrvFSPoolListItems fsPoolListItems; + virDrvFSPoolListAllItems fsPoolListAllItems; + virDrvFSItemLookupByName fsItemLookupByName; + virDrvFSItemLookupByKey fsItemLookupByKey; + virDrvFSItemLookupByPath fsItemLookupByPath; + virDrvFSItemCreateXML fsItemCreateXML; + virDrvFSItemCreateXMLFrom fsItemCreateXMLFrom; + virDrvFSItemDelete fsItemDelete; + virDrvFSItemGetInfo fsItemGetInfo; + virDrvFSItemGetXMLDesc fsItemGetXMLDesc; + virDrvFSItemGetPath fsItemGetPath; + virDrvFSPoolIsActive fsPoolIsActive; + virDrvFSPoolIsPersistent fsPoolIsPersistent; +}; + + +#endif /* __VIR_DRIVER_FS_H__ */ diff --git a/src/driver.h b/src/driver.h index e4e382b..fb93083 100644 --- a/src/driver.h +++ b/src/driver.h @@ -72,6 +72,7 @@ typedef enum { # include "driver-state.h" # include "driver-stream.h" # include "driver-storage.h" +# include "driver-fs.h" # undef __VIR_DRIVER_H_INCLUDES___ diff --git a/src/libvirt-fs.c b/src/libvirt-fs.c new file mode 100644 index 0000000..4e2980f --- /dev/null +++ b/src/libvirt-fs.c @@ -0,0 +1,1556 @@ +/* + * libvirt-fs.c: entry points for virFS{Pool, Item}Ptr APIs + * Author: Olga Krishtal <okrishtal@virtuozzo.com> + * + * Copyright (C) 2016 Parallels IP Holdings GmbH + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + */ + +#include <config.h> + +#include "datatypes.h" +#include "virlog.h" + +VIR_LOG_INIT("libvirt.fs"); + +#define VIR_FROM_THIS VIR_FROM_FSPOOL + + +/** + * virFSPoolGetConnect: + * @fspool: pointer to a fspool + * + * Provides the connection pointer associated with fspool. The + * reference counter on the connection is not increased by this + * call. + * Returns the virConnectPtr or NULL in case of failure. + */ +virConnectPtr +virFSPoolGetConnect(virFSPoolPtr fspool) +{ + VIR_DEBUG("fspool=%p", fspool); + + virResetLastError(); + + virCheckFSPoolReturn(fspool, NULL); + + return fspool->conn; +} + + +/** + * virConnectListAllFSPools: + * @conn: Pointer to the hypervisor connection. + * @fspools: Pointer to a variable to store the array containing fspool + * objects or NULL if the list is not required (just returns number + * of fspools). + * @flags: bitwise-OR of virConnectListAllFSPoolsFlags. + * + * Collect the list of fspools, and allocate an array to store those + * objects. This API solves the race inherent between + * virConnectListFSPools and virConnectListDefinedFSPools. + * + * Normally, all fspools are returned; however, @flags can be used to + * filter the results for a smaller list of targeted fspools. The valid + * flags are divided into groups, where each group contains bits that + * describe mutually exclusive attributes of a fspool, and where all bits + * within a group describe all possible fspools. + * + * The only group (at the moment) of @flags is provided to filter the fspools by the types, + * the flags include: + * VIR_CONNECT_LIST_FSPOOLS_DIR + * VIR_CONNECT_LIST_FSPOOLS_VOLUME + * VIR_CONNECT_LIST_FSPOOLS_NETFS + * + * Returns the number of fs fspools found or -1 and sets @fspools to + * NULL in case of error. On success, the array stored into @fspools is + * guaranteed to have an extra allocated element set to NULL but not included + * in the return count, to make iteration easier. The caller is responsible + * for calling virFSPoolFree() on each array element, then calling + * free() on @fspools. + */ +int +virConnectListAllFSPools(virConnectPtr conn, + virFSPoolPtr **fspools, + unsigned int flags) +{ + VIR_DEBUG("conn=%p, fspools=%p, flags=%x", conn, fspools, flags); + + virResetLastError(); + + if (fspools) + *fspools = NULL; + + virCheckConnectReturn(conn, -1); + + if (conn->fsDriver && + conn->fsDriver->connectListAllFSPools) { + int ret; + ret = conn->fsDriver->connectListAllFSPools(conn, fspools, flags); + if (ret < 0) + goto error; + return ret; + } + + virReportUnsupportedError(); + + error: + virDispatchError(conn); + return -1; +} + +/** + * virFSPoolLookupByName: + * @conn: pointer to hypervisor connection + * @name: name of fspool to fetch + * + * Fetch fspool based on its unique name + * + * virFSPoolFree should be used to free the resources after the + * fs fspool object is no longer needed. + * + * Returns a virFSPoolPtr object, or NULL if no matching fspool is found + */ +virFSPoolPtr +virFSPoolLookupByName(virConnectPtr conn, + const char *name) +{ + VIR_DEBUG("conn=%p, name=%s", conn, NULLSTR(name)); + + virResetLastError(); + + virCheckConnectReturn(conn, NULL); + virCheckNonNullArgGoto(name, error); + + if (conn->fsDriver && conn->fsDriver->fsPoolLookupByName) { + virFSPoolPtr ret; + ret = conn->fsDriver->fsPoolLookupByName(conn, name); + if (!ret) + goto error; + return ret; + } + + virReportUnsupportedError(); + + error: + virDispatchError(conn); + return NULL; +} + + +/** + * virFSPoolLookupByUUID: + * @conn: pointer to hypervisor connection + * @uuid: globally unique id of fspool to fetch + * + * Fetch a fspool based on its globally unique id + * + * virFSPoolFree should be used to free the resources after the + * fs fspool object is no longer needed. + * + * Returns a virFSPoolPtr object, or NULL if no matching fspool is found + */ +virFSPoolPtr +virFSPoolLookupByUUID(virConnectPtr conn, + const unsigned char *uuid) +{ + VIR_UUID_DEBUG(conn, uuid); + + virResetLastError(); + + virCheckConnectReturn(conn, NULL); + virCheckNonNullArgGoto(uuid, error); + + if (conn->fsDriver && conn->fsDriver->fsPoolLookupByUUID) { + virFSPoolPtr ret; + ret = conn->fsDriver->fsPoolLookupByUUID(conn, uuid); + if (!ret) + goto error; + return ret; + } + + virReportUnsupportedError(); + + error: + virDispatchError(conn); + return NULL; +} + + +/** + * virFSPoolLookupByUUIDString: + * @conn: pointer to hypervisor connection + * @uuidstr: globally unique id of fspool to fetch + * + * Fetch a fs fspool based on its globally unique id + * + * virFSPoolFree should be used to free the resources after the + * fs fspool object is no longer needed. + * + * Returns a virFSPoolPtr object, or NULL if no matching fspool is found + */ +virFSPoolPtr +virFSPoolLookupByUUIDString(virConnectPtr conn, + const char *uuidstr) +{ + unsigned char uuid[VIR_UUID_BUFLEN]; + VIR_DEBUG("conn=%p, uuidstr=%s", conn, NULLSTR(uuidstr)); + + virResetLastError(); + + virCheckConnectReturn(conn, NULL); + virCheckNonNullArgGoto(uuidstr, error); + + if (virUUIDParse(uuidstr, uuid) < 0) { + virReportInvalidArg(uuidstr, + _("uuidstr in %s must be a valid UUID"), + __FUNCTION__); + goto error; + } + + return virFSPoolLookupByUUID(conn, uuid); + + error: + virDispatchError(conn); + return NULL; +} + + +/** + * virFSPoolLookupByItem: + * @item: pointer to fspool item + * + * Fetch a fspool which contains a particular item + * + * virFSPoolFree should be used to free the resources after the + * fspool object is no longer needed. + * + * Returns a virFSPoolPtr object, or NULL if no matching fspool is found + */ +virFSPoolPtr +virFSPoolLookupByItem(virFSItemPtr item) +{ + VIR_DEBUG("item=%p", item); + + virResetLastError(); + + virCheckFSItemReturn(item, NULL); + + if (item->conn->fsDriver && item->conn->fsDriver->fsPoolLookupByItem) { + virFSPoolPtr ret; + ret = item->conn->fsDriver->fsPoolLookupByItem(item); + if (!ret) + goto error; + return ret; + } + + virReportUnsupportedError(); + + error: + virDispatchError(item->conn); + return NULL; +} + + +/** + * virFSPoolCreateXML: + * @conn: pointer to hypervisor connection + * @xmlDesc: XML description for new fspool + * @flags: bitwise-OR of virFSPoolCreateFlags + * + * Create a new fspool based on its XML description. The + * fspool is not persistent, so its definition will disappear + * when it is destroyed, or if the host is restarted + * + * virFSPoolFree should be used to free the resources after the + *fspool object is no longer needed. + * + * Returns a virFSPoolPtr object, or NULL if creation failed + */ +virFSPoolPtr +virFSPoolCreateXML(virConnectPtr conn, + const char *xmlDesc, + unsigned int flags) +{ + VIR_DEBUG("conn=%p, xmlDesc=%s, flags=%x", conn, NULLSTR(xmlDesc), flags); + + virResetLastError(); + + virCheckConnectReturn(conn, NULL); + virCheckNonNullArgGoto(xmlDesc, error); + virCheckReadOnlyGoto(conn->flags, error); + + if (conn->fsDriver && conn->fsDriver->fsPoolCreateXML) { + virFSPoolPtr ret; + ret = conn->fsDriver->fsPoolCreateXML(conn, xmlDesc, flags); + if (!ret) + goto error; + return ret; + } + + virReportUnsupportedError(); + + error: + virDispatchError(conn); + return NULL; +} + + +/** + * virFSPoolDefineXML: + * @conn: pointer to hypervisor connection + * @xml: XML description for new fspool + * @flags: extra flags; not used yet, so callers should always pass 0 + * + * Define an inactive persistent fspool or modify an existing persistent + * one from the XML description. + * + * virFSPoolFree should be used to free the resources after the + * fspool object is no longer needed. + * + * Returns a virFSPoolPtr object, or NULL if creation failed + */ +virFSPoolPtr +virFSPoolDefineXML(virConnectPtr conn, + const char *xml, + unsigned int flags) +{ + VIR_DEBUG("conn=%p, xml=%s, flags=%x", conn, NULLSTR(xml), flags); + + virResetLastError(); + + virCheckConnectReturn(conn, NULL); + virCheckReadOnlyGoto(conn->flags, error); + virCheckNonNullArgGoto(xml, error); + + if (conn->fsDriver && conn->fsDriver->fsPoolDefineXML) { + virFSPoolPtr ret; + ret = conn->fsDriver->fsPoolDefineXML(conn, xml, flags); + if (!ret) + goto error; + return ret; + } + + virReportUnsupportedError(); + + error: + virDispatchError(conn); + return NULL; +} + + +/** + * virFSPoolBuild: + * @fspool: pointer to fspool + * @flags: bitwise-OR of virFSPoolBuildFlags + * + * Build the underlying fspool + * + * Returns 0 on success, or -1 upon failure + */ +int +virFSPoolBuild(virFSPoolPtr fspool, + unsigned int flags) +{ + virConnectPtr conn; + VIR_DEBUG("fspool=%p, flags=%x", fspool, flags); + + virResetLastError(); + + virCheckFSPoolReturn(fspool, -1); + conn = fspool->conn; + + virCheckReadOnlyGoto(conn->flags, error); + + if (conn->fsDriver && conn->fsDriver->fsPoolBuild) { + int ret; + ret = conn->fsDriver->fsPoolBuild(fspool, flags); + if (ret < 0) + goto error; + return ret; + } + + virReportUnsupportedError(); + + error: + virDispatchError(fspool->conn); + return -1; +} + +/** + * virFSPoolRefresh: + * @fspool: pointer to fspool + * @flags: extra flags; not used yet, so callers should always pass 0 + * + * Request that the fspool refresh its list of itemms. This may + * involve communicating with a remote server, and/or initializing + * new devices at the OS layer + * + * Returns 0 if the items list was refreshed, -1 on failure + */ +int +virFSPoolRefresh(virFSPoolPtr fspool, + unsigned int flags) +{ + virConnectPtr conn; + VIR_DEBUG("fspool=%p, flags=%x", fspool, flags); + + virResetLastError(); + + virCheckFSPoolReturn(fspool, -1); + conn = fspool->conn; + + virCheckReadOnlyGoto(conn->flags, error); + + if (conn->fsDriver && conn->fsDriver->fsPoolRefresh) { + int ret; + ret = conn->fsDriver->fsPoolRefresh(fspool, flags); + if (ret < 0) + goto error; + return ret; + } + + virReportUnsupportedError(); + + error: + virDispatchError(fspool->conn); + return -1; +} +/** + * virFSPoolUndefine: + * @fspool: pointer to fspool + * + * Undefine an inactive fspool + * + * Returns 0 on success, -1 on failure + */ +int +virFSPoolUndefine(virFSPoolPtr fspool) +{ + virConnectPtr conn; + VIR_DEBUG("fspool=%p", fspool); + + virResetLastError(); + + virCheckFSPoolReturn(fspool, -1); + conn = fspool->conn; + + virCheckReadOnlyGoto(conn->flags, error); + + if (conn->fsDriver && conn->fsDriver->fsPoolUndefine) { + int ret; + ret = conn->fsDriver->fsPoolUndefine(fspool); + if (ret < 0) + goto error; + return ret; + } + + virReportUnsupportedError(); + + error: + virDispatchError(fspool->conn); + return -1; +} + +/** + * virFSPoolCreate: + * @fspool: pointer to fspool + * @flags: bitwise-OR of virFSPoolCreateFlags + * + * Starts an inactive fspool + * + * Returns 0 on success, or -1 if it could not be started + */ +int +virFSPoolCreate(virFSPoolPtr fspool, + unsigned int flags) +{ + virConnectPtr conn; + VIR_DEBUG("fspool=%p, flags=%x", fspool, flags); + + virResetLastError(); + + virCheckFSPoolReturn(fspool, -1); + conn = fspool->conn; + + virCheckReadOnlyGoto(conn->flags, error); + + if (conn->fsDriver && conn->fsDriver->fsPoolCreate) { + int ret; + ret = conn->fsDriver->fsPoolCreate(fspool, flags); + if (ret < 0) + goto error; + return ret; + } + + virReportUnsupportedError(); + + error: + virDispatchError(fspool->conn); + return -1; +} + +/** + * virFSPoolDestroy: + * @fspool: pointer to fspool + * + * Destroy an active fspool. This will deactivate the + * fspool on the host, but keep any persistent config associated + * with it. If it has a persistent config it can later be + * restarted with virFSPoolCreate(). This does not free + * the associated virFSPoolPtr object. + * + * Returns 0 on success, or -1 if it could not be destroyed + */ +int +virFSPoolDestroy(virFSPoolPtr fspool) +{ + virConnectPtr conn; + VIR_DEBUG("fspool=%p", fspool); + + virResetLastError(); + + virCheckFSPoolReturn(fspool, -1); + conn = fspool->conn; + + virCheckReadOnlyGoto(conn->flags, error); + + if (conn->fsDriver && conn->fsDriver->fsPoolDestroy) { + int ret; + ret = conn->fsDriver->fsPoolDestroy(fspool); + if (ret < 0) + goto error; + return ret; + } + + virReportUnsupportedError(); + + error: + virDispatchError(fspool->conn); + return -1; +} + +/** + * virFSPoolDelete: + * @fspool: pointer to fspool + * @flags: bitwise-OR of virFSPoolDeleteFlags + * + * Delete the underlying fspool resources. This is + * a non-recoverable operation. The virFSPoolPtr object + * itself is not free'd. + * + * Returns 0 on success, or -1 if it could not be obliterate + */ +int +virFSPoolDelete(virFSPoolPtr fspool, + unsigned int flags) +{ + virConnectPtr conn; + VIR_DEBUG("fspool=%p, flags=%x", fspool, flags); + + virResetLastError(); + + virCheckFSPoolReturn(fspool, -1); + conn = fspool->conn; + + virCheckReadOnlyGoto(conn->flags, error); + + if (conn->fsDriver && conn->fsDriver->fsPoolDelete) { + int ret; + ret = conn->fsDriver->fsPoolDelete(fspool, flags); + if (ret < 0) + goto error; + return ret; + } + + virReportUnsupportedError(); + + error: + virDispatchError(fspool->conn); + return -1; +} + + +/** + * virFSPoolFree: + * @fspool: pointer to fspool + * + * Free a fs fspool object, releasing all memory associated with + * it. Does not change the state of the fspool on the host. + * + * Returns 0 on success, or -1 if it could not be free'd. + */ +int +virFSPoolFree(virFSPoolPtr fspool) +{ + VIR_DEBUG("fspool=%p", fspool); + + virResetLastError(); + + virCheckFSPoolReturn(fspool, -1); + + virObjectUnref(fspool); + return 0; + +} + + +/** + * virFSPoolRef: + * @fspool: the fspool to hold a reference on + * + * Increment the reference count on the fspool. For each + * additional call to this method, there shall be a corresponding + * call to virFSPoolFree to release the reference count, once + * the caller no longer needs the reference to this object. + * + * This method is typically useful for applications where multiple + * threads are using a connection, and it is required that the + * connection remain open until all threads have finished using + * it. ie, each new thread using a fspool would increment + * the reference count. + * + * Returns 0 in case of success, -1 in case of failure. + */ +int +virFSPoolRef(virFSPoolPtr fspool) +{ + VIR_DEBUG("fspool=%p refs=%d", fspool, fspool ? fspool->object.u.s.refs : 0); + + virResetLastError(); + + virCheckFSPoolReturn(fspool, -1); + + virObjectRef(fspool); + return 0; +} + +/** + * virFSPoolGetName: + * @fspool: pointer to fs fspool + * + * Fetch the locally unique name of the fs fspool + * + * Returns the name of the fspool, or NULL on error + */ +const char* +virFSPoolGetName(virFSPoolPtr fspool) +{ + VIR_DEBUG("fspool=%p", fspool); + + virResetLastError(); + + virCheckFSPoolReturn(fspool, NULL); + + return fspool->name; +} + + +/** + * virFSPoolGetUUID: + * @fspool: pointer to fspool + * @uuid: buffer of VIR_UUID_BUFLEN bytes in size + * + * Fetch the globally unique ID of the fspool + * + * Returns 0 on success, or -1 on error; + */ +int +virFSPoolGetUUID(virFSPoolPtr fspool, + unsigned char *uuid) +{ + VIR_DEBUG("fspool=%p, uuid=%p", fspool, uuid); + + virResetLastError(); + + virCheckFSPoolReturn(fspool, -1); + virCheckNonNullArgGoto(uuid, error); + + memcpy(uuid, &fspool->uuid[0], VIR_UUID_BUFLEN); + + return 0; + + error: + virDispatchError(fspool->conn); + return -1; +} + + +/** + * virFSPoolGetUUIDString: + * @fspool: pointer to fspool + * @buf: buffer of VIR_UUID_STRING_BUFLEN bytes in size + * + * Fetch the globally unique ID of the fspool as a string + * + * Returns 0 on success, or -1 on error; + */ +int +virFSPoolGetUUIDString(virFSPoolPtr fspool, + char *buf) +{ + VIR_DEBUG("fspool=%p, buf=%p", fspool, buf); + + virResetLastError(); + + virCheckFSPoolReturn(fspool, -1); + virCheckNonNullArgGoto(buf, error); + + virUUIDFormat(fspool->uuid, buf); + return 0; + + error: + virDispatchError(fspool->conn); + return -1; +} + + +/** + * virFSPoolGetInfo: + * @fspool: pointer to fs fspool + * @info: pointer at which to store info + * + * Get information about the fspool + * such as free space / usage summary + * + * Returns 0 on success, or -1 on failure. + */ +int +virFSPoolGetInfo(virFSPoolPtr fspool, + virFSPoolInfoPtr info) +{ + virConnectPtr conn; + VIR_DEBUG("fspool=%p, info=%p", fspool, info); + + virResetLastError(); + + if (info) + memset(info, 0, sizeof(*info)); + + virCheckFSPoolReturn(fspool, -1); + virCheckNonNullArgGoto(info, error); + + conn = fspool->conn; + + if (conn->fsDriver->fsPoolGetInfo) { + int ret; + ret = conn->fsDriver->fsPoolGetInfo(fspool, info); + if (ret < 0) + goto error; + return ret; + } + + virReportUnsupportedError(); + + error: + virDispatchError(fspool->conn); + return -1; +} + +/** + * virFSPoolGetAutostart: + * @fspool: pointer to fspool + * @autostart: location in which to store autostart flag + * + * Fetches the value of the autostart flag, which determines + * whether the fspool is automatically started at boot time + * + * Returns 0 on success, -1 on failure + */ +int +virFSPoolGetAutostart(virFSPoolPtr fspool, + int *autostart) +{ + virConnectPtr conn; + VIR_DEBUG("fspool=%p, autostart=%p", fspool, autostart); + + virResetLastError(); + + virCheckFSPoolReturn(fspool, -1); + virCheckNonNullArgGoto(autostart, error); + + conn = fspool->conn; + + if (conn->fsDriver && conn->fsDriver->fsPoolGetAutostart) { + int ret; + ret = conn->fsDriver->fsPoolGetAutostart(fspool, autostart); + if (ret < 0) + goto error; + return ret; + } + + virReportUnsupportedError(); + + error: + virDispatchError(fspool->conn); + return -1; +} + + +/** + * virFSPoolSetAutostart: + * @fspool: pointer to fspool + * @autostart: new flag setting + * + * Sets the autostart flag + * + * Returns 0 on success, -1 on failure + */ +int +virFSPoolSetAutostart(virFSPoolPtr fspool, + int autostart) +{ + virConnectPtr conn; + VIR_DEBUG("fspool=%p, autostart=%d", fspool, autostart); + + virResetLastError(); + + virCheckFSPoolReturn(fspool, -1); + conn = fspool->conn; + + virCheckReadOnlyGoto(conn->flags, error); + + if (conn->fsDriver && conn->fsDriver->fsPoolSetAutostart) { + int ret; + ret = conn->fsDriver->fsPoolSetAutostart(fspool, autostart); + if (ret < 0) + goto error; + return ret; + } + + virReportUnsupportedError(); + + error: + virDispatchError(fspool->conn); + return -1; +} + +/** + * virFSPoolGetXMLDesc: + * @fspool: pointer to fspool + * @flags: bitwise-OR of virFsXMLFlags + * + * Fetch an XML document describing all aspects of the + * fs fspool. This is suitable for later feeding back + * into the virFSPoolCreateXML method. + * + * Returns a XML document (caller frees), or NULL on error + */ +char * +virFSPoolGetXMLDesc(virFSPoolPtr fspool, + unsigned int flags) +{ + virConnectPtr conn; + VIR_DEBUG("fspool=%p, flags=%x", fspool, flags); + + virResetLastError(); + + virCheckFSPoolReturn(fspool, NULL); + conn = fspool->conn; + + if (conn->fsDriver && conn->fsDriver->fsPoolGetXMLDesc) { + char *ret; + ret = conn->fsDriver->fsPoolGetXMLDesc(fspool, flags); + if (!ret) + goto error; + return ret; + } + + virReportUnsupportedError(); + + error: + virDispatchError(fspool->conn); + return NULL; +} + + +/** + * virFSPoolListAllItems: + * @fspool: Pointer to fspool + * @items: Pointer to a variable to store the array containing fs item + * objects or NULL if the list is not required (just returns number + * of items). + * @flags: extra flags; not used yet, so callers should always pass 0 + * + * Collect the list of fsitems, and allocate an array to store those + * objects. + * + * Returns the number of fs items found or -1 and sets @items to + * NULL in case of error. On success, the array stored into @items is + * guaranteed to have an extra allocated element set to NULL but not included + * in the return count, to make iteration easier. The caller is responsible + * for calling virFSItemFree() on each array element, then calling + * free() on @items. + */ +int +virFSPoolListAllItems(virFSPoolPtr fspool, + virFSItemPtr **items, + unsigned int flags) +{ + VIR_DEBUG("fspool=%p, items=%p, flags=%x", fspool, items, flags); + + virResetLastError(); + + virCheckFSPoolReturn(fspool, -1); + + if (fspool->conn->fsDriver && + fspool->conn->fsDriver->fsPoolListAllItems) { + int ret; + ret = fspool->conn->fsDriver->fsPoolListAllItems(fspool, items, flags); + if (ret < 0) + goto error; + return ret; + } + + virReportUnsupportedError(); + + error: + virDispatchError(fspool->conn); + return -1; +} + + +/** + * virFSPoolNumOfItems: + * @fspool: pointer to fspool + * + * Fetch the number of items within a fspool + * + * Returns the number of fspools, or -1 on failure + */ +int +virFSPoolNumOfItems(virFSPoolPtr fspool) +{ + VIR_DEBUG("fspool=%p", fspool); + + virResetLastError(); + + virCheckFSPoolReturn(fspool, -1); + + if (fspool->conn->fsDriver && fspool->conn->fsDriver->fsPoolNumOfItems) { + int ret; + ret = fspool->conn->fsDriver->fsPoolNumOfItems(fspool); + if (ret < 0) + goto error; + return ret; + } + + virReportUnsupportedError(); + + error: + virDispatchError(fspool->conn); + return -1; +} + + +/** + * virFSPoolListItems: + * @fspool: pointer to fspool + * @names: array in which to fsitem names + * @maxnames: size of names array + * + * Fetch list of fs item names, limiting to + * at most maxnames. + * + * To list the item objects directly, see virFSPoolListAllItems(). + * + * Returns the number of names fetched, or -1 on error + */ +int +virFSPoolListItems(virFSPoolPtr fspool, + char **const names, + int maxnames) +{ + VIR_DEBUG("fspool=%p, names=%p, maxnames=%d", fspool, names, maxnames); + + virResetLastError(); + + virCheckFSPoolReturn(fspool, -1); + virCheckNonNullArgGoto(names, error); + virCheckNonNegativeArgGoto(maxnames, error); + + if (fspool->conn->fsDriver && fspool->conn->fsDriver->fsPoolListItems) { + int ret; + ret = fspool->conn->fsDriver->fsPoolListItems(fspool, names, maxnames); + if (ret < 0) + goto error; + return ret; + } + + virReportUnsupportedError(); + + error: + virDispatchError(fspool->conn); + return -1; +} + + +/** + * virFSItemGetConnect: + * @item: pointer to a fspool + * + * Provides the connection pointer associated with a fs item. The + * reference counter on the connection is not increased by this + * call. + * + * WARNING: When writing libvirt bindings in other languages, do + * not use this function. Instead, store the connection and + * the item object together. + * + * Returns the virConnectPtr or NULL in case of failure. + */ +virConnectPtr +virFSItemGetConnect(virFSItemPtr item) +{ + VIR_DEBUG("item=%p", item); + + virResetLastError(); + + virCheckFSItemReturn(item, NULL); + + return item->conn; +} + + +/** + * virFSItemLookupByName: + * @fspool: pointer to fspool + * @name: name of fsitem + * + * Fetch a pointer to a fs item based on its name + * within a fspool + * + * virFSItemFree should be used to free the resources after the + * fs item object is no longer needed. + * + * Returns a fsitem, or NULL if not found / error + */ +virFSItemPtr +virFSItemLookupByName(virFSPoolPtr fspool, + const char *name) +{ + VIR_DEBUG("fspool=%p, name=%s", fspool, NULLSTR(name)); + + virResetLastError(); + + virCheckFSPoolReturn(fspool, NULL); + virCheckNonNullArgGoto(name, error); + + if (fspool->conn->fsDriver && fspool->conn->fsDriver->fsItemLookupByName) { + virFSItemPtr ret; + ret = fspool->conn->fsDriver->fsItemLookupByName(fspool, name); + if (!ret) + goto error; + return ret; + } + + virReportUnsupportedError(); + + error: + virDispatchError(fspool->conn); + return NULL; +} + + +/** + * virFSItemLookupByKey: + * @conn: pointer to hypervisor connection + * @key: globally unique key + * + * Fetch a pointer to a fspool item based on its + * globally unique key + * + * virFSItemFree should be used to free the resources after the + * fs item object is no longer needed. + * + * Returns a fs item, or NULL if not found / error + */ +virFSItemPtr +virFSItemLookupByKey(virConnectPtr conn, + const char *key) +{ + VIR_DEBUG("conn=%p, key=%s", conn, NULLSTR(key)); + + virResetLastError(); + + virCheckConnectReturn(conn, NULL); + virCheckNonNullArgGoto(key, error); + + if (conn->fsDriver && conn->fsDriver->fsItemLookupByKey) { + virFSItemPtr ret; + ret = conn->fsDriver->fsItemLookupByKey(conn, key); + if (!ret) + goto error; + return ret; + } + + virReportUnsupportedError(); + + error: + virDispatchError(conn); + return NULL; +} + + +/** + * virFSItemLookupByPath: + * @conn: pointer to hypervisor connection + * @path: locally unique path + * + * Fetch a pointer to a fs item based on its + * locally (host) unique path + * + * virFSItemFree should be used to free the resources after the + * fs item object is no longer needed. + * + * Returns a fs item, or NULL if not found / error + */ +virFSItemPtr +virFSItemLookupByPath(virConnectPtr conn, + const char *path) +{ + VIR_DEBUG("conn=%p, path=%s", conn, NULLSTR(path)); + + virResetLastError(); + + virCheckConnectReturn(conn, NULL); + virCheckNonNullArgGoto(path, error); + + if (conn->fsDriver && conn->fsDriver->fsItemLookupByPath) { + virFSItemPtr ret; + ret = conn->fsDriver->fsItemLookupByPath(conn, path); + if (!ret) + goto error; + return ret; + } + + virReportUnsupportedError(); + + error: + virDispatchError(conn); + return NULL; +} + + +/** + * virFSItemGetName: + * @item: pointer to fsitem + * + * Fetch the fsitem name. This is unique + * within the scope of a fspool + * + * Returns the item name, or NULL on error + */ +const char* +virFSItemGetName(virFSItemPtr item) +{ + VIR_DEBUG("item=%p", item); + + virResetLastError(); + + virCheckFSItemReturn(item, NULL); + + return item->name; +} + + +/** + * virFSItemGetKey: + * @item: pointer to fspool item + * + * Fetch the fsitem key. This is globally + * unique, so the same item will have the same + * key no matter what host it is accessed from + * + * Returns the item key, or NULL on error + */ +const char* +virFSItemGetKey(virFSItemPtr item) +{ + VIR_DEBUG("item=%p", item); + + virResetLastError(); + + virCheckFSItemReturn(item, NULL); + + return item->key; +} + + +/** + * virFSItemCreateXML: + * @fspool: pointer to fspool + * @xmlDesc: description of item to create + * @flags: bitwise-OR of virFSItemCreateFlags + * + * Create a fs item within a fspool based + * on an XML description. + * + * virFSItemFree should be used to free the resources after the + * fs item object is no longer needed. + * + * Returns the fs item, or NULL on error + */ +virFSItemPtr +virFSItemCreateXML(virFSPoolPtr fspool, + const char *xmlDesc, + unsigned int flags) +{ + VIR_DEBUG("fspool=%p, xmlDesc=%s, flags=%x", fspool, NULLSTR(xmlDesc), flags); + + virResetLastError(); + + virCheckFSPoolReturn(fspool, NULL); + virCheckNonNullArgGoto(xmlDesc, error); + virCheckReadOnlyGoto(fspool->conn->flags, error); + + if (fspool->conn->fsDriver && fspool->conn->fsDriver->fsItemCreateXML) { + virFSItemPtr ret; + ret = fspool->conn->fsDriver->fsItemCreateXML(fspool, xmlDesc, flags); + if (!ret) + goto error; + return ret; + } + + virReportUnsupportedError(); + + error: + virDispatchError(fspool->conn); + return NULL; +} + + +/** + * virFSItemCreateXMLFrom: + * @fspool: pointer to parent fspool for the new item + * @xmlDesc: description of item to create + * @cloneitem: fspool item to use as input + * @flags: bitwise-OR of virFSItemCreateFlags + * + * Create a fs item in the parent fspool, using the + * 'cloneitem' item as input. Information for the new + * item (name, perms) are passed via a typical item + * XML description. + * + * virFSItemFree should be used to free the resources after the + * fs item object is no longer needed. + * + * Returns the fs item, or NULL on error + */ +virFSItemPtr +virFSItemCreateXMLFrom(virFSPoolPtr fspool, + const char *xmlDesc, + virFSItemPtr cloneitem, + unsigned int flags) +{ + VIR_DEBUG("fspool=%p, xmlDesc=%s, cloneitem=%p, flags=%x", + fspool, NULLSTR(xmlDesc), cloneitem, flags); + + virResetLastError(); + + virCheckFSPoolReturn(fspool, NULL); + virCheckFSItemGoto(cloneitem, error); + virCheckNonNullArgGoto(xmlDesc, error); + virCheckReadOnlyGoto(fspool->conn->flags | cloneitem->conn->flags, error); + + if (fspool->conn->fsDriver && + fspool->conn->fsDriver->fsItemCreateXMLFrom) { + virFSItemPtr ret; + ret = fspool->conn->fsDriver->fsItemCreateXMLFrom(fspool, xmlDesc, + cloneitem, flags); + if (!ret) + goto error; + return ret; + } + + virReportUnsupportedError(); + + error: + virDispatchError(fspool->conn); + return NULL; +} + +/** + * virFSItemDelete: + * @item: pointer to fspool item + * @flags: bitwise-OR of virFSItemDeleteFlags + * + * Delete the fs item from the fspool + * + * Returns 0 on success, or -1 on error + */ +int +virFSItemDelete(virFSItemPtr item, + unsigned int flags) +{ + virConnectPtr conn; + VIR_DEBUG("item=%p, flags=%x", item, flags); + + virResetLastError(); + + virCheckFSItemReturn(item, -1); + conn = item->conn; + + virCheckReadOnlyGoto(conn->flags, error); + + if (conn->fsDriver && conn->fsDriver->fsItemDelete) { + int ret; + ret = conn->fsDriver->fsItemDelete(item, flags); + if (ret < 0) + goto error; + return ret; + } + + virReportUnsupportedError(); + + error: + virDispatchError(item->conn); + return -1; +} + +/** + * virFSItemFree: + * @item: pointer to fspool item + * + * Release the fs item handle. The underlying + * fs item continues to exist. + * + * Returns 0 on success, or -1 on error + */ +int +virFSItemFree(virFSItemPtr item) +{ + VIR_DEBUG("item=%p", item); + + virResetLastError(); + + virCheckFSItemReturn(item, -1); + + virObjectUnref(item); + return 0; +} + + +/** + * virFSItemRef: + * @item: the item to hold a reference on + * + * Increment the reference count on the item. For each + * additional call to this method, there shall be a corresponding + * call to virFSItemFree to release the reference count, once + * the caller no longer needs the reference to this object. + * + * This method is typically useful for applications where multiple + * threads are using a connection, and it is required that the + * connection remain open until all threads have finished using + * it. ie, each new thread using a item would increment + * the reference count. + * + * Returns 0 in case of success, -1 in case of failure. + */ +int +virFSItemRef(virFSItemPtr item) +{ + VIR_DEBUG("item=%p refs=%d", item, item ? item->object.u.s.refs : 0); + + virResetLastError(); + + virCheckFSItemReturn(item, -1); + + virObjectRef(item); + return 0; +} + + +/** + * virFSItemGetInfo: + * @item: pointer to fspool item + * @info: pointer at which to store info + * + * Fetches itematile information about the fspool + * item such as its current allocation + * + * Returns 0 on success, or -1 on failure + */ +int +virFSItemGetInfo(virFSItemPtr item, + virFSItemInfoPtr info) +{ + virConnectPtr conn; + VIR_DEBUG("item=%p, info=%p", item, info); + + virResetLastError(); + + if (info) + memset(info, 0, sizeof(*info)); + + virCheckFSItemReturn(item, -1); + virCheckNonNullArgGoto(info, error); + + conn = item->conn; + + if (conn->fsDriver->fsItemGetInfo) { + int ret; + ret = conn->fsDriver->fsItemGetInfo(item, info); + if (ret < 0) + goto error; + return ret; + } + + virReportUnsupportedError(); + + error: + virDispatchError(item->conn); + return -1; +} + + +/** + * virFSItemGetXMLDesc: + * @item: pointer to fsitem + * @flags: extra flags; not used yet, so callers should always pass 0 + * + * Fetch an XML document describing all aspects of + * the fsitem + * + * Returns the XML document, or NULL on error + */ +char * +virFSItemGetXMLDesc(virFSItemPtr item, + unsigned int flags) +{ + virConnectPtr conn; + VIR_DEBUG("item=%p, flags=%x", item, flags); + + virResetLastError(); + + virCheckFSItemReturn(item, NULL); + conn = item->conn; + + if (conn->fsDriver && conn->fsDriver->fsItemGetXMLDesc) { + char *ret; + ret = conn->fsDriver->fsItemGetXMLDesc(item, flags); + if (!ret) + goto error; + return ret; + } + + virReportUnsupportedError(); + + error: + virDispatchError(item->conn); + return NULL; +} + + +/** + * virFSItemGetPath: + * @item: pointer to fspool item + * + * Fetch the fsitem path. Depending on the fspool + * configuration this is either persistent across hosts, + * or dynamically assigned at fspool startup. Consult + * fspool documentation for information on getting the + * persistent naming + * + * Returns the fs item path, or NULL on error. The + * caller must free() the returned path after use. + */ +char * +virFSItemGetPath(virFSItemPtr item) +{ + virConnectPtr conn; + VIR_DEBUG("item=%p", item); + + virResetLastError(); + + virCheckFSItemReturn(item, NULL); + conn = item->conn; + + if (conn->fsDriver && conn->fsDriver->fsItemGetPath) { + char *ret; + ret = conn->fsDriver->fsItemGetPath(item); + if (!ret) + goto error; + return ret; + } + + virReportUnsupportedError(); + + error: + virDispatchError(item->conn); + return NULL; +} + + +/** + * virFSPoolIsActive: + */ +int +virFSPoolIsActive(virFSPoolPtr fspool) +{ + VIR_DEBUG("fspool=%p", fspool); + + virResetLastError(); + + virCheckFSPoolReturn(fspool, -1); + + if (fspool->conn->fsDriver->fsPoolIsActive) { + int ret; + ret = fspool->conn->fsDriver->fsPoolIsActive(fspool); + if (ret < 0) + goto error; + return ret; + } + + virReportUnsupportedError(); + error: + virDispatchError(fspool->conn); + return -1; +} + + +/** + * virFSPoolIsPersistent: + * @fspool: pointer to the fspool object + * + * Determine if the fspool has a persistent configuration + * which means it will still exist after shutting down + * + * Returns 1 if persistent, 0 if transient, -1 on error + */ +int +virFSPoolIsPersistent(virFSPoolPtr fspool) +{ + VIR_DEBUG("fspool=%p", fspool); + + virResetLastError(); + + virCheckFSPoolReturn(fspool, -1); + + if (fspool->conn->fsDriver->fsPoolIsPersistent) { + int ret; + ret = fspool->conn->fsDriver->fsPoolIsPersistent(fspool); + if (ret < 0) + goto error; + return ret; + } + + virReportUnsupportedError(); + error: + virDispatchError(fspool->conn); + return -1; +} diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 6a77e46..db8d337 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -982,9 +982,13 @@ virConnectCloseCallbackDataRegister; virConnectCloseCallbackDataUnregister; virDomainClass; virDomainSnapshotClass; +virFSItemClass; +virFSPoolClass; virGetConnect; virGetDomain; virGetDomainSnapshot; +virGetFSItem; +virGetFSPool; virGetInterface; virGetNetwork; virGetNodeDevice; diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index e01604c..48c60e3 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -738,6 +738,48 @@ LIBVIRT_2.0.0 { virConnectStoragePoolEventDeregisterAny; virDomainGetGuestVcpus; virDomainSetGuestVcpus; + virFSPoolGetConnect; + virConnectListAllFSPools; + virFSPoolListAllItems; + virFSPoolLookupByName; + virFSPoolLookupByUUID; + virFSPoolLookupByUUIDString; + virFSPoolLookupByItem; + virFSPoolCreateXML; + virFSPoolDefineXML; + virFSPoolUndefine; + virFSPoolCreate; + virFSPoolBuild; + virFSPoolDestroy; + virFSPoolDelete; + virFSPoolRefresh; + virFSPoolFree; + virFSPoolGetName; + virFSPoolGetUUID; + virFSPoolGetUUIDString; + virFSPoolGetInfo; + virFSPoolGetXMLDesc; + virFSPoolSetAutostart; + virFSPoolGetAutostart; + virFSPoolNumOfItems; + virFSPoolListItems; + virFSPoolRef; + virFSItemRef; + virFSItemCreateXMLFrom; + virFSItemGetConnect; + virFSItemLookupByName; + virFSItemLookupByKey; + virFSItemLookupByPath; + virFSItemCreateXML; + virFSItemDelete; + virFSItemFree; + virFSItemGetName; + virFSItemGetKey; + virFSItemGetInfo; + virFSItemGetXMLDesc; + virFSItemGetPath; + virFSPoolIsActive; + virFSPoolIsPersistent; } LIBVIRT_1.3.3; LIBVIRT_2.2.0 { diff --git a/src/util/virerror.c b/src/util/virerror.c index 2958308..3d60c82 100644 --- a/src/util/virerror.c +++ b/src/util/virerror.c @@ -138,6 +138,7 @@ VIR_ENUM_IMPL(virErrorDomain, VIR_ERR_DOMAIN_LAST, "Xen XL Config", "Perf", + "FS Driver" ) @@ -1400,6 +1401,42 @@ virErrorMsg(virErrorNumber error, const char *info) else errmsg = _("guest agent replied with wrong id to guest-sync command: %s"); break; + case VIR_ERR_INVALID_FSPOOL: + if (info == NULL) + errmsg = _("invalid fspool poiter in"); + else + errmsg = _("invalid fspool pointer in %s"); + break; + case VIR_ERR_INVALID_FSITEM: + if (info == NULL) + errmsg = _("invalid fsitem pointer in"); + else + errmsg = _("invalid fsitem pointer in %s"); + break; + case VIR_WAR_NO_FSPOOL: + if (info == NULL) + errmsg = _("Failed to find a FS driver"); + else + errmsg = _("Failed to find a FS driver: %s"); + break; + case VIR_ERR_NO_FSPOOL: + if (info == NULL) + errmsg = _("fspool not found"); + else + errmsg = _("fspool not found: %s"); + break; + case VIR_ERR_NO_FSITEM: + if (info == NULL) + errmsg = _("fspool item not found"); + else + errmsg = _("fspool item not found: %s"); + break; + case VIR_ERR_FSITEM_EXIST: + if (info == NULL) + errmsg = _("fspool item exists"); + else + errmsg = _("fspool item exists: %s"); + break; } return errmsg; } -- 1.8.3.1

Signed-off-by: Nikolay Shirokovskiy <nshirokovskiy@virtuozzo.com> Signed-off-by: Maxim Nestratov <mnestratov@virtuozzo.com> --- docs/formatfs.html.in | 208 ++++++ docs/fspool.html.in | 41 ++ docs/sitemap.html.in | 4 + po/POTFILES.in | 1 + src/Makefile.am | 5 + src/conf/fs_conf.c | 1637 ++++++++++++++++++++++++++++++++++++++++++++++ src/conf/fs_conf.h | 323 +++++++++ src/libvirt_private.syms | 42 ++ 8 files changed, 2261 insertions(+) create mode 100644 docs/formatfs.html.in create mode 100644 docs/fspool.html.in create mode 100644 src/conf/fs_conf.c create mode 100644 src/conf/fs_conf.h diff --git a/docs/formatfs.html.in b/docs/formatfs.html.in new file mode 100644 index 0000000..5d3ee17 --- /dev/null +++ b/docs/formatfs.html.in @@ -0,0 +1,208 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> + <body> + <h1>Filesystem pool and item XML format</h1> + + <ul id="toc"></ul> + + <h2><a name="FSPool">Filesystem pool XML</a></h2> + + <p> + Filesystem pools is a facility to manage filesystems. They has a lot in common with + storage pools (nearly the same api and way of managment). However the managment units + are completely different. To avoid the misunderstanding in fspool usage use this XML + description. See <a href="fspool.html">fspool driver page</a> + </p> + + <p> + The root element is named <code>fspool</code>. It has attribute <code>type</code>, + at the moment fspool has only one type <code>dir</code>. It represents fspool that + holds filesystems in subdirectories on a host filesystem. + </p> + + <h3><a name="FSPoolFirst">General metadata</a></h3> + + <pre> + <fspool type="dir"> + <name>virfs</name> + <uuid>4584ee21-db40-4e98-980e-44802c47b62f</uuid> + <allocation>10000000</allocation> + <capacity>50000000</capacity> + <available>40000000</available> + ...</pre> + + <dl> + <dt><code>name</code></dt> + <dd>Providing a name for the fspool which is unique to the host. + This is mandatory when defining a filesystem pool. <span class="since">Since 2.2.1</span></dd> + <dt><code>uuid</code></dt> + <dd>Providing an identifier for the fspool which is globally unique. + This is optional when defining a fspool, a UUID will be generated if + omitted. <span class="since">Since 2.2.1</span></dd> + <dt><code>allocation</code></dt> + <dd>Providing the total storage allocation for the pool. This value is in bytes. + This is not applicable when creating a fspool. <span class="since">Since 2.2.1</span></dd> + <dt><code>capacity</code></dt> + <dd>Providing the total capacity for the fspool. This value is in bytes. This + is not applicable when creating a fspool. <span class="since">Since 2.2.1</span></dd> + <dt><code>available</code></dt> + <dd>Providing the free space available for allocating new items + in the fspool. This value is in bytes. This is not applicable when creating a + fspool. <span class="since">Since 2.2.1</span></dd> + </dl> + + <h3><a name="FSPoolSource">Source elements</a></h3> + + <p> + A single <code>source</code> element is contained within the top level + <code>fspool</code> element. This tag is used to describe the source of + the fspool. At the moment it is left empty. + </p> + + <h3><a name="FSPoolTarget">Target elements</a></h3> + + <p> + A single <code>target</code> element is contained within the top level + <code>fspool</code> element for all types of fspools (fspool types <code>dir</code>). + This tag is used to describe the mapping of fspool into the host filesystem. + It can contain the following child elements: + </p> + + <pre> + ... + <target> + <path>/path/in/host/fs</path> + <permissions> + <owner>107</owner> + <group>107</group> + <mode>0744</mode> + </permissions> + </target> + </fspool></pre> + + <dl> + <dt><code>path</code></dt> + <dd>Provides the location at which the fspool will be mapped into + the local filesystem namespace, as an absolute path. For a fspool with directory + backend it will be a fully qualified name of the directory in which items + will be created. + <span class="since">Since 2.2.1</span> + </dd> + <dt><code>permissions</code></dt> + <dd> It provides information about the permissions to use for the + final directory when the fspool is built. There are 4 child elements. + The <code>mode</code> element contains the octal permission set. + The <code>mode</code> defaults to 0755 when not provided. + The <code>owner</code> element contains the numeric user ID. + The <code>group</code> element contains the numeric group ID. + If <code>owner</code> or <code>group</code> aren't specified when + creating a directory, the values are inherited from the parent + directory. + </dd> + </dl> + + <h2><a name="FSItem">Item XML</a></h2> + <p> + An item is a directory that holds single filesystem. The filesystem item + XML format is available <span class="since">since 2.2.1</span> + </p> + + <h3><a name="FSItemFirst">General metadata</a></h3> + + <pre> + <item> + <name>item1</name> + <key>/path/to/fspool/</key> + <allocation>0</allocation> + <capacity unit="T">1</capacity> + ...</pre> + + <dl> + <dt><code>name</code></dt> + <dd>Providing a name for the item which is unique to the fspool. + This is mandatory when defining an item.</dd> + <dt><code>key</code></dt> + <dd>Providing an identifier for the item which identifies a + single item. It is impossible to have two distinct keys + identifying a single item. This field cannot be set when creating + an item: it is always generated. + <span class="since">Since 2.2.1</span></dd> + <dt><code>allocation</code></dt> + <dd>Providing the total storage allocation for the item.</dd> + <dt><code>capacity</code></dt> + <dd>Providing the logical capacity for the item.</dd> + </dl> + + <h3><a name="FsItemTarget">Target elements</a></h3> + + <p> + A single <code>target</code> element is contained within the top level + <code>item</code> element. This tag is used to describe the mapping of + the fspool item into the host filesystem. It can contain the following + child elements: + </p> + + <pre> + ... + <target> + <path>/var/lib/virt/images/sparse.img</path> + <format type='qcow2'/> + <permissions> + <owner>107</owner> + <group>107</group> + <mode>0744</mode> + </permissions> + </target></pre> + + <dl> + <dt><code>permissions</code></dt> + <dd>Provides information about the permissions to use + when creating items. There are 4 child elements. + The <code>mode</code> element contains the octal permission set. + The <code>mode</code> defaults to 0600 when not provided. + The <code>owner</code> element contains the numeric user ID. + The <code>group</code> element contains the numeric group ID. + If <code>owner</code> or <code>group</code> aren't specified when + creating fspool item, the values are inherited from the parent + directory. + </dd> + </dl> + + + <h2><a name="examples">Example configuration</a></h2> + + <p> + Here is an example of fspool and item. + </p> + + <h3><a name="exampleDir">Directory based fspool</a></h3> + + <pre> + <fspool type="dir"> + <name>my_fspool</name> + <source> + </source> + <target> + <path>/my_fspool_dir</path> + </target> + </fspool></pre> + + <h3><a name="exampleItem">Fspool Item</a></h3> + + <pre> + <item> + <name>item1</name> + <allocation>0</allocation> + <capacity unit="T">1</capacity> + <target> + <permissions> + <owner>107</owner> + <group>107</group> + <mode>0744</mode> + </permissions> + </target> + </item></pre> + + </body> +</html> diff --git a/docs/fspool.html.in b/docs/fspool.html.in new file mode 100644 index 0000000..bee110d --- /dev/null +++ b/docs/fspool.html.in @@ -0,0 +1,41 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> + <body> + <h1 >Filesystem Pool Management</h1> + <p> + Libvirt provides filesystems managment on the physical host through + fspools and items. + </p> + <p> + Fspool is the entity that holds filesystems. It has a lot in + common with storage pools and volumes. However, fspool deals with + filesystems and not with the block devices. Single filesystem in fspool + is called an item. + </p> + <p> + Libvirt supports the following fspool types: + </p> + <ul> + <li> + <a href="#FSPoolBackendDir">Directory backend</a> + </li> + </ul> + + <h2><a name="FSPoolBackendDir">Fspool with directory backend</a></h2> + <p> + A fspool with a type of <code>dir</code> provides the means to manage + filesystems within a directory on a single host. + </p> + + <h3>Example pool input definition</h3> + <pre> + <fspool type="dir"> + <name>virtfs</name> + <target> + <path>/var/lib/virt/fs</path> + </target> + </fspool></pre> + + </body> +</html> diff --git a/docs/sitemap.html.in b/docs/sitemap.html.in index 757d5aa..cde6cf1 100644 --- a/docs/sitemap.html.in +++ b/docs/sitemap.html.in @@ -181,6 +181,10 @@ <span>The storage pool and volume XML format</span> </li> <li> + <a href="formatfs.html">Filesystem pool</a> + <span>The filesystem pool and filesystem item XML format</span> + </li> + <li> <a href="formatstorageencryption.html">Storage Encryption</a> <span>Storage volume encryption XML format</span> </li> diff --git a/po/POTFILES.in b/po/POTFILES.in index af00155..f4d2f25 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -25,6 +25,7 @@ src/conf/domain_addr.c src/conf/domain_capabilities.c src/conf/domain_conf.c src/conf/domain_event.c +src/conf/fs_conf.c src/conf/interface_conf.c src/conf/netdev_bandwidth_conf.c src/conf/netdev_vlan_conf.c diff --git a/src/Makefile.am b/src/Makefile.am index 7b911bd..d3c6e67 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -399,6 +399,10 @@ CHRDEV_CONF_SOURCES = \ DEVICE_CONF_SOURCES = \ conf/device_conf.c conf/device_conf.h +# FS driver generic impl APIs +FS_CONF_SOURCES = \ + conf/fs_conf.h conf/fs_conf.c + CONF_SOURCES = \ $(NETDEV_CONF_SOURCES) \ $(DOMAIN_CONF_SOURCES) \ @@ -411,6 +415,7 @@ CONF_SOURCES = \ $(NWFILTER_CONF_SOURCES) \ $(NODE_DEVICE_CONF_SOURCES) \ $(STORAGE_CONF_SOURCES) \ + $(FS_CONF_SOURCES) \ $(INTERFACE_CONF_SOURCES) \ $(SECRET_CONF_SOURCES) \ $(CPU_CONF_SOURCES) \ diff --git a/src/conf/fs_conf.c b/src/conf/fs_conf.c new file mode 100644 index 0000000..80f2664 --- /dev/null +++ b/src/conf/fs_conf.c @@ -0,0 +1,1637 @@ +/* + * fs_conf.c: config handling for fs driver + * Author: Olga Krishtal <okrishtal@virtuozzo.com> + * + * Copyright (C) 2016 Parallels IP Holdings GmbH + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + */ + +#include <config.h> + +#include <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 "virerror.h" +#include "datatypes.h" +#include "fs_conf.h" + +#include "virxml.h" +#include "viruuid.h" +#include "virbuffer.h" +#include "viralloc.h" +#include "virfile.h" +#include "virstring.h" +#include "virlog.h" + +#define VIR_FROM_THIS VIR_FROM_FSPOOL + +VIR_LOG_INIT("conf.fs_conf"); + +VIR_ENUM_IMPL(virFSPool, + VIR_FSPOOL_LAST, + "dir") + +VIR_ENUM_IMPL(virFSItem, + VIR_FSITEM_LAST, + "dir") + +/* Flags to indicate mandatory components in the fspool source */ +enum { + VIR_FSPOOL_SOURCE_DIR = (1 << 2), + VIR_FSPOOL_SOURCE_NAME = (1 << 4), + VIR_FSPOOL_SOURCE_NETWORK = (1 << 6), +}; + +typedef const char *(*virFSItemFormatToString)(int format); +typedef int (*virFSItemFormatFromString)(const char *format); + +typedef const char *(*virFSPoolFormatToString)(int format); +typedef int (*virFSPoolFormatFromString)(const char *format); + +typedef struct _virFSItemOptions virFSItemOptions; +typedef virFSItemOptions *virFSItemOptionsPtr; +struct _virFSItemOptions { + int defaultFormat; + virFSItemFormatToString formatToString; + virFSItemFormatFromString formatFromString; +}; + +typedef struct _virFSPoolOptions virFSPoolOptions; +typedef virFSPoolOptions *virFSPoolOptionsPtr; +struct _virFSPoolOptions { + unsigned int flags; + int defaultFormat; + virFSPoolFormatToString formatToString; + virFSPoolFormatFromString formatFromString; +}; + +typedef struct _virFSPoolTypeInfo virFSPoolTypeInfo; +typedef virFSPoolTypeInfo *virFSPoolTypeInfoPtr; +struct _virFSPoolTypeInfo { + int fspoolType; + virFSPoolOptions fspoolOptions; + virFSItemOptions itemOptions; +}; + +static virFSPoolTypeInfo fspoolTypeInfo[] = { + {.fspoolType = VIR_FSPOOL_DIR} +}; + + +static virFSPoolTypeInfoPtr +virFSPoolTypeInfoLookup(int type) +{ + size_t i; + for (i = 0; i < ARRAY_CARDINALITY(fspoolTypeInfo); i++) + if (fspoolTypeInfo[i].fspoolType == type) + return &fspoolTypeInfo[i]; + + virReportError(VIR_ERR_INTERNAL_ERROR, + _("missing backend for fspool type %d"), type); + return NULL; +} + +static virFSPoolOptionsPtr +virFSPoolOptionsForPoolType(int type) +{ + virFSPoolTypeInfoPtr backend = virFSPoolTypeInfoLookup(type); + if (backend == NULL) + return NULL; + return &backend->fspoolOptions; +} + +static virFSItemOptionsPtr +virFSItemOptionsForPoolType(int type) +{ + virFSPoolTypeInfoPtr backend = virFSPoolTypeInfoLookup(type); + if (backend == NULL) + return NULL; + return &backend->itemOptions; +} + +static void +virFSSourcePoolDefFree(virFSSourcePoolDefPtr def) +{ + if (!def) + return; + + VIR_FREE(def->pool); + VIR_FREE(def->item); + + VIR_FREE(def); +} + +static void +virFSPermsFree(virFSPermsPtr def) +{ + if (!def) + return; + + VIR_FREE(def->label); + VIR_FREE(def); +} + +static void +virFSSourceClear(virFSSourcePtr def) +{ + if (!def) + return; + + VIR_FREE(def->path); + virFSSourcePoolDefFree(def->srcpool); + VIR_FREE(def->driverName); + virFSPermsFree(def->perms); +} + +void +virFSItemDefFree(virFSItemDefPtr def) +{ + if (!def) + return; + + VIR_FREE(def->name); + VIR_FREE(def->key); + + virFSSourceClear(&def->target); + VIR_FREE(def); +} + +void +virFSPoolSourceClear(virFSPoolSourcePtr source) +{ + if (!source) + return; + + VIR_FREE(source->dir); + VIR_FREE(source->name); + VIR_FREE(source->product); +} + +void +virFSPoolSourceFree(virFSPoolSourcePtr source) +{ + virFSPoolSourceClear(source); + VIR_FREE(source); +} + +void +virFSPoolDefFree(virFSPoolDefPtr def) +{ + if (!def) + return; + + VIR_FREE(def->name); + + virFSPoolSourceClear(&def->source); + + VIR_FREE(def->target.path); + VIR_FREE(def->target.perms.label); + VIR_FREE(def); +} + +void +virFSPoolObjFree(virFSPoolObjPtr obj) +{ + if (!obj) + return; + + virFSPoolObjClearItems(obj); + + virFSPoolDefFree(obj->def); + virFSPoolDefFree(obj->newDef); + + VIR_FREE(obj->configFile); + VIR_FREE(obj->autostartLink); + + virMutexDestroy(&obj->lock); + + VIR_FREE(obj); +} + +void +virFSPoolObjListFree(virFSPoolObjListPtr fspools) +{ + size_t i; + for (i = 0; i < fspools->count; i++) + virFSPoolObjFree(fspools->objs[i]); + VIR_FREE(fspools->objs); + fspools->count = 0; +} + +void +virFSPoolObjRemove(virFSPoolObjListPtr fspools, + virFSPoolObjPtr fspool) +{ + size_t i; + + virFSPoolObjUnlock(fspool); + + for (i = 0; i < fspools->count; i++) { + virFSPoolObjLock(fspools->objs[i]); + if (fspools->objs[i] == fspool) { + virFSPoolObjUnlock(fspools->objs[i]); + virFSPoolObjFree(fspools->objs[i]); + + VIR_DELETE_ELEMENT(fspools->objs, i, fspools->count); + break; + } + virFSPoolObjUnlock(fspools->objs[i]); + } +} + +static int +virFSPoolDefParseSource(xmlXPathContextPtr ctxt, + virFSPoolSourcePtr source, + int fspool_type, + xmlNodePtr node) +{ + int ret = -1; + xmlNodePtr relnode /*, authnode, *nodeset = NULL*/; + virFSPoolOptionsPtr options; + + relnode = ctxt->node; + ctxt->node = node; + + if ((options = virFSPoolOptionsForPoolType(fspool_type)) == NULL) + goto cleanup; + + source->name = virXPathString("string(./name)", ctxt); + + if (options->formatFromString) { + char *format = virXPathString("string(./format/@type)", ctxt); + if (format == NULL) + source->format = options->defaultFormat; + else + source->format = options->formatFromString(format); + + if (source->format < 0) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("unknown fspool format type %s"), format); + VIR_FREE(format); + goto cleanup; + } + VIR_FREE(format); + } + + source->dir = virXPathString("string(./dir/@path)", ctxt); + + source->product = virXPathString("string(./product/@name)", ctxt); + + ret = 0; + cleanup: + ctxt->node = relnode; + + return ret; +} + +virFSPoolSourcePtr +virFSPoolDefParseSourceString(const char *srcSpec, + int fspool_type) +{ + xmlDocPtr doc = NULL; + xmlNodePtr node = NULL; + xmlXPathContextPtr xpath_ctxt = NULL; + virFSPoolSourcePtr def = NULL, ret = NULL; + + if (!(doc = virXMLParseStringCtxt(srcSpec, + _("(storage_source_specification)"), + &xpath_ctxt))) + goto cleanup; + + if (VIR_ALLOC(def) < 0) + goto cleanup; + + if (!(node = virXPathNode("/source", xpath_ctxt))) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("root element was not source")); + goto cleanup; + } + + if (virFSPoolDefParseSource(xpath_ctxt, def, fspool_type, + node) < 0) + goto cleanup; + + ret = def; + def = NULL; + cleanup: + virFSPoolSourceFree(def); + xmlFreeDoc(doc); + xmlXPathFreeContext(xpath_ctxt); + + return ret; +} + +static int +virFSDefParsePerms(xmlXPathContextPtr ctxt, + virFSPermsPtr perms, + const char *permxpath) +{ + char *mode; + long long val; + int ret = -1; + xmlNodePtr relnode; + xmlNodePtr node; + + node = virXPathNode(permxpath, ctxt); + if (node == NULL) { + /* Set default values if there is not <permissions> element */ + perms->mode = (mode_t) -1; + perms->uid = (uid_t) -1; + perms->gid = (gid_t) -1; + perms->label = NULL; + return 0; + } + + relnode = ctxt->node; + ctxt->node = node; + + if ((mode = virXPathString("string(./mode)", ctxt))) { + int tmp; + + if (virStrToLong_i(mode, NULL, 8, &tmp) < 0 || (tmp & ~0777)) { + VIR_FREE(mode); + virReportError(VIR_ERR_XML_ERROR, "%s", + _("malformed octal mode")); + goto error; + } + perms->mode = tmp; + VIR_FREE(mode); + } else { + perms->mode = (mode_t) -1; + } + + if (virXPathNode("./owner", ctxt) == NULL) { + perms->uid = (uid_t) -1; + } else { + /* We previously could output -1, so continue to parse it */ + if (virXPathLongLong("number(./owner)", ctxt, &val) < 0 || + ((uid_t)val != val && + val != -1)) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("malformed owner element")); + goto error; + } + + perms->uid = val; + } + + if (virXPathNode("./group", ctxt) == NULL) { + perms->gid = (gid_t) -1; + } else { + /* We previously could output -1, so continue to parse it */ + if (virXPathLongLong("number(./group)", ctxt, &val) < 0 || + ((gid_t) val != val && + val != -1)) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("malformed group element")); + goto error; + } + perms->gid = val; + } + + /* NB, we're ignoring missing labels here - they'll simply inherit */ + perms->label = virXPathString("string(./label)", ctxt); + + ret = 0; + error: + ctxt->node = relnode; + return ret; +} + +static virFSPoolDefPtr +virFSPoolDefParseXML(xmlXPathContextPtr ctxt) +{ + virFSPoolOptionsPtr options; + virFSPoolDefPtr ret; + xmlNodePtr source_node; + char *type = NULL; + char *uuid = NULL; + char *target_path = NULL; + + if (VIR_ALLOC(ret) < 0) + return NULL; + + type = virXPathString("string(./@type)", ctxt); + if (type == NULL) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("fspool missing type attribute")); + goto error; + } + + if ((ret->type = virFSPoolTypeFromString(type)) < 0) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("unknown fspool type %s"), type); + goto error; + } + + if ((options = virFSPoolOptionsForPoolType(ret->type)) == NULL) + goto error; + + source_node = virXPathNode("./source", ctxt); + if (source_node) { + if (virFSPoolDefParseSource(ctxt, &ret->source, ret->type, + source_node) < 0) + goto error; + } + + ret->name = virXPathString("string(./name)", ctxt); + if (ret->name == NULL && + options->flags & VIR_FSPOOL_SOURCE_NAME) + ret->name = ret->source.name; + if (ret->name == NULL) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("missing pool source name element")); + goto error; + } + + if (strchr(ret->name, '/')) { + virReportError(VIR_ERR_XML_ERROR, + _("name %s cannot contain '/'"), ret->name); + goto error; + } + + uuid = virXPathString("string(./uuid)", ctxt); + if (uuid == NULL) { + if (virUUIDGenerate(ret->uuid) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("unable to generate uuid")); + goto error; + } + } else { + if (virUUIDParse(uuid, ret->uuid) < 0) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("malformed uuid element")); + goto error; + } + } + + + if (options->flags & VIR_FSPOOL_SOURCE_DIR) { + if (!ret->source.dir) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("missing storage pool source path")); + goto error; + } + } + if (options->flags & VIR_FSPOOL_SOURCE_NAME) { + if (ret->source.name == NULL) { + /* source name defaults to pool name */ + if (VIR_STRDUP(ret->source.name, ret->name) < 0) + goto error; + } + } + target_path = virXPathString("string(./target/path)", ctxt); + if (!target_path) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("missing storage pool target path")); + goto error; + } + ret->target.path = virFileSanitizePath(target_path); + if (!ret->target.path) + goto error; + + if (virFSDefParsePerms(ctxt, &ret->target.perms, + "./target/permissions") < 0) + goto error; + + cleanup: + VIR_FREE(uuid); + VIR_FREE(type); + VIR_FREE(target_path); + return ret; + + error: + virFSPoolDefFree(ret); + ret = NULL; + goto cleanup; +} + +virFSPoolDefPtr +virFSPoolDefParseNode(xmlDocPtr xml, + xmlNodePtr root) +{ + xmlXPathContextPtr ctxt = NULL; + virFSPoolDefPtr def = NULL; + + if (!xmlStrEqual(root->name, BAD_CAST "fspool")) { + virReportError(VIR_ERR_XML_ERROR, + _("unexpected root element <%s>, " + "expecting <fspool>"), + root->name); + goto cleanup; + } + + ctxt = xmlXPathNewContext(xml); + if (ctxt == NULL) { + virReportOOMError(); + goto cleanup; + } + + ctxt->node = root; + def = virFSPoolDefParseXML(ctxt); + cleanup: + xmlXPathFreeContext(ctxt); + return def; +} + +static virFSPoolDefPtr +virFSPoolDefParse(const char *xmlStr, + const char *filename) +{ + virFSPoolDefPtr ret = NULL; + xmlDocPtr xml; + + if ((xml = virXMLParse(filename, xmlStr, _("(fs_pool_definition)")))) { + ret = virFSPoolDefParseNode(xml, xmlDocGetRootElement(xml)); + xmlFreeDoc(xml); + } + + return ret; +} + +virFSPoolDefPtr +virFSPoolDefParseString(const char *xmlStr) +{ + return virFSPoolDefParse(xmlStr, NULL); +} + +virFSPoolDefPtr +virFSPoolDefParseFile(const char *filename) +{ + return virFSPoolDefParse(NULL, filename); +} + +static int +virFSPoolSourceFormat(virBufferPtr buf, + virFSPoolOptionsPtr options, + virFSPoolSourcePtr src) +{ + + virBufferAddLit(buf, "<source>\n"); + virBufferAdjustIndent(buf, 2); + + if (options->flags & VIR_FSPOOL_SOURCE_DIR) + virBufferEscapeString(buf, "<dir path='%s'/>\n", src->dir); + + if (options->flags & VIR_FSPOOL_SOURCE_NAME) + virBufferEscapeString(buf, "<name>%s</name>\n", src->name); + + if (options->formatToString) { + const char *format = (options->formatToString)(src->format); + if (!format) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("unknown pool format number %d"), + src->format); + return -1; + } + virBufferAsprintf(buf, "<format type='%s'/>\n", format); + } + + + virBufferEscapeString(buf, "<product name='%s'/>\n", src->product); + + virBufferAdjustIndent(buf, -2); + virBufferAddLit(buf, "</source>\n"); + return 0; +} + + +static int +virFSPoolDefFormatBuf(virBufferPtr buf, + virFSPoolDefPtr def) +{ + virFSPoolOptionsPtr options; + char uuid[VIR_UUID_STRING_BUFLEN]; + const char *type; + + options = virFSPoolOptionsForPoolType(def->type); + if (options == NULL) + return -1; + + type = virFSPoolTypeToString(def->type); + if (!type) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("unexpected fspool type")); + return -1; + } + virBufferAsprintf(buf, "<fspool type='%s'>\n", type); + virBufferAdjustIndent(buf, 2); + virBufferEscapeString(buf, "<name>%s</name>\n", def->name); + + virUUIDFormat(def->uuid, uuid); + virBufferAsprintf(buf, "<uuid>%s</uuid>\n", uuid); + + virBufferAsprintf(buf, "<capacity unit='bytes'>%llu</capacity>\n", + def->capacity); + virBufferAsprintf(buf, "<allocation unit='bytes'>%llu</allocation>\n", + def->allocation); + virBufferAsprintf(buf, "<available unit='bytes'>%llu</available>\n", + def->available); + + if (virFSPoolSourceFormat(buf, options, &def->source) < 0) + return -1; + + virBufferAddLit(buf, "<target>\n"); + virBufferAdjustIndent(buf, 2); + + virBufferEscapeString(buf, "<path>%s</path>\n", def->target.path); + + if (def->target.perms.mode != (mode_t) -1 || + def->target.perms.uid != (uid_t) -1 || + def->target.perms.gid != (gid_t) -1 || + def->target.perms.label) { + virBufferAddLit(buf, "<permissions>\n"); + virBufferAdjustIndent(buf, 2); + if (def->target.perms.mode != (mode_t) -1) + virBufferAsprintf(buf, "<mode>0%o</mode>\n", + def->target.perms.mode); + if (def->target.perms.uid != (uid_t) -1) + virBufferAsprintf(buf, "<owner>%d</owner>\n", + (int) def->target.perms.uid); + if (def->target.perms.gid != (gid_t) -1) + virBufferAsprintf(buf, "<group>%d</group>\n", + (int) def->target.perms.gid); + virBufferEscapeString(buf, "<label>%s</label>\n", + def->target.perms.label); + + virBufferAdjustIndent(buf, -2); + virBufferAddLit(buf, "</permissions>\n"); + } + + virBufferAdjustIndent(buf, -2); + virBufferAddLit(buf, "</target>\n"); + + virBufferAdjustIndent(buf, -2); + virBufferAddLit(buf, "</fspool>\n"); + + return 0; +} + +char * +virFSPoolDefFormat(virFSPoolDefPtr def) +{ + virBuffer buf = VIR_BUFFER_INITIALIZER; + + if (virFSPoolDefFormatBuf(&buf, def) < 0) + goto error; + + if (virBufferCheckError(&buf) < 0) + goto error; + + return virBufferContentAndReset(&buf); + + error: + virBufferFreeAndReset(&buf); + return NULL; +} + + +static int +virFSSize(const char *unit, + const char *val, + unsigned long long *ret) +{ + if (virStrToLong_ull(val, NULL, 10, ret) < 0) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("malformed capacity element")); + return -1; + } + /* off_t is signed, so you cannot create a file larger than 2**63 + * bytes in the first place. */ + if (virScaleInteger(ret, unit, 1, LLONG_MAX) < 0) + return -1; + + return 0; +} + +static virFSItemDefPtr +virFSItemDefParseXML(virFSPoolDefPtr fspool, + xmlXPathContextPtr ctxt, + unsigned int flags) +{ + virFSItemDefPtr ret; + virFSItemOptionsPtr options; + char *type = NULL; + char *allocation = NULL; + char *capacity = NULL; + char *unit = NULL; + xmlNodePtr *nodes = NULL; + + virCheckFlags(VIR_ITEM_XML_PARSE_NO_CAPACITY | + VIR_ITEM_XML_PARSE_OPT_CAPACITY, NULL); + + options = virFSItemOptionsForPoolType(fspool->type); + if (options == NULL) + return NULL; + + if (VIR_ALLOC(ret) < 0) + return NULL; + + ret->name = virXPathString("string(./name)", ctxt); + if (ret->name == NULL) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("missing item name element")); + goto error; + } + + /* Normally generated by pool refresh, but useful for unit tests */ + ret->key = virXPathString("string(./key)", ctxt); + + /* Technically overridden by pool refresh, but useful for unit tests */ + type = virXPathString("string(./@type)", ctxt); + if (type) { + if ((ret->type = virFSItemTypeFromString(type)) < 0) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("unknown item type '%s'"), type); + goto error; + } + } + + capacity = virXPathString("string(./capacity)", ctxt); + unit = virXPathString("string(./capacity/@unit)", ctxt); + if (capacity) { + if (virFSSize(unit, capacity, &ret->target.capacity) < 0) + goto error; + } else if (!(flags & VIR_ITEM_XML_PARSE_NO_CAPACITY) && + !((flags & VIR_ITEM_XML_PARSE_OPT_CAPACITY))) { + virReportError(VIR_ERR_XML_ERROR, "%s", _("missing capacity element")); + goto error; + } + VIR_FREE(unit); + + allocation = virXPathString("string(./allocation)", ctxt); + if (allocation) { + unit = virXPathString("string(./allocation/@unit)", ctxt); + if (virFSSize(unit, allocation, &ret->target.allocation) < 0) + goto error; + } else { + ret->target.allocation = ret->target.capacity; + } + + ret->target.path = virXPathString("string(./target/path)", ctxt); + + if (VIR_ALLOC(ret->target.perms) < 0) + goto error; + if (virFSDefParsePerms(ctxt, ret->target.perms, + "./target/permissions") < 0) + goto error; + + cleanup: + VIR_FREE(nodes); + VIR_FREE(allocation); + VIR_FREE(capacity); + VIR_FREE(unit); + VIR_FREE(type); + return ret; + + error: + virFSItemDefFree(ret); + ret = NULL; + goto cleanup; +} + +virFSItemDefPtr +virFSItemDefParseNode(virFSPoolDefPtr fspool, + xmlDocPtr xml, + xmlNodePtr root, + unsigned int flags) +{ + xmlXPathContextPtr ctxt = NULL; + virFSItemDefPtr def = NULL; + + if (!xmlStrEqual(root->name, BAD_CAST "item")) { + virReportError(VIR_ERR_XML_ERROR, + _("unexpected root element <%s>, " + "expecting <item>"), + root->name); + goto cleanup; + } + + ctxt = xmlXPathNewContext(xml); + if (ctxt == NULL) { + virReportOOMError(); + goto cleanup; + } + + ctxt->node = root; + def = virFSItemDefParseXML(fspool, ctxt, flags); + cleanup: + xmlXPathFreeContext(ctxt); + return def; +} + +static virFSItemDefPtr +virFSItemDefParse(virFSPoolDefPtr fspool, + const char *xmlStr, + const char *filename, + unsigned int flags) +{ + virFSItemDefPtr ret = NULL; + xmlDocPtr xml; + + if ((xml = virXMLParse(filename, xmlStr, _("(fspool_item_definition)")))) { + ret = virFSItemDefParseNode(fspool, xml, xmlDocGetRootElement(xml), flags); + xmlFreeDoc(xml); + } + + return ret; +} + +virFSItemDefPtr +virFSItemDefParseString(virFSPoolDefPtr fspool, + const char *xmlStr, + unsigned int flags) +{ + return virFSItemDefParse(fspool, xmlStr, NULL, flags); +} + +virFSItemDefPtr +virFSItemDefParseFile(virFSPoolDefPtr fspool, + const char *filename, + unsigned int flags) +{ + return virFSItemDefParse(fspool, NULL, filename, flags); +} + +static int +virFSItemTargetDefFormat(virFSItemOptionsPtr options ATTRIBUTE_UNUSED, + virBufferPtr buf, + virFSSourcePtr def, + const char *type) +{ + virBufferAsprintf(buf, "<%s>\n", type); + virBufferAdjustIndent(buf, 2); + + if (def->perms && + (def->perms->mode != (mode_t) -1 || + def->perms->uid != (uid_t) -1 || + def->perms->gid != (gid_t) -1 || + def->perms->label)) { + virBufferAddLit(buf, "<permissions>\n"); + virBufferAdjustIndent(buf, 2); + + if (def->perms->mode != (mode_t) -1) + virBufferAsprintf(buf, "<mode>0%o</mode>\n", + def->perms->mode); + if (def->perms->uid != (uid_t) -1) + virBufferAsprintf(buf, "<owner>%d</owner>\n", + (int) def->perms->uid); + if (def->perms->gid != (gid_t) -1) + virBufferAsprintf(buf, "<group>%d</group>\n", + (int) def->perms->gid); + + virBufferEscapeString(buf, "<label>%s</label>\n", + def->perms->label); + + virBufferAdjustIndent(buf, -2); + virBufferAddLit(buf, "</permissions>\n"); + } + + virBufferAdjustIndent(buf, -2); + virBufferAsprintf(buf, "</%s>\n", type); + return 0; +} + +char * +virFSItemDefFormat(virFSPoolDefPtr fspool, + virFSItemDefPtr def) +{ + virFSItemOptionsPtr options; + virBuffer buf = VIR_BUFFER_INITIALIZER; + + options = virFSItemOptionsForPoolType(fspool->type); + if (options == NULL) + return NULL; + + virBufferAddLit(&buf, "<item>\n"); + virBufferAdjustIndent(&buf, 2); + + virBufferEscapeString(&buf, "<name>%s</name>\n", def->name); + virBufferEscapeString(&buf, "<key>%s</key>\n", def->key); + + virBufferAsprintf(&buf, "<capacity unit='bytes'>%llu</capacity>\n", + def->target.capacity); + virBufferAsprintf(&buf, "<allocation unit='bytes'>%llu</allocation>\n", + def->target.allocation); + + if (virFSItemTargetDefFormat(options, &buf, + &def->target, "target") < 0) + goto cleanup; + + virBufferAdjustIndent(&buf, -2); + virBufferAddLit(&buf, "</item>\n"); + + if (virBufferCheckError(&buf) < 0) + goto cleanup; + + return virBufferContentAndReset(&buf); + + cleanup: + virBufferFreeAndReset(&buf); + return NULL; +} + + +virFSPoolObjPtr +virFSPoolObjFindByUUID(virFSPoolObjListPtr fspools, + const unsigned char *uuid) +{ + size_t i; + + for (i = 0; i < fspools->count; i++) { + virFSPoolObjLock(fspools->objs[i]); + if (!memcmp(fspools->objs[i]->def->uuid, uuid, VIR_UUID_BUFLEN)) + return fspools->objs[i]; + virFSPoolObjUnlock(fspools->objs[i]); + } + + return NULL; +} + +virFSPoolObjPtr +virFSPoolObjFindByName(virFSPoolObjListPtr fspools, + const char *name) +{ + size_t i; + + for (i = 0; i < fspools->count; i++) { + virFSPoolObjLock(fspools->objs[i]); + if (STREQ(fspools->objs[i]->def->name, name)) + return fspools->objs[i]; + virFSPoolObjUnlock(fspools->objs[i]); + } + + return NULL; +} + + +void +virFSPoolObjClearItems(virFSPoolObjPtr fspool) +{ + size_t i; + for (i = 0; i < fspool->items.count; i++) + virFSItemDefFree(fspool->items.objs[i]); + + VIR_FREE(fspool->items.objs); + fspool->items.count = 0; +} + +virFSItemDefPtr +virFSItemDefFindByKey(virFSPoolObjPtr fspool, + const char *key) +{ + size_t i; + + for (i = 0; i < fspool->items.count; i++) + if (STREQ(fspool->items.objs[i]->key, key)) + return fspool->items.objs[i]; + + return NULL; +} + +virFSItemDefPtr +virFSItemDefFindByPath(virFSPoolObjPtr fspool, + const char *path) +{ + size_t i; + + for (i = 0; i < fspool->items.count; i++) + if (STREQ(fspool->items.objs[i]->target.path, path)) + return fspool->items.objs[i]; + + return NULL; +} + +virFSItemDefPtr +virFSItemDefFindByName(virFSPoolObjPtr fspool, + const char *name) +{ + size_t i; + + for (i = 0; i < fspool->items.count; i++) + if (STREQ(fspool->items.objs[i]->name, name)) + return fspool->items.objs[i]; + + return NULL; +} + +virFSPoolObjPtr +virFSPoolObjAssignDef(virFSPoolObjListPtr fspools, + virFSPoolDefPtr def) +{ + virFSPoolObjPtr fspool; + + if ((fspool = virFSPoolObjFindByName(fspools, def->name))) { + if (!virFSPoolObjIsActive(fspool)) { + virFSPoolDefFree(fspool->def); + fspool->def = def; + } else { + virFSPoolDefFree(fspool->newDef); + fspool->newDef = def; + } + return fspool; + } + + if (VIR_ALLOC(fspool) < 0) + return NULL; + + if (virMutexInit(&fspool->lock) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot initialize mutex")); + VIR_FREE(fspool); + return NULL; + } + virFSPoolObjLock(fspool); + fspool->active = 0; + + if (VIR_APPEND_ELEMENT_COPY(fspools->objs, fspools->count, fspool) < 0) { + virFSPoolObjUnlock(fspool); + virFSPoolObjFree(fspool); + return NULL; + } + fspool->def = def; + + return fspool; +} + +static virFSPoolObjPtr +virFSPoolObjLoad(virFSPoolObjListPtr fspools, + const char *file, + const char *path, + const char *autostartLink) +{ + virFSPoolDefPtr def; + virFSPoolObjPtr fspool; + + if (!(def = virFSPoolDefParseFile(path))) + return NULL; + + if (!virFileMatchesNameSuffix(file, def->name, ".xml")) { + virReportError(VIR_ERR_XML_ERROR, + _("Storage fspool config filename '%s' does " + "not match fspool name '%s'"), + path, def->name); + virFSPoolDefFree(def); + return NULL; + } + + if (!(fspool = virFSPoolObjAssignDef(fspools, def))) { + virFSPoolDefFree(def); + return NULL; + } + + VIR_FREE(fspool->configFile); /* for driver reload */ + if (VIR_STRDUP(fspool->configFile, path) < 0) { + virFSPoolObjRemove(fspools, fspool); + return NULL; + } + VIR_FREE(fspool->autostartLink); /* for driver reload */ + if (VIR_STRDUP(fspool->autostartLink, autostartLink) < 0) { + virFSPoolObjRemove(fspools, fspool); + return NULL; + } + + fspool->autostart = virFileLinkPointsTo(fspool->autostartLink, + fspool->configFile); + + return fspool; +} + + +virFSPoolObjPtr +virFSPoolLoadState(virFSPoolObjListPtr fspools, + const char *stateDir, + const char *name) +{ + char *stateFile = NULL; + virFSPoolDefPtr def = NULL; + virFSPoolObjPtr fspool = NULL; + xmlDocPtr xml = NULL; + xmlXPathContextPtr ctxt = NULL; + xmlNodePtr node = NULL; + + if (!(stateFile = virFileBuildPath(stateDir, name, ".xml"))) + goto error; + + if (!(xml = virXMLParseCtxt(stateFile, NULL, _("(fspool state)"), &ctxt))) + goto error; + + if (!(node = virXPathNode("//fspool", ctxt))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Could not find any 'fspool' element in state file")); + goto error; + } + + ctxt->node = node; + if (!(def = virFSPoolDefParseXML(ctxt))) + goto error; + + if (STRNEQ(name, def->name)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Storage fspool state file '%s' does not match " + "fspool name '%s'"), + stateFile, def->name); + goto error; + } + + /* create the object */ + if (!(fspool = virFSPoolObjAssignDef(fspools, def))) + goto error; + + /* XXX: future handling of some additional useful status data, + * for now, if a status file for a fspool exists, the fspool will be marked + * as active + */ + + fspool->active = 1; + + cleanup: + VIR_FREE(stateFile); + xmlFreeDoc(xml); + xmlXPathFreeContext(ctxt); + return fspool; + + error: + virFSPoolDefFree(def); + goto cleanup; +} + + +int +virFSPoolLoadAllState(virFSPoolObjListPtr fspools, + const char *stateDir) +{ + DIR *dir; + struct dirent *entry; + int ret = -1; + int rc; + + if ((rc = virDirOpenIfExists(&dir, stateDir)) <= 0) + return rc; + + while ((ret = virDirRead(dir, &entry, stateDir)) > 0) { + virFSPoolObjPtr fspool; + + if (!virFileStripSuffix(entry->d_name, ".xml")) + continue; + + if (!(fspool = virFSPoolLoadState(fspools, stateDir, entry->d_name))) + continue; + virFSPoolObjUnlock(fspool); + } + + VIR_DIR_CLOSE(dir); + return ret; +} + + +int +virFSPoolLoadAllConfigs(virFSPoolObjListPtr fspools, + const char *configDir, + const char *autostartDir) +{ + DIR *dir; + struct dirent *entry; + int ret; + int rc; + + if ((rc = virDirOpenIfExists(&dir, configDir)) <= 0) + return rc; + + while ((ret = virDirRead(dir, &entry, configDir)) > 0) { + char *path; + char *autostartLink; + virFSPoolObjPtr fspool; + + if (!virFileHasSuffix(entry->d_name, ".xml")) + continue; + + if (!(path = virFileBuildPath(configDir, entry->d_name, NULL))) + continue; + + if (!(autostartLink = virFileBuildPath(autostartDir, entry->d_name, + NULL))) { + VIR_FREE(path); + continue; + } + + fspool = virFSPoolObjLoad(fspools, entry->d_name, path, + autostartLink); + if (fspool) + virFSPoolObjUnlock(fspool); + + VIR_FREE(path); + VIR_FREE(autostartLink); + } + + VIR_DIR_CLOSE(dir); + return ret; +} + + +static int virFSPoolSaveXML(const char *path, + virFSPoolDefPtr def, + const char *xml) +{ + char uuidstr[VIR_UUID_STRING_BUFLEN]; + int ret = -1; + + virUUIDFormat(def->uuid, uuidstr); + ret = virXMLSaveFile(path, + virXMLPickShellSafeComment(def->name, uuidstr), + "fspool-edit", xml); + + return ret; +} + + +int +virFSPoolSaveState(const char *stateFile, + virFSPoolDefPtr def) +{ + virBuffer buf = VIR_BUFFER_INITIALIZER; + int ret = -1; + char *xml; + + virBufferAddLit(&buf, "<fspoolstate>\n"); + virBufferAdjustIndent(&buf, 2); + + if (virFSPoolDefFormatBuf(&buf, def) < 0) + goto error; + + virBufferAdjustIndent(&buf, -2); + virBufferAddLit(&buf, "</fspoolstate>\n"); + + if (virBufferCheckError(&buf) < 0) + goto error; + + if (!(xml = virBufferContentAndReset(&buf))) + goto error; + + if (virFSPoolSaveXML(stateFile, def, xml)) + goto error; + + ret = 0; + + error: + VIR_FREE(xml); + return ret; +} + + +int +virFSPoolSaveConfig(const char *configFile, + virFSPoolDefPtr def) +{ + char *xml; + int ret = -1; + + if (!(xml = virFSPoolDefFormat(def))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("failed to generate XML")); + return -1; + } + + if (virFSPoolSaveXML(configFile, def, xml)) + goto cleanup; + + ret = 0; + cleanup: + VIR_FREE(xml); + return ret; +} + +int +virFSPoolObjSaveDef(virFSDriverStatePtr driver, + virFSPoolObjPtr fspool, + virFSPoolDefPtr def) +{ + if (!fspool->configFile) { + if (virFileMakePath(driver->configDir) < 0) { + virReportSystemError(errno, + _("cannot create config directory %s"), + driver->configDir); + return -1; + } + + if (!(fspool->configFile = virFileBuildPath(driver->configDir, + def->name, ".xml"))) { + return -1; + } + + if (!(fspool->autostartLink = virFileBuildPath(driver->autostartDir, + def->name, ".xml"))) { + VIR_FREE(fspool->configFile); + return -1; + } + } + + return virFSPoolSaveConfig(fspool->configFile, def); +} + +int +virFSPoolObjDeleteDef(virFSPoolObjPtr fspool) +{ + if (!fspool->configFile) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("no config file for %s"), fspool->def->name); + return -1; + } + + if (unlink(fspool->configFile) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot remove config for %s"), + fspool->def->name); + return -1; + } + + return 0; +} + +virFSPoolSourcePtr +virFSPoolSourceListNewSource(virFSPoolSourceListPtr list) +{ + virFSPoolSourcePtr source; + + if (VIR_REALLOC_N(list->sources, list->nsources + 1) < 0) + return NULL; + + source = &list->sources[list->nsources++]; + memset(source, 0, sizeof(*source)); + + return source; +} + +char * +virFSPoolSourceListFormat(virFSPoolSourceListPtr def) +{ + virFSPoolOptionsPtr options; + virBuffer buf = VIR_BUFFER_INITIALIZER; + const char *type; + size_t i; + + options = virFSPoolOptionsForPoolType(def->type); + if (options == NULL) + return NULL; + + type = virFSPoolTypeToString(def->type); + if (!type) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("unexpected fspool type")); + goto cleanup; + } + + virBufferAddLit(&buf, "<sources>\n"); + virBufferAdjustIndent(&buf, 2); + + for (i = 0; i < def->nsources; i++) + virFSPoolSourceFormat(&buf, options, &def->sources[i]); + + virBufferAdjustIndent(&buf, -2); + virBufferAddLit(&buf, "</sources>\n"); + + if (virBufferCheckError(&buf) < 0) + goto cleanup; + + return virBufferContentAndReset(&buf); + + cleanup: + virBufferFreeAndReset(&buf); + return NULL; +} + + +/* + * virFSPoolObjIsDuplicate: + * @doms : virFSPoolObjListPtr to search + * @def : virFSPoolDefPtr definition of fspool to lookup + * @check_active: If true, ensure that fspool is not active + * + * Returns: -1 on error + * 0 if fspool is new + * 1 if fspool is a duplicate + */ +int +virFSPoolObjIsDuplicate(virFSPoolObjListPtr fspools, + virFSPoolDefPtr def, + unsigned int check_active) +{ + int ret = -1; + virFSPoolObjPtr fspool = NULL; + + /* See if a Pool with matching UUID already exists */ + fspool = virFSPoolObjFindByUUID(fspools, def->uuid); + if (fspool) { + /* UUID matches, but if names don't match, refuse it */ + if (STRNEQ(fspool->def->name, def->name)) { + char uuidstr[VIR_UUID_STRING_BUFLEN]; + virUUIDFormat(fspool->def->uuid, uuidstr); + virReportError(VIR_ERR_OPERATION_FAILED, + _("fspool '%s' is already defined with uuid %s"), + fspool->def->name, uuidstr); + goto cleanup; + } + + if (check_active) { + /* UUID & name match, but if Pool is already active, refuse it */ + if (virFSPoolObjIsActive(fspool)) { + virReportError(VIR_ERR_OPERATION_INVALID, + _("fspool is already active as '%s'"), + fspool->def->name); + goto cleanup; + } + } + + ret = 1; + } else { + /* UUID does not match, but if a name matches, refuse it */ + fspool = virFSPoolObjFindByName(fspools, def->name); + if (fspool) { + char uuidstr[VIR_UUID_STRING_BUFLEN]; + virUUIDFormat(fspool->def->uuid, uuidstr); + virReportError(VIR_ERR_OPERATION_FAILED, + _("fspool '%s' already exists with uuid %s"), + def->name, uuidstr); + goto cleanup; + } + ret = 0; + } + + cleanup: + if (fspool) + virFSPoolObjUnlock(fspool); + return ret; +} + +int +virFSPoolSourceFindDuplicate(virConnectPtr conn ATTRIBUTE_UNUSED, + virFSPoolObjListPtr fspools, + virFSPoolDefPtr def) +{ + size_t i; + int ret = 1; + virFSPoolObjPtr fspool = NULL; + virFSPoolObjPtr matchfspool = NULL; + + /* Check the fspool list for duplicate underlying storage */ + for (i = 0; i < fspools->count; i++) { + fspool = fspools->objs[i]; + if (def->type != fspool->def->type) + continue; + + /* Don't mach against ourself if re-defining existing fspool ! */ + if (STREQ(fspool->def->name, def->name)) + continue; + + virFSPoolObjLock(fspool); + + switch ((virFSPoolType)fspool->def->type) { + case VIR_FSPOOL_DIR: + if (STREQ(fspool->def->target.path, def->target.path)) + matchfspool = fspool; + break; + + case VIR_FSPOOL_LAST: + break; + } + virFSPoolObjUnlock(fspool); + + if (matchfspool) + break; + } + + if (matchfspool) { + virReportError(VIR_ERR_OPERATION_FAILED, + _("FS source conflict with fspool: '%s'"), + matchfspool->def->name); + ret = -1; + } + return ret; +} + +void +virFSPoolObjLock(virFSPoolObjPtr obj) +{ + virMutexLock(&obj->lock); +} + +void +virFSPoolObjUnlock(virFSPoolObjPtr obj) +{ + virMutexUnlock(&obj->lock); +} + +#define MATCH(FLAG) (flags & (FLAG)) +static bool +virFSPoolMatch(virFSPoolObjPtr fspoolobj, + unsigned int flags) +{ + /* filter by active state */ + if (MATCH(VIR_CONNECT_LIST_FSPOOLS_FILTERS_ACTIVE) && + !((MATCH(VIR_CONNECT_LIST_FSPOOLS_ACTIVE) && + virFSPoolObjIsActive(fspoolobj)) || + (MATCH(VIR_CONNECT_LIST_FSPOOLS_INACTIVE) && + !virFSPoolObjIsActive(fspoolobj)))) + return false; + + /* filter by persistence */ + if (MATCH(VIR_CONNECT_LIST_FSPOOLS_FILTERS_PERSISTENT) && + !((MATCH(VIR_CONNECT_LIST_FSPOOLS_PERSISTENT) && + fspoolobj->configFile) || + (MATCH(VIR_CONNECT_LIST_FSPOOLS_TRANSIENT) && + !fspoolobj->configFile))) + return false; + + /* filter by autostart option */ + if (MATCH(VIR_CONNECT_LIST_FSPOOLS_FILTERS_AUTOSTART) && + !((MATCH(VIR_CONNECT_LIST_FSPOOLS_AUTOSTART) && + fspoolobj->autostart) || + (MATCH(VIR_CONNECT_LIST_FSPOOLS_NO_AUTOSTART) && + !fspoolobj->autostart))) + return false; + + /* filter by fspool type */ + if (MATCH(VIR_CONNECT_LIST_FSPOOLS_FILTERS_POOL_TYPE)) { + if (!(MATCH(VIR_CONNECT_LIST_FSPOOLS_DIR) && + (fspoolobj->def->type == VIR_FSPOOL_DIR))) + return false; + } + + return true; +} +#undef MATCH + +int +virFSPoolObjListExport(virConnectPtr conn, + virFSPoolObjList fspoolobjs, + virFSPoolPtr **fspools, + virFSPoolObjListFilter filter, + unsigned int flags) +{ + virFSPoolPtr *tmp_fspools = NULL; + virFSPoolPtr fspool = NULL; + int nfspools = 0; + int ret = -1; + size_t i; + + if (fspools && VIR_ALLOC_N(tmp_fspools, fspoolobjs.count + 1) < 0) + goto cleanup; + + for (i = 0; i < fspoolobjs.count; i++) { + virFSPoolObjPtr fspoolobj = fspoolobjs.objs[i]; + virFSPoolObjLock(fspoolobj); + if ((!filter || filter(conn, fspoolobj->def)) && + virFSPoolMatch(fspoolobj, flags)) { + if (fspools) { + if (!(fspool = virGetFSPool(conn, + fspoolobj->def->name, + fspoolobj->def->uuid, + NULL, NULL))) { + virFSPoolObjUnlock(fspoolobj); + goto cleanup; + } + tmp_fspools[nfspools] = fspool; + } + nfspools++; + } + virFSPoolObjUnlock(fspoolobj); + } + + if (tmp_fspools) { + /* trim the array to the final size */ + ignore_value(VIR_REALLOC_N(tmp_fspools, nfspools + 1)); + *fspools = tmp_fspools; + tmp_fspools = NULL; + } + + ret = nfspools; + + cleanup: + if (tmp_fspools) { + for (i = 0; i < nfspools; i++) + virObjectUnref(tmp_fspools[i]); + } + + VIR_FREE(tmp_fspools); + return ret; +} diff --git a/src/conf/fs_conf.h b/src/conf/fs_conf.h new file mode 100644 index 0000000..9c8e9f1 --- /dev/null +++ b/src/conf/fs_conf.h @@ -0,0 +1,323 @@ +/* + * fs_conf.h: config handling for fs driver + * Author: Olga Krishtal <okrishtal@virtuozzo.com> + * + * Copyright (C) 2016 Parallels IP Holdings GmbH + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + */ + +#ifndef __VIR_FS_CONF_H__ +# define __VIR_FS_CONF_H__ + +# include "internal.h" +# include "virbitmap.h" +# include "virthread.h" +# include "virutil.h" + +# include <libxml/tree.h> + +# define VIR_CONNECT_LIST_FSPOOLS_FILTERS_POOL_TYPE \ + VIR_CONNECT_LIST_FSPOOLS_DIR + +# define VIR_CONNECT_LIST_FSPOOLS_FILTERS_ACTIVE \ + (VIR_CONNECT_LIST_FSPOOLS_ACTIVE | \ + VIR_CONNECT_LIST_FSPOOLS_INACTIVE) + +# define VIR_CONNECT_LIST_FSPOOLS_FILTERS_PERSISTENT \ + (VIR_CONNECT_LIST_FSPOOLS_PERSISTENT | \ + VIR_CONNECT_LIST_FSPOOLS_TRANSIENT) + +# define VIR_CONNECT_LIST_FSPOOLS_FILTERS_AUTOSTART \ + (VIR_CONNECT_LIST_FSPOOLS_AUTOSTART | \ + VIR_CONNECT_LIST_FSPOOLS_NO_AUTOSTART) + +# define VIR_CONNECT_LIST_FSPOOLS_FILTERS_ALL \ + (VIR_CONNECT_LIST_FSPOOLS_FILTERS_ACTIVE | \ + VIR_CONNECT_LIST_FSPOOLS_FILTERS_PERSISTENT | \ + VIR_CONNECT_LIST_FSPOOLS_FILTERS_AUTOSTART | \ + VIR_CONNECT_LIST_FSPOOLS_FILTERS_POOL_TYPE) + +VIR_ENUM_DECL(virFSItem) +VIR_ENUM_DECL(virFS) + +typedef struct _virFSPerms virFSPerms; +typedef virFSPerms *virFSPermsPtr; +struct _virFSPerms { + mode_t mode; + uid_t uid; + gid_t gid; + char *label; +}; + +typedef struct _virFSSourcePoolDef virFSSourcePoolDef; +struct _virFSSourcePoolDef { + char *pool; /* pool name */ + char *item; /* item name */ + int itemtype; /* virFSItemType, internal only */ + int pooltype; /* virFSPoolType internal only */ +}; +typedef virFSSourcePoolDef *virFSSourcePoolDefPtr; + +typedef struct _virFSSource virFSSource; +typedef virFSSource *virFSSourcePtr; + +struct _virFSSource { + int type; /* virFSType */ + char *path; + virFSSourcePoolDefPtr srcpool; + char *driverName; + virFSPermsPtr perms; + unsigned long long capacity; /* in bytes, 0 if unknown */ + unsigned long long allocation; /* in bytes, 0 if unknown */ +}; + +typedef enum { + VIR_FSPOOL_DIR, /* Local directory */ + VIR_FSPOOL_LAST, +} virFSPoolType; + +VIR_ENUM_DECL(virFSPool) + +typedef struct _virFSItemDef virFSItemDef; +typedef virFSItemDef *virFSItemDefPtr; +struct _virFSItemDef { + char *name; + char *key; + int type; /* virFSItemType */ + + bool building; + unsigned int in_use; + + virFSSource target; +}; + +typedef struct _virFSItemDefList virFSItemDefList; +typedef virFSItemDefList *virFSItemDefListPtr; +struct _virFSItemDefList { + size_t count; + virFSItemDefPtr *objs; +}; + +typedef struct _virFSPoolSource virFSPoolSource; +typedef virFSPoolSource *virFSPoolSourcePtr; +struct _virFSPoolSource { + /* An optional (maybe multiple) host(s) */ + + /* Or a directory */ + char *dir; + + /* Or a name */ + char *name; + + /* Product name of the source*/ + char *product; + + /* Pool type specific format such as filesystem type, + * or lvm version, etc. + */ + int format; +}; + +typedef struct _virFSPoolTarget virFSPoolTarget; +typedef virFSPoolTarget *virFSPoolTargetPtr; +struct _virFSPoolTarget { + char *path; /* Optional local filesystem mapping */ + virFSPerms perms; /* Default permissions for volumes */ +}; + +typedef struct _virFSPoolDef virFSPoolDef; +typedef virFSPoolDef *virFSPoolDefPtr; +struct _virFSPoolDef { + char *name; + unsigned char uuid[VIR_UUID_BUFLEN]; + int type; /* virFSPoolType */ + + unsigned long long allocation; /* bytes */ + unsigned long long capacity; /* bytes */ + unsigned long long available; /* bytes */ + + virFSPoolSource source; + virFSPoolTarget target; +}; + +typedef struct _virFSPoolObj virFSPoolObj; +typedef virFSPoolObj *virFSPoolObjPtr; + +struct _virFSPoolObj { + virMutex lock; + + char *configFile; + char *autostartLink; + bool active; + int autostart; + unsigned int asyncjobs; + + virFSPoolDefPtr def; + virFSPoolDefPtr newDef; + + virFSItemDefList items; +}; + +typedef struct _virFSPoolObjList virFSPoolObjList; +typedef virFSPoolObjList *virFSPoolObjListPtr; +struct _virFSPoolObjList { + size_t count; + virFSPoolObjPtr *objs; +}; + +typedef struct _virFSDriverState virFSDriverState; +typedef virFSDriverState *virFSDriverStatePtr; + +struct _virFSDriverState { + virMutex lock; + + virFSPoolObjList fspools; + + char *configDir; + char *autostartDir; + char *stateDir; + bool privileged; +}; + +typedef struct _virFSPoolSourceList virFSPoolSourceList; +typedef virFSPoolSourceList *virFSPoolSourceListPtr; +struct _virFSPoolSourceList { + int type; + unsigned int nsources; + virFSPoolSourcePtr sources; +}; + +typedef bool (*virFSPoolObjListFilter)(virConnectPtr conn, + virFSPoolDefPtr def); + +static inline int +virFSPoolObjIsActive(virFSPoolObjPtr fspool) +{ + return fspool->active; +} + +int virFSPoolLoadAllConfigs(virFSPoolObjListPtr fspools, + const char *configDir, + const char *autostartDir); + +int virFSPoolLoadAllState(virFSPoolObjListPtr fspools, + const char *stateDir); + +virFSPoolObjPtr +virFSPoolLoadState(virFSPoolObjListPtr fspools, + const char *stateDir, + const char *name); +virFSPoolObjPtr +virFSPoolObjFindByUUID(virFSPoolObjListPtr fspools, + const unsigned char *uuid); +virFSPoolObjPtr +virFSPoolObjFindByName(virFSPoolObjListPtr fspools, + const char *name); + +virFSItemDefPtr +virFSItemDefFindByKey(virFSPoolObjPtr fspool, + const char *key); +virFSItemDefPtr +virFSItemDefFindByPath(virFSPoolObjPtr fspool, + const char *path); +virFSItemDefPtr +virFSItemDefFindByName(virFSPoolObjPtr fspool, + const char *name); + +void virFSPoolObjClearItems(virFSPoolObjPtr fspool); + +virFSPoolDefPtr virFSPoolDefParseString(const char *xml); +virFSPoolDefPtr virFSPoolDefParseFile(const char *filename); +virFSPoolDefPtr virFSPoolDefParseNode(xmlDocPtr xml, + xmlNodePtr root); +char *virFSPoolDefFormat(virFSPoolDefPtr def); + +typedef enum { + /* do not require volume capacity at all */ + VIR_ITEM_XML_PARSE_NO_CAPACITY = 1 << 0, + /* do not require volume capacity if the volume has a backing store */ + VIR_ITEM_XML_PARSE_OPT_CAPACITY = 1 << 1, +} virFSItemDefParseFlags; + +virFSItemDefPtr +virFSItemDefParseString(virFSPoolDefPtr fspool, + const char *xml, + unsigned int flags); +virFSItemDefPtr +virFSItemDefParseFile(virFSPoolDefPtr fspool, + const char *filename, + unsigned int flags); +virFSItemDefPtr +virFSItemDefParseNode(virFSPoolDefPtr fspool, + xmlDocPtr xml, + xmlNodePtr root, + unsigned int flags); +char *virFSItemDefFormat(virFSPoolDefPtr fspool, + virFSItemDefPtr def); + +virFSPoolObjPtr +virFSPoolObjAssignDef(virFSPoolObjListPtr fspools, + virFSPoolDefPtr def); + +int virFSPoolSaveState(const char *stateFile, + virFSPoolDefPtr def); +int virFSPoolSaveConfig(const char *configFile, + virFSPoolDefPtr def); +int virFSPoolObjSaveDef(virFSDriverStatePtr driver, + virFSPoolObjPtr fspool, + virFSPoolDefPtr def); +int virFSPoolObjDeleteDef(virFSPoolObjPtr fspool); + +void virFSItemDefFree(virFSItemDefPtr def); +void virFSPoolSourceClear(virFSPoolSourcePtr source); +void virFSPoolSourceFree(virFSPoolSourcePtr source); +void virFSPoolDefFree(virFSPoolDefPtr def); +void virFSPoolObjFree(virFSPoolObjPtr fspool); +void virFSPoolObjListFree(virFSPoolObjListPtr fspools); +void virFSPoolObjRemove(virFSPoolObjListPtr fspools, + virFSPoolObjPtr fspool); + +virFSPoolSourcePtr +virFSPoolDefParseSourceString(const char *srcSpec, + int fspool_type); +virFSPoolSourcePtr +virFSPoolSourceListNewSource(virFSPoolSourceListPtr list); +char *virFSPoolSourceListFormat(virFSPoolSourceListPtr def); + +int virFSPoolObjIsDuplicate(virFSPoolObjListPtr fspools, + virFSPoolDefPtr def, + unsigned int check_active); + +char *virFSPoolGetVhbaSCSIHostParent(virConnectPtr conn, + const char *name) + ATTRIBUTE_NONNULL(1); + +int virFSPoolSourceFindDuplicate(virConnectPtr conn, + virFSPoolObjListPtr fspools, + virFSPoolDefPtr def); + +void virFSPoolObjLock(virFSPoolObjPtr obj); +void virFSPoolObjUnlock(virFSPoolObjPtr obj); + +int virFSPoolObjListExport(virConnectPtr conn, + virFSPoolObjList fspoolobjs, + virFSPoolPtr **fspools, + virFSPoolObjListFilter filter, + unsigned int flags); + + + +#endif /* __VIR_FS_CONF_H__ */ diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index db8d337..900818d 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -567,6 +567,48 @@ virDomainConfNWFilterTeardown; virDomainConfVMNWFilterTeardown; +# conf/fs_conf.h +virFSItemDefFindByKey; +virFSItemDefFindByName; +virFSItemDefFindByPath; +virFSItemDefFormat; +virFSItemDefFree; +virFSItemDefParseFile; +virFSItemDefParseNode; +virFSItemDefParseString; +virFSItemTypeFromString; +virFSItemTypeToString; +virFSPoolDefFormat; +virFSPoolDefFree; +virFSPoolDefParseFile; +virFSPoolDefParseNode; +virFSPoolDefParseSourceString; +virFSPoolDefParseString; +virFSPoolLoadAllConfigs; +virFSPoolLoadAllState; +virFSPoolObjAssignDef; +virFSPoolObjClearItems; +virFSPoolObjDeleteDef; +virFSPoolObjFindByName; +virFSPoolObjFindByUUID; +virFSPoolObjIsDuplicate; +virFSPoolObjListExport; +virFSPoolObjListFree; +virFSPoolObjLock; +virFSPoolObjRemove; +virFSPoolObjSaveDef; +virFSPoolObjUnlock; +virFSPoolSaveConfig; +virFSPoolSaveState; +virFSPoolSourceClear; +virFSPoolSourceFindDuplicate; +virFSPoolSourceFree; +virFSPoolSourceListFormat; +virFSPoolSourceListNewSource; +virFSPoolTypeFromString; +virFSPoolTypeToString; + + # conf/interface_conf.h virInterfaceAssignDef; virInterfaceDefFormat; -- 1.8.3.1

Signed-off-by: Nikolay Shirokovskiy <nshirokovskiy@virtuozzo.com> Signed-off-by: Maxim Nestratov <mnestratov@virtuozzo.com> --- src/access/viraccessdriver.h | 12 ++++ src/access/viraccessdrivernop.c | 19 ++++++ src/access/viraccessdriverpolkit.c | 47 ++++++++++++++ src/access/viraccessdriverstack.c | 49 +++++++++++++++ src/access/viraccessmanager.c | 31 ++++++++++ src/access/viraccessmanager.h | 11 ++++ src/access/viraccessperm.c | 15 ++++- src/access/viraccessperm.h | 124 +++++++++++++++++++++++++++++++++++++ src/libvirt_private.syms | 6 ++ 9 files changed, 313 insertions(+), 1 deletion(-) diff --git a/src/access/viraccessdriver.h b/src/access/viraccessdriver.h index e3050b6..443a26e 100644 --- a/src/access/viraccessdriver.h +++ b/src/access/viraccessdriver.h @@ -61,6 +61,16 @@ typedef int (*virAccessDriverCheckStorageVolDrv)(virAccessManagerPtr manager, virStorageVolDefPtr vol, virAccessPermStorageVol av); +typedef int (*virAccessDriverCheckFSPoolDrv)(virAccessManagerPtr manager, + const char *driverName, + virFSPoolDefPtr fspool, + virAccessPermFSPool av); +typedef int (*virAccessDriverCheckFSItemDrv)(virAccessManagerPtr manager, + const char *driverName, + virFSPoolDefPtr fspool, + virFSItemDefPtr item, + virAccessPermFSItem av); + typedef int (*virAccessDriverSetupDrv)(virAccessManagerPtr manager); typedef void (*virAccessDriverCleanupDrv)(virAccessManagerPtr manager); @@ -83,6 +93,8 @@ struct _virAccessDriver { virAccessDriverCheckSecretDrv checkSecret; virAccessDriverCheckStoragePoolDrv checkStoragePool; virAccessDriverCheckStorageVolDrv checkStorageVol; + virAccessDriverCheckFSPoolDrv checkFSPool; + virAccessDriverCheckFSItemDrv checkFSItem; }; diff --git a/src/access/viraccessdrivernop.c b/src/access/viraccessdrivernop.c index 86ceef3..2d53e83 100644 --- a/src/access/viraccessdrivernop.c +++ b/src/access/viraccessdrivernop.c @@ -103,7 +103,24 @@ virAccessDriverNopCheckStorageVol(virAccessManagerPtr manager ATTRIBUTE_UNUSED, return 1; /* Allow */ } +static int +virAccessDriverNopCheckFSPool(virAccessManagerPtr manager ATTRIBUTE_UNUSED, + const char *driverName ATTRIBUTE_UNUSED, + virFSPoolDefPtr fspool ATTRIBUTE_UNUSED, + virAccessPermFSPool perm ATTRIBUTE_UNUSED) +{ + return 1; /* Allow */ +} +static int +virAccessDriverNopCheckFSItem(virAccessManagerPtr manager ATTRIBUTE_UNUSED, + const char *driverName ATTRIBUTE_UNUSED, + virFSPoolDefPtr fspool ATTRIBUTE_UNUSED, + virFSItemDefPtr item ATTRIBUTE_UNUSED, + virAccessPermFSItem perm ATTRIBUTE_UNUSED) +{ + return 1; /* Allow */ +} virAccessDriver accessDriverNop = { .name = "none", .checkConnect = virAccessDriverNopCheckConnect, @@ -115,4 +132,6 @@ virAccessDriver accessDriverNop = { .checkSecret = virAccessDriverNopCheckSecret, .checkStoragePool = virAccessDriverNopCheckStoragePool, .checkStorageVol = virAccessDriverNopCheckStorageVol, + .checkFSPool = virAccessDriverNopCheckFSPool, + .checkFSItem = virAccessDriverNopCheckFSItem, }; diff --git a/src/access/viraccessdriverpolkit.c b/src/access/viraccessdriverpolkit.c index 0d9e0a1..d38680e 100644 --- a/src/access/viraccessdriverpolkit.c +++ b/src/access/viraccessdriverpolkit.c @@ -398,6 +398,50 @@ virAccessDriverPolkitCheckStorageVol(virAccessManagerPtr manager, virAccessPermStorageVolTypeToString(perm), attrs); } +static int +virAccessDriverPolkitCheckFSPool(virAccessManagerPtr manager, + const char *driverName, + virFSPoolDefPtr fspool, + virAccessPermFSPool perm) +{ + char uuidstr[VIR_UUID_STRING_BUFLEN]; + const char *attrs[] = { + "connect_driver", driverName, + "fspool_name", fspool->name, + "fspool_uuid", uuidstr, + NULL, + }; + virUUIDFormat(fspool->uuid, uuidstr); + + return virAccessDriverPolkitCheck(manager, + "fs-pool", + virAccessPermFSPoolTypeToString(perm), + attrs); +} + +static int +virAccessDriverPolkitCheckFSItem(virAccessManagerPtr manager, + const char *driverName, + virFSPoolDefPtr fspool, + virFSItemDefPtr item, + virAccessPermFSItem perm) +{ + char uuidstr[VIR_UUID_STRING_BUFLEN]; + const char *attrs[] = { + "connect_driver", driverName, + "fspool_name", fspool->name, + "fspool_uuid", uuidstr, + "item_name", item->name, + "item_key", item->key, + NULL, + }; + virUUIDFormat(fspool->uuid, uuidstr); + + return virAccessDriverPolkitCheck(manager, + "fs-item", + virAccessPermFSItemTypeToString(perm), + attrs); +} virAccessDriver accessDriverPolkit = { .privateDataLen = sizeof(virAccessDriverPolkitPrivate), @@ -412,4 +456,7 @@ virAccessDriver accessDriverPolkit = { .checkSecret = virAccessDriverPolkitCheckSecret, .checkStoragePool = virAccessDriverPolkitCheckStoragePool, .checkStorageVol = virAccessDriverPolkitCheckStorageVol, + .checkFSPool = virAccessDriverPolkitCheckFSPool, + .checkFSItem = virAccessDriverPolkitCheckFSItem, + }; diff --git a/src/access/viraccessdriverstack.c b/src/access/viraccessdriverstack.c index b43a743..eb94b8b 100644 --- a/src/access/viraccessdriverstack.c +++ b/src/access/viraccessdriverstack.c @@ -267,6 +267,53 @@ virAccessDriverStackCheckStorageVol(virAccessManagerPtr manager, return ret; } +static int +virAccessDriverStackCheckFSPool(virAccessManagerPtr manager, + const char *driverName, + virFSPoolDefPtr fspool, + virAccessPermFSPool perm) +{ + virAccessDriverStackPrivatePtr priv = virAccessManagerGetPrivateData(manager); + int ret = 1; + size_t i; + + for (i = 0; i < priv->managersLen; i++) { + int rv; + /* We do not short-circuit on first denial - always check all drivers */ + rv = virAccessManagerCheckFSPool(priv->managers[i], driverName, fspool, perm); + if (rv == 0 && ret != -1) + ret = 0; + else if (rv < 0) + ret = -1; + } + + return ret; +} + +static int +virAccessDriverStackCheckFSItem(virAccessManagerPtr manager, + const char *driverName, + virFSPoolDefPtr fspool, + virFSItemDefPtr item, + virAccessPermFSItem perm) +{ + virAccessDriverStackPrivatePtr priv = virAccessManagerGetPrivateData(manager); + int ret = 1; + size_t i; + + for (i = 0; i < priv->managersLen; i++) { + int rv; + /* We do not short-circuit on first denial - always check all drivers */ + rv = virAccessManagerCheckFSItem(priv->managers[i], driverName, fspool, item, perm); + if (rv == 0 && ret != -1) + ret = 0; + else if (rv < 0) + ret = -1; + } + + return ret; +} + virAccessDriver accessDriverStack = { .privateDataLen = sizeof(virAccessDriverStackPrivate), .name = "stack", @@ -280,4 +327,6 @@ virAccessDriver accessDriverStack = { .checkSecret = virAccessDriverStackCheckSecret, .checkStoragePool = virAccessDriverStackCheckStoragePool, .checkStorageVol = virAccessDriverStackCheckStorageVol, + .checkFSPool = virAccessDriverStackCheckFSPool, + .checkFSItem = virAccessDriverStackCheckFSItem, }; diff --git a/src/access/viraccessmanager.c b/src/access/viraccessmanager.c index bcf552b..052779a 100644 --- a/src/access/viraccessmanager.c +++ b/src/access/viraccessmanager.c @@ -344,3 +344,34 @@ int virAccessManagerCheckStorageVol(virAccessManagerPtr manager, return virAccessManagerSanitizeError(ret); } + +int virAccessManagerCheckFSPool(virAccessManagerPtr manager, + const char *driverName, + virFSPoolDefPtr fspool, + virAccessPermFSPool perm) +{ + int ret = 0; + VIR_DEBUG("manager=%p(name=%s) driver=%s pool=%p perm=%d", + manager, manager->drv->name, driverName, fspool, perm); + + if (manager->drv->checkFSPool) + ret = manager->drv->checkFSPool(manager, driverName, fspool, perm); + + return virAccessManagerSanitizeError(ret); +} + +int virAccessManagerCheckFSItem(virAccessManagerPtr manager, + const char *driverName, + virFSPoolDefPtr fspool, + virFSItemDefPtr item, + virAccessPermFSItem perm) +{ + int ret = 0; + VIR_DEBUG("manager=%p(name=%s) driver=%s pool=%p vol=%p perm=%d", + manager, manager->drv->name, driverName, fspool, item, perm); + + if (manager->drv->checkFSItem) + ret = manager->drv->checkFSItem(manager, driverName, fspool, item, perm); + + return virAccessManagerSanitizeError(ret); +} diff --git a/src/access/viraccessmanager.h b/src/access/viraccessmanager.h index e7eb15d..b9452b5 100644 --- a/src/access/viraccessmanager.h +++ b/src/access/viraccessmanager.h @@ -27,6 +27,7 @@ # include "conf/nwfilter_conf.h" # include "conf/node_device_conf.h" # include "conf/storage_conf.h" +# include "conf/fs_conf.h" # include "conf/secret_conf.h" # include "conf/interface_conf.h" # include "access/viraccessperm.h" @@ -86,6 +87,16 @@ int virAccessManagerCheckStorageVol(virAccessManagerPtr manager, virStoragePoolDefPtr pool, virStorageVolDefPtr vol, virAccessPermStorageVol perm); +int virAccessManagerCheckFSPool(virAccessManagerPtr manager, + const char *driverName, + virFSPoolDefPtr fspool, + virAccessPermFSPool perm); +int virAccessManagerCheckFSItem(virAccessManagerPtr manager, + const char *driverName, + virFSPoolDefPtr fspool, + virFSItemDefPtr item, + virAccessPermFSItem perm); + #endif /* __VIR_ACCESS_MANAGER_H__ */ diff --git a/src/access/viraccessperm.c b/src/access/viraccessperm.c index 0f58290..00b8e7d 100644 --- a/src/access/viraccessperm.c +++ b/src/access/viraccessperm.c @@ -29,7 +29,7 @@ VIR_ENUM_IMPL(virAccessPermConnect, "search_domains", "search_networks", "search_storage_pools", "search_node_devices", "search_interfaces", "search_secrets", - "search_nwfilters", + "search_nwfilters", "search_fspools", "detect_storage_pools", "pm_control", "interface_transaction"); @@ -83,3 +83,16 @@ VIR_ENUM_IMPL(virAccessPermStorageVol, "getattr", "read", "create", "delete", "format", "resize", "data_read", "data_write"); + +VIR_ENUM_IMPL(virAccessPermFSPool, + VIR_ACCESS_PERM_FSPOOL_LAST, + "getattr", "read", "write", + "save", "delete", "start", "stop", + "refresh", "search_items", + "format"); + +VIR_ENUM_IMPL(virAccessPermFSItem, + VIR_ACCESS_PERM_FSITEM_LAST, + "getattr", "read", "create", "delete", + "format", "data_read", + "data_write"); diff --git a/src/access/viraccessperm.h b/src/access/viraccessperm.h index 1817da7..d0f69a0 100644 --- a/src/access/viraccessperm.h +++ b/src/access/viraccessperm.h @@ -67,6 +67,12 @@ typedef enum { VIR_ACCESS_PERM_CONNECT_SEARCH_STORAGE_POOLS, /** + * @desc: List fs pools + * @message: Listing fs pools requires authorization + * @anonymous: 1 + */ + VIR_ACCESS_PERM_CONNECT_SEARCH_FSPOOLS, + /** * @desc: List node devices * @message: Listing node devices requires authorization * @anonymous: 1 @@ -651,6 +657,122 @@ typedef enum { VIR_ACCESS_PERM_STORAGE_VOL_LAST } virAccessPermStorageVol; +typedef enum { + + /** + * @desc: Access fs pool + * @message: Accessing fs pool requires authorization + * @anonymous: 1 + */ + VIR_ACCESS_PERM_FSPOOL_GETATTR, + + /** + * @desc: Read fs pool + * @message: Reading fs pool configuration requires authorization + * @anonymous: 1 + */ + VIR_ACCESS_PERM_FSPOOL_READ, + + /** + * @desc: Write fs pool + * @message: Writing fs pool configuration requires authorization + */ + VIR_ACCESS_PERM_FSPOOL_WRITE, + + /** + * @desc: Save fs pool + * @message: Saving fs pool configuration requires authorization + */ + VIR_ACCESS_PERM_FSPOOL_SAVE, + + /** + * @desc: Delete fs pool + * @message: Deleting fs pool configuration requires authorization + */ + VIR_ACCESS_PERM_FSPOOL_DELETE, + + /** + * @desc: Start fs pool + * @message: Starting fs pool configuration requires authorization + */ + VIR_ACCESS_PERM_FSPOOL_START, + + /** + * @desc: Stop fs pool + * @message: Stopping fs pool configuration requires authorization + */ + VIR_ACCESS_PERM_FSPOOL_STOP, + + /** + * @desc: Refresh fs pool + * @message: Refreshing fs pool items requires authorization + */ + VIR_ACCESS_PERM_FSPOOL_REFRESH, + + /** + * @desc: List fs pool items + * @message: Listing fs pool items requires authorization + */ + VIR_ACCESS_PERM_FSPOOL_SEARCH_ITEMS, + + /** + * @desc: Format fs pool + * @message: Formatting fs pool data requires authorization + */ + VIR_ACCESS_PERM_FSPOOL_FORMAT, + + VIR_ACCESS_PERM_FSPOOL_LAST +} virAccessPermFSPool; + +typedef enum { + + /** + * @desc: Access fs item + * @message: Acceessing fs item requires authorization + * @anonymous: 1 + */ + VIR_ACCESS_PERM_FSITEM_GETATTR, + + /** + * @desc: Read fs item + * @message: Reading fs item configuration requires authorization + * @anonymous: 1 + */ + VIR_ACCESS_PERM_FSITEM_READ, + + /** + * @desc: Create fs item + * @message: Creating fs item requires authorization + */ + VIR_ACCESS_PERM_FSITEM_CREATE, + + /** + * @desc: Delete fs item + * @message: Deleting fs item requires authorization + */ + VIR_ACCESS_PERM_FSITEM_DELETE, + + /** + * @desc: Format fs item + * @message: Formatting fs item data requires authorization + */ + VIR_ACCESS_PERM_FSITEM_FORMAT, + + /** + * @desc: Read fs item data + * @message: Reading fs item data requires authorization + */ + VIR_ACCESS_PERM_FSITEM_DATA_READ, + + /** + * @desc: Write fs item data + * @message: Writing fs item data requires authorization + */ + VIR_ACCESS_PERM_FSITEM_DATA_WRITE, + + VIR_ACCESS_PERM_FSITEM_LAST +} virAccessPermFSItem; + VIR_ENUM_DECL(virAccessPermConnect); VIR_ENUM_DECL(virAccessPermDomain); VIR_ENUM_DECL(virAccessPermInterface); @@ -660,5 +782,7 @@ VIR_ENUM_DECL(virAccessPermNWFilter); VIR_ENUM_DECL(virAccessPermSecret); VIR_ENUM_DECL(virAccessPermStoragePool); VIR_ENUM_DECL(virAccessPermStorageVol); +VIR_ENUM_DECL(virAccessPermFSPool); +VIR_ENUM_DECL(virAccessPermFSItem); #endif /* __VIR_ACCESS_PERM_H__ */ diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 900818d..308dcc2 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -8,6 +8,8 @@ # access/viraccessmanager.h virAccessManagerCheckConnect; virAccessManagerCheckDomain; +virAccessManagerCheckFSItem; +virAccessManagerCheckFSPool; virAccessManagerCheckInterface; virAccessManagerCheckNetwork; virAccessManagerCheckNodeDevice; @@ -26,6 +28,10 @@ virAccessPermConnectTypeFromString; virAccessPermConnectTypeToString; virAccessPermDomainTypeFromString; virAccessPermDomainTypeToString; +virAccessPermFSItemTypeFromString; +virAccessPermFSItemTypeToString; +virAccessPermFSPoolTypeFromString; +virAccessPermFSPoolTypeToString; virAccessPermInterfaceTypeFromString; virAccessPermInterfaceTypeToString; virAccessPermNetworkTypeFromString; -- 1.8.3.1

Signed-off-by: Nikolay Shirokovskiy <nshirokovskiy@virtuozzo.com> Signed-off-by: Maxim Nestratov <mnestratov@virtuozzo.com> --- daemon/libvirtd.c | 10 + daemon/remote.c | 35 ++++ src/check-driverimpls.pl | 2 + src/driver.h | 1 + src/remote/remote_driver.c | 66 ++++++ src/remote/remote_protocol.x | 466 ++++++++++++++++++++++++++++++++++++++++++- src/remote_protocol-structs | 165 +++++++++++++++ src/rpc/gendispatch.pl | 23 ++- 8 files changed, 760 insertions(+), 8 deletions(-) diff --git a/daemon/libvirtd.c b/daemon/libvirtd.c index 95c1b1c..b6d4d80 100644 --- a/daemon/libvirtd.c +++ b/daemon/libvirtd.c @@ -92,6 +92,9 @@ # ifdef WITH_STORAGE # include "storage/storage_driver.h" # endif +# ifdef WITH_FS +# include "fs/fs_driver.h" +# endif # ifdef WITH_NODE_DEVICES # include "node_device/node_device_driver.h" # endif @@ -374,6 +377,9 @@ static void daemonInitialize(void) # ifdef WITH_NWFILTER virDriverLoadModule("nwfilter"); # endif +# ifdef WITH_FS + virDriverLoadModule("fs"); +# endif # ifdef WITH_XEN virDriverLoadModule("xen"); # endif @@ -398,6 +404,7 @@ static void daemonInitialize(void) # ifdef WITH_VZ virDriverLoadModule("vz"); # endif + #else # ifdef WITH_NETWORK networkRegister(); @@ -408,6 +415,9 @@ static void daemonInitialize(void) # ifdef WITH_STORAGE storageRegister(); # endif +# ifdef WITH_FS + fsRegister(); +# endif # ifdef WITH_NODE_DEVICES nodedevRegister(); # endif diff --git a/daemon/remote.c b/daemon/remote.c index e414f92..98cd9f6 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -88,6 +88,8 @@ static virNetworkPtr get_nonnull_network(virConnectPtr conn, remote_nonnull_netw static virInterfacePtr get_nonnull_interface(virConnectPtr conn, remote_nonnull_interface iface); static virStoragePoolPtr get_nonnull_storage_pool(virConnectPtr conn, remote_nonnull_storage_pool pool); static virStorageVolPtr get_nonnull_storage_vol(virConnectPtr conn, remote_nonnull_storage_vol vol); +static virFSPoolPtr get_nonnull_fspool(virConnectPtr conn, remote_nonnull_fspool fspool); +static virFSItemPtr get_nonnull_fsitem(virConnectPtr conn, remote_nonnull_fsitem item); static virSecretPtr get_nonnull_secret(virConnectPtr conn, remote_nonnull_secret secret); static virNWFilterPtr get_nonnull_nwfilter(virConnectPtr conn, remote_nonnull_nwfilter nwfilter); static virDomainSnapshotPtr get_nonnull_domain_snapshot(virDomainPtr dom, remote_nonnull_domain_snapshot snapshot); @@ -97,6 +99,8 @@ static void make_nonnull_network(remote_nonnull_network *net_dst, virNetworkPtr static void make_nonnull_interface(remote_nonnull_interface *interface_dst, virInterfacePtr interface_src); static void make_nonnull_storage_pool(remote_nonnull_storage_pool *pool_dst, virStoragePoolPtr pool_src); static void make_nonnull_storage_vol(remote_nonnull_storage_vol *vol_dst, virStorageVolPtr vol_src); +static void make_nonnull_fspool(remote_nonnull_fspool *fspool_dst, virFSPoolPtr fspool_src); +static void make_nonnull_fsitem(remote_nonnull_fsitem *item_dst, virFSItemPtr item_src); static void make_nonnull_node_device(remote_nonnull_node_device *dev_dst, virNodeDevicePtr dev_src); static void make_nonnull_secret(remote_nonnull_secret *secret_dst, virSecretPtr secret_src); static void make_nonnull_nwfilter(remote_nonnull_nwfilter *net_dst, virNWFilterPtr nwfilter_src); @@ -6660,6 +6664,22 @@ get_nonnull_domain_snapshot(virDomainPtr dom, remote_nonnull_domain_snapshot sna return virGetDomainSnapshot(dom, snapshot.name); } +static virFSPoolPtr +get_nonnull_fspool(virConnectPtr conn, remote_nonnull_fspool fspool) +{ + return virGetFSPool(conn, fspool.name, BAD_CAST fspool.uuid, + NULL, NULL); +} + +static virFSItemPtr +get_nonnull_fsitem(virConnectPtr conn, remote_nonnull_fsitem item) +{ + virFSItemPtr ret; + ret = virGetFSItem(conn, item.fspool, item.name, item.key, + NULL, NULL); + return ret; +} + static virNodeDevicePtr get_nonnull_node_device(virConnectPtr conn, remote_nonnull_node_device dev) { @@ -6706,6 +6726,21 @@ make_nonnull_storage_vol(remote_nonnull_storage_vol *vol_dst, virStorageVolPtr v } static void +make_nonnull_fspool(remote_nonnull_fspool *fspool_dst, virFSPoolPtr fspool_src) +{ + ignore_value(VIR_STRDUP_QUIET(fspool_dst->name, fspool_src->name)); + memcpy(fspool_dst->uuid, fspool_src->uuid, VIR_UUID_BUFLEN); +} + +static void +make_nonnull_fsitem(remote_nonnull_fsitem *item_dst, virFSItemPtr item_src) +{ + ignore_value(VIR_STRDUP_QUIET(item_dst->fspool, item_src->fspool)); + ignore_value(VIR_STRDUP_QUIET(item_dst->name, item_src->name)); + ignore_value(VIR_STRDUP_QUIET(item_dst->key, item_src->key)); +} + +static void make_nonnull_node_device(remote_nonnull_node_device *dev_dst, virNodeDevicePtr dev_src) { ignore_value(VIR_STRDUP_QUIET(dev_dst->name, dev_src->name)); diff --git a/src/check-driverimpls.pl b/src/check-driverimpls.pl index e320558..bd86886 100755 --- a/src/check-driverimpls.pl +++ b/src/check-driverimpls.pl @@ -54,6 +54,8 @@ while (<>) { if ($api !~ /^$mainprefix/) { $suffix =~ s/^[a-z]+(?:Unified)?//; $suffix =~ s/^([A-Z]+)/lc $1/e; + $suffix =~ s/fsitem/fsItem/; + $suffix =~ s/fspool/fsPool/; } if ($api ne $suffix) { diff --git a/src/driver.h b/src/driver.h index fb93083..64f7460 100644 --- a/src/driver.h +++ b/src/driver.h @@ -87,6 +87,7 @@ struct _virConnectDriver { virNWFilterDriverPtr nwfilterDriver; virSecretDriverPtr secretDriver; virStorageDriverPtr storageDriver; + virFSDriverPtr fsDriver; }; int virRegisterConnectDriver(virConnectDriverPtr driver, diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index 3b8b796..e6cb753 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -144,6 +144,8 @@ static virNWFilterPtr get_nonnull_nwfilter(virConnectPtr conn, remote_nonnull_nw static virInterfacePtr get_nonnull_interface(virConnectPtr conn, remote_nonnull_interface iface); static virStoragePoolPtr get_nonnull_storage_pool(virConnectPtr conn, remote_nonnull_storage_pool pool); static virStorageVolPtr get_nonnull_storage_vol(virConnectPtr conn, remote_nonnull_storage_vol vol); +static virFSPoolPtr get_nonnull_fspool(virConnectPtr conn, remote_nonnull_fspool fspool); +static virFSItemPtr get_nonnull_fsitem(virConnectPtr conn, remote_nonnull_fsitem item); static virNodeDevicePtr get_nonnull_node_device(virConnectPtr conn, remote_nonnull_node_device dev); static virSecretPtr get_nonnull_secret(virConnectPtr conn, remote_nonnull_secret secret); static virDomainSnapshotPtr get_nonnull_domain_snapshot(virDomainPtr domain, remote_nonnull_domain_snapshot snapshot); @@ -152,6 +154,8 @@ static void make_nonnull_network(remote_nonnull_network *net_dst, virNetworkPtr static void make_nonnull_interface(remote_nonnull_interface *interface_dst, virInterfacePtr interface_src); static void make_nonnull_storage_pool(remote_nonnull_storage_pool *pool_dst, virStoragePoolPtr vol_src); static void make_nonnull_storage_vol(remote_nonnull_storage_vol *vol_dst, virStorageVolPtr vol_src); +static void make_nonnull_fspool(remote_nonnull_fspool *fspool_dst, virFSPoolPtr fspool_src); +static void make_nonnull_fsitem(remote_nonnull_fsitem *item_dst, virFSItemPtr item_src); static void make_nonnull_node_device(remote_nonnull_node_device *dev_dst, virNodeDevicePtr dev_src); static void make_nonnull_secret(remote_nonnull_secret *secret_dst, virSecretPtr secret_src); @@ -7841,6 +7845,19 @@ get_nonnull_storage_vol(virConnectPtr conn, remote_nonnull_storage_vol vol) NULL, NULL); } +static virFSPoolPtr +get_nonnull_fspool(virConnectPtr conn, remote_nonnull_fspool fspool) +{ + return virGetFSPool(conn, fspool.name, BAD_CAST fspool.uuid, + NULL, NULL); +} + +static virFSItemPtr +get_nonnull_fsitem(virConnectPtr conn, remote_nonnull_fsitem item) +{ + return virGetFSItem(conn, item.fspool, item.name, item.key, + NULL, NULL); +} static virNodeDevicePtr get_nonnull_node_device(virConnectPtr conn, remote_nonnull_node_device dev) { @@ -7906,6 +7923,21 @@ make_nonnull_storage_vol(remote_nonnull_storage_vol *vol_dst, virStorageVolPtr v } static void +make_nonnull_fspool(remote_nonnull_fspool *fspool_dst, virFSPoolPtr fspool_src) +{ + fspool_dst->name = fspool_src->name; + memcpy(fspool_dst->uuid, fspool_src->uuid, VIR_UUID_BUFLEN); +} + +static void +make_nonnull_fsitem(remote_nonnull_fsitem *item_dst, virFSItemPtr item_src) +{ + item_dst->fspool = item_src->fspool; + item_dst->name = item_src->name; + item_dst->key = item_src->key; +} + +static void make_nonnull_secret(remote_nonnull_secret *secret_dst, virSecretPtr secret_src) { memcpy(secret_dst->uuid, secret_src->uuid, VIR_UUID_BUFLEN); @@ -8296,6 +8328,39 @@ static virNWFilterDriver nwfilter_driver = { .connectListAllNWFilters = remoteConnectListAllNWFilters, /* 0.10.2 */ }; +static virFSDriver fspool_driver = { + .fsPoolLookupByName = remoteFSPoolLookupByName, + .fsPoolLookupByUUID = remoteFSPoolLookupByUUID, + .fsPoolLookupByItem = remoteFSPoolLookupByItem, + .fsPoolCreateXML = remoteFSPoolCreateXML, + .fsPoolDefineXML = remoteFSPoolDefineXML, + .fsPoolBuild = remoteFSPoolBuild, + .fsPoolUndefine = remoteFSPoolUndefine, + .fsPoolCreate = remoteFSPoolCreate, + .fsPoolDestroy = remoteFSPoolDestroy, + .fsPoolDelete = remoteFSPoolDelete, + .fsPoolRefresh = remoteFSPoolRefresh, + .fsPoolGetInfo = remoteFSPoolGetInfo, + .fsPoolGetXMLDesc = remoteFSPoolGetXMLDesc, + .fsPoolGetAutostart = remoteFSPoolGetAutostart, + .fsPoolSetAutostart = remoteFSPoolSetAutostart, + .fsPoolNumOfItems = remoteFSPoolNumOfItems, + .fsPoolListItems = remoteFSPoolListItems, + .fsPoolListAllItems = remoteFSPoolListAllItems, + .fsItemLookupByName = remoteFSItemLookupByName, + .fsItemLookupByKey = remoteFSItemLookupByKey, + .fsItemLookupByPath = remoteFSItemLookupByPath, + .fsItemCreateXML = remoteFSItemCreateXML, + .fsItemCreateXMLFrom = remoteFSItemCreateXMLFrom, + .fsItemDelete = remoteFSItemDelete, + .fsItemGetInfo = remoteFSItemGetInfo, + .fsItemGetXMLDesc = remoteFSItemGetXMLDesc, + .fsItemGetPath = remoteFSItemGetPath, + .fsPoolIsActive = remoteFSPoolIsActive, + .fsPoolIsPersistent = remoteFSPoolIsPersistent, + .connectListAllFSPools = remoteConnectListAllFSPools, +}; + static virConnectDriver connect_driver = { .hypervisorDriver = &hypervisor_driver, .interfaceDriver = &interface_driver, @@ -8304,6 +8369,7 @@ static virConnectDriver connect_driver = { .nwfilterDriver = &nwfilter_driver, .secretDriver = &secret_driver, .storageDriver = &storage_driver, + .fsDriver = &fspool_driver, }; static virStateDriver state_driver = { diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index 8a8e263..320e773 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -85,6 +85,12 @@ const REMOTE_STORAGE_POOL_LIST_MAX = 4096; /* Upper limit on lists of storage vols. */ const REMOTE_STORAGE_VOL_LIST_MAX = 16384; +/* Upper limit on lists of fspools. */ +const REMOTE_FSPOOL_LIST_MAX = 4096; + +/* Upper limit on lists of fsitems. */ +const REMOTE_FSITEM_LIST_MAX = 16384; + /* Upper limit on lists of node devices. */ const REMOTE_NODE_DEVICE_LIST_MAX = 16384; @@ -294,6 +300,19 @@ struct remote_nonnull_storage_vol { remote_nonnull_string key; }; +/* A fspool which may not be NULL. */ +struct remote_nonnull_fspool { + remote_nonnull_string name; + remote_uuid uuid; +}; + +/* A fsitem which may not be NULL. */ +struct remote_nonnull_fsitem { + remote_nonnull_string fspool; + remote_nonnull_string name; + remote_nonnull_string key; +}; + /* A node device which may not be NULL. */ struct remote_nonnull_node_device { remote_nonnull_string name; @@ -318,6 +337,8 @@ typedef remote_nonnull_network *remote_network; typedef remote_nonnull_nwfilter *remote_nwfilter; typedef remote_nonnull_storage_pool *remote_storage_pool; typedef remote_nonnull_storage_vol *remote_storage_vol; +typedef remote_nonnull_fspool *remote_fspool; +typedef remote_nonnull_fsitem *remote_fsitem; typedef remote_nonnull_node_device *remote_node_device; /* Error message. See <virterror.h> for explanation of fields. */ @@ -1955,6 +1976,206 @@ struct remote_storage_vol_resize_args { unsigned int flags; }; +/* FS pool calls: */ + +struct remote_fspool_lookup_by_uuid_args { + remote_uuid uuid; +}; + +struct remote_fspool_lookup_by_uuid_ret { + remote_nonnull_fspool pool; +}; + +struct remote_fspool_lookup_by_name_args { + remote_nonnull_string name; +}; + +struct remote_fspool_lookup_by_name_ret { + remote_nonnull_fspool fspool; +}; + +struct remote_fspool_lookup_by_item_args { + remote_nonnull_fsitem item; +}; + +struct remote_fspool_lookup_by_item_ret { + remote_nonnull_fspool fspool; +}; + +struct remote_fspool_create_xml_args { + remote_nonnull_string xml; + unsigned int flags; +}; + +struct remote_fspool_create_xml_ret { + remote_nonnull_fspool fspool; +}; + +struct remote_fspool_define_xml_args { + remote_nonnull_string xml; + unsigned int flags; +}; + +struct remote_fspool_define_xml_ret { + remote_nonnull_fspool fspool; +}; + +struct remote_fspool_build_args { + remote_nonnull_fspool fspool; + unsigned int flags; +}; + +struct remote_fspool_undefine_args { + remote_nonnull_fspool fspool; +}; + +struct remote_fspool_create_args { + remote_nonnull_fspool fspool; + unsigned int flags; +}; + +struct remote_fspool_destroy_args { + remote_nonnull_fspool fspool; +}; +struct remote_fspool_delete_args { + remote_nonnull_fspool fspool; + unsigned int flags; +}; + +struct remote_fspool_get_xml_desc_args { + remote_nonnull_fspool fspool; + unsigned int flags; +}; + +struct remote_fs_pool_get_xml_desc_ret { + remote_nonnull_string xml; +}; + +struct remote_fspool_get_info_args { + remote_nonnull_fspool fspool; +}; + +struct remote_fspool_get_info_ret { /* insert@1 */ + unsigned char state; + unsigned hyper capacity; + unsigned hyper allocation; + unsigned hyper available; +}; + +struct remote_fspool_get_autostart_args { + remote_nonnull_fspool fspool; +}; + +struct remote_fspool_get_autostart_ret { + int autostart; +}; + +struct remote_fspool_set_autostart_args { + remote_nonnull_fspool fspool; + int autostart; +}; + +struct remote_fspool_num_of_items_args { + remote_nonnull_fspool fspool; +}; + +struct remote_fspool_num_of_items_ret { + int num; +}; + +struct remote_fspool_list_items_args { + remote_nonnull_fspool fspool; + int maxnames; +}; + +struct remote_fspool_list_items_ret { + remote_nonnull_string names<REMOTE_FSITEM_LIST_MAX>; /* insert@1 */ +}; +struct remote_fspool_refresh_args { + remote_nonnull_fspool fspool; + unsigned int flags; +}; + +/* FS item calls: */ + +struct remote_fsitem_lookup_by_name_args { + remote_nonnull_fspool fspool; + remote_nonnull_string name; +}; + +struct remote_fsitem_lookup_by_name_ret { + remote_nonnull_fsitem item; +}; + +struct remote_fsitem_lookup_by_key_args { + remote_nonnull_string key; +}; + +struct remote_fsitem_lookup_by_key_ret { + remote_nonnull_fsitem item; +}; + +struct remote_fsitem_lookup_by_path_args { + remote_nonnull_string path; +}; + +struct remote_fsitem_lookup_by_path_ret { + remote_nonnull_fsitem item; +}; + +struct remote_fsitem_create_xml_args { + remote_nonnull_fspool fspool; + remote_nonnull_string xml; + unsigned int flags; +}; + +struct remote_fsitem_create_xml_ret { + remote_nonnull_fsitem item; +}; + +struct remote_fsitem_create_xml_from_args { + remote_nonnull_fspool fspool; + remote_nonnull_string xml; + remote_nonnull_fsitem cloneitem; + unsigned int flags; +}; + +struct remote_fsitem_create_xml_from_ret { + remote_nonnull_fsitem item; +}; + +struct remote_fsitem_delete_args { + remote_nonnull_fsitem item; + unsigned int flags; +}; + +struct remote_fsitem_get_xml_desc_args { + remote_nonnull_fsitem item; + unsigned int flags; +}; + +struct remote_fsitem_get_xml_desc_ret { + remote_nonnull_string xml; +}; + +struct remote_fsitem_get_info_args { + remote_nonnull_fsitem item; +}; + +struct remote_fsitem_get_info_ret { /* insert@1 */ + char type; + unsigned hyper capacity; + unsigned hyper allocation; +}; + +struct remote_fsitem_get_path_args { + remote_nonnull_fsitem item; +}; + +struct remote_fsitem_get_path_ret { + remote_nonnull_string name; +}; + /* Node driver calls: */ struct remote_node_num_of_devices_args { @@ -2244,7 +2465,21 @@ struct remote_storage_pool_is_persistent_ret { int persistent; }; +struct remote_fspool_is_active_args { + remote_nonnull_fspool fspool; +}; +struct remote_fspool_is_active_ret { + int active; +}; + +struct remote_fspool_is_persistent_args { + remote_nonnull_fspool fspool; +}; + +struct remote_fspool_is_persistent_ret { + int persistent; +}; struct remote_interface_is_active_args { remote_nonnull_interface iface; }; @@ -2874,6 +3109,27 @@ struct remote_storage_pool_list_all_volumes_ret { /* insert@1 */ unsigned int ret; }; +struct remote_connect_list_all_fspools_args { + int need_results; + unsigned int flags; +}; + +struct remote_connect_list_all_fspools_ret { /* insert@1 */ + remote_nonnull_fspool fspools<REMOTE_FSPOOL_LIST_MAX>; + unsigned int ret; +}; + +struct remote_fspool_list_all_items_args { + remote_nonnull_fspool fspool; + int need_results; + unsigned int flags; +}; + +struct remote_fspool_list_all_items_ret { /* insert@1 */ + remote_nonnull_fsitem items<REMOTE_FSITEM_LIST_MAX>; + unsigned int ret; +}; + struct remote_connect_list_all_networks_args { int need_results; unsigned int flags; @@ -5934,5 +6190,213 @@ enum remote_procedure { * @generate: both * @acl: none */ - REMOTE_PROC_NODE_DEVICE_EVENT_UPDATE = 377 + REMOTE_PROC_NODE_DEVICE_EVENT_UPDATE = 377, + + /** + * @generate: both + * @acl: fspool:start + * @acl: fspool:write + */ + REMOTE_PROC_FSPOOL_CREATE_XML = 382, + + /** + * @generate: both + * @priority: high + * @acl: fspool:write + * @acl: fspool:save + */ + REMOTE_PROC_FSPOOL_DEFINE_XML = 383, + + /** + * @generate: both + * @acl: fspool:format + */ + REMOTE_PROC_FSPOOL_BUILD = 384, + + /** + * @generate: both + * @acl: fspool:format + */ + REMOTE_PROC_FSPOOL_DELETE = 385, + + /** + * @generate: both + * @priority: high + * @acl: fspool:delete + */ + REMOTE_PROC_FSPOOL_UNDEFINE = 386, + + /** + * @generate: both + * @priority: high + * @acl: fspool:getattr + */ + REMOTE_PROC_FSPOOL_LOOKUP_BY_NAME = 387, + + /** + * @generate: both + * @priority: high + * @acl: fspool:getattr + */ + REMOTE_PROC_FSPOOL_LOOKUP_BY_UUID = 388, + + /** + * @generate: both + * @priority: high + * @acl: fspool:getattr + */ + REMOTE_PROC_FSPOOL_LOOKUP_BY_ITEM = 389, + + /** + * @generate: both + * @priority: high + * @acl: fspool:read + */ + REMOTE_PROC_FSPOOL_GET_INFO = 390, + + /** + * @generate: both + * @priority: high + * @acl: fspool:read + */ + REMOTE_PROC_FSPOOL_GET_XML_DESC = 391, + + /** + * @generate: both + * @priority: high + * @acl: fspool:search_items + * @aclfilter: fsitem:getattr + */ + REMOTE_PROC_FSPOOL_NUM_OF_ITEMS = 392, + + /** + * @generate: both + * @priority: high + * @acl: fspool:search_items + * @aclfilter: fsitem:getattr + */ + REMOTE_PROC_FSPOOL_LIST_ITEMS = 393, + + /** + * @generate: both + * @acl: fsitem:create + */ + REMOTE_PROC_FSITEM_CREATE_XML = 394, + + /** + * @generate: both + * @acl: fsitem:delete + */ + REMOTE_PROC_FSITEM_DELETE = 395, + + /** + * @generate: both + * @priority: high + * @acl: fsitem:getattr + */ + REMOTE_PROC_FSITEM_LOOKUP_BY_NAME = 396, + + /** + * @generate: both + * @priority: high + * @acl: fsitem:getattr + */ + REMOTE_PROC_FSITEM_LOOKUP_BY_KEY = 397, + + /** + * @generate: both + * @priority: high + * @acl: fsitem:getattr + */ + REMOTE_PROC_FSITEM_LOOKUP_BY_PATH = 398, + + /** + * @generate: both + * @priority: high + * @acl: fsitem:read + */ + REMOTE_PROC_FSITEM_GET_INFO = 399, + + /** + * @generate: both + * @priority: high + * @acl: fsitem:read + */ + REMOTE_PROC_FSITEM_GET_XML_DESC = 400, + + /** + * @generate: both + * @priority: high + * @acl: fsitem:read + */ + REMOTE_PROC_FSITEM_GET_PATH = 401, + + /** + * @generate: both + * @acl: fsitem:create + */ + REMOTE_PROC_FSITEM_CREATE_XML_FROM = 402, + + /** + * @generate: both + * @priority: high + * @acl: connect:search_fspools + * @aclfilter: fspool:getattr + */ + REMOTE_PROC_CONNECT_LIST_ALL_FSPOOLS = 403, + + /** + * @generate: both + * @priority: high + * @acl: fspool:search_items + * @aclfilter: fsitem:getattr + */ + REMOTE_PROC_FSPOOL_LIST_ALL_ITEMS = 404, + + /** + * @generate: both + * @acl: fspool:refresh + */ + REMOTE_PROC_FSPOOL_REFRESH = 405, + + /** + * @generate: both + * @priority: high + * @acl: fspool:read + */ + REMOTE_PROC_FSPOOL_IS_ACTIVE = 406, + + /** + * @generate: both + * @priority: high + * @acl: fspool:read + */ + REMOTE_PROC_FSPOOL_IS_PERSISTENT = 407, + + /** + * @generate: both + * @priority: high + * @acl: fspool:read + */ + REMOTE_PROC_FSPOOL_GET_AUTOSTART = 408, + + /** + * @generate: both + * @priority: high + * @acl: fspool:write + */ + REMOTE_PROC_FSPOOL_SET_AUTOSTART = 409, + + /** + * @generate: both + * @acl: fspool:start + */ + REMOTE_PROC_FSPOOL_CREATE = 410, + + /** + * @generate: both + * @priority: high + * @acl: fspool:stop + */ + REMOTE_PROC_FSPOOL_DESTROY = 411 }; diff --git a/src/remote_protocol-structs b/src/remote_protocol-structs index b71accc..bac0efb 100644 --- a/src/remote_protocol-structs +++ b/src/remote_protocol-structs @@ -34,6 +34,15 @@ struct remote_nonnull_storage_vol { remote_nonnull_string name; remote_nonnull_string key; }; +struct remote_nonnull_fspool { + remote_nonnull_string name; + remote_uuid uuid; +}; +struct remote_nonnull_fsitem { + remote_nonnull_string fspool; + remote_nonnull_string name; + remote_nonnull_string key; +}; struct remote_nonnull_node_device { remote_nonnull_string name; }; @@ -1483,6 +1492,162 @@ struct remote_storage_vol_resize_args { uint64_t capacity; u_int flags; }; +struct remote_fspool_lookup_by_uuid_args { + remote_uuid uuid; +}; +struct remote_fspool_lookup_by_uuid_ret { + remote_nonnull_fspool pool; +}; +struct remote_fspool_lookup_by_name_args { + remote_nonnull_string name; +}; +struct remote_fspool_lookup_by_name_ret { + remote_nonnull_fspool fspool; +}; +struct remote_fspool_lookup_by_item_args { + remote_nonnull_fsitem item; +}; +struct remote_fspool_lookup_by_item_ret { + remote_nonnull_fspool fspool; +}; +struct remote_fspool_create_xml_args { + remote_nonnull_string xml; + unsigned int flags; +}; +struct remote_fspool_create_xml_ret { + remote_nonnull_fspool fspool; +}; +struct remote_fspool_define_xml_args { + remote_nonnull_string xml; + unsigned int flags; +}; +struct remote_fspool_define_xml_ret { + remote_nonnull_fspool fspool; +}; +struct remote_fspool_build_args { + remote_nonnull_fspool fspool; + unsigned int flags; +}; +struct remote_fspool_undefine_args { + remote_nonnull_fspool fspool; +}; +struct remote_fspool_create_args { + remote_nonnull_fspool fspool; + unsigned int flags; +}; + +struct remote_fspool_destroy_args { + remote_nonnull_fspool fspool; +}; +struct remote_fspool_delete_args { + remote_nonnull_fspool fspool; + unsigned int flags; +}; +struct remote_fspool_get_xml_desc_args { + remote_nonnull_fspool fspool; + unsigned int flags; +}; +struct remote_fs_pool_get_xml_desc_ret { + remote_nonnull_string xml; +}; +struct remote_fspool_get_info_args { + remote_nonnull_fspool fspool; +}; +struct remote_fspool_get_info_ret { + unsigned char state; + unsigned hyper capacity; + unsigned hyper allocation; + unsigned hyper available; +}; +struct remote_fspool_get_autostart_args { + remote_nonnull_fspool fspool; +}; +struct remote_fspool_get_autostart_ret { + int autostart; +}; +struct remote_fspool_set_autostart_args { + remote_nonnull_fspool fspool; + int autostart; +}; +struct remote_fspool_num_of_items_args { + remote_nonnull_fspool fspool; +}; +struct remote_fspool_num_of_items_ret { + int num; +}; +struct remote_fspool_list_items_args { + remote_nonnull_fspool fspool; + int maxnames; +}; + +struct remote_fspool_list_items_ret { + remote_nonnull_string names<REMOTE_FSITEM_LIST_MAX>; +}; +struct remote_fspool_refresh_args { + remote_nonnull_fspool fspool; + unsigned int flags; +}; +struct remote_fsitem_lookup_by_name_args { + remote_nonnull_fspool fspool; + remote_nonnull_string name; +}; +struct remote_fsitem_lookup_by_name_ret { + remote_nonnull_fsitem item; +}; +struct remote_fsitem_lookup_by_key_args { + remote_nonnull_string key; +}; +struct remote_fsitem_lookup_by_key_ret { + remote_nonnull_fsitem item; +}; +struct remote_fsitem_lookup_by_path_args { + remote_nonnull_string path; +}; +struct remote_fsitem_lookup_by_path_ret { + remote_nonnull_fsitem item; +}; +struct remote_fsitem_create_xml_args { + remote_nonnull_fspool fspool; + remote_nonnull_string xml; + unsigned int flags; +}; +struct remote_fsitem_create_xml_ret { + remote_nonnull_fsitem item; +}; +struct remote_fsitem_create_xml_from_args { + remote_nonnull_fspool fspool; + remote_nonnull_string xml; + remote_nonnull_fsitem cloneitem; + unsigned int flags; +}; +struct remote_fsitem_create_xml_from_ret { + remote_nonnull_fsitem item; +}; +struct remote_fsitem_delete_args { + remote_nonnull_fsitem item; + unsigned int flags; +}; +struct remote_fsitem_get_xml_desc_args { + remote_nonnull_fsitem item; + unsigned int flags; +}; +struct remote_fsitem_get_xml_desc_ret { + remote_nonnull_string xml; +}; +struct remote_fsitem_get_info_args { + remote_nonnull_fsitem item; +}; +struct remote_fsitem_get_info_ret { + char type; + unsigned hyper capacity; + unsigned hyper allocation; +}; +struct remote_fsitem_get_path_args { + remote_nonnull_fsitem item; +}; +struct remote_fsitem_get_path_ret { + remote_nonnull_string name; +}; struct remote_node_num_of_devices_args { remote_string cap; u_int flags; diff --git a/src/rpc/gendispatch.pl b/src/rpc/gendispatch.pl index 173189c..7fd0bbd 100755 --- a/src/rpc/gendispatch.pl +++ b/src/rpc/gendispatch.pl @@ -56,6 +56,8 @@ sub fixup_name { my $name = shift; $name =~ s/Nwfilter/NWFilter/; + $name =~ s/Fspool/FSPool/; + $name =~ s/Fsitem/FSItem/; $name =~ s/Xml$/XML/; $name =~ s/Uri$/URI/; $name =~ s/Uuid$/UUID/; @@ -500,7 +502,7 @@ elsif ($mode eq "server") { if ($args_member =~ m/^remote_nonnull_string name;/ and $has_node_device) { # ignore the name arg for node devices next - } elsif ($args_member =~ m/^remote_nonnull_(domain|network|storage_pool|storage_vol|interface|secret|nwfilter) (\S+);/) { + } elsif ($args_member =~ m/^remote_nonnull_(domain|network|storage_pool|storage_vol|interface|secret|nwfilter|fspool|fsitem) (\S+);/) { my $type_name = name_to_TypeName($1); push(@vars_list, "vir${type_name}Ptr $2 = NULL"); @@ -665,7 +667,7 @@ elsif ($mode eq "server") { if (!$modern_ret_as_list) { push(@ret_list, "ret->$3 = tmp.$3;"); } - } elsif ($ret_member =~ m/(?:admin|remote)_nonnull_(secret|nwfilter|node_device|interface|network|storage_vol|storage_pool|domain_snapshot|domain|server|client) (\S+)<(\S+)>;/) { + } elsif ($ret_member =~ m/(?:admin|remote)_nonnull_(secret|nwfilter|node_device|interface|network|storage_vol|storage_pool|domain_snapshot|domain|server|client|fspool|fsitem) (\S+)<(\S+)>;/) { $modern_ret_struct_name = $1; $single_ret_list_error_msg_type = $1; $single_ret_list_name = $2; @@ -723,7 +725,7 @@ elsif ($mode eq "server") { $single_ret_var = $1; $single_ret_by_ref = 0; $single_ret_check = " == NULL"; - } elsif ($ret_member =~ m/^remote_nonnull_(domain|network|storage_pool|storage_vol|interface|node_device|secret|nwfilter|domain_snapshot) (\S+);/) { + } elsif ($ret_member =~ m/^remote_nonnull_(domain|network|storage_pool|storage_vol|interface|node_device|secret|nwfilter|domain_snapshot|fspool|fsitem) (\S+);/) { my $type_name = name_to_TypeName($1); if ($call->{ProcName} eq "DomainCreateWithFlags") { @@ -1268,7 +1270,7 @@ elsif ($mode eq "client") { $priv_src = "dev->conn"; push(@args_list, "virNodeDevicePtr dev"); push(@setters_list, "args.name = dev->name;"); - } elsif ($args_member =~ m/^remote_nonnull_(domain|network|storage_pool|storage_vol|interface|secret|nwfilter|domain_snapshot) (\S+);/) { + } elsif ($args_member =~ m/^remote_nonnull_(domain|network|storage_pool|storage_vol|interface|secret|nwfilter|domain_snapshot|fspool|fsitem) (\S+);/) { my $name = $1; my $arg_name = $2; my $type_name = name_to_TypeName($name); @@ -1461,7 +1463,7 @@ elsif ($mode eq "client") { } push(@ret_list, "memcpy(result->$3, ret.$3, sizeof(result->$3));"); - } elsif ($ret_member =~ m/(?:admin|remote)_nonnull_(secret|nwfilter|node_device|interface|network|storage_vol|storage_pool|domain_snapshot|domain|server|client) (\S+)<(\S+)>;/) { + } elsif ($ret_member =~ m/(?:admin|remote)_nonnull_(secret|nwfilter|node_device|interface|network|storage_vol|storage_pool|domain_snapshot|domain|server|client|fspool|fsitem) (\S+)<(\S+)>;/) { my $proc_name = name_to_TypeName($1); if ($structprefix eq "admin") { @@ -1513,7 +1515,7 @@ elsif ($mode eq "client") { push(@ret_list, "VIR_FREE(ret.$1);"); $single_ret_var = "char *rv = NULL"; $single_ret_type = "char *"; - } elsif ($ret_member =~ m/^remote_nonnull_(domain|network|storage_pool|storage_vol|node_device|interface|secret|nwfilter|domain_snapshot) (\S+);/) { + } elsif ($ret_member =~ m/^remote_nonnull_(domain|network|storage_pool|storage_vol|node_device|interface|secret|nwfilter|domain_snapshot|fspool|fsitem) (\S+);/) { my $name = $1; my $arg_name = $2; my $type_name = name_to_TypeName($name); @@ -1968,7 +1970,8 @@ elsif ($mode eq "client") { "storage_conf.h", "nwfilter_conf.h", "node_device_conf.h", - "interface_conf.h" + "interface_conf.h", + "fs_conf.h" ); foreach my $hdr (@headers) { print "#include \"$hdr\"\n"; @@ -2053,6 +2056,8 @@ elsif ($mode eq "client") { $object =~ s/^(\w)/uc $1/e; $object =~ s/_(\w)/uc $1/e; $object =~ s/Nwfilter/NWFilter/; + $object =~ s/Fspool/FSPool/; + $object =~ s/Fsitem/FSItem/; my $objecttype = $prefix . $object . "DefPtr"; $apiname .= $action . "ACL"; @@ -2065,6 +2070,8 @@ elsif ($mode eq "client") { if ($object ne "Connect") { if ($object eq "StorageVol") { push @argdecls, "virStoragePoolDefPtr pool"; + } elsif ($object eq "FSItem") { + push @argdecls, "virFSPoolDefPtr fspool"; } push @argdecls, "$objecttype $arg"; } @@ -2094,6 +2101,8 @@ elsif ($mode eq "client") { if ($object ne "Connect") { if ($object eq "StorageVol") { push @argvars, "pool"; + } elsif ($object eq "FSItem") { + push @argvars, "fspool"; } push @argvars, $arg; } -- 1.8.3.1

Implementation is backend base as in case of storage pools. This patch adds directory backend which is nothing more than managing directories inside another one as starting point. Signed-off-by: Nikolay Shirokovskiy <nshirokovskiy@virtuozzo.com> Signed-off-by: Maxim Nestratov <mnestratov@virtuozzo.com> --- configure.ac | 38 + daemon/Makefile.am | 4 + m4/virt-driver-fspool.m4 | 52 ++ po/POTFILES.in | 2 + src/Makefile.am | 38 + src/driver.h | 1 + src/fs/fs_backend.h | 107 +++ src/fs/fs_backend_dir.c | 355 ++++++++ src/fs/fs_backend_dir.h | 8 + src/fs/fs_driver.c | 2058 ++++++++++++++++++++++++++++++++++++++++++++++ src/fs/fs_driver.h | 10 + src/libvirt.c | 28 + src/libvirt_private.syms | 1 + 13 files changed, 2702 insertions(+) create mode 100644 m4/virt-driver-fspool.m4 create mode 100644 src/fs/fs_backend.h create mode 100644 src/fs/fs_backend_dir.c create mode 100644 src/fs/fs_backend_dir.h create mode 100644 src/fs/fs_driver.c create mode 100644 src/fs/fs_driver.h diff --git a/configure.ac b/configure.ac index f6076bd..5da4bf3 100644 --- a/configure.ac +++ b/configure.ac @@ -1074,6 +1074,11 @@ dnl LIBVIRT_DRIVER_CHECK_BHYVE +dnl +dnl Checks for FS Driver +dnl + +LIBVIRT_DRIVER_CHECK_FSPOOL dnl dnl check for kernel headers required by src/bridge.c @@ -1647,6 +1652,35 @@ fi AM_CONDITIONAL([WITH_SECRETS], [test "$with_secrets" = "yes"]) +AC_ARG_WITH([fs-dir], + [AS_HELP_STRING([--with-fs-dir], + [with fs backend for fs driver @<:@default=yes@:>])], + [],[with_fs_dir=yes]) + +if test "$with_libvirtd" = "no"; then + with_fs_dir=no +fi + +if test "$with_fs_dir" = "yes" ; then + AC_DEFINE_UNQUOTED([WITH_FS_DIR], 1, [whether directory backend for fs driver is enabled]) +fi +AM_CONDITIONAL([WITH_FS_DIR], [test "$with_fs_dir" = "yes"]) + +with_fs=no +for backend in dir; do + if eval test \$with_fs_$backend = yes; then + with_fs=yes + break + fi +done +if test $with_fs = yes; then + AC_DEFINE([WITH_FS], [1], + [Define to 1 if at least one fs backend is in use]) +fi +AM_CONDITIONAL([WITH_FS], [test "$with_fs" = "yes"]) + + + AC_ARG_WITH([storage-dir], [AS_HELP_STRING([--with-storage-dir], [with directory backend for the storage driver @<:@default=yes@:>@])], @@ -2760,6 +2794,10 @@ AC_MSG_NOTICE([Sheepdog: $with_storage_sheepdog]) AC_MSG_NOTICE([ Gluster: $with_storage_gluster]) AC_MSG_NOTICE([ ZFS: $with_storage_zfs]) AC_MSG_NOTICE([]) +AC_MSG_NOTICE([Fs Drivers]) +AC_MSG_NOTICE([]) +LIBVIRT_DRIVER_RESULT_FS +AC_MSG_NOTICE([]) AC_MSG_NOTICE([Security Drivers]) AC_MSG_NOTICE([]) AC_MSG_NOTICE([ SELinux: $with_secdriver_selinux ($SELINUX_MOUNT)]) diff --git a/daemon/Makefile.am b/daemon/Makefile.am index 927d16f..63444cf 100644 --- a/daemon/Makefile.am +++ b/daemon/Makefile.am @@ -241,6 +241,10 @@ if WITH_STORAGE libvirtd_LDADD += ../src/libvirt_driver_storage.la endif WITH_STORAGE +if WITH_FS + libvirtd_LDADD += ../src/libvirt_driver_fs.la +endif WITH_FS + if WITH_NETWORK libvirtd_LDADD += ../src/libvirt_driver_network.la endif WITH_NETWORK diff --git a/m4/virt-driver-fspool.m4 b/m4/virt-driver-fspool.m4 new file mode 100644 index 0000000..0f1a569 --- /dev/null +++ b/m4/virt-driver-fspool.m4 @@ -0,0 +1,52 @@ +dnl The File Systems Driver +dnl +dnl Copyright (C) 2016 Parallels IP Holdings GmbH +dnl +dnl This library is free software; you can redistribute it and/or +dnl modify it under the terms of the GNU Lesser General Public +dnl License as published by the Free Software Foundation; either +dnl version 2.1 of the License, or (at your option) any later version. +dnl +dnl This library is distributed in the hope that it will be useful, +dnl but WITHOUT ANY WARRANTY; without even the implied warranty of +dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +dnl Lesser General Public License for more details. +dnl +dnl You should have received a copy of the GNU Lesser General Public +dnl License along with this library. If not, see +dnl <http://www.gnu.org/licenses/>. +dnl + +AC_DEFUN([LIBVIRT_DRIVER_CHECK_FSPOOL],[ + AC_ARG_WITH([fs-dir], + [AS_HELP_STRING([--with-fs-dir], + [with direcktory backend for FS driver @<:@default=yes@:>@])], + [],[with_fs_dir=yes]) + m4_divert_text([DEFAULTS], [with_fs=check]) + + if test "$with_libvirtd" = "no"; then + with_fs_dir=no + fi + + if test "$with_fs_dir" = "yes" ; then + AC_DEFINE_UNQUOTED([WITH_FS_DIR], 1, [whether directory backend for fs driver is enabled]) + fi + AM_CONDITIONAL([WITH_FS_DIR], [test "$with_fs_dir" = "yes"]) + + with_fs=no + for backend in dir; do + if eval test \$with_fs_$backend = yes; then + with_fs=yes + break + fi + done + if test $with_fs = yes; then + AC_DEFINE([WITH_FS], [1], + [Define to 1 if at least one fs backend is in use]) + fi + AM_CONDITIONAL([WITH_FS], [test "$with_fs" = "yes"]) +]) + +AC_DEFUN([LIBVIRT_DRIVER_RESULT_FS],[ + AC_MSG_NOTICE([ FS Driver: $with_fs_dir]) +]) diff --git a/po/POTFILES.in b/po/POTFILES.in index f4d2f25..0fc3b79 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -59,6 +59,8 @@ src/esx/esx_vi.c src/esx/esx_vi_methods.c src/esx/esx_vi_types.c src/fdstream.c +src/fs/fs_backend_dir.c +src/fs/fs_driver.c src/hyperv/hyperv_driver.c src/hyperv/hyperv_util.c src/hyperv/hyperv_wmi.c diff --git a/src/Makefile.am b/src/Makefile.am index d3c6e67..0388c7d 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -644,6 +644,7 @@ DRIVER_SOURCE_FILES = \ $(REMOTE_DRIVER_SOURCES) \ $(SECRET_DRIVER_SOURCES) \ $(STORAGE_DRIVER_SOURCES) \ + $(FS_DRIVER_SOURCES) \ $(TEST_DRIVER_SOURCES) \ $(UML_DRIVER_SOURCES) \ $(VBOX_DRIVER_SOURCES) \ @@ -664,6 +665,7 @@ STATEFUL_DRIVER_SOURCE_FILES = \ $(QEMU_DRIVER_SOURCES) \ $(SECRET_DRIVER_SOURCES) \ $(STORAGE_DRIVER_SOURCES) \ + $(FS_DRIVER_SOURCES) \ $(UML_DRIVER_SOURCES) \ $(XEN_DRIVER_SOURCES) \ $(VZ_DRIVER_SOURCES) \ @@ -971,6 +973,14 @@ SECRET_UTIL_SOURCES = \ SECRET_DRIVER_SOURCES = \ secret/secret_driver.h secret/secret_driver.c +# FS pool backend specific impls +FS_DRIVER_SOURCES = \ + fs/fs_driver.h fs/fs_driver.c \ + fs/fs_backend.h + +FS_DRIVER_DIR_SOURCES = \ + fs/fs_backend_dir.h fs/fs_backend_dir.c + # Storage backend specific impls STORAGE_DRIVER_SOURCES = \ storage/storage_driver.h storage/storage_driver.c \ @@ -1637,6 +1647,32 @@ endif WITH_DRIVER_MODULES libvirt_driver_secret_la_SOURCES = $(SECRET_DRIVER_SOURCES) endif WITH_SECRETS +libvirt_driver_fs_impl_la_SOURCES = +libvirt_driver_fs_impl_la_CFLAGS = \ + -I$(srcdir)/access \ + -I$(srcdir)/conf \ + $(AM_CFLAGS) +libvirt_driver_fs_impl_la_LDFLAGS = $(AM_LDFLAGS) +libvirt_driver_fs_impl_la_LIBADD = +libvirt_driver_fs_impl_la_LIBADD += $(SECDRIVER_LIBS) $(LIBXML_LIBS) +if WITH_FS +noinst_LTLIBRARIES += libvirt_driver_fs_impl.la +libvirt_driver_fs_la_SOURCES = +libvirt_driver_fs_la_LIBADD = libvirt_driver_fs_impl.la +if WITH_DRIVER_MODULES +mod_LTLIBRARIES += libvirt_driver_fs.la +libvirt_driver_fs_la_LIBADD += ../gnulib/lib/libgnu.la +libvirt_driver_fs_la_LDFLAGS = -module -avoid-version $(AM_LDFLAGS) +else ! WITH_DRIVER_MODULES +noinst_LTLIBRARIES += libvirt_driver_fs.la +# Stateful, so linked to daemon instead +#libvirt_la_BUILT_LIBADD += libvirt_driver_fs.la +endif ! WITH_DRIVER_MODULES +libvirt_driver_fs_impl_la_SOURCES += $(FS_DRIVER_SOURCES) +libvirt_driver_fs_impl_la_SOURCES += $(FS_DRIVER_DIR_SOURCES) +endif WITH_FS + + # Needed to keep automake quiet about conditionals libvirt_driver_storage_impl_la_SOURCES = libvirt_driver_storage_impl_la_CFLAGS = \ @@ -1908,6 +1944,8 @@ EXTRA_DIST += \ $(BHYVE_DRIVER_SOURCES) \ $(NETWORK_DRIVER_SOURCES) \ $(INTERFACE_DRIVER_SOURCES) \ + $(FS_DRIVER_SOURCES) \ + $(FS_DRIVER_DIR_SOURCES) \ $(STORAGE_DRIVER_SOURCES) \ $(STORAGE_DRIVER_FS_SOURCES) \ $(STORAGE_DRIVER_LVM_SOURCES) \ diff --git a/src/driver.h b/src/driver.h index 64f7460..07cb3fb 100644 --- a/src/driver.h +++ b/src/driver.h @@ -100,6 +100,7 @@ int virSetSharedNodeDeviceDriver(virNodeDeviceDriverPtr driver) ATTRIBUTE_RETURN int virSetSharedNWFilterDriver(virNWFilterDriverPtr driver) ATTRIBUTE_RETURN_CHECK; int virSetSharedSecretDriver(virSecretDriverPtr driver) ATTRIBUTE_RETURN_CHECK; int virSetSharedStorageDriver(virStorageDriverPtr driver) ATTRIBUTE_RETURN_CHECK; +int virSetSharedFSDriver(virFSDriverPtr driver) ATTRIBUTE_RETURN_CHECK; void *virDriverLoadModule(const char *name); diff --git a/src/fs/fs_backend.h b/src/fs/fs_backend.h new file mode 100644 index 0000000..f7e0bec --- /dev/null +++ b/src/fs/fs_backend.h @@ -0,0 +1,107 @@ +/* + * fs_backend.h: file system backend implementation + * Author: Olga Krishtal <okrishtal@virtuozzo.com> + * + * Copyright (C) 2016 Parallels IP Holdings GmbH + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + */ + + +#ifndef __VIR_FS_BACKEND_H__ +# define __VIR_FS_BACKEND_H__ + +# include <sys/stat.h> + +# include "internal.h" +# include "fs_conf.h" +# include "fs_driver.h" + +typedef char * (*virFSBackendFindFSpoolSources)(virConnectPtr conn, + const char *srcSpec, + unsigned int flags); +typedef int (*virFSBackendCheckFSpool)(virFSPoolObjPtr fspool, + bool *active); +typedef int (*virFSBackendStartFSpool)(virConnectPtr conn, + virFSPoolObjPtr fspool); +typedef int (*virFSBackendBuildFSpool)(virConnectPtr conn, + virFSPoolObjPtr fspool, + unsigned int flags); +typedef int (*virFSBackendRefreshFSpool)(virConnectPtr conn, + virFSPoolObjPtr fspool); +typedef int (*virFSBackendStopFSpool)(virConnectPtr conn, + virFSPoolObjPtr fspool); +typedef int (*virFSBackendDeleteFSpool)(virConnectPtr conn, + virFSPoolObjPtr fspool, + unsigned int flags); + +/* FIXME */ + +/* A 'buildItem' backend must remove any volume created on error since + * the storage driver does not distinguish whether the failure is due + * to failure to create the volume, to reserve any space necessary for + * the volume, to get data about the volume, to change it's accessibility, + * etc. This avoids issues arising from a creation failure due to some + * external action which created a volume of the same name that libvirt + * was not aware of between checking the fspool and the create attempt. It + * also avoids extra round trips to just delete a file. + */ +typedef int (*virFSBackendBuildItem)(virConnectPtr conn, + virFSPoolObjPtr fspool, + virFSItemDefPtr item, + unsigned int flags); +typedef int (*virFSBackendCreateItem)(virConnectPtr conn, + virFSPoolObjPtr fspool, + virFSItemDefPtr item); +typedef int (*virFSBackendRefreshItem)(virConnectPtr conn, + virFSPoolObjPtr fspool, + virFSItemDefPtr item); +typedef int (*virFSBackendDeleteItem)(virConnectPtr conn, + virFSPoolObjPtr fspool, + virFSItemDefPtr item, + unsigned int flags); +typedef int (*virFSBackendBuildItemFrom)(virConnectPtr conn, + virFSPoolObjPtr fspool, + virFSItemDefPtr origitem, + virFSItemDefPtr newitem, + unsigned int flags); + +typedef struct _virFSBackend virFSBackend; +typedef virFSBackend *virFSBackendPtr; + +/* Callbacks are optional unless documented otherwise; but adding more + * callbacks provides better fspool support. */ +struct _virFSBackend { + int type; + + virFSBackendFindFSpoolSources findFSpoolSources; + virFSBackendCheckFSpool checkFSpool; + virFSBackendStartFSpool startFSpool; + virFSBackendBuildFSpool buildFSpool; + virFSBackendRefreshFSpool refreshFSpool; /* Must be non-NULL */ + virFSBackendStopFSpool stopFSpool; + virFSBackendDeleteFSpool deleteFSpool; + + virFSBackendBuildItem buildItem; + virFSBackendBuildItemFrom buildItemFrom; + virFSBackendCreateItem createItem; + virFSBackendRefreshItem refreshItem; + virFSBackendDeleteItem deleteItem; +}; + +# define VIR_FS_DEFAULT_POOL_PERM_MODE 0755 +# define VIR_FS_DEFAULT_ITEM_PERM_MODE 0600 + +#endif /* __VIR_FS_BACKEND_H__ */ diff --git a/src/fs/fs_backend_dir.c b/src/fs/fs_backend_dir.c new file mode 100644 index 0000000..60f18c6 --- /dev/null +++ b/src/fs/fs_backend_dir.c @@ -0,0 +1,355 @@ +/* + * fs_backend_dir.c: file system backend implementation + * Author: Olga Krishtal <okrishtal@virtuozzo.com> + * + * Copyright (C) 2016 Parallels IP Holdings GmbH + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + */ + +#include <config.h> + +#include <sys/statvfs.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <stdio.h> +#include <dirent.h> +#include <errno.h> +#include <fcntl.h> +#include <unistd.h> +#include <string.h> + +#include <libxml/parser.h> +#include <libxml/tree.h> +#include <libxml/xpath.h> + +#include "virerror.h" +#include "fs_backend_dir.h" +#include "fs_conf.h" +#include "vircommand.h" +#include "viralloc.h" +#include "virxml.h" +#include "virfile.h" +#include "virlog.h" +#include "virstring.h" +#include "fdstream.h" +#include "stat-time.h" + +#define VIR_FROM_THIS VIR_FROM_FSPOOL + +VIR_LOG_INIT("fs.fs_backend_dir"); + +static int +virFSDirBuild(virConnectPtr conn ATTRIBUTE_UNUSED, + virFSPoolObjPtr fspool, + unsigned int flags) +{ + int ret = -1; + char *parent = NULL; + char *p = NULL; + mode_t mode; + unsigned int dir_create_flags; + + virCheckFlags(0, -1); + + if (VIR_STRDUP(parent, fspool->def->target.path) < 0) + goto error; + if (!(p = strrchr(parent, '/'))) { + virReportError(VIR_ERR_INVALID_ARG, + _("path '%s' is not absolute"), + fspool->def->target.path); + goto error; + } + + if (p != parent) { + /* assure all directories in the path prior to the final dir + * exist, with default uid/gid/mode. */ + *p = '\0'; + if (virFileMakePath(parent) < 0) { + virReportSystemError(errno, _("cannot create path '%s'"), + parent); + goto error; + } + } + + dir_create_flags = VIR_DIR_CREATE_ALLOW_EXIST; + mode = fspool->def->target.perms.mode; + + if (mode == (mode_t) -1 && + (!virFileExists(fspool->def->target.path))) + mode = VIR_FS_DEFAULT_POOL_PERM_MODE; + + /* Now create the final dir in the path with the uid/gid/mode + * requested in the config. If the dir already exists, just set + * the perms. */ + if (virDirCreate(fspool->def->target.path, + mode, + fspool->def->target.perms.uid, + fspool->def->target.perms.gid, + dir_create_flags) < 0) + goto error; + + ret = 0; + + error: + VIR_FREE(parent); + return ret; +} + +static int +virFSDirRefresh(virConnectPtr conn ATTRIBUTE_UNUSED, + virFSPoolObjPtr fspool) +{ + DIR *dir; + struct dirent *entry; + virFSItemDefPtr item = NULL; + struct statvfs sb; + struct stat statbuf; + int fd = 0; + int ret = -1; + + if (virDirOpen(&dir, fspool->def->target.path) < 0) + goto cleanup; + + while (virDirRead(dir, &entry, fspool->def->target.path) > 0) { + if (virStringHasControlChars(entry->d_name)) { + VIR_WARN("Ignoring control characters under '%s'", + fspool->def->target.path); + continue; + } + + if (VIR_ALLOC(item) < 0) + goto cleanup; + + if (VIR_STRDUP(item->name, entry->d_name) < 0) + goto cleanup; + item->type = VIR_FSITEM_DIR; + if (virAsprintf(&item->target.path, "%s/%s", + fspool->def->target.path, + item->name) == -1) + goto cleanup; + + if (VIR_STRDUP(item->key, item->target.path) < 0) + goto cleanup; + + + if (VIR_APPEND_ELEMENT(fspool->items.objs, fspool->items.count, item) < 0) + goto cleanup; + } + + + if ((fd = open(fspool->def->target.path, O_RDONLY)) < 0) { + virReportSystemError(errno, + _("cannot open path '%s'"), + fspool->def->target.path); + goto cleanup; + } + + if (fstat(fd, &statbuf) < 0) { + virReportSystemError(errno, + _("cannot stat path '%s'"), + fspool->def->target.path); + goto cleanup; + } + + fspool->def->target.perms.mode = statbuf.st_mode & S_IRWXUGO; + fspool->def->target.perms.uid = statbuf.st_uid; + fspool->def->target.perms.gid = statbuf.st_gid; + + if (statvfs(fspool->def->target.path, &sb) < 0) { + virReportSystemError(errno, + _("cannot statvfs path '%s'"), + fspool->def->target.path); + goto cleanup; + } + + fspool->def->capacity = ((unsigned long long)sb.f_blocks * + (unsigned long long)sb.f_frsize); + fspool->def->available = ((unsigned long long)sb.f_bfree * + (unsigned long long)sb.f_frsize); + fspool->def->allocation = fspool->def->capacity - fspool->def->available; + + ret = 0; + + cleanup: + VIR_DIR_CLOSE(dir); + VIR_FORCE_CLOSE(fd); + virFSItemDefFree(item); + if (ret < 0) + virFSPoolObjClearItems(fspool); + return ret; +} + +static int +virFSDirDelete(virConnectPtr conn ATTRIBUTE_UNUSED, + virFSPoolObjPtr fspool, + unsigned int flags) +{ + virCheckFlags(0, -1); + + if (rmdir(fspool->def->target.path) < 0) { + virReportSystemError(errno, _("failed to remove fspool '%s'"), + fspool->def->target.path); + return -1; + } + + return 0; + +} +static int +virFSDirItemBuild(virConnectPtr conn ATTRIBUTE_UNUSED, + virFSPoolObjPtr fspool ATTRIBUTE_UNUSED, + virFSItemDefPtr item, + unsigned int flags) +{ + virCheckFlags(0, -1); + + if (item->type == VIR_FSITEM_DIR) { + if ((virDirCreate(item->target.path, + (item->target.perms->mode == (mode_t) -1 ? + VIR_FS_DEFAULT_ITEM_PERM_MODE: + item->target.perms->mode), + item->target.perms->uid, + item->target.perms->gid, + 0)) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("error creating item")); + return -1; + } + } + + return 0; +} + +static int +virFSDirItemBuildFrom(virConnectPtr conn ATTRIBUTE_UNUSED, + virFSPoolObjPtr fspool ATTRIBUTE_UNUSED, + virFSItemDefPtr item, + virFSItemDefPtr inputitem, + unsigned int flags) +{ + virCommandPtr cmd = NULL; + int ret = -1; + + virCheckFlags(0, -1); + + item->target.capacity = inputitem->target.capacity; + cmd = virCommandNewArgList("cp", "-r", inputitem->target.path, + item->target.path, NULL); + ret = virCommandRun(cmd, NULL); + + virCommandFree(cmd); + return ret; +} + +static int +virFSDirItemCreate(virConnectPtr conn ATTRIBUTE_UNUSED, + virFSPoolObjPtr fspool, + virFSItemDefPtr item) +{ + if (strchr(item->name, '/')) { + virReportError(VIR_ERR_OPERATION_INVALID, + _("volume name '%s' cannot contain '/'"), item->name); + return -1; + } + + VIR_FREE(item->target.path); + if (virAsprintf(&item->target.path, "%s/%s", + fspool->def->target.path, + item->name) == -1) + return -1; + + if (virFileExists(item->target.path)) { + virReportError(VIR_ERR_OPERATION_INVALID, + _("item target path '%s' already exists"), + item->target.path); + return -1; + } + + VIR_FREE(item->key); + return VIR_STRDUP(item->key, item->target.path); +} + + +static int +virFSDirItemRefresh(virConnectPtr conn ATTRIBUTE_UNUSED, + virFSPoolObjPtr fspool ATTRIBUTE_UNUSED, + virFSItemDefPtr item) +{ + int fd; + int ret = -1; + struct stat statbuf; + virCommandPtr cmd = NULL; + char *output = NULL, *end; + + if ((fd = open(item->target.path, O_RDONLY)) < 0) { + virReportSystemError(errno, _("cannot open directory '%s'"), + item->target.path); + return -1; + } + if (fstat(fd, &statbuf) < 0) { + virReportSystemError(errno, _("cannot stat path '%s'"), + item->target.path); + goto cleanup; + } + + cmd = virCommandNewArgList("du", "-sB1", item->target.path, NULL); + virCommandSetOutputBuffer(cmd, &output); + if ((ret = virCommandRun(cmd, NULL)) < 0) + goto cleanup; + + if (virStrToLong_ull(output, &end, 10, &item->target.allocation) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Malformed du output: %s"), output); + goto cleanup; + } + + if (&(item->target.perms) && VIR_ALLOC(*(&item->target.perms)) < 0) + goto cleanup; + item->target.perms->mode = statbuf.st_mode & S_IRWXUGO; + item->target.perms->uid = statbuf.st_uid; + item->target.perms->gid = statbuf.st_gid; + + ret = 0; + cleanup: + VIR_FORCE_CLOSE(fd); + VIR_FREE(output); + virCommandFree(cmd); + return ret; +} + +static int +virFSDirItemDelete(virConnectPtr conn ATTRIBUTE_UNUSED, + virFSPoolObjPtr fspool ATTRIBUTE_UNUSED, + virFSItemDefPtr item, + unsigned int flags) +{ + virCheckFlags(0, -1); + + return virFileDeleteTree(item->target.path); +} + +virFSBackend virFSBackendDir = { + .type = VIR_FSPOOL_DIR, + + .buildFSpool = virFSDirBuild, + .refreshFSpool = virFSDirRefresh, + .deleteFSpool = virFSDirDelete, + .buildItem = virFSDirItemBuild, + .buildItemFrom = virFSDirItemBuildFrom, + .createItem = virFSDirItemCreate, + .deleteItem = virFSDirItemDelete, + .refreshItem = virFSDirItemRefresh, +}; diff --git a/src/fs/fs_backend_dir.h b/src/fs/fs_backend_dir.h new file mode 100644 index 0000000..335e008 --- /dev/null +++ b/src/fs/fs_backend_dir.h @@ -0,0 +1,8 @@ +#ifndef __VIR_FS_BACKEND_DIR_H__ +# define __VIR_FS_BACKEND_DIR_H__ + +# include "fs_backend.h" + +extern virFSBackend virFSBackendDir; + +#endif /* __VIR_FS_BACKEND_DIR_H__ */ diff --git a/src/fs/fs_driver.c b/src/fs/fs_driver.c new file mode 100644 index 0000000..21fa590 --- /dev/null +++ b/src/fs/fs_driver.c @@ -0,0 +1,2058 @@ +/* + * fs_driver.c: file system driver implementation + * Author: Olga Krishtal <okrishtal@virtuozzo.com> + * + * Copyright (C) 2016 Parallels IP Holdings GmbH + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + */ + +#include <config.h> + +#include <stdio.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/param.h> +#include <fcntl.h> + +#include <errno.h> +#include <string.h> + +#include "virerror.h" +#include "datatypes.h" +#include "driver.h" +#include "fs_driver.h" +#include "fs_conf.h" +#include "fs_backend.h" +#include "viralloc.h" +#include "virlog.h" +#include "virfile.h" +#include "fdstream.h" +#include "configmake.h" +#include "virstring.h" +#include "viraccessapicheck.h" +#include "dirname.h" + +#if WITH_FS_DIR +# include "fs_backend_dir.h" +#endif + +#define VIR_FROM_THIS VIR_FROM_FSPOOL + +VIR_LOG_INIT("fs.fs_driver"); + +static virFSDriverStatePtr driver; + +static int fsStateCleanup(void); + +static void fsDriverLock(void) +{ + virMutexLock(&driver->lock); +} + +static void fsDriverUnlock(void) +{ + virMutexUnlock(&driver->lock); +} + +static virFSBackendPtr backends[] = { +#if WITH_FS_DIR + &virFSBackendDir, +#endif +}; + +static virFSBackendPtr +virFSBackendForType(int type) +{ + size_t i; + for (i = 0; backends[i]; i++) + if (backends[i]->type == type) + return backends[i]; + + virReportError(VIR_ERR_INTERNAL_ERROR, + _("missing backend for fspool type %d (%s)"), + type, NULLSTR(virFSPoolTypeToString(type))); + return NULL; +} + +static void +fsItemRemoveFromFSPool(virFSPoolObjPtr fspool, + virFSItemDefPtr item) +{ + size_t i; + + for (i = 0; i < fspool->items.count; i++) { + if (fspool->items.objs[i] == item) { + VIR_INFO("Deleting item '%s' from fspool '%s'", + item->name, fspool->def->name); + virFSItemDefFree(item); + + VIR_DELETE_ELEMENT(fspool->items.objs, i, fspool->items.count); + break; + } + } +} + +static int +fsItemDeleteInternal(virFSItemPtr obj, + virFSBackendPtr backend, + virFSPoolObjPtr fspool, + virFSItemDefPtr item, + unsigned int flags) +{ + int ret = -1; + + virCheckFlags(0, -1); + + if (!backend->deleteItem) { + virReportError(VIR_ERR_NO_SUPPORT, + "%s", _("fspool does not support item deletion")); + goto cleanup; + } + if (backend->deleteItem(obj->conn, fspool, item, flags) < 0) + goto cleanup; + + fsItemRemoveFromFSPool(fspool, item); + + ret = 0; + + cleanup: + return ret; +} + +static virFSItemDefPtr +virFSItemDefFromItem(virFSItemPtr obj, + virFSPoolObjPtr *fspool, + virFSBackendPtr *backend) +{ + virFSItemDefPtr item = NULL; + + *fspool = NULL; + + fsDriverLock(); + *fspool = virFSPoolObjFindByName(&driver->fspools, obj->fspool); + fsDriverUnlock(); + + if (!*fspool) { + virReportError(VIR_ERR_NO_FSPOOL, + _("no fspool with matching name '%s'"), + obj->fspool); + return NULL; + } + + if (!virFSPoolObjIsActive(*fspool)) { + virReportError(VIR_ERR_OPERATION_INVALID, + _("fspool '%s' is not active"), + (*fspool)->def->name); + goto error; + } + + if (!(item = virFSItemDefFindByName(*fspool, obj->name))) { + virReportError(VIR_ERR_NO_FSITEM, + _("no fsitem with matching name '%s'"), + obj->name); + goto error; + } + + if (backend) { + if (!(*backend = virFSBackendForType((*fspool)->def->type))) + goto error; + } + + return item; + + error: + virFSPoolObjUnlock(*fspool); + *fspool = NULL; + + return NULL; +} + +static void +fsPoolUpdateState(virFSPoolObjPtr fspool) +{ + bool active; + virFSBackendPtr backend; + int ret = -1; + char *stateFile; + + if (!(stateFile = virFileBuildPath(driver->stateDir, + fspool->def->name, ".xml"))) + goto error; + + if ((backend = virFSBackendForType(fspool->def->type)) == NULL) { + VIR_ERROR(_("Missing backend %d"), fspool->def->type); + goto error; + } + + /* Backends which do not support 'checkFSpool' are considered + * inactive by default. + */ + active = false; + if (backend->checkFSpool && + backend->checkFSpool(fspool, &active) < 0) { + virErrorPtr err = virGetLastError(); + VIR_ERROR(_("Failed to initialize fspool '%s': %s"), + fspool->def->name, err ? err->message : + _("no error message found")); + goto error; + } + + /* We can pass NULL as connection, most backends do not use + * it anyway, but if they do and fail, we want to log error and + * continue with other fspools. + */ + if (active) { + virFSPoolObjClearItems(fspool); + if (backend->refreshFSpool(NULL, fspool) < 0) { + virErrorPtr err = virGetLastError(); + if (backend->stopFSpool) + backend->stopFSpool(NULL, fspool); + VIR_ERROR(_("Failed to restart fspool '%s': %s"), + fspool->def->name, err ? err->message : + _("no error message found")); + goto error; + } + } + + fspool->active = active; + ret = 0; + error: + if (ret < 0) { + if (stateFile) + unlink(stateFile); + } + VIR_FREE(stateFile); + + return; +} + +static void +fsPoolUpdateAllState(void) +{ + size_t i; + + for (i = 0; i < driver->fspools.count; i++) { + virFSPoolObjPtr fspool = driver->fspools.objs[i]; + + virFSPoolObjLock(fspool); + fsPoolUpdateState(fspool); + virFSPoolObjUnlock(fspool); + } +} + +static void +fsDriverAutostart(void) +{ + size_t i; + virConnectPtr conn = NULL; + + /* XXX Remove hardcoding of QEMU URI */ + if (driver->privileged) + conn = virConnectOpen("qemu:///system"); + else + conn = virConnectOpen("qemu:///session"); + /* Ignoring NULL conn - let backends decide */ + + for (i = 0; i < driver->fspools.count; i++) { + virFSPoolObjPtr fspool = driver->fspools.objs[i]; + virFSBackendPtr backend; + bool started = false; + + virFSPoolObjLock(fspool); + if ((backend = virFSBackendForType(fspool->def->type)) == NULL) { + virFSPoolObjUnlock(fspool); + continue; + } + + if (fspool->autostart && + !virFSPoolObjIsActive(fspool)) { + if (backend->startFSpool && + backend->startFSpool(conn, fspool) < 0) { + virErrorPtr err = virGetLastError(); + VIR_ERROR(_("Failed to autostart fspool '%s': %s"), + fspool->def->name, err ? err->message : + _("no error message found")); + virFSPoolObjUnlock(fspool); + continue; + } + started = true; + } + + if (started) { + char *stateFile; + + virFSPoolObjClearItems(fspool); + stateFile = virFileBuildPath(driver->stateDir, + fspool->def->name, ".xml"); + if (!stateFile || + virFSPoolSaveState(stateFile, fspool->def) < 0 || + backend->refreshFSpool(conn, fspool) < 0) { + virErrorPtr err = virGetLastError(); + if (stateFile) + unlink(stateFile); + if (backend->stopFSpool) + backend->stopFSpool(conn, fspool); + VIR_ERROR(_("Failed to autostart fspool '%s': %s"), + fspool->def->name, err ? err->message : + _("no error message found")); + } else { + fspool->active = true; + } + VIR_FREE(stateFile); + } + virFSPoolObjUnlock(fspool); + } + + virObjectUnref(conn); +} + +/** + * virFSStartup: + * + * Initialization function for the FS Driver + */ +static int +fsStateInitialize(bool privileged, + virStateInhibitCallback callback ATTRIBUTE_UNUSED, + void *opaque ATTRIBUTE_UNUSED) +{ + int ret = -1; + char *configdir = NULL; + char *rundir = NULL; + + if (VIR_ALLOC(driver) < 0) + return ret; + + if (virMutexInit(&driver->lock) < 0) { + VIR_FREE(driver); + return ret; + } + fsDriverLock(); + + if (privileged) { + if (VIR_STRDUP(driver->configDir, + SYSCONFDIR "/libvirt/fs") < 0 || + VIR_STRDUP(driver->autostartDir, + SYSCONFDIR "/libvirt/fs/autostart") < 0 || + VIR_STRDUP(driver->stateDir, + LOCALSTATEDIR "/run/libvirt/fs") < 0) + goto error; + } else { + configdir = virGetUserConfigDirectory(); + rundir = virGetUserRuntimeDirectory(); + if (!(configdir && rundir)) + goto error; + + if ((virAsprintf(&driver->configDir, + "%s/fs", configdir) < 0) || + (virAsprintf(&driver->autostartDir, + "%s/fs/autostart", configdir) < 0) || + (virAsprintf(&driver->stateDir, + "%s/fs/run", rundir) < 0)) + goto error; + } + driver->privileged = privileged; + + if (virFileMakePath(driver->stateDir) < 0) { + virReportError(errno, + _("cannot create directory %s"), + driver->stateDir); + goto error; + } + + if (virFSPoolLoadAllState(&driver->fspools, + driver->stateDir) < 0) + goto error; + + if (virFSPoolLoadAllConfigs(&driver->fspools, + driver->configDir, + driver->autostartDir) < 0) + goto error; + + fsPoolUpdateAllState(); + + fsDriverUnlock(); + + ret = 0; + cleanup: + VIR_FREE(configdir); + VIR_FREE(rundir); + return ret; + + error: + fsDriverUnlock(); + fsStateCleanup(); + goto cleanup; +} + +/** + * fsStateAutoStart: + * + * Function to auto start the fs_driver + */ +static void +fsStateAutoStart(void) +{ + if (!driver) + return; + + fsDriverLock(); + fsDriverAutostart(); + fsDriverUnlock(); +} + +/** + * fsStateReload: + * + * Function to restart the fs_driver, it will recheck the configuration + * files and update its state + */ +static int +fsStateReload(void) +{ + if (!driver) + return -1; + + fsDriverLock(); + virFSPoolLoadAllState(&driver->fspools, + driver->stateDir); + virFSPoolLoadAllConfigs(&driver->fspools, + driver->configDir, + driver->autostartDir); + fsDriverAutostart(); + fsDriverUnlock(); + + return 0; +} + + +/** + * fsStateCleanup + * + * Shutdown the fs driver, it will stop all active fspools + */ +static int +fsStateCleanup(void) +{ + if (!driver) + return -1; + + fsDriverLock(); + + /* free inactive fspools */ + virFSPoolObjListFree(&driver->fspools); + + VIR_FREE(driver->configDir); + VIR_FREE(driver->autostartDir); + VIR_FREE(driver->stateDir); + fsDriverUnlock(); + virMutexDestroy(&driver->lock); + VIR_FREE(driver); + + return 0; +} + + +static virFSPoolObjPtr +virFSPoolObjFromFSPool(virFSPoolPtr fspool) +{ + char uuidstr[VIR_UUID_STRING_BUFLEN]; + virFSPoolObjPtr ret; + + fsDriverLock(); + if (!(ret = virFSPoolObjFindByUUID(&driver->fspools, fspool->uuid))) { + virUUIDFormat(fspool->uuid, uuidstr); + virReportError(VIR_ERR_NO_FSPOOL, + _("no fspool with matching uuid '%s' (%s)"), + uuidstr, fspool->name); + } + fsDriverUnlock(); + + return ret; +} + +static int +fsConnectListAllFSPools(virConnectPtr conn, + virFSPoolPtr **fspools, + unsigned int flags) +{ + int ret = -1; + + virCheckFlags(VIR_CONNECT_LIST_FSPOOLS_FILTERS_ALL, -1); + + if (virConnectListAllFSPoolsEnsureACL(conn) < 0) + goto cleanup; + + fsDriverLock(); + ret = virFSPoolObjListExport(conn, driver->fspools, fspools, + virConnectListAllFSPoolsCheckACL, + flags); + fsDriverUnlock(); + + cleanup: + return ret; +} + +static virFSPoolPtr +fsPoolLookupByUUID(virConnectPtr conn, + const unsigned char *uuid) +{ + virFSPoolObjPtr fspool; + virFSPoolPtr ret = NULL; + + fsDriverLock(); + fspool = virFSPoolObjFindByUUID(&driver->fspools, uuid); + fsDriverUnlock(); + + if (!fspool) { + char uuidstr[VIR_UUID_STRING_BUFLEN]; + virUUIDFormat(uuid, uuidstr); + virReportError(VIR_ERR_NO_FSPOOL, + _("no fspool with matching uuid '%s'"), uuidstr); + return NULL; + } + + if (virFSPoolLookupByUUIDEnsureACL(conn, fspool->def) < 0) + goto cleanup; + + ret = virGetFSPool(conn, fspool->def->name, fspool->def->uuid, + NULL, NULL); + + cleanup: + virFSPoolObjUnlock(fspool); + return ret; +} + +static virFSPoolPtr +fsPoolLookupByName(virConnectPtr conn, + const char *name) +{ + virFSPoolObjPtr fspool; + virFSPoolPtr ret = NULL; + + fsDriverLock(); + fspool = virFSPoolObjFindByName(&driver->fspools, name); + fsDriverUnlock(); + + if (!fspool) { + virReportError(VIR_ERR_NO_FSPOOL, + _("no fspool with matching name '%s'"), name); + return NULL; + } + + if (virFSPoolLookupByNameEnsureACL(conn, fspool->def) < 0) + goto cleanup; + + ret = virGetFSPool(conn, fspool->def->name, fspool->def->uuid, + NULL, NULL); + + cleanup: + virFSPoolObjUnlock(fspool); + return ret; +} + +static virFSPoolPtr +fsPoolLookupByItem(virFSItemPtr item) +{ + virFSPoolObjPtr fspool; + virFSPoolPtr ret = NULL; + + fsDriverLock(); + fspool = virFSPoolObjFindByName(&driver->fspools, item->fspool); + fsDriverUnlock(); + + if (!fspool) { + virReportError(VIR_ERR_NO_FSPOOL, + _("no fspool with matching name '%s'"), + item->fspool); + return NULL; + } + + if (virFSPoolLookupByItemEnsureACL(item->conn, fspool->def) < 0) + goto cleanup; + + ret = virGetFSPool(item->conn, fspool->def->name, fspool->def->uuid, + NULL, NULL); + + cleanup: + virFSPoolObjUnlock(fspool); + return ret; +} + + +static virFSPoolPtr +fsPoolCreateXML(virConnectPtr conn, + const char *xml, + unsigned int flags) +{ + virFSPoolDefPtr def; + virFSPoolObjPtr fspool = NULL; + virFSPoolPtr ret = NULL; + virFSBackendPtr backend; + char *stateFile = NULL; + unsigned int build_flags = 0; + + virCheckFlags(VIR_FSPOOL_CREATE_WITH_BUILD | + VIR_FSPOOL_CREATE_WITH_BUILD_OVERWRITE | + VIR_FSPOOL_CREATE_WITH_BUILD_NO_OVERWRITE, NULL); + + VIR_EXCLUSIVE_FLAGS_RET(VIR_FSPOOL_BUILD_OVERWRITE, + VIR_FSPOOL_BUILD_NO_OVERWRITE, NULL); + + fsDriverLock(); + if (!(def = virFSPoolDefParseString(xml))) + goto cleanup; + + if (virFSPoolCreateXMLEnsureACL(conn, def) < 0) + goto cleanup; + + if (virFSPoolObjIsDuplicate(&driver->fspools, def, 1) < 0) + goto cleanup; + + if (virFSPoolSourceFindDuplicate(conn, &driver->fspools, def) < 0) + goto cleanup; + + if ((backend = virFSBackendForType(def->type)) == NULL) + goto cleanup; + + if (!(fspool = virFSPoolObjAssignDef(&driver->fspools, def))) + goto cleanup; + def = NULL; + + if (backend->buildFSpool) { + if (flags & VIR_FSPOOL_CREATE_WITH_BUILD_OVERWRITE) + build_flags |= VIR_FSPOOL_BUILD_OVERWRITE; + else if (flags & VIR_FSPOOL_CREATE_WITH_BUILD_NO_OVERWRITE) + build_flags |= VIR_FSPOOL_BUILD_NO_OVERWRITE; + + if (build_flags || + (flags & VIR_FSPOOL_CREATE_WITH_BUILD)) { + if (backend->buildFSpool(conn, fspool, build_flags) < 0) { + virFSPoolObjRemove(&driver->fspools, fspool); + fspool = NULL; + goto cleanup; + } + } + } + + if (backend->startFSpool && + backend->startFSpool(conn, fspool) < 0) { + virFSPoolObjRemove(&driver->fspools, fspool); + fspool = NULL; + goto cleanup; + } + + stateFile = virFileBuildPath(driver->stateDir, + fspool->def->name, ".xml"); + + if (!stateFile || virFSPoolSaveState(stateFile, fspool->def) < 0 || + backend->refreshFSpool(conn, fspool) < 0) { + if (stateFile) + unlink(stateFile); + if (backend->stopFSpool) + backend->stopFSpool(conn, fspool); + virFSPoolObjRemove(&driver->fspools, fspool); + fspool = NULL; + goto cleanup; + } + VIR_INFO("Creating fspool '%s'", fspool->def->name); + fspool->active = true; + + ret = virGetFSPool(conn, fspool->def->name, fspool->def->uuid, + NULL, NULL); + + cleanup: + VIR_FREE(stateFile); + virFSPoolDefFree(def); + if (fspool) + virFSPoolObjUnlock(fspool); + fsDriverUnlock(); + return ret; +} + +static virFSPoolPtr +fsPoolDefineXML(virConnectPtr conn, + const char *xml, + unsigned int flags) +{ + virFSPoolDefPtr def; + virFSPoolObjPtr fspool = NULL; + virFSPoolPtr ret = NULL; + + virCheckFlags(0, NULL); + + fsDriverLock(); + if (!(def = virFSPoolDefParseString(xml))) + goto cleanup; + + if (virFSPoolDefineXMLEnsureACL(conn, def) < 0) + goto cleanup; + + if (virFSPoolObjIsDuplicate(&driver->fspools, def, 0) < 0) + goto cleanup; + + if (virFSPoolSourceFindDuplicate(conn, &driver->fspools, def) < 0) + goto cleanup; + + if (virFSBackendForType(def->type) == NULL) + goto cleanup; + + if (!(fspool = virFSPoolObjAssignDef(&driver->fspools, def))) + goto cleanup; + + if (virFSPoolObjSaveDef(driver, fspool, def) < 0) { + virFSPoolObjRemove(&driver->fspools, fspool); + def = NULL; + fspool = NULL; + goto cleanup; + } + def = NULL; + + VIR_INFO("Defining fspool '%s'", fspool->def->name); + ret = virGetFSPool(conn, fspool->def->name, fspool->def->uuid, + NULL, NULL); + + cleanup: + virFSPoolDefFree(def); + if (fspool) + virFSPoolObjUnlock(fspool); + fsDriverUnlock(); + return ret; +} + +static int +fsPoolCreate(virFSPoolPtr obj, + unsigned int flags) +{ + virFSPoolObjPtr fspool; + virFSBackendPtr backend; + int ret = -1; + char *stateFile = NULL; + unsigned int build_flags = 0; + + virCheckFlags(VIR_FSPOOL_CREATE_WITH_BUILD | + VIR_FSPOOL_CREATE_WITH_BUILD_OVERWRITE | + VIR_FSPOOL_CREATE_WITH_BUILD_NO_OVERWRITE, -1); + + VIR_EXCLUSIVE_FLAGS_RET(VIR_FSPOOL_BUILD_OVERWRITE, + VIR_FSPOOL_BUILD_NO_OVERWRITE, -1); + + if (!(fspool = virFSPoolObjFromFSPool(obj))) + return -1; + + if (virFSPoolCreateEnsureACL(obj->conn, fspool->def) < 0) + goto cleanup; + + if ((backend = virFSBackendForType(fspool->def->type)) == NULL) + goto cleanup; + + if (virFSPoolObjIsActive(fspool)) { + virReportError(VIR_ERR_OPERATION_INVALID, + _("fspool '%s' is already active"), + fspool->def->name); + goto cleanup; + } + + if (backend->buildFSpool) { + if (flags & VIR_FSPOOL_CREATE_WITH_BUILD_OVERWRITE) + build_flags |= VIR_FSPOOL_BUILD_OVERWRITE; + else if (flags & VIR_FSPOOL_CREATE_WITH_BUILD_NO_OVERWRITE) + build_flags |= VIR_FSPOOL_BUILD_NO_OVERWRITE; + + if (build_flags || + (flags & VIR_FSPOOL_CREATE_WITH_BUILD)) { + if (backend->buildFSpool(obj->conn, fspool, build_flags) < 0) { + virFSPoolObjRemove(&driver->fspools, fspool); + fspool = NULL; + goto cleanup; + } + } + } + + VIR_INFO("Starting up fspool '%s'", fspool->def->name); + if (backend->startFSpool && + backend->startFSpool(obj->conn, fspool) < 0) + goto cleanup; + + stateFile = virFileBuildPath(driver->stateDir, + fspool->def->name, ".xml"); + + virFSPoolObjClearItems(fspool); + if (!stateFile || virFSPoolSaveState(stateFile, fspool->def) < 0 || + backend->refreshFSpool(obj->conn, fspool) < 0) { + if (stateFile) + unlink(stateFile); + goto cleanup; + } + + fspool->active = true; + ret = 0; + + cleanup: + VIR_FREE(stateFile); + if (fspool) + virFSPoolObjUnlock(fspool); + return ret; +} + +static int +fsPoolBuild(virFSPoolPtr obj, + unsigned int flags) +{ + virFSPoolObjPtr fspool; + virFSBackendPtr backend; + int ret = -1; + + virCheckFlags(0, -1); + + if (!(fspool = virFSPoolObjFromFSPool(obj))) + return -1; + + if (virFSPoolBuildEnsureACL(obj->conn, fspool->def) < 0) + goto cleanup; + + if ((backend = virFSBackendForType(fspool->def->type)) == NULL) + goto cleanup; + + if (virFSPoolObjIsActive(fspool)) { + virReportError(VIR_ERR_OPERATION_INVALID, + _("fspool '%s' is already active"), + fspool->def->name); + goto cleanup; + } + + if (backend->buildFSpool && + backend->buildFSpool(obj->conn, fspool, flags) < 0) + goto cleanup; + + ret = 0; + + cleanup: + virFSPoolObjUnlock(fspool); + return ret; +} + +static int +fsPoolUndefine(virFSPoolPtr obj) +{ + virFSPoolObjPtr fspool; + int ret = -1; + + fsDriverLock(); + if (!(fspool = virFSPoolObjFindByUUID(&driver->fspools, obj->uuid))) { + char uuidstr[VIR_UUID_STRING_BUFLEN]; + virUUIDFormat(obj->uuid, uuidstr); + virReportError(VIR_ERR_NO_FSPOOL, + _("no fspool with matching uuid '%s' (%s)"), + uuidstr, obj->name); + goto cleanup; + } + + if (virFSPoolUndefineEnsureACL(obj->conn, fspool->def) < 0) + goto cleanup; + + if (virFSPoolObjIsActive(fspool)) { + virReportError(VIR_ERR_OPERATION_INVALID, + _("fspool '%s' is still active"), + fspool->def->name); + goto cleanup; + } + + if (fspool->asyncjobs > 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("fspool '%s' has asynchronous jobs running."), + fspool->def->name); + goto cleanup; + } + + if (virFSPoolObjDeleteDef(fspool) < 0) + goto cleanup; + + if (unlink(fspool->autostartLink) < 0 && + errno != ENOENT && + errno != ENOTDIR) { + char ebuf[1024]; + VIR_ERROR(_("Failed to delete autostart link '%s': %s"), + fspool->autostartLink, virStrerror(errno, ebuf, sizeof(ebuf))); + } + + VIR_FREE(fspool->configFile); + VIR_FREE(fspool->autostartLink); + + VIR_INFO("Undefining fspool '%s'", fspool->def->name); + virFSPoolObjRemove(&driver->fspools, fspool); + fspool = NULL; + ret = 0; + + cleanup: + if (fspool) + virFSPoolObjUnlock(fspool); + fsDriverUnlock(); + return ret; +} + +static int +fsPoolDestroy(virFSPoolPtr obj) +{ + virFSPoolObjPtr fspool; + virFSBackendPtr backend; + char *stateFile = NULL; + int ret = -1; + + fsDriverLock(); + if (!(fspool = virFSPoolObjFindByUUID(&driver->fspools, obj->uuid))) { + char uuidstr[VIR_UUID_STRING_BUFLEN]; + virUUIDFormat(obj->uuid, uuidstr); + virReportError(VIR_ERR_NO_FSPOOL, + _("no fspool with matching uuid '%s' (%s)"), + uuidstr, obj->name); + goto cleanup; + } + + if (virFSPoolDestroyEnsureACL(obj->conn, fspool->def) < 0) + goto cleanup; + + if ((backend = virFSBackendForType(fspool->def->type)) == NULL) + goto cleanup; + + VIR_INFO("Destroying fspool '%s'", fspool->def->name); + + if (!virFSPoolObjIsActive(fspool)) { + virReportError(VIR_ERR_OPERATION_INVALID, + _("fspool '%s' is not active"), fspool->def->name); + goto cleanup; + } + + if (fspool->asyncjobs > 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("fspool '%s' has asynchronous jobs running."), + fspool->def->name); + goto cleanup; + } + + if (!(stateFile = virFileBuildPath(driver->stateDir, + fspool->def->name, + ".xml"))) + goto cleanup; + + unlink(stateFile); + VIR_FREE(stateFile); + + if (backend->stopFSpool && + backend->stopFSpool(obj->conn, fspool) < 0) + goto cleanup; + + virFSPoolObjClearItems(fspool); + + fspool->active = false; + + if (fspool->configFile == NULL) { + virFSPoolObjRemove(&driver->fspools, fspool); + fspool = NULL; + } else if (fspool->newDef) { + virFSPoolDefFree(fspool->def); + fspool->def = fspool->newDef; + fspool->newDef = NULL; + } + + ret = 0; + + cleanup: + if (fspool) + virFSPoolObjUnlock(fspool); + fsDriverUnlock(); + return ret; +} + +static int +fsPoolDelete(virFSPoolPtr obj, + unsigned int flags) +{ + virFSPoolObjPtr fspool; + virFSBackendPtr backend; + char *stateFile = NULL; + int ret = -1; + + virCheckFlags(0, -1); + + if (!(fspool = virFSPoolObjFromFSPool(obj))) + return -1; + + if (virFSPoolDeleteEnsureACL(obj->conn, fspool->def) < 0) + goto cleanup; + + if ((backend = virFSBackendForType(fspool->def->type)) == NULL) + goto cleanup; + + VIR_INFO("Deleting fspool '%s'", fspool->def->name); + + if (virFSPoolObjIsActive(fspool)) { + virReportError(VIR_ERR_OPERATION_INVALID, + _("fspool '%s' is still active"), + fspool->def->name); + goto cleanup; + } + + if (fspool->asyncjobs > 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("fspool '%s' has asynchronous jobs running."), + fspool->def->name); + goto cleanup; + } + + if (!(stateFile = virFileBuildPath(driver->stateDir, + fspool->def->name, + ".xml"))) + goto cleanup; + + unlink(stateFile); + VIR_FREE(stateFile); + + if (!backend->deleteFSpool) { + virReportError(VIR_ERR_NO_SUPPORT, + "%s", _("fspool does not support fspool deletion")); + goto cleanup; + } + if (backend->deleteFSpool(obj->conn, fspool, flags) < 0) + goto cleanup; + + ret = 0; + + cleanup: + virFSPoolObjUnlock(fspool); + return ret; +} + +static int +fsPoolRefresh(virFSPoolPtr obj, + unsigned int flags) +{ + virFSPoolObjPtr fspool; + virFSBackendPtr backend; + int ret = -1; + + virCheckFlags(0, -1); + + fsDriverLock(); + if (!(fspool = virFSPoolObjFindByUUID(&driver->fspools, obj->uuid))) { + char uuidstr[VIR_UUID_STRING_BUFLEN]; + virUUIDFormat(obj->uuid, uuidstr); + virReportError(VIR_ERR_NO_FSPOOL, + _("no fspool with matching uuid '%s' (%s)"), + uuidstr, obj->name); + goto cleanup; + } + + if (virFSPoolRefreshEnsureACL(obj->conn, fspool->def) < 0) + goto cleanup; + + if ((backend = virFSBackendForType(fspool->def->type)) == NULL) + goto cleanup; + + if (!virFSPoolObjIsActive(fspool)) { + virReportError(VIR_ERR_OPERATION_INVALID, + _("fspool '%s' is not active"), fspool->def->name); + goto cleanup; + } + + if (fspool->asyncjobs > 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("fspool '%s' has asynchronous jobs running."), + fspool->def->name); + goto cleanup; + } + + virFSPoolObjClearItems(fspool); + if (backend->refreshFSpool(obj->conn, fspool) < 0) { + if (backend->stopFSpool) + backend->stopFSpool(obj->conn, fspool); + + fspool->active = false; + + if (fspool->configFile == NULL) { + virFSPoolObjRemove(&driver->fspools, fspool); + fspool = NULL; + } + goto cleanup; + } + ret = 0; + + cleanup: + if (fspool) + virFSPoolObjUnlock(fspool); + fsDriverUnlock(); + return ret; + + return 0; +} + + +static int +fsPoolGetInfo(virFSPoolPtr obj, + virFSPoolInfoPtr info) +{ + virFSPoolObjPtr fspool; + int ret = -1; + + if (!(fspool = virFSPoolObjFromFSPool(obj))) + return -1; + + if (virFSPoolGetInfoEnsureACL(obj->conn, fspool->def) < 0) + goto cleanup; + + if (virFSBackendForType(fspool->def->type) == NULL) + goto cleanup; + + memset(info, 0, sizeof(virFSPoolInfo)); + if (fspool->active) + info->state = VIR_FSPOOL_RUNNING; + else + info->state = VIR_FSPOOL_INACTIVE; + info->capacity = fspool->def->capacity; + info->allocation = fspool->def->allocation; + info->available = fspool->def->available; + ret = 0; + + cleanup: + virFSPoolObjUnlock(fspool); + return ret; +} + +static char * +fsPoolGetXMLDesc(virFSPoolPtr obj, + unsigned int flags) +{ + virFSPoolObjPtr fspool; + virFSPoolDefPtr def; + char *ret = NULL; + + virCheckFlags(VIR_FS_XML_INACTIVE, NULL); + + if (!(fspool = virFSPoolObjFromFSPool(obj))) + return NULL; + + if (virFSPoolGetXMLDescEnsureACL(obj->conn, fspool->def) < 0) + goto cleanup; + + if ((flags & VIR_FS_XML_INACTIVE) && fspool->newDef) + def = fspool->newDef; + else + def = fspool->def; + + ret = virFSPoolDefFormat(def); + + cleanup: + virFSPoolObjUnlock(fspool); + return ret; +} + +static int +fsPoolGetAutostart(virFSPoolPtr obj, int *autostart) +{ + virFSPoolObjPtr fspool; + int ret = -1; + + if (!(fspool = virFSPoolObjFromFSPool(obj))) + return -1; + + if (virFSPoolGetAutostartEnsureACL(obj->conn, fspool->def) < 0) + goto cleanup; + + if (!fspool->configFile) { + *autostart = 0; + } else { + *autostart = fspool->autostart; + } + ret = 0; + + cleanup: + virFSPoolObjUnlock(fspool); + return ret; +} + +static int +fsPoolSetAutostart(virFSPoolPtr obj, int autostart) +{ + virFSPoolObjPtr fspool; + int ret = -1; + + fsDriverLock(); + fspool = virFSPoolObjFindByUUID(&driver->fspools, obj->uuid); + + if (!fspool) { + char uuidstr[VIR_UUID_STRING_BUFLEN]; + virUUIDFormat(obj->uuid, uuidstr); + virReportError(VIR_ERR_NO_FSPOOL, + _("no fspool with matching uuid '%s' (%s)"), + uuidstr, obj->name); + goto cleanup; + } + + if (virFSPoolSetAutostartEnsureACL(obj->conn, fspool->def) < 0) + goto cleanup; + + if (!fspool->configFile) { + virReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("fspool has no config file")); + goto cleanup; + } + + autostart = (autostart != 0); + + if (fspool->autostart != autostart) { + if (autostart) { + if (virFileMakePath(driver->autostartDir) < 0) { + virReportSystemError(errno, + _("cannot create autostart directory %s"), + driver->autostartDir); + goto cleanup; + } + + if (symlink(fspool->configFile, fspool->autostartLink) < 0) { + virReportSystemError(errno, + _("Failed to create symlink '%s' to '%s'"), + fspool->autostartLink, fspool->configFile); + goto cleanup; + } + } else { + if (unlink(fspool->autostartLink) < 0 && + errno != ENOENT && errno != ENOTDIR) { + virReportSystemError(errno, + _("Failed to delete symlink '%s'"), + fspool->autostartLink); + goto cleanup; + } + } + fspool->autostart = autostart; + } + ret = 0; + + cleanup: + if (fspool) + virFSPoolObjUnlock(fspool); + fsDriverUnlock(); + return ret; +} + +static int +fsPoolNumOfItems(virFSPoolPtr obj) +{ + virFSPoolObjPtr fspool; + int ret = -1; + size_t i; + + if (!(fspool = virFSPoolObjFromFSPool(obj))) + return -1; + + if (virFSPoolNumOfItemsEnsureACL(obj->conn, fspool->def) < 0) + goto cleanup; + + if (!virFSPoolObjIsActive(fspool)) { + virReportError(VIR_ERR_OPERATION_INVALID, + _("fspool '%s' is not active"), fspool->def->name); + goto cleanup; + } + ret = 0; + for (i = 0; i < fspool->items.count; i++) { + if (virFSPoolNumOfItemsCheckACL(obj->conn, fspool->def, + fspool->items.objs[i])) + ret++; + } + + cleanup: + virFSPoolObjUnlock(fspool); + return ret; +} + +static int +fsPoolListItems(virFSPoolPtr obj, + char **const names, + int maxnames) +{ + virFSPoolObjPtr fspool; + size_t i; + int n = 0; + + memset(names, 0, maxnames * sizeof(*names)); + + if (!(fspool = virFSPoolObjFromFSPool(obj))) + return -1; + + if (virFSPoolListItemsEnsureACL(obj->conn, fspool->def) < 0) + goto cleanup; + + if (!virFSPoolObjIsActive(fspool)) { + virReportError(VIR_ERR_OPERATION_INVALID, + _("fspool '%s' is not active"), fspool->def->name); + goto cleanup; + } + + for (i = 0; i < fspool->items.count && n < maxnames; i++) { + if (!virFSPoolListItemsCheckACL(obj->conn, fspool->def, + fspool->items.objs[i])) + continue; + if (VIR_STRDUP(names[n++], fspool->items.objs[i]->name) < 0) + goto cleanup; + } + + virFSPoolObjUnlock(fspool); + return n; + + cleanup: + virFSPoolObjUnlock(fspool); + for (n = 0; n < maxnames; n++) + VIR_FREE(names[n]); + + memset(names, 0, maxnames * sizeof(*names)); + return -1; +} + +static int +fsPoolListAllItems(virFSPoolPtr fspool, + virFSItemPtr **items, + unsigned int flags) +{ + virFSPoolObjPtr obj; + size_t i; + virFSItemPtr *tmp_items = NULL; + virFSItemPtr item = NULL; + int nitems = 0; + int ret = -1; + + virCheckFlags(0, -1); + + if (!(obj = virFSPoolObjFromFSPool(fspool))) + return -1; + + if (virFSPoolListAllItemsEnsureACL(fspool->conn, obj->def) < 0) + goto cleanup; + + if (!virFSPoolObjIsActive(obj)) { + virReportError(VIR_ERR_OPERATION_INVALID, + _("fspool '%s' is not active"), obj->def->name); + goto cleanup; + } + + /* Just returns the items count */ + if (!items) { + ret = obj->items.count; + goto cleanup; + } + + if (VIR_ALLOC_N(tmp_items, obj->items.count + 1) < 0) + goto cleanup; + + for (i = 0; i < obj->items.count; i++) { + if (!virFSPoolListAllItemsCheckACL(fspool->conn, obj->def, + obj->items.objs[i])) + continue; + if (!(item = virGetFSItem(fspool->conn, obj->def->name, + obj->items.objs[i]->name, + obj->items.objs[i]->key, + NULL, NULL))) + goto cleanup; + tmp_items[nitems++] = item; + } + + *items = tmp_items; + tmp_items = NULL; + ret = nitems; + + cleanup: + if (tmp_items) { + for (i = 0; i < nitems; i++) + virObjectUnref(tmp_items[i]); + VIR_FREE(tmp_items); + } + + virFSPoolObjUnlock(obj); + + return ret; +} + +static virFSItemPtr +fsItemLookupByName(virFSPoolPtr obj, const char *name) +{ + virFSPoolObjPtr fspool; + virFSItemDefPtr item; + virFSItemPtr ret = NULL; + + if (!(fspool = virFSPoolObjFromFSPool(obj))) + return NULL; + + if (!virFSPoolObjIsActive(fspool)) { + virReportError(VIR_ERR_OPERATION_INVALID, + _("fspool '%s' is not active"), fspool->def->name); + goto cleanup; + } + + item = virFSItemDefFindByName(fspool, name); + + if (!item) { + virReportError(VIR_ERR_NO_FSITEM, + _("no fspool item with matching name '%s'"), + name); + goto cleanup; + } + + if (virFSItemLookupByNameEnsureACL(obj->conn, fspool->def, item) < 0) + goto cleanup; + + ret = virGetFSItem(obj->conn, fspool->def->name, item->name, item->key, + NULL, NULL); + + cleanup: + virFSPoolObjUnlock(fspool); + return ret; +} + + +static virFSItemPtr +fsItemLookupByKey(virConnectPtr conn, const char *key) +{ + size_t i; + virFSItemPtr ret = NULL; + + fsDriverLock(); + for (i = 0; i < driver->fspools.count && !ret; i++) { + virFSPoolObjLock(driver->fspools.objs[i]); + if (virFSPoolObjIsActive(driver->fspools.objs[i])) { + virFSItemDefPtr item = + virFSItemDefFindByKey(driver->fspools.objs[i], key); + + if (item) { + virFSPoolDefPtr def = driver->fspools.objs[i]->def; + if (virFSItemLookupByKeyEnsureACL(conn, def, item) < 0) { + virFSPoolObjUnlock(driver->fspools.objs[i]); + goto cleanup; + } + + ret = virGetFSItem(conn, + def->name, + item->name, + item->key, + NULL, NULL); + } + } + virFSPoolObjUnlock(driver->fspools.objs[i]); + } + + if (!ret) + virReportError(VIR_ERR_NO_FSITEM, + _("no fspool item with matching key %s"), key); + + cleanup: + fsDriverUnlock(); + return ret; +} + +static virFSItemPtr +fsItemLookupByPath(virConnectPtr conn, + const char *path) +{ + size_t i; + virFSItemPtr ret = NULL; + char *cleanpath; + + cleanpath = virFileSanitizePath(path); + if (!cleanpath) + return NULL; + + fsDriverLock(); + for (i = 0; i < driver->fspools.count && !ret; i++) { + virFSPoolObjPtr fspool = driver->fspools.objs[i]; + virFSItemDefPtr item; + + virFSPoolObjLock(fspool); + + if (!virFSPoolObjIsActive(fspool)) { + virFSPoolObjUnlock(fspool); + continue; + } + + item = virFSItemDefFindByPath(fspool, cleanpath); + + if (item) { + if (virFSItemLookupByPathEnsureACL(conn, fspool->def, item) < 0) { + virFSPoolObjUnlock(fspool); + goto cleanup; + } + + ret = virGetFSItem(conn, fspool->def->name, + item->name, item->key, + NULL, NULL); + } + + virFSPoolObjUnlock(fspool); + } + + if (!ret) { + if (STREQ(path, cleanpath)) { + virReportError(VIR_ERR_NO_FSITEM, + _("no fspool item with matching path '%s'"), path); + } else { + virReportError(VIR_ERR_NO_FSITEM, + _("no fspool item with matching path '%s' (%s)"), + path, cleanpath); + } + } + + cleanup: + VIR_FREE(cleanpath); + fsDriverUnlock(); + return ret; +} + +static virFSItemPtr +fsItemCreateXML(virFSPoolPtr obj, + const char *xmldesc, + unsigned int flags) +{ + virFSPoolObjPtr fspool; + virFSBackendPtr backend; + virFSItemDefPtr itemdef = NULL; + virFSItemPtr ret = NULL, itemobj = NULL; + + virCheckFlags(0, NULL); + + if (!(fspool = virFSPoolObjFromFSPool(obj))) + return NULL; + + if (!virFSPoolObjIsActive(fspool)) { + virReportError(VIR_ERR_OPERATION_INVALID, + _("fspool '%s' is not active"), fspool->def->name); + goto cleanup; + } + + if ((backend = virFSBackendForType(fspool->def->type)) == NULL) + goto cleanup; + + itemdef = virFSItemDefParseString(fspool->def, xmldesc, + VIR_ITEM_XML_PARSE_OPT_CAPACITY); + if (itemdef == NULL) + goto cleanup; + + if (!itemdef->target.capacity && !backend->buildItem) { + virReportError(VIR_ERR_NO_SUPPORT, + "%s", _("item capacity required for this " + "fspool")); + goto cleanup; + } + + if (virFSItemCreateXMLEnsureACL(obj->conn, fspool->def, itemdef) < 0) + goto cleanup; + + if (virFSItemDefFindByName(fspool, itemdef->name)) { + virReportError(VIR_ERR_FSITEM_EXIST, + _("'%s'"), itemdef->name); + goto cleanup; + } + + if (!backend->createItem) { + virReportError(VIR_ERR_NO_SUPPORT, + "%s", _("fspool does not support item " + "creation")); + goto cleanup; + } + + if (VIR_REALLOC_N(fspool->items.objs, + fspool->items.count+1) < 0) + goto cleanup; + + /* Wipe any key the user may have suggested, as item creation + * will generate the canonical key. */ + VIR_FREE(itemdef->key); + if (backend->createItem(obj->conn, fspool, itemdef) < 0) + goto cleanup; + + fspool->items.objs[fspool->items.count++] = itemdef; + itemobj = virGetFSItem(obj->conn, fspool->def->name, itemdef->name, + itemdef->key, NULL, NULL); + if (!itemobj) { + fspool->items.count--; + goto cleanup; + } + + + if (backend->buildItem) { + int buildret; + virFSItemDefPtr builditemdef = NULL; + + if (VIR_ALLOC(builditemdef) < 0) { + itemdef = NULL; + goto cleanup; + } + + /* Make a shallow copy of the 'defined' item definition, since the + * original allocation value will change as the user polls 'info', + * but we only need the initial requested values + */ + memcpy(builditemdef, itemdef, sizeof(*itemdef)); + + /* Drop the fspool lock during item allocation */ + fspool->asyncjobs++; + itemdef->building = true; + virFSPoolObjUnlock(fspool); + + buildret = backend->buildItem(obj->conn, fspool, builditemdef, flags); + + VIR_FREE(builditemdef); + + fsDriverLock(); + virFSPoolObjLock(fspool); + fsDriverUnlock(); + + itemdef->building = false; + fspool->asyncjobs--; + + if (buildret < 0) { + /* buildItem handles deleting item on failure */ + fsItemRemoveFromFSPool(fspool, itemdef); + itemdef = NULL; + goto cleanup; + } + + } + + if (backend->refreshItem && + backend->refreshItem(obj->conn, fspool, itemdef) < 0) { + fsItemDeleteInternal(itemobj, backend, fspool, itemdef, 0); + itemdef = NULL; + goto cleanup; + } + + /* Update fspool metadata ignoring the disk backend since + * it updates the fspool values. + */ + + VIR_INFO("Creating item '%s' in fspool '%s'", + itemobj->name, fspool->def->name); + ret = itemobj; + itemobj = NULL; + itemdef = NULL; + + cleanup: + virObjectUnref(itemobj); + virFSItemDefFree(itemdef); + if (fspool) + virFSPoolObjUnlock(fspool); + return ret; +} + +static virFSItemPtr +fsItemCreateXMLFrom(virFSPoolPtr obj, + const char *xmldesc, + virFSItemPtr vobj, + unsigned int flags) +{ + virFSPoolObjPtr fspool, origpool = NULL; + virFSBackendPtr backend; + virFSItemDefPtr origitem = NULL, newitem = NULL, shadowitem = NULL; + virFSItemPtr ret = NULL, itemobj = NULL; + int buildret; + + virCheckFlags(0, NULL); + + fsDriverLock(); + fspool = virFSPoolObjFindByUUID(&driver->fspools, obj->uuid); + if (fspool && STRNEQ(obj->name, vobj->fspool)) { + virFSPoolObjUnlock(fspool); + origpool = virFSPoolObjFindByName(&driver->fspools, vobj->fspool); + virFSPoolObjLock(fspool); + } + fsDriverUnlock(); + if (!fspool) { + char uuidstr[VIR_UUID_STRING_BUFLEN]; + virUUIDFormat(obj->uuid, uuidstr); + virReportError(VIR_ERR_NO_FSPOOL, + _("no fspool with matching uuid '%s' (%s)"), + uuidstr, obj->name); + goto cleanup; + } + + if (STRNEQ(obj->name, vobj->fspool) && !origpool) { + virReportError(VIR_ERR_NO_FSPOOL, + _("no fspool with matching name '%s'"), + vobj->fspool); + goto cleanup; + } + + if (!virFSPoolObjIsActive(fspool)) { + virReportError(VIR_ERR_OPERATION_INVALID, + _("fspool '%s' is not active"), fspool->def->name); + goto cleanup; + } + + if (origpool && !virFSPoolObjIsActive(origpool)) { + virReportError(VIR_ERR_OPERATION_INVALID, + _("fspool '%s' is not active"), + origpool->def->name); + goto cleanup; + } + + if ((backend = virFSBackendForType(fspool->def->type)) == NULL) + goto cleanup; + + origitem = virFSItemDefFindByName(origpool ? + origpool : fspool, vobj->name); + if (!origitem) { + virReportError(VIR_ERR_NO_FSITEM, + _("no fsitem with matching name '%s'"), + vobj->name); + goto cleanup; + } + + newitem = virFSItemDefParseString(fspool->def, xmldesc, + VIR_VOL_XML_PARSE_NO_CAPACITY); + if (newitem == NULL) + goto cleanup; + + if (virFSItemCreateXMLFromEnsureACL(obj->conn, fspool->def, newitem) < 0) + goto cleanup; + + if (virFSItemDefFindByName(fspool, newitem->name)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("fsitem name '%s' already in use."), + newitem->name); + goto cleanup; + } + + /* Use the original item's capacity in case the new capacity + * is less than that, or it was omitted */ + if (newitem->target.capacity < origitem->target.capacity) + newitem->target.capacity = origitem->target.capacity; + + if (!backend->buildItemFrom) { + virReportError(VIR_ERR_NO_SUPPORT, + "%s", _("fspool does not support" + " item creation from an existing item")); + goto cleanup; + } + + if (origitem->building) { + virReportError(VIR_ERR_OPERATION_INVALID, + _("fsitem '%s' is still being allocated."), + origitem->name); + goto cleanup; + } + + if (backend->refreshItem && + backend->refreshItem(obj->conn, fspool, origitem) < 0) + goto cleanup; + + if (VIR_REALLOC_N(fspool->items.objs, + fspool->items.count+1) < 0) + goto cleanup; + + /* 'Define' the new item so we get async progress reporting. + * Wipe any key the user may have suggested, as item creation + * will generate the canonical key. */ + VIR_FREE(newitem->key); + if (backend->createItem(obj->conn, fspool, newitem) < 0) + goto cleanup; + + /* Make a shallow copy of the 'defined' item definition, since the + * original allocation value will change as the user polls 'info', + * but we only need the initial requested values + */ + if (VIR_ALLOC(shadowitem) < 0) + goto cleanup; + + memcpy(shadowitem, newitem, sizeof(*newitem)); + + fspool->items.objs[fspool->items.count++] = newitem; + itemobj = virGetFSItem(obj->conn, fspool->def->name, newitem->name, + newitem->key, NULL, NULL); + if (!itemobj) { + fspool->items.count--; + goto cleanup; + } + + /* Drop the fspool lock during item allocation */ + fspool->asyncjobs++; + newitem->building = true; + origitem->in_use++; + virFSPoolObjUnlock(fspool); + + if (origpool) { + origpool->asyncjobs++; + virFSPoolObjUnlock(origpool); + } + + buildret = backend->buildItemFrom(obj->conn, fspool, shadowitem, origitem, flags); + + fsDriverLock(); + virFSPoolObjLock(fspool); + if (origpool) + virFSPoolObjLock(origpool); + fsDriverUnlock(); + + origitem->in_use--; + newitem->building = false; + fspool->asyncjobs--; + + if (origpool) { + origpool->asyncjobs--; + virFSPoolObjUnlock(origpool); + origpool = NULL; + } + + if (buildret < 0 || + (backend->refreshItem && + backend->refreshItem(obj->conn, fspool, newitem) < 0)) { + fsItemDeleteInternal(itemobj, backend, fspool, newitem, 0); + newitem = NULL; + goto cleanup; + } + + fspool->def->allocation += newitem->target.allocation; + fspool->def->available -= newitem->target.allocation; + + VIR_INFO("Creating item '%s' in fspool '%s'", + itemobj->name, fspool->def->name); + ret = itemobj; + itemobj = NULL; + newitem = NULL; + + cleanup: + virObjectUnref(itemobj); + virFSItemDefFree(newitem); + VIR_FREE(shadowitem); + if (fspool) + virFSPoolObjUnlock(fspool); + if (origpool) + virFSPoolObjUnlock(origpool); + return ret; +} + + +static int +fsItemGetInfo(virFSItemPtr obj, + virFSItemInfoPtr info) +{ + virFSPoolObjPtr fspool; + virFSBackendPtr backend; + virFSItemDefPtr item; + int ret = -1; + + if (!(item = virFSItemDefFromItem(obj, &fspool, &backend))) + return -1; + + if (virFSItemGetInfoEnsureACL(obj->conn, fspool->def, item) < 0) + goto cleanup; + + if (backend->refreshItem && + backend->refreshItem(obj->conn, fspool, item) < 0) + goto cleanup; + + memset(info, 0, sizeof(*info)); + info->type = item->type; + info->capacity = item->target.capacity; + info->allocation = item->target.allocation; + ret = 0; + + cleanup: + virFSPoolObjUnlock(fspool); + return ret; +} + +static char * +fsItemGetXMLDesc(virFSItemPtr obj, unsigned int flags) +{ + virFSPoolObjPtr fspool; + virFSBackendPtr backend; + virFSItemDefPtr item; + char *ret = NULL; + + virCheckFlags(0, NULL); + + if (!(item = virFSItemDefFromItem(obj, &fspool, &backend))) + return NULL; + + if (virFSItemGetXMLDescEnsureACL(obj->conn, fspool->def, item) < 0) + goto cleanup; + + if (backend->refreshItem && + backend->refreshItem(obj->conn, fspool, item) < 0) + goto cleanup; + + ret = virFSItemDefFormat(fspool->def, item); + + cleanup: + virFSPoolObjUnlock(fspool); + + return ret; +} + +static char * +fsItemGetPath(virFSItemPtr obj) +{ + virFSPoolObjPtr fspool; + virFSItemDefPtr item; + char *ret = NULL; + + if (!(item = virFSItemDefFromItem(obj, &fspool, NULL))) + return NULL; + + if (virFSItemGetPathEnsureACL(obj->conn, fspool->def, item) < 0) + goto cleanup; + + ignore_value(VIR_STRDUP(ret, item->target.path)); + + cleanup: + virFSPoolObjUnlock(fspool); + return ret; +} + + +static int +fsPoolIsActive(virFSPoolPtr fspool) +{ + virFSPoolObjPtr obj; + int ret = -1; + + if (!(obj = virFSPoolObjFromFSPool(fspool))) + return -1; + + if (virFSPoolIsActiveEnsureACL(fspool->conn, obj->def) < 0) + goto cleanup; + + ret = virFSPoolObjIsActive(obj); + + cleanup: + virFSPoolObjUnlock(obj); + return ret; +} + +static int +fsPoolIsPersistent(virFSPoolPtr fspool) +{ + virFSPoolObjPtr obj; + int ret = -1; + + if (!(obj = virFSPoolObjFromFSPool(fspool))) + return -1; + + if (virFSPoolIsPersistentEnsureACL(fspool->conn, obj->def) < 0) + goto cleanup; + + ret = obj->configFile ? 1 : 0; + + cleanup: + virFSPoolObjUnlock(obj); + return ret; +} + + +static int +fsItemDelete(virFSItemPtr obj, + unsigned int flags) +{ + virFSPoolObjPtr fspool; + virFSBackendPtr backend; + virFSItemDefPtr item = NULL; + int ret = -1; + + virCheckFlags(0, -1); + + if (!(item = virFSItemDefFromItem(obj, &fspool, &backend))) + return -1; + + if (virFSItemDeleteEnsureACL(obj->conn, fspool->def, item) < 0) + goto cleanup; + + if (item->in_use) { + virReportError(VIR_ERR_OPERATION_INVALID, + _("item '%s' is still in use."), + item->name); + goto cleanup; + } + + if (item->building) { + virReportError(VIR_ERR_OPERATION_INVALID, + _("item '%s' is still being allocated."), + item->name); + goto cleanup; + } + + if (fsItemDeleteInternal(obj, backend, fspool, item, flags) < 0) + goto cleanup; + + ret = 0; + + cleanup: + virFSPoolObjUnlock(fspool); + return ret; +} + +static virFSDriver fsDriver = { + .name = "fs", + .connectListAllFSPools = fsConnectListAllFSPools, /* 2.3.0 */ + .fsPoolLookupByName = fsPoolLookupByName, /* 2.3.0 */ + .fsPoolLookupByUUID = fsPoolLookupByUUID, /* 2.3.0 */ + .fsPoolLookupByItem = fsPoolLookupByItem, /* 2.3.0 */ + .fsPoolCreateXML = fsPoolCreateXML, /* 2.3.0 */ + .fsPoolDefineXML = fsPoolDefineXML, /* 2.3.0 */ + .fsPoolBuild = fsPoolBuild, /* 2.3.0 */ + .fsPoolCreate = fsPoolCreate, /* 2.3.0 */ + .fsPoolUndefine = fsPoolUndefine, /* 2.3.0 */ + .fsPoolDestroy = fsPoolDestroy, /* 2.3.0 */ + .fsPoolDelete = fsPoolDelete, /* 2.3.0 */ + .fsPoolRefresh = fsPoolRefresh, /* 2.3.0 */ + .fsPoolGetInfo = fsPoolGetInfo, /* 2.3.0 */ + .fsPoolGetXMLDesc = fsPoolGetXMLDesc, /* 2.3.0 */ + .fsPoolGetAutostart = fsPoolGetAutostart, /* 2.3.0 */ + .fsPoolSetAutostart = fsPoolSetAutostart, /* 2.3.0 */ + .fsPoolNumOfItems = fsPoolNumOfItems, /* 2.3.0 */ + .fsPoolListItems = fsPoolListItems, /* 2.3.0 */ + .fsPoolListAllItems = fsPoolListAllItems, /* 2.3.0 */ + .fsItemLookupByName = fsItemLookupByName, /* 2.3.0 */ + .fsItemLookupByKey = fsItemLookupByKey, /* 2.3.0 */ + .fsItemLookupByPath = fsItemLookupByPath, /* 2.3.0 */ + .fsItemCreateXML = fsItemCreateXML, /* 2.3.0 */ + .fsItemCreateXMLFrom = fsItemCreateXMLFrom, /* 2.3.0 */ + .fsItemDelete = fsItemDelete, /* 2.3.0 */ + .fsItemGetInfo = fsItemGetInfo, /* 2.3.0 */ + .fsItemGetXMLDesc = fsItemGetXMLDesc, /* 2.3.0 */ + .fsItemGetPath = fsItemGetPath, /* 2.3.0 */ + .fsPoolIsActive = fsPoolIsActive, /* 2.3.0 */ + .fsPoolIsPersistent = fsPoolIsPersistent, /* 2.3.0 */ +}; + + +static virStateDriver stateDriver = { + .name = "fs", + .stateInitialize = fsStateInitialize, + .stateAutoStart = fsStateAutoStart, + .stateCleanup = fsStateCleanup, + .stateReload = fsStateReload, +}; + +int fsRegister(void) +{ + VIR_DEBUG("fsDriver = %p", &fsDriver); + + if (virSetSharedFSDriver(&fsDriver) < 0) + return -1; + + if (virRegisterStateDriver(&stateDriver) < 0) + return -1; + + VIR_DEBUG("fsDriver = %p", &fsDriver); + + return 0; +} diff --git a/src/fs/fs_driver.h b/src/fs/fs_driver.h new file mode 100644 index 0000000..aaf0258 --- /dev/null +++ b/src/fs/fs_driver.h @@ -0,0 +1,10 @@ +#ifndef __VIR_FS_DRIVER_H__ +# define __VIR_FS_DRIVER_H__ + +# include <sys/stat.h> + +# include "domain_conf.h" +# include "fs_conf.h" + +int fsRegister(void); +#endif /* __VIR_FS_DRIVER_H__ */ diff --git a/src/libvirt.c b/src/libvirt.c index 52462e3..775abdb 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -116,6 +116,7 @@ static int virStateDriverTabCount; static virNetworkDriverPtr virSharedNetworkDriver; static virInterfaceDriverPtr virSharedInterfaceDriver; static virStorageDriverPtr virSharedStorageDriver; +static virFSDriverPtr virSharedFSDriver; static virNodeDeviceDriverPtr virSharedNodeDeviceDriver; static virSecretDriverPtr virSharedSecretDriver; static virNWFilterDriverPtr virSharedNWFilterDriver; @@ -587,7 +588,30 @@ virSetSharedStorageDriver(virStorageDriverPtr driver) return 0; } +/** + * virSetSharedFSDriver: + * @driver: pointer to a fs driver block + * + * Register a fs virtualization driver + * + * Returns the driver priority or -1 in case of error. + */ +int +virSetSharedFSDriver(virFSDriverPtr driver) +{ + virCheckNonNullArgReturn(driver, -1); + if (virSharedFSDriver) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("A fs driver is already registered")); + return -1; + } + + VIR_DEBUG("registering %s as fs driver", driver->name); + + virSharedFSDriver = driver; + return 0; +} /** * virSetSharedNodeDeviceDriver: * @driver: pointer to a device monitor block @@ -707,6 +731,8 @@ virRegisterConnectDriver(virConnectDriverPtr driver, driver->secretDriver = virSharedSecretDriver; if (driver->storageDriver == NULL) driver->storageDriver = virSharedStorageDriver; + if (driver->fsDriver == NULL) + driver->fsDriver = virSharedFSDriver; } virConnectDriverTab[virConnectDriverTabCount] = driver; @@ -1089,6 +1115,7 @@ virConnectOpenInternal(const char *name, ret->nwfilterDriver = virConnectDriverTab[i]->nwfilterDriver; ret->secretDriver = virConnectDriverTab[i]->secretDriver; ret->storageDriver = virConnectDriverTab[i]->storageDriver; + ret->fsDriver = virConnectDriverTab[i]->fsDriver; res = virConnectDriverTab[i]->hypervisorDriver->connectOpen(ret, auth, conf, flags); VIR_DEBUG("driver %zu %s returned %s", @@ -1107,6 +1134,7 @@ virConnectOpenInternal(const char *name, ret->nwfilterDriver = NULL; ret->secretDriver = NULL; ret->storageDriver = NULL; + ret->fsDriver = NULL; if (res == VIR_DRV_OPEN_ERROR) goto failed; diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 308dcc2..21ceeff 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1088,6 +1088,7 @@ virDomainMigratePrepareTunnel3; virDomainMigratePrepareTunnel3Params; virRegisterConnectDriver; virRegisterStateDriver; +virSetSharedFSDriver; virSetSharedInterfaceDriver; virSetSharedNetworkDriver; virSetSharedNodeDeviceDriver; -- 1.8.3.1

Signed-off-by: Nikolay Shirokovskiy <nshirokovskiy@virtuozzo.com> Signed-off-by: Maxim Nestratov <mnestratov@virtuozzo.com> --- po/POTFILES.in | 2 + tools/Makefile.am | 2 + tools/virsh-fsitem.c | 1292 ++++++++++++++++++++++++++++++++++++++++ tools/virsh-fsitem.h | 39 ++ tools/virsh-fspool.c | 1586 ++++++++++++++++++++++++++++++++++++++++++++++++++ tools/virsh-fspool.h | 38 ++ tools/virsh.c | 4 + tools/virsh.h | 9 + tools/virsh.pod | 252 +++++++- 9 files changed, 3223 insertions(+), 1 deletion(-) create mode 100644 tools/virsh-fsitem.c create mode 100644 tools/virsh-fsitem.h create mode 100644 tools/virsh-fspool.c create mode 100644 tools/virsh-fspool.h diff --git a/po/POTFILES.in b/po/POTFILES.in index 0fc3b79..5ae945b 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -289,8 +289,10 @@ tools/virsh-console.c tools/virsh-domain-monitor.c tools/virsh-domain.c tools/virsh-edit.c +tools/virsh-fspool.c tools/virsh-host.c tools/virsh-interface.c +tools/virsh-fsitem.c tools/virsh-network.c tools/virsh-nodedev.c tools/virsh-nwfilter.c diff --git a/tools/Makefile.am b/tools/Makefile.am index e7e42c3..5891bdf 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -198,9 +198,11 @@ virsh_SOURCES = \ virsh-nodedev.c virsh-nodedev.h \ virsh-nwfilter.c virsh-nwfilter.h \ virsh-pool.c virsh-pool.h \ + virsh-fspool.c virsh-fspool.h \ virsh-secret.c virsh-secret.h \ virsh-snapshot.c virsh-snapshot.h \ virsh-volume.c virsh-volume.h \ + virsh-fsitem.c virsh-fsitem.h \ $(NULL) virsh_LDFLAGS = \ diff --git a/tools/virsh-fsitem.c b/tools/virsh-fsitem.c new file mode 100644 index 0000000..53f373f --- /dev/null +++ b/tools/virsh-fsitem.c @@ -0,0 +1,1292 @@ +/* + * virsh-fsitem.c: Commands to manage storage item + * + * Copyright (C) 2016 Parallels IP Holdings GmbH + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + * + * Olga Krishtal <okrishtal@virtuozzo.com> + * + */ +#include <config.h> +#include "virsh-fsitem.h" + +#include <fcntl.h> + +#include <libxml/parser.h> +#include <libxml/tree.h> +#include <libxml/xpath.h> +#include <libxml/xmlsave.h> + +#include "internal.h" +#include "virbuffer.h" +#include "viralloc.h" +#include "virutil.h" +#include "virfile.h" +#include "virsh-fspool.h" +#include "virxml.h" +#include "virstring.h" + +#define VIRSH_COMMON_OPT_FSPOOL_FULL \ + VIRSH_COMMON_OPT_FSPOOL(N_("fspool name or uuid")) \ + +#define VIRSH_COMMON_OPT_FSPOOL_NAME \ + VIRSH_COMMON_OPT_FSPOOL(N_("fspool name")) \ + +#define VIRSH_COMMON_OPT_FSPOOL_OPTIONAL \ + {.name = "fspool", \ + .type = VSH_OT_STRING, \ + .help = N_("fspool name or uuid") \ + } \ + +#define VIRSH_COMMON_OPT_ITEM \ + {.name = "item", \ + .type = VSH_OT_DATA, \ + .flags = VSH_OFLAG_REQ, \ + .help = N_("item name, key or path") \ + } \ + +virFSItemPtr +virshCommandOptItemBy(vshControl *ctl, const vshCmd *cmd, + const char *optname, + const char *fspooloptname, + const char **name, unsigned int flags) +{ + virFSItemPtr item = NULL; + virFSPoolPtr fspool = NULL; + const char *n = NULL, *p = NULL; + virshControlPtr priv = ctl->privData; + + virCheckFlags(VIRSH_BYUUID | VIRSH_BYNAME, NULL); + + if (vshCommandOptStringReq(ctl, cmd, optname, &n) < 0) + return NULL; + + if (fspooloptname != NULL && + vshCommandOptStringReq(ctl, cmd, fspooloptname, &p) < 0) + return NULL; + + if (p) { + if (!(fspool = virshCommandOptFSPoolBy(ctl, cmd, fspooloptname, name, flags))) + return NULL; + + if (virFSPoolIsActive(fspool) != 1) { + vshError(ctl, _("fspool '%s' is not active"), p); + virFSPoolFree(fspool); + return NULL; + } + } + + vshDebug(ctl, VSH_ERR_DEBUG, "%s: found option <%s>: %s\n", + cmd->def->name, optname, n); + + if (name) + *name = n; + + /* try it by name */ + if (fspool && (flags & VIRSH_BYNAME)) { + vshDebug(ctl, VSH_ERR_DEBUG, "%s: <%s> trying as item name\n", + cmd->def->name, optname); + item = virFSItemLookupByName(fspool, n); + } + /* try it by key */ + if (!item && (flags & VIRSH_BYUUID)) { + vshDebug(ctl, VSH_ERR_DEBUG, "%s: <%s> trying as item key\n", + cmd->def->name, optname); + item = virFSItemLookupByKey(priv->conn, n); + } + /* try it by path */ + if (!item && (flags & VIRSH_BYUUID)) { + vshDebug(ctl, VSH_ERR_DEBUG, "%s: <%s> trying as item path\n", + cmd->def->name, optname); + item = virFSItemLookupByPath(priv->conn, n); + } + + if (!item) { + if (fspool || !fspooloptname) + vshError(ctl, _("failed to get item '%s'"), n); + else + vshError(ctl, _("failed to get item '%s', specifying --%s " + "might help"), n, fspooloptname); + } + + /* If the fspool was specified, then make sure that the returned + * item is from the given fspool */ + if (fspool && item) { + virFSPoolPtr itemfspool = NULL; + + if ((itemfspool = virFSPoolLookupByItem(item))) { + if (STRNEQ(virFSPoolGetName(itemfspool), + virFSPoolGetName(fspool))) { + vshResetLibvirtError(); + vshError(ctl, + _("Requested item '%s' is not in fspool '%s'"), + n, virFSPoolGetName(fspool)); + virFSItemFree(item); + item = NULL; + } + virFSPoolFree(itemfspool); + } + } + + if (fspool) + virFSPoolFree(fspool); + + return item; +} + +/* + * "item-create-as" command + */ +static const vshCmdInfo info_item_create_as[] = { + {.name = "help", + .data = N_("create a item from a set of args") + }, + {.name = "desc", + .data = N_("Create a item.") + }, + {.name = NULL} +}; + +static const vshCmdOptDef opts_item_create_as[] = { + VIRSH_COMMON_OPT_FSPOOL_NAME, + {.name = "name", + .type = VSH_OT_DATA, + .flags = VSH_OFLAG_REQ, + .help = N_("name of the item") + }, + {.name = "capacity", + .type = VSH_OT_DATA, + .flags = VSH_OFLAG_REQ, + .help = N_("size of the item, as scaled integer (default bytes)") + }, + {.name = "allocation", + .type = VSH_OT_STRING, + .help = N_("initial allocation size, as scaled integer (default bytes)") + }, + {.name = "format", + .type = VSH_OT_STRING, + .help = N_("file format type raw,bochs,qcow,qcow2,qed,vmdk") + }, + {.name = "print-xml", + .type = VSH_OT_BOOL, + .help = N_("print XML document, but don't define/create") + }, + {.name = NULL} +}; + +static int +virshItemSize(const char *data, unsigned long long *val) +{ + char *end; + if (virStrToLong_ull(data, &end, 10, val) < 0) + return -1; + return virScaleInteger(val, end, 1, ULLONG_MAX); +} + +static bool +cmdItemCreateAs(vshControl *ctl, const vshCmd *cmd) +{ + virFSPoolPtr fspool; + virFSItemPtr item = NULL; + char *xml = NULL; + bool printXML = vshCommandOptBool(cmd, "print-xml"); + const char *name, *capacityStr = NULL, *allocationStr = NULL, *format = NULL; + unsigned long long capacity, allocation = 0; + virBuffer buf = VIR_BUFFER_INITIALIZER; + unsigned long flags = 0; + bool ret = false; + + if (!(fspool = virshCommandOptFSPool(ctl, cmd, "fspool", NULL))) + return false; + + if (vshCommandOptStringReq(ctl, cmd, "name", &name) < 0) + goto cleanup; + + if (vshCommandOptStringReq(ctl, cmd, "capacity", &capacityStr) < 0) + goto cleanup; + + if (virshItemSize(capacityStr, &capacity) < 0) { + vshError(ctl, _("Malformed size %s"), capacityStr); + goto cleanup; + } + + if (vshCommandOptStringQuiet(ctl, cmd, "allocation", &allocationStr) > 0 && + virshItemSize(allocationStr, &allocation) < 0) { + vshError(ctl, _("Malformed size %s"), allocationStr); + goto cleanup; + } + + if (vshCommandOptStringReq(ctl, cmd, "format", &format)) + goto cleanup; + + virBufferAddLit(&buf, "<item>\n"); + virBufferAdjustIndent(&buf, 2); + virBufferAsprintf(&buf, "<name>%s</name>\n", name); + virBufferAsprintf(&buf, "<capacity>%llu</capacity>\n", capacity); + if (allocationStr) + virBufferAsprintf(&buf, "<allocation>%llu</allocation>\n", allocation); + + if (format) { + virBufferAddLit(&buf, "<target>\n"); + virBufferAdjustIndent(&buf, 2); + virBufferAsprintf(&buf, "<format type='%s'/>\n", format); + virBufferAdjustIndent(&buf, -2); + virBufferAddLit(&buf, "</target>\n"); + } + + virBufferAdjustIndent(&buf, -2); + virBufferAddLit(&buf, "</item>\n"); + + if (virBufferError(&buf)) { + vshPrint(ctl, "%s", _("Failed to allocate XML buffer")); + goto cleanup; + } + xml = virBufferContentAndReset(&buf); + + if (printXML) { + vshPrint(ctl, "%s", xml); + } else { + if (!(item = virFSItemCreateXML(fspool, xml, flags))) { + vshError(ctl, _("Failed to create item %s"), name); + goto cleanup; + } + vshPrint(ctl, _("Item %s created\n"), name); + } + + ret = true; + + cleanup: + virBufferFreeAndReset(&buf); + if (item) + virFSItemFree(item); + virFSPoolFree(fspool); + VIR_FREE(xml); + return ret; +} + +/* + * "item-create" command + */ +static const vshCmdInfo info_item_create[] = { + {.name = "help", + .data = N_("create a item from an XML file") + }, + {.name = "desc", + .data = N_("Create a item.") + }, + {.name = NULL} +}; + +static const vshCmdOptDef opts_item_create[] = { + VIRSH_COMMON_OPT_FSPOOL_NAME, + VIRSH_COMMON_OPT_FILE(N_("file containing an XML item description")), + {.name = NULL} +}; + +static bool +cmdItemCreate(vshControl *ctl, const vshCmd *cmd) +{ + virFSPoolPtr fspool; + virFSItemPtr item; + const char *from = NULL; + bool ret = false; + unsigned int flags = 0; + char *buffer = NULL; + + if (!(fspool = virshCommandOptFSPool(ctl, cmd, "fspool", NULL))) + return false; + + if (vshCommandOptStringReq(ctl, cmd, "file", &from) < 0) + goto cleanup; + + if (virFileReadAll(from, VSH_MAX_XML_FILE, &buffer) < 0) { + vshSaveLibvirtError(); + goto cleanup; + } + + if ((item = virFSItemCreateXML(fspool, buffer, flags))) { + vshPrint(ctl, _("Item %s created from %s\n"), + virFSItemGetName(item), from); + virFSItemFree(item); + ret = true; + } else { + vshError(ctl, _("Failed to create item from %s"), from); + } + + cleanup: + VIR_FREE(buffer); + virFSPoolFree(fspool); + return ret; +} + +/* + * "item-create-from" command + */ +static const vshCmdInfo info_item_create_from[] = { + {.name = "help", + .data = N_("create a item, using another item as input") + }, + {.name = "desc", + .data = N_("Create a item from an existing item.") + }, + {.name = NULL} +}; + +static const vshCmdOptDef opts_item_create_from[] = { + VIRSH_COMMON_OPT_FSPOOL_FULL, + VIRSH_COMMON_OPT_FILE(N_("file containing an XML item description")), + VIRSH_COMMON_OPT_ITEM, + {.name = "inputfspool", + .type = VSH_OT_STRING, + .help = N_("fspool name or uuid of the input item's fspool") + }, + {.name = NULL} +}; + +static bool +cmdItemCreateFrom(vshControl *ctl, const vshCmd *cmd) +{ + virFSPoolPtr fspool = NULL; + virFSItemPtr newitem = NULL, inputitem = NULL; + const char *from = NULL; + bool ret = false; + char *buffer = NULL; + unsigned int flags = 0; + + if (!(fspool = virshCommandOptFSPool(ctl, cmd, "fspool", NULL))) + goto cleanup; + + if (vshCommandOptStringReq(ctl, cmd, "file", &from) < 0) + goto cleanup; + + if (!(inputitem = virshCommandOptItem(ctl, cmd, "item", "inputfspool", NULL))) + goto cleanup; + + if (virFileReadAll(from, VSH_MAX_XML_FILE, &buffer) < 0) { + vshReportError(ctl); + goto cleanup; + } + + newitem = virFSItemCreateXMLFrom(fspool, buffer, inputitem, flags); + + if (newitem != NULL) { + vshPrint(ctl, _("Item %s created from input item %s\n"), + virFSItemGetName(newitem), virFSItemGetName(inputitem)); + } else { + vshError(ctl, _("Failed to create item from %s"), from); + goto cleanup; + } + + ret = true; + cleanup: + VIR_FREE(buffer); + if (fspool) + virFSPoolFree(fspool); + if (inputitem) + virFSItemFree(inputitem); + if (newitem) + virFSItemFree(newitem); + return ret; +} + +static xmlChar * +virshMakeCloneXML(const char *origxml, const char *newname) +{ + + xmlDocPtr doc = NULL; + xmlXPathContextPtr ctxt = NULL; + xmlXPathObjectPtr obj = NULL; + xmlChar *newxml = NULL; + int size; + + doc = virXMLParseStringCtxt(origxml, _("(item_definition)"), &ctxt); + if (!doc) + goto cleanup; + + obj = xmlXPathEval(BAD_CAST "/item/name", ctxt); + if (obj == NULL || obj->nodesetval == NULL || + obj->nodesetval->nodeTab == NULL) + goto cleanup; + + xmlNodeSetContent(obj->nodesetval->nodeTab[0], (const xmlChar *)newname); + xmlDocDumpMemory(doc, &newxml, &size); + + cleanup: + xmlXPathFreeObject(obj); + xmlXPathFreeContext(ctxt); + xmlFreeDoc(doc); + return newxml; +} + +/* + * "item-clone" command + */ +static const vshCmdInfo info_item_clone[] = { + {.name = "help", + .data = N_("clone a item.") + }, + {.name = "desc", + .data = N_("Clone an existing item within the parent fspool.") + }, + {.name = NULL} +}; + +static const vshCmdOptDef opts_item_clone[] = { + VIRSH_COMMON_OPT_ITEM, + {.name = "newname", + .type = VSH_OT_DATA, + .flags = VSH_OFLAG_REQ, + .help = N_("clone name") + }, + {.name = NULL} +}; + +static bool +cmdItemClone(vshControl *ctl, const vshCmd *cmd) +{ + virFSPoolPtr origfspool = NULL; + virFSItemPtr origitem = NULL, newitem = NULL; + const char *name = NULL; + char *origxml = NULL; + xmlChar *newxml = NULL; + bool ret = false; + unsigned int flags = 0; + + if (!(origitem = virshCommandOptItem(ctl, cmd, "item", "fspool", NULL))) + goto cleanup; + + origfspool = virFSPoolLookupByItem(origitem); + if (!origfspool) { + vshError(ctl, "%s", _("failed to get parent fspool")); + goto cleanup; + } + + if (vshCommandOptStringReq(ctl, cmd, "newname", &name) < 0) + goto cleanup; + + origxml = virFSItemGetXMLDesc(origitem, 0); + if (!origxml) + goto cleanup; + + newxml = virshMakeCloneXML(origxml, name); + if (!newxml) { + vshPrint(ctl, "%s", _("Failed to allocate XML buffer")); + goto cleanup; + } + + newitem = virFSItemCreateXMLFrom(origfspool, (char *) newxml, origitem, flags); + + if (newitem != NULL) { + vshPrint(ctl, _("Item %s cloned from %s\n"), + virFSItemGetName(newitem), virFSItemGetName(origitem)); + } else { + vshError(ctl, _("Failed to clone item from %s"), + virFSItemGetName(origitem)); + goto cleanup; + } + + ret = true; + + cleanup: + VIR_FREE(origxml); + xmlFree(newxml); + if (origitem) + virFSItemFree(origitem); + if (newitem) + virFSItemFree(newitem); + if (origfspool) + virFSPoolFree(origfspool); + return ret; +} + +/* + * "item-delete" command + */ +static const vshCmdInfo info_item_delete[] = { + {.name = "help", + .data = N_("delete a item") + }, + {.name = "desc", + .data = N_("Delete a given item.") + }, + {.name = NULL} +}; + +static const vshCmdOptDef opts_item_delete[] = { + VIRSH_COMMON_OPT_ITEM, + VIRSH_COMMON_OPT_FSPOOL_OPTIONAL, + {.name = NULL} +}; + +static bool +cmdItemDelete(vshControl *ctl, const vshCmd *cmd) +{ + virFSItemPtr item; + bool ret = true; + const char *name; + unsigned int flags = 0; + + if (!(item = virshCommandOptItem(ctl, cmd, "item", "fspool", &name))) + return false; + + if (virFSItemDelete(item, flags) == 0) { + vshPrint(ctl, _("Item %s deleted\n"), name); + } else { + vshError(ctl, _("Failed to delete item %s"), name); + ret = false; + } + + virFSItemFree(item); + return ret; +} + +VIR_ENUM_DECL(virshFSItem) +VIR_ENUM_IMPL(virshFSItem, + VIR_FSITEM_LAST, + N_("dir")) + +static const char * +virshItemTypeToString(int type) +{ + const char *str = virshFSItemTypeToString(type); + return str ? _(str) : _("unknown"); +} + + +/* + * "item-info" command + */ +static const vshCmdInfo info_item_info[] = { + {.name = "help", + .data = N_("storage item information") + }, + {.name = "desc", + .data = N_("Returns basic information about the storage item.") + }, + {.name = NULL} +}; + +static const vshCmdOptDef opts_item_info[] = { + VIRSH_COMMON_OPT_ITEM, + VIRSH_COMMON_OPT_FSPOOL_OPTIONAL, + {.name = "bytes", + .type = VSH_OT_BOOL, + .help = N_("sizes are represented in bytes rather than pretty units") + }, + {.name = NULL} +}; + +static bool +cmdItemInfo(vshControl *ctl, const vshCmd *cmd) +{ + virFSItemInfo info; + virFSItemPtr item; + bool bytes = vshCommandOptBool(cmd, "bytes"); + bool ret = true; + + if (!(item = virshCommandOptItem(ctl, cmd, "item", "fspool", NULL))) + return false; + + vshPrint(ctl, "%-15s %s\n", _("Name:"), virFSItemGetName(item)); + + if (virFSItemGetInfo(item, &info) == 0) { + double val; + const char *unit; + + vshPrint(ctl, "%-15s %s\n", _("Type:"), + virshItemTypeToString(info.type)); + + if (bytes) { + vshPrint(ctl, "%-15s %llu %s\n", _("Capacity:"), + info.capacity, _("bytes")); + } else { + val = vshPrettyCapacity(info.capacity, &unit); + vshPrint(ctl, "%-15s %2.2lf %s\n", _("Capacity:"), val, unit); + } + + if (bytes) { + vshPrint(ctl, "%-15s %llu %s\n", _("Allocation:"), + info.allocation, _("bytes")); + } else { + val = vshPrettyCapacity(info.allocation, &unit); + vshPrint(ctl, "%-15s %2.2lf %s\n", _("Allocation:"), val, unit); + } + } else { + ret = false; + } + + virFSItemFree(item); + return ret; +} + +/* + * "item-dumpxml" command + */ +static const vshCmdInfo info_item_dumpxml[] = { + {.name = "help", + .data = N_("item information in XML") + }, + {.name = "desc", + .data = N_("Output the item information as an XML dump to stdout.") + }, + {.name = NULL} +}; + +static const vshCmdOptDef opts_item_dumpxml[] = { + VIRSH_COMMON_OPT_ITEM, + VIRSH_COMMON_OPT_FSPOOL_OPTIONAL, + {.name = NULL} +}; + +static bool +cmdItemDumpXML(vshControl *ctl, const vshCmd *cmd) +{ + virFSItemPtr item; + bool ret = true; + char *dump; + + if (!(item = virshCommandOptItem(ctl, cmd, "item", "fspool", NULL))) + return false; + + dump = virFSItemGetXMLDesc(item, 0); + if (dump != NULL) { + vshPrint(ctl, "%s", dump); + VIR_FREE(dump); + } else { + ret = false; + } + + virFSItemFree(item); + return ret; +} + +static int +virshFSItemSorter(const void *a, const void *b) +{ + virFSItemPtr *va = (virFSItemPtr *) a; + virFSItemPtr *vb = (virFSItemPtr *) b; + + if (*va && !*vb) + return -1; + + if (!*va) + return *vb != NULL; + + return vshStrcasecmp(virFSItemGetName(*va), + virFSItemGetName(*vb)); +} + +struct virshFSItemList { + virFSItemPtr *items; + size_t nitems; +}; +typedef struct virshFSItemList *virshFSItemListPtr; + +static void +virshFSItemListFree(virshFSItemListPtr list) +{ + size_t i; + + if (list && list->items) { + for (i = 0; i < list->nitems; i++) { + if (list->items[i]) + virFSItemFree(list->items[i]); + } + VIR_FREE(list->items); + } + VIR_FREE(list); +} + +static virshFSItemListPtr +virshFSItemListCollect(vshControl *ctl, + virFSPoolPtr fspool, + unsigned int flags) +{ + virshFSItemListPtr list = vshMalloc(ctl, sizeof(*list)); + size_t i; + char **names = NULL; + virFSItemPtr item = NULL; + bool success = false; + size_t deleted = 0; + int nitems = 0; + int ret = -1; + + /* try the list with flags support (0.10.2 and later) */ + if ((ret = virFSPoolListAllItems(fspool, + &list->items, + flags)) >= 0) { + list->nitems = ret; + goto finished; + } + + /* check if the command is actually supported */ + if (last_error && last_error->code == VIR_ERR_NO_SUPPORT) + goto fallback; + + /* there was an error during the call */ + vshError(ctl, "%s", _("Failed to list items")); + goto cleanup; + + fallback: + /* fall back to old method (0.10.1 and older) */ + vshResetLibvirtError(); + + /* Determine the number of items in the fspool */ + if ((nitems = virFSPoolNumOfItems(fspool)) < 0) { + vshError(ctl, "%s", _("Failed to list storage items")); + goto cleanup; + } + + if (nitems == 0) { + success = true; + return list; + } + + /* Retrieve the list of item names in the fspool */ + names = vshCalloc(ctl, nitems, sizeof(*names)); + if ((nitems = virFSPoolListItems(fspool, names, nitems)) < 0) { + vshError(ctl, "%s", _("Failed to list storage items")); + goto cleanup; + } + + list->items = vshMalloc(ctl, sizeof(virFSItemPtr) * (nitems)); + list->nitems = 0; + + /* get the items */ + for (i = 0; i < nitems; i++) { + if (!(item = virFSItemLookupByName(fspool, names[i]))) + continue; + list->items[list->nitems++] = item; + } + + /* truncate the list for not found items */ + deleted = nitems - list->nitems; + + finished: + /* sort the list */ + if (list->items && list->nitems) + qsort(list->items, list->nitems, sizeof(*list->items), virshFSItemSorter); + + if (deleted) + VIR_SHRINK_N(list->items, list->nitems, deleted); + + success = true; + + cleanup: + if (nitems > 0) + for (i = 0; i < nitems; i++) + VIR_FREE(names[i]); + VIR_FREE(names); + + if (!success) { + virshFSItemListFree(list); + list = NULL; + } + + return list; +} + +/* + * "item-list" command + */ +static const vshCmdInfo info_item_list[] = { + {.name = "help", + .data = N_("list items") + }, + {.name = "desc", + .data = N_("Returns list of items by fspool.") + }, + {.name = NULL} +}; + +static const vshCmdOptDef opts_item_list[] = { + VIRSH_COMMON_OPT_FSPOOL_FULL, + {.name = "details", + .type = VSH_OT_BOOL, + .help = N_("display extended details for items") + }, + {.name = NULL} +}; + +static bool +cmdItemList(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED) +{ + virFSItemInfo itemInfo; + virFSPoolPtr fspool; + char *outputStr = NULL; + const char *unit; + double val; + bool details = vshCommandOptBool(cmd, "details"); + size_t i; + bool ret = false; + int stringLength = 0; + size_t allocStrLength = 0, capStrLength = 0; + size_t nameStrLength = 0, pathStrLength = 0; + size_t typeStrLength = 0; + struct itemInfoText { + char *allocation; + char *capacity; + char *path; + char *type; + }; + struct itemInfoText *itemInfoTexts = NULL; + virshFSItemListPtr list = NULL; + + /* Look up the fspool information given to us by the user */ + if (!(fspool = virshCommandOptFSPool(ctl, cmd, "fspool", NULL))) + return false; + + if (!(list = virshFSItemListCollect(ctl, fspool, 0))) + goto cleanup; + + if (list->nitems > 0) + itemInfoTexts = vshCalloc(ctl, list->nitems, sizeof(*itemInfoTexts)); + + /* Collect the rest of the item information for display */ + for (i = 0; i < list->nitems; i++) { + /* Retrieve item info */ + virFSItemPtr item = list->items[i]; + + /* Retrieve the item path */ + if ((itemInfoTexts[i].path = virFSItemGetPath(item)) == NULL) { + /* Something went wrong retrieving a item path, cope with it */ + itemInfoTexts[i].path = vshStrdup(ctl, _("unknown")); + } + + /* If requested, retrieve item type and sizing information */ + if (details) { + if (virFSItemGetInfo(item, &itemInfo) != 0) { + /* Something went wrong retrieving item info, cope with it */ + itemInfoTexts[i].allocation = vshStrdup(ctl, _("unknown")); + itemInfoTexts[i].capacity = vshStrdup(ctl, _("unknown")); + itemInfoTexts[i].type = vshStrdup(ctl, _("unknown")); + } else { + /* Convert the returned item info into output strings */ + + /* Item type */ + itemInfoTexts[i].type = vshStrdup(ctl, + virshItemTypeToString(itemInfo.type)); + + val = vshPrettyCapacity(itemInfo.capacity, &unit); + if (virAsprintf(&itemInfoTexts[i].capacity, + "%.2lf %s", val, unit) < 0) + goto cleanup; + + val = vshPrettyCapacity(itemInfo.allocation, &unit); + if (virAsprintf(&itemInfoTexts[i].allocation, + "%.2lf %s", val, unit) < 0) + goto cleanup; + } + + /* Remember the largest length for each output string. + * This lets us displaying header and item information rows + * using a single, properly sized, printf style output string. + */ + + /* Keep the length of name string if longest so far */ + stringLength = strlen(virFSItemGetName(list->items[i])); + if (stringLength > nameStrLength) + nameStrLength = stringLength; + + /* Keep the length of path string if longest so far */ + stringLength = strlen(itemInfoTexts[i].path); + if (stringLength > pathStrLength) + pathStrLength = stringLength; + + /* Keep the length of type string if longest so far */ + stringLength = strlen(itemInfoTexts[i].type); + if (stringLength > typeStrLength) + typeStrLength = stringLength; + + /* Keep the length of capacity string if longest so far */ + stringLength = strlen(itemInfoTexts[i].capacity); + if (stringLength > capStrLength) + capStrLength = stringLength; + + /* Keep the length of allocation string if longest so far */ + stringLength = strlen(itemInfoTexts[i].allocation); + if (stringLength > allocStrLength) + allocStrLength = stringLength; + } + } + + /* If the --details option wasn't selected, we output the item + * info using the fixed string format from previous versions to + * maintain backward compatibility. + */ + + /* Output basic info then return if --details option not selected */ + if (!details) { + /* The old output format */ + vshPrintExtra(ctl, " %-20s %-40s\n", _("Name"), _("Path")); + vshPrintExtra(ctl, "---------------------------------------" + "---------------------------------------\n"); + for (i = 0; i < list->nitems; i++) { + vshPrint(ctl, " %-20s %-40s\n", virFSItemGetName(list->items[i]), + itemInfoTexts[i].path); + } + + /* Cleanup and return */ + ret = true; + goto cleanup; + } + + /* We only get here if the --details option was selected. */ + + /* Use the length of name header string if it's longest */ + stringLength = strlen(_("Name")); + if (stringLength > nameStrLength) + nameStrLength = stringLength; + + /* Use the length of path header string if it's longest */ + stringLength = strlen(_("Path")); + if (stringLength > pathStrLength) + pathStrLength = stringLength; + + /* Use the length of type header string if it's longest */ + stringLength = strlen(_("Type")); + if (stringLength > typeStrLength) + typeStrLength = stringLength; + + /* Use the length of capacity header string if it's longest */ + stringLength = strlen(_("Capacity")); + if (stringLength > capStrLength) + capStrLength = stringLength; + + /* Use the length of allocation header string if it's longest */ + stringLength = strlen(_("Allocation")); + if (stringLength > allocStrLength) + allocStrLength = stringLength; + + /* Display the string lengths for debugging */ + vshDebug(ctl, VSH_ERR_DEBUG, + "Longest name string = %zu chars\n", nameStrLength); + vshDebug(ctl, VSH_ERR_DEBUG, + "Longest path string = %zu chars\n", pathStrLength); + vshDebug(ctl, VSH_ERR_DEBUG, + "Longest type string = %zu chars\n", typeStrLength); + vshDebug(ctl, VSH_ERR_DEBUG, + "Longest capacity string = %zu chars\n", capStrLength); + vshDebug(ctl, VSH_ERR_DEBUG, + "Longest allocation string = %zu chars\n", allocStrLength); + + if (virAsprintf(&outputStr, + " %%-%lus %%-%lus %%-%lus %%%lus %%%lus\n", + (unsigned long) nameStrLength, + (unsigned long) pathStrLength, + (unsigned long) typeStrLength, + (unsigned long) capStrLength, + (unsigned long) allocStrLength) < 0) + goto cleanup; + + /* Display the header */ + vshPrint(ctl, outputStr, _("Name"), _("Path"), _("Type"), + ("Capacity"), _("Allocation")); + for (i = nameStrLength + pathStrLength + typeStrLength + + capStrLength + allocStrLength + + 10; i > 0; i--) + vshPrintExtra(ctl, "-"); + vshPrintExtra(ctl, "\n"); + + /* Display the item info rows */ + for (i = 0; i < list->nitems; i++) { + vshPrint(ctl, outputStr, + virFSItemGetName(list->items[i]), + itemInfoTexts[i].path, + itemInfoTexts[i].type, + itemInfoTexts[i].capacity, + itemInfoTexts[i].allocation); + } + + /* Cleanup and return */ + ret = true; + + cleanup: + + /* Safely free the memory allocated in this function */ + if (list && list->nitems) { + for (i = 0; i < list->nitems; i++) { + /* Cleanup the memory for one item info structure per loop */ + VIR_FREE(itemInfoTexts[i].path); + VIR_FREE(itemInfoTexts[i].type); + VIR_FREE(itemInfoTexts[i].capacity); + VIR_FREE(itemInfoTexts[i].allocation); + } + } + + /* Cleanup remaining memory */ + VIR_FREE(outputStr); + VIR_FREE(itemInfoTexts); + virFSPoolFree(fspool); + virshFSItemListFree(list); + + /* Return the desired value */ + return ret; +} + +/* + * "item-name" command + */ +static const vshCmdInfo info_item_name[] = { + {.name = "help", + .data = N_("returns the item name for a given item key or path") + }, + {.name = "desc", + .data = "" + }, + {.name = NULL} +}; + +static const vshCmdOptDef opts_item_name[] = { + {.name = "item", + .type = VSH_OT_DATA, + .flags = VSH_OFLAG_REQ, + .help = N_("item key or path") + }, + {.name = NULL} +}; + +static bool +cmdItemName(vshControl *ctl, const vshCmd *cmd) +{ + virFSItemPtr item; + + if (!(item = virshCommandOptItemBy(ctl, cmd, "item", NULL, NULL, + VIRSH_BYUUID))) + return false; + + vshPrint(ctl, "%s\n", virFSItemGetName(item)); + virFSItemFree(item); + return true; +} + +/* + * "item-fspool" command + */ +static const vshCmdInfo info_item_fspool[] = { + {.name = "help", + .data = N_("returns the storage fspool for a given item key or path") + }, + {.name = "desc", + .data = "" + }, + {.name = NULL} +}; + +static const vshCmdOptDef opts_item_fspool[] = { + {.name = "item", + .type = VSH_OT_DATA, + .flags = VSH_OFLAG_REQ, + .help = N_("item key or path") + }, + {.name = "uuid", + .type = VSH_OT_BOOL, + .help = N_("return the fspool uuid rather than fspool name") + }, + {.name = NULL} +}; + +static bool +cmdItemPool(vshControl *ctl, const vshCmd *cmd) +{ + virFSPoolPtr fspool; + virFSItemPtr item; + char uuid[VIR_UUID_STRING_BUFLEN]; + + /* Use the supplied string to locate the item */ + if (!(item = virshCommandOptItemBy(ctl, cmd, "item", NULL, NULL, + VIRSH_BYUUID))) { + return false; + } + + /* Look up the parent storage fspool for the item */ + fspool = virFSPoolLookupByItem(item); + if (fspool == NULL) { + vshError(ctl, "%s", _("failed to get parent fspool")); + virFSItemFree(item); + return false; + } + + /* Return the requested details of the parent storage fspool */ + if (vshCommandOptBool(cmd, "uuid")) { + /* Retrieve and return fspool UUID string */ + if (virFSPoolGetUUIDString(fspool, &uuid[0]) == 0) + vshPrint(ctl, "%s\n", uuid); + } else { + /* Return the storage fspool name */ + vshPrint(ctl, "%s\n", virFSPoolGetName(fspool)); + } + + /* Cleanup */ + virFSItemFree(item); + virFSPoolFree(fspool); + return true; +} + +/* + * "item-key" command + */ +static const vshCmdInfo info_item_key[] = { + {.name = "help", + .data = N_("returns the item key for a given item name or path") + }, + {.name = "desc", + .data = "" + }, + {.name = NULL} +}; + +static const vshCmdOptDef opts_item_key[] = { + {.name = "item", + .type = VSH_OT_DATA, + .flags = VSH_OFLAG_REQ, + .help = N_("item name or path") + }, + VIRSH_COMMON_OPT_FSPOOL_OPTIONAL, + {.name = NULL} +}; + +static bool +cmdItemKey(vshControl *ctl, const vshCmd *cmd) +{ + virFSItemPtr item; + + if (!(item = virshCommandOptItem(ctl, cmd, "item", "fspool", NULL))) + return false; + + vshPrint(ctl, "%s\n", virFSItemGetKey(item)); + virFSItemFree(item); + return true; +} + +/* + * "item-path" command + */ +static const vshCmdInfo info_item_path[] = { + {.name = "help", + .data = N_("returns the item path for a given item name or key") + }, + {.name = "desc", + .data = "" + }, + {.name = NULL} +}; + +static const vshCmdOptDef opts_item_path[] = { + {.name = "item", + .type = VSH_OT_DATA, + .flags = VSH_OFLAG_REQ, + .help = N_("item name or key") + }, + VIRSH_COMMON_OPT_FSPOOL_OPTIONAL, + {.name = NULL} +}; + +static bool +cmdItemPath(vshControl *ctl, const vshCmd *cmd) +{ + virFSItemPtr item; + char * FSItemPath; + + if (!(item = virshCommandOptItem(ctl, cmd, "item", "fspool", NULL))) + return false; + + if ((FSItemPath = virFSItemGetPath(item)) == NULL) { + virFSItemFree(item); + return false; + } + + vshPrint(ctl, "%s\n", FSItemPath); + VIR_FREE(FSItemPath); + virFSItemFree(item); + return true; +} + +const vshCmdDef fsItemCmds[] = { + {.name = "item-clone", + .handler = cmdItemClone, + .opts = opts_item_clone, + .info = info_item_clone, + .flags = 0 + }, + {.name = "item-create-as", + .handler = cmdItemCreateAs, + .opts = opts_item_create_as, + .info = info_item_create_as, + .flags = 0 + }, + {.name = "item-create", + .handler = cmdItemCreate, + .opts = opts_item_create, + .info = info_item_create, + .flags = 0 + }, + {.name = "item-create-from", + .handler = cmdItemCreateFrom, + .opts = opts_item_create_from, + .info = info_item_create_from, + .flags = 0 + }, + {.name = "item-delete", + .handler = cmdItemDelete, + .opts = opts_item_delete, + .info = info_item_delete, + .flags = 0 + }, + {.name = "item-dumpxml", + .handler = cmdItemDumpXML, + .opts = opts_item_dumpxml, + .info = info_item_dumpxml, + .flags = 0 + }, + {.name = "item-info", + .handler = cmdItemInfo, + .opts = opts_item_info, + .info = info_item_info, + .flags = 0 + }, + {.name = "item-key", + .handler = cmdItemKey, + .opts = opts_item_key, + .info = info_item_key, + .flags = 0 + }, + {.name = "item-list", + .handler = cmdItemList, + .opts = opts_item_list, + .info = info_item_list, + .flags = 0 + }, + {.name = "item-name", + .handler = cmdItemName, + .opts = opts_item_name, + .info = info_item_name, + .flags = 0 + }, + {.name = "item-path", + .handler = cmdItemPath, + .opts = opts_item_path, + .info = info_item_path, + .flags = 0 + }, + {.name = "item-fspool", + .handler = cmdItemPool, + .opts = opts_item_fspool, + .info = info_item_fspool, + .flags = 0 + }, + {.name = NULL} +}; diff --git a/tools/virsh-fsitem.h b/tools/virsh-fsitem.h new file mode 100644 index 0000000..0607ac5 --- /dev/null +++ b/tools/virsh-fsitem.h @@ -0,0 +1,39 @@ +/* + * virsh-fsitem.h: Commands to manage fsitems + * + * Copyright (C) 2016 Parallels IP Holdings GmbH + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + * + */ + +#ifndef VIRSH_FSITEM_H +# define VIRSH_FSITEM_H + +# include "virsh.h" + +virFSItemPtr virshCommandOptItemBy(vshControl *ctl, const vshCmd *cmd, + const char *optname, + const char *fspooloptname, + const char **name, unsigned int flags); + +/* default is lookup by Name and UUID */ +# define virshCommandOptItem(_ctl, _cmd, _optname, _fspooloptname, _name) \ + virshCommandOptItemBy(_ctl, _cmd, _optname, _fspooloptname, _name, \ + VIRSH_BYUUID | VIRSH_BYNAME) + +extern const vshCmdDef fsItemCmds[]; + +#endif /* VIRSH_FSITEM_H */ diff --git a/tools/virsh-fspool.c b/tools/virsh-fspool.c new file mode 100644 index 0000000..cc0e4c9 --- /dev/null +++ b/tools/virsh-fspool.c @@ -0,0 +1,1586 @@ +/* + * virsh-fspool.c: Commands to manage fspool + * + * Copyright (C) 2016 Parallels IP Holdings GmbH + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + * + * Olga Krishtal <okrishtal@virtuozzo.com> + * + */ + +#include <config.h> +#include "virsh-fspool.h" + +#include "internal.h" +#include "virbuffer.h" +#include "viralloc.h" +#include "virfile.h" +#include "conf/fs_conf.h" +#include "virstring.h" + +#define VIRSH_COMMON_OPT_FSPOOL_FULL \ + VIRSH_COMMON_OPT_FSPOOL(N_("fspool name or uuid")) \ + +#define VIRSH_COMMON_OPT_FSPOOL_BUILD \ + {.name = "build", \ + .type = VSH_OT_BOOL, \ + .flags = 0, \ + .help = N_("build the fspool as normal") \ + } \ + +#define VIRSH_COMMON_OPT_FSPOOL_NO_OVERWRITE \ + {.name = "no-overwrite", \ + .type = VSH_OT_BOOL, \ + .flags = 0, \ + .help = N_("do not overwrite an existing fspool of this type") \ + } \ + +#define VIRSH_COMMON_OPT_FSPOOL_OVERWRITE \ + {.name = "overwrite", \ + .type = VSH_OT_BOOL, \ + .flags = 0, \ + .help = N_("overwrite any existing data") \ + } \ + +#define VIRSH_COMMON_OPT_FSPOOL_X_AS \ + {.name = "name", \ + .type = VSH_OT_DATA, \ + .flags = VSH_OFLAG_REQ, \ + .help = N_("name of the fspool") \ + }, \ + {.name = "type", \ + .type = VSH_OT_DATA, \ + .flags = VSH_OFLAG_REQ, \ + .help = N_("type of the fspool") \ + }, \ + {.name = "print-xml", \ + .type = VSH_OT_BOOL, \ + .help = N_("print XML document, but don't define/create") \ + }, \ + {.name = "source-host", \ + .type = VSH_OT_STRING, \ + .help = N_("source-host for underlying storage") \ + }, \ + {.name = "source-path", \ + .type = VSH_OT_STRING, \ + .help = N_("source path for underlying storage") \ + }, \ + {.name = "source-name", \ + .type = VSH_OT_STRING, \ + .help = N_("source name for underlying storage") \ + }, \ + {.name = "target", \ + .type = VSH_OT_STRING, \ + .help = N_("target for underlying storage") \ + }, \ + {.name = "source-format", \ + .type = VSH_OT_STRING, \ + .help = N_("format for underlying storage") \ + } \ + +virFSPoolPtr +virshCommandOptFSPoolBy(vshControl *ctl, const vshCmd *cmd, const char *optname, + const char **name, unsigned int flags) +{ + virFSPoolPtr fspool = NULL; + const char *n = NULL; + virshControlPtr priv = ctl->privData; + + virCheckFlags(VIRSH_BYUUID | VIRSH_BYNAME, NULL); + + if (vshCommandOptStringReq(ctl, cmd, optname, &n) < 0) + return NULL; + + vshDebug(ctl, VSH_ERR_INFO, "%s: found option <%s>: %s\n", + cmd->def->name, optname, n); + + if (name) + *name = n; + + /* try it by UUID */ + if ((flags & VIRSH_BYUUID) && strlen(n) == VIR_UUID_STRING_BUFLEN-1) { + vshDebug(ctl, VSH_ERR_DEBUG, "%s: <%s> trying as fspool UUID\n", + cmd->def->name, optname); + fspool = virFSPoolLookupByUUIDString(priv->conn, n); + } + /* try it by NAME */ + if (!fspool && (flags & VIRSH_BYNAME)) { + vshDebug(ctl, VSH_ERR_DEBUG, "%s: <%s> trying as fspool NAME\n", + cmd->def->name, optname); + fspool = virFSPoolLookupByName(priv->conn, n); + } + + if (!fspool) + vshError(ctl, _("failed to get fspool '%s'"), n); + + return fspool; +} + +/* + * "fspool-create" command + */ +static const vshCmdInfo info_fspool_create[] = { + {.name = "help", + .data = N_("create a fspool from an XML file") + }, + {.name = "desc", + .data = N_("Create a fspool.") + }, + {.name = NULL} +}; + +static const vshCmdOptDef opts_fspool_create[] = { + VIRSH_COMMON_OPT_FILE(N_("file containing an XML fspool description")), + VIRSH_COMMON_OPT_FSPOOL_BUILD, + VIRSH_COMMON_OPT_FSPOOL_NO_OVERWRITE, + VIRSH_COMMON_OPT_FSPOOL_OVERWRITE, + + {.name = NULL} +}; + +static bool +cmdFSPoolCreate(vshControl *ctl, const vshCmd *cmd) +{ + virFSPoolPtr fspool; + const char *from = NULL; + bool ret = true; + char *buffer; + bool build; + bool overwrite; + bool no_overwrite; + unsigned int flags = 0; + virshControlPtr priv = ctl->privData; + + if (vshCommandOptStringReq(ctl, cmd, "file", &from) < 0) + return false; + + build = vshCommandOptBool(cmd, "build"); + overwrite = vshCommandOptBool(cmd, "overwrite"); + no_overwrite = vshCommandOptBool(cmd, "no-overwrite"); + + VSH_EXCLUSIVE_OPTIONS_EXPR("overwrite", overwrite, + "no-overwrite", no_overwrite); + + if (build) + flags |= VIR_FSPOOL_CREATE_WITH_BUILD; + if (overwrite) + flags |= VIR_FSPOOL_CREATE_WITH_BUILD_OVERWRITE; + if (no_overwrite) + flags |= VIR_FSPOOL_CREATE_WITH_BUILD_NO_OVERWRITE; + + if (virFileReadAll(from, VSH_MAX_XML_FILE, &buffer) < 0) + return false; + + fspool = virFSPoolCreateXML(priv->conn, buffer, flags); + VIR_FREE(buffer); + + if (fspool != NULL) { + vshPrint(ctl, _("FSpool %s created from %s\n"), + virFSPoolGetName(fspool), from); + virFSPoolFree(fspool); + } else { + vshError(ctl, _("Failed to create fspool from %s"), from); + ret = false; + } + return ret; +} + +static const vshCmdOptDef opts_fspool_define_as[] = { + VIRSH_COMMON_OPT_FSPOOL_X_AS, + + {.name = NULL} +}; + +static int +virshBuildFSPoolXML(vshControl *ctl, + const vshCmd *cmd, + const char **retname, + char **xml) +{ + const char *name = NULL, *type = NULL, *srcHost = NULL, *srcPath = NULL, + *srcName = NULL, *srcFormat = NULL, *target = NULL; + virBuffer buf = VIR_BUFFER_INITIALIZER; + + if (vshCommandOptStringReq(ctl, cmd, "name", &name) < 0) + goto cleanup; + if (vshCommandOptStringReq(ctl, cmd, "type", &type) < 0) + goto cleanup; + + if (vshCommandOptStringReq(ctl, cmd, "source-host", &srcHost) < 0 || + vshCommandOptStringReq(ctl, cmd, "source-path", &srcPath) < 0 || + vshCommandOptStringReq(ctl, cmd, "source-name", &srcName) < 0 || + vshCommandOptStringReq(ctl, cmd, "source-format", &srcFormat) < 0 || + vshCommandOptStringReq(ctl, cmd, "target", &target) < 0) + goto cleanup; + + virBufferAsprintf(&buf, "<fspool type='%s'>\n", type); + virBufferAdjustIndent(&buf, 2); + virBufferAsprintf(&buf, "<name>%s</name>\n", name); + if (srcHost || srcPath || srcFormat || srcName) { + virBufferAddLit(&buf, "<source>\n"); + virBufferAdjustIndent(&buf, 2); + + if (srcHost) + virBufferAsprintf(&buf, "<host name='%s'/>\n", srcHost); + if (srcPath) + virBufferAsprintf(&buf, "<dir path='%s'/>\n", srcPath); + if (srcFormat) + virBufferAsprintf(&buf, "<format type='%s'/>\n", srcFormat); + if (srcName) + virBufferAsprintf(&buf, "<name>%s</name>\n", srcName); + + virBufferAdjustIndent(&buf, -2); + virBufferAddLit(&buf, "</source>\n"); + } + if (target) { + virBufferAddLit(&buf, "<target>\n"); + virBufferAdjustIndent(&buf, 2); + virBufferAsprintf(&buf, "<path>%s</path>\n", target); + virBufferAdjustIndent(&buf, -2); + virBufferAddLit(&buf, "</target>\n"); + } + virBufferAdjustIndent(&buf, -2); + virBufferAddLit(&buf, "</fspool>\n"); + + if (virBufferError(&buf)) { + vshPrint(ctl, "%s", _("Failed to allocate XML buffer")); + return false; + } + + *xml = virBufferContentAndReset(&buf); + *retname = name; + return true; + + cleanup: + virBufferFreeAndReset(&buf); + return false; +} + +/* + * "fspool-autostart" command + */ +static const vshCmdInfo info_fspool_autostart[] = { + {.name = "help", + .data = N_("autostart a fspool") + }, + {.name = "desc", + .data = N_("Configure a fspool to be automatically started at boot.") + }, + {.name = NULL} +}; + +static const vshCmdOptDef opts_fspool_autostart[] = { + VIRSH_COMMON_OPT_FSPOOL_FULL, + + {.name = "disable", + .type = VSH_OT_BOOL, + .help = N_("disable autostarting") + }, + {.name = NULL} +}; + +static bool +cmdFSPoolAutostart(vshControl *ctl, const vshCmd *cmd) +{ + virFSPoolPtr fspool; + const char *name; + int autostart; + + if (!(fspool = virshCommandOptFSPool(ctl, cmd, "fspool", &name))) + return false; + + autostart = !vshCommandOptBool(cmd, "disable"); + + if (virFSPoolSetAutostart(fspool, autostart) < 0) { + if (autostart) + vshError(ctl, _("failed to mark fspool %s as autostarted"), name); + else + vshError(ctl, _("failed to unmark fspool %s as autostarted"), name); + virFSPoolFree(fspool); + return false; + } + + if (autostart) + vshPrint(ctl, _("Fspool %s marked as autostarted\n"), name); + else + vshPrint(ctl, _("Fspool %s unmarked as autostarted\n"), name); + + virFSPoolFree(fspool); + return true; +} + +/* + * "fspool-create-as" command + */ +static const vshCmdInfo info_fspool_create_as[] = { + {.name = "help", + .data = N_("create a fspool from a set of args") + }, + {.name = "desc", + .data = N_("Create a fspool.") + }, + {.name = NULL} +}; + +static const vshCmdOptDef opts_fspool_create_as[] = { + VIRSH_COMMON_OPT_FSPOOL_X_AS, + VIRSH_COMMON_OPT_FSPOOL_BUILD, + VIRSH_COMMON_OPT_FSPOOL_NO_OVERWRITE, + VIRSH_COMMON_OPT_FSPOOL_OVERWRITE, + + {.name = NULL} +}; + +static bool +cmdFSPoolCreateAs(vshControl *ctl, const vshCmd *cmd) +{ + virFSPoolPtr fspool; + const char *name; + char *xml; + bool printXML = vshCommandOptBool(cmd, "print-xml"); + bool build; + bool overwrite; + bool no_overwrite; + unsigned int flags = 0; + virshControlPtr priv = ctl->privData; + + build = vshCommandOptBool(cmd, "build"); + overwrite = vshCommandOptBool(cmd, "overwrite"); + no_overwrite = vshCommandOptBool(cmd, "no-overwrite"); + + VSH_EXCLUSIVE_OPTIONS_EXPR("overwrite", overwrite, + "no-overwrite", no_overwrite); + + if (build) + flags |= VIR_FSPOOL_CREATE_WITH_BUILD; + if (overwrite) + flags |= VIR_FSPOOL_CREATE_WITH_BUILD_OVERWRITE; + if (no_overwrite) + flags |= VIR_FSPOOL_CREATE_WITH_BUILD_NO_OVERWRITE; + + if (!virshBuildFSPoolXML(ctl, cmd, &name, &xml)) + return false; + + if (printXML) { + vshPrint(ctl, "%s", xml); + VIR_FREE(xml); + } else { + fspool = virFSPoolCreateXML(priv->conn, xml, flags); + VIR_FREE(xml); + + if (fspool != NULL) { + vshPrint(ctl, _("FSool %s created\n"), name); + virFSPoolFree(fspool); + } else { + vshError(ctl, _("Failed to create fspool %s"), name); + return false; + } + } + return true; +} + +/* + * "fspool-define" command + */ +static const vshCmdInfo info_fspool_define[] = { + {.name = "help", + .data = N_("define an inactive persistent fspool or modify " + "an existing persistent one from an XML file") + }, + {.name = "desc", + .data = N_("Define or modify a persistent fspool.") + }, + {.name = NULL} +}; + +static const vshCmdOptDef opts_fspool_define[] = { + VIRSH_COMMON_OPT_FILE(N_("file containing an XML fspool description")), + + {.name = NULL} +}; + +static bool +cmdFSPoolDefine(vshControl *ctl, const vshCmd *cmd) +{ + virFSPoolPtr fspool; + const char *from = NULL; + bool ret = true; + char *buffer; + virshControlPtr priv = ctl->privData; + + if (vshCommandOptStringReq(ctl, cmd, "file", &from) < 0) + return false; + + if (virFileReadAll(from, VSH_MAX_XML_FILE, &buffer) < 0) + return false; + + fspool = virFSPoolDefineXML(priv->conn, buffer, 0); + VIR_FREE(buffer); + + if (fspool != NULL) { + vshPrint(ctl, _("FSool %s defined from %s\n"), + virFSPoolGetName(fspool), from); + virFSPoolFree(fspool); + } else { + vshError(ctl, _("Failed to define fspool from %s"), from); + ret = false; + } + return ret; +} + +/* + * "fspool-define-as" command + */ +static const vshCmdInfo info_fspool_define_as[] = { + {.name = "help", + .data = N_("define a fspool from a set of args") + }, + {.name = "desc", + .data = N_("Define a fspool.") + }, + {.name = NULL} +}; + +static bool +cmdFSPoolDefineAs(vshControl *ctl, const vshCmd *cmd) +{ + virFSPoolPtr fspool; + const char *name; + char *xml; + bool printXML = vshCommandOptBool(cmd, "print-xml"); + virshControlPtr priv = ctl->privData; + + if (!virshBuildFSPoolXML(ctl, cmd, &name, &xml)) + return false; + + if (printXML) { + vshPrint(ctl, "%s", xml); + VIR_FREE(xml); + } else { + fspool = virFSPoolDefineXML(priv->conn, xml, 0); + VIR_FREE(xml); + + if (fspool != NULL) { + vshPrint(ctl, _("FSpool %s defined\n"), name); + virFSPoolFree(fspool); + } else { + vshError(ctl, _("Failed to define fspool %s"), name); + return false; + } + } + return true; +} + +/* + * "fspool-build" command + */ +static const vshCmdInfo info_fspool_build[] = { + {.name = "help", + .data = N_("build a fspool") + }, + {.name = "desc", + .data = N_("Build a given fspool.") + }, + {.name = NULL} +}; + +static const vshCmdOptDef opts_fspool_build[] = { + VIRSH_COMMON_OPT_FSPOOL_FULL, + VIRSH_COMMON_OPT_FSPOOL_NO_OVERWRITE, + VIRSH_COMMON_OPT_FSPOOL_OVERWRITE, + + {.name = NULL} +}; + +static bool +cmdFSPoolBuild(vshControl *ctl, const vshCmd *cmd) +{ + virFSPoolPtr fspool; + bool ret = true; + const char *name; + unsigned int flags = 0; + + if (!(fspool = virshCommandOptFSPool(ctl, cmd, "fspool", &name))) + return false; + + if (vshCommandOptBool(cmd, "no-overwrite")) + flags |= VIR_FSPOOL_BUILD_NO_OVERWRITE; + + if (vshCommandOptBool(cmd, "overwrite")) + flags |= VIR_FSPOOL_BUILD_OVERWRITE; + + if (virFSPoolBuild(fspool, flags) == 0) { + vshPrint(ctl, _("FSpool %s built\n"), name); + } else { + vshError(ctl, _("Failed to build fspool %s"), name); + ret = false; + } + + virFSPoolFree(fspool); + + return ret; +} + +/* + * "fspool-destroy" command + */ +static const vshCmdInfo info_fspool_destroy[] = { + {.name = "help", + .data = N_("stop a fspool") + }, + {.name = "desc", + .data = N_("Forcefully stop a given fspool.") + }, + {.name = NULL} +}; + +static const vshCmdOptDef opts_fspool_destroy[] = { + VIRSH_COMMON_OPT_FSPOOL_FULL, + + {.name = NULL} +}; + +static bool +cmdFSPoolDestroy(vshControl *ctl, const vshCmd *cmd) +{ + virFSPoolPtr fspool; + bool ret = true; + const char *name; + + if (!(fspool = virshCommandOptFSPool(ctl, cmd, "fspool", &name))) + return false; + + if (virFSPoolDestroy(fspool) == 0) { + vshPrint(ctl, _("FSpool %s destroyed\n"), name); + } else { + vshError(ctl, _("Failed to destroy fspool %s"), name); + ret = false; + } + + virFSPoolFree(fspool); + return ret; +} + +/* + * "fspool-delete" command + */ +static const vshCmdInfo info_fspool_delete[] = { + {.name = "help", + .data = N_("delete a fspool") + }, + {.name = "desc", + .data = N_("Delete a given fspool.") + }, + {.name = NULL} +}; + +static const vshCmdOptDef opts_fspool_delete[] = { + VIRSH_COMMON_OPT_FSPOOL_FULL, + + {.name = NULL} +}; + +static bool +cmdFSPoolDelete(vshControl *ctl, const vshCmd *cmd) +{ + virFSPoolPtr fspool; + bool ret = true; + const char *name; + + if (!(fspool = virshCommandOptFSPool(ctl, cmd, "fspool", &name))) + return false; + + if (virFSPoolDelete(fspool, 0) == 0) { + vshPrint(ctl, _("Pool %s deleted\n"), name); + } else { + vshError(ctl, _("Failed to delete fspool %s"), name); + ret = false; + } + + virFSPoolFree(fspool); + return ret; +} + +/* + * "fspool-refresh" command + */ +static const vshCmdInfo info_fspool_refresh[] = { + {.name = "help", + .data = N_("refresh a fspool") + }, + {.name = "desc", + .data = N_("Refresh a given fspool.") + }, + {.name = NULL} +}; + +static const vshCmdOptDef opts_fspool_refresh[] = { + VIRSH_COMMON_OPT_FSPOOL_FULL, + + {.name = NULL} +}; + +static bool +cmdFSPoolRefresh(vshControl *ctl, const vshCmd *cmd) +{ + virFSPoolPtr fspool; + bool ret = true; + const char *name; + + if (!(fspool = virshCommandOptFSPool(ctl, cmd, "fspool", &name))) + return false; + + if (virFSPoolRefresh(fspool, 0) == 0) { + vshPrint(ctl, _("Pool %s refreshed\n"), name); + } else { + vshError(ctl, _("Failed to refresh fspool %s"), name); + ret = false; + } + virFSPoolFree(fspool); + + return ret; +} + +/* + * "fspool-dumpxml" command + */ +static const vshCmdInfo info_fspool_dumpxml[] = { + {.name = "help", + .data = N_("fspool information in XML") + }, + {.name = "desc", + .data = N_("Output the fspool information as an XML dump to stdout.") + }, + {.name = NULL} +}; + +static const vshCmdOptDef opts_fspool_dumpxml[] = { + VIRSH_COMMON_OPT_FSPOOL_FULL, + + {.name = "inactive", + .type = VSH_OT_BOOL, + .help = N_("show inactive defined XML") + }, + {.name = NULL} +}; + +static bool +cmdFSPoolDumpXML(vshControl *ctl, const vshCmd *cmd) +{ + virFSPoolPtr fspool; + bool ret = true; + bool inactive = vshCommandOptBool(cmd, "inactive"); + unsigned int flags = 0; + char *dump; + + if (inactive) + flags |= VIR_FS_XML_INACTIVE; + + if (!(fspool = virshCommandOptFSPool(ctl, cmd, "fspool", NULL))) + return false; + + dump = virFSPoolGetXMLDesc(fspool, flags); + if (dump != NULL) { + vshPrint(ctl, "%s", dump); + VIR_FREE(dump); + } else { + ret = false; + } + + virFSPoolFree(fspool); + return ret; +} + +static int +virshFSPoolSorter(const void *a, const void *b) +{ + virFSPoolPtr *pa = (virFSPoolPtr *) a; + virFSPoolPtr *pb = (virFSPoolPtr *) b; + + if (*pa && !*pb) + return -1; + + if (!*pa) + return *pb != NULL; + + return vshStrcasecmp(virFSPoolGetName(*pa), + virFSPoolGetName(*pb)); +} + +struct virshFSPoolList { + virFSPoolPtr *fspools; + size_t nfspools; +}; +typedef struct virshFSPoolList *virshFSPoolListPtr; + +static void +virshFSPoolListFree(virshFSPoolListPtr list) +{ + size_t i; + + if (list && list->fspools) { + for (i = 0; i < list->nfspools; i++) { + if (list->fspools[i]) + virFSPoolFree(list->fspools[i]); + } + VIR_FREE(list->fspools); + } + VIR_FREE(list); +} + +static virshFSPoolListPtr +virshFSPoolListCollect(vshControl *ctl, + unsigned int flags) +{ + virshFSPoolListPtr list = vshMalloc(ctl, sizeof(*list)); + int ret; + virshControlPtr priv = ctl->privData; + + /* try the list with flags support (0.10.2 and later) */ + if ((ret = virConnectListAllFSPools(priv->conn, + &list->fspools, + flags)) < 0) { + vshError(ctl, "%s", _("Failed to list fspools")); + return NULL; + } + + list->nfspools = ret; + + /* sort the list */ + if (list->fspools && list->nfspools) + qsort(list->fspools, list->nfspools, + sizeof(*list->fspools), virshFSPoolSorter); + + return list; +} + + +VIR_ENUM_DECL(virshFSPoolState) +VIR_ENUM_IMPL(virshFSPoolState, + VIR_FSPOOL_STATE_LAST, + N_("inactive"), + N_("building"), + N_("running")) + +static const char * +virshFSPoolStateToString(int state) +{ + const char *str = virshFSPoolStateTypeToString(state); + return str ? _(str) : _("unknown"); +} + + +/* + * "fspool-list" command + */ +static const vshCmdInfo info_fspool_list[] = { + {.name = "help", + .data = N_("list fspools") + }, + {.name = "desc", + .data = N_("Returns list of fspools.") + }, + {.name = NULL} +}; + +static const vshCmdOptDef opts_fspool_list[] = { + {.name = "inactive", + .type = VSH_OT_BOOL, + .help = N_("list inactive fspools") + }, + {.name = "all", + .type = VSH_OT_BOOL, + .help = N_("list inactive & active fspools") + }, + {.name = "transient", + .type = VSH_OT_BOOL, + .help = N_("list transient fspools") + }, + {.name = "persistent", + .type = VSH_OT_BOOL, + .help = N_("list persistent fspools") + }, + {.name = "autostart", + .type = VSH_OT_BOOL, + .help = N_("list fspools with autostart enabled") + }, + {.name = "no-autostart", + .type = VSH_OT_BOOL, + .help = N_("list fspools with autostart disabled") + }, + {.name = "type", + .type = VSH_OT_STRING, + .help = N_("only list fspool of specified type(s) (if supported)") + }, + {.name = "details", + .type = VSH_OT_BOOL, + .help = N_("display extended details for fspools") + }, + {.name = NULL} +}; + +static bool +cmdFSPoolList(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED) +{ + virFSPoolInfo info; + size_t i; + bool ret = false; + size_t stringLength = 0, nameStrLength = 0; + size_t autostartStrLength = 0, persistStrLength = 0; + size_t stateStrLength = 0, capStrLength = 0; + size_t allocStrLength = 0, availStrLength = 0; + struct fspoolInfoText { + char *state; + char *autostart; + char *persistent; + char *capacity; + char *allocation; + char *available; + }; + struct fspoolInfoText *fspoolInfoTexts = NULL; + unsigned int flags = VIR_CONNECT_LIST_FSPOOLS_ACTIVE; + virshFSPoolListPtr list = NULL; + const char *type = NULL; + bool details = vshCommandOptBool(cmd, "details"); + bool inactive, all; + char *outputStr = NULL; + + inactive = vshCommandOptBool(cmd, "inactive"); + all = vshCommandOptBool(cmd, "all"); + + if (inactive) + flags = VIR_CONNECT_LIST_FSPOOLS_INACTIVE; + + if (all) + flags = VIR_CONNECT_LIST_FSPOOLS_ACTIVE | + VIR_CONNECT_LIST_FSPOOLS_INACTIVE; + + if (vshCommandOptBool(cmd, "autostart")) + flags |= VIR_CONNECT_LIST_FSPOOLS_AUTOSTART; + + if (vshCommandOptBool(cmd, "no-autostart")) + flags |= VIR_CONNECT_LIST_FSPOOLS_NO_AUTOSTART; + + if (vshCommandOptBool(cmd, "persistent")) + flags |= VIR_CONNECT_LIST_FSPOOLS_PERSISTENT; + + if (vshCommandOptBool(cmd, "transient")) + flags |= VIR_CONNECT_LIST_FSPOOLS_TRANSIENT; + + if (vshCommandOptStringReq(ctl, cmd, "type", &type) < 0) + return false; + + if (type) { + int fspoolType = -1; + char **fspoolTypes = NULL; + int nfspoolTypes = 0; + + if ((nfspoolTypes = vshStringToArray(type, &fspoolTypes)) < 0) + return false; + + for (i = 0; i < nfspoolTypes; i++) { + if ((fspoolType = virFSPoolTypeFromString(fspoolTypes[i])) < 0) { + vshError(ctl, _("Invalid fspool type '%s'"), fspoolTypes[i]); + virStringFreeList(fspoolTypes); + return false; + } + + switch ((virFSPoolType) fspoolType) { + case VIR_FSPOOL_DIR: + flags |= VIR_CONNECT_LIST_FSPOOLS_DIR; + break; + case VIR_FSPOOL_LAST: + break; + } + } + virStringFreeList(fspoolTypes); + } + + if (!(list = virshFSPoolListCollect(ctl, flags))) + goto cleanup; + + fspoolInfoTexts = vshCalloc(ctl, list->nfspools, sizeof(*fspoolInfoTexts)); + + /* Collect the storage fspool information for display */ + for (i = 0; i < list->nfspools; i++) { + int autostart = 0, persistent = 0; + + /* Retrieve the autostart status of the fspool */ + if (virFSPoolGetAutostart(list->fspools[i], &autostart) < 0) + fspoolInfoTexts[i].autostart = vshStrdup(ctl, _("no autostart")); + else + fspoolInfoTexts[i].autostart = vshStrdup(ctl, autostart ? + _("yes") : _("no")); + + /* Retrieve the persistence status of the fspool */ + if (details) { + persistent = virFSPoolIsPersistent(list->fspools[i]); + vshDebug(ctl, VSH_ERR_DEBUG, "Persistent flag value: %d\n", + persistent); + if (persistent < 0) + fspoolInfoTexts[i].persistent = vshStrdup(ctl, _("unknown")); + else + fspoolInfoTexts[i].persistent = vshStrdup(ctl, persistent ? + _("yes") : _("no")); + + /* Keep the length of persistent string if longest so far */ + stringLength = strlen(fspoolInfoTexts[i].persistent); + if (stringLength > persistStrLength) + persistStrLength = stringLength; + } + + /* Collect further extended information about the fspool */ + if (virFSPoolGetInfo(list->fspools[i], &info) != 0) { + /* Something went wrong retrieving fspool info, cope with it */ + vshError(ctl, "%s", _("Could not retrieve fspool information")); + fspoolInfoTexts[i].state = vshStrdup(ctl, _("unknown")); + if (details) { + fspoolInfoTexts[i].capacity = vshStrdup(ctl, _("unknown")); + fspoolInfoTexts[i].allocation = vshStrdup(ctl, _("unknown")); + fspoolInfoTexts[i].available = vshStrdup(ctl, _("unknown")); + } + } else { + /* Decide which state string to display */ + if (details) { + const char *state = virshFSPoolStateToString(info.state); + + fspoolInfoTexts[i].state = vshStrdup(ctl, state); + + /* Create the fspool size related strings */ + if (info.state == VIR_FSPOOL_RUNNING) { + double val; + const char *unit; + + val = vshPrettyCapacity(info.capacity, &unit); + if (virAsprintf(&fspoolInfoTexts[i].capacity, + "%.2lf %s", val, unit) < 0) + goto cleanup; + + val = vshPrettyCapacity(info.allocation, &unit); + if (virAsprintf(&fspoolInfoTexts[i].allocation, + "%.2lf %s", val, unit) < 0) + goto cleanup; + + val = vshPrettyCapacity(info.available, &unit); + if (virAsprintf(&fspoolInfoTexts[i].available, + "%.2lf %s", val, unit) < 0) + goto cleanup; + } else { + /* Capacity related information isn't available */ + fspoolInfoTexts[i].capacity = vshStrdup(ctl, _("-")); + fspoolInfoTexts[i].allocation = vshStrdup(ctl, _("-")); + fspoolInfoTexts[i].available = vshStrdup(ctl, _("-")); + } + + /* Keep the length of capacity string if longest so far */ + stringLength = strlen(fspoolInfoTexts[i].capacity); + if (stringLength > capStrLength) + capStrLength = stringLength; + + /* Keep the length of allocation string if longest so far */ + stringLength = strlen(fspoolInfoTexts[i].allocation); + if (stringLength > allocStrLength) + allocStrLength = stringLength; + + /* Keep the length of available string if longest so far */ + stringLength = strlen(fspoolInfoTexts[i].available); + if (stringLength > availStrLength) + availStrLength = stringLength; + } else { + /* --details option was not specified, only active/inactive + * state strings are used */ + if (virFSPoolIsActive(list->fspools[i])) + fspoolInfoTexts[i].state = vshStrdup(ctl, _("active")); + else + fspoolInfoTexts[i].state = vshStrdup(ctl, _("inactive")); + } + } + + /* Keep the length of name string if longest so far */ + stringLength = strlen(virFSPoolGetName(list->fspools[i])); + if (stringLength > nameStrLength) + nameStrLength = stringLength; + + /* Keep the length of state string if longest so far */ + stringLength = strlen(fspoolInfoTexts[i].state); + if (stringLength > stateStrLength) + stateStrLength = stringLength; + + /* Keep the length of autostart string if longest so far */ + stringLength = strlen(fspoolInfoTexts[i].autostart); + if (stringLength > autostartStrLength) + autostartStrLength = stringLength; + } + + /* If the --details option wasn't selected, we output the fspool + * info using the fixed string format from previous versions to + * maintain backward compatibility. + */ + + /* Output basic info then return if --details option not selected */ + if (!details) { + /* Output old style header */ + vshPrintExtra(ctl, " %-20s %-10s %-10s\n", _("Name"), _("State"), + _("Autostart")); + vshPrintExtra(ctl, "-------------------------------------------\n"); + + /* Output old style fspool info */ + for (i = 0; i < list->nfspools; i++) { + const char *name = virFSPoolGetName(list->fspools[i]); + vshPrint(ctl, " %-20s %-10s %-10s\n", + name, + fspoolInfoTexts[i].state, + fspoolInfoTexts[i].autostart); + } + + /* Cleanup and return */ + ret = true; + goto cleanup; + } + + /* We only get here if the --details option was selected. */ + + /* Use the length of name header string if it's longest */ + stringLength = strlen(_("Name")); + if (stringLength > nameStrLength) + nameStrLength = stringLength; + + /* Use the length of state header string if it's longest */ + stringLength = strlen(_("State")); + if (stringLength > stateStrLength) + stateStrLength = stringLength; + + /* Use the length of autostart header string if it's longest */ + stringLength = strlen(_("Autostart")); + if (stringLength > autostartStrLength) + autostartStrLength = stringLength; + + /* Use the length of persistent header string if it's longest */ + stringLength = strlen(_("Persistent")); + if (stringLength > persistStrLength) + persistStrLength = stringLength; + + /* Use the length of capacity header string if it's longest */ + stringLength = strlen(_("Capacity")); + if (stringLength > capStrLength) + capStrLength = stringLength; + + /* Use the length of allocation header string if it's longest */ + stringLength = strlen(_("Allocation")); + if (stringLength > allocStrLength) + allocStrLength = stringLength; + + /* Use the length of available header string if it's longest */ + stringLength = strlen(_("Available")); + if (stringLength > availStrLength) + availStrLength = stringLength; + + /* Display the string lengths for debugging. */ + vshDebug(ctl, VSH_ERR_DEBUG, "Longest name string = %lu chars\n", + (unsigned long) nameStrLength); + vshDebug(ctl, VSH_ERR_DEBUG, "Longest state string = %lu chars\n", + (unsigned long) stateStrLength); + vshDebug(ctl, VSH_ERR_DEBUG, "Longest autostart string = %lu chars\n", + (unsigned long) autostartStrLength); + vshDebug(ctl, VSH_ERR_DEBUG, "Longest persistent string = %lu chars\n", + (unsigned long) persistStrLength); + vshDebug(ctl, VSH_ERR_DEBUG, "Longest capacity string = %lu chars\n", + (unsigned long) capStrLength); + vshDebug(ctl, VSH_ERR_DEBUG, "Longest allocation string = %lu chars\n", + (unsigned long) allocStrLength); + vshDebug(ctl, VSH_ERR_DEBUG, "Longest available string = %lu chars\n", + (unsigned long) availStrLength); + + /* Create the output template. Each column is sized according to + * the longest string. + */ + if (virAsprintf(&outputStr, + " %%-%lus %%-%lus %%-%lus %%-%lus %%%lus %%%lus %%%lus\n", + (unsigned long) nameStrLength, + (unsigned long) stateStrLength, + (unsigned long) autostartStrLength, + (unsigned long) persistStrLength, + (unsigned long) capStrLength, + (unsigned long) allocStrLength, + (unsigned long) availStrLength) < 0) + goto cleanup; + + /* Display the header */ + vshPrint(ctl, outputStr, _("Name"), _("State"), _("Autostart"), + _("Persistent"), _("Capacity"), _("Allocation"), _("Available")); + for (i = nameStrLength + stateStrLength + autostartStrLength + + persistStrLength + capStrLength + + allocStrLength + availStrLength + + 14; i > 0; i--) + vshPrintExtra(ctl, "-"); + vshPrintExtra(ctl, "\n"); + + /* Display the fspool info rows */ + for (i = 0; i < list->nfspools; i++) { + vshPrint(ctl, outputStr, + virFSPoolGetName(list->fspools[i]), + fspoolInfoTexts[i].state, + fspoolInfoTexts[i].autostart, + fspoolInfoTexts[i].persistent, + fspoolInfoTexts[i].capacity, + fspoolInfoTexts[i].allocation, + fspoolInfoTexts[i].available); + } + + /* Cleanup and return */ + ret = true; + + cleanup: + VIR_FREE(outputStr); + if (list && list->nfspools) { + for (i = 0; i < list->nfspools; i++) { + VIR_FREE(fspoolInfoTexts[i].state); + VIR_FREE(fspoolInfoTexts[i].autostart); + VIR_FREE(fspoolInfoTexts[i].persistent); + VIR_FREE(fspoolInfoTexts[i].capacity); + VIR_FREE(fspoolInfoTexts[i].allocation); + VIR_FREE(fspoolInfoTexts[i].available); + } + } + VIR_FREE(fspoolInfoTexts); + + virshFSPoolListFree(list); + return ret; +} + +/* + * "fspool-info" command + */ +static const vshCmdInfo info_fspool_info[] = { + {.name = "help", + .data = N_("storage fspool information") + }, + {.name = "desc", + .data = N_("Returns basic information about the storage fspool.") + }, + {.name = NULL} +}; + +static const vshCmdOptDef opts_fspool_info[] = { + VIRSH_COMMON_OPT_FSPOOL_FULL, + + {.name = NULL} +}; + +static bool +cmdFSPoolInfo(vshControl *ctl, const vshCmd *cmd) +{ + virFSPoolInfo info; + virFSPoolPtr fspool; + int autostart = 0; + int persistent = 0; + bool ret = true; + char uuid[VIR_UUID_STRING_BUFLEN]; + + if (!(fspool = virshCommandOptFSPool(ctl, cmd, "fspool", NULL))) + return false; + + vshPrint(ctl, "%-15s %s\n", _("Name:"), virFSPoolGetName(fspool)); + + if (virFSPoolGetUUIDString(fspool, &uuid[0]) == 0) + vshPrint(ctl, "%-15s %s\n", _("UUID:"), uuid); + + if (virFSPoolGetInfo(fspool, &info) == 0) { + double val; + const char *unit; + vshPrint(ctl, "%-15s %s\n", _("State:"), + virshFSPoolStateToString(info.state)); + + /* Check and display whether the fspool is persistent or not */ + persistent = virFSPoolIsPersistent(fspool); + vshDebug(ctl, VSH_ERR_DEBUG, "Pool persistent flag value: %d\n", + persistent); + if (persistent < 0) + vshPrint(ctl, "%-15s %s\n", _("Persistent:"), _("unknown")); + else + vshPrint(ctl, "%-15s %s\n", _("Persistent:"), persistent ? _("yes") : _("no")); + + /* Check and display whether the fspool is autostarted or not */ + if (virFSPoolGetAutostart(fspool, &autostart) < 0) + vshPrint(ctl, "%-15s %s\n", _("Autostart:"), _("no autostart")); + else + vshPrint(ctl, "%-15s %s\n", _("Autostart:"), autostart ? _("yes") : _("no")); + + if (info.state == VIR_FSPOOL_RUNNING) { + val = vshPrettyCapacity(info.capacity, &unit); + vshPrint(ctl, "%-15s %2.2lf %s\n", _("Capacity:"), val, unit); + + val = vshPrettyCapacity(info.allocation, &unit); + vshPrint(ctl, "%-15s %2.2lf %s\n", _("Allocation:"), val, unit); + + val = vshPrettyCapacity(info.available, &unit); + vshPrint(ctl, "%-15s %2.2lf %s\n", _("Available:"), val, unit); + } + } else { + ret = false; + } + + virFSPoolFree(fspool); + return ret; +} + +/* + * "fspool-name" command + */ +static const vshCmdInfo info_fspool_name[] = { + {.name = "help", + .data = N_("convert a fspool UUID to fspool name") + }, + {.name = "desc", + .data = "" + }, + {.name = NULL} +}; + +static const vshCmdOptDef opts_fspool_name[] = { + VIRSH_COMMON_OPT_FSPOOL_FULL, + + {.name = NULL} +}; + +static bool +cmdFSPoolName(vshControl *ctl, const vshCmd *cmd) +{ + virFSPoolPtr fspool; + + if (!(fspool = virshCommandOptFSPoolBy(ctl, cmd, "fspool", NULL, VIRSH_BYUUID))) + return false; + + vshPrint(ctl, "%s\n", virFSPoolGetName(fspool)); + virFSPoolFree(fspool); + return true; +} + +/* + * "fspool-start" command + */ +static const vshCmdInfo info_fspool_start[] = { + {.name = "help", + .data = N_("start a (previously defined) inactive fspool") + }, + {.name = "desc", + .data = N_("Start a fspool.") + }, + {.name = NULL} +}; + +static const vshCmdOptDef opts_fspool_start[] = { + VIRSH_COMMON_OPT_FSPOOL_FULL, + VIRSH_COMMON_OPT_FSPOOL_BUILD, + VIRSH_COMMON_OPT_FSPOOL_NO_OVERWRITE, + VIRSH_COMMON_OPT_FSPOOL_OVERWRITE, + + {.name = NULL} +}; + +static bool +cmdFSPoolStart(vshControl *ctl, const vshCmd *cmd) +{ + virFSPoolPtr fspool; + bool ret = true; + const char *name = NULL; + bool build; + bool overwrite; + bool no_overwrite; + unsigned int flags = 0; + + if (!(fspool = virshCommandOptFSPool(ctl, cmd, "fspool", &name))) + return false; + + build = vshCommandOptBool(cmd, "build"); + overwrite = vshCommandOptBool(cmd, "overwrite"); + no_overwrite = vshCommandOptBool(cmd, "no-overwrite"); + + VSH_EXCLUSIVE_OPTIONS_EXPR("overwrite", overwrite, + "no-overwrite", no_overwrite); + + if (build) + flags |= VIR_FSPOOL_CREATE_WITH_BUILD; + if (overwrite) + flags |= VIR_FSPOOL_CREATE_WITH_BUILD_OVERWRITE; + if (no_overwrite) + flags |= VIR_FSPOOL_CREATE_WITH_BUILD_NO_OVERWRITE; + + if (virFSPoolCreate(fspool, flags) == 0) { + vshPrint(ctl, _("FSpool %s started\n"), name); + } else { + vshError(ctl, _("Failed to start fspool %s"), name); + ret = false; + } + + virFSPoolFree(fspool); + return ret; +} + +/* + * "fspool-undefine" command + */ +static const vshCmdInfo info_fspool_undefine[] = { + {.name = "help", + .data = N_("undefine an inactive fspool") + }, + {.name = "desc", + .data = N_("Undefine the configuration for an inactive fspool.") + }, + {.name = NULL} +}; + +static const vshCmdOptDef opts_fspool_undefine[] = { + VIRSH_COMMON_OPT_FSPOOL_FULL, + + {.name = NULL} +}; + +static bool +cmdFSPoolUndefine(vshControl *ctl, const vshCmd *cmd) +{ + virFSPoolPtr fspool; + bool ret = true; + const char *name; + + if (!(fspool = virshCommandOptFSPool(ctl, cmd, "fspool", &name))) + return false; + + if (virFSPoolUndefine(fspool) == 0) { + vshPrint(ctl, _("Pool %s has been undefined\n"), name); + } else { + vshError(ctl, _("Failed to undefine fspool %s"), name); + ret = false; + } + + virFSPoolFree(fspool); + return ret; +} + +/* + * "fspool-uuid" command + */ +static const vshCmdInfo info_fspool_uuid[] = { + {.name = "help", + .data = N_("convert a fspool name to fspool UUID") + }, + {.name = "desc", + .data = "" + }, + {.name = NULL} +}; + +static const vshCmdOptDef opts_fspool_uuid[] = { + VIRSH_COMMON_OPT_FSPOOL_FULL, + + {.name = NULL} +}; + +static bool +cmdFSPoolUuid(vshControl *ctl, const vshCmd *cmd) +{ + virFSPoolPtr fspool; + char uuid[VIR_UUID_STRING_BUFLEN]; + + if (!(fspool = virshCommandOptFSPoolBy(ctl, cmd, "fspool", NULL, VIRSH_BYNAME))) + return false; + + if (virFSPoolGetUUIDString(fspool, uuid) != -1) + vshPrint(ctl, "%s\n", uuid); + else + vshError(ctl, "%s", _("failed to get fspool UUID")); + + virFSPoolFree(fspool); + return true; +} + +/* + * "fspool-edit" command + */ +static const vshCmdInfo info_fspool_edit[] = { + {.name = "help", + .data = N_("edit XML configuration for a fspool") + }, + {.name = "desc", + .data = N_("Edit the XML configuration for a fspool.") + }, + {.name = NULL} +}; + +static const vshCmdOptDef opts_fspool_edit[] = { + VIRSH_COMMON_OPT_FSPOOL_FULL, + + {.name = NULL} +}; + +static bool +cmdFSPoolEdit(vshControl *ctl, const vshCmd *cmd) +{ + bool ret = false; + virFSPoolPtr fspool = NULL; + virFSPoolPtr fspool_edited = NULL; + unsigned int flags = VIR_FS_XML_INACTIVE; + char *tmp_desc = NULL; + virshControlPtr priv = ctl->privData; + + fspool = virshCommandOptFSPool(ctl, cmd, "fspool", NULL); + if (fspool == NULL) + goto cleanup; + + /* Some old daemons don't support _INACTIVE flag */ + if (!(tmp_desc = virFSPoolGetXMLDesc(fspool, flags))) { + if (last_error->code == VIR_ERR_INVALID_ARG) { + flags &= ~VIR_FS_XML_INACTIVE; + vshResetLibvirtError(); + } else { + goto cleanup; + } + } else { + VIR_FREE(tmp_desc); + } + +#define EDIT_GET_XML virFSPoolGetXMLDesc(fspool, flags) +#define EDIT_NOT_CHANGED \ + do { \ + vshPrint(ctl, _("Pool %s XML configuration not changed.\n"), \ + virFSPoolGetName(fspool)); \ + ret = true; \ + goto edit_cleanup; \ + } while (0) +#define EDIT_DEFINE \ + (fspool_edited = virFSPoolDefineXML(priv->conn, doc_edited, 0)) +#include "virsh-edit.c" + + vshPrint(ctl, _("Pool %s XML configuration edited.\n"), + virFSPoolGetName(fspool_edited)); + + ret = true; + + cleanup: + if (fspool) + virFSPoolFree(fspool); + if (fspool_edited) + virFSPoolFree(fspool_edited); + + return ret; +} + +const vshCmdDef fsPoolCmds[] = { + {.name = "fspool-autostart", + .handler = cmdFSPoolAutostart, + .opts = opts_fspool_autostart, + .info = info_fspool_autostart, + .flags = 0 + }, + {.name = "fspool-build", + .handler = cmdFSPoolBuild, + .opts = opts_fspool_build, + .info = info_fspool_build, + .flags = 0 + }, + {.name = "fspool-create-as", + .handler = cmdFSPoolCreateAs, + .opts = opts_fspool_create_as, + .info = info_fspool_create_as, + .flags = 0 + }, + {.name = "fspool-create", + .handler = cmdFSPoolCreate, + .opts = opts_fspool_create, + .info = info_fspool_create, + .flags = 0 + }, + {.name = "fspool-define-as", + .handler = cmdFSPoolDefineAs, + .opts = opts_fspool_define_as, + .info = info_fspool_define_as, + .flags = 0 + }, + {.name = "fspool-define", + .handler = cmdFSPoolDefine, + .opts = opts_fspool_define, + .info = info_fspool_define, + .flags = 0 + }, + {.name = "fspool-delete", + .handler = cmdFSPoolDelete, + .opts = opts_fspool_delete, + .info = info_fspool_delete, + .flags = 0 + }, + {.name = "fspool-destroy", + .handler = cmdFSPoolDestroy, + .opts = opts_fspool_destroy, + .info = info_fspool_destroy, + .flags = 0 + }, + {.name = "fspool-dumpxml", + .handler = cmdFSPoolDumpXML, + .opts = opts_fspool_dumpxml, + .info = info_fspool_dumpxml, + .flags = 0 + }, + {.name = "fspool-edit", + .handler = cmdFSPoolEdit, + .opts = opts_fspool_edit, + .info = info_fspool_edit, + .flags = 0 + }, + {.name = "fspool-info", + .handler = cmdFSPoolInfo, + .opts = opts_fspool_info, + .info = info_fspool_info, + .flags = 0 + }, + {.name = "fspool-list", + .handler = cmdFSPoolList, + .opts = opts_fspool_list, + .info = info_fspool_list, + .flags = 0 + }, + {.name = "fspool-name", + .handler = cmdFSPoolName, + .opts = opts_fspool_name, + .info = info_fspool_name, + .flags = 0 + }, + {.name = "fspool-refresh", + .handler = cmdFSPoolRefresh, + .opts = opts_fspool_refresh, + .info = info_fspool_refresh, + .flags = 0 + }, + {.name = "fspool-undefine", + .handler = cmdFSPoolUndefine, + .opts = opts_fspool_undefine, + .info = info_fspool_undefine, + .flags = 0 + }, + {.name = "fspool-uuid", + .handler = cmdFSPoolUuid, + .opts = opts_fspool_uuid, + .info = info_fspool_uuid, + .flags = 0 + }, + {.name = "fspool-start", + .handler = cmdFSPoolStart, + .opts = opts_fspool_start, + .info = info_fspool_start, + .flags = 0 + }, + {.name = NULL} +}; diff --git a/tools/virsh-fspool.h b/tools/virsh-fspool.h new file mode 100644 index 0000000..9eb60f1 --- /dev/null +++ b/tools/virsh-fspool.h @@ -0,0 +1,38 @@ +/* + * virsh-fspool.h: Commands to manage fspool + * + * Copyright (C) 2016 Parallels IP Holdings GmbH + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + * + */ + +#ifndef VIRSH_FSPOOL_H +# define VIRSH_FSPOOL_H + +# include "virsh.h" + +virFSPoolPtr +virshCommandOptFSPoolBy(vshControl *ctl, const vshCmd *cmd, const char *optname, + const char **name, unsigned int flags); + +/* default is lookup by Name and UUID */ +# define virshCommandOptFSPool(_ctl, _cmd, _optname, _name) \ + virshCommandOptFSPoolBy(_ctl, _cmd, _optname, _name, \ + VIRSH_BYUUID | VIRSH_BYNAME) + +extern const vshCmdDef fsPoolCmds[]; + +#endif /* VIRSH_FSPOOL_H */ diff --git a/tools/virsh.c b/tools/virsh.c index cb60edc..97aa85a 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -68,9 +68,11 @@ #include "virsh-nodedev.h" #include "virsh-nwfilter.h" #include "virsh-pool.h" +#include "virsh-fspool.h" #include "virsh-secret.h" #include "virsh-snapshot.h" #include "virsh-volume.h" +#include "virsh-fsitem.h" /* Gnulib doesn't guarantee SA_SIGINFO support. */ #ifndef SA_SIGINFO @@ -921,6 +923,8 @@ static const vshCmdGrp cmdGroups[] = { {VIRSH_CMD_GRP_SNAPSHOT, "snapshot", snapshotCmds}, {VIRSH_CMD_GRP_STORAGE_POOL, "pool", storagePoolCmds}, {VIRSH_CMD_GRP_STORAGE_VOL, "volume", storageVolCmds}, + {VIRSH_CMD_GRP_FSPOOL, "fspool", fsPoolCmds}, + {VIRSH_CMD_GRP_FSITEM, "item", fsItemCmds}, {VIRSH_CMD_GRP_VIRSH, "virsh", virshCmds}, {NULL, NULL, NULL} }; diff --git a/tools/virsh.h b/tools/virsh.h index fd552bb..5b7a636 100644 --- a/tools/virsh.h +++ b/tools/virsh.h @@ -51,6 +51,8 @@ # define VIRSH_CMD_GRP_DOM_MONITORING "Domain Monitoring" # define VIRSH_CMD_GRP_STORAGE_POOL "Storage Pool" # define VIRSH_CMD_GRP_STORAGE_VOL "Storage Volume" +# define VIRSH_CMD_GRP_FSPOOL "Fspool" +# define VIRSH_CMD_GRP_FSITEM "Item" # define VIRSH_CMD_GRP_NETWORK "Networking" # define VIRSH_CMD_GRP_NODEDEV "Node Device" # define VIRSH_CMD_GRP_IFACE "Interface" @@ -70,6 +72,13 @@ .help = _helpstr \ } \ +# define VIRSH_COMMON_OPT_FSPOOL(_helpstr) \ + {.name = "fspool", \ + .type = VSH_OT_DATA, \ + .flags = VSH_OFLAG_REQ, \ + .help = _helpstr \ + } + # define VIRSH_COMMON_OPT_DOMAIN(_helpstr) \ {.name = "domain", \ .type = VSH_OT_DATA, \ diff --git a/tools/virsh.pod b/tools/virsh.pod index 5027180..edde034 100644 --- a/tools/virsh.pod +++ b/tools/virsh.pod @@ -699,7 +699,7 @@ Immediately terminate the domain I<domain>. This doesn't give the domain OS any chance to react, and it's the equivalent of ripping the power cord out on a physical machine. In most cases you will want to use the B<shutdown> command instead. However, this does not delete any -storage volumes used by the guest, and if the domain is persistent, it +storage volumes or fspool items used by the guest, and if the domain is persistent, it can be restarted later. If I<domain> is transient, then the metadata of any snapshots will @@ -3820,6 +3820,256 @@ B<blockresize> for live resizing. =back +=head1 FSPOOL COMMANDS + +The following commands manipulate filesystem pools. Many of the commands +for filesystem pools are similar to the ones used for storage pools. + +=over 4 + +=item B<fspool-autostart> I<pool-or-uuid> [I<--disable>] + +Configure whether I<fspool> should automatically start at boot. + +=item B<fspool-build> I<pool-or-uuid> [I<--overwrite>] [I<--no-overwrite>] + +Build a given fspool. + +=item B<pool-create> I<file> +[I<--build>] [[I<--overwrite>] | [I<--no-overwrite>]] + +Create and start a fspool object from the XML I<file>. + +[I<--build>] perform a B<fspool-build> after creation in order +to remove the need for a follow-up command to build the fspool. + +=item B<pool-create-as> I<name> I<type> [I<--print-xml>] +[I<--source-host hostname>] [I<--source-path path>] +[I<--source-name name>] [I<--target path>] [I<--source-format format>] +[I<--build>] [[I<--overwrite>] | [I<--no-overwrite>]] + +Create and start a fspool object I<name> from the raw parameters. If +I<--print-xml> is specified, then print the XML of the fspool object +without creating the fspool. Otherwise, the fspool has the specified +I<type>. +[I<--source-host hostname>] provides the source hostname for fspools backed +by fspool from a remote server (fspool netfs, etc). + +[I<--source-path path>] provides the source directory path for fspools backed +by directories (fspool type dir). + +[I<--source-name name>] provides the source name for fspools backed by storage +from a named element. + +[I<--target path>] is the path for the mapping of the fspool into +the host file system. + +[I<--source-format format>] provides information about the format of the +fspool filesystem type. + +[I<--build>] [[I<--overwrite>] | [I<--no-overwrite>]] perform a +B<fspool-build> after creation in order to remove the need for a +follow-up command to build the fspool. The I<--overwrite> and +I<--no-overwrite> flags follow the same rules as B<fspool-build>. If +just I<--build> is provided, then B<fspool-build> is called with no flags. + +=item B<fspool-define> I<file> + +Define an inactive persistent fspool or modify an existing persistent one +from the XML I<file>. + +=item B<pool-define-as> I<name> I<type> [I<--print-xml>] +[I<--source-host hostname>] [I<--source-path path>] +[I<--source-name name>] [I<--target path>] [I<--source-format format>] + +Create, but do not start, a fspool object I<name> from the raw parameters. +If I<--print-xml> is specified, then print the XML of the pool object +without defining the pool. Otherwise, the pool has the specified +I<type>. + +Use the same arguments as B<fspool-create-as>, except for the I<--build>, +I<--overwrite>, and I<--no-overwrite> options. + +=item B<fspool-destroy> I<fspool-or-uuid> + +Destroy (stop) a given I<fspool> object. Libvirt will no longer manage the fspool +object, but the raw data contained in +the fspool is not changed, and can be later recovered with +B<fspool-create>. + +=item B<fspool-delete> I<fspool-or-uuid> + +Destroy the resources used by a given I<fspool> object. This operation +is non-recoverable. The I<fspool> object will still exist after this +command, ready for the creation of new fspool item. + +=item B<fspool-dumpxml> [I<--inactive>] I<fspool-or-uuid> + +Returns the XML information about the I<fspool> object. +I<--inactive> tells virsh to dump fspool configuration that will be used +on next start of the fspool as opposed to the current fspool configuration. + +=item B<fspool-edit> I<fspool-or-uuid> + +Edit the XML configuration file for a fspool. + +This is equivalent to: + + virsh fspool-dumpxml fspool > fspool.xml + vi fspool.xml (or make changes with your other text editor) + virsh pool-define pool.xml + +except that it does some error checking. + +The editor used can be supplied by the C<$VISUAL> or C<$EDITOR> environment +variables, and defaults to C<vi>. + +=item B<fspool-info> I<fspool-or-uuid> + +Returns basic information about the I<fspool> object. + +=item B<pool-fslist> [I<--inactive>] [I<--all>] + [I<--persistent>] [I<--transient>] + [I<--autostart>] [I<--no-autostart>] + [[I<--details>] [<type>] + +List fspool objects known to libvirt. By default, only active pools +are listed; I<--inactive> lists just the inactive pools, and I<--all> +lists all pools. + +In addition, there are several sets of filtering flags. I<--persistent> is to +list the persistent fspools, I<--transient> is to list the transient fspools. +I<--autostart> lists the autostarting fspools, I<--no-autostart> lists the fspools +with autostarting disabled. + +You may also want to list fspools with specified types using I<type>, the +pool types must be separated by comma, e.g. --type dir. + +The I<--details> option instructs virsh to additionally +display fspool persistence and capacity related information where available. + +=item B<fspool-name> I<uuid> + +Convert the I<uuid> to a fspool name. + +=item B<fspool-refresh> I<fspool-or-uuid> + +Refresh the list of items contained in I<fspool>. + +=item B<fspool-start> I<fspool-or-uuid> +[I<--build>] [[I<--overwrite>] | [I<--no-overwrite>]] + +Start the I<fspool>, which is previously defined but inactive. + +=item B<fspool-undefine> I<fspool-or-uuid> + +Undefine the configuration for an inactive I<fspool>. + +=item B<fspool-uuid> I<fspool> + +Returns the UUID of the named I<fspool>. + +=back + +=head1 FS ITEMS COMMANDS + +=over 4 + +=item B<item-create> I<fspool-or-uuid> I<FILE> + +Create a item from an XML <file>. +I<fspool-or-uuid> is the name or UUID of the fspool to create the item in. +I<FILE> is the XML <file> with the item definition. An easy way to create the +XML <file> is to use the B<item-dumpxml> command to obtain the definition of a +pre-existing item. + +=item B<vol-create-from> I<pool-or-uuid> I<FILE> [I<--inputpool> +I<pool-or-uuid>] I<vol-name-or-key-or-path> [I<--prealloc-metadata>] +[I<--reflink>] + +Create a volume, using another item as input. +I<fspool-or-uuid> is the name or UUID of the fspool to create the item in. +I<FILE> is the XML <file> with the volume definition. +I<--inputfspool> I<fspool-or-uuid> is the name or uuid of the fspool the +source item is in. +I<item-name-or-key-or-path> is the name or key or path of the source item. + +=item B<item-create-as> I<fspool-or-uuid> I<name> I<capacity> +[I<--allocation> I<size>] [I<--format> I<string>][I<--print-xml>] + +Create a item from a set of arguments unless I<--print-xml> is specified, in +which case just the XML of the item object is printed out without any actual +object creation. +I<fspool-or-uuid> is the name or UUID of the fspool to create the item +in. +I<name> is the name of the new item. +I<capacity> is the size of the item to be created, as a scaled integer +(see B<NOTES> above), defaulting to bytes if there is no suffix. +I<--allocation> I<size> is the initial size to be allocated in the item, +also as a scaled integer defaulting to bytes. + +=item B<item-clone> [I<--fspool> I<fspool-or-uuid>] I<item-name-or-key-or-path> +I<name> + +Clone an existing item within the parent fspool. Less powerful, +but easier to type, version of B<item-create-from>. +I<--fspool> I<fspool-or-uuid> is the name or UUID of the fspool +that contains the source item, and will contain the new item. +I<item-name-or-key-or-path> is the name or key or path of the source item. +I<name> is the name of the new item. + +=item B<item-delete> [I<--fspool> I<fspool-or-uuid>] I<item-name-or-key-or-path> + +Delete a given item. +I<--pool> I<fspool-or-uuid> is the name or UUID of the fspool the item +is in. +I<item-name-or-key-or-path> is the name or key or path of the item to delete. + +=item B<item-dumpxml> [I<--fspool> I<fspool-or-uuid>] I<item-name-or-key-or-path> + +Output the item information as an XML dump to stdout. +I<--fspool> I<fspool-or-uuid> is the name or UUID of the fspool the item +is in. I<item-name-or-key-or-path> is the name or key or path of the item +to output the XML of. + +=item B<item-info> [I<--fspool> I<fspool-or-uuid>] I<item-name-or-key-or-path> +[I<--bytes>] + +Returns basic information about the given item. +I<--fspool> I<fspool-or-uuid> is the name or UUID of the fspool the item +is in. I<item-name-or-key-or-path> is the name or key or path of the item +to return information for. If I<--bytes> is specified the sizes are not +converted to human friendly units. + +=item B<item-list> [I<--pool> I<pool-or-uuid>] [I<--details>] + +Return the list of volumes in the given fspool. +I<--fspool> I<fspool-or-uuid> is the name or UUID of the fspool. +The I<--details> option instructs virsh to additionally display item +type and capacity related information where available. + + +=item B<item-path> [I<--fspool> I<fspool-or-uuid>] I<item-name-or-key> + +Return the path for a given item. +I<--fspool> I<fspool-or-uuid> is the name or UUID of the fspool the item +is in. +I<item-name-or-key> is the name or key of the item to return the path for. + +=item B<item-name> I<item-key-or-path> + +Return the name for a given item. +I<item-key-or-path> is the key or path of the item to return the name for. + +=item B<item-key> [I<--fspool> I<fspool-or-uuid>] I<item-name-or-path> + +Return the item key for a given volume. +I<--fspool> I<fspool-or-uuid> is the name or UUID of the fspool the item +is in. I<item-name-or-path> is the name or path of the item to return the +item key for. + +=back + =head1 SECRET COMMANDS The following commands manipulate "secrets" (e.g. passwords, passphrases and -- 1.8.3.1

At the moment only pool test is implemented. Signed-off-by: Olga Krishtal <okrishtal@virtuozzo.com> --- docs/schemas/fsitem.rng | 66 +++++++++++++ docs/schemas/fspool.rng | 82 ++++++++++++++++ tests/Makefile.am | 12 +++ tests/fsitemxml2xmlin/item.xml | 13 +++ tests/fsitemxml2xmlout/item.xml | 13 +++ tests/fsitemxml2xmltest.c | 105 +++++++++++++++++++++ .../dir-missing-target-path-invalid.xml | 12 +++ tests/fspoolxml2xmlin/fspool-dir.xml | 16 ++++ tests/fspoolxml2xmlout/fspool-dir.xml | 16 ++++ tests/fspoolxml2xmltest.c | 81 ++++++++++++++++ 10 files changed, 416 insertions(+) create mode 100644 docs/schemas/fsitem.rng create mode 100644 docs/schemas/fspool.rng create mode 100644 tests/fsitemxml2xmlin/item.xml create mode 100644 tests/fsitemxml2xmlout/item.xml create mode 100644 tests/fsitemxml2xmltest.c create mode 100644 tests/fspoolschemadata/dir-missing-target-path-invalid.xml create mode 100644 tests/fspoolxml2xmlin/fspool-dir.xml create mode 100644 tests/fspoolxml2xmlout/fspool-dir.xml create mode 100644 tests/fspoolxml2xmltest.c diff --git a/docs/schemas/fsitem.rng b/docs/schemas/fsitem.rng new file mode 100644 index 0000000..d828978 --- /dev/null +++ b/docs/schemas/fsitem.rng @@ -0,0 +1,66 @@ +<?xml version="1.0"?> +<!-- A Relax NG schema for the libvirt fspool item XML format --> +<grammar xmlns="http://relaxng.org/ns/structure/1.0" + datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"> + <include href='basictypes.rng'/> + <start> + <ref name='item'/> + </start> + + <define name='item'> + <element name='fsitem'> + <optional> + <attribute name='type'> + <value>dir</value> + </attribute> + </optional> + <interleave> + <element name='name'> + <ref name='itemName'/> + </element> + <optional> + <element name='key'> + <text/> + </element> + </optional> + <ref name='sizing'/> + <ref name='target'/> + </interleave> + </element> + </define> + + <define name='sizing'> + <interleave> + <optional> + <element name='capacity'> + <ref name='scaledInteger'/> + </element> + </optional> + <optional> + <element name='allocation'> + <ref name='scaledInteger'/> + </element> + </optional> + </interleave> + </define> + + <define name='target'> + <element name='target'> + <interleave> + <optional> + <element name='path'> + <choice> + <data type='anyURI'/> + <ref name='absFilePath'/> + </choice> + </element> + </optional> + <ref name='permissions'/> + <optional> + <ref name='fileFormatFeatures'/> + </optional> + </interleave> + </element> + </define> + +</grammar> diff --git a/docs/schemas/fspool.rng b/docs/schemas/fspool.rng new file mode 100644 index 0000000..33ea0a2 --- /dev/null +++ b/docs/schemas/fspool.rng @@ -0,0 +1,82 @@ +<?xml version="1.0"?> +<!-- A Relax NG schema for the libvirt fspool XML format --> +<grammar xmlns="http://relaxng.org/ns/structure/1.0" + datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"> + <include href='basictypes.rng'/> + <start> + <ref name='fspool'/> + </start> + + + <define name='fspool'> + <element name='fspool'> + <choice> + <ref name='fspooldir'/> + </choice> + </element> + </define> + + <define name='fspooldir'> + <attribute name='type'> + <value>dir</value> + </attribute> + <interleave> + <ref name='commonmetadata'/> + <ref name='sizing'/> + <ref name='sourcedir'/> + <ref name='target'/> + </interleave> + </define> + + <define name='commonmetadata'> + <interleave> + <element name='name'> + <ref name='genericName'/> + </element> + <optional> + <element name='uuid'> + <ref name='UUID'/> + </element> + </optional> + </interleave> + </define> + + <define name='sizing'> + <interleave> + <optional> + <element name='capacity'> + <ref name='scaledInteger'/> + </element> + </optional> + <optional> + <element name='allocation'> + <ref name='scaledInteger'/> + </element> + </optional> + <optional> + <element name='available'> + <ref name='scaledInteger'/> + </element> + </optional> + </interleave> + </define> + + <define name='target'> + <element name='target'> + <interleave> + <element name='path'> + <ref name='absFilePath'/> + </element> + <ref name='permissions'/> + </interleave> + </element> + </define> + + <define name='sourcedir'> + <optional> + <element name='source'> + </element> + </optional> + </define> + +</grammar> diff --git a/tests/Makefile.am b/tests/Makefile.am index 0cd8391..526366e 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -339,6 +339,8 @@ endif WITH_NSS test_programs += storagevolxml2xmltest storagepoolxml2xmltest +test_programs += fsitemxml2xmltest fspoolxml2xmltest + test_programs += nodedevxml2xmltest test_programs += interfacexml2xmltest @@ -862,6 +864,16 @@ storagepoolxml2xmltest_SOURCES = \ testutils.c testutils.h storagepoolxml2xmltest_LDADD = $(LDADDS) +fsitemxml2xmltest_SOURCES = \ + fsitemxml2xmltest.c \ + testutils.c testutils.h +fsitemxml2xmltest_LDADD = $(LDADDS) + +fspoolxml2xmltest_SOURCES = \ + fspoolxml2xmltest.c \ + testutils.c testutils.h +fspoolxml2xmltest_LDADD = $(LDADDS) + nodedevxml2xmltest_SOURCES = \ nodedevxml2xmltest.c \ testutils.c testutils.h diff --git a/tests/fsitemxml2xmlin/item.xml b/tests/fsitemxml2xmlin/item.xml new file mode 100644 index 0000000..ae1be59 --- /dev/null +++ b/tests/fsitemxml2xmlin/item.xml @@ -0,0 +1,13 @@ +<item> + <name>item1</name> + <key>/var/lib/libvirt/images/fs/item1</key> + <capacity unit='bytes'>0</capacity> + <allocation unit='bytes'>4096</allocation> + <target> + <permissions> + <mode>0600</mode> + <owner>0</owner> + <group>0</group> + </permissions> + </target> +</item> diff --git a/tests/fsitemxml2xmlout/item.xml b/tests/fsitemxml2xmlout/item.xml new file mode 100644 index 0000000..ae1be59 --- /dev/null +++ b/tests/fsitemxml2xmlout/item.xml @@ -0,0 +1,13 @@ +<item> + <name>item1</name> + <key>/var/lib/libvirt/images/fs/item1</key> + <capacity unit='bytes'>0</capacity> + <allocation unit='bytes'>4096</allocation> + <target> + <permissions> + <mode>0600</mode> + <owner>0</owner> + <group>0</group> + </permissions> + </target> +</item> diff --git a/tests/fsitemxml2xmltest.c b/tests/fsitemxml2xmltest.c new file mode 100644 index 0000000..dbfcf13 --- /dev/null +++ b/tests/fsitemxml2xmltest.c @@ -0,0 +1,105 @@ +#include <config.h> + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> + +#include <sys/types.h> +#include <fcntl.h> + +#include "internal.h" +#include "testutils.h" +#include "fs_conf.h" +#include "testutilsqemu.h" +#include "virstring.h" + +#define VIR_FROM_THIS VIR_FROM_NONE + +static int +testCompareXMLToXMLFiles(const char *fspoolxml, const char *inxml, + const char *outxml, unsigned int flags) +{ + char *actual = NULL; + int ret = -1; + virFSPoolDefPtr fspool = NULL; + virFSItemDefPtr dev = NULL; + + if (!(fspool = virFSPoolDefParseFile(fspoolxml))) + goto fail; + + if (!(dev = virFSItemDefParseFile(fspool, inxml, flags))) + goto fail; + + if (!(actual = virFSItemDefFormat(fspool, dev))) + goto fail; + + if (virTestCompareToFile(actual, outxml) < 0) + goto fail; + + ret = 0; + + fail: + VIR_FREE(actual); + virFSPoolDefFree(fspool); + virFSItemDefFree(dev); + return ret; +} + +struct testInfo { + const char *fspool; + const char *name; + unsigned int flags; +}; + +static int +testCompareXMLToXMLHelper(const void *data) +{ + int result = -1; + const struct testInfo *info = data; + char *fspoolxml = NULL; + char *inxml = NULL; + char *outxml = NULL; + + if (virAsprintf(&fspoolxml, "%s/fspoolxml2xmlin/%s.xml", + abs_srcdir, info->fspool) < 0 || + virAsprintf(&inxml, "%s/fsitemxml2xmlin/%s.xml", + abs_srcdir, info->name) < 0 || + virAsprintf(&outxml, "%s/fsitemxml2xmlout/%s.xml", + abs_srcdir, info->name) < 0) { + goto cleanup; + } + + result = testCompareXMLToXMLFiles(fspoolxml, inxml, outxml, info->flags); + + cleanup: + VIR_FREE(fspoolxml); + VIR_FREE(inxml); + VIR_FREE(outxml); + + return result; +} + + +static int +mymain(void) +{ + int ret = 0; + +#define DO_TEST_FULL(fspool, name, flags) \ + do { \ + struct testInfo info = { fspool, name, flags }; \ + if (virTestRun("FS Item XML-2-XML " name, \ + testCompareXMLToXMLHelper, &info) < 0) \ + ret = -1; \ + } \ + while (0); + +#define DO_TEST(fspool, name) DO_TEST_FULL(fspool, name, 0) + + DO_TEST("fspool-dir", "item"); + + return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE; +} + +VIRT_TEST_MAIN(mymain) diff --git a/tests/fspoolschemadata/dir-missing-target-path-invalid.xml b/tests/fspoolschemadata/dir-missing-target-path-invalid.xml new file mode 100644 index 0000000..a52bf49 --- /dev/null +++ b/tests/fspoolschemadata/dir-missing-target-path-invalid.xml @@ -0,0 +1,12 @@ +<fspool type='dir'> + <name>test</name> + <source> + </source> + <target> + <permissions> + <mode>0700</mode> + <owner>-1</owner> + <group>-1</group> + </permissions> + </target> +</fspool> diff --git a/tests/fspoolxml2xmlin/fspool-dir.xml b/tests/fspoolxml2xmlin/fspool-dir.xml new file mode 100644 index 0000000..d1a3f28 --- /dev/null +++ b/tests/fspoolxml2xmlin/fspool-dir.xml @@ -0,0 +1,16 @@ +<fspool type='dir'> + <name>virtfs</name> + <uuid>5584ee21-db40-4e98-980e-44802c47b62f</uuid> + <capacity unit='bytes'>0</capacity> + <allocation unit='bytes'>0</allocation> + <available unit='bytes'>0</available> + <source> + </source> + <target> + <path>///var/////lib/libvirt/fs//</path> + <permissions> + <mode>0700</mode> + <label>some_lable_t</label> + </permissions> + </target> +</fspool> diff --git a/tests/fspoolxml2xmlout/fspool-dir.xml b/tests/fspoolxml2xmlout/fspool-dir.xml new file mode 100644 index 0000000..dbca470 --- /dev/null +++ b/tests/fspoolxml2xmlout/fspool-dir.xml @@ -0,0 +1,16 @@ +<fspool type='dir'> + <name>virtfs</name> + <uuid>5584ee21-db40-4e98-980e-44802c47b62f</uuid> + <capacity unit='bytes'>0</capacity> + <allocation unit='bytes'>0</allocation> + <available unit='bytes'>0</available> + <source> + </source> + <target> + <path>/var/lib/libvirt/fs</path> + <permissions> + <mode>0700</mode> + <label>some_lable_t</label> + </permissions> + </target> +</fspool> diff --git a/tests/fspoolxml2xmltest.c b/tests/fspoolxml2xmltest.c new file mode 100644 index 0000000..726a3ec --- /dev/null +++ b/tests/fspoolxml2xmltest.c @@ -0,0 +1,81 @@ +#include <config.h> + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> + +#include <sys/types.h> +#include <fcntl.h> + +#include "internal.h" +#include "testutils.h" +#include "fs_conf.h" +#include "testutilsqemu.h" +#include "virstring.h" + +#define VIR_FROM_THIS VIR_FROM_NONE + +static int +testCompareXMLToXMLFiles(const char *inxml, const char *outxml) +{ + char *actual = NULL; + int ret = -1; + virFSPoolDefPtr dev = NULL; + + if (!(dev = virFSPoolDefParseFile(inxml))) + goto fail; + + if (!(actual = virFSPoolDefFormat(dev))) + goto fail; + + if (virTestCompareToFile(actual, outxml) < 0) + goto fail; + + ret = 0; + + fail: + VIR_FREE(actual); + virFSPoolDefFree(dev); + return ret; +} + +static int +testCompareXMLToXMLHelper(const void *data) +{ + int result = -1; + char *inxml = NULL; + char *outxml = NULL; + + if (virAsprintf(&inxml, "%s/fspoolxml2xmlin/%s.xml", + abs_srcdir, (const char*)data) < 0 || + virAsprintf(&outxml, "%s/fspoolxml2xmlout/%s.xml", + abs_srcdir, (const char*)data) < 0) { + goto cleanup; + } + + result = testCompareXMLToXMLFiles(inxml, outxml); + + cleanup: + VIR_FREE(inxml); + VIR_FREE(outxml); + + return result; +} + +static int +mymain(void) +{ + int ret = 0; + +#define DO_TEST(name) \ + if (virTestRun("FS Pool XML-2-XML " name, \ + testCompareXMLToXMLHelper, (name)) < 0) \ + ret = -1 + + DO_TEST("fspool-dir"); + + return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE; +} + +VIRT_TEST_MAIN(mymain) -- 1.8.3.1

Hello again. Please, drop the commit message for this patch. Use the following one: Added XML 2 XML tests for fspool and item. On 15/09/16 10:32, Olga Krishtal wrote:
At the moment only pool test is implemented.
Signed-off-by: Olga Krishtal <okrishtal@virtuozzo.com> --- docs/schemas/fsitem.rng | 66 +++++++++++++ docs/schemas/fspool.rng | 82 ++++++++++++++++ tests/Makefile.am | 12 +++ tests/fsitemxml2xmlin/item.xml | 13 +++ tests/fsitemxml2xmlout/item.xml | 13 +++ tests/fsitemxml2xmltest.c | 105 +++++++++++++++++++++ .../dir-missing-target-path-invalid.xml | 12 +++ tests/fspoolxml2xmlin/fspool-dir.xml | 16 ++++ tests/fspoolxml2xmlout/fspool-dir.xml | 16 ++++ tests/fspoolxml2xmltest.c | 81 ++++++++++++++++ 10 files changed, 416 insertions(+) create mode 100644 docs/schemas/fsitem.rng create mode 100644 docs/schemas/fspool.rng create mode 100644 tests/fsitemxml2xmlin/item.xml create mode 100644 tests/fsitemxml2xmlout/item.xml create mode 100644 tests/fsitemxml2xmltest.c create mode 100644 tests/fspoolschemadata/dir-missing-target-path-invalid.xml create mode 100644 tests/fspoolxml2xmlin/fspool-dir.xml create mode 100644 tests/fspoolxml2xmlout/fspool-dir.xml create mode 100644 tests/fspoolxml2xmltest.c
diff --git a/docs/schemas/fsitem.rng b/docs/schemas/fsitem.rng new file mode 100644 index 0000000..d828978 --- /dev/null +++ b/docs/schemas/fsitem.rng @@ -0,0 +1,66 @@ +<?xml version="1.0"?> +<!-- A Relax NG schema for the libvirt fspool item XML format --> +<grammar xmlns="http://relaxng.org/ns/structure/1.0" + datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"> + <include href='basictypes.rng'/> + <start> + <ref name='item'/> + </start> + + <define name='item'> + <element name='fsitem'> + <optional> + <attribute name='type'> + <value>dir</value> + </attribute> + </optional> + <interleave> + <element name='name'> + <ref name='itemName'/> + </element> + <optional> + <element name='key'> + <text/> + </element> + </optional> + <ref name='sizing'/> + <ref name='target'/> + </interleave> + </element> + </define> + + <define name='sizing'> + <interleave> + <optional> + <element name='capacity'> + <ref name='scaledInteger'/> + </element> + </optional> + <optional> + <element name='allocation'> + <ref name='scaledInteger'/> + </element> + </optional> + </interleave> + </define> + + <define name='target'> + <element name='target'> + <interleave> + <optional> + <element name='path'> + <choice> + <data type='anyURI'/> + <ref name='absFilePath'/> + </choice> + </element> + </optional> + <ref name='permissions'/> + <optional> + <ref name='fileFormatFeatures'/> + </optional> + </interleave> + </element> + </define> + +</grammar> diff --git a/docs/schemas/fspool.rng b/docs/schemas/fspool.rng new file mode 100644 index 0000000..33ea0a2 --- /dev/null +++ b/docs/schemas/fspool.rng @@ -0,0 +1,82 @@ +<?xml version="1.0"?> +<!-- A Relax NG schema for the libvirt fspool XML format --> +<grammar xmlns="http://relaxng.org/ns/structure/1.0" + datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"> + <include href='basictypes.rng'/> + <start> + <ref name='fspool'/> + </start> + + + <define name='fspool'> + <element name='fspool'> + <choice> + <ref name='fspooldir'/> + </choice> + </element> + </define> + + <define name='fspooldir'> + <attribute name='type'> + <value>dir</value> + </attribute> + <interleave> + <ref name='commonmetadata'/> + <ref name='sizing'/> + <ref name='sourcedir'/> + <ref name='target'/> + </interleave> + </define> + + <define name='commonmetadata'> + <interleave> + <element name='name'> + <ref name='genericName'/> + </element> + <optional> + <element name='uuid'> + <ref name='UUID'/> + </element> + </optional> + </interleave> + </define> + + <define name='sizing'> + <interleave> + <optional> + <element name='capacity'> + <ref name='scaledInteger'/> + </element> + </optional> + <optional> + <element name='allocation'> + <ref name='scaledInteger'/> + </element> + </optional> + <optional> + <element name='available'> + <ref name='scaledInteger'/> + </element> + </optional> + </interleave> + </define> + + <define name='target'> + <element name='target'> + <interleave> + <element name='path'> + <ref name='absFilePath'/> + </element> + <ref name='permissions'/> + </interleave> + </element> + </define> + + <define name='sourcedir'> + <optional> + <element name='source'> + </element> + </optional> + </define> + +</grammar> diff --git a/tests/Makefile.am b/tests/Makefile.am index 0cd8391..526366e 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -339,6 +339,8 @@ endif WITH_NSS
test_programs += storagevolxml2xmltest storagepoolxml2xmltest
+test_programs += fsitemxml2xmltest fspoolxml2xmltest + test_programs += nodedevxml2xmltest
test_programs += interfacexml2xmltest @@ -862,6 +864,16 @@ storagepoolxml2xmltest_SOURCES = \ testutils.c testutils.h storagepoolxml2xmltest_LDADD = $(LDADDS)
+fsitemxml2xmltest_SOURCES = \ + fsitemxml2xmltest.c \ + testutils.c testutils.h +fsitemxml2xmltest_LDADD = $(LDADDS) + +fspoolxml2xmltest_SOURCES = \ + fspoolxml2xmltest.c \ + testutils.c testutils.h +fspoolxml2xmltest_LDADD = $(LDADDS) + nodedevxml2xmltest_SOURCES = \ nodedevxml2xmltest.c \ testutils.c testutils.h diff --git a/tests/fsitemxml2xmlin/item.xml b/tests/fsitemxml2xmlin/item.xml new file mode 100644 index 0000000..ae1be59 --- /dev/null +++ b/tests/fsitemxml2xmlin/item.xml @@ -0,0 +1,13 @@ +<item> + <name>item1</name> + <key>/var/lib/libvirt/images/fs/item1</key> + <capacity unit='bytes'>0</capacity> + <allocation unit='bytes'>4096</allocation> + <target> + <permissions> + <mode>0600</mode> + <owner>0</owner> + <group>0</group> + </permissions> + </target> +</item> diff --git a/tests/fsitemxml2xmlout/item.xml b/tests/fsitemxml2xmlout/item.xml new file mode 100644 index 0000000..ae1be59 --- /dev/null +++ b/tests/fsitemxml2xmlout/item.xml @@ -0,0 +1,13 @@ +<item> + <name>item1</name> + <key>/var/lib/libvirt/images/fs/item1</key> + <capacity unit='bytes'>0</capacity> + <allocation unit='bytes'>4096</allocation> + <target> + <permissions> + <mode>0600</mode> + <owner>0</owner> + <group>0</group> + </permissions> + </target> +</item> diff --git a/tests/fsitemxml2xmltest.c b/tests/fsitemxml2xmltest.c new file mode 100644 index 0000000..dbfcf13 --- /dev/null +++ b/tests/fsitemxml2xmltest.c @@ -0,0 +1,105 @@ +#include <config.h> + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> + +#include <sys/types.h> +#include <fcntl.h> + +#include "internal.h" +#include "testutils.h" +#include "fs_conf.h" +#include "testutilsqemu.h" +#include "virstring.h" + +#define VIR_FROM_THIS VIR_FROM_NONE + +static int +testCompareXMLToXMLFiles(const char *fspoolxml, const char *inxml, + const char *outxml, unsigned int flags) +{ + char *actual = NULL; + int ret = -1; + virFSPoolDefPtr fspool = NULL; + virFSItemDefPtr dev = NULL; + + if (!(fspool = virFSPoolDefParseFile(fspoolxml))) + goto fail; + + if (!(dev = virFSItemDefParseFile(fspool, inxml, flags))) + goto fail; + + if (!(actual = virFSItemDefFormat(fspool, dev))) + goto fail; + + if (virTestCompareToFile(actual, outxml) < 0) + goto fail; + + ret = 0; + + fail: + VIR_FREE(actual); + virFSPoolDefFree(fspool); + virFSItemDefFree(dev); + return ret; +} + +struct testInfo { + const char *fspool; + const char *name; + unsigned int flags; +}; + +static int +testCompareXMLToXMLHelper(const void *data) +{ + int result = -1; + const struct testInfo *info = data; + char *fspoolxml = NULL; + char *inxml = NULL; + char *outxml = NULL; + + if (virAsprintf(&fspoolxml, "%s/fspoolxml2xmlin/%s.xml", + abs_srcdir, info->fspool) < 0 || + virAsprintf(&inxml, "%s/fsitemxml2xmlin/%s.xml", + abs_srcdir, info->name) < 0 || + virAsprintf(&outxml, "%s/fsitemxml2xmlout/%s.xml", + abs_srcdir, info->name) < 0) { + goto cleanup; + } + + result = testCompareXMLToXMLFiles(fspoolxml, inxml, outxml, info->flags); + + cleanup: + VIR_FREE(fspoolxml); + VIR_FREE(inxml); + VIR_FREE(outxml); + + return result; +} + + +static int +mymain(void) +{ + int ret = 0; + +#define DO_TEST_FULL(fspool, name, flags) \ + do { \ + struct testInfo info = { fspool, name, flags }; \ + if (virTestRun("FS Item XML-2-XML " name, \ + testCompareXMLToXMLHelper, &info) < 0) \ + ret = -1; \ + } \ + while (0); + +#define DO_TEST(fspool, name) DO_TEST_FULL(fspool, name, 0) + + DO_TEST("fspool-dir", "item"); + + return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE; +} + +VIRT_TEST_MAIN(mymain) diff --git a/tests/fspoolschemadata/dir-missing-target-path-invalid.xml b/tests/fspoolschemadata/dir-missing-target-path-invalid.xml new file mode 100644 index 0000000..a52bf49 --- /dev/null +++ b/tests/fspoolschemadata/dir-missing-target-path-invalid.xml @@ -0,0 +1,12 @@ +<fspool type='dir'> + <name>test</name> + <source> + </source> + <target> + <permissions> + <mode>0700</mode> + <owner>-1</owner> + <group>-1</group> + </permissions> + </target> +</fspool> diff --git a/tests/fspoolxml2xmlin/fspool-dir.xml b/tests/fspoolxml2xmlin/fspool-dir.xml new file mode 100644 index 0000000..d1a3f28 --- /dev/null +++ b/tests/fspoolxml2xmlin/fspool-dir.xml @@ -0,0 +1,16 @@ +<fspool type='dir'> + <name>virtfs</name> + <uuid>5584ee21-db40-4e98-980e-44802c47b62f</uuid> + <capacity unit='bytes'>0</capacity> + <allocation unit='bytes'>0</allocation> + <available unit='bytes'>0</available> + <source> + </source> + <target> + <path>///var/////lib/libvirt/fs//</path> + <permissions> + <mode>0700</mode> + <label>some_lable_t</label> + </permissions> + </target> +</fspool> diff --git a/tests/fspoolxml2xmlout/fspool-dir.xml b/tests/fspoolxml2xmlout/fspool-dir.xml new file mode 100644 index 0000000..dbca470 --- /dev/null +++ b/tests/fspoolxml2xmlout/fspool-dir.xml @@ -0,0 +1,16 @@ +<fspool type='dir'> + <name>virtfs</name> + <uuid>5584ee21-db40-4e98-980e-44802c47b62f</uuid> + <capacity unit='bytes'>0</capacity> + <allocation unit='bytes'>0</allocation> + <available unit='bytes'>0</available> + <source> + </source> + <target> + <path>/var/lib/libvirt/fs</path> + <permissions> + <mode>0700</mode> + <label>some_lable_t</label> + </permissions> + </target> +</fspool> diff --git a/tests/fspoolxml2xmltest.c b/tests/fspoolxml2xmltest.c new file mode 100644 index 0000000..726a3ec --- /dev/null +++ b/tests/fspoolxml2xmltest.c @@ -0,0 +1,81 @@ +#include <config.h> + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> + +#include <sys/types.h> +#include <fcntl.h> + +#include "internal.h" +#include "testutils.h" +#include "fs_conf.h" +#include "testutilsqemu.h" +#include "virstring.h" + +#define VIR_FROM_THIS VIR_FROM_NONE + +static int +testCompareXMLToXMLFiles(const char *inxml, const char *outxml) +{ + char *actual = NULL; + int ret = -1; + virFSPoolDefPtr dev = NULL; + + if (!(dev = virFSPoolDefParseFile(inxml))) + goto fail; + + if (!(actual = virFSPoolDefFormat(dev))) + goto fail; + + if (virTestCompareToFile(actual, outxml) < 0) + goto fail; + + ret = 0; + + fail: + VIR_FREE(actual); + virFSPoolDefFree(dev); + return ret; +} + +static int +testCompareXMLToXMLHelper(const void *data) +{ + int result = -1; + char *inxml = NULL; + char *outxml = NULL; + + if (virAsprintf(&inxml, "%s/fspoolxml2xmlin/%s.xml", + abs_srcdir, (const char*)data) < 0 || + virAsprintf(&outxml, "%s/fspoolxml2xmlout/%s.xml", + abs_srcdir, (const char*)data) < 0) { + goto cleanup; + } + + result = testCompareXMLToXMLFiles(inxml, outxml); + + cleanup: + VIR_FREE(inxml); + VIR_FREE(outxml); + + return result; +} + +static int +mymain(void) +{ + int ret = 0; + +#define DO_TEST(name) \ + if (virTestRun("FS Pool XML-2-XML " name, \ + testCompareXMLToXMLHelper, (name)) < 0) \ + ret = -1 + + DO_TEST("fspool-dir"); + + return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE; +} + +VIRT_TEST_MAIN(mymain)

On 09/15/2016 03:32 AM, Olga Krishtal wrote:
Hi everyone, we would like to propose the first implementation of fspool with directory backend.
Filesystem pools is a facility to manage filesystems resources similar to how storage pools manages volume resources. Furthermore new API follows storage API closely where it makes sense. Uploading/downloading operations are not defined yet as it is not obvious how to make it properly. I guess we can use some kind of tar to make a stream from a filesystem. Please share you thoughts on this particular issue.
So how do you differentiate between with the existing <pool type="fs"> http://libvirt.org/storage.html#StorageBackendFS Sure the existing fs pool requires/uses a source block device as the source path and this new variant doesn't require that source but seems to use some item in order to dictate how to "define" the source on the fly. Currently only a "DIR" is created - so how does that differ from a "dir" pool. I think it'll be confusing to have and differentiate fspool and pool commands. I didn't dig through all the patches, but from the few I did look at it seems as though all that's done is to rip out the guts of stuff not desired from the storage pool driver and replace it with this new code attributing all the work to the new author/copyright. IOW: Lots of places where StoragePool appears to be exactly the same as the FSPool. I think you need to find a different means to do what you want. It's not 100% what the end goal is. I did download/git am the patches and scan a few patches... * In patch 2 you've totally missed how to modify libvirt_public.syms. * In patch 3, the build breaks in "conf/fs_conf" since the "if { if {} }" aren't done properly in virFSPoolDefFormatBuf. * In patch 5 the remote_protocol_structs fails check/syntax-check... I stopped there in my build each patch test. John
The patchset provides 'dir' backend which simply expose directories in some directory in host filesystem. The virsh commands are provided too. So it is ready to play with, just replace 'pool' in xml descriptions and virsh commands to 'fspool' and 'volume' to 'item'. Examle and usage: Define: virsh -c qemu:///system fspool-define-as fs_pool_name dir --target /path/on/host Build virsh -c qemu:///system fspool-build fs_pool_name Start virsh -c qemu:///system fspool-start fs_pool_name Look inside virsh -c qemu:///system fspool-list (--all) fspool_name
Fspool called POOL, on the host fs uses /fs_driver to hold items. virsh -c qemu:///system fspool-dumpxml POOL <fspool type='dir'> <name>POOL</name> <uuid>c57c9d7c-b1d5-4c45-ba9c-67f03d4da160</uuid> <capacity unit='bytes'>733722615808</capacity> <allocation unit='bytes'>1331486720</allocation> <available unit='bytes'>534810800128</available> <source> </source> <target> <path>/fs_driver</path> <permissions> <mode>0755</mode> <owner>0</owner> <group>0</group> </permissions> </target> </fspool>
virsh -c qemu:///system fspool-info POOL Name: POOL UUID: c57c9d7c-b1d5-4c45-ba9c-67f03d4da160 State: running Persistent: yes Autostart: no autostart Capacity: 683.33 GiB Allocation: 1.24 GiB Available: 498.08 GiB
virsh -c qemu+unix:///system item-list POOL Name Path ------------------------------------------------------------------------------ item1 /fs_driver/item1 item10 /fs_driver/item10 item11 /fs_driver/item11 item12 /fs_driver/item12 item15 /fs_driver/item15
Fspool of directory type is some directory on host fs that holds items (subdirs). Example of usage for items: virsh -c vz+unix:///system item-create-as POOL item1 1g - create item virsh -c qemu+unix:///system item-dumpxml item1 POOL <fsitem> <name>item1</name> <key>/fs_driver/item1</key> <source> </source> <capacity unit='bytes'>0</capacity> <allocation unit='bytes'>0</allocation> <target> <format type='dir'/> </target> </fsitem>
virsh -c qemu+unix:///system item-info item1 POOL Name: item1 Type: dir Capacity: 683.33 GiB Allocation: 634.87 MiB Autostart: no autostart Capacity: 683.33 GiB Allocation: 1.24 GiB Available: 498.08 GiB virsh -c qemu+unix:///system item-list POOL Name Path ------------------------------------------------------------------------------ item1 /fs_driver/item1 item10 /fs_driver/item10 item11 /fs_driver/item11 item12 /fs_driver/item12 item15 /fs_driver/item15
v2: - renamed Fs to FS - in configure.ac script macro m4 is used - updates docs - created simple tests - updated virsh.pod - added information abot fspool in fotmatfs.html
Olga Krishtal (8): fspool: introduce filesystem pools API fspool: usual driver based implementation of filesystem pools API fspools: configuration and internal representation fspools: acl support for filesystem pools remote: filesystem pools driver implementation fspool: default implementation of filesystem pools virsh: filesystem pools commands fspools: docs and tests for fspool directory backend
configure.ac | 38 + daemon/Makefile.am | 4 + daemon/libvirtd.c | 10 + daemon/remote.c | 35 + docs/formatfs.html.in | 208 ++ docs/fspool.html.in | 41 + docs/schemas/fsitem.rng | 66 + docs/schemas/fspool.rng | 82 + docs/sitemap.html.in | 4 + include/libvirt/libvirt-fs.h | 260 +++ include/libvirt/libvirt.h | 1 + include/libvirt/virterror.h | 8 + m4/virt-driver-fspool.m4 | 52 + po/POTFILES.in | 6 + src/Makefile.am | 46 + src/access/viraccessdriver.h | 12 + src/access/viraccessdrivernop.c | 19 + src/access/viraccessdriverpolkit.c | 47 + src/access/viraccessdriverstack.c | 49 + src/access/viraccessmanager.c | 31 + src/access/viraccessmanager.h | 11 + src/access/viraccessperm.c | 15 +- src/access/viraccessperm.h | 124 ++ src/check-driverimpls.pl | 2 + src/conf/fs_conf.c | 1637 ++++++++++++++++ src/conf/fs_conf.h | 323 +++ src/datatypes.c | 154 ++ src/datatypes.h | 94 + src/driver-fs.h | 192 ++ src/driver.h | 3 + src/fs/fs_backend.h | 107 + src/fs/fs_backend_dir.c | 355 ++++ src/fs/fs_backend_dir.h | 8 + src/fs/fs_driver.c | 2058 ++++++++++++++++++++ src/fs/fs_driver.h | 10 + src/libvirt-fs.c | 1556 +++++++++++++++ src/libvirt.c | 28 + src/libvirt_private.syms | 53 + src/libvirt_public.syms | 42 + src/remote/remote_driver.c | 66 + src/remote/remote_protocol.x | 466 ++++- src/remote_protocol-structs | 165 ++ src/rpc/gendispatch.pl | 23 +- src/util/virerror.c | 37 + tests/Makefile.am | 12 + tests/fsitemxml2xmlin/item.xml | 13 + tests/fsitemxml2xmlout/item.xml | 13 + tests/fsitemxml2xmltest.c | 105 + .../dir-missing-target-path-invalid.xml | 12 + tests/fspoolxml2xmlin/fspool-dir.xml | 16 + tests/fspoolxml2xmlout/fspool-dir.xml | 16 + tests/fspoolxml2xmltest.c | 81 + tools/Makefile.am | 2 + tools/virsh-fsitem.c | 1292 ++++++++++++ tools/virsh-fsitem.h | 39 + tools/virsh-fspool.c | 1586 +++++++++++++++ tools/virsh-fspool.h | 38 + tools/virsh.c | 4 + tools/virsh.h | 9 + tools/virsh.pod | 252 ++- 60 files changed, 12028 insertions(+), 10 deletions(-) create mode 100644 docs/formatfs.html.in create mode 100644 docs/fspool.html.in create mode 100644 docs/schemas/fsitem.rng create mode 100644 docs/schemas/fspool.rng create mode 100644 include/libvirt/libvirt-fs.h create mode 100644 m4/virt-driver-fspool.m4 create mode 100644 src/conf/fs_conf.c create mode 100644 src/conf/fs_conf.h create mode 100644 src/driver-fs.h create mode 100644 src/fs/fs_backend.h create mode 100644 src/fs/fs_backend_dir.c create mode 100644 src/fs/fs_backend_dir.h create mode 100644 src/fs/fs_driver.c create mode 100644 src/fs/fs_driver.h create mode 100644 src/libvirt-fs.c create mode 100644 tests/fsitemxml2xmlin/item.xml create mode 100644 tests/fsitemxml2xmlout/item.xml create mode 100644 tests/fsitemxml2xmltest.c create mode 100644 tests/fspoolschemadata/dir-missing-target-path-invalid.xml create mode 100644 tests/fspoolxml2xmlin/fspool-dir.xml create mode 100644 tests/fspoolxml2xmlout/fspool-dir.xml create mode 100644 tests/fspoolxml2xmltest.c create mode 100644 tools/virsh-fsitem.c create mode 100644 tools/virsh-fsitem.h create mode 100644 tools/virsh-fspool.c create mode 100644 tools/virsh-fspool.h

20 сент. 2016 г., в 23:52, John Ferlan <jferlan@redhat.com> написал(а):
On 09/15/2016 03:32 AM, Olga Krishtal wrote: Hi everyone, we would like to propose the first implementation of fspool with directory backend.
Filesystem pools is a facility to manage filesystems resources similar to how storage pools manages volume resources. Furthermore new API follows storage API closely where it makes sense. Uploading/downloading operations are not defined yet as it is not obvious how to make it properly. I guess we can use some kind of tar to make a stream from a filesystem. Please share you thoughts on this particular issue.
So how do you differentiate between with the existing <pool type="fs">
Pool type=fs still provides volumes, i. e. block devices rather than filesystem, though this storage pool can mount file systems resided on a source block device.
http://libvirt.org/storage.html#StorageBackendFS
Sure the existing fs pool requires/uses a source block device as the source path and this new variant doesn't require that source but seems to use some item in order to dictate how to "define" the source on the fly. Currently only a "DIR" is created - so how does that differ from a "dir" pool.
Same here, storage "dir" provides files, which are in fact block devices for guests. While filesystem pool "dir" provides guests with file systems.
I think it'll be confusing to have and differentiate fspool and pool commands.
I didn't dig through all the patches, but from the few I did look at it seems as though all that's done is to rip out the guts of stuff not desired from the storage pool driver and replace it with this new code attributing all the work to the new author/copyright. IOW: Lots of places where StoragePool appears to be exactly the same as the FSPool.
I think you need to find a different means to do what you want. It's not 100% what the end goal is.
I did download/git am the patches and scan a few patches... * In patch 2 you've totally missed how to modify libvirt_public.syms. * In patch 3, the build breaks in "conf/fs_conf" since the "if { if {} }" aren't done properly in virFSPoolDefFormatBuf. * In patch 5 the remote_protocol_structs fails check/syntax-check... I stopped there in my build each patch test.
John
The patchset provides 'dir' backend which simply expose directories in some directory in host filesystem. The virsh commands are provided too. So it is ready to play with, just replace 'pool' in xml descriptions and virsh commands to 'fspool' and 'volume' to 'item'. Examle and usage: Define: virsh -c qemu:///system fspool-define-as fs_pool_name dir --target /path/on/host Build virsh -c qemu:///system fspool-build fs_pool_name Start virsh -c qemu:///system fspool-start fs_pool_name Look inside virsh -c qemu:///system fspool-list (--all) fspool_name
Fspool called POOL, on the host fs uses /fs_driver to hold items. virsh -c qemu:///system fspool-dumpxml POOL <fspool type='dir'> <name>POOL</name> <uuid>c57c9d7c-b1d5-4c45-ba9c-67f03d4da160</uuid> <capacity unit='bytes'>733722615808</capacity> <allocation unit='bytes'>1331486720</allocation> <available unit='bytes'>534810800128</available> <source> </source> <target> <path>/fs_driver</path> <permissions> <mode>0755</mode> <owner>0</owner> <group>0</group> </permissions> </target> </fspool>
virsh -c qemu:///system fspool-info POOL Name: POOL UUID: c57c9d7c-b1d5-4c45-ba9c-67f03d4da160 State: running Persistent: yes Autostart: no autostart Capacity: 683.33 GiB Allocation: 1.24 GiB Available: 498.08 GiB
virsh -c qemu+unix:///system item-list POOL Name Path ------------------------------------------------------------------------------ item1 /fs_driver/item1 item10 /fs_driver/item10 item11 /fs_driver/item11 item12 /fs_driver/item12 item15 /fs_driver/item15
Fspool of directory type is some directory on host fs that holds items (subdirs). Example of usage for items: virsh -c vz+unix:///system item-create-as POOL item1 1g - create item virsh -c qemu+unix:///system item-dumpxml item1 POOL <fsitem> <name>item1</name> <key>/fs_driver/item1</key> <source> </source> <capacity unit='bytes'>0</capacity> <allocation unit='bytes'>0</allocation> <target> <format type='dir'/> </target> </fsitem>
virsh -c qemu+unix:///system item-info item1 POOL Name: item1 Type: dir Capacity: 683.33 GiB Allocation: 634.87 MiB Autostart: no autostart Capacity: 683.33 GiB Allocation: 1.24 GiB Available: 498.08 GiB virsh -c qemu+unix:///system item-list POOL Name Path ------------------------------------------------------------------------------ item1 /fs_driver/item1 item10 /fs_driver/item10 item11 /fs_driver/item11 item12 /fs_driver/item12 item15 /fs_driver/item15
v2: - renamed Fs to FS - in configure.ac script macro m4 is used - updates docs - created simple tests - updated virsh.pod - added information abot fspool in fotmatfs.html
Olga Krishtal (8): fspool: introduce filesystem pools API fspool: usual driver based implementation of filesystem pools API fspools: configuration and internal representation fspools: acl support for filesystem pools remote: filesystem pools driver implementation fspool: default implementation of filesystem pools virsh: filesystem pools commands fspools: docs and tests for fspool directory backend
configure.ac | 38 + daemon/Makefile.am | 4 + daemon/libvirtd.c | 10 + daemon/remote.c | 35 + docs/formatfs.html.in | 208 ++ docs/fspool.html.in | 41 + docs/schemas/fsitem.rng | 66 + docs/schemas/fspool.rng | 82 + docs/sitemap.html.in | 4 + include/libvirt/libvirt-fs.h | 260 +++ include/libvirt/libvirt.h | 1 + include/libvirt/virterror.h | 8 + m4/virt-driver-fspool.m4 | 52 + po/POTFILES.in | 6 + src/Makefile.am | 46 + src/access/viraccessdriver.h | 12 + src/access/viraccessdrivernop.c | 19 + src/access/viraccessdriverpolkit.c | 47 + src/access/viraccessdriverstack.c | 49 + src/access/viraccessmanager.c | 31 + src/access/viraccessmanager.h | 11 + src/access/viraccessperm.c | 15 +- src/access/viraccessperm.h | 124 ++ src/check-driverimpls.pl | 2 + src/conf/fs_conf.c | 1637 ++++++++++++++++ src/conf/fs_conf.h | 323 +++ src/datatypes.c | 154 ++ src/datatypes.h | 94 + src/driver-fs.h | 192 ++ src/driver.h | 3 + src/fs/fs_backend.h | 107 + src/fs/fs_backend_dir.c | 355 ++++ src/fs/fs_backend_dir.h | 8 + src/fs/fs_driver.c | 2058 ++++++++++++++++++++ src/fs/fs_driver.h | 10 + src/libvirt-fs.c | 1556 +++++++++++++++ src/libvirt.c | 28 + src/libvirt_private.syms | 53 + src/libvirt_public.syms | 42 + src/remote/remote_driver.c | 66 + src/remote/remote_protocol.x | 466 ++++- src/remote_protocol-structs | 165 ++ src/rpc/gendispatch.pl | 23 +- src/util/virerror.c | 37 + tests/Makefile.am | 12 + tests/fsitemxml2xmlin/item.xml | 13 + tests/fsitemxml2xmlout/item.xml | 13 + tests/fsitemxml2xmltest.c | 105 + .../dir-missing-target-path-invalid.xml | 12 + tests/fspoolxml2xmlin/fspool-dir.xml | 16 + tests/fspoolxml2xmlout/fspool-dir.xml | 16 + tests/fspoolxml2xmltest.c | 81 + tools/Makefile.am | 2 + tools/virsh-fsitem.c | 1292 ++++++++++++ tools/virsh-fsitem.h | 39 + tools/virsh-fspool.c | 1586 +++++++++++++++ tools/virsh-fspool.h | 38 + tools/virsh.c | 4 + tools/virsh.h | 9 + tools/virsh.pod | 252 ++- 60 files changed, 12028 insertions(+), 10 deletions(-) create mode 100644 docs/formatfs.html.in create mode 100644 docs/fspool.html.in create mode 100644 docs/schemas/fsitem.rng create mode 100644 docs/schemas/fspool.rng create mode 100644 include/libvirt/libvirt-fs.h create mode 100644 m4/virt-driver-fspool.m4 create mode 100644 src/conf/fs_conf.c create mode 100644 src/conf/fs_conf.h create mode 100644 src/driver-fs.h create mode 100644 src/fs/fs_backend.h create mode 100644 src/fs/fs_backend_dir.c create mode 100644 src/fs/fs_backend_dir.h create mode 100644 src/fs/fs_driver.c create mode 100644 src/fs/fs_driver.h create mode 100644 src/libvirt-fs.c create mode 100644 tests/fsitemxml2xmlin/item.xml create mode 100644 tests/fsitemxml2xmlout/item.xml create mode 100644 tests/fsitemxml2xmltest.c create mode 100644 tests/fspoolschemadata/dir-missing-target-path-invalid.xml create mode 100644 tests/fspoolxml2xmlin/fspool-dir.xml create mode 100644 tests/fspoolxml2xmlout/fspool-dir.xml create mode 100644 tests/fspoolxml2xmltest.c create mode 100644 tools/virsh-fsitem.c create mode 100644 tools/virsh-fsitem.h create mode 100644 tools/virsh-fspool.c create mode 100644 tools/virsh-fspool.h

On 09/21/2016 12:17 PM, Maxim Nestratov wrote:
20 сент. 2016 г., в 23:52, John Ferlan <jferlan@redhat.com> написал(а):
On 09/15/2016 03:32 AM, Olga Krishtal wrote: Hi everyone, we would like to propose the first implementation of fspool with directory backend.
Filesystem pools is a facility to manage filesystems resources similar to how storage pools manages volume resources. Furthermore new API follows storage API closely where it makes sense. Uploading/downloading operations are not defined yet as it is not obvious how to make it properly. I guess we can use some kind of tar to make a stream from a filesystem. Please share you thoughts on this particular issue.
So how do you differentiate between with the existing <pool type="fs">
Pool type=fs still provides volumes, i. e. block devices rather than filesystem, though this storage pool can mount file systems resided on a source block device.
http://libvirt.org/storage.html#StorageBackendFS
Sure the existing fs pool requires/uses a source block device as the source path and this new variant doesn't require that source but seems to use some item in order to dictate how to "define" the source on the fly. Currently only a "DIR" is created - so how does that differ from a "dir" pool.
Same here, storage "dir" provides files, which are in fact block devices for guests. While filesystem pool "dir" provides guests with file systems.
So then what is the purpose of providing a whole new storage driver subsystem? If you consider the existing storage driver is meant to handle storage pools of various types and the "pool" commands/API's are the "means" to manage those storage backend types, I'm still failing to see the advantage of (essentially) copying the storage driver when it seems you're really trying to write new backends that provide some specific functionality. Having a guest mount a host file system would seem to be possible through other means. I also start wondering about security implications for either side (haven't put too much thought into it). What can the guest put "on" the host file system and vice versa where different security policies may exist for allowing such placement. Perhaps rather than a large dump of code the RFC should state the goal, purpose, usage, etc. and see if that's what the community wants or is willing to provide feedback on. John
I think it'll be confusing to have and differentiate fspool and pool commands.
I didn't dig through all the patches, but from the few I did look at it seems as though all that's done is to rip out the guts of stuff not desired from the storage pool driver and replace it with this new code attributing all the work to the new author/copyright. IOW: Lots of places where StoragePool appears to be exactly the same as the FSPool.
I think you need to find a different means to do what you want. It's not 100% what the end goal is.
I did download/git am the patches and scan a few patches... * In patch 2 you've totally missed how to modify libvirt_public.syms. * In patch 3, the build breaks in "conf/fs_conf" since the "if { if {} }" aren't done properly in virFSPoolDefFormatBuf. * In patch 5 the remote_protocol_structs fails check/syntax-check... I stopped there in my build each patch test.
John
The patchset provides 'dir' backend which simply expose directories in some directory in host filesystem. The virsh commands are provided too. So it is ready to play with, just replace 'pool' in xml descriptions and virsh commands to 'fspool' and 'volume' to 'item'. Examle and usage: Define: virsh -c qemu:///system fspool-define-as fs_pool_name dir --target /path/on/host Build virsh -c qemu:///system fspool-build fs_pool_name Start virsh -c qemu:///system fspool-start fs_pool_name Look inside virsh -c qemu:///system fspool-list (--all) fspool_name
Fspool called POOL, on the host fs uses /fs_driver to hold items. virsh -c qemu:///system fspool-dumpxml POOL <fspool type='dir'> <name>POOL</name> <uuid>c57c9d7c-b1d5-4c45-ba9c-67f03d4da160</uuid> <capacity unit='bytes'>733722615808</capacity> <allocation unit='bytes'>1331486720</allocation> <available unit='bytes'>534810800128</available> <source> </source> <target> <path>/fs_driver</path> <permissions> <mode>0755</mode> <owner>0</owner> <group>0</group> </permissions> </target> </fspool>
virsh -c qemu:///system fspool-info POOL Name: POOL UUID: c57c9d7c-b1d5-4c45-ba9c-67f03d4da160 State: running Persistent: yes Autostart: no autostart Capacity: 683.33 GiB Allocation: 1.24 GiB Available: 498.08 GiB
virsh -c qemu+unix:///system item-list POOL Name Path ------------------------------------------------------------------------------ item1 /fs_driver/item1 item10 /fs_driver/item10 item11 /fs_driver/item11 item12 /fs_driver/item12 item15 /fs_driver/item15
Fspool of directory type is some directory on host fs that holds items (subdirs). Example of usage for items: virsh -c vz+unix:///system item-create-as POOL item1 1g - create item virsh -c qemu+unix:///system item-dumpxml item1 POOL <fsitem> <name>item1</name> <key>/fs_driver/item1</key> <source> </source> <capacity unit='bytes'>0</capacity> <allocation unit='bytes'>0</allocation> <target> <format type='dir'/> </target> </fsitem>
virsh -c qemu+unix:///system item-info item1 POOL Name: item1 Type: dir Capacity: 683.33 GiB Allocation: 634.87 MiB Autostart: no autostart Capacity: 683.33 GiB Allocation: 1.24 GiB Available: 498.08 GiB virsh -c qemu+unix:///system item-list POOL Name Path ------------------------------------------------------------------------------ item1 /fs_driver/item1 item10 /fs_driver/item10 item11 /fs_driver/item11 item12 /fs_driver/item12 item15 /fs_driver/item15
v2: - renamed Fs to FS - in configure.ac script macro m4 is used - updates docs - created simple tests - updated virsh.pod - added information abot fspool in fotmatfs.html
Olga Krishtal (8): fspool: introduce filesystem pools API fspool: usual driver based implementation of filesystem pools API fspools: configuration and internal representation fspools: acl support for filesystem pools remote: filesystem pools driver implementation fspool: default implementation of filesystem pools virsh: filesystem pools commands fspools: docs and tests for fspool directory backend
configure.ac | 38 + daemon/Makefile.am | 4 + daemon/libvirtd.c | 10 + daemon/remote.c | 35 + docs/formatfs.html.in | 208 ++ docs/fspool.html.in | 41 + docs/schemas/fsitem.rng | 66 + docs/schemas/fspool.rng | 82 + docs/sitemap.html.in | 4 + include/libvirt/libvirt-fs.h | 260 +++ include/libvirt/libvirt.h | 1 + include/libvirt/virterror.h | 8 + m4/virt-driver-fspool.m4 | 52 + po/POTFILES.in | 6 + src/Makefile.am | 46 + src/access/viraccessdriver.h | 12 + src/access/viraccessdrivernop.c | 19 + src/access/viraccessdriverpolkit.c | 47 + src/access/viraccessdriverstack.c | 49 + src/access/viraccessmanager.c | 31 + src/access/viraccessmanager.h | 11 + src/access/viraccessperm.c | 15 +- src/access/viraccessperm.h | 124 ++ src/check-driverimpls.pl | 2 + src/conf/fs_conf.c | 1637 ++++++++++++++++ src/conf/fs_conf.h | 323 +++ src/datatypes.c | 154 ++ src/datatypes.h | 94 + src/driver-fs.h | 192 ++ src/driver.h | 3 + src/fs/fs_backend.h | 107 + src/fs/fs_backend_dir.c | 355 ++++ src/fs/fs_backend_dir.h | 8 + src/fs/fs_driver.c | 2058 ++++++++++++++++++++ src/fs/fs_driver.h | 10 + src/libvirt-fs.c | 1556 +++++++++++++++ src/libvirt.c | 28 + src/libvirt_private.syms | 53 + src/libvirt_public.syms | 42 + src/remote/remote_driver.c | 66 + src/remote/remote_protocol.x | 466 ++++- src/remote_protocol-structs | 165 ++ src/rpc/gendispatch.pl | 23 +- src/util/virerror.c | 37 + tests/Makefile.am | 12 + tests/fsitemxml2xmlin/item.xml | 13 + tests/fsitemxml2xmlout/item.xml | 13 + tests/fsitemxml2xmltest.c | 105 + .../dir-missing-target-path-invalid.xml | 12 + tests/fspoolxml2xmlin/fspool-dir.xml | 16 + tests/fspoolxml2xmlout/fspool-dir.xml | 16 + tests/fspoolxml2xmltest.c | 81 + tools/Makefile.am | 2 + tools/virsh-fsitem.c | 1292 ++++++++++++ tools/virsh-fsitem.h | 39 + tools/virsh-fspool.c | 1586 +++++++++++++++ tools/virsh-fspool.h | 38 + tools/virsh.c | 4 + tools/virsh.h | 9 + tools/virsh.pod | 252 ++- 60 files changed, 12028 insertions(+), 10 deletions(-) create mode 100644 docs/formatfs.html.in create mode 100644 docs/fspool.html.in create mode 100644 docs/schemas/fsitem.rng create mode 100644 docs/schemas/fspool.rng create mode 100644 include/libvirt/libvirt-fs.h create mode 100644 m4/virt-driver-fspool.m4 create mode 100644 src/conf/fs_conf.c create mode 100644 src/conf/fs_conf.h create mode 100644 src/driver-fs.h create mode 100644 src/fs/fs_backend.h create mode 100644 src/fs/fs_backend_dir.c create mode 100644 src/fs/fs_backend_dir.h create mode 100644 src/fs/fs_driver.c create mode 100644 src/fs/fs_driver.h create mode 100644 src/libvirt-fs.c create mode 100644 tests/fsitemxml2xmlin/item.xml create mode 100644 tests/fsitemxml2xmlout/item.xml create mode 100644 tests/fsitemxml2xmltest.c create mode 100644 tests/fspoolschemadata/dir-missing-target-path-invalid.xml create mode 100644 tests/fspoolxml2xmlin/fspool-dir.xml create mode 100644 tests/fspoolxml2xmlout/fspool-dir.xml create mode 100644 tests/fspoolxml2xmltest.c create mode 100644 tools/virsh-fsitem.c create mode 100644 tools/virsh-fsitem.h create mode 100644 tools/virsh-fspool.c create mode 100644 tools/virsh-fspool.h

On Fri, Sep 23, 2016 at 11:38:10AM -0400, John Ferlan wrote:
On 09/21/2016 12:17 PM, Maxim Nestratov wrote:
20 сент. 2016 г., в 23:52, John Ferlan <jferlan@redhat.com> написал(а):
On 09/15/2016 03:32 AM, Olga Krishtal wrote: Hi everyone, we would like to propose the first implementation of fspool with directory backend.
Filesystem pools is a facility to manage filesystems resources similar to how storage pools manages volume resources. Furthermore new API follows storage API closely where it makes sense. Uploading/downloading operations are not defined yet as it is not obvious how to make it properly. I guess we can use some kind of tar to make a stream from a filesystem. Please share you thoughts on this particular issue.
So how do you differentiate between with the existing <pool type="fs">
Pool type=fs still provides volumes, i. e. block devices rather than filesystem, though this storage pool can mount file systems resided on a source block device.
http://libvirt.org/storage.html#StorageBackendFS
Sure the existing fs pool requires/uses a source block device as the source path and this new variant doesn't require that source but seems to use some item in order to dictate how to "define" the source on the fly. Currently only a "DIR" is created - so how does that differ from a "dir" pool.
Same here, storage "dir" provides files, which are in fact block devices for guests. While filesystem pool "dir" provides guests with file systems.
So then what is the purpose of providing a whole new storage driver subsystem? If you consider the existing storage driver is meant to handle storage pools of various types and the "pool" commands/API's are the "means" to manage those storage backend types, I'm still failing to see the advantage of (essentially) copying the storage driver when it seems you're really trying to write new backends that provide some specific functionality.
This is a completely different beast to the existing storage pools driver code. Ultimately the difference here is about what's being managed. The existing storage pools code is managing storage volumes, which are essentially blobs which are used to provide virtual disks. This FS pools code is about managing filesystem trees, which are essentially directories used to provide filesystem passthrough feature for guests, most compelling for containers. Trying to shoe-horn both concepts into the same API is really not very attractive, as you have fundamentally diffrent logic requirements for managing the entries in the respective pools
Having a guest mount a host file system would seem to be possible through other means. I also start wondering about security implications for either side (haven't put too much thought into it). What can the guest put "on" the host file system and vice versa where different security policies may exist for allowing such placement.
Perhaps rather than a large dump of code the RFC should state the goal, purpose, usage, etc. and see if that's what the community wants or is willing to provide feedback on.
This was previously done in the mailing list many months ago now. Regards, Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

On 09/23/2016 11:45 AM, Daniel P. Berrange wrote:
On Fri, Sep 23, 2016 at 11:38:10AM -0400, John Ferlan wrote:
On 09/21/2016 12:17 PM, Maxim Nestratov wrote:
20 сент. 2016 г., в 23:52, John Ferlan <jferlan@redhat.com> написал(а):
On 09/15/2016 03:32 AM, Olga Krishtal wrote: Hi everyone, we would like to propose the first implementation of fspool with directory backend.
Filesystem pools is a facility to manage filesystems resources similar to how storage pools manages volume resources. Furthermore new API follows storage API closely where it makes sense. Uploading/downloading operations are not defined yet as it is not obvious how to make it properly. I guess we can use some kind of tar to make a stream from a filesystem. Please share you thoughts on this particular issue.
So how do you differentiate between with the existing <pool type="fs">
Pool type=fs still provides volumes, i. e. block devices rather than filesystem, though this storage pool can mount file systems resided on a source block device.
http://libvirt.org/storage.html#StorageBackendFS
Sure the existing fs pool requires/uses a source block device as the source path and this new variant doesn't require that source but seems to use some item in order to dictate how to "define" the source on the fly. Currently only a "DIR" is created - so how does that differ from a "dir" pool.
Same here, storage "dir" provides files, which are in fact block devices for guests. While filesystem pool "dir" provides guests with file systems.
So then what is the purpose of providing a whole new storage driver subsystem? If you consider the existing storage driver is meant to handle storage pools of various types and the "pool" commands/API's are the "means" to manage those storage backend types, I'm still failing to see the advantage of (essentially) copying the storage driver when it seems you're really trying to write new backends that provide some specific functionality.
This is a completely different beast to the existing storage pools driver code.
Ultimately the difference here is about what's being managed. The existing storage pools code is managing storage volumes, which are essentially blobs which are used to provide virtual disks.
This FS pools code is about managing filesystem trees, which are essentially directories used to provide filesystem passthrough feature for guests, most compelling for containers.
Trying to shoe-horn both concepts into the same API is really not very attractive, as you have fundamentally diffrent logic requirements for managing the entries in the respective pools
OK fair enough, might be something worth of adding to the description (cover letter). Whether it's documented further in the patches I didn't check. The whole series is a 8 rather mostly large patches. I think the other point/concern is that it appears as if the fspool driver was started by copying storage_driver and then hacking out what wasn't needed, rather than deciding what needs to be there to build up such a fs pass through driver. At least that's my impression from scanning the first couple of patches. For example, patch 1 first file - libvirt-fs.h it's essentially libvirt-storage.h copied with some things removed. The first enum has all build and build overwrite flags. Doesn't seem any of those are used in the driver code on a quick scan. The create with build flag was added to allow someone to combine those pool-create and pool-build type functions because for some backends it doesn't make sense to "build" as it's already there. That to me seems like it could be a "design decision" for this new driver. Does a FS pool need to handle build with [no] overwrite? What would overwrite be? I think rather than just copy what the storage pool does, I would think the new driver could "build up" what it needs based on some consensus based on what makes sense for the usage model.
Having a guest mount a host file system would seem to be possible through other means. I also start wondering about security implications for either side (haven't put too much thought into it). What can the guest put "on" the host file system and vice versa where different security policies may exist for allowing such placement.
Perhaps rather than a large dump of code the RFC should state the goal, purpose, usage, etc. and see if that's what the community wants or is willing to provide feedback on.
This was previously done in the mailing list many months ago now.
Well a pointer would have been nice... Obviously I didn't remember it! There was an fspools v1 posted 8/19. I think there was an assumption that list readers/reviewers would remember some original RFC. I didn't. I've just been going through older patches that haven't had review and this just came up as "next" (actually I had started thinking about the v1 when v2 showed up). John

23-Sep-16 23:51, John Ferlan пишет: [snip]
I think rather than just copy what the storage pool does, I would think the new driver could "build up" what it needs based on some consensus based on what makes sense for the usage model.
Having a guest mount a host file system would seem to be possible through other means. I also start wondering about security implications for either side (haven't put too much thought into it). What can the guest put "on" the host file system and vice versa where different security policies may exist for allowing such placement.
Perhaps rather than a large dump of code the RFC should state the goal, purpose, usage, etc. and see if that's what the community wants or is willing to provide feedback on. This was previously done in the mailing list many months ago now.
Well a pointer would have been nice... Obviously I didn't remember it! There was an fspools v1 posted 8/19. I think there was an assumption that list readers/reviewers would remember some original RFC. I didn't. I've just been going through older patches that haven't had review and this just came up as "next" (actually I had started thinking about the v1 when v2 showed up).
John
Just a pointer to the previous disscussion: https://www.redhat.com/archives/libvir-list/2016-April/msg01941.html https://www.redhat.com/archives/libvir-list/2016-May/msg00208.html Maxim

20 сент. 2016 г., в 23:52, John Ferlan <jferlan@redhat.com> написал(а):
On 09/15/2016 03:32 AM, Olga Krishtal wrote: Hi everyone, we would like to propose the first implementation of fspool with directory backend.
Filesystem pools is a facility to manage filesystems resources similar to how storage pools manages volume resources. Furthermore new API follows storage API closely where it makes sense. Uploading/downloading operations are not defined yet as it is not obvious how to make it properly. I guess we can use some kind of tar to make a stream from a filesystem. Please share you thoughts on this particular issue.
So how do you differentiate between with the existing <pool type="fs"> Pool type=fs still provides volumes, i. e. block devices rather than filesystem, though this storage pool can mount file systems resided on a source block device.
http://libvirt.org/storage.html#StorageBackendFS
Sure the existing fs pool requires/uses a source block device as the source path and this new variant doesn't require that source but seems to use some item in order to dictate how to "define" the source on the fly. Currently only a "DIR" is created - so how does that differ from a "dir" pool.
Same here, storage "dir" provides files, which are in fact block devices for guests. While filesystem pool "dir" provides guests with file systems.
I think it'll be confusing to have and differentiate fspool and pool commands. Some time ago, we wrote the proposal description and asked for everyone's advice and opinion. The aim of fspool is to provide filesystems, not volumes. The simplest type of fspool is directory pool and it do has a lot in common with storage_backend_fs. However, in the
On 21/09/16 19:17, Maxim Nestratov wrote: proposal description we said that the plan is to use other backends: eg, storage volumes from storage pool as the source of fs, zfs, etc. The final api for fspool will be significantly different, because of the other backends needs.
I didn't dig through all the patches, but from the few I did look at it seems as though all that's done is to rip out the guts of stuff not desired from the storage pool driver and replace it with this new code attributing all the work to the new author/copyright. IOW: Lots of places where StoragePool appears to be exactly the same as the FSPool.
I think you need to find a different means to do what you want. It's not 100% what the end goal is.I did download/git am the patches and scan a few patches... * In patch 2 you've totally missed how to modify libvirt_public.syms * In patch 3, the build breaks in "conf/fs_conf" since the "if { if {} }" aren't done properly in virFSPoolDefFormatBuf. * In patch 5 the remote_protocol_structs fails check/syntax-check... I stopped there in my build each patch test. According to the guide I have to do: |make check|and|make syntax-check for every patch| And it was done. At libvirt_public.syms I will look one more time. John
The patchset provides 'dir' backend which simply expose directories in some directory in host filesystem. The virsh commands are provided too. So it is ready to play with, just replace 'pool' in xml descriptions and virsh commands to 'fspool' and 'volume' to 'item'. Examle and usage: Define: virsh -c qemu:///system fspool-define-as fs_pool_name dir --target /path/on/host Build virsh -c qemu:///system fspool-build fs_pool_name Start virsh -c qemu:///system fspool-start fs_pool_name Look inside virsh -c qemu:///system fspool-list (--all) fspool_name
Fspool called POOL, on the host fs uses /fs_driver to hold items. virsh -c qemu:///system fspool-dumpxml POOL <fspool type='dir'> <name>POOL</name> <uuid>c57c9d7c-b1d5-4c45-ba9c-67f03d4da160</uuid> <capacity unit='bytes'>733722615808</capacity> <allocation unit='bytes'>1331486720</allocation> <available unit='bytes'>534810800128</available> <source> </source> <target> <path>/fs_driver</path> <permissions> <mode>0755</mode> <owner>0</owner> <group>0</group> </permissions> </target> </fspool>
virsh -c qemu:///system fspool-info POOL Name: POOL UUID: c57c9d7c-b1d5-4c45-ba9c-67f03d4da160 State: running Persistent: yes Autostart: no autostart Capacity: 683.33 GiB Allocation: 1.24 GiB Available: 498.08 GiB
virsh -c qemu+unix:///system item-list POOL Name Path ------------------------------------------------------------------------------ item1 /fs_driver/item1 item10 /fs_driver/item10 item11 /fs_driver/item11 item12 /fs_driver/item12 item15 /fs_driver/item15
Fspool of directory type is some directory on host fs that holds items (subdirs). Example of usage for items: virsh -c vz+unix:///system item-create-as POOL item1 1g - create item virsh -c qemu+unix:///system item-dumpxml item1 POOL <fsitem> <name>item1</name> <key>/fs_driver/item1</key> <source> </source> <capacity unit='bytes'>0</capacity> <allocation unit='bytes'>0</allocation> <target> <format type='dir'/> </target> </fsitem>
virsh -c qemu+unix:///system item-info item1 POOL Name: item1 Type: dir Capacity: 683.33 GiB Allocation: 634.87 MiB Autostart: no autostart Capacity: 683.33 GiB Allocation: 1.24 GiB Available: 498.08 GiB virsh -c qemu+unix:///system item-list POOL Name Path ------------------------------------------------------------------------------ item1 /fs_driver/item1 item10 /fs_driver/item10 item11 /fs_driver/item11 item12 /fs_driver/item12 item15 /fs_driver/item15
v2: - renamed Fs to FS - in configure.ac script macro m4 is used - updates docs - created simple tests - updated virsh.pod - added information abot fspool in fotmatfs.html
Olga Krishtal (8): fspool: introduce filesystem pools API fspool: usual driver based implementation of filesystem pools API fspools: configuration and internal representation fspools: acl support for filesystem pools remote: filesystem pools driver implementation fspool: default implementation of filesystem pools virsh: filesystem pools commands fspools: docs and tests for fspool directory backend
configure.ac | 38 + daemon/Makefile.am | 4 + daemon/libvirtd.c | 10 + daemon/remote.c | 35 + docs/formatfs.html.in | 208 ++ docs/fspool.html.in | 41 + docs/schemas/fsitem.rng | 66 + docs/schemas/fspool.rng | 82 + docs/sitemap.html.in | 4 + include/libvirt/libvirt-fs.h | 260 +++ include/libvirt/libvirt.h | 1 + include/libvirt/virterror.h | 8 + m4/virt-driver-fspool.m4 | 52 + po/POTFILES.in | 6 + src/Makefile.am | 46 + src/access/viraccessdriver.h | 12 + src/access/viraccessdrivernop.c | 19 + src/access/viraccessdriverpolkit.c | 47 + src/access/viraccessdriverstack.c | 49 + src/access/viraccessmanager.c | 31 + src/access/viraccessmanager.h | 11 + src/access/viraccessperm.c | 15 +- src/access/viraccessperm.h | 124 ++ src/check-driverimpls.pl | 2 + src/conf/fs_conf.c | 1637 ++++++++++++++++ src/conf/fs_conf.h | 323 +++ src/datatypes.c | 154 ++ src/datatypes.h | 94 + src/driver-fs.h | 192 ++ src/driver.h | 3 + src/fs/fs_backend.h | 107 + src/fs/fs_backend_dir.c | 355 ++++ src/fs/fs_backend_dir.h | 8 + src/fs/fs_driver.c | 2058 ++++++++++++++++++++ src/fs/fs_driver.h | 10 + src/libvirt-fs.c | 1556 +++++++++++++++ src/libvirt.c | 28 + src/libvirt_private.syms | 53 + src/libvirt_public.syms | 42 + src/remote/remote_driver.c | 66 + src/remote/remote_protocol.x | 466 ++++- src/remote_protocol-structs | 165 ++ src/rpc/gendispatch.pl | 23 +- src/util/virerror.c | 37 + tests/Makefile.am | 12 + tests/fsitemxml2xmlin/item.xml | 13 + tests/fsitemxml2xmlout/item.xml | 13 + tests/fsitemxml2xmltest.c | 105 + .../dir-missing-target-path-invalid.xml | 12 + tests/fspoolxml2xmlin/fspool-dir.xml | 16 + tests/fspoolxml2xmlout/fspool-dir.xml | 16 + tests/fspoolxml2xmltest.c | 81 + tools/Makefile.am | 2 + tools/virsh-fsitem.c | 1292 ++++++++++++ tools/virsh-fsitem.h | 39 + tools/virsh-fspool.c | 1586 +++++++++++++++ tools/virsh-fspool.h | 38 + tools/virsh.c | 4 + tools/virsh.h | 9 + tools/virsh.pod | 252 ++- 60 files changed, 12028 insertions(+), 10 deletions(-) create mode 100644 docs/formatfs.html.in create mode 100644 docs/fspool.html.in create mode 100644 docs/schemas/fsitem.rng create mode 100644 docs/schemas/fspool.rng create mode 100644 include/libvirt/libvirt-fs.h create mode 100644 m4/virt-driver-fspool.m4 create mode 100644 src/conf/fs_conf.c create mode 100644 src/conf/fs_conf.h create mode 100644 src/driver-fs.h create mode 100644 src/fs/fs_backend.h create mode 100644 src/fs/fs_backend_dir.c create mode 100644 src/fs/fs_backend_dir.h create mode 100644 src/fs/fs_driver.c create mode 100644 src/fs/fs_driver.h create mode 100644 src/libvirt-fs.c create mode 100644 tests/fsitemxml2xmlin/item.xml create mode 100644 tests/fsitemxml2xmlout/item.xml create mode 100644 tests/fsitemxml2xmltest.c create mode 100644 tests/fspoolschemadata/dir-missing-target-path-invalid.xml create mode 100644 tests/fspoolxml2xmlin/fspool-dir.xml create mode 100644 tests/fspoolxml2xmlout/fspool-dir.xml create mode 100644 tests/fspoolxml2xmltest.c create mode 100644 tools/virsh-fsitem.c create mode 100644 tools/virsh-fsitem.h create mode 100644 tools/virsh-fspool.c create mode 100644 tools/virsh-fspool.h

On 09/23/2016 11:56 AM, Olga Krishtal wrote:
20 сент. 2016 г., в 23:52, John Ferlan <jferlan@redhat.com> написал(а):
On 09/15/2016 03:32 AM, Olga Krishtal wrote: Hi everyone, we would like to propose the first implementation of fspool with directory backend.
Filesystem pools is a facility to manage filesystems resources similar to how storage pools manages volume resources. Furthermore new API follows storage API closely where it makes sense. Uploading/downloading operations are not defined yet as it is not obvious how to make it properly. I guess we can use some kind of tar to make a stream from a filesystem. Please share you thoughts on this particular issue.
So how do you differentiate between with the existing <pool type="fs"> Pool type=fs still provides volumes, i. e. block devices rather than filesystem, though this storage pool can mount file systems resided on a source block device.
http://libvirt.org/storage.html#StorageBackendFS
Sure the existing fs pool requires/uses a source block device as the source path and this new variant doesn't require that source but seems to use some item in order to dictate how to "define" the source on the fly. Currently only a "DIR" is created - so how does that differ from a "dir" pool.
Same here, storage "dir" provides files, which are in fact block devices for guests. While filesystem pool "dir" provides guests with file systems.
I think it'll be confusing to have and differentiate fspool and pool commands. Some time ago, we wrote the proposal description and asked for everyone's advice and opinion. The aim of fspool is to provide filesystems, not volumes. The simplest type of fspool is directory pool and it do has a lot in common with storage_backend_fs. However, in the
On 21/09/16 19:17, Maxim Nestratov wrote: proposal description we said that the plan is to use other backends: eg, storage volumes from storage pool as the source of fs, zfs, etc. The final api for fspool will be significantly different, because of the other backends needs.
Can you please try to create an extra line after the paragraph you're responding to and the start of your paragraph and then one after. Anyway, as I pointed out - that description wasn't in my (short term) memory. Keeping a trail of pointers to previous stuff helps those that want to refresh their memory on the history. If you're going to "reuse" things, then using the 'src/util/*' is the way to go rather than trying to drag in storage_{driver|backend*} APIs. Crossing driver boundaries is something IIRC we try to avoid.
I didn't dig through all the patches, but from the few I did look at it seems as though all that's done is to rip out the guts of stuff not desired from the storage pool driver and replace it with this new code attributing all the work to the new author/copyright. IOW: Lots of places where StoragePool appears to be exactly the same as the FSPool.
I think you need to find a different means to do what you want. It's not 100% what the end goal is.I did download/git am the patches and scan a few patches... * In patch 2 you've totally missed how to modify libvirt_public.syms * In patch 3, the build breaks in "conf/fs_conf" since the "if { if {} }" aren't done properly in virFSPoolDefFormatBuf. * In patch 5 the remote_protocol_structs fails check/syntax-check... I stopped there in my build each patch test. According to the guide I have to do: |make check| and |make syntax-check for every patch|
Always a good plan!
And it was done.
And yet as we find out *all the time* some compilers complain more than others. Watch the list - we have a CI environment in which we find all sorts of oddities. In any case, the code in question is: + if (def->target.perms.mode != (mode_t) -1 || + def->target.perms.uid != (uid_t) -1 || + def->target.perms.gid != (gid_t) -1 || + def->target.perms.label) { + virBufferAddLit(buf, "<permissions>\n"); + virBufferAdjustIndent(buf, 2); + if (def->target.perms.mode != (mode_t) -1) + virBufferAsprintf(buf, "<mode>0%o</mode>\n", + def->target.perms.mode); + if (def->target.perms.uid != (uid_t) -1) + virBufferAsprintf(buf, "<owner>%d</owner>\n", + (int) def->target.perms.uid); + if (def->target.perms.gid != (gid_t) -1) + virBufferAsprintf(buf, "<group>%d</group>\n", + (int) def->target.perms.gid); + virBufferEscapeString(buf, "<label>%s</label>\n", + def->target.perms.label); + + virBufferAdjustIndent(buf, -2); + virBufferAddLit(buf, "</permissions>\n"); + } + + virBufferAdjustIndent(buf, -2); + virBufferAddLit(buf, "</target>\n"); So do you "see" the problem? The first if has an open {, but the second one doesn't, although the code is indented and would seemingly want to have one. The second one has a close }, which gives the impression something is missing.
At libvirt_public.syms I will look one more time.
You added all those API's into LIBVIRT_2.0.0 { above the "LIBVIRT_1.3.3", but you'll notice there's a LIBVIRT_2.2.0 { afterwards which all those API's should have gone "at least for now". IOW: When adding new API's you have to add to the version specific stanza. As of now you'd be adding API's to 2.3.0, but I doubt we'll make that cut-off. Just because you added them in 2.0.0 internally, once you go to upstream them - they need to be in the latest. So this brings me back to my other recent response. I think if we can figure out what the driver will need, then work through the external API portion. There's just so much going on - trying to get a sense of it all at once is well overwhelming. John [...]

On 24/09/16 00:12, John Ferlan wrote:
On 09/23/2016 11:56 AM, Olga Krishtal wrote:
20 сент. 2016 г., в 23:52, John Ferlan <jferlan@redhat.com> написал(а):
On 09/15/2016 03:32 AM, Olga Krishtal wrote: Hi everyone, we would like to propose the first implementation of fspool with directory backend.
Filesystem pools is a facility to manage filesystems resources similar to how storage pools manages volume resources. Furthermore new API follows storage API closely where it makes sense. Uploading/downloading operations are not defined yet as it is not obvious how to make it properly. I guess we can use some kind of tar to make a stream from a filesystem. Please share you thoughts on this particular issue. So how do you differentiate between with the existing <pool type="fs"> Pool type=fs still provides volumes, i. e. block devices rather than filesystem, though this storage pool can mount file systems resided on a source block device.
http://libvirt.org/storage.html#StorageBackendFS
Sure the existing fs pool requires/uses a source block device as the source path and this new variant doesn't require that source but seems to use some item in order to dictate how to "define" the source on the fly. Currently only a "DIR" is created - so how does that differ from a "dir" pool.
Same here, storage "dir" provides files, which are in fact block devices for guests. While filesystem pool "dir" provides guests with file systems.
I think it'll be confusing to have and differentiate fspool and pool commands. Some time ago, we wrote the proposal description and asked for everyone's advice and opinion. The aim of fspool is to provide filesystems, not volumes. The simplest type of fspool is directory pool and it do has a lot in common with storage_backend_fs. However, in the
On 21/09/16 19:17, Maxim Nestratov wrote: proposal description we said that the plan is to use other backends: eg, storage volumes from storage pool as the source of fs, zfs, etc. The final api for fspool will be significantly different, because of the other backends needs. Can you please try to create an extra line after the paragraph you're responding to and the start of your paragraph and then one after.
Thanks for noticing. It looks better.
Anyway, as I pointed out - that description wasn't in my (short term) memory. Keeping a trail of pointers to previous stuff helps those that want to refresh their memory on the history.
I will hold this links through the next versions. https://www.redhat.com/archives/libvir-list/2016-April/msg01941.html https://www.redhat.com/archives/libvir-list/2016-May/msg00208.html
If you're going to "reuse" things, then using the 'src/util/*' is the way to go rather than trying to drag in storage_{driver|backend*} APIs. Crossing driver boundaries is something IIRC we try to avoid.
As I have written before at the moment we have only one backend for fspool - directory. It is the simplest backend and only the starting point. I think that it is too early to decide which parts should be moved to src/util/*. Moreover, as fspool items and storage pool volumes are pretty different, it could be possible that they have very little in common. That said, I would leave things as they are, but if you insist I can try.
I didn't dig through all the patches, but from the few I did look at it seems as though all that's done is to rip out the guts of stuff not desired from the storage pool driver and replace it with this new code attributing all the work to the new author/copyright. IOW: Lots of places where StoragePool appears to be exactly the same as the FSPool.
I have written this lines as a part of GPLv2+ boilerplate: https://www.redhat.com/archives/libvir-list/2016-August/msg01160.html, which I took from other libvirt parts. And I guess it was naturally to change name and company, don't you? And again, if you insist I can leave out the author/copyright as it wasn't the aim of this series.
I think you need to find a different means to do what you want. It's not 100% what the end goal is.I did download/git am the patches and scan a few patches... * In patch 2 you've totally missed how to modify libvirt_public.syms * In patch 3, the build breaks in "conf/fs_conf" since the "if { if {} }" aren't done properly in virFSPoolDefFormatBuf. * In patch 5 the remote_protocol_structs fails check/syntax-check... I stopped there in my build each patch test.
According to the guide I have to do: |make check| and |make syntax-check for every patch| Always a good plan!
And it was done. And yet as we find out *all the time* some compilers complain more than others. Watch the list - we have a CI environment in which we find all sorts of oddities. In any case, the code in question is:
+ if (def->target.perms.mode != (mode_t) -1 || + def->target.perms.uid != (uid_t) -1 || + def->target.perms.gid != (gid_t) -1 || + def->target.perms.label) { + virBufferAddLit(buf, "<permissions>\n"); + virBufferAdjustIndent(buf, 2); + if (def->target.perms.mode != (mode_t) -1) + virBufferAsprintf(buf, "<mode>0%o</mode>\n", + def->target.perms.mode); + if (def->target.perms.uid != (uid_t) -1) + virBufferAsprintf(buf, "<owner>%d</owner>\n", + (int) def->target.perms.uid); + if (def->target.perms.gid != (gid_t) -1) + virBufferAsprintf(buf, "<group>%d</group>\n", + (int) def->target.perms.gid); + virBufferEscapeString(buf, "<label>%s</label>\n", + def->target.perms.label); + + virBufferAdjustIndent(buf, -2); + virBufferAddLit(buf, "</permissions>\n"); + } + + virBufferAdjustIndent(buf, -2); + virBufferAddLit(buf, "</target>\n");
So do you "see" the problem? The first if has an open {, but the second one doesn't, although the code is indented and would seemingly want to have one. The second one has a close }, which gives the impression something is missing.
Thanks for pointing this out. I will be more attentive.
At libvirt_public.syms I will look one more time. You added all those API's into LIBVIRT_2.0.0 { above the "LIBVIRT_1.3.3", but you'll notice there's a LIBVIRT_2.2.0 { afterwards which all those API's should have gone "at least for now".
IOW: When adding new API's you have to add to the version specific stanza. As of now you'd be adding API's to 2.3.0, but I doubt we'll make that cut-off.
Thanks. Will do.
Just because you added them in 2.0.0 internally, once you go to upstream them - they need to be in the latest.
So this brings me back to my other recent response. I think if we can figure out what the driver will need, then work through the external API portion. There's just so much going on - trying to get a sense of it all at once is well overwhelming.
Actually, I thought we have already figured it out and it was decided to have completely separate API to manage filesystems but similar to storage pool's, and only this approach let containers get the greatest benefit from it.
John
[...]
-- Best regards, Olga

07-Oct-16 20:09, Olga Krishtal пишет:
On 24/09/16 00:12, John Ferlan wrote:
On 09/23/2016 11:56 AM, Olga Krishtal wrote:
20 сент. 2016 г., в 23:52, John Ferlan<jferlan@redhat.com> написал(а):
On 09/15/2016 03:32 AM, Olga Krishtal wrote: Hi everyone, we would like to propose the first implementation of fspool with directory backend.
Filesystem pools is a facility to manage filesystems resources similar to how storage pools manages volume resources. Furthermore new API follows storage API closely where it makes sense. Uploading/downloading operations are not defined yet as it is not obvious how to make it properly. I guess we can use some kind of tar to make a stream from a filesystem. Please share you thoughts on this particular issue. So how do you differentiate between with the existing <pool type="fs"> Pool type=fs still provides volumes, i. e. block devices rather than filesystem, though this storage pool can mount file systems resided on a source block device.
http://libvirt.org/storage.html#StorageBackendFS
Sure the existing fs pool requires/uses a source block device as the source path and this new variant doesn't require that source but seems to use some item in order to dictate how to "define" the source on the fly. Currently only a "DIR" is created - so how does that differ from a "dir" pool.
Same here, storage "dir" provides files, which are in fact block devices for guests. While filesystem pool "dir" provides guests with file systems.
I think it'll be confusing to have and differentiate fspool and pool commands. Some time ago, we wrote the proposal description and asked for everyone's advice and opinion. The aim of fspool is to provide filesystems, not volumes. The simplest type of fspool is directory pool and it do has a lot in common with storage_backend_fs. However, in the
On 21/09/16 19:17, Maxim Nestratov wrote: proposal description we said that the plan is to use other backends: eg, storage volumes from storage pool as the source of fs, zfs, etc. The final api for fspool will be significantly different, because of the other backends needs. Can you please try to create an extra line after the paragraph you're responding to and the start of your paragraph and then one after.
Thanks for noticing. It looks better.
Anyway, as I pointed out - that description wasn't in my (short term) memory. Keeping a trail of pointers to previous stuff helps those that want to refresh their memory on the history.
I will hold this links through the next versions. https://www.redhat.com/archives/libvir-list/2016-April/msg01941.html https://www.redhat.com/archives/libvir-list/2016-May/msg00208.html
If you're going to "reuse" things, then using the 'src/util/*' is the way to go rather than trying to drag in storage_{driver|backend*} APIs. Crossing driver boundaries is something IIRC we try to avoid.
As I have written before at the moment we have only one backend for fspool - directory. It is the simplest backend and only the starting point. I think that it is too early to decide which parts should be moved to src/util/*. Moreover, as fspool items and storage pool volumes are pretty different, it could be possible that they have very little in common. That said, I would leave things as they are, but if you insist I can try.
If you meant the resulting code will have very little in common, then I would agree here. More backends implementation will show us where common parts are and we will have more basis for splitting out common parts.
I didn't dig through all the patches, but from the few I did look at it seems as though all that's done is to rip out the guts of stuff not desired from the storage pool driver and replace it with this new code attributing all the work to the new author/copyright. IOW: Lots of places where StoragePool appears to be exactly the same as the FSPool.
I have written this lines as a part of GPLv2+ boilerplate: https://www.redhat.com/archives/libvir-list/2016-August/msg01160.html, which I took from other libvirt parts. And I guess it was naturally to change name and company, don't you? And again, if you insist I can leave out the author/copyright as it wasn't the aim of this series.
Indeed, storage pool is very similar to FS pool but their items are not - volumes (block devices) versus filesystems (directory trees). And intention here was to introduce a *new API*, which is also very different from storage pool one, effectivly introducing a new driver. As driver boundaries crossing isn't favored, the code was simply borrowed, following earlier practice used by libvirt to get new drivers implemented. John, keeping all said above in mind, do you think it's worth trying to reuse common code while introducing a new API? It won't allow us to leave existing code untouched and it will increase the series even more.
I think you need to find a different means to do what you want. It's not 100% what the end goal is.I did download/git am the patches and scan a few patches... * In patch 2 you've totally missed how to modify libvirt_public.syms * In patch 3, the build breaks in "conf/fs_conf" since the "if { if {} }" aren't done properly in virFSPoolDefFormatBuf. * In patch 5 the remote_protocol_structs fails check/syntax-check... I stopped there in my build each patch test. According to the guide I have to do: |make check| and |make syntax-check for every patch| Always a good plan!
And it was done. And yet as we find out *all the time* some compilers complain more than others. Watch the list - we have a CI environment in which we find all sorts of oddities. In any case, the code in question is:
+ if (def->target.perms.mode != (mode_t) -1 || + def->target.perms.uid != (uid_t) -1 || + def->target.perms.gid != (gid_t) -1 || + def->target.perms.label) { + virBufferAddLit(buf, "<permissions>\n"); + virBufferAdjustIndent(buf, 2); + if (def->target.perms.mode != (mode_t) -1) + virBufferAsprintf(buf, "<mode>0%o</mode>\n", + def->target.perms.mode); + if (def->target.perms.uid != (uid_t) -1) + virBufferAsprintf(buf, "<owner>%d</owner>\n", + (int) def->target.perms.uid); + if (def->target.perms.gid != (gid_t) -1) + virBufferAsprintf(buf, "<group>%d</group>\n", + (int) def->target.perms.gid); + virBufferEscapeString(buf, "<label>%s</label>\n", + def->target.perms.label); + + virBufferAdjustIndent(buf, -2); + virBufferAddLit(buf, "</permissions>\n"); + } + + virBufferAdjustIndent(buf, -2); + virBufferAddLit(buf, "</target>\n");
So do you "see" the problem? The first if has an open {, but the second one doesn't, although the code is indented and would seemingly want to have one. The second one has a close }, which gives the impression something is missing.
Thanks for pointing this out. I will be more attentive.
At libvirt_public.syms I will look one more time. You added all those API's into LIBVIRT_2.0.0 { above the "LIBVIRT_1.3.3", but you'll notice there's a LIBVIRT_2.2.0 { afterwards which all those API's should have gone "at least for now".
IOW: When adding new API's you have to add to the version specific stanza. As of now you'd be adding API's to 2.3.0, but I doubt we'll make that cut-off.
Thanks. Will do.
Just because you added them in 2.0.0 internally, once you go to upstream them - they need to be in the latest.
So this brings me back to my other recent response. I think if we can figure out what the driver will need, then work through the external API portion. There's just so much going on - trying to get a sense of it all at once is well overwhelming.
Actually, I thought we have already figured it out and it was decided to have completely separate API to manage filesystems but similar to storage pool's, and only this approach let containers get the greatest benefit from it.
Let me share my thoughts here. The minimum that this new API needs is ability to: - define (create a persistent FS pool), - create (create a transisional FS pool), - build (for directory/remotefs pools it is simply directory creation, for block device backend, for instanse, it will be a mkfs call ) - start/stop (activate/deactivate), - create/delete items (subdirectories), - undefine, - maybe ability to use storage pool volumes as sources for FS pools. Create and build flags will control whether we should overwrite existing directory content or leave it untouched. But currently they are not used in code and it is really difficult to guess what is their purpose. This certainly should be fixed in the next revision of the series. Maxim
John
[...]
-- Best regards, Olga

[...]
I have written this lines as a part of GPLv2+ boilerplate: https://www.redhat.com/archives/libvir-list/2016-August/msg01160.html, which I took from other libvirt parts. And I guess it was naturally to change name and company, don't you? And again, if you insist I can leave out the author/copyright as it wasn't the aim of this series.
Indeed, storage pool is very similar to FS pool but their items are not - volumes (block devices) versus filesystems (directory trees). And intention here was to introduce a *new API*, which is also very different from storage pool one, effectivly introducing a new driver. As driver boundaries crossing isn't favored, the code was simply borrowed, following earlier practice used by libvirt to get new drivers implemented.
John, keeping all said above in mind, do you think it's worth trying to reuse common code while introducing a new API? It won't allow us to leave existing code untouched and it will increase the series even more.
Sorry - just haven't been able to keep up with my work and all the activity on this list lately. Here's my point - if you find yourself copying a function from one driver to another for the express purpose that you cannot cross driver boundaries, then perhaps your first thought should be - can the copied code can go in an existing or a new src/util/vir*.c file and be used in both places. I think there are synergies in the existing code... Another thought that occurs to me - I cannot recall if it's been mentioned in this context or not (short term memory isn't what it used to be!). IIUC the model is to use these trees more for containers, then I would hope the security is "built in" very tightly rather than being an added on after thought. One particularly thorny area is NFS storage pools (and well directory trees) especially w/r/t root_squash or not. In any case, I can only imagine that for container consumers - being "held" to certain security rules will be very important. One final thought, if the existing 'storage driver' is for BLOCK storage and this new driver is a 'File System Storage Driver', then rather than using 'fspool' (which is really confusing IMO) maybe the base driver is "fs_storage". So that means we create a "src/fs_storage" and start from there. The 'fs' was just not descriptive enough. Since it's a File System Storage Driver that's essentially exposing entire directories, is there really a need for a "pool" concept? Isn't the driver providing that alone? IOW: The implementation is a pool. What else other than a directory would something like this expose? Is there something else that could be envisioned that would add to the (from patch 1) virFSItemType? John Oh and please let's not drop 12K lines of new code into one series - please! Rome wasn't built in a day and certainly it's very hard to commit the time to review 12K lines of code in one series. A logical progression to the finish line is fine. [...]

On 14/10/16 16:15, John Ferlan wrote: > [...] > >>> I have written this lines as a part of GPLv2+ boilerplate: >>> https://www.redhat.com/archives/libvir-list/2016-August/msg01160.html, >>> which I took from >>> other libvirt parts. And I guess it was naturally to change name and >>> company, don't you? >>> And again, if you insist I can leave out the author/copyright as it >>> wasn't the aim of this series. >> Indeed, storage pool is very similar to FS pool but their items are not >> - volumes (block devices) >> versus filesystems (directory trees). And intention here was to >> introduce a *new API*, which is >> also very different from storage pool one, effectivly introducing a new >> driver. As driver >> boundaries crossing isn't favored, the code was simply borrowed, >> following earlier practice used >> by libvirt to get new drivers implemented. >> >> John, keeping all said above in mind, do you think it's worth trying to >> reuse common code while >> introducing a new API? It won't allow us to leave existing code >> untouched and it will increase the >> series even more. >> > Sorry - just haven't been able to keep up with my work and all the > activity on this list lately. > > Here's my point - if you find yourself copying a function from one > driver to another for the express purpose that you cannot cross driver > boundaries, then perhaps your first thought should be - can the copied > code can go in an existing or a new src/util/vir*.c file and be used in > both places. I think there are synergies in the existing code... Then, I guess, the first think that I should do - src/util/vir*.c for both drivers. I try to do it in the next version. > Another thought that occurs to me - I cannot recall if it's been > mentioned in this context or not (short term memory isn't what it used > to be!). IIUC the model is to use these trees more for containers, then > I would hope the security is "built in" very tightly rather than being > an added on after thought. One particularly thorny area is NFS storage > pools (and well directory trees) especially w/r/t root_squash or not. In > any case, I can only imagine that for container consumers - being "held" > to certain security rules will be very important. Good point, thanks! I need some time to think it over. > > One final thought, if the existing 'storage driver' is for BLOCK storage > and this new driver is a 'File System Storage Driver', then rather than > using 'fspool' (which is really confusing IMO) maybe the base driver is > "fs_storage". So that means we create a "src/fs_storage" and start from > there. The 'fs' was just not descriptive enough. I agree. > Since it's a File System Storage Driver that's essentially exposing > entire directories, is there really a need for a "pool" concept? Isn't > the driver providing that alone? IOW: The implementation is a pool. > What else other than a directory would something like this expose? Is > there something else that could be envisioned that would add to the > (from patch 1) virFSItemType? For directory fspool item object may seem meaningless, however for zfs and virtuozzo storage, I think, it doesn't: pool can be mounted, can have some restrictions and items is the object that is exposed to costumer. I mean for zfs - we create pool, than create filesystems with different quotas, etc. For Virtuozzo storage - the entity that is exposed - is cluster. The administrator manipulations - is to mount cluster, and users are able to create items for their needs. > > John > > Oh and please let's not drop 12K lines of new code into one series - > please! Rome wasn't built in a day and certainly it's very hard to > commit the time to review 12K lines of code in one series. A logical > progression to the finish line is fine. I will split this series. > > > [...] -- Best regards, Olga
participants (4)
-
Daniel P. Berrange
-
John Ferlan
-
Maxim Nestratov
-
Olga Krishtal