Repository: ceph/go-ceph
Pull request #297 (2020-06-09 01:17:09)

rbd: add wrappers for rbd_namespace_* functions
Added wrappers for following four functions:
1. rbd_namespace_create()
2. rbd_namespace_remove()
3. rbd_namespace_exists()
4. rbd_namespace_list()

Fixes: https://github.com/ceph/go-ceph/issues/276

Signed-off-by: Mudit Agarwal muagarwa@redhat.com

## Checklist
- [ ] Added tests for features and functional changes
- [ ] Public functions and types are documented
- [ ] Standard formatting is applied to Go code


agarwal-mudit: Please ignore CI failures for now, I know the issue. Will fix once I make the PR ready for review.
agarwal-mudit: Need some help in this one see [this](https://github.com/ceph/go-ceph/issues/276#issuecomment-640327717).
agarwal-mudit: Thanks a lot @phlogistonjohn.
My bad, I got confused because of the error/length it returned.

Patch:
diff --git a/rbd/errors.go b/rbd/errors.go
index 1d0e579..482d305 100644
--- a/rbd/errors.go
+++ b/rbd/errors.go
@@ -52,11 +52,11 @@ func getErrorIfNegative(ret C.int) error {
 var (
 	// ErrNoIOContext may be returned if an api call requires an IOContext and
 	// it is not provided.
-	ErrNoIOContext = errors.New("RBD image does not have an IOContext")
+	ErrNoIOContext = errors.New("IOContext is missing")
 	// ErrNoName may be returned if an api call requires a name and it is
 	// not provided.
 	ErrNoName = errors.New("RBD image does not have a name")
-	// ErrSnapshotNoName may be returned if an aip call requires a snapshot
+	// ErrSnapshotNoName may be returned if an api call requires a snapshot
 	// name and it is not provided.
 	ErrSnapshotNoName = errors.New("RBD snapshot does not have a name")
 	// ErrImageNotOpen may be returned if an api call requires an open image handle and one is not provided.
@@ -66,6 +66,9 @@ var (
 	// ErrNotFound may be returned from an api call when the requested item is
 	// missing.
 	ErrNotFound = errors.New("RBD image not found")
+	// ErrNoNamespaceName maye be returned if an api call requires a namespace
+	// name and it is not provided.
+	ErrNoNamespaceName = errors.New("Namespace value is missing")
 
 	// revive:disable:exported for compatibility with old versions
 	RbdErrorImageNotOpen = ErrImageNotOpen
diff --git a/rbd/namespace_nautilus.go b/rbd/namespace_nautilus.go
new file mode 100644
index 0000000..fb78a9a
--- /dev/null
+++ b/rbd/namespace_nautilus.go
@@ -0,0 +1,111 @@
+// +build !luminous,!mimic
+//
+// Ceph Nautilus is the first release that includes rbd_namespace_create(),
+// rbd_namespace_remove(), rbd_namespace_exists() and rbd_namespace_list().
+
+package rbd
+
+// #cgo LDFLAGS: -lrbd
+// #include <rados/librados.h>
+// #include <rbd/librbd.h>
+// #include <stdlib.h>
+// #include <errno.h>
+import "C"
+
+import (
+	"bytes"
+	"unsafe"
+
+	"github.com/ceph/go-ceph/internal/retry"
+	"github.com/ceph/go-ceph/rados"
+)
+
+// NamespaceCreate creates the namespace for a given Rados IOContext.
+//
+// Implements:
+//  int rbd_namespace_create(rados_ioctx_t io, const char *namespace_name);
+func NamespaceCreate(ioctx *rados.IOContext, namespaceName string) error {
+	if ioctx == nil {
+		return ErrNoIOContext
+	}
+	if namespaceName == "" {
+		return ErrNoNamespaceName
+	}
+	cNamespaceName := C.CString(namespaceName)
+	defer C.free(unsafe.Pointer(cNamespaceName))
+
+	ret := C.rbd_namespace_create(cephIoctx(ioctx), cNamespaceName)
+	return getError(ret)
+}
+
+// NamespaceRemove removes a given namespace.
+//
+// Implements:
+//  int rbd_namespace_remove(rados_ioctx_t io, const char *namespace_name);
+func NamespaceRemove(ioctx *rados.IOContext, namespaceName string) error {
+	if ioctx == nil {
+		return ErrNoIOContext
+	}
+	if namespaceName == "" {
+		return ErrNoNamespaceName
+	}
+	cNamespaceName := C.CString(namespaceName)
+	defer C.free(unsafe.Pointer(cNamespaceName))
+
+	ret := C.rbd_namespace_remove(cephIoctx(ioctx), cNamespaceName)
+	return getError(ret)
+}
+
+// NamespaceExists checks whether a given namespace exists or not.
+//
+// Implements:
+//  int rbd_namespace_exists(rados_ioctx_t io, const char *namespace_name, bool *exists);
+func NamespaceExists(ioctx *rados.IOContext, namespaceName string) (bool, error) {
+	if ioctx == nil {
+		return false, ErrNoIOContext
+	}
+	if namespaceName == "" {
+		return false, ErrNoNamespaceName
+	}
+	cNamespaceName := C.CString(namespaceName)
+	defer C.free(unsafe.Pointer(cNamespaceName))
+
+	var exists C.bool
+	ret := C.rbd_namespace_exists(cephIoctx(ioctx), cNamespaceName, &exists)
+	return bool(exists), getErrorIfNegative(ret)
+}
+
+// NamespaceList returns a slice containing the names of existing rbd namespaces.
+//
+// Implements:
+//  int rbd_namespace_list(rados_ioctx_t io, char *namespace_names, size_t *size);
+func NamespaceList(ioctx *rados.IOContext) (names []string, err error) {
+	if ioctx == nil {
+		return nil, ErrNoIOContext
+	}
+	var (
+		buf   []byte
+		cSize C.size_t
+	)
+	retry.WithSizes(4096, 262144, func(size int) retry.Hint {
+		cSize = C.size_t(size)
+		buf = make([]byte, cSize)
+		ret := C.rbd_namespace_list(cephIoctx(ioctx),
+			(*C.char)(unsafe.Pointer(&buf[0])),
+			&cSize)
+		err = getErrorIfNegative(ret)
+		return retry.Size(int(cSize)).If(err == errRange)
+	})
+
+	if err != nil {
+		return nil, err
+	}
+	tmpList := bytes.Split(buf[:cSize-1], []byte{0})
+	for _, s := range tmpList {
+		if len(s) > 0 {
+			name := C.GoString((*C.char)(unsafe.Pointer(&s[0])))
+			names = append(names, name)
+		}
+	}
+	return names, nil
+}


Test patch:
diff --git a/rbd/namespace_nautilus_test.go b/rbd/namespace_nautilus_test.go
new file mode 100644
index 0000000..d2bb03f
--- /dev/null
+++ b/rbd/namespace_nautilus_test.go
@@ -0,0 +1,108 @@
+// +build !luminous,!mimic
+
+package rbd
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestNamespace(t *testing.T) {
+	conn := radosConnect(t)
+
+	poolName := GetUUID()
+	err := conn.MakePool(poolName)
+	assert.NoError(t, err)
+
+	ioctx, err := conn.OpenIOContext(poolName)
+	assert.NoError(t, err)
+
+	defer func() {
+		ioctx.Destroy()
+		conn.DeletePool(poolName)
+		conn.Shutdown()
+	}()
+
+	t.Run("invalidInputNamespace", func(t *testing.T) {
+		// NamespaceCreate.
+		err := NamespaceCreate(nil, "someName")
+		assert.Error(t, err)
+		err = NamespaceCreate(ioctx, "")
+		assert.Error(t, err)
+
+		// NamespaceRemove.
+		err = NamespaceRemove(nil, "someName")
+		assert.Error(t, err)
+		err = NamespaceRemove(ioctx, "")
+		assert.Error(t, err)
+
+		// NamespaceExists.
+		_, err = NamespaceExists(nil, "someName")
+		assert.Error(t, err)
+		_, err = NamespaceExists(ioctx, "")
+		assert.Error(t, err)
+
+		// NamespaceList.
+		_, err = NamespaceList(nil)
+		assert.Error(t, err)
+	})
+
+	t.Run("CreateNamespace", func(t *testing.T) {
+		nameSpace := "myNamespace"
+		err := NamespaceCreate(ioctx, nameSpace)
+		assert.NoError(t, err)
+
+		// Check whether it exists or not.
+		val, err := NamespaceExists(ioctx, nameSpace)
+		assert.NoError(t, err)
+		assert.Equal(t, val, true)
+
+		// Create again with same name.
+		err = NamespaceCreate(ioctx, nameSpace)
+		assert.Error(t, err)
+
+		// Remove the namespace.
+		err = NamespaceRemove(ioctx, nameSpace)
+		assert.NoError(t, err)
+	})
+
+	t.Run("NonExistingNamespace", func(t *testing.T) {
+		// Try to remove.
+		err := NamespaceRemove(ioctx, "someNamespace")
+		assert.Error(t, err)
+
+		// Check the existence.
+		val, err := NamespaceExists(ioctx, "someNamespace")
+		assert.NoError(t, err)
+		assert.Equal(t, val, false)
+	})
+
+	t.Run("NamespaceList", func(t *testing.T) {
+		var (
+			name1 = "name1"
+			name2 = "name2"
+			name3 = "name3"
+		)
+		err := NamespaceCreate(ioctx, name1)
+		assert.NoError(t, err)
+		err = NamespaceCreate(ioctx, name2)
+		assert.NoError(t, err)
+		err = NamespaceCreate(ioctx, name3)
+		assert.NoError(t, err)
+		defer func() {
+			assert.NoError(t, NamespaceRemove(ioctx, name1))
+			assert.NoError(t, NamespaceRemove(ioctx, name2))
+			assert.NoError(t, NamespaceRemove(ioctx, name3))
+		}()
+
+		eval, err := NamespaceExists(ioctx, name1)
+		assert.NoError(t, err)
+		assert.Equal(t, eval, true)
+		nList, err := NamespaceList(ioctx)
+		assert.NoError(t, err)
+		assert.Contains(t, nList, name1)
+		assert.Contains(t, nList, name2)
+		assert.Contains(t, nList, name3)
+	})
+}