morpheus-cli 4.1.4 → 4.1.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (76) hide show
  1. checksums.yaml +4 -4
  2. data/lib/morpheus.rb +5 -0
  3. data/lib/morpheus/api.rb +2 -2
  4. data/lib/morpheus/api/api_client.rb +47 -12
  5. data/lib/morpheus/api/appliance_settings_interface.rb +30 -0
  6. data/lib/morpheus/api/auth_interface.rb +14 -10
  7. data/lib/morpheus/api/clouds_interface.rb +7 -0
  8. data/lib/morpheus/api/clusters_interface.rb +17 -5
  9. data/lib/morpheus/api/custom_instance_types_interface.rb +2 -3
  10. data/lib/morpheus/api/deployments_interface.rb +7 -0
  11. data/lib/morpheus/api/execute_schedules_interface.rb +2 -3
  12. data/lib/morpheus/api/groups_interface.rb +7 -0
  13. data/lib/morpheus/api/license_interface.rb +9 -2
  14. data/lib/morpheus/api/load_balancers_interface.rb +7 -0
  15. data/lib/morpheus/api/logs_interface.rb +11 -2
  16. data/lib/morpheus/api/monitoring_alerts_interface.rb +45 -0
  17. data/lib/morpheus/api/monitoring_checks_interface.rb +2 -2
  18. data/lib/morpheus/api/monitoring_interface.rb +13 -8
  19. data/lib/morpheus/api/power_schedules_interface.rb +2 -3
  20. data/lib/morpheus/api/servers_interface.rb +5 -2
  21. data/lib/morpheus/api/setup_interface.rb +25 -7
  22. data/lib/morpheus/api/task_sets_interface.rb +7 -1
  23. data/lib/morpheus/api/tasks_interface.rb +7 -0
  24. data/lib/morpheus/api/virtual_images_interface.rb +2 -3
  25. data/lib/morpheus/api/whitelabel_settings_interface.rb +60 -0
  26. data/lib/morpheus/cli.rb +18 -14
  27. data/lib/morpheus/cli/access_token_command.rb +18 -2
  28. data/lib/morpheus/cli/appliance_settings_command.rb +303 -0
  29. data/lib/morpheus/cli/apps.rb +4 -3
  30. data/lib/morpheus/cli/archives_command.rb +0 -21
  31. data/lib/morpheus/cli/blueprints_command.rb +2 -2
  32. data/lib/morpheus/cli/cli_command.rb +32 -8
  33. data/lib/morpheus/cli/clouds.rb +6 -11
  34. data/lib/morpheus/cli/clusters.rb +346 -117
  35. data/lib/morpheus/cli/command_error.rb +4 -0
  36. data/lib/morpheus/cli/commands/standard/man_command.rb +1 -1
  37. data/lib/morpheus/cli/containers_command.rb +2 -1
  38. data/lib/morpheus/cli/credentials.rb +49 -4
  39. data/lib/morpheus/cli/deployments.rb +2 -2
  40. data/lib/morpheus/cli/dot_file.rb +2 -2
  41. data/lib/morpheus/cli/error_handler.rb +6 -3
  42. data/lib/morpheus/cli/execute_schedules_command.rb +1 -1
  43. data/lib/morpheus/cli/groups.rb +4 -4
  44. data/lib/morpheus/cli/hosts.rb +3 -2
  45. data/lib/morpheus/cli/image_builder_command.rb +0 -21
  46. data/lib/morpheus/cli/instances.rb +17 -4
  47. data/lib/morpheus/cli/library_container_types_command.rb +1 -1
  48. data/lib/morpheus/cli/library_layouts_command.rb +1 -1
  49. data/lib/morpheus/cli/library_upgrades_command.rb +1 -1
  50. data/lib/morpheus/cli/license.rb +185 -72
  51. data/lib/morpheus/cli/load_balancers.rb +4 -4
  52. data/lib/morpheus/cli/login.rb +4 -0
  53. data/lib/morpheus/cli/logs_command.rb +132 -0
  54. data/lib/morpheus/cli/mixins/infrastructure_helper.rb +2 -2
  55. data/lib/morpheus/cli/mixins/logs_helper.rb +65 -0
  56. data/lib/morpheus/cli/mixins/monitoring_helper.rb +410 -28
  57. data/lib/morpheus/cli/mixins/print_helper.rb +14 -4
  58. data/lib/morpheus/cli/monitoring_alerts_command.rb +800 -0
  59. data/lib/morpheus/cli/monitoring_apps_command.rb +85 -28
  60. data/lib/morpheus/cli/monitoring_checks_command.rb +60 -27
  61. data/lib/morpheus/cli/monitoring_contacts_command.rb +54 -79
  62. data/lib/morpheus/cli/monitoring_groups_command.rb +62 -23
  63. data/lib/morpheus/cli/monitoring_incidents_command.rb +91 -70
  64. data/lib/morpheus/cli/network_pools_command.rb +39 -23
  65. data/lib/morpheus/cli/power_schedules_command.rb +1 -1
  66. data/lib/morpheus/cli/remote.rb +834 -275
  67. data/lib/morpheus/cli/roles.rb +100 -38
  68. data/lib/morpheus/cli/tasks.rb +1 -1
  69. data/lib/morpheus/cli/user_settings_command.rb +20 -12
  70. data/lib/morpheus/cli/version.rb +1 -1
  71. data/lib/morpheus/cli/virtual_images.rb +1 -1
  72. data/lib/morpheus/cli/whitelabel_settings_command.rb +546 -0
  73. data/lib/morpheus/cli/whoami.rb +1 -1
  74. data/lib/morpheus/cli/workflows.rb +2 -2
  75. data/lib/morpheus/terminal.rb +22 -8
  76. metadata +11 -2
