diff --git a/pkg/apis/v1alpha/components/components_handler.go b/pkg/apis/v1alpha/components/components_handler.go new file mode 100644 index 000000000..daacb2f79 --- /dev/null +++ b/pkg/apis/v1alpha/components/components_handler.go @@ -0,0 +1,47 @@ +/* +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 components + +import ( + "github.com/emicklei/go-restful" + "kubesphere.io/kubesphere/pkg/filter/route" + "kubesphere.io/kubesphere/pkg/models" + "net/http" + "kubesphere.io/kubesphere/pkg/constants" +) + +func Register(ws *restful.WebService, subPath string) { + ws.Route(ws.GET(subPath).To(handleGetComponents).Filter(route.RouteLogging)). + Consumes(restful.MIME_JSON, restful.MIME_XML). + Produces(restful.MIME_JSON) + +} + +//get all components + +func handleGetComponents(request *restful.Request, response *restful.Response) { + + result, err := models.GetComponents() + + if err != nil { + + response.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()}) + } + + response.WriteAsJson(result) + +} diff --git a/pkg/apis/v1alpha/containers/containers_handler.go b/pkg/apis/v1alpha/containers/containers_handler.go index 0eac5f33c..46bae4c13 100644 --- a/pkg/apis/v1alpha/containers/containers_handler.go +++ b/pkg/apis/v1alpha/containers/containers_handler.go @@ -25,6 +25,9 @@ import ( ) func Register(ws *restful.WebService) { + ws.Route(ws.GET("/namespaces/{namespace}/pods/{podname}/containers/{containername}").To(handleContainerUnderNameSpaceAndPod).Filter(route.RouteLogging)). + Consumes(restful.MIME_JSON, restful.MIME_XML). + Produces(restful.MIME_JSON) ws.Route(ws.GET("/namespaces/{namespace}/pods/{podname}/containers").To(handleContainersUnderNameSpaceAndPod).Filter(route.RouteLogging)). Consumes(restful.MIME_JSON, restful.MIME_XML). Produces(restful.MIME_JSON) @@ -33,28 +36,23 @@ func Register(ws *restful.WebService) { Produces(restful.MIME_JSON) } +func handleContainerUnderNameSpaceAndPod(request *restful.Request, response *restful.Response) { + var resultContainer models.ResultContainer + resultContainer = models.FormatContainerMetrics(request.PathParameter("namespace"), request.PathParameter("podname"), request.PathParameter("containername")) + resultContainer.NodeName = models.GetNodeNameForPod(request.PathParameter("podname"), request.PathParameter("namespace")) + response.WriteAsJson(resultContainer) +} func handleContainersUnderNameSpaceAndPod(request *restful.Request, response *restful.Response) { - var result constants.ResultMessage - var resultNameSpaces []models.ResultNameSpaceForContainer - var resultNameSpace models.ResultNameSpaceForContainer + var resultNameSpace constants.PageableResponse resultNameSpace = models.FormatContainersMetrics("", request.PathParameter("namespace"), request.PathParameter("podname")) - - resultNameSpaces = append(resultNameSpaces, resultNameSpace) - - result.Data = resultNameSpaces - response.WriteAsJson(result) + response.WriteAsJson(resultNameSpace) } func handleContainersUnderNodeAndNameSpaceAndPod(request *restful.Request, response *restful.Response) { - var result constants.ResultMessage - var resultNameSpaces []models.ResultNameSpaceForContainer - var resultNameSpace models.ResultNameSpaceForContainer + var resultNameSpace constants.PageableResponse resultNameSpace = models.FormatContainersMetrics(request.PathParameter("nodename"), request.PathParameter("namespace"), request.PathParameter("podname")) - resultNameSpaces = append(resultNameSpaces, resultNameSpace) - - result.Data = resultNameSpaces - response.WriteAsJson(result) + response.WriteAsJson(resultNameSpace) } diff --git a/pkg/apis/v1alpha/install.go b/pkg/apis/v1alpha/install.go index 3202c3106..43549ab9c 100644 --- a/pkg/apis/v1alpha/install.go +++ b/pkg/apis/v1alpha/install.go @@ -27,6 +27,7 @@ import ( "kubesphere.io/kubesphere/pkg/apis/v1alpha/storage" "kubesphere.io/kubesphere/pkg/apis/v1alpha/volumes" "kubesphere.io/kubesphere/pkg/apis/v1alpha/iam" + "kubesphere.io/kubesphere/pkg/apis/v1alpha/components" ) func init() { @@ -43,6 +44,7 @@ func init() { pods.Register(ws) containers.Register(ws) iam.Register(ws) + components.Register(ws,"/components") // add webservice to default container restful.Add(ws) diff --git a/pkg/apis/v1alpha/nodes/nodes_handler.go b/pkg/apis/v1alpha/nodes/nodes_handler.go index 04978d100..c3682c2ba 100644 --- a/pkg/apis/v1alpha/nodes/nodes_handler.go +++ b/pkg/apis/v1alpha/nodes/nodes_handler.go @@ -35,32 +35,28 @@ func Register(ws *restful.WebService, subPath string) { } func handleNodes(request *restful.Request, response *restful.Response) { - var result constants.ResultMessage - var resultNodes []models.ResultNode + var result constants.PageableResponse var resultNode models.ResultNode nodes := models.GetNodes() - for _, node := range nodes { - + var total_count int + for i, node := range nodes { resultNode = models.FormatNodeMetrics(node) - resultNodes = append(resultNodes, resultNode) - + result.Items = append(result.Items, resultNode) + total_count = i } + total_count = total_count + 1 - result.Data = resultNodes + result.TotalCount = total_count 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) + response.WriteAsJson(resultNode) } diff --git a/pkg/apis/v1alpha/pods/pods_handler.go b/pkg/apis/v1alpha/pods/pods_handler.go index 9eb7ee314..bf969353e 100644 --- a/pkg/apis/v1alpha/pods/pods_handler.go +++ b/pkg/apis/v1alpha/pods/pods_handler.go @@ -30,55 +30,75 @@ 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/{podname}").To(handlePodUnderNameSpace).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) + ws.Route(ws.GET("/nodes/{nodename}/pods").To(handlePodsUnderNode).Filter(route.RouteLogging)). + Consumes(restful.MIME_JSON, restful.MIME_XML). + Produces(restful.MIME_JSON) ws.Route(ws.GET("/nodes/{nodename}/namespaces/{namespace}/pods").To(handlePodsUnderNodeAndNameSpace).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 + var result constants.PageableResponse namespaces := models.GetNameSpaces() - for _, namespace := range namespaces { - - resultNameSpace = models.FormatPodsMetrics("", namespace) - resultNameSpaces = append(resultNameSpaces, resultNameSpace) - + var total_count int + for i, namespace := range namespaces { + result = models.FormatPodsMetrics("", namespace) + result.Items = append(result.Items, result) + total_count = i } - result.Data = resultNameSpaces + result.TotalCount = total_count + response.WriteAsJson(result) } func handlePodsUnderNameSpace(request *restful.Request, response *restful.Response) { - var result constants.ResultMessage - var resultNameSpaces []models.ResultNameSpace - var resultNameSpace models.ResultNameSpace + var result constants.PageableResponse - resultNameSpace = models.FormatPodsMetrics("", request.PathParameter("namespace")) + result = models.FormatPodsMetrics("", request.PathParameter("namespace")) - resultNameSpaces = append(resultNameSpaces, resultNameSpace) - - result.Data = resultNameSpaces response.WriteAsJson(result) } +func handlePodsUnderNode(request *restful.Request, response *restful.Response) { + var result constants.PageableResponse + var resultNameSpace constants.PageableResponse + namespaces := models.GetNameSpaces() + + var total_count int + for _, namespace := range namespaces { + resultNameSpace = models.FormatPodsMetrics(request.PathParameter("nodename"), namespace) + + var sub_total_count int + for j, pod := range resultNameSpace.Items { + result.Items = append(result.Items, pod) + sub_total_count = j + } + total_count += sub_total_count + } + result.TotalCount = total_count + response.WriteAsJson(result) +} +func handlePodUnderNameSpace(request *restful.Request, response *restful.Response) { + var resultPod models.ResultPod + + resultPod = models.FormatPodMetrics(request.PathParameter("namespace"), request.PathParameter("podname")) + + response.WriteAsJson(resultPod) +} func handlePodsUnderNodeAndNameSpace(request *restful.Request, response *restful.Response) { - var result constants.ResultMessage - var resultNameSpaces []models.ResultNameSpace - var resultNameSpace models.ResultNameSpace + var result constants.PageableResponse - resultNameSpace = models.FormatPodsMetrics(request.PathParameter("nodename"), request.PathParameter("namespace")) + result = models.FormatPodsMetrics(request.PathParameter("nodename"), request.PathParameter("namespace")) - resultNameSpaces = append(resultNameSpaces, resultNameSpace) - - result.Data = resultNameSpaces response.WriteAsJson(result) } diff --git a/pkg/apis/v1alpha/registries/registries_handler.go b/pkg/apis/v1alpha/registries/registries_handler.go index f3b663b01..69f4cf996 100644 --- a/pkg/apis/v1alpha/registries/registries_handler.go +++ b/pkg/apis/v1alpha/registries/registries_handler.go @@ -21,6 +21,7 @@ import ( "kubesphere.io/kubesphere/pkg/models" "kubesphere.io/kubesphere/pkg/filter/route" "net/http" + "kubesphere.io/kubesphere/pkg/constants" ) func Register(ws *restful.WebService, subPath string) { @@ -39,7 +40,7 @@ func handlerRegistryValidation(request *restful.Request, response *restful.Respo if err != nil { - response.WriteError(http.StatusInternalServerError, err) + response.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()}) } diff --git a/pkg/client/heapsterclient.go b/pkg/client/heapsterclient.go index 14b883d62..23d4ef4a9 100644 --- a/pkg/client/heapsterclient.go +++ b/pkg/client/heapsterclient.go @@ -55,3 +55,23 @@ func GetHeapsterMetrics(url string) string { } return "" } +func GetCAdvisorMetrics(nodeAddr string) string { + + response, err := http.Get("http://" + nodeAddr + ":10255/stats/summary") + if err != nil { + glog.Error(err) + os.Exit(1) + } else { + defer response.Body.Close() + + contents, err := ioutil.ReadAll(response.Body) + + if err != nil { + glog.Error(err) + os.Exit(1) + } + + return string(contents) + } + return "" +} diff --git a/pkg/models/components.go b/pkg/models/components.go new file mode 100644 index 000000000..485a84c92 --- /dev/null +++ b/pkg/models/components.go @@ -0,0 +1,281 @@ +/* +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 models + +import ( + "kubesphere.io/kubesphere/pkg/client" + meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "time" + "github.com/golang/glog" + "strings" +) + +const KUBESYSTEM = "kube-system" +const OPENPITRIX = "openpitrix-system" + +type Components struct { + Name string `json:"name"` + Version string `json:"version"` + Kind string `json:"kind"` + Replicas int `json:"replicas"` + HealthStatus string `json:"healthStatus"` + SelfLink string `json:"selfLink"` + UpdateTime time.Time `json:"updateTime"` +} + +/*** +* get all components from k8s and kubesphere system, +* there are master component, node component,addons component , kubesphere component +* + */ +func GetComponents() (result []Components, err error) { + + k8sClient := client.NewK8sClient() + + label := "tier=control-plane" + + option := meta_v1.ListOptions{ + + LabelSelector: label, + } + + podlists, err := k8sClient.CoreV1().Pods(KUBESYSTEM).List(option) + + if err != nil { + + glog.Error(err) + + return result, err + } + + var components Components + + templates := [] string{"kube-apiserver", "etcd", "kube-scheduler", "kube-controller-manager", "cloud-controller-manager"} + + if len(podlists.Items) > 0 { + + for _, pod := range podlists.Items { + + for _, template := range templates { + + if strings.Contains(pod.Name, template) { + + components.Name = template + components.Kind = "Pod" + components.SelfLink = pod.SelfLink + version := strings.Split(pod.Spec.Containers[0].Image, ":") + + if len(version) < 2 { + + components.Version = "latest" + + } else { + + components.Version = version[1] + + } + components.Replicas = 1 + + if pod.Status.Phase == "Running" { + + components.HealthStatus = "health" + + } else { + + components.HealthStatus = "unhealth" + + } + components.UpdateTime = pod.Status.Conditions[0].LastTransitionTime.Time + + result = append(result, components) + + } + + } + + } + + } + + label = "component=kube-addon-manager" + + option.LabelSelector = label + + kubeaddon, err := k8sClient.CoreV1().Pods(KUBESYSTEM).List(option) + + if err != nil { + + glog.Error(err) + + return result, err + } + + if len(kubeaddon.Items) > 0 { + + for _, pod := range kubeaddon.Items { + + components.Name = "kube-addon-manager" + components.Kind = "Pod" + components.SelfLink = pod.SelfLink + version := strings.Split(pod.Spec.Containers[0].Image, ":") + + if len(version) < 2 { + + components.Version = "latest" + + } else { + + components.Version = version[1] + + } + components.Replicas = 1 + + if pod.Status.Phase == "Running" { + + components.HealthStatus = "health" + + } else { + + components.HealthStatus = "fault" + + } + components.UpdateTime = pod.Status.Conditions[0].LastTransitionTime.Time + + result = append(result, components) + + } + + } + + option.LabelSelector = "" + + dsList, err := k8sClient.AppsV1beta2().DaemonSets(KUBESYSTEM).List(option) + + if err != nil { + + glog.Error(err) + + return result, err + } + + if len(dsList.Items) > 0 { + + for _, ds := range dsList.Items { + + if strings.Contains(ds.Name, "fluent-bit") { + + continue + } + + components.Name = ds.Name + components.Kind = "Daemonset" + components.SelfLink = ds.SelfLink + version := strings.Split(ds.Spec.Template.Spec.Containers[0].Image, ":") + + if len(version) < 2 { + + components.Version = "latest" + + } else { + + components.Version = version[1] + + } + components.UpdateTime = ds.CreationTimestamp.Time + components.Replicas = int(ds.Status.DesiredNumberScheduled) + + if ds.Status.NumberAvailable == ds.Status.DesiredNumberScheduled { + + components.HealthStatus = "health" + + } else { + + components.HealthStatus = "fault" + + } + + result = append(result, components) + + } + + } + + templates = []string{"kube-dns", "heapster", "monitoring-influxdb", "iam", "openpitrix"} + + namespaces := []string{KUBESYSTEM, OPENPITRIX} + + for _, ns := range namespaces { + + deployList, err := k8sClient.AppsV1beta1().Deployments(ns).List(option) + + if err != nil { + + glog.Error(err) + + return result, err + } + + if len(deployList.Items) > 0 { + + for _, dm := range deployList.Items { + + for _, template := range templates { + + if strings.Contains(dm.Name, template) { + + components.Name = dm.Name + components.Kind = "Deployment" + components.SelfLink = dm.SelfLink + components.Replicas = int(dm.Status.Replicas) + version := strings.Split(dm.Spec.Template.Spec.Containers[0].Image, ":") + if len(version) < 2 { + + components.Version = "latest" + + } else { + + components.Version = version[1] + + } + + components.UpdateTime = dm.Status.Conditions[0].LastUpdateTime.Time + + if dm.Status.AvailableReplicas == dm.Status.Replicas { + + components.HealthStatus = "health" + + } else { + + components.HealthStatus = "fault" + + } + + result = append(result, components) + + } + + } + + } + + } + + } + + return result, nil + +} diff --git a/pkg/models/containers.go b/pkg/models/containers.go index 08210cbda..7636e9493 100644 --- a/pkg/models/containers.go +++ b/pkg/models/containers.go @@ -8,6 +8,7 @@ import ( "github.com/golang/glog" "kubesphere.io/kubesphere/pkg/client" + "kubesphere.io/kubesphere/pkg/constants" ksutil "kubesphere.io/kubesphere/pkg/util" @@ -15,18 +16,8 @@ import ( "strconv" ) -type ResultNameSpaceForContainer struct { - NameSpace string `json:"namespace"` - PodsCount string `json:"pods_count"` - Pods []ResultPodForContainer `json:"pods"` -} - -type ResultPodForContainer struct { - PodName string `json:"pod_name"` - ContainersCount string `json:"containers_count"` - Containers []ResultContainer `json:"containers"` -} type ResultContainer struct { + NodeName string `json:"node_name"` ContainerName string `json:"container_name"` CPURequest string `json:"cpu_request"` CPULimit string `json:"cpu_limit"` @@ -61,128 +52,100 @@ func GetContainers(namespace, podName string) []string { return containers } -func FormatContainersMetrics(nodeName, namespace, podName string) ResultNameSpaceForContainer { - var resultNameSpaceForContainer ResultNameSpaceForContainer - var resultPodsForContainer []ResultPodForContainer +func FormatContainersMetrics(nodeName, namespace, podName string) constants.PageableResponse { - var pods []string - if nodeName == "" { - pods = GetPods(namespace) - } else { - pods = GetPodsForNode(nodeName, namespace) - } - - resultNameSpaceForContainer.NameSpace = namespace - resultNameSpaceForContainer.PodsCount = strconv.Itoa(len(pods)) - - if podName != "" { - var resultPodForContainer ResultPodForContainer - resultPodForContainer.PodName = podName - resultPodForContainer = FormatPodMetricsWithContainers(namespace, podName) - resultPodsForContainer = append(resultPodsForContainer, resultPodForContainer) - resultNameSpaceForContainer.Pods = resultPodsForContainer - return resultNameSpaceForContainer - } - for _, pod := range pods { - var resultPodForContainer ResultPodForContainer - resultPodForContainer.PodName = pod - resultPodForContainer = FormatPodMetricsWithContainers(namespace, pod) - resultPodsForContainer = append(resultPodsForContainer, resultPodForContainer) - } - resultNameSpaceForContainer.Pods = resultPodsForContainer - return resultNameSpaceForContainer -} - -func FormatPodMetricsWithContainers(namespace, pod string) ResultPodForContainer { - - var resultPod ResultPodForContainer + var result constants.PageableResponse + var resultContainer ResultContainer var containers []string - var resultContainers []ResultContainer + var total_count int + containers = GetContainers(namespace, podName) - resultPod.PodName = pod - containers = GetContainers(namespace, pod) - resultPod.ContainersCount = strconv.Itoa(len(containers)) - - for _, container := range containers { - var resultContainer ResultContainer - var containerCPUMetrics []CPUContainer - var containerMemMetrics []MemoryContainer - var cpuMetrics CPUContainer - var memMetrics MemoryContainer - - resultContainer.ContainerName = container - cpuRequest := client.GetHeapsterMetrics("/namespaces/" + namespace + "/pods/" + pod + "/containers/" + container + "/metrics/cpu/request") - cpuRequest = ksutil.JsonRawMessage(cpuRequest).Find("metrics").ToList()[0].Find("value").ToString() - if cpuRequest != "" && cpuRequest != "0" { - resultContainer.CPURequest = cpuRequest + for i, container := range containers { + resultContainer = FormatContainerMetrics(namespace, podName, container) + if nodeName != "" { + resultContainer.NodeName = nodeName } else { - resultContainer.CPURequest = "inf" + resultContainer.NodeName = GetNodeNameForPod(podName, namespace) } - - cpuLimit := client.GetHeapsterMetrics("/namespaces/" + namespace + "/pods/" + pod + "/containers/" + container + "/metrics/cpu/limit") - cpuLimit = ksutil.JsonRawMessage(cpuLimit).Find("metrics").ToList()[0].Find("value").ToString() - if cpuLimit != "" && cpuLimit != "0" { - resultContainer.CPULimit = cpuLimit - } else { - resultContainer.CPULimit = "inf" - } - memoryRequest := client.GetHeapsterMetrics("/namespaces/" + namespace + "/pods/" + pod + "/containers/" + container + "/metrics/memory/request") - resultContainer.MemoryRequest = ConvertMemory(memoryRequest) - - memoryLimit := client.GetHeapsterMetrics("/namespaces/" + namespace + "/pods/" + pod + "/containers/" + container + "/metrics/memory/limit") - resultContainer.MemoryLimit = ConvertMemory(memoryLimit) - - cpuUsageRate := client.GetHeapsterMetrics("/namespaces/" + namespace + "/pods/" + pod + "/containers/" + container + "/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 resultContainer.CPULimit != "inf" { - cpu_limit, _ := strconv.ParseFloat(resultContainer.CPULimit, 64) - cpuMetrics.UsedCPU = fmt.Sprintf("%.1f", cpu_limit*cpu_utilization/1000) - } else { - cpuMetrics.UsedCPU = "inf" - } - glog.Info("pod " + pod + " has limit cpu " + resultContainer.CPULimit + " CPU utilization " + fmt.Sprintf("%.3f", cpu_utilization/1000) + " at time" + timestamp.ToString()) - containerCPUMetrics = append(containerCPUMetrics, cpuMetrics) - } - - } - - resultContainer.CPU = containerCPUMetrics - - var used_mem_bytes float64 - - memUsage := client.GetHeapsterMetrics("/namespaces/" + namespace + "/pods/" + pod + "/containers/" + container + "/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 resultContainer.MemoryLimit != "inf" { - mem_limit, _ := strconv.ParseFloat(resultContainer.MemoryLimit, 64) - memMetrics.MemoryUtilization = fmt.Sprintf("%.3f", used_mem/mem_limit) - } else { - memMetrics.MemoryUtilization = "inf" - } - - glog.Info("pod " + pod + " has limit mem " + resultContainer.MemoryLimit + " mem utilization " + memMetrics.MemoryUtilization + " at time" + timestamp.ToString()) - containerMemMetrics = append(containerMemMetrics, memMetrics) - } - } - - resultContainer.Memory = containerMemMetrics - resultContainers = append(resultContainers, resultContainer) + result.Items = append(result.Items, resultContainer) + total_count = i } - resultPod.Containers = resultContainers + result.TotalCount = total_count + 1 - return resultPod + return result +} + +func FormatContainerMetrics(namespace, podName, containerName string) ResultContainer { + var resultContainer ResultContainer + var containerCPUMetrics []CPUContainer + var containerMemMetrics []MemoryContainer + var cpuMetrics CPUContainer + var memMetrics MemoryContainer + + resultContainer.ContainerName = containerName + cpuRequest := client.GetHeapsterMetrics("/namespaces/" + namespace + "/pods/" + podName + "/containers/" + containerName + "/metrics/cpu/request") + cpuRequest = ksutil.JsonRawMessage(cpuRequest).Find("metrics").ToList()[0].Find("value").ToString() + if cpuRequest != "" && cpuRequest != "0" { + resultContainer.CPURequest = cpuRequest + } else { + resultContainer.CPURequest = "inf" + } + + cpuLimit := client.GetHeapsterMetrics("/namespaces/" + namespace + "/pods/" + podName + "/containers/" + containerName + "/metrics/cpu/limit") + cpuLimit = ksutil.JsonRawMessage(cpuLimit).Find("metrics").ToList()[0].Find("value").ToString() + if cpuLimit != "" && cpuLimit != "0" { + resultContainer.CPULimit = cpuLimit + } else { + resultContainer.CPULimit = "inf" + } + memoryRequest := ksutil.JsonRawMessage(client.GetHeapsterMetrics("/namespaces/" + namespace + "/pods/" + podName + "/containers/" + containerName + "/metrics/memory/request")).Find("metrics").ToList()[0].Find("value").ToString() + resultContainer.MemoryRequest = ConvertMemory(memoryRequest) + + memoryLimit := ksutil.JsonRawMessage(client.GetHeapsterMetrics("/namespaces/" + namespace + "/pods/" + podName + "/containers/" + containerName + "/metrics/memory/limit")).Find("metrics").ToList()[0].Find("value").ToString() + resultContainer.MemoryLimit = ConvertMemory(memoryLimit) + + cpuUsageRate := client.GetHeapsterMetrics("/namespaces/" + namespace + "/pods/" + podName + "/containers/" + containerName + "/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(ConvertCPUUsageRate(metric.Find("value").ToString()), 64) + cpuMetrics.TimeStamp = timestamp.ToString() + cpuMetrics.CPUUtilization = fmt.Sprintf("%.3f", cpu_utilization) + if resultContainer.CPULimit != "inf" { + cpu_limit, _ := strconv.ParseFloat(resultContainer.CPULimit, 64) + cpuMetrics.UsedCPU = fmt.Sprintf("%.1f", cpu_limit*cpu_utilization) + } else { + cpuMetrics.UsedCPU = "inf" + } + glog.Info("pod " + podName + " has limit cpu " + resultContainer.CPULimit + " CPU utilization " + fmt.Sprintf("%.3f", cpu_utilization) + " at time" + timestamp.ToString()) + containerCPUMetrics = append(containerCPUMetrics, cpuMetrics) + } + + } + + resultContainer.CPU = containerCPUMetrics + + var used_mem_bytes float64 + + memUsage := client.GetHeapsterMetrics("/namespaces/" + namespace + "/pods/" + podName + "/containers/" + containerName + "/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) + memMetrics.MemoryUtilization = fmt.Sprintf("%.3f", CalculateMemoryUsage(memoryRequest, memoryLimit, metric.Find("value").ToString())) + glog.Info("pod " + podName + " has limit mem " + resultContainer.MemoryLimit + " mem utilization " + memMetrics.MemoryUtilization + " at time" + timestamp.ToString()) + containerMemMetrics = append(containerMemMetrics, memMetrics) + } + } + + resultContainer.Memory = containerMemMetrics + + return resultContainer } diff --git a/pkg/models/nodes.go b/pkg/models/nodes.go index b0ff6ccde..9a0a28d04 100644 --- a/pkg/models/nodes.go +++ b/pkg/models/nodes.go @@ -23,24 +23,38 @@ import ( "github.com/golang/glog" - "kubesphere.io/kubesphere/pkg/client" - - ksutil "kubesphere.io/kubesphere/pkg/util" + v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "kubesphere.io/kubesphere/pkg/client" + ksutil "kubesphere.io/kubesphere/pkg/util" "fmt" "strconv" ) -type ResultNodes struct { - Nodes []ResultNode `json:"nodes"` -} +const ( + //status: "False" + OutOfDisk = "OutOfDisk" + //status: "False" + MemoryPressure = "MemoryPressure" + //status: "False" + DiskPressure = "DiskPressure" + //status: "False" + PIDPressure = "PIDPressure" + //status: "True" + KubeletReady = "Ready" +) + type ResultNode struct { - NodeName string `json:"node_name"` - PodsCount string `json:"pods_count"` - PodsCapacity string `json:"pods_capacity"` - CPU []CPUNode `json:"cpu"` - Memory []MemoryNode `json:"memory"` + NodeName string `json:"node_name"` + NodeStatus string `json:"node_status"` + PodsCount string `json:"pods_count"` + PodsCapacity string `json:"pods_capacity"` + UsedFS string `json:"used_fs"` + TotalFS string `json:"total_fs"` + FSUtilization string `json:"fs_utilization"` + CPU []CPUNode `json:"cpu"` + Memory []MemoryNode `json:"memory"` } type CPUNode struct { @@ -98,13 +112,13 @@ func FormatNodeMetrics(nodeName string) ResultNode { for _, metric := range metrics { timestamp := metric.Find("timestamp") - cpu_utilization, _ := strconv.ParseFloat(metric.Find("value").ToString(), 64) + cpu_utilization, _ := strconv.ParseFloat(ConvertCPUUsageRate(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) + cpuMetrics.CPUUtilization = fmt.Sprintf("%.3f", cpu_utilization) + cpuMetrics.UsedCPU = fmt.Sprintf("%.1f", total_cpu*cpu_utilization) - glog.Info("node " + nodeName + " has total cpu " + fmt.Sprintf("%.1f", total_cpu) + " CPU utilization " + fmt.Sprintf("%.3f", cpu_utilization/1000) + " at time" + timestamp.ToString()) + glog.Info("node " + nodeName + " has total cpu " + fmt.Sprintf("%.1f", total_cpu) + " CPU utilization " + fmt.Sprintf("%.3f", cpu_utilization) + " at time" + timestamp.ToString()) nodeCPUMetrics = append(nodeCPUMetrics, cpuMetrics) } @@ -139,25 +153,65 @@ func FormatNodeMetrics(nodeName string) ResultNode { } resultNode.NodeName = nodeName - resultNode.PodsCount = strconv.Itoa(len(GetPodsForNode(nodeName,""))) - resultNode.PodsCapacity = getPodsCapacity(nodeName) + resultNode.PodsCount = strconv.Itoa(len(GetPodsForNode(nodeName, ""))) + nodeResObj := getNodeResObj(nodeName) + resultNode.PodsCapacity = nodeResObj.Status.Capacity.Pods().String() + resultNode.NodeStatus = getNodeStatus(nodeResObj) + resultNode.UsedFS, resultNode.TotalFS, resultNode.FSUtilization = getNodeFileSystemStatus(nodeResObj) resultNode.CPU = nodeCPUMetrics resultNode.Memory = nodeMemMetrics return resultNode } -func getPodsCapacity(nodeName string) string { - var pods_capacity string +func getNodeResObj(nodeName string) *v1.Node { cli := client.NewK8sClient() - node, err := cli.CoreV1().Nodes().Get(nodeName,metav1.GetOptions{}) + node, err := cli.CoreV1().Nodes().Get(nodeName, metav1.GetOptions{}) if err != nil { glog.Error(err) } else { - pods_capacity = node.Status.Capacity.Pods().String() + return node } - fmt.Println(pods_capacity) - return pods_capacity -} \ No newline at end of file + return nil +} + +func getNodeStatus(node *v1.Node) string { + + status := "Ready" + conditions := node.Status.Conditions + for _, cond := range conditions { + if cond.Type == DiskPressure && cond.Status == "True" { + status = "NotReady" + break + } + if cond.Type == OutOfDisk && cond.Status == "True" { + status = "NotReady" + break + } + if cond.Type == MemoryPressure && cond.Status == "True" { + status = "NotReady" + } + if cond.Type == PIDPressure && cond.Status == "True" { + status = "NotReady" + break + } + if cond.Type == KubeletReady && cond.Status == "False" { + status = "NotReady" + break + } + } + return status +} + +func getNodeFileSystemStatus(node *v1.Node) (string, string, string) { + + nodeMetricsAsStr := client.GetCAdvisorMetrics(node.Annotations["alpha.kubernetes.io/provided-node-ip"]) + if nodeMetricsAsStr != "" { + usedBytesAsStr, _ := strconv.ParseFloat(ksutil.JsonRawMessage(nodeMetricsAsStr).Find("node").Find("fs").Find("usedBytes").ToString(), 64) + capacityBytesAsStr, _ := strconv.ParseFloat(ksutil.JsonRawMessage(nodeMetricsAsStr).Find("node").Find("fs").Find("capacityBytes").ToString(), 64) + return fmt.Sprintf("%.1f", usedBytesAsStr/1024/1024/1024), fmt.Sprintf("%.1f", capacityBytesAsStr/1024/1024/1024), fmt.Sprintf("%.3f", usedBytesAsStr/capacityBytesAsStr) + } + return "", "", "" +} diff --git a/pkg/models/pods.go b/pkg/models/pods.go index 6f158209c..f9e143ef9 100644 --- a/pkg/models/pods.go +++ b/pkg/models/pods.go @@ -15,18 +15,14 @@ import ( "strconv" "k8s.io/apimachinery/pkg/apis/meta/v1" + "kubesphere.io/kubesphere/pkg/constants" + "math" ) -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"` + NameSpace string `json:"namespace"` + NodeName string `json:"node_name"` CPURequest string `json:"cpu_request"` CPULimit string `json:"cpu_limit"` MemoryRequest string `json:"mem_request"` @@ -88,9 +84,9 @@ func GetPodsForNode(nodeName, namespace string) []string { return pods } -func FormatPodsMetrics(nodeName, namespace string) ResultNameSpace { - var resultNameSpace ResultNameSpace - var resultPods []ResultPod +func FormatPodsMetrics(nodeName, namespace string) constants.PageableResponse { + var result constants.PageableResponse + var resultPod ResultPod var pods []string if nodeName == "" { @@ -99,15 +95,19 @@ func FormatPodsMetrics(nodeName, namespace string) ResultNameSpace { pods = GetPodsForNode(nodeName, namespace) } - resultNameSpace.NameSpace = namespace - resultNameSpace.PodsCount = strconv.Itoa(len(pods)) - - for _, pod := range pods { + var total_count int + for i, pod := range pods { resultPod = FormatPodMetrics(namespace, pod) - resultPods = append(resultPods, resultPod) + if nodeName != "" { + resultPod.NodeName = nodeName + } else { + resultPod.NodeName = GetNodeNameForPod(pod, namespace) + } + result.Items = append(result.Items, resultPod) + total_count = i } - resultNameSpace.Pods = resultPods - return resultNameSpace + result.TotalCount = total_count + 1 + return result } func FormatPodMetrics(namespace, pod string) ResultPod { @@ -119,6 +119,7 @@ func FormatPodMetrics(namespace, pod string) ResultPod { var memMetrics MemoryPod resultPod.PodName = pod + resultPod.NameSpace = namespace 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" { @@ -134,10 +135,10 @@ func FormatPodMetrics(namespace, pod string) ResultPod { } else { resultPod.CPULimit = "inf" } - memoryRequest := client.GetHeapsterMetrics("/namespaces/" + namespace + "/pods/" + pod + "/metrics/memory/request") + memoryRequest := ksutil.JsonRawMessage(client.GetHeapsterMetrics("/namespaces/" + namespace + "/pods/" + pod + "/metrics/memory/request")).Find("metrics").ToList()[0].Find("value").ToString() resultPod.MemoryRequest = ConvertMemory(memoryRequest) - memoryLimit := client.GetHeapsterMetrics("/namespaces/" + namespace + "/pods/" + pod + "/metrics/memory/limit") + memoryLimit := ksutil.JsonRawMessage(client.GetHeapsterMetrics("/namespaces/" + namespace + "/pods/" + pod + "/metrics/memory/limit")).Find("metrics").ToList()[0].Find("value").ToString() resultPod.MemoryLimit = ConvertMemory(memoryLimit) cpuUsageRate := client.GetHeapsterMetrics("/namespaces/" + namespace + "/pods/" + pod + "/metrics/cpu/usage_rate") @@ -146,16 +147,16 @@ func FormatPodMetrics(namespace, pod string) ResultPod { for _, metric := range metrics { timestamp := metric.Find("timestamp") - cpu_utilization, _ := strconv.ParseFloat(metric.Find("value").ToString(), 64) + cpu_utilization, _ := strconv.ParseFloat(ConvertCPUUsageRate(metric.Find("value").ToString()), 64) cpuMetrics.TimeStamp = timestamp.ToString() - cpuMetrics.CPUUtilization = fmt.Sprintf("%.3f", cpu_utilization/1000) + cpuMetrics.CPUUtilization = fmt.Sprintf("%.3f", cpu_utilization) if resultPod.CPULimit != "inf" { cpu_limit, _ := strconv.ParseFloat(resultPod.CPULimit, 64) - cpuMetrics.UsedCPU = fmt.Sprintf("%.1f", cpu_limit*cpu_utilization/1000) + cpuMetrics.UsedCPU = fmt.Sprintf("%.1f", cpu_limit*cpu_utilization) } 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()) + glog.Info("pod " + pod + " has limit cpu " + resultPod.CPULimit + " CPU utilization " + fmt.Sprintf("%.3f", cpu_utilization) + " at time" + timestamp.ToString()) podCPUMetrics = append(podCPUMetrics, cpuMetrics) } @@ -175,12 +176,7 @@ func FormatPodMetrics(namespace, pod string) ResultPod { 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" - } + memMetrics.MemoryUtilization = fmt.Sprintf("%.3f", CalculateMemoryUsage(memoryRequest, memoryLimit, metric.Find("value").ToString())) glog.Info("pod " + pod + " has limit mem " + resultPod.MemoryLimit + " mem utilization " + memMetrics.MemoryUtilization + " at time" + timestamp.ToString()) podMemMetrics = append(podMemMetrics, memMetrics) @@ -195,11 +191,8 @@ 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 memBytes != "" && memBytes != "0" { + memBytes, error := strconv.ParseFloat(memBytes, 64) if error == nil { mem = fmt.Sprintf("%.3f", memBytes/1024/1024) } else { @@ -215,7 +208,65 @@ func ConvertMemory(memBytes string) string { return mem } -func getNodeNameForPod(podName, namespace string) string { +func CalculateMemoryUsage(requestMem, limitMem, usedMem string) float64 { + var requestMemInBytes, limitMemInBytes, usedMemInBytes, memUsage float64 + if requestMem != "" && requestMem != "0" && requestMem != "inf" { + requestMemInBytes, _ = strconv.ParseFloat(requestMem, 64) + } else { + glog.Info("memory request is not set") + requestMemInBytes = 0 + } + if limitMem != "" && limitMem != "0" && limitMem != "inf" { + limitMemInBytes, _ = strconv.ParseFloat(limitMem, 64) + } else { + glog.Info("memory limit is not set") + limitMemInBytes = 0 + } + if usedMem != "" && usedMem != "0" { + usedMemInBytes, _ = strconv.ParseFloat(usedMem, 64) + } else { + usedMemInBytes = 0 + } + + if usedMemInBytes > 0 { + if requestMemInBytes > 0 && limitMemInBytes > 0 { + if usedMemInBytes > requestMemInBytes { + glog.Info("used memory is higher than memory request") + memUsage = usedMemInBytes / limitMemInBytes + } else { + memUsage = usedMemInBytes / requestMemInBytes + } + } else if requestMemInBytes > 0 && limitMemInBytes == 0 { + if usedMemInBytes > requestMemInBytes { + glog.Info("used memory is higher than memory request") + memUsage = 0 + } else { + memUsage = usedMemInBytes / requestMemInBytes + } + } else if requestMemInBytes == 0 && limitMemInBytes > 0 { + if usedMemInBytes <= limitMemInBytes { + memUsage = usedMemInBytes / limitMemInBytes + } + } else { + memUsage = 0 + } + } else { + memUsage = 0 + } + return memUsage +} + +func ConvertCPUUsageRate(cpuUsageRate string) string { + if cpuUsageRate != "" && cpuUsageRate != "0" { + rate, _ := strconv.ParseFloat(cpuUsageRate, 64) + rateBase := math.Pow10(strings.Count(cpuUsageRate, "") - 1) + return fmt.Sprintf("%.3f", rate/rateBase) + } else { + return "0" + } +} + +func GetNodeNameForPod(podName, namespace string) string { var nodeName string cli := client.NewK8sClient() diff --git a/pkg/models/registries.go b/pkg/models/registries.go index 04ba8b885..2a4b2d4e9 100644 --- a/pkg/models/registries.go +++ b/pkg/models/registries.go @@ -22,9 +22,7 @@ import ( "github.com/docker/docker/client" "github.com/docker/docker/api/types" - - "kubesphere.io/kubesphere/pkg/constants" - + "github.com/golang/glog" ) @@ -34,14 +32,19 @@ type AuthInfo struct { ServerHost string `json:"serverhost"` } +type ValidationMsg struct { + + Message string `json:"message"` + Reason string `json:"reason"` +} + + const DOCKERCLIENTERROR = "Docker client error" -func RegistryLoginAuth(authinfo AuthInfo) constants.ResultMessage { +func RegistryLoginAuth(authinfo AuthInfo) ValidationMsg { - var result constants.ResultMessage - - data := make(map[string]interface{}) + var result ValidationMsg datastr := []byte(authinfo.Username + ":" + authinfo.Password) auth := base64.StdEncoding.EncodeToString(datastr) @@ -50,8 +53,8 @@ func RegistryLoginAuth(authinfo AuthInfo) constants.ResultMessage { if err != nil { - data["message"] = DOCKERCLIENTERROR - data["reason"] = err.Error() + glog.Error(err) + } authcfg := types.AuthConfig{ @@ -68,25 +71,21 @@ func RegistryLoginAuth(authinfo AuthInfo) constants.ResultMessage { if err != nil { - data["message"] = DOCKERCLIENTERROR - data["reason"] = err.Error() + glog.Error(err) } if authmsg.Status == "Login Succeeded" { - data["message"] = "Verified" + result.Message = "Verified" } else { - data["message"] = "Unverified" - data["reason"] = "Username or password is incorrect " + result.Message = "Unverified" + result.Reason = "Username or password is incorrect " } - result.Data = data - - return result }