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
@@ -5,12 +5,14 @@ require 'optparse'
5
5
  require 'morpheus/cli/cli_command'
6
6
  require 'morpheus/cli/option_types'
7
7
  require 'morpheus/cli/mixins/accounts_helper'
8
+ require 'morpheus/cli/mixins/provisioning_helper'
8
9
  require 'json'
9
10
 
10
11
  class Morpheus::Cli::Roles
11
12
  include Morpheus::Cli::CliCommand
12
13
  include Morpheus::Cli::AccountsHelper
13
- register_subcommands :list, :get, :add, :update, :remove, :'update-feature-access', :'update-global-group-access', :'update-group-access', :'update-global-cloud-access', :'update-cloud-access', :'update-global-instance-type-access', :'update-instance-type-access', :'update-global-blueprint-access', :'update-blueprint-access'
14
+ include Morpheus::Cli::ProvisioningHelper
15
+ register_subcommands :list, :get, :add, :update, :remove, :'list-permissions', :'update-feature-access', :'update-global-group-access', :'update-group-access', :'update-global-cloud-access', :'update-cloud-access', :'update-global-instance-type-access', :'update-instance-type-access', :'update-global-blueprint-access', :'update-blueprint-access'
14
16
  alias_subcommand :details, :get
15
17
  set_default_subcommand :list
16
18
 
@@ -22,7 +24,7 @@ class Morpheus::Cli::Roles
22
24
  @roles_interface = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url).roles
23
25
  @groups_interface = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url).groups
24
26
  @options_interface = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url).options
25
- #@clouds_interface = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url).instance_types
27
+ @instances_interface = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url).instances
26
28
  @instance_types_interface = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url).instance_types
27
29
  @blueprints_interface = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url).blueprints
28
30
  @active_group_id = Morpheus::Cli::Groups.active_group
@@ -88,9 +90,13 @@ class Morpheus::Cli::Roles
88
90
  options = {}
89
91
  optparse = Morpheus::Cli::OptionParser.new do |opts|
90
92
  opts.banner = subcommand_usage("[name]")
91
- opts.on('-f','--feature-access', "Display Feature Access") do |val|
93
+ opts.on('-p','--permissions', "Display Permissions") do |val|
92
94
  options[:include_feature_access] = true
93
95
  end
96
+ opts.on('-f','--feature-access', "Display Feature Access [deprecated]") do |val|
97
+ options[:include_feature_access] = true
98
+ end
99
+ opts.add_hidden_option('--feature-access')
94
100
  opts.on('-g','--group-access', "Display Group Access") do
95
101
  options[:include_group_access] = true
96
102
  end
@@ -189,9 +195,8 @@ class Morpheus::Cli::Roles
189
195
  # "CPU Count" => lambda {|it| (it && it['maxCpu'].to_i != 0) ? it['maxCpu'] : "no limit" }
190
196
  # }, role['instanceLimits'])
191
197
 
192
- # print_h2 "Feature Access", options
193
- # print cyan
194
-
198
+ print_h2 "Permissions", options
199
+ print cyan
195
200
  if options[:include_feature_access]
196
201
  rows = json_response['featurePermissions'].collect do |it|
