This is an automated email from the ASF dual-hosted git repository. rohit pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/cloudstack-cloudmonkey.git
The following commit(s) were added to refs/heads/master by this push: new 6a00de5 output: implement and refactor json, csv, table, text outputs 6a00de5 is described below commit 6a00de565e42dd532890eff19888f351e7ac3c40 Author: Rohit Yadav <ro...@apache.org> AuthorDate: Tue Jun 26 16:41:26 2018 +0530 output: implement and refactor json, csv, table, text outputs Signed-off-by: Rohit Yadav <ro...@apache.org> --- Makefile | 2 +- NOTICE | 5 -- cmd/api.go | 88 -------------------------- cmd/command.go | 10 +-- cmd/output.go | 187 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ cmd/version.go | 1 + config/cache.go | 7 ++- config/config.go | 5 +- 8 files changed, 201 insertions(+), 104 deletions(-) diff --git a/Makefile b/Makefile index 517c231..d19ba76 100644 --- a/Makefile +++ b/Makefile @@ -52,7 +52,7 @@ run: all debug: $(GO) build -gcflags='-N -l' -o cmk cmk.go && dlv --listen=:2345 --headless=true --api-version=2 exec ./cmk -dist: +dist: all cd $(BASE) rm -fr dist mkdir -p dist diff --git a/NOTICE b/NOTICE deleted file mode 100644 index 2fcb349..0000000 --- a/NOTICE +++ /dev/null @@ -1,5 +0,0 @@ -Apache CloudStack CloudMonkey -Copyright 2018 The Apache Software Foundation - -This product includes software developed at -The Apache Software Foundation (http://www.apache.org/). diff --git a/cmd/api.go b/cmd/api.go index 1100f55..0c3b7fd 100644 --- a/cmd/api.go +++ b/cmd/api.go @@ -18,16 +18,9 @@ package cmd import ( - "encoding/json" "errors" "fmt" - "github.com/olekukonko/tablewriter" - "os" - "reflect" - "sort" "strings" - - "cmk/config" ) var apiCommand *Command @@ -37,87 +30,6 @@ func GetAPIHandler() *Command { return apiCommand } -func printText(itemMap map[string]interface{}) { - for k, v := range itemMap { - valueType := reflect.TypeOf(v) - if valueType.Kind() == reflect.Slice { - fmt.Printf("%s:\n", k) - for _, item := range v.([]interface{}) { - row, isMap := item.(map[string]interface{}) - if isMap { - for field, value := range row { - fmt.Printf("%s = %v\n", field, value) - } - } else { - fmt.Printf("%v\n", item) - } - fmt.Println("================================================================================") - } - } else { - fmt.Printf("%s = %v\n", k, v) - } - } -} - -func printResult(outputType string, response map[string]interface{}, filter []string) { - switch outputType { - case config.TABLE: - table := tablewriter.NewWriter(os.Stdout) - for k, v := range response { - valueType := reflect.TypeOf(v) - if valueType.Kind() == reflect.Slice { - items, ok := v.([]interface{}) - if !ok { - continue - } - fmt.Printf("%s:\n", k) - var header []string - for _, item := range items { - row, ok := item.(map[string]interface{}) - if !ok || len(row) < 1 { - continue - } - - if len(header) == 0 { - if filter != nil && len(filter) > 0 { - for _, filterKey := range filter { - for field := range row { - if filterKey == field { - header = append(header, field) - } - } - } - } else { - for field := range row { - header = append(header, field) - } - sort.Strings(header) - } - table.SetHeader(header) - } - var rowArray []string - for _, field := range header { - rowArray = append(rowArray, fmt.Sprintf("%v", row[field])) - } - table.Append(rowArray) - } - } else { - fmt.Printf("%s = %v\n", k, v) - } - } - table.Render() - case config.TEXT: - case config.DEFAULT: - printText(response) - case config.CSV: - fmt.Println("FIXME: implement CSV output") - fmt.Println(response) - default: - jsonOutput, _ := json.MarshalIndent(response, "", " ") - fmt.Println(string(jsonOutput)) - } -} - func init() { apiCommand = &Command{ Name: "api", diff --git a/cmd/command.go b/cmd/command.go index 7d5a2fe..179dd74 100644 --- a/cmd/command.go +++ b/cmd/command.go @@ -58,12 +58,12 @@ func PrintUsage() { for _, cmd := range commands { commandHelp += fmt.Sprintf("%s\t\t%s\n", cmd.Name, cmd.Help) } - fmt.Printf(`usage: cmk [options] [commands] + fmt.Printf(`Usage: cmk [options] [commands] -Command Line Interface for Apache CloudStack +CloudMonkey (cmk) 🐵 is a command line interface for Apache CloudStack. -default commands: +Default commands: %s - -Try cmk [help]`, commandHelp) +Try cmk [help] or cmk [action api] -h +`, commandHelp) } diff --git a/cmd/output.go b/cmd/output.go new file mode 100644 index 0000000..4a13286 --- /dev/null +++ b/cmd/output.go @@ -0,0 +1,187 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you 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 cmd + +import ( + "cmk/config" + "encoding/json" + "fmt" + "github.com/olekukonko/tablewriter" + "os" + "reflect" + "sort" + "strings" +) + +func printJson(response map[string]interface{}) { + jsonOutput, err := json.MarshalIndent(response, "", " ") + if err != nil { + fmt.Println("Error during json marshalling:", err.Error()) + } + fmt.Println(string(jsonOutput)) +} + +func printTable(response map[string]interface{}) { + table := tablewriter.NewWriter(os.Stdout) + for k, v := range response { + valueType := reflect.TypeOf(v) + if valueType.Kind() == reflect.Slice { + items, ok := v.([]interface{}) + if !ok { + continue + } + fmt.Printf("%s:\n", k) + var header []string + for _, item := range items { + row, ok := item.(map[string]interface{}) + if !ok || len(row) < 1 { + continue + } + + if len(header) == 0 { + for field := range row { + header = append(header, field) + } + sort.Strings(header) + table.SetHeader(header) + } + var rowArray []string + for _, field := range header { + rowArray = append(rowArray, fmt.Sprintf("%v", row[field])) + } + table.Append(rowArray) + } + } else { + fmt.Printf("%s = %v\n", k, v) + } + } + table.Render() +} + +func printText(response map[string]interface{}) { + for k, v := range response { + valueType := reflect.TypeOf(v) + if valueType.Kind() == reflect.Slice { + fmt.Printf("%s:\n", k) + for idx, item := range v.([]interface{}) { + if idx > 0 { + fmt.Println("================================================================================") + } + row, isMap := item.(map[string]interface{}) + if isMap { + for field, value := range row { + fmt.Printf("%s = %v\n", field, value) + } + } else { + fmt.Printf("%v\n", item) + } + } + } else { + fmt.Printf("%s = %v\n", k, v) + } + } +} + +func printCsv(response map[string]interface{}) { + for _, v := range response { + valueType := reflect.TypeOf(v) + if valueType.Kind() == reflect.Slice || valueType.Kind() == reflect.Map { + items, ok := v.([]interface{}) + if !ok { + continue + } + var header []string + for idx, item := range items { + row, ok := item.(map[string]interface{}) + if !ok || len(row) < 1 { + continue + } + + if idx == 0 { + for rk, _ := range row { + header = append(header, rk) + } + sort.Strings(header) + fmt.Println(strings.Join(header, ",")) + } + var values []string + for _, key := range header { + values = append(values, fmt.Sprintf("%v", row[key])) + } + fmt.Println(strings.Join(values, ",")) + } + + } + } +} + +func filterResponse(response map[string]interface{}, filter []string) map[string]interface{} { + if filter == nil || len(filter) == 0 { + return response + } + filteredResponse := make(map[string]interface{}) + for k, v := range response { + valueType := reflect.TypeOf(v) + if valueType.Kind() == reflect.Slice || valueType.Kind() == reflect.Map { + items, ok := v.([]interface{}) + if !ok { + continue + } + var filteredRows []interface{} + for _, item := range items { + row, ok := item.(map[string]interface{}) + if !ok || len(row) < 1 { + continue + } + filteredRow := make(map[string]interface{}) + for _, filterKey := range filter { + for field := range row { + if filterKey == field { + filteredRow[field] = row[field] + } + } + } + filteredRows = append(filteredRows, filteredRow) + } + filteredResponse[k] = filteredRows + } else { + filteredResponse[k] = v + continue + } + + } + return filteredResponse +} + +func printResult(outputType string, response map[string]interface{}, filter []string) { + response = filterResponse(response, filter) + switch outputType { + case config.JSON: + printJson(response) + case config.TABLE: + printTable(response) + case config.TEXT: + printText(response) + case config.CSV: + printCsv(response) + case config.XML: + fmt.Println("Unfinished output format: xml, use something else") + default: + fmt.Println("Invalid output type configured, please fix that!") + } +} diff --git a/cmd/version.go b/cmd/version.go index c07c1d7..09f48b8 100644 --- a/cmd/version.go +++ b/cmd/version.go @@ -25,6 +25,7 @@ func init() { Help: "Version info", Handle: func(r *Request) error { fmt.Println(r.Config.Name(), r.Config.Version()) + fmt.Println("Copyright 2018 The Apache Software Foundation (http://www.apache.org/)") return nil }, }) diff --git a/config/cache.go b/config/cache.go index 216cf02..2037ff7 100644 --- a/config/cache.go +++ b/config/cache.go @@ -82,7 +82,9 @@ func (c *Config) GetCache() map[string]*API { func LoadCache(c *Config) { cache, err := ioutil.ReadFile(c.CacheFile) if err != nil { - fmt.Fprintf(os.Stderr, "Loaded in-built API cache. Failed to read API cache, please run 'sync'.\n") + if c.HasShell { + fmt.Fprintf(os.Stderr, "Loaded in-built API cache. Failed to read API cache, please run 'sync'.\n") + } cache = []byte(preCache) } var data map[string]interface{} @@ -156,6 +158,9 @@ func (c *Config) UpdateCache(response map[string]interface{}) interface{} { var responseKeys []string for _, respNode := range api["response"].([]interface{}) { if resp, ok := respNode.(map[string]interface{}); ok { + if resp == nil || resp["name"] == nil { + continue + } responseKeys = append(responseKeys, fmt.Sprintf("%v,", resp["name"])) } } diff --git a/config/config.go b/config/config.go index 7d6fc09..ccbd23a 100644 --- a/config/config.go +++ b/config/config.go @@ -36,10 +36,7 @@ const ( JSON = "json" TABLE = "table" TEXT = "text" - - // Old formats existing for some backward compatibilities - DEFAULT = "default" // This is same as 'text' - XML = "xml" + XML = "xml" ) // ServerProfile describes a management server