1/* 2Copyright (c) 2017 VMware, Inc. All Rights Reserved. 3 4Licensed under the Apache License, Version 2.0 (the "License"); 5you may not use this file except in compliance with the License. 6You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10Unless required by applicable law or agreed to in writing, software 11distributed under the License is distributed on an "AS IS" BASIS, 12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13See the License for the specific language governing permissions and 14limitations under the License. 15*/ 16 17package object 18 19import ( 20 "bytes" 21 "context" 22 "flag" 23 "fmt" 24 "io" 25 "strings" 26 27 "github.com/vmware/govmomi/govc/cli" 28 "github.com/vmware/govmomi/govc/flags" 29 "github.com/vmware/govmomi/object" 30 "github.com/vmware/govmomi/property" 31 "github.com/vmware/govmomi/view" 32 "github.com/vmware/govmomi/vim25" 33 "github.com/vmware/govmomi/vim25/types" 34) 35 36type find struct { 37 *flags.DatacenterFlag 38 39 ref bool 40 kind kinds 41 name string 42 maxdepth int 43} 44 45var alias = []struct { 46 name string 47 kind string 48}{ 49 {"a", "VirtualApp"}, 50 {"c", "ClusterComputeResource"}, 51 {"d", "Datacenter"}, 52 {"f", "Folder"}, 53 {"g", "DistributedVirtualPortgroup"}, 54 {"h", "HostSystem"}, 55 {"m", "VirtualMachine"}, 56 {"n", "Network"}, 57 {"o", "OpaqueNetwork"}, 58 {"p", "ResourcePool"}, 59 {"r", "ComputeResource"}, 60 {"s", "Datastore"}, 61 {"w", "DistributedVirtualSwitch"}, 62} 63 64func aliasHelp() string { 65 var help bytes.Buffer 66 67 for _, a := range alias { 68 fmt.Fprintf(&help, " %s %s\n", a.name, a.kind) 69 } 70 71 return help.String() 72} 73 74type kinds []string 75 76func (e *kinds) String() string { 77 return fmt.Sprint(*e) 78} 79 80func (e *kinds) Set(value string) error { 81 *e = append(*e, e.alias(value)) 82 return nil 83} 84 85func (e *kinds) alias(value string) string { 86 if len(value) != 1 { 87 return value 88 } 89 90 for _, a := range alias { 91 if a.name == value { 92 return a.kind 93 } 94 } 95 96 return value 97} 98 99func (e *kinds) wanted(kind string) bool { 100 if len(*e) == 0 { 101 return true 102 } 103 104 for _, k := range *e { 105 if kind == k { 106 return true 107 } 108 } 109 110 return false 111} 112 113func init() { 114 cli.Register("find", &find{}) 115} 116 117func (cmd *find) Register(ctx context.Context, f *flag.FlagSet) { 118 cmd.DatacenterFlag, ctx = flags.NewDatacenterFlag(ctx) 119 cmd.DatacenterFlag.Register(ctx, f) 120 121 f.Var(&cmd.kind, "type", "Resource type") 122 f.StringVar(&cmd.name, "name", "*", "Resource name") 123 f.IntVar(&cmd.maxdepth, "maxdepth", -1, "Max depth") 124 f.BoolVar(&cmd.ref, "i", false, "Print the managed object reference") 125} 126 127func (cmd *find) Usage() string { 128 return "[ROOT] [KEY VAL]..." 129} 130 131func (cmd *find) Description() string { 132 atable := aliasHelp() 133 134 return fmt.Sprintf(`Find managed objects. 135 136ROOT can be an inventory path or ManagedObjectReference. 137ROOT defaults to '.', an alias for the root folder or DC if set. 138 139Optional KEY VAL pairs can be used to filter results against object instance properties. 140Use the govc 'object.collect' command to view possible object property keys. 141 142The '-type' flag value can be a managed entity type or one of the following aliases: 143 144%s 145Examples: 146 govc find 147 govc find /dc1 -type c 148 govc find vm -name my-vm-* 149 govc find . -type n 150 govc find . -type m -runtime.powerState poweredOn 151 govc find . -type m -datastore $(govc find -i datastore -name vsanDatastore) 152 govc find . -type s -summary.type vsan 153 govc find . -type h -hardware.cpuInfo.numCpuCores 16`, atable) 154} 155 156// rootMatch returns true if the root object path should be printed 157func (cmd *find) rootMatch(ctx context.Context, root object.Reference, client *vim25.Client, filter property.Filter) bool { 158 ref := root.Reference() 159 160 if !cmd.kind.wanted(ref.Type) { 161 return false 162 } 163 164 if len(filter) == 1 && filter["name"] == "*" { 165 return true 166 } 167 168 var content []types.ObjectContent 169 170 pc := property.DefaultCollector(client) 171 _ = pc.RetrieveWithFilter(ctx, []types.ManagedObjectReference{ref}, filter.Keys(), &content, filter) 172 173 return content != nil 174} 175 176type findResult []string 177 178func (r findResult) Write(w io.Writer) error { 179 for i := range r { 180 fmt.Fprintln(w, r[i]) 181 } 182 return nil 183} 184 185func (r findResult) Dump() interface{} { 186 return []string(r) 187} 188 189func (cmd *find) Run(ctx context.Context, f *flag.FlagSet) error { 190 client, err := cmd.Client() 191 if err != nil { 192 return err 193 } 194 195 finder, err := cmd.Finder() 196 if err != nil { 197 return err 198 } 199 200 root := client.ServiceContent.RootFolder 201 rootPath := "/" 202 203 arg := f.Arg(0) 204 props := f.Args() 205 206 if len(props) > 0 { 207 if strings.HasPrefix(arg, "-") { 208 arg = "." 209 } else { 210 props = props[1:] 211 } 212 } 213 214 if len(props)%2 != 0 { 215 return flag.ErrHelp 216 } 217 218 dc, err := cmd.DatacenterIfSpecified() 219 if err != nil { 220 return err 221 } 222 223 switch arg { 224 case rootPath: 225 case "", ".": 226 if dc == nil { 227 arg = rootPath 228 } else { 229 arg = "." 230 root = dc.Reference() 231 rootPath = dc.InventoryPath 232 } 233 default: 234 path := arg 235 if !strings.Contains(arg, "/") { 236 // Force list mode 237 p := "." 238 if dc != nil { 239 p = dc.InventoryPath 240 } 241 path = strings.Join([]string{p, arg}, "/") 242 } 243 244 l, ferr := finder.ManagedObjectList(ctx, path) 245 if ferr != nil { 246 return err 247 } 248 249 switch len(l) { 250 case 0: 251 return fmt.Errorf("%s not found", arg) 252 case 1: 253 root = l[0].Object.Reference() 254 rootPath = l[0].Path 255 default: 256 return fmt.Errorf("%q matches %d objects", arg, len(l)) 257 } 258 } 259 260 filter := property.Filter{} 261 262 if len(props)%2 != 0 { 263 return flag.ErrHelp 264 } 265 266 for i := 0; i < len(props); i++ { 267 key := props[i] 268 if !strings.HasPrefix(key, "-") { 269 return flag.ErrHelp 270 } 271 272 key = key[1:] 273 i++ 274 val := props[i] 275 276 if xf := f.Lookup(key); xf != nil { 277 // Support use of -flag following the ROOT arg (flag package does not do this) 278 if err = xf.Value.Set(val); err != nil { 279 return err 280 } 281 } else { 282 filter[key] = val 283 } 284 } 285 286 filter["name"] = cmd.name 287 var paths findResult 288 289 printPath := func(o types.ManagedObjectReference, p string) { 290 if cmd.ref { 291 paths = append(paths, o.String()) 292 return 293 } 294 295 path := strings.Replace(p, rootPath, arg, 1) 296 paths = append(paths, path) 297 } 298 299 recurse := false 300 301 switch cmd.maxdepth { 302 case -1: 303 recurse = true 304 case 0: 305 case 1: 306 default: 307 return flag.ErrHelp // TODO: ? 308 } 309 310 if cmd.rootMatch(ctx, root, client, filter) { 311 printPath(root, arg) 312 } 313 314 if cmd.maxdepth == 0 { 315 return cmd.WriteResult(paths) 316 } 317 318 m := view.NewManager(client) 319 320 v, err := m.CreateContainerView(ctx, root, cmd.kind, recurse) 321 if err != nil { 322 return err 323 } 324 325 defer v.Destroy(ctx) 326 327 objs, err := v.Find(ctx, cmd.kind, filter) 328 if err != nil { 329 return err 330 } 331 332 for _, o := range objs { 333 var path string 334 335 if !cmd.ref { 336 e, err := finder.Element(ctx, o) 337 if err != nil { 338 return err 339 } 340 path = e.Path 341 } 342 343 printPath(o, path) 344 } 345 346 return cmd.WriteResult(paths) 347} 348