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
@@ -6,7 +6,7 @@ class Morpheus::Cli::MonitoringIncidentsCommand
6
6
  include Morpheus::Cli::MonitoringHelper
7
7
 
8
8
  set_command_name :'monitor-incidents'
9
- register_subcommands :list, :stats, :get, :history, :notifications, :update, :close, :reopen, :mute, :unmute
9
+ register_subcommands :list, :stats, :get, :history, :notifications, :update, :close, :reopen, :mute, :unmute, :add
10
10
  register_subcommands :'mute-all' => :mute_all
11
11
  register_subcommands :'unmute-all' => :unmute_all
12
12
 
@@ -159,7 +159,76 @@ class Morpheus::Cli::MonitoringIncidentsCommand
159
159
  exit 1
160
160
  end
161
161
  end
162
-
162
+
163
+ def add(args)
164
+ options = {}
165
+ params = {}
166
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
167
+ opts.banner = subcommand_usage("[id]")
168
+ opts.on("-c", "--comment STRING", String, "Comment on this incident. Updates summary field.") do |val|
169
+ params['comment'] = val == 'null' ? nil : val
170
+ end
171
+ opts.on("--resolution STRING", String, "Description of the resolution to this incident") do |val|
172
+ params['resolution'] = val == 'null' ? nil : val
173
+ end
174
+ opts.on("--status STATUS", String, "Set status (open or closed)") do |val|
175
+ params['status'] = val
176
+ end
177
+ opts.on("--severity STATUS", String, "Set severity (critical, warning or info)") do |val|
178
+ params['severity'] = val
179
+ end
180
+ opts.on("--name STRING", String, "Set display name (subject)") do |val|
181
+ params['name'] = val == 'null' ? nil : val
182
+ end
183
+ opts.on("--startDate TIME", String, "Set start time") do |val|
184
+ begin
185
+ params['startDate'] = parse_time(val).utc.iso8601
186
+ rescue => e
187
+ raise OptionParser::InvalidArgument.new "Failed to parse --startDate '#{val}'. Error: #{e}"
188
+ end
189
+ end
190
+ opts.on("--endDate TIME", String, "Set end time") do |val|
191
+ begin
192
+ params['endDate'] = parse_time(val).utc.iso8601
193
+ rescue => e
194
+ raise OptionParser::InvalidArgument.new "Failed to parse --endDate '#{val}'. Error: #{e}"
195
+ end
196
+ end
197
+ opts.on("--inUptime BOOL", String, "Set 'In Availability'") do |val|
198
+ params['inUptime'] = ['true','on'].include?(val.to_s.strip)
199
+ end
200
+ build_common_options(opts, options, [:json, :dry_run, :quiet, :remote])
201
+ end
202
+
203
+ optparse.parse!(args)
204
+ connect(options)
205
+
206
+ begin
207
+ params['name'] = params['name'] || 'No subject'
208
+ params['startDate'] = params['startDate'] || Time.now.utc.iso8601
209
+ payload = { 'incident' => params }
210
+
211
+ @monitoring_incidents_interface.setopts(options)
212
+ if options[:dry_run]
213
+ print_dry_run @monitoring_incidents_interface.dry.create(payload)
214
+ return
215
+ end
216
+
217
+ json_response = @monitoring_incidents_interface.create(payload)
218
+
219
+ if options[:json]
220
+ puts as_json(json_response, options)
221
+ elsif !options[:quiet]
222
+ print_green_success "Created incident #{json_response['incident']['id']}"
223
+ _get(json_response['incident']['id'], options)
224
+ end
225
+
226
+ rescue RestClient::Exception => e
227
+ print_rest_exception(e, options)
228
+ exit 1
229
+ end
230
+ end
231
+
163
232
  def get(args)
164
233
  options = {}
165
234
  optparse = Morpheus::Cli::OptionParser.new do |opts|
@@ -170,7 +239,11 @@ class Morpheus::Cli::MonitoringIncidentsCommand
170
239
  opts.on(nil,'--notifications', "Display Incident Notifications") do |val|
171
240
  options[:show_notifications] = true
172
241
  end
173
- build_common_options(opts, options, [:json, :yaml, :csv, :fields, :dry_run, :remote])
242
+ opts.on('-a','--all', "Display All Details (History, Notifications)") do
243
+ options[:show_history] = true
244
+ options[:show_notifications] = true
245
+ end
246
+ build_common_options(opts, options, [:json, :yaml, :csv, :fields, :query, :dry_run, :remote])
174
247
  end
175
248
  optparse.parse!(args)
176
249
  if args.count < 1
@@ -236,7 +309,7 @@ class Morpheus::Cli::MonitoringIncidentsCommand
236
309
  issues = json_response["issues"]
237
310
  if issues && !issues.empty?
238
311
  print_h2 "Issues"
239
- print_incident_history_table(issues, options)
312
+ print_incident_issues_table(issues, options)
240
313
  else
241
314
  print "\n"
242
315
  puts "No checks involved in this incident"
@@ -265,7 +338,7 @@ class Morpheus::Cli::MonitoringIncidentsCommand
265
338
  # gotta go get it
266
339
  notifications_json_response = @monitoring_incidents_interface.notifications(incident["id"], {max: 10})
267
340
  notification_items = notifications_json_response["notifications"]
268
- if notification_items && notification_items.empty?
341
+ if notification_items && !notification_items.empty?
269
342
  print_h2 "Notifications"
270
343
  print_incident_notifications_table(notification_items, options)
271
344
  print_results_pagination(notifications_json_response, {:label => "notification", :n_label => "notifications"})
@@ -374,14 +447,14 @@ class Morpheus::Cli::MonitoringIncidentsCommand
374
447
  return 0
375
448
  end
376
449
  notification_items = json_response['notifications']
377
- title = "Incident Notifications: #{incident['id']}: #{incident['displayName'] || incident['name']}"
450
+ title = "Incident Notifications [#{incident['id']}] #{incident['displayName'] || incident['name']}"
378
451
  subtitles = []
379
452
  subtitles += parse_list_subtitles(options)
380
453
  print_h1 title, subtitles
381
454
  if notification_items.empty?
382
455
  print cyan,"No notifications found.",reset,"\n"
383
456
  else
384
- print_incident_history_table(notification_items, options)
457
+ print_incident_notifications_table(notification_items, options)
385
458
  print_results_pagination(json_response, {:label => "notification", :n_label => "notifications"})
386
459
  end
387
460
  print reset,"\n"
@@ -396,7 +469,7 @@ class Morpheus::Cli::MonitoringIncidentsCommand
396
469
  params = {}
397
470
  optparse = Morpheus::Cli::OptionParser.new do |opts|
398
471
  opts.banner = subcommand_usage("[id]")
399
- opts.on("-c", "--comment STRING", String, "Comment on this incident") do |val|
472
+ opts.on("-c", "--comment STRING", String, "Comment on this incident. Updates summary field.") do |val|
400
473
  params['comment'] = val == 'null' ? nil : val
401
474
  end
402
475
  opts.on("--resolution STRING", String, "Description of the resolution to this incident") do |val|
@@ -467,7 +540,7 @@ class Morpheus::Cli::MonitoringIncidentsCommand
467
540
  puts as_json(json_response, options)
468
541
  elsif !options[:quiet]
469
542
  print_green_success "Updated incident #{incident['id']}"
470
- _get(incident['id'], {})
543
+ _get(incident['id'], options)
471
544
  end
472
545
 
473
546
  rescue RestClient::Exception => e
@@ -479,10 +552,11 @@ class Morpheus::Cli::MonitoringIncidentsCommand
479
552
 
480
553
  def mute(args)
481
554
  options = {}
482
- params = {'enabled' => true}
555
+ params = {}
483
556
  optparse = Morpheus::Cli::OptionParser.new do |opts|
484
557
  opts.banner = subcommand_usage("[id]")
485
558
  opts.on(nil, "--disable", "Disable mute state instead, the same as unmute") do
559
+ params['muted'] = false
486
560
  params['enabled'] = false
487
561
  end
488
562
  build_common_options(opts, options, [:options, :payload, :json, :dry_run, :quiet, :remote])
@@ -518,7 +592,7 @@ class Morpheus::Cli::MonitoringIncidentsCommand
518
592
  else
519
593
  print_green_success "Unmuted incident #{incident['id']}"
520
594
  end
521
- _get(incident['id'], {})
595
+ _get(incident['id'], options)
522
596
  end
523
597
  return 0
524
598
  rescue RestClient::Exception => e
@@ -529,7 +603,7 @@ class Morpheus::Cli::MonitoringIncidentsCommand
529
603
 
530
604
  def unmute(args)
531
605
  options = {}
532
- params = {'enabled' => false}
606
+ params = {'muted' => false, 'enabled' => false} # enabled was used pre 3.6.5
533
607
  optparse = Morpheus::Cli::OptionParser.new do |opts|
534
608
  opts.banner = subcommand_usage("[id]")
535
609
  build_common_options(opts, options, [:payload, :json, :dry_run, :quiet, :remote])
@@ -562,7 +636,7 @@ class Morpheus::Cli::MonitoringIncidentsCommand
562
636
  puts as_json(json_response, options)
563
637
  elsif !options[:quiet]
564
638
  print_green_success "Unmuted incident #{incident['id']}"
565
- _get(incident['id'], {})
639
+ _get(incident['id'], options)
566
640
  end
567
641
  return 0
568
642
  rescue RestClient::Exception => e
@@ -573,7 +647,7 @@ class Morpheus::Cli::MonitoringIncidentsCommand
573
647
 
574
648
  def mute_all(args)
575
649
  options = {}
576
- params = {'enabled' => true}
650
+ params = {}
577
651
  optparse = Morpheus::Cli::OptionParser.new do |opts|
578
652
  opts.banner = subcommand_usage()
579
653
  opts.on(nil, "--disable", "Disable mute state instead, the same as unmute-all") do
@@ -621,7 +695,7 @@ class Morpheus::Cli::MonitoringIncidentsCommand
621
695
 
622
696
  def unmute_all(args)
623
697
  options = {}
624
- params = {'enabled' => false}
698
+ params = {'muted' => false, 'enabled' => false}
625
699
  optparse = Morpheus::Cli::OptionParser.new do |opts|
626
700
  opts.banner = subcommand_usage()
627
701
  build_common_options(opts, options, [:payload, :json, :dry_run, :quiet, :remote])
@@ -698,7 +772,7 @@ class Morpheus::Cli::MonitoringIncidentsCommand
698
772
  print "\n"
699
773
  elsif !options[:quiet]
700
774
  print_green_success json_response["msg"] || "Incident #{incident['id']} is now closed"
701
- # _get(incident['id'] {})
775
+ # _get(incident['id'] options)
702
776
  end
703
777
  rescue RestClient::Exception => e
704
778
  print_rest_exception(e, options)
@@ -747,7 +821,7 @@ class Morpheus::Cli::MonitoringIncidentsCommand
747
821
  print "\n"
748
822
  elsif !options[:quiet]
749
823
  print_green_success json_response["msg"] || "Incident #{incident['id']} is now open"
750
- # _get(incident['id'] {})
824
+ # _get(incident['id'] options)
751
825
  end
752
826
  rescue RestClient::Exception => e
753
827
  print_rest_exception(e, options)
