Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.
 
 
 

405 lignes
11 KiB

  1. /***** BEGIN LICENSE BLOCK *****
  2. # This Source Code Form is subject to the terms of the Mozilla Public
  3. # License, v. 2.0. If a copy of the MPL was not distributed with this file,
  4. # You can obtain one at http://mozilla.org/MPL/2.0/.
  5. #
  6. # The Initial Developer of the Original Code is the Mozilla Foundation.
  7. # Portions created by the Initial Developer are Copyright (C) 2012
  8. # the Initial Developer. All Rights Reserved.
  9. #
  10. # Contributor(s):
  11. # Ben Bangert (bbangert@mozilla.com)
  12. # Logan Owen (lsowen@s1network.com)
  13. #
  14. # ***** END LICENSE BLOCK *****/
  15. package cloudwatch
  16. import (
  17. "encoding/xml"
  18. "errors"
  19. "fmt"
  20. "github.com/feyeleanor/sets"
  21. "github.com/goamz/goamz/aws"
  22. "strconv"
  23. "time"
  24. )
  25. // The CloudWatch type encapsulates all the CloudWatch operations in a region.
  26. type CloudWatch struct {
  27. Service aws.AWSService
  28. }
  29. type Dimension struct {
  30. Name string
  31. Value string
  32. }
  33. type StatisticSet struct {
  34. Maximum float64
  35. Minimum float64
  36. SampleCount float64
  37. Sum float64
  38. }
  39. type MetricDatum struct {
  40. Dimensions []Dimension
  41. MetricName string
  42. StatisticValues *StatisticSet
  43. Timestamp time.Time
  44. Unit string
  45. Value float64
  46. }
  47. type Datapoint struct {
  48. Average float64
  49. Maximum float64
  50. Minimum float64
  51. SampleCount float64
  52. Sum float64
  53. Timestamp time.Time
  54. Unit string
  55. }
  56. type GetMetricStatisticsRequest struct {
  57. Dimensions []Dimension
  58. EndTime time.Time
  59. StartTime time.Time
  60. MetricName string
  61. Unit string
  62. Period int
  63. Statistics []string
  64. Namespace string
  65. }
  66. type GetMetricStatisticsResult struct {
  67. Datapoints []Datapoint `xml:"Datapoints>member"`
  68. NextToken string `xml:"NextToken"`
  69. }
  70. type GetMetricStatisticsResponse struct {
  71. GetMetricStatisticsResult GetMetricStatisticsResult
  72. ResponseMetadata aws.ResponseMetadata
  73. }
  74. type Metric struct {
  75. Dimensions []Dimension `xml:"Dimensions>member"`
  76. MetricName string
  77. Namespace string
  78. }
  79. type ListMetricsResult struct {
  80. Metrics []Metric `xml:"Metrics>member"`
  81. NextToken string
  82. }
  83. type ListMetricsResponse struct {
  84. ListMetricsResult ListMetricsResult
  85. ResponseMetadata aws.ResponseMetadata
  86. }
  87. type ListMetricsRequest struct {
  88. Dimensions []Dimension
  89. MetricName string
  90. Namespace string
  91. NextToken string
  92. }
  93. type AlarmAction struct {
  94. ARN string
  95. }
  96. type MetricAlarm struct {
  97. AlarmActions []AlarmAction
  98. AlarmDescription string
  99. AlarmName string
  100. ComparisonOperator string
  101. Dimensions []Dimension
  102. EvaluationPeriods int
  103. InsufficientDataActions []AlarmAction
  104. MetricName string
  105. Namespace string
  106. OkActions []AlarmAction
  107. Period int
  108. Statistic string
  109. Threshold float64
  110. Unit string
  111. }
  112. var attempts = aws.AttemptStrategy{
  113. Min: 5,
  114. Total: 5 * time.Second,
  115. Delay: 200 * time.Millisecond,
  116. }
  117. var validUnits = sets.SSet(
  118. "Seconds",
  119. "Microseconds",
  120. "Milliseconds",
  121. "Bytes",
  122. "Kilobytes",
  123. "Megabytes",
  124. "Gigabytes",
  125. "Terabytes",
  126. "Bits",
  127. "Kilobits",
  128. "Megabits",
  129. "Gigabits",
  130. "Terabits",
  131. "Percent",
  132. "Count",
  133. "Bytes/Second",
  134. "Kilobytes/Second",
  135. "Megabytes/Second",
  136. "Gigabytes/Second",
  137. "Terabytes/Second",
  138. "Bits/Second",
  139. "Kilobits/Second",
  140. "Megabits/Second",
  141. "Gigabits/Second",
  142. "Terabits/Second",
  143. "Count/Second",
  144. )
  145. var validMetricStatistics = sets.SSet(
  146. "Average",
  147. "Sum",
  148. "SampleCount",
  149. "Maximum",
  150. "Minimum",
  151. )
  152. var validComparisonOperators = sets.SSet(
  153. "LessThanThreshold",
  154. "LessThanOrEqualToThreshold",
  155. "GreaterThanThreshold",
  156. "GreaterThanOrEqualToThreshold",
  157. )
  158. // Create a new CloudWatch object for a given namespace
  159. func NewCloudWatch(auth aws.Auth, region aws.ServiceInfo) (*CloudWatch, error) {
  160. service, err := aws.NewService(auth, region)
  161. if err != nil {
  162. return nil, err
  163. }
  164. return &CloudWatch{
  165. Service: service,
  166. }, nil
  167. }
  168. func (c *CloudWatch) query(method, path string, params map[string]string, resp interface{}) error {
  169. // Add basic Cloudwatch param
  170. params["Version"] = "2010-08-01"
  171. r, err := c.Service.Query(method, path, params)
  172. if err != nil {
  173. return err
  174. }
  175. defer r.Body.Close()
  176. if r.StatusCode != 200 {
  177. return c.Service.BuildError(r)
  178. }
  179. err = xml.NewDecoder(r.Body).Decode(resp)
  180. return err
  181. }
  182. // Get statistics for specified metric
  183. //
  184. // If the arguments are invalid or the server returns an error, the error will
  185. // be set and the other values undefined.
  186. func (c *CloudWatch) GetMetricStatistics(req *GetMetricStatisticsRequest) (result *GetMetricStatisticsResponse, err error) {
  187. statisticsSet := sets.SSet(req.Statistics...)
  188. // Kick out argument errors
  189. switch {
  190. case req.EndTime.IsZero():
  191. err = errors.New("No endTime specified")
  192. case req.StartTime.IsZero():
  193. err = errors.New("No startTime specified")
  194. case req.MetricName == "":
  195. err = errors.New("No metricName specified")
  196. case req.Namespace == "":
  197. err = errors.New("No Namespace specified")
  198. case req.Period < 60 || req.Period%60 != 0:
  199. err = errors.New("Period not 60 seconds or a multiple of 60 seconds")
  200. case len(req.Statistics) < 1:
  201. err = errors.New("No statistics supplied")
  202. case validMetricStatistics.Union(statisticsSet).Len() != validMetricStatistics.Len():
  203. err = errors.New("Invalid statistic values supplied")
  204. case req.Unit != "" && !validUnits.Member(req.Unit):
  205. err = errors.New("Unit is not a valid value")
  206. }
  207. if err != nil {
  208. return
  209. }
  210. // Serialize all the params
  211. params := aws.MakeParams("GetMetricStatistics")
  212. params["EndTime"] = req.EndTime.UTC().Format(time.RFC3339)
  213. params["StartTime"] = req.StartTime.UTC().Format(time.RFC3339)
  214. params["MetricName"] = req.MetricName
  215. params["Namespace"] = req.Namespace
  216. params["Period"] = strconv.Itoa(req.Period)
  217. if req.Unit != "" {
  218. params["Unit"] = req.Unit
  219. }
  220. // Serialize the lists of data
  221. for i, d := range req.Dimensions {
  222. prefix := "Dimensions.member." + strconv.Itoa(i+1)
  223. params[prefix+".Name"] = d.Name
  224. params[prefix+".Value"] = d.Value
  225. }
  226. for i, d := range req.Statistics {
  227. prefix := "Statistics.member." + strconv.Itoa(i+1)
  228. params[prefix] = d
  229. }
  230. result = new(GetMetricStatisticsResponse)
  231. err = c.query("GET", "/", params, result)
  232. return
  233. }
  234. // Returns a list of valid metrics stored for the AWS account owner.
  235. // Returned metrics can be used with GetMetricStatistics to obtain statistical data for a given metric.
  236. func (c *CloudWatch) ListMetrics(req *ListMetricsRequest) (result *ListMetricsResponse, err error) {
  237. // Serialize all the params
  238. params := aws.MakeParams("ListMetrics")
  239. if req.Namespace != "" {
  240. params["Namespace"] = req.Namespace
  241. }
  242. if len(req.Dimensions) > 0 {
  243. for i, d := range req.Dimensions {
  244. prefix := "Dimensions.member." + strconv.Itoa(i+1)
  245. params[prefix+".Name"] = d.Name
  246. params[prefix+".Value"] = d.Value
  247. }
  248. }
  249. result = new(ListMetricsResponse)
  250. err = c.query("GET", "/", params, &result)
  251. metrics := result.ListMetricsResult.Metrics
  252. if result.ListMetricsResult.NextToken != "" {
  253. params = aws.MakeParams("ListMetrics")
  254. params["NextToken"] = result.ListMetricsResult.NextToken
  255. for result.ListMetricsResult.NextToken != "" && err == nil {
  256. result = new(ListMetricsResponse)
  257. err = c.query("GET", "/", params, &result)
  258. if err == nil {
  259. newslice := make([]Metric, len(metrics)+len(result.ListMetricsResult.Metrics))
  260. copy(newslice, metrics)
  261. copy(newslice[len(metrics):], result.ListMetricsResult.Metrics)
  262. metrics = newslice
  263. }
  264. }
  265. result.ListMetricsResult.Metrics = metrics
  266. }
  267. return
  268. }
  269. func (c *CloudWatch) PutMetricData(metrics []MetricDatum) (result *aws.BaseResponse, err error) {
  270. return c.PutMetricDataNamespace(metrics, "")
  271. }
  272. func (c *CloudWatch) PutMetricDataNamespace(metrics []MetricDatum, namespace string) (result *aws.BaseResponse, err error) {
  273. // Serialize the params
  274. params := aws.MakeParams("PutMetricData")
  275. if namespace != "" {
  276. params["Namespace"] = namespace
  277. }
  278. for i, metric := range metrics {
  279. prefix := "MetricData.member." + strconv.Itoa(i+1)
  280. if metric.MetricName == "" {
  281. err = fmt.Errorf("No metric name supplied for metric: %d", i)
  282. return
  283. }
  284. params[prefix+".MetricName"] = metric.MetricName
  285. if metric.Unit != "" {
  286. params[prefix+".Unit"] = metric.Unit
  287. }
  288. params[prefix+".Value"] = strconv.FormatFloat(metric.Value, 'E', 10, 64)
  289. if !metric.Timestamp.IsZero() {
  290. params[prefix+".Timestamp"] = metric.Timestamp.UTC().Format(time.RFC3339)
  291. }
  292. for j, dim := range metric.Dimensions {
  293. dimprefix := prefix + ".Dimensions.member." + strconv.Itoa(j+1)
  294. params[dimprefix+".Name"] = dim.Name
  295. params[dimprefix+".Value"] = dim.Value
  296. }
  297. if metric.StatisticValues != nil {
  298. statprefix := prefix + ".StatisticValues"
  299. params[statprefix+".Maximum"] = strconv.FormatFloat(metric.StatisticValues.Maximum, 'E', 10, 64)
  300. params[statprefix+".Minimum"] = strconv.FormatFloat(metric.StatisticValues.Minimum, 'E', 10, 64)
  301. params[statprefix+".SampleCount"] = strconv.FormatFloat(metric.StatisticValues.SampleCount, 'E', 10, 64)
  302. params[statprefix+".Sum"] = strconv.FormatFloat(metric.StatisticValues.Sum, 'E', 10, 64)
  303. }
  304. }
  305. result = new(aws.BaseResponse)
  306. err = c.query("POST", "/", params, result)
  307. return
  308. }
  309. func (c *CloudWatch) PutMetricAlarm(alarm *MetricAlarm) (result *aws.BaseResponse, err error) {
  310. // Serialize the params
  311. params := aws.MakeParams("PutMetricAlarm")
  312. switch {
  313. case alarm.AlarmName == "":
  314. err = errors.New("No AlarmName supplied")
  315. case !validComparisonOperators.Member(alarm.ComparisonOperator):
  316. err = errors.New("ComparisonOperator is not valid")
  317. case alarm.EvaluationPeriods == 0:
  318. err = errors.New("No number of EvaluationPeriods specified")
  319. case alarm.MetricName == "":
  320. err = errors.New("No MetricName specified")
  321. case alarm.Namespace == "":
  322. err = errors.New("No Namespace specified")
  323. case alarm.Period == 0:
  324. err = errors.New("No Period over which statistic should apply was specified")
  325. case !validMetricStatistics.Member(alarm.Statistic):
  326. err = errors.New("Invalid statistic value supplied")
  327. case alarm.Threshold == 0:
  328. err = errors.New("No Threshold value specified")
  329. case alarm.Unit != "" && !validUnits.Member(alarm.Unit):
  330. err = errors.New("Unit is not a valid value")
  331. }
  332. if err != nil {
  333. return
  334. }
  335. for i, action := range alarm.AlarmActions {
  336. params["AlarmActions.member."+strconv.Itoa(i+1)] = action.ARN
  337. }
  338. for i, action := range alarm.InsufficientDataActions {
  339. params["InsufficientDataActions.member."+strconv.Itoa(i+1)] = action.ARN
  340. }
  341. for i, action := range alarm.OkActions {
  342. params["OKActions.member."+strconv.Itoa(i+1)] = action.ARN
  343. }
  344. if alarm.AlarmDescription != "" {
  345. params["AlarmDescription"] = alarm.AlarmDescription
  346. }
  347. params["AlarmDescription"] = alarm.AlarmDescription
  348. params["AlarmName"] = alarm.AlarmName
  349. params["ComparisonOperator"] = alarm.ComparisonOperator
  350. for i, dim := range alarm.Dimensions {
  351. dimprefix := "Dimensions.member." + strconv.Itoa(i+1)
  352. params[dimprefix+".Name"] = dim.Name
  353. params[dimprefix+".Value"] = dim.Value
  354. }
  355. params["EvaluationPeriods"] = strconv.Itoa(alarm.EvaluationPeriods)
  356. params["MetricName"] = alarm.MetricName
  357. params["Namespace"] = alarm.Namespace
  358. params["Period"] = strconv.Itoa(alarm.Period)
  359. params["Statistic"] = alarm.Statistic
  360. params["Threshold"] = strconv.FormatFloat(alarm.Threshold, 'E', 10, 64)
  361. if alarm.Unit != "" {
  362. params["Unit"] = alarm.Unit
  363. }
  364. result = new(aws.BaseResponse)
  365. err = c.query("POST", "/", params, result)
  366. return
  367. }