github.com/vmware/govmomi@v0.51.0/cli/vm/snapshot/tree.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 snapshot
     6  
     7  import (
     8  	"context"
     9  	"encoding/json"
    10  	"flag"
    11  	"fmt"
    12  	"path"
    13  	"strings"
    14  	"time"
    15  
    16  	"github.com/vmware/govmomi/cli"
    17  	"github.com/vmware/govmomi/cli/flags"
    18  	"github.com/vmware/govmomi/object"
    19  	"github.com/vmware/govmomi/units"
    20  	"github.com/vmware/govmomi/vim25/mo"
    21  	"github.com/vmware/govmomi/vim25/types"
    22  )
    23  
    24  type tree struct {
    25  	*flags.VirtualMachineFlag
    26  
    27  	current     bool
    28  	currentName bool
    29  	date        bool
    30  	description bool
    31  	fullPath    bool
    32  	id          bool
    33  	size        bool
    34  
    35  	info   *types.VirtualMachineSnapshotInfo
    36  	layout *types.VirtualMachineFileLayoutEx
    37  }
    38  
    39  func init() {
    40  	cli.Register("snapshot.tree", &tree{})
    41  }
    42  
    43  func (cmd *tree) Register(ctx context.Context, f *flag.FlagSet) {
    44  	cmd.VirtualMachineFlag, ctx = flags.NewVirtualMachineFlag(ctx)
    45  	cmd.VirtualMachineFlag.Register(ctx, f)
    46  
    47  	f.BoolVar(&cmd.current, "c", true, "Print the current snapshot")
    48  	f.BoolVar(&cmd.currentName, "C", false,
    49  		"Print the current snapshot name only")
    50  	f.BoolVar(&cmd.date, "D", false, "Print the snapshot creation date")
    51  	f.BoolVar(&cmd.description, "d", false,
    52  		"Print the snapshot description")
    53  	f.BoolVar(&cmd.fullPath, "f", false,
    54  		"Print the full path prefix for snapshot")
    55  	f.BoolVar(&cmd.id, "i", false, "Print the snapshot id")
    56  	f.BoolVar(&cmd.size, "s", false, "Print the snapshot size")
    57  }
    58  
    59  func (cmd *tree) Description() string {
    60  	return `List VM snapshots in a tree-like format.
    61  
    62  The command will exit 0 with no output if VM does not have any snapshots.
    63  
    64  Examples:
    65    govc snapshot.tree -vm my-vm
    66    govc snapshot.tree -vm my-vm -D -i -d`
    67  }
    68  
    69  func (cmd *tree) Process(ctx context.Context) error {
    70  	if err := cmd.VirtualMachineFlag.Process(ctx); err != nil {
    71  		return err
    72  	}
    73  	return nil
    74  }
    75  
    76  type SnapshotRecord struct {
    77  	CreateTime        *time.Time       `json:"createTime,omitempty"`
    78  	Id                *string          `json:"id,omitempty"`
    79  	Size              *int             `json:"size,omitempty"`
    80  	Name              string           `json:"name"`
    81  	Description       *string          `json:"description,omitempty"`
    82  	IsCurrent         bool             `json:"current"`
    83  	ChildSnapshotList []SnapshotRecord `json:"childSnapshotList"`
    84  }
    85  
    86  func (cmd *tree) IsCurrent(vm mo.VirtualMachine, moref types.ManagedObjectReference) bool {
    87  	return vm.Snapshot.CurrentSnapshot.Value == moref.Value
    88  }
    89  
    90  func (cmd *tree) CreateTime(snapshot types.VirtualMachineSnapshotTree) *time.Time {
    91  	if cmd.date {
    92  		return &snapshot.CreateTime
    93  	}
    94  	return nil
    95  
    96  }
    97  
    98  func (cmd *tree) SnapshotId(snapshot types.VirtualMachineSnapshotTree) *string {
    99  	if cmd.id {
   100  		return &snapshot.Snapshot.Value
   101  	}
   102  	return nil
   103  }
   104  
   105  func (cmd *tree) SnapshotDescription(snapshot types.VirtualMachineSnapshotTree) *string {
   106  	if cmd.description {
   107  		return &snapshot.Description
   108  	}
   109  	return nil
   110  }
   111  
   112  func (cmd *tree) SnapshotSize(vm mo.VirtualMachine, snapshot types.ManagedObjectReference, parent *types.ManagedObjectReference) *int {
   113  	if cmd.size {
   114  		size := object.SnapshotSize(snapshot, parent, vm.LayoutEx, cmd.IsCurrent(vm, snapshot))
   115  		return &size
   116  	}
   117  	return nil
   118  }
   119  
   120  func (cmd *tree) makeSnapshotRecord(vm mo.VirtualMachine, node types.VirtualMachineSnapshotTree, parent *types.ManagedObjectReference) SnapshotRecord {
   121  
   122  	var SnapshotRecords []SnapshotRecord
   123  	for _, snapshot := range node.ChildSnapshotList {
   124  		SnapshotRecords = append(SnapshotRecords, cmd.makeSnapshotRecord(vm, snapshot, &node.Snapshot))
   125  	}
   126  	return SnapshotRecord{Name: node.Name,
   127  		Id:                cmd.SnapshotId(node),
   128  		CreateTime:        cmd.CreateTime(node),
   129  		Description:       cmd.SnapshotDescription(node),
   130  		ChildSnapshotList: SnapshotRecords,
   131  		Size:              cmd.SnapshotSize(vm, node.Snapshot, parent),
   132  		IsCurrent:         cmd.IsCurrent(vm, node.Snapshot)}
   133  
   134  }
   135  
   136  func (cmd *tree) writeJson(vm mo.VirtualMachine) {
   137  	var SnapshotRecords []SnapshotRecord
   138  	for _, rootSnapshot := range vm.Snapshot.RootSnapshotList {
   139  		SnapshotRecords = append(SnapshotRecords, cmd.makeSnapshotRecord(vm, rootSnapshot, nil))
   140  	}
   141  	b, _ := json.MarshalIndent(SnapshotRecords, "", "  ")
   142  	fmt.Println(string(b))
   143  
   144  }
   145  func (cmd *tree) write(level int, parent string, pref *types.ManagedObjectReference, st []types.VirtualMachineSnapshotTree) {
   146  	for _, s := range st {
   147  		s := s // avoid implicit memory aliasing
   148  
   149  		sname := s.Name
   150  		if cmd.fullPath && parent != "" {
   151  			sname = path.Join(parent, sname)
   152  		}
   153  
   154  		var names []string
   155  
   156  		if !cmd.currentName {
   157  			names = append(names, sname)
   158  		}
   159  
   160  		isCurrent := false
   161  
   162  		if s.Snapshot == *cmd.info.CurrentSnapshot {
   163  			isCurrent = true
   164  			if cmd.current {
   165  				names = append(names, ".")
   166  			} else if cmd.currentName {
   167  				fmt.Println(sname)
   168  				return
   169  			}
   170  		}
   171  
   172  		for _, name := range names {
   173  			var attr []string
   174  			var meta string
   175  
   176  			if cmd.size {
   177  				size := object.SnapshotSize(s.Snapshot, pref, cmd.layout, isCurrent)
   178  
   179  				attr = append(attr, units.ByteSize(size).String())
   180  			}
   181  
   182  			if cmd.id {
   183  				attr = append(attr, s.Snapshot.Value)
   184  			}
   185  
   186  			if cmd.date {
   187  				attr = append(attr, s.CreateTime.Format("2006-01-02T15:04:05Z07:00"))
   188  			}
   189  
   190  			if len(attr) > 0 {
   191  				meta = fmt.Sprintf("[%s]  ", strings.Join(attr, " "))
   192  			}
   193  
   194  			if cmd.description {
   195  				fmt.Printf("%s%s%s - %4s\n",
   196  					strings.Repeat(" ", level), meta, name,
   197  					s.Description)
   198  			} else {
   199  				fmt.Printf("%s%s%s\n",
   200  					strings.Repeat(" ", level), meta, name)
   201  			}
   202  		}
   203  
   204  		cmd.write(level+2, sname, &s.Snapshot, s.ChildSnapshotList)
   205  	}
   206  }
   207  
   208  func (cmd *tree) Run(ctx context.Context, f *flag.FlagSet) error {
   209  	if f.NArg() != 0 {
   210  		return flag.ErrHelp
   211  	}
   212  
   213  	vm, err := cmd.VirtualMachine()
   214  	if err != nil {
   215  		return err
   216  	}
   217  
   218  	if vm == nil {
   219  		return flag.ErrHelp
   220  	}
   221  
   222  	var o mo.VirtualMachine
   223  
   224  	err = vm.Properties(ctx, vm.Reference(), []string{"snapshot", "layoutEx"}, &o)
   225  	if err != nil {
   226  		return err
   227  	}
   228  
   229  	if o.Snapshot == nil {
   230  		return nil
   231  	}
   232  
   233  	if o.Snapshot.CurrentSnapshot == nil || cmd.currentName {
   234  		cmd.current = false
   235  	}
   236  
   237  	cmd.info = o.Snapshot
   238  	cmd.layout = o.LayoutEx
   239  	if cmd.JSON {
   240  		cmd.writeJson(o)
   241  	} else {
   242  		cmd.write(0, "", nil, o.Snapshot.RootSnapshotList)
   243  	}
   244  
   245  	return nil
   246  }