github.com/justinjmoses/evergreen@v0.0.0-20170530173719-1d50e381ff0d/service/rest_stats.go (about) 1 package service 2 3 import ( 4 "fmt" 5 "net/http" 6 "time" 7 8 "github.com/evergreen-ci/evergreen/model" 9 "github.com/evergreen-ci/evergreen/model/build" 10 "github.com/evergreen-ci/evergreen/util" 11 "github.com/gorilla/mux" 12 ) 13 14 // restHostUtilizationBucket represents an aggregate view of the hosts and tasks Bucket for a given time frame. 15 type restHostUtilizationBucket struct { 16 StaticHost int `json:"static_host" csv:"static_host"` 17 DynamicHost int `json:"dynamic_host" csv:"dynamic_host"` 18 Task int `json:"task" csv:"task"` 19 StartTime time.Time `json:"start_time" csv:"start_time"` 20 EndTime time.Time `json:"end_time" csv:"end_time"` 21 } 22 23 // restAvgBucketW is one element in the results of a list of buckets that are created from the agg query. 24 type restAvgBucket struct { 25 Id int `json:"index" csv:"index"` 26 AverageTime int `json:"avg" csv:"avg_time"` 27 NumberTasks int `json:"number_tasks" csv:"number_tasks"` 28 Start time.Time `json:"start_time" csv:"start_time"` 29 End time.Time `json:"end_time" csv:"end_time"` 30 } 31 32 // restMakespanStats represents the actual and predicted makespan for a given build 33 type restMakespanStats struct { 34 ActualMakespan int `json:"actual" csv:"actual"` 35 PredictedMakespan int `json:"predicted" csv:"predicted"` 36 BuildId string `json:"build_id" csv:"build_id"` 37 } 38 39 // getMakespanRatios returns a list of MakespanRatio structs that contain 40 // the actual and predicted makespans for a certain number of recent builds. 41 func getMakespanRatios(numberBuilds int) ([]restMakespanStats, error) { 42 builds, err := build.Find(build.ByRecentlyFinished(numberBuilds)) 43 if err != nil { 44 return nil, err 45 } 46 makespanRatios := []restMakespanStats{} 47 for _, b := range builds { 48 makespanRatios = append(makespanRatios, restMakespanStats{ 49 BuildId: b.Id, 50 PredictedMakespan: int(b.PredictedMakespan), 51 ActualMakespan: int(b.ActualMakespan), 52 }) 53 } 54 return makespanRatios, nil 55 } 56 57 func (restapi *restAPI) getHostUtilizationStats(w http.ResponseWriter, r *http.Request) { 58 // get granularity (in seconds) 59 granularity, err := util.GetIntValue(r, "granularity", 0) 60 if err != nil { 61 restapi.WriteJSON(w, http.StatusBadRequest, responseError{Message: err.Error()}) 62 return 63 } 64 if granularity == 0 { 65 restapi.WriteJSON(w, http.StatusBadRequest, responseError{Message: "invalid granularity"}) 66 return 67 } 68 69 // get number of days back 70 daysBack, err := util.GetIntValue(r, "numberDays", 0) 71 if err != nil { 72 restapi.WriteJSON(w, http.StatusBadRequest, responseError{Message: err.Error()}) 73 return 74 } 75 if daysBack == 0 { 76 restapi.WriteJSON(w, http.StatusBadRequest, responseError{Message: "invalid days back"}) 77 return 78 } 79 80 isCSV, err := util.GetBoolValue(r, "csv", true) 81 if err != nil { 82 restapi.WriteJSON(w, http.StatusBadRequest, responseError{Message: err.Error()}) 83 return 84 } 85 buckets, err := model.CreateAllHostUtilizationBuckets(daysBack, granularity) 86 if err != nil { 87 restapi.WriteJSON(w, http.StatusInternalServerError, responseError{Message: fmt.Sprintf("error getting buckets: %v", err.Error())}) 88 return 89 } 90 restBuckets := []restHostUtilizationBucket{} 91 // convert the time.Durations into integers 92 for _, b := range buckets { 93 r := restHostUtilizationBucket{ 94 StaticHost: int(b.StaticHost), 95 DynamicHost: int(b.DynamicHost), 96 Task: int(b.Task), 97 StartTime: b.StartTime, 98 EndTime: b.EndTime, 99 } 100 restBuckets = append(restBuckets, r) 101 } 102 103 if isCSV { 104 util.WriteCSVResponse(w, http.StatusOK, restBuckets) 105 return 106 } 107 restapi.WriteJSON(w, http.StatusOK, buckets) 108 } 109 110 func (restapi *restAPI) getAverageSchedulerStats(w http.ResponseWriter, r *http.Request) { 111 // get granularity (in seconds) 112 granularity, err := util.GetIntValue(r, "granularity", 0) 113 if err != nil { 114 restapi.WriteJSON(w, http.StatusBadRequest, responseError{Message: err.Error()}) 115 return 116 } 117 if granularity == 0 { 118 restapi.WriteJSON(w, http.StatusBadRequest, responseError{Message: "invalid granularity"}) 119 return 120 } 121 122 // get number of days back 123 daysBack, err := util.GetIntValue(r, "numberDays", 0) 124 if err != nil { 125 restapi.WriteJSON(w, http.StatusBadRequest, responseError{Message: err.Error()}) 126 return 127 } 128 if daysBack == 0 { 129 restapi.WriteJSON(w, http.StatusBadRequest, responseError{Message: "invalid days back"}) 130 return 131 } 132 isCSV, err := util.GetBoolValue(r, "csv", true) 133 if err != nil { 134 restapi.WriteJSON(w, http.StatusBadRequest, responseError{Message: err.Error()}) 135 return 136 } 137 138 distroId := mux.Vars(r)["distro_id"] 139 if distroId == "" { 140 restapi.WriteJSON(w, http.StatusBadRequest, responseError{Message: "invalid distro id"}) 141 return 142 } 143 144 bounds := model.CalculateBounds(daysBack, granularity) 145 146 buckets, err := model.AverageStatistics(distroId, bounds) 147 if err != nil { 148 restapi.WriteJSON(w, http.StatusInternalServerError, responseError{Message: fmt.Sprintf("error getting buckets: %v", err.Error())}) 149 return 150 } 151 restBuckets := []restAvgBucket{} 152 // convert the time.Durations into integers 153 for _, b := range buckets { 154 r := restAvgBucket{ 155 Id: b.Id, 156 AverageTime: int(b.AverageTime), 157 NumberTasks: b.NumberTasks, 158 Start: b.Start, 159 End: b.End, 160 } 161 restBuckets = append(restBuckets, r) 162 } 163 164 if isCSV { 165 util.WriteCSVResponse(w, http.StatusOK, restBuckets) 166 return 167 } 168 restapi.WriteJSON(w, http.StatusOK, buckets) 169 170 } 171 172 func (restapi *restAPI) getOptimalAndActualMakespans(w http.ResponseWriter, r *http.Request) { 173 // get number of days back 174 numberBuilds, err := util.GetIntValue(r, "number", 0) 175 if err != nil { 176 restapi.WriteJSON(w, http.StatusBadRequest, responseError{Message: err.Error()}) 177 return 178 } 179 if numberBuilds == 0 { 180 restapi.WriteJSON(w, http.StatusBadRequest, responseError{Message: "invalid number builds"}) 181 return 182 } 183 184 isCSV, err := util.GetBoolValue(r, "csv", true) 185 if err != nil { 186 restapi.WriteJSON(w, http.StatusBadRequest, responseError{Message: err.Error()}) 187 return 188 } 189 190 makespanData, err := getMakespanRatios(numberBuilds) 191 if err != nil { 192 restapi.WriteJSON(w, http.StatusBadRequest, responseError{Message: err.Error()}) 193 return 194 } 195 196 if isCSV { 197 util.WriteCSVResponse(w, http.StatusOK, makespanData) 198 return 199 } 200 restapi.WriteJSON(w, http.StatusOK, makespanData) 201 202 }