rados/striper: start new striper package

Start a new `rados/striper` package that wraps Ceph's libradosstriper.
The libradosstriper library builds on top of the librados library to
support striping large "objects" over multiple RADOS objects.

Fixes: #1011

Signed-off-by: John Mulligan <jmulligan@redhat.com>
This commit is contained in:
John Mulligan 2024-07-29 14:26:07 -04:00 committed by mergify[bot]
parent 60ea53dabd
commit 33ed5cea3d
7 changed files with 409 additions and 0 deletions

32
rados/striper/errors.go Normal file
View File

@ -0,0 +1,32 @@
//go:build ceph_preview
package striper
/*
#include <errno.h>
*/
import "C"
import (
"github.com/ceph/go-ceph/internal/errutil"
)
// radosStriperError represents an error condition returned from the Ceph
// rados striper APIs.
type radosStriperError int
// Error returns the error string for the radosStriperError type.
func (e radosStriperError) Error() string {
return errutil.FormatErrorCode("rados", int(e))
}
func (e radosStriperError) ErrorCode() int {
return int(e)
}
func getError(e C.int) error {
if e == 0 {
return nil
}
return radosStriperError(e)
}

42
rados/striper/read.go Normal file
View File

@ -0,0 +1,42 @@
//go:build ceph_preview
package striper
// #cgo LDFLAGS: -lrados -lradosstriper
// #include <stdlib.h>
// #include <radosstriper/libradosstriper.h>
import "C"
import (
"unsafe"
)
// Read bytes into data from the striped object at the specified offset.
//
// Implements:
//
// int rados_striper_read(rados_striper_t striper,
// const char *soid,
// const char *buf,
// size_t len,
// uint64_t off);
func (s *Striper) Read(soid string, data []byte, offset uint64) (int, error) {
csoid := C.CString(soid)
defer C.free(unsafe.Pointer(csoid))
var bufptr *C.char
if len(data) > 0 {
bufptr = (*C.char)(unsafe.Pointer(&data[0]))
}
ret := C.rados_striper_read(
s.striper,
csoid,
bufptr,
C.size_t(len(data)),
C.uint64_t(offset))
if ret >= 0 {
return int(ret), nil
}
return 0, getError(ret)
}

47
rados/striper/stat.go Normal file
View File

@ -0,0 +1,47 @@
//go:build (octopus || pacific || quincy) && ceph_preview
package striper
// #cgo LDFLAGS: -lrados -lradosstriper
// #include <stdlib.h>
// #include <radosstriper/libradosstriper.h>
import "C"
import (
"unsafe"
)
// Stat returns metadata describing the striped object.
// This version of Stat uses an older API that does not provide time
// granularity below a second: the Nsec value of the StatInfo.ModTime field
// will always be zero.
//
// Implements:
//
// int rados_striper_stat(rados_striper_t striper,
// const char* soid,
// uint64_t *psize,
// time_t *pmtime);
func (s *Striper) Stat(soid string) (StatInfo, error) {
csoid := C.CString(soid)
defer C.free(unsafe.Pointer(csoid))
var (
size C.uint64_t
mtime C.time_t
)
ret := C.rados_striper_stat(
s.striper,
csoid,
&size,
&mtime)
if ret < 0 {
return StatInfo{}, getError(ret)
}
modts := Timespec{Sec: int64(mtime)}
return StatInfo{
Size: uint64(size),
ModTime: modts,
}, nil
}

45
rados/striper/stat2.go Normal file
View File

@ -0,0 +1,45 @@
//go:build !(octopus || pacific || quincy) && ceph_preview
package striper
// #cgo LDFLAGS: -lrados -lradosstriper
// #include <stdlib.h>
// #include <radosstriper/libradosstriper.h>
import "C"
import (
"unsafe"
ts "github.com/ceph/go-ceph/internal/timespec"
)
// Stat returns metadata describing the striped object.
//
// Implements:
//
// int rados_striper_stat2(rados_striper_t striper,
// const char* soid,
// uint64_t *psize,
// struct timespec *pmtime);
func (s *Striper) Stat(soid string) (StatInfo, error) {
csoid := C.CString(soid)
defer C.free(unsafe.Pointer(csoid))
var (
size C.uint64_t
mtime C.struct_timespec
)
ret := C.rados_striper_stat2(
s.striper,
csoid,
&size,
&mtime)
if ret < 0 {
return StatInfo{}, getError(ret)
}
return StatInfo{
Size: uint64(size),
ModTime: Timespec(ts.CStructToTimespec(ts.CTimespecPtr(&mtime))),
}, nil
}

View File

@ -0,0 +1,16 @@
//go:build ceph_preview
package striper
import (
ts "github.com/ceph/go-ceph/internal/timespec"
)
// Timespec behaves similarly to C's struct timespec.
type Timespec ts.Timespec
// StatInfo contains values returned by a Striper's Stat call.
type StatInfo struct {
Size uint64
ModTime Timespec
}

121
rados/striper/striper.go Normal file
View File

