github.com/vmware/govmomi@v0.51.0/cli/volume/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 volume
     6  
     7  import (
     8  	"context"
     9  	"flag"
    10  	"fmt"
    11  	"io"
    12  	"log"
    13  	"strings"
    14  	"text/tabwriter"
    15  
    16  	"github.com/vmware/govmomi/cli"
    17  	"github.com/vmware/govmomi/cli/flags"
    18  	"github.com/vmware/govmomi/cns"
    19  	"github.com/vmware/govmomi/cns/types"
    20  	"github.com/vmware/govmomi/units"
    21  	vim "github.com/vmware/govmomi/vim25/types"
    22  )
    23  
    24  type ls struct {
    25  	*flags.ClientFlag
    26  	*flags.DatastoreFlag
    27  	*flags.StorageProfileFlag
    28  	*flags.OutputFlag
    29  
    30  	types.CnsQueryFilter
    31  
    32  	long bool
    33  	id   bool
    34  	disk bool
    35  	back bool
    36  }
    37  
    38  type keyValue []vim.KeyValue
    39  
    40  func (e *keyValue) String() string {
    41  	return fmt.Sprintf("%v", *e)
    42  }
    43  
    44  func (e *keyValue) Set(v string) error {
    45  	r := strings.SplitN(v, "=", 2)
    46  	if len(r) < 2 {
    47  		return fmt.Errorf("failed to parse: %s", v)
    48  	}
    49  	*e = append(*e, vim.KeyValue{Key: r[0], Value: r[1]})
    50  	return nil
    51  }
    52  
    53  func init() {
    54  	cli.Register("volume.ls", &ls{})
    55  }
    56  
    57  func (cmd *ls) Register(ctx context.Context, f *flag.FlagSet) {
    58  	cmd.ClientFlag, ctx = flags.NewClientFlag(ctx)
    59  	cmd.ClientFlag.Register(ctx, f)
    60  
    61  	cmd.DatastoreFlag, ctx = flags.NewDatastoreFlag(ctx)
    62  	cmd.DatastoreFlag.Register(ctx, f)
    63  
    64  	cmd.StorageProfileFlag, ctx = flags.NewStorageProfileFlag(ctx)
    65  	cmd.StorageProfileFlag.Register(ctx, f)
    66  
    67  	cmd.OutputFlag, ctx = flags.NewOutputFlag(ctx)
    68  	cmd.OutputFlag.Register(ctx, f)
    69  
    70  	f.BoolVar(&cmd.long, "l", false, "Long listing format")
    71  	f.BoolVar(&cmd.id, "i", false, "List volume ID only")
    72  	f.BoolVar(&cmd.disk, "L", false, "List volume disk or file backing ID only")
    73  	f.BoolVar(&cmd.back, "b", false, "List file backing path")
    74  
    75  	f.Var((*flags.StringList)(&cmd.Names), "n", "List volumes with names")
    76  	f.Var((*keyValue)(&cmd.Labels), "label", "List volumes with labels")
    77  	f.StringVar(&cmd.HealthStatus, "H", "", "List volumes with health status")
    78  	f.Var((*flags.StringList)(&cmd.ContainerClusterIds), "c", "List volumes in clusters")
    79  }
    80  
    81  func (cmd *ls) Process(ctx context.Context) error {
    82  	if err := cmd.ClientFlag.Process(ctx); err != nil {
    83  		return err
    84  	}
    85  	if err := cmd.DatastoreFlag.Process(ctx); err != nil {
    86  		return err
    87  	}
    88  	if err := cmd.StorageProfileFlag.Process(ctx); err != nil {
    89  		return err
    90  	}
    91  	return cmd.OutputFlag.Process(ctx)
    92  }
    93  
    94  func (cmd *ls) Usage() string {
    95  	return "[ID...]"
    96  }
    97  
    98  func (cmd *ls) Description() string {
    99  	return `List CNS volumes.
   100  
   101  Examples:
   102    govc volume.ls
   103    govc volume.ls -l
   104    govc volume.ls -ds vsanDatastore
   105    govc volume.ls df86393b-5ae0-4fca-87d0-b692dbc67d45
   106    govc volume.ls -json $id | jq -r .volume[].backingObjectDetails.backingDiskPath
   107    govc volume.ls -b $id # verify backingDiskPath exists
   108    govc disk.ls -l $(govc volume.ls -L pvc-9744a4ff-07f4-43c4-b8ed-48ea7a528734)`
   109  }
   110  
   111  type lsWriter struct {
   112  	Volume []types.CnsVolume                    `json:"volume"`
   113  	Info   []types.BaseCnsVolumeOperationResult `json:"info,omitempty"`
   114  	cmd    *ls
   115  }
   116  
   117  func (r *lsWriter) Dump() any {
   118  	if len(r.Info) != 0 {
   119  		return r.Info
   120  	}
   121  	return r.Volume
   122  }
   123  
   124  func (r *lsWriter) Write(w io.Writer) error {
   125  	if r.cmd.id {
   126  		for _, volume := range r.Volume {
   127  			fmt.Fprintln(r.cmd.Out, volume.VolumeId.Id)
   128  		}
   129  		return nil
   130  	}
   131  
   132  	if r.cmd.disk {
   133  		for _, volume := range r.Volume {
   134  			var id string
   135  			switch backing := volume.BackingObjectDetails.(type) {
   136  			case *types.CnsBlockBackingDetails:
   137  				id = backing.BackingDiskId
   138  			case *types.CnsFileBackingDetails:
   139  				id = backing.BackingFileId
   140  			case *types.CnsVsanFileShareBackingDetails:
   141  				id = backing.Name
   142  			case *types.CnsBackingObjectDetails:
   143  				id = volume.VolumeId.Id
   144  			default:
   145  				log.Printf("%s unknown backing type: %T", volume.VolumeId.Id, backing)
   146  			}
   147  			fmt.Fprintln(r.cmd.Out, id)
   148  
   149  		}
   150  		return nil
   151  	}
   152  
   153  	tw := tabwriter.NewWriter(r.cmd.Out, 2, 0, 2, ' ', 0)
   154  
   155  	for _, volume := range r.Volume {
   156  		fmt.Fprintf(tw, "%s\t%s", volume.VolumeId.Id, volume.Name)
   157  		if r.cmd.back {
   158  			fmt.Fprintf(tw, "\t%s", r.backing(volume.VolumeId))
   159  		}
   160  		if r.cmd.long {
   161  			capacity := volume.BackingObjectDetails.GetCnsBackingObjectDetails().CapacityInMb
   162  			c := volume.Metadata.ContainerCluster
   163  			fmt.Fprintf(tw, "\t%s\t%s\t%s", units.ByteSize(capacity*1024*1024), c.ClusterType, c.ClusterId)
   164  		}
   165  		fmt.Fprintln(tw)
   166  	}
   167  
   168  	return tw.Flush()
   169  }
   170  
   171  func (r *lsWriter) backing(id types.CnsVolumeId) string {
   172  	for _, info := range r.Info {
   173  		res, ok := info.(*types.CnsQueryVolumeInfoResult)
   174  		if !ok {
   175  			continue
   176  		}
   177  
   178  		switch vol := res.VolumeInfo.(type) {
   179  		case *types.CnsBlockVolumeInfo:
   180  			if vol.VStorageObject.Config.Id.Id == id.Id {
   181  				switch backing := vol.VStorageObject.Config.BaseConfigInfo.Backing.(type) {
   182  				case *vim.BaseConfigInfoDiskFileBackingInfo:
   183  					return backing.FilePath
   184  				}
   185  			}
   186  		}
   187  
   188  		if fault := res.Fault; fault != nil {
   189  			if f, ok := fault.Fault.(types.CnsFault); ok {
   190  				if strings.Contains(f.Reason, id.Id) {
   191  					return f.Reason
   192  				}
   193  			}
   194  		}
   195  	}
   196  	return "???"
   197  }
   198  
   199  func (cmd *ls) Run(ctx context.Context, f *flag.FlagSet) error {
   200  	ds, err := cmd.DatastoreIfSpecified()
   201  	if err != nil {
   202  		return err
   203  	}
   204  
   205  	if ds != nil {
   206  		cmd.Datastores = []vim.ManagedObjectReference{ds.Reference()}
   207  	}
   208  
   209  	cmd.StoragePolicyId, err = cmd.StorageProfile(ctx)
   210  	if err != nil {
   211  		return err
   212  	}
   213  
   214  	c, err := cmd.CnsClient()
   215  	if err != nil {
   216  		return err
   217  	}
   218  
   219  	for _, arg := range f.Args() {
   220  		cmd.VolumeIds = append(cmd.VolumeIds, types.CnsVolumeId{Id: arg})
   221  	}
   222  
   223  	var volumes []types.CnsVolume
   224  	var info []types.BaseCnsVolumeOperationResult
   225  
   226  	for {
   227  		res, err := c.QueryVolume(ctx, cmd.CnsQueryFilter)
   228  		if err != nil {
   229  			return err
   230  		}
   231  
   232  		volumes = append(volumes, res.Volumes...)
   233  
   234  		if res.Cursor.Offset == res.Cursor.TotalRecords || len(res.Volumes) == 0 {
   235  			break
   236  		}
   237  
   238  		cmd.Cursor = &res.Cursor
   239  	}
   240  
   241  	if cmd.back {
   242  		ids := make([]types.CnsVolumeId, len(volumes))
   243  		for i := range volumes {
   244  			ids[i] = volumes[i].VolumeId
   245  		}
   246  
   247  		task, err := c.QueryVolumeInfo(ctx, ids)
   248  		if err != nil {
   249  			return err
   250  		}
   251  
   252  		res, err := cns.GetTaskInfo(ctx, task)
   253  		if err != nil {
   254  			return err
   255  		}
   256  
   257  		info, err = cns.GetTaskResultArray(ctx, res)
   258  		if err != nil {
   259  			return err
   260  		}
   261  	}
   262  
   263  	return cmd.WriteResult(&lsWriter{volumes, info, cmd})
   264  }