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