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.
- checksums.yaml +4 -4
- data/lib/morpheus.rb +5 -0
- data/lib/morpheus/api.rb +2 -2
- data/lib/morpheus/api/api_client.rb +47 -12
- data/lib/morpheus/api/appliance_settings_interface.rb +30 -0
- data/lib/morpheus/api/auth_interface.rb +14 -10
- data/lib/morpheus/api/clouds_interface.rb +7 -0
- data/lib/morpheus/api/clusters_interface.rb +17 -5
- data/lib/morpheus/api/custom_instance_types_interface.rb +2 -3
- data/lib/morpheus/api/deployments_interface.rb +7 -0
- data/lib/morpheus/api/execute_schedules_interface.rb +2 -3
- data/lib/morpheus/api/groups_interface.rb +7 -0
- data/lib/morpheus/api/license_interface.rb +9 -2
- data/lib/morpheus/api/load_balancers_interface.rb +7 -0
- data/lib/morpheus/api/logs_interface.rb +11 -2
- data/lib/morpheus/api/monitoring_alerts_interface.rb +45 -0
- data/lib/morpheus/api/monitoring_checks_interface.rb +2 -2
- data/lib/morpheus/api/monitoring_interface.rb +13 -8
- data/lib/morpheus/api/power_schedules_interface.rb +2 -3
- data/lib/morpheus/api/servers_interface.rb +5 -2
- data/lib/morpheus/api/setup_interface.rb +25 -7
- data/lib/morpheus/api/task_sets_interface.rb +7 -1
- data/lib/morpheus/api/tasks_interface.rb +7 -0
- data/lib/morpheus/api/virtual_images_interface.rb +2 -3
- data/lib/morpheus/api/whitelabel_settings_interface.rb +60 -0
- data/lib/morpheus/cli.rb +18 -14
- data/lib/morpheus/cli/access_token_command.rb +18 -2
- data/lib/morpheus/cli/appliance_settings_command.rb +303 -0
- data/lib/morpheus/cli/apps.rb +4 -3
- data/lib/morpheus/cli/archives_command.rb +0 -21
- data/lib/morpheus/cli/blueprints_command.rb +2 -2
- data/lib/morpheus/cli/cli_command.rb +32 -8
- data/lib/morpheus/cli/clouds.rb +6 -11
- data/lib/morpheus/cli/clusters.rb +346 -117
- data/lib/morpheus/cli/command_error.rb +4 -0
- data/lib/morpheus/cli/commands/standard/man_command.rb +1 -1
- data/lib/morpheus/cli/containers_command.rb +2 -1
- data/lib/morpheus/cli/credentials.rb +49 -4
- data/lib/morpheus/cli/deployments.rb +2 -2
- data/lib/morpheus/cli/dot_file.rb +2 -2
- data/lib/morpheus/cli/error_handler.rb +6 -3
- data/lib/morpheus/cli/execute_schedules_command.rb +1 -1
- data/lib/morpheus/cli/groups.rb +4 -4
- data/lib/morpheus/cli/hosts.rb +3 -2
- data/lib/morpheus/cli/image_builder_command.rb +0 -21
- data/lib/morpheus/cli/instances.rb +17 -4
- data/lib/morpheus/cli/library_container_types_command.rb +1 -1
- data/lib/morpheus/cli/library_layouts_command.rb +1 -1
- data/lib/morpheus/cli/library_upgrades_command.rb +1 -1
- data/lib/morpheus/cli/license.rb +185 -72
- data/lib/morpheus/cli/load_balancers.rb +4 -4
- data/lib/morpheus/cli/login.rb +4 -0
- data/lib/morpheus/cli/logs_command.rb +132 -0
- data/lib/morpheus/cli/mixins/infrastructure_helper.rb +2 -2
- data/lib/morpheus/cli/mixins/logs_helper.rb +65 -0
- data/lib/morpheus/cli/mixins/monitoring_helper.rb +410 -28
- data/lib/morpheus/cli/mixins/print_helper.rb +14 -4
- data/lib/morpheus/cli/monitoring_alerts_command.rb +800 -0
- data/lib/morpheus/cli/monitoring_apps_command.rb +85 -28
- data/lib/morpheus/cli/monitoring_checks_command.rb +60 -27
- data/lib/morpheus/cli/monitoring_contacts_command.rb +54 -79
- data/lib/morpheus/cli/monitoring_groups_command.rb +62 -23
- data/lib/morpheus/cli/monitoring_incidents_command.rb +91 -70
- data/lib/morpheus/cli/network_pools_command.rb +39 -23
- data/lib/morpheus/cli/power_schedules_command.rb +1 -1
- data/lib/morpheus/cli/remote.rb +834 -275
- data/lib/morpheus/cli/roles.rb +100 -38
- data/lib/morpheus/cli/tasks.rb +1 -1
- data/lib/morpheus/cli/user_settings_command.rb +20 -12
- data/lib/morpheus/cli/version.rb +1 -1
- data/lib/morpheus/cli/virtual_images.rb +1 -1
- data/lib/morpheus/cli/whitelabel_settings_command.rb +546 -0
- data/lib/morpheus/cli/whoami.rb +1 -1
- data/lib/morpheus/cli/workflows.rb +2 -2
- data/lib/morpheus/terminal.rb +22 -8
- 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.
|
42
|
+
print_dry_run @load_balancers_interface.dry.list(params)
|
43
43
|
return
|
44
44
|
end
|
45
|
-
json_response = @load_balancers_interface.
|
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.
|
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.
|
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
|
data/lib/morpheus/cli/login.rb
CHANGED
@@ -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.
|
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.
|
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.
|
112
|
+
out << "#{red}#{status_string.upcase}#{return_color}"
|
113
113
|
elsif status_string == 'warning'
|
114
|
-
out << "#{yellow}#{status_string.
|
114
|
+
out << "#{yellow}#{status_string.upcase}#{return_color}"
|
115
115
|
elsif status_string == 'info'
|
116
|
-
out << "#{cyan}#{status_string.
|
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
|
-
"
|
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 {|
|
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
|
-
|
281
|
-
{"
|
282
|
-
{"
|
283
|
-
{"
|
284
|
-
{"
|
285
|
-
{"
|
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[
|
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
|