...

Source file src/k8s.io/kubectl/pkg/metricsutil/metrics_printer.go

Documentation: k8s.io/kubectl/pkg/metricsutil

     1  /*
     2  Copyright 2016 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package metricsutil
    18  
    19  import (
    20  	"fmt"
    21  	"io"
    22  	"sort"
    23  
    24  	"k8s.io/api/core/v1"
    25  	"k8s.io/apimachinery/pkg/api/resource"
    26  	"k8s.io/cli-runtime/pkg/printers"
    27  	metricsapi "k8s.io/metrics/pkg/apis/metrics"
    28  )
    29  
    30  var (
    31  	MeasuredResources = []v1.ResourceName{
    32  		v1.ResourceCPU,
    33  		v1.ResourceMemory,
    34  	}
    35  	NodeColumns     = []string{"NAME", "CPU(cores)", "CPU%", "MEMORY(bytes)", "MEMORY%"}
    36  	PodColumns      = []string{"NAME", "CPU(cores)", "MEMORY(bytes)"}
    37  	NamespaceColumn = "NAMESPACE"
    38  	PodColumn       = "POD"
    39  )
    40  
    41  type ResourceMetricsInfo struct {
    42  	Name      string
    43  	Metrics   v1.ResourceList
    44  	Available v1.ResourceList
    45  }
    46  
    47  type TopCmdPrinter struct {
    48  	out io.Writer
    49  }
    50  
    51  func NewTopCmdPrinter(out io.Writer) *TopCmdPrinter {
    52  	return &TopCmdPrinter{out: out}
    53  }
    54  
    55  func (printer *TopCmdPrinter) PrintNodeMetrics(metrics []metricsapi.NodeMetrics, availableResources map[string]v1.ResourceList, noHeaders bool, sortBy string) error {
    56  	if len(metrics) == 0 {
    57  		return nil
    58  	}
    59  	w := printers.GetNewTabWriter(printer.out)
    60  	defer w.Flush()
    61  
    62  	sort.Sort(NewNodeMetricsSorter(metrics, sortBy))
    63  
    64  	if !noHeaders {
    65  		printColumnNames(w, NodeColumns)
    66  	}
    67  	var usage v1.ResourceList
    68  	for _, m := range metrics {
    69  		m.Usage.DeepCopyInto(&usage)
    70  		printMetricsLine(w, &ResourceMetricsInfo{
    71  			Name:      m.Name,
    72  			Metrics:   usage,
    73  			Available: availableResources[m.Name],
    74  		})
    75  		delete(availableResources, m.Name)
    76  	}
    77  
    78  	// print lines for nodes of which the metrics is unreachable.
    79  	for nodeName := range availableResources {
    80  		printMissingMetricsNodeLine(w, nodeName)
    81  	}
    82  	return nil
    83  }
    84  
    85  func (printer *TopCmdPrinter) PrintPodMetrics(metrics []metricsapi.PodMetrics, printContainers bool, withNamespace bool, noHeaders bool, sortBy string, sum bool) error {
    86  	if len(metrics) == 0 {
    87  		return nil
    88  	}
    89  	w := printers.GetNewTabWriter(printer.out)
    90  	defer w.Flush()
    91  
    92  	columnWidth := len(PodColumns)
    93  	if !noHeaders {
    94  		if withNamespace {
    95  			printValue(w, NamespaceColumn)
    96  			columnWidth++
    97  		}
    98  		if printContainers {
    99  			printValue(w, PodColumn)
   100  			columnWidth++
   101  		}
   102  		printColumnNames(w, PodColumns)
   103  	}
   104  
   105  	sort.Sort(NewPodMetricsSorter(metrics, withNamespace, sortBy))
   106  
   107  	for _, m := range metrics {
   108  		if printContainers {
   109  			sort.Sort(NewContainerMetricsSorter(m.Containers, sortBy))
   110  			printSinglePodContainerMetrics(w, &m, withNamespace)
   111  		} else {
   112  			printSinglePodMetrics(w, &m, withNamespace)
   113  		}
   114  
   115  	}
   116  
   117  	if sum {
   118  		adder := NewResourceAdder(MeasuredResources)
   119  		for _, m := range metrics {
   120  			adder.AddPodMetrics(&m)
   121  		}
   122  		printPodResourcesSum(w, adder.total, columnWidth)
   123  	}
   124  
   125  	return nil
   126  }
   127  
   128  func printColumnNames(out io.Writer, names []string) {
   129  	for _, name := range names {
   130  		printValue(out, name)
   131  	}
   132  	fmt.Fprint(out, "\n")
   133  }
   134  
   135  func printSinglePodMetrics(out io.Writer, m *metricsapi.PodMetrics, withNamespace bool) {
   136  	podMetrics := getPodMetrics(m)
   137  	if withNamespace {
   138  		printValue(out, m.Namespace)
   139  	}
   140  	printMetricsLine(out, &ResourceMetricsInfo{
   141  		Name:      m.Name,
   142  		Metrics:   podMetrics,
   143  		Available: v1.ResourceList{},
   144  	})
   145  }
   146  
   147  func printSinglePodContainerMetrics(out io.Writer, m *metricsapi.PodMetrics, withNamespace bool) {
   148  	for _, c := range m.Containers {
   149  		if withNamespace {
   150  			printValue(out, m.Namespace)
   151  		}
   152  		printValue(out, m.Name)
   153  		printMetricsLine(out, &ResourceMetricsInfo{
   154  			Name:      c.Name,
   155  			Metrics:   c.Usage,
   156  			Available: v1.ResourceList{},
   157  		})
   158  	}
   159  }
   160  
   161  func getPodMetrics(m *metricsapi.PodMetrics) v1.ResourceList {
   162  	podMetrics := make(v1.ResourceList)
   163  	for _, res := range MeasuredResources {
   164  		podMetrics[res], _ = resource.ParseQuantity("0")
   165  	}
   166  
   167  	for _, c := range m.Containers {
   168  		for _, res := range MeasuredResources {
   169  			quantity := podMetrics[res]
   170  			quantity.Add(c.Usage[res])
   171  			podMetrics[res] = quantity
   172  		}
   173  	}
   174  	return podMetrics
   175  }
   176  
   177  func printMetricsLine(out io.Writer, metrics *ResourceMetricsInfo) {
   178  	printValue(out, metrics.Name)
   179  	printAllResourceUsages(out, metrics)
   180  	fmt.Fprint(out, "\n")
   181  }
   182  
   183  func printMissingMetricsNodeLine(out io.Writer, nodeName string) {
   184  	printValue(out, nodeName)
   185  	unknownMetricsStatus := "<unknown>"
   186  	for i := 0; i < len(MeasuredResources); i++ {
   187  		printValue(out, unknownMetricsStatus)
   188  		printValue(out, unknownMetricsStatus)
   189  	}
   190  	fmt.Fprint(out, "\n")
   191  }
   192  
   193  func printValue(out io.Writer, value interface{}) {
   194  	fmt.Fprintf(out, "%v\t", value)
   195  }
   196  
   197  func printAllResourceUsages(out io.Writer, metrics *ResourceMetricsInfo) {
   198  	for _, res := range MeasuredResources {
   199  		quantity := metrics.Metrics[res]
   200  		printSingleResourceUsage(out, res, quantity)
   201  		fmt.Fprint(out, "\t")
   202  		if available, found := metrics.Available[res]; found {
   203  			fraction := float64(quantity.MilliValue()) / float64(available.MilliValue()) * 100
   204  			fmt.Fprintf(out, "%d%%\t", int64(fraction))
   205  		}
   206  	}
   207  }
   208  
   209  func printSingleResourceUsage(out io.Writer, resourceType v1.ResourceName, quantity resource.Quantity) {
   210  	switch resourceType {
   211  	case v1.ResourceCPU:
   212  		fmt.Fprintf(out, "%vm", quantity.MilliValue())
   213  	case v1.ResourceMemory:
   214  		fmt.Fprintf(out, "%vMi", quantity.Value()/(1024*1024))
   215  	default:
   216  		fmt.Fprintf(out, "%v", quantity.Value())
   217  	}
   218  }
   219  
   220  func printPodResourcesSum(out io.Writer, total v1.ResourceList, columnWidth int) {
   221  	for i := 0; i < columnWidth-2; i++ {
   222  		printValue(out, "")
   223  	}
   224  	printValue(out, "________")
   225  	printValue(out, "________")
   226  	fmt.Fprintf(out, "\n")
   227  	for i := 0; i < columnWidth-3; i++ {
   228  		printValue(out, "")
   229  	}
   230  	printMetricsLine(out, &ResourceMetricsInfo{
   231  		Name:      "",
   232  		Metrics:   total,
   233  		Available: v1.ResourceList{},
   234  	})
   235  
   236  }
   237  

View as plain text