@@ -757,58 +831,5 @@ class Morpheus::Cli::MonitoringIncidentsCommand
757
831
 
758
832
  private
759
833
 
760
- def print_incidents_table(incidents, opts={})
761
- columns = [
762
- {"ID" => lambda {|incident| incident['id'] } },
763
- {"SEVERITY" => lambda {|incident| format_severity(incident['severity']) } },
764
- {"NAME" => lambda {|incident| incident['displayName'] || incident['name'] || 'No Subject' } },
765
- {"TIME" => lambda {|incident| format_local_dt(incident['startDate']) } },
766
- {"STATUS" => lambda {|incident| format_monitoring_incident_status(incident) } },
767
- {"DURATION" => lambda {|incident| format_duration(incident['startDate'], incident['endDate']) } }
768
- ]
769
- if opts[:include_fields]
770
- columns = opts[:include_fields]
771
- end
772
- print as_pretty_table(incidents, columns, opts)
773
- end
774
-
775
- def print_incident_history_table(history_items, opts={})
776
- columns = [
777
- # {"ID" => lambda {|issue| issue['id'] } },
778
- {"SEVERITY" => lambda {|issue| format_severity(issue['severity']) } },
779
- {"AVAILABLE" => lambda {|issue| format_boolean issue['available'] } },
780
- {"TYPE" => lambda {|issue| issue["attachmentType"] } },
781
- {"NAME" => lambda {|issue| issue['name'] } },
782
- {"DATE CREATED" => lambda {|issue| format_local_dt(issue['startDate']) } }
783
- ]
784
- if opts[:include_fields]
785
- columns = opts[:include_fields]
786
- end
787
- print as_pretty_table(history_items, columns, opts)
788
- end
789
-
790
- def print_incident_notifications_table(notifications, opts={})
791
- columns = [
792
- {"NAME" => lambda {|notification| notification['recipient'] ? notification['recipient']['name'] : '' } },
793
- {"DELIVERY TYPE" => lambda {|notification| notification['addressTypes'].to_s } },
794
- {"NOTIFIED ON" => lambda {|notification| format_local_dt(notification['dateCreated']) } },
795
- # {"AVAILABLE" => lambda {|notification| format_boolean notification['available'] } },
796
- # {"TYPE" => lambda {|notification| notification["attachmentType"] } },
797
- # {"NAME" => lambda {|notification| notification['name'] } },
798
- {"DATE CREATED" => lambda {|notification|
799
- date_str = format_local_dt(notification['startDate']).to_s
800
- if notification['pendingUtil']
801
- "(pending) #{date_str}"
802
- else
803
- date_str
804
- end
805
- } }
806
- ]
807
- #event['pendingUntil']
808
- if opts[:include_fields]
809
- columns = opts[:include_fields]
810
- end
811
- print as_pretty_table(notifications, columns, opts)
812
- end
813
834
 
814
835
  end
@@ -229,14 +229,20 @@ class Morpheus::Cli::NetworkPoolsCommand
229
229
  end
230
230
 
231
231
  # Network Pool Type
232
- network_type_id = nil
232
+ network_type_code = nil
233
233
  v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'type', 'fieldLabel' => 'Pool Type', 'type' => 'select', 'optionSource' => 'networkPoolTypes', 'required' => true, 'description' => 'Choose a network pool type.'}], options, @api_client, {})
234
- network_type_id = v_prompt['type']
235
- if network_type_id.nil? || network_type_id.to_s.empty?
236
- print_red_alert "Pool Type not found by id '#{options['type']}'"
234
+ network_type_code = v_prompt['type']
235
+ if network_type_code.nil? || network_type_code.to_s.empty?
236
+ print_red_alert "Pool Type not found by code '#{options['type']}'"
237
237
  return 1
238
238
  end
239
- payload['networkPool']['type'] = {'id' => network_type_id.to_i }
239
+ # pre 4.1.1 expects ID
240
+ if network_type_code.to_s =~ /\A\d{1,}\Z/
241
+ payload['networkPool']['type'] = {'id' => network_type_code }
242
+ else
243
+ payload['networkPool']['type'] = {'code' => network_type_code }
244
+ end
245
+ # payload['networkPool']['type'] = network_type_code # this works too, simpler
240
246
 
241
247
  # IP Ranges
242
248
  if ip_range_list
@@ -283,9 +289,29 @@ class Morpheus::Cli::NetworkPoolsCommand
283
289
  opts.on('--name VALUE', String, "Name for this network pool") do |val|
284
290
  options['name'] = val
285
291
  end
286
- opts.on('--type VALUE', String, "Type of network pool") do |val|
287
- options['description'] = val
288
- end
292
+ opts.on('--code VALUE', String, "Code") do |val|
293
+ options['code'] = val
294
+ end
295
+ opts.on('--category VALUE', String, "Category") do |val|
296
+ options['category'] = val
297
+ end
298
+ # todo all of these
299
+ # internalId
300
+ # externalId
301
+ # dnsDomain
302
+ # dnsSearchPath
303
+ # hostPrefix
304
+ # httpProxy
305
+ # dnsServers
306
+ # dnsSuffixList
307
+ # dhcpServer
308
+ # dhcpIp
309
+ # gateway
310
+ # netmask
311
+ # subnetAddress
312
+ # poolEnabled
313
+ # tftpServer
314
+ # bootFile
289
315
  opts.on('--ip-ranges LIST', Array, "IP Ranges, comma separated list IP ranges in the format start-end.") do |list|
290
316
  if list.size == 1 && list[0] == 'null' # hacky way to clear it
291
317
  ip_range_list = []
@@ -333,22 +359,12 @@ class Morpheus::Cli::NetworkPoolsCommand
333
359
  # Name
334
360
  if options['name']
335
361
  payload['networkPool']['name'] = options['name']
336
- else
337
- # v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'name', 'fieldLabel' => 'Name', 'type' => 'text', 'required' => true, 'description' => 'Name for this network pool.'}], options)
338
- # payload['networkPool']['name'] = v_prompt['name']
339
362
  end
340
-
341
- # Network Pool Type
342
- # network_type_id = nil
343
- # v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'type', 'fieldLabel' => 'Pool Type', 'type' => 'select', 'optionSource' => 'networkPoolTypes', 'required' => true, 'description' => 'Choose a network pool type.'}], options, @api_client, {})
344
- # network_type_id = v_prompt['type']
345
- # if network_type_id.nil? || network_type_id.to_s.empty?
346
- # print_red_alert "Pool Type not found by id '#{options['type']}'"
347
- # return 1
348
- # end
349
- # payload['networkPool']['type'] = {'id' => network_type_id.to_i }
350
- if options['type']
351
- payload['networkPool']['type'] = {'id' => options['type'].to_i }
363
+ if options['category']
364
+ payload['networkPool']['category'] = options['category']
365
+ end
366
+ if options['code']
367
+ payload['networkPool']['code'] = options['code']
352
368
  end
353
369
 
354
370
  # IP Ranges
@@ -745,7 +745,7 @@ class Morpheus::Cli::PowerSchedulesCommand
745
745
  end
746
746
 
747
747
  def find_instance_by_name(name)
748
- instances = @instances_interface.get({name: name.to_s})['instances']
748
+ instances = @instances_interface.list({name: name.to_s})['instances']
749
749
  if instances.empty?
750
750
  print_red_alert "Instance not found by name #{name}"
751
751
  return nil
@@ -11,31 +11,35 @@ require 'morpheus/cli/cli_command'
11
11
  class Morpheus::Cli::Remote
12
12
  include Morpheus::Cli::CliCommand
13
13
 
14
- register_subcommands :list, :add, :get, :update, :remove, :use, :unuse, :current, :setup, :check
14
+ register_subcommands :list, :add, :get, :update, :rename, :remove, :use, :unuse, :current
15
+ register_subcommands :setup, :teardown, :check, :'check-all'
16
+
15
17
  set_default_subcommand :list
16
18
 
17
19
  def initialize()
18
20
  @appliance_name, @appliance_url = Morpheus::Cli::Remote.active_appliance
19
21
  end
20
22
 
23
+ def connect(opts={})
24
+ connect_opts = {:skip_verify_access_token => true}.merge(opts)
25
+ @api_client = establish_remote_appliance_connection(connect_opts)
26
+ @setup_interface = @api_client.setup
27
+ end
28
+
21
29
  def handle(args)
22
- # if args.count == 0
23
- # list(args)
24
- # else
25
- # handle_subcommand(args)
26
- # end
27
30
  handle_subcommand(args)
28
31
  end
29
32
 
30
33
  def list(args)
31
34
  options = {}
35
+ params = {}
32
36
  show_all_activity = false
33
37
  optparse = Morpheus::Cli::OptionParser.new do|opts|
34
38
  opts.banner = subcommand_usage()
35
39
  opts.on("-a",'--all', "Show all the appliance activity details") do
36
40
  show_all_activity = true
37
41
  end
38
- build_common_options(opts, options, [:json, :yaml, :csv, :fields])
42
+ build_common_options(opts, options, [:list, :json, :yaml, :csv, :fields])
39
43
  opts.footer = <<-EOT
40
44
  This outputs a list of the configured remote appliances. It also indicates
41
45
  the current appliance. The current appliance is where morpheus will send
@@ -44,10 +48,11 @@ EOT
44
48
  end
45
49
  optparse.parse!(args)
46
50
  if args.count > 0
47
- raise ::OptionParser::NeedlessArgument.new("#{args.join(' ')}")
51
+ raise_command_error "wrong number of arguments, expected 0 and got (#{args.count}) #{args.join(' ')}\n#{optparse}"
48
52
  end
49
- @appliance_name, @appliance_url = Morpheus::Cli::Remote.active_appliance
50
- appliances = ::Morpheus::Cli::Remote.load_all_remotes({})
53
+ #connect(options)
54
+ params.merge!(parse_list_options(options))
55
+ appliances = ::Morpheus::Cli::Remote.load_all_remotes(params)
51
56
  # if appliances.empty?
52
57
  # raise_command_error "You have no appliances configured. See the `remote add` command."
53
58
  # end
@@ -63,13 +68,16 @@ EOT
63
68
  puts records_as_csv(appliances, options)
64
69
  return 0
65
70
  end
66
-
67
- print_h1 "Morpheus Appliances", [], options
68
71
  if appliances.empty?
69
- print yellow
70
- puts "You have no appliances configured. See the `remote add` command."
71
- print reset, "\n"
72
+ if params[:phrase]
73
+ print cyan,"0 remotes matched '#{params[:phrase]}'", reset, "\n"
74
+ else
75
+ print yellow,"You have no appliances configured. See the `remote add` command.", reset, "\n"
76
+ end
72
77
  else
78
+ title = "Morpheus Appliances"
79
+ subtitles = parse_list_subtitles(options)
80
+ print_h1 title, subtitles, options
73
81
  print cyan
