فهرست منبع

add cluster costs

AjayTripathy 6 سال پیش
والد
کامیت
591545faf4
4فایلهای تغییر یافته به همراه149 افزوده شده و 40 حذف شده
  1. 10 0
      cloud/gcpprovider.go
  2. 99 18
      costmodel/cluster.go
  3. 24 22
      costmodel/costmodel.go
  4. 16 0
      main.go

+ 10 - 0
cloud/gcpprovider.go

@@ -567,6 +567,12 @@ func (gcp *GCP) parsePages(inputKeys map[string]Key, pvKeys map[string]PVKey) (m
 		return nil, err
 		return nil, err
 	}
 	}
 	returnPages := make(map[string]*GCPPricing)
 	returnPages := make(map[string]*GCPPricing)
+	for _, page := range pages {
+		klog.V(1).Infof("Page: %s : %+v", page)
+		for k, v := range page {
+			klog.V(1).Infof("Unmerged Page: %s : %+v", k, v)
+		}
+	}
 	for _, page := range pages {
 	for _, page := range pages {
 		for k, v := range page {
 		for k, v := range page {
 			if val, ok := returnPages[k]; ok { //keys may need to be merged
 			if val, ok := returnPages[k]; ok { //keys may need to be merged
@@ -591,6 +597,10 @@ func (gcp *GCP) parsePages(inputKeys map[string]Key, pvKeys map[string]PVKey) (m
 			}
 			}
 		}
 		}
 	}
 	}
+	klog.V(1).Infof("ALL PAGES: %+v", returnPages)
+	for k, v := range returnPages {
+		klog.V(1).Infof("Returned Page: %s : %+v", k, v.Node)
+	}
 	return returnPages, err
 	return returnPages, err
 }
 }
 
 

+ 99 - 18
costmodel/cluster.go

@@ -25,20 +25,12 @@ const (
 	  ) +
 	  ) +
 	  sum(avg(container_fs_limit_bytes{device!="tmpfs", id="/"} %s) by (instance) / 1024 / 1024 / 1024) * 0.04`
 	  sum(avg(container_fs_limit_bytes{device!="tmpfs", id="/"} %s) by (instance) / 1024 / 1024 / 1024) * 0.04`
 
 
-	queryTotal = `sum(
-		avg(kube_node_status_capacity_cpu_cores %s) by (node) * avg(node_cpu_hourly_cost %s) by (node) * 730 +
-		avg(node_gpu_hourly_cost %s) by (node) * 730 
-	  ) +
-
+	queryTotal = `sum(avg(node_total_hourly_cost) by (node)) * 730 +
 	  sum(
 	  sum(
-		avg(kube_node_status_capacity_memory_bytes %s) by (node) / 1024 / 1024 / 1024 * avg(node_ram_hourly_cost %s) by (node) * 730
+		avg(avg_over_time(pv_hourly_cost[1h])) by (persistentvolume) * 730 
+		* avg(avg_over_time(kube_persistentvolume_capacity_bytes[1h])) by (persistentvolume) / 1024 / 1024 / 1024
 	  ) +
 	  ) +
