github.com/vmware/govmomi@v0.37.1/govc/object/tree.go (about) 1 /* 2 Copyright (c) 2014-2015 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 object 18 19 import ( 20 "context" 21 "flag" 22 "fmt" 23 "net/url" 24 "os" 25 gopath "path" 26 "time" 27 28 gotree "github.com/a8m/tree" 29 30 "github.com/vmware/govmomi/govc/cli" 31 "github.com/vmware/govmomi/govc/flags" 32 "github.com/vmware/govmomi/view" 33 "github.com/vmware/govmomi/vim25" 34 "github.com/vmware/govmomi/vim25/types" 35 ) 36 37 type tree struct { 38 *flags.DatacenterFlag 39 40 long bool 41 kind bool 42 color bool 43 level int 44 } 45 46 func init() { 47 cli.Register("tree", &tree{}) 48 } 49 50 func (cmd *tree) Register(ctx context.Context, f *flag.FlagSet) { 51 cmd.DatacenterFlag, ctx = flags.NewDatacenterFlag(ctx) 52 cmd.DatacenterFlag.Register(ctx, f) 53 54 f.BoolVar(&cmd.color, "C", false, "Colorize output") 55 f.BoolVar(&cmd.long, "l", false, "Follow runtime references (e.g. HostSystem VMs)") 56 f.BoolVar(&cmd.kind, "p", false, "Print the object type") 57 f.IntVar(&cmd.level, "L", 0, "Max display depth of the inventory tree") 58 } 59 60 func (cmd *tree) Description() string { 61 return `List contents of the inventory in a tree-like format. 62 63 Examples: 64 govc tree -C / 65 govc tree /datacenter/vm` 66 } 67 68 func (cmd *tree) Usage() string { 69 return "[PATH]" 70 } 71 72 func (cmd *tree) Run(ctx context.Context, f *flag.FlagSet) error { 73 c, err := cmd.Client() 74 if err != nil { 75 return err 76 } 77 78 path := f.Arg(0) 79 if path == "" { 80 path = "/" 81 } 82 83 vfs := &virtualFileSystem{ 84 ctx: ctx, 85 cmd: cmd, 86 c: c, 87 m: view.NewManager(c), 88 names: make(map[types.ManagedObjectReference]string), 89 dvs: make(map[types.ManagedObjectReference][]types.ManagedObjectReference), 90 path: path, 91 } 92 93 treeOpts := &gotree.Options{ 94 Fs: vfs, 95 OutFile: cmd.Out, 96 Colorize: cmd.color, 97 Color: color, 98 DeepLevel: cmd.level, 99 } 100 101 inf := gotree.New(path) 102 inf.Visit(treeOpts) 103 inf.Print(treeOpts) 104 105 return nil 106 } 107 108 type virtualFileSystem struct { 109 ctx context.Context 110 cmd *tree 111 c *vim25.Client 112 m *view.Manager 113 names map[types.ManagedObjectReference]string 114 dvs map[types.ManagedObjectReference][]types.ManagedObjectReference 115 root types.ManagedObjectReference 116 path string 117 } 118 119 func style(kind string) string { 120 switch kind { 121 case "VirtualMachine": 122 return "1;32" 123 case "HostSystem": 124 return "1;33" 125 case "ResourcePool": 126 return "1;30" 127 case "Network", "OpaqueNetwork", "DistributedVirtualPortgroup": 128 return "1;35" 129 case "Datastore": 130 return "1;36" 131 case "Datacenter": 132 return "1;37" 133 default: 134 return "" 135 } 136 } 137 138 func color(node *gotree.Node, s string) string { 139 ref := pathReference(node.Path()) 140 141 switch ref.Type { 142 case "ResourcePool": 143 return s 144 } 145 146 c := style(ref.Type) 147 if c == "" { 148 return gotree.ANSIColor(node, s) 149 } 150 151 return gotree.ANSIColorFormat(c, s) 152 } 153 154 func (vfs *virtualFileSystem) Stat(path string) (os.FileInfo, error) { 155 var ref types.ManagedObjectReference 156 157 if len(vfs.names) == 0 { 158 // This is the first Stat() call, where path is the initial user input 159 if path == "/" { 160 ref = vfs.c.ServiceContent.RootFolder 161 } else { 162 var err error 163 ref, err = vfs.cmd.ManagedObject(vfs.ctx, path) 164 if err != nil { 165 return nil, err 166 } 167 } 168 vfs.names[ref] = path 169 vfs.root = ref 170 } else { 171 // The Node.Path in subsequent calls to Stat() will have a MOR base 172 ref = pathReference(path) 173 } 174 175 name := vfs.names[ref] 176 177 var mode os.FileMode 178 switch ref.Type { 179 case "ComputeResource", 180 "ClusterComputeResource", 181 "Datacenter", 182 "Folder", 183 "ResourcePool", 184 "VirtualApp", 185 "StoragePod", 186 "DistributedVirtualSwitch", 187 "VmwareDistributedVirtualSwitch": 188 mode = os.ModeDir 189 case "HostSystem": 190 if vfs.cmd.long { 191 mode = os.ModeDir 192 } 193 } 194 195 if vfs.cmd.kind { 196 name = fmt.Sprintf("[%s] %s", ref.Type, name) 197 } 198 199 return fileInfo{name: name, mode: mode}, nil 200 } 201 202 // pathReference converts the base of the given Node.Path to a MOR 203 func pathReference(s string) types.ManagedObjectReference { 204 var ref types.ManagedObjectReference 205 r, _ := url.PathUnescape(gopath.Base(s)) 206 ref.FromString(r) 207 return ref 208 } 209 210 func (vfs *virtualFileSystem) ReadDir(path string) ([]string, error) { 211 var ref types.ManagedObjectReference 212 213 if path == vfs.path { 214 // This path is the initial user input (e.g. "/" or "/dc1") 215 ref = vfs.root 216 } else { 217 // This path will have had 1 or more MORs appended to it, as returned by this func 218 ref = pathReference(path) 219 } 220 221 var childPaths []string 222 223 switch ref.Type { 224 // In the vCenter inventory switches and portgroups are siblings, hack to display them as parent child in the tree 225 case "DistributedVirtualSwitch", "VmwareDistributedVirtualSwitch": 226 pgs := vfs.dvs[ref] 227 for _, pg := range pgs { 228 childPaths = append(childPaths, url.PathEscape(pg.String())) 229 } 230 return childPaths, nil 231 } 232 233 v, err := vfs.m.CreateContainerView(vfs.ctx, ref, nil, false) 234 if err != nil { 235 return nil, err 236 } 237 defer v.Destroy(vfs.ctx) 238 239 var kind []string 240 if !vfs.cmd.long { 241 switch ref.Type { 242 case "HostSystem": 243 return nil, nil 244 case "ResourcePool", "VirtualApp": 245 kind = []string{"ResourcePool", "VirtualApp"} 246 } 247 } 248 249 var children []types.ObjectContent 250 251 pspec := []types.PropertySpec{ 252 {Type: "DistributedVirtualSwitch", PathSet: []string{"portgroup"}}, 253 {Type: "VmwareDistributedVirtualSwitch", PathSet: []string{"portgroup"}}, 254 } 255 256 err = v.Retrieve(vfs.ctx, kind, []string{"name"}, &children, pspec...) 257 if err != nil { 258 return nil, err 259 } 260 261 for _, content := range children { 262 ref = content.Obj 263 for _, p := range content.PropSet { 264 switch p.Name { 265 case "name": 266 vfs.names[ref] = p.Val.(string) 267 case "portgroup": 268 vfs.dvs[ref] = p.Val.(types.ArrayOfManagedObjectReference).ManagedObjectReference 269 } 270 } 271 if ref.Type == "DistributedVirtualPortgroup" { 272 continue // Returned on ReadDir() of the DVS above 273 } 274 childPaths = append(childPaths, url.PathEscape(ref.String())) 275 } 276 277 return childPaths, nil 278 } 279 280 type fileInfo struct { 281 name string 282 mode os.FileMode 283 } 284 285 func (f fileInfo) Name() string { 286 return f.name 287 } 288 func (f fileInfo) Size() int64 { 289 return 0 290 } 291 func (f fileInfo) Mode() os.FileMode { 292 return f.mode 293 } 294 func (f fileInfo) ModTime() time.Time { 295 return time.Now() 296 } 297 func (f fileInfo) IsDir() bool { 298 return f.mode&os.ModeDir == os.ModeDir 299 } 300 func (f fileInfo) Sys() interface{} { 301 return nil 302 }