74
82
  columns = [
75
83
  {:active => {:display_name => "", :display_method => lambda {|it| it[:active] ? "=>" : "" } } },
@@ -89,7 +97,7 @@ EOT
89
97
  print cyan, "\n# => #{@appliance_name} is the current remote appliance\n", reset
90
98
  #end
91
99
  else
92
- print "\n# => No current remote appliance, see `remote use`\n", reset
100
+ print cyan, "\n# => No current remote appliance, see `remote use`\n", reset
93
101
  end
94
102
  print reset, "\n"
95
103
  end
@@ -102,12 +110,12 @@ EOT
102
110
  params = {}
103
111
  new_appliance_map = {}
104
112
  use_it = false
105
- is_insecure = nil
113
+ secure = nil
106
114
  optparse = Morpheus::Cli::OptionParser.new do|opts|
107
115
  banner = subcommand_usage("[name] [url]")
108
116
  banner_args = <<-EOT
109
117
  [name] The name for your appliance. eg. mymorph
110
- [url] The url of your appliance eg. https://morpheus.mycompany.com
118
+ [url] The url of your appliance eg. https://demo.mymorpheus.com
111
119
  EOT
112
120
  opts.banner = banner + "\n" + banner_args
113
121
  opts.on(nil, '--use', "Make this the current appliance" ) do
@@ -120,26 +128,23 @@ EOT
120
128
  new_appliance_map[:active] = true
121
129
  end
122
130
  opts.on(nil, "--secure", "Prevent insecure HTTPS communication. This is enabled by default.") do
123
- params[:secure] = true
131
+ secure = true
124
132
  end
125
133
  opts.on(nil, "--insecure", "Allow insecure HTTPS communication. i.e. Ignore SSL errors.") do
126
- params[:insecure] = true
134
+ secure = false
127
135
  end
128
136
  build_common_options(opts, options, [:quiet])
129
137
  opts.footer = <<-EOT
130
138
  This will add a new remote appliance to your morpheus client configuration.
131
- If the new remote is your one and only, --use is automatically applied and
132
- it will be made the current remote appliance.
139
+ If this is your first remote, --use is automatically applied so
140
+ it will become the current remote appliance.
133
141
  This command will prompt you to login and/or setup a fresh appliance.
134
- Prompting can be skipped with use of the --quiet option.
142
+ To skip login/setup, use the --quiet option.
135
143
  EOT
136
144
  end
137
145
  optparse.parse!(args)
138
- if args.count < 2
139
- print_error Morpheus::Terminal.angry_prompt
140
- puts_error "#{command_name} add expects 2 arguments: [name] [url]"
141
- puts_error optparse
142
- return 1
146
+ if args.count > 2
147
+ raise_command_error "wrong number of arguments, expected 0-2 and got (#{args.count}) #{args.join(' ')}\n#{optparse}"
143
148
  end
144
149
 
145
150
  # load current appliances
@@ -150,40 +155,75 @@ EOT
150
155
  new_appliance_map[:active] = true
151
156
  end
152
157
 
153
- # validate options
154
- # construct new appliance map
155
- # and save it in the config file
156
- new_appliance_name = args[0].to_sym
158
+ new_appliance_name = args[0] if args[0]
159
+ url = args[1] if args[1]
157
160
 
158
- # for the sake of sanity
159
- if [:current, :all].include?(new_appliance_name)
160
- raise_command_error "The specified appliance name is invalid: '#{args[0]}'"
161
- end
162
- # unique name
163
- if appliances[new_appliance_name] != nil
164
- raise_command_error "Remote appliance already configured with the name '#{args[0]}'"
161
+ # Name
162
+ still_prompting = true
163
+ while still_prompting do
164
+ if new_appliance_name.to_s.empty?
165
+ v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'name', 'fieldLabel' => 'Name', 'type' => 'text', 'required' => true, 'description' => 'A unique name for the remote Morpheus appliance. Example: local'}], options[:options])
166
+ new_appliance_name = v_prompt['name']
167
+ end
168
+
169
+ # for the sake of sanity
170
+ if [:current, :all].include?(new_appliance_name.to_sym)
171
+ raise_command_error "The specified appliance name '#{new_appliance_name}' is invalid."
172
+ new_appliance_name = nil
173
+ end
174
+ # unique name
175
+ existing_appliance = appliances[new_appliance_name.to_sym]
176
+ if existing_appliance
177
+ print_error red,"The specified appliance name '#{new_appliance_name}' already exists with the URL #{existing_appliance[:url] || existing_appliance[:host]}",reset,"\n"
178
+ new_appliance_name = nil
179
+ end
180
+
181
+ if new_appliance_name.to_s.empty?
182
+ if options[:no_prompt]
183
+ return 1
184
+ end
185
+ still_prompting = true
186
+ else
187
+ still_prompting = false
188
+ end
165
189
  end
166
- new_appliance_map[:name] = new_appliance_name
167
190
 
168
- if params[:insecure]
169
- new_appliance_map[:insecure] = true
170
- elsif params[:secure]
171
- new_appliance_map.delete(:insecure)
191
+ new_appliance_map[:name] = new_appliance_name.to_sym
192
+
193
+ # URL
194
+ still_prompting = true
195
+ while still_prompting do
196
+ if !url
197
+ v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'url', 'fieldLabel' => 'URL', 'type' => 'text', 'required' => true, 'description' => 'The URL of the remote Morpheus appliance. Example: https://10.0.2.2'}], options[:options])
198
+ url = v_prompt['url']
199
+ end
200
+
201
+ if url !~ /^https?\:\/\/.+/
202
+ print_error red,"The specified appliance url '#{url}' is invalid.",reset,"\n"
203
+ still_prompting = true
204
+ url = nil
205
+ else
206
+ still_prompting = false
207
+ end
172
208
  end
173
- if params[:url] || params[:host]
174
- url = params[:url] || params[:host]
209
+
210
+ # let's replace :host with :url
211
+ new_appliance_map[:host] = url
212
+ new_appliance_map[:url] = url
213
+
214
+ # Insecure?
215
+ if url.include?('https:') && secure.nil?
216
+ # This is kind of annoying to always see, just default to true, use --insecure if you need to.
217
+ #v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'secure', 'fieldLabel' => 'Secure', 'type' => 'checkbox', 'required' => false, 'defaultValue' => true, 'description' => 'Prevent insecure HTTPS communication. This is enabled by default.'}], options[:options])
218
+ #secure = v_prompt['secure'].to_s == 'true' || v_prompt['secure'].to_s == 'on'
175
219
  end
176
220
 
177
- url = args[1]
178
- if url !~ /^https?\:\/\/.+/
179
- raise_command_error "The specified appliance url is invalid: '#{args[1]}'"
180
- #puts optparse
181
- return 1
221
+ if secure == false
222
+ new_appliance_map[:insecure] = true
182
223
  end
183
- new_appliance_map[:host] = url
184
224
 
185
225
  # save it
186
- appliance = ::Morpheus::Cli::Remote.save_remote(new_appliance_name, new_appliance_map)
226
+ appliance = ::Morpheus::Cli::Remote.save_remote(new_appliance_name.to_sym, new_appliance_map)
187
227
 
188
228
  if !options[:quiet]
189
229
  # print_green_success "Added remote #{new_appliance_name}"
@@ -195,7 +235,7 @@ EOT
195
235
  print cyan
196
236
  puts "Inspecting remote appliance url: #{appliance[:host]} ..."
197
237
  end
198
- appliance = ::Morpheus::Cli::Remote.refresh_remote(new_appliance_name)
238
+ appliance, check_json_response = ::Morpheus::Cli::Remote.refresh_remote(new_appliance_name.to_sym)
199
239
  if !options[:quiet]
200
240
  print cyan
201
241
  puts "Status is: #{format_appliance_status(appliance)}"
@@ -215,10 +255,23 @@ EOT
215
255
  return exit_code, err
216
256
  end
217
257
 
258
+ if options[:json]
259
+ puts as_json(check_json_response, options)
260
+ return exit_code, err
261
+ end
262
+
218
263
  # check_cmd_result = check_appliance([new_appliance_name, "--quiet"])
219
264
  # check_cmd_result = check_appliance([new_appliance_name])
220
265
 
221
266
  if appliance[:status] == 'fresh' # || appliance[:setup_needed] == true
267
+
268
+ if !appliance[:active]
269
+ if ::Morpheus::Cli::OptionTypes::confirm("Would you like to switch to using this remote now?", options.merge({default: true}))
270
+ use([appliance[:name]])
271
+ appliance[:active] = true # just in case, could reload instead with load_active_remote()
272
+ end
273
+ end
274
+
222
275
  print cyan
223
276
  puts "It looks like this appliance needs to be setup. Starting setup ..."
224
277
  return setup([new_appliance_name])
@@ -274,58 +327,33 @@ EOT
274
327
  options = {}
275
328
  checkall = false
276
329
  optparse = Morpheus::Cli::OptionParser.new do|opts|
277
- opts.banner = <<-EOT
278
- #{subcommand_usage("[name]")}
279
- [name] is required. This is the name of the remote. Use 'current' to check the active appliance."
280
- EOT
281
- #opts.banner = "#{opts.banner}\n" + " " + "[name] is required. This is the name of the remote. Use 'current' to check the active appliance."
282
- opts.on("-a",'--all', "Refresh all appliances") do
283
- checkall = true
284
- end
285
- build_common_options(opts, options, [:quiet])
330
+ opts.banner = subcommand_usage("[name]")
331
+ build_common_options(opts, options, [:json, :yaml, :csv, :fields, :quiet, :dry_run, :remote])
286
332
  opts.footer = <<-EOT
287
- This can be used to refresh a remote appliance.
288
- It makes an api request to the configured appliance url to check the status and version.
289
- [name] is required. This is the name of the remote. Use 'current' to check the active appliance."
333
+ Check the status of a remote appliance.
334
+ [name] is optional. This is the name of a remote. Default is the current remote. Can be passed as 'all'. to perform remote check-all.
335
+ This makes a request to the configured appliance url and updates the status and version.
290
336
  EOT
291
337
  end
292
338
  optparse.parse!(args)
293
- id_list = nil
294
- checkall = true if args[0] == "all" and args.size == 1 # sure, why not
295
- if checkall
296
- # id_list = ::Morpheus::Cli::Remote.appliances.keys # sort ?
297
- return _check_all_appliances()
298
- elsif args.count < 1
299
- print_error Morpheus::Terminal.angry_prompt
300
- puts_error "#{command_name} update expects argument [name] or option --all"
301
- puts_error optparse
302
- return 1
339
+
340
+ if args.count == 0
341
+ id_list = ['current']
303
342
  else
304
343
  id_list = parse_id_list(args)
305
344
  end
345
+ # trick for remote check all
346
+ if id_list.length == 1 && id_list[0].to_s.downcase == 'all'
347
+ return _check_all_appliances(options)
348
+ end
306
349
  #connect(options)
307
350
  return run_command_for_each_arg(id_list) do |arg|
308
351
  _check_appliance(arg, options)
309
352
  end
310
353
  end
311
354
 
312
- def _check_all_appliances()
313
- # reresh all appliances and then display the list view
314
- id_list = ::Morpheus::Cli::Remote.appliances.keys # sort ?
315
- if id_list.size > 1
316
- print cyan
317
- print "Checking #{id_list.size} appliances "
318
- end
319
- id_list.each do |appliance_name|
320
- print "."
321
- ::Morpheus::Cli::Remote.refresh_remote(appliance_name)
322
- end
323
- print "\n"
324
- list([])
325
-
326
- end
327
-
328
355
  def _check_appliance(appliance_name, options)
356
+ exit_code, err = 0, nil
329
357
  begin
330
358
  appliance = nil
331
359
  if appliance_name == "current"
