morpheus-cli 6.1.2 → 6.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (30) hide show
  1. checksums.yaml +4 -4
  2. data/Dockerfile +1 -1
  3. data/lib/morpheus/api/api_client.rb +8 -0
  4. data/lib/morpheus/api/backup_jobs_interface.rb +4 -0
  5. data/lib/morpheus/api/backup_restores_interface.rb +23 -0
  6. data/lib/morpheus/api/backup_results_interface.rb +28 -0
  7. data/lib/morpheus/api/backups_interface.rb +5 -4
  8. data/lib/morpheus/api/monitoring_settings_interface.rb +0 -4
  9. data/lib/morpheus/cli/cli_command.rb +176 -46
  10. data/lib/morpheus/cli/commands/appliance_settings_command.rb +7 -19
  11. data/lib/morpheus/cli/commands/apps.rb +1 -1
  12. data/lib/morpheus/cli/commands/backup_jobs_command.rb +78 -20
  13. data/lib/morpheus/cli/commands/backup_restores_command.rb +144 -0
  14. data/lib/morpheus/cli/commands/backup_results_command.rb +149 -0
  15. data/lib/morpheus/cli/commands/backups_command.rb +215 -93
  16. data/lib/morpheus/cli/commands/hosts.rb +15 -2
  17. data/lib/morpheus/cli/commands/instances.rb +18 -3
  18. data/lib/morpheus/cli/commands/monitoring_settings.rb +0 -16
  19. data/lib/morpheus/cli/commands/plugins.rb +2 -1
  20. data/lib/morpheus/cli/commands/roles.rb +9 -9
  21. data/lib/morpheus/cli/commands/service_catalog_command.rb +50 -83
  22. data/lib/morpheus/cli/commands/user_sources_command.rb +36 -8
  23. data/lib/morpheus/cli/commands/view.rb +20 -20
  24. data/lib/morpheus/cli/mixins/backups_helper.rb +58 -0
  25. data/lib/morpheus/cli/mixins/print_helper.rb +27 -4
  26. data/lib/morpheus/cli/option_types.rb +10 -7
  27. data/lib/morpheus/cli/version.rb +1 -1
  28. data/lib/morpheus/formatters.rb +1 -1
  29. data/lib/morpheus/routes.rb +18 -3
  30. metadata +6 -2
@@ -1206,7 +1206,7 @@ EOT
1206
1206
  role = find_role_by_name_or_id(account_id, name)
1207
1207
  exit 1 if role.nil?
1208
1208
 
1209
- role_json = @roles_interface.get(account_id, role['id'])
1209
+ role_json = @roles_interface.get(account_id, role['id'], {'includeDefaultAccess' => true})
1210
1210
 
1211
1211
  cloud = nil
1212
1212
  if !do_all
@@ -1355,7 +1355,7 @@ EOT
1355
1355
  role = find_role_by_name_or_id(account_id, name)
1356
1356
  return 1 if role.nil?
1357
1357
 
1358
- role_json = @roles_interface.get(account_id, role['id'])
1358
+ role_json = @roles_interface.get(account_id, role['id'], {'includeDefaultAccess' => true})
1359
1359
  instance_type = nil
1360
1360
  if !do_all
1361
1361
  instance_type = find_instance_type_by_name(instance_type_name)
@@ -1504,7 +1504,7 @@ EOT
1504
1504
  role = find_role_by_name_or_id(account_id, name)
1505
1505
  return 1 if role.nil?
1506
1506
 
1507
- role_json = @roles_interface.get(account_id, role['id'])
1507
+ role_json = @roles_interface.get(account_id, role['id'], {'includeDefaultAccess' => true})
1508
1508
  blueprint_global_access = role_json['globalAppTemplateAccess'] || role_json['globalBlueprintAccess']
1509
1509
  blueprint_permissions = role_json['appTemplatePermissions'] || role_json['blueprintPermissions'] || []
1510
1510
 
@@ -1666,7 +1666,7 @@ EOT
1666
1666
  role = find_role_by_name_or_id(account_id, name)
1667
1667
  return 1 if role.nil?
1668
1668
 
1669
- role_json = @roles_interface.get(account_id, role['id'])
1669
+ role_json = @roles_interface.get(account_id, role['id'], {'includeDefaultAccess' => true})
1670
1670
  catalog_item_type_global_access = role_json['globalCatalogItemTypeAccess']
1671
1671
  catalog_item_type_permissions = role_json['catalogItemTypePermissions'] || role_json['catalogItemTypes'] []
1672
1672
 
@@ -1821,7 +1821,7 @@ Update default persona access for a role.
1821
1821
  role = find_role_by_name_or_id(account_id, name)
1822
1822
  return 1 if role.nil?
