cloudwatch.go 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295
  1. package cloudwatch
  2. import (
  3. "encoding/json"
  4. "errors"
  5. "io/ioutil"
  6. "time"
  7. "github.com/aws/aws-sdk-go/aws"
  8. "github.com/aws/aws-sdk-go/aws/awsutil"
  9. "github.com/aws/aws-sdk-go/aws/credentials"
  10. "github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds"
  11. "github.com/aws/aws-sdk-go/aws/ec2metadata"
  12. "github.com/aws/aws-sdk-go/aws/session"
  13. "github.com/aws/aws-sdk-go/service/cloudwatch"
  14. "github.com/aws/aws-sdk-go/service/ec2"
  15. "github.com/grafana/grafana/pkg/middleware"
  16. m "github.com/grafana/grafana/pkg/models"
  17. )
  18. type actionHandler func(*cwRequest, *middleware.Context)
  19. var actionHandlers map[string]actionHandler
  20. type cwRequest struct {
  21. Region string `json:"region"`
  22. Action string `json:"action"`
  23. Body []byte `json:"-"`
  24. DataSource *m.DataSource
  25. }
  26. func init() {
  27. actionHandlers = map[string]actionHandler{
  28. "GetMetricStatistics": handleGetMetricStatistics,
  29. "ListMetrics": handleListMetrics,
  30. "DescribeAlarmsForMetric": handleDescribeAlarmsForMetric,
  31. "DescribeAlarmHistory": handleDescribeAlarmHistory,
  32. "DescribeInstances": handleDescribeInstances,
  33. "__GetRegions": handleGetRegions,
  34. "__GetNamespaces": handleGetNamespaces,
  35. "__GetMetrics": handleGetMetrics,
  36. "__GetDimensions": handleGetDimensions,
  37. }
  38. }
  39. func handleGetMetricStatistics(req *cwRequest, c *middleware.Context) {
  40. sess := session.New()
  41. creds := credentials.NewChainCredentials(
  42. []credentials.Provider{
  43. &credentials.EnvProvider{},
  44. &credentials.SharedCredentialsProvider{Filename: "", Profile: req.DataSource.Database},
  45. &ec2rolecreds.EC2RoleProvider{Client: ec2metadata.New(sess), ExpiryWindow: 5 * time.Minute},
  46. })
  47. cfg := &aws.Config{
  48. Region: aws.String(req.Region),
  49. Credentials: creds,
  50. }
  51. svc := cloudwatch.New(session.New(cfg), cfg)
  52. reqParam := &struct {
  53. Parameters struct {
  54. Namespace string `json:"namespace"`
  55. MetricName string `json:"metricName"`
  56. Dimensions []*cloudwatch.Dimension `json:"dimensions"`
  57. Statistics []*string `json:"statistics"`
  58. StartTime int64 `json:"startTime"`
  59. EndTime int64 `json:"endTime"`
  60. Period int64 `json:"period"`
  61. } `json:"parameters"`
  62. }{}
  63. json.Unmarshal(req.Body, reqParam)
  64. params := &cloudwatch.GetMetricStatisticsInput{
  65. Namespace: aws.String(reqParam.Parameters.Namespace),
  66. MetricName: aws.String(reqParam.Parameters.MetricName),
  67. Dimensions: reqParam.Parameters.Dimensions,
  68. Statistics: reqParam.Parameters.Statistics,
  69. StartTime: aws.Time(time.Unix(reqParam.Parameters.StartTime, 0)),
  70. EndTime: aws.Time(time.Unix(reqParam.Parameters.EndTime, 0)),
  71. Period: aws.Int64(reqParam.Parameters.Period),
  72. }
  73. resp, err := svc.GetMetricStatistics(params)
  74. if err != nil {
  75. c.JsonApiErr(500, "Unable to call AWS API", err)
  76. return
  77. }
  78. c.JSON(200, resp)
  79. }
  80. func handleListMetrics(req *cwRequest, c *middleware.Context) {
  81. sess := session.New()
  82. creds := credentials.NewChainCredentials(
  83. []credentials.Provider{
  84. &credentials.EnvProvider{},
  85. &credentials.SharedCredentialsProvider{Filename: "", Profile: req.DataSource.Database},
  86. &ec2rolecreds.EC2RoleProvider{Client: ec2metadata.New(sess), ExpiryWindow: 5 * time.Minute},
  87. })
  88. cfg := &aws.Config{
  89. Region: aws.String(req.Region),
  90. Credentials: creds,
  91. }
  92. svc := cloudwatch.New(session.New(cfg), cfg)
  93. reqParam := &struct {
  94. Parameters struct {
  95. Namespace string `json:"namespace"`
  96. MetricName string `json:"metricName"`
  97. Dimensions []*cloudwatch.DimensionFilter `json:"dimensions"`
  98. } `json:"parameters"`
  99. }{}
  100. json.Unmarshal(req.Body, reqParam)
  101. params := &cloudwatch.ListMetricsInput{
  102. Namespace: aws.String(reqParam.Parameters.Namespace),
  103. MetricName: aws.String(reqParam.Parameters.MetricName),
  104. Dimensions: reqParam.Parameters.Dimensions,
  105. }
  106. var resp cloudwatch.ListMetricsOutput
  107. err := svc.ListMetricsPages(params,
  108. func(page *cloudwatch.ListMetricsOutput, lastPage bool) bool {
  109. metrics, _ := awsutil.ValuesAtPath(page, "Metrics")
  110. for _, metric := range metrics {
  111. resp.Metrics = append(resp.Metrics, metric.(*cloudwatch.Metric))
  112. }
  113. return !lastPage
  114. })
  115. if err != nil {
  116. c.JsonApiErr(500, "Unable to call AWS API", err)
  117. return
  118. }
  119. c.JSON(200, resp)
  120. }
  121. func handleDescribeAlarmsForMetric(req *cwRequest, c *middleware.Context) {
  122. sess := session.New()
  123. creds := credentials.NewChainCredentials(
  124. []credentials.Provider{
  125. &credentials.EnvProvider{},
  126. &credentials.SharedCredentialsProvider{Filename: "", Profile: req.DataSource.Database},
  127. &ec2rolecreds.EC2RoleProvider{Client: ec2metadata.New(sess), ExpiryWindow: 5 * time.Minute},
  128. })
  129. cfg := &aws.Config{
  130. Region: aws.String(req.Region),
  131. Credentials: creds,
  132. }
  133. svc := cloudwatch.New(session.New(cfg), cfg)
  134. reqParam := &struct {
  135. Parameters struct {
  136. Namespace string `json:"namespace"`
  137. MetricName string `json:"metricName"`
  138. Dimensions []*cloudwatch.Dimension `json:"dimensions"`
  139. Statistic string `json:"statistic"`
  140. Period int64 `json:"period"`
  141. } `json:"parameters"`
  142. }{}
  143. json.Unmarshal(req.Body, reqParam)
  144. params := &cloudwatch.DescribeAlarmsForMetricInput{
  145. Namespace: aws.String(reqParam.Parameters.Namespace),
  146. MetricName: aws.String(reqParam.Parameters.MetricName),
  147. Period: aws.Int64(reqParam.Parameters.Period),
  148. }
  149. if len(reqParam.Parameters.Dimensions) != 0 {
  150. params.Dimensions = reqParam.Parameters.Dimensions
  151. }
  152. if reqParam.Parameters.Statistic != "" {
  153. params.Statistic = aws.String(reqParam.Parameters.Statistic)
  154. }
  155. resp, err := svc.DescribeAlarmsForMetric(params)
  156. if err != nil {
  157. c.JsonApiErr(500, "Unable to call AWS API", err)
  158. return
  159. }
  160. c.JSON(200, resp)
  161. }
  162. func handleDescribeAlarmHistory(req *cwRequest, c *middleware.Context) {
  163. sess := session.New()
  164. creds := credentials.NewChainCredentials(
  165. []credentials.Provider{
  166. &credentials.EnvProvider{},
  167. &credentials.SharedCredentialsProvider{Filename: "", Profile: req.DataSource.Database},
  168. &ec2rolecreds.EC2RoleProvider{Client: ec2metadata.New(sess), ExpiryWindow: 5 * time.Minute},
  169. })
  170. cfg := &aws.Config{
  171. Region: aws.String(req.Region),
  172. Credentials: creds,
  173. }
  174. svc := cloudwatch.New(session.New(cfg), cfg)
  175. reqParam := &struct {
  176. Parameters struct {
  177. AlarmName string `json:"alarmName"`
  178. HistoryItemType string `json:"historyItemType"`
  179. StartDate int64 `json:"startDate"`
  180. EndDate int64 `json:"endDate"`
  181. } `json:"parameters"`
  182. }{}
  183. json.Unmarshal(req.Body, reqParam)
  184. params := &cloudwatch.DescribeAlarmHistoryInput{
  185. AlarmName: aws.String(reqParam.Parameters.AlarmName),
  186. StartDate: aws.Time(time.Unix(reqParam.Parameters.StartDate, 0)),
  187. EndDate: aws.Time(time.Unix(reqParam.Parameters.EndDate, 0)),
  188. }
  189. if reqParam.Parameters.HistoryItemType != "" {
  190. params.HistoryItemType = aws.String(reqParam.Parameters.HistoryItemType)
  191. }
  192. resp, err := svc.DescribeAlarmHistory(params)
  193. if err != nil {
  194. c.JsonApiErr(500, "Unable to call AWS API", err)
  195. return
  196. }
  197. c.JSON(200, resp)
  198. }
  199. func handleDescribeInstances(req *cwRequest, c *middleware.Context) {
  200. sess := session.New()
  201. creds := credentials.NewChainCredentials(
  202. []credentials.Provider{
  203. &credentials.EnvProvider{},
  204. &credentials.SharedCredentialsProvider{Filename: "", Profile: req.DataSource.Database},
  205. &ec2rolecreds.EC2RoleProvider{Client: ec2metadata.New(sess), ExpiryWindow: 5 * time.Minute},
  206. })
  207. cfg := &aws.Config{
  208. Region: aws.String(req.Region),
  209. Credentials: creds,
  210. }
  211. svc := ec2.New(session.New(cfg), cfg)
  212. reqParam := &struct {
  213. Parameters struct {
  214. Filters []*ec2.Filter `json:"filters"`
  215. InstanceIds []*string `json:"instanceIds"`
  216. } `json:"parameters"`
  217. }{}
  218. json.Unmarshal(req.Body, reqParam)
  219. params := &ec2.DescribeInstancesInput{}
  220. if len(reqParam.Parameters.Filters) > 0 {
  221. params.Filters = reqParam.Parameters.Filters
  222. }
  223. if len(reqParam.Parameters.InstanceIds) > 0 {
  224. params.InstanceIds = reqParam.Parameters.InstanceIds
  225. }
  226. var resp ec2.DescribeInstancesOutput
  227. err := svc.DescribeInstancesPages(params,
  228. func(page *ec2.DescribeInstancesOutput, lastPage bool) bool {
  229. reservations, _ := awsutil.ValuesAtPath(page, "Reservations")
  230. for _, reservation := range reservations {
  231. resp.Reservations = append(resp.Reservations, reservation.(*ec2.Reservation))
  232. }
  233. return !lastPage
  234. })
  235. if err != nil {
  236. c.JsonApiErr(500, "Unable to call AWS API", err)
  237. return
  238. }
  239. c.JSON(200, resp)
  240. }
  241. func HandleRequest(c *middleware.Context, ds *m.DataSource) {
  242. var req cwRequest
  243. req.Body, _ = ioutil.ReadAll(c.Req.Request.Body)
  244. req.DataSource = ds
  245. json.Unmarshal(req.Body, &req)
  246. if handler, found := actionHandlers[req.Action]; !found {
  247. c.JsonApiErr(500, "Unexpected AWS Action", errors.New(req.Action))
  248. return
  249. } else {
  250. handler(&req, c)
  251. }
  252. }