morpheus-cli 3.6.38 → 4.0.0
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 +8 -0
- data/lib/morpheus/api/network_domain_records_interface.rb +47 -0
- data/lib/morpheus/api/network_pool_ips_interface.rb +47 -0
- data/lib/morpheus/cli/apps.rb +70 -15
- data/lib/morpheus/cli/cli_command.rb +36 -24
- data/lib/morpheus/cli/containers_command.rb +4 -3
- data/lib/morpheus/cli/execution_request_command.rb +3 -2
- data/lib/morpheus/cli/file_copy_request_command.rb +3 -3
- data/lib/morpheus/cli/hosts.rb +154 -30
- data/lib/morpheus/cli/instances.rb +16 -8
- data/lib/morpheus/cli/mixins/print_helper.rb +84 -26
- data/lib/morpheus/cli/mixins/provisioning_helper.rb +99 -33
- data/lib/morpheus/cli/network_domains_command.rb +338 -0
- data/lib/morpheus/cli/network_pools_command.rb +376 -4
- data/lib/morpheus/cli/option_parser.rb +2 -2
- data/lib/morpheus/cli/reports_command.rb +3 -3
- data/lib/morpheus/cli/version.rb +1 -1
- data/lib/morpheus/cli/wiki_command.rb +0 -3
- metadata +4 -2
data/lib/morpheus/cli/hosts.rb
CHANGED
@@ -14,7 +14,7 @@ class Morpheus::Cli::Hosts
|
|
14
14
|
include Morpheus::Cli::ProvisioningHelper
|
15
15
|
set_command_name :hosts
|
16
16
|
set_command_description "View and manage hosts (servers)."
|
17
|
-
register_subcommands :list, :count, :get, :stats, :add, :update, :remove, :logs, :start, :stop, :resize, :run_workflow, {:'make-managed' => :install_agent}, :upgrade_agent
|
17
|
+
register_subcommands :list, :count, :get, :view, :stats, :add, :update, :remove, :logs, :start, :stop, :resize, :run_workflow, {:'make-managed' => :install_agent}, :upgrade_agent
|
18
18
|
register_subcommands :'types' => :list_types
|
19
19
|
register_subcommands :exec => :execution_request
|
20
20
|
register_subcommands :wiki, :update_wiki
|
@@ -359,7 +359,7 @@ class Morpheus::Cli::Hosts
|
|
359
359
|
options = {}
|
360
360
|
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
361
361
|
opts.banner = subcommand_usage("[name]")
|
362
|
-
opts.on('--refresh [SECONDS]', String, "Refresh until status is
|
362
|
+
opts.on('--refresh [SECONDS]', String, "Refresh until status is provisioned,failed. Default interval is #{default_refresh_interval} seconds.") do |val|
|
363
363
|
options[:refresh_until_status] ||= "provisioned,failed"
|
364
364
|
if !val.to_s.empty?
|
365
365
|
options[:refresh_interval] = val.to_f
|
@@ -436,19 +436,34 @@ class Morpheus::Cli::Hosts
|
|
436
436
|
"Power" => lambda {|it| format_server_power_state(it) },
|
437
437
|
}, server)
|
438
438
|
|
439
|
+
if server['statusMessage']
|
440
|
+
print_h2 "Status Message", options
|
441
|
+
if server['status'] == 'failed'
|
442
|
+
print red, server['statusMessage'], reset
|
443
|
+
else
|
444
|
+
print server['statusMessage']
|
445
|
+
end
|
446
|
+
print "\n"
|
447
|
+
end
|
448
|
+
if server['errorMessage']
|
449
|
+
print_h2 "Error Message", options
|
450
|
+
print red, server['errorMessage'], reset, "\n"
|
451
|
+
end
|
452
|
+
|
439
453
|
print_h2 "Host Usage", options
|
440
454
|
print_stats_usage(stats)
|
441
455
|
print reset, "\n"
|
442
456
|
|
457
|
+
|
443
458
|
# refresh until a status is reached
|
444
459
|
if options[:refresh_until_status]
|
445
460
|
if options[:refresh_interval].nil? || options[:refresh_interval].to_f < 0
|
446
|
-
options[:refresh_interval] =
|
461
|
+
options[:refresh_interval] = default_refresh_interval
|
447
462
|
end
|
448
463
|
statuses = options[:refresh_until_status].to_s.downcase.split(",").collect {|s| s.strip }.select {|s| !s.to_s.empty? }
|
449
464
|
if !statuses.include?(server['status'])
|
450
465
|
print cyan
|
451
|
-
print cyan, "Refreshing in #{options[:refresh_interval]} seconds"
|
466
|
+
print cyan, "Refreshing in #{options[:refresh_interval] > 1 ? options[:refresh_interval].to_i : options[:refresh_interval]} seconds"
|
452
467
|
sleep_with_dots(options[:refresh_interval])
|
453
468
|
print "\n"
|
454
469
|
_get(arg, options)
|
@@ -599,14 +614,23 @@ class Morpheus::Cli::Hosts
|
|
599
614
|
opts.on( '-t', '--type TYPE', "Server Type Code" ) do |val|
|
600
615
|
options[:server_type_code] = val
|
601
616
|
end
|
617
|
+
opts.on("--security-groups LIST", Integer, "Security Groups, comma sepearated list of security group IDs") do |val|
|
618
|
+
options[:security_groups] = val.split(",").collect {|s| s.strip }.select {|s| !s.to_s.empty? }
|
619
|
+
end
|
620
|
+
opts.on('--refresh [SECONDS]', String, "Refresh until status is running,failed. Default interval is #{default_refresh_interval} seconds.") do |val|
|
621
|
+
options[:refresh_interval] = val.to_s.empty? ? default_refresh_interval : val.to_f
|
622
|
+
end
|
602
623
|
build_common_options(opts, options, [:options, :payload, :json, :dry_run, :quiet, :remote])
|
603
624
|
end
|
604
625
|
optparse.parse!(args)
|
605
626
|
connect(options)
|
606
627
|
begin
|
628
|
+
passed_options = options[:options] ? options[:options].reject {|k,v| k.is_a?(Symbol) } : {}
|
607
629
|
payload = nil
|
608
630
|
if options[:payload]
|
609
631
|
payload = options[:payload]
|
632
|
+
#payload.deep_merge!({'server' => passed_options}) unless passed_options.empty?
|
633
|
+
payload.deep_merge!(passed_options) unless passed_options.empty?
|
610
634
|
else
|
611
635
|
# support old format of `hosts add CLOUD NAME`
|
612
636
|
if args[0]
|
@@ -619,7 +643,6 @@ class Morpheus::Cli::Hosts
|
|
619
643
|
options[:group] ||= @active_group_id
|
620
644
|
|
621
645
|
params = {}
|
622
|
-
|
623
646
|
# Group
|
624
647
|
group_id = nil
|
625
648
|
group = options[:group] ? find_group_by_name_or_id_for_provisioning(options[:group]) : nil
|
@@ -683,20 +706,45 @@ class Morpheus::Cli::Hosts
|
|
683
706
|
plan_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'plan', 'type' => 'select', 'fieldLabel' => 'Plan', 'selectOptions' => service_plans_dropdown, 'required' => true, 'description' => 'Choose the appropriately sized plan for this server'}],options[:options])
|
684
707
|
service_plan = service_plans.find {|sp| sp["id"] == plan_prompt['plan'].to_i }
|
685
708
|
|
686
|
-
|
709
|
+
# uh ok, this actually expects config at root level, sibling of server
|
710
|
+
# payload.deep_merge!({'server' => passed_options}) unless passed_options.empty?
|
711
|
+
payload.deep_merge!(passed_options) unless passed_options.empty?
|
712
|
+
payload.deep_merge!({'server' => {
|
687
713
|
'name' => host_name,
|
688
714
|
'zone' => {'id' => cloud['id']},
|
689
715
|
'computeServerType' => {'id' => server_type['id']},
|
690
716
|
'plan' => {'id' => service_plan["id"]}
|
691
|
-
|
717
|
+
}
|
718
|
+
})
|
719
|
+
|
720
|
+
option_type_list = server_type['optionTypes']
|
721
|
+
# remove volume options if volumes were configured
|
722
|
+
if !payload['volumes'].empty?
|
723
|
+
option_type_list = reject_volume_option_types(option_type_list)
|
724
|
+
end
|
725
|
+
# remove networkId option if networks were configured above
|
726
|
+
if !payload['networkInterfaces'].empty?
|
727
|
+
option_type_list = reject_networking_option_types(option_type_list)
|
728
|
+
end
|
729
|
+
|
730
|
+
# remove cpu and memory option types, which now come from the plan
|
731
|
+
option_type_list = reject_service_plan_option_types(option_type_list)
|
692
732
|
|
693
733
|
# prompt for resource pool
|
734
|
+
pool_id = nil
|
694
735
|
has_zone_pools = server_type["provisionType"] && server_type["provisionType"]["hasZonePools"]
|
695
736
|
if has_zone_pools
|
696
|
-
|
697
|
-
|
698
|
-
|
699
|
-
|
737
|
+
# pluck out the resourcePoolId option type to prompt for..why the heck is this even needed?
|
738
|
+
resource_pool_option_type = option_type_list.find {|opt| ['resourcePool','resourcePoolId','azureResourceGroupId'].include?(opt['fieldName']) }
|
739
|
+
option_type_list = option_type_list.reject {|opt| ['resourcePool','resourcePoolId','azureResourceGroupId'].include?(opt['fieldName']) }
|
740
|
+
resource_pool_option_type ||= {'fieldContext' => 'config', 'fieldName' => 'resourcePool', 'type' => 'select', 'fieldLabel' => 'Resource Pool', 'optionSource' => 'zonePools', 'required' => true, 'skipSingleOption' => true, 'description' => 'Select resource pool.'}
|
741
|
+
resource_pool_prompt = Morpheus::Cli::OptionTypes.prompt([resource_pool_option_type],options[:options],api_client,{groupId: group_id, siteId: group_id, zoneId: cloud_id, cloudId: cloud_id, planId: service_plan["id"], serverTypeId: server_type['id']})
|
742
|
+
resource_pool_prompt.deep_compact!
|
743
|
+
payload.deep_merge!(resource_pool_prompt)
|
744
|
+
if resource_pool_option_type['fieldContext'] && resource_pool_prompt[resource_pool_option_type['fieldContext']]
|
745
|
+
pool_id = resource_pool_prompt[resource_pool_option_type['fieldContext']][resource_pool_option_type['fieldName']]
|
746
|
+
elsif resource_pool_prompt[resource_pool_option_type['fieldName']]
|
747
|
+
pool_id = resource_pool_prompt[resource_pool_option_type['fieldName']]
|
700
748
|
end
|
701
749
|
end
|
702
750
|
|
@@ -709,7 +757,7 @@ class Morpheus::Cli::Hosts
|
|
709
757
|
# prompt for network interfaces (if supported)
|
710
758
|
if server_type["provisionType"] && server_type["provisionType"]["id"] && server_type["provisionType"]["hasNetworks"]
|
711
759
|
begin
|
712
|
-
network_interfaces = prompt_network_interfaces(cloud['id'], server_type["provisionType"]["id"], options)
|
760
|
+
network_interfaces = prompt_network_interfaces(cloud['id'], server_type["provisionType"]["id"], pool_id, options)
|
713
761
|
if !network_interfaces.empty?
|
714
762
|
payload['networkInterfaces'] = network_interfaces
|
715
763
|
end
|
@@ -719,23 +767,36 @@ class Morpheus::Cli::Hosts
|
|
719
767
|
end
|
720
768
|
end
|
721
769
|
|
722
|
-
|
723
|
-
#
|
724
|
-
|
725
|
-
|
770
|
+
# Security Groups
|
771
|
+
# prompt for multiple security groups
|
772
|
+
sg_option_type = option_type_list.find {|opt| ((opt['code'] == 'provisionType.amazon.securityId') || (opt['name'] == 'securityId')) }
|
773
|
+
option_type_list = option_type_list.reject {|opt| ((opt['code'] == 'provisionType.amazon.securityId') || (opt['name'] == 'securityId')) }
|
774
|
+
# ok.. seed data has changed and serverTypes do not have this optionType anymore...
|
775
|
+
if sg_option_type.nil?
|
776
|
+
if server_type["provisionType"] && (server_type["provisionType"]["code"] == 'amazon')
|
777
|
+
sg_option_type = {'fieldContext' => 'config', 'fieldName' => 'securityId', 'type' => 'select', 'fieldLabel' => 'Security Group', 'optionSource' => 'amazonSecurityGroup', 'required' => true, 'description' => 'Select security group.'}
|
778
|
+
end
|
726
779
|
end
|
727
|
-
|
728
|
-
if
|
729
|
-
|
780
|
+
has_security_groups = !!sg_option_type
|
781
|
+
if options[:security_groups]
|
782
|
+
payload['securityGroups'] = options[:security_groups].collect {|sg_id| {'id' => sg_id} }
|
783
|
+
else
|
784
|
+
if has_security_groups
|
785
|
+
security_groups_array = prompt_security_groups(sg_option_type, {zoneId: cloud_id, poolId: pool_id}, options)
|
786
|
+
if !security_groups_array.empty?
|
787
|
+
payload['securityGroups'] = security_groups_array.collect {|sg_id| {'id' => sg_id} }
|
788
|
+
end
|
789
|
+
end
|
730
790
|
end
|
731
|
-
|
732
|
-
|
733
|
-
|
791
|
+
|
792
|
+
api_params = {}
|
793
|
+
api_params['zoneId'] = cloud['id']
|
794
|
+
api_params['poolId'] = payload['config']['resourcePool'] if (payload['config'] && payload['config']['resourcePool'])
|
795
|
+
if payload['config']
|
796
|
+
api_params.deep_merge!(payload['config'])
|
734
797
|
end
|
735
|
-
#
|
736
|
-
|
737
|
-
|
738
|
-
params = Morpheus::Cli::OptionTypes.prompt(server_type_option_types,options[:options],@api_client, {zoneId: cloud['id']})
|
798
|
+
#api_params.deep_merge(payload)
|
799
|
+
params = Morpheus::Cli::OptionTypes.prompt(option_type_list,options[:options],@api_client, api_params)
|
739
800
|
payload.deep_merge!(params)
|
740
801
|
|
741
802
|
end
|
@@ -749,8 +810,12 @@ class Morpheus::Cli::Hosts
|
|
749
810
|
print JSON.pretty_generate(json_response)
|
750
811
|
print "\n"
|
751
812
|
elsif !options[:quiet]
|
752
|
-
|
753
|
-
|
813
|
+
server_id = json_response["server"]["id"]
|
814
|
+
server_name = json_response["server"]["name"]
|
815
|
+
print_green_success "Provisioning host [#{server_id}] #{server_name}"
|
816
|
+
# print details
|
817
|
+
get_args = [server_id] + (options[:remote] ? ["-r",options[:remote]] : []) + (options[:refresh_interval] ? ['--refresh', options[:refresh_interval].to_s] : [])
|
818
|
+
get(get_args)
|
754
819
|
end
|
755
820
|
rescue RestClient::Exception => e
|
756
821
|
print_rest_exception(e, options)
|
@@ -794,7 +859,8 @@ class Morpheus::Cli::Hosts
|
|
794
859
|
server = find_host_by_name_or_id(args[0])
|
795
860
|
return 1 if server.nil?
|
796
861
|
new_group = nil
|
797
|
-
|
862
|
+
passed_options = options[:options] ? options[:options].reject {|k,v| k.is_a?(Symbol) } : {}
|
863
|
+
params.deep_merge!(passed_options) unless passed_options.empty?
|
798
864
|
payload = nil
|
799
865
|
if options[:payload]
|
800
866
|
payload = options[:payload]
|
@@ -1039,7 +1105,7 @@ class Morpheus::Cli::Hosts
|
|
1039
1105
|
# prompt for network interfaces (if supported)
|
1040
1106
|
# if server_type["provisionType"] && server_type["provisionType"]["id"] && server_type["provisionType"]["hasNetworks"]
|
1041
1107
|
# begin
|
1042
|
-
# network_interfaces = prompt_network_interfaces(cloud['id'], server_type["provisionType"]["id"], options)
|
1108
|
+
# network_interfaces = prompt_network_interfaces(cloud['id'], server_type["provisionType"]["id"], null, options)
|
1043
1109
|
# if !network_interfaces.empty?
|
1044
1110
|
# payload[:networkInterfaces] = network_interfaces
|
1045
1111
|
# end
|
@@ -1410,6 +1476,64 @@ class Morpheus::Cli::Hosts
|
|
1410
1476
|
end
|
1411
1477
|
end
|
1412
1478
|
|
1479
|
+
def view(args)
|
1480
|
+
options = {}
|
1481
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
1482
|
+
opts.banner = subcommand_usage("[host]")
|
1483
|
+
opts.on('-w','--wiki', "Open the wiki tab for this host") do
|
1484
|
+
options[:link_tab] = "wiki"
|
1485
|
+
end
|
1486
|
+
opts.on('--tab VALUE', String, "Open a specific tab") do |val|
|
1487
|
+
options[:link_tab] = val.to_s
|
1488
|
+
end
|
1489
|
+
build_common_options(opts, options, [:dry_run, :remote])
|
1490
|
+
opts.footer = "View a host in a web browser" + "\n" +
|
1491
|
+
"[host] is required. This is the name or id of a host. Supports 1-N [host] arguments."
|
1492
|
+
end
|
1493
|
+
optparse.parse!(args)
|
1494
|
+
if args.count != 1
|
1495
|
+
raise_command_error "wrong number of arguments, expected 1 and got (#{args.count}) #{args.join(' ')}\n#{optparse}"
|
1496
|
+
end
|
1497
|
+
connect(options)
|
1498
|
+
id_list = parse_id_list(args)
|
1499
|
+
return run_command_for_each_arg(id_list) do |arg|
|
1500
|
+
_view(arg, options)
|
1501
|
+
end
|
1502
|
+
end
|
1503
|
+
|
1504
|
+
def _view(arg, options={})
|
1505
|
+
begin
|
1506
|
+
host = find_host_by_name_or_id(arg)
|
1507
|
+
return 1 if host.nil?
|
1508
|
+
|
1509
|
+
link = "#{@appliance_url}/login/oauth-redirect?access_token=#{@access_token}\\&redirectUri=/infrastructure/servers/#{host['id']}"
|
1510
|
+
if options[:link_tab]
|
1511
|
+
link << "#!#{options[:link_tab]}"
|
1512
|
+
end
|
1513
|
+
|
1514
|
+
open_command = nil
|
1515
|
+
if RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/
|
1516
|
+
open_command = "start #{link}"
|
1517
|
+
elsif RbConfig::CONFIG['host_os'] =~ /darwin/
|
1518
|
+
open_command = "open #{link}"
|
1519
|
+
elsif RbConfig::CONFIG['host_os'] =~ /linux|bsd/
|
1520
|
+
open_command = "xdg-open #{link}"
|
1521
|
+
end
|
1522
|
+
|
1523
|
+
if options[:dry_run]
|
1524
|
+
puts "system: #{open_command}"
|
1525
|
+
return 0
|
1526
|
+
end
|
1527
|
+
|
1528
|
+
system(open_command)
|
1529
|
+
|
1530
|
+
return 0
|
1531
|
+
rescue RestClient::Exception => e
|
1532
|
+
print_rest_exception(e, options)
|
1533
|
+
exit 1
|
1534
|
+
end
|
1535
|
+
end
|
1536
|
+
|
1413
1537
|
def wiki(args)
|
1414
1538
|
options = {}
|
1415
1539
|
params = {}
|
@@ -22,7 +22,7 @@ class Morpheus::Cli::Instances
|
|
22
22
|
# register_subcommands {:'lb-update' => :load_balancer_update}
|
23
23
|
alias_subcommand :details, :get
|
24
24
|
set_default_subcommand :list
|
25
|
-
|
25
|
+
|
26
26
|
def initialize()
|
27
27
|
#@appliance_name, @appliance_url = Morpheus::Cli::Remote.active_appliance
|
28
28
|
end
|
@@ -333,6 +333,12 @@ class Morpheus::Cli::Instances
|
|
333
333
|
opts.on("--create-backup [on|off]", String, "Automation: Create Backups.") do |val|
|
334
334
|
options[:create_backup] = ['on','true','1',''].include?(val.to_s.downcase) ? 'on' : 'off'
|
335
335
|
end
|
336
|
+
opts.on("--security-groups LIST", Integer, "Security Groups, comma sepearated list of security group IDs") do |val|
|
337
|
+
options[:security_groups] = val.split(",").collect {|s| s.strip }.select {|s| !s.to_s.empty? }
|
338
|
+
end
|
339
|
+
opts.on('--refresh [SECONDS]', String, "Refresh until status is running,failed. Default interval is #{default_refresh_interval} seconds.") do |val|
|
340
|
+
options[:refresh_interval] = val.to_s.empty? ? default_refresh_interval : val.to_f
|
341
|
+
end
|
336
342
|
build_common_options(opts, options, [:options, :payload, :json, :dry_run, :remote, :quiet])
|
337
343
|
opts.footer = "Create a new instance." + "\n" +
|
338
344
|
"[name] is required. This is the new instance name." + "\n" +
|
@@ -427,7 +433,9 @@ class Morpheus::Cli::Instances
|
|
427
433
|
instance_id = json_response["instance"]["id"]
|
428
434
|
instance_name = json_response["instance"]["name"]
|
429
435
|
print_green_success "Provisioning instance [#{instance_id}] #{instance_name}"
|
430
|
-
|
436
|
+
# print details
|
437
|
+
get_args = [instance_id] + (options[:remote] ? ["-r",options[:remote]] : []) + (options[:refresh_interval] ? ['--refresh', options[:refresh_interval].to_s] : [])
|
438
|
+
get(get_args)
|
431
439
|
end
|
432
440
|
return 0
|
433
441
|
rescue RestClient::Exception => e
|
@@ -547,7 +555,6 @@ class Morpheus::Cli::Instances
|
|
547
555
|
end
|
548
556
|
|
549
557
|
def update_notes(args)
|
550
|
-
print_error "#{yellow}DEPRECATION WARNING: `instances update-notes` is deprecated in 4.0, use `instances update-wiki` instead.#{reset}\n"
|
551
558
|
usage = "Usage: morpheus instances update-notes [instance] [options]"
|
552
559
|
options = {}
|
553
560
|
params = {}
|
@@ -580,7 +587,7 @@ class Morpheus::Cli::Instances
|
|
580
587
|
return 1
|
581
588
|
end
|
582
589
|
connect(options)
|
583
|
-
|
590
|
+
print_error "#{yellow}DEPRECATION WARNING: `instances update-notes` is deprecated in 4.0, use `instances update-wiki` instead.#{reset}\n"
|
584
591
|
begin
|
585
592
|
instance = find_instance_by_name_or_id(args[0])
|
586
593
|
return 1 if instance.nil?
|
@@ -1092,7 +1099,7 @@ class Morpheus::Cli::Instances
|
|
1092
1099
|
opts.on( nil, '--scaling', "Display Instance Scaling Settings" ) do
|
1093
1100
|
options[:include_scaling] = true
|
1094
1101
|
end
|
1095
|
-
opts.on('--refresh [SECONDS]', String, "Refresh until status is running,failed. Default interval is
|
1102
|
+
opts.on('--refresh [SECONDS]', String, "Refresh until status is running,failed. Default interval is #{default_refresh_interval} seconds.") do |val|
|
1096
1103
|
options[:refresh_until_status] ||= "running,failed"
|
1097
1104
|
if !val.to_s.empty?
|
1098
1105
|
options[:refresh_interval] = val.to_f
|
@@ -1279,6 +1286,7 @@ class Morpheus::Cli::Instances
|
|
1279
1286
|
status: format_container_status(container),
|
1280
1287
|
name: container['server'] ? container['server']['name'] : '(no server)', # there is a server.displayName too?
|
1281
1288
|
type: container['containerType'] ? container['containerType']['name'] : '',
|
1289
|
+
host: container['server'] ? container['server']['name'] : '',
|
1282
1290
|
cloud: container['cloud'] ? container['cloud']['name'] : '',
|
1283
1291
|
location: format_container_connection_string(container),
|
1284
1292
|
cpu: cpu_usage_str + cyan,
|
@@ -1287,7 +1295,7 @@ class Morpheus::Cli::Instances
|
|
1287
1295
|
}
|
1288
1296
|
row
|
1289
1297
|
}
|
1290
|
-
columns = [:id, :status, :name, :type, :cloud, :location, :cpu, :memory, :storage]
|
1298
|
+
columns = [:id, :status, :name, :type, :cloud, :host, :location, :cpu, :memory, :storage]
|
1291
1299
|
# custom pretty table columns ...
|
1292
1300
|
if options[:include_fields]
|
1293
1301
|
columns = options[:include_fields]
|
@@ -1314,11 +1322,11 @@ class Morpheus::Cli::Instances
|
|
1314
1322
|
# refresh until a status is reached
|
1315
1323
|
if options[:refresh_until_status]
|
1316
1324
|
if options[:refresh_interval].nil? || options[:refresh_interval].to_f < 0
|
1317
|
-
options[:refresh_interval] =
|
1325
|
+
options[:refresh_interval] = default_refresh_interval
|
1318
1326
|
end
|
1319
1327
|
statuses = options[:refresh_until_status].to_s.downcase.split(",").collect {|s| s.strip }.select {|s| !s.to_s.empty? }
|
1320
1328
|
if !statuses.include?(instance['status'])
|
1321
|
-
print cyan, "Refreshing in #{options[:refresh_interval]} seconds"
|
1329
|
+
print cyan, "Refreshing in #{options[:refresh_interval] > 1 ? options[:refresh_interval].to_i : options[:refresh_interval]} seconds"
|
1322
1330
|
sleep_with_dots(options[:refresh_interval])
|
1323
1331
|
print "\n"
|
1324
1332
|
_get(arg, options)
|
@@ -5,6 +5,7 @@ require 'yaml'
|
|
5
5
|
require 'ostruct'
|
6
6
|
require 'io/console'
|
7
7
|
require 'morpheus/logging'
|
8
|
+
require 'fileutils'
|
8
9
|
|
9
10
|
module Morpheus::Cli::PrintHelper
|
10
11
|
|
@@ -144,9 +145,9 @@ module Morpheus::Cli::PrintHelper
|
|
144
145
|
params = nil
|
145
146
|
if api_request[:params] && !api_request[:params].empty?
|
146
147
|
params = api_request[:params]
|
147
|
-
elsif
|
148
|
+
elsif headers && headers[:params]
|
148
149
|
# params inside headers for restclient reasons..
|
149
|
-
params =
|
150
|
+
params = headers[:params]
|
150
151
|
elsif api_request[:query] && !api_request[:query].empty?
|
151
152
|
params = api_request[:query]
|
152
153
|
end
|
@@ -159,8 +160,54 @@ module Morpheus::Cli::PrintHelper
|
|
159
160
|
end
|
160
161
|
request_string = "#{http_method.to_s.upcase} #{url}".strip
|
161
162
|
payload = api_request[:payload] || api_request[:body]
|
162
|
-
|
163
163
|
#Morpheus::Logging::DarkPrinter.puts "API payload is: (#{payload.class}) #{payload.inspect}"
|
164
|
+
content_type = (headers && headers['Content-Type']) ? headers['Content-Type'] : 'application/x-www-form-urlencoded'
|
165
|
+
|
166
|
+
# write to a file?
|
167
|
+
if options[:outfile]
|
168
|
+
output = ""
|
169
|
+
if api_request[:curl] || options[:curl]
|
170
|
+
output = format_curl_command(http_method, url, headers, payload, options)
|
171
|
+
else
|
172
|
+
# body payload
|
173
|
+
output = payload
|
174
|
+
if content_type == 'application/json'
|
175
|
+
if payload.is_a?(String)
|
176
|
+
begin
|
177
|
+
payload = JSON.parse(payload)
|
178
|
+
rescue => e
|
179
|
+
#payload = "(unparsable) #{payload}"
|
180
|
+
end
|
181
|
+
end
|
182
|
+
output = JSON.pretty_generate(payload)
|
183
|
+
else
|
184
|
+
if payload.is_a?(File)
|
185
|
+
pretty_size = "#{payload.size} B"
|
186
|
+
output = "File: #{payload.path} (#{pretty_size})"
|
187
|
+
elsif payload.is_a?(String)
|
188
|
+
output = payload
|
189
|
+
else
|
190
|
+
if content_type == 'application/x-www-form-urlencoded'
|
191
|
+
body_str = payload.to_s
|
192
|
+
begin
|
193
|
+
body_str = URI.encode_www_form(payload)
|
194
|
+
rescue => ex
|
195
|
+
# raise ex
|
196
|
+
end
|
197
|
+
output = body_str
|
198
|
+
else
|
199
|
+
begin
|
200
|
+
output = JSON.pretty_generate(payload)
|
201
|
+
rescue
|
202
|
+
output = payload.to_s
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
208
|
+
print_result = print_to_file(output, options[:outfile], options[:overwrite])
|
209
|
+
return print_result
|
210
|
+
end
|
164
211
|
|
165
212
|
# curl output?
|
166
213
|
if api_request[:curl] || options[:curl]
|
@@ -171,30 +218,14 @@ module Morpheus::Cli::PrintHelper
|
|
171
218
|
return
|
172
219
|
end
|
173
220
|
|
174
|
-
# print this thing:
|
175
|
-
# REQUEST:
|
176
|
-
# POST http://morpheusdata.com/api/whoami
|
177
|
-
# f it..removing this, just DRY RUN printed at the start of command
|
178
221
|
print "\n"
|
179
|
-
# if command_string != false
|
180
|
-
# if command_string
|
181
|
-
# print_h1 "DRY RUN > #{command_string}"
|
182
|
-
# else
|
183
|
-
# #print "\n"
|
184
|
-
# #print_h1 "DRY RUN"
|
185
|
-
# end
|
186
|
-
# end
|
187
222
|
puts "#{cyan}#{bold}#{dark}REQUEST#{reset}\n"
|
188
|
-
# print cyan
|
189
|
-
# print "Request: ", "\n"
|
190
|
-
# print reset
|
191
223
|
request_string = "#{http_method.to_s.upcase} #{url}".strip
|
192
224
|
print request_string, "\n"
|
193
225
|
print cyan
|
194
226
|
if payload
|
195
|
-
|
196
227
|
print "\n"
|
197
|
-
if
|
228
|
+
if content_type == 'application/json'
|
198
229
|
if payload.is_a?(String)
|
199
230
|
begin
|
200
231
|
payload = JSON.parse(payload)
|
@@ -203,18 +234,12 @@ module Morpheus::Cli::PrintHelper
|
|
203
234
|
end
|
204
235
|
end
|
205
236
|
puts "#{cyan}#{bold}#{dark}JSON#{reset}\n"
|
206
|
-
# print "JSON: ", "\n"
|
207
|
-
# print reset
|
208
237
|
print JSON.pretty_generate(payload)
|
209
238
|
else
|
210
|
-
content_type = api_request[:headers]['Content-Type'] || 'application/x-www-form-urlencoded'
|
211
239
|
print "Content-Type: #{content_type}", "\n"
|
212
|
-
# print "Body: ", "\n"
|
213
240
|
print reset
|
214
241
|
if payload.is_a?(File)
|
215
|
-
# pretty_size = Filesize.from("#{payload.size} B").pretty.strip
|
216
242
|
pretty_size = "#{payload.size} B"
|
217
|
-
# print "File: #{payload.path} (#{payload.size} bytes)"
|
218
243
|
print "File: #{payload.path} (#{pretty_size})"
|
219
244
|
elsif payload.is_a?(String)
|
220
245
|
print payload
|
@@ -1039,4 +1064,37 @@ module Morpheus::Cli::PrintHelper
|
|
1039
1064
|
end
|
1040
1065
|
end
|
1041
1066
|
|
1067
|
+
def print_to_file(txt, filename, overwrite=false, access_mode = 'w+')
|
1068
|
+
Morpheus::Logging::DarkPrinter.puts "Writing #{txt.to_s.bytesize} bytes to file #{filename} ..." if Morpheus::Logging.debug?
|
1069
|
+
outfile = nil
|
1070
|
+
begin
|
1071
|
+
full_filename = File.expand_path(filename)
|
1072
|
+
if File.exists?(full_filename)
|
1073
|
+
if !overwrite
|
1074
|
+
print "#{red}Output file '#{filename}' already exists.#{reset}\n"
|
1075
|
+
print "#{red}Use --overwrite to overwrite the existing file.#{reset}\n"
|
1076
|
+
return 1
|
1077
|
+
end
|
1078
|
+
end
|
1079
|
+
if Dir.exists?(full_filename)
|
1080
|
+
print "#{red}Output file '#{filename}' is invalid. It is the name of an existing directory.#{reset}\n"
|
1081
|
+
return 1
|
1082
|
+
end
|
1083
|
+
target_dir = File.dirname(full_filename)
|
1084
|
+
if !Dir.exists?(target_dir)
|
1085
|
+
FileUtils.mkdir_p(target_dir)
|
1086
|
+
end
|
1087
|
+
outfile = File.open(full_filename, access_mode)
|
1088
|
+
outfile.print(txt)
|
1089
|
+
print "#{cyan}Wrote #{txt.to_s.bytesize} bytes to file #{filename}\n"
|
1090
|
+
return 0
|
1091
|
+
rescue => ex
|
1092
|
+
# puts_error "Error writing to outfile '#{filename}'. Error: #{ex}"
|
1093
|
+
print "#{red}Error writing to file '#{filename}'. Error: #{ex}#{reset}\n"
|
1094
|
+
return 1
|
1095
|
+
ensure
|
1096
|
+
outfile.close if outfile
|
1097
|
+
end
|
1098
|
+
end
|
1099
|
+
|
1042
1100
|
end
|