Check Pools is in use before deleting it

currently we are not checking the pool is empty or
not before deleting, This PR adds a check to check
if any images/snapshots using the pool, if yes it will
not delete the pool

Signed-off-by: Madhu Rajanna <madhupr007@gmail.com>
This commit is contained in:
Madhu Rajanna committed 2019-09-26 11:41:47 +05:30
1 parent 9affd75752
commit 4efba0247d
6 files changed
+138 -3

No files matched your search

+11
View File
@@ -149,6 +149,17 @@ func TestFilesystemRemove(t *testing.T) {
}
return "", fmt.Errorf("unexpected ceph command '%v'", args)
}
executor.MockExecuteCommandWithOutput = func(debug bool, actionName string, command string, args ...string) (string, error) {
emptyPool := "{\"images\":{\"count\":0,\"provisioned_bytes\":0,\"snap_count\":0},\"trash\":{\"count\":1,\"provisioned_bytes\":2048,\"snap_count\":0}}"
if args[0] == "pool" {
if args[1] == "stats" {
return emptyPool, nil
}
}
return "", fmt.Errorf("unexpected rbd command '%v'", args)
}
err := RemoveFilesystem(context, "ns", fs.MDSMap.FilesystemName)
assert.Nil(t, err)
assert.True(t, metadataDeleted)
+54 -2
View File
@@ -21,10 +21,9 @@ import (
"strconv"
"strings"
cephv1 "github.com/rook/rook/pkg/apis/ceph.rook.io/v1"
"github.com/rook/rook/pkg/clusterd"
"github.com/rook/rook/pkg/daemon/ceph/model"
cephv1 "github.com/rook/rook/pkg/apis/ceph.rook.io/v1"
)
const (
@@ -65,6 +64,19 @@ type CephStoragePoolStats struct {
} `json:"pools"`
}
type PoolStatistics struct {
Images struct {
Count int `json:"count"`
ProvisionedBytes int `json:"provisioned_bytes"`
SnapCount int `json:"snap_count"`
} `json:"images"`
Trash struct {
Count int `json:"count"`
ProvisionedBytes int `json:"provisioned_bytes"`
SnapCount int `json:"snap_count"`
} `json:"trash"`
}
func ListPoolSummaries(context *clusterd.Context, clusterName string) ([]CephStoragePoolSummary, error) {
args := []string{"osd", "lspools"}
buf, err := NewCephCommand(context, clusterName, args).Run()
@@ -152,6 +164,25 @@ func CreatePoolWithProfile(context *clusterd.Context, clusterName string, newPoo
)
}
func checkForImagesInPool(context *clusterd.Context, name, namespace string) error {
var err error
var stats = new(PoolStatistics)
logger.Infof("checking any images/snapshosts present in pool %s", name)
stats, err = GetPoolStatistics(context, name, namespace)
if err != nil {
if strings.Contains(err.Error(), "No such file or directory") {
return nil
}
return fmt.Errorf("failed to list images/snapshosts in pool %s. %+v", name, err)
}
if stats.Images.Count == 0 && stats.Images.SnapCount == 0 {
logger.Infof("no images/snapshosts present in pool %s", name)
return nil
}
return fmt.Errorf("pool %s contains images/snapshosts", name)
}
func DeletePool(context *clusterd.Context, clusterName string, name string) error {
// check if the pool exists
pool, err := GetPoolDetails(context, clusterName, name)
@@ -160,6 +191,10 @@ func DeletePool(context *clusterd.Context, clusterName string, name string) erro
return nil
}
err = checkForImagesInPool(context, name, clusterName)
if err != nil {
return fmt.Errorf("failed to delete pool %s. %+v", name, err)
}
logger.Infof("purging pool %s (id=%d)", name, pool.Number)
args := []string{"osd", "pool", "delete", name, name, reallyConfirmFlag}
_, err = NewCephCommand(context, clusterName, args).Run()
@@ -294,6 +329,23 @@ func GetPoolStats(context *clusterd.Context, clusterName string) (*CephStoragePo
return &poolStats, nil
}
func GetPoolStatistics(context *clusterd.Context, name, clusterName string) (*PoolStatistics, error) {
args := []string{"pool", "stats", name}
cmd := NewRBDCommand(context, clusterName, args)
cmd.JsonOutput = true
buf, err := cmd.Run()
if err != nil {
return nil, fmt.Errorf("failed to get pool stats: %+v", err)
}
var poolStats PoolStatistics
if err := json.Unmarshal(buf, &poolStats); err != nil {
return nil, fmt.Errorf("failed to unmarshal pool stats response: %+v", err)
}
return &poolStats, nil
}
func GetPools(context *clusterd.Context, clusterName string) ([]model.Pool, error) {
// list pool summaries using the ceph client
cephPoolSummaries, err := ListPoolSummaries(context, clusterName)
+36
View File
@@ -17,6 +17,7 @@ package client
import (
"fmt"
"reflect"
"testing"
"github.com/rook/rook/pkg/daemon/ceph/model"
@@ -172,3 +173,38 @@ func testIsStringInSlice(a string, list []string) bool {
}
return false
}
func TestGetPoolStatistics(t *testing.T) {
p := PoolStatistics{}
p.Images.Count = 1
p.Images.ProvisionedBytes = 1024
p.Images.SnapCount = 1
p.Trash.Count = 1
p.Trash.ProvisionedBytes = 2048
p.Trash.SnapCount = 0
executor := &exectest.MockExecutor{}
context := &clusterd.Context{Executor: executor}
executor.MockExecuteCommandWithOutput = func(debug bool, actionName string, command string, args ...string) (string, error) {
a := "{\"images\":{\"count\":1,\"provisioned_bytes\":1024,\"snap_count\":1},\"trash\":{\"count\":1,\"provisioned_bytes\":2048,\"snap_count\":0}}"
logger.Infof("Command: %s %v", command, args)
if args[0] == "pool" {
if args[1] == "stats" {
if args[2] == "replicapool" {
return a, nil
}
return "", fmt.Errorf("rbd:error opening pool '%s': (2) No such file or directory", args[3])
}
}
return "", fmt.Errorf("unexpected rbd command '%v'", args)
}
stats, err := GetPoolStatistics(context, "replicapool", "cluster")
assert.Nil(t, err)
assert.True(t, reflect.DeepEqual(stats, &p))
stats, err = GetPoolStatistics(context, "rbd", "cluster")
assert.NotNil(t, err)
assert.Nil(t, stats)
}
@@ -138,6 +138,13 @@ func deleteStore(t *testing.T, name string, existingStores string, expectedDelet
zoneDeleted = true
return "", nil
}
if args[0] == "pool" {
if args[1] == "stats" {
emptyPool := "{\"images\":{\"count\":0,\"provisioned_bytes\":0,\"snap_count\":0},\"trash\":{\"count\":1,\"provisioned_bytes\":2048,\"snap_count\":0}}"
return emptyPool, nil
}
}
return "", fmt.Errorf("unexpected ceph command '%v'", args)
}
executor.MockExecuteCommandWithOutput = executorFunc
-1
View File
@@ -143,7 +143,6 @@ func (c *PoolController) onDelete(obj interface{}) {
logger.Errorf("failed to get pool object: %+v", err)
return
}
if err := deletePool(c.context, pool); err != nil {
logger.Errorf("failed to delete pool %s. %+v", pool.ObjectMeta.Name, err)
}
+30
View File
@@ -152,6 +152,7 @@ func TestUpdatePool(t *testing.T) {
}
func TestDeletePool(t *testing.T) {
failOnDelete := false
executor := &exectest.MockExecutor{
MockExecuteCommandWithOutputFile: func(debug bool, actionName, command, outfile string, args ...string) (string, error) {
if command == "ceph" && args[1] == "lspools" {
@@ -159,8 +160,28 @@ func TestDeletePool(t *testing.T) {
} else if command == "ceph" && args[1] == "pool" && args[2] == "get" {
return `{"pool": "mypool","pool_id": 1,"size":1}`, nil
}
return "", nil
},
MockExecuteCommandWithOutput: func(debug bool, actionName string, command string, args ...string) (string, error) {
emptyPool := "{\"images\":{\"count\":0,\"provisioned_bytes\":0,\"snap_count\":0},\"trash\":{\"count\":1,\"provisioned_bytes\":2048,\"snap_count\":0}}"
p := "{\"images\":{\"count\":1,\"provisioned_bytes\":1024,\"snap_count\":0},\"trash\":{\"count\":1,\"provisioned_bytes\":2048,\"snap_count\":0}}"
logger.Infof("Command: %s %v", command, args)
if args[0] == "pool" {
if args[1] == "stats" {
if !failOnDelete {
return emptyPool, nil
}
return p, nil
}
return "", fmt.Errorf("rbd: error opening pool '%s': (2) No such file or directory", args[3])
}
return "", fmt.Errorf("unexpected rbd command '%v'", args)
},
}
context := &clusterd.Context{Executor: executor}
@@ -179,6 +200,15 @@ func TestDeletePool(t *testing.T) {
assert.False(t, exists)
err = deletePool(context, p)
assert.Nil(t, err)
// fail if images/snapshosts exist in the pool
failOnDelete = true
p = &cephv1.CephBlockPool{ObjectMeta: metav1.ObjectMeta{Name: "mypool", Namespace: "myns"}}
exists, err = poolExists(context, p)
assert.Nil(t, err)
assert.True(t, exists)
err = deletePool(context, p)
assert.NotNil(t, err)
}
func TestGetPoolObject(t *testing.T) {