@@ -39,10 +39,10 @@ class Morpheus::Cli::LoadBalancers
39
39
  end
40
40
  @load_balancers_interface.setopts(options)
41
41
  if options[:dry_run]
42
- print_dry_run @load_balancers_interface.dry.get(params)
42
+ print_dry_run @load_balancers_interface.dry.list(params)
43
43
  return
44
44
  end
45
- json_response = @load_balancers_interface.get(params)
45
+ json_response = @load_balancers_interface.list(params)
46
46
  if options[:json]
47
47
  puts as_json(json_response, options, "loadBalancers")
48
48
  return 0
@@ -95,7 +95,7 @@ class Morpheus::Cli::LoadBalancers
95
95
  if lb_name.to_s =~ /\A\d{1,}\Z/
96
96
  print_dry_run @load_balancers_interface.dry.get(lb_name.to_i)
97
97
  else
98
- print_dry_run @load_balancers_interface.dry.get({name:lb_name})
98
+ print_dry_run @load_balancers_interface.dry.list({name:lb_name})
99
99
  end
100
100
  return
101
101
  end
@@ -370,7 +370,7 @@ class Morpheus::Cli::LoadBalancers
370
370
  end
371
371
 
372
372
  def find_lb_by_name(name)
373
- lbs = @load_balancers_interface.get({name: name.to_s})['loadBalancers']
373
+ lbs = @load_balancers_interface.list({name: name.to_s})['loadBalancers']
374
374
  if lbs.empty?
375
375
  print_red_alert "Load Balancer not found by name #{name}"
376
376
  return nil
@@ -39,6 +39,10 @@ class Morpheus::Cli::Login
39
39
  opts.on( '-t', '--test', "Test credentials only, does not update stored credentials for the appliance." ) do
40
40
  options[:test_only] = true
41
41
  end
42
+ opts.on( '--client-id CLIENT', "Used to test authentication with a client_id other than #{Morpheus::APIClient::CLIENT_ID}. Currently behaves like --test, credentials are not stored." ) do |val|
43
+ options[:client_id] = val.to_s
44
+ options[:test_only] = true
45
+ end
42
46
  opts.on( '-T', '--token ACCESS_TOKEN', "Use an existing access token to login instead of authenticating with a username and password." ) do |val|
43
47
  options[:remote_token] = val
44
48
  end
