From 285758003935c46a20679763cace5ca4e73444e0 Mon Sep 17 00:00:00 2001 From: Blake Howell Date: Fri, 24 Jun 2016 15:22:13 -0400 Subject: [PATCH] Allow Volume to be Resized --- block/etcd.go | 31 +++++++++++++++++++++++++++++++ block/metadata.go | 1 + block/temp.go | 15 +++++++++++++++ block/volume.go | 20 ++++++++++++++++++++ cmd/torusctl/volume.go | 41 +++++++++++++++++++++++++++++++++++++++++ metadata/temp/temp.go | 12 ++++++++++++ 6 files changed, 120 insertions(+) diff --git a/block/etcd.go b/block/etcd.go index 9a33647..6ecb4cf 100644 --- a/block/etcd.go +++ b/block/etcd.go @@ -69,6 +69,37 @@ func (b *blockEtcd) DeleteVolume() error { } +func (b *blockEtcd) ResizeVolume(size uint64) error { + vid := uint64(b.vid) + volumeKey := etcd.MkKey("volumeid", etcd.Uint64ToHex(vid)) + resp, err := b.Etcd.Client.Get(b.getContext(), volumeKey) + if err != nil { + return err + } + var version int64 + var value []byte + kv := resp.Kvs[0] + version = kv.Version + value = kv.Value + var volume models.Volume + err = volume.Unmarshal(value) + volume.MaxBytes = size + vbytes, err := volume.Marshal() + tx := b.Etcd.Client.Txn(b.getContext()).If( + etcdv3.Compare(etcdv3.Version(volumeKey), "=", version), + ).Then( + etcdv3.OpPut(etcd.MkKey("volumeid", etcd.Uint64ToHex(vid)), string(vbytes)), + ) + txresp, err := tx.Commit() + if err != nil { + return err + } + if !txresp.Succeeded { + return torus.ErrCompareFailed + } + return nil +} + func (b *blockEtcd) getContext() context.Context { return context.TODO() } diff --git a/block/metadata.go b/block/metadata.go index 725dd6a..50cf098 100644 --- a/block/metadata.go +++ b/block/metadata.go @@ -28,6 +28,7 @@ type blockMetadata interface { CreateBlockVolume(vol *models.Volume) error DeleteVolume() error + ResizeVolume(size uint64) error SaveSnapshot(name string) error GetSnapshots() ([]Snapshot, error) diff --git a/block/temp.go b/block/temp.go index c15064b..cd23f6e 100644 --- a/block/temp.go +++ b/block/temp.go @@ -106,6 +106,21 @@ func (b *blockTempMetadata) DeleteVolume() error { return b.Client.DeleteVolume(b.name) } +func (b *blockTempMetadata) ResizeVolume(size uint64) error { + b.LockData() + defer b.UnlockData() + v, ok := b.GetData(fmt.Sprint(b.vid)) + if !ok { + return torus.ErrNotExist + } + d := v.(*blockTempVolumeData) + if d.locked != b.UUID() { + return torus.ErrLocked + } + return b.Client.ResizeVolume(b.name, size) + +} + func (b *blockTempMetadata) SaveSnapshot(name string) error { b.LockData() defer b.UnlockData() diff --git a/block/volume.go b/block/volume.go index f833278..693e669 100644 --- a/block/volume.go +++ b/block/volume.go @@ -60,6 +60,26 @@ func DeleteBlockVolume(mds torus.MetadataService, volume string) error { return bmds.DeleteVolume() } +func (s *BlockVolume) ResizeBlockVolume(mds torus.MetadataService, volume string, size uint64) error { + vol, err := mds.GetVolume(volume) + if err != nil { + return err + } + bmds, err := createBlockMetadata(mds, vol.Name, torus.VolumeID(vol.Id)) + if err != nil { + return err + } + if err = bmds.Lock(s.srv.Lease()); err != nil { + return err + } + defer func() { + if err != nil { + s.mds.Unlock() + } + }() + return bmds.ResizeVolume(size) +} + func (s *BlockVolume) SaveSnapshot(name string) error { return s.mds.SaveSnapshot(name) } func (s *BlockVolume) GetSnapshots() ([]Snapshot, error) { return s.mds.GetSnapshots() } func (s *BlockVolume) DeleteSnapshot(name string) error { return s.mds.DeleteSnapshot(name) } diff --git a/cmd/torusctl/volume.go b/cmd/torusctl/volume.go index 4da57bd..9991b4c 100644 --- a/cmd/torusctl/volume.go +++ b/cmd/torusctl/volume.go @@ -33,10 +33,18 @@ var volumeCreateBlockCommand = &cobra.Command{ Run: volumeCreateBlockAction, } +var volumeResizeBlockCommand = &cobra.Command{ + Use: "resize-block NAME SIZE", + Short: "resize a block volume in the cluster", + Long: "resize a block volume named NAME of size SIZE bytes (G,GiB,M,MiB,etc suffixes accepted)", + Run: volumeResizeBlockAction, +} + func init() { volumeCommand.AddCommand(volumeDeleteCommand) volumeCommand.AddCommand(volumeListCommand) volumeCommand.AddCommand(volumeCreateBlockCommand) + volumeCommand.AddCommand(volumeResizeBlockCommand) volumeListCommand.Flags().BoolVarP(&outputAsCSV, "csv", "", false, "output as csv instead") } @@ -78,6 +86,7 @@ func volumeDeleteAction(cmd *cobra.Command, args []string) { } name := args[0] mds := mustConnectToMDS() + vol, err := mds.GetVolume(name) if err != nil { die("cannot get volume %s (perhaps it doesn't exist): %v", name, err) @@ -108,3 +117,35 @@ func volumeCreateBlockAction(cmd *cobra.Command, args []string) { die("error creating volume %s: %v", args[0], err) } } + +func volumeResizeBlockAction(cmd *cobra.Command, args []string) { + mds := mustConnectToMDS() + if len(args) != 2 { + cmd.Usage() + os.Exit(1) + } + name := args[0] + size, err := humanize.ParseBytes(args[1]) + if err != nil { + die("error parsing size %s: %v", args[1], err) + } + vol, err := mds.GetVolume(name) + if err != nil { + die("cannot get volume %s (perhaps it doesn't exist): %v", name, err) + } + switch vol.Type { + case "block": + srv := createServer() + defer srv.Close() + blockvol, err := block.OpenBlockVolume(srv, vol.Name) + if err != nil { + die("couldn't open block volume %s: %v", vol.Name, err) + } + err = blockvol.ResizeBlockVolume(mds, args[0], size) + default: + die("unknown volume type %s", vol.Type) + } + if err != nil { + die("error resizing volume %s: %v", name, err) + } +} diff --git a/metadata/temp/temp.go b/metadata/temp/temp.go index 0881a1e..4cca58d 100644 --- a/metadata/temp/temp.go +++ b/metadata/temp/temp.go @@ -148,6 +148,18 @@ func (t *Client) CreateVolume(volume *models.Volume) error { return nil } +func (t *Client) ResizeVolume(name string, size uint64) error { + t.srv.mut.Lock() + defer t.srv.mut.Unlock() + volume, err := t.srv.volIndex[name] + if err { + return torus.ErrBlockNotExist + } + volume.MaxBytes = size + t.srv.volIndex[volume.Name] = volume + return nil +} + func (t *Client) GetVolume(volume string) (*models.Volume, error) { t.srv.mut.Lock() defer t.srv.mut.Unlock()