@ -0,0 +1,121 @@
//go:build ceph_preview
package striper
// #cgo LDFLAGS: -lrados -lradosstriper
// #include <errno.h>
// #include <stdlib.h>
// #include <radosstriper/libradosstriper.h>
import "C"
import (
"github.com/ceph/go-ceph/rados"
)
// Striper helps manage the reading, writing, and management of RADOS
// striped objects.
type Striper struct {
striper C.rados_striper_t
// Hold a reference back to the ioctx that the striper depends on so
// that Go doesn't garbage collect it prematurely.
ioctx *rados.IOContext
}
// Layout contains a group of values used to define the size parameters of
// striped objects. Note that these parameters only effect new striped objects.
// Existing striped objects retain the parameters they were created with.
type Layout struct {
StripeUnit uint
StripeCount uint
ObjectSize uint
}
// New returns a rados Striper object created from a rados IOContext.
func New(ioctx *rados.IOContext) (*Striper, error) {
var s C.rados_striper_t
ret := C.rados_striper_create(cephIoctx(ioctx), &s)
if err := getError(ret); err != nil {
return nil, err
}
return &Striper{s, ioctx}, nil
}
// NewWithLayout returns a rados Striper object created from a rados IOContext
// and striper layout parameters. These parameters will be used when new
// objects are created.
func NewWithLayout(ioctx *rados.IOContext, layout Layout) (*Striper, error) {
striper, err := New(ioctx)
if err != nil {
return nil, err
}
if err := striper.SetObjectLayoutStripeUnit(layout.StripeUnit); err != nil {
return nil, err
}
if err := striper.SetObjectLayoutStripeCount(layout.StripeCount); err != nil {
return nil, err
}
if err := striper.SetObjectLayoutObjectSize(layout.ObjectSize); err != nil {
return nil, err
}
return striper, nil
}
// Destroy the radosstriper object at the Ceph API level.
func (s *Striper) Destroy() {
C.rados_striper_destroy(s.striper)
}
// SetObjectLayoutStripeUnit sets the stripe unit value used to layout
// new objects.
//
// Implements:
//
// int rados_striper_set_object_layout_stripe_unit(rados_striper_t striper,
// unsigned int stripe_unit);
func (s *Striper) SetObjectLayoutStripeUnit(count uint) error {
ret := C.rados_striper_set_object_layout_stripe_unit(
s.striper,
C.uint(count),
)
return getError(ret)
}
// SetObjectLayoutStripeCount sets the stripe count value used to layout
// new objects.
//
// Implements:
//
// int rados_striper_set_object_layout_stripe_count(rados_striper_t striper,
// unsigned int stripe_count);
func (s *Striper) SetObjectLayoutStripeCount(count uint) error {
ret := C.rados_striper_set_object_layout_stripe_count(
s.striper,
C.uint(count),
)
return getError(ret)
}
// SetObjectLayoutObjectSize sets the object size value used to layout
// new objects.
//
// Implements:
//
// int rados_striper_set_object_layout_object_size(rados_striper_t striper,
// unsigned int object_size);
func (s *Striper) SetObjectLayoutObjectSize(count uint) error {
ret := C.rados_striper_set_object_layout_object_size(
s.striper,
C.uint(count),
)
return getError(ret)
}
// cephIoctx returns a ceph rados_ioctx_t given a go-ceph rados IOContext.
func cephIoctx(radosIoctx *rados.IOContext) C.rados_ioctx_t {
p := radosIoctx.Pointer()
if p == nil {
panic("invalid IOContext pointer")
}
return C.rados_ioctx_t(p)
}

106
rados/striper/write.go Normal file
View File

@ -0,0 +1,106 @@
//go:build ceph_preview
package striper
// #cgo LDFLAGS: -lrados -lradosstriper
// #include <stdlib.h>
// #include <radosstriper/libradosstriper.h>
import "C"
import "unsafe"
// Write bytes from data into the striped object at the specified offset.
//
// Implements:
//
// int rados_striper_write(rados_striper_t striper,
// const char *soid,
// const char *buf,
// size_t len,
// uint64_t off);
func (s *Striper) Write(soid string, data []byte, offset uint64) error {
csoid := C.CString(soid)
defer C.free(unsafe.Pointer(csoid))
bufptr := (*C.char)(unsafe.Pointer(&data[0]))
ret := C.rados_striper_write(
s.striper,
csoid,
bufptr,
C.size_t(len(data)),
C.uint64_t(offset))
return getError(ret)
}
// WriteFull writes all of the bytes in data to the striped object, truncating
// the object to the length of data.
//
// Implements:
//
// int rados_striper_write_full(rados_striper_t striper,
// const char *soid,
// const char *buf,
// size_t len);
func (s *Striper) WriteFull(soid string, data []byte) error {
csoid := C.CString(soid)
defer C.free(unsafe.Pointer(csoid))
bufptr := (*C.char)(unsafe.Pointer(&data[0]))
ret := C.rados_striper_write_full(
s.striper,
csoid,
bufptr,
C.size_t(len(data)))
return getError(ret)
}
// Append the bytes in data to the end of the striped object.
//
// Implements:
//
// int rados_striper_append(rados_striper_t striper,
// const char *soid,
// const char *buf,
// size_t len);
func (s *Striper) Append(soid string, data []byte) error {
csoid := C.CString(soid)
defer C.free(unsafe.Pointer(csoid))
bufptr := (*C.char)(unsafe.Pointer(&data[0]))
ret := C.rados_striper_append(
s.striper,
csoid,
bufptr,
C.size_t(len(data)))
return getError(ret)
}
// Remove a striped RADOS object.
//
// Implements:
//
// int rados_striper_remove(rados_striper_t striper,
// const char *soid);
func (s *Striper) Remove(soid string) error {
csoid := C.CString(soid)
defer C.free(unsafe.Pointer(csoid))
ret := C.rados_striper_remove(s.striper, csoid)
return getError(ret)
}
// Truncate a striped object, setting it to the specified size.
//
// Implements:
//
// int rados_striper_trunc(rados_striper_t striper, const char *soid, uint64_t size);
func (s *Striper) Truncate(soid string, size uint64) error {
csoid := C.CString(soid)
defer C.free(unsafe.Pointer(csoid))
ret := C.rados_striper_trunc(
s.striper,
csoid,
C.uint64_t(size))
return getError(ret)
}