github.com/vmware/govmomi@v0.51.0/simulator/snapshot.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 "fmt" 9 "os" 10 "path" 11 12 "github.com/vmware/govmomi/object" 13 "github.com/vmware/govmomi/vim25/methods" 14 "github.com/vmware/govmomi/vim25/mo" 15 "github.com/vmware/govmomi/vim25/soap" 16 "github.com/vmware/govmomi/vim25/types" 17 ) 18 19 type VirtualMachineSnapshot struct { 20 mo.VirtualMachineSnapshot 21 DataSets map[string]*DataSet 22 } 23 24 func (v *VirtualMachineSnapshot) createSnapshotFiles(ctx *Context) types.BaseMethodFault { 25 vm := ctx.Map.Get(v.Vm).(*VirtualMachine) 26 27 snapshotDirectory := vm.Config.Files.SnapshotDirectory 28 if snapshotDirectory == "" { 29 snapshotDirectory = vm.Config.Files.VmPathName 30 } 31 32 index := 1 33 for { 34 fileName := fmt.Sprintf("%s-Snapshot%d.vmsn", vm.Name, index) 35 f, err := vm.createFile(ctx, snapshotDirectory, fileName, false) 36 if err != nil { 37 switch err.(type) { 38 case *types.FileAlreadyExists: 39 index++ 40 continue 41 default: 42 return err 43 } 44 } 45 46 _ = f.Close() 47 48 p, _ := parseDatastorePath(snapshotDirectory) 49 vm.useDatastore(ctx, p.Datastore) 50 datastorePath := object.DatastorePath{ 51 Datastore: p.Datastore, 52 Path: path.Join(p.Path, fileName), 53 } 54 55 dataLayoutKey := vm.addFileLayoutEx(ctx, datastorePath, 0) 56 vm.addSnapshotLayout(ctx, v.Self, dataLayoutKey) 57 vm.addSnapshotLayoutEx(ctx, v.Self, dataLayoutKey, -1) 58 59 return nil 60 } 61 } 62 63 func (v *VirtualMachineSnapshot) removeSnapshotFiles(ctx *Context) types.BaseMethodFault { 64 // TODO: also remove delta disks that were created when snapshot was taken 65 66 vm := ctx.Map.Get(v.Vm).(*VirtualMachine) 67 68 for idx, sLayout := range vm.Layout.Snapshot { 69 if sLayout.Key == v.Self { 70 vm.Layout.Snapshot = append(vm.Layout.Snapshot[:idx], vm.Layout.Snapshot[idx+1:]...) 71 break 72 } 73 } 74 75 for idx, sLayoutEx := range vm.LayoutEx.Snapshot { 76 if sLayoutEx.Key == v.Self { 77 for _, file := range vm.LayoutEx.File { 78 if file.Key == sLayoutEx.DataKey || file.Key == sLayoutEx.MemoryKey { 79 p, fault := parseDatastorePath(file.Name) 80 if fault != nil { 81 return fault 82 } 83 84 host := ctx.Map.Get(*vm.Runtime.Host).(*HostSystem) 85 datastore := ctx.Map.FindByName(p.Datastore, host.Datastore).(*Datastore) 86 dFilePath := datastore.resolve(ctx, p.Path) 87 88 _ = os.Remove(dFilePath) 89 } 90 } 91 92 vm.LayoutEx.Snapshot = append(vm.LayoutEx.Snapshot[:idx], vm.LayoutEx.Snapshot[idx+1:]...) 93 } 94 } 95 96 vm.RefreshStorageInfo(ctx, nil) 97 98 return nil 99 } 100 101 func (v *VirtualMachineSnapshot) RemoveSnapshotTask(ctx *Context, req *types.RemoveSnapshot_Task) soap.HasFault { 102 task := CreateTask(v.Vm, "removeSnapshot", func(t *Task) (types.AnyType, types.BaseMethodFault) { 103 var changes []types.PropertyChange 104 105 vm := ctx.Map.Get(v.Vm).(*VirtualMachine) 106 ctx.WithLock(vm, func() { 107 if vm.Snapshot.CurrentSnapshot != nil && *vm.Snapshot.CurrentSnapshot == req.This { 108 parent := findParentSnapshotInTree(vm.Snapshot.RootSnapshotList, req.This) 109 changes = append(changes, types.PropertyChange{Name: "snapshot.currentSnapshot", Val: parent}) 110 } 111 112 rootSnapshots := removeSnapshotInTree(vm.Snapshot.RootSnapshotList, req.This, req.RemoveChildren) 113 changes = append(changes, types.PropertyChange{Name: "snapshot.rootSnapshotList", Val: rootSnapshots}) 114 115 rootSnapshotRefs := make([]types.ManagedObjectReference, len(rootSnapshots)) 116 for i, rs := range rootSnapshots { 117 rootSnapshotRefs[i] = rs.Snapshot 118 } 119 changes = append(changes, types.PropertyChange{Name: "rootSnapshot", Val: rootSnapshotRefs}) 120 121 if len(rootSnapshots) == 0 { 122 changes = []types.PropertyChange{ 123 {Name: "snapshot", Val: nil}, 124 {Name: "rootSnapshot", Val: nil}, 125 } 126 } 127 128 ctx.Map.Get(req.This).(*VirtualMachineSnapshot).removeSnapshotFiles(ctx) 129 130 ctx.Update(vm, changes) 131 }) 132 133 ctx.Map.Remove(ctx, req.This) 134 135 return nil, nil 136 }) 137 138 return &methods.RemoveSnapshot_TaskBody{ 139 Res: &types.RemoveSnapshot_TaskResponse{ 140 Returnval: task.Run(ctx), 141 }, 142 } 143 } 144 145 func (v *VirtualMachineSnapshot) RevertToSnapshotTask(ctx *Context, req *types.RevertToSnapshot_Task) soap.HasFault { 146 task := CreateTask(v.Vm, "revertToSnapshot", func(t *Task) (types.AnyType, types.BaseMethodFault) { 147 vm := ctx.Map.Get(v.Vm).(*VirtualMachine) 148 149 ctx.WithLock(vm, func() { 150 vm.DataSets = copyDataSetsForVmClone(v.DataSets) 151 ctx.Update(vm, []types.PropertyChange{ 152 {Name: "snapshot.currentSnapshot", Val: v.Self}, 153 }) 154 }) 155 156 return nil, nil 157 }) 158 159 return &methods.RevertToSnapshot_TaskBody{ 160 Res: &types.RevertToSnapshot_TaskResponse{ 161 Returnval: task.Run(ctx), 162 }, 163 } 164 } 165 166 func (v *VirtualMachineSnapshot) ExportSnapshot(ctx *Context, req *types.ExportSnapshot) soap.HasFault { 167 168 vm := ctx.Map.Get(v.Vm).(*VirtualMachine) 169 170 lease := newHttpNfcLease(ctx) 171 lease.InitializeProgress = 100 172 lease.TransferProgress = 0 173 lease.Mode = string(types.HttpNfcLeaseModePushOrGet) 174 lease.Capabilities = types.HttpNfcLeaseCapabilities{ 175 CorsSupported: true, 176 PullModeSupported: true, 177 } 178 179 device := object.VirtualDeviceList(v.Config.Hardware.Device) 180 ndevice := make(map[string]int) 181 var urls []types.HttpNfcLeaseDeviceUrl 182 u := leaseURL(ctx) 183 184 for _, d := range device { 185 info, ok := d.GetVirtualDevice().Backing.(types.BaseVirtualDeviceFileBackingInfo) 186 if !ok { 187 continue 188 } 189 var file object.DatastorePath 190 file.FromString(info.GetVirtualDeviceFileBackingInfo().FileName) 191 name := path.Base(file.Path) 192 ds := vm.findDatastore(ctx, file.Datastore) 193 lease.files[name] = ds.resolve(ctx, file.Path) 194 195 _, disk := d.(*types.VirtualDisk) 196 kind := device.Type(d) 197 n := ndevice[kind] 198 ndevice[kind]++ 199 200 u.Path = nfcPrefix + path.Join(lease.Reference().Value, name) 201 urls = append(urls, types.HttpNfcLeaseDeviceUrl{ 202 Key: fmt.Sprintf("/%s/%s:%d", vm.Self.Value, kind, n), 203 ImportKey: fmt.Sprintf("/%s/%s:%d", vm.Name, kind, n), 204 Url: u.String(), 205 SslThumbprint: "", 206 Disk: types.NewBool(disk), 207 TargetId: name, 208 DatastoreKey: "", 209 FileSize: 0, 210 }) 211 } 212 213 lease.ready(ctx, v.Vm, urls) 214 215 return &methods.ExportSnapshotBody{ 216 Res: &types.ExportSnapshotResponse{ 217 Returnval: lease.Reference(), 218 }, 219 } 220 }