github.com/vmware/govmomi@v0.51.0/cli/disk/ls.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 disk
     6  
     7  import (
     8  	"bytes"
     9  	"context"
    10  	"flag"
    11  	"fmt"
    12  	"io"
    13  	"strings"
    14  	"text/tabwriter"
    15  	"time"
    16  
    17  	"github.com/vmware/govmomi/cli"
    18  	"github.com/vmware/govmomi/cli/flags"
    19  	"github.com/vmware/govmomi/fault"
    20  	"github.com/vmware/govmomi/units"
    21  	"github.com/vmware/govmomi/vim25/types"
    22  	vslm "github.com/vmware/govmomi/vslm/types"
    23  )
    24  
    25  type ls struct {
    26  	*flags.DatastoreFlag
    27  	all      bool
    28  	long     bool
    29  	path     bool
    30  	r        bool
    31  	category string
    32  	tag      string
    33  	tags     bool
    34  	query    flags.StringList
    35  }
    36  
    37  func init() {
    38  	cli.Register("disk.ls", &ls{})
    39  }
    40  
    41  func (cmd *ls) Register(ctx context.Context, f *flag.FlagSet) {
    42  	cmd.DatastoreFlag, ctx = flags.NewDatastoreFlag(ctx)
    43  	cmd.DatastoreFlag.Register(ctx, f)
    44  
    45  	f.BoolVar(&cmd.all, "a", false, "List IDs with missing file backing")
    46  	f.BoolVar(&cmd.long, "l", false, "Long listing format")
    47  	f.BoolVar(&cmd.path, "L", false, "Print disk backing path instead of disk name")
    48  	f.BoolVar(&cmd.r, "R", false, "Reconcile the datastore inventory info")
    49  	f.StringVar(&cmd.category, "c", "", "Query tag category")
    50  	f.StringVar(&cmd.tag, "t", "", "Query tag name")
    51  	f.BoolVar(&cmd.tags, "T", false, "List attached tags")
    52  	f.Var(&cmd.query, "q", "Query spec")
    53  }
    54  
    55  func (cmd *ls) Usage() string {
    56  	return "[ID]..."
    57  }
    58  
    59  func (cmd *ls) Description() string {
    60  	var fields vslm.VslmVsoVStorageObjectQuerySpecQueryFieldEnum
    61  
    62  	return fmt.Sprintf(`List disk IDs on DS.
    63  
    64  The '-q' flag can be used to match disk fields.
    65  Each query must be in the form of:
    66    FIELD.OP=VAL
    67  
    68  Where FIELD can be one of:
    69    %s
    70  
    71  And OP can be one of:
    72  %s
    73  Examples:
    74    govc disk.ls
    75    govc disk.ls -l -T
    76    govc disk.ls -l e9b06a8b-d047-4d3c-b15b-43ea9608b1a6
    77    govc disk.ls -c k8s-region -t us-west-2
    78    govc disk.ls -q capacity.ge=100 # capacity in MB
    79    govc disk.ls -q name.sw=my-disk
    80    govc disk.ls -q metadataKey.eq=cns.k8s.pvc.namespace -q metadataValue.eq=dev`,
    81  		strings.Join(fields.Strings(), "\n  "),
    82  		aliasHelp())
    83  }
    84  
    85  type VStorageObject struct {
    86  	types.VStorageObject
    87  	Tags []types.VslmTagEntry `json:"tags"`
    88  }
    89  
    90  func (o *VStorageObject) tags() string {
    91  	var tags []string
    92  	for _, tag := range o.Tags {
    93  		tags = append(tags, tag.ParentCategoryName+":"+tag.TagName)
    94  	}
    95  	return strings.Join(tags, ",")
    96  }
    97  
    98  type lsResult struct {
    99  	cmd     *ls
   100  	Objects []VStorageObject `json:"objects"`
   101  }
   102  
   103  var alias = []struct {
   104  	name string
   105  	kind vslm.VslmVsoVStorageObjectQuerySpecQueryOperatorEnum
   106  }{
   107  	{"eq", vslm.VslmVsoVStorageObjectQuerySpecQueryOperatorEnumEquals},
   108  	{"ne", vslm.VslmVsoVStorageObjectQuerySpecQueryOperatorEnumNotEquals},
   109  	{"lt", vslm.VslmVsoVStorageObjectQuerySpecQueryOperatorEnumLessThan},
   110  	{"le", vslm.VslmVsoVStorageObjectQuerySpecQueryOperatorEnumLessThanOrEqual},
   111  	{"gt", vslm.VslmVsoVStorageObjectQuerySpecQueryOperatorEnumGreaterThan},
   112  	{"ge", vslm.VslmVsoVStorageObjectQuerySpecQueryOperatorEnumGreaterThanOrEqual},
   113  	{"ct", vslm.VslmVsoVStorageObjectQuerySpecQueryOperatorEnumContains},
   114  	{"sw", vslm.VslmVsoVStorageObjectQuerySpecQueryOperatorEnumStartsWith},
   115  	{"ew", vslm.VslmVsoVStorageObjectQuerySpecQueryOperatorEnumEndsWith},
   116  }
   117  
   118  func opAlias(value string) string {
   119  	if len(value) != 2 {
   120  		return value
   121  	}
   122  
   123  	for _, a := range alias {
   124  		if a.name == value {
   125  			return string(a.kind)
   126  		}
   127  	}
   128  
   129  	return value
   130  }
   131  
   132  func aliasHelp() string {
   133  	var help bytes.Buffer
   134  
   135  	for _, a := range alias {
   136  		fmt.Fprintf(&help, "  %s    %s\n", a.name, a.kind)
   137  	}
   138  
   139  	return help.String()
   140  }
   141  
   142  func (cmd *ls) querySpec() ([]vslm.VslmVsoVStorageObjectQuerySpec, error) {
   143  	q := make([]vslm.VslmVsoVStorageObjectQuerySpec, len(cmd.query))
   144  
   145  	for i, s := range cmd.query {
   146  		val := strings.SplitN(s, "=", 2)
   147  		if len(val) != 2 {
   148  			return nil, fmt.Errorf("invalid query: %s", s)
   149  		}
   150  
   151  		op := string(vslm.VslmVsoVStorageObjectQuerySpecQueryOperatorEnumEquals)
   152  		field := strings.SplitN(val[0], ".", 2)
   153  		if len(field) == 2 {
   154  			op = field[1]
   155  		}
   156  
   157  		q[i] = vslm.VslmVsoVStorageObjectQuerySpec{
   158  			QueryField:    field[0],
   159  			QueryOperator: opAlias(op),
   160  			QueryValue:    []string{val[1]},
   161  		}
   162  	}
   163  
   164  	return q, nil
   165  }
   166  
   167  func (r *lsResult) Write(w io.Writer) error {
   168  	tw := tabwriter.NewWriter(r.cmd.Out, 2, 0, 2, ' ', 0)
   169  
   170  	for _, o := range r.Objects {
   171  		name := o.Config.Name
   172  		if r.cmd.path {
   173  			if file, ok := o.Config.Backing.(*types.BaseConfigInfoDiskFileBackingInfo); ok {
   174  				name = file.FilePath
   175  			}
   176  		}
   177  		_, _ = fmt.Fprintf(tw, "%s\t%s", o.Config.Id.Id, name)
   178  		if r.cmd.long {
   179  			created := o.Config.CreateTime.Format(time.Stamp)
   180  			size := units.FileSize(o.Config.CapacityInMB * 1024 * 1024)
   181  			_, _ = fmt.Fprintf(tw, "\t%s\t%s", size, created)
   182  		}
   183  		if r.cmd.tags {
   184  			_, _ = fmt.Fprintf(tw, "\t%s", o.tags())
   185  		}
   186  		_, _ = fmt.Fprintln(tw)
   187  	}
   188  
   189  	return tw.Flush()
   190  }
   191  
   192  func (r *lsResult) Dump() any {
   193  	return r.Objects
   194  }
   195  
   196  func (cmd *ls) Run(ctx context.Context, f *flag.FlagSet) error {
   197  	m, err := NewManagerFromFlag(ctx, cmd.DatastoreFlag)
   198  	if err != nil {
   199  		return err
   200  	}
   201  
   202  	if cmd.r {
   203  		if err = m.ReconcileDatastoreInventory(ctx); err != nil {
   204  			return err
   205  		}
   206  	}
   207  	res := lsResult{cmd: cmd}
   208  
   209  	filterNotFound := false
   210  	ids := f.Args()
   211  	q, err := cmd.querySpec()
   212  	if err != nil {
   213  		return err
   214  	}
   215  
   216  	if len(ids) == 0 {
   217  		filterNotFound = true
   218  		var oids []types.ID
   219  		if cmd.category == "" {
   220  			oids, err = m.List(ctx, q...)
   221  		} else {
   222  			oids, err = m.ListAttachedObjects(ctx, cmd.category, cmd.tag)
   223  		}
   224  
   225  		if err != nil {
   226  			return err
   227  		}
   228  		for _, id := range oids {
   229  			ids = append(ids, id.Id)
   230  		}
   231  	}
   232  
   233  	for _, id := range ids {
   234  		o, err := m.Retrieve(ctx, id)
   235  		if err != nil {
   236  			if filterNotFound && fault.Is(err, &types.NotFound{}) {
   237  				// The case when an FCD is deleted by something other than DeleteVStorageObject_Task, such as VM destroy
   238  				if cmd.all {
   239  					obj := VStorageObject{VStorageObject: types.VStorageObject{
   240  						Config: types.VStorageObjectConfigInfo{
   241  							BaseConfigInfo: types.BaseConfigInfo{
   242  								Id:   types.ID{Id: id},
   243  								Name: "not found: use 'disk.ls -R' to reconcile datastore inventory",
   244  							},
   245  						},
   246  					}}
   247  					res.Objects = append(res.Objects, obj)
   248  				}
   249  				continue
   250  			}
   251  			return fmt.Errorf("retrieve %q: %s", id, err)
   252  		}
   253  
   254  		obj := VStorageObject{VStorageObject: *o}
   255  		if cmd.tags {
   256  			obj.Tags, err = m.ListAttachedTags(ctx, id)
   257  			if err != nil {
   258  				return err
   259  			}
   260  		}
   261  		res.Objects = append(res.Objects, obj)
   262  	}
   263  
   264  	return cmd.WriteResult(&res)
   265  }