github.com/vmware/govmomi@v0.37.2/vim25/mo/ancestors.go (about)

     1  /*
     2  Copyright (c) 2015 VMware, Inc. All Rights Reserved.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package mo
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  
    23  	"github.com/vmware/govmomi/vim25/soap"
    24  	"github.com/vmware/govmomi/vim25/types"
    25  )
    26  
    27  // Ancestors returns the entire ancestry tree of a specified managed object.
    28  // The return value includes the root node and the specified object itself.
    29  func Ancestors(ctx context.Context, rt soap.RoundTripper, pc, obj types.ManagedObjectReference) ([]ManagedEntity, error) {
    30  	ospec := types.ObjectSpec{
    31  		Obj: obj,
    32  		SelectSet: []types.BaseSelectionSpec{
    33  			&types.TraversalSpec{
    34  				SelectionSpec: types.SelectionSpec{Name: "traverseParent"},
    35  				Type:          "ManagedEntity",
    36  				Path:          "parent",
    37  				Skip:          types.NewBool(false),
    38  				SelectSet: []types.BaseSelectionSpec{
    39  					&types.SelectionSpec{Name: "traverseParent"},
    40  				},
    41  			},
    42  			&types.TraversalSpec{
    43  				SelectionSpec: types.SelectionSpec{},
    44  				Type:          "VirtualMachine",
    45  				Path:          "parentVApp",
    46  				Skip:          types.NewBool(false),
    47  				SelectSet: []types.BaseSelectionSpec{
    48  					&types.SelectionSpec{Name: "traverseParent"},
    49  				},
    50  			},
    51  		},
    52  		Skip: types.NewBool(false),
    53  	}
    54  
    55  	pspec := []types.PropertySpec{
    56  		{
    57  			Type:    "ManagedEntity",
    58  			PathSet: []string{"name", "parent"},
    59  		},
    60  		{
    61  			Type:    "VirtualMachine",
    62  			PathSet: []string{"parentVApp"},
    63  		},
    64  	}
    65  
    66  	req := types.RetrieveProperties{
    67  		This: pc,
    68  		SpecSet: []types.PropertyFilterSpec{
    69  			{
    70  				ObjectSet: []types.ObjectSpec{ospec},
    71  				PropSet:   pspec,
    72  			},
    73  		},
    74  	}
    75  
    76  	var ifaces []interface{}
    77  	err := RetrievePropertiesForRequest(ctx, rt, req, &ifaces)
    78  	if err != nil {
    79  		return nil, err
    80  	}
    81  
    82  	var out []ManagedEntity
    83  
    84  	// Build ancestry tree by iteratively finding a new child.
    85  	for len(out) < len(ifaces) {
    86  		var find types.ManagedObjectReference
    87  
    88  		if len(out) > 0 {
    89  			find = out[len(out)-1].Self
    90  		}
    91  
    92  		// Find entity we're looking for given the last entity in the current tree.
    93  		for _, iface := range ifaces {
    94  			me := iface.(IsManagedEntity).GetManagedEntity()
    95  
    96  			if me.Name == "" {
    97  				// The types below have their own 'Name' field, so ManagedEntity.Name (me.Name) is empty.
    98  				// We only hit this case when the 'obj' param is one of these types.
    99  				// In most cases, 'obj' is a Folder so Name isn't collected in this call.
   100  				switch x := iface.(type) {
   101  				case Network:
   102  					me.Name = x.Name
   103  				case DistributedVirtualSwitch:
   104  					me.Name = x.Name
   105  				case DistributedVirtualPortgroup:
   106  					me.Name = x.Name
   107  				case OpaqueNetwork:
   108  					me.Name = x.Name
   109  				default:
   110  					// ManagedEntity always has a Name, if we hit this point we missed a case above.
   111  					panic(fmt.Sprintf("%#v Name is empty", me.Reference()))
   112  				}
   113  			}
   114  
   115  			if me.Parent == nil {
   116  				// Special case for VirtualMachine within VirtualApp,
   117  				// unlikely to hit this other than via Finder.Element()
   118  				switch x := iface.(type) {
   119  				case VirtualMachine:
   120  					me.Parent = x.ParentVApp
   121  				}
   122  			}
   123  
   124  			if me.Parent == nil {
   125  				out = append(out, me)
   126  				break
   127  			}
   128  
   129  			if *me.Parent == find {
   130  				out = append(out, me)
   131  				break
   132  			}
   133  		}
   134  	}
   135  
   136  	return out, nil
   137  }