morpheus-cli 5.5.3.2 → 6.0.1
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/Dockerfile +1 -1
- data/lib/morpheus/api/api_client.rb +8 -0
- data/lib/morpheus/api/cloud_resource_pools_interface.rb +28 -3
- data/lib/morpheus/api/containers_interface.rb +10 -0
- data/lib/morpheus/api/doc_interface.rb +1 -10
- data/lib/morpheus/api/jobs_interface.rb +2 -2
- data/lib/morpheus/api/key_pairs_interface.rb +9 -0
- data/lib/morpheus/api/network_floating_ips_interface.rb +37 -0
- data/lib/morpheus/api/resource_pool_groups_interface.rb +51 -0
- data/lib/morpheus/cli/cli_command.rb +17 -11
- data/lib/morpheus/cli/commands/appliance_settings_command.rb +5 -0
- data/lib/morpheus/cli/commands/apps.rb +12 -6
- data/lib/morpheus/cli/commands/catalog_item_types_command.rb +44 -12
- data/lib/morpheus/cli/commands/clusters.rb +23 -2
- data/lib/morpheus/cli/commands/containers_command.rb +129 -4
- data/lib/morpheus/cli/commands/doc.rb +14 -13
- data/lib/morpheus/cli/commands/hosts.rb +2 -0
- data/lib/morpheus/cli/commands/instances.rb +9 -3
- data/lib/morpheus/cli/commands/jobs_command.rb +50 -3
- data/lib/morpheus/cli/commands/key_pairs.rb +94 -33
- data/lib/morpheus/cli/commands/network_floating_ips.rb +109 -0
- data/lib/morpheus/cli/commands/reports_command.rb +8 -1
- data/lib/morpheus/cli/commands/resource_pool_groups_command.rb +586 -0
- data/lib/morpheus/cli/commands/roles.rb +10 -10
- data/lib/morpheus/cli/commands/service_catalog_command.rb +40 -2
- data/lib/morpheus/cli/commands/service_plans_command.rb +51 -22
- data/lib/morpheus/cli/commands/shell.rb +1 -1
- data/lib/morpheus/cli/commands/tasks.rb +130 -35
- data/lib/morpheus/cli/commands/workflows.rb +109 -23
- data/lib/morpheus/cli/mixins/infrastructure_helper.rb +148 -0
- data/lib/morpheus/cli/mixins/jobs_helper.rb +30 -3
- data/lib/morpheus/cli/mixins/processes_helper.rb +2 -26
- data/lib/morpheus/cli/mixins/provisioning_helper.rb +2 -1
- data/lib/morpheus/cli/option_types.rb +2 -2
- data/lib/morpheus/cli/version.rb +1 -1
- data/test/cli/doc_test.rb +1 -1
- metadata +6 -2
@@ -8,7 +8,7 @@ class Morpheus::Cli::ContainersCommand
|
|
8
8
|
set_command_name :containers
|
9
9
|
set_command_description "View and manage containers (nodes)."
|
10
10
|
register_subcommands :get, :stop, :start, :restart, :suspend, :eject, :action, :actions, :logs,
|
11
|
-
{:exec => :execution_request}, :clone_image, :import
|
11
|
+
{:exec => :execution_request}, :clone_image, :import, :attach_floating_ip, :detach_floating_ip
|
12
12
|
|
13
13
|
def connect(opts)
|
14
14
|
@api_client = establish_remote_appliance_connection(opts)
|
@@ -17,6 +17,7 @@ class Morpheus::Cli::ContainersCommand
|
|
17
17
|
@provision_types_interface = @api_client.provision_types
|
18
18
|
@logs_interface = @api_client.logs
|
19
19
|
@execution_request_interface = @api_client.execution_request
|
20
|
+
@clouds_interface = @api_client.clouds
|
20
21
|
end
|
21
22
|
|
22
23
|
def handle(args)
|
@@ -696,7 +697,7 @@ EOT
|
|
696
697
|
end
|
697
698
|
json_response = @containers_interface.import(container['id'], payload)
|
698
699
|
render_response(json_response, options) do
|
699
|
-
print_green_success "Import initiated for container
|
700
|
+
print_green_success "Import initiated for container #{container['id']}"
|
700
701
|
end
|
701
702
|
return 0, nil
|
702
703
|
end
|
@@ -768,12 +769,121 @@ EOT
|
|
768
769
|
end
|
769
770
|
json_response = @containers_interface.clone_image(container['id'], payload)
|
770
771
|
render_response(json_response, options) do
|
771
|
-
print_green_success "Clone Image initiated for container
|
772
|
+
print_green_success "Clone Image initiated for container #{container['id']}"
|
772
773
|
end
|
773
774
|
return 0, nil
|
774
775
|
end
|
775
776
|
|
776
|
-
|
777
|
+
def attach_floating_ip(args)
|
778
|
+
options = {}
|
779
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
780
|
+
opts.banner = subcommand_usage("[id]")
|
781
|
+
opts.on('--ip ID', String, "Floating IP Address, in the format 'ip-ID'.") do |val|
|
782
|
+
options[:ip] = val
|
783
|
+
end
|
784
|
+
opts.on('--pool ID', String, "Floating IP Pool Identifier, in the format 'pool-ID'.") do |val|
|
785
|
+
options[:pool] = val
|
786
|
+
end
|
787
|
+
opts.on('--bandwidth VALUE', String, "Floating IP Bandidth (Mbit/s). Only cloud types Huawei and OpenTelekom support this option.") do |val|
|
788
|
+
options[:bandwidth] = val
|
789
|
+
end
|
790
|
+
build_standard_update_options(opts, options, [:auto_confirm])
|
791
|
+
opts.footer = <<-EOT
|
792
|
+
Attach a floating IP to a container.
|
793
|
+
[id] is required. This is the id of a container.
|
794
|
+
Only the following cloud types support this command: OpenStack, Huawei and OpenTelekom
|
795
|
+
EOT
|
796
|
+
end
|
797
|
+
optparse.parse!(args)
|
798
|
+
verify_args!(args:args, optparse:optparse, count:1)
|
799
|
+
connect(options)
|
800
|
+
container = find_container_by_id(args[0])
|
801
|
+
return 1 if container.nil?
|
802
|
+
cloud_type = load_container_cloud_type(container)
|
803
|
+
if !cloud_type['hasFloatingIps']
|
804
|
+
raise_command_error "Cloud Type #{cloud_type['name']} does support floating IPs."
|
805
|
+
end
|
806
|
+
payload = parse_payload(options)
|
807
|
+
if payload.nil?
|
808
|
+
payload = parse_passed_options(options)
|
809
|
+
attach_floating_ip_option_types = cloud_type['floatingIpTypes']
|
810
|
+
if attach_floating_ip_option_types && !attach_floating_ip_option_types.empty?
|
811
|
+
if options[:ip]
|
812
|
+
floating_ip = options[:ip].to_s.sub(/\Aip\-/i, '')
|
813
|
+
floating_ip = (floating_ip =~ /\A\d{1,}\Z/) ? "ip-#{floating_ip.to_s}" : floating_ip
|
814
|
+
options[:options]['config'] ||= {}
|
815
|
+
options[:options]['config']['osExternalNetworkId'] = floating_ip
|
816
|
+
elsif options[:pool]
|
817
|
+
floating_ip = options[:pool].to_s.sub(/\Apool\-/i, '')
|
818
|
+
floating_ip = (floating_ip =~ /\A\d{1,}\Z/) ? "pool-#{floating_ip.to_s}" : floating_ip
|
819
|
+
options[:options]['config'] ||= {}
|
820
|
+
options[:options]['config']['osExternalNetworkId'] = floating_ip
|
821
|
+
end
|
822
|
+
if options[:bandwidth]
|
823
|
+
options[:options]['config'] ||= {}
|
824
|
+
options[:options]['config']['floatingIpBandwidth'] = options[:bandwidth].to_i
|
825
|
+
end
|
826
|
+
#api_params = {zoneId: container['cloud'] ? container['cloud']['id'] : nil, resourcePoolId: container['resourcePool'] ? container['resourcePool']['id'] : nil}
|
827
|
+
api_params = {containerId: container['id']}
|
828
|
+
v_prompt = Morpheus::Cli::OptionTypes.prompt(attach_floating_ip_option_types, options[:options], @api_client, api_params)
|
829
|
+
# payload.deep_merge!({'container' => v_prompt})
|
830
|
+
payload.deep_merge!(v_prompt)
|
831
|
+
else
|
832
|
+
# raise_command_error "Cloud Type #{cloud_type['name']} does not defined any floating IP inputs."
|
833
|
+
end
|
834
|
+
end
|
835
|
+
confirm!("Are you sure you would like to attach this floating IP to container #{container['id']}?", options)
|
836
|
+
@containers_interface.setopts(options)
|
837
|
+
if options[:dry_run]
|
838
|
+
print_dry_run @containers_interface.dry.attach_floating_ip(container['id'], payload)
|
839
|
+
return
|
840
|
+
end
|
841
|
+
json_response = @containers_interface.attach_floating_ip(container['id'], payload)
|
842
|
+
render_response(json_response, options) do
|
843
|
+
print_green_success "Attaching floating IP to container #{container['id']}"
|
844
|
+
end
|
845
|
+
return 0, nil
|
846
|
+
end
|
847
|
+
|
848
|
+
def detach_floating_ip(args)
|
849
|
+
options = {}
|
850
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
851
|
+
opts.banner = subcommand_usage("[id]")
|
852
|
+
build_standard_update_options(opts, options, [:auto_confirm])
|
853
|
+
opts.footer = <<-EOT
|
854
|
+
Detach a floating IP from a container.
|
855
|
+
[id] is required. This is the id of a container.
|
856
|
+
Only the following cloud types support this command: OpenStack, Huawei and OpenTelekom
|
857
|
+
EOT
|
858
|
+
end
|
859
|
+
optparse.parse!(args)
|
860
|
+
verify_args!(args:args, optparse:optparse, count:1)
|
861
|
+
connect(options)
|
862
|
+
container = find_container_by_id(args[0])
|
863
|
+
return 1 if container.nil?
|
864
|
+
cloud_type = load_container_cloud_type(container)
|
865
|
+
if !cloud_type['hasFloatingIps']
|
866
|
+
raise_command_error "Cloud Type #{cloud_type['name']} does support floating IPs."
|
867
|
+
end
|
868
|
+
payload = parse_payload(options)
|
869
|
+
if payload.nil?
|
870
|
+
payload = parse_passed_options(options)
|
871
|
+
# prompt
|
872
|
+
end
|
873
|
+
confirm!("Are you sure you would like to detach the floating IP from container #{container['id']}?", options)
|
874
|
+
@containers_interface.setopts(options)
|
875
|
+
if options[:dry_run]
|
876
|
+
print_dry_run @containers_interface.dry.detach_floating_ip(container['id'], payload)
|
877
|
+
return
|
878
|
+
end
|
879
|
+
json_response = @containers_interface.detach_floating_ip(container['id'], payload)
|
880
|
+
render_response(json_response, options) do
|
881
|
+
print_green_success "Detaching floating IP from container #{container['id']}"
|
882
|
+
end
|
883
|
+
return 0, nil
|
884
|
+
end
|
885
|
+
|
886
|
+
private
|
777
887
|
|
778
888
|
def find_container_by_id(id)
|
779
889
|
begin
|
@@ -837,4 +947,19 @@ private
|
|
837
947
|
end
|
838
948
|
return provision_type
|
839
949
|
end
|
950
|
+
|
951
|
+
def load_container_cloud_type(container)
|
952
|
+
cloud_type_code = container['cloud']['type'] rescue nil
|
953
|
+
cloud_type = nil
|
954
|
+
if cloud_type_code
|
955
|
+
cloud_type = @clouds_interface.cloud_types({code:cloud_type_code})['zoneTypes'][0]
|
956
|
+
if cloud_type.nil?
|
957
|
+
raise_command_error "Cloud Type not found by code #{cloud_type_code}"
|
958
|
+
end
|
959
|
+
else
|
960
|
+
raise_command_error "Unable to determine cloud type for container #{container['id']}"
|
961
|
+
end
|
962
|
+
return cloud_type
|
963
|
+
end
|
964
|
+
|
840
965
|
end
|
@@ -22,7 +22,8 @@ class Morpheus::Cli::Doc
|
|
22
22
|
end
|
23
23
|
|
24
24
|
def connect(options)
|
25
|
-
@api_client = establish_remote_appliance_connection(options.merge({:no_prompt => true, :skip_verify_access_token => true, :skip_login => true}))
|
25
|
+
# @api_client = establish_remote_appliance_connection(options.merge({:no_prompt => true, :skip_verify_access_token => true, :skip_login => true}))
|
26
|
+
@api_client = establish_remote_appliance_connection(options)
|
26
27
|
@doc_interface = @api_client.doc
|
27
28
|
end
|
28
29
|
|
@@ -72,12 +73,12 @@ EOT
|
|
72
73
|
params, options = {}, {}
|
73
74
|
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
74
75
|
opts.banner = subcommand_usage()
|
75
|
-
opts.on(nil, "--refresh", "Refresh the document. By default the openapi.yml and openapi.json are cached by the server.") do
|
76
|
-
|
77
|
-
end
|
78
|
-
opts.on('-g', '--generate', "Alias for --refresh") do
|
79
|
-
|
80
|
-
end
|
76
|
+
# opts.on(nil, "--refresh", "Refresh the document. By default the openapi.yml and openapi.json are cached by the server.") do
|
77
|
+
# params['refresh'] = true
|
78
|
+
# end
|
79
|
+
# opts.on('-g', '--generate', "Alias for --refresh") do
|
80
|
+
# params['refresh'] = true
|
81
|
+
# end
|
81
82
|
build_standard_get_options(opts, options, [], [:csv])
|
82
83
|
opts.footer = <<-EOT
|
83
84
|
Print the Morpheus API OpenAPI Documentation (swagger).
|
@@ -121,12 +122,12 @@ EOT
|
|
121
122
|
options[:yaml] = true
|
122
123
|
options[:format] = :yaml
|
123
124
|
end
|
124
|
-
opts.on(nil, "--refresh", "Refresh the document. By default the openapi.yml and openapi.json are cached by the server.") do
|
125
|
-
|
126
|
-
end
|
127
|
-
opts.on('-g', '--generate', "Alias for --refresh") do
|
128
|
-
|
129
|
-
end
|
125
|
+
# opts.on(nil, "--refresh", "Refresh the document. By default the openapi.yml and openapi.json are cached by the server.") do
|
126
|
+
# params['refresh'] = true
|
127
|
+
# end
|
128
|
+
# opts.on('-g', '--generate', "Alias for --refresh") do
|
129
|
+
# params['refresh'] = true
|
130
|
+
# end
|
130
131
|
opts.on( '-f', '--force', "Overwrite existing [local-file] if it exists." ) do
|
131
132
|
options[:overwrite] = true
|
132
133
|
end
|
@@ -2936,9 +2936,15 @@ EOT
|
|
2936
2936
|
opts.on('--preserve-volumes [on|off]', ['on','off'], "Preserve Volumes. Default is off. Applies to certain types only.") do |val|
|
2937
2937
|
query_params[:preserveVolumes] = val.nil? ? 'on' : val
|
2938
2938
|
end
|
2939
|
-
opts.on('--
|
2940
|
-
query_params[:
|
2939
|
+
opts.on('--release-ips [on|off]', ['on','off'], "Release Floating IPs. Default is on. Applies to certain types only.") do |val|
|
2940
|
+
query_params[:releaseFloatingIps] = val.nil? ? 'on' : val
|
2941
|
+
query_params[:releaseEIPs] = query_params[:releaseFloatingIps] # old parameter before 6.0
|
2941
2942
|
end
|
2943
|
+
opts.on('--releaseEIPs [on|off]', ['on','off'], "Alias for Release Floating IPs") do |val|
|
2944
|
+
query_params[:releaseFloatingIps] = val.nil? ? 'on' : val
|
2945
|
+
query_params[:releaseEIPs] = query_params[:releaseFloatingIps] # old parameter before 6.0
|
2946
|
+
end
|
2947
|
+
opts.add_hidden_option('--releaseEIPs')
|
2942
2948
|
opts.on( '-f', '--force', "Force Delete" ) do
|
2943
2949
|
query_params[:force] = 'on'
|
2944
2950
|
end
|
@@ -4940,7 +4946,7 @@ EOT
|
|
4940
4946
|
end
|
4941
4947
|
|
4942
4948
|
def apply(args)
|
4943
|
-
default_refresh_interval =
|
4949
|
+
default_refresh_interval = 5
|
4944
4950
|
params, payload, options = {}, {}, {}
|
4945
4951
|
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
4946
4952
|
opts.banner = subcommand_usage("[instance] [options]")
|
@@ -280,17 +280,37 @@ class Morpheus::Cli::JobsCommand
|
|
280
280
|
end
|
281
281
|
options[:type] = 'morpheus.securityScan'
|
282
282
|
end
|
283
|
-
opts.on('--context-type [TYPE]', String, "Context type (instance|server|none). Default is none") do |val|
|
283
|
+
opts.on('--context-type [TYPE]', String, "Context type (instance|instance-label|server|server-label|none). Default is none") do |val|
|
284
|
+
val = val.to_s.downcase
|
284
285
|
params['targetType'] = (val == 'none' ? 'appliance' : val)
|
285
286
|
end
|
287
|
+
opts.on('--target-type [TYPE]', String, "alias for --context-type") do |val|
|
288
|
+
val = val.to_s.downcase
|
289
|
+
params['targetType'] = (val == 'none' ? 'appliance' : val)
|
290
|
+
end
|
291
|
+
opts.add_hidden_option('--target-type')
|
286
292
|
opts.on('--instances [LIST]', Array, "Context instances(s), comma separated list of instance IDs. Incompatible with --servers") do |list|
|
287
293
|
params['targetType'] = 'instance'
|
288
294
|
params['targets'] = list.collect {|it| it.to_s.strip.empty? ? nil : it.to_s.strip }.compact.uniq.collect {|it| {'refId' => it.to_i}}
|
289
295
|
end
|
296
|
+
opts.on('--instance-label LABEL', String, "Instance Label") do |val|
|
297
|
+
if params['targetType'] && params['targetType'] != 'instance-label'
|
298
|
+
raise ::OptionParser::InvalidOption.new("cannot be combined with another context (#{params['targetType']})")
|
299
|
+
end
|
300
|
+
params['targetType'] = 'instance-label'
|
301
|
+
params['instanceLabel'] = val
|
302
|
+
end
|
290
303
|
opts.on('--servers [LIST]', Array, "Context server(s), comma separated list of server IDs. Incompatible with --instances") do |list|
|
291
304
|
params['targetType'] = 'server'
|
292
305
|
params['targets'] = list.collect {|it| it.to_s.strip.empty? ? nil : it.to_s.strip }.compact.uniq.collect {|it| {'refId' => it.to_i}}
|
293
306
|
end
|
307
|
+
opts.on('--server-label LABEL', String, "Server Label") do |val|
|
308
|
+
if params['targetType'] && params['targetType'] != 'server-label'
|
309
|
+
raise ::OptionParser::InvalidOption.new("cannot be combined with another context (#{params['targetType']})")
|
310
|
+
end
|
311
|
+
params['targetType'] = 'server-label'
|
312
|
+
params['serverLabel'] = val
|
313
|
+
end
|
294
314
|
opts.on('-S', '--schedule [SCHEDULE]', String, "Job execution schedule type name or ID") do |val|
|
295
315
|
options[:schedule] = val
|
296
316
|
end
|
@@ -465,8 +485,15 @@ class Morpheus::Cli::JobsCommand
|
|
465
485
|
end
|
466
486
|
end
|
467
487
|
params['targets'] = targets.collect {|it| {'refId' => it}}
|
488
|
+
elsif params['targetType'] == 'instance-label'
|
489
|
+
if params['instanceLabel'].nil?
|
490
|
+
params['instanceLabel'] = Morpheus::Cli::OptionTypes.prompt([{'switch' => 'instance-label', 'fieldName' => 'instanceLabel', 'fieldLabel' => 'Instance Label', 'type' => 'text', 'required' => true, 'description' => 'Instance Label'}], options[:options], @api_client)['instanceLabel']
|
491
|
+
end
|
492
|
+
elsif params['targetType'] == 'server-label'
|
493
|
+
if params['serverLabel'].nil?
|
494
|
+
params['serverLabel'] = Morpheus::Cli::OptionTypes.prompt([{'switch' => 'server-label', 'fieldName' => 'serverLabel', 'fieldLabel' => 'Server Label', 'type' => 'text', 'required' => true, 'description' => 'Server Label'}], options[:options], @api_client)['serverLabel']
|
495
|
+
end
|
468
496
|
end
|
469
|
-
|
470
497
|
# schedule
|
471
498
|
if options[:schedule].nil?
|
472
499
|
options[:schedule] = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'scheduleMode', 'fieldLabel' => "Schedule", 'type' => 'select', 'required' => true, 'selectOptions' => job_options['schedules'], 'defaultValue' => job_options['schedules'].first['name']}], options[:options], @api_client, {})['scheduleMode']
|
@@ -554,17 +581,37 @@ class Morpheus::Cli::JobsCommand
|
|
554
581
|
options[:security_package] = val
|
555
582
|
end
|
556
583
|
end
|
557
|
-
opts.on('--context-type [TYPE]', String, "Context type (instance|server|none). Default is none") do |val|
|
584
|
+
opts.on('--context-type [TYPE]', String, "Context type (instance|instance-label|server|server-label|none). Default is none") do |val|
|
585
|
+
val = val.to_s.downcase
|
586
|
+
params['targetType'] = (val == 'none' ? 'appliance' : val)
|
587
|
+
end
|
588
|
+
opts.on('--target-type [TYPE]', String, "alias for --context-type") do |val|
|
589
|
+
val = val.to_s.downcase
|
558
590
|
params['targetType'] = (val == 'none' ? 'appliance' : val)
|
559
591
|
end
|
592
|
+
opts.add_hidden_option('--target-type')
|
560
593
|
opts.on('--instances [LIST]', Array, "Context instances(s), comma separated list of instance IDs. Incompatible with --servers") do |list|
|
561
594
|
params['targetType'] = 'instance'
|
562
595
|
options[:targets] = list.collect {|it| it.to_s.strip.empty? ? nil : it.to_s.strip.to_i }.compact.uniq.collect {|it| {'refId' => it.to_i}}
|
563
596
|
end
|
597
|
+
opts.on('--instance-label LABEL', String, "Instance Label") do |val|
|
598
|
+
if params['targetType'] && params['targetType'] != 'instance-label'
|
599
|
+
raise ::OptionParser::InvalidOption.new("cannot be combined with another context (#{params['targetType']})")
|
600
|
+
end
|
601
|
+
params['targetType'] = 'instance-label'
|
602
|
+
params['instanceLabel'] = val
|
603
|
+
end
|
564
604
|
opts.on('--servers [LIST]', Array, "Context server(s), comma separated list of server IDs. Incompatible with --instances") do |list|
|
565
605
|
params['targetType'] = 'server'
|
566
606
|
options[:targets] = list.collect {|it| it.to_s.strip.empty? ? nil : it.to_s.strip.to_i }.compact.uniq.collect {|it| {'refId' => it.to_i}}
|
567
607
|
end
|
608
|
+
opts.on('--server-label LABEL', String, "Server Label") do |val|
|
609
|
+
if params['targetType'] && params['targetType'] != 'server-label'
|
610
|
+
raise ::OptionParser::InvalidOption.new("cannot be combined with another context (#{params['targetType']})")
|
611
|
+
end
|
612
|
+
params['targetType'] = 'server-label'
|
613
|
+
params['serverLabel'] = val
|
614
|
+
end
|
568
615
|
opts.on('--schedule [SCHEDULE]', String, "Job execution schedule type name or ID") do |val|
|
569
616
|
options[:schedule] = val
|
570
617
|
end
|
@@ -3,7 +3,7 @@ require 'morpheus/cli/cli_command'
|
|
3
3
|
class Morpheus::Cli::KeyPairs
|
4
4
|
include Morpheus::Cli::CliCommand
|
5
5
|
include Morpheus::Cli::AccountsHelper
|
6
|
-
register_subcommands :list, :get, :add, :update, :remove
|
6
|
+
register_subcommands :list, :get, :add, :update, :remove, :generate
|
7
7
|
alias_subcommand :details, :get
|
8
8
|
|
9
9
|
def initialize()
|
@@ -102,38 +102,7 @@ class Morpheus::Cli::KeyPairs
|
|
102
102
|
return 0 if render_result
|
103
103
|
|
104
104
|
unless options[:quiet]
|
105
|
-
|
106
|
-
print cyan
|
107
|
-
if account
|
108
|
-
# print_description_list({"Account" => lambda {|it| it['account'] ? it['account']['name'] : '' } }, key_pair)
|
109
|
-
print_description_list({"Account" => lambda {|it| account['name'] } }, key_pair)
|
110
|
-
end
|
111
|
-
print_description_list({
|
112
|
-
"ID" => 'id',
|
113
|
-
"Name" => 'name',
|
114
|
-
"Fingerprint" => 'fingerprint',
|
115
|
-
#"MD5" => 'md5',
|
116
|
-
# "Has Private Key" => lambda {|it| format_boolean(it['hasPrivateKey']) },
|
117
|
-
# "Account" => lambda {|it| it['account'] ? it['account']['name'] : '' },
|
118
|
-
"Created" => lambda {|it| format_local_dt(it['dateCreated']) }
|
119
|
-
#"Updated" => lambda {|it| format_local_dt(it['lastUpdated']) }
|
120
|
-
}, key_pair)
|
121
|
-
|
122
|
-
print_h2 "Public Key"
|
123
|
-
print cyan
|
124
|
-
puts "#{key_pair['publicKey']}"
|
125
|
-
|
126
|
-
if key_pair['hasPrivateKey']
|
127
|
-
# print_h2 "Private Key"
|
128
|
-
# print cyan
|
129
|
-
# puts "(hidden)"
|
130
|
-
else
|
131
|
-
# print_h2 "Private Key"
|
132
|
-
print cyan, "\n"
|
133
|
-
puts "This is only a public key. It does not include a private key."
|
134
|
-
end
|
135
|
-
|
136
|
-
print reset,"\n"
|
105
|
+
print_key_pair_details(account, key_pair)
|
137
106
|
end
|
138
107
|
rescue RestClient::Exception => e
|
139
108
|
print_rest_exception(e, options)
|
@@ -180,6 +149,9 @@ class Morpheus::Cli::KeyPairs
|
|
180
149
|
end
|
181
150
|
|
182
151
|
build_common_options(opts, options, [:account, :options, :json, :dry_run, :remote])
|
152
|
+
opts.footer = <<-EOT
|
153
|
+
Add a key pair.
|
154
|
+
EOT
|
183
155
|
end
|
184
156
|
optparse.parse!(args)
|
185
157
|
# if args.count < 1
|
@@ -237,6 +209,10 @@ class Morpheus::Cli::KeyPairs
|
|
237
209
|
opts.banner = subcommand_usage("[name] [options]")
|
238
210
|
build_option_type_options(opts, options, update_key_pair_option_types)
|
239
211
|
build_common_options(opts, options, [:account, :options, :json, :dry_run, :remote])
|
212
|
+
opts.footer = <<-EOT
|
213
|
+
Update a key pair.
|
214
|
+
[name] is required. This is the name or id of a key pair.
|
215
|
+
EOT
|
240
216
|
end
|
241
217
|
optparse.parse!(args)
|
242
218
|
|
@@ -290,6 +266,10 @@ class Morpheus::Cli::KeyPairs
|
|
290
266
|
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
291
267
|
opts.banner = subcommand_usage("[name]")
|
292
268
|
build_common_options(opts, options, [:account, :auto_confirm, :json, :dry_run, :remote])
|
269
|
+
opts.footer = <<-EOT
|
270
|
+
Delete a key pair.
|
271
|
+
[name] is required. This is the name or id of a key pair.
|
272
|
+
EOT
|
293
273
|
end
|
294
274
|
optparse.parse!(args)
|
295
275
|
|
@@ -329,6 +309,50 @@ class Morpheus::Cli::KeyPairs
|
|
329
309
|
end
|
330
310
|
end
|
331
311
|
|
312
|
+
def generate(args)
|
313
|
+
options = {}
|
314
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
315
|
+
opts.banner = subcommand_usage("[name]")
|
316
|
+
build_common_options(opts, options, [:account, :options, :json, :dry_run, :remote])
|
317
|
+
opts.footer = <<-EOT
|
318
|
+
Generate a key pair.
|
319
|
+
EOT
|
320
|
+
end
|
321
|
+
optparse.parse!(args)
|
322
|
+
# if args.count < 1
|
323
|
+
# puts optparse
|
324
|
+
# exit 1
|
325
|
+
# end
|
326
|
+
if args[0]
|
327
|
+
options[:options] ||= {}
|
328
|
+
options[:options]['name'] ||= args[0]
|
329
|
+
end
|
330
|
+
connect(options)
|
331
|
+
begin
|
332
|
+
account = find_account_from_options(options)
|
333
|
+
account_id = account ? account['id'] : nil
|
334
|
+
params = Morpheus::Cli::OptionTypes.prompt(add_key_pair_option_types.select {|it| ['name'].include?(it['fieldName'])}, options[:options], @api_client, options[:params])
|
335
|
+
key_pair_payload = params.select {|k,v| ['name'].include?(k) }
|
336
|
+
payload = {keyPair: key_pair_payload}
|
337
|
+
@key_pairs_interface.setopts(options)
|
338
|
+
if options[:dry_run]
|
339
|
+
print_dry_run @key_pairs_interface.dry.generate(account_id, payload)
|
340
|
+
return
|
341
|
+
end
|
342
|
+
json_response = @key_pairs_interface.generate(account_id, payload)
|
343
|
+
if options[:json]
|
344
|
+
print JSON.pretty_generate(json_response)
|
345
|
+
print "\n"
|
346
|
+
else
|
347
|
+
print_green_success "Key Pair #{key_pair_payload['name']} added"
|
348
|
+
print_key_pair_details(account, json_response['keyPair'])
|
349
|
+
end
|
350
|
+
rescue RestClient::Exception => e
|
351
|
+
print_rest_exception(e, options)
|
352
|
+
exit 1
|
353
|
+
end
|
354
|
+
end
|
355
|
+
|
332
356
|
private
|
333
357
|
def find_key_pair_by_name_or_id(account_id, val)
|
334
358
|
if val.to_s =~ /\A\d{1,}\Z/
|
@@ -390,6 +414,43 @@ class Morpheus::Cli::KeyPairs
|
|
390
414
|
print reset
|
391
415
|
end
|
392
416
|
|
417
|
+
def print_key_pair_details(account, key_pair)
|
418
|
+
print_h1 "Key Pair Details"
|
419
|
+
print cyan
|
420
|
+
if account
|
421
|
+
# print_description_list({"Account" => lambda {|it| it['account'] ? it['account']['name'] : '' } }, key_pair)
|
422
|
+
print_description_list({"Account" => lambda {|it| account['name'] } }, key_pair)
|
423
|
+
end
|
424
|
+
print_description_list({
|
425
|
+
"ID" => 'id',
|
426
|
+
"Name" => 'name',
|
427
|
+
"Fingerprint" => 'fingerprint',
|
428
|
+
#"MD5" => 'md5',
|
429
|
+
# "Has Private Key" => lambda {|it| format_boolean(it['hasPrivateKey']) },
|
430
|
+
# "Account" => lambda {|it| it['account'] ? it['account']['name'] : '' },
|
431
|
+
"Created" => lambda {|it| format_local_dt(it['dateCreated']) }
|
432
|
+
#"Updated" => lambda {|it| format_local_dt(it['lastUpdated']) }
|
433
|
+
}, key_pair)
|
434
|
+
|
435
|
+
print_h2 "Public Key"
|
436
|
+
print cyan
|
437
|
+
puts "#{key_pair['publicKey']}"
|
438
|
+
|
439
|
+
# only happens after generate
|
440
|
+
if key_pair['hasPrivateKey']
|
441
|
+
if key_pair['privateKey']
|
442
|
+
print_h2 "Private Key"
|
443
|
+
print cyan
|
444
|
+
puts "#{key_pair['privateKey']}"
|
445
|
+
end
|
446
|
+
else
|
447
|
+
# print_h2 "Private Key"
|
448
|
+
print cyan, "\n"
|
449
|
+
puts "This is only a public key. It does not include a private key."
|
450
|
+
end
|
451
|
+
|
452
|
+
print reset,"\n"
|
453
|
+
end
|
393
454
|
|
394
455
|
def add_key_pair_option_types
|
395
456
|
[
|
@@ -0,0 +1,109 @@
|
|
1
|
+
require 'morpheus/cli/cli_command'
|
2
|
+
|
3
|
+
class Morpheus::Cli::NetworkFloatingIps
|
4
|
+
include Morpheus::Cli::CliCommand
|
5
|
+
include Morpheus::Cli::RestCommand
|
6
|
+
|
7
|
+
set_command_name :'network-floating-ips'
|
8
|
+
set_command_description "View and manage network floating IPs."
|
9
|
+
register_subcommands :list, :get, :release
|
10
|
+
|
11
|
+
# RestCommand settings
|
12
|
+
register_interfaces :network_floating_ips
|
13
|
+
set_rest_has_name false
|
14
|
+
set_rest_arg "id"
|
15
|
+
|
16
|
+
def release(args)
|
17
|
+
params = {}
|
18
|
+
options = {}
|
19
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
20
|
+
opts.banner = subcommand_usage("[#{rest_arg}]")
|
21
|
+
build_standard_remove_options(opts, options)
|
22
|
+
opts.footer = <<-EOT
|
23
|
+
Release an existing #{rest_label.downcase}.
|
24
|
+
[#{rest_arg}] is required. This is the #{rest_has_name ? 'name or id' : 'id'} of #{a_or_an(rest_label)} #{rest_label.downcase}.
|
25
|
+
Only the following cloud types support this command: OpenStack, Huawei and OpenTelekom
|
26
|
+
EOT
|
27
|
+
end
|
28
|
+
optparse.parse!(args)
|
29
|
+
verify_args!(args:args, optparse:optparse, count:1)
|
30
|
+
connect(options)
|
31
|
+
params.merge!(parse_query_options(options))
|
32
|
+
id = args[0]
|
33
|
+
record = rest_find_by_name_or_id(id)
|
34
|
+
if record.nil?
|
35
|
+
return 1, "#{rest_name} not found for '#{id}'"
|
36
|
+
end
|
37
|
+
unless options[:yes] || Morpheus::Cli::OptionTypes.confirm("Are you sure you want to release the #{rest_label.downcase} #{record['name'] || record['id']}?")
|
38
|
+
return 9, "aborted"
|
39
|
+
end
|
40
|
+
rest_interface.setopts(options)
|
41
|
+
if options[:dry_run]
|
42
|
+
print_dry_run rest_interface.dry.release(record['id'], params)
|
43
|
+
return 0, nil
|
44
|
+
end
|
45
|
+
json_response = rest_interface.release(record['id'], params)
|
46
|
+
render_response(json_response, options) do
|
47
|
+
print_green_success "Releasing #{rest_label.downcase} #{record['ipAddress'] || record['id']}"
|
48
|
+
end
|
49
|
+
return 0, nil
|
50
|
+
end
|
51
|
+
|
52
|
+
protected
|
53
|
+
|
54
|
+
def build_list_options(opts, options, params)
|
55
|
+
opts.on('--cloud CLOUD', String, "Cloud Name or ID") do |val|
|
56
|
+
options[:cloud] = val
|
57
|
+
end
|
58
|
+
opts.on('--server SERVER', String, "Server Name or ID") do |val|
|
59
|
+
options[:server] = val
|
60
|
+
end
|
61
|
+
opts.on('--vm VM', String, "Alias for --server") do |val|
|
62
|
+
options[:server] = val
|
63
|
+
end
|
64
|
+
opts.add_hidden_option('--vm')
|
65
|
+
opts.on('--ip-address VALUE', String, "Filter by IP Address") do |val|
|
66
|
+
add_query_parameter(params, 'ipAddress', val)
|
67
|
+
end
|
68
|
+
opts.on('--status VALUE', String, "Filter by Status (free, assigned, pending)") do |val|
|
69
|
+
add_query_parameter(params, 'ipStatus', val)
|
70
|
+
end
|
71
|
+
# build_standard_list_options(opts, options)
|
72
|
+
super
|
73
|
+
end
|
74
|
+
|
75
|
+
def parse_list_options!(args, options, params)
|
76
|
+
parse_parameter_as_resource_id!(:cloud, options, params, 'zoneId')
|
77
|
+
parse_parameter_as_resource_id!(:server, options, params)
|
78
|
+
super
|
79
|
+
end
|
80
|
+
|
81
|
+
def network_floating_ip_list_column_definitions(options)
|
82
|
+
{
|
83
|
+
"ID" => 'id',
|
84
|
+
"IP Address" => 'ipAddress',
|
85
|
+
"Cloud" => lambda {|it| it['cloud'] ? it['cloud']['name'] : '' },
|
86
|
+
"Status" => lambda {|it| it['ipStatus'].to_s.capitalize },
|
87
|
+
"VM" => lambda {|it| it['server'] ? it['server']['name'] : '' },
|
88
|
+
}
|
89
|
+
end
|
90
|
+
|
91
|
+
def network_floating_ip_column_definitions(options)
|
92
|
+
{
|
93
|
+
"ID" => 'id',
|
94
|
+
"IP Address" => 'ipAddress',
|
95
|
+
"Cloud" => lambda {|it| it['cloud'] ? it['cloud']['name'] : '' },
|
96
|
+
"Status" => lambda {|it| it['ipStatus'].to_s.capitalize },
|
97
|
+
"VM" => lambda {|it| it['server'] ? it['server']['name'] : '' },
|
98
|
+
}
|
99
|
+
end
|
100
|
+
|
101
|
+
def add_network_floating_ip_option_types()
|
102
|
+
[]
|
103
|
+
end
|
104
|
+
|
105
|
+
def update_network_floating_ip_option_types()
|
106
|
+
[]
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
@@ -303,7 +303,8 @@ class Morpheus::Cli::ReportsCommand
|
|
303
303
|
|
304
304
|
end
|
305
305
|
|
306
|
-
|
306
|
+
|
307
|
+
if check_payload_dates(payload)
|
307
308
|
print_green_success "The CLI generates a query that will use only month and year. However, the API does support yyyy-mm-dd from a previous version of Morpheus.\nReplace startMonth/endMonth keys with startDate,endDate ie:"
|
308
309
|
payload['report'].delete('startMonth')
|
309
310
|
payload['report'].delete('endMonth')
|
@@ -713,5 +714,11 @@ EOT
|
|
713
714
|
return metadata_filter
|
714
715
|
end
|
715
716
|
|
717
|
+
def check_payload_dates(payload)
|
718
|
+
if payload['report']['startMonth'] && payload['report']['endMonth']
|
719
|
+
return payload['report']['startMonth'].size > 7 || payload['report']['endMonth'].size > 7
|
720
|
+
end
|
721
|
+
return false
|
722
|
+
end
|
716
723
|
end
|
717
724
|
|