github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/apiserver/metricsdebug/metricsdebug.go (about) 1 // Copyright 2016 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 // Package metricsdebug contains the implementation of an api endpoint 5 // for metrics debug functionality. 6 package metricsdebug 7 8 import ( 9 "fmt" 10 "sort" 11 12 "github.com/juju/errors" 13 "gopkg.in/juju/names.v2" 14 15 "github.com/juju/juju/apiserver/common" 16 "github.com/juju/juju/apiserver/facade" 17 "github.com/juju/juju/apiserver/params" 18 "github.com/juju/juju/state" 19 ) 20 21 func init() { 22 common.RegisterStandardFacade("MetricsDebug", 2, NewMetricsDebugAPI) 23 } 24 25 type metricsDebug interface { 26 // MetricBatchesForUnit returns metric batches for the given unit. 27 MetricBatchesForUnit(unit string) ([]state.MetricBatch, error) 28 29 // MetricBatchesForApplication returns metric batches for the given application. 30 MetricBatchesForApplication(application string) ([]state.MetricBatch, error) 31 32 //MetricBatchesForModel returns all metrics batches in the model. 33 MetricBatchesForModel() ([]state.MetricBatch, error) 34 35 // Unit returns the unit based on its name. 36 Unit(string) (*state.Unit, error) 37 38 // Application returns the application based on its name. 39 Application(string) (*state.Application, error) 40 } 41 42 // MetricsDebug defines the methods on the metricsdebug API end point. 43 type MetricsDebug interface { 44 // GetMetrics returns all metrics stored by the state server. 45 GetMetrics(arg params.Entities) (params.MetricResults, error) 46 47 // SetMeterStatus will set the meter status on the given entity tag. 48 SetMeterStatus(params.MeterStatusParams) (params.ErrorResults, error) 49 } 50 51 // MetricsDebugAPI implements the metricsdebug interface and is the concrete 52 // implementation of the api end point. 53 type MetricsDebugAPI struct { 54 state metricsDebug 55 } 56 57 var _ MetricsDebug = (*MetricsDebugAPI)(nil) 58 59 // NewMetricsDebugAPI creates a new API endpoint for calling metrics debug functions. 60 func NewMetricsDebugAPI( 61 st *state.State, 62 resources facade.Resources, 63 authorizer facade.Authorizer, 64 ) (*MetricsDebugAPI, error) { 65 if !authorizer.AuthClient() { 66 return nil, common.ErrPerm 67 } 68 69 return &MetricsDebugAPI{ 70 state: st, 71 }, nil 72 } 73 74 // GetMetrics returns all metrics stored by the state server. 75 func (api *MetricsDebugAPI) GetMetrics(args params.Entities) (params.MetricResults, error) { 76 results := params.MetricResults{ 77 Results: make([]params.EntityMetrics, len(args.Entities)), 78 } 79 if len(args.Entities) == 0 { 80 batches, err := api.state.MetricBatchesForModel() 81 if err != nil { 82 return results, errors.Annotate(err, "failed to get metrics") 83 } 84 return params.MetricResults{ 85 Results: []params.EntityMetrics{{ 86 Metrics: api.filterLastValuePerKeyPerUnit(batches), 87 }}, 88 }, nil 89 } 90 for i, arg := range args.Entities { 91 tag, err := names.ParseTag(arg.Tag) 92 if err != nil { 93 results.Results[i].Error = common.ServerError(err) 94 continue 95 } 96 var batches []state.MetricBatch 97 switch tag.Kind() { 98 case names.UnitTagKind: 99 batches, err = api.state.MetricBatchesForUnit(tag.Id()) 100 if err != nil { 101 err = errors.Annotate(err, "failed to get metrics") 102 results.Results[i].Error = common.ServerError(err) 103 continue 104 } 105 case names.ApplicationTagKind: 106 batches, err = api.state.MetricBatchesForApplication(tag.Id()) 107 if err != nil { 108 err = errors.Annotate(err, "failed to get metrics") 109 results.Results[i].Error = common.ServerError(err) 110 continue 111 } 112 default: 113 err := errors.Errorf("invalid tag %v", arg.Tag) 114 results.Results[i].Error = common.ServerError(err) 115 } 116 results.Results[i].Metrics = api.filterLastValuePerKeyPerUnit(batches) 117 } 118 return results, nil 119 } 120 121 type byUnit []params.MetricResult 122 123 func (t byUnit) Len() int { return len(t) } 124 func (t byUnit) Swap(i, j int) { t[i], t[j] = t[j], t[i] } 125 func (t byUnit) Less(i, j int) bool { 126 return t[i].Unit < t[j].Unit 127 } 128 129 func (api *MetricsDebugAPI) filterLastValuePerKeyPerUnit(batches []state.MetricBatch) []params.MetricResult { 130 metrics := []params.MetricResult{} 131 for _, mb := range batches { 132 for _, m := range mb.UniqueMetrics() { 133 metrics = append(metrics, params.MetricResult{ 134 Key: m.Key, 135 Value: m.Value, 136 Time: m.Time, 137 Unit: mb.Unit(), 138 }) 139 } 140 } 141 uniq := map[string]params.MetricResult{} 142 for _, m := range metrics { 143 // we want unique keys per unit 144 uniq[fmt.Sprintf("%s-%s", m.Key, m.Unit)] = m 145 } 146 results := make([]params.MetricResult, len(uniq)) 147 i := 0 148 for _, m := range uniq { 149 results[i] = m 150 i++ 151 } 152 sort.Sort(byUnit(results)) 153 return results 154 } 155 156 // SetMeterStatus sets meter statuses for entities. 157 func (api *MetricsDebugAPI) SetMeterStatus(args params.MeterStatusParams) (params.ErrorResults, error) { 158 results := params.ErrorResults{ 159 Results: make([]params.ErrorResult, len(args.Statuses)), 160 } 161 for i, arg := range args.Statuses { 162 tag, err := names.ParseTag(arg.Tag) 163 if err != nil { 164 results.Results[i].Error = common.ServerError(err) 165 continue 166 } 167 err = api.setEntityMeterStatus(tag, state.MeterStatus{ 168 Code: state.MeterStatusFromString(arg.Code), 169 Info: arg.Info, 170 }) 171 if err != nil { 172 results.Results[i].Error = common.ServerError(err) 173 continue 174 } 175 } 176 return results, nil 177 } 178 179 func (api *MetricsDebugAPI) setEntityMeterStatus(entity names.Tag, status state.MeterStatus) error { 180 switch entity := entity.(type) { 181 case names.UnitTag: 182 unit, err := api.state.Unit(entity.Id()) 183 if err != nil { 184 return errors.Trace(err) 185 } 186 chURL, found := unit.CharmURL() 187 if !found { 188 return errors.New("no charm url") 189 } 190 if chURL.Schema != "local" { 191 return errors.New("not a local charm") 192 } 193 err = unit.SetMeterStatus(status.Code.String(), status.Info) 194 if err != nil { 195 return errors.Trace(err) 196 } 197 case names.ApplicationTag: 198 application, err := api.state.Application(entity.Id()) 199 if err != nil { 200 return errors.Trace(err) 201 } 202 chURL, _ := application.CharmURL() 203 if chURL.Schema != "local" { 204 return errors.New("not a local charm") 205 } 206 units, err := application.AllUnits() 207 if err != nil { 208 return errors.Trace(err) 209 } 210 for _, unit := range units { 211 err := unit.SetMeterStatus(status.Code.String(), status.Info) 212 if err != nil { 213 return errors.Trace(err) 214 } 215 } 216 default: 217 return errors.Errorf("expected application or unit tag, got %T", entity) 218 } 219 return nil 220 }