sensu-plugins-aws 0.0.1.alpha.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,144 @@
1
+ #! /usr/bin/env ruby
2
+ #
3
+ # ec2-count-metrics
4
+ #
5
+ # DESCRIPTION:
6
+ # This plugin retrives number of EC2 status
7
+ #
8
+ # OUTPUT:
9
+ # plain-text
10
+ #
11
+ # PLATFORMS:
12
+ # Linux
13
+ #
14
+ # DEPENDENCIES:
15
+ # gem: aws-sdk
16
+ # gem: sensu-plugin
17
+ #
18
+ # USAGE:
19
+ # #YELLOW
20
+ #
21
+ # NOTES:
22
+ #
23
+ # LICENSE:
24
+ # Copyright (c) 2014, Tim Smith, tim@cozy.co
25
+ # Released under the same terms as Sensu (the MIT license); see LICENSE
26
+ # for details.
27
+ #
28
+
29
+ require 'rubygems' if RUBY_VERSION < '1.9.0'
30
+ require 'sensu-plugin/metric/cli'
31
+ require 'aws-sdk'
32
+
33
+ class EC2Metrics < Sensu::Plugin::Metric::CLI::Graphite
34
+ option :scheme,
35
+ description: 'Metric naming scheme, text to prepend to metric',
36
+ short: '-s SCHEME',
37
+ long: '--scheme SCHEME',
38
+ default: 'sensu.aws.ec2'
39
+
40
+ option :aws_access_key,
41
+ short: '-a AWS_ACCESS_KEY',
42
+ long: '--aws-access-key AWS_ACCESS_KEY',
43
+ description: "AWS Access Key. Either set ENV['AWS_ACCESS_KEY_ID'] or provide it as an option"
44
+
45
+ option :aws_secret_access_key,
46
+ short: '-k AWS_SECRET_ACCESS_KEY',
47
+ long: '--aws-secret-access-key AWS_SECRET_ACCESS_KEY',
48
+ description: "AWS Secret Access Key. Either set ENV['AWS_SECRET_ACCESS_KEY'] or provide it as an option"
49
+
50
+ option :aws_region,
51
+ short: '-r AWS_REGION',
52
+ long: '--aws-region REGION',
53
+ description: 'AWS Region (such as us-east-1).',
54
+ default: 'us-east-1'
55
+
56
+ option :type,
57
+ short: '-t METRIC type',
58
+ long: '--type METRIC type',
59
+ description: 'Count by type: status, instance',
60
+ default: 'instance'
61
+
62
+ def aws_config
63
+ hash = {}
64
+ hash.update access_key_id: config[:access_key_id], secret_access_key: config[:secret_access_key] if config[:access_key_id] && config[:secret_access_key]
65
+ hash.update region: config[:aws_region]
66
+ hash
67
+ end
68
+
69
+ def run
70
+ begin
71
+
72
+ client = AWS::EC2::Client.new aws_config
73
+
74
+ def by_instances_status(client)
75
+ if config[:scheme] == 'sensu.aws.ec2'
76
+ config[:scheme] += '.count'
77
+ end
78
+
79
+ options = { include_all_instances: true }
80
+ data = client.describe_instance_status(options)
81
+
82
+ total = data[:instance_status_set].count
83
+ status = {}
84
+
85
+ unless total.nil?
86
+ data[:instance_status_set].each do |value|
87
+ stat = value[:instance_state][:name]
88
+ if status[stat].nil?
89
+ status[stat] = 1
90
+ else
91
+ status[stat] = status[stat] + 1
92
+ end
93
+ end
94
+ end
95
+
96
+ unless data.nil?
97
+ # We only return data when we have some to return
98
+ output config[:scheme] + '.total', total
99
+ status.each do |name, count|
100
+ output config[:scheme] + ".#{name}", count
101
+ end
102
+ end
103
+ end
104
+
105
+ def by_instances_type(client)
106
+ if config[:scheme] == 'sensu.aws.ec2'
107
+ config[:scheme] += '.types'
108
+ end
109
+
110
+ data = {}
111
+
112
+ instances = client.describe_instances
113
+ instances[:reservation_set].each do |i|
114
+ i[:instances_set].each do |instance|
115
+ type = instance[:instance_type]
116
+ if data[type].nil?
117
+ data[type] = 1
118
+ else
119
+ data[type] = data[type] + 1
120
+ end
121
+ end
122
+ end
123
+
124
+ unless data.nil?
125
+ # We only return data when we have some to return
126
+ data.each do |name, count|
127
+ output config[:scheme] + ".#{name}", count
128
+ end
129
+ end
130
+ end
131
+
132
+ if config[:type] == 'instance'
133
+ by_instances_type(client)
134
+ elsif config[:type] == 'status'
135
+ by_instances_status(client)
136
+ end
137
+
138
+ rescue => e
139
+ puts "Error: exception: #{e}"
140
+ critical
141
+ end
142
+ ok
143
+ end
144
+ end
data/bin/ec2-node.rb ADDED
@@ -0,0 +1,157 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # CHANGELOG:
4
+ # * 0.4.0:
5
+ # - Adds ability to specify a list of states an individual client can have in
6
+ # EC2. If none is specified, it filters out 'terminated' and 'stopped'
7
+ # instances by default.
8
+ # - Updates how we are "puts"-ing to the log.
9
+ # * 0.3.0:
10
+ # - Updates handler to additionally filter stopped instances.
11
+ # * 0.2.1:
12
+ # - Updates requested configuration snippets so they'll be redacted by
13
+ # default.
14
+ # * 0.2.0:
15
+ # - Renames handler from chef_ec2_node to ec2_node
16
+ # - Removes Chef-related stuff from handler
17
+ # - Updates documentation
18
+ # * 0.1.0:
19
+ # - Initial release
20
+ #
21
+ # This handler deletes a Sensu client if it's been stopped or terminated in EC2.
22
+ # Optionally, you may specify a client attribute `ec2_states`, a list of valid
23
+ # states an instance may have.
24
+ #
25
+ # NOTE: The implementation for correlating Sensu clients to EC2 instances may
26
+ # need to be modified to fit your organization. The current implementation
27
+ # assumes that Sensu clients' names are the same as their instance IDs in EC2.
28
+ # If this is not the case, you can either sub-class this handler and override
29
+ # `ec2_node_exists?` in your own organization-specific handler, or modify this
30
+ # handler to suit your needs.
31
+ #
32
+ # Requires the following Rubygems (`gem install $GEM`):
33
+ # - sensu-plugin
34
+ # - fog
35
+ #
36
+ # Requires a Sensu configuration snippet:
37
+ # {
38
+ # "aws": {
39
+ # "access_key": "adsafdafda",
40
+ # "secret_key": "qwuieohajladsafhj23nm",
41
+ # "region": "us-east-1c"
42
+ # }
43
+ # }
44
+ #
45
+ # Or you can set the following environment variables:
46
+ # - AWS_ACCESS_KEY_ID
47
+ # - AWS_SECRET_ACCESS_KEY
48
+ # - EC2_REGION
49
+ #
50
+ #
51
+ # To use, you can set it as the keepalive handler for a client:
52
+ # {
53
+ # "client": {
54
+ # "name": "i-424242",
55
+ # "address": "127.0.0.1",
56
+ # "keepalive": {
57
+ # "handler": "ec2_node"
58
+ # },
59
+ # "subscriptions": ["all"]
60
+ # }
61
+ # }
62
+ #
63
+ # You can also use this handler with a filter:
64
+ # {
65
+ # "filters": {
66
+ # "ghost_nodes": {
67
+ # "attributes": {
68
+ # "check": {
69
+ # "name": "keepalive",
70
+ # "status": 2
71
+ # },
72
+ # "occurences": "eval: value > 2"
73
+ # }
74
+ # }
75
+ # },
76
+ # "handlers": {
77
+ # "ec2_node": {
78
+ # "type": "pipe",
79
+ # "command": "/etc/sensu/handlers/ec2_node.rb",
80
+ # "severities": ["warning","critical"],
81
+ # "filter": "ghost_nodes"
82
+ # }
83
+ # }
84
+ # }
85
+ #
86
+ # Copyleft 2013 Yet Another Clever Name
87
+ #
88
+ # Based off of the `chef_node` handler by Heavy Water Operations, LLC
89
+ #
90
+ # Released under the same terms as Sensu (the MIT license); see
91
+ # LICENSE for details
92
+
93
+ require 'timeout'
94
+ require 'rubygems' if RUBY_VERSION < '1.9.0'
95
+ require 'sensu-handler'
96
+ require 'fog'
97
+
98
+ class Ec2Node < Sensu::Handler
99
+ def filter; end
100
+
101
+ def handle
102
+ # #YELLOW
103
+ unless ec2_node_exists? # rubocop:disable UnlessElse
104
+ delete_sensu_client!
105
+ else
106
+ puts "[EC2 Node] #{@event['client']['name']} appears to exist in EC2"
107
+ end
108
+ end
109
+
110
+ def delete_sensu_client!
111
+ response = api_request(:DELETE, '/clients/' + @event['client']['name']).code
112
+ deletion_status(response)
113
+ end
114
+
115
+ def ec2_node_exists?
116
+ states = acquire_valid_states
117
+ filtered_instances = ec2.servers.select { |s| states.include?(s.state) }
118
+ instance_ids = filtered_instances.map(&:id)
119
+ instance_ids.each do |id|
120
+ return true if id == @event['client']['name']
121
+ end
122
+ false # no match found, node doesn't exist
123
+ end
124
+
125
+ def ec2
126
+ @ec2 ||= begin
127
+ key = settings['aws']['access_key'] || ENV['AWS_ACCESS_KEY_ID']
128
+ secret = settings['aws']['secret_key'] || ENV['AWS_SECRET_ACCESS_KEY']
129
+ region = settings['aws']['region'] || ENV['EC2_REGION']
130
+ Fog::Compute.new(provider: 'AWS',
131
+ aws_access_key_id: key,
132
+ aws_secret_access_key: secret,
133
+ region: region)
134
+ end
135
+ end
136
+
137
+ def deletion_status(code)
138
+ case code
139
+ when '202'
140
+ puts "[EC2 Node] 202: Successfully deleted Sensu client: #{node}"
141
+ when '404'
142
+ puts "[EC2 Node] 404: Unable to delete #{node}, doesn't exist!"
143
+ when '500'
144
+ puts "[EC2 Node] 500: Miscellaneous error when deleting #{node}"
145
+ else
146
+ puts "[EC2 Node] #{res}: Completely unsure of what happened!"
147
+ end
148
+ end
149
+
150
+ def acquire_valid_states
151
+ if @event['client'].key?('ec2_states')
152
+ return @event['client']['ec2_states']
153
+ else
154
+ return ['running']
155
+ end
156
+ end
157
+ end
@@ -0,0 +1,200 @@
1
+ #! /usr/bin/env ruby
2
+ #
3
+ # elasticache-metrics
4
+ #
5
+ # DESCRIPTION:
6
+ # Fetch Elasticache metrics from CloudWatch
7
+ #
8
+ # OUTPUT:
9
+ # metric-data
10
+ #
11
+ # PLATFORMS:
12
+ # Linux
13
+ #
14
+ # DEPENDENCIES:
15
+ # gem: aws-sdk
16
+ # gem: sensu-plugin
17
+ #
18
+ # needs example command
19
+ # USAGE:
20
+ # #YELLOW
21
+
22
+ # NOTES:
23
+ # Redis: http://docs.aws.amazon.com/AmazonCloudWatch/latest/DeveloperGuide/CacheMetrics.Redis.html
24
+ # Memcached: http://docs.aws.amazon.com/AmazonCloudWatch/latest/DeveloperGuide/CacheMetrics.Memcached.html
25
+ #
26
+ # By default fetches all available statistics from one minute ago. You may need to fetch further back than this;
27
+ #
28
+ # LICENSE:
29
+ # Copyright 2014 Yann Verry
30
+ # Released under the same terms as Sensu (the MIT license); see LICENSE
31
+ # for details.
32
+ #
33
+
34
+ require 'rubygems' if RUBY_VERSION < '1.9.0'
35
+ require 'sensu-plugin/metric/cli'
36
+ require 'aws-sdk'
37
+
38
+ class ElastiCacheMetrics < Sensu::Plugin::Metric::CLI::Graphite
39
+ option :cacheclusterid,
40
+ description: 'Name of the Cache Cluster',
41
+ short: '-n ELASTICACHE_NAME',
42
+ long: '--name ELASTICACHE_NAME',
43
+ required: true
44
+
45
+ option :cachenodeid,
46
+ description: 'Cache Node ID',
47
+ short: '-i CACHE_NODE_ID',
48
+ long: '--cache-node-id CACHE_NODE_ID',
49
+ default: '0001'
50
+
51
+ option :elasticachetype,
52
+ description: 'Elasticache type redis or memcached',
53
+ short: '-c TYPE',
54
+ long: '--cachetype TYPE',
55
+ required: true
56
+
57
+ option :scheme,
58
+ description: 'Metric naming scheme, text to prepend to metric',
59
+ short: '-s SCHEME',
60
+ long: '--scheme SCHEME',
61
+ default: ''
62
+
63
+ option :fetch_age,
64
+ description: 'How long ago to fetch metrics for',
65
+ short: '-f AGE',
66
+ long: '--fetch_age',
67
+ default: 60,
68
+ proc: proc(&:to_i)
69
+
70
+ option :aws_access_key,
71
+ short: '-a AWS_ACCESS_KEY',
72
+ long: '--aws-access-key AWS_ACCESS_KEY',
73
+ description: "AWS Access Key. Either set ENV['AWS_ACCESS_KEY_ID'] or provide it as an option"
74
+
75
+ option :aws_secret_access_key,
76
+ short: '-k AWS_SECRET_ACCESS_KEY',
77
+ long: '--aws-secret-access-key AWS_SECRET_ACCESS_KEY',
78
+ description: "AWS Secret Access Key. Either set ENV['AWS_SECRET_ACCESS_KEY'] or provide it as an option"
79
+
80
+ option :aws_region,
81
+ short: '-r AWS_REGION',
82
+ long: '--aws-region REGION',
83
+ description: 'AWS Region (such as us-east-1).',
84
+ default: 'us-east-1'
85
+
86
+ def aws_config
87
+ hash = {}
88
+ hash.update access_key_id: config[:access_key_id], secret_access_key: config[:secret_access_key] if config[:access_key_id] && config[:secret_access_key]
89
+ hash.update region: config[:aws_region]
90
+ hash
91
+ end
92
+
93
+ def run
94
+ if config[:scheme] == ''
95
+ graphitepath = "#{config[:elasticachename]}.#{config[:metric].downcase}"
96
+ else
97
+ graphitepath = config[:scheme]
98
+ end
99
+
100
+ statistic_type = {
101
+ 'redis' => {
102
+ 'CPUUtilization' => 'Percent',
103
+ 'SwapUsage' => 'Bytes',
104
+ 'FreeableMemory' => 'Bytes',
105
+ 'NetworkBytesIn' => 'Bytes',
106
+ 'NetworkBytesOut' => 'Bytes',
107
+ 'GetTypeCmds' => 'Count',
108
+ 'SetTypeCmds' => 'Count',
109
+ 'KeyBasedCmds' => 'Count',
110
+ 'StringBasedCmds' => 'Count',
111
+ 'HashBasedCmds' => 'Count',
112
+ 'ListBasedCmds' => 'Count',
113
+ 'SetBasedCmds' => 'Count',
114
+ 'SortedSetBasedCmds' => 'Count',
115
+ 'CurrItems' => 'Count'
116
+ },
117
+ 'memcached' => {
118
+ 'CPUUtilization' => 'Percent',
119
+ 'SwapUsage' => 'Bytes',
120
+ 'FreeableMemory' => 'Bytes',
121
+ 'NetworkBytesIn' => 'Bytes',
122
+ 'NetworkBytesOut' => 'Bytes',
123
+ 'BytesUsedForCacheItems' => 'Bytes',
124
+ 'BytesReadIntoMemcached' => 'Bytes',
125
+ 'BytesWrittenOutFromMemcached' => 'Bytes',
126
+ 'CasBadval' => 'Count',
127
+ 'CasHits' => 'Count',
128
+ 'CasMisses' => 'Count',
129
+ 'CmdFlush' => 'Count',
130
+ 'CmdGet' => 'Count',
131
+ 'CmdSet' => 'Count',
132
+ 'CurrConnections' => 'Count',
133
+ 'CurrItems' => 'Count',
134
+ 'DecrHits' => 'Count',
135
+ 'DecrMisses' => 'Count',
136
+ 'DeleteHits' => 'Count',
137
+ 'DeleteMisses' => 'Count',
138
+ 'Evictions' => 'Count',
139
+ 'GetHits' => 'Count',
140
+ 'GetMisses' => 'Count',
141
+ 'IncrHits' => 'Count',
142
+ 'IncrMisses' => 'Count',
143
+ 'Reclaimed' => 'Count',
144
+ 'BytesUsedForHash' => 'Bytes',
145
+ 'CmdConfigGet' => 'Count',
146
+ 'CmdConfigSet' => 'Count',
147
+ 'CmdTouch' => 'Count',
148
+ 'CurrConfig' => 'Count',
149
+ 'EvictedUnfetched' => 'Count',
150
+ 'ExpiredUnfetched' => 'Count',
151
+ 'SlabsMoved' => 'Count',
152
+ 'TouchHits' => 'Count',
153
+ 'TouchMisses' => 'Count',
154
+ 'NewConnections' => 'Count',
155
+ 'NewItems' => 'Count',
156
+ 'UnusedMemory' => 'Bytes'
157
+ }
158
+ }
159
+
160
+ begin
161
+ et = Time.now - config[:fetch_age]
162
+ st = et - 60
163
+
164
+ cw = AWS::CloudWatch::Client.new aws_config
165
+
166
+ # define all options
167
+ options = {
168
+ 'namespace' => 'AWS/ElastiCache',
169
+ 'metric_name' => config[:metric],
170
+ 'dimensions' => [
171
+ { 'name' => 'CacheClusterId', 'value' => config[:cacheclusterid] }
172
+ ],
173
+ 'start_time' => st.iso8601,
174
+ 'end_time' => et.iso8601,
175
+ 'period' => 60,
176
+ 'statistics' => ['Average']
177
+ }
178
+
179
+ result = {}
180
+
181
+ # Fetch all metrics by elasticachetype (redis or memcached).
182
+ statistic_type[config[:elasticachetype]].each do |m|
183
+ options['metric_name'] = m[0] # override metric
184
+ r = cw.get_metric_statistics(options)
185
+ result[m[0]] = r[:datapoints][0] unless r[:datapoints][0].nil?
186
+ end
187
+
188
+ unless result.nil?
189
+ result.each do |name, d|
190
+ # We only return data when we have some to return
191
+ output graphitepath + '.' + name.downcase, d[:average], d[:timestamp].to_i
192
+ end
193
+ end
194
+ rescue => e
195
+ puts "Error: exception: #{e}"
196
+ critical
197
+ end
198
+ ok
199
+ end
200
+ end
@@ -0,0 +1,144 @@
1
+ #! /usr/bin/env ruby
2
+ #
3
+ # elb-full-metrics
4
+ #
5
+ # DESCRIPTION:
6
+ # Gets latency metrics from CloudWatch and puts them in Graphite for longer term storage
7
+ #
8
+ # OUTPUT:
9
+ # metric-data
10
+ #
11
+ # PLATFORMS:
12
+ # Linux
13
+ #
14
+ # DEPENDENCIES:
15
+ # gem: sensu-plugin
16
+ #
17
+ # USAGE:
18
+ # #YELLOW
19
+ #
20
+ # NOTES:
21
+ # Returns latency statistics by default. You can specify any valid ELB metric type, see
22
+ # http://docs.aws.amazon.com/AmazonCloudWatch/latest/DeveloperGuide/CW_Support_For_AWS.html#elb-metricscollected
23
+ #
24
+ # By default fetches statistics from one minute ago. You may need to fetch further back than this;
25
+ # high traffic ELBs can sometimes experience statistic delays of up to 10 minutes. If you experience this,
26
+ # raising a ticket with AWS support should get the problem resolved.
27
+ # As a workaround you can use eg -f 300 to fetch data from 5 minutes ago.
28
+ #
29
+ # LICENSE:
30
+ # Copyright 2013 Bashton Ltd http://www.bashton.com/
31
+ # Released under the same terms as Sensu (the MIT license); see LICENSE
32
+ # for details.
33
+ #
34
+
35
+ require 'rubygems' if RUBY_VERSION < '1.9.0'
36
+ require 'sensu-plugin/metric/cli'
37
+ require 'aws-sdk'
38
+
39
+ class ELBMetrics < Sensu::Plugin::Metric::CLI::Graphite
40
+ option :elbname,
41
+ description: 'Name of the Elastic Load Balancer',
42
+ short: '-n ELB_NAME',
43
+ long: '--name ELB_NAME'
44
+
45
+ option :scheme,
46
+ description: 'Metric naming scheme, text to prepend to metric',
47
+ short: '-s SCHEME',
48
+ long: '--scheme SCHEME',
49
+ default: ''
50
+
51
+ option :fetch_age,
52
+ description: 'How long ago to fetch metrics for',
53
+ short: '-f AGE',
54
+ long: '--fetch_age',
55
+ default: 60,
56
+ proc: proc(&:to_i)
57
+
58
+ option :aws_access_key,
59
+ short: '-a AWS_ACCESS_KEY',
60
+ long: '--aws-access-key AWS_ACCESS_KEY',
61
+ description: "AWS Access Key. Either set ENV['AWS_ACCESS_KEY'] or provide it as an option",
62
+ required: true,
63
+ default: ENV['AWS_ACCESS_KEY']
64
+
65
+ option :aws_secret_access_key,
66
+ short: '-k AWS_SECRET_KEY',
67
+ long: '--aws-secret-access-key AWS_SECRET_KEY',
68
+ description: "AWS Secret Access Key. Either set ENV['AWS_SECRET_KEY'] or provide it as an option",
69
+ required: true,
70
+ default: ENV['AWS_SECRET_KEY']
71
+
72
+ option :aws_region,
73
+ short: '-r AWS_REGION',
74
+ long: '--aws-region REGION',
75
+ description: 'AWS Region (such as eu-west-1).',
76
+ default: 'us-east-1'
77
+
78
+ def aws_config
79
+ hash = {}
80
+ hash.update access_key_id: config[:access_key_id], secret_access_key: config[:secret_access_key] if config[:access_key_id] && config[:secret_access_key]
81
+ hash.update region: config[:aws_region]
82
+ hash
83
+ end
84
+
85
+ def run
86
+ statistic_type = {
87
+ 'Latency' => 'Average',
88
+ 'RequestCount' => 'Sum',
89
+ 'UnHealthyHostCount' => 'Average',
90
+ 'HealthyHostCount' => 'Average',
91
+ 'HTTPCode_Backend_2XX' => 'Sum',
92
+ 'HTTPCode_Backend_4XX' => 'Sum',
93
+ 'HTTPCode_Backend_5XX' => 'Sum',
94
+ 'HTTPCode_ELB_4XX' => 'Sum',
95
+ 'HTTPCode_ELB_5XX' => 'Sum'
96
+ }
97
+
98
+ begin
99
+ et = Time.now - config[:fetch_age]
100
+ st = et - 60
101
+
102
+ cw = AWS::CloudWatch::Client.new aws_config
103
+
104
+ options = {
105
+ 'namespace' => 'AWS/ELB',
106
+ 'dimensions' => [
107
+ {
108
+ 'name' => 'LoadBalancerName',
109
+ 'value' => config[:elbname]
110
+ }
111
+ ],
112
+ 'start_time' => st.iso8601,
113
+ 'end_time' => et.iso8601,
114
+ 'period' => 60
115
+ }
116
+
117
+ result = {}
118
+ graphitepath = config[:scheme]
119
+
120
+ config[:elbname].split(' ').each do |elbname|
121
+ statistic_type.each do |key, value|
122
+ if config[:scheme] == ''
123
+ graphitepath = "#{config[:elbname]}.#{key.downcase}"
124
+ end
125
+ options['metric_name'] = key
126
+ options['dimensions'][0]['value'] = elbname
127
+ options['statistics'] = [value]
128
+ r = cw.get_metric_statistics(options)
129
+ result[key] = r[:datapoints][0] unless r[:datapoints][0].nil?
130
+ end
131
+ unless result.nil?
132
+ # We only return data when we have some to return
133
+ result.each do |key, value|
134
+ puts key, value
135
+ output graphitepath + ".#{key}", value.to_a.last[1], value[:timestamp].to_i
136
+ end
137
+ end
138
+ end
139
+ rescue => e
140
+ critical "Error: exception: #{e}"
141
+ end
142
+ ok
143
+ end
144
+ end