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:
1 parent
9affd75752
commit
4efba0247d
6 files changed
+138
-3
No files matched your search
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in new issue
Block a user