@@ -337,35 +365,35 @@ EOT
337
365
  else
338
366
  appliance = ::Morpheus::Cli::Remote.load_remote(appliance_name)
339
367
  if !appliance
340
- raise_command_error "Remote appliance not found by the name '#{appliance_name}'"
368
+ raise_command_error "Remote not found by the name '#{appliance_name}'"
341
369
  end
342
370
  end
343
371
 
344
- # found appliance
345
- # now refresh it
372
+ # found appliance, now refresh it
346
373
 
347
374
  start_time = Time.now
348
375
 
349
- Morpheus::Logging::DarkPrinter.puts "checking remote appliance url: #{appliance[:host]} ..." if Morpheus::Logging.debug?
376
+ # print cyan
377
+ # print "Checking remote url: #{appliance[:host]} ..."
350
378
 
351
- appliance = ::Morpheus::Cli::Remote.refresh_remote(appliance_name)
379
+ appliance, check_json_response = ::Morpheus::Cli::Remote.refresh_remote(appliance_name)
352
380
 
353
381
  took_sec = (Time.now - start_time)
354
382
 
355
- if options[:quiet]
356
- return 0
357
- end
383
+ exit_code = (appliance[:status] == 'ready' || appliance[:status] == 'fresh') ? 0 : 1
358
384
 
359
- Morpheus::Logging::DarkPrinter.puts "remote appliance check completed in #{took_sec.round(3)}s" if Morpheus::Logging.debug?
385
+ if exit_code == 0
386
+ if appliance[:error]
387
+ exit_code = 1
388
+ err = "Check Failed: #{appliance[:error]}"
389
+ end
390
+ end
360
391
 
361
- # puts "remote #{appliance[:name]} status: #{format_appliance_status(appliance)}"
392
+ render_result = render_with_format(check_json_response, options)
393
+ return exit_code if render_result
362
394
 
363
- # if options[:json]
364
- # print JSON.pretty_generate(json_response), "\n"
365
- # return
366
- # end
395
+ print_green_success "Completed remote check of #{appliance_name} in #{took_sec.round(3)}s"
367
396
 
368
- # show user latest info
369
397
  return _get(appliance[:name], {})
370
398
 
371
399
  rescue RestClient::Exception => e
@@ -374,16 +402,119 @@ EOT
374
402
  end
375
403
  end
376
404
 
405
+ def check_all(args)
406
+ options = {}
407
+ checkall = false
408
+ optparse = Morpheus::Cli::OptionParser.new do|opts|
409
+ opts.banner = subcommand_usage()
410
+ build_common_options(opts, options, [:quiet])
411
+ opts.footer = <<-EOT
412
+ Refresh all remote appliances.
413
+ This makes a request to each of the configured appliance urls and updates the status and version.
414
+ EOT
415
+ end
416
+ optparse.parse!(args)
417
+
418
+ if args.count != 0
419
+ raise_command_error "wrong number of arguments, expected 0-1 and got (#{args.count}) #{args.join(' ')}\n#{optparse}"
420
+ end
421
+ connect() # needed?
422
+ _check_all_appliances(options)
423
+ end
424
+
425
+ def _check_all_appliances(options)
426
+ start_time = Time.now
427
+ # reresh all appliances and then display the list view
428
+ id_list = ::Morpheus::Cli::Remote.appliances.keys # sort ?
429
+ if id_list.size > 1
430
+ print cyan
431
+ puts "Checking #{id_list.size} appliances"
432
+ elsif id_list.size == 1
433
+ puts "Checking #{Morpheus::Cli::Remote.appliances.keys.first}"
434
+ end
435
+ id_list.each do |appliance_name|
436
+ #print "."
437
+ appliance, check_json_response = ::Morpheus::Cli::Remote.refresh_remote(appliance_name)
438
+ end
439
+ took_sec = (Time.now - start_time)
440
+ print_green_success "Completed remote check of #{id_list.size} #{id_list.size == 1 ? 'appliance' : 'appliances'} in #{took_sec.round(3)}s"
441
+
442
+ if options[:quiet]
443
+ return 0
444
+ end
445
+ list([])
446
+ return 0
447
+ end
448
+
449
+ def rename(args)
450
+ options = {}
451
+ params = {}
452
+ use_it = false
453
+ is_insecure = nil
454
+ new_name = nil
455
+ optparse = Morpheus::Cli::OptionParser.new do|opts|
456
+ opts.banner = subcommand_usage("[name] [new name]")
457
+ opts.on(nil, "--name NAME", "Update the name of your remote appliance") do |val|
458
+ new_name = val
459
+ end
460
+
461
+ # opts.on(nil, '--use', "Make this the current appliance" ) do
462
+ # use_it = true
463
+ # params[:active] = true
464
+ # end
465
+ build_common_options(opts, options, [:auto_confirm, :quiet])
466
+ opts.footer = <<-EOT
467
+ Rename a remote.
468
+ This changes your client configuration remote name, not the appliance itself.
469
+ [name] is required. This is the current name of a remote.
470
+ [new name] is required. This is the new name for the remote. This must not already be in use.
471
+ EOT
472
+ end
473
+ optparse.parse!(args)
474
+ if args.count != 2
475
+ print_error Morpheus::Terminal.angry_prompt
476
+ puts_error "#{command_name} rename expects argument [name]."
477
+ puts_error optparse
478
+ return 1
479
+ end
480
+ appliance_name = args[0].to_sym
481
+ new_appliance_name = args[1].to_sym
482
+ appliance = ::Morpheus::Cli::Remote.load_remote(appliance_name)
483
+ if !appliance
484
+ raise_command_error "Remote appliance not found by the name '#{appliance_name}'"
485
+ end
486
+ # don't allow overwrite yet
487
+ matching_appliance = ::Morpheus::Cli::Remote.load_remote(new_appliance_name)
488
+ if matching_appliance
489
+ raise_command_error "Remote appliance already exists with the name '#{new_appliance_name}'"
490
+ end
491
+
492
+ unless options[:yes] || ::Morpheus::Cli::OptionTypes::confirm("Are you sure you would like to rename #{appliance_name} to #{new_appliance_name}?", options)
493
+ return 9, "aborted command"
494
+ end
495
+ # this does all the work
496
+ ::Morpheus::Cli::Remote.rename_remote(appliance_name, new_appliance_name)
497
+
498
+ print_green_success "Renamed remote #{appliance_name} to #{new_appliance_name}"
499
+ # todo: just go ahead and refresh it now...
500
+ # _check(appliance_name, {:quiet => true})
501
+ # appliance, check_json_response = ::Morpheus::Cli::Remote.refresh_remote(new_appliance_name)
502
+ # print new appliance details
503
+ _get(new_appliance_name, {})
504
+ return 0, nil
505
+ end
506
+
377
507
  def update(args)
378
508
  options = {}
379
509
  params = {}
380
510
  use_it = false
381
511
  is_insecure = nil
512
+ new_name = nil
382
513
  optparse = Morpheus::Cli::OptionParser.new do|opts|
383
514
  opts.banner = subcommand_usage("[name]")
384
- # opts.on(nil, "--name STRING", "Update the name of your remote appliance") do |val|
385
- # params['name'] = val
386
- # end
515
+ opts.on(nil, "--name NAME", "Update the name of your remote appliance") do |val|
516
+ new_name = val
517
+ end
387
518
  opts.on("--url URL", String, "Update the url of your remote appliance") do |val|
388
519
  params[:host] = val
389
520
  end
@@ -437,7 +568,7 @@ EOT
437
568
  print_green_success "Updated remote #{appliance_name}"
438
569
  # todo: just go ahead and refresh it now...
439
570
  # _check(appliance_name, {:quiet => true})
440
- appliance = ::Morpheus::Cli::Remote.refresh_remote(appliance_name)
571
+ appliance, check_json_response = ::Morpheus::Cli::Remote.refresh_remote(appliance_name)
441
572
  # print new appliance details
442
573
  _get(appliance[:name], {})
443
574
  return 0, nil
@@ -467,37 +598,56 @@ EOT
467
598
  end
468
599
 
469
600
  def _get(appliance_name, options)
601
+ exit_code, err = 0, nil
470
602
  begin
471
603
  appliance = nil
472
604
  if appliance_name == "current"
473
605
  appliance = ::Morpheus::Cli::Remote.load_active_remote()
474
606
  if !appliance
475
- raise_command_error "No current appliance, see `remote use`."
607
+ err = "No current appliance, see `remote use`."
608
+ exit_code = 1
476
609
  end
477
610
  appliance_name = appliance[:name]
478
611
  else
479
612
  appliance = ::Morpheus::Cli::Remote.load_remote(appliance_name)
480
613
  if !appliance
481
- raise_command_error "Remote appliance not found by the name '#{appliance_name}'"
614
+ err = "Remote appliance not found by the name '#{appliance_name}'"
615
+ exit_code = 1
482
616
  end
483
617
  end
484
618
 
619
+ if options[:quiet]
620
+ return exit_code, err
621
+ end
622
+
485
623
  if options[:json]
486
- json_response = {remote_appliance: appliance} # mock payload
487
- puts as_json(json_response, options)
488
- return
624
+ json_response = {'appliance' => appliance} # mock payload
625
+ puts as_json(json_response, options, "appliance")
626
+ return exit_code, err
489
627
  end
490
628
 
491
- if options[:url_only]
492
- print cyan, appliance[:host],"\n",reset
493
- return 0
629
+ if options[:yaml]
630
+ json_response = {'appliance' => appliance} # mock payload
631
+ puts as_yaml(json_response, options, "appliance")
632
+ return exit_code, err
494
633
  end
495
- # expando
496
- # appliance = OStruct.new(appliance)
497
634
 
498
- # todo: just go ahead and refresh it now...
499
- # _check(appliance_name, {:quiet => true})
500
- # appliance = ::Morpheus::Cli::Remote.refresh_remote(appliance_name)
635
+
636
+
637
+ if options[:url_only]
638
+ if appliance
639
+ print cyan, (appliance[:url] || appliance[:host]),"\n",reset
640
+ return exit_code, err
641
+ else
642
+ print_error red, err,"\n",reset
643
+ return exit_code, err
644
+ end
645
+ end
646
+
647
+ if exit_code != 0
648
+ print_error red, err,"\n",reset
649
+ return exit_code, err
650
+ end
501
651
 
502
652
  if appliance[:active]
503
653
  # print_h1 "Current Remote Appliance: #{appliance[:name]}"
@@ -542,39 +692,58 @@ EOT
542
692
  options = {}
543
693
  optparse = Morpheus::Cli::OptionParser.new do|opts|
544
694
  opts.banner = subcommand_usage("[name]")
545
- # opts.on( '-f', '--force', "Remote appliance anyway??" ) do
546
- # options[:default] = true
547
- # end
548
- opts.footer = "This will delete an appliance from your list."
549
695
  build_common_options(opts, options, [:auto_confirm, :quiet])
696
+ opts.footer = <<-EOT
697
+ This will delete the specified remote appliance(s) from your local configuration.
698
+ [name] is required. This is the name of a remote. More than one can be passed.
699
+ EOT
550
700
  end
551
701
  optparse.parse!(args)
