sensu-cli-compat 0.6.3.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 93c6437ccfa0bfec4c2ef12ae2fc7f795da9ea2f
4
+ data.tar.gz: 8d1b4470a3bb9248737fb91dc76eb9abcd106b00
5
+ SHA512:
6
+ metadata.gz: ba28bd44a8487cf939f6f23892b6af8bb0186ee0cd790a6d230f5d30fcd6a3a15555961deb92c59ef9e67df9dde32fa617002c97d224bb635028c09458b8e9f0
7
+ data.tar.gz: a45e1623c30498a7114ab72194b87c6bb3ea826a439bb7a8607158e25593a1bb4cf72d8f6bc412218c1ca12a8355c83d0d0070b208b01ca4813028be4c057e4d
data/README.md ADDED
@@ -0,0 +1,127 @@
1
+ sensu-cli
2
+ =========
3
+ ```
4
+ #
5
+ # Welcome to the sensu-cli.
6
+ # ______
7
+ # .-' '-.
8
+ # .' __ '.
9
+ # / / \ \
10
+ # ------------------
11
+ # /\
12
+ # '--'
13
+ # SENSU-CLI
14
+ #
15
+ ```
16
+ A sensu-cli for interacting with the sensu api.
17
+
18
+ What is Sensu? http://sensuapp.org/
19
+
20
+ Features
21
+ --------
22
+ * API interaction with info, health, stashes, events, clients, aggregates and checks
23
+ * Resolve Events
24
+ * Silence clients and checks
25
+ * Get Requests (get clients, stashes, events, etc.)
26
+ * Delete Requests (delete clients, stashes, aggregates and events)
27
+
28
+
29
+ Usage and Configuration
30
+ -----------------------
31
+ * gem install sensu-cli
32
+
33
+ * There is one settings file for host, port, ssl, and HTTP timeout that lives in your user directory ~/.sensu/settings.rb. You can alternatively place this in /etc/sensu/sensu-cli/settings.rb.
34
+
35
+ ````
36
+ host "127.0.0.1"
37
+ port "4567"
38
+ ssl false
39
+ read_timeout 20
40
+ open_timeout 20
41
+ ````
42
+ This format was chosen so you can do some ENV magic via your profile and setting up an alias. For details see the [wiki](https://github.com/agent462/sensu-cli/wiki)
43
+
44
+ * All Configuration Settings
45
+ `host` String - Required
46
+ `port` String/Integer - Required
47
+ `ssl` Boolean - Optional - Defaults False
48
+ `read_timeout` Integer - Optional - Default 15 (seconds)
49
+ `open_timeout` Integer - Optional - Default 5 (seconds)
50
+ `pretty_colors` Boolean - Optional - Default True
51
+ `proxy_address` String - Optional
52
+ `proxy_port` Integer - Optional
53
+ `user` String - Optional
54
+ `password` String - Optional
55
+
56
+ Examples
57
+ -----------
58
+ ````
59
+ Available subcommands: (for details, sensu SUB-COMMAND --help)
60
+
61
+ ** Aggregate Commands **
62
+ sensu-cli aggregate list (OPTIONS)
63
+ sensu-cli aggregate show CHECK (OPTIONS)
64
+ sensu-cli aggregate delete CHECK
65
+
66
+ ** Check Commands **
67
+ sensu-cli check list
68
+ sensu-cli check show CHECK
69
+ sensu-cli check request CHECK SUB1,SUB2
70
+
71
+ ** Client Commands **
72
+ sensu-cli client list (OPTIONS)
73
+ sensu-cli client show NODE
74
+ sensu-cli client delete NODE
75
+ sensu-cli client history NODE
76
+
77
+ ** Event Commands **
78
+ sensu-cli event list
79
+ sensu-cli event show NODE (OPTIONS)
80
+ sensu-cli event delete NODE CHECK
81
+
82
+ ** Health Commands **
83
+ sensu-cli health (OPTIONS)
84
+
85
+ ** Info Commands **
86
+ sensu-cli info
87
+
88
+ ** Silence Commands **
89
+ sensu-cli silence NODE (OPTIONS)
90
+
91
+ ** Stash Commands **
92
+ sensu-cli stash list (OPTIONS)
93
+ sensu-cli stash show STASHPATH
94
+ sensu-cli stash delete STASHPATH
95
+ sensu-cli stash create PATH
96
+
97
+ ** Resolve Commands **
98
+ sensu-cli resolve NODE CHECK
99
+
100
+ --version, -v: Print version and exit
101
+ --help, -h: Show this message
102
+ ````
103
+
104
+ Contributions
105
+ -------------
106
+ Please provide a pull request. I'm an ops guy, not a developer, so if you're submitting code cleanup, all I ask is that you explain the improvement so I can learn.
107
+
108
+
109
+ License and Author
110
+ ==================
111
+ I'm releasing this under the MIT or Apache 2.0 license. You pick.
112
+
113
+ Author:: Bryan Brandau <agent462@gmail.com>
114
+
115
+ Copyright:: 2013, Bryan Brandau
116
+
117
+ Licensed under the Apache License, Version 2.0 (the "License");
118
+ you may not use this file except in compliance with the License.
119
+ You may obtain a copy of the License at
120
+
121
+ http://www.apache.org/licenses/LICENSE-2.0
122
+
123
+ Unless required by applicable law or agreed to in writing, software
124
+ distributed under the License is distributed on an "AS IS" BASIS,
125
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
126
+ See the License for the specific language governing permissions and
127
+ limitations under the License.
data/bin/sensu ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ $LOAD_PATH << File.dirname(__FILE__) + '/../lib' unless $LOAD_PATH.include?(File.dirname(__FILE__) + '/../lib/')
3
+
4
+ require 'sensu-cli'
5
+ sensu = SensuCli::Base.new
6
+ sensu.setup
data/bin/sensu-cli ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ $LOAD_PATH << File.dirname(__FILE__) + '/../lib' unless $LOAD_PATH.include?(File.dirname(__FILE__) + '/../lib/')
3
+
4
+ require 'sensu-cli'
5
+ sensu = SensuCli::Base.new
6
+ sensu.setup
data/lib/sensu-cli.rb ADDED
@@ -0,0 +1,16 @@
1
+ require 'sensu-cli/settings'
2
+ require 'sensu-cli/cli'
3
+ require 'sensu-cli/editor'
4
+ require 'sensu-cli/pretty'
5
+ require 'sensu-cli/path.rb'
6
+ require 'sensu-cli/api.rb'
7
+ require 'sensu-cli/base.rb'
8
+ require 'sensu-cli/version.rb'
9
+ require 'sensu-cli/filter.rb'
10
+
11
+ module SensuCli
12
+ def self.die(code = 0, msg = nil)
13
+ at_exit { puts msg }
14
+ Kernel.exit(code)
15
+ end
16
+ end
@@ -0,0 +1,62 @@
1
+ require 'net/https'
2
+ require 'json'
3
+ require 'rainbow/ext/string'
4
+
5
+ module SensuCli
6
+ class Api
7
+ def request(opts)
8
+ http = Net::HTTP.new(opts[:host], opts[:port], opts[:proxy_address],
9
+ opts[:proxy_port])
10
+ http.read_timeout = opts[:read_timeout]
11
+ http.open_timeout = opts[:open_timeout]
12
+ if opts[:ssl]
13
+ http.use_ssl = true
14
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
15
+ end
16
+ proxy_header = { 'api-proxy' => 'true' }
17
+ case opts[:method]
18
+ when 'Get'
19
+ req = Net::HTTP::Get.new(opts[:path], proxy_header)
20
+ when 'Delete'
21
+ req = Net::HTTP::Delete.new(opts[:path], proxy_header)
22
+ when 'Post'
23
+ req = Net::HTTP::Post.new(opts[:path], proxy_header.merge!('Content-Type' => 'application/json'))
24
+ req.body = opts[:payload]
25
+ end
26
+ req.basic_auth(opts[:user], opts[:password]) if opts[:user] && opts[:password]
27
+ begin
28
+ http.request(req)
29
+ rescue Timeout::Error
30
+ SensuCli::die(1, 'HTTP request has timed out.'.color(:red))
31
+ rescue StandardError => e
32
+ SensuCli::die(1, "An HTTP error occurred. Check your settings. #{e}".color(:red))
33
+ end
34
+ end
35
+
36
+ def response(code, body, command = nil)
37
+ case code
38
+ when '200'
39
+ JSON.parse(body)
40
+ when '201'
41
+ puts 'The stash has been created.' if command == 'stashes' || command == 'silence'
42
+ when '202'
43
+ puts 'The item was submitted for processing.'
44
+ when '204'
45
+ puts 'Sensu is healthy' if command == 'health'
46
+ puts 'The item was successfully deleted.' if command == 'aggregates' || command == 'stashes'
47
+ when '400'
48
+ puts 'The payload is malformed.'.color(:red)
49
+ when '401'
50
+ puts 'The request requires user authentication.'.color(:red)
51
+ when '404'
52
+ puts 'The item did not exist.'.color(:cyan)
53
+ else
54
+ if command == 'health'
55
+ puts 'Sensu is not healthy.'.color(:red)
56
+ else
57
+ puts "There was an error while trying to complete your request. Response code: #{code}".color(:red)
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,66 @@
1
+ module SensuCli
2
+ class Base
3
+ def setup
4
+ @cli = Cli.new.global
5
+ settings
6
+ api_path(@cli)
7
+ make_call
8
+ end
9
+
10
+ def settings
11
+ directory = "#{Dir.home}/.sensu"
12
+ file = "#{directory}/settings.rb"
13
+ alt = '/etc/sensu/sensu-cli/settings.rb'
14
+ settings = Settings.new
15
+ if settings.file?(file)
16
+ SensuCli::Config.from_file(file)
17
+ elsif settings.file?(alt)
18
+ SensuCli::Config.from_file(alt)
19
+ else
20
+ settings.create(directory, file)
21
+ end
22
+ Rainbow.enabled = Config.pretty_colors == false ? false : true
23
+ end
24
+
25
+ def api_path(cli)
26
+ p = PathCreator.new
27
+ p.respond_to?(cli[:command]) ? path = p.send(cli[:command], cli) : SensuCli::die(1, 'Something Bad Happened')
28
+ @api = { :path => path[:path], :method => cli[:method], :command => cli[:command], :payload => (path[:payload] || false) }
29
+ end
30
+
31
+ def make_call
32
+ opts = {
33
+ :path => @api[:path],
34
+ :method => @api[:method],
35
+ :payload => @api[:payload],
36
+ :host => Config.host,
37
+ :port => Config.port,
38
+ :ssl => Config.ssl || false,
39
+ :user => Config.user || nil,
40
+ :read_timeout => Config.read_timeout || 15,
41
+ :open_timeout => Config.open_timeout || 5,
42
+ :password => Config.password || nil,
43
+ :proxy_address => Config.proxy_address || nil,
44
+ :proxy_port => Config.proxy_port || nil
45
+ }
46
+ api = Api.new
47
+ res = api.request(opts)
48
+ msg = api.response(res.code, res.body, @api[:command])
49
+ msg = Filter.new(@cli[:fields][:filter]).process(msg) if @cli[:fields][:filter]
50
+ endpoint = @api[:command]
51
+ if res.code != '200'
52
+ SensuCli::die(0)
53
+ elsif @cli[:fields][:format] == 'single'
54
+ Pretty.single(msg, endpoint)
55
+ elsif @cli[:fields][:format] == 'table'
56
+ fields = nil || @cli[:fields][:fields]
57
+ Pretty.table(msg, endpoint, fields)
58
+ elsif @cli[:fields][:format] == 'json'
59
+ Pretty.json(msg)
60
+ else
61
+ Pretty.print(msg, endpoint)
62
+ end
63
+ Pretty.count(msg) unless @cli[:fields][:format] == 'table' or @cli[:fields][:format] == 'json'
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,313 @@
1
+ require 'trollop'
2
+ require 'sensu-cli/version'
3
+ require 'rainbow/ext/string'
4
+
5
+ module SensuCli
6
+ class Cli # rubocop:disable ClassLength
7
+ SUB_COMMANDS = %w(info client check event stash aggregate silence resolve health)
8
+ CLIENT_COMMANDS = %w(list show delete history)
9
+ CHECK_COMMANDS = %w(list show request)
10
+ EVENT_COMMANDS = %w(list show delete)
11
+ STASH_COMMANDS = %w(list show delete create)
12
+ AGG_COMMANDS = %w(list show delete)
13
+ SIL_COMMANDS = ''
14
+ RES_COMMANDS = ''
15
+ INFO_COMMANDS = ''
16
+ HEALTH_COMMANDS = ''
17
+
18
+ CLIENT_BANNER = <<-EOS.gsub(/^ {10}/, '')
19
+ ** Client Commands **
20
+ sensu-cli client list (OPTIONS)
21
+ sensu-cli client show NODE
22
+ sensu-cli client delete NODE
23
+ sensu-cli client history NODE\n\r
24
+ EOS
25
+ INFO_BANNER = <<-EOS.gsub(/^ {10}/, '')
26
+ ** Info Commands **
27
+ sensu-cli info\n\r
28
+ EOS
29
+ HEALTH_BANNER = <<-EOS.gsub(/^ {10}/, '')
30
+ ** Health Commands **
31
+ sensu-cli health (OPTIONS)\n\r
32
+ EOS
33
+ CHECK_BANNER = <<-EOS.gsub(/^ {10}/, '')
34
+ ** Check Commands **
35
+ sensu-cli check list
36
+ sensu-cli check show CHECK
37
+ sensu-cli check request CHECK SUB1,SUB2\n\r
38
+ EOS
39
+ EVENT_BANNER = <<-EOS.gsub(/^ {10}/, '')
40
+ ** Event Commands **
41
+ sensu-cli event list (OPTIONS)
42
+ sensu-cli event show NODE (OPTIONS)
43
+ sensu-cli event delete NODE CHECK\n\r
44
+ EOS
45
+ STASH_BANNER = <<-EOS.gsub(/^ {10}/, '')
46
+ ** Stash Commands **
47
+ sensu-cli stash list (OPTIONS)
48
+ sensu-cli stash show STASHPATH
49
+ sensu-cli stash delete STASHPATH
50
+ sensu-cli stash create PATH\n\r
51
+ EOS
52
+ AGG_BANNER = <<-EOS.gsub(/^ {10}/, '')
53
+ ** Aggregate Commands **
54
+ sensu-cli aggregate list (OPTIONS)
55
+ sensu-cli aggregate show CHECK (OPTIONS)
56
+ sensu-cli aggregate delete CHECK\n\r
57
+ EOS
58
+ SIL_BANNER = <<-EOS.gsub(/^ {10}/, '')
59
+ ** Silence Commands **
60
+ sensu-cli silence NODE (OPTIONS)\n\r
61
+ EOS
62
+ RES_BANNER = <<-EOS.gsub(/^ {10}/, '')
63
+ ** Resolve Commands **
64
+ sensu-cli resolve NODE CHECK\n\r
65
+ EOS
66
+
67
+ def global
68
+ global_opts = Trollop::Parser.new do
69
+ version "sensu-cli version: #{SensuCli::VERSION}"
70
+ banner <<-'EOS'.gsub(/^ {10}/, '')
71
+ #
72
+ # Welcome to the sensu-cli.
73
+ # ______
74
+ # .-' '-.
75
+ # .' __ '.
76
+ # / / \ \
77
+ # ------------------
78
+ # /\
79
+ # '--'
80
+ # SENSU-CLI
81
+ #
82
+ EOS
83
+ banner "\n\rAvailable subcommands: (for details, sensu-cli SUB-COMMAND --help)\n\r"
84
+ banner AGG_BANNER
85
+ banner CHECK_BANNER
86
+ banner CLIENT_BANNER
87
+ banner EVENT_BANNER
88
+ banner HEALTH_BANNER
89
+ banner INFO_BANNER
90
+ banner SIL_BANNER
91
+ banner STASH_BANNER
92
+ banner RES_BANNER
93
+ stop_on SUB_COMMANDS
94
+ end
95
+
96
+ Trollop::with_standard_exception_handling global_opts do
97
+ global_opts.parse ARGV
98
+ raise Trollop::HelpNeeded if ARGV.empty? # show help screen
99
+ end
100
+
101
+ cmd = next_argv
102
+ self.respond_to?(cmd) ? send(cmd) : explode(global_opts)
103
+ end
104
+
105
+ def explode(opts)
106
+ Trollop::with_standard_exception_handling opts do
107
+ raise Trollop::HelpNeeded # show help screen
108
+ end
109
+ end
110
+
111
+ def deep_merge(hash_one, hash_two)
112
+ hash_one.merge(hash_two) { |_key, hash_one_item, hash_two_item| deep_merge(hash_one_item, hash_two_item) }
113
+ end
114
+
115
+ def parser(cli)
116
+ Trollop::Parser.new do
117
+ banner Cli.const_get("#{cli}_BANNER")
118
+ stop_on Cli.const_get("#{cli}_COMMANDS")
119
+ end
120
+ end
121
+
122
+ def explode_if_empty(opts, command)
123
+ explode(opts) if ARGV.empty? && command != 'list'
124
+ end
125
+
126
+ def next_argv
127
+ ARGV.shift
128
+ end
129
+
130
+ def client # rubocop:disable MethodLength
131
+ opts = parser('CLIENT')
132
+ command = next_argv
133
+ explode_if_empty(opts, command)
134
+ case command
135
+ when 'list'
136
+ p = Trollop::options do
137
+ opt :limit, 'The number if clients to return', :short => 'l', :type => :string
138
+ opt :offset, 'The number of clients to offset before returning', :short => 'o', :type => :string
139
+ opt :format, 'Available formats; single, table, json', :short => 'f', :type => :string
140
+ opt :fields, 'Fields for table ouput: -F name,address,subscriptions', :short => 'F', :type => :string
141
+ opt :filter, 'Field and value to filter on: name,graphite', :type => :string
142
+ end
143
+ Trollop::die :format, 'Available optional formats: single, table, json'.color(:red) if p[:format] != 'table' && p[:format] != 'single' && p[:format] != 'json' && p[:format]
144
+ Trollop::die :fields, 'Fields must be used in conjunction with --format table'.color(:red) if p[:format] != 'table' && p[:fields]
145
+ Trollop::die :offset, 'Offset depends on the limit option --limit ( -l )'.color(:red) if p[:offset] && !p[:limit]
146
+ { :command => 'clients', :method => 'Get', :fields => p }
147
+ when 'delete'
148
+ p = Trollop::options
149
+ item = next_argv # the ARGV.shift needs to happen after Trollop::options to catch --help
150
+ deep_merge({ :command => 'clients', :method => 'Delete', :fields => { :name => item } }, { :fields => p })
151
+ when 'show'
152
+ p = Trollop::options
153
+ item = next_argv
154
+ deep_merge({ :command => 'clients', :method => 'Get', :fields => { :name => item } }, { :fields => p })
155
+ when 'history'
156
+ p = Trollop::options
157
+ item = next_argv
158
+ deep_merge({ :command => 'clients', :method => 'Get', :fields => { :name => item, :history => true } }, { :fields => p })
159
+ else
160
+ explode(opts)
161
+ end
162
+ end
163
+
164
+ def info
165
+ parser('INFO')
166
+ p = Trollop::options
167
+ { :command => 'info', :method => 'Get', :fields => p }
168
+ end
169
+
170
+ def health
171
+ parser('HEALTH')
172
+ p = Trollop::options do
173
+ opt :consumers, 'The minimum number of consumers', :short => 'c', :type => :string, :required => true
174
+ opt :messages, 'The maximum number of messages', :short => 'm', :type => :string, :required => true
175
+ end
176
+ { :command => 'health', :method => 'Get', :fields => p }
177
+ end
178
+
179
+ def check
180
+ opts = parser('CHECK')
181
+ command = next_argv
182
+ explode_if_empty(opts, command)
183
+ item = next_argv
184
+ case command
185
+ when 'list'
186
+ p = Trollop::options do
187
+ opt :filter, 'Field and value to filter on: command,procs', :type => :string
188
+ end
189
+ { :command => 'checks', :method => 'Get', :fields => p }
190
+ when 'show'
191
+ p = Trollop::options
192
+ deep_merge({ :command => 'checks', :method => 'Get', :fields => { :name => item } }, { :fields => p })
193
+ when 'request'
194
+ p = Trollop::options
195
+ ARGV.empty? ? explode(opts) : subscribers = next_argv.split(',')
196
+ deep_merge({ :command => 'checks', :method => 'Post', :fields => { :check => item, :subscribers => subscribers } }, { :fields => p })
197
+ else
198
+ explode(opts)
199
+ end
200
+ end
201
+
202
+ def event
203
+ opts = parser('EVENT')
204
+ command = next_argv
205
+ explode_if_empty(opts, command)
206
+ case command
207
+ when 'list'
208
+ p = Trollop::options do
209
+ opt :filter, 'Field and value to filter on: client,graphite (use "name" as field for client or event name)', :type => :string
210
+ opt :format, 'Available formats; single, table, json', :short => 'f', :type => :string
211
+ end
212
+ Trollop::die :format, 'Available optional formats: single, table, json'.color(:red) if p[:format] != 'table' && p[:format] != 'single' && p[:format] != 'json' && p[:format]
213
+ { :command => 'events', :method => 'Get', :fields => p }
214
+ when 'show'
215
+ p = Trollop::options do
216
+ opt :check, 'Returns the check associated with the client', :short => 'k', :type => :string
217
+ end
218
+ item = next_argv
219
+ deep_merge({ :command => 'events', :method => 'Get', :fields => { :client => item } }, { :fields => p })
220
+ when 'delete'
221
+ p = Trollop::options
222
+ item = next_argv
223
+ check = next_argv
224
+ explode(opts) if check.nil?
225
+ deep_merge({ :command => 'events', :method => 'Delete', :fields => { :client => item, :check => check } }, { :fields => p })
226
+ else
227
+ explode(opts)
228
+ end
229
+ end
230
+
231
+ def stash
232
+ opts = parser('STASH')
233
+ command = next_argv
234
+ explode_if_empty(opts, command)
235
+ case command
236
+ when 'list'
237
+ p = Trollop::options do
238
+ opt :limit, 'The number of stashes to return', :short => 'l', :type => :string
239
+ opt :offset, 'The number of stashes to offset before returning', :short => 'o', :type => :string
240
+ opt :format, 'Available formats; single, table, json', :short => 'f', :type => :string
241
+ opt :filter, 'Field and value to filter on: path,graphite', :type => :string
242
+ end
243
+ Trollop::die :offset, 'Offset depends on the limit option --limit ( -l )'.color(:red) if p[:offset] && !p[:limit]
244
+ Trollop::die :format, 'Available optional formats: single, table, json'.color(:red) if p[:format] != 'table' && p[:format] != 'single' && p[:format] != 'json' && p[:format]
245
+ { :command => 'stashes', :method => 'Get', :fields => p }
246
+ when 'show'
247
+ p = Trollop::options
248
+ item = next_argv
249
+ deep_merge({ :command => 'stashes', :method => 'Get', :fields => { :path => item } }, { :fields => p })
250
+ when 'delete'
251
+ p = Trollop::options
252
+ item = next_argv
253
+ deep_merge({ :command => 'stashes', :method => 'Delete', :fields => { :path => item } }, { :fields => p })
254
+ when 'create'
255
+ p = Trollop::options
256
+ item = next_argv
257
+ deep_merge({ :command => 'stashes', :method => 'Post', :fields => { :create => true, :create_path => item } }, { :fields => p })
258
+ else
259
+ explode(opts)
260
+ end
261
+ end
262
+
263
+ def aggregate
264
+ opts = parser('AGG')
265
+ command = next_argv
266
+ explode_if_empty(opts, command)
267
+ case command
268
+ when 'list'
269
+ p = Trollop::options do
270
+ opt :filter, 'Field and value to filter on: issued,1399505890', :type => :string
271
+ end
272
+ { :command => 'aggregates', :method => 'Get', :fields => p }
273
+ when 'show'
274
+ p = Trollop::options do
275
+ opt :id, 'The id of the check issued.', :short => 'i', :type => :integer
276
+ opt :limit, 'The number of aggregates to return', :short => 'l', :type => :string
277
+ opt :offset, 'The number of aggregates to offset before returning', :short => 'o', :type => :string
278
+ # opt :results, 'Include the check results', :short => 'r', :type => :boolean
279
+ end
280
+ Trollop::die :offset, 'Offset depends on the limit option --limit ( -l )'.color(:red) if p[:offset] && !p[:limit]
281
+ item = next_argv
282
+ deep_merge({ :command => 'aggregates', :method => 'Get', :fields => { :check => item } }, { :fields => p })
283
+ when 'delete'
284
+ p = Trollop::options
285
+ item = next_argv
286
+ deep_merge({ :command => 'aggregates', :method => 'Delete', :fields => { :check => item } }, { :fields => p })
287
+ else
288
+ explode(opts)
289
+ end
290
+ end
291
+
292
+ def silence
293
+ opts = parser('SIL')
294
+ p = Trollop::options do
295
+ opt :check, 'The check to silence', :short => 'k', :type => :string
296
+ opt :owner, 'The owner of the stash', :short => 'o', :type => :string
297
+ opt :reason, 'The reason this check/node is being silenced', :short => 'r', :type => :string
298
+ opt :expire, 'The number of seconds the silenced event is valid', :short => 'e', :type => :integer
299
+ end
300
+ command = next_argv
301
+ explode(opts) if command.nil?
302
+ deep_merge({ :command => 'silence', :method => 'Post', :fields => { :client => command } }, { :fields => p })
303
+ end
304
+
305
+ def resolve
306
+ opts = parser('RES')
307
+ command = next_argv
308
+ p = Trollop::options
309
+ ARGV.empty? ? explode(opts) : check = next_argv
310
+ deep_merge({ :command => 'resolve', :method => 'Post', :fields => { :client => command, :check => check } }, { :fields => p })
311
+ end
312
+ end
313
+ end
@@ -0,0 +1,28 @@
1
+ require 'json'
2
+ require 'tempfile'
3
+
4
+ module SensuCli
5
+ class Editor
6
+ def create_stash(create_path)
7
+ file = temp_file(:path => create_path, :content => { :timestamp => Time.now.to_i })
8
+ edit(file)
9
+ end
10
+
11
+ def edit(file)
12
+ editor = ENV['EDITOR'] ? ENV['EDITOR'] : 'vi'
13
+ system("#{editor} #{file}")
14
+ begin
15
+ JSON.parse(File.read(file))
16
+ rescue JSON::ParserError
17
+ SensuCli::die(1, 'The stash you created has invalid JSON.')
18
+ end
19
+ end
20
+
21
+ def temp_file(template)
22
+ file = Tempfile.new('sensu')
23
+ file.write(JSON.pretty_generate(template))
24
+ file.close
25
+ file.path
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,39 @@
1
+ module SensuCli
2
+ class Filter
3
+ attr_reader :filter
4
+
5
+ def initialize(filter_data)
6
+ filter_split(filter_data)
7
+ end
8
+
9
+ def filter_split(filter)
10
+ @filter = filter.sub(' ', '').split(',')
11
+ end
12
+
13
+ def match?(data)
14
+ data.to_s.include? filter[1]
15
+ end
16
+
17
+ def inspect_hash(data)
18
+ data.any? do |key, value|
19
+ if value.is_a?(Array)
20
+ match?(value) if key == filter[0]
21
+ elsif value.is_a?(Hash)
22
+ process(value)
23
+ else
24
+ match?(value) if key == filter[0]
25
+ end
26
+ end
27
+ end
28
+
29
+ def process(data)
30
+ if data.is_a?(Array)
31
+ data.select do |value|
32
+ process(value)
33
+ end
34
+ elsif data.is_a?(Hash)
35
+ inspect_hash(data)
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,96 @@
1
+ module SensuCli
2
+ class PathCreator
3
+ def clients(cli)
4
+ path = '/clients'
5
+ path << "/#{cli[:fields][:name]}" if cli[:fields][:name]
6
+ path << '/history' if cli[:fields][:history]
7
+ path << pagination(cli)
8
+ respond(path)
9
+ end
10
+
11
+ def info(*)
12
+ path = '/info'
13
+ respond(path)
14
+ end
15
+
16
+ def health(cli)
17
+ path = "/health?consumers=#{cli[:fields][:consumers]}&messages=#{cli[:fields][:messages]}"
18
+ respond(path)
19
+ end
20
+
21
+ def stashes(cli)
22
+ if cli[:fields][:create]
23
+ e = Editor.new
24
+ payload = e.create_stash(cli[:fields][:create_path]).to_json
25
+ end
26
+ path = '/stashes'
27
+ path << "/#{cli[:fields][:path]}" if cli[:fields][:path]
28
+ path << pagination(cli)
29
+ respond(path, payload)
30
+ end
31
+
32
+ def checks(cli)
33
+ if cli[:fields][:name]
34
+ path = "/check/#{cli[:fields][:name]}"
35
+ elsif cli[:fields][:subscribers]
36
+ payload = { :check => cli[:fields][:check], :subscribers => cli[:fields][:subscribers] }.to_json
37
+ path = '/request'
38
+ else
39
+ path = '/checks'
40
+ end
41
+ respond(path, payload)
42
+ end
43
+
44
+ def events(cli)
45
+ path = '/events'
46
+ path << "/#{cli[:fields][:client]}" if cli[:fields][:client]
47
+ path << "/#{cli[:fields][:check]}" if cli[:fields][:check]
48
+ respond(path)
49
+ end
50
+
51
+ def resolve(cli)
52
+ payload = { :client => cli[:fields][:client], :check => cli[:fields][:check] }.to_json
53
+ path = '/resolve'
54
+ respond(path, payload)
55
+ end
56
+
57
+ def silence(cli)
58
+ content = { :timestamp => Time.now.to_i }
59
+ content.merge!(:owner => cli[:fields][:owner]) if cli[:fields][:owner]
60
+ content.merge!(:reason => cli[:fields][:reason]) if cli[:fields][:reason]
61
+ payload = { :content => content }
62
+ payload.merge!(:expire => cli[:fields][:expire].to_i) if cli[:fields][:expire]
63
+ silence_path = 'silence'
64
+ silence_path << "/#{cli[:fields][:client]}" if cli[:fields][:client]
65
+ silence_path << "/#{cli[:fields][:check]}" if cli[:fields][:check]
66
+ payload = payload.merge!(:path => silence_path).to_json
67
+ respond('/stashes', payload)
68
+ end
69
+
70
+ def aggregates(cli)
71
+ path = '/aggregates'
72
+ path << "/#{cli[:fields][:check]}" if cli[:fields][:check]
73
+ path << "/#{cli[:fields][:id]}" if cli[:fields][:id]
74
+ path << pagination(cli)
75
+ respond(path)
76
+ end
77
+
78
+ def pagination(cli)
79
+ if cli[:fields].key?(:limit) && cli[:fields].key?(:offset)
80
+ "?limit=#{cli[:fields][:limit]}&offset=#{cli[:fields][:offset]}"
81
+ elsif cli[:fields].key?(:limit)
82
+ "?limit=#{cli[:fields][:limit]}"
83
+ else
84
+ ''
85
+ end
86
+ end
87
+
88
+ def method_missing(method_name, *_args)
89
+ puts "Path method: #{method_name} does not exist. "
90
+ end
91
+
92
+ def respond(path, payload = false)
93
+ { :path => path, :payload => payload }
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,136 @@
1
+ require 'rainbow/ext/string'
2
+ require 'hirb'
3
+ require 'json'
4
+ require 'erubis'
5
+
6
+ module SensuCli
7
+ class Pretty
8
+ class << self
9
+ def print(res, endpoint = nil)
10
+ return no_values if res.empty?
11
+ case endpoint
12
+ when 'events'
13
+ template = File.read(File.expand_path(File.join(File.dirname(__FILE__), 'templates/event.erb')))
14
+ renderer = Erubis::Eruby.new(template)
15
+ res.each do |event|
16
+ puts renderer.result(event)
17
+ end
18
+ return
19
+ end
20
+ if res.is_a?(Hash)
21
+ res.each do |key, value|
22
+ puts "#{key}: ".color(:cyan) + "#{value}".color(:green)
23
+ end
24
+ elsif res.is_a?(Array)
25
+ res.each do |item|
26
+ puts '-------'.color(:yellow)
27
+ if item.is_a?(Hash)
28
+ item.each do |key, value|
29
+ puts "#{key}: ".color(:cyan) + "#{value}".color(:green)
30
+ end
31
+ else
32
+ puts item.to_s.color(:cyan)
33
+ end
34
+ end
35
+ end
36
+ end
37
+
38
+ def json(res)
39
+ puts JSON.pretty_generate(res)
40
+ end
41
+
42
+ def count(res)
43
+ res.is_a?(Hash) ? count = res.length : count = res.count
44
+ puts "#{count} total items".color(:yellow) if count
45
+ end
46
+
47
+ def clean(thing)
48
+ thing = thing.gsub("\n", '/\n') if thing.is_a?(String)
49
+ thing
50
+ end
51
+
52
+ def no_values
53
+ puts 'no values for this request'.color(:cyan)
54
+ end
55
+
56
+ def single(res, endpoint = nil)
57
+ return no_values if res.empty?
58
+ case endpoint
59
+ when 'events'
60
+ res = event_data(res)
61
+ end
62
+ keys = res.map { |item| item.keys }.flatten.uniq.sort
63
+
64
+ # Remove fields with spaces (breaks awkage)
65
+ keys.select! do |key|
66
+ res.none? { |item| item[key].to_s.include?(' ') }
67
+ end
68
+
69
+ # Find max value lengths
70
+ value_lengths = {}
71
+ keys.each do |key|
72
+ max_value_length = res.map { |item| item[key].to_s.length }.max
73
+ value_lengths[key] = [max_value_length, key.length].max
74
+ end
75
+
76
+ # Print header
77
+ lengths = keys.map { |key| "%-#{value_lengths[key]}s" }.join(' ')
78
+ puts format(lengths, *keys)
79
+
80
+ # Print value rows
81
+ res.each do |item|
82
+ if item.is_a?(Hash)
83
+ values = keys.map { |key| item[key] }
84
+ puts format(lengths, *values)
85
+ else
86
+ puts item.to_s.color(:cyan)
87
+ end
88
+ end
89
+ end
90
+
91
+ def parse_fields(fields)
92
+ fields.split(',')
93
+ end
94
+
95
+ def event_data(res)
96
+ events = []
97
+ res.each do |event|
98
+ events << {
99
+ 'client' => event['client']['name'],
100
+ 'address' => event['client']['address'],
101
+ 'check' => event['check']['name'],
102
+ 'interval' => event['check']['interval'],
103
+ 'occurrences' => event['occurrences'],
104
+ 'status' => event['check']['status'],
105
+ 'handlers' => event['check']['handlers'],
106
+ 'issued' => event['check']['issued'],
107
+ 'executed' => event['check']['executed'],
108
+ 'output' => event['check']['output'].rstrip
109
+ }
110
+ end
111
+ events
112
+ end
113
+
114
+ def table(res, endpoint = nil, fields = nil)
115
+ return no_values if res.empty?
116
+ case endpoint
117
+ when 'events'
118
+ keys = %w(check client address interval occurrences status handlers issued executed output)
119
+ render_table(event_data(res), keys)
120
+ else
121
+ if fields
122
+ keys = parse_fields(fields)
123
+ else
124
+ keys = res.map { |item| item.keys }.flatten.uniq
125
+ end
126
+ render_table(res, keys)
127
+ end
128
+ end
129
+
130
+ def render_table(data, keys)
131
+ terminal_size = Hirb::Util.detect_terminal_size
132
+ puts Hirb::Helpers::AutoTable.render(data, :max_width => terminal_size[0], :fields => keys)
133
+ end
134
+ end
135
+ end
136
+ end
@@ -0,0 +1,21 @@
1
+ require 'fileutils'
2
+ require 'mixlib/config'
3
+ require 'rainbow/ext/string'
4
+
5
+ module SensuCli
6
+ class Settings
7
+ def file?(file)
8
+ !File.readable?(file) ? false : true # rubocop:disable FavorUnlessOverNegatedIf
9
+ end
10
+
11
+ def create(directory, file)
12
+ FileUtils.mkdir_p(directory) unless File.directory?(directory)
13
+ FileUtils.cp(File.join(File.dirname(__FILE__), '../../settings.example.rb'), file)
14
+ SensuCli::die(0, "We created the configuration file for you at #{file}. You can also place this in /etc/sensu/sensu-cli/settings.rb. Edit the settings as needed.".color(:red))
15
+ end
16
+ end
17
+
18
+ class Config
19
+ extend(Mixlib::Config)
20
+ end
21
+ end
@@ -0,0 +1,20 @@
1
+ <%= "-------".color(:yellow) %>
2
+ <%= "id:".color(:blue).bright %> <%= id.to_s.color(:green) %>
3
+ <%= "client name:".color(:blue).bright %> <%= client['name'].to_s.color(:green) %>
4
+ <%= "address:".color(:cyan) %> <%= client['address'].to_s.color(:green) %>
5
+ <%= "subscriptions:".color(:cyan) %> <%= client['subscriptions'].to_s.color(:green) %>
6
+ <%= "version:".color(:cyan) %> <%= client['version'].to_s.color(:green) %>
7
+ <%= "timestamp:".color(:cyan) %> <%= client['timestamp'].to_s.color(:green) %>
8
+ <%= "check name:".color(:blue).bright %> <%= check['name'].to_s.color(:green) %>
9
+ <%= "command:".color(:cyan) %> <%= check['command'].to_s.color(:green) %>
10
+ <%= "handlers:".color(:cyan) %> <%= check['handlers'].to_s.color(:green) %>
11
+ <%= "interval:".color(:cyan) %> <%= check['interval'].to_s.color(:green) %>
12
+ <%= "subscribers:".color(:cyan) %> <%= check['subscribers'].to_s.color(:green) %>
13
+ <%= "issued:".color(:cyan) %> <%= check['issued'].to_s.color(:green) %>
14
+ <%= "executed:".color(:cyan) %> <%= check['executed'].to_s.color(:green) %>
15
+ <%= "duration:".color(:cyan) %> <%= check['duration'].to_s.color(:green) %>
16
+ <%= "output:".color(:cyan) %> <%= check['output'].rstrip.to_s.color(:green) %>
17
+ <%= "status:".color(:cyan) %> <%= check['status'].to_s.color(:green) %>
18
+ <%= "history:".color(:cyan) %> <%= check['history'].to_s.color(:green) %>
19
+ <%= "occurrences:".color(:blue).bright %> <%= occurrences.to_s.color(:green) %>
20
+ <%= "action:".color(:blue).bright %> <%= action.to_s.color(:green) %>
@@ -0,0 +1,3 @@
1
+ module SensuCli
2
+ VERSION = '0.6.3.2'
3
+ end
data/sensu-cli.gemspec ADDED
@@ -0,0 +1,28 @@
1
+ require File.join(File.dirname(__FILE__), 'lib', 'sensu-cli', 'version')
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = 'sensu-cli-compat'
5
+ s.version = SensuCli::VERSION
6
+ s.platform = Gem::Platform::RUBY
7
+ s.summary = 'A command line utility for Sensu.'
8
+ s.description = 'A command line utility for interacting with the Sensu api.'
9
+ s.authors = ['Bryan Brandau', 'Rob Thomas']
10
+ s.email = ['agent462@gmail.com', 'xrobau@gmail.com']
11
+ s.has_rdoc = false
12
+ s.licenses = %w(MIT APACHE)
13
+ s.homepage = 'http://github.com/xrobau/sensu-cli'
14
+
15
+ s.add_dependency('rainbow', '1.99.2')
16
+ s.add_dependency('trollop', '2.0')
17
+ s.add_dependency('mixlib-config', '2.1.0')
18
+ s.add_dependency('hirb', '0.7.1')
19
+ s.add_dependency('erubis', '2.7.0')
20
+ s.add_dependency('json', '1.8.1')
21
+
22
+ s.add_development_dependency('rspec')
23
+ s.add_development_dependency('rubocop')
24
+
25
+ s.files = Dir.glob('{bin,lib}/**/*') + %w(sensu-cli.gemspec README.md settings.example.rb)
26
+ s.executables = Dir.glob('bin/**/*').map { |file| File.basename(file) }
27
+ s.require_paths = ['lib']
28
+ end
@@ -0,0 +1,3 @@
1
+ host '127.0.0.1'
2
+ port '4567'
3
+ ssl false
metadata ADDED
@@ -0,0 +1,147 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sensu-cli-compat
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.6.3.2
5
+ platform: ruby
6
+ authors:
7
+ - Bryan Brandau
8
+ - Rob Thomas
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2014-12-04 00:00:00 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: rainbow
17
+ prerelease: false
18
+ requirement: &id001 !ruby/object:Gem::Requirement
19
+ requirements:
20
+ - - "="
21
+ - !ruby/object:Gem::Version
22
+ version: 1.99.2
23
+ type: :runtime
24
+ version_requirements: *id001
25
+ - !ruby/object:Gem::Dependency
26
+ name: trollop
27
+ prerelease: false
28
+ requirement: &id002 !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - "="
31
+ - !ruby/object:Gem::Version
32
+ version: "2.0"
33
+ type: :runtime
34
+ version_requirements: *id002
35
+ - !ruby/object:Gem::Dependency
36
+ name: mixlib-config
37
+ prerelease: false
38
+ requirement: &id003 !ruby/object:Gem::Requirement
39
+ requirements:
40
+ - - "="
41
+ - !ruby/object:Gem::Version
42
+ version: 2.1.0
43
+ type: :runtime
44
+ version_requirements: *id003
45
+ - !ruby/object:Gem::Dependency
46
+ name: hirb
47
+ prerelease: false
48
+ requirement: &id004 !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - "="
51
+ - !ruby/object:Gem::Version
52
+ version: 0.7.1
53
+ type: :runtime
54
+ version_requirements: *id004
55
+ - !ruby/object:Gem::Dependency
56
+ name: erubis
57
+ prerelease: false
58
+ requirement: &id005 !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - "="
61
+ - !ruby/object:Gem::Version
62
+ version: 2.7.0
63
+ type: :runtime
64
+ version_requirements: *id005
65
+ - !ruby/object:Gem::Dependency
66
+ name: json
67
+ prerelease: false
68
+ requirement: &id006 !ruby/object:Gem::Requirement
69
+ requirements:
70
+ - - "="
71
+ - !ruby/object:Gem::Version
72
+ version: 1.8.1
73
+ type: :runtime
74
+ version_requirements: *id006
75
+ - !ruby/object:Gem::Dependency
76
+ name: rspec
77
+ prerelease: false
78
+ requirement: &id007 !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - &id008
81
+ - ">="
82
+ - !ruby/object:Gem::Version
83
+ version: "0"
84
+ type: :development
85
+ version_requirements: *id007
86
+ - !ruby/object:Gem::Dependency
87
+ name: rubocop
88
+ prerelease: false
89
+ requirement: &id009 !ruby/object:Gem::Requirement
90
+ requirements:
91
+ - *id008
92
+ type: :development
93
+ version_requirements: *id009
94
+ description: A command line utility for interacting with the Sensu api.
95
+ email:
96
+ - agent462@gmail.com
97
+ - xrobau@gmail.com
98
+ executables:
99
+ - sensu
100
+ - sensu-cli
101
+ extensions: []
102
+
103
+ extra_rdoc_files: []
104
+
105
+ files:
106
+ - README.md
107
+ - bin/sensu
108
+ - bin/sensu-cli
109
+ - lib/sensu-cli.rb
110
+ - lib/sensu-cli/api.rb
111
+ - lib/sensu-cli/base.rb
112
+ - lib/sensu-cli/cli.rb
113
+ - lib/sensu-cli/editor.rb
114
+ - lib/sensu-cli/filter.rb
115
+ - lib/sensu-cli/path.rb
116
+ - lib/sensu-cli/pretty.rb
117
+ - lib/sensu-cli/settings.rb
118
+ - lib/sensu-cli/templates/event.erb
119
+ - lib/sensu-cli/version.rb
120
+ - sensu-cli.gemspec
121
+ - settings.example.rb
122
+ homepage: http://github.com/xrobau/sensu-cli
123
+ licenses:
124
+ - MIT
125
+ - APACHE
126
+ metadata: {}
127
+
128
+ post_install_message:
129
+ rdoc_options: []
130
+
131
+ require_paths:
132
+ - lib
133
+ required_ruby_version: !ruby/object:Gem::Requirement
134
+ requirements:
135
+ - *id008
136
+ required_rubygems_version: !ruby/object:Gem::Requirement
137
+ requirements:
138
+ - *id008
139
+ requirements: []
140
+
141
+ rubyforge_project:
142
+ rubygems_version: 2.4.5
143
+ signing_key:
144
+ specification_version: 4
145
+ summary: A command line utility for Sensu.
146
+ test_files: []
147
+