1823
1823
 
1824
- role_json = @roles_interface.get(account_id, role['id'])
1824
+ role_json = @roles_interface.get(account_id, role['id'], {'includeDefaultAccess' => true})
1825
1825
 
1826
1826
  # no lookup right now, pass the code serviceCatalog|standard
1827
1827
  persona_code = persona_id
@@ -1963,7 +1963,7 @@ EOT
1963
1963
  role = find_role_by_name_or_id(account_id, name)
1964
1964
  return 1 if role.nil?
1965
1965
 
1966
- role_json = @roles_interface.get(account_id, role['id'])
1966
+ role_json = @roles_interface.get(account_id, role['id'], {'includeDefaultAccess' => true})
1967
1967
  vdi_pool_global_access = role_json['globalVdiPoolAccess']
1968
1968
  vdi_pool_permissions = role_json['vdiPoolPermissions'] || role_json['vdiPools'] || []
1969
1969
 
@@ -2119,7 +2119,7 @@ EOT
2119
2119
  role = find_role_by_name_or_id(account_id, name)
2120
2120
  return 1 if role.nil?
2121
2121
 
2122
- role_json = @roles_interface.get(account_id, role['id'])
2122
+ role_json = @roles_interface.get(account_id, role['id'], {'includeDefaultAccess' => true})
2123
2123
  report_type_global_access = role_json['globalReportTypeAccess']
2124
2124
  report_type_permissions = role_json['reportTypePermissions'] || role_json['reportTypes'] || []
2125
2125
 
@@ -2273,7 +2273,7 @@ Update default task access for a role.
2273
2273
  role = find_role_by_name_or_id(account_id, name)
2274
2274
  return 1 if role.nil?
2275
2275
 
2276
- role_json = @roles_interface.get(account_id, role['id'])
2276
+ role_json = @roles_interface.get(account_id, role['id'], {'includeDefaultAccess' => true})
2277
2277
  task_permissions = role_json['taskPermissions'] || role_json['tasks'] || []
2278
2278
 
2279
2279
  # hacky, but support name or code lookup via the list returned in the show payload
@@ -2425,7 +2425,7 @@ Update default workflow access for a role.
2425
2425
  role = find_role_by_name_or_id(account_id, name)
2426
2426
  return 1 if role.nil?
2427
2427
 
2428
- role_json = @roles_interface.get(account_id, role['id'])
2428
+ role_json = @roles_interface.get(account_id, role['id'], {'includeDefaultAccess' => true})
2429
2429
  workflow_permissions = role_json['taskSetPermissions'] || role_json['taskSets'] || []
2430
2430
 
2431
2431
  # hacky, but support name or code lookup via the list returned in the show payload
@@ -560,27 +560,17 @@ EOT
560
560
  # fetch current cart
561
561
  # cart = @service_catalog_interface.get_cart()['cart']
562
562
  update_cart_object_key = 'order'
563
- payloads = parse_payloads(options, update_cart_object_key) do |payload|
563
+ parse_payload(options, update_cart_object_key) do |payload|
564
564
  payload.deep_merge!({update_cart_object_key => parse_passed_options(options)})
565
565
  if payload[update_cart_object_key].empty? # || options[:no_prompt]
566
566
  raise_command_error "Specify at least one option to update.\n#{optparse}"
567
567
  end
568
568
  end
569
- process_payloads(payloads, options) do |payload|
570
- @service_catalog_interface.setopts(options)
571
- if options[:dry_run]
572
- print_dry_run @service_catalog_interface.dry.update_cart(payload)
573
- next
574
- end
575
- json_response = @service_catalog_interface.update_cart(payload)
576
- #cart = json_response['cart']
577
- #cart = @service_catalog_interface.get_cart()['cart']
578
- render_response(json_response, options, 'cart') do
579
- print_green_success "Updated cart"
580
- get_cart([] + (options[:remote] ? ["-r",options[:remote]] : []))
581
- end
569
+ execute_api(@service_catalog_interface, :update_cart, [params], options, "cart") do |json_response|
570
+ #cart = json_response["cart"]
571
+ print_green_success "Updated cart"
572
+ get_cart([] + (options[:remote] ? ["-r",options[:remote]] : []))
582
573
  end
583
- return 0, nil
584
574
  end
585
575
 
586
576
  def add(args)
@@ -600,6 +590,7 @@ EOT
600
590
  end
601
591
  opts.on('--validate','--validate', "Validate Only. Validates the configuration and skips adding the item.") do
602
592
  options[:validate_only] = true
593
+ params['validate'] = true
603
594
  end
604
595
  opts.on('--context [instance|server]', String, "Context Type for operational workflow types") do |val|
