github.com/vmware/govmomi@v0.37.2/govc/device/model/tree.go (about)

     1  /*
     2  Copyright (c) 2023-2023 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 model
    18  
    19  import (
    20  	"context"
    21  	"flag"
    22  	"fmt"
    23  	"reflect"
    24  	"sort"
    25  	"unsafe"
    26  
    27  	"github.com/xlab/treeprint"
    28  
    29  	"github.com/vmware/govmomi/govc/cli"
    30  	"github.com/vmware/govmomi/vim25/types"
    31  )
    32  
    33  type tree struct {
    34  	backings bool
    35  }
    36  
    37  func init() {
    38  	cli.Register("device.model.tree", &tree{})
    39  }
    40  
    41  func (cmd *tree) Register(ctx context.Context, f *flag.FlagSet) {
    42  	f.BoolVar(&cmd.backings, "backings", false, "Print the devices backings")
    43  }
    44  
    45  func (cmd *tree) Description() string {
    46  	return `Print the device model as a tree.
    47  
    48  Examples:
    49    govc device.model.tree
    50    govc device.model.tree VirtualEthernetCard
    51    govc device.model.tree -backings
    52    govc device.model.tree -backings VirtualDiskRawDiskVer2BackingInfo`
    53  }
    54  
    55  func (cmd *tree) Process(ctx context.Context) error {
    56  	return nil
    57  }
    58  
    59  func (cmd *tree) Run(ctx context.Context, f *flag.FlagSet) error {
    60  
    61  	typeName := f.Arg(0)
    62  
    63  	var node treeprint.Tree
    64  	if cmd.backings {
    65  		node = getTree[
    66  			types.BaseVirtualDeviceBackingInfo,
    67  			types.VirtualDeviceBackingInfo,
    68  		]()
    69  	} else {
    70  		node = getTree[
    71  			types.BaseVirtualDevice,
    72  			types.VirtualDevice,
    73  		]()
    74  	}
    75  
    76  	if typeName != "" {
    77  		var found treeprint.Tree
    78  		node.VisitAll(func(n *treeprint.Node) {
    79  			if n.Value == typeName {
    80  				found = n
    81  			}
    82  		})
    83  		if found == nil {
    84  			return fmt.Errorf("%q not found", typeName)
    85  		}
    86  		node = found
    87  		node = node.Branch()
    88  	}
    89  
    90  	fmt.Print(node.String())
    91  
    92  	return nil
    93  }
    94  
    95  //go:linkname typelinks reflect.typelinks
    96  func typelinks() (sections []unsafe.Pointer, offset [][]int32)
    97  
    98  //go:linkname add reflect.add
    99  func add(p unsafe.Pointer, x uintptr, whySafe string) unsafe.Pointer
   100  
   101  // typeEmbeds returns true if a embeds any of the b's.
   102  func typeEmbeds(a reflect.Type, b ...reflect.Type) bool {
   103  	for i := range b {
   104  		if _, ok := a.FieldByName(b[i].Name()); ok {
   105  			return true
   106  		}
   107  	}
   108  	return false
   109  }
   110  
   111  // typeEmbeddedBy returns true if a is embedded by any of the b's.
   112  func typeEmbeddedBy(a reflect.Type, b ...reflect.Type) bool {
   113  	for i := range b {
   114  		if _, ok := b[i].FieldByName(a.Name()); ok {
   115  			return true
   116  		}
   117  	}
   118  	return false
   119  }
   120  
   121  func getTree[T, K any]() treeprint.Tree {
   122  	var (
   123  		rootObj       K
   124  		rootType      = reflect.TypeOf(rootObj)
   125  		allTypes      = []reflect.Type{}
   126  		embedsTypeMap = map[reflect.Type][]reflect.Type{}
   127  		rootIfaceType = reflect.TypeOf((*T)(nil)).Elem()
   128  	)
   129  
   130  	sections, offsets := typelinks()
   131  	for i := range sections {
   132  		base := sections[i]
   133  		for _, offset := range offsets[i] {
   134  			typeAddr := add(base, uintptr(offset), "")
   135  			typ3 := reflect.TypeOf(*(*any)(unsafe.Pointer(&typeAddr)))
   136  			if typ3.Implements(rootIfaceType) {
   137  				realType := reflect.Zero(typ3.Elem()).Type()
   138  				allTypes = append(allTypes, realType)
   139  				embedsTypeMap[realType] = []reflect.Type{}
   140  			}
   141  		}
   142  	}
   143  
   144  	// Create the child->parents map.
   145  	for i := range allTypes {
   146  		a := allTypes[i]
   147  		for b := range embedsTypeMap {
   148  			if typeEmbeds(a, b) {
   149  				embedsTypeMap[a] = append(embedsTypeMap[a], b)
   150  			}
   151  		}
   152  	}
   153  
   154  	// Each child should have a single parent.
   155  	for child, parents := range embedsTypeMap {
   156  		notAncestors := []reflect.Type{}
   157  		for i := range parents {
   158  			p := parents[i]
   159  			if !typeEmbeddedBy(p, parents...) {
   160  				notAncestors = append(notAncestors, p)
   161  			}
   162  		}
   163  		embedsTypeMap[child] = notAncestors
   164  	}
   165  
   166  	// Create the parent->children map.
   167  	typeMap := map[string][]string{}
   168  	for child, parents := range embedsTypeMap {
   169  		for i := range parents {
   170  			p := parents[i]
   171  			typeMap[p.Name()] = append(typeMap[p.Name()], child.Name())
   172  		}
   173  	}
   174  
   175  	// Sort the children for each parent by name.
   176  	for _, children := range typeMap {
   177  		sort.Strings(children)
   178  	}
   179  
   180  	var buildTree func(parent string, tree treeprint.Tree) treeprint.Tree
   181  	buildTree = func(parent string, tree treeprint.Tree) treeprint.Tree {
   182  		children := typeMap[parent]
   183  		for i := range children {
   184  			child := children[i]
   185  			if _, childIsParentToo := typeMap[child]; childIsParentToo {
   186  				buildTree(child, tree.AddBranch(child))
   187  			} else {
   188  				tree.AddNode(child)
   189  			}
   190  		}
   191  		return tree
   192  	}
   193  
   194  	return buildTree(rootType.Name(), treeprint.NewWithRoot(rootType.Name()))
   195  }