morpheus-cli 5.0.2 → 5.2.4
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/.gitignore +1 -0
- data/Dockerfile +1 -1
- data/lib/morpheus/api/api_client.rb +4 -0
- data/lib/morpheus/api/instances_interface.rb +30 -2
- data/lib/morpheus/api/invoices_interface.rb +12 -3
- data/lib/morpheus/api/servers_interface.rb +7 -0
- data/lib/morpheus/api/service_catalog_interface.rb +89 -0
- data/lib/morpheus/cli.rb +2 -1
- data/lib/morpheus/cli/apps.rb +3 -23
- data/lib/morpheus/cli/budgets_command.rb +389 -319
- data/lib/morpheus/cli/{catalog_command.rb → catalog_item_types_command.rb} +182 -67
- data/lib/morpheus/cli/cli_command.rb +25 -1
- data/lib/morpheus/cli/commands/standard/curl_command.rb +25 -10
- data/lib/morpheus/cli/commands/standard/history_command.rb +6 -2
- data/lib/morpheus/cli/containers_command.rb +0 -24
- data/lib/morpheus/cli/cypher_command.rb +6 -2
- data/lib/morpheus/cli/dashboard_command.rb +260 -20
- data/lib/morpheus/cli/health_command.rb +57 -0
- data/lib/morpheus/cli/hosts.rb +128 -11
- data/lib/morpheus/cli/instances.rb +270 -108
- data/lib/morpheus/cli/invoices_command.rb +67 -4
- data/lib/morpheus/cli/jobs_command.rb +94 -92
- data/lib/morpheus/cli/library_option_lists_command.rb +1 -1
- data/lib/morpheus/cli/library_option_types_command.rb +10 -5
- data/lib/morpheus/cli/mixins/accounts_helper.rb +5 -1
- data/lib/morpheus/cli/mixins/print_helper.rb +13 -6
- data/lib/morpheus/cli/mixins/provisioning_helper.rb +88 -5
- data/lib/morpheus/cli/option_types.rb +10 -10
- data/lib/morpheus/cli/projects_command.rb +1 -1
- data/lib/morpheus/cli/roles.rb +193 -155
- data/lib/morpheus/cli/service_catalog_command.rb +1474 -0
- data/lib/morpheus/cli/tasks.rb +9 -11
- data/lib/morpheus/cli/version.rb +1 -1
- data/lib/morpheus/cli/virtual_images.rb +162 -68
- data/lib/morpheus/formatters.rb +55 -20
- metadata +5 -4
- data/lib/morpheus/cli/mixins/catalog_helper.rb +0 -66
@@ -26,8 +26,8 @@ class Morpheus::Cli::CurlCommand
|
|
26
26
|
options = {}
|
27
27
|
optparse = Morpheus::Cli::OptionParser.new do|opts|
|
28
28
|
opts.banner = "Usage: morpheus curl [path] -- [*args]"
|
29
|
-
opts.on( '-p', '--pretty', "Print result as parsed JSON." ) do
|
30
|
-
options[:
|
29
|
+
opts.on( '-p', '--pretty', "Print result as parsed JSON. Alias for -j" ) do
|
30
|
+
options[:json] = true
|
31
31
|
end
|
32
32
|
opts.on( '-X', '--request METHOD', "HTTP request method. Default is GET" ) do |val|
|
33
33
|
curl_method = val
|
@@ -134,29 +134,44 @@ EOT
|
|
134
134
|
print reset
|
135
135
|
return 0
|
136
136
|
end
|
137
|
+
exit_code, err = 0, nil
|
137
138
|
# print cyan
|
138
139
|
# print "#{cyan}#{curl_cmd_str}#{reset}"
|
139
140
|
# print "\n\n"
|
140
141
|
print reset
|
141
142
|
# print result
|
142
143
|
curl_output = `#{curl_cmd}`
|
143
|
-
|
144
|
+
|
145
|
+
if $?.success? != true
|
146
|
+
exit_code = $?.exitstatus
|
147
|
+
err = "curl command exited non-zero"
|
148
|
+
end
|
149
|
+
json_response = {}
|
150
|
+
other_output = nil
|
151
|
+
if options[:json] || options[:yaml] || options[:csv]
|
144
152
|
output_lines = curl_output.split("\n")
|
145
153
|
last_line = output_lines.pop
|
146
154
|
if output_lines.size > 0
|
147
|
-
|
155
|
+
other_output = output_lines.join("\n")
|
148
156
|
end
|
149
157
|
begin
|
150
|
-
|
158
|
+
json_response = JSON.parse(last_line)
|
151
159
|
rescue => ex
|
152
|
-
|
153
|
-
|
160
|
+
puts_error curl_output
|
161
|
+
print_red_alert "failed to parse curl result as JSON data Error: #{ex.message}"
|
162
|
+
|
163
|
+
exit_code = 2
|
164
|
+
err = "failed to parse curl result as JSON data Error: #{ex.message}"
|
165
|
+
return exit_code, err
|
154
166
|
end
|
155
167
|
else
|
156
|
-
|
168
|
+
other_output = curl_output
|
157
169
|
end
|
158
|
-
|
159
|
-
|
170
|
+
curl_object_key = nil # json_response.keys.first
|
171
|
+
render_response(json_response, options, curl_object_key) do
|
172
|
+
puts other_output
|
173
|
+
end
|
174
|
+
return exit_code, err
|
160
175
|
end
|
161
176
|
|
162
177
|
def command_available?(cmd)
|
@@ -15,7 +15,7 @@ class Morpheus::Cli::HistoryCommand
|
|
15
15
|
def handle(args)
|
16
16
|
options = {show_pagination:false}
|
17
17
|
optparse = Morpheus::Cli::OptionParser.new do|opts|
|
18
|
-
opts.banner = "Usage: morpheus #{command_name}"
|
18
|
+
opts.banner = "Usage: morpheus #{command_name} [search]"
|
19
19
|
# -n is a hidden alias for -m
|
20
20
|
opts.on( '-n', '--max-commands MAX', "Alias for -m, --max option." ) do |val|
|
21
21
|
options[:max] = val
|
@@ -35,6 +35,7 @@ The --flush option can be used to purge the history.
|
|
35
35
|
Examples:
|
36
36
|
history
|
37
37
|
history -m 100
|
38
|
+
history "instances list"
|
38
39
|
history --flush
|
39
40
|
|
40
41
|
The most recently executed commands are seen by default. Use --reverse to see the oldest commands.
|
@@ -42,7 +43,10 @@ EOT
|
|
42
43
|
end
|
43
44
|
raw_cmd = "#{command_name} #{args.join(' ')}"
|
44
45
|
optparse.parse!(args)
|
45
|
-
verify_args!(args:args, count: 0, optparse:optparse)
|
46
|
+
# verify_args!(args:args, count: 0, optparse:optparse)
|
47
|
+
if args.count > 0
|
48
|
+
options[:phrase] = args.join(" ")
|
49
|
+
end
|
46
50
|
if options[:do_flush]
|
47
51
|
command_count = Morpheus::Cli::Shell.instance.history_commands_count
|
48
52
|
unless options[:yes] || Morpheus::Cli::OptionTypes.confirm("Are you sure you want to flush your command history (#{format_number(command_count)} #{command_count == 1 ? 'command' : 'commands'})?")
|
@@ -699,28 +699,4 @@ private
|
|
699
699
|
end
|
700
700
|
end
|
701
701
|
|
702
|
-
def format_container_status(container, return_color=cyan)
|
703
|
-
out = ""
|
704
|
-
status_string = container['status'].to_s
|
705
|
-
if status_string == 'running'
|
706
|
-
out << "#{green}#{status_string.upcase}#{return_color}"
|
707
|
-
elsif status_string == 'stopped' or status_string == 'failed'
|
708
|
-
out << "#{red}#{status_string.upcase}#{return_color}"
|
709
|
-
elsif status_string == 'unknown'
|
710
|
-
out << "#{white}#{status_string.upcase}#{return_color}"
|
711
|
-
else
|
712
|
-
out << "#{yellow}#{status_string.upcase}#{return_color}"
|
713
|
-
end
|
714
|
-
out
|
715
|
-
end
|
716
|
-
|
717
|
-
def format_container_connection_string(container)
|
718
|
-
if !container['ports'].nil? && container['ports'].empty? == false
|
719
|
-
connection_string = "#{container['ip']}:#{container['ports'][0]['external']}"
|
720
|
-
else
|
721
|
-
# eh? more logic needed here i think, see taglib morph:containerLocationMenu
|
722
|
-
connection_string = "#{container['ip']}"
|
723
|
-
end
|
724
|
-
end
|
725
|
-
|
726
702
|
end
|
@@ -265,6 +265,9 @@ EOT
|
|
265
265
|
opts.on( '-v', '--value VALUE', "Secret value. This can be used to store a string instead of an object, and can also be passed as the second argument." ) do |val|
|
266
266
|
item_value = val
|
267
267
|
end
|
268
|
+
# opts.on( '--type VALUE', String, "Type, default is based on key engine, string or object" ) do |val|
|
269
|
+
# params['type'] = val
|
270
|
+
# end
|
268
271
|
opts.on( '-t', '--ttl SECONDS', "Time to live, the lease duration before this key expires." ) do |val|
|
269
272
|
ttl = val
|
270
273
|
if val.to_s.empty? || val.to_s == '0'
|
@@ -276,7 +279,7 @@ EOT
|
|
276
279
|
# opts.on( '--no-overwrite', '--no-overwrite', "Do not overwrite existing keys. Existing keys are overwritten by default." ) do
|
277
280
|
# params['overwrite'] = false
|
278
281
|
# end
|
279
|
-
build_common_options(opts, options, [:auto_confirm, :options, :payload, :json, :yaml, :csv, :fields, :outfile, :dry_run, :quiet, :remote])
|
282
|
+
build_common_options(opts, options, [:auto_confirm, :options, :payload, :query, :json, :yaml, :csv, :fields, :outfile, :dry_run, :quiet, :remote])
|
280
283
|
opts.footer = "Create or update a cypher key." + "\n" +
|
281
284
|
"[key] is required. This is the key of the cypher being created or updated. The key includes the mount prefix eg. secret/hello" + "\n" +
|
282
285
|
"[value] is required for some cypher engines, such as secret. This is the secret value or k=v pairs being stored. Supports 1-N arguments." + "\n" +
|
@@ -292,7 +295,8 @@ EOT
|
|
292
295
|
# return 1
|
293
296
|
# end
|
294
297
|
connect(options)
|
295
|
-
|
298
|
+
params.merge!(parse_query_options(options))
|
299
|
+
|
296
300
|
# parse arguments like [value] or [k=v]
|
297
301
|
if args.count == 0
|
298
302
|
# prompt for key and value
|
@@ -4,8 +4,9 @@ require 'json'
|
|
4
4
|
|
5
5
|
class Morpheus::Cli::DashboardCommand
|
6
6
|
include Morpheus::Cli::CliCommand
|
7
|
+
include Morpheus::Cli::ProvisioningHelper
|
7
8
|
set_command_name :dashboard
|
8
|
-
|
9
|
+
set_command_description "View Morpheus Dashboard"
|
9
10
|
|
10
11
|
def initialize()
|
11
12
|
# @appliance_name, @appliance_url = Morpheus::Cli::Remote.active_appliance
|
@@ -28,34 +29,273 @@ class Morpheus::Cli::DashboardCommand
|
|
28
29
|
options = {}
|
29
30
|
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
30
31
|
opts.banner = usage
|
31
|
-
|
32
|
+
opts.on('-a', '--details', "Display all details: more instance usage stats, etc" ) do
|
33
|
+
options[:details] = true
|
34
|
+
end
|
35
|
+
build_standard_list_options(opts, options)
|
36
|
+
opts.footer = <<-EOT
|
37
|
+
View Morpheus Dashboard.
|
38
|
+
This includes instance and backup counts, favorite instances, monitoring and recent activity.
|
39
|
+
EOT
|
32
40
|
end
|
33
41
|
optparse.parse!(args)
|
34
|
-
|
42
|
+
verify_args!(args:args, optparse:optparse, count:0)
|
35
43
|
connect(options)
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
44
|
+
params = {}
|
45
|
+
params.merge!(parse_list_options(options))
|
46
|
+
@dashboard_interface.setopts(options)
|
47
|
+
if options[:dry_run]
|
48
|
+
print_dry_run @dashboard_interface.dry.get(params)
|
49
|
+
return
|
50
|
+
end
|
51
|
+
json_response = @dashboard_interface.get(params)
|
52
|
+
render_response(json_response, options) do
|
53
|
+
print_h1 "Morpheus Dashboard", [], options
|
54
|
+
|
55
|
+
## STATUS
|
56
|
+
|
57
|
+
status_column_definitions = {
|
58
|
+
"Instances" => lambda {|it|
|
59
|
+
format_number(it['instanceStats']['total']) rescue nil
|
60
|
+
},
|
61
|
+
"Running" => lambda {|it|
|
62
|
+
format_number(it['instanceStats']['running']) rescue nil
|
63
|
+
},
|
64
|
+
# "Used Storage" => lambda {|it|
|
65
|
+
# ((it['instanceStats']['maxStorage'].to_i > 0) ? ((it['instanceStats']['usedStorage'].to_f / it['instanceStats']['maxStorage'].to_f) * 100).round(1) : 0).to_s + '%' rescue nil
|
66
|
+
# },
|
67
|
+
}
|
68
|
+
print as_description_list(json_response, status_column_definitions, options)
|
69
|
+
# print reset,"\n"
|
70
|
+
|
71
|
+
stats = json_response['instanceStats']
|
72
|
+
if stats
|
73
|
+
print_h2 "Instance Usage", options
|
74
|
+
print_stats_usage(stats, {include: [:max_cpu, :avg_cpu, :memory, :storage]})
|
42
75
|
end
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
76
|
+
|
77
|
+
|
78
|
+
|
79
|
+
open_incident_count = json_response['monitoring']['openIncidents'] rescue (json_response['appStatus']['openIncidents'] rescue nil)
|
80
|
+
|
81
|
+
avg_response_time = json_response['monitoring']['avgResponseTime'] rescue nil
|
82
|
+
warning_apps = json_response['monitoring']['warningApps'] rescue 0
|
83
|
+
warning_checks = json_response['monitoring']['warningChecks'] rescue 0
|
84
|
+
fail_checks = json_response['monitoring']['failChecks'] rescue 0
|
85
|
+
fail_apps = json_response['monitoring']['failApps'] rescue 0
|
86
|
+
success_checks = json_response['monitoring']['successChecks'] rescue 0
|
87
|
+
success_apps = json_response['monitoring']['successApps'] rescue 0
|
88
|
+
monitoring_status_color = cyan
|
89
|
+
if fail_checks > 0 || fail_apps > 0
|
90
|
+
monitoring_status_color = red
|
91
|
+
elsif warning_checks > 0 || warning_apps > 0
|
92
|
+
monitoring_status_color = yellow
|
93
|
+
end
|
94
|
+
|
95
|
+
print_h2 "Monitoring"
|
96
|
+
|
97
|
+
monitoring_column_definitions = {
|
98
|
+
"Status" => lambda {|it|
|
99
|
+
if fail_apps > 0 # || fail_checks > 0
|
100
|
+
# check_summary = [fail_apps > 0 ? "#{fail_apps} Apps" : nil,fail_checks > 0 ? "#{fail_checks} Checks" : nil].compact.join(", ")
|
101
|
+
# red + "ERROR" + " (" + check_summary + ")" + cyan
|
102
|
+
red + "ERROR" + cyan
|
103
|
+
elsif warning_apps > 0 || warning_checks > 0
|
104
|
+
# check_summary = [warning_apps > 0 ? "#{warning_apps} Apps" : nil,warning_checks > 0 ? "#{warning_checks} Checks" : nil].compact.join(", ")
|
105
|
+
# red + "WARNING" + " (" + check_summary + ")" + cyan
|
106
|
+
yellow + "WARNING" + cyan
|
107
|
+
else
|
108
|
+
cyan + "HEALTHY" + cyan
|
109
|
+
end
|
110
|
+
},
|
111
|
+
# "Availability" => lambda {|it|
|
112
|
+
# # todo
|
113
|
+
# },
|
114
|
+
"Response Time" => lambda {|it|
|
115
|
+
# format_number(avg_response_time).to_s + "ms"
|
116
|
+
(avg_response_time.round).to_s + "ms"
|
117
|
+
},
|
118
|
+
"Open Incidents" => lambda {|it|
|
119
|
+
monitoring_status_color = cyan
|
120
|
+
# if fail_checks > 0 || fail_apps > 0
|
121
|
+
# monitoring_status_color = red
|
122
|
+
# elsif warning_checks > 0 || warning_apps > 0
|
123
|
+
# monitoring_status_color = yellow
|
124
|
+
# end
|
125
|
+
if open_incident_count.nil?
|
126
|
+
yellow + "n/a" + cyan + "\n"
|
127
|
+
elsif open_incident_count == 0
|
128
|
+
monitoring_status_color + "0 Open Incidents" + cyan
|
129
|
+
elsif open_incident_count == 1
|
130
|
+
monitoring_status_color + "1 Open Incident" + cyan
|
131
|
+
else
|
132
|
+
monitoring_status_color + "#{open_incident_count} Open Incidents" + cyan
|
133
|
+
end
|
134
|
+
}
|
135
|
+
}
|
136
|
+
#print as_description_list(json_response, monitoring_column_definitions, options)
|
137
|
+
print as_pretty_table([json_response], monitoring_column_definitions.upcase_keys!, options)
|
138
|
+
|
139
|
+
|
140
|
+
if json_response['logStats']
|
141
|
+
# todo: should come from monitoring.startMs-endMs
|
142
|
+
log_period_display = "7 Days"
|
143
|
+
print_h2 "Logs (#{log_period_display})", options
|
144
|
+
error_log_data = json_response['logStats']['data'].find {|it| it['key'].to_s.upcase == 'ERROR' }
|
145
|
+
error_count = error_log_data["count"] rescue 0
|
146
|
+
fatal_log_data = json_response['logStats']['data'].find {|it| it['key'].to_s.upcase == 'FATAL' }
|
147
|
+
fatal_count = fatal_log_data["count"] rescue 0
|
148
|
+
# total error is actaully error + fatal
|
149
|
+
total_error_count = error_count + fatal_count
|
150
|
+
# if total_error_count.nil?
|
151
|
+
# print yellow + "n/a" + cyan + "\n"
|
152
|
+
# elsif total_error_count == 0
|
153
|
+
# print cyan + "0 Errors" + cyan + "\n"
|
154
|
+
# elsif total_error_count == 1
|
155
|
+
# print red + "1 Error" + cyan + "\n"
|
156
|
+
# else
|
157
|
+
# print red + "#{total_error_count} Errors" + cyan + "\n"
|
158
|
+
# end
|
159
|
+
if total_error_count == 0
|
160
|
+
print cyan + "(0 Errors)" + cyan + "\n"
|
161
|
+
#print cyan + "0-0-0-0-0-0-0-0 (0 Errors)" + cyan + "\n"
|
162
|
+
end
|
163
|
+
if error_count > 0
|
164
|
+
if error_log_data["values"]
|
165
|
+
log_plot = ""
|
166
|
+
plot_index = 0
|
167
|
+
error_log_data["values"].each do |k, v|
|
168
|
+
if v.to_i == 0
|
169
|
+
log_plot << cyan + v.to_s
|
170
|
+
else
|
171
|
+
log_plot << red + v.to_s
|
172
|
+
end
|
173
|
+
if plot_index != error_log_data["values"].size - 1
|
174
|
+
log_plot << cyan + "-"
|
175
|
+
end
|
176
|
+
plot_index +=1
|
177
|
+
end
|
178
|
+
print log_plot
|
179
|
+
print " "
|
180
|
+
if error_count == 0
|
181
|
+
print cyan + "(0 Errors)" + cyan
|
182
|
+
elsif error_count == 1
|
183
|
+
print red + "(1 Errors)" + cyan
|
184
|
+
else
|
185
|
+
print red + "(#{error_count} Errors)" + cyan
|
186
|
+
end
|
187
|
+
print reset + "\n"
|
188
|
+
end
|
189
|
+
end
|
190
|
+
if fatal_count > 0
|
191
|
+
if fatal_log_data["values"]
|
192
|
+
log_plot = ""
|
193
|
+
plot_index = 0
|
194
|
+
fatal_log_data["values"].each do |k, v|
|
195
|
+
if v.to_i == 0
|
196
|
+
log_plot << cyan + v.to_s
|
197
|
+
else
|
198
|
+
log_plot << red + v.to_s
|
199
|
+
end
|
200
|
+
if plot_index != fatal_log_data["values"].size - 1
|
201
|
+
log_plot << cyan + "-"
|
202
|
+
end
|
203
|
+
plot_index +=1
|
204
|
+
end
|
205
|
+
print log_plot
|
206
|
+
print " "
|
207
|
+
if fatal_count == 0
|
208
|
+
print cyan + "(0 FATAL)" + cyan
|
209
|
+
elsif fatal_count == 1
|
210
|
+
print red + "(1 FATAL)" + cyan
|
211
|
+
else
|
212
|
+
print red + "(#{fatal_count} FATAL)" + cyan
|
213
|
+
end
|
214
|
+
print reset + "\n"
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
print_h2 "Backups (7 Days)"
|
220
|
+
backup_status_column_definitions = {
|
221
|
+
# "Total" => lambda {|it|
|
222
|
+
# it['backups']['accountStats']['lastSevenDays']['completed'] rescue nil
|
223
|
+
# },
|
224
|
+
"Successful" => lambda {|it|
|
225
|
+
it['backups']['accountStats']['lastSevenDays']['successful'] rescue nil
|
226
|
+
},
|
227
|
+
"Failed" => lambda {|it|
|
228
|
+
n = it['backups']['accountStats']['lastSevenDays']['failed'] rescue nil
|
229
|
+
if n == 0
|
230
|
+
cyan + n.to_s + reset
|
231
|
+
else
|
232
|
+
red + n.to_s + reset
|
233
|
+
end
|
234
|
+
}
|
235
|
+
}
|
236
|
+
print as_description_list(json_response, backup_status_column_definitions, options)
|
237
|
+
#print as_pretty_table([json_response], backup_status_column_definitions, options)
|
238
|
+
# print reset,"\n"
|
239
|
+
|
240
|
+
favorite_instances = json_response["provisioning"]["favoriteInstances"] || [] rescue []
|
241
|
+
if favorite_instances.empty?
|
242
|
+
# print cyan, "No favorite instances.",reset,"\n"
|
47
243
|
else
|
244
|
+
print_h2 "My Instances"
|
245
|
+
favorite_instances_columns = {
|
246
|
+
"ID" => lambda {|instance|
|
247
|
+
instance['id']
|
248
|
+
},
|
249
|
+
"Name" => lambda {|instance|
|
250
|
+
instance['name']
|
251
|
+
},
|
252
|
+
"Type" => lambda {|instance|
|
253
|
+
instance['instanceType']['name'] rescue nil
|
254
|
+
},
|
255
|
+
"IP/PORT" => lambda {|instance|
|
256
|
+
format_instance_connection_string(instance)
|
257
|
+
},
|
258
|
+
"Status" => lambda {|it| format_instance_status(it) }
|
259
|
+
}
|
260
|
+
#print as_description_list(json_response, status_column_definitions, options)
|
261
|
+
print as_pretty_table(favorite_instances, favorite_instances_columns, options)
|
262
|
+
# print reset,"\n"
|
263
|
+
end
|
48
264
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
265
|
+
# RECENT ACTIVITY
|
266
|
+
activity = json_response["activity"] || json_response["recentActivity"] || []
|
267
|
+
print_h2 "Recent Activity", [], options
|
268
|
+
if activity.empty?
|
269
|
+
print cyan, "No activity found.",reset,"\n"
|
270
|
+
else
|
271
|
+
columns = [
|
272
|
+
# {"SEVERITY" => lambda {|record| format_activity_severity(record['severity']) } },
|
273
|
+
{"TYPE" => lambda {|record| record['activityType'] } },
|
274
|
+
{"NAME" => lambda {|record| record['name'] } },
|
275
|
+
{"RESOURCE" => lambda {|record| "#{record['objectType']} #{record['objectId']}" } },
|
276
|
+
{"MESSAGE" => lambda {|record| record['message'] || '' } },
|
277
|
+
{"USER" => lambda {|record| record['user'] ? record['user']['username'] : record['userName'] } },
|
278
|
+
#{"DATE" => lambda {|record| "#{format_duration_ago(record['ts'] || record['timestamp'])}" } },
|
279
|
+
{"DATE" => lambda {|record|
|
280
|
+
# show full time if searching for custom timerange, otherwise the default is to show relative time
|
281
|
+
if params['start'] || params['end'] || params['timeframe']
|
282
|
+
"#{format_local_dt(record['ts'] || record['timestamp'])}"
|
283
|
+
else
|
284
|
+
"#{format_duration_ago(record['ts'] || record['timestamp'])}"
|
285
|
+
end
|
286
|
+
|
287
|
+
} },
|
288
|
+
]
|
289
|
+
print as_pretty_table(activity, columns, options)
|
290
|
+
# print_results_pagination(json_response)
|
291
|
+
# print reset,"\n"
|
53
292
|
|
54
293
|
end
|
55
|
-
|
56
|
-
print_rest_exception(e, options)
|
57
|
-
exit 1
|
294
|
+
|
58
295
|
end
|
296
|
+
print reset,"\n"
|
297
|
+
return 0, nil
|
59
298
|
end
|
60
299
|
|
300
|
+
|
61
301
|
end
|