605
596
  workflow_context = val.to_s
@@ -608,7 +599,7 @@ EOT
608
599
  workflow_target = val.to_s
609
600
  end
610
601
  opts.add_hidden_option('--sigdig')
611
- build_standard_update_options(opts, options, [:payloads, :sigdig])
602
+ build_standard_add_many_options(opts, options, [:sigdig])
612
603
  opts.footer = <<-EOT
613
604
  Add an item to your cart
614
605
  [type] is required, this is name or id of a catalog item type.
@@ -622,7 +613,7 @@ EOT
622
613
  type_id = args.join(" ")
623
614
  end
624
615
  add_item_object_key = 'item'
625
- payloads = parse_payloads(options, add_item_object_key) do |payload|
616
+ parse_payload(options, add_item_object_key) do |payload|
626
617
  payload.deep_merge!({add_item_object_key => parse_passed_options(options)})
627
618
  # prompt for Type
628
619
  if type_id
@@ -712,46 +703,35 @@ EOT
712
703
  end
713
704
  end
714
705
  end
715
- if options[:validate_only]
716
- params['validate'] = true
717
- end
718
- process_payloads(payloads, options) do |payload|
719
- @service_catalog_interface.setopts(options)
720
- if options[:dry_run]
721
- print_dry_run @service_catalog_interface.dry.create_cart_item(payload, params)
722
- next
723
- end
724
- json_response = @service_catalog_interface.create_cart_item(payload, params)
706
+ execute_api(@service_catalog_interface, :create_cart_item, [params], options, 'item') do |json_response|
725
707
  cart_item = json_response['item']
726
- render_response(json_response, options) do
727
- if options[:validate_only]
728
- if json_response['success']
729
- print_h2 "Validated Cart Item", [], options
730
- cart_item_columns = {
731
- "Type" => lambda {|it| it['type']['name'] rescue '' },
732
- #"Qty" => lambda {|it| it['quantity'] },
733
- "Price" => lambda {|it| it['price'] ? format_money(it['price'] , it['currency'], {sigdig:options[:sigdig] || default_sigdig}) : "No pricing configured" },
734
- "Status" => lambda {|it|
735
- status_string = format_catalog_item_status(it)
736
- if it['errorMessage'].to_s != ""
737
- status_string << " - #{it['errorMessage']}"
738
- end
739
- status_string
740
- },
741
- #"Config" => lambda {|it| truncate_string(format_name_values(it['config']), 50) }
742
- }
743
- print as_pretty_table([cart_item], cart_item_columns.upcase_keys!)
744
- print reset, "\n"
745
- print_green_success(json_response['msg'] || "Item is valid")
746
- print reset, "\n"
747
- else
748
- # not needed because it will be http 400
749
- print_rest_errors(json_response, options)
750
- end
708
+ if options[:validate_only]
709
+ if json_response['success']
710
+ print_h2 "Validated Cart Item", [], options
711
+ cart_item_columns = {
712
+ "Type" => lambda {|it| it['type']['name'] rescue '' },
713
+ #"Qty" => lambda {|it| it['quantity'] },
714
+ "Price" => lambda {|it| it['price'] ? format_money(it['price'] , it['currency'], {sigdig:options[:sigdig] || default_sigdig}) : "No pricing configured" },
715
+ "Status" => lambda {|it|
716
+ status_string = format_catalog_item_status(it)
717
+ if it['errorMessage'].to_s != ""
718
+ status_string << " - #{it['errorMessage']}"
719
+ end
720
+ status_string
721
+ },
722
+ #"Config" => lambda {|it| truncate_string(format_name_values(it['config']), 50) }
723
+ }
724
+ print as_pretty_table([cart_item], cart_item_columns.upcase_keys!)
725
+ print reset, "\n"
726
+ print_green_success(json_response['msg'] || "Item is valid")
727
+ print reset, "\n"
751
728
  else
752
- print_green_success "Added item to cart"
753
- get_cart([] + (options[:remote] ? ["-r",options[:remote]] : []))
729
+ # not needed because it will be http 400
730
+ print_rest_errors(json_response, options)
754
731
  end
732
+ else
733
+ print_green_success "Added item to cart"
734
+ get_cart([] + (options[:remote] ? ["-r",options[:remote]] : []))
755
735
  end
756
736
  end
757
737
  end
@@ -935,6 +915,7 @@ EOT
935
915
  end
936
916
  opts.on('--validate','--validate', "Validate Only. Validates the configuration and skips creating the order.") do
937
917
  options[:validate_only] = true
918
+ params['validate'] = true
938
919
  end
939
920
  opts.on('-a', '--details', "Display all details: item configuration." ) do
940
921
  options[:details] = true
