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 }