197
202
  {
@@ -202,7 +207,7 @@ class Morpheus::Cli::Roles
202
207
  end
203
208
  print as_pretty_table(rows, [:code, :name, :access], options)
204
209
  else
205
- puts "Use --feature-access to list feature access"
210
+ puts "Use --permissions to list permissions"
206
211
  end
207
212
 
208
213
  print_h2 "Group Access", options
@@ -283,6 +288,89 @@ class Morpheus::Cli::Roles
283
288
  end
284
289
  end
285
290
 
291
+ def list_permissions(args)
292
+ options = {}
293
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
294
+ opts.banner = subcommand_usage("[role]")
295
+ build_common_options(opts, options, [:json, :yaml, :csv, :fields, :dry_run, :remote])
296
+ opts.footer = "List the permissions for a role.\n" +
297
+ "[role] is required. This is the name or id of a role."
298
+ end
299
+ optparse.parse!(args)
300
+
301
+ if args.count < 1
302
+ puts optparse
303
+ return 1
304
+ end
305
+
306
+ connect(options)
307
+ begin
308
+ account = find_account_from_options(options)
309
+ account_id = account ? account['id'] : nil
310
+
311
+ # role = find_role_by_name_or_id(account_id, args[0])
312
+ # exit 1 if role.nil?
313
+
314
+ @roles_interface.setopts(options)
315
+ if options[:dry_run]
316
+ if args[0].to_s =~ /\A\d{1,}\Z/
317
+ print_dry_run @roles_interface.dry.get(account_id, args[0].to_i)
318
+ else
319
+ print_dry_run @roles_interface.dry.list(account_id, {name: args[0]})
320
+ end
321
+ return
322
+ end
323
+
324
+ json_response = nil
325
+ if args[0].to_s =~ /\A\d{1,}\Z/
326
+ json_response = @roles_interface.get(account_id, args[0].to_i)
327
+ role = json_response['role']
328
+ else
329
+ role = find_role_by_name_or_id(account_id, args[0])
330
+ exit 1 if role.nil?
331
+ # refetch from show action, argh
332
+ json_response = @roles_interface.get(account_id, role['id'])
333
+ role = json_response['role']
334
+ end
335
+
336
+ role_permissions = json_response['featurePermissions']
337
+
338
+ if options[:json]
339
+ puts as_json(role_permissions, options)
340
+ return 0
341
+ elsif options[:yaml]
342
+ puts as_yaml(role_permissions, options)
343
+ return 0
344
+ elsif options[:csv]
345
+ puts records_as_csv(role_permissions)
346
+ return 0
347
+ end
348
+
349
+ print cyan
350
+ print_h1 "Role Permissions: [#{role['id']}] #{role['authority']}", options
351
+
352
+ print cyan
353
+ if role_permissions && role_permissions.size > 0
354
+ rows = role_permissions.collect do |it|
355
+ {
356
+ code: it['code'],
357
+ name: it['name'],
358
+ access: get_access_string(it['access']),
359
+ }
360
+ end
361
+ print as_pretty_table(rows, [:code, :name, :access], options)
362
+ else
363
+ puts "No permissions found?"
364
+ end
365
+
366
+ print reset,"\n"
367
+ return 0
368
+ rescue RestClient::Exception => e
369
+ print_rest_exception(e, options)
370
+ exit 1
371
+ end
372
+ end
373
+
286
374
  def add(args)
287
375
  usage = "Usage: morpheus roles add [options]"
288
376
  options = {}
@@ -387,7 +475,7 @@ class Morpheus::Cli::Roles
387
475
  optparse = Morpheus::Cli::OptionParser.new do |opts|
388
476
  opts.banner = subcommand_usage("[name] [options]")
389
477
  build_option_type_options(opts, options, update_role_option_types)
390
- build_common_options(opts, options, [:options, :json, :dry_run, :remote])
478
+ build_common_options(opts, options, [:options, :payload, :json, :dry_run, :remote])
391
479
  end
392
480
  optparse.parse!(args)
393
481
 
@@ -543,7 +631,7 @@ class Morpheus::Cli::Roles
543
631
  print JSON.pretty_generate(json_response)
544
632
  print "\n"
545
633
  else
546
- print_green_success "Role #{role['authority']} feature access updated"
634
+ print_green_success "Role #{role['authority']} permission #{permission_code} set to #{access_value}"
547
635
  end
548
636
  rescue RestClient::Exception => e
549
637
  print_rest_exception(e, options)
@@ -555,7 +643,7 @@ class Morpheus::Cli::Roles
555
643
  usage = "Usage: morpheus roles update-global-group-access [name] [full|read|custom|none]"
556
644
  options = {}
557
645
  optparse = Morpheus::Cli::OptionParser.new do |opts|
558
- opts.banner = subcommand_usage("[name] [code] [full|read|custom|none]")
646
+ opts.banner = subcommand_usage("[name] [full|read|custom|none]")
559
647
  build_common_options(opts, options, [:json, :dry_run, :remote])
560
648
  end
561
649
  optparse.parse!(args)
@@ -658,12 +746,10 @@ class Morpheus::Cli::Roles
658
746
  exit 1
659
747
  end
660
748
 
661
- # group_id = find_group_id_by_name(group_name)
662
- # exit 1 if group_id.nil?
663
749
  group = nil
664
750
  group_id = nil
665
751
  if !do_all
666
- group = find_group_by_name(group_name)
752
+ group = find_group_by_name_or_id_for_provisioning(group_name)
667
753
  return 1 if group.nil?
668
754
  group_id = group['id']
669
755
  end
@@ -817,9 +903,7 @@ class Morpheus::Cli::Roles
817
903
  if !do_all
818
904
  group_id = nil
819
905
  if !options[:group].nil?
820
- #group_id = find_group_id_by_name(options[:group])
821
- group = find_group_by_name(options[:group])
822
- return 1 if group.nil?
906
+ group = find_group_by_name_or_id_for_provisioning(options[:group])
823
907
  group_id = group['id']
824
908
  else
825
909
  group_id = @active_group_id
@@ -1193,28 +1277,6 @@ class Morpheus::Cli::Roles
1193
1277
  end
1194
1278
 
1195
1279
 
1196
- def find_group_by_name(name)
1197
- group_results = @groups_interface.get(name)
1198
- if group_results['groups'].empty?
1199
- print_red_alert "Group not found by name #{name}"
1200
- return nil
1201
- end
1202
- return group_results['groups'][0]
1203
- end
1204
-
1205
- # no worky, returning {"success"=>true, "data"=>[]}
1206
- # def find_group_id_by_name(name)
1207
- # option_results = @options_interface.options_for_source('groups',{})
1208
- # puts "option_results: #{option_results.inspect}"
1209
- # match = option_results['data'].find { |grp| grp['value'].to_s == name.to_s || grp['name'].downcase == name.downcase}
1210
- # if match.nil?
1211
- # print_red_alert "Group not found by name #{name}"
1212
- # return nil
1213
- # else
1214
- # return match['value']
1215
- # end
1216
- # end
1217
-
1218
1280
  def find_cloud_id_by_name(group_id, name)
1219
1281
  option_results = @options_interface.options_for_source('clouds', {groupId: group_id})
1220
1282
  match = option_results['data'].find { |grp| grp['value'].to_s == name.to_s || grp['name'].downcase == name.downcase}
@@ -641,7 +641,7 @@ class Morpheus::Cli::Tasks
641
641
  end
642
642
 
643
643
  def find_task_by_name(name)
644
- tasks = @tasks_interface.get({name: name.to_s})['tasks']
644
+ tasks = @tasks_interface.list({name: name.to_s})['tasks']
645
645
  if tasks.empty?
646
646
  print_red_alert "Task not found by name #{name}"
647
647
  return nil
@@ -87,9 +87,10 @@ class Morpheus::Cli::UserSettingsCommand
87
87
  print_h2 "API Access Tokens"
88
88
  cols = {
89
89
  #"ID" => lambda {|it| it['id'] },
90
- "Client ID" => lambda {|it| it['clientId'] },
91
- "Username" => lambda {|it| it['username'] },
92
- "Expiration" => lambda {|it| format_local_dt(it['expiration']) }
90
+ "CLIENT ID" => lambda {|it| it['clientId'] },
91
+ "USERNAME" => lambda {|it| it['username'] },
92
+ "EXPIRATION" => lambda {|it| format_local_dt(it['expiration']) },
93
+ "TTL" => lambda {|it| it['expiration'] ? (format_duration(it['expiration']) rescue '') : '' }
93
94
  }
94
95
  print cyan
95
96
  puts as_pretty_table(access_tokens, cols)
@@ -217,7 +218,7 @@ class Morpheus::Cli::UserSettingsCommand
217
218
  options = {}
218
219
  params = {}
219
220
  optparse = Morpheus::Cli::OptionParser.new do |opts|
220
- opts.banner = subcommand_usage("[file]")
221
+ opts.banner = subcommand_usage()
221
222
  opts.on("--user-id ID", String, "User ID") do |val|
222
223
  params['userId'] = val.to_s
223
224
  end
@@ -332,12 +333,15 @@ class Morpheus::Cli::UserSettingsCommand
332
333
  return
333
334
  end
334
335
  json_response = @user_settings_interface.regenerate_access_token(params, payload)
335
- new_access_token = json_response['token']
336
+ new_access_token = json_response['access_token'] || json_response['token']
336
337
  # update credentials if regenerating cli token
337
- if params['clientId'] == 'morph-cli'
338
- if new_access_token
339
- login_opts = {:remote_token => new_access_token}
340
- login_result = Morpheus::Cli::Credentials.new(@appliance_name, @appliance_url).login(login_opts)
338
+ if params['clientId'] == Morpheus::APIClient::CLIENT_ID
339
+ if params['userId'].nil? # should check against current user id
340
+ if new_access_token
341
+ # this sux, need to save refresh_token too.. just save to wallet and refresh shell maybe?
342
+ login_opts = {:remote_token => new_access_token}
343
+ login_result = Morpheus::Cli::Credentials.new(@appliance_name, @appliance_url).login(login_opts)
344
+ end
341
345
  end
342
346
  end
343
347
  if options[:quiet]
@@ -393,11 +397,11 @@ class Morpheus::Cli::UserSettingsCommand
393
397
  end
394
398
  new_access_token = json_response['token']
395
399
  # update credentials if regenerating cli token
396
- # if params['clientId'] == 'morph-cli'
400
+ # if params['clientId'] == Morpheus::APIClient::CLIENT_ID
397
401
  # logout_result = Morpheus::Cli::Credentials.new(@appliance_name, @appliance_url).logout
398
402
  # end
399
403
  print_green_success "Cleared #{params['clientId']} access token"
400
- if params['clientId'] == 'morph-cli'
404
+ if params['clientId'] == Morpheus::APIClient::CLIENT_ID
401
405
  print yellow,"Your current access token is no longer valid, you will need to login again.",reset,"\n"
402
406
  end
403
407
  # get_args = [] + (options[:remote] ? ["-r",options[:remote]] : []) + (params['userId'] ? ['--user-id', params['userId'].to_s] : [])
@@ -451,7 +455,11 @@ class Morpheus::Cli::UserSettingsCommand
451
455
  clients = json_response['clients'] || json_response['apiClients']
452
456
  print_h1 "Morpheus API Clients"
453
457
  columns = {
454
- "Client ID" => lambda {|it| it['clientId'] }
458
+ "CLIENT ID" => lambda {|it| it['clientId'] },
459
+ "NAME" => lambda {|it| it['name'] },
460
+ "TTL" => lambda {|it| it['accessTokenValiditySeconds'] ? "#{it['accessTokenValiditySeconds']}" : '' },
461
+ "DURATION" => lambda {|it| it['accessTokenValiditySeconds'] ? (format_duration_seconds(it['accessTokenValiditySeconds']) rescue '') : '' },
462
+ # "USABLE" => lambda {|it| format_boolean(it['usable']) }
455
463
  }
456
464
  print cyan
457
465
  puts as_pretty_table(clients, columns)
@@ -1,6 +1,6 @@
1
1
 
2
2
  module Morpheus
3
3
  module Cli
4
- VERSION = "4.1.4"
4
+ VERSION = "4.1.5"
5
5
  end
6
6
  end
@@ -649,7 +649,7 @@ class Morpheus::Cli::VirtualImages
649
649
  end
650
650
 
651
651
  def find_virtual_image_by_name(name)
652
- json_results = @virtual_images_interface.get({name: name.to_s})
652
+ json_results = @virtual_images_interface.list({name: name.to_s})
653
653
  if json_results['virtualImages'].empty?
654
654
  print_red_alert "Virtual Image not found by name #{name}"
655
655
  return nil
@@ -0,0 +1,546 @@
1
+ require 'morpheus/cli/cli_command'
2
+
3
+ class Morpheus::Cli::WhitelabelSettingsCommand
4
+ include Morpheus::Cli::CliCommand
5
+ # include Morpheus::Cli::AccountsHelper
6
+
7
+ set_command_name :'whitelabel-settings'
8
+
9
+ register_subcommands :get, :update
10
+ register_subcommands :update_images, :reset_image, :download_image, :view_image
11
+ set_default_subcommand :get
12
+
13
+ set_command_hidden # hiding until 4.2 release
14
+
15
+ def initialize()
16
+ @image_types = {'header-logo' => 'headerLogo', 'footer-logo' => 'footerLogo', 'login-logo' => 'loginLogo', 'favicon' => 'favicon'}
17
+ end
18
+
19
+ def connect(opts)
20
+ @api_client = establish_remote_appliance_connection(opts)
21
+ @whitelabel_settings_interface = @api_client.whitelabel_settings
22
+ end
23
+
24
+ def handle(args)
25
+ handle_subcommand(args)
26
+ end
27
+
28
+ def get(args)
29
+ options = {}
30
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
31
+ opts.banner = subcommand_usage()
32
+ opts.on('--details', "Show full (not truncated) contents of Terms of Use, Privacy Policy, Override CSS" ) do
33
+ options[:details] = true
34
+ end
35
+ build_common_options(opts, options, [:query, :json, :yaml, :csv, :fields, :dry_run, :remote])
36
+ opts.footer = "Get whitelabel settings."
37
+ end
38
+
39
+ optparse.parse!(args)
40
+ connect(options)
41
+
42
+ if args.count != 0
43
+ raise_command_error "wrong number of arguments, expected 0 and got (#{args.count}) #{args}\n#{optparse}"
44
+ return 1
45
+ end
46
+
47
+ begin
48
+ params = parse_list_options(options)
49
+ @whitelabel_settings_interface.setopts(options)
50
+ if options[:dry_run]
51
+ print_dry_run @whitelabel_settings_interface.dry.get()
52
+ return
53
+ end
54
+ json_response = @whitelabel_settings_interface.get()
55
+ if options[:json]
56
+ puts as_json(json_response, options, "whitelabelSettings")
57
+ return 0
58
+ elsif options[:yaml]
59
+ puts as_yaml(json_response, options, "whitelabelSettings")
60
+ return 0
61
+ elsif options[:csv]
62
+ puts records_as_csv([json_response['whitelabelSettings']], options)
63
+ return 0
64
+ end
65
+
66
+ whitelabel_settings = json_response['whitelabelSettings']
67
+
68
+ print_h1 "Whitelabel Settings"
69
+ print cyan
70
+ description_cols = {
71
+ "Enabled" => lambda {|it| format_boolean(it['enabled']) },
72
+ "Appliance Name" => lambda {|it| it['applianceName'] },
73
+ "Disable Support Menu" => lambda {|it| format_boolean(it['disableSupportMenu'])},
74
+ "Header Logo" => lambda {|it| it['headerLogo'] ? it['headerLogo'].split('/').last : '' },
75
+ "Footer Logo" => lambda {|it| it['footerLogo'] ? it['footerLogo'].split('/').last : '' },
76
+ "Login Logo" => lambda {|it| it['loginLogo'] ? it['loginLogo'].split('/').last : '' },
77
+ "Favicon" => lambda {|it| it['favicon'] ? it['favicon'].split('/').last : '' },
78
+ "Header Background" => lambda {|it| it['headerBgColor']},
79
+ "Header Foreground" => lambda {|it| it['headerFgColor']},
80
+ "Nav Background" => lambda {|it| it['navBgColor']},
81
+ "Nav Foreground" => lambda {|it| it['navFgColor']},
82
+ "Nav Hover" => lambda {|it| it['navHoverColor']},
83
+ "Primary Button Background" => lambda {|it| it['primaryButtonBgColor']},
84
+ "Primary Button Foreground" => lambda {|it| it['primaryButtonFgColor']},
85
+ "Primary Button Hover Background" => lambda {|it| it['primaryButtonHoverBgColor']},
86
+ "Primary Button Hover Foreground" => lambda {|it| it['primaryButtonHoverFgColor']},
87
+ "Footer Background" => lambda {|it| it['footerBgColor']},
88
+ "Footer Foreground" => lambda {|it| it['footerFgColor']},
89
+ "Login Background" => lambda {|it| it['loginBgColor']},
90
+ "Copyright String" => lambda {|it| it['copyrightString']}
91
+ }
92
+
93
+ print_description_list(description_cols, whitelabel_settings)
94
+
95
+ # Support Menu Links
96
+ if !whitelabel_settings['supportMenuLinks'].empty?
97
+ print_h2 "Support Menu Links"
98
+ print cyan
99
+ print as_pretty_table(whitelabel_settings['supportMenuLinks'], [:url, :label, :labelCode])
100
+ end
101
+
102
+ trunc_len = 80
103
+ if !(content = whitelabel_settings['overrideCss']).nil? && content.length
104
+ title = "Override CSS"
105
+ title = title + ' (truncated, use --details for all content)' if content && content.length > trunc_len && !options[:details]
106
+ print_h2 title
107
+ print cyan
108
+ print options[:details] ? content : truncate_string(content, trunc_len), "\n"
109
+ end
110
+ if !(content = whitelabel_settings['termsOfUse']).nil? && content.length
111
+ title = "Terms of Use"
112
+ title = title + ' (truncated, use --details for all content)' if content && content.length > trunc_len && !options[:details]
113
+ print_h2 title
114
+ print cyan
115
+ print options[:details] ? content : truncate_string(content, trunc_len), "\n"
116
+ end
117
+ if !(content = whitelabel_settings['privacyPolicy']).nil? && content.length
118
+ title = "Privacy Policy"
119
+ title = title + ' (truncated, use --details for all content)' if content && content.length > trunc_len && !options[:details]
120
+ print_h2 title
121
+ print cyan
122
+ print options[:details] ? content : truncate_string(content, trunc_len), "\n"
123
+ end
124
+ print reset "\n"
125
+ return 0
126
+ rescue RestClient::Exception => e
127
+ print_rest_exception(e, options)
128
+ return 1
129
+ end
130
+ end
131
+
132
+ def update(args)
133
+ options = {}
134
+ params = {}
135
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
136
+ opts.banner = opts.banner = subcommand_usage()
137
+ opts.on('--active [on|off]', String, "Can be used to enable / disable whitelabel feature") do |val|
138
+ params['enabled'] = val.to_s == 'on' || val.to_s == 'true' || val.to_s == '1' || val.to_s == ''
139
+ end
140
+ opts.on("--appliance-name NAME", String, "Appliance name. Only available to master account") do |val|
141
+ params['applianceName'] = val == 'null' ? nil : val
142
+ end
143
+ opts.on("--disable-support-menu [on|off]", ['on','off'], "Can be used to disable support menu") do |val|
144
+ params['disableSupportMenu'] = val.to_s == 'on' || val.to_s == 'true' || val.to_s == '1' || val.to_s == ''
145
+ end
146
+ opts.on("--reset-header-logo", String, "Resets header logo to default header logo") do |val|
147
+ params['resetHeaderLogo'] = true
148
+ end
149
+ opts.on("--reset-footer-logo", String, "Resets footer logo to default footer logo") do |val|
150
+ params['resetFooterLogo'] = true
151
+ end
152
+ opts.on("--reset-login-logo", String, "Resets login logo to default login logo") do |val|
153
+ params['resetLoginLogo'] = true
154
+ end
155
+ opts.on("--reset-favicon", String, "Resets favicon default favicon") do |val|
156
+ params['resetFavicon'] = true
157
+ end
158
+ opts.on("--header-bg-color VALUE", String, "Header background color") do |val|
159
+ params['headerBgColor'] = val
160
+ end
161
+ opts.on("--header-fg-color VALUE", String, "Header foreground color") do |val|
162
+ params['headerFgColor'] = val
163
+ end
164
+ opts.on("--nav-bg-color VALUE", String, "Nav background color") do |val|
165
+ params['navBgColor'] = val
166
+ end
167
+ opts.on("--nav-fg-color VALUE", String, "Nav foreground color") do |val|
168
+ params['navFgColor'] = val
169
+ end
170
+ opts.on("--nav-hover-color VALUE", String, "Nav hover color") do |val|
171
+ params['navHoverColor'] = val
172
+ end
173
+ opts.on("--primary-button-bg-color VALUE", String, "Primary button background color") do |val|
174
+ params['primaryButtonBgColor'] = val
175
+ end
176
+ opts.on("--primary-button-fg-color VALUE", String, "Primary button foreground color") do |val|
177
+ params['primaryButtonFgColor'] = val
178
+ end
179
+ opts.on("--primary-button-hover-bg-color VALUE", String, "Primary button hover background color") do |val|
180
+ params['primaryButtonHoverBgColor'] = val
181
+ end
182
+ opts.on("--primary-button-hover-fg-color VALUE", String, "Primary button hover foreground color") do |val|
183
+ params['primaryButtonHoverFgColor'] = val
184
+ end
185
+ opts.on("--footer-bg-color VALUE", String, "Footer background color") do |val|
186
+ params['footerBgColor'] = val
187
+ end
188
+ opts.on("--footer-fg-color VALUE", String, "Footer foreground color") do |val|
189
+ params['footerFgColor'] = val
190
+ end
191
+ opts.on("--login-bg-color VALUE", String, "Login background color") do |val|
192
+ params['loginBgColor'] = val
193
+ end
194
+ opts.on("--copyright TEXT", String, "Copyright String") do |val|
195
+ params['copyrightString'] = val
196
+ end
197
+ opts.on("--css TEXT", String, "Override CSS") do |val|
198
+ params['overrideCss'] = val
199
+ end
200
+ opts.on("--css-file FILE", String, "Override CSS from local file") do |val|
201
+ options[:overrideCssFile] = val
202
+ end
203
+ opts.on("--terms TEXT", String, "Terms of use content") do |val|
204
+ params['termsOfUse'] = val
205
+ end
206
+ opts.on("--terms-file FILE", String, "Terms of use content from local file") do |val|
207
+ options[:termsOfUseFile] = val
208
+ end
209
+ opts.on("--privacy-policy TEXT", String, "Privacy policy content") do |val|
210
+ params['privacyPolicy'] = val
211
+ end
212
+ opts.on("--privacy-policy-file FILE", String, "Privacy policy content from local file") do |val|
213
+ options[:privacyPolicyFile] = val
214
+ end
215
+ opts.on('--support-menu-links LIST', Array, "Support menu links. Comma delimited list of menu links. Each menu link is pipe delimited url1|label1|code1,url2|label2|code2") do |val|
216
+ options[:supportMenuLinks] = val
217
+ end
218
+ build_common_options(opts, options, [:json, :dry_run, :quiet, :remote])
219
+ end
220
+
221
+ optparse.parse!(args)
222
+ connect(options)
223
+
224
+ if args.count != 0
225
+ raise_command_error "wrong number of arguments, expected 0 and got (#{args.count}) #{args}\n#{optparse}"
226
+ return 1
227
+ end
228
+
229
+ begin
230
+ payload = parse_payload(options)
231
+ image_files = {}
232
+
233
+ if !payload
234
+ [:overrideCssFile, :termsOfUseFile, :privacyPolicyFile].each do |sym|
235
+ if options[sym]
236
+ filename = File.expand_path(options[sym])
237
+
238
+ if filename && File.file?(filename)
239
+ params[sym.to_s.delete_suffix('File')] = File.read(filename)
240
+ else
241
+ print_red_alert("File not found: #{filename}")
242
+ exit 1
243
+ end
244
+ end
245
+ end
246
+ if options[:supportMenuLinks]
247
+ params['supportMenuLinks'] = options[:supportMenuLinks].collect { |link|
248
+ parts = link.split('|')
249
+ {'url' => parts[0].strip, 'label' => (parts.count > 1 ? parts[1].strip : ''), 'labelCode' => (parts.count > 2 ? parts[2].strip : '')}
250
+ }
251
+ end
252
+ payload = {'whitelabelSettings' => params}
253
+ end
254
+
255
+ @whitelabel_settings_interface.setopts(options)
256
+ if options[:dry_run]
257
+ print_dry_run @whitelabel_settings_interface.dry.update(payload, image_files)
258
+ return
259
+ end
260
+ json_response = @whitelabel_settings_interface.update(payload, image_files)
261
+
262
+ if options[:json]
263
+ puts as_json(json_response, options)
264
+ elsif !options[:quiet]
265
+ if json_response['success']
266
+ print_green_success "Updated whitelabel settings"
267
+ get([] + (options[:remote] ? ["-r",options[:remote]] : []))
268
+ else
269
+ print_red_alert "Error updating whitelabel settings: #{json_response['msg'] || json_response['errors']}"
270
+ end
271
+ end
272
+ return 0
273
+ rescue RestClient::Exception => e
274
+ print_rest_exception(e, options)
275
+ exit 1
276
+ end
277
+ end
278
+
279
+ def update_images(args)
280
+ options = {}
281
+ params = {}
282
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
283
+ opts.banner = opts.banner = subcommand_usage()
284
+ opts.on("--header-logo FILE", String, "Header logo image. Local path of a file to upload (png|jpg|svg)") do |val|
285
+ options[:headerLogo] = val
286
+ end
287
+ opts.on("--reset-header-logo", String, "Resets header logo to default header logo") do |val|
288
+ params['resetHeaderLogo'] = true
289
+ end
290
+ opts.on("--footer-logo FILE", String, "Footer logo image. Local path of a file to upload (png|jpg|svg)") do |val|
291
+ options[:footerLogo] = val
292
+ end
293
+ opts.on("--reset-footer-logo", String, "Resets footer logo to default footer logo") do |val|
294
+ params['resetFooterLogo'] = true
295
+ end
296
+ opts.on("--login-logo FILE", String, "Login logo image. Local path of a file to upload (png|jpg|svg)") do |val|
297
+ options[:loginLogo] = val
298
+ end
299
+ opts.on("--reset-login-logo", String, "Resets login logo to default login logo") do |val|
300
+ params['resetLoginLogo'] = true
301
+ end
302
+ opts.on("--favicon FILE", String, "Favicon icon image. Local path of a file to upload") do |val|
303
+ options[:favicon] = val
304
+ end
305
+ opts.on("--reset-favicon", String, "Resets favicon default favicon") do |val|
306
+ params['resetFavicon'] = true
307
+ end
308
+ build_common_options(opts, options, [:json, :dry_run, :quiet, :remote])
309
+ opts.footer = "Update your whitelabel images."
310
+ end
311
+
312
+ optparse.parse!(args)
313
+ connect(options)
314
+
315
+ if args.count != 0
316
+ raise_command_error "wrong number of arguments, expected 0 and got (#{args.count}) #{args}\n#{optparse}"
317
+ return 1
318
+ end
319
+
320
+ begin
321
+ payload = parse_payload(options)
322
+
323
+ if !payload
324
+ payload = params
325
+
326
+ [:headerLogo, :footerLogo, :loginLogo, :favicon].each do |sym|
327
+ if options[sym]
328
+ filename = File.expand_path(options[sym])
329
+
330
+ if filename && File.file?(filename)
331
+ payload["#{sym.to_s}.file"] = File.new(filename, 'rb')
332
+ else
333
+ print_red_alert("File not found: #{filename}")
334
+ exit 1
335
+ end
336
+ end
337
+ end
338
+ end
339
+
340
+ if payload.empty?
341
+ print_green_success "Nothing to update"
342
+ exit 1
343
+ end
344
+
345
+ @whitelabel_settings_interface.setopts(options)
346
+ if options[:dry_run]
347
+ print_dry_run @whitelabel_settings_interface.dry.update_images(payload)
348
+ return
349
+ end
350
+
351
+ json_response = @whitelabel_settings_interface.update_images(payload)
352
+
353
+ if options[:json]
354
+ puts as_json(json_response, options)
355
+ elsif !options[:quiet]
356
+ print_red_alert "Error updating whitelabel image: #{json_response['msg'] || json_response['errors']}" if json_response['success'] == false
357
+ print_green_success "Updated whitelabel image" if json_response['success'] == true
358
+ end
359
+ rescue RestClient::Exception => e
360
+ print_rest_exception(e, options)
361
+ exit 1
362
+ end
363
+ end
364
+
365
+ def reset_image(args)
366
+ options = {}
367
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
368
+ opts.banner = opts.banner = subcommand_usage("[image-type]")
369
+ build_common_options(opts, options, [:json, :dry_run, :quiet, :remote])
370
+ opts.footer = "Reset your whitelabel image.\n" +
371
+ "[image-type] is required. This is the whitelabel image type (#{@image_types.collect {|k,v| k}.join('|')})"
372
+ end
373
+
374
+ optparse.parse!(args)
375
+ connect(options)
376
+
377
+ if args.count != 1
378
+ raise_command_error "wrong number of arguments, expected 1 and got (#{args.count}) #{args}\n#{optparse}"
379
+ return 1
380
+ end
381
+ if !@image_types[args[0]]
382
+ raise_command_error "Invalid image type specified: #{args[0]}. Must be one of the following (#{@image_types.collect {|k,v| k}.join('|')})"
383
+ return 1
384
+ end
385
+
386
+ begin
387
+ image_type = @image_types[args[0]]
388
+ @whitelabel_settings_interface.setopts(options)
389
+ if options[:dry_run]
390
+ print_dry_run @whitelabel_settings_interface.dry.reset_image(image_type)
391
+ return
392
+ end
393
+
394
+ json_response = @whitelabel_settings_interface.reset_image(image_type)
395
+
396
+ if options[:json]
397
+ puts as_json(json_response, options)
398
+ elsif !options[:quiet]
399
+ print_red_alert "Error resetting whitelabel image: #{json_response['msg'] || json_response['errors']}" if json_response['success'] == false
400
+ print_green_success "Reset whitelabel image" if json_response['success'] == true
401
+ end
402
+ rescue RestClient::Exception => e
403
+ print_rest_exception(e, options)
404
+ exit 1
405
+ end
406
+ end
407
+
408
+ def view_image(args)
409
+ options = {}
410
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
411
+ opts.banner = opts.banner = subcommand_usage("[image-type]")
412
+ build_common_options(opts, options, [:json, :dry_run, :quiet, :remote])
413
+ opts.footer = "View your image of specified [image-type].\n" +
414
+ "[image-type] is required. This is the whitelabel image type (#{@image_types.collect {|k,v| k}.join('|')})\n" +
415
+ "This opens the image url with a web browser."
416
+ end
417
+
418
+ optparse.parse!(args)
419
+ connect(options)
420
+
421
+ if args.count != 1
422
+ raise_command_error "wrong number of arguments, expected 1 and got (#{args.count}) #{args}\n#{optparse}"
423
+ return 1
424
+ end
425
+ if !@image_types[args[0]]
426
+ raise_command_error "Invalid image type specified: #{args[0]}. Must be one of the following (#{@image_types.collect {|k,v| k}.join('|')})"
427
+ return 1
428
+ end
429
+
430
+ begin
431
+ image_type = @image_types[args[0]]
432
+ @whitelabel_settings_interface.setopts(options)
433
+ if options[:dry_run]
434
+ print_dry_run @whitelabel_settings_interface.dry.get()
435
+ return
436
+ end
437
+
438
+ whitelabel_settings = @whitelabel_settings_interface.get()['whitelabelSettings']
439
+
440
+ if link = whitelabel_settings[image_type]
441
+ if RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/
442
+ system "start #{link}"
443
+ elsif RbConfig::CONFIG['host_os'] =~ /darwin/
444
+ system "open #{link}"
445
+ elsif RbConfig::CONFIG['host_os'] =~ /linux|bsd/
446
+ system "xdg-open #{link}"
447
+ end
448
+ return 0, nil
449
+ else
450
+ print_error red,"No image found for #{image_type}.",reset,"\n"
451
+ return 1
452
+ end
453
+ rescue RestClient::Exception => e
454
+ print_rest_exception(e, options)
455
+ return 1
456
+ end
457
+ end
458
+
459
+ def download_image(args)
460
+ options = {}
461
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
462
+ opts.banner = opts.banner = subcommand_usage("[image-type] [local-file]")
463
+ opts.on( '-f', '--force', "Overwrite existing [local-file] if it exists." ) do
464
+ options[:overwrite] = true
465
+ end
466
+ opts.on( '-p', '--mkdir', "Create missing directories for [local-file] if they do not exist." ) do
467
+ options[:mkdir] = true
468
+ end
469
+ build_common_options(opts, options, [:dry_run, :quiet, :remote])
470
+ opts.footer = "Download an image file.\n" +
471
+ "[image-type] is required. This is the whitelabel image type (#{@image_types.collect {|k,v| k}.join('|')}) to be downloaded.\n" +
472
+ "[local-file] is required. This is the full local filepath for the downloaded file."
473
+ end
474
+
475
+ optparse.parse!(args)
476
+ connect(options)
477
+
478
+ if args.count != 2
479
+ raise_command_error "wrong number of arguments, expected 2 and got (#{args.count}) #{args}\n#{optparse}"
480
+ return 1
481
+ end
482
+ if !@image_types[args[0]]
483
+ raise_command_error "Invalid image type specified: #{args[0]}. Must be one of the following (#{@image_types.collect {|k,v| k}.join('|')})"
484
+ return 1
485
+ end
486
+
487
+ begin
488
+ image_type = @image_types[args[0]]
489
+ outfile = File.expand_path(args[1])
490
+ outdir = File.dirname(outfile)
491
+
492
+ if Dir.exists?(outfile)
493
+ print_red_alert "[local-file] is invalid. It is the name of an existing directory: #{outfile}"
494
+ return 1
495
+ end
496
+ if !Dir.exists?(outdir)
497
+ if options[:mkdir]
498
+ print cyan,"Creating local directory #{outdir}",reset,"\n"
499
+ FileUtils.mkdir_p(outdir)
500
+ else
501
+ print_red_alert "[local-file] is invalid. Directory not found: #{outdir}"
502
+ return 1
503
+ end
504
+ end
505
+ if File.exists?(outfile) && !options[:overwrite]
506
+ print_red_alert "[local-file] is invalid. File already exists: #{outfile}\nUse -f to overwrite the existing file."
507
+ return 1
508
+ end
509
+
510
+ @whitelabel_settings_interface.setopts(options)
511
+ if options[:dry_run]
512
+ print_dry_run @whitelabel_settings_interface.dry.download_image(image_type, outfile)
513
+ return
514
+ end
515
+
516
+ if !options[:quite]
517
+ print cyan + "Downloading #{args[0]} to #{outfile} ... "
518
+ end
519
+
520
+ http_response = @whitelabel_settings_interface.download_image(image_type, outfile)
521
+
522
+ success = http_response.code.to_i == 200
523
+ if success
524
+ if !options[:quiet]
525
+ print green + "SUCCESS" + reset + "\n"
526
+ end
527
+ return 0
528
+ else
529
+ if !options[:quiet]
530
+ print red + "ERROR" + reset + " HTTP #{http_response.code}" + "\n"
531
+ end
532
+ if File.exists?(outfile) && File.file?(outfile)
533
+ Morpheus::Logging::DarkPrinter.puts "Deleting bad file download: #{outfile}" if Morpheus::Logging.debug?
534
+ File.delete(outfile)
535
+ end
536
+ if options[:debug]
537
+ puts_error http_response.inspect
538
+ end
539
+ return 1
540
+ end
541
+ rescue RestClient::Exception => e
542
+ print_rest_exception(e, options)
543
+ return 1
544
+ end
545
+ end
546
+ end