morpheus-cli 3.5.2 → 3.5.3
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/api/api_client.rb +16 -0
- data/lib/morpheus/api/blueprints_interface.rb +84 -0
- data/lib/morpheus/api/execution_request_interface.rb +33 -0
- data/lib/morpheus/api/instances_interface.rb +21 -0
- data/lib/morpheus/api/packages_interface.rb +25 -5
- data/lib/morpheus/api/processes_interface.rb +34 -0
- data/lib/morpheus/api/roles_interface.rb +7 -0
- data/lib/morpheus/api/servers_interface.rb +8 -0
- data/lib/morpheus/api/user_settings_interface.rb +76 -0
- data/lib/morpheus/cli.rb +5 -1
- data/lib/morpheus/cli/alias_command.rb +1 -1
- data/lib/morpheus/cli/app_templates.rb +2 -1
- data/lib/morpheus/cli/apps.rb +173 -19
- data/lib/morpheus/cli/blueprints_command.rb +2134 -0
- data/lib/morpheus/cli/cli_command.rb +3 -1
- data/lib/morpheus/cli/clouds.rb +4 -10
- data/lib/morpheus/cli/coloring_command.rb +14 -8
- data/lib/morpheus/cli/containers_command.rb +92 -5
- data/lib/morpheus/cli/execution_request_command.rb +313 -0
- data/lib/morpheus/cli/hosts.rb +188 -7
- data/lib/morpheus/cli/instances.rb +472 -9
- data/lib/morpheus/cli/login.rb +1 -1
- data/lib/morpheus/cli/mixins/print_helper.rb +8 -0
- data/lib/morpheus/cli/mixins/processes_helper.rb +134 -0
- data/lib/morpheus/cli/option_types.rb +21 -16
- data/lib/morpheus/cli/packages_command.rb +469 -17
- data/lib/morpheus/cli/processes_command.rb +313 -0
- data/lib/morpheus/cli/remote.rb +20 -9
- data/lib/morpheus/cli/roles.rb +186 -6
- data/lib/morpheus/cli/shell.rb +10 -1
- data/lib/morpheus/cli/tasks.rb +4 -1
- data/lib/morpheus/cli/user_settings_command.rb +431 -0
- data/lib/morpheus/cli/version.rb +1 -1
- data/lib/morpheus/cli/whoami.rb +1 -1
- data/lib/morpheus/formatters.rb +14 -0
- data/lib/morpheus/morpkg.rb +119 -0
- data/morpheus-cli.gemspec +1 -0
- metadata +26 -2
data/lib/morpheus/cli/hosts.rb
CHANGED
@@ -3,14 +3,17 @@ require 'io/console'
|
|
3
3
|
require 'rest_client'
|
4
4
|
require 'optparse'
|
5
5
|
require 'morpheus/cli/cli_command'
|
6
|
+
require 'morpheus/cli/mixins/accounts_helper'
|
6
7
|
require 'morpheus/cli/mixins/provisioning_helper'
|
7
8
|
require 'morpheus/cli/option_types'
|
8
9
|
require 'json'
|
9
10
|
|
10
11
|
class Morpheus::Cli::Hosts
|
11
12
|
include Morpheus::Cli::CliCommand
|
13
|
+
include Morpheus::Cli::AccountsHelper
|
12
14
|
include Morpheus::Cli::ProvisioningHelper
|
13
|
-
register_subcommands :list, :count, :get, :stats, :add, :remove, :logs, :start, :stop, :resize, :run_workflow, {:'make-managed' => :install_agent}, :upgrade_agent, :server_types
|
15
|
+
register_subcommands :list, :count, :get, :stats, :add, :update, :remove, :logs, :start, :stop, :resize, :run_workflow, {:'make-managed' => :install_agent}, :upgrade_agent, :server_types
|
16
|
+
register_subcommands :exec => :execution_request
|
14
17
|
alias_subcommand :details, :get
|
15
18
|
set_default_subcommand :list
|
16
19
|
|
@@ -26,7 +29,9 @@ class Morpheus::Cli::Hosts
|
|
26
29
|
@task_sets_interface = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url).task_sets
|
27
30
|
@servers_interface = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url).servers
|
28
31
|
@logs_interface = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url).logs
|
32
|
+
@accounts_interface = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url).accounts
|
29
33
|
@active_group_id = Morpheus::Cli::Groups.active_group
|
34
|
+
@execution_request_interface = @api_client.execution_request
|
30
35
|
end
|
31
36
|
|
32
37
|
def handle(args)
|
@@ -38,6 +43,9 @@ class Morpheus::Cli::Hosts
|
|
38
43
|
params = {}
|
39
44
|
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
40
45
|
opts.banner = subcommand_usage()
|
46
|
+
opts.on( '-a', '--account ACCOUNT', "Account Name or ID" ) do |val|
|
47
|
+
options[:account] = val
|
48
|
+
end
|
41
49
|
opts.on( '-g', '--group GROUP', "Group Name or ID" ) do |val|
|
42
50
|
options[:group] = val
|
43
51
|
end
|
@@ -87,6 +95,15 @@ class Morpheus::Cli::Hosts
|
|
87
95
|
optparse.parse!(args)
|
88
96
|
connect(options)
|
89
97
|
begin
|
98
|
+
account = nil
|
99
|
+
if options[:account]
|
100
|
+
account = find_account_by_name_or_id(options[:account])
|
101
|
+
if account.nil?
|
102
|
+
return 1
|
103
|
+
else
|
104
|
+
params['accountId'] = account['id']
|
105
|
+
end
|
106
|
+
end
|
90
107
|
group = options[:group] ? find_group_by_name_or_id_for_provisioning(options[:group]) : nil
|
91
108
|
if group
|
92
109
|
params['siteId'] = group['id']
|
@@ -127,6 +144,7 @@ class Morpheus::Cli::Hosts
|
|
127
144
|
return 0
|
128
145
|
else
|
129
146
|
servers = json_response['servers']
|
147
|
+
multi_tenant = json_response['multiTenant'] == true
|
130
148
|
title = "Morpheus Hosts"
|
131
149
|
subtitles = []
|
132
150
|
if group
|
@@ -164,6 +182,7 @@ class Morpheus::Cli::Hosts
|
|
164
182
|
storage_usage_str = !stats ? "" : generate_usage_bar(stats['usedStorage'], stats['maxStorage'], {max_bars: 10})
|
165
183
|
row = {
|
166
184
|
id: server['id'],
|
185
|
+
tenant: server['account'] ? server['account']['name'] : server['accountId'],
|
167
186
|
name: server['name'],
|
168
187
|
platform: server['serverOs'] ? server['serverOs']['name'].upcase : 'N/A',
|
169
188
|
cloud: server['zone'] ? server['zone']['name'] : '',
|
@@ -178,6 +197,9 @@ class Morpheus::Cli::Hosts
|
|
178
197
|
row
|
179
198
|
}
|
180
199
|
columns = [:id, :name, :type, :cloud, :nodes, :status, :power]
|
200
|
+
if multi_tenant
|
201
|
+
columns.insert(4, :tenant)
|
202
|
+
end
|
181
203
|
term_width = current_terminal_width()
|
182
204
|
if term_width > 170
|
183
205
|
columns += [:cpu, :memory, :storage]
|
@@ -232,9 +254,9 @@ class Morpheus::Cli::Hosts
|
|
232
254
|
options = {}
|
233
255
|
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
234
256
|
opts.banner = subcommand_usage("[name]")
|
235
|
-
opts.on('--refresh
|
257
|
+
opts.on('--refresh [status]', String, "Refresh until status is reached. Default status is provisioned.") do |val|
|
236
258
|
if val.to_s.empty?
|
237
|
-
options[:refresh_until_status] = "provisioned"
|
259
|
+
options[:refresh_until_status] = "provisioned,failed"
|
238
260
|
else
|
239
261
|
options[:refresh_until_status] = val.to_s.downcase
|
240
262
|
end
|
@@ -312,10 +334,13 @@ class Morpheus::Cli::Hosts
|
|
312
334
|
if options[:refresh_interval].nil? || options[:refresh_interval].to_f < 0
|
313
335
|
options[:refresh_interval] = 5
|
314
336
|
end
|
315
|
-
|
337
|
+
statuses = options[:refresh_until_status].to_s.downcase.split(",").collect {|s| s.strip }.select {|s| !s.to_s.empty? }
|
338
|
+
if !statuses.include?(server['status'])
|
316
339
|
print cyan
|
317
|
-
print "
|
318
|
-
sleep(options[:refresh_interval])
|
340
|
+
print "Status is #{server['status'] || 'unknown'}. Refreshing in #{options[:refresh_interval]} seconds"
|
341
|
+
#sleep(options[:refresh_interval])
|
342
|
+
sleep_with_dots(options[:refresh_interval])
|
343
|
+
print "\n"
|
319
344
|
_get(arg, options)
|
320
345
|
end
|
321
346
|
end
|
@@ -438,7 +463,7 @@ class Morpheus::Cli::Hosts
|
|
438
463
|
when 'FATAL'
|
439
464
|
log_level = "#{red}#{bold}FATAL#{reset}"
|
440
465
|
end
|
441
|
-
output << "[#{log_entry['ts']}] #{log_level} - #{log_entry['message']}\n"
|
466
|
+
output << "[#{log_entry['ts']}] #{log_level} - #{log_entry['message'].to_s.strip}\n"
|
442
467
|
end
|
443
468
|
end
|
444
469
|
end
|
@@ -655,6 +680,80 @@ class Morpheus::Cli::Hosts
|
|
655
680
|
end
|
656
681
|
end
|
657
682
|
|
683
|
+
def update(args)
|
684
|
+
options = {}
|
685
|
+
params = {}
|
686
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
687
|
+
opts.banner = subcommand_usage("[name]")
|
688
|
+
opts.on('--name VALUE', String, "Name") do |val|
|
689
|
+
params['name'] = val == "null" ? nil : val
|
690
|
+
end
|
691
|
+
opts.on('--description VALUE', String, "Description") do |val|
|
692
|
+
params['description'] = val == "null" ? nil : val
|
693
|
+
end
|
694
|
+
opts.on('--ssh-username VALUE', String, "SSH Username") do |val|
|
695
|
+
params['sshUsername'] = val == "null" ? nil : val
|
696
|
+
end
|
697
|
+
opts.on('--ssh-password VALUE', String, "SSH Password") do |val|
|
698
|
+
params['sshPassword'] = val == "null" ? nil : val
|
699
|
+
end
|
700
|
+
opts.on('--power-schedule-type ID', String, "Power Schedule Type ID") do |val|
|
701
|
+
params['powerScheduleType'] = val == "null" ? nil : val
|
702
|
+
end
|
703
|
+
# opts.on('--created-by ID', String, "Created By User ID") do |val|
|
704
|
+
# params['createdById'] = val
|
705
|
+
# end
|
706
|
+
build_common_options(opts, options, [:options, :payload, :json, :dry_run, :remote])
|
707
|
+
end
|
708
|
+
optparse.parse!(args)
|
709
|
+
if args.count != 1
|
710
|
+
puts optparse
|
711
|
+
return 1
|
712
|
+
end
|
713
|
+
connect(options)
|
714
|
+
|
715
|
+
begin
|
716
|
+
server = find_host_by_name_or_id(args[0])
|
717
|
+
return 1 if server.nil?
|
718
|
+
new_group = nil
|
719
|
+
params.deep_merge!(options[:options].reject {|k,v| k.is_a?(Symbol) }) if options[:options]
|
720
|
+
payload = nil
|
721
|
+
if options[:payload]
|
722
|
+
payload = options[:payload]
|
723
|
+
# support args and option parameters on top of payload
|
724
|
+
if !params.empty?
|
725
|
+
payload['server'] ||= {}
|
726
|
+
payload['server'].deep_merge!(params)
|
727
|
+
end
|
728
|
+
else
|
729
|
+
if params.empty?
|
730
|
+
print_red_alert "Specify atleast one option to update"
|
731
|
+
puts optparse
|
732
|
+
return 1
|
733
|
+
end
|
734
|
+
payload = {}
|
735
|
+
payload['server'] = params
|
736
|
+
end
|
737
|
+
|
738
|
+
if options[:dry_run]
|
739
|
+
print_dry_run @servers_interface.dry.update(server["id"], payload)
|
740
|
+
return
|
741
|
+
end
|
742
|
+
json_response = @servers_interface.update(server["id"], payload)
|
743
|
+
|
744
|
+
if options[:json]
|
745
|
+
puts as_json(json_response, options)
|
746
|
+
else
|
747
|
+
print_green_success "Updated host #{server['name']}"
|
748
|
+
get([server['id']])
|
749
|
+
end
|
750
|
+
return 0
|
751
|
+
rescue RestClient::Exception => e
|
752
|
+
print_rest_exception(e, options)
|
753
|
+
exit 1
|
754
|
+
end
|
755
|
+
end
|
756
|
+
|
658
757
|
def remove(args)
|
659
758
|
options = {}
|
660
759
|
query_params = {}
|
@@ -1033,6 +1132,88 @@ class Morpheus::Cli::Hosts
|
|
1033
1132
|
end
|
1034
1133
|
end
|
1035
1134
|
|
1135
|
+
def execution_request(args)
|
1136
|
+
options = {}
|
1137
|
+
params = {}
|
1138
|
+
script_content = nil
|
1139
|
+
do_refresh = true
|
1140
|
+
optparse = Morpheus::Cli::OptionParser.new do|opts|
|
1141
|
+
opts.banner = subcommand_usage("[id] [options]")
|
1142
|
+
opts.on('--script SCRIPT', "Script to be executed" ) do |val|
|
1143
|
+
script_content = val
|
1144
|
+
end
|
1145
|
+
opts.on('--file FILE', "File containing the script. This can be used instead of --script" ) do |filename|
|
1146
|
+
full_filename = File.expand_path(filename)
|
1147
|
+
if File.exists?(full_filename)
|
1148
|
+
script_content = File.read(full_filename)
|
1149
|
+
else
|
1150
|
+
print_red_alert "File not found: #{full_filename}"
|
1151
|
+
exit 1
|
1152
|
+
end
|
1153
|
+
end
|
1154
|
+
opts.on(nil, '--no-refresh', "Do not refresh until finished" ) do
|
1155
|
+
do_refresh = false
|
1156
|
+
end
|
1157
|
+
#build_option_type_options(opts, options, add_user_source_option_types())
|
1158
|
+
build_common_options(opts, options, [:options, :payload, :json, :dry_run, :quiet, :remote])
|
1159
|
+
opts.footer = "Execute an arbitrary command or script on a host." + "\n" +
|
1160
|
+
"[id] is required. This is the id a host." + "\n" +
|
1161
|
+
"[script] is required. This is the script that is to be executed."
|
1162
|
+
end
|
1163
|
+
optparse.parse!(args)
|
1164
|
+
connect(options)
|
1165
|
+
if args.count != 1
|
1166
|
+
print_error Morpheus::Terminal.angry_prompt
|
1167
|
+
puts_error "wrong number of arguments, expected 1 and got (#{args.count}) #{args.inspect}\n#{optparse}"
|
1168
|
+
return 1
|
1169
|
+
end
|
1170
|
+
|
1171
|
+
|
1172
|
+
begin
|
1173
|
+
host = find_host_by_name_or_id(args[0])
|
1174
|
+
return 1 if host.nil?
|
1175
|
+
params['serverId'] = host['id']
|
1176
|
+
# construct payload
|
1177
|
+
payload = {}
|
1178
|
+
if options[:payload]
|
1179
|
+
payload = options[:payload]
|
1180
|
+
else
|
1181
|
+
payload.deep_merge!(options[:options].reject {|k,v| k.is_a?(Symbol) }) if options[:options]
|
1182
|
+
# prompt for Script
|
1183
|
+
if script_content.nil?
|
1184
|
+
v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'script', 'type' => 'code-editor', 'fieldLabel' => 'Script', 'required' => true, 'description' => 'The script content'}], options[:options])
|
1185
|
+
script_content = v_prompt['script']
|
1186
|
+
end
|
1187
|
+
payload['script'] = script_content
|
1188
|
+
end
|
1189
|
+
# dry run?
|
1190
|
+
if options[:dry_run]
|
1191
|
+
print_dry_run @execution_request_interface.dry.create(params, payload)
|
1192
|
+
return 0
|
1193
|
+
end
|
1194
|
+
# do it
|
1195
|
+
json_response = @execution_request_interface.create(params, payload)
|
1196
|
+
# print and return result
|
1197
|
+
if options[:quiet]
|
1198
|
+
return 0
|
1199
|
+
elsif options[:json]
|
1200
|
+
puts as_json(json_response, options)
|
1201
|
+
return 0
|
1202
|
+
end
|
1203
|
+
execution_request = json_response['executionRequest']
|
1204
|
+
print_green_success "Executing request #{execution_request['uniqueId']}"
|
1205
|
+
if do_refresh
|
1206
|
+
Morpheus::Cli::ExecutionRequestCommand.new.handle(["get", execution_request['uniqueId'], "--refresh"])
|
1207
|
+
else
|
1208
|
+
Morpheus::Cli::ExecutionRequestCommand.new.handle(["get", execution_request['uniqueId']])
|
1209
|
+
end
|
1210
|
+
return 0
|
1211
|
+
rescue RestClient::Exception => e
|
1212
|
+
print_rest_exception(e, options)
|
1213
|
+
exit 1
|
1214
|
+
end
|
1215
|
+
end
|
1216
|
+
|
1036
1217
|
private
|
1037
1218
|
|
1038
1219
|
def find_host_by_id(id)
|
@@ -5,13 +5,16 @@ require 'filesize'
|
|
5
5
|
require 'table_print'
|
6
6
|
require 'morpheus/cli/cli_command'
|
7
7
|
require 'morpheus/cli/mixins/provisioning_helper'
|
8
|
+
require 'morpheus/cli/mixins/processes_helper'
|
8
9
|
require 'morpheus/cli/option_types'
|
9
10
|
|
10
11
|
class Morpheus::Cli::Instances
|
11
12
|
include Morpheus::Cli::CliCommand
|
12
13
|
include Morpheus::Cli::ProvisioningHelper
|
14
|
+
include Morpheus::Cli::ProcessesHelper
|
13
15
|
|
14
|
-
register_subcommands :list, :count, :get, :add, :update, :update_notes, :remove, :logs, :stats, :stop, :start, :restart, :actions, :action, :suspend, :eject, :backup, :backups, :stop_service, :start_service, :restart_service, :resize, :clone, :envs, :setenv, :delenv, :security_groups, :apply_security_groups, :firewall_enable, :firewall_disable, :run_workflow, :import_snapshot, :console, :status_check, {:containers => :list_containers}, :scaling, {:'scaling-update' => :scaling_update}
|
16
|
+
register_subcommands :list, :count, :get, :add, :update, :update_notes, :remove, :logs, :history, {:'history-details' => :history_details}, {:'history-event' => :history_event_details}, :stats, :stop, :start, :restart, :actions, :action, :suspend, :eject, :backup, :backups, :stop_service, :start_service, :restart_service, :resize, :clone, :envs, :setenv, :delenv, :security_groups, :apply_security_groups, :firewall_enable, :firewall_disable, :run_workflow, :import_snapshot, :console, :status_check, {:containers => :list_containers}, :scaling, {:'scaling-update' => :scaling_update}
|
17
|
+
register_subcommands :exec => :execution_request
|
15
18
|
# register_subcommands {:'lb-update' => :load_balancer_update}
|
16
19
|
alias_subcommand :details, :get
|
17
20
|
set_default_subcommand :list
|
@@ -32,6 +35,7 @@ class Morpheus::Cli::Instances
|
|
32
35
|
@provision_types_interface = @api_client.provision_types
|
33
36
|
@options_interface = @api_client.options
|
34
37
|
@active_group_id = Morpheus::Cli::Groups.active_group
|
38
|
+
@execution_request_interface = @api_client.execution_request
|
35
39
|
end
|
36
40
|
|
37
41
|
def handle(args)
|
@@ -229,7 +233,7 @@ class Morpheus::Cli::Instances
|
|
229
233
|
options[:layout_size] = val.to_i
|
230
234
|
end
|
231
235
|
opts.on("--workflow ID", String, "Automation: Workflow ID") do |val|
|
232
|
-
options[:workflow_id] = val
|
236
|
+
options[:workflow_id] = val
|
233
237
|
end
|
234
238
|
# opts.on('-L', "--lb", "Enable Load Balancer") do
|
235
239
|
# options[:enable_load_balancer] = true
|
@@ -314,7 +318,11 @@ class Morpheus::Cli::Instances
|
|
314
318
|
payload['instance']['userGroup'] = {'id' => options[:user_group_id] }
|
315
319
|
end
|
316
320
|
if options[:workflow_id]
|
317
|
-
|
321
|
+
if options[:workflow_id].to_s =~ /\A\d{1,}\Z/
|
322
|
+
payload['taskSetId'] = options[:workflow_id].to_i
|
323
|
+
else
|
324
|
+
payload['taskSetName'] = options[:workflow_id]
|
325
|
+
end
|
318
326
|
end
|
319
327
|
if options[:enable_load_balancer]
|
320
328
|
lb_payload = prompt_instance_load_balancer(payload['instance'], nil, options)
|
@@ -722,7 +730,7 @@ class Morpheus::Cli::Instances
|
|
722
730
|
def get(args)
|
723
731
|
options = {}
|
724
732
|
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
725
|
-
opts.banner = subcommand_usage("[
|
733
|
+
opts.banner = subcommand_usage("[instance]")
|
726
734
|
opts.on( nil, '--containers', "Display Instance Containers" ) do
|
727
735
|
options[:include_containers] = true
|
728
736
|
end
|
@@ -735,9 +743,9 @@ class Morpheus::Cli::Instances
|
|
735
743
|
opts.on( nil, '--scaling', "Display Instance Scaling Settings" ) do
|
736
744
|
options[:include_scaling] = true
|
737
745
|
end
|
738
|
-
opts.on('--refresh
|
746
|
+
opts.on('--refresh [status]', String, "Refresh until status is reached. Default status is running.") do |val|
|
739
747
|
if val.to_s.empty?
|
740
|
-
options[:refresh_until_status] = "running"
|
748
|
+
options[:refresh_until_status] = "running,failed"
|
741
749
|
else
|
742
750
|
options[:refresh_until_status] = val.to_s.downcase
|
743
751
|
end
|
@@ -752,6 +760,8 @@ class Morpheus::Cli::Instances
|
|
752
760
|
# options[:include_lb] = true
|
753
761
|
# end
|
754
762
|
build_common_options(opts, options, [:json, :yaml, :csv, :fields, :dry_run, :remote])
|
763
|
+
opts.footer = "Get details about an instance.\n" +
|
764
|
+
"[instance] is required. This is the name or id of an instance. Supports 1-N [instance] arguments."
|
755
765
|
end
|
756
766
|
optparse.parse!(args)
|
757
767
|
if args.count < 1
|
@@ -945,10 +955,13 @@ class Morpheus::Cli::Instances
|
|
945
955
|
if options[:refresh_interval].nil? || options[:refresh_interval].to_f < 0
|
946
956
|
options[:refresh_interval] = 5
|
947
957
|
end
|
948
|
-
|
958
|
+
statuses = options[:refresh_until_status].to_s.downcase.split(",").collect {|s| s.strip }.select {|s| !s.to_s.empty? }
|
959
|
+
if !statuses.include?(instance['status'])
|
949
960
|
print cyan
|
950
|
-
print "
|
951
|
-
sleep(options[:refresh_interval])
|
961
|
+
print "Status is #{instance['status'] || 'unknown'}. Refreshing in #{options[:refresh_interval]} seconds"
|
962
|
+
#sleep(options[:refresh_interval])
|
963
|
+
sleep_with_dots(options[:refresh_interval])
|
964
|
+
print "\n"
|
952
965
|
_get(arg, options)
|
953
966
|
end
|
954
967
|
end
|
@@ -2434,6 +2447,396 @@ class Morpheus::Cli::Instances
|
|
2434
2447
|
end
|
2435
2448
|
end
|
2436
2449
|
|
2450
|
+
def history(args)
|
2451
|
+
raw_args = args.dup
|
2452
|
+
options = {}
|
2453
|
+
#options[:show_output] = true
|
2454
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
2455
|
+
opts.banner = subcommand_usage("[instance]")
|
2456
|
+
# opts.on( '-n', '--node NODE_ID', "Scope history to specific Container or VM" ) do |node_id|
|
2457
|
+
# options[:node_id] = node_id.to_i
|
2458
|
+
# end
|
2459
|
+
opts.on( nil, '--events', "Display sub processes (events)." ) do
|
2460
|
+
options[:show_events] = true
|
2461
|
+
end
|
2462
|
+
opts.on( nil, '--output', "Display process output." ) do
|
2463
|
+
options[:show_output] = true
|
2464
|
+
end
|
2465
|
+
opts.on('--process-id ID', String, "Display details about a specfic process only." ) do |val|
|
2466
|
+
options[:process_id] = val
|
2467
|
+
end
|
2468
|
+
opts.on('--event-id ID', String, "Display details about a specfic process event only." ) do |val|
|
2469
|
+
options[:event_id] = val
|
2470
|
+
end
|
2471
|
+
build_common_options(opts, options, [:list, :query, :json, :yaml, :csv, :fields, :dry_run, :remote])
|
2472
|
+
opts.footer = "List historical processes for a specific instance.\n" +
|
2473
|
+
"[instance] is required. This is the name or id of an instance."
|
2474
|
+
end
|
2475
|
+
optparse.parse!(args)
|
2476
|
+
|
2477
|
+
# shortcut to other actions
|
2478
|
+
if options[:process_id]
|
2479
|
+
return history_details(raw_args)
|
2480
|
+
elsif options[:event_id]
|
2481
|
+
return history_event_details(raw_args)
|
2482
|
+
end
|
2483
|
+
|
2484
|
+
if args.count != 1
|
2485
|
+
puts optparse
|
2486
|
+
return 1
|
2487
|
+
end
|
2488
|
+
connect(options)
|
2489
|
+
begin
|
2490
|
+
instance = find_instance_by_name_or_id(args[0])
|
2491
|
+
# container_ids = instance['containers']
|
2492
|
+
# if options[:node_id] && container_ids.include?(options[:node_id])
|
2493
|
+
# container_ids = [options[:node_id]]
|
2494
|
+
# end
|
2495
|
+
params = {}
|
2496
|
+
params.merge!(parse_list_options(options))
|
2497
|
+
# params[:query] = params.delete(:phrase) unless params[:phrase].nil?
|
2498
|
+
if options[:dry_run]
|
2499
|
+
print_dry_run @instances_interface.dry.history(instance['id'], params)
|
2500
|
+
return
|
2501
|
+
end
|
2502
|
+
json_response = @instances_interface.history(instance['id'], params)
|
2503
|
+
if options[:json]
|
2504
|
+
puts as_json(json_response, options, "processes")
|
2505
|
+
return 0
|
2506
|
+
elsif options[:yaml]
|
2507
|
+
puts as_yaml(json_response, options, "processes")
|
2508
|
+
return 0
|
2509
|
+
elsif options[:csv]
|
2510
|
+
puts records_as_csv(json_response['processes'], options)
|
2511
|
+
return 0
|
2512
|
+
else
|
2513
|
+
|
2514
|
+
title = "Instance History: #{instance['name']}"
|
2515
|
+
subtitles = []
|
2516
|
+
if params[:query]
|
2517
|
+
subtitles << "Search: #{params[:query]}".strip
|
2518
|
+
end
|
2519
|
+
subtitles += parse_list_subtitles(options)
|
2520
|
+
print_h1 title, subtitles
|
2521
|
+
if json_response['processes'].empty?
|
2522
|
+
print "#{cyan}No process history found.#{reset}\n\n"
|
2523
|
+
else
|
2524
|
+
history_records = []
|
2525
|
+
json_response["processes"].each do |process|
|
2526
|
+
row = {
|
2527
|
+
id: process['id'],
|
2528
|
+
eventId: nil,
|
2529
|
+
uniqueId: process['uniqueId'],
|
2530
|
+
name: process['displayName'],
|
2531
|
+
description: process['description'],
|
2532
|
+
processType: process['processType'] ? (process['processType']['name'] || process['processType']['code']) : process['processTypeName'],
|
2533
|
+
createdBy: process['createdBy'] ? (process['createdBy']['displayName'] || process['createdBy']['username']) : '',
|
2534
|
+
startDate: format_local_dt(process['startDate']),
|
2535
|
+
duration: format_process_duration(process),
|
2536
|
+
status: format_process_status(process),
|
2537
|
+
error: format_process_error(process),
|
2538
|
+
output: format_process_output(process)
|
2539
|
+
}
|
2540
|
+
history_records << row
|
2541
|
+
process_events = process['events'] || process['processEvents']
|
2542
|
+
if options[:show_events]
|
2543
|
+
if process_events
|
2544
|
+
process_events.each do |process_event|
|
2545
|
+
event_row = {
|
2546
|
+
id: process['id'],
|
2547
|
+
eventId: process_event['id'],
|
2548
|
+
uniqueId: process_event['uniqueId'],
|
2549
|
+
name: process_event['displayName'], # blank like the UI
|
2550
|
+
description: process_event['description'],
|
2551
|
+
processType: process_event['processType'] ? (process_event['processType']['name'] || process_event['processType']['code']) : process['processTypeName'],
|
2552
|
+
createdBy: process_event['createdBy'] ? (process_event['createdBy']['displayName'] || process_event['createdBy']['username']) : '',
|
2553
|
+
startDate: format_local_dt(process_event['startDate']),
|
2554
|
+
duration: format_process_duration(process_event),
|
2555
|
+
status: format_process_status(process_event),
|
2556
|
+
error: format_process_error(process_event),
|
2557
|
+
output: format_process_output(process_event)
|
2558
|
+
}
|
2559
|
+
history_records << event_row
|
2560
|
+
end
|
2561
|
+
else
|
2562
|
+
|
2563
|
+
end
|
2564
|
+
end
|
2565
|
+
end
|
2566
|
+
columns = [
|
2567
|
+
{:id => {:display_name => "PROCESS ID"} },
|
2568
|
+
:name,
|
2569
|
+
:description,
|
2570
|
+
{:processType => {:display_name => "PROCESS TYPE"} },
|
2571
|
+
{:createdBy => {:display_name => "CREATED BY"} },
|
2572
|
+
{:startDate => {:display_name => "START DATE"} },
|
2573
|
+
{:duration => {:display_name => "ETA/DURATION"} },
|
2574
|
+
:status,
|
2575
|
+
:error
|
2576
|
+
]
|
2577
|
+
if options[:show_events]
|
2578
|
+
columns.insert(1, {:eventId => {:display_name => "EVENT ID"} })
|
2579
|
+
end
|
2580
|
+
if options[:show_output]
|
2581
|
+
columns << :output
|
2582
|
+
end
|
2583
|
+
# custom pretty table columns ...
|
2584
|
+
if options[:include_fields]
|
2585
|
+
columns = options[:include_fields]
|
2586
|
+
end
|
2587
|
+
print cyan
|
2588
|
+
print as_pretty_table(history_records, columns, options)
|
2589
|
+
#print_results_pagination(json_response)
|
2590
|
+
print_results_pagination(json_response, {:label => "process", :n_label => "processes"})
|
2591
|
+
print reset, "\n"
|
2592
|
+
return 0
|
2593
|
+
end
|
2594
|
+
end
|
2595
|
+
rescue RestClient::Exception => e
|
2596
|
+
print_rest_exception(e, options)
|
2597
|
+
exit 1
|
2598
|
+
end
|
2599
|
+
end
|
2600
|
+
|
2601
|
+
def history_details(args)
|
2602
|
+
options = {}
|
2603
|
+
process_id = nil
|
2604
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
2605
|
+
opts.banner = subcommand_usage("[instance] [process-id]")
|
2606
|
+
opts.on('--process-id ID', String, "Display details about a specfic event." ) do |val|
|
2607
|
+
options[:process_id] = val
|
2608
|
+
end
|
2609
|
+
opts.add_hidden_option('process-id')
|
2610
|
+
build_common_options(opts, options, [:query, :json, :yaml, :csv, :fields, :dry_run, :remote])
|
2611
|
+
opts.footer = "Display history details for a specific process.\n" +
|
2612
|
+
"[instance] is required. This is the name or id of an instance.\n" +
|
2613
|
+
"[process-id] is required. This is the id of the process."
|
2614
|
+
end
|
2615
|
+
optparse.parse!(args)
|
2616
|
+
if args.count == 2
|
2617
|
+
process_id = args[1]
|
2618
|
+
elsif args.count == 1 && options[:process_id]
|
2619
|
+
process_id = options[:process_id]
|
2620
|
+
else
|
2621
|
+
puts_error optparse
|
2622
|
+
return 1
|
2623
|
+
end
|
2624
|
+
connect(options)
|
2625
|
+
begin
|
2626
|
+
instance = find_instance_by_name_or_id(args[0])
|
2627
|
+
params = {}
|
2628
|
+
params.merge!(parse_list_options(options))
|
2629
|
+
params[:query] = params.delete(:phrase) unless params[:phrase].nil?
|
2630
|
+
if options[:dry_run]
|
2631
|
+
print_dry_run @instances_interface.dry.history_details(instance['id'], process_id, params)
|
2632
|
+
return
|
2633
|
+
end
|
2634
|
+
json_response = @instances_interface.history_details(instance['id'], process_id, params)
|
2635
|
+
if options[:json]
|
2636
|
+
puts as_json(json_response, options, "process")
|
2637
|
+
return 0
|
2638
|
+
elsif options[:yaml]
|
2639
|
+
puts as_yaml(json_response, options, "process")
|
2640
|
+
return 0
|
2641
|
+
elsif options[:csv]
|
2642
|
+
puts records_as_csv(json_response['process'], options)
|
2643
|
+
return 0
|
2644
|
+
else
|
2645
|
+
process = json_response["process"]
|
2646
|
+
title = "Instance History Details"
|
2647
|
+
subtitles = []
|
2648
|
+
subtitles << " Process ID: #{process_id}"
|
2649
|
+
subtitles += parse_list_subtitles(options)
|
2650
|
+
print_h1 title, subtitles
|
2651
|
+
print_process_details(process)
|
2652
|
+
|
2653
|
+
print_h2 "Process Events"
|
2654
|
+
process_events = process['events'] || process['processEvents'] || []
|
2655
|
+
history_records = []
|
2656
|
+
if process_events.empty?
|
2657
|
+
puts "#{cyan}No events found.#{reset}"
|
2658
|
+
else
|
2659
|
+
process_events.each do |process_event|
|
2660
|
+
event_row = {
|
2661
|
+
id: process_event['id'],
|
2662
|
+
eventId: process_event['id'],
|
2663
|
+
uniqueId: process_event['uniqueId'],
|
2664
|
+
name: process_event['displayName'], # blank like the UI
|
2665
|
+
description: process_event['description'],
|
2666
|
+
processType: process_event['processType'] ? (process_event['processType']['name'] || process_event['processType']['code']) : process['processTypeName'],
|
2667
|
+
createdBy: process_event['createdBy'] ? (process_event['createdBy']['displayName'] || process_event['createdBy']['username']) : '',
|
2668
|
+
startDate: format_local_dt(process_event['startDate']),
|
2669
|
+
duration: format_process_duration(process_event),
|
2670
|
+
status: format_process_status(process_event),
|
2671
|
+
error: format_process_error(process_event),
|
2672
|
+
output: format_process_output(process_event)
|
2673
|
+
}
|
2674
|
+
history_records << event_row
|
2675
|
+
end
|
2676
|
+
columns = [
|
2677
|
+
{:id => {:display_name => "EVENT ID"} },
|
2678
|
+
:name,
|
2679
|
+
:description,
|
2680
|
+
{:processType => {:display_name => "PROCESS TYPE"} },
|
2681
|
+
{:createdBy => {:display_name => "CREATED BY"} },
|
2682
|
+
{:startDate => {:display_name => "START DATE"} },
|
2683
|
+
{:duration => {:display_name => "ETA/DURATION"} },
|
2684
|
+
:status,
|
2685
|
+
:error,
|
2686
|
+
:output
|
2687
|
+
]
|
2688
|
+
print cyan
|
2689
|
+
print as_pretty_table(history_records, columns, options)
|
2690
|
+
print_results_pagination({size: process_events.size, total: process_events.size})
|
2691
|
+
print reset, "\n"
|
2692
|
+
return 0
|
2693
|
+
end
|
2694
|
+
end
|
2695
|
+
rescue RestClient::Exception => e
|
2696
|
+
print_rest_exception(e, options)
|
2697
|
+
exit 1
|
2698
|
+
end
|
2699
|
+
end
|
2700
|
+
|
2701
|
+
def history_event_details(args)
|
2702
|
+
options = {}
|
2703
|
+
process_event_id = nil
|
2704
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
2705
|
+
opts.banner = subcommand_usage("[instance] [event-id]")
|
2706
|
+
opts.on('--event-id ID', String, "Display details about a specfic event." ) do |val|
|
2707
|
+
options[:event_id] = val
|
2708
|
+
end
|
2709
|
+
opts.add_hidden_option('event-id')
|
2710
|
+
build_common_options(opts, options, [:query, :json, :yaml, :csv, :fields, :dry_run, :remote])
|
2711
|
+
opts.footer = "Display history details for a specific process event.\n" +
|
2712
|
+
"[instance] is required. This is the name or id of an instance.\n" +
|
2713
|
+
"[event-id] is required. This is the id of the process event."
|
2714
|
+
end
|
2715
|
+
optparse.parse!(args)
|
2716
|
+
if args.count == 2
|
2717
|
+
process_event_id = args[1]
|
2718
|
+
elsif args.count == 1 && options[:event_id]
|
2719
|
+
process_event_id = options[:event_id]
|
2720
|
+
else
|
2721
|
+
puts_error optparse
|
2722
|
+
return 1
|
2723
|
+
end
|
2724
|
+
connect(options)
|
2725
|
+
begin
|
2726
|
+
instance = find_instance_by_name_or_id(args[0])
|
2727
|
+
params = {}
|
2728
|
+
params.merge!(parse_list_options(options))
|
2729
|
+
if options[:dry_run]
|
2730
|
+
print_dry_run @instances_interface.dry.history_event_details(instance['id'], process_event_id, params)
|
2731
|
+
return
|
2732
|
+
end
|
2733
|
+
json_response = @instances_interface.history_event_details(instance['id'], process_event_id, params)
|
2734
|
+
if options[:json]
|
2735
|
+
puts as_json(json_response, options, "processEvent")
|
2736
|
+
return 0
|
2737
|
+
elsif options[:yaml]
|
2738
|
+
puts as_yaml(json_response, options, "processEvent")
|
2739
|
+
return 0
|
2740
|
+
elsif options[:csv]
|
2741
|
+
puts records_as_csv(json_response['processEvent'], options)
|
2742
|
+
return 0
|
2743
|
+
else
|
2744
|
+
process_event = json_response['processEvent'] || json_response['event']
|
2745
|
+
title = "Instance History Event"
|
2746
|
+
subtitles = []
|
2747
|
+
subtitles += parse_list_subtitles(options)
|
2748
|
+
print_h1 title, subtitles
|
2749
|
+
print_process_event_details(process_event)
|
2750
|
+
print reset, "\n"
|
2751
|
+
return 0
|
2752
|
+
end
|
2753
|
+
rescue RestClient::Exception => e
|
2754
|
+
print_rest_exception(e, options)
|
2755
|
+
exit 1
|
2756
|
+
end
|
2757
|
+
end
|
2758
|
+
|
2759
|
+
def execution_request(args)
|
2760
|
+
options = {}
|
2761
|
+
params = {}
|
2762
|
+
script_content = nil
|
2763
|
+
do_refresh = true
|
2764
|
+
optparse = Morpheus::Cli::OptionParser.new do|opts|
|
2765
|
+
opts.banner = subcommand_usage("[id] [options]")
|
2766
|
+
opts.on('--script SCRIPT', "Script to be executed" ) do |val|
|
2767
|
+
script_content = val
|
2768
|
+
end
|
2769
|
+
opts.on('--file FILE', "File containing the script. This can be used instead of --script" ) do |filename|
|
2770
|
+
full_filename = File.expand_path(filename)
|
2771
|
+
if File.exists?(full_filename)
|
2772
|
+
script_content = File.read(full_filename)
|
2773
|
+
else
|
2774
|
+
print_red_alert "File not found: #{full_filename}"
|
2775
|
+
exit 1
|
2776
|
+
end
|
2777
|
+
end
|
2778
|
+
opts.on(nil, '--no-refresh', "Do not refresh until finished" ) do
|
2779
|
+
do_refresh = false
|
2780
|
+
end
|
2781
|
+
#build_option_type_options(opts, options, add_user_source_option_types())
|
2782
|
+
build_common_options(opts, options, [:options, :payload, :json, :dry_run, :quiet, :remote])
|
2783
|
+
opts.footer = "Execute an arbitrary script or command on an instance." + "\n" +
|
2784
|
+
"[id] is required. This is the id or name of an instance." + "\n" +
|
2785
|
+
"[script] is required. This is the script that is to be executed."
|
2786
|
+
end
|
2787
|
+
optparse.parse!(args)
|
2788
|
+
connect(options)
|
2789
|
+
if args.count != 1
|
2790
|
+
print_error Morpheus::Terminal.angry_prompt
|
2791
|
+
puts_error "wrong number of arguments, expected 1 and got (#{args.count}) #{args.inspect}\n#{optparse}"
|
2792
|
+
return 1
|
2793
|
+
end
|
2794
|
+
|
2795
|
+
begin
|
2796
|
+
instance = find_instance_by_name_or_id(args[0])
|
2797
|
+
return 1 if instance.nil?
|
2798
|
+
params['instanceId'] = instance['id']
|
2799
|
+
# construct payload
|
2800
|
+
payload = {}
|
2801
|
+
if options[:payload]
|
2802
|
+
payload = options[:payload]
|
2803
|
+
else
|
2804
|
+
payload.deep_merge!(options[:options].reject {|k,v| k.is_a?(Symbol) }) if options[:options]
|
2805
|
+
# prompt for Script
|
2806
|
+
if script_content.nil?
|
2807
|
+
v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'script', 'type' => 'code-editor', 'fieldLabel' => 'Script', 'required' => true, 'description' => 'The script content'}], options[:options])
|
2808
|
+
script_content = v_prompt['script']
|
2809
|
+
end
|
2810
|
+
payload['script'] = script_content
|
2811
|
+
end
|
2812
|
+
# dry run?
|
2813
|
+
if options[:dry_run]
|
2814
|
+
print_dry_run @execution_request_interface.dry.create(params, payload)
|
2815
|
+
return 0
|
2816
|
+
end
|
2817
|
+
# do it
|
2818
|
+
json_response = @execution_request_interface.create(params, payload)
|
2819
|
+
# print and return result
|
2820
|
+
if options[:quiet]
|
2821
|
+
return 0
|
2822
|
+
elsif options[:json]
|
2823
|
+
puts as_json(json_response, options)
|
2824
|
+
return 0
|
2825
|
+
end
|
2826
|
+
execution_request = json_response['executionRequest']
|
2827
|
+
print_green_success "Executing request #{execution_request['uniqueId']}"
|
2828
|
+
if do_refresh
|
2829
|
+
Morpheus::Cli::ExecutionRequestCommand.new.handle(["get", execution_request['uniqueId'], "--refresh"])
|
2830
|
+
else
|
2831
|
+
Morpheus::Cli::ExecutionRequestCommand.new.handle(["get", execution_request['uniqueId']])
|
2832
|
+
end
|
2833
|
+
return 0
|
2834
|
+
rescue RestClient::Exception => e
|
2835
|
+
print_rest_exception(e, options)
|
2836
|
+
exit 1
|
2837
|
+
end
|
2838
|
+
end
|
2839
|
+
|
2437
2840
|
private
|
2438
2841
|
|
2439
2842
|
def find_zone_by_name_or_id(group_id, val)
|
@@ -2661,6 +3064,66 @@ private
|
|
2661
3064
|
print_description_list(description_cols, instance_threshold)
|
2662
3065
|
end
|
2663
3066
|
|
3067
|
+
def print_process_details(process)
|
3068
|
+
description_cols = {
|
3069
|
+
"Process ID" => lambda {|it| it['id'] },
|
3070
|
+
"Name" => lambda {|it| it['displayName'] },
|
3071
|
+
"Description" => lambda {|it| it['description'] },
|
3072
|
+
"Process Type" => lambda {|it| it['processType'] ? (it['processType']['name'] || it['processType']['code']) : it['processTypeName'] },
|
3073
|
+
"Created By" => lambda {|it| it['createdBy'] ? (it['createdBy']['displayName'] || it['createdBy']['username']) : '' },
|
3074
|
+
"Start Date" => lambda {|it| format_local_dt(it['startDate']) },
|
3075
|
+
"End Date" => lambda {|it| format_local_dt(it['endDate']) },
|
3076
|
+
"Duration" => lambda {|it| format_process_duration(it) },
|
3077
|
+
"Status" => lambda {|it| format_process_status(it) },
|
3078
|
+
# "# Events" => lambda {|it| (it['events'] || []).size() },
|
3079
|
+
}
|
3080
|
+
print_description_list(description_cols, process)
|
3081
|
+
|
3082
|
+
if process['error']
|
3083
|
+
print_h2 "Error"
|
3084
|
+
print reset
|
3085
|
+
#puts format_process_error(process_event)
|
3086
|
+
puts process['error'].to_s.strip
|
3087
|
+
end
|
3088
|
+
|
3089
|
+
if process['output']
|
3090
|
+
print_h2 "Output"
|
3091
|
+
print reset
|
3092
|
+
#puts format_process_error(process_event)
|
3093
|
+
puts process['output'].to_s.strip
|
3094
|
+
end
|
3095
|
+
end
|
3096
|
+
|
3097
|
+
def print_process_event_details(process_event)
|
3098
|
+
# process_event =~ process
|
3099
|
+
description_cols = {
|
3100
|
+
"Process ID" => lambda {|it| it['processId'] },
|
3101
|
+
"Event ID" => lambda {|it| it['id'] },
|
3102
|
+
"Name" => lambda {|it| it['displayName'] },
|
3103
|
+
"Description" => lambda {|it| it['description'] },
|
3104
|
+
"Process Type" => lambda {|it| it['processType'] ? (it['processType']['name'] || it['processType']['code']) : it['processTypeName'] },
|
3105
|
+
"Created By" => lambda {|it| it['createdBy'] ? (it['createdBy']['displayName'] || it['createdBy']['username']) : '' },
|
3106
|
+
"Start Date" => lambda {|it| format_local_dt(it['startDate']) },
|
3107
|
+
"End Date" => lambda {|it| format_local_dt(it['endDate']) },
|
3108
|
+
"Duration" => lambda {|it| format_process_duration(it) },
|
3109
|
+
"Status" => lambda {|it| format_process_status(it) },
|
3110
|
+
}
|
3111
|
+
print_description_list(description_cols, process_event)
|
3112
|
+
|
3113
|
+
if process_event['error']
|
3114
|
+
print_h2 "Error"
|
3115
|
+
print reset
|
3116
|
+
#puts format_process_error(process_event)
|
3117
|
+
puts process_event['error'].to_s.strip
|
3118
|
+
end
|
3119
|
+
|
3120
|
+
if process_event['output']
|
3121
|
+
print_h2 "Output"
|
3122
|
+
print reset
|
3123
|
+
#puts format_process_error(process_event)
|
3124
|
+
puts process_event['output'].to_s.strip
|
3125
|
+
end
|
3126
|
+
end
|
2664
3127
|
|
2665
3128
|
|
2666
3129
|
end
|