morpheus-cli 4.1.4 → 4.1.5

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 (76) hide show
  1. checksums.yaml +4 -4
  2. data/lib/morpheus.rb +5 -0
  3. data/lib/morpheus/api.rb +2 -2
  4. data/lib/morpheus/api/api_client.rb +47 -12
  5. data/lib/morpheus/api/appliance_settings_interface.rb +30 -0
  6. data/lib/morpheus/api/auth_interface.rb +14 -10
  7. data/lib/morpheus/api/clouds_interface.rb +7 -0
  8. data/lib/morpheus/api/clusters_interface.rb +17 -5
  9. data/lib/morpheus/api/custom_instance_types_interface.rb +2 -3
  10. data/lib/morpheus/api/deployments_interface.rb +7 -0
  11. data/lib/morpheus/api/execute_schedules_interface.rb +2 -3
  12. data/lib/morpheus/api/groups_interface.rb +7 -0
  13. data/lib/morpheus/api/license_interface.rb +9 -2
  14. data/lib/morpheus/api/load_balancers_interface.rb +7 -0
  15. data/lib/morpheus/api/logs_interface.rb +11 -2
  16. data/lib/morpheus/api/monitoring_alerts_interface.rb +45 -0
  17. data/lib/morpheus/api/monitoring_checks_interface.rb +2 -2
  18. data/lib/morpheus/api/monitoring_interface.rb +13 -8
  19. data/lib/morpheus/api/power_schedules_interface.rb +2 -3
  20. data/lib/morpheus/api/servers_interface.rb +5 -2
  21. data/lib/morpheus/api/setup_interface.rb +25 -7
  22. data/lib/morpheus/api/task_sets_interface.rb +7 -1
  23. data/lib/morpheus/api/tasks_interface.rb +7 -0
  24. data/lib/morpheus/api/virtual_images_interface.rb +2 -3
  25. data/lib/morpheus/api/whitelabel_settings_interface.rb +60 -0
  26. data/lib/morpheus/cli.rb +18 -14
  27. data/lib/morpheus/cli/access_token_command.rb +18 -2
  28. data/lib/morpheus/cli/appliance_settings_command.rb +303 -0
  29. data/lib/morpheus/cli/apps.rb +4 -3
  30. data/lib/morpheus/cli/archives_command.rb +0 -21
  31. data/lib/morpheus/cli/blueprints_command.rb +2 -2
  32. data/lib/morpheus/cli/cli_command.rb +32 -8
  33. data/lib/morpheus/cli/clouds.rb +6 -11
  34. data/lib/morpheus/cli/clusters.rb +346 -117
  35. data/lib/morpheus/cli/command_error.rb +4 -0
  36. data/lib/morpheus/cli/commands/standard/man_command.rb +1 -1
  37. data/lib/morpheus/cli/containers_command.rb +2 -1
  38. data/lib/morpheus/cli/credentials.rb +49 -4
  39. data/lib/morpheus/cli/deployments.rb +2 -2
  40. data/lib/morpheus/cli/dot_file.rb +2 -2
  41. data/lib/morpheus/cli/error_handler.rb +6 -3
  42. data/lib/morpheus/cli/execute_schedules_command.rb +1 -1
  43. data/lib/morpheus/cli/groups.rb +4 -4
  44. data/lib/morpheus/cli/hosts.rb +3 -2
  45. data/lib/morpheus/cli/image_builder_command.rb +0 -21
  46. data/lib/morpheus/cli/instances.rb +17 -4
  47. data/lib/morpheus/cli/library_container_types_command.rb +1 -1
  48. data/lib/morpheus/cli/library_layouts_command.rb +1 -1
  49. data/lib/morpheus/cli/library_upgrades_command.rb +1 -1
  50. data/lib/morpheus/cli/license.rb +185 -72
  51. data/lib/morpheus/cli/load_balancers.rb +4 -4
  52. data/lib/morpheus/cli/login.rb +4 -0
  53. data/lib/morpheus/cli/logs_command.rb +132 -0
  54. data/lib/morpheus/cli/mixins/infrastructure_helper.rb +2 -2
  55. data/lib/morpheus/cli/mixins/logs_helper.rb +65 -0
  56. data/lib/morpheus/cli/mixins/monitoring_helper.rb +410 -28
  57. data/lib/morpheus/cli/mixins/print_helper.rb +14 -4
  58. data/lib/morpheus/cli/monitoring_alerts_command.rb +800 -0
  59. data/lib/morpheus/cli/monitoring_apps_command.rb +85 -28
  60. data/lib/morpheus/cli/monitoring_checks_command.rb +60 -27
  61. data/lib/morpheus/cli/monitoring_contacts_command.rb +54 -79
  62. data/lib/morpheus/cli/monitoring_groups_command.rb +62 -23
  63. data/lib/morpheus/cli/monitoring_incidents_command.rb +91 -70
  64. data/lib/morpheus/cli/network_pools_command.rb +39 -23
  65. data/lib/morpheus/cli/power_schedules_command.rb +1 -1
  66. data/lib/morpheus/cli/remote.rb +834 -275
  67. data/lib/morpheus/cli/roles.rb +100 -38
  68. data/lib/morpheus/cli/tasks.rb +1 -1
  69. data/lib/morpheus/cli/user_settings_command.rb +20 -12
  70. data/lib/morpheus/cli/version.rb +1 -1
  71. data/lib/morpheus/cli/virtual_images.rb +1 -1
  72. data/lib/morpheus/cli/whitelabel_settings_command.rb +546 -0
  73. data/lib/morpheus/cli/whoami.rb +1 -1
  74. data/lib/morpheus/cli/workflows.rb +2 -2
  75. data/lib/morpheus/terminal.rb +22 -8
  76. metadata +11 -2
