rebase: Make use of latest go ceph library

The go-ceph version 0.4.0 is available now which got some important
library changes required for ceph csi project.

Signed-off-by: Humble Chirammal <hchiramm@redhat.com>
This commit is contained in:
Humble Chirammal
2020-06-17 15:07:06 +05:30
committed by mergify[bot]
parent 306d5b1da0
commit 58bf45a13e
67 changed files with 1133 additions and 362 deletions

View File

@ -0,0 +1,62 @@
package cutil
/*
#include <stdlib.h>
*/
import "C"
import (
"unsafe"
)
// CommandInput can be used to manage the input to ceph's *_command functions.
type CommandInput struct {
cmd []*C.char
inbuf []byte
}
// NewCommandInput creates C-level pointers from go byte buffers suitable
// for passing off to ceph's *_command functions.
func NewCommandInput(cmd [][]byte, inputBuffer []byte) *CommandInput {
ci := &CommandInput{
cmd: make([]*C.char, len(cmd)),
inbuf: inputBuffer,
}
for i := range cmd {
ci.cmd[i] = C.CString(string(cmd[i]))
}
return ci
}
// Free any C memory managed by this CommandInput.
func (ci *CommandInput) Free() {
for i := range ci.cmd {
C.free(unsafe.Pointer(ci.cmd[i]))
}
ci.cmd = nil
}
// Cmd returns an unsafe wrapper around an array of C-strings.
func (ci *CommandInput) Cmd() CharPtrPtr {
ptr := &ci.cmd[0]
return CharPtrPtr(ptr)
}
// CmdLen returns the length of the array of C-strings returned by
// Cmd.
func (ci *CommandInput) CmdLen() SizeT {
return SizeT(len(ci.cmd))
}
// InBuf returns an unsafe wrapper to a C char*.
func (ci *CommandInput) InBuf() CharPtr {
if len(ci.inbuf) == 0 {
return nil
}
return CharPtr(&ci.inbuf[0])
}
// InBufLen returns the length of the buffer returned by InBuf.
func (ci *CommandInput) InBufLen() SizeT {
return SizeT(len(ci.inbuf))
}

View File

@ -0,0 +1,100 @@
package cutil
/*
#include <stdlib.h>
*/
import "C"
import (
"unsafe"
)
// CommandOutput can be used to manage the outputs of ceph's *_command
// functions.
type CommandOutput struct {
free FreeFunc
outBuf *C.char
outBufLen C.size_t
outs *C.char
outsLen C.size_t
}
// NewCommandOutput returns an empty CommandOutput. The pointers that
// a CommandOutput provides can be used to get the results of ceph's
// *_command functions.
func NewCommandOutput() *CommandOutput {
return &CommandOutput{
free: free,
}
}
// SetFreeFunc sets the function used to free memory held by CommandOutput.
// Not all uses of CommandOutput expect to use the basic C.free function
// and either require or prefer the use of a custom deallocation function.
// Use SetFreeFunc to change the free function and return the modified
// CommandOutput object.
func (co *CommandOutput) SetFreeFunc(f FreeFunc) *CommandOutput {
co.free = f
return co
}
// Free any C memory tracked by this object.
func (co *CommandOutput) Free() {
if co.outBuf != nil {
co.free(unsafe.Pointer(co.outBuf))
}
if co.outs != nil {
co.free(unsafe.Pointer(co.outs))
}
}
// OutBuf returns an unsafe wrapper around a pointer to a `char*`.
func (co *CommandOutput) OutBuf() CharPtrPtr {
return CharPtrPtr(&co.outBuf)
}
// OutBufLen returns an unsafe wrapper around a pointer to a size_t.
func (co *CommandOutput) OutBufLen() SizeTPtr {
return SizeTPtr(&co.outBufLen)
}
// Outs returns an unsafe wrapper around a pointer to a `char*`.
func (co *CommandOutput) Outs() CharPtrPtr {
return CharPtrPtr(&co.outs)
}
// OutsLen returns an unsafe wrapper around a pointer to a size_t.
func (co *CommandOutput) OutsLen() SizeTPtr {
return SizeTPtr(&co.outsLen)
}
// GoValues returns native go values converted from the internal C-language
// values tracked by this object.
func (co *CommandOutput) GoValues() (buf []byte, status string) {
if co.outBufLen > 0 {
buf = C.GoBytes(unsafe.Pointer(co.outBuf), C.int(co.outBufLen))
}
if co.outsLen > 0 {
status = C.GoStringN(co.outs, C.int(co.outsLen))
}
return buf, status
}
// testSetString is only used by the unit tests for this file.
// It is located here due to the restriction on having import "C" in
// go test files. :-(
// It mimics a C function that takes a pointer to a
// string and length and allocates memory and sets the pointers
// to the new string and its length.
func testSetString(strp CharPtrPtr, lenp SizeTPtr, s string) {
sp := (**C.char)(strp)
lp := (*C.size_t)(lenp)
*sp = C.CString(s)
*lp = C.size_t(len(s))
}
// free wraps C.free.
// Required for unit tests that may not use cgo directly.
func free(p unsafe.Pointer) {
C.free(p)
}

View File

@ -0,0 +1,28 @@
package cutil
import "C"
import (
"unsafe"
)
// Basic types from C that we can make "public" without too much fuss.
// SizeT wraps size_t from C.
type SizeT C.size_t
// This section contains a bunch of types that are basically just
// unsafe.Pointer but have specific types to help "self document" what the
// underlying pointer is really meant to represent.
// CharPtrPtr is an unsafe pointer wrapping C's `char**`.
type CharPtrPtr unsafe.Pointer
// CharPtr is an unsafe pointer wrapping C's `char*`.
type CharPtr unsafe.Pointer
// SizeTPtr is an unsafe pointer wrapping C's `size_t*`.
type SizeTPtr unsafe.Pointer
// FreeFunc is a wrapper around calls to, or act like, C's free function.
type FreeFunc func(unsafe.Pointer)

64
vendor/github.com/ceph/go-ceph/internal/retry/sizer.go generated vendored Normal file
View File