552
- if args.count < 1
553
- #raise_command_error "#{command_name} remove requires argument [name].", optparse
554
- print_error Morpheus::Terminal.angry_prompt
555
- puts_error "#{command_name} remove requires argument [name]."
556
- puts_error optparse
557
- return 1, nil
702
+ if args.count == 0
703
+ #id_list = ['current']
704
+ raise_command_error "wrong number of arguments, expected 1-N and got 0\n#{optparse}"
705
+ else
706
+ id_list = parse_id_list(args)
558
707
  end
559
- appliance_name = args[0].to_sym
708
+ #connect(options)
709
+ unless options[:yes] || ::Morpheus::Cli::OptionTypes::confirm("Are you sure you would like to delete #{id_list.size == 1 ? 'remote' : 'remotes'}: #{anded_list(id_list)}?", options)
710
+ return 9, "aborted command"
711
+ end
712
+ return run_command_for_each_arg(id_list) do |arg|
713
+ _remove_appliance(arg, options)
714
+ end
715
+ end
716
+
717
+ def _remove_appliance(appliance_name, options)
718
+
719
+ appliance_name = appliance_name.to_sym
560
720
  appliance = ::Morpheus::Cli::Remote.load_remote(appliance_name)
561
721
  if !appliance
562
722
  raise_command_error "Remote appliance not found by the name '#{appliance_name}'"
563
723
  end
564
- unless options[:yes] || ::Morpheus::Cli::OptionTypes::confirm("Are you sure you would like to delete '#{appliance_name}' from your list of remote appliances?", options)
565
- return 9, "aborted command" # new exit code for aborting confirmation
724
+
725
+
726
+ appliances = ::Morpheus::Cli::Remote.appliances
727
+
728
+ if appliances[appliance_name].nil?
729
+ if options[:quiet]
730
+ return 1
731
+ end
732
+ print_red_alert "Remote does not exist with name '#{appliance_name.to_s}'"
733
+ return 1
566
734
  end
567
735
 
568
736
  # ok, delete it
737
+
569
738
  ::Morpheus::Cli::Remote.delete_remote(appliance_name)
570
739
 
571
740
  # return result
572
741
  if options[:quiet]
573
- return 0, nil
742
+ return 0
574
743
  end
575
744
  print_green_success "Deleted remote #{appliance_name}"
576
- list([])
577
- return 0, nil
745
+ # list([])
746
+ return 0
578
747
  end
579
748
 
580
749
  def use(args)
@@ -587,36 +756,37 @@ EOT
587
756
  "You may override this with the --remote option in your commands."
588
757
  end
589
758
  optparse.parse!(args)
590
- if args.count < 1
759
+ if args.count != 1
591
760
  print_error Morpheus::Terminal.angry_prompt
592
761
  puts_error "#{command_name} use expects argument [name]."
593
762
  puts_error optparse
594
763
  return 1
595
764
  end
765
+ current_appliance_name, current_appliance_url = @appliance_name, @appliance_url
596
766
  appliance_name = args[0].to_sym
597
767
  appliance = ::Morpheus::Cli::Remote.load_remote(appliance_name)
598
768
  if !appliance
599
769
  raise_command_error "Remote appliance not found by the name '#{appliance_name}'"
600
770
  end
601
771
 
602
- if appliance[:active] == true
603
- if !options[:quiet]
604
- print cyan
605
- puts "Using remote #{appliance_name} (still)"
606
- end
607
- return true
608
- end
609
772
  # appliance = ::Morpheus::Cli::Remote.set_active_appliance(appliance_name)
610
773
  appliance[:active] = true
611
774
  appliance = ::Morpheus::Cli::Remote.save_remote(appliance_name, appliance)
775
+
776
+ if options[:quiet]
777
+ return 0
778
+ end
779
+
780
+ if current_appliance_name.to_s == appliance_name.to_s
781
+ print green, "Using remote #{appliance_name} (still)", reset, "\n"
782
+ else
783
+ print green, "Using remote #{appliance_name}", reset, "\n"
784
+ end
612
785
 
613
786
  # recalculate session variables
614
787
  ::Morpheus::Cli::Remote.recalculate_variable_map()
615
788
 
616
- if !options[:quiet]
617
- puts "#{cyan}Using remote #{appliance_name}#{reset}"
618
- end
619
- return true
789
+ return 0
620
790
  end
621
791
 
622
792
  def unuse(args)
@@ -624,18 +794,21 @@ EOT
624
794
  optparse = Morpheus::Cli::OptionParser.new do|opts|
625
795
  opts.banner = subcommand_usage()
626
796
  opts.footer = "" +
627
- "This clears the current remote appliance.\n" +
628
- "You will need to use an appliance, or pass the --remote option to your commands."
797
+ "This clears the current remote appliance.\n"
629
798
  build_common_options(opts, options, [])
630
799
  end
631
800
  optparse.parse!(args)
801
+ if args.count != 0
802
+ raise_command_error "wrong number of arguments, expected 0-1 and got (#{args.count}) #{args.join(' ')}\n#{optparse}"
803
+ end
804
+ #connect(options)
632
805
  @appliance_name, @appliance_url = Morpheus::Cli::Remote.active_appliance
633
806
  if !@appliance_name
634
- puts "You are not using any appliance"
635
- return false
807
+ print yellow,"You are not using any appliance",reset,"\n"
808
+ return 0
636
809
  end
637
810
  Morpheus::Cli::Remote.clear_active_appliance()
638
- puts "You are no longer using the appliance #{@appliance_name}"
811
+ print cyan, "You are no longer using the appliance #{@appliance_name}", reset, "\n"
639
812
  # recalculate session variables
640
813
  ::Morpheus::Cli::Remote.recalculate_variable_map()
641
814
  return true
@@ -658,12 +831,15 @@ EOT
658
831
  "The default behavior is the same as 'remote get current'."
659
832
  end
660
833
  optparse.parse!(args)
661
-
662
- if !@appliance_name
663
- print yellow, "No current appliance, see `remote use`\n", reset
664
- return 1
834
+ if args.count != 0
835
+ raise_command_error "wrong number of arguments, expected 0 and got (#{args.count}) #{args.join(' ')}\n#{optparse}"
665
836
  end
666
-
837
+ connect(options)
838
+ # if !@appliance_name
839
+ # print yellow, "No current appliance, see `remote use`\n", reset
840
+ # return 1
841
+ # end
842
+ #connect(options)
667
843
  if name_only
668
844
  print cyan, @appliance_name,"\n",reset
669
845
  return 0
@@ -683,67 +859,223 @@ EOT
683
859
  options = {}
684
860
  optparse = Morpheus::Cli::OptionParser.new do|opts|
685
861
  opts.banner = subcommand_usage()
686
- build_common_options(opts, options, [:options, :json, :dry_run])
687
- opts.on('-I','--insecure', "Allow insecure HTTPS communication. i.e. bad SSL certificate.") do |val|
688
- options[:insecure] = true
689
- Morpheus::RestClient.enable_ssl_verification = false
862
+ build_common_options(opts, options, [:payload, :options, :json, :dry_run, :quiet, :remote])
863
+ opts.on('--hubmode MODE','--hubmode MODE', "Choose an option for hub registration possible values are login, register, skip.") do |val|
864
+ options[:hubmode] = val.to_s.downcase
690
865
  end
691
- opts.footer = "This can be used to initialize a new appliance.\n" +
692
- "You will be prompted to create the master account.\n" +
693
- "This is only available on a new, freshly installed, remote appliance."
866
+ opts.footer = "Initialize a fresh appliance.\n" +
867
+ "You will be prompted to create the master tenant and admin user.\n" +
868
+ "If Morpheus Hub registration is enabled, you may login or register to retrieve a license key.\n" +
869
+ "Setup is only available on a new, freshly installed, remote appliance\n" +
870
+ "and it may only be used successfully once."
694
871
  end
695
872
  optparse.parse!(args)
873
+
874
+ # first arg as remote name otherwise the active appliance is connected to
875
+ if args.count > 1
876
+ raise_command_error "wrong number of arguments, expected 0-1 and got (#{args.count}) #{args.join(' ')}\n#{optparse}"
877
+ end
878
+ if args[0]
879
+ options[:remote] = args[0]
880
+ end
881
+ connect(options)
696
882
 
697
883
  if !@appliance_name
698
884
  print yellow, "No active appliance, see `remote use`\n", reset
699
885
  return false
700
886
  end
701
887
 
702
- # this works without any authentication!
703
- # it will allow anyone to use it, if there are no users/accounts in the system.
704
- #@api_client = establish_remote_appliance_connection(options)
705
- #@setup_interface = @api_client.setup
706
- @setup_interface = Morpheus::SetupInterface.new(@appliance_url)
707
- appliance_status_json = nil
708
- begin
709
- appliance_status_json = @setup_interface.get()
710
- if appliance_status_json['success'] != true
711
- print red, "Setup not available for appliance #{@appliance_name} - #{@appliance_url}.\n", reset
712
- print red, "#{appliance_status_json['msg']}\n", reset
888
+ # construct payload
889
+ payload = nil
890
+ if options[:payload]
891
+ payload = options[:payload]
892
+ else
893
+ params = {}
894
+ params.deep_merge!(options[:options].reject {|k,v| k.is_a?(Symbol) }) if options[:options]
895
+
896
+ # this works without any authentication!
897
+ # it will allow anyone to use it, if there are no users/accounts in the system.
898
+ #@api_client = establish_remote_appliance_connection(options)
899
+ @setup_interface = @api_client.setup #use me
900
+ # @setup_interface = Morpheus::SetupInterface.new({url:@appliance_url,access_token:@access_token})
901
+ appliance_status_json = nil
902
+ begin
903
+ appliance_status_json = @setup_interface.get()
904
+ if appliance_status_json['success'] != true
905
+ print_error red, "Setup not available for appliance #{@appliance_name} - #{@appliance_url}.\n", reset
906
+ print_error red, "#{appliance_status_json['msg']}\n", reset
907
+ return false
908
+ end
909
+ rescue RestClient::Exception => e
910
+ print_rest_exception(e, options)
713
911
  return false
714
912
  end
715
- rescue RestClient::Exception => e
716
- print_rest_exception(e, options)
717
- return false
718
- end
719
-
720
- payload = {}
721
-
722
- if appliance_status_json['hubRegistrationEnabled']
723
- link = File.join(@appliance_url, '/setup')
724
- print red, "Sorry, setup with hub registration is not yet available.\n", reset
725
- print "You can use the UI to setup your appliance.\n"
726
- print "Go to #{link}\n", reset
727
- # if RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/
728
- # system "start #{link}"
729
- # elsif RbConfig::CONFIG['host_os'] =~ /darwin/
730
- # system "open #{link}"
731
- # elsif RbConfig::CONFIG['host_os'] =~ /linux|bsd/
732
- # system "xdg-open #{link}"
733
- # end
734
- return false
735
- else
736
- print_h1 "Morpheus Appliance Setup", [], options
737
-
738
- puts "It looks like you're the first one here."
739
- puts "Let's initialize your remote appliance at #{@appliance_url}"
740
913
 
914
+ # retrieved hub.enabled and hub.url
915
+ hub_settings = appliance_status_json['hubSettings'] || appliance_status_json['hub'] || {}
741
916
 
917
+ # store login/registration info in here, for prompt default values
918
+ hub_info = nil
919
+ print cyan
920
+ print_h2 "Remote Setup: #{@appliance_name} - #{@appliance_url}"
742
921
 
