github.com/vmware/govmomi@v0.43.0/govc/vm/snapshot/tree.go (about)

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