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