@@ -15,4 +15,8 @@ class Morpheus::Cli::CommandError < StandardError
15
15
  super(msg)
16
16
  end
17
17
 
18
+ # def inspect
19
+ # to_s
20
+ # end
21
+
18
22
  end
@@ -294,7 +294,7 @@ You can use this to create isolated environments (sandboxes), within which to ex
294
294
 
295
295
  ```shell
296
296
  export MORPHEUS_CLI_HOME=~/morpheus_test
297
- morpheus remote add myremote https://testmorpheusappliance.mycompany.com --insecure
297
+ morpheus remote add demo https://demo.mymorpheus.com --insecure
298
298
  morpheus instances list
299
299
  ```
300
300
 
@@ -526,6 +526,7 @@ class Morpheus::Cli::ContainersCommand
526
526
  params = {}
527
527
  params.merge!(parse_list_options(options))
528
528
  params[:query] = params.delete(:phrase) unless params[:phrase].nil?
529
+ params['order'] = params['direction'] unless params['direction'].nil? # old api version expects order instead of direction
529
530
  @logs_interface.setopts(options)
530
531
  if options[:dry_run]
531
532
  print_dry_run @logs_interface.dry.container_logs(containers, params)
@@ -546,7 +547,7 @@ class Morpheus::Cli::ContainersCommand
546
547
  if logs['data'].empty?
547
548
  puts "#{cyan}No logs found.#{reset}"
548
549
  else
549
- logs['data'].reverse.each do |log_entry|
550
+ logs['data'].each do |log_entry|
550
551
  log_level = ''
551
552
  case log_entry['level']
552
553
  when 'INFO'
@@ -106,11 +106,18 @@ module Morpheus
106
106
  unless options[:quiet] || options[:no_prompt]
107
107
  # if username.empty? || password.empty?
108
108
  if options[:test_only]
109
- print "Test Morpheus Credentials for #{display_appliance(@appliance_name, @appliance_url)}\n",reset
109
+ print "Test Morpheus Credentials for #{display_appliance(@appliance_name, @appliance_url)}", "\n", reset
110
110
  else
111
- print "Enter Morpheus Credentials for #{display_appliance(@appliance_name, @appliance_url)}\n",reset
111
+ print "Enter Morpheus Credentials for #{display_appliance(@appliance_name, @appliance_url)}", "\n", reset
112
112
  end
113
113
  # end
114
+ if options[:client_id].empty?
115
+ # print "Client ID: #{required_blue_prompt} #{options[:client_id]}", "\n", reset
116
+ else
117
+ if options[:client_id] != Morpheus::APIClient::CLIENT_ID
118
+ print "Client ID: #{required_blue_prompt} #{options[:client_id]}", "\n", reset
119
+ end
120
+ end
114
121
  if username.empty?
115
122
  print "Username: #{required_blue_prompt} "
116
123
  username = $stdin.gets.chomp!
@@ -131,7 +138,7 @@ module Morpheus
131
138
  return nil
132
139
  end
133
140
  begin
134
- auth_interface = Morpheus::AuthInterface.new(@appliance_url)
141
+ auth_interface = Morpheus::AuthInterface.new({url:@appliance_url, client_id: options[:client_id]})
135
142
  auth_interface.setopts(options)
136
143
  if options[:dry_run]
137
144
  print_dry_run auth_interface.dry.login(username, password)
@@ -270,7 +277,7 @@ module Morpheus
270
277
  username = wallet['username']
271
278
 
272
279
  begin
273
- auth_interface = Morpheus::AuthInterface.new(@appliance_url)
280
+ auth_interface = Morpheus::AuthInterface.new({url:@appliance_url})
274
281
  auth_interface.setopts(options)
275
282
  if options[:dry_run]
276
283
  print_dry_run auth_interface.dry.use_refresh_token(wallet['refresh_token'])
@@ -414,6 +421,44 @@ module Morpheus
414
421
  end
415
422
  end
416
423
  end
