mirror of https://gitee.com/openkylin/libvirt.git
Add a sheepdog backend for the storage driver
This patch brings support to manage sheepdog pools and volumes to libvirt. It uses the "collie" command-line utility that comes with sheepdog for that. A sheepdog pool in libvirt maps to a sheepdog cluster. It needs a host and port to connect to, which in most cases is just going to be the default of localhost on port 7000. A sheepdog volume in libvirt maps to a sheepdog vdi. To create one specify the pool, a name and the capacity. Volumes can also be resized later. In the volume XML the vdi name has to be put into the <target><path>. To use the volume as a disk source for virtual machines specify the vdi name as "name" attribute of the <source>. The host and port information from the pool are specified inside the host tag. <disk type='network'> ... <source protocol="sheepdog" name="vdi_name"> <host name="localhost" port="7000"/> </source> </disk> To work right this patch parses the output of collie, so it relies on the raw output option. There recently was a bug which caused size information to be reported wrong. This is fixed upstream already and will be in the next release. Signed-off-by: Sebastian Wiedenroth <wiedi@frubar.net>
This commit is contained in:
parent
e00184291e
commit
29bc4fe646
|
@ -145,6 +145,7 @@
|
|||
/tests/sockettest
|
||||
/tests/ssh
|
||||
/tests/statstest
|
||||
/tests/storagebackendsheepdogtest
|
||||
/tests/utiltest
|
||||
/tests/virauthconfigtest
|
||||
/tests/virbuftest
|
||||
|
|
2
AUTHORS
2
AUTHORS
|
@ -249,7 +249,7 @@ Patches have also been contributed by:
|
|||
Eiichi Tsukata <eiichi.tsukata.xh@hitachi.com>
|
||||
Sascha Peilicke <saschpe@suse.de>
|
||||
Chuck Short <chuck.short@canonical.com>
|
||||
|
||||
Sebastian Wiedenroth <wiedi@frubar.net>
|
||||
[....send patches to get your name here....]
|
||||
|
||||
The libvirt Logo was designed by Diana Fong
|
||||
|
|
32
configure.ac
32
configure.ac
|
@ -1825,6 +1825,8 @@ AC_ARG_WITH([storage-disk],
|
|||
AC_HELP_STRING([--with-storage-disk], [with GPartd Disk backend for the storage driver @<:@default=check@:>@]),[],[with_storage_disk=check])
|
||||
AC_ARG_WITH([storage-rbd],
|
||||
AC_HELP_STRING([--with-storage-rbd], [with RADOS Block Device backend for the storage driver @<:@default=check@:>@]),[],[with_storage_rbd=check])
|
||||
AC_ARG_WITH([storage-sheepdog],
|
||||
AC_HELP_STRING([--with-storage-sheepdog], [with Sheepdog backend for the storage driver @<:@default=check@:>@]),[],[with_storage_sheepdog=check])
|
||||
|
||||
if test "$with_libvirtd" = "no"; then
|
||||
with_storage_dir=no
|
||||
|
@ -1835,6 +1837,7 @@ if test "$with_libvirtd" = "no"; then
|
|||
with_storage_mpath=no
|
||||
with_storage_disk=no
|
||||
with_storage_rbd=no
|
||||
with_storage_sheepdog=no
|
||||
fi
|
||||
if test "$with_storage_dir" = "yes" ; then
|
||||
AC_DEFINE_UNQUOTED([WITH_STORAGE_DIR], 1, [whether directory backend for storage driver is enabled])
|
||||
|
@ -2009,6 +2012,34 @@ fi
|
|||
AM_CONDITIONAL([WITH_STORAGE_RBD], [test "$with_storage_rbd" = "yes"])
|
||||
AC_SUBST([LIBRBD_LIBS])
|
||||
|
||||
if test "$with_storage_sheepdog" = "yes" ||
|
||||
test "$with_storage_sheepdog" = "check"; then
|
||||
AC_PATH_PROG([COLLIE], [collie], [], [$PATH:/sbin:/usr/sbin])
|
||||
|
||||
if test "$with_storage_sheepdog" = "yes"; then
|
||||
if test -z "$COLLIE"; then
|
||||
AC_MSG_ERROR([We need collie for Sheepdog storage driver])
|
||||
fi
|
||||
else
|
||||
if test -z "$COLLIE"; then
|
||||
with_storage_sheepdog=no
|
||||
fi
|
||||
|
||||
if test "$with_storage_sheepdog" = "check"; then
|
||||
with_storage_sheepdog=yes
|
||||
fi
|
||||
fi
|
||||
|
||||
if test "$with_storage_sheepdog" = "yes"; then
|
||||
AC_DEFINE_UNQUOTED([WITH_STORAGE_SHEEPDOG], 1,
|
||||
[whether Sheepdog backend for storage driver is enabled])
|
||||
AC_DEFINE_UNQUOTED([COLLIE],["$COLLIE"],[Location of collie program])
|
||||
fi
|
||||
fi
|
||||
AM_CONDITIONAL([WITH_STORAGE_SHEEPDOG],
|
||||
[test "$with_storage_sheepdog" = "yes"])
|
||||
|
||||
|
||||
LIBPARTED_CFLAGS=
|
||||
LIBPARTED_LIBS=
|
||||
if test "$with_storage_disk" = "yes" ||
|
||||
|
@ -2824,6 +2855,7 @@ AC_MSG_NOTICE([ SCSI: $with_storage_scsi])
|
|||
AC_MSG_NOTICE([ mpath: $with_storage_mpath])
|
||||
AC_MSG_NOTICE([ Disk: $with_storage_disk])
|
||||
AC_MSG_NOTICE([ RBD: $with_storage_rbd])
|
||||
AC_MSG_NOTICE([Sheepdog: $with_storage_sheepdog])
|
||||
AC_MSG_NOTICE([])
|
||||
AC_MSG_NOTICE([Security Drivers])
|
||||
AC_MSG_NOTICE([])
|
||||
|
|
|
@ -43,6 +43,7 @@
|
|||
<li><strong><a href="storage.html#StorageBackendSCSI">SCSI backend</a></strong></li>
|
||||
<li><strong><a href="storage.html#StorageBackendMultipath">Multipath backend</a></strong></li>
|
||||
<li><strong><a href="storage.html#StorageBackendRBD">RBD (RADOS Block Device) backend</a></strong></li>
|
||||
<li><strong><a href="storage.html#StorageBackendSheepdog">Sheepdog backend</a></strong></li>
|
||||
</ul>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
<ref name='poolscsi'/>
|
||||
<ref name='poolmpath'/>
|
||||
<ref name='poolrbd'/>
|
||||
<ref name='poolsheepdog'/>
|
||||
</choice>
|
||||
</element>
|
||||
</define>
|
||||
|
@ -115,6 +116,15 @@
|
|||
<ref name='sourcerbd'/>
|
||||
</define>
|
||||
|
||||
<define name='poolsheepdog'>
|
||||
<attribute name='type'>
|
||||
<value>sheepdog</value>
|
||||
</attribute>
|
||||
<ref name='commonmetadata'/>
|
||||
<ref name='sizing'/>
|
||||
<ref name='sourcesheepdog'/>
|
||||
</define>
|
||||
|
||||
<define name='sourceinfovendor'>
|
||||
<optional>
|
||||
<element name='vendor'>
|
||||
|
@ -496,6 +506,13 @@
|
|||
</element>
|
||||
</define>
|
||||
|
||||
<define name='sourcesheepdog'>
|
||||
<element name='source'>
|
||||
<ref name='sourceinfohost'/>
|
||||
<ref name='sourceinfoname'/>
|
||||
</element>
|
||||
</define>
|
||||
|
||||
<define name='name'>
|
||||
<data type='string'>
|
||||
<param name="pattern">[a-zA-Z0-9_\+\-]+</param>
|
||||
|
|
|
@ -67,7 +67,7 @@
|
|||
<element name='target'>
|
||||
<optional>
|
||||
<element name='path'>
|
||||
<ref name='absFilePath'/>
|
||||
<data type='anyURI'/>
|
||||
</element>
|
||||
</optional>
|
||||
<ref name='format'/>
|
||||
|
@ -144,6 +144,7 @@
|
|||
|
||||
<define name='formatfile'>
|
||||
<choice>
|
||||
<value>unknown</value>
|
||||
<value>raw</value>
|
||||
<value>dir</value>
|
||||
<value>bochs</value>
|
||||
|
|
|
@ -110,6 +110,9 @@
|
|||
<li>
|
||||
<a href="#StorageBackendRBD">RBD (RADOS Block Device) backend</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#StorageBackendSheepdog">Sheepdog backend</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h2><a name="StorageBackendDir">Directory pool</a></h2>
|
||||
|
@ -565,5 +568,64 @@
|
|||
The RBD pool does not use the volume format type element.
|
||||
</p>
|
||||
|
||||
<h2><a name="StorageBackendSheepdog">Sheepdog pools</a></h2>
|
||||
<p>
|
||||
This provides a pool based on a Sheepdog Cluster.
|
||||
Sheepdog is a distributed storage system for QEMU/KVM.
|
||||
It provides highly available block level storage volumes that
|
||||
can be attached to QEMU/KVM virtual machines.
|
||||
|
||||
The cluster must already be formatted.
|
||||
|
||||
<span class="since">Since 0.9.13</span>
|
||||
</p>
|
||||
|
||||
<h3>Example pool input</h3>
|
||||
<pre>
|
||||
<pool type="sheepdog">
|
||||
<name>mysheeppool</name>
|
||||
<source>
|
||||
<name>mysheeppool</name>
|
||||
<host name='localhost' port='7000'/>
|
||||
</source>
|
||||
</pool></pre>
|
||||
|
||||
<h3>Example volume output</h3>
|
||||
<pre>
|
||||
<volume>
|
||||
<name>myvol</name>
|
||||
<key>sheep/myvol</key>
|
||||
<source>
|
||||
</source>
|
||||
<capacity unit='bytes'>53687091200</capacity>
|
||||
<allocation unit='bytes'>53687091200</allocation>
|
||||
<target>
|
||||
<path>sheepdog:myvol</path>
|
||||
<format type='unknown'/>
|
||||
<permissions>
|
||||
<mode>00</mode>
|
||||
<owner>0</owner>
|
||||
<group>0</group>
|
||||
</permissions>
|
||||
</target>
|
||||
</volume></pre>
|
||||
|
||||
<h3>Example disk attachment</h3>
|
||||
<p>Sheepdog images can be attached to Qemu guests.
|
||||
Information about attaching a Sheepdog image to a
|
||||
guest can be found
|
||||
at the <a href="formatdomain.html#elementsDisks">format domain</a>
|
||||
page.</p>
|
||||
|
||||
<h3>Valid pool format types</h3>
|
||||
<p>
|
||||
The Sheepdog pool does not use the pool format type element.
|
||||
</p>
|
||||
|
||||
<h3>Valid volume format types</h3>
|
||||
<p>
|
||||
The Sheepdog pool does not use the volume format type element.
|
||||
</p>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -69,19 +69,24 @@
|
|||
%define with_xenapi 0%{!?_without_xenapi:1}
|
||||
|
||||
# Then the secondary host drivers, which run inside libvirtd
|
||||
%define with_network 0%{!?_without_network:%{server_drivers}}
|
||||
%define with_storage_fs 0%{!?_without_storage_fs:%{server_drivers}}
|
||||
%define with_storage_lvm 0%{!?_without_storage_lvm:%{server_drivers}}
|
||||
%define with_storage_iscsi 0%{!?_without_storage_iscsi:%{server_drivers}}
|
||||
%define with_storage_disk 0%{!?_without_storage_disk:%{server_drivers}}
|
||||
%define with_storage_mpath 0%{!?_without_storage_mpath:%{server_drivers}}
|
||||
%define with_network 0%{!?_without_network:%{server_drivers}}
|
||||
%define with_storage_fs 0%{!?_without_storage_fs:%{server_drivers}}
|
||||
%define with_storage_lvm 0%{!?_without_storage_lvm:%{server_drivers}}
|
||||
%define with_storage_iscsi 0%{!?_without_storage_iscsi:%{server_drivers}}
|
||||
%define with_storage_disk 0%{!?_without_storage_disk:%{server_drivers}}
|
||||
%define with_storage_mpath 0%{!?_without_storage_mpath:%{server_drivers}}
|
||||
%if 0%{?fedora} >= 16
|
||||
%define with_storage_rbd 0%{!?_without_storage_rbd:%{server_drivers}}
|
||||
%define with_storage_rbd 0%{!?_without_storage_rbd:%{server_drivers}}
|
||||
%else
|
||||
%define with_storage_rbd 0
|
||||
%define with_storage_rbd 0
|
||||
%endif
|
||||
%define with_numactl 0%{!?_without_numactl:%{server_drivers}}
|
||||
%define with_selinux 0%{!?_without_selinux:%{server_drivers}}
|
||||
%if 0%{?fedora} >= 17
|
||||
%define with_storage_sheepdog 0%{!?_without_storage_sheepdog:%{server_drivers}}
|
||||
%else
|
||||
%define with_storage_sheepdog 0
|
||||
%endif
|
||||
%define with_numactl 0%{!?_without_numactl:%{server_drivers}}
|
||||
%define with_selinux 0%{!?_without_selinux:%{server_drivers}}
|
||||
|
||||
# A few optional bits off by default, we enable later
|
||||
%define with_polkit 0%{!?_without_polkit:0}
|
||||
|
@ -224,6 +229,7 @@
|
|||
%define with_storage_iscsi 0
|
||||
%define with_storage_mpath 0
|
||||
%define with_storage_rbd 0
|
||||
%define with_storage_sheepdog 0
|
||||
%define with_storage_disk 0
|
||||
%endif
|
||||
|
||||
|
@ -603,6 +609,10 @@ Requires: device-mapper
|
|||
# For RBD support
|
||||
Requires: ceph
|
||||
%endif
|
||||
%if %{with_storage_sheepdog}
|
||||
# For Sheepdog support
|
||||
Requires: sheepdog
|
||||
%endif
|
||||
%if %{with_cgconfig}
|
||||
Requires: libcgroup
|
||||
%endif
|
||||
|
@ -1080,6 +1090,10 @@ of recent versions of Linux (and other OSes).
|
|||
%define _without_storage_rbd --without-storage-rbd
|
||||
%endif
|
||||
|
||||
%if ! %{with_storage_sheepdog}
|
||||
%define _without_storage_sheepdog --without-storage-sheepdog
|
||||
%endif
|
||||
|
||||
%if ! %{with_numactl}
|
||||
%define _without_numactl --without-numactl
|
||||
%endif
|
||||
|
@ -1178,6 +1192,7 @@ autoreconf -if
|
|||
%{?_without_storage_disk} \
|
||||
%{?_without_storage_mpath} \
|
||||
%{?_without_storage_rbd} \
|
||||
%{?_without_storage_sheepdog} \
|
||||
%{?_without_numactl} \
|
||||
%{?_without_numad} \
|
||||
%{?_without_capng} \
|
||||
|
|
|
@ -108,6 +108,7 @@ src/storage/storage_backend_logical.c
|
|||
src/storage/storage_backend_mpath.c
|
||||
src/storage/storage_backend_rbd.c
|
||||
src/storage/storage_backend_scsi.c
|
||||
src/storage/storage_backend_sheepdog.c
|
||||
src/storage/storage_driver.c
|
||||
src/test/test_driver.c
|
||||
src/uml/uml_conf.c
|
||||
|
|
|
@ -515,6 +515,9 @@ STORAGE_DRIVER_DISK_SOURCES = \
|
|||
STORAGE_DRIVER_RBD_SOURCES = \
|
||||
storage/storage_backend_rbd.h storage/storage_backend_rbd.c
|
||||
|
||||
STORAGE_DRIVER_SHEEPDOG_SOURCES = \
|
||||
storage/storage_backend_sheepdog.h storage/storage_backend_sheepdog.c
|
||||
|
||||
STORAGE_HELPER_DISK_SOURCES = \
|
||||
storage/parthelper.c
|
||||
|
||||
|
@ -1018,6 +1021,10 @@ libvirt_driver_storage_la_SOURCES += $(STORAGE_DRIVER_RBD_SOURCES)
|
|||
libvirt_driver_storage_la_LIBADD += $(LIBRBD_LIBS)
|
||||
endif
|
||||
|
||||
if WITH_STORAGE_SHEEPDOG
|
||||
libvirt_driver_storage_la_SOURCES += $(STORAGE_DRIVER_SHEEPDOG_SOURCES)
|
||||
endif
|
||||
|
||||
if WITH_NODE_DEVICES
|
||||
# Needed to keep automake quiet about conditionals
|
||||
if WITH_DRIVER_MODULES
|
||||
|
@ -1116,6 +1123,7 @@ EXTRA_DIST += \
|
|||
$(STORAGE_DRIVER_MPATH_SOURCES) \
|
||||
$(STORAGE_DRIVER_DISK_SOURCES) \
|
||||
$(STORAGE_DRIVER_RBD_SOURCES) \
|
||||
$(STORAGE_DRIVER_SHEEPDOG_SOURCES) \
|
||||
$(NODE_DEVICE_DRIVER_SOURCES) \
|
||||
$(NODE_DEVICE_DRIVER_HAL_SOURCES) \
|
||||
$(NODE_DEVICE_DRIVER_UDEV_SOURCES) \
|
||||
|
|
|
@ -54,7 +54,7 @@ VIR_ENUM_IMPL(virStoragePool,
|
|||
VIR_STORAGE_POOL_LAST,
|
||||
"dir", "fs", "netfs",
|
||||
"logical", "disk", "iscsi",
|
||||
"scsi", "mpath", "rbd")
|
||||
"scsi", "mpath", "rbd", "sheepdog")
|
||||
|
||||
VIR_ENUM_IMPL(virStoragePoolFormatFileSystem,
|
||||
VIR_STORAGE_POOL_FS_LAST,
|
||||
|
@ -208,6 +208,17 @@ static virStoragePoolTypeInfo poolTypeInfo[] = {
|
|||
.formatToString = virStoragePoolFormatDiskTypeToString,
|
||||
}
|
||||
},
|
||||
{ .poolType = VIR_STORAGE_POOL_SHEEPDOG,
|
||||
.poolOptions = {
|
||||
.flags = (VIR_STORAGE_POOL_SOURCE_HOST |
|
||||
VIR_STORAGE_POOL_SOURCE_NETWORK |
|
||||
VIR_STORAGE_POOL_SOURCE_NAME),
|
||||
},
|
||||
.volOptions = {
|
||||
.defaultFormat = VIR_STORAGE_FILE_RAW,
|
||||
.formatToString = virStoragePoolFormatDiskTypeToString,
|
||||
}
|
||||
},
|
||||
{ .poolType = VIR_STORAGE_POOL_MPATH,
|
||||
.volOptions = {
|
||||
.formatToString = virStoragePoolFormatDiskTypeToString,
|
||||
|
@ -1014,9 +1025,9 @@ virStoragePoolDefFormat(virStoragePoolDefPtr def) {
|
|||
if (virStoragePoolSourceFormat(&buf, options, &def->source) < 0)
|
||||
goto cleanup;
|
||||
|
||||
/* RBD devices are not local block devs nor files, so it doesn't
|
||||
/* RBD and Sheepdog devices are not local block devs nor files, so it doesn't
|
||||
* have a target */
|
||||
if (def->type != VIR_STORAGE_POOL_RBD) {
|
||||
if (def->type != VIR_STORAGE_POOL_RBD && def->type != VIR_STORAGE_POOL_SHEEPDOG) {
|
||||
virBufferAddLit(&buf," <target>\n");
|
||||
|
||||
if (def->target.path)
|
||||
|
@ -1301,7 +1312,7 @@ virStorageVolDefFormat(virStoragePoolDefPtr pool,
|
|||
|
||||
virBufferAddLit(&buf, "<volume>\n");
|
||||
virBufferAsprintf(&buf," <name>%s</name>\n", def->name);
|
||||
virBufferAsprintf(&buf," <key>%s</key>\n", def->key);
|
||||
virBufferAsprintf(&buf," <key>%s</key>\n", def->key ? def->key : "(null)");
|
||||
virBufferAddLit(&buf, " <source>\n");
|
||||
|
||||
if (def->source.nextent) {
|
||||
|
|
|
@ -121,6 +121,7 @@ enum virStoragePoolType {
|
|||
VIR_STORAGE_POOL_SCSI, /* SCSI HBA */
|
||||
VIR_STORAGE_POOL_MPATH, /* Multipath devices */
|
||||
VIR_STORAGE_POOL_RBD, /* RADOS Block Device */
|
||||
VIR_STORAGE_POOL_SHEEPDOG, /* Sheepdog device */
|
||||
|
||||
VIR_STORAGE_POOL_LAST,
|
||||
};
|
||||
|
|
|
@ -79,6 +79,9 @@
|
|||
#if WITH_STORAGE_RBD
|
||||
# include "storage_backend_rbd.h"
|
||||
#endif
|
||||
#if WITH_STORAGE_SHEEPDOG
|
||||
# include "storage_backend_sheepdog.h"
|
||||
#endif
|
||||
|
||||
#define VIR_FROM_THIS VIR_FROM_STORAGE
|
||||
|
||||
|
@ -107,6 +110,9 @@ static virStorageBackendPtr backends[] = {
|
|||
#endif
|
||||
#if WITH_STORAGE_RBD
|
||||
&virStorageBackendRBD,
|
||||
#endif
|
||||
#if WITH_STORAGE_SHEEPDOG
|
||||
&virStorageBackendSheepdog,
|
||||
#endif
|
||||
NULL
|
||||
};
|
||||
|
|
|
@ -0,0 +1,311 @@
|
|||
/*
|
||||
* storage_backend_sheepdog.c: storage backend for Sheepdog handling
|
||||
*
|
||||
* Copyright (C) 2012 Wido den Hollander
|
||||
* Copyright (C) 2012 Frank Spijkerman
|
||||
* Copyright (C) 2012 Sebastian Wiedenroth
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
* Author: Wido den Hollander <wido@widodh.nl>
|
||||
* Frank Spijkerman <frank.spijkerman@avira.com>
|
||||
* Sebastian Wiedenroth <sebastian.wiedenroth@skylime.net>
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include "virterror_internal.h"
|
||||
#include "storage_backend_sheepdog.h"
|
||||
#include "storage_conf.h"
|
||||
#include "util/command.h"
|
||||
#include "util.h"
|
||||
#include "memory.h"
|
||||
#include "logging.h"
|
||||
|
||||
#define VIR_FROM_THIS VIR_FROM_STORAGE
|
||||
|
||||
static int virStorageBackendSheepdogRefreshVol(virConnectPtr conn,
|
||||
virStoragePoolObjPtr pool,
|
||||
virStorageVolDefPtr vol);
|
||||
|
||||
void virStorageBackendSheepdogAddHostArg(virCommandPtr cmd,
|
||||
virStoragePoolObjPtr pool);
|
||||
|
||||
int
|
||||
virStorageBackendSheepdogParseNodeInfo(virStoragePoolDefPtr pool,
|
||||
char *output)
|
||||
{
|
||||
/* fields:
|
||||
* node id/total, size, used, use%, [total vdi size]
|
||||
*
|
||||
* example output:
|
||||
* 0 15245667872 117571104 0%
|
||||
* Total 15245667872 117571104 0% 20972341
|
||||
*/
|
||||
|
||||
const char *p, *next;
|
||||
|
||||
pool->allocation = pool->capacity = pool->available = 0;
|
||||
|
||||
p = output;
|
||||
do {
|
||||
char *end;
|
||||
|
||||
if ((next = strchr(p, '\n')))
|
||||
++next;
|
||||
else
|
||||
return -1;
|
||||
|
||||
if (!STRPREFIX(p, "Total "))
|
||||
continue;
|
||||
|
||||
p = p + 6;
|
||||
|
||||
if (virStrToLong_ull(p, &end, 10, &pool->capacity) < 0)
|
||||
return -1;
|
||||
|
||||
if ((p = end + 1) > next)
|
||||
return -1;
|
||||
|
||||
if (virStrToLong_ull(p, &end, 10, &pool->allocation) < 0)
|
||||
return -1;
|
||||
|
||||
pool->available = pool->capacity - pool->allocation;
|
||||
return 0;
|
||||
|
||||
} while ((p = next));
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void
|
||||
virStorageBackendSheepdogAddHostArg(virCommandPtr cmd,
|
||||
virStoragePoolObjPtr pool)
|
||||
{
|
||||
const char *address = "localhost";
|
||||
int port = 7000;
|
||||
if (pool->def->source.nhost > 0) {
|
||||
if (pool->def->source.hosts[0].name != NULL) {
|
||||
address = pool->def->source.hosts[0].name;
|
||||
}
|
||||
if (pool->def->source.hosts[0].port) {
|
||||
port = pool->def->source.hosts[0].port;
|
||||
}
|
||||
}
|
||||
virCommandAddArg(cmd, "-a");
|
||||
virCommandAddArgFormat(cmd, "%s", address);
|
||||
virCommandAddArg(cmd, "-p");
|
||||
virCommandAddArgFormat(cmd, "%d", port);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
virStorageBackendSheepdogRefreshPool(virConnectPtr conn ATTRIBUTE_UNUSED,
|
||||
virStoragePoolObjPtr pool)
|
||||
{
|
||||
int ret;
|
||||
char *output = NULL;
|
||||
virCommandPtr cmd;
|
||||
|
||||
cmd = virCommandNewArgList(COLLIE, "node", "info", "-r", NULL);
|
||||
virStorageBackendSheepdogAddHostArg(cmd, pool);
|
||||
virCommandSetOutputBuffer(cmd, &output);
|
||||
ret = virCommandRun(cmd, NULL);
|
||||
if (ret == 0)
|
||||
ret = virStorageBackendSheepdogParseNodeInfo(pool->def, output);
|
||||
|
||||
virCommandFree(cmd);
|
||||
VIR_FREE(output);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
virStorageBackendSheepdogDeleteVol(virConnectPtr conn ATTRIBUTE_UNUSED,
|
||||
virStoragePoolObjPtr pool,
|
||||
virStorageVolDefPtr vol,
|
||||
unsigned int flags)
|
||||
{
|
||||
|
||||
virCheckFlags(0, -1);
|
||||
|
||||
virCommandPtr cmd = virCommandNewArgList(COLLIE, "vdi", "delete", vol->name, NULL);
|
||||
virStorageBackendSheepdogAddHostArg(cmd, pool);
|
||||
int ret = virCommandRun(cmd, NULL);
|
||||
|
||||
virCommandFree(cmd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
virStorageBackendSheepdogCreateVol(virConnectPtr conn ATTRIBUTE_UNUSED,
|
||||
virStoragePoolObjPtr pool,
|
||||
virStorageVolDefPtr vol)
|
||||
{
|
||||
|
||||
int ret;
|
||||
|
||||
if (vol->target.encryption != NULL) {
|
||||
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
||||
_("Sheepdog does not support encrypted volumes"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
virCommandPtr cmd = virCommandNewArgList(COLLIE, "vdi", "create", vol->name, NULL);
|
||||
virCommandAddArgFormat(cmd, "%llu", vol->capacity);
|
||||
virStorageBackendSheepdogAddHostArg(cmd, pool);
|
||||
ret = virCommandRun(cmd, NULL);
|
||||
|
||||
virStorageBackendSheepdogRefreshVol(conn, pool, vol);
|
||||
|
||||
virCommandFree(cmd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
virStorageBackendSheepdogParseVdiList(virStorageVolDefPtr vol,
|
||||
char *output)
|
||||
{
|
||||
/* fields:
|
||||
* current/clone/snapshot, name, id, size, used, shared, creation time, vdi id, [tag]
|
||||
*
|
||||
* example output:
|
||||
* s test 1 10 0 0 1336556634 7c2b25
|
||||
* s test 2 10 0 0 1336557203 7c2b26
|
||||
* = test 3 10 0 0 1336557216 7c2b27
|
||||
*/
|
||||
|
||||
int id;
|
||||
const char *p, *next;
|
||||
|
||||
vol->allocation = vol->capacity = 0;
|
||||
|
||||
p = output;
|
||||
do {
|
||||
char *end;
|
||||
|
||||
if ((next = strchr(p, '\n')))
|
||||
++next;
|
||||
|
||||
/* ignore snapshots */
|
||||
if (*p != '=')
|
||||
continue;
|
||||
|
||||
/* skip space */
|
||||
if (p + 2 < next) {
|
||||
p += 2;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* skip name */
|
||||
while (*p != '\0' && *p != ' ') {
|
||||
if (*p == '\\')
|
||||
++p;
|
||||
++p;
|
||||
}
|
||||
|
||||
if (virStrToLong_i(p, &end, 10, &id) < 0)
|
||||
return -1;
|
||||
|
||||
p = end + 1;
|
||||
|
||||
if (virStrToLong_ull(p, &end, 10, &vol->capacity) < 0)
|
||||
return -1;
|
||||
|
||||
p = end + 1;
|
||||
|
||||
if (virStrToLong_ull(p, &end, 10, &vol->allocation) < 0)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
} while ((p = next));
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int
|
||||
virStorageBackendSheepdogRefreshVol(virConnectPtr conn ATTRIBUTE_UNUSED,
|
||||
virStoragePoolObjPtr pool,
|
||||
virStorageVolDefPtr vol)
|
||||
{
|
||||
int ret;
|
||||
char *output = NULL;
|
||||
|
||||
virCommandPtr cmd = virCommandNewArgList(COLLIE, "vdi", "list", vol->name, "-r", NULL);
|
||||
virStorageBackendSheepdogAddHostArg(cmd, pool);
|
||||
virCommandSetOutputBuffer(cmd, &output);
|
||||
ret = virCommandRun(cmd, NULL);
|
||||
|
||||
if (ret < 0)
|
||||
goto cleanup;
|
||||
|
||||
if ((ret = virStorageBackendSheepdogParseVdiList(vol, output)) < 0)
|
||||
goto cleanup;
|
||||
|
||||
vol->type = VIR_STORAGE_VOL_NETWORK;
|
||||
|
||||
VIR_FREE(vol->key);
|
||||
if (virAsprintf(&vol->key, "%s/%s",
|
||||
pool->def->source.name, vol->name) == -1) {
|
||||
virReportOOMError();
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
VIR_FREE(vol->target.path);
|
||||
if (virAsprintf(&vol->target.path, "%s", vol->name) == -1) {
|
||||
virReportOOMError();
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
cleanup:
|
||||
virCommandFree(cmd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
virStorageBackendSheepdogResizeVol(virConnectPtr conn ATTRIBUTE_UNUSED,
|
||||
virStoragePoolObjPtr pool,
|
||||
virStorageVolDefPtr vol,
|
||||
unsigned long long capacity,
|
||||
unsigned int flags)
|
||||
{
|
||||
|
||||
virCheckFlags(0, -1);
|
||||
|
||||
virCommandPtr cmd = virCommandNewArgList(COLLIE, "vdi", "resize", vol->name, NULL);
|
||||
virCommandAddArgFormat(cmd, "%llu", capacity);
|
||||
virStorageBackendSheepdogAddHostArg(cmd, pool);
|
||||
int ret = virCommandRun(cmd, NULL);
|
||||
|
||||
virCommandFree(cmd);
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
virStorageBackend virStorageBackendSheepdog = {
|
||||
.type = VIR_STORAGE_POOL_SHEEPDOG,
|
||||
|
||||
.refreshPool = virStorageBackendSheepdogRefreshPool,
|
||||
.createVol = virStorageBackendSheepdogCreateVol,
|
||||
.refreshVol = virStorageBackendSheepdogRefreshVol,
|
||||
.deleteVol = virStorageBackendSheepdogDeleteVol,
|
||||
.resizeVol = virStorageBackendSheepdogResizeVol,
|
||||
};
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* storage_backend_sheepog.h: storage backend for Sheepdog handling
|
||||
*
|
||||
* Copyright (C) 2012 Wido den Hollander
|
||||
* Copyright (C) 2012 Frank Spijkerman
|
||||
* Copyright (C) 2012 Sebastian Wiedenroth
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
* Author: Wido den Hollander <wido@widodh.nl>
|
||||
* Frank Spijkerman <frank.spijkerman@avira.com>
|
||||
* Sebastian Wiedenroth <sebastian.wiedenroth@skylime.net>
|
||||
*/
|
||||
|
||||
#ifndef __VIR_STORAGE_BACKEND_SHEEPDOG_H__
|
||||
# define __VIR_STORAGE_BACKEND_SHEEPDOG_H__
|
||||
|
||||
# include "storage_backend.h"
|
||||
|
||||
int virStorageBackendSheepdogParseNodeInfo(virStoragePoolDefPtr pool,
|
||||
char *output);
|
||||
int virStorageBackendSheepdogParseVdiList(virStorageVolDefPtr vol,
|
||||
char *output);
|
||||
|
||||
extern virStorageBackend virStorageBackendSheepdog;
|
||||
|
||||
#endif /* __VIR_STORAGE_BACKEND_SHEEPDOG_H__ */
|
|
@ -141,6 +141,10 @@ if WITH_NETWORK
|
|||
test_programs += networkxml2argvtest
|
||||
endif
|
||||
|
||||
if WITH_STORAGE_SHEEPDOG
|
||||
test_programs += storagebackendsheepdogtest
|
||||
endif
|
||||
|
||||
test_programs += nwfilterxml2xmltest
|
||||
|
||||
test_programs += storagevolxml2xmltest storagepoolxml2xmltest
|
||||
|
@ -398,6 +402,15 @@ else
|
|||
EXTRA_DIST += networkxml2argvtest.c
|
||||
endif
|
||||
|
||||
if WITH_STORAGE_SHEEPDOG
|
||||
storagebackendsheepdogtest_SOURCES = \
|
||||
storagebackendsheepdogtest.c \
|
||||
testutils.c testutils.h
|
||||
storagebackendsheepdogtest_LDADD = ../src/libvirt_driver_storage.la $(LDADDS)
|
||||
else
|
||||
EXTRA_DIST += storagebackendsheepdogtest.c
|
||||
endif
|
||||
|
||||
nwfilterxml2xmltest_SOURCES = \
|
||||
nwfilterxml2xmltest.c \
|
||||
testutils.c testutils.h
|
||||
|
|
|
@ -0,0 +1,214 @@
|
|||
/*
|
||||
* storagebackendsheepdogtest.c: storage backend for Sheepdog handling
|
||||
*
|
||||
* Copyright (C) 2012 Sebastian Wiedenroth
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
* Author: Sebastian Wiedenroth <sebastian.wiedenroth@skylime.net>
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "internal.h"
|
||||
#include "testutils.h"
|
||||
#include "storage/storage_backend_sheepdog.h"
|
||||
|
||||
|
||||
typedef struct {
|
||||
const char *output;
|
||||
int expected_return;
|
||||
uint64_t expected_capacity;
|
||||
uint64_t expected_allocation;
|
||||
} collie_test;
|
||||
|
||||
|
||||
static int
|
||||
test_node_info_parser(collie_test test, char *poolxml)
|
||||
{
|
||||
int ret = -1;
|
||||
char *output = NULL;
|
||||
char *poolXmlData = NULL;
|
||||
virStoragePoolDefPtr pool = NULL;
|
||||
|
||||
if (virtTestLoadFile(poolxml, &poolXmlData) < 0)
|
||||
goto cleanup;
|
||||
|
||||
if (!(pool = virStoragePoolDefParseString(poolXmlData)))
|
||||
goto cleanup;
|
||||
|
||||
output = strdup(test.output);
|
||||
if (!output)
|
||||
goto cleanup;
|
||||
|
||||
if (virStorageBackendSheepdogParseNodeInfo(pool, output) !=
|
||||
test.expected_return)
|
||||
goto cleanup;
|
||||
|
||||
if (test.expected_return) {
|
||||
ret = 0;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (pool->capacity == test.expected_capacity &&
|
||||
pool->allocation == test.expected_allocation)
|
||||
ret = 0;
|
||||
|
||||
cleanup:
|
||||
VIR_FREE(output);
|
||||
VIR_FREE(poolXmlData);
|
||||
virStoragePoolDefFree(pool);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
test_vdi_list_parser(collie_test test, char *poolxml, char *volxml)
|
||||
{
|
||||
int ret = -1;
|
||||
char *poolXmlData = NULL;
|
||||
char *volXmlData = NULL;
|
||||
char *output = NULL;
|
||||
virStoragePoolDefPtr pool = NULL;
|
||||
virStorageVolDefPtr vol = NULL;
|
||||
|
||||
if (virtTestLoadFile(poolxml, &poolXmlData) < 0)
|
||||
goto cleanup;
|
||||
if (virtTestLoadFile(volxml, &volXmlData) < 0)
|
||||
goto cleanup;
|
||||
|
||||
if (!(pool = virStoragePoolDefParseString(poolXmlData)))
|
||||
goto cleanup;
|
||||
|
||||
if (!(vol = virStorageVolDefParseString(pool, volXmlData)))
|
||||
goto cleanup;
|
||||
|
||||
output = strdup(test.output);
|
||||
if (!output)
|
||||
goto cleanup;
|
||||
|
||||
if (virStorageBackendSheepdogParseVdiList(vol, output) !=
|
||||
test.expected_return)
|
||||
goto cleanup;
|
||||
|
||||
if (test.expected_return) {
|
||||
ret = 0;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (vol->capacity == test.expected_capacity &&
|
||||
vol->allocation == test.expected_allocation)
|
||||
ret = 0;
|
||||
|
||||
cleanup:
|
||||
VIR_FREE(output);
|
||||
VIR_FREE(poolXmlData);
|
||||
VIR_FREE(volXmlData);
|
||||
virStoragePoolDefFree(pool);
|
||||
virStorageVolDefFree(vol);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
mymain(void)
|
||||
{
|
||||
int ret = -1;
|
||||
char *poolxml = NULL;
|
||||
char *volxml = NULL;
|
||||
|
||||
collie_test node_info_tests[] = {
|
||||
{"", -1, 0, 0},
|
||||
{"Total 15245667872 117571104 0% 20972341\n", 0, 15245667872, 117571104},
|
||||
{"To", -1, 0, 0},
|
||||
{"asdf\nasdf", -1, 0, 0},
|
||||
{"Total ", -1, 0, 0},
|
||||
{"Total 1", -1, 0, 0},
|
||||
{"Total 1\n", -1, 0, 0},
|
||||
{"Total 1 ", -1, 0, 0},
|
||||
{"Total 1 2", -1, 0, 0},
|
||||
{"Total 1 2 ", -1, 0, 0},
|
||||
{"Total 1 2\n", 0, 1, 2},
|
||||
{"Total 1 2 \n", 0, 1, 2},
|
||||
{"Total a 2 \n", -1, 0, 0},
|
||||
{"Total 1 b \n", -1, 0, 0},
|
||||
{"Total a b \n", -1, 0, 0},
|
||||
{"stuff\nTotal 1 2 \n", 0, 1, 2},
|
||||
{"0 1 2 3\nTotal 1 2 \n", 0, 1, 2},
|
||||
{NULL, 0, 0, 0}
|
||||
};
|
||||
|
||||
collie_test vdi_list_tests[] = {
|
||||
{"", -1, 0, 0},
|
||||
{"= test 3 10 20 0 1336557216 7c2b27\n", 0, 10, 20},
|
||||
{"= test\\ with\\ spaces 3 10 20 0 1336557216 7c2b27\n", 0, 10, 20},
|
||||
{"= backslashattheend\\\\ 3 10 20 0 1336557216 7c2b27\n", 0, 10, 20},
|
||||
{"s test 1 10 20 0 1336556634 7c2b25\n= test 3 50 60 0 1336557216 7c2b27\n", 0, 50, 60},
|
||||
{"=", -1, 0, 0},
|
||||
{"= test", -1, 0, 0},
|
||||
{"= test ", -1, 0, 0},
|
||||
{"= test 1", -1, 0, 0},
|
||||
{"= test 1 ", -1, 0, 0},
|
||||
{"= test 1 2", -1, 0, 0},
|
||||
{"= test 1 2 ", -1, 0, 0},
|
||||
{"= test 1 2 3", -1, 0, 0},
|
||||
{NULL, 0, 0, 0}
|
||||
};
|
||||
|
||||
collie_test *test = node_info_tests;
|
||||
|
||||
if (virAsprintf(&poolxml, "%s/storagepoolxml2xmlin/pool-sheepdog.xml",
|
||||
abs_srcdir) < 0)
|
||||
goto cleanup;
|
||||
|
||||
if (virAsprintf(&volxml, "%s/storagevolxml2xmlin/vol-sheepdog.xml",
|
||||
abs_srcdir) < 0)
|
||||
goto cleanup;
|
||||
|
||||
while (test->output != NULL) {
|
||||
ret = test_node_info_parser(*test, poolxml);
|
||||
virtTestResult("node_info_parser", ret, NULL);
|
||||
++test;
|
||||
if (ret < 0)
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
test = vdi_list_tests;
|
||||
|
||||
while (test->output != NULL) {
|
||||
ret = test_vdi_list_parser(*test, poolxml, volxml);
|
||||
virtTestResult("vdi_list_parser", ret, NULL);
|
||||
++test;
|
||||
if (ret < 0)
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
|
||||
cleanup:
|
||||
VIR_FREE(poolxml);
|
||||
VIR_FREE(volxml);
|
||||
return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
}
|
||||
|
||||
VIRT_TEST_MAIN(mymain)
|
|
@ -0,0 +1,8 @@
|
|||
<pool type='sheepdog'>
|
||||
<name>sheepdog</name>
|
||||
<uuid>65fcba04-5b13-bd93-cff3-52ce48e11ad7</uuid>
|
||||
<source>
|
||||
<host name='localhost' port='7000'/>
|
||||
<name>sheepdog</name>
|
||||
</source>
|
||||
</pool>
|
|
@ -0,0 +1,11 @@
|
|||
<pool type='sheepdog'>
|
||||
<name>sheepdog</name>
|
||||
<uuid>65fcba04-5b13-bd93-cff3-52ce48e11ad7</uuid>
|
||||
<capacity unit='bytes'>0</capacity>
|
||||
<allocation unit='bytes'>0</allocation>
|
||||
<available unit='bytes'>0</available>
|
||||
<source>
|
||||
<host name='localhost' port='7000'/>
|
||||
<name>sheepdog</name>
|
||||
</source>
|
||||
</pool>
|
|
@ -93,6 +93,7 @@ mymain(void)
|
|||
DO_TEST("pool-mpath");
|
||||
DO_TEST("pool-iscsi-multiiqn");
|
||||
DO_TEST("pool-iscsi-vendor-product");
|
||||
DO_TEST("pool-sheepdog");
|
||||
|
||||
return ret==0 ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
<volume>
|
||||
<name>test2</name>
|
||||
<source>
|
||||
</source>
|
||||
<capacity unit='bytes'>1024</capacity>
|
||||
<allocation unit='bytes'>0</allocation>
|
||||
<target>
|
||||
<path>sheepdog:test2</path>
|
||||
</target>
|
||||
</volume>
|
|
@ -0,0 +1,17 @@
|
|||
<volume>
|
||||
<name>test2</name>
|
||||
<key>(null)</key>
|
||||
<source>
|
||||
</source>
|
||||
<capacity unit='bytes'>1024</capacity>
|
||||
<allocation unit='bytes'>0</allocation>
|
||||
<target>
|
||||
<path>sheepdog:test2</path>
|
||||
<format type='unknown'/>
|
||||
<permissions>
|
||||
<mode>0600</mode>
|
||||
<owner>4294967295</owner>
|
||||
<group>4294967295</group>
|
||||
</permissions>
|
||||
</target>
|
||||
</volume>
|
|
@ -112,6 +112,7 @@ mymain(void)
|
|||
DO_TEST("pool-disk", "vol-partition");
|
||||
DO_TEST("pool-logical", "vol-logical");
|
||||
DO_TEST("pool-logical", "vol-logical-backing");
|
||||
DO_TEST("pool-sheepdog", "vol-sheepdog");
|
||||
|
||||
return ret==0 ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
}
|
||||
|
|
|
@ -20873,6 +20873,9 @@ vshShowVersion(vshControl *ctl ATTRIBUTE_UNUSED)
|
|||
#endif
|
||||
#ifdef WITH_STORAGE_RBD
|
||||
vshPrint(ctl, " RBD");
|
||||
#endif
|
||||
#ifdef WITH_STORAGE_SHEEPDOG
|
||||
vshPrint(ctl, " Sheepdog");
|
||||
#endif
|
||||
vshPrint(ctl, "\n");
|
||||
|
||||
|
|
Loading…
Reference in New Issue