2020-05-21 19:58:08 +00:00
|
|
|
/*
|
|
|
|
Copyright 2020 The Ceph-CSI Authors.
|
|
|
|
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
you may not use this file except in compliance with the License.
|
|
|
|
You may obtain a copy of the License at
|
|
|
|
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
See the License for the specific language governing permissions and
|
|
|
|
limitations under the License.
|
|
|
|
*/
|
|
|
|
|
|
|
|
package journal
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2020-07-02 21:43:40 +00:00
|
|
|
"errors"
|
2024-02-02 08:41:18 +00:00
|
|
|
"fmt"
|
2020-05-21 19:58:08 +00:00
|
|
|
|
|
|
|
"github.com/ceph/ceph-csi/internal/util"
|
2021-08-24 15:03:25 +00:00
|
|
|
"github.com/ceph/ceph-csi/internal/util/log"
|
2020-05-21 19:58:08 +00:00
|
|
|
|
|
|
|
"github.com/ceph/go-ceph/rados"
|
|
|
|
)
|
|
|
|
|
2020-11-05 15:47:57 +00:00
|
|
|
// chunkSize is the number of key-value pairs that will be fetched in
|
|
|
|
// one call. This is set fairly large to avoid calling into ceph APIs
|
|
|
|
// over and over.
|
|
|
|
const chunkSize int64 = 512
|
2020-05-15 18:02:51 +00:00
|
|
|
|
|
|
|
func getOMapValues(
|
2020-05-21 19:58:08 +00:00
|
|
|
ctx context.Context,
|
|
|
|
conn *Connection,
|
2022-06-01 10:17:19 +00:00
|
|
|
poolName, namespace, oid, prefix string, keys []string,
|
|
|
|
) (map[string]string, error) {
|
2020-05-21 19:58:08 +00:00
|
|
|
// fetch and configure the rados ioctx
|
|
|
|
ioctx, err := conn.conn.GetIoctx(poolName)
|
|
|
|
if err != nil {
|
2020-07-08 23:00:23 +00:00
|
|
|
return nil, omapPoolError(err)
|
2020-05-21 19:58:08 +00:00
|
|
|
}
|
|
|
|
defer ioctx.Destroy()
|
|
|
|
|
|
|
|
if namespace != "" {
|
|
|
|
ioctx.SetNamespace(namespace)
|
|
|
|
}
|
|
|
|
|
2020-05-15 18:02:51 +00:00
|
|
|
results := map[string]string{}
|
|
|
|
// want is our "lookup map" that ensures O(1) checks for keys
|
|
|
|
// while iterating, without needing to complicate the caller.
|
|
|
|
want := make(map[string]bool, len(keys))
|
|
|
|
for i := range keys {
|
|
|
|
want[keys[i]] = true
|
|
|
|
}
|
2020-11-05 15:47:57 +00:00
|
|
|
numKeys := uint64(0)
|
|
|
|
startAfter := ""
|
|
|
|
for {
|
|
|
|
prevNumKeys := numKeys
|
|
|
|
err = ioctx.ListOmapValues(
|
|
|
|
oid, startAfter, prefix, chunkSize,
|
|
|
|
func(key string, value []byte) {
|
|
|
|
numKeys++
|
|
|
|
startAfter = key
|
|
|
|
if want[key] {
|
|
|
|
results[key] = string(value)
|
|
|
|
}
|
|
|
|
},
|
|
|
|
)
|
|
|
|
// if we hit an error, or no new keys were seen, exit the loop
|
|
|
|
if err != nil || numKeys == prevNumKeys {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
2020-05-15 18:02:51 +00:00
|
|
|
|
2020-07-02 21:43:40 +00:00
|
|
|
if err != nil {
|
|
|
|
if errors.Is(err, rados.ErrNotFound) {
|
2021-08-24 15:03:25 +00:00
|
|
|
log.ErrorLog(ctx, "omap not found (pool=%q, namespace=%q, name=%q): %v",
|
2020-07-02 21:43:40 +00:00
|
|
|
poolName, namespace, oid, err)
|
2021-07-22 05:45:17 +00:00
|
|
|
|
2024-02-02 08:41:18 +00:00
|
|
|
return nil, fmt.Errorf("%w: %w", util.ErrKeyNotFound, err)
|
2020-07-02 21:43:40 +00:00
|
|
|
}
|
2021-07-22 05:45:17 +00:00
|
|
|
|
2020-05-15 18:02:51 +00:00
|
|
|
return nil, err
|
2020-05-21 19:58:08 +00:00
|
|
|
}
|
|
|
|
|
2021-08-24 15:03:25 +00:00
|
|
|
log.DebugLog(ctx, "got omap values: (pool=%q, namespace=%q, name=%q): %+v",
|
2020-05-15 18:02:51 +00:00
|
|
|
poolName, namespace, oid, results)
|
2021-07-22 05:45:17 +00:00
|
|
|
|
2020-05-15 18:02:51 +00:00
|
|
|
return results, nil
|
2020-05-21 19:58:08 +00:00
|
|
|
}
|
|
|
|
|
2020-05-21 18:59:09 +00:00
|
|
|
func removeMapKeys(
|
2020-05-21 19:58:08 +00:00
|
|
|
ctx context.Context,
|
|
|
|
conn *Connection,
|
2022-06-01 10:17:19 +00:00
|
|
|
poolName, namespace, oid string, keys []string,
|
|
|
|
) error {
|
2020-05-21 19:58:08 +00:00
|
|
|
// fetch and configure the rados ioctx
|
|
|
|
ioctx, err := conn.conn.GetIoctx(poolName)
|
|
|
|
if err != nil {
|
2020-07-08 23:00:23 +00:00
|
|
|
return omapPoolError(err)
|
2020-05-21 19:58:08 +00:00
|
|
|
}
|
|
|
|
defer ioctx.Destroy()
|
|
|
|
|
|
|
|
if namespace != "" {
|
|
|
|
ioctx.SetNamespace(namespace)
|
|
|
|
}
|
|
|
|
|
2020-05-21 18:59:09 +00:00
|
|
|
err = ioctx.RmOmapKeys(oid, keys)
|
2020-07-02 21:43:40 +00:00
|
|
|
if err != nil {
|
|
|
|
if errors.Is(err, rados.ErrNotFound) {
|
|
|
|
// the previous implementation of removing omap keys (via the cli)
|
|
|
|
// treated failure to find the omap as a non-error. Do so here to
|
|
|
|
// mimic the previous behavior.
|
2021-08-24 15:03:25 +00:00
|
|
|
log.DebugLog(ctx, "when removing omap keys, omap not found (pool=%q, namespace=%q, name=%q): %+v",
|
2020-07-02 21:43:40 +00:00
|
|
|
poolName, namespace, oid, keys)
|
|
|
|
} else {
|
2021-08-24 15:03:25 +00:00
|
|
|
log.ErrorLog(ctx, "failed removing omap keys (pool=%q, namespace=%q, name=%q): %v",
|
2020-07-02 21:43:40 +00:00
|
|
|
poolName, namespace, oid, err)
|
2021-07-22 05:45:17 +00:00
|
|
|
|
2020-07-02 21:43:40 +00:00
|
|
|
return err
|
|
|
|
}
|
2020-05-21 19:58:08 +00:00
|
|
|
}
|
2021-08-24 15:03:25 +00:00
|
|
|
log.DebugLog(ctx, "removed omap keys (pool=%q, namespace=%q, name=%q): %+v",
|
2020-06-08 21:14:47 +00:00
|
|
|
poolName, namespace, oid, keys)
|
2021-07-22 05:45:17 +00:00
|
|
|
|
2020-06-08 21:14:47 +00:00
|
|
|
return nil
|
2020-05-21 19:58:08 +00:00
|
|
|
}
|
|
|
|
|
2020-05-21 20:36:50 +00:00
|
|
|
func setOMapKeys(
|
2020-05-21 19:58:08 +00:00
|
|
|
ctx context.Context,
|
|
|
|
conn *Connection,
|
2022-06-01 10:17:19 +00:00
|
|
|
poolName, namespace, oid string, pairs map[string]string,
|
|
|
|
) error {
|
2020-05-21 19:58:08 +00:00
|
|
|
// fetch and configure the rados ioctx
|
|
|
|
ioctx, err := conn.conn.GetIoctx(poolName)
|
|
|
|
if err != nil {
|
2020-07-08 23:00:23 +00:00
|
|
|
return omapPoolError(err)
|
2020-05-21 19:58:08 +00:00
|
|
|
}
|
|
|
|
defer ioctx.Destroy()
|
|
|
|
|
|
|
|
if namespace != "" {
|
|
|
|
ioctx.SetNamespace(namespace)
|
|
|
|
}
|
|
|
|
|
2020-05-21 20:36:50 +00:00
|
|
|
bpairs := make(map[string][]byte, len(pairs))
|
|
|
|
for k, v := range pairs {
|
|
|
|
bpairs[k] = []byte(v)
|
2020-05-21 19:58:08 +00:00
|
|
|
}
|
2020-05-21 20:36:50 +00:00
|
|
|
err = ioctx.SetOmap(oid, bpairs)
|
2020-05-21 19:58:08 +00:00
|
|
|
if err != nil {
|
2021-08-24 15:03:25 +00:00
|
|
|
log.ErrorLog(ctx, "failed setting omap keys (pool=%q, namespace=%q, name=%q, pairs=%+v): %v",
|
2020-05-21 20:36:50 +00:00
|
|
|
poolName, namespace, oid, pairs, err)
|
2021-07-22 05:45:17 +00:00
|
|
|
|
2020-05-21 20:36:50 +00:00
|
|
|
return err
|
2020-05-21 19:58:08 +00:00
|
|
|
}
|
2021-08-24 15:03:25 +00:00
|
|
|
log.DebugLog(ctx, "set omap keys (pool=%q, namespace=%q, name=%q): %+v)",
|
2020-05-21 20:36:50 +00:00
|
|
|
poolName, namespace, oid, pairs)
|
2021-07-22 05:45:17 +00:00
|
|
|
|
2020-05-21 20:36:50 +00:00
|
|
|
return nil
|
2020-05-21 19:58:08 +00:00
|
|
|
}
|
|
|
|
|
2020-07-08 23:00:23 +00:00
|
|
|
func omapPoolError(err error) error {
|
2020-07-02 21:43:40 +00:00
|
|
|
if errors.Is(err, rados.ErrNotFound) {
|
2024-05-07 22:51:05 +00:00
|
|
|
return fmt.Errorf("Failed as %w (internal %w)", util.ErrPoolNotFound, err)
|
2020-05-21 19:58:08 +00:00
|
|
|
}
|
2021-07-22 05:45:17 +00:00
|
|
|
|
2020-05-21 19:58:08 +00:00
|
|
|
return err
|
|
|
|
}
|
2024-02-02 08:41:18 +00:00
|
|
|
|
|
|
|
// listOMapValues fetches all omap values for a given oid, prefix, and namespace.
|
|
|
|
func listOMapValues(
|
|
|
|
ctx context.Context,
|
|
|
|
conn *Connection,
|
|
|
|
poolName, namespace, oid, prefix string,
|
|
|
|
) (map[string]string, error) {
|
|
|
|
// fetch and configure the rados ioctx
|
|
|
|
ioctx, err := conn.conn.GetIoctx(poolName)
|
|
|
|
if err != nil {
|
|
|
|
return nil, omapPoolError(err)
|
|
|
|
}
|
|
|
|
defer ioctx.Destroy()
|
|
|
|
|
|
|
|
if namespace != "" {
|
|
|
|
ioctx.SetNamespace(namespace)
|
|
|
|
}
|
|
|
|
|
|
|
|
results := map[string]string{}
|
|
|
|
|
|
|
|
numKeys := uint64(0)
|
|
|
|
startAfter := ""
|
|
|
|
for {
|
|
|
|
prevNumKeys := numKeys
|
|
|
|
err = ioctx.ListOmapValues(
|
|
|
|
oid, startAfter, prefix, chunkSize,
|
|
|
|
func(key string, value []byte) {
|
|
|
|
numKeys++
|
|
|
|
startAfter = key
|
|
|
|
results[key] = string(value)
|
|
|
|
},
|
|
|
|
)
|
|
|
|
// if we hit an error, or no new keys were seen, exit the loop
|
|
|
|
if err != nil || numKeys == prevNumKeys {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
if errors.Is(err, rados.ErrNotFound) {
|
|
|
|
log.ErrorLog(ctx, "omap not found (pool=%q, namespace=%q, name=%q): %v",
|
|
|
|
poolName, namespace, oid, err)
|
|
|
|
|
|
|
|
return nil, fmt.Errorf("%w: %w", util.ErrKeyNotFound, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
log.DebugLog(ctx, "got omap values: (pool=%q, namespace=%q, name=%q): %+v",
|
|
|
|
poolName, namespace, oid, results)
|
|
|
|
|
|
|
|
return results, nil
|
|
|
|
}
|