From ba99275f900adad61800af4f16258bedaf09c90e Mon Sep 17 00:00:00 2001 From: Niels de Vos Date: Tue, 7 Jan 2020 14:30:27 +0100 Subject: [PATCH] dep: add github.com/ceph/go-ceph for rbd API Signed-off-by: Niels de Vos --- Gopkg.lock | 14 + Gopkg.toml | 5 +- vendor/github.com/ceph/go-ceph/LICENSE | 21 + .../ceph/go-ceph/errutil/strerror.go | 45 + vendor/github.com/ceph/go-ceph/rados/conn.go | 408 +++++ vendor/github.com/ceph/go-ceph/rados/doc.go | 4 + vendor/github.com/ceph/go-ceph/rados/ioctx.go | 622 ++++++++ .../ceph/go-ceph/rados/object_iter.go | 92 ++ vendor/github.com/ceph/go-ceph/rados/omap.go | 228 +++ vendor/github.com/ceph/go-ceph/rados/rados.go | 137 ++ vendor/github.com/ceph/go-ceph/rbd/doc.go | 4 + vendor/github.com/ceph/go-ceph/rbd/options.go | 169 ++ vendor/github.com/ceph/go-ceph/rbd/rbd.go | 1403 +++++++++++++++++ .../github.com/ceph/go-ceph/rbd/rbd_mimic.go | 43 + .../ceph/go-ceph/rbd/rbd_nautilus.go | 45 + .../ceph/go-ceph/rbd/snapshot_mimic.go | 101 ++ .../ceph/go-ceph/rbd/snapshot_nautilus.go | 104 ++ 17 files changed, 3444 insertions(+), 1 deletion(-) create mode 100644 vendor/github.com/ceph/go-ceph/LICENSE create mode 100644 vendor/github.com/ceph/go-ceph/errutil/strerror.go create mode 100644 vendor/github.com/ceph/go-ceph/rados/conn.go create mode 100644 vendor/github.com/ceph/go-ceph/rados/doc.go create mode 100644 vendor/github.com/ceph/go-ceph/rados/ioctx.go create mode 100644 vendor/github.com/ceph/go-ceph/rados/object_iter.go create mode 100644 vendor/github.com/ceph/go-ceph/rados/omap.go create mode 100644 vendor/github.com/ceph/go-ceph/rados/rados.go create mode 100644 vendor/github.com/ceph/go-ceph/rbd/doc.go create mode 100644 vendor/github.com/ceph/go-ceph/rbd/options.go create mode 100644 vendor/github.com/ceph/go-ceph/rbd/rbd.go create mode 100644 vendor/github.com/ceph/go-ceph/rbd/rbd_mimic.go create mode 100644 vendor/github.com/ceph/go-ceph/rbd/rbd_nautilus.go create mode 100644 vendor/github.com/ceph/go-ceph/rbd/snapshot_mimic.go create mode 100644 vendor/github.com/ceph/go-ceph/rbd/snapshot_nautilus.go diff --git a/Gopkg.lock b/Gopkg.lock index 15681ec43..2e2e94902 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -25,6 +25,18 @@ revision = "ba2c2ddd89069b46a7011d4106f6868f17ee1705" version = "v3.6.1" +[[projects]] + digest = "1:286a840b63ddce8e6f85368e22a834aefcaaf23f18a58ce01a42d7a978d21baf" + name = "github.com/ceph/go-ceph" + packages = [ + "errutil", + "rados", + "rbd", + ] + pruneopts = "NUT" + revision = "dc289754c8976f8cca2eb47dbea81895a4cac872" + version = "v0.2.0" + [[projects]] digest = "1:7f21fa1f8ab9a529dba26a7e9cf20de217c307fa1d96cb599d3afd9e5c83e9d6" name = "github.com/container-storage-interface/spec" @@ -1331,6 +1343,8 @@ analyzer-name = "dep" analyzer-version = 1 input-imports = [ + "github.com/ceph/go-ceph/rados", + "github.com/ceph/go-ceph/rbd", "github.com/container-storage-interface/spec/lib/go/csi", "github.com/golang/protobuf/ptypes", "github.com/golang/protobuf/ptypes/timestamp", diff --git a/Gopkg.toml b/Gopkg.toml index 317e9da86..be2880365 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -75,6 +75,10 @@ branch = "master" name = "k8s.io/utils" +[[constraint]] + name = "github.com/ceph/go-ceph" + version = "0.2" + [prune] go-tests = true non-go = true @@ -84,4 +88,3 @@ [[prune.project]] name = "github.com/kubernetes-csi/external-snapshotter" non-go = false - diff --git a/vendor/github.com/ceph/go-ceph/LICENSE b/vendor/github.com/ceph/go-ceph/LICENSE new file mode 100644 index 000000000..08d70bfc0 --- /dev/null +++ b/vendor/github.com/ceph/go-ceph/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014 Noah Watkins + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/ceph/go-ceph/errutil/strerror.go b/vendor/github.com/ceph/go-ceph/errutil/strerror.go new file mode 100644 index 000000000..907c70213 --- /dev/null +++ b/vendor/github.com/ceph/go-ceph/errutil/strerror.go @@ -0,0 +1,45 @@ +/* +Package errutil provides common functions for dealing with error conditions for +all ceph api wrappers. +*/ +package errutil + +/* force XSI-complaint strerror_r() */ + +// #define _POSIX_C_SOURCE 200112L +// #undef _GNU_SOURCE +// #include +// #include +// #include +import "C" + +import ( + "unsafe" +) + +// FormatErrno returns the absolute value of the errno as well as a string +// describing the errno. The string will be empty is the errno is not known. +func FormatErrno(errno int) (int, string) { + buf := make([]byte, 1024) + // strerror expects errno >= 0 + if errno < 0 { + errno = -errno + } + + ret := C.strerror_r( + C.int(errno), + (*C.char)(unsafe.Pointer(&buf[0])), + C.size_t(len(buf))) + if ret != 0 { + return errno, "" + } + + return errno, C.GoString((*C.char)(unsafe.Pointer(&buf[0]))) +} + +// StrError returns a string describing the errno. The string will be empty if +// the errno is not known. +func StrError(errno int) string { + _, s := FormatErrno(errno) + return s +} diff --git a/vendor/github.com/ceph/go-ceph/rados/conn.go b/vendor/github.com/ceph/go-ceph/rados/conn.go new file mode 100644 index 000000000..78c3b268a --- /dev/null +++ b/vendor/github.com/ceph/go-ceph/rados/conn.go @@ -0,0 +1,408 @@ +package rados + +// #cgo LDFLAGS: -lrados +// #include +// #include +import "C" + +import ( + "bytes" + "errors" + "unsafe" +) + +var ( + // ErrNotConnected is returned when functions are called without a RADOS connection + ErrNotConnected = errors.New("RADOS not connected") +) + +// ClusterStat represents Ceph cluster statistics. +type ClusterStat struct { + Kb uint64 + Kb_used uint64 + Kb_avail uint64 + Num_objects uint64 +} + +// 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) { + c_id := C.CString(id) + defer C.free(unsafe.Pointer(c_id)) + + var strlen C.size_t + var strout *C.char + + ret := C.rados_ping_monitor(c.cluster, c_id, &strout, &strlen) + defer C.rados_buffer_free(strout) + + if ret == 0 { + reply := C.GoStringN(strout, (C.int)(strlen)) + return reply, nil + } + return "", RadosError(int(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 RadosError(int(ret)) + } + c.connected = true + return nil +} + +// Shutdown disconnects from the cluster. +func (c *Conn) Shutdown() { + if err := c.ensure_connected(); err != nil { + return + } + freeConn(c) +} + +// ReadConfigFile configures the connection using a Ceph configuration file. +func (c *Conn) ReadConfigFile(path string) error { + c_path := C.CString(path) + defer C.free(unsafe.Pointer(c_path)) + ret := C.rados_conf_read_file(c.cluster, c_path) + return getRadosError(int(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 getRadosError(int(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) { + c_pool := C.CString(pool) + defer C.free(unsafe.Pointer(c_pool)) + ioctx := &IOContext{} + ret := C.rados_ioctx_create(c.cluster, c_pool, &ioctx.ioctx) + if ret == 0 { + return ioctx, nil + } + return nil, RadosError(int(ret)) +} + +// ListPools returns the names of all existing pools. +func (c *Conn) ListPools() (names []string, err error) { + buf := make([]byte, 4096) + for { + ret := int(C.rados_pool_list(c.cluster, + (*C.char)(unsafe.Pointer(&buf[0])), C.size_t(len(buf)))) + if ret < 0 { + return nil, RadosError(int(ret)) + } + + if ret > len(buf) { + buf = make([]byte, ret) + continue + } + + tmp := bytes.SplitAfter(buf[:ret-1], []byte{0}) + for _, s := range tmp { + if len(s) > 0 { + name := C.GoString((*C.char)(unsafe.Pointer(&s[0]))) + names = append(names, name) + } + } + + return names, nil + } +} + +// SetConfigOption sets the value of the configuration option identified by +// the given name. +func (c *Conn) SetConfigOption(option, value string) error { + c_opt, c_val := C.CString(option), C.CString(value) + defer C.free(unsafe.Pointer(c_opt)) + defer C.free(unsafe.Pointer(c_val)) + ret := C.rados_conf_set(c.cluster, c_opt, c_val) + return getRadosError(int(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) { + buf := make([]byte, 4096) + c_name := C.CString(name) + defer C.free(unsafe.Pointer(c_name)) + ret := int(C.rados_conf_get(c.cluster, c_name, + (*C.char)(unsafe.Pointer(&buf[0])), C.size_t(len(buf)))) + // FIXME: ret may be -ENAMETOOLONG if the buffer is not large enough. We + // can handle this case, but we need a reliable way to test for + // -ENAMETOOLONG constant. Will the syscall/Errno stuff in Go help? + if ret == 0 { + value = C.GoString((*C.char)(unsafe.Pointer(&buf[0]))) + return value, nil + } + return "", RadosError(ret) +} + +// 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 getRadosError(int(ret)) +} + +func (c *Conn) ensure_connected() 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.ensure_connected(); err != nil { + return ClusterStat{}, err + } + c_stat := C.struct_rados_cluster_stat_t{} + ret := C.rados_cluster_stat(c.cluster, &c_stat) + if ret < 0 { + return ClusterStat{}, RadosError(int(ret)) + } + return ClusterStat{ + Kb: uint64(c_stat.kb), + Kb_used: uint64(c_stat.kb_used), + Kb_avail: uint64(c_stat.kb_avail), + Num_objects: uint64(c_stat.num_objects), + }, nil +} + +// ParseCmdLineArgs configures the connection from command line arguments. +func (c *Conn) ParseCmdLineArgs(args []string) error { + // add an empty element 0 -- Ceph treats the array as the actual contents + // of argv and skips the first element (the executable name) + argc := C.int(len(args) + 1) + argv := make([]*C.char, argc) + + // make the first element a string just in case it is ever examined + argv[0] = C.CString("placeholder") + defer C.free(unsafe.Pointer(argv[0])) + + for i, arg := range args { + argv[i+1] = C.CString(arg) + defer C.free(unsafe.Pointer(argv[i+1])) + } + + ret := C.rados_conf_parse_argv(c.cluster, argc, &argv[0]) + return getRadosError(int(ret)) +} + +// ParseDefaultConfigEnv configures the connection from the default Ceph +// environment variable(s). +func (c *Conn) ParseDefaultConfigEnv() error { + ret := C.rados_conf_parse_env(c.cluster, nil) + return getRadosError(int(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 := int(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 "", RadosError(int(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 { + c_name := C.CString(name) + defer C.free(unsafe.Pointer(c_name)) + ret := int(C.rados_pool_create(c.cluster, c_name)) + return getRadosError(int(ret)) +} + +// DeletePool deletes a pool and all the data inside the pool. +func (c *Conn) DeletePool(name string) error { + if err := c.ensure_connected(); err != nil { + return err + } + c_name := C.CString(name) + defer C.free(unsafe.Pointer(c_name)) + ret := int(C.rados_pool_delete(c.cluster, c_name)) + return getRadosError(int(ret)) +} + +// GetPoolByName returns the ID of the pool with a given name. +func (c *Conn) GetPoolByName(name string) (int64, error) { + if err := c.ensure_connected(); err != nil { + return 0, err + } + c_name := C.CString(name) + defer C.free(unsafe.Pointer(c_name)) + ret := int64(C.rados_pool_lookup(c.cluster, c_name)) + if ret < 0 { + return 0, RadosError(ret) + } + return 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.ensure_connected(); err != nil { + return "", err + } + c_id := C.int64_t(id) + ret := int(C.rados_pool_reverse_lookup(c.cluster, c_id, (*C.char)(unsafe.Pointer(&buf[0])), C.size_t(len(buf)))) + if ret < 0 { + return "", RadosError(ret) + } + return C.GoString((*C.char)(unsafe.Pointer(&buf[0]))), nil +} + +// MonCommand sends a command to one of the monitors +func (c *Conn) MonCommand(args []byte) (buffer []byte, info string, err error) { + return c.monCommand(args, nil) +} + +// MonCommandWithInputBuffer sends a command to one of the monitors, with an input buffer +func (c *Conn) MonCommandWithInputBuffer(args, inputBuffer []byte) (buffer []byte, info string, err error) { + return c.monCommand(args, inputBuffer) +} + +func (c *Conn) monCommand(args, inputBuffer []byte) (buffer []byte, info string, err error) { + argv := C.CString(string(args)) + defer C.free(unsafe.Pointer(argv)) + + var ( + outs, outbuf *C.char + outslen, outbuflen C.size_t + ) + inbuf := C.CString(string(inputBuffer)) + inbufLen := len(inputBuffer) + defer C.free(unsafe.Pointer(inbuf)) + + ret := C.rados_mon_command(c.cluster, + &argv, 1, + inbuf, // bulk input (e.g. crush map) + C.size_t(inbufLen), // length inbuf + &outbuf, // buffer + &outbuflen, // buffer length + &outs, // status string + &outslen) + + if outslen > 0 { + info = C.GoStringN(outs, C.int(outslen)) + C.free(unsafe.Pointer(outs)) + } + if outbuflen > 0 { + buffer = C.GoBytes(unsafe.Pointer(outbuf), C.int(outbuflen)) + C.free(unsafe.Pointer(outbuf)) + } + if ret != 0 { + err = RadosError(int(ret)) + return nil, info, err + } + + return +} + +// PGCommand sends a command to one of the PGs +// +// Implements: +// int rados_pg_command(rados_t cluster, const char *pgstr, +// const char **cmd, size_t cmdlen, +// const char *inbuf, size_t inbuflen, +// char **outbuf, size_t *outbuflen, +// char **outs, size_t *outslen); +func (c *Conn) PGCommand(pgid []byte, args [][]byte) (buffer []byte, info string, err error) { + return c.pgCommand(pgid, args, nil) +} + +// PGCommandWithInputBuffer sends a command to one of the PGs, with an input buffer +// +// Implements: +// int rados_pg_command(rados_t cluster, const char *pgstr, +// const char **cmd, size_t cmdlen, +// const char *inbuf, size_t inbuflen, +// char **outbuf, size_t *outbuflen, +// char **outs, size_t *outslen); +func (c *Conn) PGCommandWithInputBuffer(pgid []byte, args [][]byte, inputBuffer []byte) (buffer []byte, info string, err error) { + return c.pgCommand(pgid, args, inputBuffer) +} + +func (c *Conn) pgCommand(pgid []byte, args [][]byte, inputBuffer []byte) (buffer []byte, info string, err error) { + name := C.CString(string(pgid)) + defer C.free(unsafe.Pointer(name)) + + argc := len(args) + argv := make([]*C.char, argc) + + for i, arg := range args { + argv[i] = C.CString(string(arg)) + defer C.free(unsafe.Pointer(argv[i])) + } + + var ( + outs, outbuf *C.char + outslen, outbuflen C.size_t + ) + inbuf := C.CString(string(inputBuffer)) + inbufLen := len(inputBuffer) + defer C.free(unsafe.Pointer(inbuf)) + + ret := C.rados_pg_command(c.cluster, + name, + &argv[0], + C.size_t(argc), + inbuf, // bulk input + C.size_t(inbufLen), // length inbuf + &outbuf, // buffer + &outbuflen, // buffer length + &outs, // status string + &outslen) + + if outslen > 0 { + info = C.GoStringN(outs, C.int(outslen)) + C.free(unsafe.Pointer(outs)) + } + if outbuflen > 0 { + buffer = C.GoBytes(unsafe.Pointer(outbuf), C.int(outbuflen)) + C.free(unsafe.Pointer(outbuf)) + } + if ret != 0 { + err = RadosError(int(ret)) + return nil, info, err + } + + return +} diff --git a/vendor/github.com/ceph/go-ceph/rados/doc.go b/vendor/github.com/ceph/go-ceph/rados/doc.go new file mode 100644 index 000000000..5b7d82bf1 --- /dev/null +++ b/vendor/github.com/ceph/go-ceph/rados/doc.go @@ -0,0 +1,4 @@ +/* +Package rados contains a set of wrappers around Ceph's librados API. +*/ +package rados diff --git a/vendor/github.com/ceph/go-ceph/rados/ioctx.go b/vendor/github.com/ceph/go-ceph/rados/ioctx.go new file mode 100644 index 000000000..0c7e641ef --- /dev/null +++ b/vendor/github.com/ceph/go-ceph/rados/ioctx.go @@ -0,0 +1,622 @@ +package rados + +// #cgo LDFLAGS: -lrados +// #include +// #include +// #include +// +// 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" +) + +// 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 +) + +// 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 +} + +// 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 +} + +// Pointer returns a uintptr representation of the IOContext. +func (ioctx *IOContext) Pointer() uintptr { + return uintptr(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. +func (ioctx *IOContext) SetNamespace(namespace string) { + var c_ns *C.char + if len(namespace) > 0 { + c_ns = C.CString(namespace) + defer C.free(unsafe.Pointer(c_ns)) + } + C.rados_ioctx_set_namespace(ioctx.ioctx, c_ns) +} + +// 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 { + c_oid := C.CString(oid) + defer C.free(unsafe.Pointer(c_oid)) + + op := C.rados_create_write_op() + C.rados_write_op_create(op, C.int(exclusive), nil) + ret := C.rados_write_op_operate(op, ioctx.ioctx, c_oid, nil, 0) + C.rados_release_write_op(op) + + return getRadosError(int(ret)) +} + +// 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 { + c_oid := C.CString(oid) + defer C.free(unsafe.Pointer(c_oid)) + + dataPointer := unsafe.Pointer(nil) + if len(data) > 0 { + dataPointer = unsafe.Pointer(&data[0]) + } + + ret := C.rados_write(ioctx.ioctx, c_oid, + (*C.char)(dataPointer), + (C.size_t)(len(data)), + (C.uint64_t)(offset)) + + return getRadosError(int(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 { + c_oid := C.CString(oid) + defer C.free(unsafe.Pointer(c_oid)) + + ret := C.rados_write_full(ioctx.ioctx, c_oid, + (*C.char)(unsafe.Pointer(&data[0])), + (C.size_t)(len(data))) + return getRadosError(int(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 { + c_oid := C.CString(oid) + defer C.free(unsafe.Pointer(c_oid)) + + ret := C.rados_append(ioctx.ioctx, c_oid, + (*C.char)(unsafe.Pointer(&data[0])), + (C.size_t)(len(data))) + return getRadosError(int(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) { + c_oid := C.CString(oid) + defer C.free(unsafe.Pointer(c_oid)) + + var buf *C.char + if len(data) > 0 { + buf = (*C.char)(unsafe.Pointer(&data[0])) + } + + ret := C.rados_read( + ioctx.ioctx, + c_oid, + buf, + (C.size_t)(len(data)), + (C.uint64_t)(offset)) + + if ret >= 0 { + return int(ret), nil + } + return 0, getRadosError(int(ret)) +} + +// Delete deletes the object with key oid. It returns an error, if any. +func (ioctx *IOContext) Delete(oid string) error { + c_oid := C.CString(oid) + defer C.free(unsafe.Pointer(c_oid)) + + return getRadosError(int(C.rados_remove(ioctx.ioctx, c_oid))) +} + +// 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 { + c_oid := C.CString(oid) + defer C.free(unsafe.Pointer(c_oid)) + + return getRadosError(int(C.rados_trunc(ioctx.ioctx, c_oid, (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) { + c_stat := C.struct_rados_pool_stat_t{} + ret := C.rados_ioctx_pool_stat(ioctx.ioctx, &c_stat) + if ret < 0 { + return PoolStat{}, getRadosError(int(ret)) + } + return PoolStat{ + Num_bytes: uint64(c_stat.num_bytes), + Num_kb: uint64(c_stat.num_kb), + Num_objects: uint64(c_stat.num_objects), + Num_object_clones: uint64(c_stat.num_object_clones), + Num_object_copies: uint64(c_stat.num_object_copies), + Num_objects_missing_on_primary: uint64(c_stat.num_objects_missing_on_primary), + Num_objects_unfound: uint64(c_stat.num_objects_unfound), + Num_objects_degraded: uint64(c_stat.num_objects_degraded), + Num_rd: uint64(c_stat.num_rd), + Num_rd_kb: uint64(c_stat.num_rd_kb), + Num_wr: uint64(c_stat.num_wr), + Num_wr_kb: uint64(c_stat.num_wr_kb), + }, nil +} + +// GetPoolName returns the name of the pool associated with the I/O context. +func (ioctx *IOContext) GetPoolName() (name string, err error) { + buf := make([]byte, 128) + for { + ret := C.rados_ioctx_get_pool_name(ioctx.ioctx, + (*C.char)(unsafe.Pointer(&buf[0])), C.unsigned(len(buf))) + if ret == -C.ERANGE { + buf = make([]byte, len(buf)*2) + continue + } else if ret < 0 { + return "", getRadosError(int(ret)) + } + 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 { + var ctx C.rados_list_ctx_t + ret := C.rados_nobjects_list_open(ioctx.ioctx, &ctx) + if ret < 0 { + return getRadosError(int(ret)) + } + defer func() { C.rados_nobjects_list_close(ctx) }() + + for { + var c_entry *C.char + ret := C.rados_nobjects_list_next(ctx, &c_entry, nil, nil) + if ret == -C.ENOENT { + return nil + } else if ret < 0 { + return getRadosError(int(ret)) + } + listFn(C.GoString(c_entry)) + } +} + +// Stat returns the size of the object and its last modification time +func (ioctx *IOContext) Stat(object string) (stat ObjectStat, err error) { + var c_psize C.uint64_t + var c_pmtime C.time_t + c_object := C.CString(object) + defer C.free(unsafe.Pointer(c_object)) + + ret := C.rados_stat( + ioctx.ioctx, + c_object, + &c_psize, + &c_pmtime) + + if ret < 0 { + return ObjectStat{}, getRadosError(int(ret)) + } + return ObjectStat{ + Size: uint64(c_psize), + ModTime: time.Unix(int64(c_pmtime), 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) { + c_object := C.CString(object) + c_name := C.CString(name) + defer C.free(unsafe.Pointer(c_object)) + defer C.free(unsafe.Pointer(c_name)) + + ret := C.rados_getxattr( + ioctx.ioctx, + c_object, + c_name, + (*C.char)(unsafe.Pointer(&data[0])), + (C.size_t)(len(data))) + + if ret >= 0 { + return int(ret), nil + } + return 0, getRadosError(int(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 { + c_object := C.CString(object) + c_name := C.CString(name) + defer C.free(unsafe.Pointer(c_object)) + defer C.free(unsafe.Pointer(c_name)) + + ret := C.rados_setxattr( + ioctx.ioctx, + c_object, + c_name, + (*C.char)(unsafe.Pointer(&data[0])), + (C.size_t)(len(data))) + + return getRadosError(int(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) { + c_oid := C.CString(oid) + defer C.free(unsafe.Pointer(c_oid)) + + var it C.rados_xattrs_iter_t + + ret := C.rados_getxattrs(ioctx.ioctx, c_oid, &it) + if ret < 0 { + return nil, getRadosError(int(ret)) + } + defer func() { C.rados_getxattrs_end(it) }() + m := make(map[string][]byte) + for { + var c_name, c_val *C.char + var c_len C.size_t + defer C.free(unsafe.Pointer(c_name)) + defer C.free(unsafe.Pointer(c_val)) + + ret := C.rados_getxattrs_next(it, &c_name, &c_val, &c_len) + if ret < 0 { + return nil, getRadosError(int(ret)) + } + // rados api returns a null name,val & 0-length upon + // end of iteration + if c_name == nil { + return m, nil // stop iteration + } + m[C.GoString(c_name)] = C.GoBytes(unsafe.Pointer(c_val), (C.int)(c_len)) + } +} + +// RmXattr removes an xattr with key `name` from object `oid` +func (ioctx *IOContext) RmXattr(oid string, name string) error { + c_oid := C.CString(oid) + c_name := C.CString(name) + defer C.free(unsafe.Pointer(c_oid)) + defer C.free(unsafe.Pointer(c_name)) + + ret := C.rados_rmxattr( + ioctx.ioctx, + c_oid, + c_name) + + return getRadosError(int(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) { + c_oid := C.CString(oid) + c_name := C.CString(name) + c_cookie := C.CString(cookie) + c_desc := C.CString(desc) + + var c_duration C.struct_timeval + if duration != 0 { + tv := syscall.NsecToTimeval(duration.Nanoseconds()) + c_duration = C.struct_timeval{tv_sec: C.ceph_time_t(tv.Sec), tv_usec: C.ceph_suseconds_t(tv.Usec)} + } + + var c_flags C.uint8_t + if flags != nil { + c_flags = C.uint8_t(*flags) + } + + defer C.free(unsafe.Pointer(c_oid)) + defer C.free(unsafe.Pointer(c_name)) + defer C.free(unsafe.Pointer(c_cookie)) + defer C.free(unsafe.Pointer(c_desc)) + + ret := C.rados_lock_exclusive( + ioctx.ioctx, + c_oid, + c_name, + c_cookie, + c_desc, + &c_duration, + c_flags) + + // 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), RadosError(int(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) { + c_oid := C.CString(oid) + c_name := C.CString(name) + c_cookie := C.CString(cookie) + c_tag := C.CString(tag) + c_desc := C.CString(desc) + + var c_duration C.struct_timeval + if duration != 0 { + tv := syscall.NsecToTimeval(duration.Nanoseconds()) + c_duration = C.struct_timeval{tv_sec: C.ceph_time_t(tv.Sec), tv_usec: C.ceph_suseconds_t(tv.Usec)} + } + + var c_flags C.uint8_t + if flags != nil { + c_flags = C.uint8_t(*flags) + } + + defer C.free(unsafe.Pointer(c_oid)) + defer C.free(unsafe.Pointer(c_name)) + defer C.free(unsafe.Pointer(c_cookie)) + defer C.free(unsafe.Pointer(c_tag)) + defer C.free(unsafe.Pointer(c_desc)) + + ret := C.rados_lock_shared( + ioctx.ioctx, + c_oid, + c_name, + c_cookie, + c_tag, + c_desc, + &c_duration, + c_flags) + + // 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), RadosError(int(ret)) + } +} + +// Unlock releases a shared or exclusive lock on an object. +func (ioctx *IOContext) Unlock(oid, name, cookie string) (int, error) { + c_oid := C.CString(oid) + c_name := C.CString(name) + c_cookie := C.CString(cookie) + + defer C.free(unsafe.Pointer(c_oid)) + defer C.free(unsafe.Pointer(c_name)) + defer C.free(unsafe.Pointer(c_cookie)) + + // 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, + c_oid, + c_name, + c_cookie) + + switch ret { + case 0: + return int(ret), nil + case -C.ENOENT: + return int(ret), nil + default: + return int(ret), RadosError(int(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) { + c_oid := C.CString(oid) + c_name := C.CString(name) + + c_tag := (*C.char)(C.malloc(C.size_t(1024))) + c_clients := (*C.char)(C.malloc(C.size_t(1024))) + c_cookies := (*C.char)(C.malloc(C.size_t(1024))) + c_addrs := (*C.char)(C.malloc(C.size_t(1024))) + + var c_exclusive C.int + c_tag_len := C.size_t(1024) + c_clients_len := C.size_t(1024) + c_cookies_len := C.size_t(1024) + c_addrs_len := C.size_t(1024) + + defer C.free(unsafe.Pointer(c_oid)) + defer C.free(unsafe.Pointer(c_name)) + defer C.free(unsafe.Pointer(c_tag)) + defer C.free(unsafe.Pointer(c_clients)) + defer C.free(unsafe.Pointer(c_cookies)) + defer C.free(unsafe.Pointer(c_addrs)) + + ret := C.rados_list_lockers( + ioctx.ioctx, + c_oid, + c_name, + &c_exclusive, + c_tag, + &c_tag_len, + c_clients, + &c_clients_len, + c_cookies, + &c_cookies_len, + c_addrs, + &c_addrs_len) + + 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, RadosError(int(ret)) + } + return &LockInfo{int(ret), c_exclusive == 1, C.GoString(c_tag), splitCString(c_clients, c_clients_len), splitCString(c_cookies, c_cookies_len), splitCString(c_addrs, c_addrs_len)}, 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) { + c_oid := C.CString(oid) + c_name := C.CString(name) + c_client := C.CString(client) + c_cookie := C.CString(cookie) + + defer C.free(unsafe.Pointer(c_oid)) + defer C.free(unsafe.Pointer(c_name)) + defer C.free(unsafe.Pointer(c_client)) + defer C.free(unsafe.Pointer(c_cookie)) + + // 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, + c_oid, + c_name, + c_client, + c_cookie) + + 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), RadosError(int(ret)) + } +} diff --git a/vendor/github.com/ceph/go-ceph/rados/object_iter.go b/vendor/github.com/ceph/go-ceph/rados/object_iter.go new file mode 100644 index 000000000..c0969eb20 --- /dev/null +++ b/vendor/github.com/ceph/go-ceph/rados/object_iter.go @@ -0,0 +1,92 @@ +package rados + +// #cgo LDFLAGS: -lrados +// #include +// +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, getRadosError(int(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 c_entry *C.char + var c_namespace *C.char + if cerr := C.rados_nobjects_list_next(iter.ctx, &c_entry, nil, &c_namespace); cerr < 0 { + iter.err = getRadosError(int(cerr)) + return false + } + iter.entry = C.GoString(c_entry) + iter.namespace = C.GoString(c_namespace) + 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) +} diff --git a/vendor/github.com/ceph/go-ceph/rados/omap.go b/vendor/github.com/ceph/go-ceph/rados/omap.go new file mode 100644 index 000000000..bea0ddaea --- /dev/null +++ b/vendor/github.com/ceph/go-ceph/rados/omap.go @@ -0,0 +1,228 @@ +package rados + +// #cgo LDFLAGS: -lrados +// #include +// #include +// +import "C" + +import ( + "unsafe" +) + +// SetOmap appends the map `pairs` to the omap `oid` +func (ioctx *IOContext) SetOmap(oid string, pairs map[string][]byte) error { + c_oid := C.CString(oid) + defer C.free(unsafe.Pointer(c_oid)) + + var s C.size_t + var c *C.char + ptrSize := unsafe.Sizeof(c) + + c_keys := C.malloc(C.size_t(len(pairs)) * C.size_t(ptrSize)) + c_values := C.malloc(C.size_t(len(pairs)) * C.size_t(ptrSize)) + c_lengths := C.malloc(C.size_t(len(pairs)) * C.size_t(unsafe.Sizeof(s))) + + defer C.free(unsafe.Pointer(c_keys)) + defer C.free(unsafe.Pointer(c_values)) + defer C.free(unsafe.Pointer(c_lengths)) + + i := 0 + for key, value := range pairs { + // key + c_key_ptr := (**C.char)(unsafe.Pointer(uintptr(c_keys) + uintptr(i)*ptrSize)) + *c_key_ptr = C.CString(key) + defer C.free(unsafe.Pointer(*c_key_ptr)) + + // value and its length + c_value_ptr := (**C.char)(unsafe.Pointer(uintptr(c_values) + uintptr(i)*ptrSize)) + + var c_length C.size_t + if len(value) > 0 { + *c_value_ptr = (*C.char)(unsafe.Pointer(&value[0])) + c_length = C.size_t(len(value)) + } else { + *c_value_ptr = nil + c_length = C.size_t(0) + } + + c_length_ptr := (*C.size_t)(unsafe.Pointer(uintptr(c_lengths) + uintptr(i)*ptrSize)) + *c_length_ptr = c_length + + i++ + } + + op := C.rados_create_write_op() + C.rados_write_op_omap_set( + op, + (**C.char)(c_keys), + (**C.char)(c_values), + (*C.size_t)(c_lengths), + C.size_t(len(pairs))) + + ret := C.rados_write_op_operate(op, ioctx.ioctx, c_oid, nil, 0) + C.rados_release_write_op(op) + + return getRadosError(int(ret)) +} + +// 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 { + c_oid := C.CString(oid) + c_start_after := C.CString(startAfter) + c_filter_prefix := C.CString(filterPrefix) + c_max_return := C.uint64_t(maxReturn) + + defer C.free(unsafe.Pointer(c_oid)) + defer C.free(unsafe.Pointer(c_start_after)) + defer C.free(unsafe.Pointer(c_filter_prefix)) + + op := C.rados_create_read_op() + + var c_iter C.rados_omap_iter_t + var c_prval C.int + C.rados_read_op_omap_get_vals2( + op, + c_start_after, + c_filter_prefix, + c_max_return, + &c_iter, + nil, + &c_prval, + ) + + ret := C.rados_read_op_operate(op, ioctx.ioctx, c_oid, 0) + + if int(ret) != 0 { + return getRadosError(int(ret)) + } else if int(c_prval) != 0 { + return RadosError(int(c_prval)) + } + + for { + var c_key *C.char + var c_val *C.char + var c_len C.size_t + + ret = C.rados_omap_get_next(c_iter, &c_key, &c_val, &c_len) + + if int(ret) != 0 { + return getRadosError(int(ret)) + } + + if c_key == nil { + break + } + + listFn(C.GoString(c_key), C.GoBytes(unsafe.Pointer(c_val), C.int(c_len))) + } + + C.rados_omap_get_end(c_iter) + C.rados_release_read_op(op) + + 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 { + c_oid := C.CString(oid) + defer C.free(unsafe.Pointer(c_oid)) + + var c *C.char + ptrSize := unsafe.Sizeof(c) + + c_keys := C.malloc(C.size_t(len(keys)) * C.size_t(ptrSize)) + defer C.free(unsafe.Pointer(c_keys)) + + i := 0 + for _, key := range keys { + c_key_ptr := (**C.char)(unsafe.Pointer(uintptr(c_keys) + uintptr(i)*ptrSize)) + *c_key_ptr = C.CString(key) + defer C.free(unsafe.Pointer(*c_key_ptr)) + i++ + } + + op := C.rados_create_write_op() + C.rados_write_op_omap_rm_keys( + op, + (**C.char)(c_keys), + C.size_t(len(keys))) + + ret := C.rados_write_op_operate(op, ioctx.ioctx, c_oid, nil, 0) + C.rados_release_write_op(op) + + return getRadosError(int(ret)) +} + +// CleanOmap clears the omap `oid` +func (ioctx *IOContext) CleanOmap(oid string) error { + c_oid := C.CString(oid) + defer C.free(unsafe.Pointer(c_oid)) + + op := C.rados_create_write_op() + C.rados_write_op_omap_clear(op) + + ret := C.rados_write_op_operate(op, ioctx.ioctx, c_oid, nil, 0) + C.rados_release_write_op(op) + + return getRadosError(int(ret)) +} diff --git a/vendor/github.com/ceph/go-ceph/rados/rados.go b/vendor/github.com/ceph/go-ceph/rados/rados.go new file mode 100644 index 000000000..2e4a48756 --- /dev/null +++ b/vendor/github.com/ceph/go-ceph/rados/rados.go @@ -0,0 +1,137 @@ +package rados + +// #cgo LDFLAGS: -lrados +// #include +// #include +// #include +import "C" + +import ( + "fmt" + "runtime" + "unsafe" + + "github.com/ceph/go-ceph/errutil" +) + +// RadosError represents an error condition returned from the Ceph RADOS APIs. +type RadosError int + +// Error returns the error string for the RadosError type. +func (e RadosError) Error() string { + errno, s := errutil.FormatErrno(int(e)) + if s == "" { + return fmt.Sprintf("rados: ret=%d", errno) + } + return fmt.Sprintf("rados: ret=%d, %s", errno, s) +} + +const ( + // AllNamespaces is used to reset a selected namespace to all + // namespaces. See the IOContext SetNamespace function. + AllNamespaces = C.LIBRADOS_ALL_NSPACES + + // ErrNotFound indicates a missing resource. + ErrNotFound = RadosError(-C.ENOENT) + // ErrPermissionDenied indicates a permissions issue. + ErrPermissionDenied = RadosError(-C.EPERM) + // ErrObjectExists indicates that an exclusive object creation failed. + ErrObjectExists = RadosError(-C.EEXIST) + + // 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 + // RadosErrorNotFound indicates a missing resource. + // + // Deprecated: use ErrNotFound instead + RadosErrorNotFound = ErrNotFound + // RadosErrorPermissionDenied indicates a permissions issue. + // + // Deprecated: use ErrPermissionDenied instead + RadosErrorPermissionDenied = ErrPermissionDenied +) + +func getRadosError(err int) error { + if err == 0 { + return nil + } + return RadosError(err) +} + +// Version returns the major, minor, and patch components of the version of +// the RADOS library linked against. +func Version() (int, int, int) { + var c_major, c_minor, c_patch C.int + C.rados_version(&c_major, &c_minor, &c_patch) + return int(c_major), int(c_minor), int(c_patch) +} + +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, RadosError(int(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. +// It returns the connection and an error, if any. +func NewConnWithUser(user string) (*Conn, error) { + c_user := C.CString(user) + defer C.free(unsafe.Pointer(c_user)) + return newConn(c_user) +} + +// NewConnWithClusterAndUser creates a new connection object for a specific cluster and username. +// It returns the connection and an error, if any. +func NewConnWithClusterAndUser(clusterName string, userName string) (*Conn, error) { + c_cluster_name := C.CString(clusterName) + defer C.free(unsafe.Pointer(c_cluster_name)) + + c_name := C.CString(userName) + defer C.free(unsafe.Pointer(c_name)) + + conn := makeConn() + ret := C.rados_create2(&conn.cluster, c_cluster_name, c_name, 0) + if ret != 0 { + return nil, RadosError(int(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 { + C.rados_shutdown(conn.cluster) + // prevent calling rados_shutdown() more than once + conn.cluster = nil + } +} diff --git a/vendor/github.com/ceph/go-ceph/rbd/doc.go b/vendor/github.com/ceph/go-ceph/rbd/doc.go new file mode 100644 index 000000000..76643435a --- /dev/null +++ b/vendor/github.com/ceph/go-ceph/rbd/doc.go @@ -0,0 +1,4 @@ +/* +Package rbd contains a set of wrappers around Ceph's librbd API. +*/ +package rbd diff --git a/vendor/github.com/ceph/go-ceph/rbd/options.go b/vendor/github.com/ceph/go-ceph/rbd/options.go new file mode 100644 index 000000000..46ece701b --- /dev/null +++ b/vendor/github.com/ceph/go-ceph/rbd/options.go @@ -0,0 +1,169 @@ +package rbd + +// #cgo LDFLAGS: -lrbd +// #include +// #include +import "C" + +import ( + "fmt" + "unsafe" +) + +const ( + // RBD image options. + RbdImageOptionFormat = C.RBD_IMAGE_OPTION_FORMAT + RbdImageOptionFeatures = C.RBD_IMAGE_OPTION_FEATURES + RbdImageOptionOrder = C.RBD_IMAGE_OPTION_ORDER + RbdImageOptionStripeUnit = C.RBD_IMAGE_OPTION_STRIPE_UNIT + RbdImageOptionStripeCount = C.RBD_IMAGE_OPTION_STRIPE_COUNT + RbdImageOptionJournalOrder = C.RBD_IMAGE_OPTION_JOURNAL_ORDER + RbdImageOptionJournalSplayWidth = C.RBD_IMAGE_OPTION_JOURNAL_SPLAY_WIDTH + RbdImageOptionJournalPool = C.RBD_IMAGE_OPTION_JOURNAL_POOL + RbdImageOptionFeaturesSet = C.RBD_IMAGE_OPTION_FEATURES_SET + RbdImageOptionFeaturesClear = C.RBD_IMAGE_OPTION_FEATURES_CLEAR + RbdImageOptionDataPool = C.RBD_IMAGE_OPTION_DATA_POOL + // introduced with Ceph Mimic + //RbdImageOptionFlatten = C.RBD_IMAGE_OPTION_FLATTEN +) + +type RbdImageOptions struct { + options C.rbd_image_options_t +} + +type RbdImageOption C.int + +// NewRbdImageOptions creates a new RbdImageOptions struct. Call +// RbdImageOptions.Destroy() to free the resources. +// +// Implements: +// void rbd_image_options_create(rbd_image_options_t* opts) +func NewRbdImageOptions() *RbdImageOptions { + rio := &RbdImageOptions{} + C.rbd_image_options_create(&rio.options) + return rio +} + +// Destroy a RbdImageOptions struct and free the associated resources. +// +// Implements: +// void rbd_image_options_destroy(rbd_image_options_t opts); +func (rio *RbdImageOptions) Destroy() { + C.rbd_image_options_destroy(rio.options) +} + +// SetString sets the value of the RbdImageOption to the given string. +// +// Implements: +// int rbd_image_options_set_string(rbd_image_options_t opts, int optname, +// const char* optval); +func (rio *RbdImageOptions) SetString(option RbdImageOption, value string) error { + c_value := C.CString(value) + defer C.free(unsafe.Pointer(c_value)) + + ret := C.rbd_image_options_set_string(rio.options, C.int(option), c_value) + if ret != 0 { + return fmt.Errorf("%v, could not set option %v to \"%v\"", + getError(ret), option, value) + } + + return nil +} + +// GetString returns the string value of the RbdImageOption. +// +// Implements: +// int rbd_image_options_get_string(rbd_image_options_t opts, int optname, +// char* optval, size_t maxlen); +func (rio *RbdImageOptions) GetString(option RbdImageOption) (string, error) { + value := make([]byte, 4096) + + ret := C.rbd_image_options_get_string(rio.options, C.int(option), + (*C.char)(unsafe.Pointer(&value[0])), + C.size_t(len(value))) + if ret != 0 { + return "", fmt.Errorf("%v, could not get option %v", getError(ret), option) + } + + return C.GoString((*C.char)(unsafe.Pointer(&value[0]))), nil +} + +// SetUint64 sets the value of the RbdImageOption to the given uint64. +// +// Implements: +// int rbd_image_options_set_uint64(rbd_image_options_t opts, int optname, +// const uint64_t optval); +func (rio *RbdImageOptions) SetUint64(option RbdImageOption, value uint64) error { + c_value := C.uint64_t(value) + + ret := C.rbd_image_options_set_uint64(rio.options, C.int(option), c_value) + if ret != 0 { + return fmt.Errorf("%v, could not set option %v to \"%v\"", + getError(ret), option, value) + } + + return nil +} + +// GetUint64 returns the uint64 value of the RbdImageOption. +// +// Implements: +// int rbd_image_options_get_uint64(rbd_image_options_t opts, int optname, +// uint64_t* optval); +func (rio *RbdImageOptions) GetUint64(option RbdImageOption) (uint64, error) { + var c_value C.uint64_t + + ret := C.rbd_image_options_get_uint64(rio.options, C.int(option), &c_value) + if ret != 0 { + return 0, fmt.Errorf("%v, could not get option %v", getError(ret), option) + } + + return uint64(c_value), nil +} + +// IsSet returns a true if the RbdImageOption is set, false otherwise. +// +// Implements: +// int rbd_image_options_is_set(rbd_image_options_t opts, int optname, +// bool* is_set); +func (rio *RbdImageOptions) IsSet(option RbdImageOption) (bool, error) { + var c_set C.bool + + ret := C.rbd_image_options_is_set(rio.options, C.int(option), &c_set) + if ret != 0 { + return false, fmt.Errorf("%v, could not check option %v", getError(ret), option) + } + + return bool(c_set), nil +} + +// Unset a given RbdImageOption. +// +// Implements: +// int rbd_image_options_unset(rbd_image_options_t opts, int optname) +func (rio *RbdImageOptions) Unset(option RbdImageOption) error { + ret := C.rbd_image_options_unset(rio.options, C.int(option)) + if ret != 0 { + return fmt.Errorf("%v, could not unset option %v", getError(ret), option) + } + + return nil +} + +// Clear all options in the RbdImageOptions. +// +// Implements: +// void rbd_image_options_clear(rbd_image_options_t opts) +func (rio *RbdImageOptions) Clear() { + C.rbd_image_options_clear(rio.options) +} + +// IsEmpty returns true if there are no options set in the RbdImageOptions, +// false otherwise. +// +// Implements: +// int rbd_image_options_is_empty(rbd_image_options_t opts) +func (rio *RbdImageOptions) IsEmpty() bool { + ret := C.rbd_image_options_is_empty(rio.options) + return ret != 0 +} diff --git a/vendor/github.com/ceph/go-ceph/rbd/rbd.go b/vendor/github.com/ceph/go-ceph/rbd/rbd.go new file mode 100644 index 000000000..ad6bf0755 --- /dev/null +++ b/vendor/github.com/ceph/go-ceph/rbd/rbd.go @@ -0,0 +1,1403 @@ +package rbd + +// #cgo LDFLAGS: -lrbd +// /* force XSI-complaint strerror_r() */ +// #define _POSIX_C_SOURCE 200112L +// #undef _GNU_SOURCE +// #include +// #include +// #include +// #include +// #include +import "C" + +import ( + "bytes" + "errors" + "fmt" + "io" + "time" + "unsafe" + + "github.com/ceph/go-ceph/errutil" + "github.com/ceph/go-ceph/rados" +) + +const ( + // RBD features. + RbdFeatureLayering = uint64(C.RBD_FEATURE_LAYERING) + RbdFeatureStripingV2 = uint64(C.RBD_FEATURE_STRIPINGV2) + RbdFeatureExclusiveLock = uint64(C.RBD_FEATURE_EXCLUSIVE_LOCK) + RbdFeatureObjectMap = uint64(C.RBD_FEATURE_OBJECT_MAP) + RbdFeatureFastDiff = uint64(C.RBD_FEATURE_FAST_DIFF) + RbdFeatureDeepFlatten = uint64(C.RBD_FEATURE_DEEP_FLATTEN) + RbdFeatureJournaling = uint64(C.RBD_FEATURE_JOURNALING) + RbdFeatureDataPool = uint64(C.RBD_FEATURE_DATA_POOL) + + RbdFeaturesDefault = uint64(C.RBD_FEATURES_DEFAULT) + + // Features that make an image inaccessible for read or write by clients that don't understand + // them. + RbdFeaturesIncompatible = uint64(C.RBD_FEATURES_INCOMPATIBLE) + + // Features that make an image unwritable by clients that don't understand them. + RbdFeaturesRwIncompatible = uint64(C.RBD_FEATURES_RW_INCOMPATIBLE) + + // Features that may be dynamically enabled or disabled. + RbdFeaturesMutable = uint64(C.RBD_FEATURES_MUTABLE) + + // Features that only work when used with a single client using the image for writes. + RbdFeaturesSingleClient = uint64(C.RBD_FEATURES_SINGLE_CLIENT) + + // Image.Seek() constants + SeekSet = int(C.SEEK_SET) + SeekCur = int(C.SEEK_CUR) + SeekEnd = int(C.SEEK_END) +) + +// bits for Image.validate() and Snapshot.validate() +const ( + imageNeedsName uint32 = 1 << iota + imageNeedsIOContext + imageIsOpen + snapshotNeedsName + + // NoSnapshot indicates that no snapshot name is in use (see OpenImage) + NoSnapshot = "" +) + +// +type RBDError int + +var ( + ErrNoIOContext = errors.New("RBD image does not have an IOContext") + ErrNoName = errors.New("RBD image does not have a name") + ErrSnapshotNoName = errors.New("RBD snapshot does not have a name") + ErrImageNotOpen = errors.New("RBD image not open") + ErrNotFound = errors.New("RBD image not found") + + // retained for compatibility with old versions + RbdErrorImageNotOpen = ErrImageNotOpen + RbdErrorNotFound = ErrNotFound +) + +// +type ImageInfo struct { + Size uint64 + Obj_size uint64 + Num_objs uint64 + Order int + Block_name_prefix string + Parent_pool int64 + Parent_name string +} + +// +type SnapInfo struct { + Id uint64 + Size uint64 + Name string +} + +// +type Locker struct { + Client string + Cookie string + Addr string +} + +// +type Image struct { + io.Reader + io.Writer + io.Seeker + io.ReaderAt + io.WriterAt + name string + offset int64 + ioctx *rados.IOContext + image C.rbd_image_t +} + +// +type Snapshot struct { + image *Image + name string +} + +// TrashInfo contains information about trashed RBDs. +type TrashInfo struct { + Id string // Id string, required to remove / restore trashed RBDs. + Name string // Original name of trashed RBD. + DeletionTime time.Time // Date / time at which the RBD was moved to the trash. + DefermentEndTime time.Time // Date / time after which the trashed RBD may be permanently deleted. +} + +// +func split(buf []byte) (values []string) { + tmp := bytes.Split(buf[:len(buf)-1], []byte{0}) + for _, s := range tmp { + if len(s) > 0 { + go_s := C.GoString((*C.char)(unsafe.Pointer(&s[0]))) + values = append(values, go_s) + } + } + return values +} + +// test if a bit is set in the given value +func hasBit(value, bit uint32) bool { + return (value & bit) == bit +} + +// validate the attributes listed in the req bitmask, and return an error in +// case the attribute is not set +func (image *Image) validate(req uint32) error { + if hasBit(req, imageNeedsName) && image.name == "" { + return ErrNoName + } else if hasBit(req, imageNeedsIOContext) && image.ioctx == nil { + return ErrNoIOContext + } else if hasBit(req, imageIsOpen) && image.image == nil { + return ErrImageNotOpen + } + + return nil +} + +// validate the attributes listed in the req bitmask, and return an error in +// case the attribute is not set +// Calls snapshot.image.validate(req) to validate the image attributes. +func (snapshot *Snapshot) validate(req uint32) error { + if hasBit(req, snapshotNeedsName) && snapshot.name == "" { + return ErrSnapshotNoName + } else if snapshot.image != nil { + return snapshot.image.validate(req) + } + + return nil +} + +func (e RBDError) Error() string { + errno, s := errutil.FormatErrno(int(e)) + if s == "" { + return fmt.Sprintf("rbd: ret=%d", errno) + } + return fmt.Sprintf("rbd: ret=%d, %s", errno, s) +} + +func getError(err C.int) error { + if err != 0 { + if err == -C.ENOENT { + return ErrNotFound + } + return RBDError(err) + } else { + return nil + } +} + +// Version returns the major, minor, and patch level of the librbd library. +func Version() (int, int, int) { + var c_major, c_minor, c_patch C.int + C.rbd_version(&c_major, &c_minor, &c_patch) + return int(c_major), int(c_minor), int(c_patch) +} + +// GetImage gets a reference to a previously created rbd image. +func GetImage(ioctx *rados.IOContext, name string) *Image { + return &Image{ + ioctx: ioctx, + name: name, + } +} + +// Create a new rbd image. +// +// Implements: +// int rbd_create(rados_ioctx_t io, const char *name, uint64_t size, int *order); +// +// Also implements (for backward compatibility): +// int rbd_create2(rados_ioctx_t io, const char *name, uint64_t size, +// uint64_t features, int *order); +// int rbd_create3(rados_ioctx_t io, const char *name, uint64_t size, +// uint64_t features, int *order, +// uint64_t stripe_unit, uint64_t stripe_count); +func Create(ioctx *rados.IOContext, name string, size uint64, order int, + args ...uint64) (image *Image, err error) { + var ret C.int + + switch len(args) { + case 3: + return Create3(ioctx, name, size, args[0], order, args[1], + args[2]) + case 1: + return Create2(ioctx, name, size, args[0], order) + case 0: + c_order := C.int(order) + c_name := C.CString(name) + + defer C.free(unsafe.Pointer(c_name)) + + ret = C.rbd_create(C.rados_ioctx_t(ioctx.Pointer()), + c_name, C.uint64_t(size), &c_order) + default: + return nil, errors.New("Wrong number of argument") + } + + if ret < 0 { + return nil, RBDError(ret) + } + + return &Image{ + ioctx: ioctx, + name: name, + }, nil +} + +// Create2 creates a new rbd image using provided features. +// +// Implements: +// int rbd_create2(rados_ioctx_t io, const char *name, uint64_t size, +// uint64_t features, int *order); +func Create2(ioctx *rados.IOContext, name string, size uint64, features uint64, + order int) (image *Image, err error) { + var ret C.int + + c_order := C.int(order) + c_name := C.CString(name) + + defer C.free(unsafe.Pointer(c_name)) + + ret = C.rbd_create2(C.rados_ioctx_t(ioctx.Pointer()), c_name, + C.uint64_t(size), C.uint64_t(features), &c_order) + if ret < 0 { + return nil, RBDError(ret) + } + + return &Image{ + ioctx: ioctx, + name: name, + }, nil +} + +// Create3 creates a new rbd image using provided features and stripe +// parameters. +// +// Implements: +// int rbd_create3(rados_ioctx_t io, const char *name, uint64_t size, +// uint64_t features, int *order, +// uint64_t stripe_unit, uint64_t stripe_count); +func Create3(ioctx *rados.IOContext, name string, size uint64, features uint64, + order int, stripe_unit uint64, stripe_count uint64) (image *Image, err error) { + var ret C.int + + c_order := C.int(order) + c_name := C.CString(name) + + defer C.free(unsafe.Pointer(c_name)) + + ret = C.rbd_create3(C.rados_ioctx_t(ioctx.Pointer()), c_name, + C.uint64_t(size), C.uint64_t(features), &c_order, + C.uint64_t(stripe_unit), C.uint64_t(stripe_count)) + if ret < 0 { + return nil, RBDError(ret) + } + + return &Image{ + ioctx: ioctx, + name: name, + }, nil +} + +// Clone a new rbd image from a snapshot. +// +// Implements: +// int rbd_clone(rados_ioctx_t p_ioctx, const char *p_name, +// const char *p_snapname, rados_ioctx_t c_ioctx, +// const char *c_name, uint64_t features, int *c_order); +func (image *Image) Clone(snapname string, c_ioctx *rados.IOContext, c_name string, features uint64, order int) (*Image, error) { + if err := image.validate(imageNeedsIOContext); err != nil { + return nil, err + } + + c_order := C.int(order) + c_p_name := C.CString(image.name) + c_p_snapname := C.CString(snapname) + c_c_name := C.CString(c_name) + + defer C.free(unsafe.Pointer(c_p_name)) + defer C.free(unsafe.Pointer(c_p_snapname)) + defer C.free(unsafe.Pointer(c_c_name)) + + ret := C.rbd_clone(C.rados_ioctx_t(image.ioctx.Pointer()), + c_p_name, c_p_snapname, + C.rados_ioctx_t(c_ioctx.Pointer()), + c_c_name, C.uint64_t(features), &c_order) + if ret < 0 { + return nil, RBDError(ret) + } + + return &Image{ + ioctx: c_ioctx, + name: c_name, + }, nil +} + +// Remove the specified rbd image. +// +// Implements: +// int rbd_remove(rados_ioctx_t io, const char *name); +func (image *Image) Remove() error { + if err := image.validate(imageNeedsIOContext | imageNeedsName); err != nil { + return err + } + return RemoveImage(image.ioctx, image.name) +} + +// Trash will move an image into the RBD trash, where it will be protected (i.e., salvageable) for +// at least the specified delay. +func (image *Image) Trash(delay time.Duration) error { + if err := image.validate(imageNeedsIOContext | imageNeedsName); err != nil { + return err + } + + c_name := C.CString(image.name) + defer C.free(unsafe.Pointer(c_name)) + + return getError(C.rbd_trash_move(C.rados_ioctx_t(image.ioctx.Pointer()), c_name, + C.uint64_t(delay.Seconds()))) +} + +// Rename an rbd image. +// +// Implements: +// int rbd_rename(rados_ioctx_t src_io_ctx, const char *srcname, const char *destname); +func (image *Image) Rename(destname string) error { + if err := image.validate(imageNeedsIOContext | imageNeedsName); err != nil { + return err + } + + c_srcname := C.CString(image.name) + c_destname := C.CString(destname) + + defer C.free(unsafe.Pointer(c_srcname)) + defer C.free(unsafe.Pointer(c_destname)) + + err := RBDError(C.rbd_rename(C.rados_ioctx_t(image.ioctx.Pointer()), + c_srcname, c_destname)) + if err == 0 { + image.name = destname + return nil + } + return err +} + +// Open the rbd image. +// +// Deprecated: The Open function was provided in earlier versions of the API +// and now exists to support older code. The use of OpenImage and +// OpenImageReadOnly is preferred. +func (image *Image) Open(args ...interface{}) error { + if err := image.validate(imageNeedsIOContext | imageNeedsName); err != nil { + return err + } + + var ( + snapName string + readOnly bool + ) + for _, arg := range args { + switch t := arg.(type) { + case string: + snapName = t + case bool: + readOnly = t + default: + return errors.New("Unexpected argument") + } + } + + var ( + tmp *Image + err error + ) + if readOnly { + tmp, err = OpenImageReadOnly(image.ioctx, image.name, snapName) + } else { + tmp, err = OpenImage(image.ioctx, image.name, snapName) + } + if err != nil { + return err + } + + image.image = tmp.image + return nil +} + +// Close an open rbd image. +// +// Implements: +// int rbd_close(rbd_image_t image); +func (image *Image) Close() error { + if err := image.validate(imageIsOpen); err != nil { + return err + } + + if ret := C.rbd_close(image.image); ret != 0 { + return RBDError(ret) + } + + image.image = nil + return nil +} + +// Resize an rbd image. +// +// Implements: +// int rbd_resize(rbd_image_t image, uint64_t size); +func (image *Image) Resize(size uint64) error { + if err := image.validate(imageIsOpen); err != nil { + return err + } + + return getError(C.rbd_resize(image.image, C.uint64_t(size))) +} + +// Stat an rbd image. +// +// Implements: +// int rbd_stat(rbd_image_t image, rbd_image_info_t *info, size_t infosize); +func (image *Image) Stat() (info *ImageInfo, err error) { + if err := image.validate(imageIsOpen); err != nil { + return nil, err + } + + var c_stat C.rbd_image_info_t + + if ret := C.rbd_stat(image.image, &c_stat, C.size_t(unsafe.Sizeof(info))); ret < 0 { + return info, RBDError(ret) + } + + return &ImageInfo{ + Size: uint64(c_stat.size), + Obj_size: uint64(c_stat.obj_size), + Num_objs: uint64(c_stat.num_objs), + Order: int(c_stat.order), + Block_name_prefix: C.GoString((*C.char)(&c_stat.block_name_prefix[0])), + Parent_pool: int64(c_stat.parent_pool), + Parent_name: C.GoString((*C.char)(&c_stat.parent_name[0]))}, nil +} + +// IsOldFormat returns true if the rbd image uses the old format. +// +// Implements: +// int rbd_get_old_format(rbd_image_t image, uint8_t *old); +func (image *Image) IsOldFormat() (old_format bool, err error) { + if err := image.validate(imageIsOpen); err != nil { + return false, err + } + + var c_old_format C.uint8_t + ret := C.rbd_get_old_format(image.image, + &c_old_format) + if ret < 0 { + return false, RBDError(ret) + } + + return c_old_format != 0, nil +} + +// GetSize returns the size of the rbd image. +// +// Implements: +// int rbd_size(rbd_image_t image, uint64_t *size); +func (image *Image) GetSize() (size uint64, err error) { + if err := image.validate(imageIsOpen); err != nil { + return 0, err + } + + if ret := C.rbd_get_size(image.image, (*C.uint64_t)(&size)); ret < 0 { + return 0, RBDError(ret) + } + + return size, nil +} + +// GetFeatures returns the features bitmask for the rbd image. +// +// Implements: +// int rbd_get_features(rbd_image_t image, uint64_t *features); +func (image *Image) GetFeatures() (features uint64, err error) { + if err := image.validate(imageIsOpen); err != nil { + return 0, err + } + + if ret := C.rbd_get_features(image.image, (*C.uint64_t)(&features)); ret < 0 { + return 0, RBDError(ret) + } + + return features, nil +} + +// GetStripeUnit returns the stripe-unit value for the rbd image. +// +// Implements: +// int rbd_get_stripe_unit(rbd_image_t image, uint64_t *stripe_unit); +func (image *Image) GetStripeUnit() (stripe_unit uint64, err error) { + if err := image.validate(imageIsOpen); err != nil { + return 0, err + } + + if ret := C.rbd_get_stripe_unit(image.image, (*C.uint64_t)(&stripe_unit)); ret < 0 { + return 0, RBDError(ret) + } + + return stripe_unit, nil +} + +// GetStripeCount returns the stripe-count value for the rbd image. +// +// Implements: +// int rbd_get_stripe_count(rbd_image_t image, uint64_t *stripe_count); +func (image *Image) GetStripeCount() (stripe_count uint64, err error) { + if err := image.validate(imageIsOpen); err != nil { + return 0, err + } + + if ret := C.rbd_get_stripe_count(image.image, (*C.uint64_t)(&stripe_count)); ret < 0 { + return 0, RBDError(ret) + } + + return stripe_count, nil +} + +// GetOverlap returns the overlapping bytes between the rbd image and its +// parent. +// +// Implements: +// int rbd_get_overlap(rbd_image_t image, uint64_t *overlap); +func (image *Image) GetOverlap() (overlap uint64, err error) { + if err := image.validate(imageIsOpen); err != nil { + return 0, err + } + + if ret := C.rbd_get_overlap(image.image, (*C.uint64_t)(&overlap)); ret < 0 { + return overlap, RBDError(ret) + } + + return overlap, nil +} + +// Copy one rbd image to another. +// +// Implements: +// int rbd_copy(rbd_image_t image, rados_ioctx_t dest_io_ctx, const char *destname); +func (image *Image) Copy(ioctx *rados.IOContext, destname string) error { + if err := image.validate(imageIsOpen); err != nil { + return err + } else if ioctx == nil { + return ErrNoIOContext + } else if len(destname) == 0 { + return ErrNoName + } + + c_destname := C.CString(destname) + defer C.free(unsafe.Pointer(c_destname)) + + return getError(C.rbd_copy(image.image, + C.rados_ioctx_t(ioctx.Pointer()), c_destname)) +} + +// Copy one rbd image to another, using an image handle. +// +// Implements: +// int rbd_copy2(rbd_image_t src, rbd_image_t dest); +func (image *Image) Copy2(dest *Image) error { + if err := image.validate(imageIsOpen); err != nil { + return err + } else if err := dest.validate(imageIsOpen); err != nil { + return err + } + + return getError(C.rbd_copy2(image.image, dest.image)) +} + +// Flatten removes snapshot references from the image. +// +// Implements: +// int rbd_flatten(rbd_image_t image); +func (image *Image) Flatten() error { + if err := image.validate(imageIsOpen); err != nil { + return err + } + + return getError(C.rbd_flatten(image.image)) +} + +// ListLockers returns a list of clients that have locks on the image. +// +// Impelemnts: +// ssize_t rbd_list_lockers(rbd_image_t image, int *exclusive, +// char *tag, size_t *tag_len, +// char *clients, size_t *clients_len, +// char *cookies, size_t *cookies_len, +// char *addrs, size_t *addrs_len); +func (image *Image) ListLockers() (tag string, lockers []Locker, err error) { + if err := image.validate(imageIsOpen); err != nil { + return "", nil, err + } + + var c_exclusive C.int + var c_tag_len, c_clients_len, c_cookies_len, c_addrs_len C.size_t + var c_locker_cnt C.ssize_t + + C.rbd_list_lockers(image.image, &c_exclusive, + nil, (*C.size_t)(&c_tag_len), + nil, (*C.size_t)(&c_clients_len), + nil, (*C.size_t)(&c_cookies_len), + nil, (*C.size_t)(&c_addrs_len)) + + // no locker held on rbd image when either c_clients_len, + // c_cookies_len or c_addrs_len is *0*, so just quickly returned + if int(c_clients_len) == 0 || int(c_cookies_len) == 0 || + int(c_addrs_len) == 0 { + lockers = make([]Locker, 0) + return "", lockers, nil + } + + tag_buf := make([]byte, c_tag_len) + clients_buf := make([]byte, c_clients_len) + cookies_buf := make([]byte, c_cookies_len) + addrs_buf := make([]byte, c_addrs_len) + + c_locker_cnt = C.rbd_list_lockers(image.image, &c_exclusive, + (*C.char)(unsafe.Pointer(&tag_buf[0])), (*C.size_t)(&c_tag_len), + (*C.char)(unsafe.Pointer(&clients_buf[0])), (*C.size_t)(&c_clients_len), + (*C.char)(unsafe.Pointer(&cookies_buf[0])), (*C.size_t)(&c_cookies_len), + (*C.char)(unsafe.Pointer(&addrs_buf[0])), (*C.size_t)(&c_addrs_len)) + + // rbd_list_lockers returns negative value for errors + // and *0* means no locker held on rbd image. + // but *0* is unexpected here because first rbd_list_lockers already + // dealt with no locker case + if int(c_locker_cnt) <= 0 { + return "", nil, RBDError(c_locker_cnt) + } + + clients := split(clients_buf) + cookies := split(cookies_buf) + addrs := split(addrs_buf) + + lockers = make([]Locker, c_locker_cnt) + for i := 0; i < int(c_locker_cnt); i++ { + lockers[i] = Locker{Client: clients[i], + Cookie: cookies[i], + Addr: addrs[i]} + } + + return string(tag_buf), lockers, nil +} + +// LockExclusive acquires an exclusive lock on the rbd image. +// +// Implements: +// int rbd_lock_exclusive(rbd_image_t image, const char *cookie); +func (image *Image) LockExclusive(cookie string) error { + if err := image.validate(imageIsOpen); err != nil { + return err + } + + c_cookie := C.CString(cookie) + defer C.free(unsafe.Pointer(c_cookie)) + + return getError(C.rbd_lock_exclusive(image.image, c_cookie)) +} + +// LockShared acquires a shared lock on the rbd image. +// +// Implements: +// int rbd_lock_shared(rbd_image_t image, const char *cookie, const char *tag); +func (image *Image) LockShared(cookie string, tag string) error { + if err := image.validate(imageIsOpen); err != nil { + return err + } + + c_cookie := C.CString(cookie) + c_tag := C.CString(tag) + defer C.free(unsafe.Pointer(c_cookie)) + defer C.free(unsafe.Pointer(c_tag)) + + return getError(C.rbd_lock_shared(image.image, c_cookie, c_tag)) +} + +// Unlock releases a lock on the image. +// +// Implements: +// int rbd_lock_shared(rbd_image_t image, const char *cookie, const char *tag); +func (image *Image) Unlock(cookie string) error { + if err := image.validate(imageIsOpen); err != nil { + return err + } + + c_cookie := C.CString(cookie) + defer C.free(unsafe.Pointer(c_cookie)) + + return getError(C.rbd_unlock(image.image, c_cookie)) +} + +// BreakLock forces the release of a lock held by another client. +// +// Implements: +// int rbd_break_lock(rbd_image_t image, const char *client, const char *cookie); +func (image *Image) BreakLock(client string, cookie string) error { + if err := image.validate(imageIsOpen); err != nil { + return err + } + + c_client := C.CString(client) + c_cookie := C.CString(cookie) + defer C.free(unsafe.Pointer(c_client)) + defer C.free(unsafe.Pointer(c_cookie)) + + return getError(C.rbd_break_lock(image.image, c_client, c_cookie)) +} + +// ssize_t rbd_read(rbd_image_t image, uint64_t ofs, size_t len, char *buf); +// TODO: int64_t rbd_read_iterate(rbd_image_t image, uint64_t ofs, size_t len, +// int (*cb)(uint64_t, size_t, const char *, void *), void *arg); +// TODO: int rbd_read_iterate2(rbd_image_t image, uint64_t ofs, uint64_t len, +// int (*cb)(uint64_t, size_t, const char *, void *), void *arg); +// TODO: int rbd_diff_iterate(rbd_image_t image, +// const char *fromsnapname, +// uint64_t ofs, uint64_t len, +// int (*cb)(uint64_t, size_t, int, void *), void *arg); +func (image *Image) Read(data []byte) (int, error) { + if err := image.validate(imageIsOpen); err != nil { + return 0, err + } + + if len(data) == 0 { + return 0, nil + } + + ret := int(C.rbd_read( + image.image, + (C.uint64_t)(image.offset), + (C.size_t)(len(data)), + (*C.char)(unsafe.Pointer(&data[0])))) + + if ret < 0 { + return 0, RBDError(ret) + } + + image.offset += int64(ret) + if ret < len(data) { + return ret, io.EOF + } + + return ret, nil +} + +// ssize_t rbd_write(rbd_image_t image, uint64_t ofs, size_t len, const char *buf); +func (image *Image) Write(data []byte) (n int, err error) { + if err := image.validate(imageIsOpen); err != nil { + return 0, err + } + + ret := int(C.rbd_write(image.image, C.uint64_t(image.offset), + C.size_t(len(data)), (*C.char)(unsafe.Pointer(&data[0])))) + + if ret >= 0 { + image.offset += int64(ret) + } + + if ret != len(data) { + err = RBDError(-C.EPERM) + } + + return ret, err +} + +func (image *Image) Seek(offset int64, whence int) (int64, error) { + switch whence { + case SeekSet: + image.offset = offset + case SeekCur: + image.offset += offset + case SeekEnd: + stats, err := image.Stat() + if err != nil { + return 0, err + } + image.offset = int64(stats.Size) - offset + default: + return 0, errors.New("Wrong value for whence") + } + return image.offset, nil +} + +// int rbd_discard(rbd_image_t image, uint64_t ofs, uint64_t len); +func (image *Image) Discard(ofs uint64, length uint64) (int, error) { + if err := image.validate(imageIsOpen); err != nil { + return 0, err + } + + ret := C.rbd_discard(image.image, C.uint64_t(ofs), C.uint64_t(length)) + if ret < 0 { + return 0, RBDError(ret) + } + + return int(ret), nil +} + +func (image *Image) ReadAt(data []byte, off int64) (int, error) { + if err := image.validate(imageIsOpen); err != nil { + return 0, err + } + + if len(data) == 0 { + return 0, nil + } + + ret := int(C.rbd_read( + image.image, + (C.uint64_t)(off), + (C.size_t)(len(data)), + (*C.char)(unsafe.Pointer(&data[0])))) + + if ret < 0 { + return 0, RBDError(ret) + } + + if ret < len(data) { + return ret, io.EOF + } + + return ret, nil +} + +func (image *Image) WriteAt(data []byte, off int64) (n int, err error) { + if err := image.validate(imageIsOpen); err != nil { + return 0, err + } + + if len(data) == 0 { + return 0, nil + } + + ret := int(C.rbd_write(image.image, C.uint64_t(off), + C.size_t(len(data)), (*C.char)(unsafe.Pointer(&data[0])))) + + if ret != len(data) { + err = RBDError(-C.EPERM) + } + + return ret, err +} + +// int rbd_flush(rbd_image_t image); +func (image *Image) Flush() error { + if err := image.validate(imageIsOpen); err != nil { + return err + } + + return getError(C.rbd_flush(image.image)) +} + +// int rbd_snap_list(rbd_image_t image, rbd_snap_info_t *snaps, int *max_snaps); +// void rbd_snap_list_end(rbd_snap_info_t *snaps); +func (image *Image) GetSnapshotNames() (snaps []SnapInfo, err error) { + if err := image.validate(imageIsOpen); err != nil { + return nil, err + } + + var c_max_snaps C.int + + ret := C.rbd_snap_list(image.image, nil, &c_max_snaps) + + c_snaps := make([]C.rbd_snap_info_t, c_max_snaps) + snaps = make([]SnapInfo, c_max_snaps) + + ret = C.rbd_snap_list(image.image, + &c_snaps[0], &c_max_snaps) + if ret < 0 { + return nil, RBDError(ret) + } + + for i, s := range c_snaps { + snaps[i] = SnapInfo{Id: uint64(s.id), + Size: uint64(s.size), + Name: C.GoString(s.name)} + } + + C.rbd_snap_list_end(&c_snaps[0]) + return snaps[:len(snaps)-1], nil +} + +// int rbd_snap_create(rbd_image_t image, const char *snapname); +func (image *Image) CreateSnapshot(snapname string) (*Snapshot, error) { + if err := image.validate(imageIsOpen); err != nil { + return nil, err + } + + c_snapname := C.CString(snapname) + defer C.free(unsafe.Pointer(c_snapname)) + + ret := C.rbd_snap_create(image.image, c_snapname) + if ret < 0 { + return nil, RBDError(ret) + } + + return &Snapshot{ + image: image, + name: snapname, + }, nil +} + +// +func (image *Image) GetSnapshot(snapname string) *Snapshot { + return &Snapshot{ + image: image, + name: snapname, + } +} + +// int rbd_metadata_get(rbd_image_t image, const char *key, char *value, size_t *vallen) +func (image *Image) GetMetadata(key string) (string, error) { + if err := image.validate(imageIsOpen); err != nil { + return "", err + } + + c_key := C.CString(key) + defer C.free(unsafe.Pointer(c_key)) + + var c_vallen C.size_t + ret := C.rbd_metadata_get(image.image, c_key, nil, (*C.size_t)(&c_vallen)) + // get size of value + // ret -34 because we pass nil as value pointer + if ret != 0 && ret != -C.ERANGE { + return "", RBDError(ret) + } + + // make a bytes array with a good size + value := make([]byte, c_vallen-1) + ret = C.rbd_metadata_get(image.image, c_key, (*C.char)(unsafe.Pointer(&value[0])), (*C.size_t)(&c_vallen)) + if ret < 0 { + return "", RBDError(ret) + } + + return string(value), nil +} + +// int rbd_metadata_set(rbd_image_t image, const char *key, const char *value) +func (image *Image) SetMetadata(key string, value string) error { + if err := image.validate(imageIsOpen); err != nil { + return err + } + + c_key := C.CString(key) + c_value := C.CString(value) + defer C.free(unsafe.Pointer(c_key)) + defer C.free(unsafe.Pointer(c_value)) + + ret := C.rbd_metadata_set(image.image, c_key, c_value) + if ret < 0 { + return RBDError(ret) + } + + return nil +} + +// int rbd_metadata_remove(rbd_image_t image, const char *key) +func (image *Image) RemoveMetadata(key string) error { + if err := image.validate(imageIsOpen); err != nil { + return err + } + + c_key := C.CString(key) + defer C.free(unsafe.Pointer(c_key)) + + ret := C.rbd_metadata_remove(image.image, c_key) + if ret < 0 { + return RBDError(ret) + } + + return nil +} + +// GetId returns the internal image ID string. +// +// Implements: +// int rbd_get_id(rbd_image_t image, char *id, size_t id_len); +func (image *Image) GetId() (string, error) { + if err := image.validate(imageIsOpen); err != nil { + return "", err + } + size := C.size_t(1024) + buf := make([]byte, size) + for { + ret := C.rbd_get_id( + image.image, + (*C.char)(unsafe.Pointer(&buf[0])), + size) + if ret == -C.ERANGE && size <= 8192 { + size *= 2 + buf = make([]byte, size) + } else if ret < 0 { + return "", getError(ret) + } + id := C.GoString((*C.char)(unsafe.Pointer(&buf[0]))) + return id, nil + } +} + +// int rbd_snap_remove(rbd_image_t image, const char *snapname); +func (snapshot *Snapshot) Remove() error { + if err := snapshot.validate(snapshotNeedsName | imageIsOpen); err != nil { + return err + } + + c_snapname := C.CString(snapshot.name) + defer C.free(unsafe.Pointer(c_snapname)) + + return getError(C.rbd_snap_remove(snapshot.image.image, c_snapname)) +} + +// int rbd_snap_rollback(rbd_image_t image, const char *snapname); +// int rbd_snap_rollback_with_progress(rbd_image_t image, const char *snapname, +// librbd_progress_fn_t cb, void *cbdata); +func (snapshot *Snapshot) Rollback() error { + if err := snapshot.validate(snapshotNeedsName | imageIsOpen); err != nil { + return err + } + + c_snapname := C.CString(snapshot.name) + defer C.free(unsafe.Pointer(c_snapname)) + + return getError(C.rbd_snap_rollback(snapshot.image.image, c_snapname)) +} + +// int rbd_snap_protect(rbd_image_t image, const char *snap_name); +func (snapshot *Snapshot) Protect() error { + if err := snapshot.validate(snapshotNeedsName | imageIsOpen); err != nil { + return err + } + + c_snapname := C.CString(snapshot.name) + defer C.free(unsafe.Pointer(c_snapname)) + + return getError(C.rbd_snap_protect(snapshot.image.image, c_snapname)) +} + +// int rbd_snap_unprotect(rbd_image_t image, const char *snap_name); +func (snapshot *Snapshot) Unprotect() error { + if err := snapshot.validate(snapshotNeedsName | imageIsOpen); err != nil { + return err + } + + c_snapname := C.CString(snapshot.name) + defer C.free(unsafe.Pointer(c_snapname)) + + return getError(C.rbd_snap_unprotect(snapshot.image.image, c_snapname)) +} + +// int rbd_snap_is_protected(rbd_image_t image, const char *snap_name, +// int *is_protected); +func (snapshot *Snapshot) IsProtected() (bool, error) { + if err := snapshot.validate(snapshotNeedsName | imageIsOpen); err != nil { + return false, err + } + + var c_is_protected C.int + + c_snapname := C.CString(snapshot.name) + defer C.free(unsafe.Pointer(c_snapname)) + + ret := C.rbd_snap_is_protected(snapshot.image.image, c_snapname, + &c_is_protected) + if ret < 0 { + return false, RBDError(ret) + } + + return c_is_protected != 0, nil +} + +// int rbd_snap_set(rbd_image_t image, const char *snapname); +func (snapshot *Snapshot) Set() error { + if err := snapshot.validate(snapshotNeedsName | imageIsOpen); err != nil { + return err + } + + c_snapname := C.CString(snapshot.name) + defer C.free(unsafe.Pointer(c_snapname)) + + return getError(C.rbd_snap_set(snapshot.image.image, c_snapname)) +} + +// GetTrashList returns a slice of TrashInfo structs, containing information about all RBD images +// currently residing in the trash. +func GetTrashList(ioctx *rados.IOContext) ([]TrashInfo, error) { + var num_entries C.size_t + + // Call rbd_trash_list with nil pointer to get number of trash entries. + if C.rbd_trash_list(C.rados_ioctx_t(ioctx.Pointer()), nil, &num_entries); num_entries == 0 { + return nil, nil + } + + c_entries := make([]C.rbd_trash_image_info_t, num_entries) + trashList := make([]TrashInfo, num_entries) + + if ret := C.rbd_trash_list(C.rados_ioctx_t(ioctx.Pointer()), &c_entries[0], &num_entries); ret < 0 { + return nil, RBDError(ret) + } + + for i, ti := range c_entries { + trashList[i] = TrashInfo{ + Id: C.GoString(ti.id), + Name: C.GoString(ti.name), + DeletionTime: time.Unix(int64(ti.deletion_time), 0), + DefermentEndTime: time.Unix(int64(ti.deferment_end_time), 0), + } + } + + // Free rbd_trash_image_info_t pointers + C.rbd_trash_list_cleanup(&c_entries[0], num_entries) + + return trashList, nil +} + +// TrashRemove permanently deletes the trashed RBD with the specified id. +func TrashRemove(ioctx *rados.IOContext, id string, force bool) error { + c_id := C.CString(id) + defer C.free(unsafe.Pointer(c_id)) + + return getError(C.rbd_trash_remove(C.rados_ioctx_t(ioctx.Pointer()), c_id, C.bool(force))) +} + +// TrashRestore restores the trashed RBD with the specified id back to the pool from whence it +// came, with the specified new name. +func TrashRestore(ioctx *rados.IOContext, id, name string) error { + c_id := C.CString(id) + c_name := C.CString(name) + defer C.free(unsafe.Pointer(c_id)) + defer C.free(unsafe.Pointer(c_name)) + + return getError(C.rbd_trash_restore(C.rados_ioctx_t(ioctx.Pointer()), c_id, c_name)) +} + +// OpenImage will open an existing rbd image by name and snapshot name, +// returning a new opened image. Pass the NoSnapshot sentinel value as the +// snapName to explicitly indicate that no snapshot name is being provided. +// +// Implements: +// int rbd_open(rados_ioctx_t io, const char *name, +// rbd_image_t *image, const char *snap_name); +func OpenImage(ioctx *rados.IOContext, name, snapName string) (*Image, error) { + cName := C.CString(name) + defer C.free(unsafe.Pointer(cName)) + + var cSnapName *C.char + if snapName != NoSnapshot { + cSnapName = C.CString(snapName) + defer C.free(unsafe.Pointer(cSnapName)) + } + + var cImage C.rbd_image_t + ret := C.rbd_open( + C.rados_ioctx_t(ioctx.Pointer()), + cName, + &cImage, + cSnapName) + + if ret != 0 { + return nil, getError(ret) + } + + return &Image{ + ioctx: ioctx, + name: name, + image: cImage, + }, nil +} + +// OpenImageReadOnly will open an existing rbd image by name and snapshot name, +// returning a new opened-for-read image. Pass the NoSnapshot sentinel value +// as the snapName to explicitly indicate that no snapshot name is being +// provided. +// +// Implements: +// int rbd_open_read_only(rados_ioctx_t io, const char *name, +// rbd_image_t *image, const char *snap_name); +func OpenImageReadOnly(ioctx *rados.IOContext, name, snapName string) (*Image, error) { + cName := C.CString(name) + defer C.free(unsafe.Pointer(cName)) + + var cSnapName *C.char + if snapName != NoSnapshot { + cSnapName = C.CString(snapName) + defer C.free(unsafe.Pointer(cSnapName)) + } + + var cImage C.rbd_image_t + ret := C.rbd_open_read_only( + C.rados_ioctx_t(ioctx.Pointer()), + cName, + &cImage, + cSnapName) + + if ret != 0 { + return nil, getError(ret) + } + + return &Image{ + ioctx: ioctx, + name: name, + image: cImage, + }, nil +} + +// OpenImageById will open an existing rbd image by ID and snapshot name, +// returning a new opened image. Pass the NoSnapshot sentinel value as the +// snapName to explicitly indicate that no snapshot name is being provided. +// Error handling will fail & segfault unless compiled with a version of ceph +// that fixes https://tracker.ceph.com/issues/43178 +// +// Implements: +// int rbd_open_by_id(rados_ioctx_t io, const char *id, +// rbd_image_t *image, const char *snap_name); +func OpenImageById(ioctx *rados.IOContext, id, snapName string) (*Image, error) { + cid := C.CString(id) + defer C.free(unsafe.Pointer(cid)) + + var cSnapName *C.char + if snapName != NoSnapshot { + cSnapName = C.CString(snapName) + defer C.free(unsafe.Pointer(cSnapName)) + } + + var cImage C.rbd_image_t + ret := C.rbd_open_by_id( + C.rados_ioctx_t(ioctx.Pointer()), + cid, + &cImage, + cSnapName) + + if ret != 0 { + return nil, getError(ret) + } + + return &Image{ + ioctx: ioctx, + image: cImage, + }, nil +} + +// OpenImageByIdReadOnly will open an existing rbd image by ID and snapshot +// name, returning a new opened-for-read image. Pass the NoSnapshot sentinel +// value as the snapName to explicitly indicate that no snapshot name is being +// provided. +// Error handling will fail & segfault unless compiled with a version of ceph +// that fixes https://tracker.ceph.com/issues/43178 +// +// Implements: +// int rbd_open_by_id_read_only(rados_ioctx_t io, const char *id, +// rbd_image_t *image, const char *snap_name); +func OpenImageByIdReadOnly(ioctx *rados.IOContext, id, snapName string) (*Image, error) { + cid := C.CString(id) + defer C.free(unsafe.Pointer(cid)) + + var cSnapName *C.char + if snapName != NoSnapshot { + cSnapName = C.CString(snapName) + defer C.free(unsafe.Pointer(cSnapName)) + } + + var cImage C.rbd_image_t + ret := C.rbd_open_by_id_read_only( + C.rados_ioctx_t(ioctx.Pointer()), + cid, + &cImage, + cSnapName) + + if ret != 0 { + return nil, getError(ret) + } + + return &Image{ + ioctx: ioctx, + image: cImage, + }, nil +} + +// CreateImage creates a new rbd image using provided image options. +// +// Implements: +// int rbd_create4(rados_ioctx_t io, const char *name, uint64_t size, +// rbd_image_options_t opts); +func CreateImage(ioctx *rados.IOContext, name string, size uint64, rio *RbdImageOptions) error { + + if rio == nil { + return RBDError(C.EINVAL) + } + + c_name := C.CString(name) + defer C.free(unsafe.Pointer(c_name)) + + ret := C.rbd_create4(C.rados_ioctx_t(ioctx.Pointer()), c_name, + C.uint64_t(size), C.rbd_image_options_t(rio.options)) + return getError(ret) +} + +// RemoveImage removes the specified rbd image. +// +// Implements: +// int rbd_remove(rados_ioctx_t io, const char *name); +func RemoveImage(ioctx *rados.IOContext, name string) error { + c_name := C.CString(name) + defer C.free(unsafe.Pointer(c_name)) + return getError(C.rbd_remove(C.rados_ioctx_t(ioctx.Pointer()), c_name)) +} + +// CloneImage creates a clone of the image from the named snapshot in the +// provided io-context with the given name and image options. +// +// Implements: +// int rbd_clone3(rados_ioctx_t p_ioctx, const char *p_name, +// const char *p_snapname, rados_ioctx_t c_ioctx, +// const char *c_name, rbd_image_options_t c_opts); +func CloneImage(ioctx *rados.IOContext, parentName, snapName string, + destctx *rados.IOContext, name string, rio *RbdImageOptions) error { + + if rio == nil { + return RBDError(C.EINVAL) + } + + cParentName := C.CString(parentName) + defer C.free(unsafe.Pointer(cParentName)) + cParentSnapName := C.CString(snapName) + defer C.free(unsafe.Pointer(cParentSnapName)) + cCloneName := C.CString(name) + defer C.free(unsafe.Pointer(cCloneName)) + + ret := C.rbd_clone3( + C.rados_ioctx_t(ioctx.Pointer()), + cParentName, + cParentSnapName, + C.rados_ioctx_t(destctx.Pointer()), + cCloneName, + C.rbd_image_options_t(rio.options)) + return getError(ret) +} + +// CloneFromImage creates a clone of the image from the named snapshot in the +// provided io-context with the given name and image options. +// This function is a convenience wrapper around CloneImage to support cloning +// from an existing Image. +func CloneFromImage(parent *Image, snapName string, + destctx *rados.IOContext, name string, rio *RbdImageOptions) error { + + if err := parent.validate(imageNeedsIOContext); err != nil { + return err + } + return CloneImage(parent.ioctx, parent.name, snapName, destctx, name, rio) +} diff --git a/vendor/github.com/ceph/go-ceph/rbd/rbd_mimic.go b/vendor/github.com/ceph/go-ceph/rbd/rbd_mimic.go new file mode 100644 index 000000000..5e602ece3 --- /dev/null +++ b/vendor/github.com/ceph/go-ceph/rbd/rbd_mimic.go @@ -0,0 +1,43 @@ +// +build luminous mimic +// +build !nautilus +// +// Ceph Nautilus includes rbd_list2() and marked rbd_list() deprecated. + +package rbd + +// #cgo LDFLAGS: -lrbd +// #include +// #include +// #include +import "C" + +import ( + "bytes" + "unsafe" + + "github.com/ceph/go-ceph/rados" +) + +// GetImageNames returns the list of current RBD images. +func GetImageNames(ioctx *rados.IOContext) (names []string, err error) { + buf := make([]byte, 4096) + for { + size := C.size_t(len(buf)) + ret := C.rbd_list(C.rados_ioctx_t(ioctx.Pointer()), + (*C.char)(unsafe.Pointer(&buf[0])), &size) + if ret == -C.ERANGE { + buf = make([]byte, size) + continue + } else if ret < 0 { + return nil, RBDError(ret) + } + tmp := bytes.Split(buf[:size-1], []byte{0}) + for _, s := range tmp { + if len(s) > 0 { + name := C.GoString((*C.char)(unsafe.Pointer(&s[0]))) + names = append(names, name) + } + } + return names, nil + } +} diff --git a/vendor/github.com/ceph/go-ceph/rbd/rbd_nautilus.go b/vendor/github.com/ceph/go-ceph/rbd/rbd_nautilus.go new file mode 100644 index 000000000..f722c0a21 --- /dev/null +++ b/vendor/github.com/ceph/go-ceph/rbd/rbd_nautilus.go @@ -0,0 +1,45 @@ +// +build !luminous,!mimic +// +// Ceph Nautilus is the first release that includes rbd_list2(). + +package rbd + +// #cgo LDFLAGS: -lrbd +// #include +// #include +// #include +import "C" + +import ( + "fmt" + "unsafe" + + "github.com/ceph/go-ceph/rados" +) + +// GetImageNames returns the list of current RBD images. +func GetImageNames(ioctx *rados.IOContext) ([]string, error) { + size := C.size_t(0) + ret := C.rbd_list2(C.rados_ioctx_t(ioctx.Pointer()), nil, &size) + if ret < 0 && ret != -C.ERANGE { + return nil, RBDError(ret) + } else if ret > 0 { + return nil, fmt.Errorf("rbd_list2() returned %d names, expected 0", ret) + } else if ret == 0 && size == 0 { + return nil, nil + } + + // expected: ret == -ERANGE, size contains number of image names + images := make([]C.rbd_image_spec_t, size) + ret = C.rbd_list2(C.rados_ioctx_t(ioctx.Pointer()), (*C.rbd_image_spec_t)(unsafe.Pointer(&images[0])), &size) + if ret < 0 { + return nil, RBDError(ret) + } + defer C.rbd_image_spec_list_cleanup((*C.rbd_image_spec_t)(unsafe.Pointer(&images[0])), size) + + names := make([]string, size) + for i, image := range images { + names[i] = C.GoString(image.name) + } + return names, nil +} diff --git a/vendor/github.com/ceph/go-ceph/rbd/snapshot_mimic.go b/vendor/github.com/ceph/go-ceph/rbd/snapshot_mimic.go new file mode 100644 index 000000000..cdc384730 --- /dev/null +++ b/vendor/github.com/ceph/go-ceph/rbd/snapshot_mimic.go @@ -0,0 +1,101 @@ +// +build luminous mimic +// +build !nautilus +// +// Ceph Nautilus introduced rbd_get_parent() and deprecated rbd_get_parent_info(). +// Ceph Nautilus introduced rbd_list_children3() and deprecated rbd_list_children(). + +package rbd + +// #cgo LDFLAGS: -lrbd +// #include +// #include +import "C" + +import ( + "bytes" + "unsafe" +) + +// GetParentInfo looks for the parent of the image and stores the pool, name +// and snapshot-name in the byte-arrays that are passed as arguments. +// +// Implements: +// int rbd_get_parent_info(rbd_image_t image, char *parent_pool_name, +// size_t ppool_namelen, char *parent_name, +// size_t pnamelen, char *parent_snap_name, +// size_t psnap_namelen) +func (image *Image) GetParentInfo(p_pool, p_name, p_snapname []byte) error { + if err := image.validate(imageIsOpen); err != nil { + return err + } + + ret := C.rbd_get_parent_info( + image.image, + (*C.char)(unsafe.Pointer(&p_pool[0])), + (C.size_t)(len(p_pool)), + (*C.char)(unsafe.Pointer(&p_name[0])), + (C.size_t)(len(p_name)), + (*C.char)(unsafe.Pointer(&p_snapname[0])), + (C.size_t)(len(p_snapname))) + if ret == 0 { + return nil + } else { + return RBDError(ret) + } +} + +// ListChildren returns arrays with the pools and names of the images that are +// children of the given image. The index of the pools and images arrays can be +// used to link the two items together. +// +// Implements: +// ssize_t rbd_list_children(rbd_image_t image, char *pools, +// size_t *pools_len, +// char *images, size_t *images_len); +func (image *Image) ListChildren() (pools []string, images []string, err error) { + if err := image.validate(imageIsOpen); err != nil { + return nil, nil, err + } + + var c_pools_len, c_images_len C.size_t + + ret := C.rbd_list_children(image.image, + nil, &c_pools_len, + nil, &c_images_len) + if ret == 0 { + return nil, nil, nil + } + if ret < 0 && ret != -C.ERANGE { + return nil, nil, RBDError(ret) + } + + pools_buf := make([]byte, c_pools_len) + images_buf := make([]byte, c_images_len) + + ret = C.rbd_list_children(image.image, + (*C.char)(unsafe.Pointer(&pools_buf[0])), + &c_pools_len, + (*C.char)(unsafe.Pointer(&images_buf[0])), + &c_images_len) + if ret < 0 { + return nil, nil, RBDError(ret) + } + + tmp := bytes.Split(pools_buf[:c_pools_len-1], []byte{0}) + for _, s := range tmp { + if len(s) > 0 { + name := C.GoString((*C.char)(unsafe.Pointer(&s[0]))) + pools = append(pools, name) + } + } + + tmp = bytes.Split(images_buf[:c_images_len-1], []byte{0}) + for _, s := range tmp { + if len(s) > 0 { + name := C.GoString((*C.char)(unsafe.Pointer(&s[0]))) + images = append(images, name) + } + } + + return pools, images, nil +} diff --git a/vendor/github.com/ceph/go-ceph/rbd/snapshot_nautilus.go b/vendor/github.com/ceph/go-ceph/rbd/snapshot_nautilus.go new file mode 100644 index 000000000..e46390d37 --- /dev/null +++ b/vendor/github.com/ceph/go-ceph/rbd/snapshot_nautilus.go @@ -0,0 +1,104 @@ +// +build !luminous,!mimic +// +// Ceph Nautilus introduced rbd_get_parent() and deprecated rbd_get_parent_info(). +// Ceph Nautilus introduced rbd_list_children3() and deprecated rbd_list_children(). + +package rbd + +// #cgo LDFLAGS: -lrbd +// #include +// #include +import "C" + +import ( + "fmt" + "unsafe" +) + +// GetParentInfo looks for the parent of the image and stores the pool, name +// and snapshot-name in the byte-arrays that are passed as arguments. +// +// Implements: +// int rbd_get_parent(rbd_image_t image, +// rbd_linked_image_spec_t *parent_image, +// rbd_snap_spec_t *parent_snap) +func (image *Image) GetParentInfo(pool, name, snapname []byte) error { + if err := image.validate(imageIsOpen); err != nil { + return err + } + + parentImage := C.rbd_linked_image_spec_t{} + parentSnap := C.rbd_snap_spec_t{} + ret := C.rbd_get_parent(image.image, &parentImage, &parentSnap) + if ret != 0 { + return RBDError(ret) + } + + defer C.rbd_linked_image_spec_cleanup(&parentImage) + defer C.rbd_snap_spec_cleanup(&parentSnap) + + strlen := int(C.strlen(parentImage.pool_name)) + if len(pool) < strlen { + return RBDError(C.ERANGE) + } + if copy(pool, C.GoString(parentImage.pool_name)) != strlen { + return RBDError(C.ERANGE) + } + + strlen = int(C.strlen(parentImage.image_name)) + if len(name) < strlen { + return RBDError(C.ERANGE) + } + if copy(name, C.GoString(parentImage.image_name)) != strlen { + return RBDError(C.ERANGE) + } + + strlen = int(C.strlen(parentSnap.name)) + if len(snapname) < strlen { + return RBDError(C.ERANGE) + } + if copy(snapname, C.GoString(parentSnap.name)) != strlen { + return RBDError(C.ERANGE) + } + + return nil +} + +// ListChildren returns arrays with the pools and names of the images that are +// children of the given image. The index of the pools and images arrays can be +// used to link the two items together. +// +// Implements: +// int rbd_list_children3(rbd_image_t image, rbd_linked_image_spec_t *images, +// size_t *max_images); +func (image *Image) ListChildren() (pools []string, images []string, err error) { + if err := image.validate(imageIsOpen); err != nil { + return nil, nil, err + } + + size := C.size_t(0) + ret := C.rbd_list_children3(image.image, nil, &size) + if ret < 0 && ret != -C.ERANGE { + return nil, nil, RBDError(ret) + } else if ret > 0 { + return nil, nil, fmt.Errorf("rbd_list_children3() returned %d, expected 0", ret) + } else if ret == 0 && size == 0 { + return nil, nil, nil + } + + // expected: ret == -ERANGE, size contains number of image names + children := make([]C.rbd_linked_image_spec_t, size) + ret = C.rbd_list_children3(image.image, (*C.rbd_linked_image_spec_t)(unsafe.Pointer(&children[0])), &size) + if ret < 0 { + return nil, nil, RBDError(ret) + } + defer C.rbd_linked_image_spec_list_cleanup((*C.rbd_linked_image_spec_t)(unsafe.Pointer(&children[0])), size) + + pools = make([]string, size) + images = make([]string, size) + for i, child := range children { + pools[i] = C.GoString(child.pool_name) + images[i] = C.GoString(child.image_name) + } + return pools, images, nil +}