424
+
425
+ def rename_credentials(appliance_name, new_appliance_name)
426
+ # reloading file is better for now, otherwise you can lose credentials with multiple shells.
427
+ credential_map = load_credentials_file || {}
428
+
429
+ if new_appliance_name.to_s.empty?
430
+ puts "Cannot rename credentials without specifying a new name"
431
+ return nil
432
+ elsif credential_map[new_appliance_name.to_s] || credential_map[new_appliance_name.to_sym]
433
+ # yes you can, maybe require --force?
434
+ puts "Cannot rename credentials to a name that already exists: #{new_appliance_name}"
435
+ return nil
436
+ end
437
+
438
+ # always remove symbol, which was used pre 3.6.9
439
+ credential_map.delete(appliance_name.to_sym)
440
+ begin
441
+ fn = credentials_file_path
442
+ if !Dir.exists?(File.dirname(fn))
443
+ FileUtils.mkdir_p(File.dirname(fn))
444
+ end
445
+ #Morpheus::Logging::DarkPrinter.puts "renaming credentials for #{appliance_name} to #{fn}" if Morpheus::Logging.debug?
446
+ Morpheus::Logging::DarkPrinter.puts "renaming credentials from #{appliance_name} to #{appliance_name}" if Morpheus::Logging.debug?
447
+ File.open(fn, 'w') {|f| f.write credential_map.to_yaml } #Store
448
+ FileUtils.chmod(0600, fn)
449
+ @@appliance_credentials_map = credential_map
450
+ rescue => e
451
+ puts "failed to save #{fn}. #{e}" if Morpheus::Logging.debug?
452
+ ensure
453
+ # recalcuate echo vars
454
+ #puts "Recalculating variable maps for username change"
455
+ Morpheus::Cli::Echo.recalculate_variable_map()
456
+ # recalculate shell prompt after this change
457
+ if Morpheus::Cli::Shell.has_instance?
458
+ Morpheus::Cli::Shell.instance.reinitialize()
459
+ end
460
+ end
461
+ end
417
462
  end
418
463
  end
419
464
  end
@@ -36,10 +36,10 @@ class Morpheus::Cli::Deployments
36
36
  end
37
37
  @deployments_interface.setopts(options)
38
38
  if options[:dry_run]
39
- print_dry_run @deployments_interface.dry.get(params)
39
+ print_dry_run @deployments_interface.dry.list(params)
40
40
  return
41
41
  end
42
- json_response = @deployments_interface.get(params)
42
+ json_response = @deployments_interface.list(params)
43
43
  if options[:json]
44
44
  puts JSON.pretty_generate(json_response)
45
45
  else
@@ -62,12 +62,12 @@ class Morpheus::Cli::DotFile
62
62
  if err.success?
63
63
  cmd_result = true
64
64
  else
65
- puts "#{red} source file: #{@filename} line: #{line_num} command: #{line}#{reset} error: exited non zero - #{err}"
65
+ puts "#{red} source file: #{@filename}, line: #{line_num}, command: #{line}, error: #{err}#{reset}"
66
66
  cmd_result = false
67
67
  end
68
68
  rescue => err
69
69
  # raise err
70
- puts "#{red} source file: #{@filename} line: #{line_num} command: #{line}#{reset} error: unexpected error - #{err}"
70
+ puts "#{red} source file: #{@filename}, line: #{line_num}, command: #{line}, error: #{err}#{reset}"
71
71
  cmd_result = false
72
72
  end
73
73
  if cmd_result == false
@@ -16,6 +16,7 @@ class Morpheus::Cli::ErrorHandler
16
16
 
17
17
  def handle_error(err, options={})
18
18
  exit_code = 1
19
+ options = (options || {}).clone
19
20
  # heh
20
21
  if Morpheus::Logging.debug? && options[:debug].nil?
21
22
  options[:debug] = true
@@ -147,6 +148,8 @@ class Morpheus::Cli::ErrorHandler
147
148
  else
148
149
  @stderr.print red, "Error Communicating with the Appliance. #{e}", reset, "\n"
149
150
  end
151
+ # uh, having this print method return exit_code, err to standardize return values of methods that are still calling it, at the end just by chance..
152
+ return exit_code, err #.to_s
150
153
  end
151
154
 
152
155
  def print_rest_errors(response, options={})
@@ -178,7 +181,7 @@ class Morpheus::Cli::ErrorHandler
178
181
  end
179
182
 
180
183
  def print_rest_request(req)
181
- @stderr.print "Request:"
184
+ @stderr.print "REQUEST"
182
185
  @stderr.print "\n"
183
186
  @stderr.print "#{req.method.to_s.upcase} #{req.url.inspect}"
184
187
  @stderr.print "\n"
@@ -187,10 +190,10 @@ class Morpheus::Cli::ErrorHandler
187
190
  def print_rest_response(res)
188
191
  # size = @raw_response ? File.size(@tf.path) : (res.body.nil? ? 0 : res.body.size)
189
192
  size = (res.body.nil? ? 0 : res.body.size)
190
- @stderr.print "Response:"
193
+ @stderr.print "RESPONSE"
191
194
  @stderr.print "\n"
192
195
  display_size = Filesize.from("#{size} B").pretty rescue size
