morpheus-cli 4.1.8 → 4.1.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Dockerfile +1 -1
- data/lib/morpheus/api/api_client.rb +24 -0
- data/lib/morpheus/api/{old_cypher_interface.rb → budgets_interface.rb} +10 -11
- data/lib/morpheus/api/cloud_datastores_interface.rb +7 -0
- data/lib/morpheus/api/cloud_resource_pools_interface.rb +2 -2
- data/lib/morpheus/api/cypher_interface.rb +18 -12
- data/lib/morpheus/api/health_interface.rb +72 -0
- data/lib/morpheus/api/instances_interface.rb +1 -1
- data/lib/morpheus/api/library_instance_types_interface.rb +7 -0
- data/lib/morpheus/api/log_settings_interface.rb +6 -0
- data/lib/morpheus/api/network_security_servers_interface.rb +30 -0
- data/lib/morpheus/api/price_sets_interface.rb +42 -0
- data/lib/morpheus/api/prices_interface.rb +68 -0
- data/lib/morpheus/api/provisioning_settings_interface.rb +29 -0
- data/lib/morpheus/api/servers_interface.rb +1 -1
- data/lib/morpheus/api/service_plans_interface.rb +34 -11
- data/lib/morpheus/api/task_sets_interface.rb +8 -0
- data/lib/morpheus/api/tasks_interface.rb +8 -0
- data/lib/morpheus/cli.rb +6 -3
- data/lib/morpheus/cli/appliance_settings_command.rb +13 -5
- data/lib/morpheus/cli/approvals_command.rb +1 -1
- data/lib/morpheus/cli/apps.rb +88 -28
- data/lib/morpheus/cli/backup_settings_command.rb +1 -1
- data/lib/morpheus/cli/blueprints_command.rb +2 -0
- data/lib/morpheus/cli/budgets_command.rb +672 -0
- data/lib/morpheus/cli/cli_command.rb +13 -2
- data/lib/morpheus/cli/cli_registry.rb +1 -0
- data/lib/morpheus/cli/clusters.rb +40 -274
- data/lib/morpheus/cli/commands/standard/benchmark_command.rb +114 -66
- data/lib/morpheus/cli/commands/standard/coloring_command.rb +12 -0
- data/lib/morpheus/cli/commands/standard/curl_command.rb +31 -6
- data/lib/morpheus/cli/commands/standard/echo_command.rb +8 -3
- data/lib/morpheus/cli/commands/standard/set_prompt_command.rb +1 -1
- data/lib/morpheus/cli/containers_command.rb +37 -24
- data/lib/morpheus/cli/cypher_command.rb +191 -150
- data/lib/morpheus/cli/health_command.rb +903 -0
- data/lib/morpheus/cli/hosts.rb +43 -32
- data/lib/morpheus/cli/instances.rb +119 -68
- data/lib/morpheus/cli/jobs_command.rb +1 -1
- data/lib/morpheus/cli/library_instance_types_command.rb +61 -11
- data/lib/morpheus/cli/library_option_types_command.rb +2 -2
- data/lib/morpheus/cli/log_settings_command.rb +46 -3
- data/lib/morpheus/cli/logs_command.rb +24 -17
- data/lib/morpheus/cli/mixins/accounts_helper.rb +2 -0
- data/lib/morpheus/cli/mixins/logs_helper.rb +73 -19
- data/lib/morpheus/cli/mixins/print_helper.rb +29 -1
- data/lib/morpheus/cli/mixins/provisioning_helper.rb +554 -96
- data/lib/morpheus/cli/mixins/whoami_helper.rb +13 -1
- data/lib/morpheus/cli/networks_command.rb +3 -0
- data/lib/morpheus/cli/option_types.rb +83 -53
- data/lib/morpheus/cli/price_sets_command.rb +543 -0
- data/lib/morpheus/cli/prices_command.rb +669 -0
- data/lib/morpheus/cli/processes_command.rb +0 -2
- data/lib/morpheus/cli/provisioning_settings_command.rb +237 -0
- data/lib/morpheus/cli/remote.rb +9 -4
- data/lib/morpheus/cli/reports_command.rb +10 -4
- data/lib/morpheus/cli/roles.rb +93 -38
- data/lib/morpheus/cli/security_groups.rb +10 -0
- data/lib/morpheus/cli/service_plans_command.rb +736 -0
- data/lib/morpheus/cli/tasks.rb +220 -8
- data/lib/morpheus/cli/tenants_command.rb +3 -16
- data/lib/morpheus/cli/users.rb +2 -25
- data/lib/morpheus/cli/version.rb +1 -1
- data/lib/morpheus/cli/whitelabel_settings_command.rb +18 -18
- data/lib/morpheus/cli/whoami.rb +28 -10
- data/lib/morpheus/cli/workflows.rb +488 -36
- data/lib/morpheus/formatters.rb +22 -0
- data/morpheus-cli.gemspec +1 -0
- metadata +28 -5
- data/lib/morpheus/cli/accounts.rb +0 -335
- data/lib/morpheus/cli/old_cypher_command.rb +0 -412
@@ -0,0 +1,903 @@
|
|
1
|
+
require 'io/console'
|
2
|
+
require 'rest_client'
|
3
|
+
require 'optparse'
|
4
|
+
require 'morpheus/cli/cli_command'
|
5
|
+
require 'morpheus/cli/mixins/logs_helper'
|
6
|
+
require 'morpheus/cli/option_types'
|
7
|
+
require 'json'
|
8
|
+
|
9
|
+
class Morpheus::Cli::HealthCommand
|
10
|
+
include Morpheus::Cli::CliCommand
|
11
|
+
include Morpheus::Cli::LogsHelper
|
12
|
+
set_command_name :health
|
13
|
+
register_subcommands :get, :alarms, :'get-alarm', :'acknowledge-alarms', :'unacknowledge-alarms', :logs
|
14
|
+
|
15
|
+
def connect(opts)
|
16
|
+
@api_client = establish_remote_appliance_connection(opts)
|
17
|
+
@health_interface = @api_client.health
|
18
|
+
end
|
19
|
+
|
20
|
+
def handle(args)
|
21
|
+
handle_subcommand(args)
|
22
|
+
end
|
23
|
+
|
24
|
+
def get(args)
|
25
|
+
options = {}
|
26
|
+
params = {}
|
27
|
+
live_health = false
|
28
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
29
|
+
opts.banner = subcommand_usage("[-a] [options]")
|
30
|
+
opts.on('-a', '--all', "Display all details: CPU, Memory, Database, etc." ) do
|
31
|
+
options[:details] = true
|
32
|
+
options[:show_cpu] = true
|
33
|
+
options[:show_memory] = true
|
34
|
+
options[:show_database] = true
|
35
|
+
options[:show_elastic] = true
|
36
|
+
options[:show_queue] = true
|
37
|
+
end
|
38
|
+
opts.on('--details', '--details', "Display all details: CPU, Memory, Database, etc." ) do
|
39
|
+
options[:details] = true
|
40
|
+
options[:show_cpu] = true
|
41
|
+
options[:show_memory] = true
|
42
|
+
options[:show_database] = true
|
43
|
+
options[:show_elastic] = true
|
44
|
+
options[:show_queue] = true
|
45
|
+
end
|
46
|
+
opts.add_hidden_option('--details') # prefer -a, --all
|
47
|
+
opts.on('--cpu', "Display CPU details" ) do
|
48
|
+
options[:show_cpu] = true
|
49
|
+
end
|
50
|
+
opts.on('--memory', "Display Memory details" ) do
|
51
|
+
options[:show_memory] = true
|
52
|
+
end
|
53
|
+
opts.on('--database', "Display Database details" ) do
|
54
|
+
options[:show_database] = true
|
55
|
+
end
|
56
|
+
opts.on('--elastic', "Display Elasticsearch details" ) do
|
57
|
+
options[:show_elastic] = true
|
58
|
+
end
|
59
|
+
opts.on('--queue', "Display Queue (Rabbit) details" ) do
|
60
|
+
options[:show_queue] = true
|
61
|
+
end
|
62
|
+
opts.on('--queues', "Display Queue (Rabbit) details" ) do
|
63
|
+
options[:show_queue] = true
|
64
|
+
end
|
65
|
+
opts.on('--rabbit', "Display Queue (Rabbit) details" ) do
|
66
|
+
options[:show_queue] = true
|
67
|
+
end
|
68
|
+
opts.add_hidden_option('--queues')
|
69
|
+
opts.add_hidden_option('--rabbit')
|
70
|
+
opts.on('--live', "Fetch Live Health Data. By default the last cached health data is returned. This also retrieves all elastic indices." ) do
|
71
|
+
live_health = true
|
72
|
+
end
|
73
|
+
build_common_options(opts, options, [:query, :json, :yaml, :csv, :fields, :dry_run, :remote])
|
74
|
+
opts.footer = "Get appliance health information." + "\n" +
|
75
|
+
"By default, only the health status and levels are displayed." + "\n" +
|
76
|
+
"Display more details with the options --cpu, --database, --memory, etc." + "\n" +
|
77
|
+
"Display all details with the -a option."
|
78
|
+
end
|
79
|
+
optparse.parse!(args)
|
80
|
+
|
81
|
+
if args.count != 0
|
82
|
+
raise_command_error "wrong number of arguments, expected 0 and got (#{args.count}) #{args}\n#{optparse}"
|
83
|
+
end
|
84
|
+
|
85
|
+
connect(options)
|
86
|
+
begin
|
87
|
+
@health_interface.setopts(options)
|
88
|
+
if options[:dry_run]
|
89
|
+
print_dry_run(live_health ? @health_interface.dry.live(params) : @health_interface.dry.get(params))
|
90
|
+
return 0
|
91
|
+
end
|
92
|
+
json_response = live_health ? @health_interface.live(params) : @health_interface.get(params)
|
93
|
+
render_result = render_with_format(json_response, options, 'health')
|
94
|
+
exit_code = json_response['success'] == true ? 0 : 1
|
95
|
+
return exit_code if render_result
|
96
|
+
|
97
|
+
health = json_response['health']
|
98
|
+
subtitles = []
|
99
|
+
if options[:details]
|
100
|
+
subtitles << "Details"
|
101
|
+
end
|
102
|
+
if live_health
|
103
|
+
subtitles << "(Live)"
|
104
|
+
end
|
105
|
+
print_h1 "Morpheus Health", subtitles, options
|
106
|
+
# thin print below here
|
107
|
+
options.merge!({thin:true})
|
108
|
+
|
109
|
+
if health.nil?
|
110
|
+
print yellow,"No health data returned.",reset,"\n"
|
111
|
+
return 1
|
112
|
+
end
|
113
|
+
if health['elastic'] && health['elastic']['noticeMessage'].to_s != ""
|
114
|
+
print cyan,health['elastic']['noticeMessage'],reset,"\n"
|
115
|
+
print "\n"
|
116
|
+
end
|
117
|
+
|
118
|
+
#print_h2 "Health Summary", options
|
119
|
+
print cyan
|
120
|
+
health_summary_columns = {
|
121
|
+
"Overall" => lambda {|it| format_health_status(it['cpu']['status']) rescue '' },
|
122
|
+
"CPU" => lambda {|it| format_health_status(it['cpu']['status']) rescue '' },
|
123
|
+
"Memory" => lambda {|it| format_health_status(it['memory']['status']) rescue '' },
|
124
|
+
"Database" => lambda {|it| format_health_status(it['database']['status']) rescue '' },
|
125
|
+
"Elastic" => lambda {|it| format_health_status(it['elastic']['status']) rescue '' },
|
126
|
+
"Queue" => lambda {|it| format_health_status(it['rabbit']['status']) rescue '' },
|
127
|
+
}
|
128
|
+
print as_pretty_table(health, health_summary_columns, options)
|
129
|
+
print "\n"
|
130
|
+
|
131
|
+
# flash warnings
|
132
|
+
if health['cpu'] && health['cpu']['status'] != 'ok' && health['cpu']['statusMessage']
|
133
|
+
status_color = health['cpu']['status'] == 'error' ? red : yellow
|
134
|
+
print status_color,health['cpu']['statusMessage'],reset,"\n"
|
135
|
+
end
|
136
|
+
if health['memory'] && health['memory']['status'] != 'ok' && health['memory']['statusMessage']
|
137
|
+
status_color = health['memory']['status'] == 'error' ? red : yellow
|
138
|
+
print status_color,health['memory']['statusMessage'],reset,"\n"
|
139
|
+
end
|
140
|
+
if health['database'] && health['database']['status'] != 'ok' && health['database']['statusMessage']
|
141
|
+
status_color = health['database']['status'] == 'error' ? red : yellow
|
142
|
+
print status_color,health['database']['statusMessage'],reset,"\n"
|
143
|
+
end
|
144
|
+
# if health['elastic'] && health['elastic']['noticeMessage'].to_s != ""
|
145
|
+
# print cyan,health['elastic']['noticeMessage'],reset,"\n"
|
146
|
+
# end
|
147
|
+
if health['elastic'] && health['elastic']['status'] != 'ok' && health['elastic']['statusMessage']
|
148
|
+
status_color = health['elastic']['status'] == 'error' ? red : yellow
|
149
|
+
print status_color,health['elastic']['statusMessage'],reset,"\n"
|
150
|
+
end
|
151
|
+
if health['rabbit'] && health['rabbit']['status'] != 'ok' && health['rabbit']['statusMessage']
|
152
|
+
status_color = health['rabbit']['status'] == 'error' ? red : yellow
|
153
|
+
print status_color,health['rabbit']['statusMessage'],reset,"\n"
|
154
|
+
end
|
155
|
+
|
156
|
+
print_h2 "Health Levels", options
|
157
|
+
print cyan
|
158
|
+
health_levels_columns = {
|
159
|
+
"Morpheus CPU" => lambda {|it| format_percent(it['cpu']['cpuLoad'].to_f, 0) rescue '' },
|
160
|
+
"System CPU" => lambda {|it| format_percent(it['cpu']['cpuTotalLoad'].to_f, 0) rescue '' },
|
161
|
+
"Morpheus Memory" => lambda {|it| format_percent(it['memory']['memoryPercent'].to_f * 100, 0) rescue '' },
|
162
|
+
"System Memory" => lambda {|it| format_percent(it['memory']['systemMemoryPercent'].to_f * 100, 0) rescue '' },
|
163
|
+
"Used Swap" => lambda {|it| format_percent(it['memory']['swapPercent'].to_f * 100, 0) rescue '' },
|
164
|
+
}
|
165
|
+
print as_pretty_table(health, health_levels_columns, options)
|
166
|
+
# print "\n"
|
167
|
+
|
168
|
+
if options[:show_cpu]
|
169
|
+
# CPU
|
170
|
+
if health['cpu'].nil?
|
171
|
+
print yellow,"No cpu health information returned.",reset,"\n"
|
172
|
+
else
|
173
|
+
print_h2 "CPU", options
|
174
|
+
print cyan
|
175
|
+
cpu_columns = {
|
176
|
+
"Processor Count" => lambda {|it| it['processorCount'] rescue '' },
|
177
|
+
"Process Time" => lambda {|it| format_human_duration(it['processTime'].to_f / 1000) rescue '' },
|
178
|
+
"Morpheus CPU" => lambda {|it| (it['cpuLoad'].to_f.round(2).to_s + '%') rescue '' },
|
179
|
+
"System CPU" => lambda {|it| (it['cpuTotalLoad'].to_f.round(2).to_s + '%') rescue '' },
|
180
|
+
"System Load" => lambda {|it| (it['systemLoad'].to_f.round(3)) rescue '' },
|
181
|
+
}
|
182
|
+
#print as_pretty_table(health['cpu'], cpu_columns, options)
|
183
|
+
print_description_list(cpu_columns, health['cpu'], options)
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
# Memory
|
188
|
+
if options[:show_memory]
|
189
|
+
if health['memory'].nil?
|
190
|
+
print yellow,"No memory health information returned.",reset,"\n"
|
191
|
+
else
|
192
|
+
print_h2 "Memory", options
|
193
|
+
print cyan
|
194
|
+
memory_columns = {
|
195
|
+
"Morpheus Memory" => lambda {|it| format_bytes_short(it['totalMemory']) rescue '' },
|
196
|
+
"Morpheus Used Memory" => lambda {|it| format_bytes_short(it['usedMemory']) rescue '' },
|
197
|
+
"Morpheus Free Memory" => lambda {|it| format_bytes_short(it['freeMemory']) rescue '' },
|
198
|
+
"Morpheus Memory Usage" => lambda {|it| format_percent(it['memoryPercent'].to_f * 100) rescue '' },
|
199
|
+
"System Memory" => lambda {|it| format_bytes_short(it['systemMemory']) rescue '' },
|
200
|
+
"System Used Memory" => lambda {|it| format_bytes_short(it['committedMemory']) rescue '' },
|
201
|
+
"System Free Memory" => lambda {|it| format_bytes_short(it['systemFreeMemory']) rescue '' },
|
202
|
+
"System Memory Usage" => lambda {|it| format_percent(it['systemMemoryPercent'].to_f * 100) rescue '' },
|
203
|
+
"System Swap" => lambda {|it| format_bytes_short(it['systemSwap']) rescue '' },
|
204
|
+
"Free Swap" => lambda {|it| format_bytes_short(it['systemFreeSwap']) rescue '' },
|
205
|
+
#"Used Swap" => lambda {|it| format_percent(it['swapPercent'].to_f * 100) rescue '' }
|
206
|
+
# "System Load" => lambda {|it| (it['systemLoad'].to_f(3)) rescue '' },
|
207
|
+
}
|
208
|
+
#print as_pretty_table(health['memory'], memory_columns, options)
|
209
|
+
print_description_list(memory_columns, health['memory'], options)
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
# Database (mysql)
|
214
|
+
if options[:show_database]
|
215
|
+
if health['database'].nil?
|
216
|
+
print yellow,"No database health information returned.",reset,"\n"
|
217
|
+
else
|
218
|
+
print_h2 "Database", options
|
219
|
+
print cyan
|
220
|
+
database_columns = {
|
221
|
+
"Lifetime Connections" => lambda {|it| it['stats']['Connections'] rescue '' },
|
222
|
+
"Aborted Connections" => lambda {|it| it['stats']['Aborted_connects'] rescue '' },
|
223
|
+
"Max Used Connections" => lambda {|it| it['stats']['Max_used_connections'] rescue '' },
|
224
|
+
"Max Connections" => lambda {|it| it['maxConnections'] rescue '' },
|
225
|
+
"Threads Running" => lambda {|it| it['stats']['Threads_running'] rescue '' },
|
226
|
+
"Threads Connected" => lambda {|it| it['stats']['Threads_connected'] rescue '' },
|
227
|
+
"Slow Queries" => lambda {|it| it['stats']['Slow_queries'] rescue '' },
|
228
|
+
"Temp Tables" => lambda {|it| it['stats']['Created_tmp_disk_tables'] rescue '' },
|
229
|
+
"Handler Read First" => lambda {|it| it['stats']['Handler_read_first'] rescue '' },
|
230
|
+
"Buffer Pool Free" => lambda {|it| it['stats']['Innodb_buffer_pool_wait_free'] rescue '' },
|
231
|
+
"Open Tables" => lambda {|it| it['stats']['Open_tables'] rescue '' },
|
232
|
+
"Table Scans" => lambda {|it| it['stats']['Select_scan'] rescue '' },
|
233
|
+
"Full Joins" => lambda {|it| it['stats']['Select_full_join'] rescue '' },
|
234
|
+
"Key Read Requests" => lambda {|it| it['stats']['Key_read_requests'] rescue '' },
|
235
|
+
"Key Reads" => lambda {|it| it['stats']['Key_reads'] rescue '' },
|
236
|
+
"Engine Waits" => lambda {|it| it['stats']['Innodb_log_waits'] rescue '' },
|
237
|
+
"Lock Waits" => lambda {|it| it['stats']['Table_locks_waited'] rescue '' },
|
238
|
+
"Handler Read Rnd" => lambda {|it| it['stats']['Handler_read_rnd'] rescue '' },
|
239
|
+
"Engine IO Writes" => lambda {|it| it['stats']['Innodb_data_writes'] rescue '' },
|
240
|
+
"Engine IO Reads" => lambda {|it| it['stats']['Innodb_data_reads'] rescue '' },
|
241
|
+
"Engine IO Double Writes" => lambda {|it| it['stats']['Innodb_dblwr_writes'] rescue '' },
|
242
|
+
"Engine Log Writes" => lambda {|it| it['stats']['Innodb_log_writes'] rescue '' },
|
243
|
+
"Engine Memory" => lambda {|it| format_bytes_short(it['innodbStats']['largeMemory']) rescue '' },
|
244
|
+
"Dictionary Memory" => lambda {|it| format_bytes_short(it['innodbStats']['dictionaryMemory']) rescue '' },
|
245
|
+
"Buffer Pool Size" => lambda {|it| it['innodbStats']['bufferPoolSize'] rescue '' },
|
246
|
+
"Free Buffers" => lambda {|it| it['innodbStats']['freeBuffers'] rescue '' },
|
247
|
+
"Database Pages" => lambda {|it| it['innodbStats']['databasePages'] rescue '' },
|
248
|
+
"Old Pages" => lambda {|it| it['innodbStats']['oldPages'] rescue '' },
|
249
|
+
"Dirty Page Percent" => lambda {|it| format_percent(it['innodbStats']['dirtyPagePercent'] ? it['innodbStats']['dirtyPagePercent'] : '') rescue '' },
|
250
|
+
"Max Dirty Pages" => lambda {|it| format_percent(it['innodbStats']['maxDirtyPagePercent'].to_f) rescue '' },
|
251
|
+
"Pending Reads" => lambda {|it| format_number(it['innodbStats']['pendingReads']) rescue '' },
|
252
|
+
"Insert Rate" => lambda {|it| format_rate(it['innodbStats']['insertsPerSecond'].to_f) rescue '' },
|
253
|
+
"Update Rate" => lambda {|it| format_rate(it['innodbStats']['updatesPerSecond'].to_f) rescue '' },
|
254
|
+
"Delete Rate" => lambda {|it| format_rate(it['innodbStats']['deletesPerSecond'].to_f) rescue '' },
|
255
|
+
"Read Rate" => lambda {|it| format_rate(it['innodbStats']['readsPerSecond']) rescue '' },
|
256
|
+
"Buffer Hit Rate" => lambda {|it| format_percent(it['innodbStats']['bufferHitRate'].to_f * 100) rescue '' },
|
257
|
+
"Read Write Ratio" => lambda {|it|
|
258
|
+
rw_ratio = ""
|
259
|
+
begin
|
260
|
+
total_writes = (it['stats']['Com_update'].to_i) + (it['stats']['Com_insert'].to_i) + (it['stats']['Com_delete'].to_f)
|
261
|
+
total_reads = (it['stats']['Com_select'].to_i)
|
262
|
+
if total_writes > 0
|
263
|
+
rw_ratio = (total_reads.to_f / total_writes.to_f).round(2).to_s
|
264
|
+
end
|
265
|
+
rescue => ex
|
266
|
+
puts ex
|
267
|
+
end
|
268
|
+
rw_ratio
|
269
|
+
},
|
270
|
+
"Uptime" => lambda {|it| (it['stats']['Uptime'] ? format_human_duration(it['stats']['Uptime'].to_i) : '') rescue '' },
|
271
|
+
}
|
272
|
+
|
273
|
+
print_description_list(database_columns, health['database'], options)
|
274
|
+
#print as_pretty_table(health['database'], database_columns, options)
|
275
|
+
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
279
|
+
# Elasticsearch
|
280
|
+
if options[:show_elastic]
|
281
|
+
if health['elastic'].nil?
|
282
|
+
print yellow,"No elastic health information returned.",reset,"\n\n"
|
283
|
+
else
|
284
|
+
print_h2 "Elastic", options
|
285
|
+
print cyan
|
286
|
+
|
287
|
+
elastic_columns = {
|
288
|
+
"Status" => 'status',
|
289
|
+
# "Status" => lambda {|it| format_health_status(it['status']) rescue '' },
|
290
|
+
# "Status" => lambda {|it|
|
291
|
+
# begin
|
292
|
+
# if it['statusMessage'].to_s != ""
|
293
|
+
# format_health_status(it['status']).to_s + " - " + it['statusMessage']
|
294
|
+
# else
|
295
|
+
# format_health_status(it['status'])
|
296
|
+
# end
|
297
|
+
# rescue => ex
|
298
|
+
# ''
|
299
|
+
# end
|
300
|
+
# },
|
301
|
+
"Cluster" => lambda {|it| it['stats']['clusterName'] rescue '' },
|
302
|
+
"Node Count" => lambda {|it| it['stats']['nodeTotal'] rescue '' },
|
303
|
+
"Data Nodes" => lambda {|it| it['stats']['nodeData'] rescue '' },
|
304
|
+
"Shards" => lambda {|it| it['stats']['shards'] rescue '' },
|
305
|
+
"Primary Shards" => lambda {|it| it['stats']['primary'] rescue '' },
|
306
|
+
"Relocating Shards" => lambda {|it| it['stats']['relocating'] rescue '' },
|
307
|
+
"Initializing" => lambda {|it| it['stats']['initializing'] rescue '' },
|
308
|
+
"Unassigned" => lambda {|it| it['stats']['unassigned'] rescue '' },
|
309
|
+
"Pending Tasks" => lambda {|it| it['stats']['pendingTasks'] rescue '' },
|
310
|
+
"Active Shards" => lambda {|it| it['stats']['activePercent'] rescue '' },
|
311
|
+
}
|
312
|
+
|
313
|
+
print_description_list(elastic_columns, health['elastic'], options)
|
314
|
+
#print as_pretty_table(health['elastic'], elastic_columns, options)
|
315
|
+
|
316
|
+
elastic_nodes_columns = [
|
317
|
+
{"NODE" => lambda {|it| it['name'] } },
|
318
|
+
{"MASTER" => lambda {|it| it['master'] == '*' } },
|
319
|
+
{"LOCATION" => lambda {|it| it['ip'] } },
|
320
|
+
{"RAM" => lambda {|it| it['ramPercent'] } },
|
321
|
+
{"HEAP" => lambda {|it| it['heapPercent'] } },
|
322
|
+
{"CPU USAGE" => lambda {|it| it['cpuCount'] } },
|
323
|
+
{"1M LOAD" => lambda {|it| it['loadOne'] } },
|
324
|
+
{"5M LOAD" => lambda {|it| it['loadFive'] } },
|
325
|
+
{"15M LOAD" => lambda {|it| it['loadFifteen'] } }
|
326
|
+
]
|
327
|
+
|
328
|
+
print_h2 "Elastic Nodes"
|
329
|
+
if health['elastic']['nodes'].nil? || health['elastic']['nodes'].empty?
|
330
|
+
print yellow,"No nodes found.",reset,"\n\n"
|
331
|
+
else
|
332
|
+
print as_pretty_table(health['elastic']['nodes'], elastic_nodes_columns, options)
|
333
|
+
end
|
334
|
+
|
335
|
+
elastic_indices_columns = [
|
336
|
+
{"Health".upcase => lambda {|it| format_index_health(it['health']) } },
|
337
|
+
{"Index".upcase => lambda {|it| it['index']} },
|
338
|
+
{"Status".upcase => lambda {|it| it['status'] } },
|
339
|
+
{"Primary".upcase => lambda {|it| it['primary'] } },
|
340
|
+
{"Replicas".upcase => lambda {|it| it['replicas'] } },
|
341
|
+
{"Doc Count".upcase => lambda {|it| format_number(it['count']) } },
|
342
|
+
{"Primary Size".upcase => lambda {|it| it['primarySize'] } },
|
343
|
+
{"Total Size".upcase => lambda {|it| it['totalSize'] } },
|
344
|
+
]
|
345
|
+
|
346
|
+
# when the api returns indices, it will include badIndices, so don't show both.
|
347
|
+
if health['elastic']['indices'] && health['elastic']['indices'].size > 0
|
348
|
+
print_h2 "Elastic Indices"
|
349
|
+
if health['elastic']['indices'].nil? || health['elastic']['indices'].empty?
|
350
|
+
print yellow,"No indices found.",reset,"\n\n"
|
351
|
+
else
|
352
|
+
print cyan
|
353
|
+
print as_pretty_table(health['elastic']['indices'], elastic_indices_columns, options)
|
354
|
+
end
|
355
|
+
else
|
356
|
+
print_h2 "Bad Elastic Indices"
|
357
|
+
if health['elastic']['badIndices'].nil? || health['elastic']['badIndices'].empty?
|
358
|
+
# print cyan,"No bad indices found.",reset,"\n\n"
|
359
|
+
else
|
360
|
+
print cyan
|
361
|
+
print as_pretty_table(health['elastic']['badIndices'], elastic_indices_columns, options)
|
362
|
+
end
|
363
|
+
end
|
364
|
+
|
365
|
+
|
366
|
+
end
|
367
|
+
end
|
368
|
+
|
369
|
+
# Queues (rabbit)
|
370
|
+
if options[:show_queue]
|
371
|
+
print_h2 "Queue (Rabbit)", options
|
372
|
+
if health['rabbit'].nil?
|
373
|
+
print yellow,"No rabbit queue health information returned.",reset,"\n\n"
|
374
|
+
else
|
375
|
+
print cyan
|
376
|
+
|
377
|
+
rabbit_summary_columns = {
|
378
|
+
"Status" => lambda {|it|
|
379
|
+
begin
|
380
|
+
if it['statusMessage'].to_s != ""
|
381
|
+
format_health_status(it['status']).to_s + " - " + it['statusMessage']
|
382
|
+
else
|
383
|
+
format_health_status(it['status'])
|
384
|
+
end
|
385
|
+
rescue => ex
|
386
|
+
''
|
387
|
+
end
|
388
|
+
},
|
389
|
+
"Queues" => lambda {|it| it['queues'].size rescue '' },
|
390
|
+
"Busy Queues" => lambda {|it| it['busyQueues'].size rescue '' },
|
391
|
+
"Error Queues" => lambda {|it| it['errorQueues'].size rescue '' }
|
392
|
+
}
|
393
|
+
|
394
|
+
print_description_list(rabbit_summary_columns, health['rabbit'], options)
|
395
|
+
#print as_pretty_table(health['rabbit'], rabbit_summary_columns, options)
|
396
|
+
|
397
|
+
print_h2 "Queues"
|
398
|
+
queue_columns = [
|
399
|
+
{"Status".upcase => lambda {|it|
|
400
|
+
# hrmm
|
401
|
+
status_string = it['status'].to_s.downcase # || 'green'
|
402
|
+
if status_string == 'warning'
|
403
|
+
"#{yellow}WARNING#{cyan}"
|
404
|
+
elsif status_string == 'error'
|
405
|
+
"#{red}ERROR#{cyan}"
|
406
|
+
elsif status_string == 'ok'
|
407
|
+
"#{green}OK#{cyan}"
|
408
|
+
else
|
409
|
+
# hrmm
|
410
|
+
it['status']
|
411
|
+
end
|
412
|
+
} },
|
413
|
+
{"Name".upcase => lambda {|it| it['name']} },
|
414
|
+
{"Count".upcase => lambda {|it| format_number(it['count']) } }
|
415
|
+
]
|
416
|
+
|
417
|
+
if health['rabbit'].nil? || health['rabbit']['queues'].nil? || health['rabbit']['queues'].empty?
|
418
|
+
print yellow,"No queues found.",reset,"\n\n"
|
419
|
+
else
|
420
|
+
print cyan
|
421
|
+
print as_pretty_table(health['rabbit']['queues'], queue_columns, options)
|
422
|
+
end
|
423
|
+
|
424
|
+
if health['rabbit'].nil? || health['rabbit']['busyQueues'].nil? || health['rabbit']['busyQueues'].empty?
|
425
|
+
# print cyan,"No busy queues found.",reset,"\n"
|
426
|
+
else
|
427
|
+
print_h2 "Busy Queues"
|
428
|
+
print cyan
|
429
|
+
print as_pretty_table(health['rabbit']['busyQueues'], queue_columns, options)
|
430
|
+
end
|
431
|
+
|
432
|
+
if health['rabbit'].nil? || health['rabbit']['errorQueues'].nil? || health['rabbit']['errorQueues'].empty?
|
433
|
+
# print cyan,"No error queues found.",reset,"\n"
|
434
|
+
else
|
435
|
+
print_h2 "Error Queues"
|
436
|
+
print cyan
|
437
|
+
print as_pretty_table(health['rabbit']['errorQueues'], queue_columns, options)
|
438
|
+
end
|
439
|
+
|
440
|
+
end
|
441
|
+
|
442
|
+
end
|
443
|
+
|
444
|
+
|
445
|
+
print "\n"
|
446
|
+
return 0
|
447
|
+
rescue RestClient::Exception => e
|
448
|
+
print_rest_exception(e, options)
|
449
|
+
exit 1
|
450
|
+
end
|
451
|
+
end
|
452
|
+
|
453
|
+
def logs(args)
|
454
|
+
options = {}
|
455
|
+
params = {}
|
456
|
+
start_date, end_date = nil, nil
|
457
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
458
|
+
opts.banner = subcommand_usage()
|
459
|
+
opts.on('--level VALUE', String, "Log Level. DEBUG,INFO,WARN,ERROR") do |val|
|
460
|
+
params['level'] = params['level'] ? [params['level'], val].flatten : val
|
461
|
+
end
|
462
|
+
opts.on('--start TIMESTAMP','--start TIMESTAMP', "Start timestamp. Default is 30 days ago.") do |val|
|
463
|
+
start_date = parse_time(val) #.utc.iso8601
|
464
|
+
end
|
465
|
+
opts.on('--end TIMESTAMP','--end TIMESTAMP', "End timestamp. Default is now.") do |val|
|
466
|
+
end_date = parse_time(val) #.utc.iso8601
|
467
|
+
end
|
468
|
+
opts.on('--table', '--table', "Format output as a table.") do
|
469
|
+
options[:table] = true
|
470
|
+
end
|
471
|
+
opts.on('-a', '--all', "Display all details: entire message." ) do
|
472
|
+
options[:details] = true
|
473
|
+
end
|
474
|
+
build_common_options(opts, options, [:list, :query, :json, :yaml, :csv, :fields, :dry_run, :remote])
|
475
|
+
opts.footer = "List health logs. These are the logs of the morpheus appliance itself."
|
476
|
+
end
|
477
|
+
optparse.parse!(args)
|
478
|
+
if args.count != 0
|
479
|
+
raise_command_error "wrong number of arguments, expected 0 and got (#{args.count}) #{args}\n#{optparse}"
|
480
|
+
end
|
481
|
+
connect(options)
|
482
|
+
begin
|
483
|
+
# params['startDate'] = start_date.utc.iso8601 if start_date
|
484
|
+
# params['endDate'] = end_date.utc.iso8601 if end_date
|
485
|
+
params['startMs'] = (start_date.to_i * 1000) if start_date
|
486
|
+
params['endMs'] = (end_date.to_i * 1000) if end_date
|
487
|
+
params.merge!(parse_list_options(options))
|
488
|
+
@health_interface.setopts(options)
|
489
|
+
if options[:dry_run]
|
490
|
+
print_dry_run @health_interface.dry.logs(params)
|
491
|
+
return 0
|
492
|
+
end
|
493
|
+
json_response = @health_interface.logs(params)
|
494
|
+
render_result = json_response['logs'] ? render_with_format(json_response, options, 'logs') : render_with_format(json_response, options, 'data')
|
495
|
+
return 0 if render_result
|
496
|
+
logs = json_response['data'] || json_response['logs']
|
497
|
+
title = "Morpheus Health Logs"
|
498
|
+
subtitles = []
|
499
|
+
if params['level']
|
500
|
+
subtitles << "Level: #{params['level']}"
|
501
|
+
end
|
502
|
+
if start_date
|
503
|
+
subtitles << "Start: #{start_date}"
|
504
|
+
end
|
505
|
+
if end_date
|
506
|
+
subtitles << "End: #{end_date}"
|
507
|
+
end
|
508
|
+
subtitles += parse_list_subtitles(options)
|
509
|
+
print_h1 title, subtitles
|
510
|
+
|
511
|
+
if logs.empty?
|
512
|
+
print "#{cyan}No logs found.#{reset}\n"
|
513
|
+
else
|
514
|
+
print format_log_records(logs, options, false)
|
515
|
+
print_results_pagination(json_response)
|
516
|
+
end
|
517
|
+
print reset,"\n"
|
518
|
+
return 0
|
519
|
+
rescue RestClient::Exception => e
|
520
|
+
print_rest_exception(e, options)
|
521
|
+
exit 1
|
522
|
+
end
|
523
|
+
end
|
524
|
+
|
525
|
+
def alarms(args)
|
526
|
+
options = {}
|
527
|
+
params = {}
|
528
|
+
start_date, end_date = nil, nil
|
529
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
530
|
+
opts.banner = subcommand_usage()
|
531
|
+
opts.on('--category VALUE', String, "Filter by Alarm Category. datastore, computeZone, computeServer, etc.") do |val|
|
532
|
+
params['alarmCategory'] = params['alarmCategory'] ? [params['alarmCategory'], val].flatten : val
|
533
|
+
end
|
534
|
+
opts.on('--status VALUE', String, "Filter by status. warning, error") do |val|
|
535
|
+
params['status'] = params['status'] ? [params['status'], val].flatten : val
|
536
|
+
end
|
537
|
+
opts.on('--acknowledged', '--acknowledged', "Filter by acknowledged. By default only open alarms are returned.") do
|
538
|
+
params['alarmStatus'] = 'acknowledged'
|
539
|
+
end
|
540
|
+
opts.on('--start TIMESTAMP','--start TIMESTAMP', "Start timestamp. Default is 30 days ago.") do |val|
|
541
|
+
start_date = parse_time(val) #.utc.iso8601
|
542
|
+
end
|
543
|
+
opts.on('--end TIMESTAMP','--end TIMESTAMP', "End timestamp. Default is now.") do |val|
|
544
|
+
end_date = parse_time(val) #.utc.iso8601
|
545
|
+
end
|
546
|
+
build_common_options(opts, options, [:list, :query, :json, :yaml, :csv, :fields, :dry_run, :remote])
|
547
|
+
opts.footer = "List health alarms."
|
548
|
+
end
|
549
|
+
optparse.parse!(args)
|
550
|
+
if args.count != 0
|
551
|
+
raise_command_error "wrong number of arguments, expected 0 and got (#{args.count}) #{args}\n#{optparse}"
|
552
|
+
end
|
553
|
+
connect(options)
|
554
|
+
begin
|
555
|
+
params['startDate'] = start_date.utc.iso8601 if start_date
|
556
|
+
params['endDate'] = end_date.utc.iso8601 if end_date
|
557
|
+
params.merge!(parse_list_options(options))
|
558
|
+
@health_interface.setopts(options)
|
559
|
+
if options[:dry_run]
|
560
|
+
print_dry_run @health_interface.dry.list_alarms(params)
|
561
|
+
return 0
|
562
|
+
end
|
563
|
+
json_response = @health_interface.list_alarms(params)
|
564
|
+
render_result = render_with_format(json_response, options, 'alarms')
|
565
|
+
return 0 if render_result
|
566
|
+
alarms = json_response['alarms']
|
567
|
+
title = "Morpheus Health Alarms"
|
568
|
+
subtitles = []
|
569
|
+
# if params['category']
|
570
|
+
# subtitles << "Category: #{params['category']}"
|
571
|
+
# end
|
572
|
+
if params['status']
|
573
|
+
subtitles << "Status: #{params['status']}"
|
574
|
+
end
|
575
|
+
if params['alarmStatus'] == 'acknowledged'
|
576
|
+
subtitles << "(Acknowledged)"
|
577
|
+
end
|
578
|
+
if params['startDate']
|
579
|
+
subtitles << "Start Date: #{params['startDate']}"
|
580
|
+
end
|
581
|
+
if params['endDate']
|
582
|
+
subtitles << "End Date: #{params['endDate']}"
|
583
|
+
end
|
584
|
+
subtitles += parse_list_subtitles(options)
|
585
|
+
print_h1 title, subtitles
|
586
|
+
if alarms.empty?
|
587
|
+
print cyan,"No alarms found.",reset,"\n"
|
588
|
+
else
|
589
|
+
alarm_columns = [
|
590
|
+
{"ID" => lambda {|alarm| alarm['id'] } },
|
591
|
+
{"STATUS" => lambda {|alarm| format_health_status(alarm['status']) } },
|
592
|
+
{"RESOURCE" => lambda {|alarm| alarm['resourceName'] || alarm['refName'] } },
|
593
|
+
{"INFO" => lambda {|alarm| alarm['name'] } },
|
594
|
+
{"START DATE" => lambda {|alarm| format_local_dt(alarm['startDate']) } },
|
595
|
+
{"DURATION" => lambda {|alarm| format_duration(alarm['startDate'], alarm['acknoDate'] || Time.now) } },
|
596
|
+
]
|
597
|
+
if options[:include_fields]
|
598
|
+
columns = options[:include_fields]
|
599
|
+
end
|
600
|
+
print as_pretty_table(alarms, alarm_columns, options)
|
601
|
+
print_results_pagination(json_response)
|
602
|
+
end
|
603
|
+
print reset,"\n"
|
604
|
+
return 0
|
605
|
+
rescue RestClient::Exception => e
|
606
|
+
print_rest_exception(e, options)
|
607
|
+
exit 1
|
608
|
+
end
|
609
|
+
end
|
610
|
+
|
611
|
+
def get_alarm(args)
|
612
|
+
options = {}
|
613
|
+
params = {}
|
614
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
615
|
+
opts.banner = subcommand_usage("[id]")
|
616
|
+
build_common_options(opts, options, [:query, :json, :yaml, :csv, :fields, :dry_run, :remote])
|
617
|
+
opts.footer = "Get details about a health alarm.\n[id] is required. Health Alarm ID"
|
618
|
+
end
|
619
|
+
optparse.parse!(args)
|
620
|
+
|
621
|
+
if args.count != 1
|
622
|
+
raise_command_error "wrong number of arguments, expected 1 and got (#{args.count}) #{args}\n#{optparse}"
|
623
|
+
end
|
624
|
+
|
625
|
+
connect(options)
|
626
|
+
|
627
|
+
@health_interface.setopts(options)
|
628
|
+
if options[:dry_run]
|
629
|
+
print_dry_run @health_interface.dry.get_alarm(args[0], params)
|
630
|
+
return 0
|
631
|
+
end
|
632
|
+
json_response = @health_interface.get_alarm(args[0], params)
|
633
|
+
render_result = render_with_format(json_response, options, 'alarm')
|
634
|
+
return 0 if render_result
|
635
|
+
if json_response['alarm'].nil?
|
636
|
+
print_red_alert "Alarm not found by id #{args[0]}"
|
637
|
+
return 1
|
638
|
+
end
|
639
|
+
print_h1 "Alarm Details"
|
640
|
+
print cyan
|
641
|
+
alarm_columns = [
|
642
|
+
{"ID" => lambda {|alarm| alarm['id'] } },
|
643
|
+
{"Status" => lambda {|alarm| format_health_status(alarm['status']) } },
|
644
|
+
{"Resource" => lambda {|alarm| alarm['resourceName'] || alarm['refName'] } },
|
645
|
+
{"Info" => lambda {|alarm| alarm['name'] } },
|
646
|
+
{"Active" => lambda {|alarm| format_boolean(alarm['active']) } },
|
647
|
+
{"Start Date" => lambda {|alarm| format_local_dt(alarm['startDate']) } },
|
648
|
+
{"Duration" => lambda {|alarm| format_duration(alarm['startDate'], alarm['acknowledgedDate'] || Time.now) } },
|
649
|
+
{"Acknowledged Date" => lambda {|alarm| format_local_dt(alarm['acknowledgedDate']) } },
|
650
|
+
{"Acknowledged" => lambda {|alarm| format_boolean(alarm['acknowledged']) } }
|
651
|
+
]
|
652
|
+
print_description_list(alarm_columns, json_response['alarm'])
|
653
|
+
print reset,"\n"
|
654
|
+
return 0
|
655
|
+
|
656
|
+
end
|
657
|
+
|
658
|
+
def acknowledge_alarms(args)
|
659
|
+
options = {}
|
660
|
+
params = {}
|
661
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
662
|
+
opts.banner = subcommand_usage("[alarm] [options]")
|
663
|
+
opts.on('-a', '--all', "Acknowledge all open alarms. This can be used instead of passing specific alarms.") do
|
664
|
+
params['all'] = true
|
665
|
+
end
|
666
|
+
build_common_options(opts, options, [:payload, :options, :json, :dry_run, :remote])
|
667
|
+
opts.footer = "Acknowledge health alarm(s).\n[alarm] is required. Alarm ID, supports multiple arguments."
|
668
|
+
end
|
669
|
+
optparse.parse!(args)
|
670
|
+
|
671
|
+
if params['all']
|
672
|
+
# updating all
|
673
|
+
if args.count > 0
|
674
|
+
raise_command_error "wrong number of arguments, --all option expects 0 and got (#{args.count}) #{args}\n#{optparse}"
|
675
|
+
end
|
676
|
+
else
|
677
|
+
# updating 1-N ids
|
678
|
+
if args.count < 0
|
679
|
+
raise_command_error "wrong number of arguments, expected 1-N and got (#{args.count}) #{args}\n#{optparse}"
|
680
|
+
end
|
681
|
+
params['ids'] = args.collect {|arg| arg }
|
682
|
+
end
|
683
|
+
connect(options)
|
684
|
+
begin
|
685
|
+
# validate ids
|
686
|
+
if params['ids']
|
687
|
+
parsed_id_list = []
|
688
|
+
params['ids'].each do |alarm_id|
|
689
|
+
alarm = find_health_alarm_by_name_or_id(alarm_id)
|
690
|
+
if alarm.nil?
|
691
|
+
# print_red_alert "Alarm not found by id #{args[0]}"
|
692
|
+
return 1
|
693
|
+
end
|
694
|
+
parsed_id_list << alarm['id']
|
695
|
+
end
|
696
|
+
params['ids'] = parsed_id_list.uniq
|
697
|
+
end
|
698
|
+
|
699
|
+
# construct payload
|
700
|
+
passed_options = options[:options] ? options[:options].reject {|k,v| k.is_a?(Symbol) } : {}
|
701
|
+
payload = nil
|
702
|
+
if options[:payload]
|
703
|
+
payload = options[:payload]
|
704
|
+
payload.deep_merge!(passed_options) unless passed_options.empty?
|
705
|
+
else
|
706
|
+
payload = {}
|
707
|
+
# allow arbitrary -O options
|
708
|
+
payload.deep_merge!(passed_options) unless passed_options.empty?
|
709
|
+
end
|
710
|
+
id_list = params['ids'] || []
|
711
|
+
confirm_msg = params['all'] ? "Are you sure you want to acknowledge all open alarms?" : "Are you sure you want to acknowledge the #{id_list.size == 1 ? 'alarm' : 'alarms'} #{anded_list(id_list)}?"
|
712
|
+
unless options[:yes] || Morpheus::Cli::OptionTypes.confirm(confirm_msg)
|
713
|
+
return 9, "aborted command"
|
714
|
+
end
|
715
|
+
@health_interface.setopts(options)
|
716
|
+
if options[:dry_run]
|
717
|
+
print_dry_run @health_interface.dry.acknowledge_alarms(params, payload)
|
718
|
+
return
|
719
|
+
end
|
720
|
+
json_response = @health_interface.acknowledge_alarms(params, payload)
|
721
|
+
render_result = render_with_format(json_response, options)
|
722
|
+
exit_code = 0 # json_response['success'] == true ? 0 : 1
|
723
|
+
return exit_code if render_result
|
724
|
+
|
725
|
+
if params['all']
|
726
|
+
print_green_success "Acknowledged all alarms"
|
727
|
+
else
|
728
|
+
print_green_success "Acknowledged #{id_list.size == 1 ? 'alarm' : 'alarms'} #{anded_list(id_list)}"
|
729
|
+
end
|
730
|
+
return exit_code
|
731
|
+
rescue RestClient::Exception => e
|
732
|
+
print_rest_exception(e, options)
|
733
|
+
exit 1
|
734
|
+
end
|
735
|
+
end
|
736
|
+
|
737
|
+
def unacknowledge_alarms(args)
|
738
|
+
options = {}
|
739
|
+
params = {acknowledged:false}
|
740
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
741
|
+
opts.banner = subcommand_usage("[alarm] [options]")
|
742
|
+
# opts.on('-a', '--all', "Acknowledge all open alarms. This can be used instead of passing specific alarms.") do
|
743
|
+
# params['all'] = true
|
744
|
+
# end
|
745
|
+
build_common_options(opts, options, [:payload, :options, :json, :dry_run, :remote])
|
746
|
+
opts.footer = "Unacknowledge health alarm(s).\n[alarm] is required. Alarm ID, supports multiple arguments."
|
747
|
+
end
|
748
|
+
optparse.parse!(args)
|
749
|
+
|
750
|
+
if params['all']
|
751
|
+
# updating all
|
752
|
+
if args.count > 0
|
753
|
+
raise_command_error "wrong number of arguments, --all option expects 0 and got (#{args.count}) #{args}\n#{optparse}"
|
754
|
+
end
|
755
|
+
else
|
756
|
+
# updating 1-N ids
|
757
|
+
if args.count < 0
|
758
|
+
raise_command_error "wrong number of arguments, expected 1-N and got (#{args.count}) #{args}\n#{optparse}"
|
759
|
+
end
|
760
|
+
params['ids'] = args.collect {|arg| arg }
|
761
|
+
end
|
762
|
+
connect(options)
|
763
|
+
begin
|
764
|
+
# validate ids
|
765
|
+
if params['ids']
|
766
|
+
parsed_id_list = []
|
767
|
+
params['ids'].each do |alarm_id|
|
768
|
+
alarm = find_health_alarm_by_name_or_id(alarm_id)
|
769
|
+
if alarm.nil?
|
770
|
+
# print_red_alert "Alarm not found by id #{args[0]}"
|
771
|
+
return 1
|
772
|
+
end
|
773
|
+
parsed_id_list << alarm['id']
|
774
|
+
end
|
775
|
+
params['ids'] = parsed_id_list.uniq
|
776
|
+
end
|
777
|
+
|
778
|
+
# construct payload
|
779
|
+
passed_options = options[:options] ? options[:options].reject {|k,v| k.is_a?(Symbol) } : {}
|
780
|
+
payload = nil
|
781
|
+
if options[:payload]
|
782
|
+
payload = options[:payload]
|
783
|
+
payload.deep_merge!(passed_options) unless passed_options.empty?
|
784
|
+
else
|
785
|
+
payload = {}
|
786
|
+
# allow arbitrary -O options
|
787
|
+
payload.deep_merge!(passed_options) unless passed_options.empty?
|
788
|
+
end
|
789
|
+
id_list = params['ids'] || []
|
790
|
+
confirm_msg = params['all'] ? "Are you sure you want to unacknowledge all alarms?" : "Are you sure you want to unacknowledge the #{id_list.size == 1 ? 'alarm' : 'alarms'} #{anded_list(id_list)}?"
|
791
|
+
unless options[:yes] || Morpheus::Cli::OptionTypes.confirm(confirm_msg)
|
792
|
+
return 9, "aborted command"
|
793
|
+
end
|
794
|
+
@health_interface.setopts(options)
|
795
|
+
if options[:dry_run]
|
796
|
+
print_dry_run @health_interface.dry.acknowledge_alarms(params, payload)
|
797
|
+
return
|
798
|
+
end
|
799
|
+
json_response = @health_interface.acknowledge_alarms(params, payload)
|
800
|
+
render_result = render_with_format(json_response, options)
|
801
|
+
exit_code = 0 # json_response['success'] == true ? 0 : 1
|
802
|
+
return exit_code if render_result
|
803
|
+
|
804
|
+
if params['all']
|
805
|
+
print_green_success "Acknowledged all alarms"
|
806
|
+
else
|
807
|
+
print_green_success "Acknowledged #{id_list.size == 1 ? 'alarm' : 'alarms'} #{anded_list(id_list)}"
|
808
|
+
end
|
809
|
+
return exit_code
|
810
|
+
rescue RestClient::Exception => e
|
811
|
+
print_rest_exception(e, options)
|
812
|
+
exit 1
|
813
|
+
end
|
814
|
+
end
|
815
|
+
private
|
816
|
+
|
817
|
+
def find_health_alarm_by_name_or_id(val)
|
818
|
+
if val.to_s =~ /\A\d{1,}\Z/
|
819
|
+
return find_health_alarm_by_id(val)
|
820
|
+
else
|
821
|
+
return find_health_alarm_by_name(val)
|
822
|
+
end
|
823
|
+
end
|
824
|
+
|
825
|
+
def find_health_alarm_by_id(id)
|
826
|
+
raise "#{self.class} has not defined @health_interface" if @health_interface.nil?
|
827
|
+
begin
|
828
|
+
json_response = @health_interface.get_alarm(id)
|
829
|
+
return json_response['alarm']
|
830
|
+
rescue RestClient::Exception => e
|
831
|
+
if e.response && e.response.code == 404
|
832
|
+
print_red_alert "Alarm not found by id #{id}"
|
833
|
+
else
|
834
|
+
raise e
|
835
|
+
end
|
836
|
+
end
|
837
|
+
end
|
838
|
+
|
839
|
+
def find_health_alarm_by_name(name)
|
840
|
+
raise "#{self.class} has not defined @health_interface" if @health_interface.nil?
|
841
|
+
alarms = @health_interface.list_alarms({name: name.to_s})['alarms']
|
842
|
+
if alarm.empty?
|
843
|
+
print_red_alert "Alarm not found by name #{name}"
|
844
|
+
return nil
|
845
|
+
elsif alarms.size > 1
|
846
|
+
print_red_alert "#{alarms.size} alarms found by name #{name}"
|
847
|
+
print as_pretty_table(alarms, [:id,:name], {color:red})
|
848
|
+
print reset,"\n"
|
849
|
+
return nil
|
850
|
+
else
|
851
|
+
return alarms[0]
|
852
|
+
end
|
853
|
+
end
|
854
|
+
|
855
|
+
def format_health_status(val, return_color=cyan)
|
856
|
+
out = ""
|
857
|
+
status_string = val.to_s.downcase
|
858
|
+
if(status_string)
|
859
|
+
if(status_string == 'ok' || status_string == 'running')
|
860
|
+
out << "#{green}#{status_string.upcase}#{return_color}"
|
861
|
+
elsif(status_string == 'error' || status_string == 'offline')
|
862
|
+
out << "#{red}#{status_string.upcase}#{return_color}"
|
863
|
+
elsif status_string == 'syncing'
|
864
|
+
out << "#{cyan}#{status_string.upcase}#{return_color}"
|
865
|
+
else
|
866
|
+
out << "#{yellow}#{status_string.upcase}#{return_color}"
|
867
|
+
end
|
868
|
+
end
|
869
|
+
out
|
870
|
+
end
|
871
|
+
|
872
|
+
# this is for weird elastic status values that are actually colors
|
873
|
+
def format_index_health(val, return_color=cyan)
|
874
|
+
# hrmm
|
875
|
+
status_string = val.to_s.downcase # || 'green'
|
876
|
+
if status_string == 'warning' || status_string == 'yellow'
|
877
|
+
"#{yellow}WARNING#{cyan}"
|
878
|
+
elsif status_string == 'error' || status_string == 'red'
|
879
|
+
"#{red}ERROR#{cyan}"
|
880
|
+
elsif status_string == 'ok' || status_string == 'green'
|
881
|
+
"#{green}OK#{cyan}"
|
882
|
+
else
|
883
|
+
# hrmm
|
884
|
+
it['status']
|
885
|
+
end
|
886
|
+
end
|
887
|
+
|
888
|
+
def format_queue_status(val, return_color=cyan)
|
889
|
+
# hrmm
|
890
|
+
status_string = val.to_s.downcase # || 'ok'
|
891
|
+
if status_string == 'warning'
|
892
|
+
"#{yellow}WARNING#{cyan}"
|
893
|
+
elsif status_string == 'error'
|
894
|
+
"#{red}ERROR#{cyan}"
|
895
|
+
elsif status_string == 'ok'
|
896
|
+
"#{green}OK#{cyan}"
|
897
|
+
else
|
898
|
+
# hrmm
|
899
|
+
it['status']
|
900
|
+
end
|
901
|
+
end
|
902
|
+
|
903
|
+
end
|