diff --git a/autoconf/configure.in b/autoconf/configure.in index eb0ea492228..f028de49a85 100644 --- a/autoconf/configure.in +++ b/autoconf/configure.in @@ -3505,6 +3505,67 @@ fi AC_SUBST(DROPLET_INC) AC_SUBST(DROPLET_LIBS) +dnl +dnl Check for CEPH via librados. +dnl +RADOS_LIBS="-lrados" +RADOS_INC="" +have_ceph=no +AC_ARG_WITH(rados, + AC_HELP_STRING([--with-rados@<:@=DIR@:>@], [Directory holding RADOS includes/libs]), + with_rados_directory=$withval +) + +if test "x${with_rados_directory}" != "xyes" && test x"${with_rados_directory}" != "x"; then + # + # Make sure the $with_rados_directory also makes sense + # + if test -d "${with_rados_directory}/lib" -a -d "${with_rados_directory}/include"; then + RADOS_LIBS="-L${with_rados_directory}/lib ${RADOS_LIBS}" + RADOS_INC="-I${with_rados_directory}/include ${RADOS_INC}" + fi +fi + +saved_LIBS="${LIBS}" +saved_CFLAGS="${CFLAGS}" +saved_CPPFLAGS="${CPPFLAGS}" +LIBS="${saved_LIBS} ${RADOS_LIBS}" +CFLAGS="${saved_CFLAGS} ${RADOS_INC}" +CPPFLAGS="${saved_CPPFLAGS} ${RADOS_INC}" + +AC_CHECK_HEADER(rados/librados.h) + +AC_MSG_CHECKING(for glfs_init in gfapi library) +AC_TRY_LINK( + [ + #include + ], [ + rados_t cluster; + + rados_create(&cluster, NULL); + ], [ + AC_MSG_RESULT(yes) + have_ceph="yes" + ], [ + AC_MSG_RESULT(no) + have_ceph="no" + ] +) + +LIBS="${saved_LIBS}" +CFLAGS="${saved_CFLAGS}" +CPPFLAGS="${saved_CPPFLAGS}" + +if test "x${have_ceph}" = "xyes"; then + AC_DEFINE(HAVE_RADOS, 1, [Define to 1 if you have rados lib]) +else + RADOS_LIBS="" + RADOS_INC="" +fi + +AC_SUBST(RADOS_INC) +AC_SUBST(RADOS_LIBS) + dnl dnl Check for pthread libraries dnl @@ -4102,6 +4163,7 @@ Configuration on `date`: SCSI Crypto support: ${have_scsi_crypto} GLUSTERFS support: ${have_glusterfs} DROPLET support: ${have_droplet} + CEPH support: ${have_ceph} Python support: ${support_python} ${PYTHON_LIBS} systemd support: ${support_systemd} ${SYSTEMD_UNITDIR} Batch insert enabled: ${batch_insert_db_backends} diff --git a/src/stored/Makefile.in b/src/stored/Makefile.in index 55ea1810661..63a05f4a908 100644 --- a/src/stored/Makefile.in +++ b/src/stored/Makefile.in @@ -29,6 +29,7 @@ dummy: DEVICE_API_SRCS = gfapi_device.c \ object_store_device.c \ + rados_device.c \ unix_file_device.c \ unix_tape_device.c \ vtape.c @@ -84,8 +85,10 @@ BEXTRACT_LIBS += @FASTLZ_LIBS_NONSHARED@ GLUSTER_INC = @GLUSTER_INC@ DROPLET_INC = @DROPLET_INC@ +RADOS_INC = @RADOS_INC@ LIBS += @GLUSTER_LIBS@ LIBS += @DROPLET_LIBS@ +LIBS += @RADOS_LIBS@ INCLUDES += -I$(srcdir) -I$(basedir) -I$(basedir)/include DEBUG = @DEBUG@ @@ -107,7 +110,7 @@ NDMP_LIBS = @NDMP_LIBS@ dev.o: dev.c @echo "Compiling $<" - $(NO_ECHO)$(CXX) $(DEFS) $(DEBUG) $(GLUSTER_INC) $(DROPLET_INC) -c $(WCFLAGS) $(CPPFLAGS) $(INCLUDES) $(DINCLUDE) $(CXXFLAGS) $< + $(NO_ECHO)$(CXX) $(DEFS) $(DEBUG) $(GLUSTER_INC) $(DROPLET_INC) $(RADOS_INC) -c $(WCFLAGS) $(CPPFLAGS) $(INCLUDES) $(DINCLUDE) $(CXXFLAGS) $< gfapi_device.o: gfapi_device.c @echo "Compiling $<" @@ -117,6 +120,10 @@ object_storage_device.o: object_storage_device.c @echo "Compiling $<" $(NO_ECHO)$(CXX) $(DEFS) $(DEBUG) $(DROPLET_INC) -c $(WCFLAGS) $(CPPFLAGS) $(INCLUDES) $(DINCLUDE) $(CXXFLAGS) $< +rados_device.o: rados_device.c + @echo "Compiling $<" + $(NO_ECHO)$(CXX) $(DEFS) $(DEBUG) $(RADOS_INC) -c $(WCFLAGS) $(CPPFLAGS) $(INCLUDES) $(DINCLUDE) $(CXXFLAGS) $< + all: Makefile bareos-sd @STATIC_SD@ bls bextract bscan btape bcopy @echo "===== Make of stored is good ====" @echo " " diff --git a/src/stored/backends/rados_device.c b/src/stored/backends/rados_device.c new file mode 100644 index 00000000000..8e012ac443b --- /dev/null +++ b/src/stored/backends/rados_device.c @@ -0,0 +1,251 @@ +/* + BAREOSĀ® - Backup Archiving REcovery Open Sourced + + Copyright (C) 2014-2014 Bareos GmbH & Co. KG + + This program is Free Software; you can redistribute it and/or + modify it under the terms of version three of the GNU Affero General Public + License as published by the Free Software Foundation and included + in the file LICENSE. + + This program 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 + General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ +/* + * CEPH (librados) API device abstraction. + * + * Marco van Wieringen, February 2014 + */ + +#include "bareos.h" + +#ifdef HAVE_RADOS +#include "stored.h" +#include "rados_device.h" + +/* + * Open a volume using librados. + */ +int rados_device::d_open(const char *pathname, int flags, int mode) +{ + if (!m_rados_configstring) { + char *bp; + + m_rados_configstring = bstrdup(dev_name); + bp = strchr(m_rados_configstring, ':'); + if (!bp) { + Mmsg1(errmsg, _("Unable to parse device %s.\n"), dev_name); + Emsg0(M_FATAL, 0, errmsg); + goto bail_out; + } + + *bp++ = '\0'; + m_rados_conffile = m_rados_configstring; + m_rados_poolname = bp; + } + + if (!m_cluster_initialized) { + if (rados_create(&m_cluster, NULL) < 0) { + berrno be; + + Mmsg1(errmsg, _("Unable to create RADOS cluster: ERR=%s\n"), be.bstrerror()); + Emsg0(M_FATAL, 0, errmsg); + goto bail_out; + } + + if (rados_conf_read_file(m_cluster, m_rados_conffile) < 0) { + berrno be; + + Mmsg2(errmsg, _("Unable to read RADOS config %s: ERR=%s\n"), m_rados_conffile, be.bstrerror()); + Emsg0(M_FATAL, 0, errmsg); + rados_shutdown(m_cluster); + goto bail_out; + } + + if (rados_connect(m_cluster) < 0) { + berrno be; + + Mmsg1(errmsg, _("Unable to connect to RADOS cluster: ERR=%s\n"), be.bstrerror()); + Emsg0(M_FATAL, 0, errmsg); + rados_shutdown(m_cluster); + goto bail_out; + } + + m_cluster_initialized = true; + } + + if (!m_ctx) { + if (rados_ioctx_create(m_cluster, m_rados_poolname, &m_ctx) < 0) { + berrno be; + + Mmsg2(errmsg, _("Unable to create RADOS IO context for pool %s: ERR=%s\n"), m_rados_poolname, be.bstrerror()); + Emsg0(M_FATAL, 0, errmsg); + goto bail_out; + } + } + + m_offset = 0; + + return 0; + +bail_out: + return -1; +} + +/* + * Read data from a volume using librados. + */ +ssize_t rados_device::d_read(int fd, void *buffer, size_t count) +{ + if (m_ctx) { + int nr_read; + + nr_read = rados_read(m_ctx, getVolCatName(), (char *)buffer, count, m_offset); + if (nr_read > 0) { + m_offset += nr_read; + } + return nr_read; + } else { + errno = EBADF; + return -1; + } +} + +/* + * Write data to a volume using librados. + */ +ssize_t rados_device::d_write(int fd, const void *buffer, size_t count) +{ + if (m_ctx) { + int nr_written; + + nr_written = rados_write(m_ctx, getVolCatName(), (char *)buffer, count, m_offset); + if (nr_written > 0) { + m_offset += nr_written; + } + return nr_written; + } else { + errno = EBADF; + return -1; + } +} + +int rados_device::d_close(int fd) +{ + /* + * Destroy the IOcontext. + */ + if (m_ctx) { + rados_ioctx_destroy(m_ctx); + m_ctx = NULL; + } else { + errno = EBADF; + return -1; + } + + return 0; +} + +int rados_device::d_ioctl(int fd, ioctl_req_t request, char *op) +{ + return -1; +} + +boffset_t rados_device::d_lseek(DCR *dcr, boffset_t offset, int whence) +{ + switch (whence) { + case SEEK_SET: + m_offset = offset; + break; + case SEEK_CUR: + m_offset += offset; + break; + case SEEK_END: { + uint64_t object_size; + time_t object_mtime; + + if (rados_stat(m_ctx, getVolCatName(), &object_size, &object_mtime) == 0) { + m_offset = object_size + offset; + } else { + return -1; + } + break; + } + default: + return -1; + } + + return m_offset; +} + +bool rados_device::d_truncate(DCR *dcr) +{ + if (m_ctx) { + uint64_t object_size; + time_t object_mtime; + + if (rados_trunc(m_ctx, getVolCatName(), 0) != 0) { + berrno be; + + Mmsg2(errmsg, _("Unable to truncate device %s. ERR=%s\n"), + print_name(), be.bstrerror()); + Emsg0(M_FATAL, 0, errmsg); + return false; + } + + if (rados_stat(m_ctx, getVolCatName(), &object_size, &object_mtime) == 0) { + berrno be; + + Mmsg2(errmsg, _("Unable to stat volume %s. ERR=%s\n"), getVolCatName(), be.bstrerror()); + return false; + } + + if (object_size != 0) { /* rados_trunc() didn't work. */ + if (rados_remove(m_ctx, getVolCatName()) != 0) { + berrno be; + + Mmsg2(errmsg, _("Unable to remove volume %s. ERR=%s\n"), getVolCatName(), be.bstrerror()); + return false; + } + } + + m_offset = 0; + } + + return true; +} + +rados_device::~rados_device() +{ + if (m_ctx) { + rados_ioctx_destroy(m_ctx); + m_ctx = NULL; + } + + if (m_cluster_initialized) { + rados_shutdown(&m_cluster); + m_cluster_initialized = false; + } + + if (m_rados_configstring) { + free(m_rados_configstring); + } +} + +rados_device::rados_device() +{ + m_fd = -1; + m_rados_configstring = NULL; + m_rados_conffile = NULL; + m_rados_poolname = NULL; + m_cluster_initialized = false; + m_ctx = NULL; +} +#endif diff --git a/src/stored/backends/rados_device.h b/src/stored/backends/rados_device.h new file mode 100644 index 00000000000..bd55e454668 --- /dev/null +++ b/src/stored/backends/rados_device.h @@ -0,0 +1,58 @@ +/* + BAREOSĀ® - Backup Archiving REcovery Open Sourced + + Copyright (C) 2014-2014 Bareos GmbH & Co. KG + + This program is Free Software; you can redistribute it and/or + modify it under the terms of version three of the GNU Affero General Public + License as published by the Free Software Foundation, which is + listed in the file LICENSE. + + This program 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 + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ +/* + * CEPH (librados) API device abstraction. + * + * Marco van Wieringen, February 2014 + */ + +#ifndef RADOS_DEVICE_H +#define RADOS_DEVICE_H + +#include + +class rados_device: public DEVICE { +private: + char *m_rados_configstring; + char *m_rados_conffile; + char *m_rados_poolname; + bool m_cluster_initialized; + rados_t m_cluster; + rados_ioctx_t m_ctx; + boffset_t m_offset; + +public: + rados_device(); + ~rados_device(); + + /* + * Interface from DEVICE + */ + int d_close(int); + int d_open(const char *pathname, int flags, int mode); + int d_ioctl(int fd, ioctl_req_t request, char *mt = NULL); + boffset_t d_lseek(DCR *dcr, boffset_t offset, int whence); + ssize_t d_read(int fd, void *buffer, size_t count); + ssize_t d_write(int fd, const void *buffer, size_t count); + bool d_truncate(DCR *dcr); +}; + +#endif /* RADOS_DEVICE_H */ diff --git a/src/stored/dev.c b/src/stored/dev.c index 2f006b44afc..dad65764c51 100644 --- a/src/stored/dev.c +++ b/src/stored/dev.c @@ -81,6 +81,9 @@ #ifdef HAVE_OBJECTSTORE #include "backends/object_store_device.h" #endif +#ifdef HAVE_RADOS +#include "backends/rados_device.h" +#endif #ifdef HAVE_WIN32 #include "backends/win32_tape_device.h" #include "backends/win32_file_device.h" @@ -177,6 +180,11 @@ m_init_dev(JCR *jcr, DEVRES *device, bool new_init) dev = New(object_store_device); break; #endif +#ifdef HAVE_RADOS + case B_RADOS_DEV: + dev = New(rados_device); + break; +#endif #ifdef HAVE_WIN32 case B_TAPE_DEV: dev = New(win32_tape_device); @@ -1974,6 +1982,8 @@ bool DEVICE::mount(DCR *dcr, int timeout) break; case B_OBJECT_STORE_DEV: break; + case B_RADOS_DEV: + break; default: break; } @@ -2045,6 +2055,8 @@ bool DEVICE::unmount(DCR *dcr, int timeout) break; case B_OBJECT_STORE_DEV: break; + case B_RADOS_DEV: + break; default: break; } diff --git a/src/stored/dev.h b/src/stored/dev.h index 3cc4305b439..c07df10e2ce 100644 --- a/src/stored/dev.h +++ b/src/stored/dev.h @@ -88,7 +88,8 @@ enum { B_VTAPE_DEV, B_VTL_DEV, B_GFAPI_DEV, - B_OBJECT_STORE_DEV + B_OBJECT_STORE_DEV, + B_RADOS_DEV }; /* IO directions */ @@ -314,7 +315,8 @@ class DEVICE: public SMARTALLOC { dev_type == B_VTAPE_DEV); } int is_file() const { return (dev_type == B_FILE_DEV || dev_type == B_GFAPI_DEV || - dev_type == B_OBJECT_STORE_DEV); } + dev_type == B_OBJECT_STORE_DEV || + dev_type == B_RADOS_DEV); } int is_fifo() const { return dev_type == B_FIFO_DEV; } int is_vtl() const { return dev_type == B_VTL_DEV; } int is_vtape() const { return dev_type == B_VTAPE_DEV; } diff --git a/src/stored/stored_conf.c b/src/stored/stored_conf.c index f16f7155c3a..ce6870ff1f2 100644 --- a/src/stored/stored_conf.c +++ b/src/stored/stored_conf.c @@ -279,6 +279,7 @@ static s_kw dev_types[] = { { "vtape", B_VTAPE_DEV }, { "gfapi", B_GFAPI_DEV }, { "object", B_OBJECT_STORE_DEV }, + { "rados", B_RADOS_DEV }, { NULL, 0 } };