mirror of
https://hubproxy.babadafafafafa.cn/https://github.com/yunionio/cloudpods.git
synced 2026-09-20 08:03:53 +08:00
326 lines
8.7 KiB
Go
326 lines
8.7 KiB
Go
// Copyright 2019 Yunion
|
|
//
|
|
// 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 printutils
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"sort"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"yunion.io/x/jsonutils"
|
|
"yunion.io/x/pkg/prettytable"
|
|
"yunion.io/x/pkg/util/sets"
|
|
)
|
|
|
|
type StatusInfo struct {
|
|
Status string `json:"status"`
|
|
TotalCount int64 `json:"total_count"`
|
|
PendingDeletedCount int64 `json:"pending_deleted_count"`
|
|
}
|
|
|
|
type StatusInfoList []StatusInfo
|
|
|
|
func (a StatusInfoList) Len() int { return len(a) }
|
|
func (a StatusInfoList) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
|
func (a StatusInfoList) Less(i, j int) bool {
|
|
if a[i].TotalCount != a[j].TotalCount {
|
|
return a[i].TotalCount > a[j].TotalCount
|
|
}
|
|
if a[i].PendingDeletedCount != a[j].PendingDeletedCount {
|
|
return a[i].PendingDeletedCount > a[j].PendingDeletedCount
|
|
}
|
|
return a[i].Status < a[j].Status
|
|
}
|
|
|
|
type TotalCountWithStatusInfo struct {
|
|
StatusInfo StatusInfoList `json:"status_info"`
|
|
}
|
|
|
|
func PrintJSONList(list *ListResult, columns []string) {
|
|
colsWithData := make([]string, 0)
|
|
if len(columns) == 0 {
|
|
colsWithDataMap := make(map[string]bool)
|
|
for _, obj := range list.Data {
|
|
objdict, _ := obj.(*jsonutils.JSONDict)
|
|
objmap, _ := objdict.GetMap()
|
|
for k := range objmap {
|
|
colsWithDataMap[k] = true
|
|
}
|
|
}
|
|
prefixCols := []string{}
|
|
for k := range colsWithDataMap {
|
|
if sets.NewString("id", "name").Has(strings.ToLower(k)) {
|
|
prefixCols = append(prefixCols, k)
|
|
} else {
|
|
colsWithData = append(colsWithData, k)
|
|
}
|
|
}
|
|
sort.Strings(prefixCols)
|
|
sort.Strings(colsWithData)
|
|
colsWithData = append(prefixCols, colsWithData...)
|
|
} else {
|
|
colsWithDataMap := make(map[string]bool)
|
|
for _, obj := range list.Data {
|
|
for _, k := range columns {
|
|
if _, ok := colsWithDataMap[k]; !ok {
|
|
_, e := obj.GetIgnoreCases(k)
|
|
if e == nil {
|
|
colsWithDataMap[k] = true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
for _, k := range columns {
|
|
if _, ok := colsWithDataMap[k]; ok {
|
|
colsWithData = append(colsWithData, k)
|
|
}
|
|
}
|
|
}
|
|
|
|
const (
|
|
OS_MAX_COLUMN_TEXT_LENGTH = "OS_MAX_COLUMN_TEXT_LENGTH"
|
|
OS_TRY_TERM_WIDTH = "OS_TRY_TERM_WIDTH"
|
|
|
|
defaultMaxColLength = 512
|
|
)
|
|
maxColLength := int64(-1)
|
|
colTruncated := false
|
|
tryTermWidth := false
|
|
screenWidth, _ := termWidth()
|
|
if screenWidth > 0 {
|
|
// the width of screen is available, which is an interactive shell
|
|
// truncate the text to make it prettier
|
|
osMaxTextLength := os.Getenv(OS_MAX_COLUMN_TEXT_LENGTH)
|
|
if len(osMaxTextLength) > 0 {
|
|
maxColLength, _ = strconv.ParseInt(osMaxTextLength, 10, 64)
|
|
if maxColLength >= 0 && maxColLength < 3 {
|
|
maxColLength = defaultMaxColLength
|
|
}
|
|
} else {
|
|
maxColLength = defaultMaxColLength
|
|
}
|
|
osTryTermWidth := os.Getenv(OS_TRY_TERM_WIDTH)
|
|
if strings.ToLower(osTryTermWidth) == "true" {
|
|
tryTermWidth = true
|
|
}
|
|
}
|
|
pt := prettytable.NewPrettyTableWithTryTermWidth(colsWithData, tryTermWidth)
|
|
rows := make([][]string, 0)
|
|
for _, obj := range list.Data {
|
|
row := make([]string, 0)
|
|
for _, k := range colsWithData {
|
|
v, e := obj.GetIgnoreCases(k)
|
|
if e == nil {
|
|
s, _ := v.GetString()
|
|
if maxColLength > 0 && int64(len(s)) > maxColLength {
|
|
s = s[0:maxColLength-3] + "..."
|
|
colTruncated = true
|
|
}
|
|
row = append(row, s)
|
|
} else {
|
|
row = append(row, "")
|
|
}
|
|
}
|
|
rows = append(rows, row)
|
|
}
|
|
fmt.Print(pt.GetString(rows))
|
|
total := int64(list.Total)
|
|
if list.Total == 0 {
|
|
total = int64(len(list.Data))
|
|
}
|
|
title := fmt.Sprintf("Total: %d", total)
|
|
if len(list.MarkerField) > 0 {
|
|
title += fmt.Sprintf(" Field: %s Order: %s", list.MarkerField, list.MarkerOrder)
|
|
if len(list.NextMarker) > 0 {
|
|
title += fmt.Sprintf(" NextMarker: %s", list.NextMarker)
|
|
}
|
|
} else {
|
|
if list.Limit == 0 && total > int64(len(list.Data)) {
|
|
list.Limit = len(list.Data)
|
|
}
|
|
if list.Limit > 0 {
|
|
pages := int(total / int64(list.Limit))
|
|
if int64(pages*list.Limit) < total {
|
|
pages += 1
|
|
}
|
|
page := int(list.Offset/list.Limit) + 1
|
|
title = fmt.Sprintf("%s Pages: %d Limit: %d Offset: %d Page: %d",
|
|
title, pages, list.Limit, list.Offset, page)
|
|
}
|
|
}
|
|
fmt.Println("*** ", title, " ***")
|
|
if list.Totals != nil {
|
|
totalWithStatusInfo := TotalCountWithStatusInfo{}
|
|
err := list.Totals.Unmarshal(&totalWithStatusInfo)
|
|
if err != nil {
|
|
fmt.Println("error to unmarshal totals to TotalCountWithStatusInfo", err)
|
|
} else if len(totalWithStatusInfo.StatusInfo) > 0 {
|
|
sort.Sort(totalWithStatusInfo.StatusInfo)
|
|
pt := prettytable.NewPrettyTableWithTryTermWidth([]string{"#", "Status", "Count", "PendingDeletedCount"}, tryTermWidth)
|
|
rows := make([][]string, 0)
|
|
for i, statusInfo := range totalWithStatusInfo.StatusInfo {
|
|
rows = append(rows, []string{fmt.Sprintf("#%d", i+1), statusInfo.Status, strconv.FormatInt(statusInfo.TotalCount, 10), strconv.FormatInt(statusInfo.PendingDeletedCount, 10)})
|
|
}
|
|
fmt.Print(pt.GetString(rows))
|
|
}
|
|
}
|
|
if colTruncated {
|
|
fmt.Println("!!!Some text truncated, set env", OS_MAX_COLUMN_TEXT_LENGTH, "=-1 to show full text!!!")
|
|
}
|
|
}
|
|
|
|
func printJSONObject(dict *jsonutils.JSONDict, cb PrintJSONObjectFunc) {
|
|
keys := dict.SortedKeys()
|
|
pt := prettytable.NewPrettyTable([]string{"Field", "Value"})
|
|
rows := make([][]string, 0)
|
|
for _, k := range keys {
|
|
row := make([]string, 0)
|
|
row = append(row, k)
|
|
vs, e := dict.GetString(k)
|
|
if e != nil {
|
|
row = append(row, fmt.Sprintf("Error: %s", e))
|
|
} else {
|
|
row = append(row, vs)
|
|
}
|
|
rows = append(rows, row)
|
|
}
|
|
cb(pt.GetString(rows))
|
|
}
|
|
|
|
func PrintJSONObject(obj jsonutils.JSONObject) {
|
|
switch jObj := obj.(type) {
|
|
case *jsonutils.JSONDict:
|
|
printJSONObject(jObj, func(s string) {
|
|
fmt.Print(s)
|
|
})
|
|
case *jsonutils.JSONArray:
|
|
printJSONArray(jObj)
|
|
default:
|
|
fmt.Println("Not a valid JSON object:", obj.String())
|
|
return
|
|
}
|
|
}
|
|
|
|
func printJSONArray(array *jsonutils.JSONArray) {
|
|
objs, err := array.GetArray()
|
|
if err != nil {
|
|
fmt.Printf("GetArray objects error: %v", err)
|
|
}
|
|
|
|
if len(objs) == 0 {
|
|
return
|
|
}
|
|
|
|
columns := sets.NewString()
|
|
|
|
for _, obj := range objs {
|
|
dict, ok := obj.(*jsonutils.JSONDict)
|
|
if !ok {
|
|
fmt.Printf("Object %q is not dict", obj)
|
|
return
|
|
}
|
|
jMap, err := dict.GetMap()
|
|
if err != nil {
|
|
fmt.Printf("GetMap error: %v, object: %q", err, obj)
|
|
return
|
|
}
|
|
for k := range jMap {
|
|
columns.Insert(k)
|
|
}
|
|
}
|
|
|
|
list := &ListResult{
|
|
Data: objs,
|
|
Total: array.Length(),
|
|
Limit: 0,
|
|
Offset: 0,
|
|
}
|
|
PrintJSONList(list, columns.List())
|
|
}
|
|
|
|
func flattenJSONObjectRecursive(v jsonutils.JSONObject, k string, rootDict *jsonutils.JSONDict) {
|
|
switch vv := v.(type) {
|
|
case *jsonutils.JSONString, *jsonutils.JSONInt, *jsonutils.JSONBool, *jsonutils.JSONFloat:
|
|
rootDict.Set(k, vv)
|
|
case *jsonutils.JSONArray:
|
|
arr, _ := vv.GetArray()
|
|
for i, arrElem := range arr {
|
|
nextK := fmt.Sprintf("%s.%d", k, i)
|
|
flattenJSONObjectRecursive(arrElem, nextK, rootDict)
|
|
}
|
|
if k != "" {
|
|
rootDict.Remove(k)
|
|
}
|
|
case *jsonutils.JSONDict:
|
|
m, _ := vv.GetMap()
|
|
for kk, w := range m {
|
|
nextK := kk
|
|
if k != "" {
|
|
nextK = k + "." + nextK
|
|
}
|
|
flattenJSONObjectRecursive(w, nextK, rootDict)
|
|
}
|
|
if k != "" {
|
|
rootDict.Remove(k)
|
|
}
|
|
}
|
|
}
|
|
|
|
type PrintJSONObjectRecursiveExFunc func(jsonutils.JSONObject)
|
|
type PrintJSONObjectFunc func(string)
|
|
|
|
func printJSONObjectRecursive_(obj jsonutils.JSONObject, cb PrintJSONObjectRecursiveExFunc) {
|
|
dict, ok := obj.(*jsonutils.JSONDict)
|
|
if !ok {
|
|
fmt.Println("Not a valid JSON object:", obj.String())
|
|
return
|
|
}
|
|
dictCopy := jsonutils.DeepCopy(dict).(*jsonutils.JSONDict)
|
|
flattenJSONObjectRecursive(dictCopy, "", dictCopy)
|
|
cb(dictCopy)
|
|
}
|
|
|
|
func PrintJSONObjectRecursive(obj jsonutils.JSONObject) {
|
|
printJSONObjectRecursive_(obj, PrintJSONObject)
|
|
}
|
|
|
|
func PrintJSONObjectRecursiveEx(obj jsonutils.JSONObject, cb PrintJSONObjectRecursiveExFunc) {
|
|
printJSONObjectRecursive_(obj, cb)
|
|
}
|
|
|
|
func PrintJSONBatchResults(results []SubmitResult, columns []string) {
|
|
objs := make([]jsonutils.JSONObject, 0)
|
|
errs := make([]jsonutils.JSONObject, 0)
|
|
for _, r := range results {
|
|
if r.Status == 200 {
|
|
objs = append(objs, r.Data)
|
|
} else {
|
|
err := jsonutils.NewDict()
|
|
err.Add(jsonutils.NewInt(int64(r.Status)), "status")
|
|
err.Add(jsonutils.Marshal(r.Id), "id")
|
|
err.Add(r.Data, "error")
|
|
errs = append(errs, err)
|
|
}
|
|
}
|
|
if len(objs) > 0 {
|
|
PrintJSONList(&ListResult{Data: objs}, columns)
|
|
}
|
|
if len(errs) > 0 {
|
|
PrintJSONList(&ListResult{Data: errs}, []string{"status", "id", "error"})
|
|
}
|
|
}
|