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 }