morpheus-cli 4.1.4 → 4.1.5

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.
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