@@ -962,7 +943,7 @@ EOT
962
943
  end
963
944
  payload = {}
964
945
  order_object_key = 'order'
965
- payloads = parse_payloads(options, order_object_key) do |payload|
946
+ parse_payload(options, order_object_key) do |payload|
966
947
  payload.deep_merge!({order_object_key => {}})
967
948
  # Prompt for 1-N Types
968
949
  # still_prompting = options[:no_prompt] != true
@@ -1076,38 +1057,24 @@ EOT
1076
1057
  end
1077
1058
  end
1078
1059
 
1079
- end
1080
-
1081
-
1060
+ end
1082
1061
  end
1083
- if options[:validate_only]
1084
- params['validate'] = true
1085
- #payload['validate'] = true
1086
- end
1087
- process_payloads(payloads, options) do |payload|
1088
- @service_catalog_interface.setopts(options)
1089
- if options[:dry_run]
1090
- print_dry_run @service_catalog_interface.dry.create_order(payload, params)
1091
- next
1092
- end
1093
- json_response = @service_catalog_interface.create_order(payload, params)
1062
+ execute_api(@service_catalog_interface, :create_order, [params], options, "order") do |json_response|
1094
1063
  order = json_response['order'] || json_response['cart']
1095
- render_response(json_response, options) do
1096
- if options[:validate_only]
1097
- if json_response['success']
1098
- print_h2 "Review Order", [], options
1099
- print_order_details(order, options)
1100
- print_green_success(json_response['msg'] || "Order is valid")
1101
- print reset, "\n"
1102
- else
1103
- # not needed because it will be http 400
1104
- print_rest_errors(json_response, options)
1105
- end
1106
- else
1107
- print_green_success "Order placed"
1108
- print_h2 "Order Details", [], options
1064
+ if options[:validate_only]
1065
+ if json_response['success']
1066
+ print_h2 "Review Order", [], options
1109
1067
  print_order_details(order, options)
1068
+ print_green_success(json_response['msg'] || "Order is valid")
1069
+ print reset, "\n"
1070
+ else
1071
+ # not needed because it will be http 400
1072
+ print_rest_errors(json_response, options)
1110
1073
  end
1074
+ else
1075
+ print_green_success "Order placed"
1076
+ print_h2 "Order Details", [], options
1077
+ print_order_details(order, options)
1111
1078
  end
1112
1079
  end
1113
1080
  end
@@ -158,7 +158,8 @@ EOT
158
158
  "Login URL" => lambda {|it| it['loginURL'] },
159
159
  "Default Role" => lambda {|it| it['defaultAccountRole'] ? it['defaultAccountRole']['authority'] : '' },
160
160
  "External Login" => lambda {|it| format_boolean it['externalLogin'] },
161
- "Allow Custom Mappings" => lambda {|it| format_boolean it['allowCustomMappings'] },
161
+ "Enable Role Mapping Permission" => lambda {|it| format_boolean it['allowCustomMappings'] },
162
+ "Manual Role Assignment" => lambda {|it| it['manualRoleAssignment'].nil? ? '' : format_boolean(it['manualRoleAssignment']) },
162
163
  "Active" => lambda {|it| format_boolean it['active'] },
163
164
  }
164
165
  print_description_list(description_cols, user_source)
@@ -235,13 +236,27 @@ EOT
235
236
  opts.on('--description VALUE', String, "Description") do |val|
236
237
  params['description'] = val
237
238
  end
238
- opts.on("--allow-custom-mappings [on|off]", ['on','off'], "Allow Custom Mappings, Enable Role Mapping Permissions") do |val|
239
+ opts.on("--allow-custom-mappings [on|off]", ['on','off'], "Enable Role Mapping Permissions") do |val|
239
240
  params['allowCustomMappings'] = val.to_s == 'on' || val.to_s == 'true' || val.to_s == '1' || val.to_s == ''
240
241
  end
241
- opts.on("--allowCustomMappings [on|off]", ['on','off'], "Allow Custom Mappings, Enable Role Mapping Permissions") do |val|
242
+ opts.on("--allowCustomMappings [on|off]", ['on','off'], "Enable Role Mapping Permissions") do |val|
242
243
  params['allowCustomMappings'] = val.to_s == 'on' || val.to_s == 'true' || val.to_s == '1' || val.to_s == ''
243
244
  end
244
245
  opts.add_hidden_option('--allowCustomMappings')