193
- @stderr.print "HTTP #{res.net_http_res.code} - #{res.net_http_res.message} | #{(res['Content-type'] || '').gsub(/;.*$/, '')} #{display_size}"
196
+ @stderr.print "HTTP #{res.net_http_res.code} #{res.net_http_res.message} | #{(res['Content-type'] || '').gsub(/;.*$/, '')} #{display_size}"
194
197
  @stderr.print "\n"
195
198
  begin
196
199
  @stderr.print JSON.pretty_generate(JSON.parse(res.body))
@@ -680,7 +680,7 @@ class Morpheus::Cli::ExecuteSchedulesCommand
680
680
  end
681
681
 
682
682
  def find_instance_by_name(name)
683
- instances = @instances_interface.get({name: name.to_s})['instances']
683
+ instances = @instances_interface.list({name: name.to_s})['instances']
684
684
  if instances.empty?
685
685
  print_red_alert "Instance not found by name #{name}"
686
686
  return nil
@@ -45,10 +45,10 @@ class Morpheus::Cli::Groups
45
45
  params.merge!(parse_list_options(options))
46
46
  @groups_interface.setopts(options)
47
47
  if options[:dry_run]
48
- print_dry_run @groups_interface.get(params)
48
+ print_dry_run @groups_interface.list(params)
49
49
  return 0
50
50
  end
51
- json_response = @groups_interface.get(params)
51
+ json_response = @groups_interface.list(params)
52
52
  render_result = render_with_format(json_response, options, 'groups')
53
53
  return 0 if render_result
54
54
 
@@ -69,7 +69,7 @@ class Morpheus::Cli::Groups
69
69
  #end
70
70
  else
71
71
  unless options[:remote]
72
- print "\n# => No active group, see `groups use`\n", reset
72
+ print cyan, "\n# => No active group, see `groups use`\n", reset
73
73
  end
74
74
  end
75
75
  end
@@ -107,7 +107,7 @@ class Morpheus::Cli::Groups
107
107
  if arg.to_s =~ /\A\d{1,}\Z/
108
108
  print_dry_run @groups_interface.dry.get(arg.to_i)
109
109
  else
110
- print_dry_run @groups_interface.dry.get({name:arg})
110
+ print_dry_run @groups_interface.dry.list({name:arg})
111
111
  end
112
112
  return 0
113
113
  end
@@ -577,6 +577,7 @@ class Morpheus::Cli::Hosts
577
577
  params = {}
578
578
  params.merge!(parse_list_options(options))
579
579
  params[:query] = params.delete(:phrase) unless params[:phrase].nil?
580
+ params[:order] = params[:direction] unless params[:direction].nil? # old api version expects order instead of direction
580
581
  @logs_interface.setopts(options)
581
582
  if options[:dry_run]
582
583
  print_dry_run @logs_interface.dry.server_logs([server['id']], params)
@@ -600,7 +601,7 @@ class Morpheus::Cli::Hosts
600
601
  if logs['data'].empty?
601
602
  puts "#{cyan}No logs found.#{reset}"
602
603
  else
603
- logs['data'].reverse.each do |log_entry|
604
+ logs['data'].each do |log_entry|
604
605
  log_level = ''
605
606
  case log_entry['level']
606
607
  when 'INFO'
@@ -1805,7 +1806,7 @@ class Morpheus::Cli::Hosts
1805
1806
  exit 1
1806
1807
  end
1807
1808
  else
1808
- json_results = @clouds_interface.get({groupId: group_id, name: val})
1809
+ json_results = @clouds_interface.list({groupId: group_id, name: val})
1809
1810
  zone = json_results['zones'] ? json_results['zones'][0] : nil
1810
1811
  if zone.nil?
1811
1812
  print_red_alert "Cloud not found by name #{val}"
@@ -739,27 +739,6 @@ class Morpheus::Cli::ImageBuilderCommand
739
739
  end
740
740
  end
741
741
 
742
- # def find_group_by_name(name)
743
- # group_results = @groups_interface.get(name)
744
- # if group_results['groups'].empty?
745
- # print_red_alert "Group not found by name #{name}"
746
- # return nil
747
- # end
748
- # return group_results['groups'][0]
749
- # end
750
-
751
- # def find_cloud_by_name(group_id, name)
752
- # option_results = @options_interface.options_for_source('clouds',{groupId: group_id})
753
- # match = option_results['data'].find { |grp| grp['value'].to_s == name.to_s || grp['name'].downcase == name.downcase}
754
- # if match.nil?
755
- # print_red_alert "Cloud not found by name #{name}"
756
- # return nil
757
- # else
758
- # return match['value']
759
- # end
760
- # end
761
-
762
-
763
742
  def format_image_build_status(image_build, return_color=cyan)
764
743
  out = ""
765
744
  return out if !image_build
@@ -966,7 +966,7 @@ class Morpheus::Cli::Instances
966
966
  "[instance] is required. This is the name or id of an instance. Supports 1-N [instance] arguments."
967
967
  end
968
968
  optparse.parse!(args)