@@ -0,0 +1,132 @@
1
+ require 'optparse'
2
+ require 'morpheus/cli/cli_command'
3
+ require 'morpheus/cli/mixins/logs_helper'
4
+ require 'json'
5
+
6
+ class Morpheus::Cli::LogsCommand
7
+ include Morpheus::Cli::CliCommand
8
+ include Morpheus::Cli::LogsHelper
9
+ register_subcommands :list
10
+ set_command_name :'logs'
11
+
12
+ def initialize()
13
+ # @appliance_name, @appliance_url = Morpheus::Cli::Remote.active_appliance
14
+ end
15
+
16
+ def connect(opts)
17
+ @api_client = establish_remote_appliance_connection(opts)
18
+ @logs_interface = @api_client.logs
19
+ @servers_interface = @api_client.servers
20
+ @containers_interface = @api_client.containers
21
+ @clusters_interface = @api_client.clusters
22
+ end
23
+
24
+ def usage
25
+ "Usage: morpheus #{command_name}"
26
+ end
27
+
28
+ def handle(args)
29
+ # list(args)
30
+ handle_subcommand(args)
31
+ end
32
+
33
+ def list(args)
34
+ options = {}
35
+ params = {}
36
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
37
+ opts.banner = subcommand_usage("[id]")
38
+ opts.on('--hosts HOSTS', String, "Filter logs to specific Host ID(s)") do |val|
39
+ params['servers'] = val.to_s.split(",").collect {|it| it.to_s.strip }.select {|it| it }.compact
40
+ end
41
+ opts.on('--servers HOSTS', String, "alias for --hosts") do |val|
42
+ params['servers'] = val.to_s.split(",").collect {|it| it.to_s.strip }.select {|it| it }.compact
43
+ end
44
+ opts.on('--vms HOSTS', String, "alias for --hosts") do |val|
45
+ params['servers'] = val.to_s.split(",").collect {|it| it.to_s.strip }.select {|it| it }.compact
46
+ end
47
+ opts.on('--container CONTAINER', String, "Filter logs to specific Container ID(s)") do |val|
48
+ params['containers'] = val.to_s.split(",").collect {|it| it.to_s.strip }.select {|it| it }.compact
49
+ end
50
+ # opts.on('--nodes HOST', String, "alias for --containers") do |val|
51
+ # params['containers'] = val.to_s.split(",").collect {|it| it.to_s.strip }.select {|it| it }.compact
52
+ # end
53
+ opts.on('--cluster ID', String, "Filter logs to specific Cluster ID") do |val|
54
+ params['clusters'] = val.to_s.split(",").collect {|it| it.to_s.strip }.select {|it| it }.compact
55
+ end
56
+ opts.on('--start TIMESTAMP','--start TIMESTAMP', "Start timestamp. Default is 30 days ago.") do |val|
57
+ options[:start] = parse_time(val) #.utc.iso8601
58
+ end
59
+ opts.on('--end TIMESTAMP','--end TIMESTAMP', "End timestamp. Default is now.") do |val|
60
+ options[:end] = parse_time(val) #.utc.iso8601
61
+ end
62
+ # opts.on('--interval TIME','--interval TIME', "Interval of time to include, in seconds. Default is 30 days ago.") do |val|
63
+ # options[:interval] = parse_time(val).utc.iso8601
64
+ # end
65
+ build_common_options(opts, options, [:list, :query, :json, :yaml, :csv, :fields, :dry_run, :remote])
66
+ opts.footer = "List logs for a container.\n" +
67
+ "[id] is required. This is the id of a container."
68
+ end
69
+ optparse.parse!(args)
70
+ if args.count != 0
71
+ raise_command_error "wrong number of arguments, expected 0 and got (#{args.count}) #{args.join(' ')}\n#{optparse}"
72
+ end
73
+ connect(options)
74
+ begin
75
+ params.merge!(parse_list_options(options))
76
+ params[:query] = params.delete(:phrase) unless params[:phrase].nil?
77
+ params['order'] = params['direction'] unless params['direction'].nil? # old api version expects order instead of direction
78
+ params['startMs'] = (options[:start].to_i * 1000) if options[:start]
79
+ params['endMs'] = (options[:end].to_i * 1000) if options[:end]
80
+ params['interval'] = options[:interval].to_s if options[:interval]
81
+ # could find_by_name_or_id for params['servers'] and params['containers']
82
+ @logs_interface.setopts(options)
83
+ if options[:dry_run]
84
+ print_dry_run @logs_interface.dry.list(params)
85
+ return
86
+ end
87
+ json_response = @logs_interface.list(params)
88
+ render_result = render_with_format(json_response, options, 'data')
89
+ return 0 if render_result
90
+
91
+ title = "Morpheus Logs"
92
+ subtitles = parse_list_subtitles(options)
93
+ if params[:start]
94
+ subtitles << "Start: #{params[:start]}".strip
95
+ end
96
+ if params[:end]
97
+ subtitles << "End: #{params[:end]}".strip
98
+ end
99
+ if params[:interval]
100
+ subtitles << "Interval: #{params[:interval]}".strip
101
+ end
102
+ if params[:query]
103
+ subtitles << "Search: #{params[:query]}".strip
104
+ end
105
+ if params['servers']
106
+ subtitles << "Servers: #{params['servers']}".strip
107
+ end
108
+ if params['containers']
109
+ subtitles << "Containers: #{params['containers']}".strip
110
+ end
111
+ if params['clusters']
112
+ subtitles << "Clusters: #{params['clusters']}".strip
113
+ end
114
+ if params[:query]
115
+ subtitles << "Search: #{params[:query]}".strip
116
+ end
117
+ print_h1 title, subtitles, options
118
+ if json_response['data'].empty?
119
+ puts "#{cyan}No logs found.#{reset}"
120
+ else
121
+ puts format_log_records(json_response['data'], options)
122
+ print_results_pagination({'meta'=>{'total'=>json_response['total'],'size'=>json_response['data'].size,'max'=>(json_response['max'] || options[:max]),'offset'=>(json_response['offset'] || options[:offset] || 0)}})
123
+ end
124
+ print reset,"\n"
125
+ return 0
126
+ rescue RestClient::Exception => e
127
+ print_rest_exception(e, options)
128
+ exit 1
129
+ end
130
+ end
131
+
132
+ end
@@ -44,7 +44,7 @@ module Morpheus::Cli::InfrastructureHelper
44
44
  end
45
45
 
46
46
  def find_group_by_name(name)
47
- json_results = groups_interface.get({name: name})
47
+ json_results = groups_interface.list({name: name})
48
48
  if json_results['groups'].empty?
49
49
  print_red_alert "Group not found by name #{name}"
50
50
  exit 1
@@ -72,7 +72,7 @@ module Morpheus::Cli::InfrastructureHelper
72
72
  end
73
73
 
74
74
  def find_cloud_by_name(name)
75
- json_results = clouds_interface.get({name: name})
75
+ json_results = clouds_interface.list({name: name})
76
76
  if json_results['zones'].empty?
77
77
  print_red_alert "Cloud not found by name #{name}"
78
78
  exit 1