246
+ opts.on("--manual-role-assignment [on|off]", ['on','off'], "Manual Role Assignment") do |val|
247
+ params['manualRoleAssignment'] = val.to_s == 'on' || val.to_s == 'true' || val.to_s == '1' || val.to_s == ''
248
+ end
249
+ opts.on("--manualRoleAssignment [on|off]", ['on','off'], "Manual Role Assignment") do |val|
250
+ params['manualRoleAssignment'] = val.to_s == 'on' || val.to_s == 'true' || val.to_s == '1' || val.to_s == ''
251
+ end
252
+ opts.add_hidden_option('--manualRoleAssignment')
253
+ opts.on("--manual-role-assignment [on|off]", ['on','off'], "Manual Role Assignment") do |val|
254
+ params['manualRoleAssignment'] = val.to_s == 'on' || val.to_s == 'true' || val.to_s == '1' || val.to_s == ''
255
+ end
256
+ opts.on("--manualRoleAssignment [on|off]", ['on','off'], "Manual Role Assignment") do |val|
257
+ params['manualRoleAssignment'] = val.to_s == 'on' || val.to_s == 'true' || val.to_s == '1' || val.to_s == ''
258
+ end
259
+ opts.add_hidden_option('--manualRoleAssignment')
245
260
  opts.on('--role-mappings MAPPINGS', String, "Role Mappings FQN in the format id1:FQN1,id2:FQN2") do |val|
246
261
  role_mappings = {}
247
262
  val.split(',').collect {|it| it.strip.split(':') }.each do |pair|
@@ -386,14 +401,22 @@ EOT
386
401
  end
387
402
  payload['userSource']['defaultAccountRole'] = {'id' => default_role_id }
388
403
 
389
- # Allow Custom Mappings
404
+ # Enable Role Mapping Permissions
390
405
  if !params['allowCustomMappings'].nil?
391
406
  payload['userSource']['allowCustomMappings'] = ["on","true"].include?(params['allowCustomMappings'].to_s)
392
407
  else
393
- v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'allowCustomMappings', 'type' => 'checkbox', 'fieldLabel' => 'Allow Custom Mappings', 'defaultValue' => false}], options[:options])
408
+ v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'allowCustomMappings', 'type' => 'checkbox', 'fieldLabel' => 'Enable Role Mapping Permissions', 'defaultValue' => false}], options[:options])
394
409
  payload['userSource']['allowCustomMappings'] = ["on","true"].include?(v_prompt['allowCustomMappings'].to_s)
395
410
  end
396
411
 
412
+ # Manual Role Assignment
413
+ if !params['manualRoleAssignment'].nil?
414
+ payload['userSource']['manualRoleAssignment'] = ["on","true"].include?(params['allowCustomMappings'].to_s)
415
+ else
416
+ v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'manualRoleAssignment', 'type' => 'checkbox', 'fieldLabel' => 'Manual Role Assignment', 'defaultValue' => false}], options[:options])
417
+ payload['userSource']['manualRoleAssignment'] = ["on","true"].include?(v_prompt['manualRoleAssignment'].to_s)
418
+ end
419
+
397
420
  if role_mappings
398
421
  payload['roleMappings'] = role_mappings
399
422
  end
@@ -435,10 +458,10 @@ EOT
435
458
  opts.on('--description VALUE', String, "Description") do |val|
436
459
  params['description'] = val
437
460
  end
438
- opts.on("--allow-custom-mappings [on|off]", ['on','off'], "Allow Custom Mappings, Enable Role Mapping Permissions") do |val|
461
+ opts.on("--allow-custom-mappings [on|off]", ['on','off'], "Enable Role Mapping Permissions") do |val|
439
462
  params['allowCustomMappings'] = val.to_s == 'on' || val.to_s == 'true' || val.to_s == '1' || val.to_s == ''
440
463
  end
441
- opts.on("--allowCustomMappings [on|off]", ['on','off'], "Allow Custom Mappings, Enable Role Mapping Permissions") do |val|
464
+ opts.on("--allowCustomMappings [on|off]", ['on','off'], "Enable Role Mapping Permissions") do |val|
442
465
  params['allowCustomMappings'] = val.to_s == 'on' || val.to_s == 'true' || val.to_s == '1' || val.to_s == ''
443
466
  end
444
467
  opts.add_hidden_option('--allowCustomMappings')
@@ -493,11 +516,16 @@ EOT
493
516
  payload['userSource']['description'] = params['description']
494
517
  end
495
518
 
496
- # Allow Custom Mappings
519
+ # Enable Role Mapping Permissions
497
520
  if !params['allowCustomMappings'].nil?
498
521
  payload['userSource']['allowCustomMappings'] = params['allowCustomMappings']
499
522
  end
500
523
 
524
+ # Manual Role Assignment
525
+ if !params['manualRoleAssignment'].nil?
526
+ payload['userSource']['manualRoleAssignment'] = params['manualRoleAssignment']
527
+ end
528
+
501
529
  if role_mappings
