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  }