969
- if args.count != 1
969
+ if args.count < 1
970
970
  raise_command_error "wrong number of arguments, expected 1 and got (#{args.count}) #{args.join(' ')}\n#{optparse}"
971
971
  end
972
972
  connect(options)
@@ -1011,6 +1011,7 @@ class Morpheus::Cli::Instances
1011
1011
  end
1012
1012
 
1013
1013
  def logs(args)
1014
+ params = {}
1014
1015
  options = {}
1015
1016
  optparse = Morpheus::Cli::OptionParser.new do |opts|
1016
1017
  opts.banner = subcommand_usage("[instance]")
@@ -1031,9 +1032,9 @@ class Morpheus::Cli::Instances
1031
1032
  if options[:node_id] && container_ids.include?(options[:node_id])
1032
1033
  container_ids = [options[:node_id]]
1033
1034
  end
1034
- params = {}
1035
1035
  params.merge!(parse_list_options(options))
1036
1036
  params[:query] = params.delete(:phrase) unless params[:phrase].nil?
1037
+ params['order'] = params['direction'] unless params['direction'].nil? # old api version expects order instead of direction
1037
1038
  @logs_interface.setopts(options)
1038
1039
  if options[:dry_run]
1039
1040
  print_dry_run @logs_interface.dry.container_logs(container_ids, params)
@@ -1054,7 +1055,7 @@ class Morpheus::Cli::Instances
1054
1055
  if logs['data'].empty?
1055
1056
  puts "#{cyan}No logs found.#{reset}"
1056
1057
  else
1057
- logs['data'].reverse.each do |log_entry|
1058
+ logs['data'].each do |log_entry|
1058
1059
  log_level = ''
1059
1060
  case log_entry['level']
1060
1061
  when 'INFO'
@@ -1539,6 +1540,9 @@ class Morpheus::Cli::Instances
1539
1540
  opts.on( '-c', '--cloud CLOUD', "Cloud Name or ID for the new instance" ) do |val|
1540
1541
  options[:cloud] = val
1541
1542
  end
1543
+ opts.on("--create-user on|off", String, "User Config: Create Your User. Default is on") do |val|
1544
+ options[:create_user] = !['false','off','0'].include?(val.to_s)
1545
+ end
1542
1546
  build_common_options(opts, options, [:options, :payload, :auto_confirm, :json, :dry_run, :remote])
1543
1547
  end
1544
1548
  optparse.parse!(args)
@@ -1607,6 +1611,15 @@ class Morpheus::Cli::Instances
1607
1611
  passed_options.delete('group')
1608
1612
  payload.deep_merge!(passed_options)
1609
1613
  end
1614
+
1615
+ # JD: this actually fixed a customer problem
1616
+ # It appears to be important to pass this... not sure if config.createUser is necessary...
1617
+ if options[:create_user].nil?
1618
+ options[:create_user] = true
1619
+ end
1620
+ if options[:create_user] != nil
1621
+ payload.deep_merge!({'createUser' => options[:create_user], 'config' =>{'createUser' => options[:create_user]}})
1622
+ end
1610
1623
  unless options[:yes] || ::Morpheus::Cli::OptionTypes::confirm("Are you sure you would like to clone the instance #{instance['name']} as '#{payload['name']}'?", options)
1611
1624
  return 9, "aborted command"
1612
1625
  end
@@ -3626,7 +3639,7 @@ private
3626
3639
  end
3627
3640
 
3628
3641
  def find_workflow_by_name(name)
3629
- workflows = @task_sets_interface.get({name: name.to_s})['taskSets']
3642
+ workflows = @task_sets_interface.list({name: name.to_s})['taskSets']
3630
3643
  if workflows.empty?
3631
3644
  print_red_alert "Workflow not found by name #{name}"
3632
3645
  return nil
@@ -315,7 +315,7 @@ class Morpheus::Cli::LibraryContainerTypesCommand
315
315
  # err, these optionTypes have the fieldContext
316
316
  # so merge them at the root level of the request.
317
317
 
318
- provision_types = @provision_types_interface.get({customSupported: true})['provisionTypes']
318
+ provision_types = @provision_types_interface.list({customSupported: true})['provisionTypes']
319
319
  if provision_types.empty?
320
320
  print_red_alert "No available provision types found!"
321
321
  return 1
@@ -331,7 +331,7 @@ class Morpheus::Cli::LibraryLayoutsCommand
331
331
  params['description'] = v_prompt['description'] if v_prompt['description']
332
332
  end
333
333
 
334
- provision_types = @provision_types_interface.get({customSupported: true})['provisionTypes']
334
+ provision_types = @provision_types_interface.list({customSupported: true})['provisionTypes']
335
335
  if provision_types.empty?
336
336
  print_red_alert "No available provision types found!"
337
337
  exit 1
@@ -288,7 +288,7 @@ class Morpheus::Cli::LibraryUpgradesCommand
288
288
  end
289
289
 
290
290
 
291
- # provision_types = @provision_types_interface.get({customSupported: true})['provisionTypes']
291
+ # provision_types = @provision_types_interface.list({customSupported: true})['provisionTypes']
292
292
  # if provision_types.empty?