502
530
  payload['roleMappings'] = role_mappings
503
531
  end
@@ -46,20 +46,22 @@ Examples:
46
46
  EOT
47
47
  end
48
48
  optparse.parse!(args)
49
- # verify_args!(args:args, optparse:optparse, min: 0, max: 2)
49
+ verify_args!(args:args, optparse:optparse, min: 0, max: 2)
50
50
  connect(options)
51
51
  # todo: it would actually be cool to use the params and include them on the path..
52
52
  # params.merge!(parse_query_options(options))
53
- path, *ids = args
53
+ # input, *ids = args
54
+ input = args[0]
55
+ id = args[1]
54
56
  # default to index page "/"
55
- path = path || "/"
57
+ path = input || "/"
56
58
  if options[:absolute_path] != true
57
59
  if path.start_with?("/")
58
60
  # treat like absolute path, no lookup
59
61
  else
60
62
  # lookup best matching route from sitemap
61
63
  # lookup plural routes first, so 'app' finds apps and not approvals
62
- found_route = Morpheus::Routes.lookup(path)
64
+ found_route = Morpheus::Routes.lookup(path, id)
63
65
  if found_route
64
66
  # Morpheus::Logging::DarkPrinter.puts "Found matching route: '#{path}' => '#{found_route}'" if Morpheus::Logging.debug?
65
67
  path = found_route
@@ -69,26 +71,24 @@ EOT
69
71
  end
70
72
  # always add a leading slash
71
73
  path = path.start_with?("/") ? path : "/#{path}"
72
- # append id(s) to path if passed
73
- if ids.size > 0
74
- # convert names to ids
74
+ # append id to path if passed
75
+ if id
76
+ # convert name to id
75
77
  # assume the last part of path is the type and use generic finder
76
78
  # only lookup names, and allow any id
77
- ids = ids.collect do |id|
78
- if id.to_s !~ /\A\d{1,}\Z/
79
- # assume the last part of path is the type
80
- record_type = path.split("/").last
81
- record_type.sub!('#!', '')
82
- record = find_by_name(record_type, id)
83
- if record.nil?
84
- raise_command_error("[id] is invalid. No #{record_type} found for '#{id}'", args, optparse)
85
- end
86
- record['id'].to_s
87
- else
88
- id
79
+ if id.to_s !~ /\A\d{1,}\Z/
80
+ # record type is just args[0]
81
+ record_type = input
82
+ # assume the last part of path is the type
83
+ # record_type = path.split("/").last
84
+ # record_type.sub!('#!', '')
85
+ record = find_by_name(record_type, id)
86
+ if record.nil?
87
+ raise_command_error("[id] is invalid. No #{record_type} found for '#{id}'", args, optparse)
89
88
  end
89
+ id = record['id'].to_s
90
90
  end
91
- path = "#{path}/" + ids.join("/")
91
+ path = "#{path}/#{id}"
92
92
  end
93
93
  end
94
94
  # build the link to use, either our path or oauth-redirect to that path
@@ -110,4 +110,62 @@ module Morpheus::Cli::BackupsHelper
110
110
  return backup_jobs[0]
111
111
  end
112
112
  end
113
+
114
+ ## Backup Results
115
+
116
+ def backup_result_list_column_definitions()
117
+ {
118
+ "ID" => 'id',
119
+ "Backup" => lambda {|it| it['backup']['name'] rescue '' },
120
+ "Status" => lambda {|it| format_backup_result_status(it) },
121
+ #"Duration" => lambda {|it| format_duration(it['startDate'], it['endDate']) },
122
+ "Duration" => lambda {|it| format_duration_milliseconds(it['durationMillis']) },
123
+ "Start Date" => lambda {|it| format_local_dt(it['startDate']) },
124
+ "End Date" => lambda {|it| format_local_dt(it['endDate']) },
125
+ "Size" => lambda {|it| format_bytes(it['sizeInMb'], 'MB') },
126
+ }
127
+ end
128
+
129
+ def backup_result_column_definitions()
130
+ backup_result_list_column_definitions()
131
+ end
132
+
133
+ def format_backup_result_status(backup_result, return_color=cyan)
134
+ out = ""
135
+ status_string = backup_result['status'].to_s.upcase
136
+ if status_string == 'SUCCEEDED' || status_string == 'SUCCESS'
137
+ out << "#{green}#{status_string.upcase}#{return_color}"
138
+ elsif status_string == 'FAILED'
139
+ out << "#{red}#{status_string.upcase}#{return_color}"
140
+ elsif status_string
141
+ out << "#{cyan}#{status_string.upcase}#{return_color}"
142
+ else
143
+ out << ""
144
+ end
145
+ out
146
+ end
147
+
148
+ ## Backup Restores
149
+
150
+ def backup_restore_list_column_definitions()
151
+ {
152
+ "ID" => 'id',
153
+ "Backup" => lambda {|it| it['backup']['name'] rescue '' },
154
+ "Backup Result ID" => lambda {|it| it['backupResultId'] rescue '' },
155
+ "Target" => lambda {|it| it['instance']['name'] rescue '' },
156
+ "Status" => lambda {|it| format_backup_result_status(it) },
157
+ #"Duration" => lambda {|it| format_duration(it['startDate'], it['endDate']) },
158
+ "Duration" => lambda {|it| format_duration_milliseconds(it['durationMillis']) },
159
+ "Start Date" => lambda {|it| format_local_dt(it['startDate']) },
160
+ "End Date" => lambda {|it| format_local_dt(it['endDate']) },
161
+ }
162
+ end
163
+
164
+ def backup_restore_column_definitions()
165
+ backup_restore_list_column_definitions()
166
+ end
167
+
168
+ def format_backup_restore_status(backup_restore, return_color=cyan)
169
+ format_backup_result_status(backup_restore, return_color)
170
+ end
113
171
  end
