add api to get cpu/memory realtime data

This commit is contained in:
Calvin Yu
2018-05-26 16:02:33 +08:00
parent 6abbd86601
commit 64485f478c
9 changed files with 498 additions and 33 deletions

View File

@@ -9,7 +9,7 @@ TRAG.Version:=$(TRAG.Gopkg)/pkg/version
DOCKER_TAGS=latest
RUN_IN_DOCKER:=docker run -it -v `pwd`:/go/src/$(TRAG.Gopkg) -v `pwd`/tmp/cache:/root/.cache/go-build -w /go/src/$(TRAG.Gopkg) -e GOBIN=/go/src/$(TRAG.Gopkg)/tmp/bin -e USER_ID=`id -u` -e GROUP_ID=`id -g` kubesphere/kubesphere-builder
GO_FMT:=goimports -l -w -e -local=kubesphere -srcdir=/go/src/$(TRAG.Gopkg)
GO_FILES:=./cmd ./test ./pkg
GO_FILES:=./cmd ./pkg
define get_diff_files
$(eval DIFF_FILES=$(shell git diff --name-only --diff-filter=ad | grep -E "^(test|cmd|pkg)/.+\.go"))

View File

@@ -21,6 +21,7 @@ import (
"kubesphere.io/kubesphere/pkg/apis/v1alpha/kubeconfig"
"kubesphere.io/kubesphere/pkg/apis/v1alpha/kubectl"
"kubesphere.io/kubesphere/pkg/apis/v1alpha/nodes"
"kubesphere.io/kubesphere/pkg/apis/v1alpha/pods"
"kubesphere.io/kubesphere/pkg/apis/v1alpha/registries"
"kubesphere.io/kubesphere/pkg/apis/v1alpha/storage"
"kubesphere.io/kubesphere/pkg/apis/v1alpha/volumes"
@@ -29,7 +30,7 @@ import (
func init() {
ws := new(restful.WebService)
ws.Path("/api/v1alpha")
ws.Path("/api/v1alpha1")
nodes.Register(ws, "/nodes")
kubeconfig.Register(ws, "/namespaces/{namespace}/kubeconfig")
@@ -37,7 +38,8 @@ func init() {
registries.Register(ws, "/registries")
storage.Register(ws, "/storage")
volumes.Register(ws, "/volumes")
nodes.Register(ws, "/nodes")
pods.Register(ws)
// add webservice to default container
restful.Add(ws)

View File

@@ -18,16 +18,49 @@ package nodes
import (
"github.com/emicklei/go-restful"
"kubesphere.io/kubesphere/pkg/models"
"kubesphere.io/kubesphere/pkg/constants"
"kubesphere.io/kubesphere/pkg/filter/route"
"kubesphere.io/kubesphere/pkg/models"
)
func Register(ws *restful.WebService, subPath string) {
ws.Route(ws.GET(subPath).To(models.HandleNodes).Filter(route.RouteLogging)).
ws.Route(ws.GET(subPath).To(handleNodes).Filter(route.RouteLogging)).
Consumes(restful.MIME_JSON, restful.MIME_XML).
Produces(restful.MIME_JSON)
ws.Route(ws.GET(subPath+"/{nodename}").To(handleSingleNode).Filter(route.RouteLogging)).
Consumes(restful.MIME_JSON, restful.MIME_XML).
Produces(restful.MIME_JSON)
}
func handleNodes(request *restful.Request, response *restful.Response) {
var result constants.ResultMessage
var resultNodes []models.ResultNode
var resultNode models.ResultNode
nodes := models.GetNodes()
for _, node := range nodes {
resultNode = models.FormatNodeMetrics(node)
resultNodes = append(resultNodes, resultNode)
}
result.Data = resultNodes
response.WriteAsJson(result)
}
func handleSingleNode(request *restful.Request, response *restful.Response) {
nodeName := request.PathParameter("nodename")
var result constants.ResultMessage
var resultNodes []models.ResultNode
var resultNode models.ResultNode
resultNode = models.FormatNodeMetrics(nodeName)
resultNodes = append(resultNodes, resultNode)
result.Data = resultNodes
response.WriteAsJson(result)
}

View File

@@ -0,0 +1,69 @@
/*
Copyright 2018 The KubeSphere Authors.
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 pods
import (
"github.com/emicklei/go-restful"
"kubesphere.io/kubesphere/pkg/filter/route"
"kubesphere.io/kubesphere/pkg/constants"
"kubesphere.io/kubesphere/pkg/models"
)
func Register(ws *restful.WebService) {
ws.Route(ws.GET("/pods").To(handleAllPods).Filter(route.RouteLogging)).
Consumes(restful.MIME_JSON, restful.MIME_XML).
Produces(restful.MIME_JSON)
ws.Route(ws.GET("/namespaces/{namespace}/pods").To(handlePodsUnderNameSpace).Filter(route.RouteLogging)).
Consumes(restful.MIME_JSON, restful.MIME_XML).
Produces(restful.MIME_JSON)
}
func handleAllPods(request *restful.Request, response *restful.Response) {
var result constants.ResultMessage
var resultNameSpaces []models.ResultNameSpace
var resultNameSpace models.ResultNameSpace
namespaces := models.GetNameSpaces()
for _, namespace := range namespaces {
resultNameSpace = models.FormatNameSpaceMetrics(namespace)
resultNameSpaces = append(resultNameSpaces, resultNameSpace)
}
result.Data = resultNameSpaces
response.WriteAsJson(result)
}
func handlePodsUnderNameSpace(request *restful.Request, response *restful.Response) {
var result constants.ResultMessage
var resultNameSpaces []models.ResultNameSpace
var resultNameSpace models.ResultNameSpace
resultNameSpace = models.FormatNameSpaceMetrics(request.PathParameter("namespace"))
resultNameSpaces = append(resultNameSpaces, resultNameSpace)
result.Data = resultNameSpaces
response.WriteAsJson(result)
}

View File

@@ -17,27 +17,40 @@ limitations under the License.
package client
import (
"fmt"
"net/http"
"os"
"github.com/golang/glog"
"io/ioutil"
"os"
)
const (
DefaultHeapsterScheme = "http"
DefaultHeapsterService = "heapster" //"heapster"
DefaultHeapsterPort = "80" // use the first exposed port on the service
)
var (
prefix = "/api/v1/model"
)
func GetHeapsterMetrics(url string) string {
response, err := http.Get(url)
//glog.Info("Querying data from " + DefaultHeapsterScheme + "://" + DefaultHeapsterService + ":" + DefaultHeapsterPort + prefix + url)
response, err := http.Get(DefaultHeapsterScheme + "://" + DefaultHeapsterService + ":" + DefaultHeapsterPort + prefix + url)
if err != nil {
fmt.Printf("%s", err)
glog.Error(err)
os.Exit(1)
} else {
defer response.Body.Close()
contents, err := ioutil.ReadAll(response.Body)
if err != nil {
fmt.Printf("%s", err)
glog.Error(err)
os.Exit(1)
}
return string(contents)
}
return ""
}
}

View File

@@ -17,33 +17,128 @@ limitations under the License.
package models
import (
"github.com/emicklei/go-restful"
"encoding/json"
"net/http"
"strings"
"github.com/golang/glog"
"kubesphere.io/kubesphere/pkg/client"
ksutil "kubesphere.io/kubesphere/pkg/util"
"fmt"
"strconv"
)
type ResultMessage struct {
Ret int `json:"ret"`
Msg string `json:"msg"`
Data interface{} `json:"data"`
type ResultNodes struct {
Nodes []ResultNode `json:"nodes"`
}
type ResultNode struct {
NodeName string `json:"node_name"`
CPU []CPUNode `json:"cpu"`
Memory []MemoryNode `json:"memory"`
}
func HandleNodes(request *restful.Request, response *restful.Response) {
var result ResultMessage
data := make(map[string]string)
data["output"] = client.GetHeapsterMetrics("http://139.198.0.79/api/monitor/v1/model/namespaces/kube-system/pods/qingcloud-volume-provisioner-i-o5pmakm7/metrics/cpu/request")
result.Data = data
result.Ret = http.StatusOK
result.Msg = "success"
glog.Infoln(result)
response.WriteAsJson(result)
type CPUNode struct {
TimeStamp string `json:"timestamp"`
UsedCPU string `json:"used_cpu"`
TotalCPU string `json:"total_cpu"`
CPUUtilization string `json:"cpu_utilization"`
}
type MemoryNode struct {
TimeStamp string `json:"timestamp"`
UsedMemory string `json:"used_mem"`
TotalMemory string `json:"total_mem"`
MemoryUtilization string `json:"mem_utilization"`
}
/*
Get all nodes in default cluster
*/
func GetNodes() []string {
nodesList := client.GetHeapsterMetrics("/nodes")
var nodes []string
dec := json.NewDecoder(strings.NewReader(nodesList))
err := dec.Decode(&nodes)
if err != nil {
glog.Error(err)
}
return nodes
}
/*
Format cpu/memory data for specified node
*/
func FormatNodeMetrics(nodeName string) ResultNode {
var resultNode ResultNode
var nodeCPUMetrics []CPUNode
var nodeMemMetrics []MemoryNode
var cpuMetrics CPUNode
var memMetrics MemoryNode
var total_cpu float64
var total_mem float64
cpuNodeAllocated := client.GetHeapsterMetrics("/nodes/" + nodeName + "/metrics/cpu/node_allocatable")
if cpuNodeAllocated != "" {
var err error
total_cpu, err = strconv.ParseFloat(ksutil.JsonRawMessage(cpuNodeAllocated).Find("metrics").ToList()[0].Find("value").ToString(), 64)
if err == nil {
total_cpu = total_cpu / 1000
}
}
cpuUsageRate := client.GetHeapsterMetrics("/nodes/" + nodeName + "/metrics/cpu/usage_rate")
if cpuUsageRate != "" {
metrics := ksutil.JsonRawMessage(cpuUsageRate).Find("metrics").ToList()
for _, metric := range metrics {
timestamp := metric.Find("timestamp")
cpu_utilization, _ := strconv.ParseFloat(metric.Find("value").ToString(), 64)
cpuMetrics.TimeStamp = timestamp.ToString()
cpuMetrics.TotalCPU = fmt.Sprintf("%.1f", total_cpu)
cpuMetrics.CPUUtilization = fmt.Sprintf("%.3f", cpu_utilization/1000)
cpuMetrics.UsedCPU = fmt.Sprintf("%.1f", total_cpu*cpu_utilization/1000)
glog.Info("node " + nodeName + " has total cpu " + fmt.Sprintf("%.1f", total_cpu) + " CPU utilization " + fmt.Sprintf("%.3f", cpu_utilization/1000) + " at time" + timestamp.ToString())
nodeCPUMetrics = append(nodeCPUMetrics, cpuMetrics)
}
}
memNodeAllocated := client.GetHeapsterMetrics("/nodes/" + nodeName + "/metrics/memory/node_allocatable")
var total_mem_bytes, used_mem_bytes float64
if memNodeAllocated != "" {
var err error
total_mem_bytes, err = strconv.ParseFloat(ksutil.JsonRawMessage(memNodeAllocated).Find("metrics").ToList()[0].Find("value").ToString(), 64)
if err == nil {
total_mem = total_mem_bytes / 1024 / 1024 / 1024
}
}
memUsage := client.GetHeapsterMetrics("/nodes/" + nodeName + "/metrics/memory/usage")
if memUsage != "" {
metrics := ksutil.JsonRawMessage(memUsage).Find("metrics").ToList()
for _, metric := range metrics {
timestamp := metric.Find("timestamp")
used_mem_bytes, _ = strconv.ParseFloat(metric.Find("value").ToString(), 64)
used_mem := used_mem_bytes / 1024 / 1024 / 1024
memMetrics.TimeStamp = timestamp.ToString()
memMetrics.TotalMemory = fmt.Sprintf("%.1f", total_mem)
memMetrics.UsedMemory = fmt.Sprintf("%.1f", used_mem)
memMetrics.MemoryUtilization = fmt.Sprintf("%.3f", used_mem_bytes/total_mem_bytes)
glog.Info("node " + nodeName + " has total mem " + fmt.Sprintf("%.1f", total_mem) + " mem utilization " + fmt.Sprintf("%.3f", used_mem_bytes/total_mem_bytes) + " at time" + timestamp.ToString())
nodeMemMetrics = append(nodeMemMetrics, memMetrics)
}
}
resultNode.NodeName = nodeName
resultNode.CPU = nodeCPUMetrics
resultNode.Memory = nodeMemMetrics
return resultNode
}

196
pkg/models/pods.go Normal file
View File

@@ -0,0 +1,196 @@
package models
import (
"encoding/json"
"strings"
"github.com/golang/glog"
"kubesphere.io/kubesphere/pkg/client"
ksutil "kubesphere.io/kubesphere/pkg/util"
"fmt"
"strconv"
)
type ResultNameSpaces struct {
NameSpaces []ResultNameSpace `json:"namespaces"`
}
type ResultNameSpace struct {
NameSpace string `json:"namespace"`
PodsCount string `json:"pods_count"`
Pods []ResultPod `json:"pods"`
}
type ResultPod struct {
PodName string `json:"pod_name"`
CPURequest string `json:"cpu_request"`
CPULimit string `json:"cpu_limit"`
MemoryRequest string `json:"mem_request"`
MemoryLimit string `json:"mem_limit"`
CPU []CPUPod `json:"cpu"`
Memory []MemoryPod `json:"memory"`
}
type CPUPod struct {
TimeStamp string `json:"timestamp"`
UsedCPU string `json:"used_cpu"`
CPUUtilization string `json:"cpu_utilization"`
}
type MemoryPod struct {
TimeStamp string `json:"timestamp"`
UsedMemory string `json:"used_mem"`
MemoryUtilization string `json:"mem_utilization"`
}
/*
Get all namespaces in default cluster
*/
func GetNameSpaces() []string {
namespacesList := client.GetHeapsterMetrics("/namespaces")
var namespaces []string
dec := json.NewDecoder(strings.NewReader(namespacesList))
err := dec.Decode(&namespaces)
if err != nil {
glog.Error(err)
}
return namespaces
}
/*
Get all pods under specified namespace in default cluster
*/
func GetPods(namespace string) []string {
fmt.Println(namespace)
podsList := client.GetHeapsterMetrics("/namespaces/" + namespace + "/pods")
var pods []string
dec := json.NewDecoder(strings.NewReader(podsList))
err := dec.Decode(&pods)
if err != nil {
glog.Error(err)
}
return pods
}
func FormatNameSpaceMetrics(namespace string) ResultNameSpace {
var resultNameSpace ResultNameSpace
var resultPods []ResultPod
var resultPod ResultPod
pods := GetPods(namespace)
resultNameSpace.NameSpace = namespace
resultNameSpace.PodsCount = strconv.Itoa(len(pods))
for _, pod := range pods {
resultPod = FormatPodMetrics(namespace, pod)
resultPods = append(resultPods, resultPod)
}
resultNameSpace.Pods = resultPods
return resultNameSpace
}
func FormatPodMetrics(namespace, pod string) ResultPod {
var resultPod ResultPod
var podCPUMetrics []CPUPod
var podMemMetrics []MemoryPod
var cpuMetrics CPUPod
var memMetrics MemoryPod
resultPod.PodName = pod
cpuRequest := client.GetHeapsterMetrics("/namespaces/" + namespace + "/pods/" + pod + "/metrics/cpu/request")
cpuRequest = ksutil.JsonRawMessage(cpuRequest).Find("metrics").ToList()[0].Find("value").ToString()
if cpuRequest != "" && cpuRequest != "0" {
resultPod.CPURequest = cpuRequest
} else {
resultPod.CPURequest = "inf"
}
cpuLimit := client.GetHeapsterMetrics("/namespaces/" + namespace + "/pods/" + pod + "/metrics/cpu/limit")
cpuLimit = ksutil.JsonRawMessage(cpuLimit).Find("metrics").ToList()[0].Find("value").ToString()
if cpuLimit != "" && cpuLimit != "0" {
resultPod.CPULimit = cpuLimit
} else {
resultPod.CPULimit = "inf"
}
memoryRequest := client.GetHeapsterMetrics("/namespaces/" + namespace + "/pods/" + pod + "/metrics/memory/request")
resultPod.MemoryRequest = convertMemory(memoryRequest)
memoryLimit := client.GetHeapsterMetrics("/namespaces/" + namespace + "/pods/" + pod + "/metrics/memory/limit")
resultPod.MemoryLimit = convertMemory(memoryLimit)
cpuUsageRate := client.GetHeapsterMetrics("/namespaces/" + namespace + "/pods/" + pod + "/metrics/cpu/usage_rate")
if cpuUsageRate != "" {
metrics := ksutil.JsonRawMessage(cpuUsageRate).Find("metrics").ToList()
for _, metric := range metrics {
timestamp := metric.Find("timestamp")
cpu_utilization, _ := strconv.ParseFloat(metric.Find("value").ToString(), 64)
cpuMetrics.TimeStamp = timestamp.ToString()
cpuMetrics.CPUUtilization = fmt.Sprintf("%.3f", cpu_utilization/1000)
if resultPod.CPULimit != "inf" {
cpu_limit, _ := strconv.ParseFloat(resultPod.CPULimit, 64)
cpuMetrics.UsedCPU = fmt.Sprintf("%.1f", cpu_limit*cpu_utilization/1000)
} else {
cpuMetrics.UsedCPU = "inf"
}
glog.Info("pod " + pod + " has limit cpu " + resultPod.CPULimit + " CPU utilization " + fmt.Sprintf("%.3f", cpu_utilization/1000) + " at time" + timestamp.ToString())
podCPUMetrics = append(podCPUMetrics, cpuMetrics)
}
}
resultPod.CPU = podCPUMetrics
var used_mem_bytes float64
memUsage := client.GetHeapsterMetrics("/namespaces/" + namespace + "/pods/" + pod + "/metrics/memory/usage")
if memUsage != "" {
metrics := ksutil.JsonRawMessage(memUsage).Find("metrics").ToList()
for _, metric := range metrics {
timestamp := metric.Find("timestamp")
used_mem_bytes, _ = strconv.ParseFloat(metric.Find("value").ToString(), 64)
used_mem := used_mem_bytes / 1024 / 1024
memMetrics.TimeStamp = timestamp.ToString()
memMetrics.UsedMemory = fmt.Sprintf("%.1f", used_mem)
if resultPod.MemoryLimit != "inf" {
mem_limit, _ := strconv.ParseFloat(resultPod.MemoryLimit, 64)
memMetrics.MemoryUtilization = fmt.Sprintf("%.3f", used_mem/mem_limit)
} else {
memMetrics.MemoryUtilization = "inf"
}
glog.Info("pod " + pod + " has limit mem " + resultPod.MemoryLimit + " mem utilization " + memMetrics.MemoryUtilization + " at time" + timestamp.ToString())
podMemMetrics = append(podMemMetrics, memMetrics)
}
}
resultPod.Memory = podMemMetrics
return resultPod
}
func convertMemory(memBytes string) string {
var mem string
if memBytes != "" {
memMetric := ksutil.JsonRawMessage(memBytes).Find("metrics").ToList()[0].Find("value").ToString()
if memMetric != "" && memMetric != "0" {
memBytes, error := strconv.ParseFloat(memMetric, 64)
if error == nil {
mem = fmt.Sprintf("%.3f", memBytes/1024/1024)
} else {
mem = "inf"
}
} else {
mem = "inf"
}
} else {
mem = "inf"
}
return mem
}

56
pkg/util/util.go Normal file
View File

@@ -0,0 +1,56 @@
/*
Copyright 2018 The KubeSphere Authors.
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 util
import (
"encoding/json"
"github.com/golang/glog"
"strings"
)
type JsonRawMessage []byte
func (m JsonRawMessage) Find(key string) JsonRawMessage {
var objmap map[string]json.RawMessage
err := json.Unmarshal(m, &objmap)
if err != nil {
glog.Errorf("Resolve JSON Key failed, find key =%s, err=%s",
key, err)
return nil
}
return JsonRawMessage(objmap[key])
}
func (m JsonRawMessage) ToList() []JsonRawMessage {
var lists []json.RawMessage
err := json.Unmarshal(m, &lists)
if err != nil {
glog.Errorf("Resolve JSON List failed, err=%s",
err)
return nil
}
var res []JsonRawMessage
for _, v := range lists {
res = append(res, JsonRawMessage(v))
}
return res
}
func (m JsonRawMessage) ToString() string {
res := strings.Replace(string(m[:]), "\"", "", -1)
return res
}

1
vendor/github.com/influxdata/influxdb generated vendored Submodule

Submodule vendor/github.com/influxdata/influxdb added at 531dc49717