| /* |
| Copyright (c) 2015 VMware, Inc. All Rights Reserved. |
| |
| Licensed under the Apache License, Version 2.0 (the "License"); |
| you may not use this file except in compliance with the License. |
| You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| */ |
| |
| package mo |
| |
| import ( |
| "context" |
| "fmt" |
| |
| "github.com/vmware/govmomi/vim25/soap" |
| "github.com/vmware/govmomi/vim25/types" |
| ) |
| |
| // Ancestors returns the entire ancestry tree of a specified managed object. |
| // The return value includes the root node and the specified object itself. |
| func Ancestors(ctx context.Context, rt soap.RoundTripper, pc, obj types.ManagedObjectReference) ([]ManagedEntity, error) { |
| ospec := types.ObjectSpec{ |
| Obj: obj, |
| SelectSet: []types.BaseSelectionSpec{ |
| &types.TraversalSpec{ |
| SelectionSpec: types.SelectionSpec{Name: "traverseParent"}, |
| Type: "ManagedEntity", |
| Path: "parent", |
| Skip: types.NewBool(false), |
| SelectSet: []types.BaseSelectionSpec{ |
| &types.SelectionSpec{Name: "traverseParent"}, |
| }, |
| }, |
| &types.TraversalSpec{ |
| SelectionSpec: types.SelectionSpec{}, |
| Type: "VirtualMachine", |
| Path: "parentVApp", |
| Skip: types.NewBool(false), |
| SelectSet: []types.BaseSelectionSpec{ |
| &types.SelectionSpec{Name: "traverseParent"}, |
| }, |
| }, |
| }, |
| Skip: types.NewBool(false), |
| } |
| |
| pspec := []types.PropertySpec{ |
| { |
| Type: "ManagedEntity", |
| PathSet: []string{"name", "parent"}, |
| }, |
| { |
| Type: "VirtualMachine", |
| PathSet: []string{"parentVApp"}, |
| }, |
| } |
| |
| req := types.RetrieveProperties{ |
| This: pc, |
| SpecSet: []types.PropertyFilterSpec{ |
| { |
| ObjectSet: []types.ObjectSpec{ospec}, |
| PropSet: pspec, |
| }, |
| }, |
| } |
| |
| var ifaces []interface{} |
| err := RetrievePropertiesForRequest(ctx, rt, req, &ifaces) |
| if err != nil { |
| return nil, err |
| } |
| |
| var out []ManagedEntity |
| |
| // Build ancestry tree by iteratively finding a new child. |
| for len(out) < len(ifaces) { |
| var find types.ManagedObjectReference |
| |
| if len(out) > 0 { |
| find = out[len(out)-1].Self |
| } |
| |
| // Find entity we're looking for given the last entity in the current tree. |
| for _, iface := range ifaces { |
| me := iface.(IsManagedEntity).GetManagedEntity() |
| |
| if me.Name == "" { |
| // The types below have their own 'Name' field, so ManagedEntity.Name (me.Name) is empty. |
| // We only hit this case when the 'obj' param is one of these types. |
| // In most cases, 'obj' is a Folder so Name isn't collected in this call. |
| switch x := iface.(type) { |
| case Network: |
| me.Name = x.Name |
| case DistributedVirtualSwitch: |
| me.Name = x.Name |
| case DistributedVirtualPortgroup: |
| me.Name = x.Name |
| case OpaqueNetwork: |
| me.Name = x.Name |
| default: |
| // ManagedEntity always has a Name, if we hit this point we missed a case above. |
| panic(fmt.Sprintf("%#v Name is empty", me.Reference())) |
| } |
| } |
| |
| if me.Parent == nil { |
| // Special case for VirtualMachine within VirtualApp, |
| // unlikely to hit this other than via Finder.Element() |
| switch x := iface.(type) { |
| case VirtualMachine: |
| me.Parent = x.ParentVApp |
| } |
| } |
| |
| if me.Parent == nil { |
| out = append(out, me) |
| break |
| } |
| |
| if *me.Parent == find { |
| out = append(out, me) |
| break |
| } |
| } |
| } |
| |
| return out, nil |
| } |