Skip to content

Commit

Permalink
api: Add a method for extending VMDK
Browse files Browse the repository at this point in the history
Signed-off-by: Stoyan Zhelyazkov <[email protected]>
  • Loading branch information
spacegospod committed Jul 1, 2024
1 parent 71f407c commit 3139de3
Show file tree
Hide file tree
Showing 5 changed files with 226 additions and 0 deletions.
18 changes: 18 additions & 0 deletions govc/USAGE.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ but appear via `govc $cmd -h`:
- [datastore.cp](#datastorecp)
- [datastore.create](#datastorecreate)
- [datastore.disk.create](#datastorediskcreate)
- [datastore.disk.extend](#datastorediskextend)
- [datastore.disk.inflate](#datastorediskinflate)
- [datastore.disk.info](#datastorediskinfo)
- [datastore.disk.shrink](#datastorediskshrink)
Expand Down Expand Up @@ -1222,6 +1223,23 @@ Options:
-uuid= Disk UUID
```

## datastore.disk.extend

```
Usage: govc datastore.disk.extend [OPTIONS] VMDK
Extend VMDK on DS.
Examples:
govc datastore.disk.extend disks/disk1.vmdk -size=24G
govc datastore.disk.extend disks/disk1.vmdk -size=24G -eagerZero=true
Options:
-ds= Datastore [GOVC_DATASTORE]
-eagerZero=false If true, the extended part of the disk will be explicitly filled with zeroes
-size=0B New capacity for the disk
```

## datastore.disk.inflate

```
Expand Down
92 changes: 92 additions & 0 deletions govc/datastore/disk/extend.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
/*
Copyright (c) 2024-2024 VMware, Inc. All Rights Reserved.
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
https://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 disk

import (
"context"
"flag"
"fmt"

"github.com/vmware/govmomi/units"

"github.com/vmware/govmomi/govc/cli"
"github.com/vmware/govmomi/govc/flags"
"github.com/vmware/govmomi/object"
)

type extend struct {
*flags.DatastoreFlag
Bytes units.ByteSize
EagerZero bool
}

func init() {
cli.Register("datastore.disk.extend", &extend{})
}

func (cmd *extend) Register(ctx context.Context, f *flag.FlagSet) {
cmd.DatastoreFlag, ctx = flags.NewDatastoreFlag(ctx)
cmd.DatastoreFlag.Register(ctx, f)

f.Var(&cmd.Bytes, "size", "New capacity for the disk")
f.BoolVar(&cmd.EagerZero, "eagerZero", false, "If true, the extended part of the disk will be explicitly filled with zeroes")
}

func (cmd *extend) Process(ctx context.Context) error {
return cmd.DatastoreFlag.Process(ctx)
}

func (cmd *extend) Usage() string {
return "VMDK"
}

func (cmd *extend) Description() string {
return `Extend VMDK on DS.
Examples:
govc datastore.disk.extend disks/disk1.vmdk -size=24G
govc datastore.disk.extend disks/disk1.vmdk -size=24G -eagerZero=true`
}

func (cmd *extend) Run(ctx context.Context, f *flag.FlagSet) error {
if f.NArg() == 0 {
return flag.ErrHelp
}

dc, err := cmd.Datacenter()
if err != nil {
return err
}

ds, err := cmd.Datastore()
if err != nil {
return err
}

m := object.NewVirtualDiskManager(ds.Client())
path := ds.Path(f.Arg(0))
task, err := m.ExtendVirtualDisk(ctx, path, dc, int64(cmd.Bytes/1024), &cmd.EagerZero)
if err != nil {
return err
}

logger := cmd.ProgressLogger(fmt.Sprintf("Extending %s...", path))
defer logger.Wait()

_, err = task.WaitForResult(ctx, logger)
return err
}
27 changes: 27 additions & 0 deletions object/virtual_disk_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,33 @@ func (m VirtualDiskManager) CreateVirtualDisk(
return NewTask(m.c, res.Returnval), nil
}

// ExtendVirtualDisk extends an existing virtual disk.
func (m VirtualDiskManager) ExtendVirtualDisk(
ctx context.Context,
name string, datacenter *Datacenter,
capacityKb int64,
eagerZero *bool) (*Task, error) {

req := types.ExtendVirtualDisk_Task{
This: m.Reference(),
Name: name,
NewCapacityKb: capacityKb,
EagerZero: eagerZero,
}

if datacenter != nil {
ref := datacenter.Reference()
req.Datacenter = &ref
}

res, err := methods.ExtendVirtualDisk_Task(ctx, m.c, &req)
if err != nil {
return nil, err
}

return NewTask(m.c, res.Returnval), nil
}

// MoveVirtualDisk moves a virtual disk.
func (m VirtualDiskManager) MoveVirtualDisk(
ctx context.Context,
Expand Down
63 changes: 63 additions & 0 deletions simulator/virtual_disk_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package simulator
import (
"fmt"
"os"
"strconv"
"strings"

"github.com/google/uuid"
Expand Down Expand Up @@ -76,12 +77,59 @@ func vdmCreateVirtualDisk(op types.VirtualDeviceConfigSpecFileOperation, req *ty
return fm.fault(name, err, new(types.CannotCreateFile))
}

if req.Spec != nil {
var spec interface{} = req.Spec
fileBackedSpec, ok := spec.(*types.FileBackedVirtualDiskSpec)

if !ok {
return fm.fault(name, nil, new(types.FileFault))
}

if _, err = f.WriteString(strconv.FormatInt(fileBackedSpec.CapacityKb, 10)); err != nil {
return fm.fault(name, err, new(types.FileFault))
}
}

_ = f.Close()
}

return nil
}

func vdmExtendVirtualDisk(req *types.ExtendVirtualDisk_Task) types.BaseMethodFault {
fm := Map.FileManager()

file, fault := fm.resolve(req.Datacenter, req.Name)
if fault != nil {
return fault
}

for _, name := range vdmNames(file) {
_, err := os.Stat(name)
if err == nil {
content, err := os.ReadFile(name)
if err != nil {
return fm.fault(name, err, new(types.FileFault))
}

capacity, err := strconv.Atoi(string(content))
if err != nil {
return fm.fault(name, err, new(types.FileFault))
}

if int64(capacity) > req.NewCapacityKb {
// cannot shrink disk
return fm.fault(name, nil, new(types.FileFault))
}
return nil
} else {
return fm.fault(name, nil, new(types.FileNotFound))
}
}

return nil
}

func (m *VirtualDiskManager) CreateVirtualDiskTask(ctx *Context, req *types.CreateVirtualDisk_Task) soap.HasFault {
task := CreateTask(m, "createVirtualDisk", func(*Task) (types.AnyType, types.BaseMethodFault) {
if err := vdmCreateVirtualDisk(types.VirtualDeviceConfigSpecFileOperationCreate, req); err != nil {
Expand All @@ -97,6 +145,21 @@ func (m *VirtualDiskManager) CreateVirtualDiskTask(ctx *Context, req *types.Crea
}
}

func (m *VirtualDiskManager) ExtendVirtualDiskTask(ctx *Context, req *types.ExtendVirtualDisk_Task) soap.HasFault {
task := CreateTask(m, "extendVirtualDisk", func(*Task) (types.AnyType, types.BaseMethodFault) {
if err := vdmExtendVirtualDisk(req); err != nil {
return "", err
}
return req.Name, nil
})

return &methods.ExtendVirtualDisk_TaskBody{
Res: &types.ExtendVirtualDisk_TaskResponse{
Returnval: task.Run(ctx),
},
}
}

func (m *VirtualDiskManager) DeleteVirtualDiskTask(ctx *Context, req *types.DeleteVirtualDisk_Task) soap.HasFault {
task := CreateTask(m, "deleteVirtualDisk", func(*Task) (types.AnyType, types.BaseMethodFault) {
fm := Map.FileManager()
Expand Down
26 changes: 26 additions & 0 deletions simulator/virtual_disk_manager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,32 @@ func TestVirtualDiskManager(t *testing.T) {
}

qname := name
for i, fail := range []bool{false, true, true} {
if i == 1 {
spec.CapacityKb = 0
}

if i == 2 {
qname += "_missing_file"
}
task, err := dm.ExtendVirtualDisk(ctx, qname, nil, spec.CapacityKb*2, nil)
if err != nil {
t.Fatal(err)
}

err = task.Wait(ctx)
if fail {
if err == nil {
t.Error("expected error")
}
} else {
if err != nil {
t.Error(err)
}
}
}

qname = name
for _, fail := range []bool{false, true} {
id, err := dm.QueryVirtualDiskUuid(ctx, qname, nil)
if fail {
Expand Down

0 comments on commit 3139de3

Please sign in to comment.