@@ -191,7 +191,24 @@ module Morpheus::Cli::PrintHelper
191
191
  output = ""
192
192
  if api_request[:curl] || options[:curl]
193
193
  output = format_curl_command(http_method, url, headers, payload, options)
194
+ elsif options[:json]
195
+ # --dry --json should print the payload only
196
+ payload_object = payload.is_a?(String) ? JSON.parse(payload) : payload
197
+ output = as_json(payload_object, options)
198
+ elsif options[:yaml]
199
+ # --dry --yaml should print the payload only as yaml
200
+ payload_object = payload.is_a?(String) ? JSON.parse(payload) : payload
201
+ output = as_yaml(payload_object, options)
194
202
  else
203
+ # default format is
204
+ # DRY RUN
205
+ # REQUEST
206
+ # GET https://server/api/things
207
+ #
208
+ # JSON
209
+ # {
210
+ # "thing": { ... }
211
+ # }
195
212
  output = format_api_request(http_method, url, headers, payload, options)
196
213
  end
197
214
  # this is an extra scrub, should remove
@@ -210,14 +227,20 @@ module Morpheus::Cli::PrintHelper
210
227
  if api_request[:curl] || options[:curl]
211
228
  print "\n"
212
229
  print "#{cyan}#{bold}#{dark}CURL COMMAND#{reset}\n"
230
+ print output
231
+ print reset, "\n"
232
+ print reset
233
+ elsif options[:json] || options[:yaml]
234
+ # print just the just payload
235
+ print output, "\n"
213
236
  else
214
237
  print "\n"
215
238
  print "#{cyan}#{bold}#{dark}REQUEST#{reset}\n"
239
+ print output
240
+ print reset, "\n"
241
+ print reset
216
242
  end
217
- print output
218
- print reset, "\n"
219
- print reset
220
- return
243
+ return output
221
244
  end
222
245
 
223
246
  def print_system_command_dry_run(cmd, options={})
@@ -48,6 +48,7 @@ module Morpheus
48
48
 
49
49
  # supresses prompting unless --prompt has been passed
50
50
  def self.no_prompt(option_types, options={}, api_client=nil,api_params={})
51
+ options[:edit_mode] = true # hack used for updates to avoid default values being used
51
52
  if options[:always_prompt]
52
53
  prompt(option_types, options, api_client, api_params)
53
54
  else
@@ -218,7 +219,7 @@ module Morpheus
218
219
 
219
220
  # credential type
