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.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/Dockerfile +1 -1
  3. data/lib/morpheus/api/api_client.rb +8 -0
  4. data/lib/morpheus/api/cloud_resource_pools_interface.rb +28 -3
  5. data/lib/morpheus/api/containers_interface.rb +10 -0
  6. data/lib/morpheus/api/doc_interface.rb +1 -10
  7. data/lib/morpheus/api/jobs_interface.rb +2 -2
  8. data/lib/morpheus/api/key_pairs_interface.rb +9 -0
  9. data/lib/morpheus/api/network_floating_ips_interface.rb +37 -0
  10. data/lib/morpheus/api/resource_pool_groups_interface.rb +51 -0
  11. data/lib/morpheus/cli/cli_command.rb +17 -11
  12. data/lib/morpheus/cli/commands/appliance_settings_command.rb +5 -0
  13. data/lib/morpheus/cli/commands/apps.rb +12 -6
  14. data/lib/morpheus/cli/commands/catalog_item_types_command.rb +44 -12
  15. data/lib/morpheus/cli/commands/clusters.rb +23 -2
  16. data/lib/morpheus/cli/commands/containers_command.rb +129 -4
  17. data/lib/morpheus/cli/commands/doc.rb +14 -13
  18. data/lib/morpheus/cli/commands/hosts.rb +2 -0
  19. data/lib/morpheus/cli/commands/instances.rb +9 -3
  20. data/lib/morpheus/cli/commands/jobs_command.rb +50 -3
  21. data/lib/morpheus/cli/commands/key_pairs.rb +94 -33
  22. data/lib/morpheus/cli/commands/network_floating_ips.rb +109 -0
  23. data/lib/morpheus/cli/commands/reports_command.rb +8 -1
  24. data/lib/morpheus/cli/commands/resource_pool_groups_command.rb +586 -0
  25. data/lib/morpheus/cli/commands/roles.rb +10 -10
  26. data/lib/morpheus/cli/commands/service_catalog_command.rb +40 -2
  27. data/lib/morpheus/cli/commands/service_plans_command.rb +51 -22
  28. data/lib/morpheus/cli/commands/shell.rb +1 -1
  29. data/lib/morpheus/cli/commands/tasks.rb +130 -35
  30. data/lib/morpheus/cli/commands/workflows.rb +109 -23
  31. data/lib/morpheus/cli/mixins/infrastructure_helper.rb +148 -0
  32. data/lib/morpheus/cli/mixins/jobs_helper.rb +30 -3
  33. data/lib/morpheus/cli/mixins/processes_helper.rb +2 -26
  34. data/lib/morpheus/cli/mixins/provisioning_helper.rb +2 -1
  35. data/lib/morpheus/cli/option_types.rb +2 -2
  36. data/lib/morpheus/cli/version.rb +1 -1
  37. data/test/cli/doc_test.rb +1 -1
  38. 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 [#{container['id']}] #{container['name']}"
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 [#{container['id']}] #{container['name']}"
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
- private
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
- params['refresh'] = true
77
- end
78
- opts.on('-g', '--generate', "Alias for --refresh") do
79
- params['refresh'] = true
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
- params['refresh'] = true
126
- end
127
- opts.on('-g', '--generate', "Alias for --refresh") do
128
- params['refresh'] = true
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
@@ -943,6 +943,8 @@ class Morpheus::Cli::Hosts
943
943
  end
944
944
  end
945
945
  end
946
+
947
+ metadata_option_type = option_type_list.find {|type| type['fieldName'] == 'metadata' }
946
948
 
947
949
  # Metadata Tags
948
950
  if metadata_option_type
@@ -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('--releaseEIPs [on|off]', ['on','off'], "Release EIPs. Default is on. Applies to Amazon only.") do |val|
2940
- query_params[:releaseEIPs] = val.nil? ? 'on' : val
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 = 10
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
- print_h1 "Key Pair Details"
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
- if payload['report']['startMonth'].size > 7 || payload['report']['endMonth'].size > 7
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