SEBA-672 model update commands
Change-Id: I7fef3a4c1ee5ccb8c33a01f37a772142a82249c1
diff --git a/commands/backup.go b/commands/backup.go
index db6e51a..9d11db9 100644
--- a/commands/backup.go
+++ b/commands/backup.go
@@ -20,7 +20,6 @@
"errors"
"fmt"
flags "github.com/jessevdk/go-flags"
- "github.com/opencord/cordctl/format"
"time"
)
@@ -139,27 +138,13 @@
// STEP 6: Show results
- outputFormat := CharReplacer.Replace(options.Format)
- if outputFormat == "" {
- outputFormat = DEFAULT_BACKUP_FORMAT
- }
- if options.Quiet {
- outputFormat = "{{.Status}}"
- }
-
data := make([]BackupOutput, 1)
data[0].Chunks = h.chunks
data[0].Bytes = h.bytes
data[0].Status = h.status
data[0].Checksum = h.GetChecksum()
- result := CommandResult{
- Format: format.Format(outputFormat),
- OutputAs: toOutputType(options.OutputAs),
- Data: data,
- }
-
- GenerateOutput(&result)
+ FormatAndGenerateOutput(&options.OutputOptions, DEFAULT_BACKUP_FORMAT, "{{.Status}}", data)
return nil
}
@@ -235,14 +220,6 @@
// STEP 5: Show results
- outputFormat := CharReplacer.Replace(options.Format)
- if outputFormat == "" {
- outputFormat = DEFAULT_BACKUP_FORMAT
- }
- if options.Quiet {
- outputFormat = "{{.Status}}"
- }
-
data := make([]BackupOutput, 1)
data[0].Checksum = upload_result.GetFieldByName("checksum").(string)
data[0].Chunks = int(upload_result.GetFieldByName("chunks_received").(int32))
@@ -254,13 +231,7 @@
data[0].Status = "FAILURE"
}
- result := CommandResult{
- Format: format.Format(outputFormat),
- OutputAs: toOutputType(options.OutputAs),
- Data: data,
- }
-
- GenerateOutput(&result)
+ FormatAndGenerateOutput(&options.OutputOptions, DEFAULT_BACKUP_FORMAT, "{{.Status}}", data)
return nil
}
diff --git a/commands/command.go b/commands/command.go
index 6a8d663..53f1d8a 100644
--- a/commands/command.go
+++ b/commands/command.go
@@ -80,6 +80,7 @@
Cert string `long:"tlscert" value-name:"CERT_FILE" description:"Path to TLS vertificate file"`
Key string `long:"tlskey" value-name:"KEY_FILE" description:"Path to TLS key file"`
Verify bool `long:"tlsverify" description:"Use TLS and verify the remote"`
+ Yes bool `short:"y" long:"yes" description:"answer yes to any confirmation prompts"`
}
type OutputOptions struct {
@@ -143,6 +144,7 @@
GlobalConfig.Password = GlobalOptions.Password
}
+ // Generate error messages for required settings
if GlobalConfig.Server == "" {
log.Fatal("Server is not set. Please update config file or use the -s option")
}
@@ -179,3 +181,22 @@
}
}
}
+
+// Applies common output options to format and generate output
+func FormatAndGenerateOutput(options *OutputOptions, default_format string, quiet_format string, data interface{}) {
+ outputFormat := CharReplacer.Replace(options.Format)
+ if outputFormat == "" {
+ outputFormat = default_format
+ }
+ if (options.Quiet) && (quiet_format != "") {
+ outputFormat = quiet_format
+ }
+
+ result := CommandResult{
+ Format: format.Format(outputFormat),
+ OutputAs: toOutputType(options.OutputAs),
+ Data: data,
+ }
+
+ GenerateOutput(&result)
+}
diff --git a/commands/common.go b/commands/common.go
index 9461f99..93c1b5d 100644
--- a/commands/common.go
+++ b/commands/common.go
@@ -16,6 +16,7 @@
package commands
import (
+ "bufio"
b64 "encoding/base64"
"fmt"
"github.com/fullstorydev/grpcurl"
@@ -23,6 +24,9 @@
"golang.org/x/net/context"
"google.golang.org/grpc"
reflectpb "google.golang.org/grpc/reflection/grpc_reflection_v1alpha"
+ "log"
+ "os"
+ "strings"
)
func GenerateHeaders() []string {
@@ -62,3 +66,30 @@
fmt.Printf(format, args...)
}
}
+
+// Print a confirmation prompt and get a response from the user
+func Confirmf(format string, args ...interface{}) bool {
+ if GlobalOptions.Yes {
+ return true
+ }
+
+ reader := bufio.NewReader(os.Stdin)
+
+ for {
+ msg := fmt.Sprintf(format, args...)
+ fmt.Print(msg)
+
+ response, err := reader.ReadString('\n')
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ response = strings.ToLower(strings.TrimSpace(response))
+
+ if response == "y" || response == "yes" {
+ return true
+ } else if response == "n" || response == "no" {
+ return false
+ }
+ }
+}
diff --git a/commands/handler.go b/commands/handler.go
index 8aadb44..678883d 100644
--- a/commands/handler.go
+++ b/commands/handler.go
@@ -69,7 +69,15 @@
}
for k, v := range fields {
- dmsg.TrySetFieldByName(k, v)
+ // _json is a special field name that indicates we should unmarshal json data
+ if k == "_json" {
+ err = dmsg.UnmarshalMergeJSON(v.([]byte))
+ if err != nil {
+ return err
+ }
+ } else {
+ dmsg.SetFieldByName(k, v)
+ }
}
delete(h.Fields, dmsg.XXX_MessageName())
diff --git a/commands/models.go b/commands/models.go
index 16e4dc4..26e9d9f 100644
--- a/commands/models.go
+++ b/commands/models.go
@@ -20,15 +20,9 @@
"fmt"
flags "github.com/jessevdk/go-flags"
"github.com/jhump/protoreflect/dynamic"
- "github.com/opencord/cordctl/format"
- "sort"
"strings"
)
-const (
- DEFAULT_MODEL_AVAILABLE_FORMAT = "{{ . }}"
-)
-
type ModelNameString string
type ModelList struct {
@@ -42,13 +36,22 @@
} `positional-args:"yes" required:"yes"`
}
-type ModelAvailable struct {
+type ModelUpdate struct {
OutputOptions
+ Filter string `long:"filter" description:"Comma-separated list of filters"`
+ SetFields string `long:"set-field" description:"Comma-separated list of field=value to set"`
+ SetJSON string `long:"set-json" description:"JSON dictionary to use for settings fields"`
+ Args struct {
+ ModelName ModelNameString
+ } `positional-args:"yes" required:"yes"`
+ IDArgs struct {
+ ID []int32
+ } `positional-args:"yes" required:"no"`
}
type ModelOpts struct {
- List ModelList `command:"list"`
- Available ModelAvailable `command:"available"`
+ List ModelList `command:"list"`
+ Update ModelUpdate `command:"update"`
}
var modelOpts = ModelOpts{}
@@ -57,42 +60,6 @@
parser.AddCommand("model", "model commands", "Commands to query and manipulate XOS models", &modelOpts)
}
-func (options *ModelAvailable) Execute(args []string) error {
- conn, descriptor, err := InitReflectionClient()
- if err != nil {
- return err
- }
-
- defer conn.Close()
-
- models, err := GetModelNames(descriptor)
- if err != nil {
- return err
- }
-
- model_names := []string{}
- for k := range models {
- model_names = append(model_names, k)
- }
-
- sort.Strings(model_names)
-
- outputFormat := CharReplacer.Replace(options.Format)
- if outputFormat == "" {
- outputFormat = DEFAULT_MODEL_AVAILABLE_FORMAT
- }
-
- result := CommandResult{
- Format: format.Format(outputFormat),
- OutputAs: toOutputType(options.OutputAs),
- Data: model_names,
- }
-
- GenerateOutput(&result)
-
- return nil
-}
-
func (options *ModelList) Execute(args []string) error {
conn, descriptor, err := InitReflectionClient()
if err != nil {
@@ -106,18 +73,12 @@
return err
}
- var models []*dynamic.Message
-
- queries, err := CommaSeparatedQueryToMap(options.Filter)
+ queries, err := CommaSeparatedQueryToMap(options.Filter, true)
if err != nil {
return err
}
- if len(queries) == 0 {
- models, err = ListModels(conn, descriptor, string(options.Args.ModelName))
- } else {
- models, err = FilterModels(conn, descriptor, string(options.Args.ModelName), queries)
- }
+ models, err := ListOrFilterModels(conn, descriptor, string(options.Args.ModelName), queries)
if err != nil {
return err
}
@@ -167,21 +128,89 @@
}
}
- outputFormat := CharReplacer.Replace(options.Format)
- if outputFormat == "" {
- outputFormat = default_format.String()
- }
- if options.Quiet {
- outputFormat = "{{.Id}}"
+ FormatAndGenerateOutput(&options.OutputOptions, default_format.String(), "{{.id}}", data)
+
+ return nil
+}
+
+func (options *ModelUpdate) Execute(args []string) error {
+ conn, descriptor, err := InitReflectionClient()
+ if err != nil {
+ return err
}
- result := CommandResult{
- Format: format.Format(outputFormat),
- OutputAs: toOutputType(options.OutputAs),
- Data: data,
+ defer conn.Close()
+
+ err = CheckModelName(descriptor, string(options.Args.ModelName))
+ if err != nil {
+ return err
}
- GenerateOutput(&result)
+ if (len(options.IDArgs.ID) == 0 && len(options.Filter) == 0) ||
+ (len(options.IDArgs.ID) != 0 && len(options.Filter) != 0) {
+ return fmt.Errorf("Use either an ID or a --filter to specify which models to update")
+ }
+
+ queries, err := CommaSeparatedQueryToMap(options.Filter, true)
+ if err != nil {
+ return err
+ }
+
+ updates, err := CommaSeparatedQueryToMap(options.SetFields, true)
+ if err != nil {
+ return err
+ }
+
+ modelName := string(options.Args.ModelName)
+
+ var models []*dynamic.Message
+
+ if len(options.IDArgs.ID) > 0 {
+ models = make([]*dynamic.Message, len(options.IDArgs.ID))
+ for i, id := range options.IDArgs.ID {
+ models[i], err = GetModel(conn, descriptor, modelName, id)
+ if err != nil {
+ return err
+ }
+ }
+ } else {
+ models, err = ListOrFilterModels(conn, descriptor, modelName, queries)
+ if err != nil {
+ return err
+ }
+ }
+
+ if len(models) == 0 {
+ return fmt.Errorf("Filter matches no objects")
+ } else if len(models) > 1 {
+ if !Confirmf("Filter matches %d objects. Continue [y/n] ? ", len(models)) {
+ return fmt.Errorf("Aborted by user")
+ }
+ }
+
+ fields := make(map[string]interface{})
+
+ if len(options.SetJSON) > 0 {
+ fields["_json"] = []byte(options.SetJSON)
+ }
+
+ for fieldName, value := range updates {
+ value = value[1:]
+ proto_value, err := TypeConvert(descriptor, modelName, fieldName, value)
+ if err != nil {
+ return err
+ }
+ fields[fieldName] = proto_value
+ }
+
+ for _, model := range models {
+ fields["id"] = model.GetFieldByName("id").(int32)
+ UpdateModel(conn, descriptor, modelName, fields)
+ }
+
+ count := len(models)
+ FormatAndGenerateOutput(&options.OutputOptions, "{{.}} models updated.", "{{.}}", count)
+
return nil
}
diff --git a/commands/modeltypes.go b/commands/modeltypes.go
new file mode 100644
index 0000000..be2294c
--- /dev/null
+++ b/commands/modeltypes.go
@@ -0,0 +1,65 @@
+/*
+ * Portions copyright 2019-present Open Networking Foundation
+ * Original copyright 2019-present Ciena Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package commands
+
+import (
+ flags "github.com/jessevdk/go-flags"
+ "sort"
+)
+
+const (
+ DEFAULT_MODELTYPE_LIST_FORMAT = "{{ . }}"
+)
+
+type ModelTypeList struct {
+ OutputOptions
+}
+
+type ModelTypeOpts struct {
+ List ModelTypeList `command:"list"`
+}
+
+var modelTypeOpts = ModelTypeOpts{}
+
+func RegisterModelTypeCommands(parser *flags.Parser) {
+ parser.AddCommand("modeltype", "model type commands", "Commands to query the types of models", &modelTypeOpts)
+}
+
+func (options *ModelTypeList) Execute(args []string) error {
+ conn, descriptor, err := InitReflectionClient()
+ if err != nil {
+ return err
+ }
+
+ defer conn.Close()
+
+ models, err := GetModelNames(descriptor)
+ if err != nil {
+ return err
+ }
+
+ model_names := []string{}
+ for k := range models {
+ model_names = append(model_names, k)
+ }
+
+ sort.Strings(model_names)
+
+ FormatAndGenerateOutput(&options.OutputOptions, DEFAULT_MODELTYPE_LIST_FORMAT, "", model_names)
+
+ return nil
+}
diff --git a/commands/orm.go b/commands/orm.go
index 8a4108e..65304a2 100644
--- a/commands/orm.go
+++ b/commands/orm.go
@@ -51,21 +51,21 @@
// "==foo" --> "EQUAL", "foo"
func DecodeOperator(query string) (string, string, bool, error) {
if strings.HasPrefix(query, "!=") {
- return query[2:], "EQUAL", true, nil
+ return strings.TrimSpace(query[2:]), "EQUAL", true, nil
} else if strings.HasPrefix(query, "==") {
return "", "", false, errors.New("Operator == is now allowed. Suggest using = instead.")
} else if strings.HasPrefix(query, "=") {
- return query[1:], "EQUAL", false, nil
+ return strings.TrimSpace(query[1:]), "EQUAL", false, nil
} else if strings.HasPrefix(query, ">=") {
- return query[2:], "GREATER_THAN_OR_EQUAL", false, nil
+ return strings.TrimSpace(query[2:]), "GREATER_THAN_OR_EQUAL", false, nil
} else if strings.HasPrefix(query, ">") {
- return query[1:], "GREATER_THAN", false, nil
+ return strings.TrimSpace(query[1:]), "GREATER_THAN", false, nil
} else if strings.HasPrefix(query, "<=") {
- return query[2:], "LESS_THAN_OR_EQUAL", false, nil
+ return strings.TrimSpace(query[2:]), "LESS_THAN_OR_EQUAL", false, nil
} else if strings.HasPrefix(query, "<") {
- return query[1:], "LESS_THAN", false, nil
+ return strings.TrimSpace(query[1:]), "LESS_THAN", false, nil
} else {
- return query, "EQUAL", false, nil
+ return strings.TrimSpace(query), "EQUAL", false, nil
}
}
@@ -95,15 +95,32 @@
nm := dynamic.NewMessage(elements_mt)
- field_type := h.Model.FindFieldByName(field_name).GetType()
- if field_type == descriptor.FieldDescriptorProto_TYPE_INT32 {
- i, _ := strconv.ParseInt(value, 10, 32)
+ field_descriptor := h.Model.FindFieldByName(field_name)
+ if field_descriptor == nil {
+ return fmt.Errorf("Field %s does not exist", field_name)
+ }
+
+ field_type := field_descriptor.GetType()
+ switch field_type {
+ case descriptor.FieldDescriptorProto_TYPE_INT32:
+ var i int64
+ i, err = strconv.ParseInt(value, 10, 32)
nm.SetFieldByName("iValue", int32(i))
- } else if field_type == descriptor.FieldDescriptorProto_TYPE_UINT32 {
- i, _ := strconv.ParseInt(value, 10, 32)
+ case descriptor.FieldDescriptorProto_TYPE_UINT32:
+ var i int64
+ i, err = strconv.ParseInt(value, 10, 32)
nm.SetFieldByName("iValue", uint32(i))
- } else {
+ case descriptor.FieldDescriptorProto_TYPE_FLOAT:
+ err = errors.New("Floating point filters are unsupported")
+ case descriptor.FieldDescriptorProto_TYPE_DOUBLE:
+ err = errors.New("Floating point filters are unsupported")
+ default:
nm.SetFieldByName("sValue", value)
+ err = nil
+ }
+
+ if err != nil {
+ return err
}
nm.SetFieldByName("name", field_name)
@@ -120,33 +137,83 @@
}
// Take a string list of queries and turns it into a map of queries
-func QueryStringsToMap(query_args []string) (map[string]string, error) {
+func QueryStringsToMap(query_args []string, allow_inequality bool) (map[string]string, error) {
queries := make(map[string]string)
for _, query_str := range query_args {
- query_str := strings.Trim(query_str, " ")
+ query_str := strings.TrimSpace(query_str)
operator_pos := -1
for i, ch := range query_str {
- if (ch == '!') || (ch == '=') || (ch == '>') || (ch == '<') {
- operator_pos = i
- break
+ if allow_inequality {
+ if (ch == '!') || (ch == '=') || (ch == '>') || (ch == '<') {
+ operator_pos = i
+ break
+ }
+ } else {
+ if ch == '=' {
+ operator_pos = i
+ break
+ }
}
}
if operator_pos == -1 {
- return nil, fmt.Errorf("Illegal query string %s", query_str)
+ return nil, fmt.Errorf("Illegal operator/value string %s", query_str)
}
- queries[query_str[:operator_pos]] = query_str[operator_pos:]
+ queries[strings.TrimSpace(query_str[:operator_pos])] = query_str[operator_pos:]
}
return queries, nil
}
// Take a string of comma-separated queries and turn it into a map of queries
-func CommaSeparatedQueryToMap(query_str string) (map[string]string, error) {
+func CommaSeparatedQueryToMap(query_str string, allow_inequality bool) (map[string]string, error) {
if query_str == "" {
return nil, nil
}
query_strings := strings.Split(query_str, ",")
- return QueryStringsToMap(query_strings)
+ return QueryStringsToMap(query_strings, allow_inequality)
+}
+
+// Convert a string into the appropriate gRPC type for a given field
+func TypeConvert(source grpcurl.DescriptorSource, modelName string, field_name string, v string) (interface{}, error) {
+ model_descriptor, err := source.FindSymbol("xos." + modelName)
+ if err != nil {
+ return nil, err
+ }
+ model_md, ok := model_descriptor.(*desc.MessageDescriptor)
+ if !ok {
+ return nil, fmt.Errorf("Failed to convert model %s to a messagedescriptor", modelName)
+ }
+ field_descriptor := model_md.FindFieldByName(field_name)
+ if field_descriptor == nil {
+ return nil, fmt.Errorf("Field %s does not exist in model %s", field_name, modelName)
+ }
+ field_type := field_descriptor.GetType()
+
+ var result interface{}
+
+ switch field_type {
+ case descriptor.FieldDescriptorProto_TYPE_INT32:
+ var i int64
+ i, err = strconv.ParseInt(v, 10, 32)
+ result = int32(i)
+ case descriptor.FieldDescriptorProto_TYPE_UINT32:
+ var i int64
+ i, err = strconv.ParseInt(v, 10, 32)
+ result = uint32(i)
+ case descriptor.FieldDescriptorProto_TYPE_FLOAT:
+ var f float64
+ f, err = strconv.ParseFloat(v, 32)
+ result = float32(f)
+ case descriptor.FieldDescriptorProto_TYPE_DOUBLE:
+ var f float64
+ f, err = strconv.ParseFloat(v, 64)
+ result = f
+ default:
+ result = v
+ err = nil
+ }
+
+ return result, err
}
// Return a list of all available model names
@@ -211,6 +278,34 @@
return nil
}
+// Update a model in XOS given a map of fields
+func UpdateModel(conn *grpc.ClientConn, descriptor grpcurl.DescriptorSource, modelName string, fields map[string]interface{}) error {
+ ctx, cancel := context.WithTimeout(context.Background(), GlobalConfig.Grpc.Timeout)
+ defer cancel()
+
+ headers := GenerateHeaders()
+
+ h := &RpcEventHandler{
+ Fields: map[string]map[string]interface{}{"xos." + modelName: fields},
+ }
+ err := grpcurl.InvokeRPC(ctx, descriptor, conn, "xos.xos.Update"+modelName, headers, h, h.GetParams)
+ if err != nil {
+ return err
+ } else if h.Status != nil && h.Status.Err() != nil {
+ return h.Status.Err()
+ }
+
+ resp, err := dynamic.AsDynamicMessage(h.Response)
+ if err != nil {
+ return err
+ }
+
+ // TODO: Do we need to do anything with the response?
+ _ = resp
+
+ return nil
+}
+
// Get a model from XOS given its ID
func GetModel(conn *grpc.ClientConn, descriptor grpcurl.DescriptorSource, modelName string, id int32) (*dynamic.Message, error) {
ctx, cancel := context.WithTimeout(context.Background(), GlobalConfig.Grpc.Timeout)
@@ -385,6 +480,15 @@
return ItemsToDynamicMessageList(items), nil
}
+// Call ListModels or FilterModels as appropriate
+func ListOrFilterModels(conn *grpc.ClientConn, descriptor grpcurl.DescriptorSource, modelName string, queries map[string]string) ([]*dynamic.Message, error) {
+ if len(queries) == 0 {
+ return ListModels(conn, descriptor, modelName)
+ } else {
+ return FilterModels(conn, descriptor, modelName, queries)
+ }
+}
+
// Get a model from XOS given a fieldName/fieldValue
func FindModel(conn *grpc.ClientConn, descriptor grpcurl.DescriptorSource, modelName string, queries map[string]string) (*dynamic.Message, error) {
models, err := FilterModels(conn, descriptor, modelName, queries)
diff --git a/commands/services.go b/commands/services.go
index a38c469..99d39c0 100644
--- a/commands/services.go
+++ b/commands/services.go
@@ -21,7 +21,7 @@
"github.com/fullstorydev/grpcurl"
flags "github.com/jessevdk/go-flags"
"github.com/jhump/protoreflect/dynamic"
- "github.com/opencord/cordctl/format"
+ //"github.com/opencord/cordctl/format"
)
const (
@@ -86,14 +86,6 @@
return err
}
- outputFormat := CharReplacer.Replace(options.Format)
- if outputFormat == "" {
- outputFormat = DEFAULT_SERVICE_FORMAT
- }
- if options.Quiet {
- outputFormat = "{{.Id}}"
- }
-
data := make([]ServiceListOutput, len(items.([]interface{})))
for i, item := range items.([]interface{}) {
@@ -103,12 +95,7 @@
data[i].State = val.GetFieldByName("state").(string)
}
- result := CommandResult{
- Format: format.Format(outputFormat),
- OutputAs: toOutputType(options.OutputAs),
- Data: data,
- }
+ FormatAndGenerateOutput(&options.OutputOptions, DEFAULT_SERVICE_FORMAT, "{{.Name}}", data)
- GenerateOutput(&result)
return nil
}
diff --git a/commands/transfer.go b/commands/transfer.go
index 819e82f..699a4f7 100644
--- a/commands/transfer.go
+++ b/commands/transfer.go
@@ -20,7 +20,6 @@
"errors"
"fmt"
flags "github.com/jessevdk/go-flags"
- "github.com/opencord/cordctl/format"
"strings"
)
@@ -92,27 +91,13 @@
upload_result.GetFieldByName("checksum").(string))
}
- outputFormat := CharReplacer.Replace(options.Format)
- if outputFormat == "" {
- outputFormat = DEFAULT_TRANSFER_FORMAT
- }
- if options.Quiet {
- outputFormat = "{{.Status}}"
- }
-
data := make([]TransferOutput, 1)
data[0].Checksum = upload_result.GetFieldByName("checksum").(string)
data[0].Chunks = int(upload_result.GetFieldByName("chunks_received").(int32))
data[0].Bytes = int(upload_result.GetFieldByName("bytes_received").(int32))
data[0].Status = GetEnumValue(upload_result, "status")
- result := CommandResult{
- Format: format.Format(outputFormat),
- OutputAs: toOutputType(options.OutputAs),
- Data: data,
- }
-
- GenerateOutput(&result)
+ FormatAndGenerateOutput(&options.OutputOptions, DEFAULT_TRANSFER_FORMAT, "{{.Status}}", data)
return nil
}
@@ -144,26 +129,13 @@
return err
}
- outputFormat := CharReplacer.Replace(options.Format)
- if outputFormat == "" {
- outputFormat = DEFAULT_TRANSFER_FORMAT
- }
- if options.Quiet {
- outputFormat = "{{.Status}}"
- }
-
data := make([]TransferOutput, 1)
data[0].Chunks = h.chunks
data[0].Bytes = h.bytes
data[0].Status = h.status
+ data[0].Checksum = h.GetChecksum()
- result := CommandResult{
- Format: format.Format(outputFormat),
- OutputAs: toOutputType(options.OutputAs),
- Data: data,
- }
-
- GenerateOutput(&result)
+ FormatAndGenerateOutput(&options.OutputOptions, DEFAULT_TRANSFER_FORMAT, "{{.Status}}", data)
return nil
}