blob: f95177e946cafc6ff61a9055c3f88276e68b7d9f [file] [log] [blame]
Scott Baker2c0ebda2019-05-06 16:55:47 -07001/*
2 * Portions copyright 2019-present Open Networking Foundation
3 * Original copyright 2019-present Ciena Corporation
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17package commands
18
19import (
Scott Bakerc328cf12019-05-28 16:03:12 -070020 "context"
Scott Baker2c0ebda2019-05-06 16:55:47 -070021 "fmt"
Scott Baker1dd06672019-06-14 15:40:56 -070022 "github.com/fullstorydev/grpcurl"
Scott Baker2c0ebda2019-05-06 16:55:47 -070023 flags "github.com/jessevdk/go-flags"
24 "github.com/jhump/protoreflect/dynamic"
Scott Baker1dd06672019-06-14 15:40:56 -070025 "google.golang.org/grpc"
Scott Bakerea815fc2019-06-05 13:25:44 -070026 "sort"
Scott Baker2c0ebda2019-05-06 16:55:47 -070027 "strings"
Scott Bakerc328cf12019-05-28 16:03:12 -070028 "time"
Scott Baker2c0ebda2019-05-06 16:55:47 -070029)
30
Scott Baker175cb402019-05-17 16:13:06 -070031const (
Scott Baker8b8a2b52019-05-21 08:56:28 -070032 DEFAULT_CREATE_FORMAT = "table{{ .Id }}\t{{ .Message }}"
Scott Baker175cb402019-05-17 16:13:06 -070033 DEFAULT_DELETE_FORMAT = "table{{ .Id }}\t{{ .Message }}"
34 DEFAULT_UPDATE_FORMAT = "table{{ .Id }}\t{{ .Message }}"
Scott Baker8b8a2b52019-05-21 08:56:28 -070035 DEFAULT_SYNC_FORMAT = "table{{ .Id }}\t{{ .Message }}"
Scott Baker175cb402019-05-17 16:13:06 -070036)
37
Scott Baker63ce82e2019-05-15 09:01:42 -070038type ModelNameString string
39
Scott Baker2c0ebda2019-05-06 16:55:47 -070040type ModelList struct {
Scott Bakera00418a2019-06-03 16:15:28 -070041 ListOutputOptions
Scott Baker5201c0b2019-05-15 15:35:56 -070042 ShowHidden bool `long:"showhidden" description:"Show hidden fields in default output"`
43 ShowFeedback bool `long:"showfeedback" description:"Show feedback fields in default output"`
44 ShowBookkeeping bool `long:"showbookkeeping" description:"Show bookkeeping fields in default output"`
Scott Baker175cb402019-05-17 16:13:06 -070045 Filter string `short:"f" long:"filter" description:"Comma-separated list of filters"`
Scott Baker1dd06672019-06-14 15:40:56 -070046 State string `short:"s" long:"state" description:"Filter model state [DEFAULT | ALL | DIRTY | DELETED | DIRTYPOL | DELETEDPOL]"`
Scott Baker6cf525a2019-05-09 12:25:08 -070047 Args struct {
Scott Baker63ce82e2019-05-15 09:01:42 -070048 ModelName ModelNameString
Scott Baker2c0ebda2019-05-06 16:55:47 -070049 } `positional-args:"yes" required:"yes"`
50}
51
Scott Baker5281d002019-05-16 10:45:26 -070052type ModelUpdate struct {
Scott Baker6cf525a2019-05-09 12:25:08 -070053 OutputOptions
Scott Bakerc328cf12019-05-28 16:03:12 -070054 Unbuffered bool `short:"u" long:"unbuffered" description:"Do not buffer console output and suppress default output processor"`
55 Filter string `short:"f" long:"filter" description:"Comma-separated list of filters"`
56 SetFields string `long:"set-field" description:"Comma-separated list of field=value to set"`
57 SetJSON string `long:"set-json" description:"JSON dictionary to use for settings fields"`
58 Sync bool `long:"sync" description:"Synchronize before returning"`
59 SyncTimeout time.Duration `long:"synctimeout" default:"600s" description:"Timeout for --sync option"`
60 Args struct {
Scott Baker5281d002019-05-16 10:45:26 -070061 ModelName ModelNameString
62 } `positional-args:"yes" required:"yes"`
63 IDArgs struct {
64 ID []int32
65 } `positional-args:"yes" required:"no"`
Scott Baker6cf525a2019-05-09 12:25:08 -070066}
67
Scott Baker175cb402019-05-17 16:13:06 -070068type ModelDelete struct {
69 OutputOptions
Scott Baker8b8a2b52019-05-21 08:56:28 -070070 Unbuffered bool `short:"u" long:"unbuffered" description:"Do not buffer console output and suppress default output processor"`
71 Filter string `short:"f" long:"filter" description:"Comma-separated list of filters"`
Scott Baker1dd06672019-06-14 15:40:56 -070072 All bool `short:"a" long:"all" description:"Operate on all models"`
Scott Baker8b8a2b52019-05-21 08:56:28 -070073 Args struct {
74 ModelName ModelNameString
75 } `positional-args:"yes" required:"yes"`
76 IDArgs struct {
77 ID []int32
78 } `positional-args:"yes" required:"no"`
79}
80
81type ModelCreate struct {
82 OutputOptions
Scott Bakerc328cf12019-05-28 16:03:12 -070083 Unbuffered bool `short:"u" long:"unbuffered" description:"Do not buffer console output"`
84 SetFields string `long:"set-field" description:"Comma-separated list of field=value to set"`
85 SetJSON string `long:"set-json" description:"JSON dictionary to use for settings fields"`
86 Sync bool `long:"sync" description:"Synchronize before returning"`
87 SyncTimeout time.Duration `long:"synctimeout" default:"600s" description:"Timeout for --sync option"`
88 Args struct {
Scott Baker8b8a2b52019-05-21 08:56:28 -070089 ModelName ModelNameString
90 } `positional-args:"yes" required:"yes"`
91}
92
93type ModelSync struct {
94 OutputOptions
Scott Bakerc328cf12019-05-28 16:03:12 -070095 Unbuffered bool `short:"u" long:"unbuffered" description:"Do not buffer console output and suppress default output processor"`
96 Filter string `short:"f" long:"filter" description:"Comma-separated list of filters"`
97 SyncTimeout time.Duration `long:"synctimeout" default:"600s" description:"Timeout for synchronization"`
Scott Baker1dd06672019-06-14 15:40:56 -070098 All bool `short:"a" long:"all" description:"Operate on all models"`
Scott Bakerc328cf12019-05-28 16:03:12 -070099 Args struct {
Scott Baker175cb402019-05-17 16:13:06 -0700100 ModelName ModelNameString
101 } `positional-args:"yes" required:"yes"`
102 IDArgs struct {
103 ID []int32
104 } `positional-args:"yes" required:"no"`
105}
106
Scott Baker1dd06672019-06-14 15:40:56 -0700107type ModelSetDirty struct {
108 OutputOptions
109 Unbuffered bool `short:"u" long:"unbuffered" description:"Do not buffer console output and suppress default output processor"`
110 Filter string `short:"f" long:"filter" description:"Comma-separated list of filters"`
111 All bool `short:"a" long:"all" description:"Operate on all models"`
112 Args struct {
113 ModelName ModelNameString
114 } `positional-args:"yes" required:"yes"`
115 IDArgs struct {
116 ID []int32
117 } `positional-args:"yes" required:"no"`
118}
119
Scott Baker2c0ebda2019-05-06 16:55:47 -0700120type ModelOpts struct {
Scott Baker1dd06672019-06-14 15:40:56 -0700121 List ModelList `command:"list"`
122 Update ModelUpdate `command:"update"`
123 Delete ModelDelete `command:"delete"`
124 Create ModelCreate `command:"create"`
125 Sync ModelSync `command:"sync"`
126 SetDirty ModelSetDirty `command:"setdirty"`
Scott Baker175cb402019-05-17 16:13:06 -0700127}
128
129type ModelStatusOutputRow struct {
Scott Baker8b8a2b52019-05-21 08:56:28 -0700130 Id interface{} `json:"id"`
131 Message string `json:"message"`
Scott Baker175cb402019-05-17 16:13:06 -0700132}
133
134type ModelStatusOutput struct {
Scott Baker8b8a2b52019-05-21 08:56:28 -0700135 Rows []ModelStatusOutputRow
136 Unbuffered bool
Scott Baker2c0ebda2019-05-06 16:55:47 -0700137}
138
139var modelOpts = ModelOpts{}
140
141func RegisterModelCommands(parser *flags.Parser) {
142 parser.AddCommand("model", "model commands", "Commands to query and manipulate XOS models", &modelOpts)
143}
144
Scott Baker8b8a2b52019-05-21 08:56:28 -0700145// Initialize ModelStatusOutput structure, creating a row for each model that will be output
146func InitModelStatusOutput(unbuffered bool, count int) ModelStatusOutput {
147 return ModelStatusOutput{Rows: make([]ModelStatusOutputRow, count), Unbuffered: unbuffered}
Scott Baker175cb402019-05-17 16:13:06 -0700148}
149
Scott Baker8b8a2b52019-05-21 08:56:28 -0700150// Update model status output row for the model
151// If unbuffered is set then we will output directly to the console. Regardless of the unbuffered
152// setting, we always update the row, as callers may check that row for status.
153// Args:
154// output - ModelStatusOutput struct to update
155// i - index of row to update
156// id - id of model, <nil> if no model exists
157// status - status text to set if there is no error
158// errror - if non-nil, then apply error text instead of status text
159// final - true if successful status should be reported, false if successful status is yet to come
160
161func UpdateModelStatusOutput(output *ModelStatusOutput, i int, id interface{}, status string, err error, final bool) {
Scott Baker175cb402019-05-17 16:13:06 -0700162 if err != nil {
Scott Baker8b8a2b52019-05-21 08:56:28 -0700163 if output.Unbuffered {
164 fmt.Printf("%v: %s\n", id, HumanReadableError(err))
Scott Baker175cb402019-05-17 16:13:06 -0700165 }
Scott Baker8b8a2b52019-05-21 08:56:28 -0700166 output.Rows[i] = ModelStatusOutputRow{Id: id, Message: HumanReadableError(err)}
Scott Baker175cb402019-05-17 16:13:06 -0700167 } else {
Scott Baker8b8a2b52019-05-21 08:56:28 -0700168 if output.Unbuffered && final {
Scott Baker175cb402019-05-17 16:13:06 -0700169 fmt.Println(id)
Scott Baker175cb402019-05-17 16:13:06 -0700170 }
Scott Baker8b8a2b52019-05-21 08:56:28 -0700171 output.Rows[i] = ModelStatusOutputRow{Id: id, Message: status}
Scott Baker175cb402019-05-17 16:13:06 -0700172 }
173}
174
Scott Baker1dd06672019-06-14 15:40:56 -0700175// Convert a user-supplied state filter argument to the appropriate enum name
176func GetFilterKind(kindArg string) (string, error) {
177 kindMap := map[string]string{
178 "default": FILTER_DEFAULT,
179 "all": FILTER_ALL,
180 "dirty": FILTER_DIRTY,
181 "deleted": FILTER_DELETED,
182 "dirtypol": FILTER_DIRTYPOL,
183 "deletedpo": FILTER_DELETEDPOL,
184 }
185
186 // If no arg then use default
187 if kindArg == "" {
188 return kindMap["default"], nil
189 }
190
191 val, ok := kindMap[strings.ToLower(kindArg)]
192 if !ok {
193 return "", fmt.Errorf("Failed to understand model state %s", kindArg)
194 }
195
196 return val, nil
197}
198
199// Common processing for commands that take a modelname and a list of ids or a filter
200func GetIDList(conn *grpc.ClientConn, descriptor grpcurl.DescriptorSource, modelName string, ids []int32, filter string, all bool) ([]int32, error) {
201 err := CheckModelName(descriptor, modelName)
202 if err != nil {
203 return nil, err
204 }
205
206 // we require exactly one of ID, --filter, or --all
207 exclusiveCount := 0
208 if len(ids) > 0 {
209 exclusiveCount++
210 }
211 if filter != "" {
212 exclusiveCount++
213 }
214 if all {
215 exclusiveCount++
216 }
217
218 if (exclusiveCount == 0) || (exclusiveCount > 1) {
219 return nil, fmt.Errorf("Use either an ID, --filter, or --all to specify which models to operate on")
220 }
221
222 queries, err := CommaSeparatedQueryToMap(filter, true)
223 if err != nil {
224 return nil, err
225 }
226
227 if len(ids) > 0 {
228 // do nothing
229 } else {
230 models, err := ListOrFilterModels(context.Background(), conn, descriptor, modelName, FILTER_DEFAULT, queries)
231 if err != nil {
232 return nil, err
233 }
234 ids = make([]int32, len(models))
235 for i, model := range models {
236 ids[i] = model.GetFieldByName("id").(int32)
237 }
238 if len(ids) == 0 {
239 return nil, fmt.Errorf("Filter matches no objects")
240 } else if len(ids) > 1 {
241 if !Confirmf("Filter matches %d objects. Continue [y/n] ? ", len(models)) {
242 return nil, fmt.Errorf("Aborted by user")
243 }
244 }
245 }
246
247 return ids, nil
248}
249
Scott Baker6cf525a2019-05-09 12:25:08 -0700250func (options *ModelList) Execute(args []string) error {
251 conn, descriptor, err := InitReflectionClient()
252 if err != nil {
253 return err
254 }
255
256 defer conn.Close()
257
Scott Baker63ce82e2019-05-15 09:01:42 -0700258 err = CheckModelName(descriptor, string(options.Args.ModelName))
Scott Baker6cf525a2019-05-09 12:25:08 -0700259 if err != nil {
260 return err
261 }
262
Scott Baker1dd06672019-06-14 15:40:56 -0700263 filterKind, err := GetFilterKind(options.State)
264 if err != nil {
265 return err
266 }
267
Scott Baker5281d002019-05-16 10:45:26 -0700268 queries, err := CommaSeparatedQueryToMap(options.Filter, true)
Scott Baker2c0ebda2019-05-06 16:55:47 -0700269 if err != nil {
270 return err
271 }
272
Scott Baker1dd06672019-06-14 15:40:56 -0700273 models, err := ListOrFilterModels(context.Background(), conn, descriptor, string(options.Args.ModelName), filterKind, queries)
Scott Baker2c0ebda2019-05-06 16:55:47 -0700274 if err != nil {
275 return err
276 }
277
Scott Bakerea815fc2019-06-05 13:25:44 -0700278 var field_names []string
Scott Baker5201c0b2019-05-15 15:35:56 -0700279 data := make([]map[string]interface{}, len(models))
280 for i, val := range models {
Scott Baker2c0ebda2019-05-06 16:55:47 -0700281 data[i] = make(map[string]interface{})
282 for _, field_desc := range val.GetKnownFields() {
283 field_name := field_desc.GetName()
Scott Baker2c0ebda2019-05-06 16:55:47 -0700284
285 isGuiHidden := strings.Contains(field_desc.GetFieldOptions().String(), "1005:1")
286 isFeedback := strings.Contains(field_desc.GetFieldOptions().String(), "1006:1")
287 isBookkeeping := strings.Contains(field_desc.GetFieldOptions().String(), "1007:1")
288
289 if isGuiHidden && (!options.ShowHidden) {
290 continue
291 }
292
293 if isFeedback && (!options.ShowFeedback) {
294 continue
295 }
296
Scott Baker6cf525a2019-05-09 12:25:08 -0700297 if isBookkeeping && (!options.ShowBookkeeping) {
Scott Baker2c0ebda2019-05-06 16:55:47 -0700298 continue
299 }
300
301 if field_desc.IsRepeated() {
302 continue
303 }
304
Scott Baker6cf525a2019-05-09 12:25:08 -0700305 data[i][field_name] = val.GetFieldByName(field_name)
Scott Baker2c0ebda2019-05-06 16:55:47 -0700306
Scott Bakerea815fc2019-06-05 13:25:44 -0700307 // Every row has the same set of known field names, so it suffices to use the names
308 // from the first row.
309 if i == 0 {
310 field_names = append(field_names, field_name)
311 }
Scott Baker2c0ebda2019-05-06 16:55:47 -0700312 }
313 }
314
Scott Bakerea815fc2019-06-05 13:25:44 -0700315 // Sort field names, making sure "id" appears first
316 sort.SliceStable(field_names, func(i, j int) bool {
317 if field_names[i] == "id" {
318 return true
319 } else if field_names[j] == "id" {
320 return false
321 } else {
322 return (field_names[i] < field_names[j])
323 }
324 })
325
Scott Baker2c0ebda2019-05-06 16:55:47 -0700326 var default_format strings.Builder
327 default_format.WriteString("table")
Scott Bakerea815fc2019-06-05 13:25:44 -0700328 for i, field_name := range field_names {
329 if i == 0 {
Scott Baker2c0ebda2019-05-06 16:55:47 -0700330 fmt.Fprintf(&default_format, "{{ .%s }}", field_name)
Scott Baker2c0ebda2019-05-06 16:55:47 -0700331 } else {
332 fmt.Fprintf(&default_format, "\t{{ .%s }}", field_name)
333 }
334 }
335
Scott Bakera00418a2019-06-03 16:15:28 -0700336 FormatAndGenerateListOutput(&options.ListOutputOptions, default_format.String(), "{{.id}}", data)
Scott Baker5281d002019-05-16 10:45:26 -0700337
338 return nil
339}
340
341func (options *ModelUpdate) Execute(args []string) error {
342 conn, descriptor, err := InitReflectionClient()
343 if err != nil {
344 return err
Scott Baker2c0ebda2019-05-06 16:55:47 -0700345 }
346
Scott Baker5281d002019-05-16 10:45:26 -0700347 defer conn.Close()
348
349 err = CheckModelName(descriptor, string(options.Args.ModelName))
350 if err != nil {
351 return err
Scott Baker2c0ebda2019-05-06 16:55:47 -0700352 }
353
Scott Baker5281d002019-05-16 10:45:26 -0700354 if (len(options.IDArgs.ID) == 0 && len(options.Filter) == 0) ||
355 (len(options.IDArgs.ID) != 0 && len(options.Filter) != 0) {
356 return fmt.Errorf("Use either an ID or a --filter to specify which models to update")
357 }
358
359 queries, err := CommaSeparatedQueryToMap(options.Filter, true)
360 if err != nil {
361 return err
362 }
363
364 updates, err := CommaSeparatedQueryToMap(options.SetFields, true)
365 if err != nil {
366 return err
367 }
368
369 modelName := string(options.Args.ModelName)
370
371 var models []*dynamic.Message
372
373 if len(options.IDArgs.ID) > 0 {
374 models = make([]*dynamic.Message, len(options.IDArgs.ID))
375 for i, id := range options.IDArgs.ID {
Scott Bakerc328cf12019-05-28 16:03:12 -0700376 models[i], err = GetModel(context.Background(), conn, descriptor, modelName, id)
Scott Baker5281d002019-05-16 10:45:26 -0700377 if err != nil {
378 return err
379 }
380 }
381 } else {
Scott Baker1dd06672019-06-14 15:40:56 -0700382 models, err = ListOrFilterModels(context.Background(), conn, descriptor, modelName, FILTER_DEFAULT, queries)
Scott Baker5281d002019-05-16 10:45:26 -0700383 if err != nil {
384 return err
385 }
386 }
387
388 if len(models) == 0 {
389 return fmt.Errorf("Filter matches no objects")
390 } else if len(models) > 1 {
391 if !Confirmf("Filter matches %d objects. Continue [y/n] ? ", len(models)) {
392 return fmt.Errorf("Aborted by user")
393 }
394 }
395
396 fields := make(map[string]interface{})
397
398 if len(options.SetJSON) > 0 {
399 fields["_json"] = []byte(options.SetJSON)
400 }
401
402 for fieldName, value := range updates {
403 value = value[1:]
404 proto_value, err := TypeConvert(descriptor, modelName, fieldName, value)
405 if err != nil {
406 return err
407 }
408 fields[fieldName] = proto_value
409 }
410
Scott Baker8b8a2b52019-05-21 08:56:28 -0700411 modelStatusOutput := InitModelStatusOutput(options.Unbuffered, len(models))
Scott Baker175cb402019-05-17 16:13:06 -0700412 for i, model := range models {
413 id := model.GetFieldByName("id").(int32)
414 fields["id"] = id
415 err := UpdateModel(conn, descriptor, modelName, fields)
416
Scott Baker8b8a2b52019-05-21 08:56:28 -0700417 UpdateModelStatusOutput(&modelStatusOutput, i, id, "Updated", err, !options.Sync)
Scott Baker5281d002019-05-16 10:45:26 -0700418 }
419
Scott Baker8b8a2b52019-05-21 08:56:28 -0700420 if options.Sync {
Scott Bakerc328cf12019-05-28 16:03:12 -0700421 ctx, cancel := context.WithTimeout(context.Background(), options.SyncTimeout)
422 defer cancel()
Scott Baker8b8a2b52019-05-21 08:56:28 -0700423 for i, model := range models {
424 id := model.GetFieldByName("id").(int32)
425 if modelStatusOutput.Rows[i].Message == "Updated" {
426 conditional_printf(!options.Quiet, "Wait for sync: %d ", id)
Scott Bakerc328cf12019-05-28 16:03:12 -0700427 conn, _, err = GetModelWithRetry(ctx, conn, descriptor, modelName, id, GM_UNTIL_ENACTED|Ternary_uint32(options.Quiet, GM_QUIET, 0))
Scott Baker8b8a2b52019-05-21 08:56:28 -0700428 conditional_printf(!options.Quiet, "\n")
429 UpdateModelStatusOutput(&modelStatusOutput, i, id, "Enacted", err, true)
430 }
431 }
432 }
433
434 if !options.Unbuffered {
435 FormatAndGenerateOutput(&options.OutputOptions, DEFAULT_UPDATE_FORMAT, DEFAULT_UPDATE_FORMAT, modelStatusOutput.Rows)
Scott Baker175cb402019-05-17 16:13:06 -0700436 }
437
438 return nil
439}
440
441func (options *ModelDelete) Execute(args []string) error {
442 conn, descriptor, err := InitReflectionClient()
443 if err != nil {
444 return err
445 }
446
447 defer conn.Close()
448
Scott Baker175cb402019-05-17 16:13:06 -0700449 modelName := string(options.Args.ModelName)
Scott Baker1dd06672019-06-14 15:40:56 -0700450 ids, err := GetIDList(conn, descriptor, modelName, options.IDArgs.ID, options.Filter, options.All)
451 if err != nil {
452 return err
Scott Baker175cb402019-05-17 16:13:06 -0700453 }
454
Scott Baker8b8a2b52019-05-21 08:56:28 -0700455 modelStatusOutput := InitModelStatusOutput(options.Unbuffered, len(ids))
Scott Baker175cb402019-05-17 16:13:06 -0700456 for i, id := range ids {
457 err = DeleteModel(conn, descriptor, modelName, id)
Scott Baker8b8a2b52019-05-21 08:56:28 -0700458 UpdateModelStatusOutput(&modelStatusOutput, i, id, "Deleted", err, true)
Scott Baker175cb402019-05-17 16:13:06 -0700459 }
460
Scott Baker8b8a2b52019-05-21 08:56:28 -0700461 if !options.Unbuffered {
462 FormatAndGenerateOutput(&options.OutputOptions, DEFAULT_DELETE_FORMAT, DEFAULT_DELETE_FORMAT, modelStatusOutput.Rows)
463 }
464
465 return nil
466}
467
468func (options *ModelCreate) Execute(args []string) error {
469 conn, descriptor, err := InitReflectionClient()
470 if err != nil {
471 return err
472 }
473
474 defer conn.Close()
475
476 err = CheckModelName(descriptor, string(options.Args.ModelName))
477 if err != nil {
478 return err
479 }
480
481 updates, err := CommaSeparatedQueryToMap(options.SetFields, true)
482 if err != nil {
483 return err
484 }
485
486 modelName := string(options.Args.ModelName)
487
488 fields := make(map[string]interface{})
489
490 if len(options.SetJSON) > 0 {
491 fields["_json"] = []byte(options.SetJSON)
492 }
493
494 for fieldName, value := range updates {
495 value = value[1:]
496 proto_value, err := TypeConvert(descriptor, modelName, fieldName, value)
497 if err != nil {
498 return err
499 }
500 fields[fieldName] = proto_value
501 }
502
503 modelStatusOutput := InitModelStatusOutput(options.Unbuffered, 1)
504
505 err = CreateModel(conn, descriptor, modelName, fields)
506 UpdateModelStatusOutput(&modelStatusOutput, 0, fields["id"], "Created", err, !options.Sync)
507
508 if options.Sync {
Scott Bakerc328cf12019-05-28 16:03:12 -0700509 ctx, cancel := context.WithTimeout(context.Background(), options.SyncTimeout)
510 defer cancel()
Scott Baker8b8a2b52019-05-21 08:56:28 -0700511 if modelStatusOutput.Rows[0].Message == "Created" {
512 id := fields["id"].(int32)
513 conditional_printf(!options.Quiet, "Wait for sync: %d ", id)
Scott Bakerc328cf12019-05-28 16:03:12 -0700514 conn, _, err = GetModelWithRetry(ctx, conn, descriptor, modelName, id, GM_UNTIL_ENACTED|Ternary_uint32(options.Quiet, GM_QUIET, 0))
Scott Baker8b8a2b52019-05-21 08:56:28 -0700515 conditional_printf(!options.Quiet, "\n")
516 UpdateModelStatusOutput(&modelStatusOutput, 0, id, "Enacted", err, true)
517 }
518 }
519
520 if !options.Unbuffered {
521 FormatAndGenerateOutput(&options.OutputOptions, DEFAULT_CREATE_FORMAT, DEFAULT_CREATE_FORMAT, modelStatusOutput.Rows)
522 }
523
524 return nil
525}
526
527func (options *ModelSync) Execute(args []string) error {
528 conn, descriptor, err := InitReflectionClient()
529 if err != nil {
530 return err
531 }
532
533 defer conn.Close()
534
Scott Baker8b8a2b52019-05-21 08:56:28 -0700535 modelName := string(options.Args.ModelName)
Scott Baker1dd06672019-06-14 15:40:56 -0700536 ids, err := GetIDList(conn, descriptor, modelName, options.IDArgs.ID, options.Filter, options.All)
537 if err != nil {
538 return err
Scott Baker8b8a2b52019-05-21 08:56:28 -0700539 }
540
Scott Bakerc328cf12019-05-28 16:03:12 -0700541 ctx, cancel := context.WithTimeout(context.Background(), options.SyncTimeout)
542 defer cancel()
543
Scott Baker8b8a2b52019-05-21 08:56:28 -0700544 modelStatusOutput := InitModelStatusOutput(options.Unbuffered, len(ids))
545 for i, id := range ids {
546 conditional_printf(!options.Quiet, "Wait for sync: %d ", id)
Scott Bakerc328cf12019-05-28 16:03:12 -0700547 conn, _, err = GetModelWithRetry(ctx, conn, descriptor, modelName, id, GM_UNTIL_ENACTED|Ternary_uint32(options.Quiet, GM_QUIET, 0))
Scott Baker8b8a2b52019-05-21 08:56:28 -0700548 conditional_printf(!options.Quiet, "\n")
549 UpdateModelStatusOutput(&modelStatusOutput, i, id, "Enacted", err, true)
550 }
551
552 if !options.Unbuffered {
553 FormatAndGenerateOutput(&options.OutputOptions, DEFAULT_SYNC_FORMAT, DEFAULT_SYNC_FORMAT, modelStatusOutput.Rows)
Scott Baker175cb402019-05-17 16:13:06 -0700554 }
Scott Baker5281d002019-05-16 10:45:26 -0700555
Scott Baker2c0ebda2019-05-06 16:55:47 -0700556 return nil
557}
Scott Baker63ce82e2019-05-15 09:01:42 -0700558
Scott Baker1dd06672019-06-14 15:40:56 -0700559func (options *ModelSetDirty) Execute(args []string) error {
560 conn, descriptor, err := InitReflectionClient()
561 if err != nil {
562 return err
563 }
564
565 defer conn.Close()
566
567 modelName := string(options.Args.ModelName)
568 ids, err := GetIDList(conn, descriptor, modelName, options.IDArgs.ID, options.Filter, options.All)
569 if err != nil {
570 return err
571 }
572
573 modelStatusOutput := InitModelStatusOutput(options.Unbuffered, len(ids))
574 for i, id := range ids {
575 updateMap := map[string]interface{}{"id": id}
576 err := UpdateModel(conn, descriptor, modelName, updateMap)
577 UpdateModelStatusOutput(&modelStatusOutput, i, id, "Dirtied", err, true)
578 }
579
580 if !options.Unbuffered {
581 FormatAndGenerateOutput(&options.OutputOptions, DEFAULT_SYNC_FORMAT, DEFAULT_SYNC_FORMAT, modelStatusOutput.Rows)
582 }
583
584 return nil
585}
586
Scott Baker63ce82e2019-05-15 09:01:42 -0700587func (modelName *ModelNameString) Complete(match string) []flags.Completion {
588 conn, descriptor, err := InitReflectionClient()
589 if err != nil {
590 return nil
591 }
592
593 defer conn.Close()
594
595 models, err := GetModelNames(descriptor)
596 if err != nil {
597 return nil
598 }
599
600 list := make([]flags.Completion, 0)
601 for k := range models {
602 if strings.HasPrefix(k, match) {
603 list = append(list, flags.Completion{Item: k})
604 }
605 }
606
607 return list
608}