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