github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/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  	"github.com/juju/errors"
    10  	"github.com/juju/names"
    11  
    12  	"github.com/juju/juju/apiserver/common"
    13  	"github.com/juju/juju/apiserver/params"
    14  	"github.com/juju/juju/state"
    15  )
    16  
    17  func init() {
    18  	common.RegisterStandardFacade("MetricsDebug", 1, NewMetricsDebugAPI)
    19  }
    20  
    21  type metricsDebug interface {
    22  	// MetricBatchesForUnit returns metric batches for the given unit.
    23  	MetricBatchesForUnit(unit string) ([]state.MetricBatch, error)
    24  
    25  	// MetricBatchesForService returns metric batches for the given service.
    26  	MetricBatchesForService(service string) ([]state.MetricBatch, error)
    27  
    28  	// Unit returns the unit based on its name.
    29  	Unit(string) (*state.Unit, error)
    30  
    31  	// Service returns the service based on its name.
    32  	Service(string) (*state.Service, error)
    33  }
    34  
    35  // MetricsDebug defines the methods on the metricsdebug API end point.
    36  type MetricsDebug interface {
    37  	// GetMetrics returns all metrics stored by the state server.
    38  	GetMetrics(arg params.Entities) (params.MetricResults, error)
    39  
    40  	// SetMeterStatus will set the meter status on the given entity tag.
    41  	SetMeterStatus(params.MeterStatusParams) (params.ErrorResults, error)
    42  }
    43  
    44  // MetricsDebugAPI implements the metricsdebug interface and is the concrete
    45  // implementation of the api end point.
    46  type MetricsDebugAPI struct {
    47  	state metricsDebug
    48  }
    49  
    50  var _ MetricsDebug = (*MetricsDebugAPI)(nil)
    51  
    52  // NewMetricsDebugAPI creates a new API endpoint for calling metrics debug functions.
    53  func NewMetricsDebugAPI(
    54  	st *state.State,
    55  	resources *common.Resources,
    56  	authorizer common.Authorizer,
    57  ) (*MetricsDebugAPI, error) {
    58  	if !authorizer.AuthClient() {
    59  		return nil, common.ErrPerm
    60  	}
    61  
    62  	return &MetricsDebugAPI{
    63  		state: st,
    64  	}, nil
    65  }
    66  
    67  // GetMetrics returns all metrics stored by the state server.
    68  func (api *MetricsDebugAPI) GetMetrics(args params.Entities) (params.MetricResults, error) {
    69  	results := params.MetricResults{
    70  		Results: make([]params.EntityMetrics, len(args.Entities)),
    71  	}
    72  	if len(args.Entities) == 0 {
    73  		return results, nil
    74  	}
    75  	for i, arg := range args.Entities {
    76  		tag, err := names.ParseTag(arg.Tag)
    77  		if err != nil {
    78  			results.Results[i].Error = common.ServerError(err)
    79  			continue
    80  		}
    81  		var batches []state.MetricBatch
    82  		switch tag.Kind() {
    83  		case names.UnitTagKind:
    84  			batches, err = api.state.MetricBatchesForUnit(tag.Id())
    85  			if err != nil {
    86  				err = errors.Annotate(err, "failed to get metrics")
    87  				results.Results[i].Error = common.ServerError(err)
    88  				continue
    89  			}
    90  		case names.ServiceTagKind:
    91  			batches, err = api.state.MetricBatchesForService(tag.Id())
    92  			if err != nil {
    93  				err = errors.Annotate(err, "failed to get metrics")
    94  				results.Results[i].Error = common.ServerError(err)
    95  				continue
    96  			}
    97  		default:
    98  			err := errors.Errorf("invalid tag %v", arg.Tag)
    99  			results.Results[i].Error = common.ServerError(err)
   100  		}
   101  		metricCount := 0
   102  		for _, b := range batches {
   103  			metricCount += len(b.Metrics())
   104  		}
   105  		metrics := make([]params.MetricResult, metricCount)
   106  		ix := 0
   107  		for _, mb := range batches {
   108  			for _, m := range mb.Metrics() {
   109  				metrics[ix] = params.MetricResult{
   110  					Key:   m.Key,
   111  					Value: m.Value,
   112  					Time:  m.Time,
   113  				}
   114  				ix++
   115  			}
   116  			results.Results[i].Metrics = metrics
   117  		}
   118  	}
   119  	return results, nil
   120  }
   121  
   122  // SetMeterStatus sets meter statuses for entities.
   123  func (api *MetricsDebugAPI) SetMeterStatus(args params.MeterStatusParams) (params.ErrorResults, error) {
   124  	results := params.ErrorResults{
   125  		Results: make([]params.ErrorResult, len(args.Statuses)),
   126  	}
   127  	for i, arg := range args.Statuses {
   128  		tag, err := names.ParseTag(arg.Tag)
   129  		if err != nil {
   130  			results.Results[i].Error = common.ServerError(err)
   131  			continue
   132  		}
   133  		err = api.setEntityMeterStatus(tag, state.MeterStatus{
   134  			Code: state.MeterStatusFromString(arg.Code),
   135  			Info: arg.Info,
   136  		})
   137  		if err != nil {
   138  			results.Results[i].Error = common.ServerError(err)
   139  			continue
   140  		}
   141  	}
   142  	return results, nil
   143  }
   144  
   145  func (api *MetricsDebugAPI) setEntityMeterStatus(entity names.Tag, status state.MeterStatus) error {
   146  	switch entity := entity.(type) {
   147  	case names.UnitTag:
   148  		unit, err := api.state.Unit(entity.Id())
   149  		if err != nil {
   150  			return errors.Trace(err)
   151  		}
   152  		chURL, found := unit.CharmURL()
   153  		if !found {
   154  			return errors.New("no charm url")
   155  		}
   156  		if chURL.Schema != "local" {
   157  			return errors.New("not a local charm")
   158  		}
   159  		err = unit.SetMeterStatus(status.Code.String(), status.Info)
   160  		if err != nil {
   161  			return errors.Trace(err)
   162  		}
   163  	case names.ServiceTag:
   164  		service, err := api.state.Service(entity.Id())
   165  		if err != nil {
   166  			return errors.Trace(err)
   167  		}
   168  		chURL, _ := service.CharmURL()
   169  		if chURL.Schema != "local" {
   170  			return errors.New("not a local charm")
   171  		}
   172  		units, err := service.AllUnits()
   173  		if err != nil {
   174  			return errors.Trace(err)
   175  		}
   176  		for _, unit := range units {
   177  			err := unit.SetMeterStatus(status.Code.String(), status.Info)
   178  			if err != nil {
   179  				return errors.Trace(err)
   180  			}
   181  		}
   182  	default:
   183  		return errors.Errorf("expected service or unit tag, got %T", entity)
   184  	}
   185  	return nil
   186  }