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