morpheus-cli 6.1.2 → 6.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Dockerfile +1 -1
- data/lib/morpheus/api/api_client.rb +8 -0
- data/lib/morpheus/api/backup_jobs_interface.rb +4 -0
- data/lib/morpheus/api/backup_restores_interface.rb +23 -0
- data/lib/morpheus/api/backup_results_interface.rb +28 -0
- data/lib/morpheus/api/backups_interface.rb +5 -4
- data/lib/morpheus/api/monitoring_settings_interface.rb +0 -4
- data/lib/morpheus/cli/cli_command.rb +176 -46
- data/lib/morpheus/cli/commands/appliance_settings_command.rb +7 -19
- data/lib/morpheus/cli/commands/apps.rb +1 -1
- data/lib/morpheus/cli/commands/backup_jobs_command.rb +78 -20
- data/lib/morpheus/cli/commands/backup_restores_command.rb +144 -0
- data/lib/morpheus/cli/commands/backup_results_command.rb +149 -0
- data/lib/morpheus/cli/commands/backups_command.rb +215 -93
- data/lib/morpheus/cli/commands/hosts.rb +15 -2
- data/lib/morpheus/cli/commands/instances.rb +18 -3
- data/lib/morpheus/cli/commands/monitoring_settings.rb +0 -16
- data/lib/morpheus/cli/commands/plugins.rb +2 -1
- data/lib/morpheus/cli/commands/roles.rb +9 -9
- data/lib/morpheus/cli/commands/service_catalog_command.rb +50 -83
- data/lib/morpheus/cli/commands/user_sources_command.rb +36 -8
- data/lib/morpheus/cli/commands/view.rb +20 -20
- data/lib/morpheus/cli/mixins/backups_helper.rb +58 -0
- data/lib/morpheus/cli/mixins/print_helper.rb +27 -4
- data/lib/morpheus/cli/option_types.rb +10 -7
- data/lib/morpheus/cli/version.rb +1 -1
- data/lib/morpheus/formatters.rb +1 -1
- data/lib/morpheus/routes.rb +18 -3
- 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
|
-
|
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
|
-
|
570
|
-
|
571
|
-
|
572
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
727
|
-
if
|
728
|
-
|
729
|
-
|
730
|
-
|
731
|
-
|
732
|
-
|
733
|
-
|
734
|
-
|
735
|
-
|
736
|
-
|
737
|
-
|
738
|
-
|
739
|
-
|
740
|
-
|
741
|
-
|
742
|
-
|
743
|
-
|
744
|
-
|
745
|
-
|
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
|
-
|
753
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
1096
|
-
if
|
1097
|
-
|
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
|
-
"
|
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'], "
|
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'], "
|
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
|
-
#
|
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' => '
|
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'], "
|
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'], "
|
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
|
-
#
|
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
|
-
|
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
|
-
|
53
|
+
# input, *ids = args
|
54
|
+
input = args[0]
|
55
|
+
id = args[1]
|
54
56
|
# default to index page "/"
|
55
|
-
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
|
73
|
-
if
|
74
|
-
# convert
|
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
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
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}
|
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
|
-
|
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
|
data/lib/morpheus/cli/version.rb
CHANGED
data/lib/morpheus/formatters.rb
CHANGED
@@ -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
|
137
|
+
milliseconds = milliseconds.to_i.abs
|
138
138
|
if ms_threshold && ms_threshold > milliseconds
|
139
139
|
out = "#{milliseconds}ms"
|
140
140
|
else
|