rbd: add minimal support for listing watchers on an RBD image

Checking if there are other clients interested in changes to an RBD
image is important for certain actions. For example, the Ceph-CSI
project would not want to remove an image in case it is still mounted
somewhere.

Signed-off-by: Niels de Vos <ndevos@redhat.com>
This commit is contained in:
Niels de Vos 2019-11-29 11:51:12 +01:00 committed by John Mulligan
parent a65e39ca3d
commit d0943906f7
2 changed files with 174 additions and 0 deletions

57
rbd/watchers_mimic.go Normal file
View File

@ -0,0 +1,57 @@
// +build !luminous
//
// Ceph Mimic is the first version that supports watchers through librbd.
package rbd
// #cgo LDFLAGS: -lrbd
// #include <errno.h>
// #include <rbd/librbd.h>
import "C"
// ImageWatcher is a representation of the rbd_image_watcher_t from librbd.h
type ImageWatcher struct {
Addr string
Id int64
Cookie uint64
}
// ListWatchers returns the watchers on an RBD image. In case of an error, nil
// and an error are returned.
//
// Note:
// Only supported in Ceph Mimic and newer.
//
// Implements:
// int rbd_watchers_list(rbd_image_t image,
// rbd_image_watcher_t *watchers, size_t *max_watchers)
func (image *Image) ListWatchers() ([]ImageWatcher, error) {
if err := image.validate(imageIsOpen); err != nil {
return nil, err
}
count := C.ulong(0)
ret := C.rbd_watchers_list(image.image, nil, &count)
if ret != 0 && ret != -C.ERANGE {
return nil, getError(ret)
}
if ret == 0 && count == 0 {
return nil, nil
}
watchers := make([]C.rbd_image_watcher_t, count)
ret = C.rbd_watchers_list(image.image, &watchers[0], &count)
if ret != 0 && ret != -C.ERANGE {
return nil, getError(ret)
}
defer C.rbd_watchers_list_cleanup(&watchers[0], count)
imageWatchers := make([]ImageWatcher, len(watchers))
for i, watcher := range watchers {
imageWatchers[i].Addr = C.GoString(watcher.addr)
imageWatchers[i].Id = int64(watcher.id)
imageWatchers[i].Cookie = uint64(watcher.cookie)
}
return imageWatchers, nil
}

117
rbd/watchers_mimic_test.go Normal file
View File

@ -0,0 +1,117 @@
// +build !luminous
//
// Ceph Mimic is the first version that supports watchers through librbd.
package rbd
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestListWatchers(t *testing.T) {
conn := radosConnect(t)
require.NotNil(t, conn)
defer conn.Shutdown()
poolname := GetUUID()
err := conn.MakePool(poolname)
require.NoError(t, err)
defer conn.DeletePool(poolname)
ioctx, err := conn.OpenIOContext(poolname)
require.NoError(t, err)
defer ioctx.Destroy()
name := GetUUID()
options := NewRbdImageOptions()
err = CreateImage(ioctx, name, 1<<22, options)
require.NoError(t, err)
defer func() { assert.NoError(t, RemoveImage(ioctx, name)) }()
t.Run("imageNotOpen", func(t *testing.T) {
image, err := OpenImageReadOnly(ioctx, name, NoSnapshot)
require.NoError(t, err)
require.NotNil(t, image)
err = image.Close()
require.NoError(t, err)
_, err = image.ListWatchers()
assert.Equal(t, ErrImageNotOpen, err)
})
t.Run("noWatchers", func(t *testing.T) {
// open image read-only, as OpenImage() automatically adds a watcher
image, err := OpenImageReadOnly(ioctx, name, NoSnapshot)
require.NoError(t, err)
require.NotNil(t, image)
defer func() { assert.NoError(t, image.Close()) }()
watchers, err := image.ListWatchers()
assert.NoError(t, err)
assert.Equal(t, 0, len(watchers))
})
t.Run("addWatchers", func(t *testing.T) {
// open image read-only, as OpenImage() automatically adds a watcher
image, err := OpenImageReadOnly(ioctx, name, NoSnapshot)
require.NoError(t, err)
require.NotNil(t, image)
defer func() { assert.NoError(t, image.Close()) }()
watchers, err := image.ListWatchers()
assert.NoError(t, err)
assert.Equal(t, 0, len(watchers))
// opening an image writable adds a watcher automatically
image2, err := OpenImage(ioctx, name, NoSnapshot)
require.NoError(t, err)
require.NotNil(t, image2)
watchers, err = image.ListWatchers()
assert.NoError(t, err)
assert.Equal(t, 1, len(watchers))
watchers, err = image2.ListWatchers()
assert.NoError(t, err)
assert.Equal(t, 1, len(watchers))
image3, err := OpenImage(ioctx, name, NoSnapshot)
require.NoError(t, err)
require.NotNil(t, image3)
watchers, err = image.ListWatchers()
assert.NoError(t, err)
assert.Equal(t, 2, len(watchers))
watchers, err = image2.ListWatchers()
assert.NoError(t, err)
assert.Equal(t, 2, len(watchers))
watchers, err = image3.ListWatchers()
assert.NoError(t, err)
assert.Equal(t, 2, len(watchers))
// closing an image removes the watchers
err = image3.Close()
require.NoError(t, err)
watchers, err = image.ListWatchers()
assert.NoError(t, err)
assert.Equal(t, 1, len(watchers))
watchers, err = image2.ListWatchers()
assert.NoError(t, err)
assert.Equal(t, 1, len(watchers))
err = image2.Close()
require.NoError(t, err)
watchers, err = image.ListWatchers()
assert.NoError(t, err)
assert.Equal(t, 0, len(watchers))
})
}