github.com/vmware/govmomi@v0.37.2/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  }