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. 139 140The '-type' flag value can be a managed entity type or one of the following aliases: 141 142%s 143Examples: 144 govc find 145 govc find /dc1 -type c 146 govc find vm -name my-vm-* 147 govc find . -type n 148 govc find . -type m -runtime.powerState poweredOn 149 govc find . -type m -datastore $(govc find -i datastore -name vsanDatastore) 150 govc find . -type s -summary.type vsan 151 govc find . -type h -hardware.cpuInfo.numCpuCores 16`, atable) 152} 153 154func (cmd *find) Process(ctx context.Context) error { 155 if err := cmd.DatacenterFlag.Process(ctx); err != nil { 156 return err 157 } 158 return nil 159} 160 161// rootMatch returns true if the root object path should be printed 162func (cmd *find) rootMatch(ctx context.Context, root object.Reference, client *vim25.Client, filter property.Filter) bool { 163 ref := root.Reference() 164 165 if !cmd.kind.wanted(ref.Type) { 166 return false 167 } 168 169 if len(filter) == 1 && filter["name"] == "*" { 170 return true 171 } 172 173 var content []types.ObjectContent 174 175 pc := property.DefaultCollector(client) 176 _ = pc.RetrieveWithFilter(ctx, []types.ManagedObjectReference{ref}, filter.Keys(), &content, filter) 177 178 return content != nil 179} 180 181func (cmd *find) Run(ctx context.Context, f *flag.FlagSet) error { 182 client, err := cmd.Client() 183 if err != nil { 184 return err 185 } 186 187 finder, err := cmd.Finder() 188 if err != nil { 189 return err 190 } 191 192 root := client.ServiceContent.RootFolder 193 rootPath := "/" 194 195 arg := f.Arg(0) 196 props := f.Args() 197 198 if len(props) > 0 { 199 if strings.HasPrefix(arg, "-") { 200 arg = "." 201 } else { 202 props = props[1:] 203 } 204 } 205 206 if len(props)%2 != 0 { 207 return flag.ErrHelp 208 } 209 210 switch arg { 211 case rootPath: 212 case "", ".": 213 dc, _ := cmd.DatacenterIfSpecified() 214 if dc == nil { 215 arg = rootPath 216 } else { 217 arg = "." 218 root = dc.Reference() 219 rootPath = dc.InventoryPath 220 } 221 default: 222 l, ferr := finder.ManagedObjectList(ctx, arg) 223 if ferr != nil { 224 return err 225 } 226 227 switch len(l) { 228 case 0: 229 return fmt.Errorf("%s not found", arg) 230 case 1: 231 root = l[0].Object.Reference() 232 rootPath = l[0].Path 233 default: 234 return flag.ErrHelp 235 } 236 } 237 238 filter := property.Filter{} 239 240 if len(props)%2 != 0 { 241 return flag.ErrHelp 242 } 243 244 for i := 0; i < len(props); i++ { 245 key := props[i] 246 if !strings.HasPrefix(key, "-") { 247 return flag.ErrHelp 248 } 249 250 key = key[1:] 251 i++ 252 val := props[i] 253 254 if xf := f.Lookup(key); xf != nil { 255 // Support use of -flag following the ROOT arg (flag package does not do this) 256 if err = xf.Value.Set(val); err != nil { 257 return err 258 } 259 } else { 260 filter[key] = val 261 } 262 } 263 264 filter["name"] = cmd.name 265 266 printPath := func(o types.ManagedObjectReference, p string) { 267 if cmd.ref { 268 fmt.Fprintln(cmd.Out, o) 269 return 270 } 271 272 path := strings.Replace(p, rootPath, arg, 1) 273 fmt.Fprintln(cmd.Out, path) 274 } 275 276 recurse := false 277 278 switch cmd.maxdepth { 279 case -1: 280 recurse = true 281 case 0: 282 case 1: 283 default: 284 return flag.ErrHelp // TODO: ? 285 } 286 287 if cmd.rootMatch(ctx, root, client, filter) { 288 printPath(root, arg) 289 } 290 291 if cmd.maxdepth == 0 { 292 return nil 293 } 294 295 m := view.NewManager(client) 296 297 v, err := m.CreateContainerView(ctx, root, cmd.kind, recurse) 298 if err != nil { 299 return err 300 } 301 302 defer v.Destroy(ctx) 303 304 objs, err := v.Find(ctx, cmd.kind, filter) 305 if err != nil { 306 return err 307 } 308 309 for _, o := range objs { 310 var path string 311 312 if !cmd.ref { 313 e, err := finder.Element(ctx, o) 314 if err != nil { 315 return err 316 } 317 path = e.Path 318 } 319 320 printPath(o, path) 321 } 322 323 return nil 324} 325