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 }