github.com/vmware/govmomi@v0.51.0/cli/object/find.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 object 6 7 import ( 8 "bytes" 9 "context" 10 "flag" 11 "fmt" 12 "io" 13 "strings" 14 "text/tabwriter" 15 16 "github.com/vmware/govmomi/cli" 17 "github.com/vmware/govmomi/cli/flags" 18 "github.com/vmware/govmomi/fault" 19 "github.com/vmware/govmomi/internal" 20 "github.com/vmware/govmomi/object" 21 "github.com/vmware/govmomi/property" 22 "github.com/vmware/govmomi/view" 23 "github.com/vmware/govmomi/vim25" 24 "github.com/vmware/govmomi/vim25/mo" 25 "github.com/vmware/govmomi/vim25/types" 26 ) 27 28 type find struct { 29 *flags.DatacenterFlag 30 31 ref bool 32 id bool 33 long bool 34 parent bool 35 kind kinds 36 name string 37 maxdepth int 38 } 39 40 var alias = []struct { 41 name string 42 kind string 43 }{ 44 {"a", "VirtualApp"}, 45 {"c", "ClusterComputeResource"}, 46 {"d", "Datacenter"}, 47 {"f", "Folder"}, 48 {"g", "DistributedVirtualPortgroup"}, 49 {"h", "HostSystem"}, 50 {"m", "VirtualMachine"}, 51 {"n", "Network"}, 52 {"o", "OpaqueNetwork"}, 53 {"p", "ResourcePool"}, 54 {"r", "ComputeResource"}, 55 {"s", "Datastore"}, 56 {"w", "DistributedVirtualSwitch"}, 57 } 58 59 func aliasHelp() string { 60 var help bytes.Buffer 61 62 for _, a := range alias { 63 fmt.Fprintf(&help, " %s %s\n", a.name, a.kind) 64 } 65 66 return help.String() 67 } 68 69 type kinds []string 70 71 func (e *kinds) String() string { 72 return fmt.Sprint(*e) 73 } 74 75 func (e *kinds) Set(value string) error { 76 *e = append(*e, e.alias(value)) 77 return nil 78 } 79 80 func (e *kinds) alias(value string) string { 81 if len(value) != 1 { 82 return value 83 } 84 85 for _, a := range alias { 86 if a.name == value { 87 return a.kind 88 } 89 } 90 91 return value 92 } 93 94 func (e *kinds) wanted(kind string) bool { 95 if len(*e) == 0 { 96 return true 97 } 98 99 for _, k := range *e { 100 if kind == k { 101 return true 102 } 103 } 104 105 return false 106 } 107 108 func init() { 109 cli.Register("find", &find{}) 110 } 111 112 func (cmd *find) Register(ctx context.Context, f *flag.FlagSet) { 113 cmd.DatacenterFlag, ctx = flags.NewDatacenterFlag(ctx) 114 cmd.DatacenterFlag.Register(ctx, f) 115 116 f.Var(&cmd.kind, "type", "Resource type") 117 f.StringVar(&cmd.name, "name", "*", "Resource name") 118 f.IntVar(&cmd.maxdepth, "maxdepth", -1, "Max depth") 119 f.BoolVar(&cmd.ref, "i", false, "Print the managed object reference") 120 f.BoolVar(&cmd.id, "I", false, "Print the managed object ID") 121 f.BoolVar(&cmd.long, "l", false, "Long listing format") 122 f.BoolVar(&cmd.parent, "p", false, "Find parent objects") 123 } 124 125 func (cmd *find) Usage() string { 126 return "[ROOT] [KEY VAL]..." 127 } 128 129 func (cmd *find) Description() string { 130 atable := aliasHelp() 131 132 return fmt.Sprintf(`Find managed objects. 133 134 ROOT can be an inventory path or ManagedObjectReference. 135 ROOT defaults to '.', an alias for the root folder or DC if set. 136 137 Optional KEY VAL pairs can be used to filter results against object instance properties. 138 Use the govc 'collect' command to view possible object property keys. 139 140 The '-type' flag value can be a managed entity type or one of the following aliases: 141 142 %s 143 Examples: 144 govc find 145 govc find -l / # include object type in output 146 govc find -l -I / # include MOID in output 147 govc find /dc1 -type c 148 govc find vm -name my-vm-* 149 govc find . -type n 150 govc find -p /folder-a/dc-1/host/folder-b/cluster-a -type Datacenter # prints /folder-a/dc-1 151 govc find . -type m -runtime.powerState poweredOn 152 govc find . -type m -datastore $(govc find -i datastore -name vsanDatastore) 153 govc find . -type s -summary.type vsan 154 govc find . -type s -customValue *:prod # Key:Value 155 govc find . -type h -hardware.cpuInfo.numCpuCores 16`, atable) 156 } 157 158 // rootMatch returns true if the root object path should be printed 159 func (cmd *find) rootMatch(ctx context.Context, root object.Reference, client *vim25.Client, filter property.Match) bool { 160 ref := root.Reference() 161 162 if !cmd.kind.wanted(ref.Type) { 163 return false 164 } 165 166 if len(filter) == 1 && filter["name"] == "*" { 167 return true 168 } 169 170 var content []types.ObjectContent 171 172 pc := property.DefaultCollector(client) 173 _ = pc.RetrieveWithFilter(ctx, []types.ManagedObjectReference{ref}, filter.Keys(), &content, filter) 174 175 return content != nil 176 } 177 178 type findResult []string 179 180 func (r findResult) Write(w io.Writer) error { 181 for i := range r { 182 fmt.Fprintln(w, r[i]) 183 } 184 return nil 185 } 186 187 func (r findResult) Dump() any { 188 return []string(r) 189 } 190 191 type findResultLong []string 192 193 func (r findResultLong) Write(w io.Writer) error { 194 tw := tabwriter.NewWriter(w, 2, 0, 2, ' ', 0) 195 for i := range r { 196 fmt.Fprintln(tw, r[i]) 197 } 198 return tw.Flush() 199 } 200 201 func (cmd *find) writeResult(paths []string) error { 202 if cmd.long { 203 return cmd.WriteResult(findResultLong(paths)) 204 } 205 return cmd.WriteResult(findResult(paths)) 206 } 207 208 func (cmd *find) mo(o types.ManagedObjectReference) string { 209 if cmd.id { 210 return o.Value 211 } 212 return o.String() 213 } 214 215 func (cmd *find) Run(ctx context.Context, f *flag.FlagSet) error { 216 client, err := cmd.Client() 217 if err != nil { 218 return err 219 } 220 221 finder, err := cmd.Finder() 222 if err != nil { 223 return err 224 } 225 226 if cmd.id { 227 cmd.ref = true 228 } 229 230 root := client.ServiceContent.RootFolder 231 rootPath := "/" 232 233 arg := f.Arg(0) 234 props := f.Args() 235 236 if len(props) > 0 { 237 if strings.HasPrefix(arg, "-") { 238 arg = "." 239 } else { 240 props = props[1:] 241 } 242 } 243 244 if len(props)%2 != 0 { 245 return flag.ErrHelp 246 } 247 248 dc, err := cmd.DatacenterIfSpecified() 249 if err != nil { 250 return err 251 } 252 253 switch arg { 254 case rootPath: 255 case "", ".": 256 if dc == nil { 257 arg = rootPath 258 } else { 259 arg = "." 260 root = dc.Reference() 261 rootPath = dc.InventoryPath 262 } 263 default: 264 path := arg 265 if !strings.Contains(arg, "/") { 266 // Force list mode 267 p := "." 268 if dc != nil { 269 p = dc.InventoryPath 270 } 271 path = strings.Join([]string{p, arg}, "/") 272 } 273 274 l, ferr := finder.ManagedObjectList(ctx, path) 275 if ferr != nil { 276 return err 277 } 278 279 switch len(l) { 280 case 0: 281 return fmt.Errorf("%s not found", arg) 282 case 1: 283 root = l[0].Object.Reference() 284 rootPath = l[0].Path 285 default: 286 return fmt.Errorf("%q matches %d objects", arg, len(l)) 287 } 288 } 289 290 filter := property.Match{} 291 292 if len(props)%2 != 0 { 293 return flag.ErrHelp 294 } 295 296 for i := 0; i < len(props); i++ { 297 key := props[i] 298 if !strings.HasPrefix(key, "-") { 299 return flag.ErrHelp 300 } 301 302 key = key[1:] 303 i++ 304 val := props[i] 305 306 if xf := f.Lookup(key); xf != nil { 307 // Support use of -flag following the ROOT arg (flag package does not do this) 308 if err = xf.Value.Set(val); err != nil { 309 return err 310 } 311 } else { 312 filter[key] = val 313 } 314 } 315 316 filter["name"] = cmd.name 317 var paths []string 318 319 printPath := func(o types.ManagedObjectReference, p string) { 320 if cmd.ref && !cmd.long { 321 paths = append(paths, cmd.mo(o)) 322 return 323 } 324 325 path := strings.Replace(p, rootPath, arg, 1) 326 if cmd.long { 327 id := strings.TrimPrefix(o.Type, "Vmware") 328 if cmd.ref { 329 id = cmd.mo(o) 330 } 331 332 path = id + "\t" + path 333 } 334 paths = append(paths, path) 335 } 336 337 recurse := false 338 339 switch cmd.maxdepth { 340 case -1: 341 recurse = true 342 case 0: 343 case 1: 344 default: 345 return flag.ErrHelp // TODO: ? 346 } 347 348 if cmd.parent { 349 entities, err := mo.Ancestors(ctx, client, client.ServiceContent.PropertyCollector, root) 350 if err != nil { 351 return err 352 } 353 354 for i := len(entities) - 1; i >= 0; i-- { 355 if cmd.rootMatch(ctx, entities[i], client, filter) { 356 printPath(entities[i].Reference(), internal.InventoryPath(entities[:i+1])) 357 } 358 } 359 360 return cmd.writeResult(paths) 361 } 362 363 if cmd.rootMatch(ctx, root, client, filter) { 364 printPath(root, arg) 365 } 366 367 if cmd.maxdepth == 0 { 368 return cmd.writeResult(paths) 369 } 370 371 m := view.NewManager(client) 372 373 v, err := m.CreateContainerView(ctx, root, cmd.kind, recurse) 374 if err != nil { 375 return err 376 } 377 378 defer func() { 379 _ = v.Destroy(ctx) 380 }() 381 382 objs, err := v.Find(ctx, cmd.kind, filter) 383 if err != nil { 384 return err 385 } 386 387 for _, o := range objs { 388 var path string 389 390 if cmd.long || !cmd.ref { 391 e, err := finder.Element(ctx, o) 392 if err != nil { 393 if fault.Is(err, &types.ManagedObjectNotFound{}) { 394 continue // object was deleted after v.Find() returned 395 } 396 return err 397 } 398 path = e.Path 399 } 400 401 printPath(o, path) 402 } 403 404 return cmd.writeResult(paths) 405 }