922
+ print cyan
923
+ puts "Welcome to the setup of your new Morpheus Appliance #{@appliance_name} @ #{@appliance_url}"
924
+ puts "It looks like you're the first here, so let's begin."
925
+
926
+ hubmode = nil
927
+ hub_init_payload = nil # gets included as payload for hub scoped like hub.email
928
+ if hub_settings['enabled']
929
+
930
+ # Hub Registration
931
+ hub_action_dropdown = [
932
+ {'name' => 'Login to existing hub account', 'value' => 'login', 'isDefault' => true},
933
+ {'name' => 'Register a new hub account', 'value' => 'register'},
934
+ {'name' => 'Skip this step and manually install a license later.', 'value' => 'skip'},
935
+ {'name' => 'Abort', 'value' => 'abort'}
936
+ ]
937
+
938
+
939
+ print cyan
940
+ puts "Morpheus Hub registration is enabled for your appliance."
941
+ puts "This step will connect to the Morpheus Hub at #{hub_settings['url']}"
942
+ puts "This is done to retrieve and install the license key for your appliance."
943
+ puts "You have several options for how to proceed:"
944
+ hub_action_dropdown.each_with_index do |hub_action, idx|
945
+ puts "#{idx+1}. #{hub_action['name']} [#{hub_action['value']}]"
946
+ end
947
+ print "\n", reset
948
+
949
+ while hubmode == nil do
950
+
951
+ options[:options]['hubmode'] = options[:hubmode] if options.key?(:hubmode)
952
+ v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'hubmode', 'fieldLabel' => 'Choose Hub Mode', 'type' => 'select', 'selectOptions' => hub_action_dropdown, 'required' => true, 'defaultValue' => 'login'}], options[:options])
953
+ hubmode = v_prompt['hubmode']
954
+
955
+ if hubmode == 'login'
956
+
957
+ # print cyan
958
+ # puts "MORPHEUS HUB #{hub_settings['url']}"
959
+ # puts "The Command Center for DevOps"
960
+ # print reset
961
+
962
+ # Hub Login
963
+ print_h2 "Morpheus Hub Login @ #{hub_settings['url']}", options
964
+ hub_login_option_types = [
965
+ {'fieldContext' => 'hub', 'fieldName' => 'email', 'fieldLabel' => 'Email', 'type' => 'text', 'required' => true, 'description' => 'Email Address of existing Morpheus Hub user to link with.'},
966
+ {'fieldContext' => 'hub', 'fieldName' => 'password', 'fieldLabel' => 'Password', 'type' => 'password', 'required' => true, 'description' => 'Password of existing Morpheus Hub user.'},
967
+ ]
968
+ v_prompt = Morpheus::Cli::OptionTypes.prompt(hub_login_option_types, options[:options])
969
+ hub_login_payload = v_prompt['hub']
970
+ hub_login_response = nil
971
+ begin
972
+ hub_login_response = @setup_interface.hub_login(hub_login_payload)
973
+ hub_init_payload = hub_login_payload
974
+ hub_info = {'email' => hub_login_payload['email'], 'password' => hub_login_payload['password'] }
975
+ hub_info.deep_merge!(hub_login_response['data']['info']) if (hub_login_response['data'] && hub_login_response['data']['info'])
976
+ hub_info.deep_merge!(hub_login_response['hub']) if hub_login_response['hub'].is_a?(Hash)
977
+ print_green_success "Logged into Morpheus Hub as #{hub_info['email']}"
978
+ rescue RestClient::Exception => e
979
+ hub_login_response = parse_rest_exception(e)
980
+ error_msg = hub_login_response["msg"] || "Hub login failed."
981
+ print_error red,error_msg,reset,"\n"
982
+ hubmode = nil
983
+ #print_rest_exception(e, options)
984
+ #exit 1
985
+ end
986
+
987
+ # DEBUG
988
+ if options[:debug] && hub_login_response
989
+ print_h2 "JSON response for hub login"
990
+ Morpheus::Logging::DarkPrinter.puts as_json(hub_login_response)
991
+ end
992
+
993
+ elsif hubmode == 'register'
994
+ # Hub Registration
995
+ print_h2 "Morpheus Hub Registration", options
996
+ hub_register_option_types = [
997
+ {'fieldContext' => 'hub', 'fieldName' => 'companyName', 'fieldLabel' => 'Company Name', 'type' => 'text', 'required' => true, 'description' => 'Company Name of new Morpheus Hub account to be created.'},
998
+ {'fieldContext' => 'hub', 'fieldName' => 'firstName', 'fieldLabel' => 'First Name', 'type' => 'text', 'required' => true, 'description' => 'First Name of new Morpheus Hub user.'},
999
+ {'fieldContext' => 'hub', 'fieldName' => 'lastName', 'fieldLabel' => 'Last Name', 'type' => 'text', 'required' => true, 'description' => 'Last Name of new Morpheus Hub user.'},
1000
+ {'fieldContext' => 'hub', 'fieldName' => 'email', 'fieldLabel' => 'Email', 'type' => 'text', 'required' => true, 'description' => 'Email Address of new Morpheus Hub user.'}
1001
+ ]
1002
+ v_prompt = Morpheus::Cli::OptionTypes.prompt(hub_register_option_types, options[:options])
1003
+ hub_register_payload = v_prompt['hub']
1004
+
1005
+ # Password prompt with re-prompting if no match
1006
+ need_password = true
1007
+ if options[:no_prompt]
1008
+ if options[:options]['hub'] && options[:options]['hub']['password']
1009
+ options[:options]['hub']['confirmPassword'] = options[:options]['hub']['password']
1010
+ end
1011
+ end
1012
+ while need_password do
1013
+ password_option_types = [
1014
+ {'fieldContext' => 'hub', 'fieldName' => 'password', 'fieldLabel' => 'Create Password', 'type' => 'password', 'required' => true, 'description' => 'Confirm password of new Morpheus Hub user.'},
1015
+ {'fieldContext' => 'hub', 'fieldName' => 'confirmPassword', 'fieldLabel' => 'Confirm Password', 'type' => 'password', 'required' => true, 'description' => 'Confirm password of new Morpheus Hub user.'}
1016
+ ]
1017
+ v_prompt = Morpheus::Cli::OptionTypes.prompt(password_option_types, options[:options])
1018
+ if v_prompt['hub']['password'] == v_prompt['hub']['confirmPassword']
1019
+ hub_register_payload.deep_merge!(v_prompt['hub'])
1020
+ need_password = false
1021
+ else
1022
+ print_error red, "Password confirmation does not match. Re-enter your new password.", reset, "\n"
1023
+ end
1024
+ end
1025
+
1026
+ begin
1027
+ hub_register_response = @setup_interface.hub_register(hub_register_payload)
1028
+ hub_init_payload = hub_register_payload
1029
+ hub_info = {'email' => hub_register_payload['email'], 'password' => hub_register_payload['password'] }
1030
+ hub_info.deep_merge!(hub_register_payload)
1031
+ hub_info.deep_merge!(hub_register_response['data']['info']) if (hub_register_response['data'] && hub_register_response['data']['info'])
1032
+ hub_info.deep_merge!(hub_register_response['hub']) if hub_register_response['hub'].is_a?(Hash)
1033
+ print_green_success "Registered with Morpheus Hub as #{hub_info['email']}"
1034
+ # uh ok so that means the init() request can use login
1035
+ # this avoid duplicate email error
1036
+ # but it can also just omit hubMode from the init() payload to achieve the same thing.
1037
+ # hubmode = nil
1038
+ rescue RestClient::Exception => e
1039
+ hub_register_response = parse_rest_exception(e)
1040
+ error_msg = hub_register_response["msg"] || "Hub Registration failed."
1041
+ print_error red,error_msg,reset,"\n"
1042
+ hubmode = nil
1043
+ #print_rest_exception(e, options)
1044
+ #exit 1
1045
+ end
1046
+
1047
+ # DEBUG
1048
+ if options[:debug] && hub_register_response
1049
+ print_h2 "JSON response for hub registration"
1050
+ Morpheus::Logging::DarkPrinter.puts as_json(hub_register_response)
1051
+ end
1052
+
1053
+ elsif hubmode == 'skip'
1054
+ print cyan,"Skipping hub registraton for now...",reset,"\n"
1055
+ # puts "You may enter a license key later."
1056
+ elsif hubmode == 'abort'
1057
+ return 9, "aborted command"
1058
+ else
1059
+ hubmode = nil
1060
+ end
1061
+ end
1062
+ end
1063
+
1064
+ # ok, we're done with the hub.
1065
+ # now build the payload for POST /api/setup/init
1066
+
1067
+ payload = {}
1068
+ payload.deep_merge!(params)
1069
+
1070
+ # print cyan
1071
+ #print_h1 "Morpheus Appliance Setup", [], options
1072
+ #print cyan
1073
+ #puts "Initializing remote appliance at URL: #{@appliance_url}"
1074
+
743
1075
  # Master Account
744
- print_h2 "Create Master Account", options
1076
+ print_h2 "Create Master Tenant", options
745
1077
  account_option_types = [
746
- {'fieldName' => 'accountName', 'fieldLabel' => 'Master Account Name', 'type' => 'text', 'required' => true, 'displayOrder' => 1},
1078
+ {'fieldName' => 'accountName', 'fieldLabel' => 'Master Tenant Name', 'type' => 'text', 'required' => true, 'defaultValue' => (hub_info ? hub_info['companyName'] : nil), 'description' => 'A unique name for the Master Tenant (account).'},
747
1079
  ]
748
1080
  v_prompt = Morpheus::Cli::OptionTypes.prompt(account_option_types, options[:options])
749
1081
  payload.merge!(v_prompt)
@@ -751,88 +1083,207 @@ EOT
751
1083
  # Master User
752
1084
  print_h2 "Create Master User", options
753
1085
  user_option_types = [
754
- {'fieldName' => 'firstName', 'fieldLabel' => 'First Name', 'type' => 'text', 'required' => false, 'displayOrder' => 1},
755
- {'fieldName' => 'lastName', 'fieldLabel' => 'Last Name', 'type' => 'text', 'required' => false, 'displayOrder' => 2},
756
- {'fieldName' => 'username', 'fieldLabel' => 'Username', 'type' => 'text', 'required' => true, 'displayOrder' => 3},
757
- {'fieldName' => 'email', 'fieldLabel' => 'Email', 'type' => 'text', 'required' => true, 'displayOrder' => 4},
1086
+ {'fieldName' => 'firstName', 'fieldLabel' => 'First Name', 'type' => 'text', 'required' => false, 'defaultValue' => (hub_info ? hub_info['firstName'] : nil), 'description' => 'First name of the user.'},
1087
+ {'fieldName' => 'lastName', 'fieldLabel' => 'Last Name', 'type' => 'text', 'required' => false, 'defaultValue' => (hub_info ? hub_info['lastName'] : nil), 'description' => 'Last name of the user.'},
1088
+ {'fieldName' => 'email', 'fieldLabel' => 'Email', 'type' => 'text', 'required' => true, 'defaultValue' => (hub_info ? hub_info['email'] : nil), 'description' => 'A unique email address for the user.'},
1089
+ {'fieldName' => 'username', 'fieldLabel' => 'Username', 'type' => 'text', 'required' => true, 'description' => 'A unique username for the master user.'}
758
1090
  ]
