Rainbond/api/handler/cluster.go
2020-09-06 11:09:48 +08:00

187 lines
5.5 KiB
Go

package handler
import (
"fmt"
"runtime"
"github.com/sirupsen/logrus"
"github.com/goodrain/rainbond/api/model"
"github.com/shirou/gopsutil/disk"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/client-go/kubernetes"
)
// ClusterHandler -
type ClusterHandler interface {
GetClusterInfo() (*model.ClusterResource, error)
}
// NewClusterHandler -
func NewClusterHandler(clientset *kubernetes.Clientset) ClusterHandler {
return &clusterAction{
clientset: clientset,
}
}
type clusterAction struct {
clientset *kubernetes.Clientset
}
func (c *clusterAction) GetClusterInfo() (*model.ClusterResource, error) {
nodes, err := c.listNodes()
if err != nil {
return nil, fmt.Errorf("[GetClusterInfo] list nodes: %v", err)
}
var healthCapCPU, healthCapMem, unhealthCapCPU, unhealthCapMem int64
nodeLen := len(nodes)
_ = nodeLen
usedNodeList := make([]*corev1.Node, len(nodes))
for i := range nodes {
node := nodes[i]
if !isNodeReady(node) {
logrus.Debugf("[GetClusterInfo] node(%s) not ready", node.GetName())
unhealthCapCPU += node.Status.Allocatable.Cpu().Value()
unhealthCapMem += node.Status.Allocatable.Memory().Value()
continue
}
healthCapCPU += node.Status.Allocatable.Cpu().Value()
healthCapMem += node.Status.Allocatable.Memory().Value()
if node.Spec.Unschedulable == false {
usedNodeList[i] = node
}
}
var healthcpuR, healthmemR, unhealthCPUR, unhealthMemR int64
nodeAllocatableResourceList := make(map[string]*model.NodeResource, len(usedNodeList))
var maxAllocatableMemory *model.NodeResource
for i := range usedNodeList {
node := usedNodeList[i]
pods, err := c.listPods(node.Name)
if err != nil {
return nil, fmt.Errorf("list pods: %v", err)
}
nodeAllocatableResource := model.NewResource(node.Status.Allocatable)
for _, pod := range pods {
nodeAllocatableResource.AllowedPodNumber--
for _, c := range pod.Spec.Containers {
nodeAllocatableResource.Memory -= c.Resources.Requests.Memory().Value()
nodeAllocatableResource.MilliCPU -= c.Resources.Requests.Cpu().MilliValue()
nodeAllocatableResource.EphemeralStorage -= c.Resources.Requests.StorageEphemeral().Value()
if isNodeReady(node) {
healthcpuR += c.Resources.Requests.Cpu().MilliValue()
healthmemR += c.Resources.Requests.Memory().Value()
} else {
unhealthCPUR += c.Resources.Requests.Cpu().MilliValue()
unhealthMemR += c.Resources.Requests.Memory().Value()
}
}
}
nodeAllocatableResourceList[node.Name] = nodeAllocatableResource
// Gets the node resource with the maximum remaining scheduling memory
if maxAllocatableMemory == nil {
maxAllocatableMemory = nodeAllocatableResource
} else {
if nodeAllocatableResource.Memory > maxAllocatableMemory.Memory {
maxAllocatableMemory = nodeAllocatableResource
}
}
}
var diskstauts *disk.UsageStat
if runtime.GOOS != "windows" {
diskstauts, _ = disk.Usage("/grdata")
} else {
diskstauts, _ = disk.Usage(`z:\\`)
}
var diskCap, reqDisk uint64
if diskstauts != nil {
diskCap = diskstauts.Total
reqDisk = diskstauts.Used
}
result := &model.ClusterResource{
CapCPU: int(healthCapCPU + unhealthCapCPU),
CapMem: int(healthCapMem+unhealthCapMem) / 1024 / 1024,
HealthCapCPU: int(healthCapCPU),
HealthCapMem: int(healthCapMem) / 1024 / 1024,
UnhealthCapCPU: int(unhealthCapCPU),
UnhealthCapMem: int(unhealthCapMem) / 1024 / 1024,
ReqCPU: float32(healthcpuR+unhealthCPUR) / 1000,
ReqMem: int(healthmemR+unhealthMemR) / 1024 / 1024,
HealthReqCPU: float32(healthcpuR) / 1000,
HealthReqMem: int(healthmemR) / 1024 / 1024,
UnhealthReqCPU: float32(unhealthCPUR) / 1000,
UnhealthReqMem: int(unhealthMemR) / 1024 / 1024,
ComputeNode: len(nodes),
CapDisk: diskCap,
ReqDisk: reqDisk,
MaxAllocatableMemoryNodeResource: maxAllocatableMemory,
}
result.AllNode = len(nodes)
for _, node := range nodes {
if !isNodeReady(node) {
result.NotReadyNode++
}
}
return result, nil
}
func (c *clusterAction) listNodes() ([]*corev1.Node, error) {
opts := metav1.ListOptions{}
nodeList, err := c.clientset.CoreV1().Nodes().List(opts)
if err != nil {
return nil, err
}
var nodes []*corev1.Node
for idx := range nodeList.Items {
node := &nodeList.Items[idx]
// check if node contains taints
if containsTaints(node) {
logrus.Debugf("[GetClusterInfo] node(%s) contains NoSchedule taints", node.GetName())
continue
}
nodes = append(nodes, node)
}
return nodes, nil
}
func isNodeReady(node *corev1.Node) bool {
for _, cond := range node.Status.Conditions {
if cond.Type == corev1.NodeReady && cond.Status == corev1.ConditionTrue {
return true
}
}
return false
}
func containsTaints(node *corev1.Node) bool {
for _, taint := range node.Spec.Taints {
if taint.Effect == corev1.TaintEffectNoSchedule {
return true
}
}
return false
}
func (c *clusterAction) listPods(nodeName string) (pods []corev1.Pod, err error) {
podList, err := c.clientset.CoreV1().Pods(metav1.NamespaceAll).List(metav1.ListOptions{
FieldSelector: fields.SelectorFromSet(fields.Set{"spec.nodeName": nodeName}).String()})
if err != nil {
return pods, err
}
return podList.Items, nil
}