1/* 2Copyright (c) 2014-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 find 18 19import ( 20 "context" 21 "errors" 22 "path" 23 "strings" 24 25 "github.com/vmware/govmomi/list" 26 "github.com/vmware/govmomi/object" 27 "github.com/vmware/govmomi/property" 28 "github.com/vmware/govmomi/vim25" 29 "github.com/vmware/govmomi/vim25/mo" 30 "github.com/vmware/govmomi/vim25/types" 31) 32 33type Finder struct { 34 client *vim25.Client 35 r recurser 36 dc *object.Datacenter 37 si *object.SearchIndex 38 folders *object.DatacenterFolders 39} 40 41func NewFinder(client *vim25.Client, all bool) *Finder { 42 f := &Finder{ 43 client: client, 44 si: object.NewSearchIndex(client), 45 r: recurser{ 46 Collector: property.DefaultCollector(client), 47 All: all, 48 }, 49 } 50 51 return f 52} 53 54func (f *Finder) SetDatacenter(dc *object.Datacenter) *Finder { 55 f.dc = dc 56 f.folders = nil 57 return f 58} 59 60// findRoot makes it possible to use "find" mode with a different root path. 61// Example: ResourcePoolList("/dc1/host/cluster1/...") 62func (f *Finder) findRoot(ctx context.Context, root *list.Element, parts []string) bool { 63 if len(parts) == 0 { 64 return false 65 } 66 67 ix := len(parts) - 1 68 69 if parts[ix] != "..." { 70 return false 71 } 72 73 if ix == 0 { 74 return true // We already have the Object for root.Path 75 } 76 77 // Lookup the Object for the new root.Path 78 rootPath := path.Join(root.Path, path.Join(parts[:ix]...)) 79 80 ref, err := f.si.FindByInventoryPath(ctx, rootPath) 81 if err != nil || ref == nil { 82 // If we get an error or fail to match, fall through to find() with the original root and path 83 return false 84 } 85 86 root.Path = rootPath 87 root.Object = ref 88 89 return true 90} 91 92func (f *Finder) find(ctx context.Context, arg string, s *spec) ([]list.Element, error) { 93 isPath := strings.Contains(arg, "/") 94 95 root := list.Element{ 96 Path: "/", 97 Object: object.NewRootFolder(f.client), 98 } 99 100 parts := list.ToParts(arg) 101 102 if len(parts) > 0 { 103 switch parts[0] { 104 case "..": // Not supported; many edge case, little value 105 return nil, errors.New("cannot traverse up a tree") 106 case ".": // Relative to whatever 107 pivot, err := s.Relative(ctx) 108 if err != nil { 109 return nil, err 110 } 111 112 mes, err := mo.Ancestors(ctx, f.client, f.client.ServiceContent.PropertyCollector, pivot.Reference()) 113 if err != nil { 114 return nil, err 115 } 116 117 for _, me := range mes { 118 // Skip root entity in building inventory path. 119 if me.Parent == nil { 120 continue 121 } 122 root.Path = path.Join(root.Path, me.Name) 123 } 124 125 root.Object = pivot 126 parts = parts[1:] 127 } 128 } 129 130 if s.listMode(isPath) { 131 if f.findRoot(ctx, &root, parts) { 132 parts = []string{"*"} 133 } else { 134 return f.r.List(ctx, s, root, parts) 135 } 136 } 137 138 s.Parents = append(s.Parents, s.Nested...) 139 140 return f.r.Find(ctx, s, root, parts) 141} 142 143func (f *Finder) datacenter() (*object.Datacenter, error) { 144 if f.dc == nil { 145 return nil, errors.New("please specify a datacenter") 146 } 147 148 return f.dc, nil 149} 150 151// datacenterPath returns the absolute path to the Datacenter containing the given ref 152func (f *Finder) datacenterPath(ctx context.Context, ref types.ManagedObjectReference) (string, error) { 153 mes, err := mo.Ancestors(ctx, f.client, f.client.ServiceContent.PropertyCollector, ref) 154 if err != nil { 155 return "", err 156 } 157 158 // Chop leaves under the Datacenter 159 for i := len(mes) - 1; i > 0; i-- { 160 if mes[i].Self.Type == "Datacenter" { 161 break 162 } 163 mes = mes[:i] 164 } 165 166 var p string 167 168 for _, me := range mes { 169 // Skip root entity in building inventory path. 170 if me.Parent == nil { 171 continue 172 } 173 174 p = p + "/" + me.Name 175 } 176 177 return p, nil 178} 179 180func (f *Finder) dcFolders(ctx context.Context) (*object.DatacenterFolders, error) { 181 if f.folders != nil { 182 return f.folders, nil 183 } 184 185 dc, err := f.datacenter() 186 if err != nil { 187 return nil, err 188 } 189 190 folders, err := dc.Folders(ctx) 191 if err != nil { 192 return nil, err 193 } 194 195 f.folders = folders 196 197 return f.folders, nil 198} 199 200func (f *Finder) dcReference(_ context.Context) (object.Reference, error) { 201 dc, err := f.datacenter() 202 if err != nil { 203 return nil, err 204 } 205 206 return dc, nil 207} 208 209func (f *Finder) vmFolder(ctx context.Context) (object.Reference, error) { 210 folders, err := f.dcFolders(ctx) 211 if err != nil { 212 return nil, err 213 } 214 215 return folders.VmFolder, nil 216} 217 218func (f *Finder) hostFolder(ctx context.Context) (object.Reference, error) { 219 folders, err := f.dcFolders(ctx) 220 if err != nil { 221 return nil, err 222 } 223 224 return folders.HostFolder, nil 225} 226 227func (f *Finder) datastoreFolder(ctx context.Context) (object.Reference, error) { 228 folders, err := f.dcFolders(ctx) 229 if err != nil { 230 return nil, err 231 } 232 233 return folders.DatastoreFolder, nil 234} 235 236func (f *Finder) networkFolder(ctx context.Context) (object.Reference, error) { 237 folders, err := f.dcFolders(ctx) 238 if err != nil { 239 return nil, err 240 } 241 242 return folders.NetworkFolder, nil 243} 244 245func (f *Finder) rootFolder(_ context.Context) (object.Reference, error) { 246 return object.NewRootFolder(f.client), nil 247} 248 249func (f *Finder) managedObjectList(ctx context.Context, path string, tl bool, include []string) ([]list.Element, error) { 250 fn := f.rootFolder 251 252 if f.dc != nil { 253 fn = f.dcReference 254 } 255 256 if len(path) == 0 { 257 path = "." 258 } 259 260 s := &spec{ 261 Relative: fn, 262 Parents: []string{"ComputeResource", "ClusterComputeResource", "HostSystem", "VirtualApp", "StoragePod"}, 263 Include: include, 264 } 265 266 if tl { 267 s.Contents = true 268 s.ListMode = types.NewBool(true) 269 } 270 271 return f.find(ctx, path, s) 272} 273 274// Element returns an Element for the given ManagedObjectReference 275// This method is only useful for looking up the InventoryPath of a ManagedObjectReference. 276func (f *Finder) Element(ctx context.Context, ref types.ManagedObjectReference) (*list.Element, error) { 277 rl := func(_ context.Context) (object.Reference, error) { 278 return ref, nil 279 } 280 281 s := &spec{ 282 Relative: rl, 283 } 284 285 e, err := f.find(ctx, "./", s) 286 if err != nil { 287 return nil, err 288 } 289 290 if len(e) == 0 { 291 return nil, &NotFoundError{ref.Type, ref.Value} 292 } 293 294 if len(e) > 1 { 295 panic("ManagedObjectReference must be unique") 296 } 297 298 return &e[0], nil 299} 300 301// ObjectReference converts the given ManagedObjectReference to a type from the object package via object.NewReference 302// with the object.Common.InventoryPath field set. 303func (f *Finder) ObjectReference(ctx context.Context, ref types.ManagedObjectReference) (object.Reference, error) { 304 e, err := f.Element(ctx, ref) 305 if err != nil { 306 return nil, err 307 } 308 309 r := object.NewReference(f.client, ref) 310 311 type common interface { 312 SetInventoryPath(string) 313 } 314 315 r.(common).SetInventoryPath(e.Path) 316 317 if f.dc != nil { 318 if ds, ok := r.(*object.Datastore); ok { 319 ds.DatacenterPath = f.dc.InventoryPath 320 } 321 } 322 323 return r, nil 324} 325 326func (f *Finder) ManagedObjectList(ctx context.Context, path string, include ...string) ([]list.Element, error) { 327 return f.managedObjectList(ctx, path, false, include) 328} 329 330func (f *Finder) ManagedObjectListChildren(ctx context.Context, path string, include ...string) ([]list.Element, error) { 331 return f.managedObjectList(ctx, path, true, include) 332} 333 334func (f *Finder) DatacenterList(ctx context.Context, path string) ([]*object.Datacenter, error) { 335 s := &spec{ 336 Relative: f.rootFolder, 337 Include: []string{"Datacenter"}, 338 } 339 340 es, err := f.find(ctx, path, s) 341 if err != nil { 342 return nil, err 343 } 344 345 var dcs []*object.Datacenter 346 for _, e := range es { 347 ref := e.Object.Reference() 348 if ref.Type == "Datacenter" { 349 dc := object.NewDatacenter(f.client, ref) 350 dc.InventoryPath = e.Path 351 dcs = append(dcs, dc) 352 } 353 } 354 355 if len(dcs) == 0 { 356 return nil, &NotFoundError{"datacenter", path} 357 } 358 359 return dcs, nil 360} 361 362func (f *Finder) Datacenter(ctx context.Context, path string) (*object.Datacenter, error) { 363 dcs, err := f.DatacenterList(ctx, path) 364 if err != nil { 365 return nil, err 366 } 367 368 if len(dcs) > 1 { 369 return nil, &MultipleFoundError{"datacenter", path} 370 } 371 372 return dcs[0], nil 373} 374 375func (f *Finder) DefaultDatacenter(ctx context.Context) (*object.Datacenter, error) { 376 dc, err := f.Datacenter(ctx, "*") 377 if err != nil { 378 return nil, toDefaultError(err) 379 } 380 381 return dc, nil 382} 383 384func (f *Finder) DatacenterOrDefault(ctx context.Context, path string) (*object.Datacenter, error) { 385 if path != "" { 386 dc, err := f.Datacenter(ctx, path) 387 if err != nil { 388 return nil, err 389 } 390 return dc, nil 391 } 392 393 return f.DefaultDatacenter(ctx) 394} 395 396func (f *Finder) DatastoreList(ctx context.Context, path string) ([]*object.Datastore, error) { 397 s := &spec{ 398 Relative: f.datastoreFolder, 399 Parents: []string{"StoragePod"}, 400 } 401 402 es, err := f.find(ctx, path, s) 403 if err != nil { 404 return nil, err 405 } 406 407 var dss []*object.Datastore 408 for _, e := range es { 409 ref := e.Object.Reference() 410 if ref.Type == "Datastore" { 411 ds := object.NewDatastore(f.client, ref) 412 ds.InventoryPath = e.Path 413 414 if f.dc == nil { 415 // In this case SetDatacenter was not called and path is absolute 416 ds.DatacenterPath, err = f.datacenterPath(ctx, ref) 417 if err != nil { 418 return nil, err 419 } 420 } else { 421 ds.DatacenterPath = f.dc.InventoryPath 422 } 423 424 dss = append(dss, ds) 425 } 426 } 427 428 if len(dss) == 0 { 429 return nil, &NotFoundError{"datastore", path} 430 } 431 432 return dss, nil 433} 434 435func (f *Finder) Datastore(ctx context.Context, path string) (*object.Datastore, error) { 436 dss, err := f.DatastoreList(ctx, path) 437 if err != nil { 438 return nil, err 439 } 440 441 if len(dss) > 1 { 442 return nil, &MultipleFoundError{"datastore", path} 443 } 444 445 return dss[0], nil 446} 447 448func (f *Finder) DefaultDatastore(ctx context.Context) (*object.Datastore, error) { 449 ds, err := f.Datastore(ctx, "*") 450 if err != nil { 451 return nil, toDefaultError(err) 452 } 453 454 return ds, nil 455} 456 457func (f *Finder) DatastoreOrDefault(ctx context.Context, path string) (*object.Datastore, error) { 458 if path != "" { 459 ds, err := f.Datastore(ctx, path) 460 if err != nil { 461 return nil, err 462 } 463 return ds, nil 464 } 465 466 return f.DefaultDatastore(ctx) 467} 468 469func (f *Finder) DatastoreClusterList(ctx context.Context, path string) ([]*object.StoragePod, error) { 470 s := &spec{ 471 Relative: f.datastoreFolder, 472 } 473 474 es, err := f.find(ctx, path, s) 475 if err != nil { 476 return nil, err 477 } 478 479 var sps []*object.StoragePod 480 for _, e := range es { 481 ref := e.Object.Reference() 482 if ref.Type == "StoragePod" { 483 sp := object.NewStoragePod(f.client, ref) 484 sp.InventoryPath = e.Path 485 sps = append(sps, sp) 486 } 487 } 488 489 if len(sps) == 0 { 490 return nil, &NotFoundError{"datastore cluster", path} 491 } 492 493 return sps, nil 494} 495 496func (f *Finder) DatastoreCluster(ctx context.Context, path string) (*object.StoragePod, error) { 497 sps, err := f.DatastoreClusterList(ctx, path) 498 if err != nil { 499 return nil, err 500 } 501 502 if len(sps) > 1 { 503 return nil, &MultipleFoundError{"datastore cluster", path} 504 } 505 506 return sps[0], nil 507} 508 509func (f *Finder) DefaultDatastoreCluster(ctx context.Context) (*object.StoragePod, error) { 510 sp, err := f.DatastoreCluster(ctx, "*") 511 if err != nil { 512 return nil, toDefaultError(err) 513 } 514 515 return sp, nil 516} 517 518func (f *Finder) DatastoreClusterOrDefault(ctx context.Context, path string) (*object.StoragePod, error) { 519 if path != "" { 520 sp, err := f.DatastoreCluster(ctx, path) 521 if err != nil { 522 return nil, err 523 } 524 return sp, nil 525 } 526 527 return f.DefaultDatastoreCluster(ctx) 528} 529 530func (f *Finder) ComputeResourceList(ctx context.Context, path string) ([]*object.ComputeResource, error) { 531 s := &spec{ 532 Relative: f.hostFolder, 533 } 534 535 es, err := f.find(ctx, path, s) 536 if err != nil { 537 return nil, err 538 } 539 540 var crs []*object.ComputeResource 541 for _, e := range es { 542 var cr *object.ComputeResource 543 544 switch o := e.Object.(type) { 545 case mo.ComputeResource, mo.ClusterComputeResource: 546 cr = object.NewComputeResource(f.client, o.Reference()) 547 default: 548 continue 549 } 550 551 cr.InventoryPath = e.Path 552 crs = append(crs, cr) 553 } 554 555 if len(crs) == 0 { 556 return nil, &NotFoundError{"compute resource", path} 557 } 558 559 return crs, nil 560} 561 562func (f *Finder) ComputeResource(ctx context.Context, path string) (*object.ComputeResource, error) { 563 crs, err := f.ComputeResourceList(ctx, path) 564 if err != nil { 565 return nil, err 566 } 567 568 if len(crs) > 1 { 569 return nil, &MultipleFoundError{"compute resource", path} 570 } 571 572 return crs[0], nil 573} 574 575func (f *Finder) DefaultComputeResource(ctx context.Context) (*object.ComputeResource, error) { 576 cr, err := f.ComputeResource(ctx, "*") 577 if err != nil { 578 return nil, toDefaultError(err) 579 } 580 581 return cr, nil 582} 583 584func (f *Finder) ComputeResourceOrDefault(ctx context.Context, path string) (*object.ComputeResource, error) { 585 if path != "" { 586 cr, err := f.ComputeResource(ctx, path) 587 if err != nil { 588 return nil, err 589 } 590 return cr, nil 591 } 592 593 return f.DefaultComputeResource(ctx) 594} 595 596func (f *Finder) ClusterComputeResourceList(ctx context.Context, path string) ([]*object.ClusterComputeResource, error) { 597 s := &spec{ 598 Relative: f.hostFolder, 599 } 600 601 es, err := f.find(ctx, path, s) 602 if err != nil { 603 return nil, err 604 } 605 606 var ccrs []*object.ClusterComputeResource 607 for _, e := range es { 608 var ccr *object.ClusterComputeResource 609 610 switch o := e.Object.(type) { 611 case mo.ClusterComputeResource: 612 ccr = object.NewClusterComputeResource(f.client, o.Reference()) 613 default: 614 continue 615 } 616 617 ccr.InventoryPath = e.Path 618 ccrs = append(ccrs, ccr) 619 } 620 621 if len(ccrs) == 0 { 622 return nil, &NotFoundError{"cluster", path} 623 } 624 625 return ccrs, nil 626} 627 628func (f *Finder) DefaultClusterComputeResource(ctx context.Context) (*object.ClusterComputeResource, error) { 629 cr, err := f.ClusterComputeResource(ctx, "*") 630 if err != nil { 631 return nil, toDefaultError(err) 632 } 633 634 return cr, nil 635} 636 637func (f *Finder) ClusterComputeResource(ctx context.Context, path string) (*object.ClusterComputeResource, error) { 638 ccrs, err := f.ClusterComputeResourceList(ctx, path) 639 if err != nil { 640 return nil, err 641 } 642 643 if len(ccrs) > 1 { 644 return nil, &MultipleFoundError{"cluster", path} 645 } 646 647 return ccrs[0], nil 648} 649 650func (f *Finder) ClusterComputeResourceOrDefault(ctx context.Context, path string) (*object.ClusterComputeResource, error) { 651 if path != "" { 652 cr, err := f.ClusterComputeResource(ctx, path) 653 if err != nil { 654 return nil, err 655 } 656 return cr, nil 657 } 658 659 return f.DefaultClusterComputeResource(ctx) 660} 661 662func (f *Finder) HostSystemList(ctx context.Context, path string) ([]*object.HostSystem, error) { 663 s := &spec{ 664 Relative: f.hostFolder, 665 Parents: []string{"ComputeResource", "ClusterComputeResource"}, 666 Include: []string{"HostSystem"}, 667 } 668 669 es, err := f.find(ctx, path, s) 670 if err != nil { 671 return nil, err 672 } 673 674 var hss []*object.HostSystem 675 for _, e := range es { 676 var hs *object.HostSystem 677 678 switch o := e.Object.(type) { 679 case mo.HostSystem: 680 hs = object.NewHostSystem(f.client, o.Reference()) 681 682 hs.InventoryPath = e.Path 683 hss = append(hss, hs) 684 case mo.ComputeResource, mo.ClusterComputeResource: 685 cr := object.NewComputeResource(f.client, o.Reference()) 686 687 cr.InventoryPath = e.Path 688 689 hosts, err := cr.Hosts(ctx) 690 if err != nil { 691 return nil, err 692 } 693 694 hss = append(hss, hosts...) 695 } 696 } 697 698 if len(hss) == 0 { 699 return nil, &NotFoundError{"host", path} 700 } 701 702 return hss, nil 703} 704 705func (f *Finder) HostSystem(ctx context.Context, path string) (*object.HostSystem, error) { 706 hss, err := f.HostSystemList(ctx, path) 707 if err != nil { 708 return nil, err 709 } 710 711 if len(hss) > 1 { 712 return nil, &MultipleFoundError{"host", path} 713 } 714 715 return hss[0], nil 716} 717 718func (f *Finder) DefaultHostSystem(ctx context.Context) (*object.HostSystem, error) { 719 hs, err := f.HostSystem(ctx, "*") 720 if err != nil { 721 return nil, toDefaultError(err) 722 } 723 724 return hs, nil 725} 726 727func (f *Finder) HostSystemOrDefault(ctx context.Context, path string) (*object.HostSystem, error) { 728 if path != "" { 729 hs, err := f.HostSystem(ctx, path) 730 if err != nil { 731 return nil, err 732 } 733 return hs, nil 734 } 735 736 return f.DefaultHostSystem(ctx) 737} 738 739func (f *Finder) NetworkList(ctx context.Context, path string) ([]object.NetworkReference, error) { 740 s := &spec{ 741 Relative: f.networkFolder, 742 } 743 744 es, err := f.find(ctx, path, s) 745 if err != nil { 746 return nil, err 747 } 748 749 var ns []object.NetworkReference 750 for _, e := range es { 751 ref := e.Object.Reference() 752 switch ref.Type { 753 case "Network": 754 r := object.NewNetwork(f.client, ref) 755 r.InventoryPath = e.Path 756 ns = append(ns, r) 757 case "OpaqueNetwork": 758 r := object.NewOpaqueNetwork(f.client, ref) 759 r.InventoryPath = e.Path 760 ns = append(ns, r) 761 case "DistributedVirtualPortgroup": 762 r := object.NewDistributedVirtualPortgroup(f.client, ref) 763 r.InventoryPath = e.Path 764 ns = append(ns, r) 765 case "DistributedVirtualSwitch", "VmwareDistributedVirtualSwitch": 766 r := object.NewDistributedVirtualSwitch(f.client, ref) 767 r.InventoryPath = e.Path 768 ns = append(ns, r) 769 } 770 } 771 772 if len(ns) == 0 { 773 return nil, &NotFoundError{"network", path} 774 } 775 776 return ns, nil 777} 778 779func (f *Finder) Network(ctx context.Context, path string) (object.NetworkReference, error) { 780 networks, err := f.NetworkList(ctx, path) 781 if err != nil { 782 return nil, err 783 } 784 785 if len(networks) > 1 { 786 return nil, &MultipleFoundError{"network", path} 787 } 788 789 return networks[0], nil 790} 791 792func (f *Finder) DefaultNetwork(ctx context.Context) (object.NetworkReference, error) { 793 network, err := f.Network(ctx, "*") 794 if err != nil { 795 return nil, toDefaultError(err) 796 } 797 798 return network, nil 799} 800 801func (f *Finder) NetworkOrDefault(ctx context.Context, path string) (object.NetworkReference, error) { 802 if path != "" { 803 network, err := f.Network(ctx, path) 804 if err != nil { 805 return nil, err 806 } 807 return network, nil 808 } 809 810 return f.DefaultNetwork(ctx) 811} 812 813func (f *Finder) ResourcePoolList(ctx context.Context, path string) ([]*object.ResourcePool, error) { 814 s := &spec{ 815 Relative: f.hostFolder, 816 Parents: []string{"ComputeResource", "ClusterComputeResource", "VirtualApp"}, 817 Nested: []string{"ResourcePool"}, 818 Contents: true, 819 } 820 821 es, err := f.find(ctx, path, s) 822 if err != nil { 823 return nil, err 824 } 825 826 var rps []*object.ResourcePool 827 for _, e := range es { 828 var rp *object.ResourcePool 829 830 switch o := e.Object.(type) { 831 case mo.ResourcePool: 832 rp = object.NewResourcePool(f.client, o.Reference()) 833 rp.InventoryPath = e.Path 834 rps = append(rps, rp) 835 } 836 } 837 838 if len(rps) == 0 { 839 return nil, &NotFoundError{"resource pool", path} 840 } 841 842 return rps, nil 843} 844 845func (f *Finder) ResourcePool(ctx context.Context, path string) (*object.ResourcePool, error) { 846 rps, err := f.ResourcePoolList(ctx, path) 847 if err != nil { 848 return nil, err 849 } 850 851 if len(rps) > 1 { 852 return nil, &MultipleFoundError{"resource pool", path} 853 } 854 855 return rps[0], nil 856} 857 858func (f *Finder) DefaultResourcePool(ctx context.Context) (*object.ResourcePool, error) { 859 rp, err := f.ResourcePool(ctx, "*/Resources") 860 if err != nil { 861 return nil, toDefaultError(err) 862 } 863 864 return rp, nil 865} 866 867func (f *Finder) ResourcePoolOrDefault(ctx context.Context, path string) (*object.ResourcePool, error) { 868 if path != "" { 869 rp, err := f.ResourcePool(ctx, path) 870 if err != nil { 871 return nil, err 872 } 873 return rp, nil 874 } 875 876 return f.DefaultResourcePool(ctx) 877} 878 879// ResourcePoolListAll combines ResourcePoolList and VirtualAppList 880// VirtualAppList is only called if ResourcePoolList does not find any pools with the given path. 881func (f *Finder) ResourcePoolListAll(ctx context.Context, path string) ([]*object.ResourcePool, error) { 882 pools, err := f.ResourcePoolList(ctx, path) 883 if err != nil { 884 if _, ok := err.(*NotFoundError); !ok { 885 return nil, err 886 } 887 888 vapps, _ := f.VirtualAppList(ctx, path) 889 890 if len(vapps) == 0 { 891 return nil, err 892 } 893 894 for _, vapp := range vapps { 895 pools = append(pools, vapp.ResourcePool) 896 } 897 } 898 899 return pools, nil 900} 901 902func (f *Finder) DefaultFolder(ctx context.Context) (*object.Folder, error) { 903 ref, err := f.vmFolder(ctx) 904 if err != nil { 905 return nil, toDefaultError(err) 906 } 907 folder := object.NewFolder(f.client, ref.Reference()) 908 909 return folder, nil 910} 911 912func (f *Finder) FolderOrDefault(ctx context.Context, path string) (*object.Folder, error) { 913 if path != "" { 914 folder, err := f.Folder(ctx, path) 915 if err != nil { 916 return nil, err 917 } 918 return folder, nil 919 } 920 return f.DefaultFolder(ctx) 921} 922 923func (f *Finder) VirtualMachineList(ctx context.Context, path string) ([]*object.VirtualMachine, error) { 924 s := &spec{ 925 Relative: f.vmFolder, 926 Parents: []string{"VirtualApp"}, 927 } 928 929 es, err := f.find(ctx, path, s) 930 if err != nil { 931 return nil, err 932 } 933 934 var vms []*object.VirtualMachine 935 for _, e := range es { 936 switch o := e.Object.(type) { 937 case mo.VirtualMachine: 938 vm := object.NewVirtualMachine(f.client, o.Reference()) 939 vm.InventoryPath = e.Path 940 vms = append(vms, vm) 941 } 942 } 943 944 if len(vms) == 0 { 945 return nil, &NotFoundError{"vm", path} 946 } 947 948 return vms, nil 949} 950 951func (f *Finder) VirtualMachine(ctx context.Context, path string) (*object.VirtualMachine, error) { 952 vms, err := f.VirtualMachineList(ctx, path) 953 if err != nil { 954 return nil, err 955 } 956 957 if len(vms) > 1 { 958 return nil, &MultipleFoundError{"vm", path} 959 } 960 961 return vms[0], nil 962} 963 964func (f *Finder) VirtualAppList(ctx context.Context, path string) ([]*object.VirtualApp, error) { 965 s := &spec{ 966 Relative: f.vmFolder, 967 } 968 969 es, err := f.find(ctx, path, s) 970 if err != nil { 971 return nil, err 972 } 973 974 var apps []*object.VirtualApp 975 for _, e := range es { 976 switch o := e.Object.(type) { 977 case mo.VirtualApp: 978 app := object.NewVirtualApp(f.client, o.Reference()) 979 app.InventoryPath = e.Path 980 apps = append(apps, app) 981 } 982 } 983 984 if len(apps) == 0 { 985 return nil, &NotFoundError{"app", path} 986 } 987 988 return apps, nil 989} 990 991func (f *Finder) VirtualApp(ctx context.Context, path string) (*object.VirtualApp, error) { 992 apps, err := f.VirtualAppList(ctx, path) 993 if err != nil { 994 return nil, err 995 } 996 997 if len(apps) > 1 { 998 return nil, &MultipleFoundError{"app", path} 999 } 1000 1001 return apps[0], nil 1002} 1003 1004func (f *Finder) FolderList(ctx context.Context, path string) ([]*object.Folder, error) { 1005 es, err := f.ManagedObjectList(ctx, path) 1006 if err != nil { 1007 return nil, err 1008 } 1009 1010 var folders []*object.Folder 1011 1012 for _, e := range es { 1013 switch o := e.Object.(type) { 1014 case mo.Folder, mo.StoragePod: 1015 folder := object.NewFolder(f.client, o.Reference()) 1016 folder.InventoryPath = e.Path 1017 folders = append(folders, folder) 1018 case *object.Folder: 1019 // RootFolder 1020 folders = append(folders, o) 1021 } 1022 } 1023 1024 if len(folders) == 0 { 1025 return nil, &NotFoundError{"folder", path} 1026 } 1027 1028 return folders, nil 1029} 1030 1031func (f *Finder) Folder(ctx context.Context, path string) (*object.Folder, error) { 1032 folders, err := f.FolderList(ctx, path) 1033 if err != nil { 1034 return nil, err 1035 } 1036 1037 if len(folders) > 1 { 1038 return nil, &MultipleFoundError{"folder", path} 1039 } 1040 1041 return folders[0], nil 1042} 1043