Skip to content

Commit 13cb119

Browse files
authored
Merge pull request #3682 from dougm/vcsim-vslm
vcsim: add disk query and metadata support
2 parents a6f1508 + 116ba7e commit 13cb119

12 files changed

+1049
-49
lines changed

cli/disk/ls.go

+92-3
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
package disk
66

77
import (
8+
"bytes"
89
"context"
910
"flag"
1011
"fmt"
@@ -18,6 +19,7 @@ import (
1819
"github.com/vmware/govmomi/fault"
1920
"github.com/vmware/govmomi/units"
2021
"github.com/vmware/govmomi/vim25/types"
22+
vslm "github.com/vmware/govmomi/vslm/types"
2123
)
2224

2325
type ls struct {
@@ -29,6 +31,7 @@ type ls struct {
2931
category string
3032
tag string
3133
tags bool
34+
query flags.StringList
3235
}
3336

3437
func init() {
@@ -46,20 +49,37 @@ func (cmd *ls) Register(ctx context.Context, f *flag.FlagSet) {
4649
f.StringVar(&cmd.category, "c", "", "Query tag category")
4750
f.StringVar(&cmd.tag, "t", "", "Query tag name")
4851
f.BoolVar(&cmd.tags, "T", false, "List attached tags")
52+
f.Var(&cmd.query, "q", "Query spec")
4953
}
5054

5155
func (cmd *ls) Usage() string {
5256
return "[ID]..."
5357
}
5458

5559
func (cmd *ls) Description() string {
56-
return `List disk IDs on DS.
60+
var fields vslm.VslmVsoVStorageObjectQuerySpecQueryFieldEnum
5761

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
5873
Examples:
5974
govc disk.ls
6075
govc disk.ls -l -T
6176
govc disk.ls -l e9b06a8b-d047-4d3c-b15b-43ea9608b1a6
62-
govc disk.ls -c k8s-region -t us-west-2`
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())
6383
}
6484

6585
type VStorageObject struct {
@@ -80,6 +100,70 @@ type lsResult struct {
80100
Objects []VStorageObject `json:"objects"`
81101
}
82102

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+
83167
func (r *lsResult) Write(w io.Writer) error {
84168
tw := tabwriter.NewWriter(r.cmd.Out, 2, 0, 2, ' ', 0)
85169

@@ -124,11 +208,16 @@ func (cmd *ls) Run(ctx context.Context, f *flag.FlagSet) error {
124208

125209
filterNotFound := false
126210
ids := f.Args()
211+
q, err := cmd.querySpec()
212+
if err != nil {
213+
return err
214+
}
215+
127216
if len(ids) == 0 {
128217
filterNotFound = true
129218
var oids []types.ID
130219
if cmd.category == "" {
131-
oids, err = m.List(ctx)
220+
oids, err = m.List(ctx, q...)
132221
} else {
133222
oids, err = m.ListAttachedObjects(ctx, cmd.category, cmd.tag)
134223
}

cli/disk/manager.go

+5-29
Original file line numberDiff line numberDiff line change
@@ -139,41 +139,17 @@ func (m *Manager) Retrieve(ctx context.Context, id string) (*types.VStorageObjec
139139
return m.GlobalObjectManager.Retrieve(ctx, types.ID{Id: id})
140140
}
141141

142-
func (m *Manager) List(ctx context.Context) ([]types.ID, error) {
142+
func (m *Manager) List(ctx context.Context, qs ...vslmtypes.VslmVsoVStorageObjectQuerySpec) ([]types.ID, error) {
143143
if m.Datastore != nil {
144144
return m.ObjectManager.List(ctx, m.Datastore)
145145
}
146146

147-
// TODO: move this logic to vslm.GlobalObjectManager
148-
// Need to better understand the QuerySpec + implement in vcsim.
149-
// For now we just want the complete list of IDs (govc disk.ls)
150-
maxResults := int32(100)
151-
152-
spec := vslmtypes.VslmVsoVStorageObjectQuerySpec{
153-
QueryField: string(vslmtypes.VslmVsoVStorageObjectQuerySpecQueryFieldEnumId),
154-
QueryOperator: string(vslmtypes.VslmVsoVStorageObjectQuerySpecQueryOperatorEnumGreaterThan),
155-
}
156-
var query []vslmtypes.VslmVsoVStorageObjectQuerySpec
157-
var ids []types.ID
158-
159-
for {
160-
res, err := m.GlobalObjectManager.ListObjectsForSpec(ctx, query, maxResults)
161-
if err != nil {
162-
return nil, err
163-
}
164-
165-
ids = append(ids, res.Id...)
166-
167-
if res.AllRecordsReturned {
168-
break
169-
}
170-
171-
spec.QueryValue = []string{ids[len(ids)-1].Id}
172-
173-
query = []vslmtypes.VslmVsoVStorageObjectQuerySpec{spec}
147+
res, err := m.GlobalObjectManager.List(ctx, qs...)
148+
if err != nil {
149+
return nil, err
174150
}
175151

176-
return ids, nil
152+
return res.Id, nil
177153
}
178154

179155
func (m *Manager) RegisterDisk(ctx context.Context, path, name string) (*types.VStorageObject, error) {

cli/disk/metadata/ls.go

+115
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
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 metadata
6+
7+
import (
8+
"context"
9+
"flag"
10+
"fmt"
11+
"io"
12+
"os"
13+
"text/tabwriter"
14+
15+
"github.com/vmware/govmomi/cli"
16+
"github.com/vmware/govmomi/cli/flags"
17+
"github.com/vmware/govmomi/vim25/types"
18+
"github.com/vmware/govmomi/vslm"
19+
)
20+
21+
type ls struct {
22+
*flags.OutputFlag
23+
*flags.ClientFlag
24+
25+
key string
26+
prefix string
27+
snapshot string
28+
}
29+
30+
func init() {
31+
cli.Register("disk.metadata.ls", &ls{})
32+
}
33+
34+
func (cmd *ls) Register(ctx context.Context, f *flag.FlagSet) {
35+
cmd.OutputFlag, ctx = flags.NewOutputFlag(ctx)
36+
cmd.OutputFlag.Register(ctx, f)
37+
cmd.ClientFlag, ctx = flags.NewClientFlag(ctx)
38+
cmd.ClientFlag.Register(ctx, f)
39+
40+
f.StringVar(&cmd.key, "K", "", "Get value for key only")
41+
f.StringVar(&cmd.prefix, "p", "", "Key filter prefix")
42+
f.StringVar(&cmd.snapshot, "s", "", "Snapshot ID")
43+
}
44+
45+
func (cmd *ls) Process(ctx context.Context) error {
46+
if err := cmd.OutputFlag.Process(ctx); err != nil {
47+
return err
48+
}
49+
return cmd.ClientFlag.Process(ctx)
50+
}
51+
52+
func (cmd *ls) Usage() string {
53+
return "ID"
54+
}
55+
56+
func (cmd *ls) Description() string {
57+
return `List metadata for disk ID.
58+
59+
Examples:
60+
govc disk.metadata.ls 9b06a8b-d047-4d3c-b15b-43ea9608b1a6`
61+
}
62+
63+
type lsResult []types.KeyValue
64+
65+
func (r lsResult) Write(w io.Writer) error {
66+
tw := tabwriter.NewWriter(os.Stdout, 2, 0, 2, ' ', 0)
67+
for _, data := range r {
68+
fmt.Fprintf(tw, "%s\t%s\n", data.Key, data.Value)
69+
}
70+
return tw.Flush()
71+
}
72+
73+
func (r lsResult) Dump() interface{} {
74+
return []types.KeyValue(r)
75+
}
76+
77+
func (cmd *ls) Run(ctx context.Context, f *flag.FlagSet) error {
78+
if f.NArg() != 1 {
79+
return flag.ErrHelp
80+
}
81+
82+
c, err := cmd.Client()
83+
if err != nil {
84+
return err
85+
}
86+
87+
vc, err := vslm.NewClient(ctx, c)
88+
if err != nil {
89+
return err
90+
}
91+
92+
m := vslm.NewGlobalObjectManager(vc)
93+
94+
id := types.ID{Id: f.Arg(0)}
95+
var data []types.KeyValue
96+
var sid *types.ID
97+
if cmd.snapshot != "" {
98+
sid = &types.ID{Id: cmd.snapshot}
99+
}
100+
101+
if cmd.key != "" {
102+
val, err := m.RetrieveMetadataValue(ctx, id, sid, cmd.key)
103+
if err != nil {
104+
return err
105+
}
106+
data = []types.KeyValue{{Key: cmd.key, Value: val}}
107+
} else {
108+
data, err = m.RetrieveMetadata(ctx, id, sid, cmd.prefix)
109+
if err != nil {
110+
return err
111+
}
112+
}
113+
114+
return cmd.WriteResult(lsResult(data))
115+
}

cli/disk/metadata/update.go

+80
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
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 metadata
6+
7+
import (
8+
"context"
9+
"flag"
10+
"strings"
11+
"time"
12+
13+
"github.com/vmware/govmomi/cli"
14+
"github.com/vmware/govmomi/cli/flags"
15+
"github.com/vmware/govmomi/vim25/types"
16+
"github.com/vmware/govmomi/vslm"
17+
)
18+
19+
type update struct {
20+
*flags.ClientFlag
21+
22+
remove flags.StringList
23+
}
24+
25+
func init() {
26+
cli.Register("disk.metadata.update", &update{})
27+
}
28+
29+
func (cmd *update) Register(ctx context.Context, f *flag.FlagSet) {
30+
cmd.ClientFlag, ctx = flags.NewClientFlag(ctx)
31+
cmd.ClientFlag.Register(ctx, f)
32+
33+
f.Var(&cmd.remove, "d", "Delete keys")
34+
}
35+
36+
func (cmd *update) Usage() string {
37+
return "ID"
38+
}
39+
40+
func (cmd *update) Description() string {
41+
return `Update metadata for disk ID.
42+
43+
Examples:
44+
govc disk.metadata.update $id foo=bar biz=baz
45+
govc disk.metadata.update -d foo -d biz $id`
46+
}
47+
48+
func (cmd *update) Run(ctx context.Context, f *flag.FlagSet) error {
49+
c, err := cmd.Client()
50+
if err != nil {
51+
return err
52+
}
53+
54+
vc, err := vslm.NewClient(ctx, c)
55+
if err != nil {
56+
return err
57+
}
58+
59+
m := vslm.NewGlobalObjectManager(vc)
60+
61+
id := types.ID{Id: f.Arg(0)}
62+
63+
var update []types.KeyValue
64+
65+
for _, arg := range f.Args()[1:] {
66+
kv := strings.SplitN(arg, "=", 2)
67+
if len(kv) == 1 {
68+
kv = append(kv, "")
69+
}
70+
update = append(update, types.KeyValue{Key: kv[0], Value: kv[1]})
71+
}
72+
73+
task, err := m.UpdateMetadata(ctx, id, update, cmd.remove)
74+
if err != nil {
75+
return err
76+
}
77+
78+
_, err = task.Wait(ctx, time.Hour)
79+
return err
80+
}

0 commit comments

Comments
 (0)