github.com/vmware/govmomi@v0.51.0/simulator/virtual_disk_manager.go (about)

     1  // © Broadcom. All Rights Reserved.
     2  // The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
     3  // SPDX-License-Identifier: Apache-2.0
     4  
     5  package simulator
     6  
     7  import (
     8  	"errors"
     9  	"fmt"
    10  	"os"
    11  	"path/filepath"
    12  	"strings"
    13  
    14  	"github.com/vmware/govmomi/internal"
    15  	"github.com/vmware/govmomi/vim25/methods"
    16  	"github.com/vmware/govmomi/vim25/mo"
    17  	"github.com/vmware/govmomi/vim25/soap"
    18  	"github.com/vmware/govmomi/vim25/types"
    19  	"github.com/vmware/govmomi/vmdk"
    20  )
    21  
    22  type VirtualDiskManager struct {
    23  	mo.VirtualDiskManager
    24  }
    25  
    26  func (m *VirtualDiskManager) MO() mo.VirtualDiskManager {
    27  	return m.VirtualDiskManager
    28  }
    29  
    30  func VirtualDiskBackingFileName(name string) string {
    31  	return strings.Replace(name, ".vmdk", "-flat.vmdk", 1)
    32  }
    33  
    34  func vdmNames(name string) []string {
    35  	return []string{
    36  		VirtualDiskBackingFileName(name),
    37  		name,
    38  	}
    39  }
    40  
    41  func vdmCreateVirtualDisk(ctx *Context, op types.VirtualDeviceConfigSpecFileOperation, req *types.CreateVirtualDisk_Task) types.BaseMethodFault {
    42  	fm := ctx.Map.FileManager()
    43  
    44  	file, fault := fm.resolve(ctx, req.Datacenter, req.Name)
    45  	if fault != nil {
    46  		return fault
    47  	}
    48  
    49  	shouldReplace := op == types.VirtualDeviceConfigSpecFileOperationReplace
    50  	shouldExist := op == ""
    51  
    52  	_, err := os.Stat(file)
    53  	if err == nil {
    54  		if shouldExist {
    55  			return nil
    56  		}
    57  		if !shouldReplace {
    58  			return fm.fault(file, nil, new(types.FileAlreadyExists))
    59  		}
    60  	} else if shouldExist {
    61  		return fm.fault(file, nil, new(types.FileNotFound))
    62  	}
    63  
    64  	backing := VirtualDiskBackingFileName(file)
    65  
    66  	extent := vmdk.Extent{
    67  		Info: filepath.Base(backing),
    68  	}
    69  
    70  	f, err := os.Create(file)
    71  	if err != nil {
    72  		return fm.fault(file, err, new(types.CannotCreateFile))
    73  	}
    74  
    75  	defer f.Close()
    76  
    77  	if req.Spec != nil {
    78  		spec, ok := req.Spec.(types.BaseFileBackedVirtualDiskSpec)
    79  		if !ok {
    80  			return fm.fault(file, nil, new(types.FileFault))
    81  		}
    82  
    83  		fileSpec := spec.GetFileBackedVirtualDiskSpec()
    84  		extent.Size = fileSpec.CapacityKb * 1024 / vmdk.SectorSize
    85  	}
    86  
    87  	desc := vmdk.NewDescriptor(extent)
    88  	if err := desc.Write(f); err != nil {
    89  		return fm.fault(file, err, new(types.FileFault))
    90  	}
    91  
    92  	b, err := os.Create(backing)
    93  	if err != nil {
    94  		return fm.fault(backing, err, new(types.CannotCreateFile))
    95  	}
    96  	_ = b.Close()
    97  
    98  	return nil
    99  }
   100  
   101  func vdmExtendVirtualDisk(ctx *Context, req *types.ExtendVirtualDisk_Task) types.BaseMethodFault {
   102  	fm := ctx.Map.FileManager()
   103  
   104  	desc, file, fault := fm.DiskDescriptor(ctx, req.Datacenter, req.Name)
   105  	if fault != nil {
   106  		return fault
   107  	}
   108  
   109  	newCapacity := req.NewCapacityKb * 1024
   110  	if desc.Capacity() > newCapacity {
   111  		return fm.fault(req.Name, errors.New("cannot shrink disk"), new(types.FileFault))
   112  	}
   113  
   114  	desc.Extent[0].Size = newCapacity / vmdk.SectorSize
   115  
   116  	return fm.SaveDiskDescriptor(ctx, desc, file)
   117  }
   118  
   119  func (m *VirtualDiskManager) CreateVirtualDiskTask(ctx *Context, req *types.CreateVirtualDisk_Task) soap.HasFault {
   120  	task := CreateTask(m, "createVirtualDisk", func(*Task) (types.AnyType, types.BaseMethodFault) {
   121  		if err := vdmCreateVirtualDisk(ctx, types.VirtualDeviceConfigSpecFileOperationCreate, req); err != nil {
   122  			return "", err
   123  		}
   124  		return req.Name, nil
   125  	})
   126  
   127  	return &methods.CreateVirtualDisk_TaskBody{
   128  		Res: &types.CreateVirtualDisk_TaskResponse{
   129  			Returnval: task.Run(ctx),
   130  		},
   131  	}
   132  }
   133  
   134  func (m *VirtualDiskManager) ExtendVirtualDiskTask(ctx *Context, req *types.ExtendVirtualDisk_Task) soap.HasFault {
   135  	task := CreateTask(m, "extendVirtualDisk", func(*Task) (types.AnyType, types.BaseMethodFault) {
   136  		if err := vdmExtendVirtualDisk(ctx, req); err != nil {
   137  			return "", err
   138  		}
   139  		return req.Name, nil
   140  	})
   141  
   142  	return &methods.ExtendVirtualDisk_TaskBody{
   143  		Res: &types.ExtendVirtualDisk_TaskResponse{
   144  			Returnval: task.Run(ctx),
   145  		},
   146  	}
   147  }
   148  
   149  func (m *VirtualDiskManager) DeleteVirtualDiskTask(ctx *Context, req *types.DeleteVirtualDisk_Task) soap.HasFault {
   150  	task := CreateTask(m, "deleteVirtualDisk", func(*Task) (types.AnyType, types.BaseMethodFault) {
   151  		fm := ctx.Map.FileManager()
   152  
   153  		for _, name := range vdmNames(req.Name) {
   154  			err := fm.deleteDatastoreFile(ctx, &types.DeleteDatastoreFile_Task{
   155  				Name:       name,
   156  				Datacenter: req.Datacenter,
   157  			})
   158  
   159  			if err != nil {
   160  				return nil, err
   161  			}
   162  		}
   163  
   164  		return nil, nil
   165  	})
   166  
   167  	return &methods.DeleteVirtualDisk_TaskBody{
   168  		Res: &types.DeleteVirtualDisk_TaskResponse{
   169  			Returnval: task.Run(ctx),
   170  		},
   171  	}
   172  }
   173  
   174  func (m *VirtualDiskManager) MoveVirtualDiskTask(ctx *Context, req *types.MoveVirtualDisk_Task) soap.HasFault {
   175  	task := CreateTask(m, "moveVirtualDisk", func(*Task) (types.AnyType, types.BaseMethodFault) {
   176  		fm := ctx.Map.FileManager()
   177  
   178  		dest := vdmNames(req.DestName)
   179  
   180  		for i, name := range vdmNames(req.SourceName) {
   181  			err := fm.moveDatastoreFile(ctx, &types.MoveDatastoreFile_Task{
   182  				SourceName:            name,
   183  				SourceDatacenter:      req.SourceDatacenter,
   184  				DestinationName:       dest[i],
   185  				DestinationDatacenter: req.DestDatacenter,
   186  				Force:                 req.Force,
   187  			})
   188  
   189  			if err != nil {
   190  				return nil, err
   191  			}
   192  		}
   193  
   194  		return nil, nil
   195  	})
   196  
   197  	return &methods.MoveVirtualDisk_TaskBody{
   198  		Res: &types.MoveVirtualDisk_TaskResponse{
   199  			Returnval: task.Run(ctx),
   200  		},
   201  	}
   202  }
   203  
   204  func (m *VirtualDiskManager) CopyVirtualDiskTask(ctx *Context, req *types.CopyVirtualDisk_Task) soap.HasFault {
   205  	task := CreateTask(m, "copyVirtualDisk", func(*Task) (types.AnyType, types.BaseMethodFault) {
   206  		if req.DestSpec != nil {
   207  			// TODO: apply to destination vmdk.Descriptor
   208  		}
   209  
   210  		fm := ctx.Map.FileManager()
   211  
   212  		dest := vdmNames(req.DestName)
   213  
   214  		for i, name := range vdmNames(req.SourceName) {
   215  			err := fm.copyDatastoreFile(ctx, &types.CopyDatastoreFile_Task{
   216  				SourceName:            name,
   217  				SourceDatacenter:      req.SourceDatacenter,
   218  				DestinationName:       dest[i],
   219  				DestinationDatacenter: req.DestDatacenter,
   220  				Force:                 req.Force,
   221  			})
   222  
   223  			if err != nil {
   224  				return nil, err
   225  			}
   226  		}
   227  
   228  		return nil, nil
   229  	})
   230  
   231  	return &methods.CopyVirtualDisk_TaskBody{
   232  		Res: &types.CopyVirtualDisk_TaskResponse{
   233  			Returnval: task.Run(ctx),
   234  		},
   235  	}
   236  }
   237  
   238  func virtualDiskUUID(dc *types.ManagedObjectReference, file string) string {
   239  	if dc != nil {
   240  		file = dc.String() + file
   241  	}
   242  	return newUUID(file)
   243  }
   244  
   245  func (m *VirtualDiskManager) QueryVirtualDiskUuid(ctx *Context, req *types.QueryVirtualDiskUuid) soap.HasFault {
   246  	body := new(methods.QueryVirtualDiskUuidBody)
   247  
   248  	fm := ctx.Map.FileManager()
   249  
   250  	file, fault := fm.resolve(ctx, req.Datacenter, req.Name)
   251  	if fault != nil {
   252  		body.Fault_ = Fault("", fault)
   253  		return body
   254  	}
   255  
   256  	_, err := os.Stat(file)
   257  	if err != nil {
   258  		fault = fm.fault(req.Name, err, new(types.CannotAccessFile))
   259  		body.Fault_ = Fault(fmt.Sprintf("File %s was not found", req.Name), fault)
   260  		return body
   261  	}
   262  
   263  	body.Res = &types.QueryVirtualDiskUuidResponse{
   264  		Returnval: virtualDiskUUID(req.Datacenter, req.Name),
   265  	}
   266  
   267  	return body
   268  }
   269  
   270  func (m *VirtualDiskManager) SetVirtualDiskUuid(_ *Context, req *types.SetVirtualDiskUuid) soap.HasFault {
   271  	body := new(methods.SetVirtualDiskUuidBody)
   272  	// TODO: validate uuid format and persist
   273  	body.Res = new(types.SetVirtualDiskUuidResponse)
   274  	return body
   275  }
   276  
   277  func (m *VirtualDiskManager) QueryVirtualDiskInfoTask(ctx *Context, req *internal.QueryVirtualDiskInfoTaskRequest) soap.HasFault {
   278  	task := CreateTask(m, "queryVirtualDiskInfo", func(*Task) (types.AnyType, types.BaseMethodFault) {
   279  		var res []internal.VirtualDiskInfo
   280  
   281  		fm := ctx.Map.FileManager()
   282  
   283  		_, fault := fm.resolve(ctx, req.Datacenter, req.Name)
   284  		if fault != nil {
   285  			return nil, fault
   286  		}
   287  
   288  		res = append(res, internal.VirtualDiskInfo{Name: req.Name, DiskType: "thin"})
   289  
   290  		if req.IncludeParents {
   291  			// TODO
   292  		}
   293  
   294  		return res, nil
   295  	})
   296  
   297  	return &internal.QueryVirtualDiskInfoTaskBody{
   298  		Res: &internal.QueryVirtualDiskInfo_TaskResponse{
   299  			Returnval: task.Run(ctx),
   300  		},
   301  	}
   302  }