github.com/vmware/govmomi@v0.43.0/simulator/snapshot.go (about) 1 /* 2 Copyright (c) 2017-2023 VMware, Inc. All Rights Reserved. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package simulator 18 19 import ( 20 "fmt" 21 "os" 22 "path" 23 24 "github.com/vmware/govmomi/object" 25 "github.com/vmware/govmomi/vim25/methods" 26 "github.com/vmware/govmomi/vim25/mo" 27 "github.com/vmware/govmomi/vim25/soap" 28 "github.com/vmware/govmomi/vim25/types" 29 ) 30 31 type VirtualMachineSnapshot struct { 32 mo.VirtualMachineSnapshot 33 DataSets map[string]*DataSet 34 } 35 36 func (v *VirtualMachineSnapshot) createSnapshotFiles() types.BaseMethodFault { 37 vm := Map.Get(v.Vm).(*VirtualMachine) 38 39 snapshotDirectory := vm.Config.Files.SnapshotDirectory 40 if snapshotDirectory == "" { 41 snapshotDirectory = vm.Config.Files.VmPathName 42 } 43 44 index := 1 45 for { 46 fileName := fmt.Sprintf("%s-Snapshot%d.vmsn", vm.Name, index) 47 f, err := vm.createFile(snapshotDirectory, fileName, false) 48 if err != nil { 49 switch err.(type) { 50 case *types.FileAlreadyExists: 51 index++ 52 continue 53 default: 54 return err 55 } 56 } 57 58 _ = f.Close() 59 60 p, _ := parseDatastorePath(snapshotDirectory) 61 vm.useDatastore(p.Datastore) 62 datastorePath := object.DatastorePath{ 63 Datastore: p.Datastore, 64 Path: path.Join(p.Path, fileName), 65 } 66 67 dataLayoutKey := vm.addFileLayoutEx(datastorePath, 0) 68 vm.addSnapshotLayout(v.Self, dataLayoutKey) 69 vm.addSnapshotLayoutEx(v.Self, dataLayoutKey, -1) 70 71 return nil 72 } 73 } 74 75 func (v *VirtualMachineSnapshot) removeSnapshotFiles(ctx *Context) types.BaseMethodFault { 76 // TODO: also remove delta disks that were created when snapshot was taken 77 78 vm := ctx.Map.Get(v.Vm).(*VirtualMachine) 79 80 for idx, sLayout := range vm.Layout.Snapshot { 81 if sLayout.Key == v.Self { 82 vm.Layout.Snapshot = append(vm.Layout.Snapshot[:idx], vm.Layout.Snapshot[idx+1:]...) 83 break 84 } 85 } 86 87 for idx, sLayoutEx := range vm.LayoutEx.Snapshot { 88 if sLayoutEx.Key == v.Self { 89 for _, file := range vm.LayoutEx.File { 90 if file.Key == sLayoutEx.DataKey || file.Key == sLayoutEx.MemoryKey { 91 p, fault := parseDatastorePath(file.Name) 92 if fault != nil { 93 return fault 94 } 95 96 host := ctx.Map.Get(*vm.Runtime.Host).(*HostSystem) 97 datastore := ctx.Map.FindByName(p.Datastore, host.Datastore).(*Datastore) 98 dFilePath := path.Join(datastore.Info.GetDatastoreInfo().Url, p.Path) 99 100 _ = os.Remove(dFilePath) 101 } 102 } 103 104 vm.LayoutEx.Snapshot = append(vm.LayoutEx.Snapshot[:idx], vm.LayoutEx.Snapshot[idx+1:]...) 105 } 106 } 107 108 vm.RefreshStorageInfo(ctx, nil) 109 110 return nil 111 } 112 113 func (v *VirtualMachineSnapshot) RemoveSnapshotTask(ctx *Context, req *types.RemoveSnapshot_Task) soap.HasFault { 114 task := CreateTask(v.Vm, "removeSnapshot", func(t *Task) (types.AnyType, types.BaseMethodFault) { 115 var changes []types.PropertyChange 116 117 vm := ctx.Map.Get(v.Vm).(*VirtualMachine) 118 ctx.WithLock(vm, func() { 119 if vm.Snapshot.CurrentSnapshot != nil && *vm.Snapshot.CurrentSnapshot == req.This { 120 parent := findParentSnapshotInTree(vm.Snapshot.RootSnapshotList, req.This) 121 changes = append(changes, types.PropertyChange{Name: "snapshot.currentSnapshot", Val: parent}) 122 } 123 124 rootSnapshots := removeSnapshotInTree(vm.Snapshot.RootSnapshotList, req.This, req.RemoveChildren) 125 changes = append(changes, types.PropertyChange{Name: "snapshot.rootSnapshotList", Val: rootSnapshots}) 126 127 rootSnapshotRefs := make([]types.ManagedObjectReference, len(rootSnapshots)) 128 for i, rs := range rootSnapshots { 129 rootSnapshotRefs[i] = rs.Snapshot 130 } 131 changes = append(changes, types.PropertyChange{Name: "rootSnapshot", Val: rootSnapshotRefs}) 132 133 if len(rootSnapshots) == 0 { 134 changes = []types.PropertyChange{ 135 {Name: "snapshot", Val: nil}, 136 {Name: "rootSnapshot", Val: nil}, 137 } 138 } 139 140 ctx.Map.Get(req.This).(*VirtualMachineSnapshot).removeSnapshotFiles(ctx) 141 142 ctx.Map.Update(vm, changes) 143 }) 144 145 ctx.Map.Remove(ctx, req.This) 146 147 return nil, nil 148 }) 149 150 return &methods.RemoveSnapshot_TaskBody{ 151 Res: &types.RemoveSnapshot_TaskResponse{ 152 Returnval: task.Run(ctx), 153 }, 154 } 155 } 156 157 func (v *VirtualMachineSnapshot) RevertToSnapshotTask(ctx *Context, req *types.RevertToSnapshot_Task) soap.HasFault { 158 task := CreateTask(v.Vm, "revertToSnapshot", func(t *Task) (types.AnyType, types.BaseMethodFault) { 159 vm := ctx.Map.Get(v.Vm).(*VirtualMachine) 160 161 ctx.WithLock(vm, func() { 162 vm.DataSets = copyDataSetsForVmClone(v.DataSets) 163 ctx.Map.Update(vm, []types.PropertyChange{ 164 {Name: "snapshot.currentSnapshot", Val: v.Self}, 165 }) 166 }) 167 168 return nil, nil 169 }) 170 171 return &methods.RevertToSnapshot_TaskBody{ 172 Res: &types.RevertToSnapshot_TaskResponse{ 173 Returnval: task.Run(ctx), 174 }, 175 } 176 }