Initial QSfera import
This commit is contained in:
+34
@@ -0,0 +1,34 @@
|
||||
package rados
|
||||
|
||||
// #cgo LDFLAGS: -lrados
|
||||
// #include <rados/librados.h>
|
||||
//
|
||||
import "C"
|
||||
|
||||
// AllocHintFlags control the behavior of read and write operations.
|
||||
type AllocHintFlags uint32
|
||||
|
||||
const (
|
||||
// AllocHintNoHint indicates no predefined behavior
|
||||
AllocHintNoHint = AllocHintFlags(0)
|
||||
// AllocHintSequentialWrite TODO
|
||||
AllocHintSequentialWrite = AllocHintFlags(C.LIBRADOS_ALLOC_HINT_FLAG_SEQUENTIAL_WRITE)
|
||||
// AllocHintRandomWrite TODO
|
||||
AllocHintRandomWrite = AllocHintFlags(C.LIBRADOS_ALLOC_HINT_FLAG_RANDOM_WRITE)
|
||||
// AllocHintSequentialRead TODO
|
||||
AllocHintSequentialRead = AllocHintFlags(C.LIBRADOS_ALLOC_HINT_FLAG_SEQUENTIAL_READ)
|
||||
// AllocHintRandomRead TODO
|
||||
AllocHintRandomRead = AllocHintFlags(C.LIBRADOS_ALLOC_HINT_FLAG_RANDOM_READ)
|
||||
// AllocHintAppendOnly TODO
|
||||
AllocHintAppendOnly = AllocHintFlags(C.LIBRADOS_ALLOC_HINT_FLAG_APPEND_ONLY)
|
||||
// AllocHintImmutable TODO
|
||||
AllocHintImmutable = AllocHintFlags(C.LIBRADOS_ALLOC_HINT_FLAG_IMMUTABLE)
|
||||
// AllocHintShortlived TODO
|
||||
AllocHintShortlived = AllocHintFlags(C.LIBRADOS_ALLOC_HINT_FLAG_SHORTLIVED)
|
||||
// AllocHintLonglived TODO
|
||||
AllocHintLonglived = AllocHintFlags(C.LIBRADOS_ALLOC_HINT_FLAG_LONGLIVED)
|
||||
// AllocHintCompressible TODO
|
||||
AllocHintCompressible = AllocHintFlags(C.LIBRADOS_ALLOC_HINT_FLAG_COMPRESSIBLE)
|
||||
// AllocHintIncompressible TODO
|
||||
AllocHintIncompressible = AllocHintFlags(C.LIBRADOS_ALLOC_HINT_FLAG_INCOMPRESSIBLE)
|
||||
)
|
||||
+238
@@ -0,0 +1,238 @@
|
||||
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
|
||||
//
|
||||
// The args parameter takes a slice of byte slices but typically a single
|
||||
// slice element is sufficient. The use of two slices exists to best match
|
||||
// the structure of the underlying C call which is often a legacy interface
|
||||
// in Ceph.
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// The args parameter takes a slice of byte slices but typically a single
|
||||
// slice element is sufficient. The use of two slices exists to best match
|
||||
// the structure of the underlying C call which is often a legacy interface
|
||||
// in Ceph.
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
// The args parameter takes a slice of byte slices but typically a single
|
||||
// slice element is sufficient. The use of two slices exists to best match
|
||||
// the structure of the underlying C call which is often a legacy interface
|
||||
// in Ceph.
|
||||
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.
|
||||
//
|
||||
// The args parameter takes a slice of byte slices but typically a single
|
||||
// slice element is sufficient. The use of two slices exists to best match
|
||||
// the structure of the underlying C call which is often a legacy interface
|
||||
// in Ceph.
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
// The args parameter takes a slice of byte slices but typically a single
|
||||
// slice element is sufficient. The use of two slices exists to best match
|
||||
// the structure of the underlying C call which is often a legacy interface
|
||||
// in Ceph.
|
||||
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.
|
||||
//
|
||||
// The args parameter takes a slice of byte slices but typically a single
|
||||
// slice element is sufficient. The use of two slices exists to best match
|
||||
// the structure of the underlying C call which is often a legacy interface
|
||||
// in Ceph.
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
// The args parameter takes a slice of byte slices but typically a single
|
||||
// slice element is sufficient. The use of two slices exists to best match
|
||||
// the structure of the underlying C call which is often a legacy interface
|
||||
// in Ceph.
|
||||
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.
|
||||
//
|
||||
// The args parameter takes a slice of byte slices but typically a single
|
||||
// slice element is sufficient. The use of two slices exists to best match
|
||||
// the structure of the underlying C call which is often a legacy interface
|
||||
// in Ceph.
|
||||
//
|
||||
// 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)
|
||||
}
|
||||
+313
@@ -0,0 +1,313 @@
|
||||
package rados
|
||||
|
||||
// #cgo LDFLAGS: -lrados
|
||||
// #include <stdlib.h>
|
||||
// #include <rados/librados.h>
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
|
||||
"github.com/ceph/go-ceph/internal/cutil"
|
||||
"github.com/ceph/go-ceph/internal/retry"
|
||||
)
|
||||
|
||||
var argvPlaceholder = "placeholder"
|
||||
|
||||
//revive:disable:var-naming old-yet-exported public api
|
||||
|
||||
// ClusterStat represents Ceph cluster statistics.
|
||||
type ClusterStat struct {
|
||||
Kb uint64
|
||||
Kb_used uint64
|
||||
Kb_avail uint64
|
||||
Num_objects uint64
|
||||
}
|
||||
|
||||
//revive:enable:var-naming
|
||||
|
||||
// Conn is a connection handle to a Ceph cluster.
|
||||
type Conn struct {
|
||||
cluster C.rados_t
|
||||
connected bool
|
||||
}
|
||||
|
||||
// ClusterRef represents a fundamental RADOS cluster connection.
|
||||
type ClusterRef C.rados_t
|
||||
|
||||
// Cluster returns the underlying RADOS cluster reference for this Conn.
|
||||
func (c *Conn) Cluster() ClusterRef {
|
||||
return ClusterRef(c.cluster)
|
||||
}
|
||||
|
||||
// PingMonitor sends a ping to a monitor and returns the reply.
|
||||
func (c *Conn) PingMonitor(id string) (string, error) {
|
||||
cid := C.CString(id)
|
||||
defer C.free(unsafe.Pointer(cid))
|
||||
|
||||
var strlen C.size_t
|
||||
var strout *C.char
|
||||
|
||||
ret := C.rados_ping_monitor(c.cluster, cid, &strout, &strlen)
|
||||
defer C.rados_buffer_free(strout)
|
||||
|
||||
if ret == 0 {
|
||||
reply := C.GoStringN(strout, (C.int)(strlen))
|
||||
return reply, nil
|
||||
}
|
||||
return "", getError(ret)
|
||||
}
|
||||
|
||||
// Connect establishes a connection to a RADOS cluster. It returns an error,
|
||||
// if any.
|
||||
func (c *Conn) Connect() error {
|
||||
ret := C.rados_connect(c.cluster)
|
||||
if ret != 0 {
|
||||
return getError(ret)
|
||||
}
|
||||
c.connected = true
|
||||
return nil
|
||||
}
|
||||
|
||||
// Shutdown disconnects from the cluster.
|
||||
func (c *Conn) Shutdown() {
|
||||
if err := c.ensureConnected(); err != nil {
|
||||
return
|
||||
}
|
||||
freeConn(c)
|
||||
}
|
||||
|
||||
// ReadConfigFile configures the connection using a Ceph configuration file.
|
||||
func (c *Conn) ReadConfigFile(path string) error {
|
||||
cPath := C.CString(path)
|
||||
defer C.free(unsafe.Pointer(cPath))
|
||||
ret := C.rados_conf_read_file(c.cluster, cPath)
|
||||
return getError(ret)
|
||||
}
|
||||
|
||||
// ReadDefaultConfigFile configures the connection using a Ceph configuration
|
||||
// file located at default locations.
|
||||
func (c *Conn) ReadDefaultConfigFile() error {
|
||||
ret := C.rados_conf_read_file(c.cluster, nil)
|
||||
return getError(ret)
|
||||
}
|
||||
|
||||
// OpenIOContext creates and returns a new IOContext for the given pool.
|
||||
//
|
||||
// Implements:
|
||||
//
|
||||
// int rados_ioctx_create(rados_t cluster, const char *pool_name,
|
||||
// rados_ioctx_t *ioctx);
|
||||
func (c *Conn) OpenIOContext(pool string) (*IOContext, error) {
|
||||
cPool := C.CString(pool)
|
||||
defer C.free(unsafe.Pointer(cPool))
|
||||
ioctx := &IOContext{conn: c}
|
||||
ret := C.rados_ioctx_create(c.cluster, cPool, &ioctx.ioctx)
|
||||
if ret == 0 {
|
||||
return ioctx, nil
|
||||
}
|
||||
return nil, getError(ret)
|
||||
}
|
||||
|
||||
// ListPools returns the names of all existing pools.
|
||||
func (c *Conn) ListPools() (names []string, err error) {
|
||||
buf := make([]byte, 4096)
|
||||
for {
|
||||
ret := C.rados_pool_list(c.cluster,
|
||||
(*C.char)(unsafe.Pointer(&buf[0])), C.size_t(len(buf)))
|
||||
if ret < 0 {
|
||||
return nil, getError(ret)
|
||||
}
|
||||
|
||||
if int(ret) > len(buf) {
|
||||
buf = make([]byte, ret)
|
||||
continue
|
||||
}
|
||||
|
||||
names = cutil.SplitSparseBuffer(buf[:ret])
|
||||
return names, nil
|
||||
}
|
||||
}
|
||||
|
||||
// SetConfigOption sets the value of the configuration option identified by
|
||||
// the given name.
|
||||
func (c *Conn) SetConfigOption(option, value string) error {
|
||||
cOpt, cVal := C.CString(option), C.CString(value)
|
||||
defer C.free(unsafe.Pointer(cOpt))
|
||||
defer C.free(unsafe.Pointer(cVal))
|
||||
ret := C.rados_conf_set(c.cluster, cOpt, cVal)
|
||||
return getError(ret)
|
||||
}
|
||||
|
||||
// GetConfigOption returns the value of the Ceph configuration option
|
||||
// identified by the given name.
|
||||
func (c *Conn) GetConfigOption(name string) (value string, err error) {
|
||||
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
|
||||
}
|
||||
value = C.GoString((*C.char)(unsafe.Pointer(&buf[0])))
|
||||
return value, nil
|
||||
}
|
||||
|
||||
// WaitForLatestOSDMap blocks the caller until the latest OSD map has been
|
||||
// retrieved.
|
||||
func (c *Conn) WaitForLatestOSDMap() error {
|
||||
ret := C.rados_wait_for_latest_osdmap(c.cluster)
|
||||
return getError(ret)
|
||||
}
|
||||
|
||||
func (c *Conn) ensureConnected() error {
|
||||
if c.connected {
|
||||
return nil
|
||||
}
|
||||
return ErrNotConnected
|
||||
}
|
||||
|
||||
// GetClusterStats returns statistics about the cluster associated with the
|
||||
// connection.
|
||||
func (c *Conn) GetClusterStats() (stat ClusterStat, err error) {
|
||||
if err := c.ensureConnected(); err != nil {
|
||||
return ClusterStat{}, err
|
||||
}
|
||||
cStat := C.struct_rados_cluster_stat_t{}
|
||||
ret := C.rados_cluster_stat(c.cluster, &cStat)
|
||||
if ret < 0 {
|
||||
return ClusterStat{}, getError(ret)
|
||||
}
|
||||
return ClusterStat{
|
||||
Kb: uint64(cStat.kb),
|
||||
Kb_used: uint64(cStat.kb_used),
|
||||
Kb_avail: uint64(cStat.kb_avail),
|
||||
Num_objects: uint64(cStat.num_objects),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ParseConfigArgv configures the connection using a unix style command line
|
||||
// argument vector.
|
||||
//
|
||||
// Implements:
|
||||
//
|
||||
// int rados_conf_parse_argv(rados_t cluster, int argc,
|
||||
// const char **argv);
|
||||
func (c *Conn) ParseConfigArgv(argv []string) error {
|
||||
if c.cluster == nil {
|
||||
return ErrNotConnected
|
||||
}
|
||||
if len(argv) == 0 {
|
||||
return ErrEmptyArgument
|
||||
}
|
||||
cargv := make([]*C.char, len(argv))
|
||||
for i := range argv {
|
||||
cargv[i] = C.CString(argv[i])
|
||||
defer C.free(unsafe.Pointer(cargv[i]))
|
||||
}
|
||||
|
||||
ret := C.rados_conf_parse_argv(c.cluster, C.int(len(cargv)), &cargv[0])
|
||||
return getError(ret)
|
||||
}
|
||||
|
||||
// ParseCmdLineArgs configures the connection from command line arguments.
|
||||
//
|
||||
// This function passes a placeholder value to Ceph as argv[0], see
|
||||
// ParseConfigArgv for a version of this function that allows the caller to
|
||||
// specify argv[0].
|
||||
func (c *Conn) ParseCmdLineArgs(args []string) error {
|
||||
argv := make([]string, len(args)+1)
|
||||
// Ceph expects a proper argv array as the actual contents with the
|
||||
// first element containing the executable name
|
||||
argv[0] = argvPlaceholder
|
||||
for i := range args {
|
||||
argv[i+1] = args[i]
|
||||
}
|
||||
return c.ParseConfigArgv(argv)
|
||||
}
|
||||
|
||||
// ParseDefaultConfigEnv configures the connection from the default Ceph
|
||||
// environment variable CEPH_ARGS.
|
||||
func (c *Conn) ParseDefaultConfigEnv() error {
|
||||
ret := C.rados_conf_parse_env(c.cluster, nil)
|
||||
return getError(ret)
|
||||
}
|
||||
|
||||
// GetFSID returns the fsid of the cluster as a hexadecimal string. The fsid
|
||||
// is a unique identifier of an entire Ceph cluster.
|
||||
func (c *Conn) GetFSID() (fsid string, err error) {
|
||||
buf := make([]byte, 37)
|
||||
ret := C.rados_cluster_fsid(c.cluster,
|
||||
(*C.char)(unsafe.Pointer(&buf[0])), C.size_t(len(buf)))
|
||||
// FIXME: the success case isn't documented correctly in librados.h
|
||||
if ret == 36 {
|
||||
fsid = C.GoString((*C.char)(unsafe.Pointer(&buf[0])))
|
||||
return fsid, nil
|
||||
}
|
||||
return "", getError(ret)
|
||||
}
|
||||
|
||||
// GetInstanceID returns a globally unique identifier for the cluster
|
||||
// connection instance.
|
||||
func (c *Conn) GetInstanceID() uint64 {
|
||||
// FIXME: are there any error cases for this?
|
||||
return uint64(C.rados_get_instance_id(c.cluster))
|
||||
}
|
||||
|
||||
// MakePool creates a new pool with default settings.
|
||||
func (c *Conn) MakePool(name string) error {
|
||||
cName := C.CString(name)
|
||||
defer C.free(unsafe.Pointer(cName))
|
||||
ret := C.rados_pool_create(c.cluster, cName)
|
||||
return getError(ret)
|
||||
}
|
||||
|
||||
// DeletePool deletes a pool and all the data inside the pool.
|
||||
func (c *Conn) DeletePool(name string) error {
|
||||
if err := c.ensureConnected(); err != nil {
|
||||
return err
|
||||
}
|
||||
cName := C.CString(name)
|
||||
defer C.free(unsafe.Pointer(cName))
|
||||
ret := C.rados_pool_delete(c.cluster, cName)
|
||||
return getError(ret)
|
||||
}
|
||||
|
||||
// GetPoolByName returns the ID of the pool with a given name.
|
||||
func (c *Conn) GetPoolByName(name string) (int64, error) {
|
||||
if err := c.ensureConnected(); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
cName := C.CString(name)
|
||||
defer C.free(unsafe.Pointer(cName))
|
||||
ret := C.rados_pool_lookup(c.cluster, cName)
|
||||
if ret < 0 {
|
||||
return 0, getError(C.int(ret))
|
||||
}
|
||||
return int64(ret), nil
|
||||
}
|
||||
|
||||
// GetPoolByID returns the name of a pool by a given ID.
|
||||
func (c *Conn) GetPoolByID(id int64) (string, error) {
|
||||
buf := make([]byte, 4096)
|
||||
if err := c.ensureConnected(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
cid := C.int64_t(id)
|
||||
ret := C.rados_pool_reverse_lookup(c.cluster, cid, (*C.char)(unsafe.Pointer(&buf[0])), C.size_t(len(buf)))
|
||||
if ret < 0 {
|
||||
return "", getError(ret)
|
||||
}
|
||||
return C.GoString((*C.char)(unsafe.Pointer(&buf[0]))), nil
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
/*
|
||||
Package rados contains a set of wrappers around Ceph's librados API.
|
||||
*/
|
||||
package rados
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
package rados
|
||||
|
||||
/*
|
||||
#include <errno.h>
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/ceph/go-ceph/internal/errutil"
|
||||
)
|
||||
|
||||
// Public go errors:
|
||||
|
||||
var (
|
||||
// ErrNotConnected is returned when functions are called
|
||||
// without a RADOS connection.
|
||||
ErrNotConnected = getError(-C.ENOTCONN)
|
||||
// ErrEmptyArgument may be returned if a function argument is passed
|
||||
// a zero-length slice or map.
|
||||
ErrEmptyArgument = errors.New("Argument must contain at least one item")
|
||||
// ErrInvalidIOContext may be returned if an api call requires an IOContext
|
||||
// but IOContext is not ready for use.
|
||||
ErrInvalidIOContext = errors.New("IOContext is not ready for use")
|
||||
// ErrOperationIncomplete is returned from write op or read op steps for
|
||||
// which the operation has not been performed yet.
|
||||
ErrOperationIncomplete = errors.New("Operation has not been performed yet")
|
||||
|
||||
// ErrNotFound indicates a missing resource.
|
||||
ErrNotFound = getError(-C.ENOENT)
|
||||
// ErrPermissionDenied indicates a permissions issue.
|
||||
ErrPermissionDenied = getError(-C.EPERM)
|
||||
// ErrObjectExists indicates that an exclusive object creation failed.
|
||||
ErrObjectExists = getError(-C.EEXIST)
|
||||
|
||||
// RadosErrorNotFound indicates a missing resource.
|
||||
//
|
||||
// Deprecated: use ErrNotFound instead
|
||||
RadosErrorNotFound = ErrNotFound
|
||||
// RadosErrorPermissionDenied indicates a permissions issue.
|
||||
//
|
||||
// Deprecated: use ErrPermissionDenied instead
|
||||
RadosErrorPermissionDenied = ErrPermissionDenied
|
||||
|
||||
// Private errors:
|
||||
|
||||
errNameTooLong = getError(-C.ENAMETOOLONG)
|
||||
errRange = getError(-C.ERANGE)
|
||||
)
|
||||
|
||||
func getError(errno C.int) error {
|
||||
return errutil.GetError("rados", int(errno))
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
+725
@@ -0,0 +1,725 @@
|
||||
package rados
|
||||
|
||||
// #cgo LDFLAGS: -lrados
|
||||
// #include <errno.h>
|
||||
// #include <stdlib.h>
|
||||
// #include <rados/librados.h>
|
||||
//
|
||||
// char* nextChunk(char **idx) {
|
||||
// char *copy;
|
||||
// copy = strdup(*idx);
|
||||
// *idx += strlen(*idx) + 1;
|
||||
// return copy;
|
||||
// }
|
||||
//
|
||||
// #if __APPLE__
|
||||
// #define ceph_time_t __darwin_time_t
|
||||
// #define ceph_suseconds_t __darwin_suseconds_t
|
||||
// #elif __GLIBC__
|
||||
// #define ceph_time_t __time_t
|
||||
// #define ceph_suseconds_t __suseconds_t
|
||||
// #else
|
||||
// #define ceph_time_t time_t
|
||||
// #define ceph_suseconds_t suseconds_t
|
||||
// #endif
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"github.com/ceph/go-ceph/internal/retry"
|
||||
)
|
||||
|
||||
// CreateOption is passed to IOContext.Create() and should be one of
|
||||
// CreateExclusive or CreateIdempotent.
|
||||
type CreateOption int
|
||||
|
||||
const (
|
||||
// CreateExclusive if used with IOContext.Create() and the object
|
||||
// already exists, the function will return an error.
|
||||
CreateExclusive = C.LIBRADOS_CREATE_EXCLUSIVE
|
||||
// CreateIdempotent if used with IOContext.Create() and the object
|
||||
// already exists, the function will not return an error.
|
||||
CreateIdempotent = C.LIBRADOS_CREATE_IDEMPOTENT
|
||||
|
||||
defaultListObjectsResultSize = 1000
|
||||
// listEndSentinel is the value returned by rados_list_object_list_is_end
|
||||
// when a cursor has reached the end of a pool
|
||||
listEndSentinel = 1
|
||||
)
|
||||
|
||||
//revive:disable:var-naming old-yet-exported public api
|
||||
|
||||
// PoolStat represents Ceph pool statistics.
|
||||
type PoolStat struct {
|
||||
// space used in bytes
|
||||
Num_bytes uint64
|
||||
// space used in KB
|
||||
Num_kb uint64
|
||||
// number of objects in the pool
|
||||
Num_objects uint64
|
||||
// number of clones of objects
|
||||
Num_object_clones uint64
|
||||
// num_objects * num_replicas
|
||||
Num_object_copies uint64
|
||||
Num_objects_missing_on_primary uint64
|
||||
// number of objects found on no OSDs
|
||||
Num_objects_unfound uint64
|
||||
// number of objects replicated fewer times than they should be
|
||||
// (but found on at least one OSD)
|
||||
Num_objects_degraded uint64
|
||||
Num_rd uint64
|
||||
Num_rd_kb uint64
|
||||
Num_wr uint64
|
||||
Num_wr_kb uint64
|
||||
}
|
||||
|
||||
//revive:enable:var-naming
|
||||
|
||||
// ObjectStat represents an object stat information
|
||||
type ObjectStat struct {
|
||||
// current length in bytes
|
||||
Size uint64
|
||||
// last modification time
|
||||
ModTime time.Time
|
||||
}
|
||||
|
||||
// LockInfo represents information on a current Ceph lock
|
||||
type LockInfo struct {
|
||||
NumLockers int
|
||||
Exclusive bool
|
||||
Tag string
|
||||
Clients []string
|
||||
Cookies []string
|
||||
Addrs []string
|
||||
}
|
||||
|
||||
// IOContext represents a context for performing I/O within a pool.
|
||||
type IOContext struct {
|
||||
ioctx C.rados_ioctx_t
|
||||
|
||||
// Hold a reference back to the connection that the ioctx depends on so
|
||||
// that Go's GC doesn't trigger the Conn's finalizer before this
|
||||
// IOContext is destroyed.
|
||||
conn *Conn
|
||||
}
|
||||
|
||||
// validate returns an error if the ioctx is not ready to be used
|
||||
// with ceph C calls.
|
||||
func (ioctx *IOContext) validate() error {
|
||||
if ioctx.ioctx == nil {
|
||||
return ErrInvalidIOContext
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Pointer returns a pointer reference to an internal structure.
|
||||
// This function should NOT be used outside of go-ceph itself.
|
||||
func (ioctx *IOContext) Pointer() unsafe.Pointer {
|
||||
return unsafe.Pointer(ioctx.ioctx)
|
||||
}
|
||||
|
||||
// SetNamespace sets the namespace for objects within this IO context (pool).
|
||||
// Setting namespace to a empty or zero length string sets the pool to the default namespace.
|
||||
//
|
||||
// Implements:
|
||||
//
|
||||
// void rados_ioctx_set_namespace(rados_ioctx_t io,
|
||||
// const char *nspace);
|
||||
func (ioctx *IOContext) SetNamespace(namespace string) {
|
||||
var cns *C.char
|
||||
if len(namespace) > 0 {
|
||||
cns = C.CString(namespace)
|
||||
defer C.free(unsafe.Pointer(cns))
|
||||
}
|
||||
C.rados_ioctx_set_namespace(ioctx.ioctx, cns)
|
||||
}
|
||||
|
||||
// Create a new object with key oid.
|
||||
//
|
||||
// Implements:
|
||||
//
|
||||
// void rados_write_op_create(rados_write_op_t write_op, int exclusive,
|
||||
// const char* category)
|
||||
func (ioctx *IOContext) Create(oid string, exclusive CreateOption) error {
|
||||
op := CreateWriteOp()
|
||||
defer op.Release()
|
||||
op.Create(exclusive)
|
||||
return op.operateCompat(ioctx, oid)
|
||||
}
|
||||
|
||||
// Write writes len(data) bytes to the object with key oid starting at byte
|
||||
// offset offset. It returns an error, if any.
|
||||
func (ioctx *IOContext) Write(oid string, data []byte, offset uint64) error {
|
||||
coid := C.CString(oid)
|
||||
defer C.free(unsafe.Pointer(coid))
|
||||
|
||||
dataPointer := unsafe.Pointer(nil)
|
||||
if len(data) > 0 {
|
||||
dataPointer = unsafe.Pointer(&data[0])
|
||||
}
|
||||
|
||||
ret := C.rados_write(ioctx.ioctx, coid,
|
||||
(*C.char)(dataPointer),
|
||||
(C.size_t)(len(data)),
|
||||
(C.uint64_t)(offset))
|
||||
|
||||
return getError(ret)
|
||||
}
|
||||
|
||||
// WriteFull writes len(data) bytes to the object with key oid.
|
||||
// The object is filled with the provided data. If the object exists,
|
||||
// it is atomically truncated and then written. It returns an error, if any.
|
||||
func (ioctx *IOContext) WriteFull(oid string, data []byte) error {
|
||||
coid := C.CString(oid)
|
||||
defer C.free(unsafe.Pointer(coid))
|
||||
|
||||
ret := C.rados_write_full(ioctx.ioctx, coid,
|
||||
(*C.char)(unsafe.Pointer(&data[0])),
|
||||
(C.size_t)(len(data)))
|
||||
return getError(ret)
|
||||
}
|
||||
|
||||
// Append appends len(data) bytes to the object with key oid.
|
||||
// The object is appended with the provided data. If the object exists,
|
||||
// it is atomically appended to. It returns an error, if any.
|
||||
func (ioctx *IOContext) Append(oid string, data []byte) error {
|
||||
coid := C.CString(oid)
|
||||
defer C.free(unsafe.Pointer(coid))
|
||||
|
||||
ret := C.rados_append(ioctx.ioctx, coid,
|
||||
(*C.char)(unsafe.Pointer(&data[0])),
|
||||
(C.size_t)(len(data)))
|
||||
return getError(ret)
|
||||
}
|
||||
|
||||
// Read reads up to len(data) bytes from the object with key oid starting at byte
|
||||
// offset offset. It returns the number of bytes read and an error, if any.
|
||||
func (ioctx *IOContext) Read(oid string, data []byte, offset uint64) (int, error) {
|
||||
coid := C.CString(oid)
|
||||
defer C.free(unsafe.Pointer(coid))
|
||||
|
||||
var buf *C.char
|
||||
if len(data) > 0 {
|
||||
buf = (*C.char)(unsafe.Pointer(&data[0]))
|
||||
}
|
||||
|
||||
ret := C.rados_read(
|
||||
ioctx.ioctx,
|
||||
coid,
|
||||
buf,
|
||||
(C.size_t)(len(data)),
|
||||
(C.uint64_t)(offset))
|
||||
|
||||
if ret >= 0 {
|
||||
return int(ret), nil
|
||||
}
|
||||
return 0, getError(ret)
|
||||
}
|
||||
|
||||
// Delete deletes the object with key oid. It returns an error, if any.
|
||||
func (ioctx *IOContext) Delete(oid string) error {
|
||||
coid := C.CString(oid)
|
||||
defer C.free(unsafe.Pointer(coid))
|
||||
|
||||
return getError(C.rados_remove(ioctx.ioctx, coid))
|
||||
}
|
||||
|
||||
// Truncate resizes the object with key oid to size size. If the operation
|
||||
// enlarges the object, the new area is logically filled with zeroes. If the
|
||||
// operation shrinks the object, the excess data is removed. It returns an
|
||||
// error, if any.
|
||||
func (ioctx *IOContext) Truncate(oid string, size uint64) error {
|
||||
coid := C.CString(oid)
|
||||
defer C.free(unsafe.Pointer(coid))
|
||||
|
||||
return getError(C.rados_trunc(ioctx.ioctx, coid, (C.uint64_t)(size)))
|
||||
}
|
||||
|
||||
// Destroy informs librados that the I/O context is no longer in use.
|
||||
// Resources associated with the context may not be freed immediately, and the
|
||||
// context should not be used again after calling this method.
|
||||
func (ioctx *IOContext) Destroy() {
|
||||
C.rados_ioctx_destroy(ioctx.ioctx)
|
||||
}
|
||||
|
||||
// GetPoolStats returns a set of statistics about the pool associated with this I/O
|
||||
// context.
|
||||
//
|
||||
// Implements:
|
||||
//
|
||||
// int rados_ioctx_pool_stat(rados_ioctx_t io,
|
||||
// struct rados_pool_stat_t *stats);
|
||||
func (ioctx *IOContext) GetPoolStats() (stat PoolStat, err error) {
|
||||
cStat := C.struct_rados_pool_stat_t{}
|
||||
ret := C.rados_ioctx_pool_stat(ioctx.ioctx, &cStat)
|
||||
if ret < 0 {
|
||||
return PoolStat{}, getError(ret)
|
||||
}
|
||||
return PoolStat{
|
||||
Num_bytes: uint64(cStat.num_bytes),
|
||||
Num_kb: uint64(cStat.num_kb),
|
||||
Num_objects: uint64(cStat.num_objects),
|
||||
Num_object_clones: uint64(cStat.num_object_clones),
|
||||
Num_object_copies: uint64(cStat.num_object_copies),
|
||||
Num_objects_missing_on_primary: uint64(cStat.num_objects_missing_on_primary),
|
||||
Num_objects_unfound: uint64(cStat.num_objects_unfound),
|
||||
Num_objects_degraded: uint64(cStat.num_objects_degraded),
|
||||
Num_rd: uint64(cStat.num_rd),
|
||||
Num_rd_kb: uint64(cStat.num_rd_kb),
|
||||
Num_wr: uint64(cStat.num_wr),
|
||||
Num_wr_kb: uint64(cStat.num_wr_kb),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetPoolID returns the pool ID associated with the I/O context.
|
||||
//
|
||||
// Implements:
|
||||
//
|
||||
// int64_t rados_ioctx_get_id(rados_ioctx_t io)
|
||||
func (ioctx *IOContext) GetPoolID() int64 {
|
||||
ret := C.rados_ioctx_get_id(ioctx.ioctx)
|
||||
return int64(ret)
|
||||
}
|
||||
|
||||
// GetPoolName returns the name of the pool associated with the I/O context.
|
||||
func (ioctx *IOContext) GetPoolName() (name string, err error) {
|
||||
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
|
||||
// by ListObjects.
|
||||
type ObjectListFunc func(oid string)
|
||||
|
||||
// ListObjects lists all of the objects in the pool associated with the I/O
|
||||
// context, and called the provided listFn function for each object, passing
|
||||
// to the function the name of the object. Call SetNamespace with
|
||||
// RadosAllNamespaces before calling this function to return objects from all
|
||||
// namespaces
|
||||
func (ioctx *IOContext) ListObjects(listFn ObjectListFunc) error {
|
||||
pageResults := C.size_t(defaultListObjectsResultSize)
|
||||
var filterLen C.size_t
|
||||
results := make([]C.rados_object_list_item, pageResults)
|
||||
|
||||
next := C.rados_object_list_begin(ioctx.ioctx)
|
||||
if next == nil {
|
||||
return ErrNotFound
|
||||
}
|
||||
defer C.rados_object_list_cursor_free(ioctx.ioctx, next)
|
||||
finish := C.rados_object_list_end(ioctx.ioctx)
|
||||
if finish == nil {
|
||||
return ErrNotFound
|
||||
}
|
||||
defer C.rados_object_list_cursor_free(ioctx.ioctx, finish)
|
||||
|
||||
for {
|
||||
res := (*C.rados_object_list_item)(unsafe.Pointer(&results[0]))
|
||||
ret := C.rados_object_list(ioctx.ioctx, next, finish, pageResults, nil, filterLen, res, &next)
|
||||
if ret < 0 {
|
||||
return getError(ret)
|
||||
}
|
||||
|
||||
numEntries := int(ret)
|
||||
for i := 0; i < numEntries; i++ {
|
||||
item := results[i]
|
||||
listFn(C.GoStringN(item.oid, (C.int)(item.oid_length)))
|
||||
}
|
||||
C.rados_object_list_free(C.size_t(ret), res)
|
||||
|
||||
if C.rados_object_list_is_end(ioctx.ioctx, next) == listEndSentinel {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Stat returns the size of the object and its last modification time
|
||||
func (ioctx *IOContext) Stat(object string) (stat ObjectStat, err error) {
|
||||
var cPsize C.uint64_t
|
||||
var cPmtime C.time_t
|
||||
cObject := C.CString(object)
|
||||
defer C.free(unsafe.Pointer(cObject))
|
||||
|
||||
ret := C.rados_stat(
|
||||
ioctx.ioctx,
|
||||
cObject,
|
||||
&cPsize,
|
||||
&cPmtime)
|
||||
|
||||
if ret < 0 {
|
||||
return ObjectStat{}, getError(ret)
|
||||
}
|
||||
return ObjectStat{
|
||||
Size: uint64(cPsize),
|
||||
ModTime: time.Unix(int64(cPmtime), 0),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetXattr gets an xattr with key `name`, it returns the length of
|
||||
// the key read or an error if not successful
|
||||
func (ioctx *IOContext) GetXattr(object string, name string, data []byte) (int, error) {
|
||||
cObject := C.CString(object)
|
||||
cName := C.CString(name)
|
||||
defer C.free(unsafe.Pointer(cObject))
|
||||
defer C.free(unsafe.Pointer(cName))
|
||||
|
||||
ret := C.rados_getxattr(
|
||||
ioctx.ioctx,
|
||||
cObject,
|
||||
cName,
|
||||
(*C.char)(unsafe.Pointer(&data[0])),
|
||||
(C.size_t)(len(data)))
|
||||
|
||||
if ret >= 0 {
|
||||
return int(ret), nil
|
||||
}
|
||||
return 0, getError(ret)
|
||||
}
|
||||
|
||||
// SetXattr sets an xattr for an object with key `name` with value as `data`
|
||||
func (ioctx *IOContext) SetXattr(object string, name string, data []byte) error {
|
||||
cObject := C.CString(object)
|
||||
cName := C.CString(name)
|
||||
defer C.free(unsafe.Pointer(cObject))
|
||||
defer C.free(unsafe.Pointer(cName))
|
||||
|
||||
ret := C.rados_setxattr(
|
||||
ioctx.ioctx,
|
||||
cObject,
|
||||
cName,
|
||||
(*C.char)(unsafe.Pointer(&data[0])),
|
||||
(C.size_t)(len(data)))
|
||||
|
||||
return getError(ret)
|
||||
}
|
||||
|
||||
// ListXattrs lists all the xattrs for an object. The xattrs are returned as a
|
||||
// mapping of string keys and byte-slice values.
|
||||
func (ioctx *IOContext) ListXattrs(oid string) (map[string][]byte, error) {
|
||||
coid := C.CString(oid)
|
||||
defer C.free(unsafe.Pointer(coid))
|
||||
|
||||
var it C.rados_xattrs_iter_t
|
||||
|
||||
ret := C.rados_getxattrs(ioctx.ioctx, coid, &it)
|
||||
if ret < 0 {
|
||||
return nil, getError(ret)
|
||||
}
|
||||
defer func() { C.rados_getxattrs_end(it) }()
|
||||
m := make(map[string][]byte)
|
||||
for {
|
||||
var cName, cVal *C.char
|
||||
var cLen C.size_t
|
||||
defer C.free(unsafe.Pointer(cName))
|
||||
defer C.free(unsafe.Pointer(cVal))
|
||||
|
||||
ret := C.rados_getxattrs_next(it, &cName, &cVal, &cLen)
|
||||
if ret < 0 {
|
||||
return nil, getError(ret)
|
||||
}
|
||||
// rados api returns a null name,val & 0-length upon
|
||||
// end of iteration
|
||||
if cName == nil {
|
||||
return m, nil // stop iteration
|
||||
}
|
||||
m[C.GoString(cName)] = C.GoBytes(unsafe.Pointer(cVal), (C.int)(cLen))
|
||||
}
|
||||
}
|
||||
|
||||
// RmXattr removes an xattr with key `name` from object `oid`
|
||||
func (ioctx *IOContext) RmXattr(oid string, name string) error {
|
||||
coid := C.CString(oid)
|
||||
cName := C.CString(name)
|
||||
defer C.free(unsafe.Pointer(coid))
|
||||
defer C.free(unsafe.Pointer(cName))
|
||||
|
||||
ret := C.rados_rmxattr(
|
||||
ioctx.ioctx,
|
||||
coid,
|
||||
cName)
|
||||
|
||||
return getError(ret)
|
||||
}
|
||||
|
||||
// LockExclusive takes an exclusive lock on an object.
|
||||
func (ioctx *IOContext) LockExclusive(oid, name, cookie, desc string, duration time.Duration, flags *byte) (int, error) {
|
||||
coid := C.CString(oid)
|
||||
cName := C.CString(name)
|
||||
cCookie := C.CString(cookie)
|
||||
cDesc := C.CString(desc)
|
||||
|
||||
var cDuration C.struct_timeval
|
||||
if duration != 0 {
|
||||
tv := syscall.NsecToTimeval(duration.Nanoseconds())
|
||||
cDuration = C.struct_timeval{tv_sec: C.ceph_time_t(tv.Sec), tv_usec: C.ceph_suseconds_t(tv.Usec)}
|
||||
}
|
||||
|
||||
var cFlags C.uint8_t
|
||||
if flags != nil {
|
||||
cFlags = C.uint8_t(*flags)
|
||||
}
|
||||
|
||||
defer C.free(unsafe.Pointer(coid))
|
||||
defer C.free(unsafe.Pointer(cName))
|
||||
defer C.free(unsafe.Pointer(cCookie))
|
||||
defer C.free(unsafe.Pointer(cDesc))
|
||||
|
||||
ret := C.rados_lock_exclusive(
|
||||
ioctx.ioctx,
|
||||
coid,
|
||||
cName,
|
||||
cCookie,
|
||||
cDesc,
|
||||
&cDuration,
|
||||
cFlags)
|
||||
|
||||
// 0 on success, negative error code on failure
|
||||
// -EBUSY if the lock is already held by another (client, cookie) pair
|
||||
// -EEXIST if the lock is already held by the same (client, cookie) pair
|
||||
|
||||
switch ret {
|
||||
case 0:
|
||||
return int(ret), nil
|
||||
case -C.EBUSY:
|
||||
return int(ret), nil
|
||||
case -C.EEXIST:
|
||||
return int(ret), nil
|
||||
default:
|
||||
return int(ret), getError(ret)
|
||||
}
|
||||
}
|
||||
|
||||
// LockShared takes a shared lock on an object.
|
||||
func (ioctx *IOContext) LockShared(oid, name, cookie, tag, desc string, duration time.Duration, flags *byte) (int, error) {
|
||||
coid := C.CString(oid)
|
||||
cName := C.CString(name)
|
||||
cCookie := C.CString(cookie)
|
||||
cTag := C.CString(tag)
|
||||
cDesc := C.CString(desc)
|
||||
|
||||
var cDuration C.struct_timeval
|
||||
if duration != 0 {
|
||||
tv := syscall.NsecToTimeval(duration.Nanoseconds())
|
||||
cDuration = C.struct_timeval{tv_sec: C.ceph_time_t(tv.Sec), tv_usec: C.ceph_suseconds_t(tv.Usec)}
|
||||
}
|
||||
|
||||
var cFlags C.uint8_t
|
||||
if flags != nil {
|
||||
cFlags = C.uint8_t(*flags)
|
||||
}
|
||||
|
||||
defer C.free(unsafe.Pointer(coid))
|
||||
defer C.free(unsafe.Pointer(cName))
|
||||
defer C.free(unsafe.Pointer(cCookie))
|
||||
defer C.free(unsafe.Pointer(cTag))
|
||||
defer C.free(unsafe.Pointer(cDesc))
|
||||
|
||||
ret := C.rados_lock_shared(
|
||||
ioctx.ioctx,
|
||||
coid,
|
||||
cName,
|
||||
cCookie,
|
||||
cTag,
|
||||
cDesc,
|
||||
&cDuration,
|
||||
cFlags)
|
||||
|
||||
// 0 on success, negative error code on failure
|
||||
// -EBUSY if the lock is already held by another (client, cookie) pair
|
||||
// -EEXIST if the lock is already held by the same (client, cookie) pair
|
||||
|
||||
switch ret {
|
||||
case 0:
|
||||
return int(ret), nil
|
||||
case -C.EBUSY:
|
||||
return int(ret), nil
|
||||
case -C.EEXIST:
|
||||
return int(ret), nil
|
||||
default:
|
||||
return int(ret), getError(ret)
|
||||
}
|
||||
}
|
||||
|
||||
// Unlock releases a shared or exclusive lock on an object.
|
||||
func (ioctx *IOContext) Unlock(oid, name, cookie string) (int, error) {
|
||||
coid := C.CString(oid)
|
||||
cName := C.CString(name)
|
||||
cCookie := C.CString(cookie)
|
||||
|
||||
defer C.free(unsafe.Pointer(coid))
|
||||
defer C.free(unsafe.Pointer(cName))
|
||||
defer C.free(unsafe.Pointer(cCookie))
|
||||
|
||||
// 0 on success, negative error code on failure
|
||||
// -ENOENT if the lock is not held by the specified (client, cookie) pair
|
||||
|
||||
ret := C.rados_unlock(
|
||||
ioctx.ioctx,
|
||||
coid,
|
||||
cName,
|
||||
cCookie)
|
||||
|
||||
switch ret {
|
||||
case 0:
|
||||
return int(ret), nil
|
||||
case -C.ENOENT:
|
||||
return int(ret), nil
|
||||
default:
|
||||
return int(ret), getError(ret)
|
||||
}
|
||||
}
|
||||
|
||||
// ListLockers lists clients that have locked the named object lock and
|
||||
// information about the lock.
|
||||
// The number of bytes required in each buffer is put in the corresponding size
|
||||
// out parameter. If any of the provided buffers are too short, -ERANGE is
|
||||
// returned after these sizes are filled in.
|
||||
func (ioctx *IOContext) ListLockers(oid, name string) (*LockInfo, error) {
|
||||
coid := C.CString(oid)
|
||||
cName := C.CString(name)
|
||||
|
||||
cTag := (*C.char)(C.malloc(C.size_t(1024)))
|
||||
cClients := (*C.char)(C.malloc(C.size_t(1024)))
|
||||
cCookies := (*C.char)(C.malloc(C.size_t(1024)))
|
||||
cAddrs := (*C.char)(C.malloc(C.size_t(1024)))
|
||||
|
||||
var cExclusive C.int
|
||||
cTagLen := C.size_t(1024)
|
||||
cClientsLen := C.size_t(1024)
|
||||
cCookiesLen := C.size_t(1024)
|
||||
cAddrsLen := C.size_t(1024)
|
||||
|
||||
defer C.free(unsafe.Pointer(coid))
|
||||
defer C.free(unsafe.Pointer(cName))
|
||||
defer C.free(unsafe.Pointer(cTag))
|
||||
defer C.free(unsafe.Pointer(cClients))
|
||||
defer C.free(unsafe.Pointer(cCookies))
|
||||
defer C.free(unsafe.Pointer(cAddrs))
|
||||
|
||||
ret := C.rados_list_lockers(
|
||||
ioctx.ioctx,
|
||||
coid,
|
||||
cName,
|
||||
&cExclusive,
|
||||
cTag,
|
||||
&cTagLen,
|
||||
cClients,
|
||||
&cClientsLen,
|
||||
cCookies,
|
||||
&cCookiesLen,
|
||||
cAddrs,
|
||||
&cAddrsLen)
|
||||
|
||||
splitCString := func(items *C.char, itemsLen C.size_t) []string {
|
||||
currLen := 0
|
||||
clients := []string{}
|
||||
for currLen < int(itemsLen) {
|
||||
client := C.GoString(C.nextChunk(&items))
|
||||
clients = append(clients, client)
|
||||
currLen += len(client) + 1
|
||||
}
|
||||
return clients
|
||||
}
|
||||
|
||||
if ret < 0 {
|
||||
return nil, getError(C.int(ret))
|
||||
}
|
||||
return &LockInfo{int(ret), cExclusive == 1, C.GoString(cTag), splitCString(cClients, cClientsLen), splitCString(cCookies, cCookiesLen), splitCString(cAddrs, cAddrsLen)}, nil
|
||||
}
|
||||
|
||||
// BreakLock releases a shared or exclusive lock on an object, which was taken by the specified client.
|
||||
func (ioctx *IOContext) BreakLock(oid, name, client, cookie string) (int, error) {
|
||||
coid := C.CString(oid)
|
||||
cName := C.CString(name)
|
||||
cClient := C.CString(client)
|
||||
cCookie := C.CString(cookie)
|
||||
|
||||
defer C.free(unsafe.Pointer(coid))
|
||||
defer C.free(unsafe.Pointer(cName))
|
||||
defer C.free(unsafe.Pointer(cClient))
|
||||
defer C.free(unsafe.Pointer(cCookie))
|
||||
|
||||
// 0 on success, negative error code on failure
|
||||
// -ENOENT if the lock is not held by the specified (client, cookie) pair
|
||||
// -EINVAL if the client cannot be parsed
|
||||
|
||||
ret := C.rados_break_lock(
|
||||
ioctx.ioctx,
|
||||
coid,
|
||||
cName,
|
||||
cClient,
|
||||
cCookie)
|
||||
|
||||
switch ret {
|
||||
case 0:
|
||||
return int(ret), nil
|
||||
case -C.ENOENT:
|
||||
return int(ret), nil
|
||||
case -C.EINVAL: // -EINVAL
|
||||
return int(ret), nil
|
||||
default:
|
||||
return int(ret), getError(ret)
|
||||
}
|
||||
}
|
||||
|
||||
// GetLastVersion will return the version number of the last object read or
|
||||
// written to.
|
||||
//
|
||||
// Implements:
|
||||
//
|
||||
// uint64_t rados_get_last_version(rados_ioctx_t io);
|
||||
func (ioctx *IOContext) GetLastVersion() (uint64, error) {
|
||||
if err := ioctx.validate(); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
v := C.rados_get_last_version(ioctx.ioctx)
|
||||
return uint64(v), nil
|
||||
}
|
||||
|
||||
// GetNamespace gets the namespace used for objects within this IO context.
|
||||
//
|
||||
// Implements:
|
||||
//
|
||||
// int rados_ioctx_get_namespace(rados_ioctx_t io, char *buf,
|
||||
// unsigned maxlen);
|
||||
func (ioctx *IOContext) GetNamespace() (string, error) {
|
||||
if err := ioctx.validate(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
var (
|
||||
err error
|
||||
buf []byte
|
||||
ret C.int
|
||||
)
|
||||
retry.WithSizes(128, 8192, func(size int) retry.Hint {
|
||||
buf = make([]byte, size)
|
||||
ret = C.rados_ioctx_get_namespace(
|
||||
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
|
||||
}
|
||||
return string(buf[:ret]), nil
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
//go:build nautilus
|
||||
// +build nautilus
|
||||
|
||||
package rados
|
||||
|
||||
// #cgo LDFLAGS: -lrados
|
||||
// #include <rados/librados.h>
|
||||
//
|
||||
import "C"
|
||||
|
||||
// SetPoolFullTry makes sure to send requests to the cluster despite
|
||||
// the cluster or pool being marked full; ops will either succeed(e.g., delete)
|
||||
// or return EDQUOT or ENOSPC.
|
||||
//
|
||||
// Implements:
|
||||
//
|
||||
// void rados_set_osdmap_full_try(rados_ioctx_t io);
|
||||
func (ioctx *IOContext) SetPoolFullTry() error {
|
||||
if err := ioctx.validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
C.rados_set_osdmap_full_try(ioctx.ioctx)
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnsetPoolFullTry unsets the flag set by SetPoolFullTry()
|
||||
//
|
||||
// Implements:
|
||||
//
|
||||
// void rados_unset_osdmap_full_try(rados_ioctx_t io);
|
||||
func (ioctx *IOContext) UnsetPoolFullTry() error {
|
||||
if err := ioctx.validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
C.rados_unset_osdmap_full_try(ioctx.ioctx)
|
||||
return nil
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
//go:build !nautilus
|
||||
// +build !nautilus
|
||||
|
||||
package rados
|
||||
|
||||
// #cgo LDFLAGS: -lrados
|
||||
// #include <rados/librados.h>
|
||||
//
|
||||
import "C"
|
||||
|
||||
// Ceph octopus deprecates rados_set_osdmap_full_try() and implements rados_set_pool_full_try()
|
||||
// Ceph octopus deprecates rados_unset_osdmap_full_try() and implements rados_unset_pool_full_try()
|
||||
|
||||
// SetPoolFullTry makes sure to send requests to the cluster despite
|
||||
// the cluster or pool being marked full; ops will either succeed(e.g., delete)
|
||||
// or return EDQUOT or ENOSPC.
|
||||
//
|
||||
// Implements:
|
||||
//
|
||||
// void rados_set_pool_full_try(rados_ioctx_t io);
|
||||
func (ioctx *IOContext) SetPoolFullTry() error {
|
||||
if err := ioctx.validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
C.rados_set_pool_full_try(ioctx.ioctx)
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnsetPoolFullTry unsets the flag set by SetPoolFullTry()
|
||||
//
|
||||
// Implements:
|
||||
//
|
||||
// void rados_unset_pool_full_try(rados_ioctx_t io);
|
||||
func (ioctx *IOContext) UnsetPoolFullTry() error {
|
||||
if err := ioctx.validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
C.rados_unset_pool_full_try(ioctx.ioctx)
|
||||
return nil
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
package rados
|
||||
|
||||
// #cgo LDFLAGS: -lrados
|
||||
// #include <rados/librados.h>
|
||||
// #include <stdlib.h>
|
||||
//
|
||||
import "C"
|
||||
|
||||
// Alignment returns the required stripe size in bytes for pools supporting/requiring it, or an error if unsuccessful.
|
||||
// For an EC pool, a buffer size multiple of its stripe size is required to call Append. To know if the pool requires
|
||||
// alignment or not, use RequiresAlignment.
|
||||
//
|
||||
// Implements:
|
||||
//
|
||||
// int rados_ioctx_pool_required_alignment2(rados_ioctx_t io, uint64_t *alignment)
|
||||
func (ioctx *IOContext) Alignment() (uint64, error) {
|
||||
var alignSizeBytes C.uint64_t
|
||||
ret := C.rados_ioctx_pool_required_alignment2(
|
||||
ioctx.ioctx,
|
||||
&alignSizeBytes)
|
||||
if ret != 0 {
|
||||
return 0, getError(ret)
|
||||
}
|
||||
return uint64(alignSizeBytes), nil
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
package rados
|
||||
|
||||
// #cgo LDFLAGS: -lrados
|
||||
// #include <rados/librados.h>
|
||||
// #include <stdlib.h>
|
||||
//
|
||||
import "C"
|
||||
|
||||
// RequiresAlignment returns true if the pool supports/requires alignment or an error if not successful.
|
||||
// For an EC pool, a buffer size multiple of its stripe size is required to call Append. See
|
||||
// Alignment to know how to get the stripe size for pools requiring it.
|
||||
//
|
||||
// Implements:
|
||||
//
|
||||
// int rados_ioctx_pool_requires_alignment2(rados_ioctx_t io, int *req)
|
||||
func (ioctx *IOContext) RequiresAlignment() (bool, error) {
|
||||
var alignRequired C.int
|
||||
ret := C.rados_ioctx_pool_requires_alignment2(
|
||||
ioctx.ioctx,
|
||||
&alignRequired)
|
||||
if ret != 0 {
|
||||
return false, getError(ret)
|
||||
}
|
||||
return (alignRequired != 0), nil
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
package rados
|
||||
|
||||
// #cgo LDFLAGS: -lrados
|
||||
// #include <rados/librados.h>
|
||||
// #include <stdlib.h>
|
||||
//
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// SetAllocationHint sets allocation hint for an object. This is an advisory
|
||||
// operation, it will always succeed (as if it was submitted with a
|
||||
// LIBRADOS_OP_FLAG_FAILOK flag set) and is not guaranteed to do anything on
|
||||
// the backend.
|
||||
//
|
||||
// Implements:
|
||||
//
|
||||
// int rados_set_alloc_hint2(rados_ioctx_t io,
|
||||
// const char *o,
|
||||
// uint64_t expected_object_size,
|
||||
// uint64_t expected_write_size,
|
||||
// uint32_t flags);
|
||||
func (ioctx *IOContext) SetAllocationHint(oid string, expectedObjectSize uint64, expectedWriteSize uint64, flags AllocHintFlags) error {
|
||||
coid := C.CString(oid)
|
||||
defer C.free(unsafe.Pointer(coid))
|
||||
|
||||
return getError(C.rados_set_alloc_hint2(
|
||||
ioctx.ioctx,
|
||||
coid,
|
||||
(C.uint64_t)(expectedObjectSize),
|
||||
(C.uint64_t)(expectedWriteSize),
|
||||
(C.uint32_t)(flags),
|
||||
))
|
||||
}
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
package rados
|
||||
|
||||
// #cgo LDFLAGS: -lrados
|
||||
// #include <rados/librados.h>
|
||||
//
|
||||
import "C"
|
||||
|
||||
// Iter supports iterating over objects in the ioctx.
|
||||
type Iter struct {
|
||||
ctx C.rados_list_ctx_t
|
||||
err error
|
||||
entry string
|
||||
namespace string
|
||||
}
|
||||
|
||||
// IterToken supports reporting on and seeking to different positions.
|
||||
type IterToken uint32
|
||||
|
||||
// Iter returns a Iterator object that can be used to list the object names in the current pool
|
||||
func (ioctx *IOContext) Iter() (*Iter, error) {
|
||||
iter := Iter{}
|
||||
if cerr := C.rados_nobjects_list_open(ioctx.ioctx, &iter.ctx); cerr < 0 {
|
||||
return nil, getError(cerr)
|
||||
}
|
||||
return &iter, nil
|
||||
}
|
||||
|
||||
// Token returns a token marking the current position of the iterator. To be used in combination with Iter.Seek()
|
||||
func (iter *Iter) Token() IterToken {
|
||||
return IterToken(C.rados_nobjects_list_get_pg_hash_position(iter.ctx))
|
||||
}
|
||||
|
||||
// Seek moves the iterator to the position indicated by the token.
|
||||
func (iter *Iter) Seek(token IterToken) {
|
||||
C.rados_nobjects_list_seek(iter.ctx, C.uint32_t(token))
|
||||
}
|
||||
|
||||
// Next retrieves the next object name in the pool/namespace iterator.
|
||||
// Upon a successful invocation (return value of true), the Value method should
|
||||
// be used to obtain the name of the retrieved object name. When the iterator is
|
||||
// exhausted, Next returns false. The Err method should used to verify whether the
|
||||
// end of the iterator was reached, or the iterator received an error.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// iter := pool.Iter()
|
||||
// defer iter.Close()
|
||||
// for iter.Next() {
|
||||
// fmt.Printf("%v\n", iter.Value())
|
||||
// }
|
||||
// return iter.Err()
|
||||
func (iter *Iter) Next() bool {
|
||||
var cEntry *C.char
|
||||
var cNamespace *C.char
|
||||
if cerr := C.rados_nobjects_list_next(iter.ctx, &cEntry, nil, &cNamespace); cerr < 0 {
|
||||
iter.err = getError(cerr)
|
||||
return false
|
||||
}
|
||||
iter.entry = C.GoString(cEntry)
|
||||
iter.namespace = C.GoString(cNamespace)
|
||||
return true
|
||||
}
|
||||
|
||||
// Value returns the current value of the iterator (object name), after a successful call to Next.
|
||||
func (iter *Iter) Value() string {
|
||||
if iter.err != nil {
|
||||
return ""
|
||||
}
|
||||
return iter.entry
|
||||
}
|
||||
|
||||
// Namespace returns the namespace associated with the current value of the iterator (object name), after a successful call to Next.
|
||||
func (iter *Iter) Namespace() string {
|
||||
if iter.err != nil {
|
||||
return ""
|
||||
}
|
||||
return iter.namespace
|
||||
}
|
||||
|
||||
// Err checks whether the iterator has encountered an error.
|
||||
func (iter *Iter) Err() error {
|
||||
if iter.err == ErrNotFound {
|
||||
return nil
|
||||
}
|
||||
return iter.err
|
||||
}
|
||||
|
||||
// Close the iterator cursor on the server. Be aware that iterators are not closed automatically
|
||||
// at the end of iteration.
|
||||
func (iter *Iter) Close() {
|
||||
C.rados_nobjects_list_close(iter.ctx)
|
||||
}
|
||||
+205
@@ -0,0 +1,205 @@
|
||||
package rados
|
||||
|
||||
/*
|
||||
#cgo LDFLAGS: -lrados
|
||||
#include <stdlib.h>
|
||||
#include <rados/librados.h>
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// OmapKeyValue items are returned by the GetOmapStep's Next call.
|
||||
type OmapKeyValue struct {
|
||||
Key string
|
||||
Value []byte
|
||||
}
|
||||
|
||||
// GetOmapStep values are used to get the results of an GetOmapValues call
|
||||
// on a WriteOp. Until the Operate method of the WriteOp is called the Next
|
||||
// call will return an error. After Operate is called, the Next call will
|
||||
// return valid results.
|
||||
//
|
||||
// The life cycle of the GetOmapStep is bound to the ReadOp, if the ReadOp
|
||||
// Release method is called the public methods of the step must no longer be
|
||||
// used and may return errors.
|
||||
type GetOmapStep struct {
|
||||
// C returned data:
|
||||
iter C.rados_omap_iter_t
|
||||
more *C.uchar
|
||||
rval *C.int
|
||||
|
||||
// internal state:
|
||||
|
||||
// canIterate is only set after the operation is performed and is
|
||||
// intended to prevent premature fetching of data
|
||||
canIterate bool
|
||||
}
|
||||
|
||||
func newGetOmapStep() *GetOmapStep {
|
||||
gos := &GetOmapStep{
|
||||
more: (*C.uchar)(C.malloc(C.sizeof_uchar)),
|
||||
rval: (*C.int)(C.malloc(C.sizeof_int)),
|
||||
}
|
||||
runtime.SetFinalizer(gos, opStepFinalizer)
|
||||
return gos
|
||||
}
|
||||
|
||||
func (gos *GetOmapStep) free() {
|
||||
gos.canIterate = false
|
||||
if gos.iter != nil {
|
||||
C.rados_omap_get_end(gos.iter)
|
||||
}
|
||||
gos.iter = nil
|
||||
C.free(unsafe.Pointer(gos.more))
|
||||
gos.more = nil
|
||||
C.free(unsafe.Pointer(gos.rval))
|
||||
gos.rval = nil
|
||||
}
|
||||
|
||||
func (gos *GetOmapStep) update() error {
|
||||
err := getError(*gos.rval)
|
||||
gos.canIterate = (err == nil)
|
||||
return err
|
||||
}
|
||||
|
||||
// Next returns the next key value pair or nil if iteration is exhausted.
|
||||
func (gos *GetOmapStep) Next() (*OmapKeyValue, error) {
|
||||
if !gos.canIterate {
|
||||
return nil, ErrOperationIncomplete
|
||||
}
|
||||
var (
|
||||
cKey *C.char
|
||||
cVal *C.char
|
||||
cKeyLen C.size_t
|
||||
cValLen C.size_t
|
||||
)
|
||||
ret := C.rados_omap_get_next2(gos.iter, &cKey, &cVal, &cKeyLen, &cValLen)
|
||||
if ret != 0 {
|
||||
return nil, getError(ret)
|
||||
}
|
||||
if cKey == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return &OmapKeyValue{
|
||||
Key: string(C.GoBytes(unsafe.Pointer(cKey), C.int(cKeyLen))),
|
||||
Value: C.GoBytes(unsafe.Pointer(cVal), C.int(cValLen)),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// More returns true if there are more matching keys available.
|
||||
func (gos *GetOmapStep) More() bool {
|
||||
// tad bit hacky, but go can't automatically convert from
|
||||
// unsigned char to bool
|
||||
return *gos.more != 0
|
||||
}
|
||||
|
||||
// SetOmap appends the map `pairs` to the omap `oid`
|
||||
func (ioctx *IOContext) SetOmap(oid string, pairs map[string][]byte) error {
|
||||
op := CreateWriteOp()
|
||||
defer op.Release()
|
||||
op.SetOmap(pairs)
|
||||
return op.operateCompat(ioctx, oid)
|
||||
}
|
||||
|
||||
// OmapListFunc is the type of the function called for each omap key
|
||||
// visited by ListOmapValues
|
||||
type OmapListFunc func(key string, value []byte)
|
||||
|
||||
// ListOmapValues iterates over the keys and values in an omap by way of
|
||||
// a callback function.
|
||||
//
|
||||
// `startAfter`: iterate only on the keys after this specified one
|
||||
// `filterPrefix`: iterate only on the keys beginning with this prefix
|
||||
// `maxReturn`: iterate no more than `maxReturn` key/value pairs
|
||||
// `listFn`: the function called at each iteration
|
||||
func (ioctx *IOContext) ListOmapValues(oid string, startAfter string, filterPrefix string, maxReturn int64, listFn OmapListFunc) error {
|
||||
|
||||
op := CreateReadOp()
|
||||
defer op.Release()
|
||||
gos := op.GetOmapValues(startAfter, filterPrefix, uint64(maxReturn))
|
||||
err := op.operateCompat(ioctx, oid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for {
|
||||
kv, err := gos.Next()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if kv == nil {
|
||||
break
|
||||
}
|
||||
listFn(kv.Key, kv.Value)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetOmapValues fetches a set of keys and their values from an omap and returns then as a map
|
||||
// `startAfter`: retrieve only the keys after this specified one
|
||||
// `filterPrefix`: retrieve only the keys beginning with this prefix
|
||||
// `maxReturn`: retrieve no more than `maxReturn` key/value pairs
|
||||
func (ioctx *IOContext) GetOmapValues(oid string, startAfter string, filterPrefix string, maxReturn int64) (map[string][]byte, error) {
|
||||
omap := map[string][]byte{}
|
||||
|
||||
err := ioctx.ListOmapValues(
|
||||
oid, startAfter, filterPrefix, maxReturn,
|
||||
func(key string, value []byte) {
|
||||
omap[key] = value
|
||||
},
|
||||
)
|
||||
|
||||
return omap, err
|
||||
}
|
||||
|
||||
// GetAllOmapValues fetches all the keys and their values from an omap and returns then as a map
|
||||
// `startAfter`: retrieve only the keys after this specified one
|
||||
// `filterPrefix`: retrieve only the keys beginning with this prefix
|
||||
// `iteratorSize`: internal number of keys to fetch during a read operation
|
||||
func (ioctx *IOContext) GetAllOmapValues(oid string, startAfter string, filterPrefix string, iteratorSize int64) (map[string][]byte, error) {
|
||||
omap := map[string][]byte{}
|
||||
omapSize := 0
|
||||
|
||||
for {
|
||||
err := ioctx.ListOmapValues(
|
||||
oid, startAfter, filterPrefix, iteratorSize,
|
||||
func(key string, value []byte) {
|
||||
omap[key] = value
|
||||
startAfter = key
|
||||
},
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return omap, err
|
||||
}
|
||||
|
||||
// End of omap
|
||||
if len(omap) == omapSize {
|
||||
break
|
||||
}
|
||||
|
||||
omapSize = len(omap)
|
||||
}
|
||||
|
||||
return omap, nil
|
||||
}
|
||||
|
||||
// RmOmapKeys removes the specified `keys` from the omap `oid`
|
||||
func (ioctx *IOContext) RmOmapKeys(oid string, keys []string) error {
|
||||
op := CreateWriteOp()
|
||||
defer op.Release()
|
||||
op.RmOmapKeys(keys)
|
||||
return op.operateCompat(ioctx, oid)
|
||||
}
|
||||
|
||||
// CleanOmap clears the omap `oid`
|
||||
func (ioctx *IOContext) CleanOmap(oid string) error {
|
||||
op := CreateWriteOp()
|
||||
defer op.Release()
|
||||
op.CleanOmap()
|
||||
return op.operateCompat(ioctx, oid)
|
||||
}
|
||||
+165
@@ -0,0 +1,165 @@
|
||||
package rados
|
||||
|
||||
// #include <stdlib.h>
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"unsafe"
|
||||
|
||||
"github.com/ceph/go-ceph/internal/log"
|
||||
)
|
||||
|
||||
// The file operation.go exists to support both read op and write op types that
|
||||
// have some pretty common behaviors between them. In C/C++ its assumed that
|
||||
// the buffer types and other pointers will not be freed between passing them
|
||||
// to the action setup calls (things like rados_write_op_write or
|
||||
// rados_read_op_omap_get_vals2) and the call to Operate(...). Since there's
|
||||
// nothing stopping one from sleeping for hours between these calls, or passing
|
||||
// the op to other functions and calling Operate there, we want a mechanism
|
||||
// that's (fairly) simple to understand and won't run afoul of Go's garbage
|
||||
// collection. That's one reason the operation type tracks the steps (the
|
||||
// parts that track complex inputs and outputs) so that as long as the op
|
||||
// exists it will have a reference to the step, which will have references
|
||||
// to the C language types.
|
||||
|
||||
type opKind string
|
||||
|
||||
const (
|
||||
readOp opKind = "read"
|
||||
writeOp opKind = "write"
|
||||
)
|
||||
|
||||
// OperationError is an error type that may be returned by an Operate call.
|
||||
// It captures the error from the operate call itself and any errors from
|
||||
// steps that can return an error.
|
||||
type OperationError struct {
|
||||
kind opKind
|
||||
OpError error
|
||||
StepErrors map[int]error
|
||||
}
|
||||
|
||||
func (e OperationError) Error() string {
|
||||
subErrors := []string{}
|
||||
if e.OpError != nil {
|
||||
subErrors = append(subErrors,
|
||||
fmt.Sprintf("op=%s", e.OpError))
|
||||
}
|
||||
for idx, es := range e.StepErrors {
|
||||
subErrors = append(subErrors,
|
||||
fmt.Sprintf("Step#%d=%s", idx, es))
|
||||
}
|
||||
return fmt.Sprintf(
|
||||
"%s operation error: %s",
|
||||
e.kind,
|
||||
strings.Join(subErrors, ", "))
|
||||
}
|
||||
|
||||
func (e OperationError) Unwrap() []error {
|
||||
subErrors := make([]error, 0, len(e.StepErrors)+1)
|
||||
if e.OpError != nil {
|
||||
subErrors = append(subErrors, e.OpError)
|
||||
}
|
||||
for _, es := range e.StepErrors {
|
||||
subErrors = append(subErrors, es)
|
||||
}
|
||||
return subErrors
|
||||
}
|
||||
|
||||
// opStep provides an interface for types that are tied to the management of
|
||||
// data being input or output from write ops and read ops. The steps are
|
||||
// meant to simplify the internals of the ops themselves and be exportable when
|
||||
// appropriate. If a step is not being exported it should not be returned
|
||||
// from an ops action function. If the step is exported it should be
|
||||
// returned from an ops action function.
|
||||
//
|
||||
// Not all types implementing opStep are expected to need all the functions
|
||||
// in the interface. However, for the sake of simplicity on the op side, we use
|
||||
// the same interface for all cases and expect those implementing opStep
|
||||
// just embed the without* types that provide no-op implementation of
|
||||
// functions that make up this interface.
|
||||
type opStep interface {
|
||||
// update the state of the step after the call to Operate.
|
||||
// It can be used to convert values from C and cache them and/or
|
||||
// communicate a failure of the action associated with the step. The
|
||||
// update call will only be made once. Implementations are not required to
|
||||
// handle this call being made more than once.
|
||||
update() error
|
||||
// free will be called to free any resources, especially C memory, that
|
||||
// the step is managing. The behavior of free should be idempotent and
|
||||
// handle being called more than once.
|
||||
free()
|
||||
}
|
||||
|
||||
// operation represents some of the shared underlying mechanisms for
|
||||
// both read and write op types.
|
||||
type operation struct {
|
||||
steps []opStep
|
||||
}
|
||||
|
||||
// free will call the free method of all the steps this operation
|
||||
// contains.
|
||||
func (o *operation) free() {
|
||||
for i := range o.steps {
|
||||
o.steps[i].free()
|
||||
}
|
||||
}
|
||||
|
||||
// update the operation and the steps it contains. The top-level result
|
||||
// of the rados call is passed in as ret and used to construct errors.
|
||||
// The update call of each step is used to update the contents of each
|
||||
// step and gather any errors from those steps.
|
||||
func (o *operation) update(kind opKind, ret C.int) error {
|
||||
stepErrors := map[int]error{}
|
||||
for i := range o.steps {
|
||||
if err := o.steps[i].update(); err != nil {
|
||||
stepErrors[i] = err
|
||||
}
|
||||
}
|
||||
if ret == 0 && len(stepErrors) == 0 {
|
||||
return nil
|
||||
}
|
||||
return OperationError{
|
||||
kind: kind,
|
||||
OpError: getError(ret),
|
||||
StepErrors: stepErrors,
|
||||
}
|
||||
}
|
||||
|
||||
func opStepFinalizer(s opStep) {
|
||||
if s != nil {
|
||||
log.Warnf("unreachable opStep object found. Cleaning up.")
|
||||
s.free()
|
||||
}
|
||||
}
|
||||
|
||||
// withoutUpdate can be embedded in a struct to help indicate
|
||||
// the type implements the opStep interface but has a no-op
|
||||
// update function.
|
||||
type withoutUpdate struct{}
|
||||
|
||||
func (*withoutUpdate) update() error { return nil }
|
||||
|
||||
// withoutFree can be embedded in a struct to help indicate
|
||||
// the type implements the opStep interface but has a no-op
|
||||
// free function.
|
||||
type withoutFree struct{}
|
||||
|
||||
func (*withoutFree) free() {}
|
||||
|
||||
// withRefs is a embeddable type to help track and free C memory.
|
||||
type withRefs struct {
|
||||
refs []unsafe.Pointer
|
||||
}
|
||||
|
||||
func (w *withRefs) free() {
|
||||
for i := range w.refs {
|
||||
C.free(w.refs[i])
|
||||
}
|
||||
w.refs = nil
|
||||
}
|
||||
|
||||
func (w *withRefs) add(ptr unsafe.Pointer) {
|
||||
w.refs = append(w.refs, ptr)
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
package rados
|
||||
|
||||
// #cgo LDFLAGS: -lrados
|
||||
// #include <errno.h>
|
||||
// #include <stdlib.h>
|
||||
// #include <rados/librados.h>
|
||||
//
|
||||
import "C"
|
||||
|
||||
// OperationFlags control the behavior of read and write operations.
|
||||
type OperationFlags int
|
||||
|
||||
const (
|
||||
// OperationNoFlag indicates no special behavior is requested.
|
||||
OperationNoFlag = OperationFlags(C.LIBRADOS_OPERATION_NOFLAG)
|
||||
// OperationBalanceReads TODO
|
||||
OperationBalanceReads = OperationFlags(C.LIBRADOS_OPERATION_BALANCE_READS)
|
||||
// OperationLocalizeReads TODO
|
||||
OperationLocalizeReads = OperationFlags(C.LIBRADOS_OPERATION_LOCALIZE_READS)
|
||||
// OperationOrderReadsWrites TODO
|
||||
OperationOrderReadsWrites = OperationFlags(C.LIBRADOS_OPERATION_ORDER_READS_WRITES)
|
||||
// OperationIgnoreCache TODO
|
||||
OperationIgnoreCache = OperationFlags(C.LIBRADOS_OPERATION_IGNORE_CACHE)
|
||||
// OperationSkipRWLocks TODO
|
||||
OperationSkipRWLocks = OperationFlags(C.LIBRADOS_OPERATION_SKIPRWLOCKS)
|
||||
// OperationIgnoreOverlay TODO
|
||||
OperationIgnoreOverlay = OperationFlags(C.LIBRADOS_OPERATION_IGNORE_OVERLAY)
|
||||
// OperationFullTry send request to a full cluster or pool, ops such as delete
|
||||
// can succeed while other ops will return out-of-space errors.
|
||||
OperationFullTry = OperationFlags(C.LIBRADOS_OPERATION_FULL_TRY)
|
||||
// OperationFullForce TODO
|
||||
OperationFullForce = OperationFlags(C.LIBRADOS_OPERATION_FULL_FORCE)
|
||||
// OperationIgnoreRedirect TODO
|
||||
OperationIgnoreRedirect = OperationFlags(C.LIBRADOS_OPERATION_IGNORE_REDIRECT)
|
||||
// OperationOrderSnap TODO
|
||||
OperationOrderSnap = OperationFlags(C.LIBRADOS_OPERATION_ORDERSNAP)
|
||||
)
|
||||
+132
@@ -0,0 +1,132 @@
|
||||
package rados
|
||||
|
||||
// #cgo LDFLAGS: -lrados
|
||||
// #include <errno.h>
|
||||
// #include <stdlib.h>
|
||||
// #include <rados/librados.h>
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"unsafe"
|
||||
|
||||
"github.com/ceph/go-ceph/internal/log"
|
||||
)
|
||||
|
||||
const (
|
||||
// AllNamespaces is used to reset a selected namespace to all
|
||||
// namespaces. See the IOContext SetNamespace function.
|
||||
AllNamespaces = C.LIBRADOS_ALL_NSPACES
|
||||
|
||||
// FIXME: for backwards compatibility
|
||||
|
||||
// RadosAllNamespaces is used to reset a selected namespace to all
|
||||
// namespaces. See the IOContext SetNamespace function.
|
||||
//
|
||||
// Deprecated: use AllNamespaces instead
|
||||
RadosAllNamespaces = AllNamespaces
|
||||
)
|
||||
|
||||
// OpFlags are flags that can be set on a per-op basis.
|
||||
type OpFlags uint
|
||||
|
||||
const (
|
||||
// OpFlagNone can be use to not set any flags.
|
||||
OpFlagNone = OpFlags(0)
|
||||
// OpFlagExcl marks an op to fail a create operation if the object
|
||||
// already exists.
|
||||
OpFlagExcl = OpFlags(C.LIBRADOS_OP_FLAG_EXCL)
|
||||
// OpFlagFailOk allows the transaction to succeed even if the flagged
|
||||
// op fails.
|
||||
OpFlagFailOk = OpFlags(C.LIBRADOS_OP_FLAG_FAILOK)
|
||||
// OpFlagFAdviseRandom indicates read/write op random.
|
||||
OpFlagFAdviseRandom = OpFlags(C.LIBRADOS_OP_FLAG_FADVISE_RANDOM)
|
||||
// OpFlagFAdviseSequential indicates read/write op sequential.
|
||||
OpFlagFAdviseSequential = OpFlags(C.LIBRADOS_OP_FLAG_FADVISE_SEQUENTIAL)
|
||||
// OpFlagFAdviseWillNeed indicates read/write data will be accessed in
|
||||
// the near future (by someone).
|
||||
OpFlagFAdviseWillNeed = OpFlags(C.LIBRADOS_OP_FLAG_FADVISE_WILLNEED)
|
||||
// OpFlagFAdviseDontNeed indicates read/write data will not accessed in
|
||||
// the near future (by anyone).
|
||||
OpFlagFAdviseDontNeed = OpFlags(C.LIBRADOS_OP_FLAG_FADVISE_DONTNEED)
|
||||
// OpFlagFAdviseNoCache indicates read/write data will not accessed
|
||||
// again (by *this* client).
|
||||
OpFlagFAdviseNoCache = OpFlags(C.LIBRADOS_OP_FLAG_FADVISE_NOCACHE)
|
||||
)
|
||||
|
||||
// Version returns the major, minor, and patch components of the version of
|
||||
// the RADOS library linked against.
|
||||
func Version() (int, int, int) {
|
||||
var cMajor, cMinor, cPatch C.int
|
||||
C.rados_version(&cMajor, &cMinor, &cPatch)
|
||||
return int(cMajor), int(cMinor), int(cPatch)
|
||||
}
|
||||
|
||||
func makeConn() *Conn {
|
||||
return &Conn{connected: false}
|
||||
}
|
||||
|
||||
func newConn(user *C.char) (*Conn, error) {
|
||||
conn := makeConn()
|
||||
ret := C.rados_create(&conn.cluster, user)
|
||||
|
||||
if ret != 0 {
|
||||
return nil, getError(ret)
|
||||
}
|
||||
|
||||
runtime.SetFinalizer(conn, freeConn)
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
// NewConn creates a new connection object. It returns the connection and an
|
||||
// error, if any.
|
||||
func NewConn() (*Conn, error) {
|
||||
return newConn(nil)
|
||||
}
|
||||
|
||||
// NewConnWithUser creates a new connection object with a custom username.
|
||||
// The supplied username should not include a 'client.' prefix. It returns
|
||||
// the connection and an error, if any.
|
||||
func NewConnWithUser(user string) (*Conn, error) {
|
||||
cUser := C.CString(user)
|
||||
defer C.free(unsafe.Pointer(cUser))
|
||||
return newConn(cUser)
|
||||
}
|
||||
|
||||
// NewConnWithClusterAndUser creates a new connection object for a specific
|
||||
// cluster and username. The supplied username should be in the 'client.<user>'
|
||||
// format. It returns the connection and an error, if any.
|
||||
func NewConnWithClusterAndUser(clusterName string, userName string) (*Conn, error) {
|
||||
cClusterName := C.CString(clusterName)
|
||||
defer C.free(unsafe.Pointer(cClusterName))
|
||||
|
||||
cName := C.CString(userName)
|
||||
defer C.free(unsafe.Pointer(cName))
|
||||
|
||||
conn := makeConn()
|
||||
ret := C.rados_create2(&conn.cluster, cClusterName, cName, 0)
|
||||
if ret != 0 {
|
||||
return nil, getError(ret)
|
||||
}
|
||||
|
||||
runtime.SetFinalizer(conn, freeConn)
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
// freeConn releases resources that are allocated while configuring the
|
||||
// connection to the cluster. rados_shutdown() should only be needed after a
|
||||
// successful call to rados_connect(), however if the connection has been
|
||||
// configured with non-default parameters, some of the parameters may be
|
||||
// allocated before connecting. rados_shutdown() will free the allocated
|
||||
// resources, even if there has not been a connection yet.
|
||||
//
|
||||
// This function is setup as a destructor/finalizer when rados_create() is
|
||||
// called.
|
||||
func freeConn(conn *Conn) {
|
||||
if conn.cluster != nil {
|
||||
log.Warnf("unreachable Conn object has not been shut down. Cleaning up.")
|
||||
C.rados_shutdown(conn.cluster)
|
||||
// prevent calling rados_shutdown() more than once
|
||||
conn.cluster = nil
|
||||
}
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
package rados
|
||||
|
||||
// #cgo LDFLAGS: -lrados
|
||||
// #include <stdlib.h>
|
||||
// #include <rados/librados.h>
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// GetAddrs returns the addresses of the RADOS session,
|
||||
// suitable for blocklisting.
|
||||
//
|
||||
// Implements:
|
||||
//
|
||||
// int rados_getaddrs(rados_t cluster, char **addrs)
|
||||
func (c *Conn) GetAddrs() (string, error) {
|
||||
var cAddrs *C.char
|
||||
defer C.free(unsafe.Pointer(cAddrs))
|
||||
|
||||
ret := C.rados_getaddrs(c.cluster, &cAddrs)
|
||||
if ret < 0 {
|
||||
return "", getError(ret)
|
||||
}
|
||||
|
||||
return C.GoString(cAddrs), nil
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
//go:build !mimic
|
||||
// +build !mimic
|
||||
|
||||
package rados
|
||||
|
||||
// #include <rados/librados.h>
|
||||
import "C"
|
||||
|
||||
const (
|
||||
// OpFlagFAdviseFUA optionally support FUA (force unit access) on write
|
||||
// requests.
|
||||
OpFlagFAdviseFUA = OpFlags(C.LIBRADOS_OP_FLAG_FADVISE_FUA)
|
||||
)
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
package rados
|
||||
|
||||
// #cgo LDFLAGS: -lrados
|
||||
// #include <rados/librados.h>
|
||||
// #include <stdlib.h>
|
||||
//
|
||||
import "C"
|
||||
|
||||
// AssertVersion ensures that the object exists and that its internal version
|
||||
// number is equal to "ver" before reading. "ver" should be a version number
|
||||
// previously obtained with IOContext.GetLastVersion().
|
||||
//
|
||||
// Implements:
|
||||
//
|
||||
// void rados_read_op_assert_version(rados_read_op_t read_op,
|
||||
// uint64_t ver)
|
||||
func (r *ReadOp) AssertVersion(ver uint64) {
|
||||
C.rados_read_op_assert_version(r.op, C.uint64_t(ver))
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
package rados
|
||||
|
||||
// #cgo LDFLAGS: -lrados
|
||||
// #include <rados/librados.h>
|
||||
// #include <stdlib.h>
|
||||
//
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// SetLocator sets the key for mapping objects to pgs within an io context.
|
||||
// Until a different locator key is set, all objects in this io context will be placed in the same pg.
|
||||
// To reset the locator, an empty string must be set.
|
||||
//
|
||||
// Implements:
|
||||
//
|
||||
// void rados_ioctx_locator_set_key(rados_ioctx_t io, const char *key);
|
||||
func (ioctx *IOContext) SetLocator(locator string) {
|
||||
if locator == "" {
|
||||
C.rados_ioctx_locator_set_key(ioctx.ioctx, nil)
|
||||
} else {
|
||||
var cLoc *C.char = C.CString(locator)
|
||||
defer C.free(unsafe.Pointer(cLoc))
|
||||
C.rados_ioctx_locator_set_key(ioctx.ioctx, cLoc)
|
||||
}
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
package rados
|
||||
|
||||
// #cgo LDFLAGS: -lrados
|
||||
// #include <rados/librados.h>
|
||||
// #include <stdlib.h>
|
||||
//
|
||||
import "C"
|
||||
|
||||
// AssertVersion ensures that the object exists and that its internal version
|
||||
// number is equal to "ver" before writing. "ver" should be a version number
|
||||
// previously obtained with IOContext.GetLastVersion().
|
||||
//
|
||||
// Implements:
|
||||
//
|
||||
// void rados_read_op_assert_version(rados_read_op_t read_op,
|
||||
// uint64_t ver)
|
||||
func (w *WriteOp) AssertVersion(ver uint64) {
|
||||
C.rados_write_op_assert_version(w.op, C.uint64_t(ver))
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
package rados
|
||||
|
||||
// #cgo LDFLAGS: -lrados
|
||||
// #include <rados/librados.h>
|
||||
// #include <stdlib.h>
|
||||
//
|
||||
import "C"
|
||||
|
||||
// Remove object.
|
||||
//
|
||||
// Implements:
|
||||
//
|
||||
// void rados_write_op_remove(rados_write_op_t write_op)
|
||||
func (w *WriteOp) Remove() {
|
||||
C.rados_write_op_remove(w.op)
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
package rados
|
||||
|
||||
// #cgo LDFLAGS: -lrados
|
||||
// #include <rados/librados.h>
|
||||
// #include <stdlib.h>
|
||||
//
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// SetXattr sets an xattr.
|
||||
//
|
||||
// Implements:
|
||||
//
|
||||
// void rados_write_op_setxattr(rados_write_op_t write_op,
|
||||
// const char * name,
|
||||
// const char * value,
|
||||
// size_t value_len)
|
||||
func (w *WriteOp) SetXattr(name string, value []byte) {
|
||||
cName := C.CString(name)
|
||||
defer C.free(unsafe.Pointer(cName))
|
||||
|
||||
C.rados_write_op_setxattr(
|
||||
w.op,
|
||||
cName,
|
||||
(*C.char)(unsafe.Pointer(&value[0])),
|
||||
C.size_t(len(value)),
|
||||
)
|
||||
}
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
package rados
|
||||
|
||||
// #cgo LDFLAGS: -lrados
|
||||
// #include <errno.h>
|
||||
// #include <stdlib.h>
|
||||
// #include <rados/librados.h>
|
||||
//
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// ReadOp manages a set of discrete object read actions that will be performed
|
||||
// together atomically.
|
||||
type ReadOp struct {
|
||||
operation
|
||||
op C.rados_read_op_t
|
||||
}
|
||||
|
||||
// CreateReadOp returns a newly constructed read operation.
|
||||
func CreateReadOp() *ReadOp {
|
||||
return &ReadOp{
|
||||
op: C.rados_create_read_op(),
|
||||
}
|
||||
}
|
||||
|
||||
// Release the resources associated with this read operation.
|
||||
func (r *ReadOp) Release() {
|
||||
C.rados_release_read_op(r.op)
|
||||
r.op = nil
|
||||
r.free()
|
||||
}
|
||||
|
||||
// Operate will perform the operation(s).
|
||||
func (r *ReadOp) Operate(ioctx *IOContext, oid string, flags OperationFlags) error {
|
||||
if err := ioctx.validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cOid := C.CString(oid)
|
||||
defer C.free(unsafe.Pointer(cOid))
|
||||
|
||||
ret := C.rados_read_op_operate(r.op, ioctx.ioctx, cOid, C.int(flags))
|
||||
return r.update(readOp, ret)
|
||||
}
|
||||
|
||||
func (r *ReadOp) operateCompat(ioctx *IOContext, oid string) error {
|
||||
switch err := r.Operate(ioctx, oid, OperationNoFlag).(type) {
|
||||
case nil:
|
||||
return nil
|
||||
case OperationError:
|
||||
return err.OpError
|
||||
default:
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// AssertExists assures the object targeted by the read op exists.
|
||||
//
|
||||
// Implements:
|
||||
//
|
||||
// void rados_read_op_assert_exists(rados_read_op_t read_op);
|
||||
func (r *ReadOp) AssertExists() {
|
||||
C.rados_read_op_assert_exists(r.op)
|
||||
}
|
||||
|
||||
// GetOmapValues is used to iterate over a set, or sub-set, of omap keys
|
||||
// as part of a read operation. An GetOmapStep is returned from this
|
||||
// function. The GetOmapStep may be used to iterate over the key-value
|
||||
// pairs after the Operate call has been performed.
|
||||
func (r *ReadOp) GetOmapValues(startAfter, filterPrefix string, maxReturn uint64) *GetOmapStep {
|
||||
gos := newGetOmapStep()
|
||||
r.steps = append(r.steps, gos)
|
||||
|
||||
cStartAfter := C.CString(startAfter)
|
||||
cFilterPrefix := C.CString(filterPrefix)
|
||||
defer C.free(unsafe.Pointer(cStartAfter))
|
||||
defer C.free(unsafe.Pointer(cFilterPrefix))
|
||||
|
||||
C.rados_read_op_omap_get_vals2(
|
||||
r.op,
|
||||
cStartAfter,
|
||||
cFilterPrefix,
|
||||
C.uint64_t(maxReturn),
|
||||
&gos.iter,
|
||||
gos.more,
|
||||
gos.rval,
|
||||
)
|
||||
return gos
|
||||
}
|
||||
+108
@@ -0,0 +1,108 @@
|
||||
package rados
|
||||
|
||||
// #cgo LDFLAGS: -lrados
|
||||
// #include <stdlib.h>
|
||||
// #include <rados/librados.h>
|
||||
//
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// ReadOpExecStep - step for exec operation code.
|
||||
type ReadOpExecStep struct {
|
||||
withoutFree
|
||||
|
||||
inBuffPtr *C.char
|
||||
inBuffLen C.size_t
|
||||
outBuffPtr *C.char
|
||||
outBuffLen C.size_t
|
||||
prval C.int
|
||||
canReadOutput bool
|
||||
}
|
||||
|
||||
// newExecStepOp - init new *execStepOp.
|
||||
func newReadOpExecStep(in []byte) *ReadOpExecStep {
|
||||
es := &ReadOpExecStep{
|
||||
outBuffPtr: nil,
|
||||
outBuffLen: 0,
|
||||
prval: 0,
|
||||
}
|
||||
|
||||
if len(in) > 0 {
|
||||
es.inBuffPtr = (*C.char)(unsafe.Pointer(&in[0]))
|
||||
es.inBuffLen = C.size_t(len(in))
|
||||
}
|
||||
|
||||
runtime.SetFinalizer(es, func(es *ReadOpExecStep) {
|
||||
if es != nil {
|
||||
es.freeBuffer()
|
||||
es = nil
|
||||
}
|
||||
})
|
||||
return es
|
||||
}
|
||||
|
||||
// freeBuffer - releases C allocated buffer. It is separated from es.free() because lifespan of C allocated buffer is
|
||||
// longer than lifespan of read operation.
|
||||
func (es *ReadOpExecStep) freeBuffer() {
|
||||
if es.outBuffPtr != nil {
|
||||
C.free(unsafe.Pointer(es.outBuffPtr))
|
||||
es.outBuffPtr = nil
|
||||
es.canReadOutput = false
|
||||
}
|
||||
}
|
||||
|
||||
// update - update state operation.
|
||||
func (es *ReadOpExecStep) update() error {
|
||||
err := getError(es.prval)
|
||||
es.canReadOutput = err == nil
|
||||
return err
|
||||
}
|
||||
|
||||
// Bytes returns the result of the executed command as a byte slice.
|
||||
func (es *ReadOpExecStep) Bytes() ([]byte, error) {
|
||||
if !es.canReadOutput {
|
||||
return nil, ErrOperationIncomplete
|
||||
}
|
||||
|
||||
return C.GoBytes(unsafe.Pointer(es.outBuffPtr), C.int(es.outBuffLen)), nil
|
||||
}
|
||||
|
||||
// Exec executes an OSD class method on an object.
|
||||
// See rados_exec() in the RADOS C api documentation for a general description.
|
||||
//
|
||||
// Implements:
|
||||
//
|
||||
// void rados_read_op_exec(rados_read_op_t read_op,
|
||||
// const char *cls,
|
||||
// const char *method,
|
||||
// const char *in_buf,
|
||||
// size_t in_len,
|
||||
// char **out_buf,
|
||||
// size_t *out_len,
|
||||
// int *prval);
|
||||
func (r *ReadOp) Exec(clsName, method string, in []byte) *ReadOpExecStep {
|
||||
cClsName := C.CString(clsName)
|
||||
defer C.free(unsafe.Pointer(cClsName))
|
||||
|
||||
cMethod := C.CString(method)
|
||||
defer C.free(unsafe.Pointer(cMethod))
|
||||
|
||||
es := newReadOpExecStep(in)
|
||||
r.steps = append(r.steps, es)
|
||||
C.rados_read_op_exec(
|
||||
r.op,
|
||||
cClsName,
|
||||
cMethod,
|
||||
es.inBuffPtr,
|
||||
es.inBuffLen,
|
||||
&es.outBuffPtr,
|
||||
&es.outBuffLen,
|
||||
&es.prval,
|
||||
)
|
||||
|
||||
return es
|
||||
}
|
||||
+113
@@ -0,0 +1,113 @@
|
||||
package rados
|
||||
|
||||
// #cgo LDFLAGS: -lrados
|
||||
// #include <rados/librados.h>
|
||||
// #include <stdlib.h>
|
||||
//
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
|
||||
"github.com/ceph/go-ceph/internal/cutil"
|
||||
)
|
||||
|
||||
// ReadOpOmapGetValsByKeysStep holds the result of the
|
||||
// GetOmapValuesByKeys read operation.
|
||||
// Result is valid only after Operate() was called.
|
||||
type ReadOpOmapGetValsByKeysStep struct {
|
||||
// C arguments
|
||||
|
||||
iter C.rados_omap_iter_t
|
||||
prval *C.int
|
||||
|
||||
// Internal state
|
||||
|
||||
// canIterate is only set after the operation is performed and is
|
||||
// intended to prevent premature fetching of data.
|
||||
canIterate bool
|
||||
}
|
||||
|
||||
func newReadOpOmapGetValsByKeysStep() *ReadOpOmapGetValsByKeysStep {
|
||||
s := &ReadOpOmapGetValsByKeysStep{
|
||||
prval: (*C.int)(C.malloc(C.sizeof_int)),
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *ReadOpOmapGetValsByKeysStep) free() {
|
||||
s.canIterate = false
|
||||
C.rados_omap_get_end(s.iter)
|
||||
|
||||
C.free(unsafe.Pointer(s.prval))
|
||||
s.prval = nil
|
||||
}
|
||||
|
||||
func (s *ReadOpOmapGetValsByKeysStep) update() error {
|
||||
err := getError(*s.prval)
|
||||
s.canIterate = (err == nil)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// Next gets the next omap key/value pair referenced by
|
||||
// ReadOpOmapGetValsByKeysStep's internal iterator.
|
||||
// If there are no more elements to retrieve, (nil, nil) is returned.
|
||||
// May be called only after Operate() finished.
|
||||
func (s *ReadOpOmapGetValsByKeysStep) Next() (*OmapKeyValue, error) {
|
||||
if !s.canIterate {
|
||||
return nil, ErrOperationIncomplete
|
||||
}
|
||||
|
||||
var (
|
||||
cKey *C.char
|
||||
cVal *C.char
|
||||
cKeyLen C.size_t
|
||||
cValLen C.size_t
|
||||
)
|
||||
|
||||
ret := C.rados_omap_get_next2(s.iter, &cKey, &cVal, &cKeyLen, &cValLen)
|
||||
if ret != 0 {
|
||||
return nil, getError(ret)
|
||||
}
|
||||
|
||||
if cKey == nil {
|
||||
// Iterator has reached the end of the list.
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return &OmapKeyValue{
|
||||
Key: string(C.GoBytes(unsafe.Pointer(cKey), C.int(cKeyLen))),
|
||||
Value: C.GoBytes(unsafe.Pointer(cVal), C.int(cValLen)),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetOmapValuesByKeys starts iterating over specific key/value pairs.
|
||||
//
|
||||
// Implements:
|
||||
//
|
||||
// void rados_read_op_omap_get_vals_by_keys2(rados_read_op_t read_op,
|
||||
// char const * const * keys,
|
||||
// size_t num_keys,
|
||||
// const size_t * key_lens,
|
||||
// rados_omap_iter_t * iter,
|
||||
// int * prval)
|
||||
func (r *ReadOp) GetOmapValuesByKeys(keys []string) *ReadOpOmapGetValsByKeysStep {
|
||||
s := newReadOpOmapGetValsByKeysStep()
|
||||
r.steps = append(r.steps, s)
|
||||
|
||||
cKeys := cutil.NewBufferGroupStrings(keys)
|
||||
defer cKeys.Free()
|
||||
|
||||
C.rados_read_op_omap_get_vals_by_keys2(
|
||||
r.op,
|
||||
(**C.char)(cKeys.BuffersPtr()),
|
||||
C.size_t(len(keys)),
|
||||
(*C.size_t)(cKeys.LengthsPtr()),
|
||||
&s.iter,
|
||||
s.prval,
|
||||
)
|
||||
|
||||
return s
|
||||
}
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
package rados
|
||||
|
||||
// #cgo LDFLAGS: -lrados
|
||||
// #include <rados/librados.h>
|
||||
// #include <stdlib.h>
|
||||
//
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// ReadOpReadStep holds the result of the Read read operation.
|
||||
// Result is valid only after Operate() was called.
|
||||
type ReadOpReadStep struct {
|
||||
// C returned data:
|
||||
bytesRead *C.size_t
|
||||
prval *C.int
|
||||
|
||||
BytesRead int64 // Bytes read by this action.
|
||||
Result int // Result of this action.
|
||||
}
|
||||
|
||||
func (s *ReadOpReadStep) update() error {
|
||||
s.BytesRead = (int64)(*s.bytesRead)
|
||||
s.Result = (int)(*s.prval)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *ReadOpReadStep) free() {
|
||||
C.free(unsafe.Pointer(s.bytesRead))
|
||||
C.free(unsafe.Pointer(s.prval))
|
||||
|
||||
s.bytesRead = nil
|
||||
s.prval = nil
|
||||
}
|
||||
|
||||
func newReadOpReadStep() *ReadOpReadStep {
|
||||
return &ReadOpReadStep{
|
||||
bytesRead: (*C.size_t)(C.malloc(C.sizeof_size_t)),
|
||||
prval: (*C.int)(C.malloc(C.sizeof_int)),
|
||||
}
|
||||
}
|
||||
|
||||
// Read bytes from offset into buffer.
|
||||
// len(buffer) is the maximum number of bytes read from the object.
|
||||
// buffer[:ReadOpReadStep.BytesRead] then contains object data.
|
||||
//
|
||||
// Implements:
|
||||
//
|
||||
// void rados_read_op_read(rados_read_op_t read_op,
|
||||
// uint64_t offset,
|
||||
// size_t len,
|
||||
// char * buffer,
|
||||
// size_t * bytes_read,
|
||||
// int * prval)
|
||||
func (r *ReadOp) Read(offset uint64, buffer []byte) *ReadOpReadStep {
|
||||
oe := newReadStep(buffer, offset)
|
||||
readStep := newReadOpReadStep()
|
||||
r.steps = append(r.steps, oe, readStep)
|
||||
C.rados_read_op_read(
|
||||
r.op,
|
||||
oe.cOffset,
|
||||
oe.cReadLen,
|
||||
oe.cBuffer,
|
||||
readStep.bytesRead,
|
||||
readStep.prval,
|
||||
)
|
||||
|
||||
return readStep
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
package rados
|
||||
|
||||
// #include <stdint.h>
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type readStep struct {
|
||||
withoutUpdate
|
||||
withoutFree
|
||||
// the c pointer utilizes the Go byteslice data and no free is needed
|
||||
|
||||
// inputs:
|
||||
b []byte
|
||||
|
||||
// arguments:
|
||||
cBuffer *C.char
|
||||
cReadLen C.size_t
|
||||
cOffset C.uint64_t
|
||||
}
|
||||
|
||||
func newReadStep(b []byte, offset uint64) *readStep {
|
||||
return &readStep{
|
||||
b: b,
|
||||
cBuffer: (*C.char)(unsafe.Pointer(&b[0])), // TODO: must be pinned
|
||||
cReadLen: C.size_t(len(b)),
|
||||
cOffset: C.uint64_t(offset),
|
||||
}
|
||||
}
|
||||
+196
@@ -0,0 +1,196 @@
|
||||
package rados
|
||||
|
||||
// #cgo LDFLAGS: -lrados
|
||||
// #include <stdlib.h>
|
||||
// #include <rados/librados.h>
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"github.com/ceph/go-ceph/internal/retry"
|
||||
)
|
||||
|
||||
// CreateSnap creates a pool-wide snapshot.
|
||||
//
|
||||
// Implements:
|
||||
// int rados_ioctx_snap_create(rados_ioctx_t io, const char *snapname)
|
||||
func (ioctx *IOContext) CreateSnap(snapName string) error {
|
||||
if err := ioctx.validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cSnapName := C.CString(snapName)
|
||||
defer C.free(unsafe.Pointer(cSnapName))
|
||||
|
||||
ret := C.rados_ioctx_snap_create(ioctx.ioctx, cSnapName)
|
||||
return getError(ret)
|
||||
}
|
||||
|
||||
// RemoveSnap deletes the pool snapshot.
|
||||
//
|
||||
// Implements:
|
||||
//
|
||||
// int rados_ioctx_snap_remove(rados_ioctx_t io, const char *snapname)
|
||||
func (ioctx *IOContext) RemoveSnap(snapName string) error {
|
||||
if err := ioctx.validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cSnapName := C.CString(snapName)
|
||||
defer C.free(unsafe.Pointer(cSnapName))
|
||||
|
||||
ret := C.rados_ioctx_snap_remove(ioctx.ioctx, cSnapName)
|
||||
return getError(ret)
|
||||
}
|
||||
|
||||
// SnapID represents the ID of a rados snapshot.
|
||||
type SnapID C.rados_snap_t
|
||||
|
||||
// LookupSnap returns the ID of a pool snapshot.
|
||||
//
|
||||
// Implements:
|
||||
//
|
||||
// int rados_ioctx_snap_lookup(rados_ioctx_t io, const char *name, rados_snap_t *id)
|
||||
func (ioctx *IOContext) LookupSnap(snapName string) (SnapID, error) {
|
||||
var snapID SnapID
|
||||
|
||||
if err := ioctx.validate(); err != nil {
|
||||
return snapID, err
|
||||
}
|
||||
|
||||
cSnapName := C.CString(snapName)
|
||||
defer C.free(unsafe.Pointer(cSnapName))
|
||||
|
||||
ret := C.rados_ioctx_snap_lookup(
|
||||
ioctx.ioctx,
|
||||
cSnapName,
|
||||
(*C.rados_snap_t)(&snapID))
|
||||
return snapID, getError(ret)
|
||||
}
|
||||
|
||||
// GetSnapName returns the name of a pool snapshot with the given snapshot ID.
|
||||
//
|
||||
// Implements:
|
||||
//
|
||||
// int rados_ioctx_snap_get_name(rados_ioctx_t io, rados_snap_t id, char *name, int maxlen)
|
||||
func (ioctx *IOContext) GetSnapName(snapID SnapID) (string, error) {
|
||||
if err := ioctx.validate(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
var (
|
||||
buf []byte
|
||||
err error
|
||||
)
|
||||
// range from 1k to 64KiB
|
||||
retry.WithSizes(1024, 1<<16, func(length int) retry.Hint {
|
||||
cLen := C.int(length)
|
||||
buf = make([]byte, cLen)
|
||||
ret := C.rados_ioctx_snap_get_name(
|
||||
ioctx.ioctx,
|
||||
(C.rados_snap_t)(snapID),
|
||||
(*C.char)(unsafe.Pointer(&buf[0])),
|
||||
cLen)
|
||||
err = getError(ret)
|
||||
return retry.Size(int(cLen)).If(err == errRange)
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return C.GoString((*C.char)(unsafe.Pointer(&buf[0]))), nil
|
||||
}
|
||||
|
||||
// GetSnapStamp returns the time of the pool snapshot creation.
|
||||
//
|
||||
// Implements:
|
||||
//
|
||||
// int rados_ioctx_snap_get_stamp(rados_ioctx_t io, rados_snap_t id, time_t *t)
|
||||
func (ioctx *IOContext) GetSnapStamp(snapID SnapID) (time.Time, error) {
|
||||
var cTime C.time_t
|
||||
|
||||
if err := ioctx.validate(); err != nil {
|
||||
return time.Unix(int64(cTime), 0), err
|
||||
}
|
||||
|
||||
ret := C.rados_ioctx_snap_get_stamp(
|
||||
ioctx.ioctx,
|
||||
(C.rados_snap_t)(snapID),
|
||||
&cTime)
|
||||
return time.Unix(int64(cTime), 0), getError(ret)
|
||||
}
|
||||
|
||||
// ListSnaps returns a slice containing the SnapIDs of existing pool snapshots.
|
||||
//
|
||||
// Implements:
|
||||
//
|
||||
// int rados_ioctx_snap_list(rados_ioctx_t io, rados_snap_t *snaps, int maxlen)
|
||||
func (ioctx *IOContext) ListSnaps() ([]SnapID, error) {
|
||||
if err := ioctx.validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var (
|
||||
snapList []SnapID
|
||||
cLen C.int
|
||||
err error
|
||||
ret C.int
|
||||
)
|
||||
retry.WithSizes(100, 1000, func(maxlen int) retry.Hint {
|
||||
cLen = C.int(maxlen)
|
||||
snapList = make([]SnapID, cLen)
|
||||
ret = C.rados_ioctx_snap_list(
|
||||
ioctx.ioctx,
|
||||
(*C.rados_snap_t)(unsafe.Pointer(&snapList[0])),
|
||||
cLen)
|
||||
err = getErrorIfNegative(ret)
|
||||
return retry.Size(int(cLen)).If(err == errRange)
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return snapList[:ret], nil
|
||||
}
|
||||
|
||||
// RollbackSnap rollbacks the object with key oID to the pool snapshot.
|
||||
// The contents of the object will be the same as when the snapshot was taken.
|
||||
//
|
||||
// Implements:
|
||||
//
|
||||
// int rados_ioctx_snap_rollback(rados_ioctx_t io, const char *oid, const char *snapname);
|
||||
func (ioctx *IOContext) RollbackSnap(oid, snapName string) error {
|
||||
if err := ioctx.validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
coid := C.CString(oid)
|
||||
defer C.free(unsafe.Pointer(coid))
|
||||
cSnapName := C.CString(snapName)
|
||||
defer C.free(unsafe.Pointer(cSnapName))
|
||||
|
||||
ret := C.rados_ioctx_snap_rollback(ioctx.ioctx, coid, cSnapName)
|
||||
return getError(ret)
|
||||
}
|
||||
|
||||
// SnapHead is the representation of LIBRADOS_SNAP_HEAD from librados.
|
||||
// SnapHead can be used to reset the IOContext to stop reading from a snapshot.
|
||||
const SnapHead = SnapID(C.LIBRADOS_SNAP_HEAD)
|
||||
|
||||
// SetReadSnap sets the snapshot from which reads are performed.
|
||||
// Subsequent reads will return data as it was at the time of that snapshot.
|
||||
// Pass SnapHead for no snapshot (i.e. normal operation).
|
||||
//
|
||||
// Implements:
|
||||
//
|
||||
// void rados_ioctx_snap_set_read(rados_ioctx_t io, rados_snap_t snap);
|
||||
func (ioctx *IOContext) SetReadSnap(snapID SnapID) error {
|
||||
if err := ioctx.validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
C.rados_ioctx_snap_set_read(ioctx.ioctx, (C.rados_snap_t)(snapID))
|
||||
return nil
|
||||
}
|
||||
+375
@@ -0,0 +1,375 @@
|
||||
package rados
|
||||
|
||||
/*
|
||||
#cgo LDFLAGS: -lrados
|
||||
#include <stdlib.h>
|
||||
#include <rados/librados.h>
|
||||
extern void watchNotifyCb(void*, uint64_t, uint64_t, uint64_t, void*, size_t);
|
||||
extern void watchErrorCb(void*, uint64_t, int);
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"math"
|
||||
"sync"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"github.com/ceph/go-ceph/internal/log"
|
||||
)
|
||||
|
||||
type (
|
||||
// WatcherID is the unique id of a Watcher.
|
||||
WatcherID uint64
|
||||
// NotifyID is the unique id of a NotifyEvent.
|
||||
NotifyID uint64
|
||||
// NotifierID is the unique id of a notifying client.
|
||||
NotifierID uint64
|
||||
)
|
||||
|
||||
// NotifyEvent is received by a watcher for each notification.
|
||||
type NotifyEvent struct {
|
||||
ID NotifyID
|
||||
WatcherID WatcherID
|
||||
NotifierID NotifierID
|
||||
Data []byte
|
||||
}
|
||||
|
||||
// NotifyAck represents an acknowleged notification.
|
||||
type NotifyAck struct {
|
||||
WatcherID WatcherID
|
||||
NotifierID NotifierID
|
||||
Response []byte
|
||||
}
|
||||
|
||||
// NotifyTimeout represents an unacknowleged notification.
|
||||
type NotifyTimeout struct {
|
||||
WatcherID WatcherID
|
||||
NotifierID NotifierID
|
||||
}
|
||||
|
||||
// Watcher receives all notifications for certain object.
|
||||
type Watcher struct {
|
||||
id WatcherID
|
||||
oid string
|
||||
ioctx *IOContext
|
||||
events chan NotifyEvent
|
||||
errors chan error
|
||||
done chan struct{}
|
||||
}
|
||||
|
||||
var (
|
||||
watchers = map[WatcherID]*Watcher{}
|
||||
watchersMtx sync.RWMutex
|
||||
)
|
||||
|
||||
// Watch creates a Watcher for the specified object.
|
||||
//
|
||||
// A Watcher receives all notifications that are sent to the object on which it
|
||||
// has been created. It exposes two read-only channels: Events() receives all
|
||||
// the NotifyEvents and Errors() receives all occuring errors. A typical code
|
||||
// creating a Watcher could look like this:
|
||||
//
|
||||
// watcher, err := ioctx.Watch(oid)
|
||||
// go func() { // event handler
|
||||
// for ne := range watcher.Events() {
|
||||
// ...
|
||||
// ne.Ack([]byte("response data..."))
|
||||
// ...
|
||||
// }
|
||||
// }()
|
||||
// go func() { // error handler
|
||||
// for err := range watcher.Errors() {
|
||||
// ... handle err ...
|
||||
// }
|
||||
// }()
|
||||
//
|
||||
// CAUTION: the Watcher references the IOContext in which it has been created.
|
||||
// Therefore all watchers must be deleted with the Delete() method before the
|
||||
// IOContext is being destroyed.
|
||||
//
|
||||
// Implements:
|
||||
//
|
||||
// int rados_watch2(rados_ioctx_t io, const char* o, uint64_t* cookie,
|
||||
// rados_watchcb2_t watchcb, rados_watcherrcb_t watcherrcb, void* arg)
|
||||
func (ioctx *IOContext) Watch(obj string) (*Watcher, error) {
|
||||
return ioctx.WatchWithTimeout(obj, 0)
|
||||
}
|
||||
|
||||
// WatchWithTimeout creates a watcher on an object. Same as Watcher(), but
|
||||
// different timeout than the default can be specified.
|
||||
//
|
||||
// Implements:
|
||||
//
|
||||
// int rados_watch3(rados_ioctx_t io, const char *o, uint64_t *cookie,
|
||||
// rados_watchcb2_t watchcb, rados_watcherrcb_t watcherrcb, uint32_t timeout,
|
||||
// void *arg);
|
||||
func (ioctx *IOContext) WatchWithTimeout(oid string, timeout time.Duration) (*Watcher, error) {
|
||||
cObj := C.CString(oid)
|
||||
defer C.free(unsafe.Pointer(cObj))
|
||||
var id C.uint64_t
|
||||
watchersMtx.Lock()
|
||||
defer watchersMtx.Unlock()
|
||||
ret := C.rados_watch3(
|
||||
ioctx.ioctx,
|
||||
cObj,
|
||||
&id,
|
||||
(C.rados_watchcb2_t)(C.watchNotifyCb),
|
||||
(C.rados_watcherrcb_t)(C.watchErrorCb),
|
||||
C.uint32_t(timeout.Milliseconds()/1000),
|
||||
nil,
|
||||
)
|
||||
if err := getError(ret); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
evCh := make(chan NotifyEvent)
|
||||
errCh := make(chan error)
|
||||
w := &Watcher{
|
||||
id: WatcherID(id),
|
||||
ioctx: ioctx,
|
||||
oid: oid,
|
||||
events: evCh,
|
||||
errors: errCh,
|
||||
done: make(chan struct{}),
|
||||
}
|
||||
watchers[WatcherID(id)] = w
|
||||
return w, nil
|
||||
}
|
||||
|
||||
// ID returns the WatcherId of the Watcher
|
||||
func (w *Watcher) ID() WatcherID {
|
||||
return w.id
|
||||
}
|
||||
|
||||
// Events returns a read-only channel, that receives all notifications that are
|
||||
// sent to the object of the Watcher.
|
||||
func (w *Watcher) Events() <-chan NotifyEvent {
|
||||
return w.events
|
||||
}
|
||||
|
||||
// Errors returns a read-only channel, that receives all errors for the Watcher.
|
||||
func (w *Watcher) Errors() <-chan error {
|
||||
return w.errors
|
||||
}
|
||||
|
||||
// Check on the status of a Watcher.
|
||||
//
|
||||
// Returns the time since it was last confirmed. If there is an error, the
|
||||
// Watcher is no longer valid, and should be destroyed with the Delete() method.
|
||||
//
|
||||
// Implements:
|
||||
//
|
||||
// int rados_watch_check(rados_ioctx_t io, uint64_t cookie)
|
||||
func (w *Watcher) Check() (time.Duration, error) {
|
||||
ret := C.rados_watch_check(w.ioctx.ioctx, C.uint64_t(w.id))
|
||||
if ret < 0 {
|
||||
return 0, getError(ret)
|
||||
}
|
||||
return time.Millisecond * time.Duration(ret), nil
|
||||
}
|
||||
|
||||
// Delete the watcher. This closes both the event and error channel.
|
||||
//
|
||||
// Implements:
|
||||
//
|
||||
// int rados_unwatch2(rados_ioctx_t io, uint64_t cookie)
|
||||
func (w *Watcher) Delete() error {
|
||||
watchersMtx.Lock()
|
||||
_, ok := watchers[w.id]
|
||||
if ok {
|
||||
delete(watchers, w.id)
|
||||
}
|
||||
watchersMtx.Unlock()
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
ret := C.rados_unwatch2(w.ioctx.ioctx, C.uint64_t(w.id))
|
||||
if ret != 0 {
|
||||
return getError(ret)
|
||||
}
|
||||
close(w.done) // unblock blocked callbacks
|
||||
close(w.events)
|
||||
close(w.errors)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Notify sends a notification with the provided data to all Watchers of the
|
||||
// specified object.
|
||||
//
|
||||
// CAUTION: even if the error is not nil. the returned slices
|
||||
// might still contain data.
|
||||
func (ioctx *IOContext) Notify(obj string, data []byte) ([]NotifyAck, []NotifyTimeout, error) {
|
||||
return ioctx.NotifyWithTimeout(obj, data, 0)
|
||||
}
|
||||
|
||||
// NotifyWithTimeout is like Notify() but with a different timeout than the
|
||||
// default.
|
||||
//
|
||||
// Implements:
|
||||
//
|
||||
// int rados_notify2(rados_ioctx_t io, const char* o, const char* buf, int buf_len,
|
||||
// uint64_t timeout_ms, char** reply_buffer, size_t* reply_buffer_len)
|
||||
func (ioctx *IOContext) NotifyWithTimeout(obj string, data []byte, timeout time.Duration) ([]NotifyAck,
|
||||
[]NotifyTimeout, error) {
|
||||
cObj := C.CString(obj)
|
||||
defer C.free(unsafe.Pointer(cObj))
|
||||
var cResponse *C.char
|
||||
defer C.rados_buffer_free(cResponse)
|
||||
var responseLen C.size_t
|
||||
var dataPtr *C.char
|
||||
if len(data) > 0 {
|
||||
dataPtr = (*C.char)(unsafe.Pointer(&data[0]))
|
||||
}
|
||||
ret := C.rados_notify2(
|
||||
ioctx.ioctx,
|
||||
cObj,
|
||||
dataPtr,
|
||||
C.int(len(data)),
|
||||
C.uint64_t(timeout.Milliseconds()),
|
||||
&cResponse,
|
||||
&responseLen,
|
||||
)
|
||||
// cResponse has been set even if an error is returned, so we decode it anyway
|
||||
acks, timeouts := decodeNotifyResponse(cResponse, responseLen)
|
||||
return acks, timeouts, getError(ret)
|
||||
}
|
||||
|
||||
// Ack sends an acknowledgement with the specified response data to the notfier
|
||||
// of the NotifyEvent. If a notify is not ack'ed, the originating Notify() call
|
||||
// blocks and eventiually times out.
|
||||
//
|
||||
// Implements:
|
||||
//
|
||||
// int rados_notify_ack(rados_ioctx_t io, const char *o, uint64_t notify_id,
|
||||
// uint64_t cookie, const char *buf, int buf_len)
|
||||
func (ne *NotifyEvent) Ack(response []byte) error {
|
||||
watchersMtx.RLock()
|
||||
w, ok := watchers[ne.WatcherID]
|
||||
watchersMtx.RUnlock()
|
||||
if !ok {
|
||||
return fmt.Errorf("can't ack on deleted watcher %v", ne.WatcherID)
|
||||
}
|
||||
cOID := C.CString(w.oid)
|
||||
defer C.free(unsafe.Pointer(cOID))
|
||||
var respPtr *C.char
|
||||
if len(response) > 0 {
|
||||
respPtr = (*C.char)(unsafe.Pointer(&response[0]))
|
||||
}
|
||||
ret := C.rados_notify_ack(
|
||||
w.ioctx.ioctx,
|
||||
cOID,
|
||||
C.uint64_t(ne.ID),
|
||||
C.uint64_t(ne.WatcherID),
|
||||
respPtr,
|
||||
C.int(len(response)),
|
||||
)
|
||||
return getError(ret)
|
||||
}
|
||||
|
||||
// WatcherFlush flushes all pending notifications of the cluster.
|
||||
//
|
||||
// Implements:
|
||||
//
|
||||
// int rados_watch_flush(rados_t cluster)
|
||||
func (c *Conn) WatcherFlush() error {
|
||||
if !c.connected {
|
||||
return ErrNotConnected
|
||||
}
|
||||
ret := C.rados_watch_flush(c.cluster)
|
||||
return getError(ret)
|
||||
}
|
||||
|
||||
// decoder for this notify response format:
|
||||
//
|
||||
// le32 num_acks
|
||||
// {
|
||||
// le64 gid global id for the client (for client.1234 that's 1234)
|
||||
// le64 cookie cookie for the client
|
||||
// le32 buflen length of reply message buffer
|
||||
// u8 buflen payload
|
||||
// } num_acks
|
||||
// le32 num_timeouts
|
||||
// {
|
||||
// le64 gid global id for the client
|
||||
// le64 cookie cookie for the client
|
||||
// } num_timeouts
|
||||
//
|
||||
// NOTE: starting with pacific this is implemented as a C function and this can
|
||||
// be replaced later
|
||||
func decodeNotifyResponse(response *C.char, length C.size_t) ([]NotifyAck, []NotifyTimeout) {
|
||||
if length == 0 || response == nil {
|
||||
return nil, nil
|
||||
}
|
||||
b := (*[math.MaxInt32]byte)(unsafe.Pointer(response))[:length:length]
|
||||
pos := 0
|
||||
|
||||
num := binary.LittleEndian.Uint32(b[pos:])
|
||||
pos += 4
|
||||
acks := make([]NotifyAck, num)
|
||||
for i := range acks {
|
||||
acks[i].NotifierID = NotifierID(binary.LittleEndian.Uint64(b[pos:]))
|
||||
pos += 8
|
||||
acks[i].WatcherID = WatcherID(binary.LittleEndian.Uint64(b[pos:]))
|
||||
pos += 8
|
||||
dataLen := binary.LittleEndian.Uint32(b[pos:])
|
||||
pos += 4
|
||||
if dataLen > 0 {
|
||||
acks[i].Response = C.GoBytes(unsafe.Pointer(&b[pos]), C.int(dataLen))
|
||||
pos += int(dataLen)
|
||||
}
|
||||
}
|
||||
|
||||
num = binary.LittleEndian.Uint32(b[pos:])
|
||||
pos += 4
|
||||
timeouts := make([]NotifyTimeout, num)
|
||||
for i := range timeouts {
|
||||
timeouts[i].NotifierID = NotifierID(binary.LittleEndian.Uint64(b[pos:]))
|
||||
pos += 8
|
||||
timeouts[i].WatcherID = WatcherID(binary.LittleEndian.Uint64(b[pos:]))
|
||||
pos += 8
|
||||
}
|
||||
return acks, timeouts
|
||||
}
|
||||
|
||||
//export watchNotifyCb
|
||||
func watchNotifyCb(_ unsafe.Pointer, notifyID C.uint64_t, id C.uint64_t,
|
||||
notifierID C.uint64_t, cData unsafe.Pointer, dataLen C.size_t) {
|
||||
ev := NotifyEvent{
|
||||
ID: NotifyID(notifyID),
|
||||
WatcherID: WatcherID(id),
|
||||
NotifierID: NotifierID(notifierID),
|
||||
}
|
||||
if dataLen > 0 {
|
||||
ev.Data = C.GoBytes(cData, C.int(dataLen))
|
||||
}
|
||||
watchersMtx.RLock()
|
||||
w, ok := watchers[WatcherID(id)]
|
||||
watchersMtx.RUnlock()
|
||||
if !ok {
|
||||
// usually this should not happen, but who knows
|
||||
log.Warnf("received notification for unknown watcher ID: %#v", ev)
|
||||
return
|
||||
}
|
||||
select {
|
||||
case <-w.done: // unblock when deleted
|
||||
case w.events <- ev:
|
||||
}
|
||||
}
|
||||
|
||||
//export watchErrorCb
|
||||
func watchErrorCb(_ unsafe.Pointer, id C.uint64_t, err C.int) {
|
||||
watchersMtx.RLock()
|
||||
w, ok := watchers[WatcherID(id)]
|
||||
watchersMtx.RUnlock()
|
||||
if !ok {
|
||||
// usually this should not happen, but who knows
|
||||
log.Warnf("received error for unknown watcher ID: id=%d err=%#v", id, err)
|
||||
return
|
||||
}
|
||||
select {
|
||||
case <-w.done: // unblock when deleted
|
||||
case w.errors <- getError(err):
|
||||
}
|
||||
}
|
||||
+199
@@ -0,0 +1,199 @@
|
||||
package rados
|
||||
|
||||
// #cgo LDFLAGS: -lrados
|
||||
// #include <errno.h>
|
||||
// #include <stdlib.h>
|
||||
// #include <rados/librados.h>
|
||||
//
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
|
||||
"github.com/ceph/go-ceph/internal/cutil"
|
||||
ts "github.com/ceph/go-ceph/internal/timespec"
|
||||
)
|
||||
|
||||
// Timespec is a public type for the internal C 'struct timespec'
|
||||
type Timespec ts.Timespec
|
||||
|
||||
// WriteOp manages a set of discrete actions that will be performed together
|
||||
// atomically.
|
||||
type WriteOp struct {
|
||||
operation
|
||||
op C.rados_write_op_t
|
||||
}
|
||||
|
||||
// CreateWriteOp returns a newly constructed write operation.
|
||||
func CreateWriteOp() *WriteOp {
|
||||
return &WriteOp{
|
||||
op: C.rados_create_write_op(),
|
||||
}
|
||||
}
|
||||
|
||||
// Release the resources associated with this write operation.
|
||||
func (w *WriteOp) Release() {
|
||||
C.rados_release_write_op(w.op)
|
||||
w.op = nil
|
||||
w.free()
|
||||
}
|
||||
|
||||
func (w WriteOp) operate2(
|
||||
ioctx *IOContext, oid string, mtime *Timespec, flags OperationFlags) error {
|
||||
|
||||
if err := ioctx.validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cOid := C.CString(oid)
|
||||
defer C.free(unsafe.Pointer(cOid))
|
||||
var cMtime *C.struct_timespec
|
||||
if mtime != nil {
|
||||
cMtime = &C.struct_timespec{}
|
||||
ts.CopyToCStruct(
|
||||
ts.Timespec(*mtime),
|
||||
ts.CTimespecPtr(cMtime))
|
||||
}
|
||||
|
||||
ret := C.rados_write_op_operate2(
|
||||
w.op, ioctx.ioctx, cOid, cMtime, C.int(flags))
|
||||
return w.update(writeOp, ret)
|
||||
}
|
||||
|
||||
// Operate will perform the operation(s).
|
||||
func (w *WriteOp) Operate(ioctx *IOContext, oid string, flags OperationFlags) error {
|
||||
return w.operate2(ioctx, oid, nil, flags)
|
||||
}
|
||||
|
||||
// OperateWithMtime will perform the operation while setting the modification
|
||||
// time stamp to the supplied value.
|
||||
func (w *WriteOp) OperateWithMtime(
|
||||
ioctx *IOContext, oid string, mtime Timespec, flags OperationFlags) error {
|
||||
|
||||
return w.operate2(ioctx, oid, &mtime, flags)
|
||||
}
|
||||
|
||||
func (w *WriteOp) operateCompat(ioctx *IOContext, oid string) error {
|
||||
switch err := w.Operate(ioctx, oid, OperationNoFlag).(type) {
|
||||
case nil:
|
||||
return nil
|
||||
case OperationError:
|
||||
return err.OpError
|
||||
default:
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Create a rados object.
|
||||
func (w *WriteOp) Create(exclusive CreateOption) {
|
||||
// category, the 3rd param, is deprecated and has no effect so we do not
|
||||
// implement it in go-ceph
|
||||
C.rados_write_op_create(w.op, C.int(exclusive), nil)
|
||||
}
|
||||
|
||||
// SetOmap appends the map `pairs` to the omap `oid`.
|
||||
func (w *WriteOp) SetOmap(pairs map[string][]byte) {
|
||||
keys := make([]string, len(pairs))
|
||||
values := make([][]byte, len(pairs))
|
||||
idx := 0
|
||||
for k, v := range pairs {
|
||||
keys[idx] = k
|
||||
values[idx] = v
|
||||
idx++
|
||||
}
|
||||
|
||||
cKeys := cutil.NewBufferGroupStrings(keys)
|
||||
cValues := cutil.NewBufferGroupBytes(values)
|
||||
defer cKeys.Free()
|
||||
defer cValues.Free()
|
||||
|
||||
C.rados_write_op_omap_set2(
|
||||
w.op,
|
||||
(**C.char)(cKeys.BuffersPtr()),
|
||||
(**C.char)(cValues.BuffersPtr()),
|
||||
(*C.size_t)(cKeys.LengthsPtr()),
|
||||
(*C.size_t)(cValues.LengthsPtr()),
|
||||
(C.size_t)(len(pairs)))
|
||||
}
|
||||
|
||||
// RmOmapKeys removes the specified `keys` from the omap `oid`.
|
||||
func (w *WriteOp) RmOmapKeys(keys []string) {
|
||||
cKeys := cutil.NewBufferGroupStrings(keys)
|
||||
defer cKeys.Free()
|
||||
|
||||
C.rados_write_op_omap_rm_keys2(
|
||||
w.op,
|
||||
(**C.char)(cKeys.BuffersPtr()),
|
||||
(*C.size_t)(cKeys.LengthsPtr()),
|
||||
(C.size_t)(len(keys)))
|
||||
}
|
||||
|
||||
// CleanOmap clears the omap `oid`.
|
||||
func (w *WriteOp) CleanOmap() {
|
||||
C.rados_write_op_omap_clear(w.op)
|
||||
}
|
||||
|
||||
// AssertExists assures the object targeted by the write op exists.
|
||||
//
|
||||
// Implements:
|
||||
//
|
||||
// void rados_write_op_assert_exists(rados_write_op_t write_op);
|
||||
func (w *WriteOp) AssertExists() {
|
||||
C.rados_write_op_assert_exists(w.op)
|
||||
}
|
||||
|
||||
// Write a given byte slice at the supplied offset.
|
||||
//
|
||||
// Implements:
|
||||
//
|
||||
// void rados_write_op_write(rados_write_op_t write_op,
|
||||
// const char *buffer,
|
||||
// size_t len,
|
||||
// uint64_t offset);
|
||||
func (w *WriteOp) Write(b []byte, offset uint64) {
|
||||
oe := newWriteStep(b, 0, offset)
|
||||
w.steps = append(w.steps, oe)
|
||||
C.rados_write_op_write(
|
||||
w.op,
|
||||
oe.cBuffer,
|
||||
oe.cDataLen,
|
||||
oe.cOffset)
|
||||
}
|
||||
|
||||
// WriteFull writes a given byte slice as the whole object,
|
||||
// atomically replacing it.
|
||||
//
|
||||
// Implements:
|
||||
//
|
||||
// void rados_write_op_write_full(rados_write_op_t write_op,
|
||||
// const char *buffer,
|
||||
// size_t len);
|
||||
func (w *WriteOp) WriteFull(b []byte) {
|
||||
oe := newWriteStep(b, 0, 0)
|
||||
w.steps = append(w.steps, oe)
|
||||
C.rados_write_op_write_full(
|
||||
w.op,
|
||||
oe.cBuffer,
|
||||
oe.cDataLen)
|
||||
}
|
||||
|
||||
// WriteSame write a given byte slice to the object multiple times, until
|
||||
// writeLen is satisfied.
|
||||
//
|
||||
// Implements:
|
||||
//
|
||||
// void rados_write_op_writesame(rados_write_op_t write_op,
|
||||
// const char *buffer,
|
||||
// size_t data_len,
|
||||
// size_t write_len,
|
||||
// uint64_t offset);
|
||||
func (w *WriteOp) WriteSame(b []byte, writeLen, offset uint64) {
|
||||
oe := newWriteStep(b, writeLen, offset)
|
||||
w.steps = append(w.steps, oe)
|
||||
C.rados_write_op_writesame(
|
||||
w.op,
|
||||
oe.cBuffer,
|
||||
oe.cDataLen,
|
||||
oe.cWriteLen,
|
||||
oe.cOffset)
|
||||
}
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
package rados
|
||||
|
||||
// #cgo LDFLAGS: -lrados
|
||||
// #include <rados/librados.h>
|
||||
// #include <stdlib.h>
|
||||
//
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// WriteOpCmpExtStep holds result of the CmpExt write operation.
|
||||
// Result is valid only after Operate() was called.
|
||||
type WriteOpCmpExtStep struct {
|
||||
// C returned data:
|
||||
prval *C.int
|
||||
|
||||
// Result of the CmpExt write operation.
|
||||
Result int
|
||||
}
|
||||
|
||||
func (s *WriteOpCmpExtStep) update() error {
|
||||
s.Result = int(*s.prval)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *WriteOpCmpExtStep) free() {
|
||||
C.free(unsafe.Pointer(s.prval))
|
||||
s.prval = nil
|
||||
}
|
||||
|
||||
func newWriteOpCmpExtStep() *WriteOpCmpExtStep {
|
||||
return &WriteOpCmpExtStep{
|
||||
prval: (*C.int)(C.malloc(C.sizeof_int)),
|
||||
}
|
||||
}
|
||||
|
||||
// CmpExt ensures that given object range (extent) satisfies comparison.
|
||||
//
|
||||
// Implements:
|
||||
//
|
||||
// void rados_write_op_cmpext(rados_write_op_t write_op,
|
||||
// const char * cmp_buf,
|
||||
// size_t cmp_len,
|
||||
// uint64_t off,
|
||||
// int * prval);
|
||||
func (w *WriteOp) CmpExt(b []byte, offset uint64) *WriteOpCmpExtStep {
|
||||
oe := newWriteStep(b, 0, offset)
|
||||
cmpExtStep := newWriteOpCmpExtStep()
|
||||
w.steps = append(w.steps, oe, cmpExtStep)
|
||||
C.rados_write_op_cmpext(
|
||||
w.op,
|
||||
oe.cBuffer,
|
||||
oe.cDataLen,
|
||||
oe.cOffset,
|
||||
cmpExtStep.prval)
|
||||
|
||||
return cmpExtStep
|
||||
}
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
package rados
|
||||
|
||||
// #cgo LDFLAGS: -lrados
|
||||
// #include <stdlib.h>
|
||||
// #include <rados/librados.h>
|
||||
//
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// writeOpExecStep - exec step in write operation.
|
||||
type writeOpExecStep struct {
|
||||
withoutFree
|
||||
|
||||
inBuffPtr *C.char
|
||||
inBuffLen C.size_t
|
||||
prval C.int
|
||||
}
|
||||
|
||||
// newWriteOpExecStep - init new *writeOpExecStep.
|
||||
func newWriteOpExecStep(in []byte) *writeOpExecStep {
|
||||
es := &writeOpExecStep{
|
||||
prval: 0,
|
||||
}
|
||||
if len(in) > 0 {
|
||||
es.inBuffPtr = (*C.char)(unsafe.Pointer(&in[0]))
|
||||
es.inBuffLen = C.size_t(len(in))
|
||||
}
|
||||
|
||||
return es
|
||||
}
|
||||
|
||||
// update - update state operation.
|
||||
func (es *writeOpExecStep) update() error {
|
||||
return getError(es.prval)
|
||||
}
|
||||
|
||||
// Exec executes an OSD class method on an object.
|
||||
// See rados_exec() in the RADOS C api documentation for a general description.
|
||||
//
|
||||
// Implements:
|
||||
//
|
||||
// void rados_write_op_exec(rados_write_op_t write_op,
|
||||
// const char *cls,
|
||||
// const char *method,
|
||||
// const char *in_buf,
|
||||
// size_t in_len,
|
||||
// int *prval)
|
||||
func (w *WriteOp) Exec(clsName, method string, in []byte) {
|
||||
cClsName := C.CString(clsName)
|
||||
defer C.free(unsafe.Pointer(cClsName))
|
||||
|
||||
cMethod := C.CString(method)
|
||||
defer C.free(unsafe.Pointer(cMethod))
|
||||
|
||||
es := newWriteOpExecStep(in)
|
||||
w.steps = append(w.steps, es)
|
||||
C.rados_write_op_exec(
|
||||
w.op,
|
||||
cClsName,
|
||||
cMethod,
|
||||
es.inBuffPtr,
|
||||
es.inBuffLen,
|
||||
&es.prval,
|
||||
)
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
package rados
|
||||
|
||||
// #cgo LDFLAGS: -lrados
|
||||
// #include <rados/librados.h>
|
||||
// #include <stdlib.h>
|
||||
//
|
||||
import "C"
|
||||
|
||||
// SetAllocationHint sets allocation hint for an object. This is an advisory
|
||||
// operation, it will always succeed (as if it was submitted with a
|
||||
// LIBRADOS_OP_FLAG_FAILOK flag set) and is not guaranteed to do anything on
|
||||
// the backend.
|
||||
//
|
||||
// Implements:
|
||||
//
|
||||
// void rados_write_op_set_alloc_hint2(rados_write_op_t write_op,
|
||||
// uint64_t expected_object_size,
|
||||
// uint64_t expected_write_size,
|
||||
// uint32_t flags);
|
||||
func (w *WriteOp) SetAllocationHint(expectedObjectSize uint64, expectedWriteSize uint64, flags AllocHintFlags) {
|
||||
C.rados_write_op_set_alloc_hint2(
|
||||
w.op,
|
||||
C.uint64_t(expectedObjectSize),
|
||||
C.uint64_t(expectedWriteSize),
|
||||
C.uint32_t(flags))
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
package rados
|
||||
|
||||
// #include <stdint.h>
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type writeStep struct {
|
||||
withoutUpdate
|
||||
withoutFree
|
||||
// the c pointer utilizes the Go byteslice data and no free is needed
|
||||
|
||||
// inputs:
|
||||
b []byte
|
||||
|
||||
// arguments:
|
||||
cBuffer *C.char
|
||||
cDataLen C.size_t
|
||||
cWriteLen C.size_t
|
||||
cOffset C.uint64_t
|
||||
}
|
||||
|
||||
func newWriteStep(b []byte, writeLen, offset uint64) *writeStep {
|
||||
return &writeStep{
|
||||
b: b,
|
||||
cBuffer: (*C.char)(unsafe.Pointer(&b[0])), // TODO: must be pinned
|
||||
cDataLen: C.size_t(len(b)),
|
||||
cWriteLen: C.size_t(writeLen),
|
||||
cOffset: C.uint64_t(offset),
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user