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  }