759
1091
  v_prompt = Morpheus::Cli::OptionTypes.prompt(user_option_types, options[:options])
760
1092
  payload.merge!(v_prompt)
761
1093
 
762
1094
  # Password prompt with re-prompting if no match
763
- password_option_types = [
764
- {'fieldName' => 'password', 'fieldLabel' => 'Password', 'type' => 'password', 'required' => true, 'displayOrder' => 6},
765
- {'fieldName' => 'passwordConfirmation', 'fieldLabel' => 'Confirm Password', 'type' => 'password', 'required' => true, 'displayOrder' => 7},
766
- ]
767
- v_prompt = Morpheus::Cli::OptionTypes.prompt(password_option_types, options[:options])
768
- while v_prompt['passwordConfirmation'] != v_prompt['password']
769
- print red, "Password confirmation does not match. Re-enter your new password.", reset, "\n"
1095
+ need_password = true
1096
+ if options[:no_prompt]
1097
+ options[:options]['confirmPassword'] = payload['password']
1098
+ payload['confirmPassword'] = payload['password'] if payload['password']
1099
+ end
1100
+ while need_password do
1101
+ password_option_types = [
1102
+ {'fieldName' => 'password', 'fieldLabel' => 'Create Password', 'type' => 'password', 'required' => true, 'description' => 'Create a new password for the user.'},
1103
+ {'fieldName' => 'confirmPassword', 'fieldLabel' => 'Confirm Password', 'type' => 'password', 'required' => true, 'description' => 'Confirm the new password for the user.'},
1104
+ ]
770
1105
  v_prompt = Morpheus::Cli::OptionTypes.prompt(password_option_types, options[:options])
1106
+ if v_prompt['password'] == v_prompt['confirmPassword']
1107
+ payload.deep_merge!(v_prompt)
1108
+ need_password = false
1109
+ else
1110
+ print_error red, "Password confirmation does not match. Re-enter your new password.", reset, "\n"
1111
+ end
771
1112
  end
772
- payload.merge!(v_prompt)
773
1113
 
774
- # Extra settings
1114
+ # Appliance Settings
1115
+ default_appliance_url = appliance_status_json['applianceUrl']
1116
+ if default_appliance_url && default_appliance_url.include?('10.0.2.2:8080') # ignore this default value.
1117
+ default_appliance_url = @appliance_url
1118
+ end
1119
+ default_appliance_name = appliance_status_json['applianceName']
1120
+ if default_appliance_name.nil?
1121
+ default_appliance_name = @appliance_name
1122
+ end
775
1123
  print_h2 "Initial Setup", options
776
1124
  extra_option_types = [
777
- {'fieldName' => 'applianceName', 'fieldLabel' => 'Appliance Name', 'type' => 'text', 'required' => true, 'defaultValue' => nil},
778
- {'fieldName' => 'applianceUrl', 'fieldLabel' => 'Appliance URL', 'type' => 'text', 'required' => true, 'defaultValue' => appliance_status_json['applianceUrl']},
779
- {'fieldName' => 'backups', 'fieldLabel' => 'Enable Backups', 'type' => 'checkbox', 'required' => false, 'defaultValue' => 'off'},
780
- {'fieldName' => 'monitoring', 'fieldLabel' => 'Enable Monitoring', 'type' => 'checkbox', 'required' => false, 'defaultValue' => 'on'},
781
- {'fieldName' => 'logs', 'fieldLabel' => 'Enable Logs', 'type' => 'checkbox', 'required' => false, 'defaultValue' => 'on'}
1125
+ {'fieldName' => 'applianceName', 'fieldLabel' => 'Appliance Name', 'type' => 'text', 'required' => true, 'defaultValue' => default_appliance_name, 'description' => 'A name for identifying your morpheus appliance.'},
1126
+ {'fieldName' => 'applianceUrl', 'fieldLabel' => 'Appliance URL', 'type' => 'text', 'required' => true, 'defaultValue' => default_appliance_url, 'description' => 'Appliance URL. Can be used for integrations and callbacks.'},
1127
+ {'fieldName' => 'backups', 'fieldLabel' => 'Enable Backups', 'type' => 'checkbox', 'required' => false, 'defaultValue' => 'off', 'description' => 'Backups. Default is off. This means backups are created automatically during provisioning.'},
1128
+ {'fieldName' => 'monitoring', 'fieldLabel' => 'Enable Monitoring', 'type' => 'checkbox', 'required' => false, 'defaultValue' => 'on', 'description' => 'Enable Monitoring. This means checks are created automatically during provisioning.'},
1129
+ {'fieldName' => 'logs', 'fieldLabel' => 'Enable Logs', 'type' => 'checkbox', 'required' => false, 'defaultValue' => 'on', 'description' => 'Enable Logs. This means container logs are collected.'}
782
1130
  ]
783
1131
  v_prompt = Morpheus::Cli::OptionTypes.prompt(extra_option_types, options[:options])
784
1132
  payload.merge!(v_prompt)
785
-
786
- begin
787
- @setup_interface.setopts(options)
788
- if options[:dry_run]
789
- print_dry_run @setup_interface.dry.init(payload)
790
- return
791
- end
792
- if !options[:json]
793
- print "Initializing the appliance...\n"
794
- end
795
- json_response = @setup_interface.init(payload)
796
- rescue RestClient::Exception => e
797
- print_rest_exception(e, options)
798
- return false
1133
+
1134
+ # include hubmode and hub params for login or registration
1135
+ # actually we remove hubMode because it has already been setup, probably just now,
1136
+ # and the init() request will just used the same creds instead of
1137
+ # reauthenticated/registering with the hub
1138
+ if hubmode
1139
+ payload['hubMode'] = hubmode
799
1140
  end
800
-
801
- if options[:json]
802
- print JSON.pretty_generate(json_response)
803
- print "\n"
804
- return
1141
+ if hub_init_payload
1142
+ payload['hub'] = hub_init_payload
1143
+ end
1144
+ if hubmode == 'register' || hubmode == 'login'
1145
+ payload.delete('hubMode')
1146
+ payload.delete('hub')
805
1147
  end
806
- print "\n"
807
- print cyan, "You have successfully setup the appliance.\n"
808
- #print cyan, "You may now login with the command `login`.\n"
809
- # uh, just use Credentials.login(username, password, {save: true})
810
- cmd_res = Morpheus::Cli::Login.new.login(['--username', payload['username'], '--password', payload['password'], '-q'])
811
- # print "\n"
812
- print cyan, "You are now logged in as the System Admin #{payload['username']}.\n"
813
- print reset
814
- #print "\n"
815
1148
 
1149
+ end
1150
+
1151
+ # ok, make the api request
1152
+ @setup_interface.setopts(options)
1153
+ if options[:dry_run]
1154
+ print_dry_run @setup_interface.dry.init(payload)
1155
+ return
1156
+ end
1157
+
1158
+ json_response = @setup_interface.init(payload)
1159
+
1160
+ render_result = render_with_format(json_response, options)
1161
+ return 0 if render_result
1162
+
1163
+ if options[:json]
1164
+ print JSON.pretty_generate(json_response)
1165
+ print "\n"
1166
+ return
1167
+ end
1168
+ print "\n"
1169
+ print green,"Setup complete for remote #{@appliance_name} - #{@appliance_url}",reset,"\n"
1170
+ #print cyan, "You may now login with the command `login`.\n"
1171
+ # uh, just use Credentials.login(username, password, {save: true})
1172
+ cmd_res = Morpheus::Cli::Login.new.login(['--username', payload['username'], '--password', payload['password'], '-q'] + (options[:remote] ? ["-r",options[:remote]] : []))
1173
+ # print "\n"
1174
+ print cyan, "You are now logged in as the System Admin #{payload['username']}.\n"
1175
+ print reset
1176
+ #print "\n"
1177
+
1178
+ if hubmode == 'skip'
816
1179
  if ::Morpheus::Cli::OptionTypes::confirm("Would you like to apply your License Key now?", options.merge({:default => true}))
817
- cmd_res = Morpheus::Cli::License.new.apply([])
1180
+ cmd_res = Morpheus::Cli::License.new.apply([] + (options[:remote] ? ["-r",options[:remote]] : []))
818
1181
  # license_is_valid = cmd_res != false
819
1182
  end
1183
+ end
820
1184
 
821
- if ::Morpheus::Cli::OptionTypes::confirm("Do you want to create the first group now?", options.merge({:default => true}))
822
- cmd_res = Morpheus::Cli::Groups.new.add(['--use'])
1185
+ if ::Morpheus::Cli::OptionTypes::confirm("Do you want to create the first group now?", options.merge({:default => true}))
1186
+ cmd_res = Morpheus::Cli::Groups.new.add(['--use'] + (options[:remote] ? ["-r",options[:remote]] : []))
823
1187
 
824
- #print "\n"
1188
+ #print "\n"
825
1189
 
826
- # if cmd_res !=
827
- if ::Morpheus::Cli::OptionTypes::confirm("Do you want to create the first cloud now?", options.merge({:default => true}))
828
- cmd_res = Morpheus::Cli::Clouds.new.add([])
829
- #print "\n"
830
- end
831
- # end
1190
+ # if cmd_res !=
1191
+ if ::Morpheus::Cli::OptionTypes::confirm("Do you want to create the first cloud now?", options.merge({:default => true}))
1192
+ cmd_res = Morpheus::Cli::Clouds.new.add([] + (options[:remote] ? ["-r",options[:remote]] : []))
1193
+ #print "\n"
1194
+ end
1195
+ # end
1196
+ end
1197
+ print "\n",reset
1198
+
1199
+ end
1200
+
1201
+
1202
+ # this is just for testing new appliances really
1203
+ # it can be used
1204
+ def teardown(args)
1205
+ options = {}
1206
+ optparse = Morpheus::Cli::OptionParser.new do|opts|
1207
+ opts.banner = subcommand_usage()
1208
+ build_common_options(opts, options, [:json, :dry_run, :quiet, :remote])
1209
+ opts.footer = "Provides a way to uninitialize a fresh appliance. Useful for testing appliance setup."
1210
+ end
1211
+ optparse.parse!(args)
1212
+
1213
+ # first arg as remote name otherwise the active appliance is connected to
1214
+ if args.count > 1
1215
+ raise_command_error "wrong number of arguments, expected 0-1 and got (#{args.count}) #{args.join(' ')}\n#{optparse}"
1216
+ end
1217
+ if args[0]
1218
+ options[:remote] = args[0]
1219
+ end
1220
+ connect(options)
1221
+
1222
+ if !@appliance_name
1223
+ print yellow, "No active appliance, see `remote use`\n", reset
1224
+ return false
1225
+ end
1226
+
1227
+ unless options[:quiet]
1228
+ print yellow
1229
+ print "\n"
1230
+ puts "WARNING: You are about to reset your appliance installation."
1231
+ puts "It's only possible to perform teardown when the appliance has just been installed."
1232
+ puts "This provides a way to reset your appliance and run setup again."
1233
+ print reset
1234
+ print "\n"
1235
+ end
1236
+
1237
+ unless options[:yes] || ::Morpheus::Cli::OptionTypes::confirm("Are you sure you would like teardown appliance '#{@appliance_name}'.", options)
1238
+ return 9, "aborted command" # new exit code for aborting confirmation
1239
+ end
1240
+
1241
+ #@setup_interface = @api_client.setup
1242
+
1243
+ # construct payload
1244
+
1245
+ params = {}
1246
+ params.deep_merge!(options[:options].reject {|k,v| k.is_a?(Symbol) }) if options[:options]
1247
+
1248
+ # this works without any authentication!
1249
+ # it will allow anyone to use it, if there are no users/accounts in the system.
1250
+ #@api_client = establish_remote_appliance_connection(options)
1251
+ #@setup_interface = @api_client.setup
1252
+ @setup_interface = Morpheus::SetupInterface.new({url:@appliance_url,access_token:@access_token, very_ssl:false})
1253
+ json_response = nil
1254
+ begin
1255
+ json_response = @setup_interface.teardown(params)
1256
+ if json_response['success'] != true
1257
+ print_error red, (json_response['msg'] || "Teardown failed").to_s, reset, "\n"
1258
+ return false
832
1259
  end