220
221
  handle_credential_type = -> {
221
- credential_type = select_prompt(option_type.merge({'defaultValue' => value}), api_client, option_params.merge({'credentialTypes' => option_type['config']['credentialTypes']}), !value.nil?, nil, paging_enabled, ignore_empty)
222
+ credential_type = select_prompt(option_type.merge({'defaultValue' => value}), api_client, option_params.merge({'credentialTypes' => option_type['config']['credentialTypes']}), !value.nil?, nil, paging_enabled, ignore_empty, options[:edit_mode])
222
223
  # continue prompting for local creds
223
224
  if credential_type == 'local'
224
225
  parent_context_map.reject! {|k,v| k == 'credential'}
@@ -247,14 +248,14 @@ module Morpheus
247
248
  end
248
249
  # these select prompts should just fall down through below, with the extra params no_prompt, use_value
249
250
  elsif option_type['type'] == 'select'
250
- value = select_prompt(option_type.merge({'defaultValue' => value, 'defaultInputValue' => input_value}), api_client, option_params, true, nil, false, ignore_empty)
251
+ value = select_prompt(option_type.merge({'defaultValue' => value, 'defaultInputValue' => input_value}), api_client, option_params, true, nil, false, ignore_empty, options[:edit_mode])
251
252
  elsif option_type['type'] == 'multiSelect'
252
253
  # support value as csv like "thing1, thing2"
253
254
  value_list = value.is_a?(String) ? value.parse_csv.collect {|v| v ? v.to_s.strip : v } : [value].flatten
254
255
  input_value_list = input_value.is_a?(String) ? input_value.parse_csv.collect {|v| v ? v.to_s.strip : v } : [input_value].flatten
255
256
  select_value_list = []
256
257
  value_list.each_with_index do |v, i|
257
- select_value_list << select_prompt(option_type.merge({'defaultValue' => v, 'defaultInputValue' => input_value_list[i]}), api_client, option_params, true, nil, false, ignore_empty)
258
+ select_value_list << select_prompt(option_type.merge({'defaultValue' => v, 'defaultInputValue' => input_value_list[i]}), api_client, option_params, true, nil, false, ignore_empty, options[:edit_mode])
258
259
  end
259
260
  value = select_value_list
260
261
  elsif option_type['type'] == 'typeahead'
@@ -302,7 +303,7 @@ module Morpheus
302
303
  next
303
304
  end
304
305
  if ['select', 'multiSelect'].include?(option_type['type'])
305
- value = select_prompt(option_type, api_client, option_params, true, nil, false, ignore_empty)
306
+ value = select_prompt(option_type, api_client, option_params, true, nil, false, ignore_empty, options[:edit_mode])
306
307
  value_found = !!value
307
308
  end
308
309
  if ['typeahead', 'multiTypeahead'].include?(option_type['type'])
@@ -347,12 +348,12 @@ module Morpheus
347
348
  # I suppose the entered value should take precedence
348
349
  # api_params = api_params.merge(options) # this might be good enough
349
350
  # dup it
350
- value = select_prompt(option_type, api_client, option_params, options[:no_prompt], nil, paging_enabled, ignore_empty)
351
+ value = select_prompt(option_type, api_client, option_params, options[:no_prompt], nil, paging_enabled, ignore_empty, options[:edit_mode])
351
352
  if value && option_type['type'] == 'multiSelect'
352
353
  value = [value]
353
354
  recommended_count = (option_type['config'] || {})['recommendedCount'] || 0
354
355
  while self.confirm("Add another #{option_type['fieldLabel']}?", {:default => recommended_count > value.count}) do
355
- if addn_value = select_prompt(option_type, api_client, option_params, options[:no_prompt], nil, paging_enabled, ignore_empty)
356
+ if addn_value = select_prompt(option_type, api_client, option_params, options[:no_prompt], nil, paging_enabled, ignore_empty, options[:edit_mode])
356
357
  value << addn_value
357
358
  else
358
359
  break
@@ -476,7 +477,7 @@ module Morpheus
476
477
  Thread.current[:_last_select]
477
478
  end
478
479
 
479
- def self.select_prompt(option_type, api_client, api_params={}, no_prompt=false, use_value=nil, paging_enabled=false, ignore_empty=false)
480
+ def self.select_prompt(option_type, api_client, api_params={}, no_prompt=false, use_value=nil, paging_enabled=false, ignore_empty=false, edit_mode=false)
480
481
  paging_enabled = false if Morpheus::Cli.windows?
481
482
  field_key = [option_type['fieldContext'], option_type['fieldName']].select {|it| it && it != '' }.join('.')
482
483
  help_field_key = option_type[:help_field_prefix] ? "#{option_type[:help_field_prefix]}.#{field_key}" : field_key
@@ -550,6 +551,8 @@ module Morpheus
550
551
  print "\n"
551
552
  exit 1
552
553
  end
554
+ elsif edit_mode
555
+ # do not use a default value for edit mode
553
556
  # skipSingleOption is no longer supported
554
557
  # elsif !select_options.nil? && select_options.count == 1 && option_type['skipSingleOption'] == true
555
558
  # value_found = true
@@ -1,6 +1,6 @@
1
1
 
2
2
  module Morpheus
3
3
  module Cli
4
- VERSION = "6.1.2"
4
+ VERSION = "6.2.1"
5
5
  end
6
6
  end
@@ -134,7 +134,7 @@ end
134
134
 
135
135
  def format_duration_milliseconds(milliseconds, format="human", ms_threshold=1000)
136
136
  out = ""
137
- milliseconds = milliseconds.abs.to_i
137
+ milliseconds = milliseconds.to_i.abs
138
138
  if ms_threshold && ms_threshold > milliseconds
139
139
  out = "#{milliseconds}ms"
140
140
  else