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

     1  /*
     2  Copyright (c) 2014-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 object
    18  
    19  import (
    20  	"context"
    21  	"flag"
    22  	"fmt"
    23  	"net/url"
    24  	"os"
    25  	gopath "path"
    26  	"time"
    27  
    28  	gotree "github.com/a8m/tree"
    29  
    30  	"github.com/vmware/govmomi/govc/cli"
    31  	"github.com/vmware/govmomi/govc/flags"
    32  	"github.com/vmware/govmomi/view"
    33  	"github.com/vmware/govmomi/vim25"
    34  	"github.com/vmware/govmomi/vim25/types"
    35  )
    36  
    37  type tree struct {
    38  	*flags.DatacenterFlag
    39  
    40  	long  bool
    41  	kind  bool
    42  	color bool
    43  	level int
    44  }
    45  
    46  func init() {
    47  	cli.Register("tree", &tree{})
    48  }
    49  
    50  func (cmd *tree) Register(ctx context.Context, f *flag.FlagSet) {
    51  	cmd.DatacenterFlag, ctx = flags.NewDatacenterFlag(ctx)
    52  	cmd.DatacenterFlag.Register(ctx, f)
    53  
    54  	f.BoolVar(&cmd.color, "C", false, "Colorize output")
    55  	f.BoolVar(&cmd.long, "l", false, "Follow runtime references (e.g. HostSystem VMs)")
    56  	f.BoolVar(&cmd.kind, "p", false, "Print the object type")
    57  	f.IntVar(&cmd.level, "L", 0, "Max display depth of the inventory tree")
    58  }
    59  
    60  func (cmd *tree) Description() string {
    61  	return `List contents of the inventory in a tree-like format.
    62  
    63  Examples:
    64    govc tree -C /
    65    govc tree /datacenter/vm`
    66  }
    67  
    68  func (cmd *tree) Usage() string {
    69  	return "[PATH]"
    70  }
    71  
    72  func (cmd *tree) Run(ctx context.Context, f *flag.FlagSet) error {
    73  	c, err := cmd.Client()
    74  	if err != nil {
    75  		return err
    76  	}
    77  
    78  	path := f.Arg(0)
    79  	if path == "" {
    80  		path = "/"
    81  	}
    82  
    83  	vfs := &virtualFileSystem{
    84  		ctx:   ctx,
    85  		cmd:   cmd,
    86  		c:     c,
    87  		m:     view.NewManager(c),
    88  		names: make(map[types.ManagedObjectReference]string),
    89  		dvs:   make(map[types.ManagedObjectReference][]types.ManagedObjectReference),
    90  		path:  path,
    91  	}
    92  
    93  	treeOpts := &gotree.Options{
    94  		Fs:        vfs,
    95  		OutFile:   cmd.Out,
    96  		Colorize:  cmd.color,
    97  		Color:     color,
    98  		DeepLevel: cmd.level,
    99  	}
   100  
   101  	inf := gotree.New(path)
   102  	inf.Visit(treeOpts)
   103  	inf.Print(treeOpts)
   104  
   105  	return nil
   106  }
   107  
   108  type virtualFileSystem struct {
   109  	ctx   context.Context
   110  	cmd   *tree
   111  	c     *vim25.Client
   112  	m     *view.Manager
   113  	names map[types.ManagedObjectReference]string
   114  	dvs   map[types.ManagedObjectReference][]types.ManagedObjectReference
   115  	root  types.ManagedObjectReference
   116  	path  string
   117  }
   118  
   119  func style(kind string) string {
   120  	switch kind {
   121  	case "VirtualMachine":
   122  		return "1;32"
   123  	case "HostSystem":
   124  		return "1;33"
   125  	case "ResourcePool":
   126  		return "1;30"
   127  	case "Network", "OpaqueNetwork", "DistributedVirtualPortgroup":
   128  		return "1;35"
   129  	case "Datastore":
   130  		return "1;36"
   131  	case "Datacenter":
   132  		return "1;37"
   133  	default:
   134  		return ""
   135  	}
   136  }
   137  
   138  func color(node *gotree.Node, s string) string {
   139  	ref := pathReference(node.Path())
   140  
   141  	switch ref.Type {
   142  	case "ResourcePool":
   143  		return s
   144  	}
   145  
   146  	c := style(ref.Type)
   147  	if c == "" {
   148  		return gotree.ANSIColor(node, s)
   149  	}
   150  
   151  	return gotree.ANSIColorFormat(c, s)
   152  }
   153  
   154  func (vfs *virtualFileSystem) Stat(path string) (os.FileInfo, error) {
   155  	var ref types.ManagedObjectReference
   156  
   157  	if len(vfs.names) == 0 {
   158  		// This is the first Stat() call, where path is the initial user input
   159  		if path == "/" {
   160  			ref = vfs.c.ServiceContent.RootFolder
   161  		} else {
   162  			var err error
   163  			ref, err = vfs.cmd.ManagedObject(vfs.ctx, path)
   164  			if err != nil {
   165  				return nil, err
   166  			}
   167  		}
   168  		vfs.names[ref] = path
   169  		vfs.root = ref
   170  	} else {
   171  		// The Node.Path in subsequent calls to Stat() will have a MOR base
   172  		ref = pathReference(path)
   173  	}
   174  
   175  	name := vfs.names[ref]
   176  
   177  	var mode os.FileMode
   178  	switch ref.Type {
   179  	case "ComputeResource",
   180  		"ClusterComputeResource",
   181  		"Datacenter",
   182  		"Folder",
   183  		"ResourcePool",
   184  		"VirtualApp",
   185  		"StoragePod",
   186  		"DistributedVirtualSwitch",
   187  		"VmwareDistributedVirtualSwitch":
   188  		mode = os.ModeDir
   189  	case "HostSystem":
   190  		if vfs.cmd.long {
   191  			mode = os.ModeDir
   192  		}
   193  	}
   194  
   195  	if vfs.cmd.kind {
   196  		name = fmt.Sprintf("[%s] %s", ref.Type, name)
   197  	}
   198  
   199  	return fileInfo{name: name, mode: mode}, nil
   200  }
   201  
   202  // pathReference converts the base of the given Node.Path to a MOR
   203  func pathReference(s string) types.ManagedObjectReference {
   204  	var ref types.ManagedObjectReference
   205  	r, _ := url.PathUnescape(gopath.Base(s))
   206  	ref.FromString(r)
   207  	return ref
   208  }
   209  
   210  func (vfs *virtualFileSystem) ReadDir(path string) ([]string, error) {
   211  	var ref types.ManagedObjectReference
   212  
   213  	if path == vfs.path {
   214  		// This path is the initial user input (e.g. "/" or "/dc1")
   215  		ref = vfs.root
   216  	} else {
   217  		// This path will have had 1 or more MORs appended to it, as returned by this func
   218  		ref = pathReference(path)
   219  	}
   220  
   221  	var childPaths []string
   222  
   223  	switch ref.Type {
   224  	// In the vCenter inventory switches and portgroups are siblings, hack to display them as parent child in the tree
   225  	case "DistributedVirtualSwitch", "VmwareDistributedVirtualSwitch":
   226  		pgs := vfs.dvs[ref]
   227  		for _, pg := range pgs {
   228  			childPaths = append(childPaths, url.PathEscape(pg.String()))
   229  		}
   230  		return childPaths, nil
   231  	}
   232  
   233  	v, err := vfs.m.CreateContainerView(vfs.ctx, ref, nil, false)
   234  	if err != nil {
   235  		return nil, err
   236  	}
   237  	defer v.Destroy(vfs.ctx)
   238  
   239  	var kind []string
   240  	if !vfs.cmd.long {
   241  		switch ref.Type {
   242  		case "HostSystem":
   243  			return nil, nil
   244  		case "ResourcePool", "VirtualApp":
   245  			kind = []string{"ResourcePool", "VirtualApp"}
   246  		}
   247  	}
   248  
   249  	var children []types.ObjectContent
   250  
   251  	pspec := []types.PropertySpec{
   252  		{Type: "DistributedVirtualSwitch", PathSet: []string{"portgroup"}},
   253  		{Type: "VmwareDistributedVirtualSwitch", PathSet: []string{"portgroup"}},
   254  	}
   255  
   256  	err = v.Retrieve(vfs.ctx, kind, []string{"name"}, &children, pspec...)
   257  	if err != nil {
   258  		return nil, err
   259  	}
   260  
   261  	for _, content := range children {
   262  		ref = content.Obj
   263  		for _, p := range content.PropSet {
   264  			switch p.Name {
   265  			case "name":
   266  				vfs.names[ref] = p.Val.(string)
   267  			case "portgroup":
   268  				vfs.dvs[ref] = p.Val.(types.ArrayOfManagedObjectReference).ManagedObjectReference
   269  			}
   270  		}
   271  		if ref.Type == "DistributedVirtualPortgroup" {
   272  			continue // Returned on ReadDir() of the DVS above
   273  		}
   274  		childPaths = append(childPaths, url.PathEscape(ref.String()))
   275  	}
   276  
   277  	return childPaths, nil
   278  }
   279  
   280  type fileInfo struct {
   281  	name string
   282  	mode os.FileMode
   283  }
   284  
   285  func (f fileInfo) Name() string {
   286  	return f.name
   287  }
   288  func (f fileInfo) Size() int64 {
   289  	return 0
   290  }
   291  func (f fileInfo) Mode() os.FileMode {
   292  	return f.mode
   293  }
   294  func (f fileInfo) ModTime() time.Time {
   295  	return time.Now()
   296  }
   297  func (f fileInfo) IsDir() bool {
   298  	return f.mode&os.ModeDir == os.ModeDir
   299  }
   300  func (f fileInfo) Sys() interface{} {
   301  	return nil
   302  }