@ -0,0 +1,64 @@
package retry
// Hint is a type for retry hints
type Hint interface {
If(bool) Hint
size() int
}
type hintInt int
func (hint hintInt) size() int {
return int(hint)
}
// If is a convenience function, that returns a given hint only if a certain
// condition is met (for example a test for a "buffer too small" error).
// Otherwise it returns a nil which stops the retries.
func (hint hintInt) If(cond bool) Hint {
if cond {
return hint
}
return nil
}
// DoubleSize is a hint to retry with double the size
const DoubleSize = hintInt(0)
// Size returns a hint for a specific size
func Size(s int) Hint {
return hintInt(s)
}
// SizeFunc is used to implement 'resize loops' that hides the complexity of the
// sizing away from most of the application. It's a function that takes a size
// argument and returns nil, if no retry is necessary, or a hint indicating the
// size for the next retry. If errors or other results are required from the
// function, the function can write them to function closures of the surrounding
// scope. See tests for examples.
type SizeFunc func(size int) (hint Hint)
// WithSizes repeatingly calls a SizeFunc with increasing sizes until either it
// returns nil, or the max size has been reached. If the returned hint is
// DoubleSize or indicating a size not greater than the current size, the size
// is doubled. If the hint or next size is greater than the max size, the max
// size is used for a last retry.
func WithSizes(size int, max int, f SizeFunc) {
if size > max {
return
}
for {
hint := f(size)
if hint == nil || size == max {
break
}
if hint.size() > size {
size = hint.size()
} else {
size *= 2
}
if size > max {
size = max
}
}
}

193
vendor/github.com/ceph/go-ceph/rados/command.go generated vendored Normal file
View File

@ -0,0 +1,193 @@
package rados
// #cgo LDFLAGS: -lrados
// #include <stdlib.h>
// #include <rados/librados.h>
import "C"
import (
"unsafe"
"github.com/ceph/go-ceph/internal/cutil"
)
func radosBufferFree(p unsafe.Pointer) {
C.rados_buffer_free((*C.char)(p))
}
// MonCommand sends a command to one of the monitors
func (c *Conn) MonCommand(args []byte) ([]byte, string, error) {
return c.MonCommandWithInputBuffer(args, nil)
}
// MonCommandWithInputBuffer sends a command to one of the monitors, with an input buffer
func (c *Conn) MonCommandWithInputBuffer(args, inputBuffer []byte) ([]byte, string, error) {
ci := cutil.NewCommandInput([][]byte{args}, inputBuffer)
defer ci.Free()
co := cutil.NewCommandOutput().SetFreeFunc(radosBufferFree)
defer co.Free()
ret := C.rados_mon_command(
c.cluster,
(**C.char)(ci.Cmd()),
C.size_t(ci.CmdLen()),
(*C.char)(ci.InBuf()),
C.size_t(ci.InBufLen()),
(**C.char)(co.OutBuf()),
(*C.size_t)(co.OutBufLen()),
(**C.char)(co.Outs()),
(*C.size_t)(co.OutsLen()))
buf, status := co.GoValues()
return buf, status, getError(ret)
}
// PGCommand sends a command to one of the PGs
//
// Implements:
// int rados_pg_command(rados_t cluster, const char *pgstr,
// const char **cmd, size_t cmdlen,
// const char *inbuf, size_t inbuflen,
// char **outbuf, size_t *outbuflen,
// char **outs, size_t *outslen);
func (c *Conn) PGCommand(pgid []byte, args [][]byte) ([]byte, string, error) {
return c.PGCommandWithInputBuffer(pgid, args, nil)
}
// PGCommandWithInputBuffer sends a command to one of the PGs, with an input buffer
//
// Implements:
// int rados_pg_command(rados_t cluster, const char *pgstr,
// const char **cmd, size_t cmdlen,
// const char *inbuf, size_t inbuflen,
// char **outbuf, size_t *outbuflen,
// char **outs, size_t *outslen);
func (c *Conn) PGCommandWithInputBuffer(pgid []byte, args [][]byte, inputBuffer []byte) ([]byte, string, error) {
name := C.CString(string(pgid))
defer C.free(unsafe.Pointer(name))
ci := cutil.NewCommandInput(args, inputBuffer)
defer ci.Free()
co := cutil.NewCommandOutput().SetFreeFunc(radosBufferFree)
defer co.Free()
ret := C.rados_pg_command(
c.cluster,
name,
(**C.char)(ci.Cmd()),
C.size_t(ci.CmdLen()),
(*C.char)(ci.InBuf()),
C.size_t(ci.InBufLen()),
(**C.char)(co.OutBuf()),
(*C.size_t)(co.OutBufLen()),
(**C.char)(co.Outs()),
(*C.size_t)(co.OutsLen()))
buf, status := co.GoValues()
return buf, status, getError(ret)
}
// MgrCommand sends a command to a ceph-mgr.
func (c *Conn) MgrCommand(args [][]byte) ([]byte, string, error) {
return c.MgrCommandWithInputBuffer(args, nil)
}
// MgrCommandWithInputBuffer sends a command, with an input buffer, to a ceph-mgr.
//
// Implements:
// int rados_mgr_command(rados_t cluster, const char **cmd,
// size_t cmdlen, const char *inbuf,
// size_t inbuflen, char **outbuf,
// size_t *outbuflen, char **outs,
// size_t *outslen);
func (c *Conn) MgrCommandWithInputBuffer(args [][]byte, inputBuffer []byte) ([]byte, string, error) {
ci := cutil.NewCommandInput(args, inputBuffer)
defer ci.Free()
co := cutil.NewCommandOutput().SetFreeFunc(radosBufferFree)
defer co.Free()
ret := C.rados_mgr_command(
c.cluster,
(**C.char)(ci.Cmd()),
C.size_t(ci.CmdLen()),
(*C.char)(ci.InBuf()),
C.size_t(ci.InBufLen()),
(**C.char)(co.OutBuf()),
(*C.size_t)(co.OutBufLen()),
(**C.char)(co.Outs()),
(*C.size_t)(co.OutsLen()))
buf, status := co.GoValues()
return buf, status, getError(ret)
}
// OsdCommand sends a command to the specified ceph OSD.
func (c *Conn) OsdCommand(osd int, args [][]byte) ([]byte, string, error) {
return c.OsdCommandWithInputBuffer(osd, args, nil)
}
// OsdCommandWithInputBuffer sends a command, with an input buffer, to the
// specified ceph OSD.
//
// Implements:
// int rados_osd_command(rados_t cluster, int osdid,
// const char **cmd, size_t cmdlen,
// const char *inbuf, size_t inbuflen,
// char **outbuf, size_t *outbuflen,
// char **outs, size_t *outslen);
func (c *Conn) OsdCommandWithInputBuffer(
osd int, args [][]byte, inputBuffer []byte) ([]byte, string, error) {
ci := cutil.NewCommandInput(args, inputBuffer)
defer ci.Free()
co := cutil.NewCommandOutput().SetFreeFunc(radosBufferFree)
defer co.Free()
ret := C.rados_osd_command(
c.cluster,
C.int(osd),
(**C.char)(ci.Cmd()),
C.size_t(ci.CmdLen()),
(*C.char)(ci.InBuf()),
C.size_t(ci.InBufLen()),
(**C.char)(co.OutBuf()),
(*C.size_t)(co.OutBufLen()),
(**C.char)(co.Outs()),
(*C.size_t)(co.OutsLen()))
buf, status := co.GoValues()
return buf, status, getError(ret)
}
// MonCommandTarget sends a command to a specified monitor.
func (c *Conn) MonCommandTarget(name string, args [][]byte) ([]byte, string, error) {
return c.MonCommandTargetWithInputBuffer(name, args, nil)
}
// MonCommandTargetWithInputBuffer sends a command, with an input buffer, to a specified monitor.
//
// Implements:
// int rados_mon_command_target(rados_t cluster, const char *name,
// const char **cmd, size_t cmdlen,
// const char *inbuf, size_t inbuflen,
// char **outbuf, size_t *outbuflen,
// char **outs, size_t *outslen);
func (c *Conn) MonCommandTargetWithInputBuffer(
name string, args [][]byte, inputBuffer []byte) ([]byte, string, error) {
cName := C.CString(name)
defer C.free(unsafe.Pointer(cName))
ci := cutil.NewCommandInput(args, inputBuffer)
defer ci.Free()
co := cutil.NewCommandOutput().SetFreeFunc(radosBufferFree)
defer co.Free()
ret := C.rados_mon_command_target(
c.cluster,
cName,
(**C.char)(ci.Cmd()),
C.size_t(ci.CmdLen()),
(*C.char)(ci.InBuf()),
C.size_t(ci.InBufLen()),
(**C.char)(co.OutBuf()),
(*C.size_t)(co.OutBufLen()),
(**C.char)(co.Outs()),
(*C.size_t)(co.OutsLen()))
buf, status := co.GoValues()
return buf, status, getError(ret)
}