@@ -0,0 +1,65 @@
1
+ require 'morpheus/cli/mixins/print_helper'
2
+
3
+ # Mixin for Morpheus::Cli command classes
4
+ # Provides common methods for fetching log records.
5
+ # The including class must establish @logs_interface, @containers_interface, @servers_interface
6
+ module Morpheus::Cli::LogsHelper
7
+
8
+ def self.included(klass)
9
+ klass.send :include, Morpheus::Cli::PrintHelper
10
+ end
11
+
12
+ def logs_interface
13
+ # @api_client.logs
14
+ raise "#{self.class} has not defined @logs_interface" if @logs_interface.nil?
15
+ @logs_interface
16
+ end
17
+
18
+ def containers_interface
19
+ ## @api_client.containers
20
+ raise "#{self.class} has not defined @logs_interface" if @containers_interface.nil?
21
+ @containers_interface
22
+ end
23
+
24
+ def servers_interface
25
+ # @api_client.servers
26
+ raise "#{self.class} has not defined @servers_interface" if @servers_interface.nil?
27
+ @servers_interface
28
+ end
29
+
30
+ def clusters_interface
31
+ # @api_client.clusters
32
+ raise "#{self.class} has not defined @clusters_interface" if @clusters_interface.nil?
33
+ @clusters_interface
34
+ end
35
+
36
+ def print_log_records(log_records, options={})
37
+ print format_log_records(log_records, options)
38
+ end
39
+
40
+ def format_log_records(log_records, options={})
41
+ out = ""
42
+ table_color = options.key?(:color) ? options[:color] : cyan
43
+ log_records.each do |log_entry|
44
+ log_level = ''
45
+ case log_entry['level']
46
+ when 'INFO'
47
+ log_level = "#{blue}#{bold}INFO#{reset}"
48
+ when 'DEBUG'
49
+ log_level = "#{white}#{bold}DEBUG#{reset}"
50
+ when 'WARN'
51
+ log_level = "#{yellow}#{bold}WARN#{reset}"
52
+ when 'ERROR'
53
+ log_level = "#{red}#{bold}ERROR#{reset}"
54
+ when 'FATAL'
55
+ log_level = "#{red}#{bold}FATAL#{reset}"
56
+ end
57
+ out << table_color if table_color
58
+ out << "[#{log_entry['ts']}] #{log_level} - #{log_entry['message'].to_s.strip}"
59
+ out << table_color if table_color
60
+ out << "\n"
61
+ end
62
+ return out
63
+ end
64
+
65
+ end
@@ -109,13 +109,38 @@ module Morpheus::Cli::MonitoringHelper
109
109
  out = ""
110
110
  status_string = severity
111
111
  if status_string == 'critical'
112
- out << "#{red}#{status_string.capitalize}#{return_color}"
112
+ out << "#{red}#{status_string.upcase}#{return_color}"
113
113
  elsif status_string == 'warning'
114
- out << "#{yellow}#{status_string.capitalize}#{return_color}"
114
+ out << "#{yellow}#{status_string.upcase}#{return_color}"
115
115
  elsif status_string == 'info'
116
- out << "#{cyan}#{status_string.capitalize}#{return_color}"
116
+ out << "#{cyan}#{status_string.upcase}#{return_color}"
117
117
  else
118
- out << "#{cyan}#{status_string}#{return_color}"
118
+ out << "#{cyan}#{status_string.to_s.upcase}#{return_color}"
119
+ end
120
+ out
121
+ end
122
+
123
+ def format_health_status(item, return_color=cyan)
124
+ out = ""
125
+ if item
126
+ attrs = {}
127
+ attrs[:unknown] = item['lastRunDate'] ? false : true
128
+ attrs[:muted] = item['createIncident'] == false
129
+ attrs[:failure] = item['lastCheckStatus'] == 'error'
130
+ attrs[:health] = item['health'] ? item['health'].to_i : 0
131
+
132
+ if attrs[:unknown]
133
+ out << "#{cyan}UNKNOWN#{return_color}"
134
+ elsif attrs[:health] >= 10
135
+ out << "#{green}HEALTHY#{return_color}"
136
+ elsif attrs[:failure]
137
+ out << "#{red}ERROR#{return_color}"
138
+ else
139
+ out << "#{yellow}CAUTION#{return_color}"
140
+ end
141
+ if attrs[:muted]
142
+ out << "#{cyan} (Muted)#{return_color}"
143
+ end
119
144
  end
120
145
  out
121
146
  end
@@ -133,12 +158,18 @@ module Morpheus::Cli::MonitoringHelper
133
158
  end
134
159
 
135
160
  def format_monitoring_incident_status(incident)
161
+ out = ""
162
+ muted = incident['inUptime'] == false
136
163
  status_string = incident['status']
137
164
  if status_string == 'closed'
138
- "closed ✓"
165
+ out << "CLOSED ✓"
139
166
  else
140
- status_string
167
+ out << status_string.to_s.upcase
168
+ if muted
169
+ out << " (MUTED)"
170
+ end
141
171
  end
172
+ out
142
173
  end
143
174
 
144
175
  def format_monitoring_issue_status(issue)