-
-	  sum(
-		avg(avg_over_time(pv_hourly_cost[%s] %s)) by (persistentvolume) * 730 
-		* avg(avg_over_time(kube_persistentvolume_capacity_bytes[%s] %s)) by (persistentvolume) / 1024 / 1024 / 1024
-	  ) +
-	  sum(avg(container_fs_limit_bytes{device!="tmpfs", id="/"} %s) by (instance) / 1024 / 1024 / 1024) * 0.04`
+	  sum(avg(container_fs_limit_bytes{device!="tmpfs", id="/"}) by (instance) / 1024 / 1024 / 1024) * 0.04`
 )
 )
 
 
 type Totals struct {
 type Totals struct {
@@ -48,7 +40,7 @@ type Totals struct {
 	StorageCost [][]string `json:"storageCost"`
 	StorageCost [][]string `json:"storageCost"`
 }
 }
 
 
-func resultToTotal(qr interface{}) ([][]string, error) {
+func resultToTotals(qr interface{}) ([][]string, error) {
 	data, ok := qr.(map[string]interface{})["data"]
 	data, ok := qr.(map[string]interface{})["data"]
 	if !ok {
 	if !ok {
 		return nil, fmt.Errorf("Improperly formatted response from prometheus, response %+v has no data field", data)
 		return nil, fmt.Errorf("Improperly formatted response from prometheus, response %+v has no data field", data)
@@ -82,6 +74,95 @@ func resultToTotal(qr interface{}) ([][]string, error) {
 	return totals, nil
 	return totals, nil
 }
 }
 
 
+func resultToTotal(qr interface{}) ([][]string, error) {
+	data, ok := qr.(map[string]interface{})["data"]
+	if !ok {
+		return nil, fmt.Errorf("Improperly formatted response from prometheus, response %+v has no data field", data)
+	}
+	r, ok := data.(map[string]interface{})["result"]
+	if !ok {
+		return nil, fmt.Errorf("Improperly formatted data from prometheus, data has no result field")
+	}
+	results, ok := r.([]interface{})
+	if !ok {
+		return nil, fmt.Errorf("Improperly formatted results from prometheus, result field is not a slice")
+	}
+	val, ok := results[0].(map[string]interface{})["value"]
+	totals := [][]string{}
+	if !ok {
+		return nil, fmt.Errorf("Improperly formatted results from prometheus, value is not a field in the vector")
+	}
+	dataPoint, ok := val.([]interface{})
+	//log.Printf("%+v", dataPoint)
+	if !ok || len(dataPoint) != 2 {
+		return nil, fmt.Errorf("Improperly formatted datapoint from Prometheus")
+	}
+	d0 := fmt.Sprintf("%f", dataPoint[0].(float64))
+	toAppend := []string{
+		d0,
+		dataPoint[1].(string),
+	}
+	totals = append(totals, toAppend)
+	return totals, nil
+}
+
+// ClusterCostsOverTime gives the current full cluster costs averaged over a window of time.
+func ClusterCosts(cli prometheusClient.Client, windowString, offset string) (*Totals, error) {
+
+	qCores := fmt.Sprintf(queryClusterCores, offset, offset, offset)
+	qRAM := fmt.Sprintf(queryClusterRAM, offset, offset)
+	qStorage := fmt.Sprintf(queryStorage, windowString, offset, windowString, offset, offset)
+	qTotal := fmt.Sprintf(queryTotal)
+	log.Printf("%s", qTotal)
+
+	resultClusterCores, err := query(cli, qCores)
+	if err != nil {
+		return nil, err
+	}
+	resultClusterRAM, err := query(cli, qRAM)
+	if err != nil {
+		return nil, err
+	}
+
+	resultStorage, err := query(cli, qStorage)
+	if err != nil {
+		return nil, err
+	}
+
+	resultTotal, err := query(cli, qTotal)
+	if err != nil {
+		return nil, err
+	}
+
+	coreTotal, err := resultToTotal(resultClusterCores)
+	if err != nil {
+		return nil, err
+	}
+
+	ramTotal, err := resultToTotal(resultClusterRAM)
+	if err != nil {
+		return nil, err
+	}
+
+	storageTotal, err := resultToTotal(resultStorage)
+	if err != nil {
+		return nil, err
+	}
+
+	clusterTotal, err := resultToTotal(resultTotal)
+	if err != nil {
+		return nil, err
+	}
+
+	return &Totals{
+		TotalCost:   clusterTotal,
+		CPUCost:     coreTotal,
+		MemCost:     ramTotal,
+		StorageCost: storageTotal,
+	}, nil
+
+}
+
 // ClusterCostsOverTime gives the full cluster costs over time
 // ClusterCostsOverTime gives the full cluster costs over time
 func ClusterCostsOverTime(cli prometheusClient.Client, startString, endString, windowString, offset string) (*Totals, error) {
 func ClusterCostsOverTime(cli prometheusClient.Client, startString, endString, windowString, offset string) (*Totals, error) {
 
 
@@ -106,7 +187,7 @@ func ClusterCostsOverTime(cli prometheusClient.Client, startString, endString, w
 	qCores := fmt.Sprintf(queryClusterCores, offset, offset, offset)
 	qCores := fmt.Sprintf(queryClusterCores, offset, offset, offset)
 	qRAM := fmt.Sprintf(queryClusterRAM, offset, offset)
 	qRAM := fmt.Sprintf(queryClusterRAM, offset, offset)
 	qStorage := fmt.Sprintf(queryStorage, windowString, offset, windowString, offset, offset)
 	qStorage := fmt.Sprintf(queryStorage, windowString, offset, windowString, offset, offset)
-	qTotal := fmt.Sprintf(queryTotal, offset, offset, offset, offset, offset, windowString, offset, windowString, offset, offset)
+	qTotal := fmt.Sprintf(queryTotal)
 	log.Printf("%s", qTotal)
 	log.Printf("%s", qTotal)
 
 
 	resultClusterCores, err := queryRange(cli, qCores, start, end, window)
 	resultClusterCores, err := queryRange(cli, qCores, start, end, window)
@@ -128,22 +209,22 @@ func ClusterCostsOverTime(cli prometheusClient.Client, startString, endString, w
 		return nil, err
 		return nil, err
 	}
 	}
 
 
-	coreTotal, err := resultToTotal(resultClusterCores)
+	coreTotal, err := resultToTotals(resultClusterCores)
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
 
 
-	ramTotal, err := resultToTotal(resultClusterRAM)
+	ramTotal, err := resultToTotals(resultClusterRAM)
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
 
 
-	storageTotal, err := resultToTotal(resultStorage)
+	storageTotal, err := resultToTotals(resultStorage)
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
 
 
-	clusterTotal, err := resultToTotal(resultTotal)
+	clusterTotal, err := resultToTotals(resultTotal)
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}

+ 24 - 22
costmodel/costmodel.go

@@ -712,21 +712,23 @@ func getNodeCost(clientset kubernetes.Interface, cloud costAnalyzerCloud.Provide
 			klog.V(1).Infof("Error getting node. Error: " + err.Error())
 			klog.V(1).Infof("Error getting node. Error: " + err.Error())
 			continue
 			continue
 		}
 		}
+		newCnode := *cnode
 
 
 		var cpu float64
 		var cpu float64
-		if cnode.VCPU == "" {
+		if newCnode.VCPU == "" {
 			cpu = float64(n.Status.Capacity.Cpu().Value())
 			cpu = float64(n.Status.Capacity.Cpu().Value())
-			cnode.VCPU = n.Status.Capacity.Cpu().String()
+			newCnode.VCPU = n.Status.Capacity.Cpu().String()
 		} else {
 		} else {
-			cpu, _ = strconv.ParseFloat(cnode.VCPU, 64)
+			cpu, _ = strconv.ParseFloat(newCnode.VCPU, 64)
 		}
 		}
 		var ram float64
 		var ram float64
-		if cnode.RAM == "" {
-			cnode.RAM = n.Status.Capacity.Memory().String()
+		if newCnode.RAM == "" {
+			newCnode.RAM = n.Status.Capacity.Memory().String()
 		}
 		}
 		ram = float64(n.Status.Capacity.Memory().Value())
 		ram = float64(n.Status.Capacity.Memory().Value())
+		newCnode.RAMBytes = fmt.Sprintf("%f", ram)
 
 
-		if cnode.GPU != "" && cnode.GPUCost == "" { // We couldn't find a gpu cost, so fix cpu and ram, then accordingly
+		if newCnode.GPU != "" && newCnode.GPUCost == "" { // We couldn't find a gpu cost, so fix cpu and ram, then accordingly
 			klog.V(4).Infof("GPU without cost found for %s, calculating...", cloud.GetKey(nodeLabels).Features())
 			klog.V(4).Infof("GPU without cost found for %s, calculating...", cloud.GetKey(nodeLabels).Features())
 			defaultCPU, err := strconv.ParseFloat(cfg.CPU, 64)
 			defaultCPU, err := strconv.ParseFloat(cfg.CPU, 64)
 			if err != nil {
 			if err != nil {
@@ -749,14 +751,14 @@ func getNodeCost(clientset kubernetes.Interface, cloud costAnalyzerCloud.Provide
 			ramGB := ram / 1024 / 1024 / 1024
 			ramGB := ram / 1024 / 1024 / 1024
 			ramMultiple := gpuToRAMRatio + cpu*cpuToRAMRatio + ramGB
 			ramMultiple := gpuToRAMRatio + cpu*cpuToRAMRatio + ramGB
 			var nodePrice float64
 			var nodePrice float64
-			if cnode.Cost != "" {
-				nodePrice, err = strconv.ParseFloat(cnode.Cost, 64)
+			if newCnode.Cost != "" {
+				nodePrice, err = strconv.ParseFloat(newCnode.Cost, 64)
 				if err != nil {
 				if err != nil {
 					klog.V(3).Infof("Could not parse total node price")
 					klog.V(3).Infof("Could not parse total node price")
 					return nil, err
 					return nil, err
 				}
 				}
 			} else {
 			} else {
-				nodePrice, err = strconv.ParseFloat(cnode.VCPUCost, 64) // all the price was allocated the the CPU
+				nodePrice, err = strconv.ParseFloat(newCnode.VCPUCost, 64) // all the price was allocated the the CPU
 				if err != nil {
 				if err != nil {
 					klog.V(3).Infof("Could not parse node vcpu price")
 					klog.V(3).Infof("Could not parse node vcpu price")
 					return nil, err
 					return nil, err
@@ -766,13 +768,13 @@ func getNodeCost(clientset kubernetes.Interface, cloud costAnalyzerCloud.Provide
 			ramPrice := (nodePrice / ramMultiple)
 			ramPrice := (nodePrice / ramMultiple)
 			cpuPrice := ramPrice * cpuToRAMRatio
 			cpuPrice := ramPrice * cpuToRAMRatio
 			gpuPrice := ramPrice * gpuToRAMRatio
 			gpuPrice := ramPrice * gpuToRAMRatio
-			cnode.VCPUCost = fmt.Sprintf("%f", cpuPrice)
-			cnode.RAMCost = fmt.Sprintf("%f", ramPrice)
-			cnode.RAMBytes = fmt.Sprintf("%f", ram)
-			cnode.GPUCost = fmt.Sprintf("%f", gpuPrice)
+			newCnode.VCPUCost = fmt.Sprintf("%f", cpuPrice)
+			newCnode.RAMCost = fmt.Sprintf("%f", ramPrice)
+			newCnode.RAMBytes = fmt.Sprintf("%f", ram)
+			newCnode.GPUCost = fmt.Sprintf("%f", gpuPrice)
 
 
 		} else {
 		} else {
-			if cnode.RAMCost == "" { // We couldn't find a ramcost, so fix cpu and allocate ram accordingly
+			if newCnode.RAMCost == "" { // We couldn't find a ramcost, so fix cpu and allocate ram accordingly
 				klog.V(4).Infof("No RAM cost found for %s, calculating...", cloud.GetKey(nodeLabels).Features())
 				klog.V(4).Infof("No RAM cost found for %s, calculating...", cloud.GetKey(nodeLabels).Features())
 				defaultCPU, err := strconv.ParseFloat(cfg.CPU, 64)
 				defaultCPU, err := strconv.ParseFloat(cfg.CPU, 64)
 				if err != nil {
 				if err != nil {
@@ -791,14 +793,14 @@ func getNodeCost(clientset kubernetes.Interface, cloud costAnalyzerCloud.Provide
 				ramMultiple := cpu*cpuToRAMRatio + ramGB
 				ramMultiple := cpu*cpuToRAMRatio + ramGB
 
 
 				var nodePrice float64
 				var nodePrice float64
-				if cnode.Cost != "" {
-					nodePrice, err = strconv.ParseFloat(cnode.Cost, 64)
+				if newCnode.Cost != "" {
+					nodePrice, err = strconv.ParseFloat(newCnode.Cost, 64)
 					if err != nil {
 					if err != nil {
 						klog.V(3).Infof("Could not parse total node price")
 						klog.V(3).Infof("Could not parse total node price")
 						return nil, err
 						return nil, err
 					}
 					}
 				} else {
 				} else {
-					nodePrice, err = strconv.ParseFloat(cnode.VCPUCost, 64) // all the price was allocated the the CPU
+					nodePrice, err = strconv.ParseFloat(newCnode.VCPUCost, 64) // all the price was allocated the the CPU
 					if err != nil {
 					if err != nil {
 						klog.V(3).Infof("Could not parse node vcpu price")
 						klog.V(3).Infof("Could not parse node vcpu price")
 						return nil, err
 						return nil, err
@@ -808,14 +810,14 @@ func getNodeCost(clientset kubernetes.Interface, cloud costAnalyzerCloud.Provide
 				ramPrice := (nodePrice / ramMultiple)
 				ramPrice := (nodePrice / ramMultiple)
 				cpuPrice := ramPrice * cpuToRAMRatio
 				cpuPrice := ramPrice * cpuToRAMRatio
 
 
-				cnode.VCPUCost = fmt.Sprintf("%f", cpuPrice)
-				cnode.RAMCost = fmt.Sprintf("%f", ramPrice)
-				cnode.RAMBytes = fmt.Sprintf("%f", ram)
-				klog.V(4).Infof("Computed \"%s\" RAM Cost := %v", name, cnode.RAMCost)
+				newCnode.VCPUCost = fmt.Sprintf("%f", cpuPrice)
+				newCnode.RAMCost = fmt.Sprintf("%f", ramPrice)
+				newCnode.RAMBytes = fmt.Sprintf("%f", ram)
+				klog.V(4).Infof("Computed \"%s\" RAM Cost := %v", name, newCnode.RAMCost)
 			}
 			}
 		}
 		}
 
 
-		nodes[name] = cnode
+		nodes[name] = &newCnode
 	}
 	}
 	return nodes, nil
 	return nodes, nil
 }
 }

+ 16 - 0
main.go

@@ -140,6 +140,21 @@ func (a *Accesses) CostDataModel(w http.ResponseWriter, r *http.Request, ps http
 	}
 	}
 }
 }
 
 
+func (a *Accesses) ClusterCosts(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
+	w.Header().Set("Content-Type", "application/json")
+	w.Header().Set("Access-Control-Allow-Origin", "*")
+
+	window := r.URL.Query().Get("window")
+	offset := r.URL.Query().Get("offset")
+
+	if offset != "" {
+		offset = "offset " + offset
+	}
+
+	data, err := costModel.ClusterCosts(a.PrometheusClient, window, offset)
+	w.Write(wrapData(data, err))
+}
+
 func (a *Accesses) ClusterCostsOverTime(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
 func (a *Accesses) ClusterCostsOverTime(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
 	w.Header().Set("Content-Type", "application/json")
 	w.Header().Set("Content-Type", "application/json")
 	w.Header().Set("Access-Control-Allow-Origin", "*")
 	w.Header().Set("Access-Control-Allow-Origin", "*")
@@ -476,6 +491,7 @@ func main() {
 	router.POST("/updateBigQueryInfoConfigs", a.UpdateBigQueryInfoConfigs)
 	router.POST("/updateBigQueryInfoConfigs", a.UpdateBigQueryInfoConfigs)
 	router.POST("/updateConfigByKey", a.UpdateConfigByKey)
 	router.POST("/updateConfigByKey", a.UpdateConfigByKey)
 	router.GET("/clusterCostsOverTime", a.ClusterCostsOverTime)
 	router.GET("/clusterCostsOverTime", a.ClusterCostsOverTime)
+	router.GET("/clusterCosts", a.ClusterCosts)
 	router.GET("/validatePrometheus", a.GetPrometheusMetadata)
 	router.GET("/validatePrometheus", a.GetPrometheusMetadata)
 
 
 	rootMux := http.NewServeMux()
 	rootMux := http.NewServeMux()