293
293
  # print_red_alert "No available provision types found!"
294
294
  # exit 1
@@ -6,8 +6,10 @@ require 'morpheus/cli/cli_command'
6
6
  class Morpheus::Cli::License
7
7
  include Morpheus::Cli::CliCommand
8
8
 
9
- register_subcommands :get, :apply, :decode
10
- alias_subcommand :details, :get
9
+ register_subcommands :get, :install, :uninstall, :test
10
+ # deprecated
11
+ register_subcommands :decode, :apply
12
+ #alias_subcommand :details, :get
11
13
 
12
14
  def initialize()
13
15
  # @appliance_name, @appliance_url = Morpheus::Cli::Remote.active_appliance
@@ -25,50 +27,82 @@ class Morpheus::Cli::License
25
27
 
26
28
  def get(args)
27
29
  options = {}
30
+ params = {}
28
31
  optparse = Morpheus::Cli::OptionParser.new do |opts|
29
32
  opts.banner = subcommand_usage()
30
- build_common_options(opts, options, [:json, :dry_run, :remote])
33
+ build_common_options(opts, options, [:json, :yaml, :fields, :dry_run, :remote])
34
+ opts.footer = "Get details about the currently installed license.\n" +
35
+ "This information includes license features and limits.\n" +
36
+ "The actual secret license key value will never be returned."
31
37
  end
32
38
  optparse.parse!(args)
39
+ if args.count > 0
40
+ raise_command_error "wrong number of arguments, expected 0 and got (#{args.count}) #{args.join(' ')}\n#{optparse}"
41
+ end
33
42
  connect(options)
34
43
  begin
44
+ # make api request
35
45
  @license_interface.setopts(options)
36
46
  if options[:dry_run]
37
47
  print_dry_run @license_interface.dry.get()
38
- return
48
+ return 0, nil
39
49
  end
40
- json_result = @license_interface.get()
41
- license = json_result['license']
42
- used_memory = json_result['licenseUsedMemory']
43
- if options[:json]
44
- puts JSON.pretty_generate(json_result)
45
- else
46
- if license.nil?
47
- puts_error "No license currently applied to the appliance."
48
- return 1
49
- else
50
- print_h1 "License"
51
- max_memory = "Unlimited"
52
- max_storage = "Unlimited"
53
- max_memory = Filesize.from("#{license['maxMemory']} B").pretty if license['maxMemory'].to_i != 0
54
- max_storage = Filesize.from("#{license['maxStorage']} B").pretty if license['maxStorage'].to_i != 0
55
- used_memory = Filesize.from("#{used_memory} B").pretty if used_memory.to_i != 0
56
- print cyan
57
- description_cols = {
58
- "Account" => 'accountName',
59
- "Product Tier" => lambda {|it| format_product_tier(it) },
60
- "Start Date" => lambda {|it| format_local_dt(it['startDate']) },
61
- "End Date" => lambda {|it| format_local_dt(it['endDate']) },
62
- "Memory" => lambda {|it| "#{used_memory} / #{max_memory}" },
63
- "Max Storage" => lambda {|it| "#{max_storage}" },
64
- "Max Instances" => lambda {|it| it["maxInstances"].to_i == 0 ? 'Unlimited' : it["maxInstances"] },
65
- "Hard Limit" => lambda {|it| it[""] == false ? 'Yes' : 'No' },
66
- }
67
- print_description_list(description_cols, license)
68
- print reset,"\n"
69
- end
50
+ json_response = @license_interface.get()
51
+ # 200 OK, parse results
52
+ license = json_response['license']
53
+ used_memory = json_response['licenseUsedMemory']
54
+
55
+ # Determine exit status and any error conditions for the command
56
+ exit_code = 0
57
+ err = nil
58
+ if license.nil?
59
+ exit_code = 1
60
+ err = "No license currently installed."
70
61
  end
71
- return 0
62
+
63
+ # render common formats like dry run, curl, json , yaml , etc.
64
+ render_result = render_with_format(json_response, options)
65
+ return exit_code, err if render_result
66
+
67
+ if options[:quiet]
68
+ return exit_code, err
69
+ end
70
+
71
+ # if exit_code != 0
72
+ # print_error red, err.to_s, reset, "\n"
73
+ # return exit_code, err
74
+ # end
75
+
76
+ # render output
77
+
78
+ print_h1 "License"
79
+ max_memory = "Unlimited"
80
+ max_storage = "Unlimited"
81
+ max_memory = Filesize.from("#{license['maxMemory']} B").pretty if license['maxMemory'].to_i != 0
82
+ max_storage = Filesize.from("#{license['maxStorage']} B").pretty if license['maxStorage'].to_i != 0
83
+ used_memory = Filesize.from("#{used_memory} B").pretty if used_memory.to_i != 0
84
+ print cyan
85
+ description_cols = {
86
+ "Account" => 'accountName',
87
+ "Product Tier" => lambda {|it| format_product_tier(it) },
88
+ "Start Date" => lambda {|it| format_local_dt(it['startDate']) },
89
+ "End Date" => lambda {|it|
90
+ if it['endDate']
91
+ format_local_dt(it['endDate']).to_s + ' (' + format_duration(Time.now, it['endDate']).to_s + ')'
92
+ else
93
+ 'Never'
94
+ end
95
+ },
96
+ "Memory" => lambda {|it| "#{used_memory} / #{max_memory}" },
97
+ "Max Storage" => lambda {|it| "#{max_storage}" },
98
+ "Max Instances" => lambda {|it| it["maxInstances"].to_i == 0 ? 'Unlimited' : it["maxInstances"] },
99
+ "Hard Limit" => lambda {|it| it[""] == false ? 'Yes' : 'No' },
100
+ }
101
+ print_description_list(description_cols, license)
102
+ print reset,"\n"
103
+
104
+ return exit_code, err
105
+
72
106
  rescue RestClient::Exception => e