@@ -153,7 +184,7 @@ module Morpheus::Cli::MonitoringHelper
153
184
  columns = [
154
185
  {"ID" => lambda {|incident| incident['id'] } },
155
186
  {"SEVERITY" => lambda {|incident| format_severity(incident['severity']) } },
156
- {"NAME" => lambda {|incident| incident['name'] || 'No Subject' } },
187
+ {"NAME" => lambda {|incident| incident['displayName'] || incident['name'] || 'No Subject' } },
157
188
  {"TIME" => lambda {|incident| format_local_dt(incident['startDate']) } },
158
189
  {"STATUS" => lambda {|incident| format_monitoring_incident_status(incident) } },
159
190
  {"DURATION" => lambda {|incident| format_duration(incident['startDate'], incident['endDate']) } }
@@ -164,9 +195,25 @@ module Morpheus::Cli::MonitoringHelper
164
195
  print as_pretty_table(incidents, columns, opts)
165
196
  end
166
197
 
198
+ def print_incident_issues_table(history_items, opts={})
199
+ columns = [
200
+ # {"ID" => lambda {|issue| issue['id'] } },
201
+ {"SEVERITY" => lambda {|issue| format_severity(issue['severity']) } },
202
+ {"AVAILABLE" => lambda {|issue| format_boolean issue['available'] } },
203
+ {"TYPE" => lambda {|issue| issue["attachmentType"] } },
204
+ {"NAME" => lambda {|issue| issue['name'] } },
205
+ {"DATE CREATED" => lambda {|issue| format_local_dt(issue['startDate']) } }
206
+ ]
207
+ if opts[:include_fields]
208
+ columns = opts[:include_fields]
209
+ end
210
+ print as_pretty_table(history_items, columns, opts)
211
+ end
212
+
167
213
  def print_incident_history_table(history_items, opts={})
168
214
  columns = [
169
215
  # {"ID" => lambda {|issue| issue['id'] } },
216
+ # {"SEVERITY" => lambda {|issue| format_health_status(issue) } },
170
217
  {"SEVERITY" => lambda {|issue| format_severity(issue['severity']) } },
171
218
  {"AVAILABLE" => lambda {|issue| format_boolean issue['available'] } },
172
219
  {"TYPE" => lambda {|issue| issue["attachmentType"] } },
@@ -182,21 +229,9 @@ module Morpheus::Cli::MonitoringHelper
182
229
  def print_incident_notifications_table(notifications, opts={})
183
230
  columns = [
184
231
  {"NAME" => lambda {|notification| notification['recipient'] ? notification['recipient']['name'] : '' } },
185
- {"DELIVERY TYPE" => lambda {|notification| notification['addressTypes'].to_s } },
232
+ {"DELIVERY TYPE" => lambda {|recipient| format_recipient_method(recipient['method'] || recipient['addressTypes']) } },
186
233
  {"NOTIFIED ON" => lambda {|notification| format_local_dt(notification['dateCreated']) } },
187
- # {"AVAILABLE" => lambda {|notification| format_boolean notification['available'] } },
188
- # {"TYPE" => lambda {|notification| notification["attachmentType"] } },
189
- # {"NAME" => lambda {|notification| notification['name'] } },
190
- {"DATE CREATED" => lambda {|notification|
191
- date_str = format_local_dt(notification['startDate']).to_s
192
- if notification['pendingUtil']
193
- "(pending) #{date_str}"
194
- else
195
- date_str
196
- end
197
- } }
198
234
  ]
199
- #event['pendingUntil']
200
235
  if opts[:include_fields]
201
236
  columns = opts[:include_fields]
202
237
  end
@@ -277,12 +312,43 @@ module Morpheus::Cli::MonitoringHelper
277
312
 
278
313
  def print_check_history_table(history_items, opts={})
279
314
  columns = [
280
- # {"ID" => lambda {|issue| issue['id'] } },
281
- {"SEVERITY" => lambda {|issue| format_severity(issue['severity']) } },
282
- {"AVAILABLE" => lambda {|issue| format_boolean issue['available'] } },
283
- {"TYPE" => lambda {|issue| issue["attachmentType"] } },
284
- {"NAME" => lambda {|issue| issue['name'] } },
285
- {"DATE CREATED" => lambda {|issue| format_local_dt(issue['startDate']) } }
315
+ {"STATUS" => lambda {|issue| format_health_status(issue) } },
316
+ {"DATE CHECKED" => lambda {|issue| format_local_dt(issue['lastRunDate']) } },
317
+ # {"NAME" => lambda {|issue| issue['name'] } },
318
+ # {"AVAILABLE" => lambda {|issue| format_boolean issue['createIncident'] } },
319
+ {"RESPONSE TIME" => lambda {|issue| issue["lastTimer"] ? "#{issue['lastTimer']}ms" : "" } },
320
+ {"LAST METRIC" => lambda {|issue| issue["lastMetric"] } },
321
+ {"MESSAGE" => lambda {|issue|
322
+ # issue["lastError"].to_s.empty? ? issue["lastMessage"] : issue["lastError"]
323
+ if issue['lastCheckStatus'] == 'error'
324
+ issue["lastError"].to_s
325
+ else
326
+ issue["lastMessage"]
327
+ end
328
+ } },
329
+ ]
330
+ if opts[:include_fields]
331
+ columns = opts[:include_fields]
332
+ end
333
+ print as_pretty_table(history_items, columns, opts)
334
+ end
335
+
336
+ def print_check_group_history_table(history_items, opts={})
337
+ columns = [
338
+ {"STATUS" => lambda {|issue| format_health_status(issue) } },
339
+ {"DATE CHECKED" => lambda {|issue| format_local_dt(issue['lastRunDate']) } },
340
+ {"CHECK" => lambda {|issue| issue['name'] } },
341
+ # {"AVAILABLE" => lambda {|issue| format_boolean issue['createIncident'] } },
342
+ {"RESPONSE TIME" => lambda {|issue| issue["lastTimer"] ? "#{issue['lastTimer']}ms" : "" } },
343
+ {"LAST METRIC" => lambda {|issue| issue["lastMetric"] } },
344
+ {"MESSAGE" => lambda {|issue|
345
+ # issue["lastError"].to_s.empty? ? issue["lastMessage"] : issue["lastError"]
346
+ if issue['lastCheckStatus'] == 'error'
347
+ issue["lastError"].to_s
348
+ else
349
+ issue["lastMessage"]
350
+ end
351
+ } },
286
352
  ]
287
353
  if opts[:include_fields]
288
354
  columns = opts[:include_fields]
@@ -290,6 +356,10 @@ module Morpheus::Cli::MonitoringHelper
290
356
  print as_pretty_table(history_items, columns, opts)
291
357
  end
292
358
 
359
+ def print_monitor_app_history_table(history_items, opts={})
360
+ print_check_group_history_table(history_items, opts)
361
+ end
362
+
293
363
  def print_check_notifications_table(notifications, opts={})
294
364
  columns = [
295
365
  {"NAME" => lambda {|notification| notification['recipient'] ? notification['recipient']['name'] : '' } },
@@ -347,7 +417,7 @@ module Morpheus::Cli::MonitoringHelper
347
417
  elsif contacts.size > 1
348
418
  print_red_alert "#{contacts.size} Contacts found by name #{name}"
349
419
  print "\n"
350
- puts as_pretty_table(contacts, [{"ID" => "id" }, {"NAME" => "name"}], {color: red})
420
+ puts as_pretty_table(contacts, [{"ID" => "id" }, {"NAME" => "name"}, {"EMAIL" => "emailAddress"}], {color: red})
351
421
  print_red_alert "Try passing ID instead"
352
422
  print reset,"\n"
353
423
  exit 1 # return nil
@@ -356,6 +426,47 @@ module Morpheus::Cli::MonitoringHelper
356
426
  end
357
427
  end
358
428
 
429
+ # Monitoring Alerts
430
+
431
+ def find_alert_by_name_or_id(val)
432
+ if val.to_s =~ /\A\d{1,}\Z/
433
+ return find_alert_by_id(val)
434
+ else
435
+ return find_alert_by_name(val)
436
+ end
437
+ end
438
+
439
+ def find_alert_by_id(id)
440
+ begin
441
+ json_response = monitoring_interface.alerts.get(id.to_i)
442
+ return json_response['alert']
443
+ rescue RestClient::Exception => e
444
+ if e.response && e.response.code == 404
445
+ print_red_alert "Alert not found by id #{id}"
446
+ exit 1 # return nil
447
+ else
448
+ raise e
449
+ end
450
+ end
451
+ end
452
+
453
+ def find_alert_by_name(name)
454
+ json_results = monitoring_interface.alerts.list({name: name})
455
+ alerts = json_results["alerts"]
456
+ if alerts.empty?
457
+ print_red_alert "Alert not found by name #{name}"
458
+ exit 1 # return nil
459
+ elsif alerts.size > 1
460
+ print_red_alert "#{alerts.size} Alerts found by name #{name}"
461
+ print "\n"
462
+ puts as_pretty_table(alerts, [{"ID" => "id" }, {"NAME" => "name"}], {color: red})
463
+ print_red_alert "Try passing ID instead"
464
+ print reset,"\n"
465
+ exit 1 # return nil
466
+ else
467
+ return alerts[0]
468
+ end
469
+ end
359
470
 
360
471
  # Monitoring Check Groups
361
472
 
@@ -429,7 +540,7 @@ module Morpheus::Cli::MonitoringHelper
429
540
  def find_monitoring_app_by_id(id)
430
541
  begin
431
542
  json_response = monitoring_interface.apps.get(id.to_i)
432
- return json_response["monitorApp"] || json_response["app"]
543
+ return json_response['monitorApp'] || json_response['app']
433
544
  rescue RestClient::Exception => e
434
545
  if e.response && e.response.code == 404
435
546
  print_red_alert "Monitor App not found by id #{id}"
@@ -491,4 +602,275 @@ module Morpheus::Cli::MonitoringHelper
491
602
  print as_pretty_table(apps, columns, opts)
492
603
  end
493
604
 
605
+ def available_severities
606
+ [
607
+ [name:'Critical', code:'critical', value:'critical'],
608
+ [name:'Warning', code:'warning', value:'warning'],
609
+ [name:'Info', code:'info', value:'info']
610
+ ]
611
+ end
612
+
613
+ def format_recipient_method(address_types)
614
+ address_types = address_types.to_s
615
+ alert_method_names = []
616
+ if address_types =~ /email/i
617
+ alert_method_names << "Email"
618
+ end
619
+ if address_types =~ /sms/i
620
+ alert_method_names << "SMS"
621
+ end
622
+ if address_types =~ /apn/i
623
+ alert_method_names << "APN"
624
+ end
625
+ # if alert_method_names.empty?
626
+ # alert_method_names << "None"
627
+ # end
628
+ anded_list(alert_method_names)
629
+ end
630
+
631
+ # server expects "emailAddress" or "smsAddress" or "emailAddress,smsAddress"
632
+ # todo: just use array, expect server to parse it.
633
+ def parse_recipient_method(address_types)
634
+ requested_methods = address_types.to_s
635
+ alert_methods = []
636
+ if address_types =~ /email/i
637
+ alert_methods << "emailAddress"
638
+ end
639
+ if address_types =~ /sms/i || address_types =~ /phone/i || address_types =~ /mobile/i
640
+ alert_methods << "smsAddress"
641
+ end
642
+ if address_types =~ /apn/i
643
+ alert_methods << "apns"
644
+ end
645
+ alert_methods.join(',')
646
+ end
647
+
648
+ def prompt_for_recipients(params, options={})
649
+ #todo
650
+ end
651
+
652
+ def prompt_for_check_groups(params, options={}, api_client=nil, api_params={})
653
+ # def prompt_for_check_groups(params, options={})
654
+ # Check Groups
655
+ check_group_list = nil
656
+ check_group_ids = []
657
+ still_prompting = true
658
+
659
+ if params['checkGroups'].nil?
660
+ while still_prompting
661
+ v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'checkGroups', 'type' => 'text', 'fieldLabel' => 'Check Groups', 'required' => false, 'description' => 'Check Groups to include in this alert rule, comma separated list of names or IDs.'}], options[:options])
662
+ unless v_prompt['checkGroups'].to_s.empty?
663
+ check_group_list = v_prompt['checkGroups'].split(",").collect {|it| it.to_s.strip.empty? ? nil : it.to_s.strip }.compact.uniq
664
+ end
665
+ bad_ids = []
666
+ if check_group_list && check_group_list.size > 0
667
+ check_group_list.each do |it|
668
+ found_check = nil
669
+ begin
670
+ found_check = find_check_group_by_name_or_id(it)
671
+ rescue SystemExit => cmdexit
672
+ end
673
+ if found_check
674
+ check_group_ids << found_check['id']
675
+ else
676
+ bad_ids << it
677
+ end
678
+ end
679
+ end
680
+ still_prompting = bad_ids.empty? ? false : true
681
+ end
682
+ else
683
+ check_group_list = params['checkGroups']
684
+ still_prompting = false
685
+ bad_ids = []
686
+ if check_group_list && check_group_list.size > 0
687
+ check_group_list.each do |it|
688
+ found_check = nil
689
+ begin
690
+ found_check = find_check_group_by_name_or_id(it)
691
+ rescue SystemExit => cmdexit
692
+ end
693
+ if found_check
694
+ check_group_ids << found_check['id']
695
+ else
696
+ bad_ids << it
697
+ end
698
+ end
699
+ end
700
+ if !bad_ids.empty?
701
+ return {success:false, msg:"Check Groups not found: #{bad_ids}"}
702
+ end
703
+ # return check_group_ids
704
+ # payload = {'checkGroups':check_group_ids}
705
+ # return payload
706
+ return {success:true, data: check_group_ids}
707
+ end
708
+ end
709
+
710
+ def prompt_for_checks(params, options={}, api_client=nil, api_params={})
711
+ # Checks
712
+ check_list = nil
713
+ check_ids = nil
714
+ still_prompting = true
715
+ if params['checks'].nil?
716
+ still_prompting = true
717
+ while still_prompting do
718
+ v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'checks', 'type' => 'text', 'fieldLabel' => 'Checks', 'required' => false, 'description' => 'Checks to include, comma separated list of names or IDs.'}], options[:options])
719
+ unless v_prompt['checks'].to_s.empty?
720
+ check_list = v_prompt['checks'].split(",").collect {|it| it.to_s.strip.empty? ? nil : it.to_s.strip }.compact.uniq
721
+ end
722
+ check_ids = []
723
+ bad_ids = []
724
+ if check_list && check_list.size > 0
725
+ check_list.each do |it|
726
+ found_check = nil
727
+ begin
728
+ found_check = find_check_by_name_or_id(it)
729
+ rescue SystemExit => cmdexit
730
+ end
731
+ if found_check
732
+ check_ids << found_check['id']
733
+ else
734
+ bad_ids << it
735
+ end
736
+ end
737
+ end
738
+ still_prompting = bad_ids.empty? ? false : true
739
+ end
740
+ else
741
+ check_list = params['checks']
742
+ still_prompting = false
743
+ check_ids = []
744
+ bad_ids = []
745
+ if check_list && check_list.size > 0
746
+ check_list.each do |it|
747
+ found_check = nil
748
+ begin
749
+ found_check = find_check_by_name_or_id(it)
750
+ rescue SystemExit => cmdexit
751
+ end
752
+ if found_check
753
+ check_ids << found_check['id']
754
+ else
755
+ bad_ids << it
756
+ end
757
+ end
758
+ end
759
+ if !bad_ids.empty?
760
+ return {success:false, msg:"Checks not found: #{bad_ids}"}
761
+ end
762
+ end
763
+ return {success:true, data: check_ids}
764
+ end
765
+
766
+ def prompt_for_check_groups(params, options={}, api_client=nil, api_params={})
767
+ # def prompt_for_check_groups(params, options={})
768
+ # Check Groups
769
+ check_group_list = nil
770
+ check_group_ids = nil
771
+ bad_ids = []
772
+ if params['checkGroups'].nil?
773
+ still_prompting = true
774
+ while still_prompting do
775
+ v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'checkGroups', 'type' => 'text', 'fieldLabel' => 'Check Groups', 'required' => false, 'description' => 'Check Groups to include, comma separated list of names or IDs.'}], options[:options])
776
+ unless v_prompt['checkGroups'].to_s.empty?
777
+ check_group_list = v_prompt['checkGroups'].split(",").collect {|it| it.to_s.strip.empty? ? nil : it.to_s.strip }.compact.uniq
778
+ end
779
+ check_group_ids = []
780
+ bad_ids = []
781
+ if check_group_list && check_group_list.size > 0
782
+ check_group_list.each do |it|
783
+ found_check = nil
784
+ begin
785
+ found_check = find_check_group_by_name_or_id(it)
786
+ rescue SystemExit => cmdexit
787
+ end
788
+ if found_check
789
+ check_group_ids << found_check['id']
790
+ else
791
+ bad_ids << it
792
+ end
793
+ end
794
+ end
795
+ still_prompting = bad_ids.empty? ? false : true
796
+ end
797
+ else
798
+ check_group_list = params['checkGroups']
799
+ check_group_ids = []
800
+ bad_ids = []
801
+ if check_group_list && check_group_list.size > 0
802
+ check_group_list.each do |it|
803
+ found_check = nil
804
+ begin
805
+ found_check = find_check_group_by_name_or_id(it)
806
+ rescue SystemExit => cmdexit
807
+ end
808
+ if found_check
809
+ check_group_ids << found_check['id']
810
+ else
811
+ bad_ids << it
812
+ end
813
+ end
814
+ end
815
+ if !bad_ids.empty?
816
+ return {success:false, msg:"Check Groups not found: #{bad_ids}"}
817
+ end
818
+ end
819
+ return {success:true, data: check_group_ids}
820
+ end
821
+
822
+ def prompt_for_monitor_apps(params, options={}, api_client=nil, api_params={})
823
+ # Apps
824
+
825
+ monitor_app_list = nil
826
+ monitor_app_ids = nil
827
+ if params['apps'].nil?
828
+ still_prompting = true
829
+ while still_prompting
830
+ v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'apps', 'type' => 'text', 'fieldLabel' => 'Apps', 'required' => false, 'description' => 'Monitor Apps to include, comma separated list of names or IDs.'}], options[:options])
831
+ unless v_prompt['apps'].to_s.empty?
832
+ monitor_app_list = v_prompt['apps'].split(",").collect {|it| it.to_s.strip.empty? ? nil : it.to_s.strip }.compact.uniq
833
+ end
834
+ check_group_ids = []
835
+ bad_ids = []
836
+ if monitor_app_list && monitor_app_list.size > 0
837
+ monitor_app_list.each do |it|
838
+ found_monitor_app = nil
839
+ begin
840
+ found_monitor_app = find_monitoring_app_by_name_or_id(it)
841
+ rescue SystemExit => cmdexit
842
+ end
843
+ if found_monitor_app
844
+ monitor_app_ids << found_monitor_app['id']
845
+ else
846
+ bad_ids << it
847
+ end
848
+ end
849
+ end
850
+ still_prompting = bad_ids.empty? ? false : true
851
+ end
852
+ else
853
+ monitor_app_list = params['apps']
854
+ check_group_ids = []
855
+ bad_ids = []
856
+ if monitor_app_list && monitor_app_list.size > 0
857
+ monitor_app_list.each do |it|
858
+ found_monitor_app = nil
859
+ begin
860
+ found_monitor_app = find_monitoring_app_by_name_or_id(it)
861
+ rescue SystemExit => cmdexit
862
+ end
863
+ if found_monitor_app
864
+ monitor_app_ids << found_monitor_app['id']
865
+ else
866
+ bad_ids << it
867
+ end
868
+ end
869
+ end
870
+ if !bad_ids.empty?
871
+ return {success:false, msg:"Monitor Apps not found: #{bad_ids}"}
872
+ end
873
+ end
874
+ return {success:true, data: monitor_app_ids}
875
+ end
494
876
  end