From db380dc8c1549e7fdba08c740c4cc4fb7d2523ef Mon Sep 17 00:00:00 2001 From: Damiano Cipriani Date: Thu, 28 Nov 2024 11:17:08 +0100 Subject: [PATCH] feat(lvol): add snapshot checksum APIs Longhorn 9709 Signed-off-by: Damiano Cipriani --- app/cmd/basic/bdev_lvol.go | 95 ++++++++++++++++++++++++++++++++++++++ pkg/spdk/client/basic.go | 43 ++++++++++++++++- pkg/spdk/types/lvol.go | 19 ++++++-- 3 files changed, 152 insertions(+), 5 deletions(-) diff --git a/app/cmd/basic/bdev_lvol.go b/app/cmd/basic/bdev_lvol.go index 5815565..3483cf3 100644 --- a/app/cmd/basic/bdev_lvol.go +++ b/app/cmd/basic/bdev_lvol.go @@ -33,6 +33,8 @@ func BdevLvolCmd() cli.Command { BdevLvolGetXattrCmd(), BdevLvolGetFragmapCmd(), BdevLvolRenameCmd(), + BdevLvolRegisterSnapshotChecksumCmd(), + BdevLvolGetSnapshotChecksumCmd(), }, } } @@ -654,3 +656,96 @@ func bdevLvolRename(c *cli.Context) error { return util.PrintObject(renamed) } + +func BdevLvolRegisterSnapshotChecksumCmd() cli.Command { + return cli.Command{ + Name: "register-snapshot-checksum", + Flags: []cli.Flag{ + cli.StringFlag{ + Name: "alias", + Usage: "The alias of a snapshot is /. Specify this or uuid", + }, + cli.StringFlag{ + Name: "uuid", + Usage: "Specify this or alias", + }, + }, + Usage: "compute and store checksum of snapshot's data: \"register-snapshot-checksum --alias /\"," + + " or \"register-snapshot-checksum --uuid \"", + Action: func(c *cli.Context) { + if err := bdevLvolRegisterSnapshotChecksum(c); err != nil { + logrus.WithError(err).Fatalf("Failed to run register snapshot checksum command") + } + }, + } +} + +func bdevLvolRegisterSnapshotChecksum(c *cli.Context) error { + spdkCli, err := client.NewClient(context.Background()) + if err != nil { + return err + } + + name := c.String("alias") + if name == "" { + name = c.String("uuid") + } + if name == "" { + return fmt.Errorf("either alias or uuid must be provided") + } + + registered, err := spdkCli.BdevLvolRegisterSnapshotChecksum(name) + if err != nil { + return fmt.Errorf("failed to register checksum for snapshot %q: %v", name, err) + } + + return util.PrintObject(registered) +} + +func BdevLvolGetSnapshotChecksumCmd() cli.Command { + return cli.Command{ + Name: "get-snapshot-checksum", + Flags: []cli.Flag{ + cli.StringFlag{ + Name: "alias", + Usage: "The alias of a snapshot is /. Specify this or uuid", + }, + cli.StringFlag{ + Name: "uuid", + Usage: "Specify this or alias", + }, + }, + Usage: "get checksum of snapshot's data: \"get-snapshot-checksum --alias /\"," + + " or \"get-snapshot-checksum --uuid \"", + Action: func(c *cli.Context) { + if err := bdevLvolGetSnapshotChecksum(c); err != nil { + logrus.WithError(err).Fatalf("Failed to run get snapshot checksum command") + } + }, + } +} + +func bdevLvolGetSnapshotChecksum(c *cli.Context) error { + spdkCli, err := client.NewClient(context.Background()) + if err != nil { + return err + } + + name := c.String("alias") + if name == "" { + name = c.String("uuid") + } + if name == "" { + return fmt.Errorf("either alias or uuid must be provided") + } + + checksum, err := spdkCli.BdevLvolGetSnapshotChecksum(name) + if err != nil { + return fmt.Errorf("failed to get checksum for snapshot %q: %v", name, err) + } + if checksum == nil { + return fmt.Errorf("no checksum found for snapshot %q", name) + } + + return util.PrintObject(*checksum) +} diff --git a/pkg/spdk/client/basic.go b/pkg/spdk/client/basic.go index e257dab..8baf662 100644 --- a/pkg/spdk/client/basic.go +++ b/pkg/spdk/client/basic.go @@ -310,8 +310,9 @@ func (c *Client) BdevLvolGetWithFilter(name string, timeout uint64, filter func( // "snapshotName": Required. the logical volume name for the newly created snapshot. func (c *Client) BdevLvolSnapshot(name, snapshotName string, xattrs []Xattr) (uuid string, err error) { req := spdktypes.BdevLvolSnapshotRequest{ - LvolName: name, - SnapshotName: snapshotName, + LvolName: name, + SnapshotName: snapshotName, + EnableAddUpdateXattrs: true, } req.Xattrs = make(map[string]string) @@ -502,6 +503,44 @@ func (c *Client) BdevLvolGetFragmap(name string, offset, size uint64) (*spdktype return &result, nil } +// BdevLvolRegisterSnapshotChecksum compute and store checksum of snapshot's data. Overwrite old checksum if already registered. +// +// "name": Required. UUID or alias of the snapshot. The alias of a snapshot is /. +func (c *Client) BdevLvolRegisterSnapshotChecksum(name string) (registered bool, err error) { + req := spdktypes.BdevLvolRegisterSnapshotChecksumRequest{ + Name: name, + } + + cmdOutput, err := c.jsonCli.SendCommandWithLongTimeout("bdev_lvol_register_snapshot_checksum", req) + if err != nil { + return false, err + } + + return registered, json.Unmarshal(cmdOutput, ®istered) +} + +// BdevLvolGetSnapshotChecksum gets snapshot's stored checksum. The checksum must has been previously registered. +// +// "name": Required. UUID or alias of the snapshot. The alias of a snapshot is /. +func (c *Client) BdevLvolGetSnapshotChecksum(name string) (checksum *uint64, err error) { + req := spdktypes.BdevLvolGetSnapshotChecksumRequest{ + Name: name, + } + + cmdOutput, err := c.jsonCli.SendCommandWithLongTimeout("bdev_lvol_get_snapshot_checksum", req) + if err != nil { + return nil, err + } + + var snapshotChecksum spdktypes.BdevLvolSnapshotChecksum + err = json.Unmarshal(cmdOutput, &snapshotChecksum) + if err != nil { + return nil, err + } + + return &snapshotChecksum.Checksum, nil +} + // BdevLvolRename renames a logical volume. // // "oldName": Required. UUID or alias of the existing logical volume. diff --git a/pkg/spdk/types/lvol.go b/pkg/spdk/types/lvol.go index a246b4f..2d95ae0 100644 --- a/pkg/spdk/types/lvol.go +++ b/pkg/spdk/types/lvol.go @@ -104,9 +104,10 @@ type BdevLvolDeleteRequest struct { } type BdevLvolSnapshotRequest struct { - LvolName string `json:"lvol_name"` - SnapshotName string `json:"snapshot_name"` - Xattrs map[string]string `json:"xattrs,omitempty"` + LvolName string `json:"lvol_name"` + SnapshotName string `json:"snapshot_name"` + Xattrs map[string]string `json:"xattrs,omitempty"` + EnableAddUpdateXattrs bool `json:"enable_add_update_xattrs,omitempty"` } type BdevLvolCloneRequest struct { @@ -150,6 +151,18 @@ type BdevLvolRenameRequest struct { NewName string `json:"new_name"` } +type BdevLvolRegisterSnapshotChecksumRequest struct { + Name string `json:"name"` +} + +type BdevLvolGetSnapshotChecksumRequest struct { + Name string `json:"name"` +} + +type BdevLvolSnapshotChecksum struct { + Checksum uint64 `json:"checksum"` +} + func GetLvolAlias(lvsName, lvolName string) string { return fmt.Sprintf("%s/%s", lvsName, lvolName) }