833
- print "\n",reset
1260
+ rescue RestClient::Exception => e
1261
+ print_rest_exception(e, options)
1262
+ return false
1263
+ end
1264
+
1265
+
1266
+ # ok, make the api request and render the response or print a message
1267
+ @setup_interface.setopts(options)
1268
+ if options[:dry_run]
1269
+ print_dry_run @setup_interface.dry.teardown(params)
1270
+ return
1271
+ end
834
1272
 
1273
+ json_response = @setup_interface.teardown(params)
1274
+
1275
+ render_result = render_with_format(json_response, options)
1276
+ return 0 if render_result
1277
+ if options[:quiet]
1278
+ return 0
835
1279
  end
1280
+ if json_response['msg']
1281
+ print_green_success json_response['msg']
1282
+ else
1283
+ print_green_success "Teardown complete for remote #{@appliance_name} - #{@appliance_url}. Now see `remote setup`"
1284
+ end
1285
+ return 0
1286
+
836
1287
  end
837
1288
 
838
1289
  def format_appliance_status(app_map, return_color=cyan)
@@ -842,18 +1293,18 @@ EOT
842
1293
  out = ""
843
1294
  if status_str == "new"
844
1295
  out << "#{cyan}#{status_str.upcase}#{return_color}"
1296
+ elsif status_str == "fresh"
1297
+ # maybe just green instead?
1298
+ out << "#{magenta}#{status_str.upcase}#{return_color}"
845
1299
  elsif status_str == "ready"
846
1300
  out << "#{green}#{status_str.upcase}#{return_color}"
847
- elsif status_str == "unreachable"
1301
+ elsif status_str == "http-error"
1302
+ out << "#{red}#{status_str.upcase}#{return_color}"
1303
+ elsif ['error', 'net-error', 'ssl-error', 'http-timeout', 'unreachable', 'unrecognized'].include?(status_str)
848
1304
  out << "#{red}#{status_str.upcase}#{return_color}"
849
- elsif ['error', 'net-error', 'ssl-error', 'http-timeout', 'unreachable']
850
- out << "#{red}#{status_str.upcase.gsub('-',' ')}#{return_color}"
851
- elsif status_str == "fresh"
852
- # cold appliance, needs setup
853
- out << "#{magenta}#{status_str.upcase}#{return_color}"
854
1305
  else
855
1306
  # dunno
856
- out << "#{status_str}"
1307
+ out << "#{yellow}#{status_str.upcase}#{return_color}"
857
1308
  end
858
1309
  out
859
1310
  end
@@ -915,7 +1366,8 @@ EOT
915
1366
  blurbs << "Last checked #{format_duration(app_map[:last_check][:timestamp])} ago."
916
1367
  end
917
1368
  if app_map[:last_check][:error]
918
- blurbs << "Error: #{app_map[:last_check][:error]}"
1369
+ last_error_msg = truncate_string(app_map[:last_check][:error], 250)
1370
+ blurbs << "Error: #{last_error_msg}"
919
1371
  end
920
1372
  if app_map[:last_check][:http_status]
921
1373
  blurbs << "HTTP #{app_map[:last_check][:http_status]}"
@@ -987,6 +1439,39 @@ EOT
987
1439
  # last_command_result: v[:last_command_result]
988
1440
  # }
989
1441
  end
1442
+
1443
+ # filter results
1444
+ params[:phrase] = params['phrase'] if params['phrase']
1445
+ params[:name] = params['name'] if params['name']
1446
+ params[:url] = params['url'] if params['url']
1447
+ # params[:insecure] = params['insecure'] if params['insecure']
1448
+ params[:max] = params['max'] if params['max']
1449
+ params[:offset] = params['offset'] if params['offset']
1450
+ params[:sort] = params['sort'] if params['sort']
1451
+ params[:direction] = params['direction'] if params['direction']
1452
+
1453
+
1454
+ if all_appliances
1455
+ # apply filters
1456
+ if params[:phrase]
1457
+ all_appliances = all_appliances.select do |app|
1458
+ app_name = app[:name] || app['name']
1459
+ app_url = app[:url] || app['url'] || app[:host]
1460
+ app_name.to_s.include?(params[:phrase]) || app_url.to_s.include?(params[:phrase])
1461
+ end
1462
+ end
1463
+ # apply sort
1464
+ sort_key = params[:sort] ? params[:sort].to_sym : :name
1465
+ if params['direction'] == 'desc'
1466
+ all_appliances = all_appliances.sort {|a,b| b[sort_key] <=> a[sort_key] }
1467
+ else
1468
+ all_appliances = all_appliances.sort {|a,b| a[sort_key] <=> b[sort_key] }
1469
+ end
1470
+ # limit
1471
+ if params[:max]
1472
+ all_appliances = all_appliances.first(params[:max])
1473
+ end
1474
+ end
990
1475
  return all_appliances
991
1476
  end
992
1477
 
@@ -1110,6 +1595,76 @@ EOT
1110
1595
  return app_map
1111
1596
  end
1112
1597
 
1598
+ # use this to rename, it update appliances file and others.
1599
+ # oh maybe just put this in the command handler
1600
+ #
1601
+ # first check if the requested name exits
1602
+ # and that the new name does not exist.
1603
+ #
1604
+ # clone it and delete the old one.
1605
+ # todo: switch replace symbols with strings please, makes for nicer appliances.yaml
1606
+ def rename_remote(app_name, new_app_name)
1607
+ app_name = app_name.to_sym
1608
+ new_app_name = new_app_name.to_sym
1609
+ cur_appliances = self.appliances #.clone
1610
+ app_map = cur_appliances[app_name]
1611
+ if app_map.nil?
1612
+ print_red_alert "A remote not found by the name '#{app_name}'"
1613
+ #print "Did you mean one of these commands: #{suggestions.join(', ')?", reset, "\n"
1614
+ return nil
1615
+ end
1616
+ if cur_appliances[new_app_name]
1617
+ print_red_alert "A remote already exists with name '#{new_app_name}'."
1618
+ puts "First, you must rename or remove the existing remote."
1619
+ return nil
1620
+ end
1621
+
1622
+ # clone the existing data
1623
+
1624
+ # copy remote
1625
+ new_appliance_map = app_map.clone()
1626
+ new_appliance_map[:name] = new_app_name # inject name
1627
+ save_remote(new_app_name, new_appliance_map)
1628
+
1629
+ # clone credentials...just overwrite keys there, f it.
1630
+ old_wallet = ::Morpheus::Cli::Credentials.new(app_name, nil).load_saved_credentials()
1631
+ if old_wallet
1632
+ ::Morpheus::Cli::Credentials.new(new_app_name, nil).save_credentials(new_app_name, old_wallet)
1633
+ #::Morpheus::Cli::Credentials.new(app_name, nil).clear_saved_credentials(app_name)
1634
+ end
1635
+ # clone groups...just overwrite keys there, f it.
1636
+ old_active_group = ::Morpheus::Cli::Groups.active_group(app_name)
1637
+ if old_active_group
1638
+ ::Morpheus::Cli::Groups.set_active_group(new_app_name, old_active_group)
1639
+ #::Morpheus::Cli::Groups.clear_active_group(app_name)
1640
+ end
1641
+
1642
+ # delete stuff last
1643
+
1644
+ # delete creds
1645
+ if old_wallet
1646
+ ::Morpheus::Cli::Credentials.new(app_name, nil).clear_saved_credentials(app_name)
1647
+ end
1648
+
1649
+ # delete groups
1650
+ if old_active_group
1651
+ ::Morpheus::Cli::Groups.clear_active_group(app_name)
1652
+ end
1653
+
1654
+ # delete remote
1655
+ delete_remote(app_name)
1656
+
1657
+ # this is all redundant after above
1658
+ # # this should be a class method too
1659
+ # ::Morpheus::Cli::Credentials.new(app_name, nil).clear_saved_credentials(app_name)
1660
+ # # delete from groups too..
1661
+ # ::Morpheus::Cli::Groups.clear_active_group(app_name)
1662
+ # # recalculate session variables
1663
+ # recalculate_variable_map()
1664
+ # return the deleted value
1665
+ return app_map
1666
+ end
1667
+
1113
1668
  def delete_remote(app_name)
1114
1669
  app_name = app_name.to_sym
1115
1670
  cur_appliances = self.appliances #.clone
@@ -1149,7 +1704,8 @@ EOT
1149
1704
  Morpheus::RestClient.enable_ssl_verification = false
1150
1705
  end
1151
1706
  # Morpheus::RestClient.enable_http = app_map[:insecure].to_s == 'true'
1152
- setup_interface = Morpheus::SetupInterface.new(app_url)
1707
+ setup_interface = Morpheus::SetupInterface.new({url:app_url, verify_ssl: (app_map[:insecure] != true)})
1708
+ check_json_response = nil
1153
1709
  begin
1154
1710
  now = Time.now.to_i
1155
1711
  app_map[:last_check] = {}
@@ -1191,6 +1747,9 @@ EOT
1191
1747
  rescue OpenSSL::SSL::SSLError => err
1192
1748
  app_map[:status] = 'ssl-error'
1193
1749
  app_map[:last_check][:error] = err.message
1750
+ rescue JSON::ParserError => err
1751
+ app_map[:status] = 'unrecognized'
1752
+ app_map[:last_check][:error] = err.message
1194
1753
  rescue RestClient::Exception => err
1195
1754
  app_map[:status] = 'http-error'
1196
1755
  app_map[:http_status] = err.response ? err.response.code : nil
@@ -1198,7 +1757,7 @@ EOT
1198
1757
  # fallback to /ping for older appliance versions (pre 2.10.5)
1199
1758
  begin
1200
1759
  Morpheus::Logging::DarkPrinter.puts "falling back to remote check via /ping ..." if Morpheus::Logging.debug?
1201
- setup_interface.ping()
1760
+ check_json_response = @setup_interface.ping()
1202
1761
  app_map[:last_check][:ping_fallback] = true
1203
1762
  app_map[:last_check][:http_status] = 200
1204
1763
  app_map[:last_check][:success] = true
@@ -1228,7 +1787,7 @@ EOT
1228
1787
  save_remote(app_name, app_map)
1229
1788
 
1230
1789
  # return the updated data
1231
- return app_map
1790
+ return app_map, check_json_response
1232
1791
 
1233
1792
  end
1234
1793