73
107
  print_rest_exception(e, options)
74
108
  return false
@@ -76,6 +110,11 @@ class Morpheus::Cli::License
76
110
  end
77
111
 
78
112
  def apply(args)
113
+ print_error "#{yellow}DEPRECATION WARNING: `license apply` has been deprecated and replaced with `license install`. Please use `license install` instead.#{reset}\n"
114
+ install(args)
115
+ end
116
+
117
+ def install(args)
79
118
  options = {}
80
119
  account_name = nil
81
120
  optparse = Morpheus::Cli::OptionParser.new do |opts|
@@ -83,6 +122,9 @@ class Morpheus::Cli::License
83
122
  build_common_options(opts, options, [:json, :dry_run, :remote])
84
123
  end
85
124
  optparse.parse!(args)
125
+ if args.count > 1
126
+ raise_command_error "wrong number of arguments, expected 0-1 and got (#{args.count}) #{args.join(' ')}\n#{optparse}"
127
+ end
86
128
  connect(options)
87
129
  begin
88
130
  if args[0]
@@ -93,13 +135,13 @@ class Morpheus::Cli::License
93
135
  end
94
136
  @license_interface.setopts(options)
95
137
  if options[:dry_run]
96
- print_dry_run @license_interface.dry.apply(key)
138
+ print_dry_run @license_interface.dry.install(key)
97
139
  return 0
98
140
  end
99
- json_result = @license_interface.apply(key)
100
- license = json_result['license']
141
+ json_response = @license_interface.install(key)
142
+ license = json_response['license']
101
143
  if options[:json]
102
- puts JSON.pretty_generate(json_result)
144
+ puts JSON.pretty_generate(json_response)
103
145
  else
104
146
  print_green_success "License applied successfully!"
105
147
  # get([]) # show it
@@ -112,13 +154,22 @@ class Morpheus::Cli::License
112
154
  end
113
155
 
114
156
  def decode(args)
157
+ print_error "#{yellow}DEPRECATION WARNING: `license decode` has been deprecated and replaced with `license test`. Please use `license test` instead.#{reset}\n"
158
+ test(args)
159
+ end
160
+
161
+ def test(args)
115
162
  options = {}
116
163
  optparse = Morpheus::Cli::OptionParser.new do |opts|
117
164
  opts.banner = subcommand_usage("[key]")
118
- build_common_options(opts, options, [:json, :dry_run, :remote])
119
- opts.footer = "Decode a license key."
165
+ build_common_options(opts, options, [:json, :yaml, :csv, :fields, :dry_run, :remote])
166
+ opts.footer = "Test a license key.\n" +
167
+ "This is a way to decode and view a license key before installing it."
120
168
  end
121
169
  optparse.parse!(args)
170
+ if args.count > 1
171
+ raise_command_error "wrong number of arguments, expected 0-1 and got (#{args.count}) #{args.join(' ')}\n#{optparse}"
172
+ end
122
173
  connect(options)
123
174
  key = nil
124
175
  if args[0]
@@ -130,46 +181,108 @@ class Morpheus::Cli::License
130
181
  begin
131
182
  @license_interface.setopts(options)
132
183
  if options[:dry_run]
133
- print_dry_run @license_interface.dry.decode(key)
184
+ print_dry_run @license_interface.dry.test(key)
134
185
  return
135
186
  end