View File

@ -8,6 +8,8 @@ import "C"
import (
"bytes"
"unsafe"
"github.com/ceph/go-ceph/internal/retry"
)
// ClusterStat represents Ceph cluster statistics.
@ -140,19 +142,26 @@ func (c *Conn) SetConfigOption(option, value string) error {
// GetConfigOption returns the value of the Ceph configuration option
// identified by the given name.
func (c *Conn) GetConfigOption(name string) (value string, err error) {
buf := make([]byte, 4096)
c_name := C.CString(name)
defer C.free(unsafe.Pointer(c_name))
ret := int(C.rados_conf_get(c.cluster, c_name,
(*C.char)(unsafe.Pointer(&buf[0])), C.size_t(len(buf))))
// FIXME: ret may be -ENAMETOOLONG if the buffer is not large enough. We
// can handle this case, but we need a reliable way to test for
// -ENAMETOOLONG constant. Will the syscall/Errno stuff in Go help?
if ret == 0 {
value = C.GoString((*C.char)(unsafe.Pointer(&buf[0])))
return value, nil
cOption := C.CString(name)
defer C.free(unsafe.Pointer(cOption))
var buf []byte
// range from 4k to 256KiB
retry.WithSizes(4096, 1<<18, func(size int) retry.Hint {
buf = make([]byte, size)
ret := C.rados_conf_get(
c.cluster,
cOption,
(*C.char)(unsafe.Pointer(&buf[0])),
C.size_t(len(buf)))
err = getError(ret)
return retry.DoubleSize.If(err == errNameTooLong)
})
if err != nil {
return "", err
}
return "", RadosError(ret)
value = C.GoString((*C.char)(unsafe.Pointer(&buf[0])))
return value, nil
}
// WaitForLatestOSDMap blocks the caller until the latest OSD map has been
@ -282,121 +291,3 @@ func (c *Conn) GetPoolByID(id int64) (string, error) {
}
return C.GoString((*C.char)(unsafe.Pointer(&buf[0]))), nil
}
// MonCommand sends a command to one of the monitors
func (c *Conn) MonCommand(args []byte) (buffer []byte, info string, err error) {
return c.monCommand(args, nil)
}
// MonCommandWithInputBuffer sends a command to one of the monitors, with an input buffer
func (c *Conn) MonCommandWithInputBuffer(args, inputBuffer []byte) (buffer []byte, info string, err error) {
return c.monCommand(args, inputBuffer)
}
func (c *Conn) monCommand(args, inputBuffer []byte) (buffer []byte, info string, err error) {
argv := C.CString(string(args))
defer C.free(unsafe.Pointer(argv))
var (
outs, outbuf *C.char
outslen, outbuflen C.size_t
)
inbuf := C.CString(string(inputBuffer))
inbufLen := len(inputBuffer)
defer C.free(unsafe.Pointer(inbuf))
ret := C.rados_mon_command(c.cluster,
&argv, 1,
inbuf, // bulk input (e.g. crush map)
C.size_t(inbufLen), // length inbuf
&outbuf, // buffer
&outbuflen, // buffer length
&outs, // status string
&outslen)
if outslen > 0 {
info = C.GoStringN(outs, C.int(outslen))
C.free(unsafe.Pointer(outs))
}
if outbuflen > 0 {
buffer = C.GoBytes(unsafe.Pointer(outbuf), C.int(outbuflen))
C.free(unsafe.Pointer(outbuf))
}
if ret != 0 {
err = getError(ret)
return nil, info, err
}
return
}
// PGCommand sends a command to one of the PGs
//
// Implements:
// int rados_pg_command(rados_t cluster, const char *pgstr,
// const char **cmd, size_t cmdlen,
// const char *inbuf, size_t inbuflen,
// char **outbuf, size_t *outbuflen,
// char **outs, size_t *outslen);
func (c *Conn) PGCommand(pgid []byte, args [][]byte) (buffer []byte, info string, err error) {
return c.pgCommand(pgid, args, nil)
}
// PGCommandWithInputBuffer sends a command to one of the PGs, with an input buffer
//
// Implements:
// int rados_pg_command(rados_t cluster, const char *pgstr,
// const char **cmd, size_t cmdlen,
// const char *inbuf, size_t inbuflen,
// char **outbuf, size_t *outbuflen,
// char **outs, size_t *outslen);
func (c *Conn) PGCommandWithInputBuffer(pgid []byte, args [][]byte, inputBuffer []byte) (buffer []byte, info string, err error) {
return c.pgCommand(pgid, args, inputBuffer)
}
func (c *Conn) pgCommand(pgid []byte, args [][]byte, inputBuffer []byte) (buffer []byte, info string, err error) {
name := C.CString(string(pgid))
defer C.free(unsafe.Pointer(name))
argc := len(args)
argv := make([]*C.char, argc)
for i, arg := range args {
argv[i] = C.CString(string(arg))
defer C.free(unsafe.Pointer(argv[i]))
}
var (
outs, outbuf *C.char
outslen, outbuflen C.size_t
)
inbuf := C.CString(string(inputBuffer))
inbufLen := len(inputBuffer)
defer C.free(unsafe.Pointer(inbuf))
ret := C.rados_pg_command(c.cluster,
name,
&argv[0],
C.size_t(argc),
inbuf, // bulk input
C.size_t(inbufLen), // length inbuf
&outbuf, // buffer
&outbuflen, // buffer length
&outs, // status string
&outslen)
if outslen > 0 {
info = C.GoStringN(outs, C.int(outslen))
C.free(unsafe.Pointer(outs))
}
if outbuflen > 0 {
buffer = C.GoBytes(unsafe.Pointer(outbuf), C.int(outbuflen))
C.free(unsafe.Pointer(outbuf))
}
if ret != 0 {
err = getError(ret)
return nil, info, err
}
return
}

View File

@ -35,6 +35,16 @@ func getError(e C.int) error {
return RadosError(e)
}
// getErrorIfNegative converts a ceph return code to error if negative.
// This is useful for functions that return a usable positive value on
// success but a negative error number on error.
func getErrorIfNegative(ret C.int) error {
if ret >= 0 {
return nil
}
return getError(ret)
}
// Public go errors:
var (
@ -61,3 +71,11 @@ const (
// Deprecated: use ErrPermissionDenied instead
RadosErrorPermissionDenied = ErrPermissionDenied
)
// Private errors:
const (
errNameTooLong = RadosError(-C.ENAMETOOLONG)
errRange = RadosError(-C.ERANGE)
)

View File

@ -28,6 +28,8 @@ import (
"syscall"
"time"
"unsafe"
"github.com/ceph/go-ceph/internal/retry"
)
// CreateOption is passed to IOContext.Create() and should be one of
@ -249,19 +251,24 @@ func (ioctx *IOContext) GetPoolStats() (stat PoolStat, err error) {
// GetPoolName returns the name of the pool associated with the I/O context.
func (ioctx *IOContext) GetPoolName() (name string, err error) {
buf := make([]byte, 128)
for {
ret := C.rados_ioctx_get_pool_name(ioctx.ioctx,
(*C.char)(unsafe.Pointer(&buf[0])), C.unsigned(len(buf)))
if ret == -C.ERANGE {
buf = make([]byte, len(buf)*2)
continue
} else if ret < 0 {
return "", getError(ret)
}
name = C.GoStringN((*C.char)(unsafe.Pointer(&buf[0])), ret)
return name, nil
var (
buf []byte
ret C.int
)
retry.WithSizes(128, 8192, func(size int) retry.Hint {
buf = make([]byte, size)
ret = C.rados_ioctx_get_pool_name(
ioctx.ioctx,
(*C.char)(unsafe.Pointer(&buf[0])),
C.unsigned(len(buf)))
err = getErrorIfNegative(ret)
return retry.DoubleSize.If(err == errRange)
})
if err != nil {
return "", err
}
name = C.GoStringN((*C.char)(unsafe.Pointer(&buf[0])), ret)
return name, nil
}
// ObjectListFunc is the type of the function called for each object visited

View File

@ -37,26 +37,47 @@ func getError(err C.int) error {
return nil
}
// getErrorIfNegative converts a ceph return code to error if negative.
// This is useful for functions that return a usable positive value on
// success but a negative error number on error.
func getErrorIfNegative(ret C.int) error {
if ret >= 0 {
return nil
}
return getError(ret)
}
// Public go errors:
var (
// ErrNoIOContext may be returned if an api call requires an IOContext and
// it is not provided.
ErrNoIOContext = errors.New("RBD image does not have an IOContext")
ErrNoIOContext = errors.New("IOContext is missing")
// ErrNoName may be returned if an api call requires a name and it is
// not provided.
ErrNoName = errors.New("RBD image does not have a name")
// ErrSnapshotNoName may be returned if an aip call requires a snapshot
// ErrSnapshotNoName may be returned if an api call requires a snapshot
// name and it is not provided.
ErrSnapshotNoName = errors.New("RBD snapshot does not have a name")
// ErrImageNotOpen may be returnened if an api call requires an open image handle and one is not provided.
// ErrImageNotOpen may be returned if an api call requires an open image handle and one is not provided.
ErrImageNotOpen = errors.New("RBD image not open")
// ErrImageIsOpen may be returned if an api call requires a closed image handle and one is not provided.
ErrImageIsOpen = errors.New("RBD image is open")
// ErrNotFound may be returned from an api call when the requested item is
// missing.
ErrNotFound = errors.New("RBD image not found")
// ErrNoNamespaceName maye be returned if an api call requires a namespace
// name and it is not provided.
ErrNoNamespaceName = errors.New("Namespace value is missing")
// revive:disable:exported for compatibility with old versions
RbdErrorImageNotOpen = ErrImageNotOpen
RbdErrorNotFound = ErrNotFound
// revive:enable:exported
)
// Private errors:
const (
errRange = RBDError(-C.ERANGE)
)

View File

@ -102,7 +102,7 @@ const (
// revive:enable:exported
)
// FeatureSet is a combination of the bit value for multiple featurs.
// FeatureSet is a combination of the bit value for multiple features.
type FeatureSet uint64
var (

View File

@ -0,0 +1,111 @@
// +build !luminous,!mimic
//
// Ceph Nautilus is the first release that includes rbd_namespace_create(),
// rbd_namespace_remove(), rbd_namespace_exists() and rbd_namespace_list().
package rbd
// #cgo LDFLAGS: -lrbd
// #include <rados/librados.h>
// #include <rbd/librbd.h>
// #include <stdlib.h>
// #include <errno.h>
import "C"
import (
"bytes"
"unsafe"
"github.com/ceph/go-ceph/internal/retry"
"github.com/ceph/go-ceph/rados"
)
// NamespaceCreate creates the namespace for a given Rados IOContext.
//
// Implements:
// int rbd_namespace_create(rados_ioctx_t io, const char *namespace_name);
func NamespaceCreate(ioctx *rados.IOContext, namespaceName string) error {
if ioctx == nil {
return ErrNoIOContext
}
if namespaceName == "" {
return ErrNoNamespaceName
}
cNamespaceName := C.CString(namespaceName)
defer C.free(unsafe.Pointer(cNamespaceName))
ret := C.rbd_namespace_create(cephIoctx(ioctx), cNamespaceName)
return getError(ret)
}
// NamespaceRemove removes a given namespace.
//
// Implements:
// int rbd_namespace_remove(rados_ioctx_t io, const char *namespace_name);
func NamespaceRemove(ioctx *rados.IOContext, namespaceName string) error {
if ioctx == nil {
return ErrNoIOContext
}
if namespaceName == "" {
return ErrNoNamespaceName
}
cNamespaceName := C.CString(namespaceName)
defer C.free(unsafe.Pointer(cNamespaceName))
ret := C.rbd_namespace_remove(cephIoctx(ioctx), cNamespaceName)
return getError(ret)
}
// NamespaceExists checks whether a given namespace exists or not.
//
// Implements:
// int rbd_namespace_exists(rados_ioctx_t io, const char *namespace_name, bool *exists);
func NamespaceExists(ioctx *rados.IOContext, namespaceName string) (bool, error) {
if ioctx == nil {
return false, ErrNoIOContext
}
if namespaceName == "" {
return false, ErrNoNamespaceName
}
cNamespaceName := C.CString(namespaceName)
defer C.free(unsafe.Pointer(cNamespaceName))
var exists C.bool
ret := C.rbd_namespace_exists(cephIoctx(ioctx), cNamespaceName, &exists)
return bool(exists), getErrorIfNegative(ret)
}
// NamespaceList returns a slice containing the names of existing rbd namespaces.
//
// Implements:
// int rbd_namespace_list(rados_ioctx_t io, char *namespace_names, size_t *size);
func NamespaceList(ioctx *rados.IOContext) (names []string, err error) {
if ioctx == nil {
return nil, ErrNoIOContext
}
var (
buf []byte
cSize C.size_t
)
retry.WithSizes(4096, 262144, func(size int) retry.Hint {
cSize = C.size_t(size)
buf = make([]byte, cSize)
ret := C.rbd_namespace_list(cephIoctx(ioctx),
(*C.char)(unsafe.Pointer(&buf[0])),
&cSize)
err = getErrorIfNegative(ret)
return retry.Size(int(cSize)).If(err == errRange)
})
if err != nil {
return nil, err
}
tmpList := bytes.Split(buf[:cSize-1], []byte{0})
for _, s := range tmpList {
if len(s) > 0 {
name := C.GoString((*C.char)(unsafe.Pointer(&s[0])))
names = append(names, name)
}
}
return names, nil
}

View File

@ -69,9 +69,6 @@ const (
RbdImageOptionFeaturesClear = ImageOptionFeaturesClear
// RbdImageOptionDataPool deprecated alias for ImageOptionDataPool
RbdImageOptionDataPool = ImageOptionDataPool
// introduced with Ceph Mimic
//RbdImageOptionFlatten = C.RBD_IMAGE_OPTION_FLATTEN
)
// ImageOptions represents a group of configurable image options.

12
vendor/github.com/ceph/go-ceph/rbd/options_mimic.go generated vendored Normal file
View File

@ -0,0 +1,12 @@
// +build !luminous
package rbd
// #include <rbd/librbd.h>
import "C"
const (
// ImageOptionFlatten is the representation of RBD_IMAGE_OPTION_FLATTEN
// from librbd
ImageOptionFlatten = C.RBD_IMAGE_OPTION_FLATTEN
)

12
vendor/github.com/ceph/go-ceph/rbd/options_nautilus.go generated vendored Normal file
View File

@ -0,0 +1,12 @@
// +build !luminous,!mimic
package rbd
// #include <rbd/librbd.h>
import "C"
const (
// ImageOptionCloneFormat is the representation of
// RBD_IMAGE_OPTION_CLONE_FORMAT from librbd
ImageOptionCloneFormat = C.RBD_IMAGE_OPTION_CLONE_FORMAT
)

12
vendor/github.com/ceph/go-ceph/rbd/options_octopus.go generated vendored Normal file
View File

@ -0,0 +1,12 @@
// +build !luminous,!mimic,!nautilus
package rbd
// #include <rbd/librbd.h>
import "C"
const (
// ImageOptionMirrorImageMode is the representation of
// RBD_IMAGE_OPTION_MIRROR_IMAGE_MODE from librbd
ImageOptionMirrorImageMode = C.RBD_IMAGE_OPTION_MIRROR_IMAGE_MODE
)

86
vendor/github.com/ceph/go-ceph/rbd/pool_nautilus.go generated vendored Normal file
View File

@ -0,0 +1,86 @@
// +build !luminous,!mimic
//
// Ceph Nautilus is the first release that includes rbd_pool_metadata_get(),
// rbd_pool_metadata_set() and rbd_pool_metadata_remove().
package rbd
// #cgo LDFLAGS: -lrbd
// #include <rados/librados.h>
// #include <rbd/librbd.h>
// #include <stdlib.h>
import "C"
import (
"unsafe"
"github.com/ceph/go-ceph/internal/retry"
"github.com/ceph/go-ceph/rados"
)
// GetPoolMetadata returns pool metadata associated with the given key.
//
// Implements:
// int rbd_pool_metadata_get(rados_ioctx_t io_ctx, const char *key, char *value, size_t *val_len);
func GetPoolMetadata(ioctx *rados.IOContext, key string) (string, error) {
if ioctx == nil {
return "", ErrNoIOContext
}
cKey := C.CString(key)
defer C.free(unsafe.Pointer(cKey))
var (
buf []byte
err error
)
retry.WithSizes(4096, 262144, func(size int) retry.Hint {
cSize := C.size_t(size)
buf = make([]byte, cSize)
ret := C.rbd_pool_metadata_get(cephIoctx(ioctx),
cKey,
(*C.char)(unsafe.Pointer(&buf[0])),
&cSize)
err = getError(ret)
return retry.Size(int(cSize)).If(err == errRange)
})
if err != nil {
return "", err
}
return C.GoString((*C.char)(unsafe.Pointer(&buf[0]))), nil
}
// SetPoolMetadata updates the pool metadata string associated with the given key.
//
// Implements:
// int rbd_pool_metadata_set(rados_ioctx_t io_ctx, const char *key, const char *value);
func SetPoolMetadata(ioctx *rados.IOContext, key, value string) error {
if ioctx == nil {
return ErrNoIOContext
}
cKey := C.CString(key)
defer C.free(unsafe.Pointer(cKey))
cValue := C.CString(value)
defer C.free(unsafe.Pointer(cValue))
ret := C.rbd_pool_metadata_set(cephIoctx(ioctx), cKey, cValue)
return getError(ret)
}
// RemovePoolMetadata removes the pool metadata value for a given pool metadata key.
//
// Implements:
// int rbd_pool_metadata_remove(rados_ioctx_t io_ctx, const char *key)
func RemovePoolMetadata(ioctx *rados.IOContext, key string) error {
if ioctx == nil {
return ErrNoIOContext
}
cKey := C.CString(key)
defer C.free(unsafe.Pointer(cKey))
ret := C.rbd_pool_metadata_remove(cephIoctx(ioctx), cKey)
return getError(ret)
}

View File

@ -17,6 +17,7 @@ import (
"time"
"unsafe"
"github.com/ceph/go-ceph/internal/retry"
"github.com/ceph/go-ceph/rados"
)
@ -37,6 +38,7 @@ const (
imageNeedsName uint32 = 1 << iota
imageNeedsIOContext
imageIsOpen
imageIsNotOpen
snapshotNeedsName
// NoSnapshot indicates that no snapshot name is in use (see OpenImage)
@ -120,6 +122,8 @@ func (image *Image) validate(req uint32) error {
return ErrNoIOContext
} else if hasBit(req, imageIsOpen) && image.image == nil {
return ErrImageNotOpen
} else if hasBit(req, imageIsNotOpen) && image.image != nil {
return ErrImageIsOpen
}
return nil
@ -277,7 +281,7 @@ func (image *Image) Clone(snapname string, c_ioctx *rados.IOContext, c_name stri
// Implements:
// int rbd_remove(rados_ioctx_t io, const char *name);
func (image *Image) Remove() error {
if err := image.validate(imageNeedsIOContext | imageNeedsName); err != nil {
if err := image.validate(imageNeedsIOContext | imageNeedsName | imageIsNotOpen); err != nil {
return err
}
return RemoveImage(image.ioctx, image.name)
@ -873,22 +877,24 @@ func (image *Image) GetMetadata(key string) (string, error) {
c_key := C.CString(key)
defer C.free(unsafe.Pointer(c_key))
var c_vallen C.size_t
ret := C.rbd_metadata_get(image.image, c_key, nil, (*C.size_t)(&c_vallen))
// get size of value
// ret -34 because we pass nil as value pointer
if ret != 0 && ret != -C.ERANGE {
return "", RBDError(ret)
var (
buf []byte
err error
)
retry.WithSizes(4096, 262144, func(size int) retry.Hint {
csize := C.size_t(size)
buf = make([]byte, csize)
// rbd_metadata_get is a bit quirky and *does not* update the size
// value if the size passed in >= the needed size.
ret := C.rbd_metadata_get(
image.image, c_key, (*C.char)(unsafe.Pointer(&buf[0])), &csize)
err = getError(ret)
return retry.Size(int(csize)).If(err == errRange)
})
if err != nil {
return "", err
}
// make a bytes array with a good size
value := make([]byte, c_vallen-1)
ret = C.rbd_metadata_get(image.image, c_key, (*C.char)(unsafe.Pointer(&value[0])), (*C.size_t)(&c_vallen))
if ret < 0 {
return "", RBDError(ret)
}
return string(value), nil
return C.GoString((*C.char)(unsafe.Pointer(&buf[0]))), nil
}
// SetMetadata updates the metadata string associated with the given key.
@ -941,42 +947,50 @@ func (image *Image) GetId() (string, error) {
if err := image.validate(imageIsOpen); err != nil {
return "", err
}
size := C.size_t(1024)
buf := make([]byte, size)
for {
var (
err error
buf []byte
)
retry.WithSizes(1, 8192, func(size int) retry.Hint {
buf = make([]byte, size)
ret := C.rbd_get_id(
image.image,
(*C.char)(unsafe.Pointer(&buf[0])),
size)
if ret == -C.ERANGE && size <= 8192 {
size *= 2
buf = make([]byte, size)
} else if ret < 0 {
return "", getError(ret)
}
id := C.GoString((*C.char)(unsafe.Pointer(&buf[0])))
return id, nil
C.size_t(size))
err = getErrorIfNegative(ret)
return retry.DoubleSize.If(err == errRange)
})
if err != nil {
return "", err
}
id := C.GoString((*C.char)(unsafe.Pointer(&buf[0])))
return id, nil
}
// GetTrashList returns a slice of TrashInfo structs, containing information about all RBD images
// currently residing in the trash.
func GetTrashList(ioctx *rados.IOContext) ([]TrashInfo, error) {
var num_entries C.size_t
// Call rbd_trash_list with nil pointer to get number of trash entries.
if C.rbd_trash_list(cephIoctx(ioctx), nil, &num_entries); num_entries == 0 {
return nil, nil
var (
err error
count C.size_t
entries []C.rbd_trash_image_info_t
)
retry.WithSizes(32, 1024, func(size int) retry.Hint {
count = C.size_t(size)
entries = make([]C.rbd_trash_image_info_t, count)
ret := C.rbd_trash_list(cephIoctx(ioctx), &entries[0], &count)
err = getErrorIfNegative(ret)
return retry.Size(int(count)).If(err == errRange)
})
if err != nil {
return nil, err
}
// Free rbd_trash_image_info_t pointers
defer C.rbd_trash_list_cleanup(&entries[0], count)
c_entries := make([]C.rbd_trash_image_info_t, num_entries)
trashList := make([]TrashInfo, num_entries)
if ret := C.rbd_trash_list(cephIoctx(ioctx), &c_entries[0], &num_entries); ret < 0 {
return nil, RBDError(ret)
}
for i, ti := range c_entries {
trashList := make([]TrashInfo, count)
for i, ti := range entries[:count] {
trashList[i] = TrashInfo{
Id: C.GoString(ti.id),
Name: C.GoString(ti.name),
@ -984,10 +998,6 @@ func GetTrashList(ioctx *rados.IOContext) ([]TrashInfo, error) {
DefermentEndTime: time.Unix(int64(ti.deferment_end_time), 0),
}
}
// Free rbd_trash_image_info_t pointers
C.rbd_trash_list_cleanup(&c_entries[0], num_entries)
return trashList, nil
}
@ -1018,6 +1028,13 @@ func TrashRestore(ioctx *rados.IOContext, id, name string) error {
// int rbd_open(rados_ioctx_t io, const char *name,
// rbd_image_t *image, const char *snap_name);
func OpenImage(ioctx *rados.IOContext, name, snapName string) (*Image, error) {
if ioctx == nil {
return nil, ErrNoIOContext
}
if name == "" {
return nil, ErrNoName
}
cName := C.CString(name)
defer C.free(unsafe.Pointer(cName))
@ -1054,6 +1071,13 @@ func OpenImage(ioctx *rados.IOContext, name, snapName string) (*Image, error) {
// int rbd_open_read_only(rados_ioctx_t io, const char *name,
// rbd_image_t *image, const char *snap_name);
func OpenImageReadOnly(ioctx *rados.IOContext, name, snapName string) (*Image, error) {
if ioctx == nil {
return nil, ErrNoIOContext
}
if name == "" {
return nil, ErrNoName
}
cName := C.CString(name)
defer C.free(unsafe.Pointer(cName))
@ -1091,6 +1115,13 @@ func OpenImageReadOnly(ioctx *rados.IOContext, name, snapName string) (*Image, e
// int rbd_open_by_id(rados_ioctx_t io, const char *id,
// rbd_image_t *image, const char *snap_name);
func OpenImageById(ioctx *rados.IOContext, id, snapName string) (*Image, error) {
if ioctx == nil {
return nil, ErrNoIOContext
}
if id == "" {
return nil, ErrNoName
}
cid := C.CString(id)
defer C.free(unsafe.Pointer(cid))
@ -1128,6 +1159,13 @@ func OpenImageById(ioctx *rados.IOContext, id, snapName string) (*Image, error)
// int rbd_open_by_id_read_only(rados_ioctx_t io, const char *id,
// rbd_image_t *image, const char *snap_name);
func OpenImageByIdReadOnly(ioctx *rados.IOContext, id, snapName string) (*Image, error) {
if ioctx == nil {
return nil, ErrNoIOContext
}
if id == "" {
return nil, ErrNoName
}
cid := C.CString(id)
defer C.free(unsafe.Pointer(cid))
@ -1160,7 +1198,12 @@ func OpenImageByIdReadOnly(ioctx *rados.IOContext, id, snapName string) (*Image,
// int rbd_create4(rados_ioctx_t io, const char *name, uint64_t size,
// rbd_image_options_t opts);
func CreateImage(ioctx *rados.IOContext, name string, size uint64, rio *ImageOptions) error {
if ioctx == nil {
return ErrNoIOContext
}
if name == "" {
return ErrNoName
}
if rio == nil {
return RBDError(C.EINVAL)
}
@ -1178,6 +1221,13 @@ func CreateImage(ioctx *rados.IOContext, name string, size uint64, rio *ImageOpt
// Implements:
// int rbd_remove(rados_ioctx_t io, const char *name);
func RemoveImage(ioctx *rados.IOContext, name string) error {
if ioctx == nil {
return ErrNoIOContext
}
if name == "" {
return ErrNoName
}
c_name := C.CString(name)
defer C.free(unsafe.Pointer(c_name))
return getError(C.rbd_remove(cephIoctx(ioctx), c_name))

View File

@ -15,29 +15,34 @@ import (
"bytes"
"unsafe"
"github.com/ceph/go-ceph/internal/retry"
"github.com/ceph/go-ceph/rados"
)
// GetImageNames returns the list of current RBD images.
func GetImageNames(ioctx *rados.IOContext) (names []string, err error) {
buf := make([]byte, 4096)
for {
size := C.size_t(len(buf))
var (
buf []byte
csize C.size_t
)
// from 4KiB to 32KiB
retry.WithSizes(4096, 1<<15, func(size int) retry.Hint {
csize = C.size_t(size)
buf = make([]byte, csize)
ret := C.rbd_list(cephIoctx(ioctx),
(*C.char)(unsafe.Pointer(&buf[0])), &size)
if ret == -C.ERANGE {
buf = make([]byte, size)
continue
} else if ret < 0 {
return nil, RBDError(ret)
}
tmp := bytes.Split(buf[:size-1], []byte{0})
for _, s := range tmp {
if len(s) > 0 {
name := C.GoString((*C.char)(unsafe.Pointer(&s[0])))
names = append(names, name)
}
}
return names, nil
(*C.char)(unsafe.Pointer(&buf[0])), &csize)
err = getErrorIfNegative(ret)
return retry.Size(int(csize)).If(err == errRange)
})
if err != nil {
return nil, err
}
tmp := bytes.Split(buf[:csize-1], []byte{0})
for _, s := range tmp {
if len(s) > 0 {
name := C.GoString((*C.char)(unsafe.Pointer(&s[0])))
names = append(names, name)
}
}
return names, nil
}

View File

@ -11,34 +11,36 @@ package rbd
import "C"
import (
"fmt"
"unsafe"
"github.com/ceph/go-ceph/internal/retry"
"github.com/ceph/go-ceph/rados"
)
// GetImageNames returns the list of current RBD images.
func GetImageNames(ioctx *rados.IOContext) ([]string, error) {
size := C.size_t(0)
ret := C.rbd_list2(cephIoctx(ioctx), nil, &size)
if ret < 0 && ret != -C.ERANGE {
return nil, RBDError(ret)
} else if ret > 0 {
return nil, fmt.Errorf("rbd_list2() returned %d names, expected 0", ret)
} else if ret == 0 && size == 0 {
return nil, nil
}
// expected: ret == -ERANGE, size contains number of image names
images := make([]C.rbd_image_spec_t, size)
ret = C.rbd_list2(cephIoctx(ioctx), (*C.rbd_image_spec_t)(unsafe.Pointer(&images[0])), &size)
if ret < 0 {
return nil, RBDError(ret)
var (
err error
images []C.rbd_image_spec_t
size C.size_t
)
retry.WithSizes(32, 4096, func(s int) retry.Hint {
size = C.size_t(s)
images = make([]C.rbd_image_spec_t, size)
ret := C.rbd_list2(
cephIoctx(ioctx),
(*C.rbd_image_spec_t)(unsafe.Pointer(&images[0])),
&size)
err = getErrorIfNegative(ret)
return retry.Size(int(size)).If(err == errRange)
})
if err != nil {
return nil, err
}
defer C.rbd_image_spec_list_cleanup((*C.rbd_image_spec_t)(unsafe.Pointer(&images[0])), size)
names := make([]string, size)
for i, image := range images {
for i, image := range images[:size] {
names[i] = C.GoString(image.name)
}
return names, nil

View File

@ -11,8 +11,9 @@ package rbd
import "C"
import (
"fmt"
"unsafe"
"github.com/ceph/go-ceph/internal/retry"
)
// GetParentInfo looks for the parent of the image and stores the pool, name
@ -76,27 +77,28 @@ func (image *Image) ListChildren() (pools []string, images []string, err error)
return nil, nil, err
}
size := C.size_t(0)
ret := C.rbd_list_children3(image.image, nil, &size)
if ret < 0 && ret != -C.ERANGE {
return nil, nil, RBDError(ret)
} else if ret > 0 {
return nil, nil, fmt.Errorf("rbd_list_children3() returned %d, expected 0", ret)
} else if ret == 0 && size == 0 {
return nil, nil, nil
var (
csize C.size_t
children []C.rbd_linked_image_spec_t
)
retry.WithSizes(16, 4096, func(size int) retry.Hint {
csize = C.size_t(size)
children = make([]C.rbd_linked_image_spec_t, csize)
ret := C.rbd_list_children3(
image.image,
(*C.rbd_linked_image_spec_t)(unsafe.Pointer(&children[0])),
&csize)
err = getErrorIfNegative(ret)
return retry.Size(int(csize)).If(err == errRange)
})
if err != nil {
return nil, nil, err
}
defer C.rbd_linked_image_spec_list_cleanup((*C.rbd_linked_image_spec_t)(unsafe.Pointer(&children[0])), csize)
// expected: ret == -ERANGE, size contains number of image names
children := make([]C.rbd_linked_image_spec_t, size)
ret = C.rbd_list_children3(image.image, (*C.rbd_linked_image_spec_t)(unsafe.Pointer(&children[0])), &size)
if ret < 0 {
return nil, nil, RBDError(ret)
}
defer C.rbd_linked_image_spec_list_cleanup((*C.rbd_linked_image_spec_t)(unsafe.Pointer(&children[0])), size)
pools = make([]string, size)
images = make([]string, size)
for i, child := range children {
pools = make([]string, csize)
images = make([]string, csize)
for i, child := range children[:csize] {
pools[i] = C.GoString(child.pool_name)
images[i] = C.GoString(child.image_name)
}

View File

@ -9,6 +9,10 @@ package rbd
// #include <rbd/librbd.h>
import "C"
import (
"github.com/ceph/go-ceph/internal/retry"
)
// ImageWatcher is a representation of the rbd_image_watcher_t from librbd.h
type ImageWatcher struct {
Addr string
@ -30,28 +34,28 @@ func (image *Image) ListWatchers() ([]ImageWatcher, error) {
return nil, err
}
count := C.ulong(0)
ret := C.rbd_watchers_list(image.image, nil, &count)
if ret != 0 && ret != -C.ERANGE {
return nil, getError(ret)
}
if ret == 0 && count == 0 {
return nil, nil
}
watchers := make([]C.rbd_image_watcher_t, count)
ret = C.rbd_watchers_list(image.image, &watchers[0], &count)
if ret != 0 && ret != -C.ERANGE {
return nil, getError(ret)
var (
err error
count C.size_t
watchers []C.rbd_image_watcher_t
)
retry.WithSizes(16, 4096, func(size int) retry.Hint {
count = C.size_t(size)
watchers = make([]C.rbd_image_watcher_t, count)
ret := C.rbd_watchers_list(image.image, &watchers[0], &count)
err = getErrorIfNegative(ret)
return retry.Size(int(count)).If(err == errRange)
})
if err != nil {
return nil, err
}
defer C.rbd_watchers_list_cleanup(&watchers[0], count)
imageWatchers := make([]ImageWatcher, len(watchers))
for i, watcher := range watchers {
imageWatchers := make([]ImageWatcher, count)
for i, watcher := range watchers[:count] {
imageWatchers[i].Addr = C.GoString(watcher.addr)
imageWatchers[i].Id = int64(watcher.id)
imageWatchers[i].Cookie = uint64(watcher.cookie)
}
return imageWatchers, nil
}