From d190db0855d554227840fbab316843f1e29f4628 Mon Sep 17 00:00:00 2001 From: Damiano Cipriani Date: Tue, 25 Jun 2024 15:13:23 +0200 Subject: [PATCH] engine: add timestamp to user created snapshot Longhorn 7641 Signed-off-by: Damiano Cipriani --- pkg/api/types.go | 44 +++++++++++++++++++++----------------- pkg/client/client.go | 35 ++++++++++++++++++------------ pkg/spdk/engine.go | 14 ++++++++++-- pkg/spdk/replica.go | 51 ++++++++++++++++++++++++++++---------------- pkg/spdk/server.go | 14 ++++++++++-- 5 files changed, 102 insertions(+), 56 deletions(-) diff --git a/pkg/api/types.go b/pkg/api/types.go index 2116f0f3..3ea04627 100644 --- a/pkg/api/types.go +++ b/pkg/api/types.go @@ -9,6 +9,7 @@ import ( type SnapshotOptions struct { UserCreated bool + Timestamp string } type Replica struct { @@ -28,14 +29,15 @@ type Replica struct { } type Lvol struct { - Name string `json:"name"` - UUID string `json:"uuid"` - SpecSize uint64 `json:"spec_size"` - ActualSize uint64 `json:"actual_size"` - Parent string `json:"parent"` - Children map[string]bool `json:"children"` - CreationTime string `json:"creation_time"` - UserCreated bool `json:"user_created"` + Name string `json:"name"` + UUID string `json:"uuid"` + SpecSize uint64 `json:"spec_size"` + ActualSize uint64 `json:"actual_size"` + Parent string `json:"parent"` + Children map[string]bool `json:"children"` + CreationTime string `json:"creation_time"` + UserCreated bool `json:"user_created"` + SnapshotTimestamp string `json:"snapshot_timestamp"` } func ProtoLvolToLvol(l *spdkrpc.Lvol) *Lvol { @@ -45,12 +47,13 @@ func ProtoLvolToLvol(l *spdkrpc.Lvol) *Lvol { return &Lvol{ Name: l.Name, // UUID: l.Uuid, - SpecSize: l.SpecSize, - ActualSize: l.ActualSize, - Parent: l.Parent, - Children: l.Children, - CreationTime: l.CreationTime, - UserCreated: l.UserCreated, + SpecSize: l.SpecSize, + ActualSize: l.ActualSize, + Parent: l.Parent, + Children: l.Children, + CreationTime: l.CreationTime, + UserCreated: l.UserCreated, + SnapshotTimestamp: l.SnapshotTimestamp, } } @@ -61,12 +64,13 @@ func LvolToProtoLvol(l *Lvol) *spdkrpc.Lvol { return &spdkrpc.Lvol{ Name: l.Name, // Uuid: l.UUID, - SpecSize: l.SpecSize, - ActualSize: l.ActualSize, - Parent: l.Parent, - Children: l.Children, - CreationTime: l.CreationTime, - UserCreated: l.UserCreated, + SpecSize: l.SpecSize, + ActualSize: l.ActualSize, + Parent: l.Parent, + Children: l.Children, + CreationTime: l.CreationTime, + UserCreated: l.UserCreated, + SnapshotTimestamp: l.SnapshotTimestamp, } } diff --git a/pkg/client/client.go b/pkg/client/client.go index 8aec449b..c306a80a 100644 --- a/pkg/client/client.go +++ b/pkg/client/client.go @@ -139,19 +139,23 @@ func (c *SPDKClient) ReplicaWatch(ctx context.Context) (*api.ReplicaStream, erro } func (c *SPDKClient) ReplicaSnapshotCreate(name, snapshotName string, opts *api.SnapshotOptions) error { - if name == "" || snapshotName == "" { - return fmt.Errorf("failed to create SPDK replica snapshot: missing required parameter name or snapshot name") + if name == "" || snapshotName == "" || opts == nil { + return fmt.Errorf("failed to create SPDK replica snapshot: missing required parameter name, snapshot name or opts") } client := c.getSPDKServiceClient() ctx, cancel := context.WithTimeout(context.Background(), GRPCServiceTimeout) defer cancel() - _, err := client.ReplicaSnapshotCreate(ctx, &spdkrpc.SnapshotRequest{ - Name: name, - SnapshotName: snapshotName, - UserCreated: opts != nil && opts.UserCreated, - }) + snapshotRequest := spdkrpc.SnapshotRequest{ + Name: name, + SnapshotName: snapshotName, + UserCreated: opts.UserCreated, + SnaphotTimestamp: opts.Timestamp, + } + + _, err := client.ReplicaSnapshotCreate(ctx, &snapshotRequest) + return errors.Wrapf(err, "failed to create SPDK replica %s snapshot %s", name, snapshotName) } @@ -320,19 +324,22 @@ func (c *SPDKClient) ReplicaRebuildingDstFinish(replicaName string, unexposeRequ } func (c *SPDKClient) ReplicaRebuildingDstSnapshotCreate(name, snapshotName string, opts *api.SnapshotOptions) error { - if name == "" || snapshotName == "" { - return fmt.Errorf("failed to create dst SPDK replica rebuilding snapshot: missing required parameter name or snapshot name") + if name == "" || snapshotName == "" || opts == nil { + return fmt.Errorf("failed to create dst SPDK replica rebuilding snapshot: missing required parameter name, snapshot name or opts") } client := c.getSPDKServiceClient() ctx, cancel := context.WithTimeout(context.Background(), GRPCServiceTimeout) defer cancel() - _, err := client.ReplicaRebuildingDstSnapshotCreate(ctx, &spdkrpc.SnapshotRequest{ - Name: name, - SnapshotName: snapshotName, - UserCreated: opts != nil && opts.UserCreated, - }) + snapshotRequest := spdkrpc.SnapshotRequest{ + Name: name, + SnapshotName: snapshotName, + UserCreated: opts.UserCreated, + SnaphotTimestamp: opts.Timestamp, + } + + _, err := client.ReplicaRebuildingDstSnapshotCreate(ctx, &snapshotRequest) return errors.Wrapf(err, "failed to create dst SPDK replica %s rebuilding snapshot %s", name, snapshotName) } diff --git a/pkg/spdk/engine.go b/pkg/spdk/engine.go index 54ac9693..81b326f2 100644 --- a/pkg/spdk/engine.go +++ b/pkg/spdk/engine.go @@ -772,7 +772,10 @@ func (e *Engine) ReplicaAddStart(spdkClient *spdkclient.Client, replicaName, rep // TODO: For online rebuilding, the IO should be paused first snapshotName := GenerateRebuildingSnapshotName() - updateRequired, err = e.snapshotOperationWithoutLock(spdkClient, replicaClients, snapshotName, SnapshotOperationCreate, nil) + opts := api.SnapshotOptions{ + Timestamp: time.Now().String(), + } + updateRequired, err = e.snapshotOperationWithoutLock(spdkClient, replicaClients, snapshotName, SnapshotOperationCreate, &opts) if err != nil { return err } @@ -963,7 +966,13 @@ func (e *Engine) ReplicaShallowCopy(dstReplicaName, dstReplicaAddress string) (e if err = srcReplicaServiceCli.ReplicaSnapshotShallowCopy(srcReplicaName, currentSnapshotName); err != nil { return err } - if err = dstReplicaServiceCli.ReplicaRebuildingDstSnapshotCreate(dstReplicaName, currentSnapshotName, &api.SnapshotOptions{UserCreated: rpcSrcReplica.Snapshots[currentSnapshotName].UserCreated}); err != nil { + + snapshotOptions := api.SnapshotOptions{ + UserCreated: rpcSrcReplica.Snapshots[currentSnapshotName].UserCreated, + Timestamp: rpcSrcReplica.Snapshots[currentSnapshotName].SnapshotTimestamp, + } + + if err = dstReplicaServiceCli.ReplicaRebuildingDstSnapshotCreate(dstReplicaName, currentSnapshotName, &snapshotOptions); err != nil { return err } prevSnapshotName = currentSnapshotName @@ -1066,6 +1075,7 @@ func (e *Engine) SnapshotCreate(spdkClient *spdkclient.Client, inputSnapshotName opts := api.SnapshotOptions{ UserCreated: true, + Timestamp: time.Now().String(), } return e.snapshotOperation(spdkClient, inputSnapshotName, SnapshotOperationCreate, &opts) diff --git a/pkg/spdk/replica.go b/pkg/spdk/replica.go index 6d793da7..b429b769 100644 --- a/pkg/spdk/replica.go +++ b/pkg/spdk/replica.go @@ -92,9 +92,10 @@ type Lvol struct { ActualSize uint64 Parent string // Children is map[] rather than map[]. consists of `-snap-` - Children map[string]*Lvol - CreationTime string - UserCreated bool + Children map[string]*Lvol + CreationTime string + UserCreated bool + SnapshotTimestamp string } func ServiceReplicaToProtoReplica(r *Replica) *spdkrpc.Replica { @@ -122,13 +123,14 @@ func ServiceReplicaToProtoReplica(r *Replica) *spdkrpc.Replica { func ServiceLvolToProtoLvol(replicaName string, lvol *Lvol) *spdkrpc.Lvol { res := &spdkrpc.Lvol{ - Uuid: lvol.UUID, - SpecSize: lvol.SpecSize, - ActualSize: lvol.ActualSize, - Parent: GetSnapshotNameFromReplicaSnapshotLvolName(replicaName, lvol.Parent), - Children: map[string]bool{}, - CreationTime: lvol.CreationTime, - UserCreated: lvol.UserCreated, + Uuid: lvol.UUID, + SpecSize: lvol.SpecSize, + ActualSize: lvol.ActualSize, + Parent: GetSnapshotNameFromReplicaSnapshotLvolName(replicaName, lvol.Parent), + Children: map[string]bool{}, + CreationTime: lvol.CreationTime, + UserCreated: lvol.UserCreated, + SnapshotTimestamp: lvol.SnapshotTimestamp, } if lvol.Name == replicaName { @@ -158,9 +160,10 @@ func BdevLvolInfoToServiceLvol(bdev *spdktypes.BdevInfo) *Lvol { ActualSize: bdev.DriverSpecific.Lvol.NumAllocatedClusters * defaultClusterSize, Parent: bdev.DriverSpecific.Lvol.BaseSnapshot, // Need to update this separately - Children: map[string]*Lvol{}, - CreationTime: bdev.CreationTime, - UserCreated: bdev.DriverSpecific.Lvol.Xattrs[spdkclient.UserCreated] == "true", + Children: map[string]*Lvol{}, + CreationTime: bdev.CreationTime, + UserCreated: bdev.DriverSpecific.Lvol.Xattrs[spdkclient.UserCreated] == "true", + SnapshotTimestamp: bdev.DriverSpecific.Lvol.Xattrs[spdkclient.SnapshotTimestamp], } } @@ -403,7 +406,7 @@ func compareSvcLvols(prev, cur *Lvol, checkChildren, checkActualSize bool) error if cur == nil { return fmt.Errorf("cannot find the corresponding cur lvol") } - if prev.Name != cur.Name || prev.UUID != cur.UUID || prev.CreationTime != cur.CreationTime || prev.SpecSize != cur.SpecSize || prev.Parent != cur.Parent || len(prev.Children) != len(cur.Children) { + if prev.Name != cur.Name || prev.UUID != cur.UUID || prev.SnapshotTimestamp != cur.SnapshotTimestamp || prev.SpecSize != cur.SpecSize || prev.Parent != cur.Parent || len(prev.Children) != len(cur.Children) { return fmt.Errorf("found mismatching lvol %+v with recorded prev lvol %+v", cur, prev) } if checkChildren { @@ -830,11 +833,17 @@ func (r *Replica) SnapshotCreate(spdkClient *spdkclient.Client, snapshotName str var xattrs []spdkclient.Xattr if opts != nil { - xattr := spdkclient.Xattr{ + userCreated := spdkclient.Xattr{ Name: spdkclient.UserCreated, Value: strconv.FormatBool(opts.UserCreated), } - xattrs = append(xattrs, xattr) + xattrs = append(xattrs, userCreated) + + snapshotTimestamp := spdkclient.Xattr{ + Name: spdkclient.SnapshotTimestamp, + Value: opts.Timestamp, + } + xattrs = append(xattrs, snapshotTimestamp) } snapUUID, err := spdkClient.BdevLvolSnapshot(headSvcLvol.UUID, snapLvolName, xattrs) @@ -1446,11 +1455,17 @@ func (r *Replica) RebuildingDstSnapshotCreate(spdkClient *spdkclient.Client, sna var xattrs []spdkclient.Xattr if opts != nil { - xattr := spdkclient.Xattr{ + userCreated := spdkclient.Xattr{ Name: spdkclient.UserCreated, Value: strconv.FormatBool(opts.UserCreated), } - xattrs = append(xattrs, xattr) + xattrs = append(xattrs, userCreated) + + snapshotTimestamp := spdkclient.Xattr{ + Name: spdkclient.SnapshotTimestamp, + Value: opts.Timestamp, + } + xattrs = append(xattrs, snapshotTimestamp) } snapLvolName := GetReplicaSnapshotLvolName(r.Name, snapshotName) diff --git a/pkg/spdk/server.go b/pkg/spdk/server.go index 06ae6ce5..2e30789a 100644 --- a/pkg/spdk/server.go +++ b/pkg/spdk/server.go @@ -452,7 +452,12 @@ func (s *Server) ReplicaSnapshotCreate(ctx context.Context, req *spdkrpc.Snapsho return nil, grpcstatus.Errorf(grpccodes.NotFound, "cannot find replica %s during snapshot create", req.Name) } - return r.SnapshotCreate(spdkClient, req.SnapshotName, &api.SnapshotOptions{UserCreated: req.UserCreated}) + opts := &api.SnapshotOptions{ + UserCreated: req.UserCreated, + Timestamp: req.SnaphotTimestamp, + } + + return r.SnapshotCreate(spdkClient, req.SnapshotName, opts) } func (s *Server) ReplicaSnapshotDelete(ctx context.Context, req *spdkrpc.SnapshotRequest) (ret *emptypb.Empty, err error) { @@ -660,7 +665,12 @@ func (s *Server) ReplicaRebuildingDstSnapshotCreate(ctx context.Context, req *sp return nil, grpcstatus.Errorf(grpccodes.NotFound, "cannot find replica %s during rebuilding dst snapshot create", req.Name) } - if err = r.RebuildingDstSnapshotCreate(spdkClient, req.SnapshotName, &api.SnapshotOptions{UserCreated: req.UserCreated}); err != nil { + opts := &api.SnapshotOptions{ + UserCreated: req.UserCreated, + Timestamp: req.SnaphotTimestamp, + } + + if err = r.RebuildingDstSnapshotCreate(spdkClient, req.SnapshotName, opts); err != nil { return nil, err } return &emptypb.Empty{}, nil