136
- json_result = @license_interface.decode(key)
137
- license = json_result['license']
138
- if options[:json]
139
- puts JSON.pretty_generate(json_result)
140
- else
141
- if license.nil?
142
- puts_error "Unable to decode license."
143
- puts_error json_results['msg'] if json_results['msg']
144
- return 1
145
- else
146
- print_h1 "License"
147
- max_memory = "Unlimited"
148
- max_storage = "Unlimited"
149
- max_memory = Filesize.from("#{license['maxMemory']} B").pretty if license['maxMemory'].to_i != 0
150
- max_storage = Filesize.from("#{license['maxStorage']} B").pretty if license['maxStorage'].to_i != 0
151
- print cyan
152
- description_cols = {
153
- "Account" => 'accountName',
154
- "Product Tier" => lambda {|it| format_product_tier(it) },
155
- "Start Date" => lambda {|it| format_local_dt(it['startDate']) },
156
- "End Date" => lambda {|it| format_local_dt(it['endDate']) },
157
- "Max Memory" => lambda {|it| max_memory },
158
- "Max Storage" => lambda {|it| max_storage },
159
- "Max Instances" => lambda {|it| it["maxInstances"].to_i == 0 ? 'Unlimited' : it["maxInstances"] },
160
- "Hard Limit" => lambda {|it| it[""] == false ? 'Yes' : 'No' },
161
- }
162
- print_description_list(description_cols, license)
163
- print reset,"\n"
164
- end
187
+ json_response = @license_interface.test(key)
188
+ license = json_response['license']
189
+
190
+ exit_code, err = 0, nil
191
+ if license.nil?
192
+ err = "Unable to decode license."
193
+ exit_code = 1
194
+ #return err, exit_code
195
+ end
196
+ render_result = render_with_format(json_response, options)
197
+ if render_result
198
+ return exit_code, err
199
+ end
200
+ if options[:quiet]
201
+ return exit_code, err
202
+ end
203
+ if exit_code != 0
204
+ print_error red, err.to_s, reset, "\n"
205
+ return exit_code, err
206
+ end
207
+
208
+ # all good
209
+ print_h1 "License"
210
+ max_memory = "Unlimited"
211
+ max_storage = "Unlimited"
212
+ max_memory = Filesize.from("#{license['maxMemory']} B").pretty if license['maxMemory'].to_i != 0
213
+ max_storage = Filesize.from("#{license['maxStorage']} B").pretty if license['maxStorage'].to_i != 0
214
+ print cyan
215
+ description_cols = {
216
+ "Account" => 'accountName',
217
+ "Product Tier" => lambda {|it| format_product_tier(it) },
218
+ "Start Date" => lambda {|it| format_local_dt(it['startDate']) },
219
+ "End Date" => lambda {|it| format_local_dt(it['endDate']) },
220
+ "Max Memory" => lambda {|it| max_memory },
221
+ "Max Storage" => lambda {|it| max_storage },
222
+ "Max Instances" => lambda {|it| it["maxInstances"].to_i == 0 ? 'Unlimited' : it["maxInstances"] },
223
+ "Hard Limit" => lambda {|it| it[""] == false ? 'Yes' : 'No' },
224
+ }
225
+ print_description_list(description_cols, license)
226
+ print reset,"\n"
227
+ return exit_code, err
228
+ rescue RestClient::Exception => e
229
+ print_rest_exception(e, options)
230
+ return 1
231
+ end
232
+ end
233
+
234
+ def uninstall(args)
235
+ options = {}
236
+ params = {}
237
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
238
+ opts.banner = subcommand_usage("[key]")
239
+ build_common_options(opts, options, [:auto_confirm, :json, :yaml, :dry_run, :remote])
240
+ opts.footer = "Uninstall the current license key.\n" +
241
+ "This clears out the current license key from the appliance.\n" +
242
+ "The function of the remote appliance will be restricted without a license installed.\n" +
243
+ "Be careful using this."
244
+ end
245
+ optparse.parse!(args)
246
+ if args.count > 0
247
+ raise_command_error "wrong number of arguments, expected 0 and got (#{args.count}) #{args.join(' ')}\n#{optparse}"
248
+ end
249
+ connect(options)
250
+ begin
251
+ @license_interface.setopts(options)
252
+ if options[:dry_run]
253
+ print_dry_run @license_interface.dry.uninstall(params)
254
+ return
255
+ end
256
+
257
+ unless options[:quiet]
258
+ print cyan,"#{bold}WARNING!#{reset}#{cyan} You are about to uninstall your license key.",reset,"\n"
259
+ print yellow, "Be careful using this. Make sure you have a copy of your key somewhere if you intend to use it again.",reset, "\n"
260
+ print "\n"
261
+ end
262
+
263
+ unless options[:yes] || ::Morpheus::Cli::OptionTypes::confirm("Are you sure you want to uninstall the license key for remote #{@appliance_name} - #{@appliance_url}?")
264
+ return 9, "command aborted"
165
265
  end
266
+
267
+ json_response = @license_interface.uninstall(params)
268
+
269
+ @apps_interface.setopts(options)
270
+ if options[:dry_run]
271
+ print_dry_run @apps_interface.dry.wiki(app["id"], params)
272
+ return
273
+ end
274
+ render_result = render_with_format(json_response, options)
275
+ return 0 if render_result
276
+ return 0 if options[:quiet]
277
+ print_green_success "License uninstalled!"
166
278
  return 0
167
279
  rescue RestClient::Exception => e
168
280
  print_rest_exception(e, options)
169
- return false
281
+ return 1
170
282
  end
171
283
  end
172
284
 
285
+
173
286
  def format_product_tier(license)
174
287
  product_tier = license['productTier'] || 'capacity'
175
288
  if product_tier == 'capacity'