blob: e4131787dd63419c75fecaad9b5783b6fe53423a [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 Baker20481aa2019-06-20 11:00:54 -070025 corderrors "github.com/opencord/cordctl/error"
Scott Baker1dd06672019-06-14 15:40:56 -070026 "google.golang.org/grpc"
Scott Bakerea815fc2019-06-05 13:25:44 -070027 "sort"
Scott Baker2c0ebda2019-05-06 16:55:47 -070028 "strings"
Scott Bakerc328cf12019-05-28 16:03:12 -070029 "time"
Scott Baker2c0ebda2019-05-06 16:55:47 -070030)
31
Scott Baker175cb402019-05-17 16:13:06 -070032const (
Scott Baker8b8a2b52019-05-21 08:56:28 -070033 DEFAULT_CREATE_FORMAT = "table{{ .Id }}\t{{ .Message }}"
Scott Baker175cb402019-05-17 16:13:06 -070034 DEFAULT_DELETE_FORMAT = "table{{ .Id }}\t{{ .Message }}"
35 DEFAULT_UPDATE_FORMAT = "table{{ .Id }}\t{{ .Message }}"
Scott Baker8b8a2b52019-05-21 08:56:28 -070036 DEFAULT_SYNC_FORMAT = "table{{ .Id }}\t{{ .Message }}"
Scott Baker175cb402019-05-17 16:13:06 -070037)
38
Scott Baker63ce82e2019-05-15 09:01:42 -070039type ModelNameString string
40
Scott Baker2c0ebda2019-05-06 16:55:47 -070041type ModelList struct {
Scott Bakera00418a2019-06-03 16:15:28 -070042 ListOutputOptions
Scott Baker5201c0b2019-05-15 15:35:56 -070043 ShowHidden bool `long:"showhidden" description:"Show hidden fields in default output"`
44 ShowFeedback bool `long:"showfeedback" description:"Show feedback fields in default output"`
45 ShowBookkeeping bool `long:"showbookkeeping" description:"Show bookkeeping fields in default output"`
Scott Baker175cb402019-05-17 16:13:06 -070046 Filter string `short:"f" long:"filter" description:"Comma-separated list of filters"`
Scott Baker1dd06672019-06-14 15:40:56 -070047 State string `short:"s" long:"state" description:"Filter model state [DEFAULT | ALL | DIRTY | DELETED | DIRTYPOL | DELETEDPOL]"`
Scott Baker6cf525a2019-05-09 12:25:08 -070048 Args struct {
Scott Baker63ce82e2019-05-15 09:01:42 -070049 ModelName ModelNameString
Scott Baker2c0ebda2019-05-06 16:55:47 -070050 } `positional-args:"yes" required:"yes"`
51}
52
Scott Baker5281d002019-05-16 10:45:26 -070053type ModelUpdate struct {
Scott Baker6cf525a2019-05-09 12:25:08 -070054 OutputOptions
Scott Bakerc328cf12019-05-28 16:03:12 -070055 Unbuffered bool `short:"u" long:"unbuffered" description:"Do not buffer console output and suppress default output processor"`
56 Filter string `short:"f" long:"filter" description:"Comma-separated list of filters"`
57 SetFields string `long:"set-field" description:"Comma-separated list of field=value to set"`
58 SetJSON string `long:"set-json" description:"JSON dictionary to use for settings fields"`
59 Sync bool `long:"sync" description:"Synchronize before returning"`
60 SyncTimeout time.Duration `long:"synctimeout" default:"600s" description:"Timeout for --sync option"`
61 Args struct {
Scott Baker5281d002019-05-16 10:45:26 -070062 ModelName ModelNameString
63 } `positional-args:"yes" required:"yes"`
64 IDArgs struct {
65 ID []int32
66 } `positional-args:"yes" required:"no"`
Scott Baker6cf525a2019-05-09 12:25:08 -070067}
68
Scott Baker175cb402019-05-17 16:13:06 -070069type ModelDelete struct {
70 OutputOptions
Scott Baker8b8a2b52019-05-21 08:56:28 -070071 Unbuffered bool `short:"u" long:"unbuffered" description:"Do not buffer console output and suppress default output processor"`
72 Filter string `short:"f" long:"filter" description:"Comma-separated list of filters"`
Scott Baker1dd06672019-06-14 15:40:56 -070073 All bool `short:"a" long:"all" description:"Operate on all models"`
Scott Baker8b8a2b52019-05-21 08:56:28 -070074 Args struct {
75 ModelName ModelNameString
76 } `positional-args:"yes" required:"yes"`
77 IDArgs struct {
78 ID []int32
79 } `positional-args:"yes" required:"no"`
80}
81
82type ModelCreate struct {
83 OutputOptions
Scott Bakerc328cf12019-05-28 16:03:12 -070084 Unbuffered bool `short:"u" long:"unbuffered" description:"Do not buffer console output"`
85 SetFields string `long:"set-field" description:"Comma-separated list of field=value to set"`
86 SetJSON string `long:"set-json" description:"JSON dictionary to use for settings fields"`
87 Sync bool `long:"sync" description:"Synchronize before returning"`
88 SyncTimeout time.Duration `long:"synctimeout" default:"600s" description:"Timeout for --sync option"`
89 Args struct {
Scott Baker8b8a2b52019-05-21 08:56:28 -070090 ModelName ModelNameString
91 } `positional-args:"yes" required:"yes"`
92}
93
94type ModelSync struct {
95 OutputOptions
Scott Bakerc328cf12019-05-28 16:03:12 -070096 Unbuffered bool `short:"u" long:"unbuffered" description:"Do not buffer console output and suppress default output processor"`
97 Filter string `short:"f" long:"filter" description:"Comma-separated list of filters"`
98 SyncTimeout time.Duration `long:"synctimeout" default:"600s" description:"Timeout for synchronization"`
Scott Baker1dd06672019-06-14 15:40:56 -070099 All bool `short:"a" long:"all" description:"Operate on all models"`
Scott Bakerc328cf12019-05-28 16:03:12 -0700100 Args struct {
Scott Baker175cb402019-05-17 16:13:06 -0700101 ModelName ModelNameString
102 } `positional-args:"yes" required:"yes"`
103 IDArgs struct {
104 ID []int32
105 } `positional-args:"yes" required:"no"`
106}
107
Scott Baker1dd06672019-06-14 15:40:56 -0700108type ModelSetDirty struct {
109 OutputOptions
110 Unbuffered bool `short:"u" long:"unbuffered" description:"Do not buffer console output and suppress default output processor"`
111 Filter string `short:"f" long:"filter" description:"Comma-separated list of filters"`
112 All bool `short:"a" long:"all" description:"Operate on all models"`
113 Args struct {
114 ModelName ModelNameString
115 } `positional-args:"yes" required:"yes"`
116 IDArgs struct {
117 ID []int32
118 } `positional-args:"yes" required:"no"`
119}
120
Scott Baker2c0ebda2019-05-06 16:55:47 -0700121type ModelOpts struct {
Scott Baker1dd06672019-06-14 15:40:56 -0700122 List ModelList `command:"list"`
123 Update ModelUpdate `command:"update"`
124 Delete ModelDelete `command:"delete"`
125 Create ModelCreate `command:"create"`
126 Sync ModelSync `command:"sync"`
127 SetDirty ModelSetDirty `command:"setdirty"`
Scott Baker175cb402019-05-17 16:13:06 -0700128}
129
130type ModelStatusOutputRow struct {
Scott Baker8b8a2b52019-05-21 08:56:28 -0700131 Id interface{} `json:"id"`
132 Message string `json:"message"`
Scott Baker175cb402019-05-17 16:13:06 -0700133}
134
135type ModelStatusOutput struct {
Scott Baker8b8a2b52019-05-21 08:56:28 -0700136 Rows []ModelStatusOutputRow
137 Unbuffered bool
Scott Baker2c0ebda2019-05-06 16:55:47 -0700138}
139
140var modelOpts = ModelOpts{}
141
142func RegisterModelCommands(parser *flags.Parser) {
143 parser.AddCommand("model", "model commands", "Commands to query and manipulate XOS models", &modelOpts)
144}
145
Scott Baker8b8a2b52019-05-21 08:56:28 -0700146// Initialize ModelStatusOutput structure, creating a row for each model that will be output
147func InitModelStatusOutput(unbuffered bool, count int) ModelStatusOutput {
148 return ModelStatusOutput{Rows: make([]ModelStatusOutputRow, count), Unbuffered: unbuffered}
Scott Baker175cb402019-05-17 16:13:06 -0700149}
150
Scott Baker8b8a2b52019-05-21 08:56:28 -0700151// Update model status output row for the model
152// If unbuffered is set then we will output directly to the console. Regardless of the unbuffered
153// setting, we always update the row, as callers may check that row for status.
154// Args:
155// output - ModelStatusOutput struct to update
156// i - index of row to update
157// id - id of model, <nil> if no model exists
158// status - status text to set if there is no error
159// errror - if non-nil, then apply error text instead of status text
160// final - true if successful status should be reported, false if successful status is yet to come
161
162func UpdateModelStatusOutput(output *ModelStatusOutput, i int, id interface{}, status string, err error, final bool) {
Scott Baker175cb402019-05-17 16:13:06 -0700163 if err != nil {
Scott Baker8b8a2b52019-05-21 08:56:28 -0700164 if output.Unbuffered {
Scott Bakera55e6452019-06-25 11:10:30 -0700165 fmt.Printf("%v: %s\n", id, err)
Scott Baker175cb402019-05-17 16:13:06 -0700166 }
Scott Bakera55e6452019-06-25 11:10:30 -0700167 output.Rows[i] = ModelStatusOutputRow{Id: id, Message: err.Error()}
Scott Baker175cb402019-05-17 16:13:06 -0700168 } else {
Scott Baker8b8a2b52019-05-21 08:56:28 -0700169 if output.Unbuffered && final {
Scott Baker175cb402019-05-17 16:13:06 -0700170 fmt.Println(id)
Scott Baker175cb402019-05-17 16:13:06 -0700171 }
Scott Baker8b8a2b52019-05-21 08:56:28 -0700172 output.Rows[i] = ModelStatusOutputRow{Id: id, Message: status}
Scott Baker175cb402019-05-17 16:13:06 -0700173 }
174}
175
Scott Baker1dd06672019-06-14 15:40:56 -0700176// Convert a user-supplied state filter argument to the appropriate enum name
177func GetFilterKind(kindArg string) (string, error) {
178 kindMap := map[string]string{
179 "default": FILTER_DEFAULT,
180 "all": FILTER_ALL,
181 "dirty": FILTER_DIRTY,
182 "deleted": FILTER_DELETED,
183 "dirtypol": FILTER_DIRTYPOL,
184 "deletedpo": FILTER_DELETEDPOL,
185 }
186
187 // If no arg then use default
188 if kindArg == "" {
189 return kindMap["default"], nil
190 }
191
192 val, ok := kindMap[strings.ToLower(kindArg)]
193 if !ok {
Scott Baker20481aa2019-06-20 11:00:54 -0700194 return "", corderrors.WithStackTrace(&corderrors.UnknownModelStateError{Name: kindArg})
Scott Baker1dd06672019-06-14 15:40:56 -0700195 }
196
197 return val, nil
198}
199
200// Common processing for commands that take a modelname and a list of ids or a filter
201func GetIDList(conn *grpc.ClientConn, descriptor grpcurl.DescriptorSource, modelName string, ids []int32, filter string, all bool) ([]int32, error) {
202 err := CheckModelName(descriptor, modelName)
203 if err != nil {
204 return nil, err
205 }
206
207 // we require exactly one of ID, --filter, or --all
208 exclusiveCount := 0
209 if len(ids) > 0 {
210 exclusiveCount++
211 }
212 if filter != "" {
213 exclusiveCount++
214 }
215 if all {
216 exclusiveCount++
217 }
218
219 if (exclusiveCount == 0) || (exclusiveCount > 1) {
Scott Baker20481aa2019-06-20 11:00:54 -0700220 return nil, corderrors.WithStackTrace(&corderrors.FilterRequiredError{})
Scott Baker1dd06672019-06-14 15:40:56 -0700221 }
222
223 queries, err := CommaSeparatedQueryToMap(filter, true)
224 if err != nil {
225 return nil, err
226 }
227
228 if len(ids) > 0 {
229 // do nothing
230 } else {
231 models, err := ListOrFilterModels(context.Background(), conn, descriptor, modelName, FILTER_DEFAULT, queries)
232 if err != nil {
233 return nil, err
234 }
235 ids = make([]int32, len(models))
236 for i, model := range models {
237 ids[i] = model.GetFieldByName("id").(int32)
238 }
239 if len(ids) == 0 {
Scott Baker20481aa2019-06-20 11:00:54 -0700240 return nil, corderrors.WithStackTrace(&corderrors.NoMatchError{})
Scott Baker1dd06672019-06-14 15:40:56 -0700241 } else if len(ids) > 1 {
242 if !Confirmf("Filter matches %d objects. Continue [y/n] ? ", len(models)) {
Scott Baker20481aa2019-06-20 11:00:54 -0700243 return nil, corderrors.WithStackTrace(&corderrors.AbortedError{})
Scott Baker1dd06672019-06-14 15:40:56 -0700244 }
245 }
246 }
247
248 return ids, nil
249}
250
Scott Baker6cf525a2019-05-09 12:25:08 -0700251func (options *ModelList) Execute(args []string) error {
Scott Baker867aa302019-06-19 13:18:45 -0700252 conn, descriptor, err := InitClient(INIT_DEFAULT)
Scott Baker6cf525a2019-05-09 12:25:08 -0700253 if err != nil {
254 return err
255 }
256
257 defer conn.Close()
258
Scott Baker63ce82e2019-05-15 09:01:42 -0700259 err = CheckModelName(descriptor, string(options.Args.ModelName))
Scott Baker6cf525a2019-05-09 12:25:08 -0700260 if err != nil {
261 return err
262 }
263
Scott Baker1dd06672019-06-14 15:40:56 -0700264 filterKind, err := GetFilterKind(options.State)
265 if err != nil {
266 return err
267 }
268
Scott Baker5281d002019-05-16 10:45:26 -0700269 queries, err := CommaSeparatedQueryToMap(options.Filter, true)
Scott Baker2c0ebda2019-05-06 16:55:47 -0700270 if err != nil {
271 return err
272 }
273
Scott Baker1dd06672019-06-14 15:40:56 -0700274 models, err := ListOrFilterModels(context.Background(), conn, descriptor, string(options.Args.ModelName), filterKind, queries)
Scott Baker2c0ebda2019-05-06 16:55:47 -0700275 if err != nil {
276 return err
277 }
278
Scott Bakerea815fc2019-06-05 13:25:44 -0700279 var field_names []string
Scott Baker5201c0b2019-05-15 15:35:56 -0700280 data := make([]map[string]interface{}, len(models))
281 for i, val := range models {
Scott Baker2c0ebda2019-05-06 16:55:47 -0700282 data[i] = make(map[string]interface{})
283 for _, field_desc := range val.GetKnownFields() {
284 field_name := field_desc.GetName()
Scott Baker2c0ebda2019-05-06 16:55:47 -0700285
286 isGuiHidden := strings.Contains(field_desc.GetFieldOptions().String(), "1005:1")
287 isFeedback := strings.Contains(field_desc.GetFieldOptions().String(), "1006:1")
288 isBookkeeping := strings.Contains(field_desc.GetFieldOptions().String(), "1007:1")
289
290 if isGuiHidden && (!options.ShowHidden) {
291 continue
292 }
293
294 if isFeedback && (!options.ShowFeedback) {
295 continue
296 }
297
Scott Baker6cf525a2019-05-09 12:25:08 -0700298 if isBookkeeping && (!options.ShowBookkeeping) {
Scott Baker2c0ebda2019-05-06 16:55:47 -0700299 continue
300 }
301
302 if field_desc.IsRepeated() {
303 continue
304 }
305
Scott Baker6cf525a2019-05-09 12:25:08 -0700306 data[i][field_name] = val.GetFieldByName(field_name)
Scott Baker2c0ebda2019-05-06 16:55:47 -0700307
Scott Bakerea815fc2019-06-05 13:25:44 -0700308 // Every row has the same set of known field names, so it suffices to use the names
309 // from the first row.
310 if i == 0 {
311 field_names = append(field_names, field_name)
312 }
Scott Baker2c0ebda2019-05-06 16:55:47 -0700313 }
314 }
315
Scott Bakerea815fc2019-06-05 13:25:44 -0700316 // Sort field names, making sure "id" appears first
317 sort.SliceStable(field_names, func(i, j int) bool {
318 if field_names[i] == "id" {
319 return true
320 } else if field_names[j] == "id" {
321 return false
322 } else {
323 return (field_names[i] < field_names[j])
324 }
325 })
326
Scott Baker2c0ebda2019-05-06 16:55:47 -0700327 var default_format strings.Builder
328 default_format.WriteString("table")
Scott Bakerea815fc2019-06-05 13:25:44 -0700329 for i, field_name := range field_names {
330 if i == 0 {
Scott Baker2c0ebda2019-05-06 16:55:47 -0700331 fmt.Fprintf(&default_format, "{{ .%s }}", field_name)
Scott Baker2c0ebda2019-05-06 16:55:47 -0700332 } else {
333 fmt.Fprintf(&default_format, "\t{{ .%s }}", field_name)
334 }
335 }
336
Scott Bakera00418a2019-06-03 16:15:28 -0700337 FormatAndGenerateListOutput(&options.ListOutputOptions, default_format.String(), "{{.id}}", data)
Scott Baker5281d002019-05-16 10:45:26 -0700338
339 return nil
340}
341
342func (options *ModelUpdate) Execute(args []string) error {
Scott Baker867aa302019-06-19 13:18:45 -0700343 conn, descriptor, err := InitClient(INIT_DEFAULT)
Scott Baker5281d002019-05-16 10:45:26 -0700344 if err != nil {
345 return err
Scott Baker2c0ebda2019-05-06 16:55:47 -0700346 }
347
Scott Baker5281d002019-05-16 10:45:26 -0700348 defer conn.Close()
349
350 err = CheckModelName(descriptor, string(options.Args.ModelName))
351 if err != nil {
352 return err
Scott Baker2c0ebda2019-05-06 16:55:47 -0700353 }
354
Scott Baker5281d002019-05-16 10:45:26 -0700355 if (len(options.IDArgs.ID) == 0 && len(options.Filter) == 0) ||
356 (len(options.IDArgs.ID) != 0 && len(options.Filter) != 0) {
Scott Baker20481aa2019-06-20 11:00:54 -0700357 return corderrors.WithStackTrace(&corderrors.FilterRequiredError{})
Scott Baker5281d002019-05-16 10:45:26 -0700358 }
359
360 queries, err := CommaSeparatedQueryToMap(options.Filter, true)
361 if err != nil {
362 return err
363 }
364
365 updates, err := CommaSeparatedQueryToMap(options.SetFields, true)
366 if err != nil {
367 return err
368 }
369
370 modelName := string(options.Args.ModelName)
371
372 var models []*dynamic.Message
373
374 if len(options.IDArgs.ID) > 0 {
375 models = make([]*dynamic.Message, len(options.IDArgs.ID))
376 for i, id := range options.IDArgs.ID {
Scott Bakerc328cf12019-05-28 16:03:12 -0700377 models[i], err = GetModel(context.Background(), conn, descriptor, modelName, id)
Scott Baker5281d002019-05-16 10:45:26 -0700378 if err != nil {
379 return err
380 }
381 }
382 } else {
Scott Baker1dd06672019-06-14 15:40:56 -0700383 models, err = ListOrFilterModels(context.Background(), conn, descriptor, modelName, FILTER_DEFAULT, queries)
Scott Baker5281d002019-05-16 10:45:26 -0700384 if err != nil {
385 return err
386 }
387 }
388
389 if len(models) == 0 {
Scott Baker20481aa2019-06-20 11:00:54 -0700390 return corderrors.WithStackTrace(&corderrors.NoMatchError{})
Scott Baker5281d002019-05-16 10:45:26 -0700391 } else if len(models) > 1 {
392 if !Confirmf("Filter matches %d objects. Continue [y/n] ? ", len(models)) {
Scott Baker20481aa2019-06-20 11:00:54 -0700393 return corderrors.WithStackTrace(&corderrors.AbortedError{})
Scott Baker5281d002019-05-16 10:45:26 -0700394 }
395 }
396
397 fields := make(map[string]interface{})
398
399 if len(options.SetJSON) > 0 {
400 fields["_json"] = []byte(options.SetJSON)
401 }
402
403 for fieldName, value := range updates {
404 value = value[1:]
405 proto_value, err := TypeConvert(descriptor, modelName, fieldName, value)
406 if err != nil {
407 return err
408 }
409 fields[fieldName] = proto_value
410 }
411
Scott Baker8b8a2b52019-05-21 08:56:28 -0700412 modelStatusOutput := InitModelStatusOutput(options.Unbuffered, len(models))
Scott Baker175cb402019-05-17 16:13:06 -0700413 for i, model := range models {
414 id := model.GetFieldByName("id").(int32)
415 fields["id"] = id
416 err := UpdateModel(conn, descriptor, modelName, fields)
417
Scott Baker8b8a2b52019-05-21 08:56:28 -0700418 UpdateModelStatusOutput(&modelStatusOutput, i, id, "Updated", err, !options.Sync)
Scott Baker5281d002019-05-16 10:45:26 -0700419 }
420
Scott Baker8b8a2b52019-05-21 08:56:28 -0700421 if options.Sync {
Scott Bakerc328cf12019-05-28 16:03:12 -0700422 ctx, cancel := context.WithTimeout(context.Background(), options.SyncTimeout)
423 defer cancel()
Scott Baker8b8a2b52019-05-21 08:56:28 -0700424 for i, model := range models {
425 id := model.GetFieldByName("id").(int32)
426 if modelStatusOutput.Rows[i].Message == "Updated" {
427 conditional_printf(!options.Quiet, "Wait for sync: %d ", id)
Scott Bakerc328cf12019-05-28 16:03:12 -0700428 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 -0700429 conditional_printf(!options.Quiet, "\n")
430 UpdateModelStatusOutput(&modelStatusOutput, i, id, "Enacted", err, true)
431 }
432 }
433 }
434
435 if !options.Unbuffered {
436 FormatAndGenerateOutput(&options.OutputOptions, DEFAULT_UPDATE_FORMAT, DEFAULT_UPDATE_FORMAT, modelStatusOutput.Rows)
Scott Baker175cb402019-05-17 16:13:06 -0700437 }
438
439 return nil
440}
441
442func (options *ModelDelete) Execute(args []string) error {
Scott Baker867aa302019-06-19 13:18:45 -0700443 conn, descriptor, err := InitClient(INIT_DEFAULT)
Scott Baker175cb402019-05-17 16:13:06 -0700444 if err != nil {
445 return err
446 }
447
448 defer conn.Close()
449
Scott Baker175cb402019-05-17 16:13:06 -0700450 modelName := string(options.Args.ModelName)
Scott Baker1dd06672019-06-14 15:40:56 -0700451 ids, err := GetIDList(conn, descriptor, modelName, options.IDArgs.ID, options.Filter, options.All)
452 if err != nil {
453 return err
Scott Baker175cb402019-05-17 16:13:06 -0700454 }
455
Scott Baker8b8a2b52019-05-21 08:56:28 -0700456 modelStatusOutput := InitModelStatusOutput(options.Unbuffered, len(ids))
Scott Baker175cb402019-05-17 16:13:06 -0700457 for i, id := range ids {
458 err = DeleteModel(conn, descriptor, modelName, id)
Scott Baker8b8a2b52019-05-21 08:56:28 -0700459 UpdateModelStatusOutput(&modelStatusOutput, i, id, "Deleted", err, true)
Scott Baker175cb402019-05-17 16:13:06 -0700460 }
461
Scott Baker8b8a2b52019-05-21 08:56:28 -0700462 if !options.Unbuffered {
463 FormatAndGenerateOutput(&options.OutputOptions, DEFAULT_DELETE_FORMAT, DEFAULT_DELETE_FORMAT, modelStatusOutput.Rows)
464 }
465
466 return nil
467}
468
469func (options *ModelCreate) Execute(args []string) error {
Scott Baker867aa302019-06-19 13:18:45 -0700470 conn, descriptor, err := InitClient(INIT_DEFAULT)
Scott Baker8b8a2b52019-05-21 08:56:28 -0700471 if err != nil {
472 return err
473 }
474
475 defer conn.Close()
476
477 err = CheckModelName(descriptor, string(options.Args.ModelName))
478 if err != nil {
479 return err
480 }
481
482 updates, err := CommaSeparatedQueryToMap(options.SetFields, true)
483 if err != nil {
484 return err
485 }
486
487 modelName := string(options.Args.ModelName)
488
489 fields := make(map[string]interface{})
490
491 if len(options.SetJSON) > 0 {
492 fields["_json"] = []byte(options.SetJSON)
493 }
494
495 for fieldName, value := range updates {
496 value = value[1:]
497 proto_value, err := TypeConvert(descriptor, modelName, fieldName, value)
498 if err != nil {
499 return err
500 }
501 fields[fieldName] = proto_value
502 }
503
504 modelStatusOutput := InitModelStatusOutput(options.Unbuffered, 1)
505
506 err = CreateModel(conn, descriptor, modelName, fields)
507 UpdateModelStatusOutput(&modelStatusOutput, 0, fields["id"], "Created", err, !options.Sync)
508
509 if options.Sync {
Scott Bakerc328cf12019-05-28 16:03:12 -0700510 ctx, cancel := context.WithTimeout(context.Background(), options.SyncTimeout)
511 defer cancel()
Scott Baker8b8a2b52019-05-21 08:56:28 -0700512 if modelStatusOutput.Rows[0].Message == "Created" {
513 id := fields["id"].(int32)
514 conditional_printf(!options.Quiet, "Wait for sync: %d ", id)
Scott Bakerc328cf12019-05-28 16:03:12 -0700515 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 -0700516 conditional_printf(!options.Quiet, "\n")
517 UpdateModelStatusOutput(&modelStatusOutput, 0, id, "Enacted", err, true)
518 }
519 }
520
521 if !options.Unbuffered {
522 FormatAndGenerateOutput(&options.OutputOptions, DEFAULT_CREATE_FORMAT, DEFAULT_CREATE_FORMAT, modelStatusOutput.Rows)
523 }
524
525 return nil
526}
527
528func (options *ModelSync) Execute(args []string) error {
Scott Baker867aa302019-06-19 13:18:45 -0700529 conn, descriptor, err := InitClient(INIT_DEFAULT)
Scott Baker8b8a2b52019-05-21 08:56:28 -0700530 if err != nil {
531 return err
532 }
533
534 defer conn.Close()
535
Scott Baker8b8a2b52019-05-21 08:56:28 -0700536 modelName := string(options.Args.ModelName)
Scott Baker1dd06672019-06-14 15:40:56 -0700537 ids, err := GetIDList(conn, descriptor, modelName, options.IDArgs.ID, options.Filter, options.All)
538 if err != nil {
539 return err
Scott Baker8b8a2b52019-05-21 08:56:28 -0700540 }
541
Scott Bakerc328cf12019-05-28 16:03:12 -0700542 ctx, cancel := context.WithTimeout(context.Background(), options.SyncTimeout)
543 defer cancel()
544
Scott Baker8b8a2b52019-05-21 08:56:28 -0700545 modelStatusOutput := InitModelStatusOutput(options.Unbuffered, len(ids))
546 for i, id := range ids {
547 conditional_printf(!options.Quiet, "Wait for sync: %d ", id)
Scott Bakerc328cf12019-05-28 16:03:12 -0700548 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 -0700549 conditional_printf(!options.Quiet, "\n")
550 UpdateModelStatusOutput(&modelStatusOutput, i, id, "Enacted", err, true)
551 }
552
553 if !options.Unbuffered {
554 FormatAndGenerateOutput(&options.OutputOptions, DEFAULT_SYNC_FORMAT, DEFAULT_SYNC_FORMAT, modelStatusOutput.Rows)
Scott Baker175cb402019-05-17 16:13:06 -0700555 }
Scott Baker5281d002019-05-16 10:45:26 -0700556
Scott Baker2c0ebda2019-05-06 16:55:47 -0700557 return nil
558}
Scott Baker63ce82e2019-05-15 09:01:42 -0700559
Scott Baker1dd06672019-06-14 15:40:56 -0700560func (options *ModelSetDirty) Execute(args []string) error {
Scott Baker867aa302019-06-19 13:18:45 -0700561 conn, descriptor, err := InitClient(INIT_DEFAULT)
Scott Baker1dd06672019-06-14 15:40:56 -0700562 if err != nil {
563 return err
564 }
565
566 defer conn.Close()
567
568 modelName := string(options.Args.ModelName)
569 ids, err := GetIDList(conn, descriptor, modelName, options.IDArgs.ID, options.Filter, options.All)
570 if err != nil {
571 return err
572 }
573
574 modelStatusOutput := InitModelStatusOutput(options.Unbuffered, len(ids))
575 for i, id := range ids {
576 updateMap := map[string]interface{}{"id": id}
577 err := UpdateModel(conn, descriptor, modelName, updateMap)
578 UpdateModelStatusOutput(&modelStatusOutput, i, id, "Dirtied", err, true)
579 }
580
581 if !options.Unbuffered {
582 FormatAndGenerateOutput(&options.OutputOptions, DEFAULT_SYNC_FORMAT, DEFAULT_SYNC_FORMAT, modelStatusOutput.Rows)
583 }
584
585 return nil
586}
587
Scott Baker63ce82e2019-05-15 09:01:42 -0700588func (modelName *ModelNameString) Complete(match string) []flags.Completion {
Scott Baker867aa302019-06-19 13:18:45 -0700589 conn, descriptor, err := InitClient(INIT_DEFAULT)
Scott Baker63ce82e2019-05-15 09:01:42 -0700590 if err != nil {
591 return nil
592 }
593
594 defer conn.Close()
595
596 models, err := GetModelNames(descriptor)
597 if err != nil {
598 return nil
599 }
600
601 list := make([]flags.Completion, 0)
602 for k := range models {
603 if strings.HasPrefix(k, match) {
604 list = append(list, flags.Completion{Item: k})
605 }
606 }
607
608 return list
609}