github.com/vmware/govmomi@v0.51.0/cli/flags/search.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 flags 6 7 import ( 8 "context" 9 "errors" 10 "flag" 11 "fmt" 12 "strings" 13 14 "github.com/vmware/govmomi/fault" 15 "github.com/vmware/govmomi/find" 16 "github.com/vmware/govmomi/object" 17 "github.com/vmware/govmomi/vim25" 18 "github.com/vmware/govmomi/vim25/types" 19 ) 20 21 const ( 22 SearchVirtualMachines = iota + 1 23 SearchHosts 24 SearchVirtualApps 25 ) 26 27 type SearchFlag struct { 28 common 29 30 *ClientFlag 31 *DatacenterFlag 32 33 t int 34 entity string 35 36 byDatastorePath string 37 byDNSName string 38 byInventoryPath string 39 byIP string 40 byUUID string 41 42 isset bool 43 } 44 45 func NewSearchFlag(ctx context.Context, t int) (*SearchFlag, context.Context) { 46 searchFlagKey := flagKey(fmt.Sprintf("search%d", t)) 47 48 if v := ctx.Value(searchFlagKey); v != nil { 49 return v.(*SearchFlag), ctx 50 } 51 52 v := &SearchFlag{ 53 t: t, 54 } 55 56 v.ClientFlag, ctx = NewClientFlag(ctx) 57 v.DatacenterFlag, ctx = NewDatacenterFlag(ctx) 58 59 switch t { 60 case SearchVirtualMachines: 61 v.entity = "VM" 62 case SearchHosts: 63 v.entity = "host" 64 case SearchVirtualApps: 65 v.entity = "vapp" 66 default: 67 panic("invalid search type") 68 } 69 70 ctx = context.WithValue(ctx, searchFlagKey, v) 71 return v, ctx 72 } 73 74 func (flag *SearchFlag) Register(ctx context.Context, fs *flag.FlagSet) { 75 flag.RegisterOnce(func() { 76 flag.ClientFlag.Register(ctx, fs) 77 flag.DatacenterFlag.Register(ctx, fs) 78 79 register := func(v *string, f string, d string) { 80 f = fmt.Sprintf("%s.%s", strings.ToLower(flag.entity), f) 81 d = fmt.Sprintf(d, flag.entity) 82 fs.StringVar(v, f, "", d) 83 } 84 85 switch flag.t { 86 case SearchVirtualMachines: 87 register(&flag.byDatastorePath, "path", "Find %s by path to .vmx file") 88 } 89 90 switch flag.t { 91 case SearchVirtualMachines, SearchHosts: 92 register(&flag.byDNSName, "dns", "Find %s by FQDN") 93 register(&flag.byIP, "ip", "Find %s by IP address") 94 register(&flag.byUUID, "uuid", "Find %s by UUID") 95 } 96 97 register(&flag.byInventoryPath, "ipath", "Find %s by inventory path") 98 }) 99 } 100 101 func (flag *SearchFlag) Process(ctx context.Context) error { 102 return flag.ProcessOnce(func() error { 103 if err := flag.ClientFlag.Process(ctx); err != nil { 104 return err 105 } 106 if err := flag.DatacenterFlag.Process(ctx); err != nil { 107 return err 108 } 109 110 flags := []string{ 111 flag.byDatastorePath, 112 flag.byDNSName, 113 flag.byInventoryPath, 114 flag.byIP, 115 flag.byUUID, 116 } 117 118 flag.isset = false 119 for _, f := range flags { 120 if f != "" { 121 if flag.isset { 122 return errors.New("cannot use more than one search flag") 123 } 124 flag.isset = true 125 } 126 } 127 128 return nil 129 }) 130 } 131 132 func (flag *SearchFlag) IsSet() bool { 133 return flag.isset 134 } 135 136 func (flag *SearchFlag) searchIndex(c *vim25.Client) *object.SearchIndex { 137 return object.NewSearchIndex(c) 138 } 139 140 func (flag *SearchFlag) searchByDatastorePath(c *vim25.Client, dc *object.Datacenter) (object.Reference, error) { 141 ctx := context.TODO() 142 switch flag.t { 143 case SearchVirtualMachines: 144 return flag.searchIndex(c).FindByDatastorePath(ctx, dc, flag.byDatastorePath) 145 default: 146 panic("unsupported type") 147 } 148 } 149 150 func (flag *SearchFlag) searchByDNSName(c *vim25.Client, dc *object.Datacenter) (object.Reference, error) { 151 ctx := context.TODO() 152 switch flag.t { 153 case SearchVirtualMachines: 154 return flag.searchIndex(c).FindByDnsName(ctx, dc, flag.byDNSName, true) 155 case SearchHosts: 156 return flag.searchIndex(c).FindByDnsName(ctx, dc, flag.byDNSName, false) 157 default: 158 panic("unsupported type") 159 } 160 } 161 162 func (flag *SearchFlag) searchByInventoryPath(c *vim25.Client) (object.Reference, error) { 163 ctx := context.TODO() 164 return flag.searchIndex(c).FindByInventoryPath(ctx, flag.byInventoryPath) 165 } 166 167 func (flag *SearchFlag) searchByIP(c *vim25.Client, dc *object.Datacenter) (object.Reference, error) { 168 ctx := context.TODO() 169 switch flag.t { 170 case SearchVirtualMachines: 171 return flag.searchIndex(c).FindByIp(ctx, dc, flag.byIP, true) 172 case SearchHosts: 173 return flag.searchIndex(c).FindByIp(ctx, dc, flag.byIP, false) 174 default: 175 panic("unsupported type") 176 } 177 } 178 179 func (flag *SearchFlag) searchByUUID(c *vim25.Client, dc *object.Datacenter) (object.Reference, error) { 180 ctx := context.TODO() 181 isVM := false 182 switch flag.t { 183 case SearchVirtualMachines: 184 isVM = true 185 case SearchHosts: 186 default: 187 panic("unsupported type") 188 } 189 190 var ref object.Reference 191 var err error 192 193 for _, iu := range []*bool{nil, types.NewBool(true)} { 194 ref, err = flag.searchIndex(c).FindByUuid(ctx, dc, flag.byUUID, isVM, iu) 195 if err != nil { 196 if fault.Is(err, &types.InvalidArgument{}) { 197 continue 198 } 199 return nil, err 200 } 201 if ref != nil { 202 break 203 } 204 } 205 206 return ref, nil 207 } 208 209 func (flag *SearchFlag) search() (object.Reference, error) { 210 ctx := context.TODO() 211 var ref object.Reference 212 var err error 213 var dc *object.Datacenter 214 215 c, err := flag.Client() 216 if err != nil { 217 return nil, err 218 } 219 220 isPath := flag.byInventoryPath != "" 221 if !isPath { 222 // All other SearchIndex methods require a Datacenter param 223 dc, err = flag.Datacenter() 224 if err != nil { 225 return nil, err 226 } 227 } 228 229 switch { 230 case isPath: 231 ref, err = flag.searchByInventoryPath(c) 232 case flag.byDatastorePath != "": 233 ref, err = flag.searchByDatastorePath(c, dc) 234 case flag.byDNSName != "": 235 ref, err = flag.searchByDNSName(c, dc) 236 case flag.byIP != "": 237 ref, err = flag.searchByIP(c, dc) 238 case flag.byUUID != "": 239 ref, err = flag.searchByUUID(c, dc) 240 default: 241 err = errors.New("no search flag specified") 242 } 243 244 if err != nil { 245 return nil, err 246 } 247 248 if ref == nil { 249 return nil, fmt.Errorf("no such %s", flag.entity) 250 } 251 252 // set the InventoryPath field 253 finder, err := flag.Finder() 254 if err != nil { 255 return nil, err 256 } 257 ref, err = finder.ObjectReference(ctx, ref.Reference()) 258 if err != nil { 259 return nil, err 260 } 261 262 return ref, nil 263 } 264 265 func (flag *SearchFlag) VirtualMachine() (*object.VirtualMachine, error) { 266 ref, err := flag.search() 267 if err != nil { 268 return nil, err 269 } 270 271 vm, ok := ref.(*object.VirtualMachine) 272 if !ok { 273 return nil, fmt.Errorf("expected VirtualMachine entity, got %s", ref.Reference().Type) 274 } 275 276 return vm, nil 277 } 278 279 func (flag *SearchFlag) VirtualMachines(args []string) ([]*object.VirtualMachine, error) { 280 ctx := context.TODO() 281 var out []*object.VirtualMachine 282 283 if flag.IsSet() { 284 vm, err := flag.VirtualMachine() 285 if err != nil { 286 return nil, err 287 } 288 289 out = append(out, vm) 290 return out, nil 291 } 292 293 // List virtual machines 294 if len(args) == 0 { 295 return nil, errors.New("no argument") 296 } 297 298 finder, err := flag.Finder() 299 if err != nil { 300 return nil, err 301 } 302 303 var nfe error 304 305 // List virtual machines for every argument 306 for _, arg := range args { 307 vms, err := finder.VirtualMachineList(ctx, arg) 308 if err != nil { 309 if _, ok := err.(*find.NotFoundError); ok { 310 // Let caller decide how to handle NotFoundError 311 nfe = err 312 continue 313 } 314 return nil, err 315 } 316 317 out = append(out, vms...) 318 } 319 320 return out, nfe 321 } 322 323 func (flag *SearchFlag) VirtualApp() (*object.VirtualApp, error) { 324 ref, err := flag.search() 325 if err != nil { 326 return nil, err 327 } 328 329 app, ok := ref.(*object.VirtualApp) 330 if !ok { 331 return nil, fmt.Errorf("expected VirtualApp entity, got %s", ref.Reference().Type) 332 } 333 334 return app, nil 335 } 336 337 func (flag *SearchFlag) VirtualApps(args []string) ([]*object.VirtualApp, error) { 338 ctx := context.TODO() 339 var out []*object.VirtualApp 340 341 if flag.IsSet() { 342 app, err := flag.VirtualApp() 343 if err != nil { 344 return nil, err 345 } 346 347 out = append(out, app) 348 return out, nil 349 } 350 351 // List virtual apps 352 if len(args) == 0 { 353 return nil, errors.New("no argument") 354 } 355 356 finder, err := flag.Finder() 357 if err != nil { 358 return nil, err 359 } 360 361 // List virtual apps for every argument 362 for _, arg := range args { 363 apps, err := finder.VirtualAppList(ctx, arg) 364 if err != nil { 365 return nil, err 366 } 367 368 out = append(out, apps...) 369 } 370 371 return out, nil 372 } 373 374 func (flag *SearchFlag) HostSystem() (*object.HostSystem, error) { 375 ref, err := flag.search() 376 if err != nil { 377 return nil, err 378 } 379 380 host, ok := ref.(*object.HostSystem) 381 if !ok { 382 return nil, fmt.Errorf("expected HostSystem entity, got %s", ref.Reference().Type) 383 } 384 385 return host, nil 386 } 387 388 func (flag *SearchFlag) HostSystems(args []string) ([]*object.HostSystem, error) { 389 ctx := context.TODO() 390 var out []*object.HostSystem 391 392 if flag.IsSet() { 393 host, err := flag.HostSystem() 394 if err != nil { 395 return nil, err 396 } 397 398 out = append(out, host) 399 return out, nil 400 } 401 402 // List host system 403 if len(args) == 0 { 404 return nil, errors.New("no argument") 405 } 406 407 finder, err := flag.Finder() 408 if err != nil { 409 return nil, err 410 } 411 412 // List host systems for every argument 413 for _, arg := range args { 414 vms, err := finder.HostSystemList(ctx, arg) 415 if err != nil { 416 return nil, err 417 } 418 419 out = append(out, vms...) 420 } 421 422 return out, nil 423 }