morpheus-cli 5.4.5.1 → 5.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Dockerfile +1 -1
- data/lib/morpheus/api/prices_interface.rb +6 -0
- data/lib/morpheus/cli/cli_command.rb +16 -3
- data/lib/morpheus/cli/commands/clouds.rb +51 -27
- data/lib/morpheus/cli/commands/cypher_command.rb +22 -23
- data/lib/morpheus/cli/commands/instances.rb +10 -10
- data/lib/morpheus/cli/commands/library_option_lists_command.rb +18 -8
- data/lib/morpheus/cli/commands/prices_command.rb +25 -11
- data/lib/morpheus/cli/commands/tasks.rb +45 -10
- data/lib/morpheus/cli/mixins/provisioning_helper.rb +1 -0
- data/lib/morpheus/cli/option_types.rb +25 -0
- data/lib/morpheus/cli/version.rb +1 -1
- data/lib/morpheus/routes.rb +3 -5
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ca02ec5cffb310afe4458568f745332e8f81f1d03a525ad1a9d9d1a3ecd08313
|
4
|
+
data.tar.gz: 323d4e3627c67fbd676bb34eb0b6f62b0eecff6180285af93599aba21f0a9781
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bc72cdba318abc74aef8dec7ab8602ad7ec7196101950dfc0b29b8fe8c4298c2de5b41265a386457c38fa0ac9e46082cb86d04d7cfbc75652a1824d5a036cec1
|
7
|
+
data.tar.gz: f546fe9ed24fcc380d8aa1f2fc6d05a1cfb7186dcee48e66c340c51951b69e3b04408db5400ddcb23c9c596ccb14867571f1e21b6ceeb3e253c720d0525c21b0
|
data/Dockerfile
CHANGED
@@ -62,4 +62,10 @@ class Morpheus::PricesInterface < Morpheus::APIClient
|
|
62
62
|
headers = { params: params, authorization: "Bearer #{@access_token}" }
|
63
63
|
execute(method: :get, url: url, headers: headers)
|
64
64
|
end
|
65
|
+
|
66
|
+
def list_currencies(params={})
|
67
|
+
url = "#{base_path}/currencies"
|
68
|
+
headers = { params: params, authorization: "Bearer #{@access_token}" }
|
69
|
+
execute(method: :get, url: url, headers: headers)
|
70
|
+
end
|
65
71
|
end
|
@@ -253,7 +253,7 @@ module Morpheus
|
|
253
253
|
|
254
254
|
# list is GET that supports phrase,max,offset,sort,direction
|
255
255
|
def build_standard_list_options(opts, options, includes=[], excludes=[])
|
256
|
-
build_standard_get_options(opts, options, [:list] + includes, excludes
|
256
|
+
build_standard_get_options(opts, options, [:list] + includes, excludes)
|
257
257
|
end
|
258
258
|
|
259
259
|
def build_standard_add_options(opts, options, includes=[], excludes=[])
|
@@ -1569,7 +1569,7 @@ module Morpheus
|
|
1569
1569
|
#Morpheus::Logging::DarkPrinter.puts "find_all(#{args.join(', ')})" if Morpheus::Logging.debug?
|
1570
1570
|
type, *request_args = args
|
1571
1571
|
type = type.to_s.singularize.underscore
|
1572
|
-
list_key = respond_to?("#{type}_list_key", true) ? send("#{type}_list_key") : type
|
1572
|
+
list_key = respond_to?("#{type}_list_key", true) ? send("#{type}_list_key") : get_list_key(type)
|
1573
1573
|
json_response = find_all_json(*args)
|
1574
1574
|
if !json_response.key?(list_key)
|
1575
1575
|
# maybe just use the first key like this:
|
@@ -1600,7 +1600,7 @@ module Morpheus
|
|
1600
1600
|
#Morpheus::Logging::DarkPrinter.puts "find_record(#{args.join(', ')})" if Morpheus::Logging.debug?
|
1601
1601
|
type, *request_args = args
|
1602
1602
|
type = type.to_s.singularize.underscore
|
1603
|
-
object_key = respond_to?("#{type}_object_key", true) ? send("#{type}_object_key") : type
|
1603
|
+
object_key = respond_to?("#{type}_object_key", true) ? send("#{type}_object_key") : get_object_key(type)
|
1604
1604
|
json_response = find_record_json(*args)
|
1605
1605
|
if !json_response.key?(object_key)
|
1606
1606
|
# maybe just use the first key like this:
|
@@ -1645,6 +1645,19 @@ module Morpheus
|
|
1645
1645
|
return interface
|
1646
1646
|
end
|
1647
1647
|
|
1648
|
+
def get_list_key(type)
|
1649
|
+
return get_object_key(type).pluralize
|
1650
|
+
end
|
1651
|
+
|
1652
|
+
def get_object_key(type)
|
1653
|
+
key = type.camelcase.singularize
|
1654
|
+
# add aliases here as needed
|
1655
|
+
if key == "cloud"
|
1656
|
+
key = "zone"
|
1657
|
+
end
|
1658
|
+
return key
|
1659
|
+
end
|
1660
|
+
|
1648
1661
|
module ClassMethods
|
1649
1662
|
|
1650
1663
|
def prog_name
|
@@ -80,8 +80,9 @@ class Morpheus::Cli::Clouds
|
|
80
80
|
print_h1 title, subtitles
|
81
81
|
if clouds.empty?
|
82
82
|
print cyan,"No clouds found.",reset,"\n"
|
83
|
-
else
|
84
|
-
|
83
|
+
else
|
84
|
+
columns = cloud_list_column_definitions(options).upcase_keys!
|
85
|
+
print as_pretty_table(clouds, columns, options)
|
85
86
|
print_results_pagination(json_response)
|
86
87
|
end
|
87
88
|
print reset,"\n"
|
@@ -164,13 +165,14 @@ class Morpheus::Cli::Clouds
|
|
164
165
|
else
|
165
166
|
server_counts = json_response['serverCounts'] # legacy
|
166
167
|
end
|
167
|
-
cloud_type = cloud_type_for_id(cloud['zoneTypeId'])
|
168
|
+
#cloud_type = cloud_type_for_id(cloud['zoneTypeId'])
|
168
169
|
print_h1 "Cloud Details"
|
169
170
|
print cyan
|
170
171
|
description_cols = {
|
171
172
|
"ID" => 'id',
|
172
173
|
"Name" => 'name',
|
173
|
-
"Type" => lambda {|it| cloud_type ? cloud_type['name'] : '' },
|
174
|
+
# "Type" => lambda {|it| cloud_type ? cloud_type['name'] : '' },
|
175
|
+
"Type" => lambda {|it| it['zoneType'] ? it['zoneType']['name'] : '' },
|
174
176
|
"Code" => 'code',
|
175
177
|
"Location" => 'location',
|
176
178
|
"Region Code" => 'regionCode',
|
@@ -215,6 +217,13 @@ class Morpheus::Cli::Clouds
|
|
215
217
|
opts.on( '--certificate-provider CODE', String, "Certificate Provider. Default is 'internal'" ) do |val|
|
216
218
|
params[:certificate_provider] = val
|
217
219
|
end
|
220
|
+
opts.on('--costing-mode VALUE', String, "Costing Mode can be off,costing,full, Default is off." ) do |val|
|
221
|
+
options[:options]['costingMode'] = val
|
222
|
+
end
|
223
|
+
opts.on('--credential VALUE', String, "Credential ID or \"local\"" ) do |val|
|
224
|
+
options[:options]['credential'] = val
|
225
|
+
end
|
226
|
+
|
218
227
|
build_common_options(opts, options, [:options, :payload, :json, :dry_run, :remote])
|
219
228
|
end
|
220
229
|
optparse.parse!(args)
|
@@ -329,6 +338,12 @@ class Morpheus::Cli::Clouds
|
|
329
338
|
# opts.on( '-d', '--description DESCRIPTION', "Description (optional)" ) do |desc|
|
330
339
|
# params[:description] = desc
|
331
340
|
# end
|
341
|
+
opts.on('--costing-mode VALUE', String, "Costing Mode can be off, costing, or full. Default is off." ) do |val|
|
342
|
+
options[:options]['costingMode'] = val
|
343
|
+
end
|
344
|
+
opts.on('--credential VALUE', String, "Credential ID or \"local\"" ) do |val|
|
345
|
+
options[:options]['credential'] = val
|
346
|
+
end
|
332
347
|
build_common_options(opts, options, [:options, :payload, :json, :dry_run, :remote])
|
333
348
|
end
|
334
349
|
optparse.parse!(args)
|
@@ -352,12 +367,26 @@ class Morpheus::Cli::Clouds
|
|
352
367
|
cloud_type = cloud_type_for_id(cloud['zoneTypeId'])
|
353
368
|
cloud_payload = {}
|
354
369
|
all_option_types = update_cloud_option_types(cloud_type)
|
355
|
-
#params = Morpheus::Cli::OptionTypes.
|
370
|
+
#params = Morpheus::Cli::OptionTypes.no_prompt(all_option_types, options[:options], @api_client, {zoneId: cloud['id'], zoneTypeId: cloud_type['id']})
|
356
371
|
params = options[:options] || {}
|
372
|
+
|
373
|
+
# Credentials (ideally only if value passed in and name can be parsed)
|
374
|
+
if options[:options]['credential']
|
375
|
+
credential_code = "credential"
|
376
|
+
credential_option_type = {'code' => credential_code, 'fieldName' => credential_code, 'fieldLabel' => 'Credentials', 'type' => 'select', 'optionSource' => 'credentials', 'description' => 'Enter an existing credential ID or choose "local"', 'defaultValue' => "local", 'required' => true}
|
377
|
+
# supported_credential_types = ['username-keypair', 'username-password', 'username-password-keypair'].compact.flatten.join(",").split(",").collect {|it| it.strip }
|
378
|
+
credential_params = {"new" => false, "zoneId" => cloud['id']}
|
379
|
+
credential_value = Morpheus::Cli::OptionTypes.select_prompt(credential_option_type, @api_client, credential_params, true, options[:options][credential_code])
|
380
|
+
if !credential_value.to_s.empty?
|
381
|
+
if credential_value == "local"
|
382
|
+
params[credential_code] = {"type" => credential_value}
|
383
|
+
elsif credential_value.to_s =~ /\A\d{1,}\Z/
|
384
|
+
params[credential_code] = {"id" => credential_value.to_i}
|
385
|
+
end
|
386
|
+
end
|
387
|
+
end
|
357
388
|
if params.empty?
|
358
|
-
|
359
|
-
puts_error format_available_options(all_option_types)
|
360
|
-
exit 1
|
389
|
+
raise_command_error "Specify at least one option to update.\n#{optparse}"
|
361
390
|
end
|
362
391
|
# some optionTypes have fieldContext='zone', so move those to the root level of the zone payload
|
363
392
|
if params['zone'].is_a?(Hash)
|
@@ -919,25 +948,17 @@ class Morpheus::Cli::Clouds
|
|
919
948
|
|
920
949
|
private
|
921
950
|
|
922
|
-
def
|
923
|
-
|
924
|
-
|
925
|
-
|
926
|
-
{
|
927
|
-
|
928
|
-
|
929
|
-
|
930
|
-
|
931
|
-
|
932
|
-
|
933
|
-
servers: cloud['serverCount'],
|
934
|
-
status: format_cloud_status(cloud)
|
935
|
-
}
|
936
|
-
end
|
937
|
-
columns = [
|
938
|
-
:id, :name, :type, :location, "REGION CODE", :groups, :servers, :status
|
939
|
-
]
|
940
|
-
print as_pretty_table(rows, columns, opts)
|
951
|
+
def cloud_list_column_definitions(options)
|
952
|
+
{
|
953
|
+
"ID" => 'id',
|
954
|
+
"Name" => 'name',
|
955
|
+
"Type" => lambda {|it| it['zoneType'] ? it['zoneType']['name'] : '' },
|
956
|
+
"Location" => 'location',
|
957
|
+
"Region Code" => lambda {|it| it['regionCode'] },
|
958
|
+
"Groups" => lambda {|it| (it['groups'] || []).collect {|g| g.instance_of?(Hash) ? g['name'] : g.to_s }.join(', ') },
|
959
|
+
"Servers" => lambda {|it| it['serverCount'] },
|
960
|
+
"Status" => lambda {|it| format_cloud_status(it) },
|
961
|
+
}
|
941
962
|
end
|
942
963
|
|
943
964
|
def add_cloud_option_types(cloud_type)
|
@@ -948,6 +969,9 @@ class Morpheus::Cli::Clouds
|
|
948
969
|
{'fieldName' => 'code', 'fieldLabel' => 'Code', 'type' => 'text', 'required' => false, 'displayOrder' => 2},
|
949
970
|
{'fieldName' => 'location', 'fieldLabel' => 'Location', 'type' => 'text', 'required' => false, 'displayOrder' => 3},
|
950
971
|
{'fieldName' => 'visibility', 'fieldLabel' => 'Visibility', 'type' => 'select', 'selectOptions' => [{'name' => 'Private', 'value' => 'private'},{'name' => 'Public', 'value' => 'public'}], 'required' => false, 'description' => 'Visibility', 'category' => 'permissions', 'defaultValue' => 'private', 'displayOrder' => 4},
|
972
|
+
{'fieldName' => 'enabled', 'fieldLabel' => 'Enabled', 'type' => 'checkbox', 'required' => false, 'defaultValue' => true, 'displayOrder' => 5},
|
973
|
+
{'fieldName' => 'autoRecoverPowerState', 'fieldLabel' => 'Automatically Power On VMs', 'type' => 'checkbox', 'required' => false, 'defaultValue' => false, 'displayOrder' => 6},
|
974
|
+
{'fieldName' => 'credential', 'fieldLabel' => 'Credentials', 'type' => 'select', 'optionSource' => 'credentials', 'description' => 'Credential ID or use "local" to specify username and password', 'displayOrder' => 9, 'defaultValue' => "local", 'required' => true, :for_help_only => true}, # hacky way to render this but not prompt for it
|
951
975
|
]
|
952
976
|
|
953
977
|
# TODO: Account
|
@@ -185,30 +185,29 @@ class Morpheus::Cli::CypherCommand
|
|
185
185
|
# This response does contain cypher too though.
|
186
186
|
|
187
187
|
if cypher_item.empty?
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
188
|
+
puts "Cypher item not found in response"
|
189
|
+
else
|
190
|
+
description_cols = {
|
191
|
+
#"ID" => 'id',
|
192
|
+
"Key" => lambda {|it| it["itemKey"] },
|
193
|
+
"TTL" => lambda {|it|
|
194
|
+
format_expiration_ttl(it["expireDate"])
|
195
|
+
},
|
196
|
+
# "Type" => lambda {|it|
|
197
|
+
# data_type
|
198
|
+
# },
|
199
|
+
"Expiration" => lambda {|it|
|
200
|
+
format_expiration_date(it["expireDate"])
|
201
|
+
},
|
202
|
+
# "Date Created" => lambda {|it| format_local_dt(it["dateCreated"]) },
|
203
|
+
"Last Updated" => lambda {|it| format_local_dt(it["lastUpdated"]) },
|
204
|
+
"Last Accessed" => lambda {|it| format_local_dt(it["lastAccessed"]) }
|
205
|
+
}
|
206
|
+
if cypher_item["expireDate"].nil?
|
207
|
+
description_cols.delete("Expires")
|
208
|
+
end
|
209
|
+
print_description_list(description_cols, cypher_item)
|
209
210
|
end
|
210
|
-
print_description_list(description_cols, cypher_item)
|
211
|
-
|
212
211
|
# print_h2 "Value", options
|
213
212
|
# print_h2 "Data", options
|
214
213
|
print_h2 "Data (#{data_type})", options
|
@@ -496,15 +496,15 @@ class Morpheus::Cli::Instances
|
|
496
496
|
payload["zoneId"] = cloud["id"]
|
497
497
|
payload.deep_merge!({"instance" => {"cloud" => cloud["name"] } })
|
498
498
|
end
|
499
|
-
if options[:cloud]
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
end
|
499
|
+
# if options[:cloud]
|
500
|
+
# group_id = group ? group["id"] : ((payload["instance"] && payload["instance"]["site"].is_a?(Hash)) ? payload["instance"]["site"]["id"] : nil)
|
501
|
+
# cloud = find_cloud_by_name_or_id_for_provisioning(group_id, options[:cloud])
|
502
|
+
# if cloud.nil?
|
503
|
+
# return 1, "cloud not found by #{options[:cloud]}"
|
504
|
+
# end
|
505
|
+
# payload["zoneId"] = cloud["id"]
|
506
|
+
# payload.deep_merge!({"instance" => {"cloud" => cloud["name"] } })
|
507
|
+
# end
|
508
508
|
if options[:instance_type_code]
|
509
509
|
# should just use find_instance_type_by_name_or_id
|
510
510
|
# note that the api actually will match name name or code
|
@@ -539,7 +539,7 @@ class Morpheus::Cli::Instances
|
|
539
539
|
end
|
540
540
|
end
|
541
541
|
end
|
542
|
-
|
542
|
+
|
543
543
|
payload['instance'] ||= {}
|
544
544
|
if options[:instance_name]
|
545
545
|
payload['instance']['name'] = options[:instance_name]
|
@@ -156,9 +156,15 @@ class Morpheus::Cli::LibraryOptionListsCommand
|
|
156
156
|
"Source URL" => 'sourceUrl',
|
157
157
|
"Real Time" => lambda {|it| format_boolean it['realTime'] },
|
158
158
|
"Ignore SSL Errors" => lambda {|it| format_boolean it['ignoreSSLErrors'] },
|
159
|
-
"Source Method" => lambda {|it| it['sourceMethod'].to_s.upcase }
|
159
|
+
"Source Method" => lambda {|it| it['sourceMethod'].to_s.upcase },
|
160
|
+
"Credentials" => lambda {|it| it['credential'] ? (it['credential']['type'] == 'local' ? '(Local)' : it['credential']['name']) : nil },
|
161
|
+
"Username" => 'serviceUsername',
|
162
|
+
"Password" => 'servicePassword',
|
160
163
|
}
|
161
164
|
option_list_columns.delete("API Type") if option_type_list['type'] != 'api'
|
165
|
+
option_list_columns.delete("Credentials") if !['rest','ldap'].include?(option_type_list['type']) # || !(option_type_list['credential'] && option_type_list['credential']['id'])
|
166
|
+
option_list_columns.delete("Username") if !['rest','ldap'].include?(option_type_list['type']) || !(option_type_list['serviceUsername'])
|
167
|
+
option_list_columns.delete("Password") if !['rest','ldap'].include?(option_type_list['type']) || !(option_type_list['servicePassword'])
|
162
168
|
source_headers = []
|
163
169
|
if option_type_list['config'] && option_type_list['config']['sourceHeaders']
|
164
170
|
source_headers = option_type_list['config']['sourceHeaders'].collect do |header|
|
@@ -420,14 +426,18 @@ class Morpheus::Cli::LibraryOptionListsCommand
|
|
420
426
|
{'dependsOnCode' => 'optionTypeList.type:rest', 'fieldName' => 'ignoreSSLErrors', 'fieldLabel' => 'Ignore SSL Errors', 'type' => 'checkbox', 'defaultValue' => false, 'displayOrder' => 6},
|
421
427
|
{'dependsOnCode' => 'optionTypeList.type:rest', 'fieldName' => 'realTime', 'fieldLabel' => 'Real Time', 'type' => 'checkbox', 'defaultValue' => false, 'displayOrder' => 7},
|
422
428
|
{'dependsOnCode' => 'optionTypeList.type:rest', 'fieldName' => 'sourceMethod', 'fieldLabel' => 'Source Method', 'type' => 'select', 'selectOptions' => [{'name' => 'GET', 'value' => 'GET'}, {'name' => 'POST', 'value' => 'POST'}], 'defaultValue' => 'GET', 'required' => true, 'displayOrder' => 8},
|
429
|
+
{'dependsOnCode' => 'optionTypeList.type:rest|ldap', 'fieldName' => 'credential', 'fieldLabel' => 'Credentials', 'type' => 'select', 'optionSource' => 'credentials', 'description' => 'Credential ID or use "local" to specify username and password', 'displayOrder' => 9, 'defaultValue' => "local", 'required' => true, :for_help_only => true}, # hacky way to render this but not prompt for it
|
430
|
+
{'dependsOnCode' => 'optionTypeList.type:rest', 'fieldName' => 'serviceUsername', 'fieldLabel' => 'Username', 'type' => 'text', 'description' => "A Basic Auth Username for use when type is 'rest'.", 'displayOrder' => 9, "credentialFieldContext" => 'credential', "credentialFieldName" => 'username', "credentialType" => "username-password,oauth2"},
|
431
|
+
{'dependsOnCode' => 'optionTypeList.type:rest', 'fieldName' => 'servicePassword', 'fieldLabel' => 'Password', 'type' => 'password', 'description' => "A Basic Auth Password for use when type is 'rest'.", 'displayOrder' => 10, "credentialFieldContext" => 'credential', "credentialFieldName" => 'password', "credentialType" => "username-password,oauth2"},
|
423
432
|
# sourceHeaders component (is done afterwards manually)
|
424
|
-
{'dependsOnCode' => 'optionTypeList.type:api', 'fieldName' => 'apiType', 'fieldLabel' => 'Option List', 'type' => 'select', 'optionSource' => 'apiOptionLists', 'required' => true, 'description' => 'The code of the api option list to use, eg. clouds, environments, groups, instances, instance-wiki, networks, servicePlans, resourcePools, securityGroups, servers, server-wiki', 'displayOrder' =>
|
425
|
-
{'dependsOnCode' => 'optionTypeList.type:ldap', 'fieldName' => '
|
426
|
-
{'dependsOnCode' => 'optionTypeList.type:ldap', 'fieldName' => '
|
427
|
-
{'dependsOnCode' => 'optionTypeList.type:ldap', 'fieldName' => '
|
428
|
-
{'dependsOnCode' => 'optionTypeList.type:
|
429
|
-
{'dependsOnCode' => 'optionTypeList.type:rest|api|
|
430
|
-
{'dependsOnCode' => 'optionTypeList.type:rest|api', 'fieldName' => '
|
433
|
+
{'dependsOnCode' => 'optionTypeList.type:api', 'fieldName' => 'apiType', 'fieldLabel' => 'Option List', 'type' => 'select', 'optionSource' => 'apiOptionLists', 'required' => true, 'description' => 'The code of the api option list to use, eg. clouds, environments, groups, instances, instance-wiki, networks, servicePlans, resourcePools, securityGroups, servers, server-wiki', 'displayOrder' => 10},
|
434
|
+
{'dependsOnCode' => 'optionTypeList.type:rest|ldap', 'fieldName' => 'credential', 'fieldLabel' => 'Credentials', 'type' => 'select', 'optionSource' => 'credentials', 'description' => 'Credential ID or use "local" to specify username and password', 'displayOrder' => 9, 'defaultValue' => "local", 'required' => true, :for_help_only => true}, # hacky way to render this but not prompt for it
|
435
|
+
{'dependsOnCode' => 'optionTypeList.type:ldap', 'fieldName' => 'serviceUsername', 'fieldLabel' => 'Username', 'type' => 'text', 'description' => "An LDAP Username for use when type is 'ldap'.", 'displayOrder' => 11, "credentialFieldContext" => 'credential', "credentialFieldName" => 'username', "credentialType" => "username-password"},
|
436
|
+
{'dependsOnCode' => 'optionTypeList.type:ldap', 'fieldName' => 'servicePassword', 'fieldLabel' => 'Password', 'type' => 'password', 'description' => "An LDAP Password for use when type is 'ldap'.", 'displayOrder' => 12, "credentialFieldContext" => 'credential', "credentialFieldName" => 'password', "credentialType" => "username-password"},
|
437
|
+
{'dependsOnCode' => 'optionTypeList.type:ldap', 'fieldName' => 'ldapQuery', 'fieldLabel' => 'LDAP Query', 'type' => 'text', 'description' => "LDAP Queries are standard LDAP formatted queries where different objects can be searched. Dependent parameters can be loaded into the query using the <%=phrase%> syntax.", 'displayOrder' => 13},
|
438
|
+
{'dependsOnCode' => 'optionTypeList.type:rest|api|manual', 'fieldName' => 'initialDataset', 'fieldLabel' => 'Initial Dataset', 'type' => 'code-editor', 'description' => "Create an initial json dataset to be used as the collection for this option list. It should be a list containing objects with properties 'name', and 'value'. However, if there is a translation script, that will also be passed through.", 'displayOrder' => 14, 'dataType' => 'string'},
|
439
|
+
{'dependsOnCode' => 'optionTypeList.type:rest|api|ldap', 'fieldName' => 'translationScript', 'fieldLabel' => 'Translation Script', 'type' => 'code-editor', 'description' => "Create a js script to translate the result data object into an Array containing objects with properties name, and value. The input data is provided as data and the result should be put on the global variable results.", 'displayOrder' => 15, 'dataType' => 'string'},
|
440
|
+
{'dependsOnCode' => 'optionTypeList.type:rest|api', 'fieldName' => 'requestScript', 'fieldLabel' => 'Request Script', 'type' => 'code-editor', 'description' => "Create a js script to prepare the request. Return a data object as the body for a post, and return an array containing properties name and value for a get. The input data is provided as data and the result should be put on the global variable results.", 'displayOrder' => 16, 'dataType' => 'string'},
|
431
441
|
]
|
432
442
|
|
433
443
|
end
|
@@ -238,11 +238,7 @@ class Morpheus::Cli::PricesCommand
|
|
238
238
|
end
|
239
239
|
end
|
240
240
|
opts.on("--currency [CURRENCY]", String, "Price currency") do |val|
|
241
|
-
|
242
|
-
params['currency'] = val.upcase
|
243
|
-
else
|
244
|
-
raise_command_error "Unsupported currency '#{val}'. Available currencies: #{avail_currencies.join(', ')}"
|
245
|
-
end
|
241
|
+
options[:currency] = val
|
246
242
|
end
|
247
243
|
opts.on("--cost [AMOUNT]", Float, "Price cost") do |val|
|
248
244
|
params['cost'] = val
|
@@ -269,6 +265,15 @@ class Morpheus::Cli::PricesCommand
|
|
269
265
|
return 1
|
270
266
|
end
|
271
267
|
|
268
|
+
if options[:currency]
|
269
|
+
if avail_currencies.include?(options[:currency].upcase)
|
270
|
+
params['currency'] = options[:currency].upcase
|
271
|
+
else
|
272
|
+
raise_command_error "Unsupported currency '#{options[:currency]}'. Available currencies: #{avail_currencies.join(', ')}"
|
273
|
+
return 1
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
272
277
|
begin
|
273
278
|
payload = parse_payload(options)
|
274
279
|
|
@@ -402,11 +407,7 @@ class Morpheus::Cli::PricesCommand
|
|
402
407
|
end
|
403
408
|
end
|
404
409
|
opts.on("--currency [CURRENCY]", String, "Price currency") do |val|
|
405
|
-
|
406
|
-
params['currency'] = val.upcase
|
407
|
-
else
|
408
|
-
raise_command_error "Unsupported currency '#{val}'. Available currencies: #{avail_currencies.join(', ')}"
|
409
|
-
end
|
410
|
+
options[:currency] = val
|
410
411
|
end
|
411
412
|
opts.on("--cost [AMOUNT]", Float, "Price cost") do |val|
|
412
413
|
params['cost'] = val
|
@@ -431,11 +432,21 @@ class Morpheus::Cli::PricesCommand
|
|
431
432
|
end
|
432
433
|
optparse.parse!(args)
|
433
434
|
connect(options)
|
435
|
+
|
434
436
|
if args.count != 1
|
435
437
|
raise_command_error "wrong number of arguments, expected 1 and got (#{args.count}) #{args}\n#{optparse}"
|
436
438
|
return 1
|
437
439
|
end
|
438
440
|
|
441
|
+
if options[:currency]
|
442
|
+
if avail_currencies.include?(options[:currency].upcase)
|
443
|
+
params['currency'] = options[:currency].upcase
|
444
|
+
else
|
445
|
+
raise_command_error "Unsupported currency '#{options[:currency]}'. Available currencies: #{avail_currencies.join(', ')}"
|
446
|
+
return 1
|
447
|
+
end
|
448
|
+
end
|
449
|
+
|
439
450
|
begin
|
440
451
|
price = find_price(args[0])
|
441
452
|
|
@@ -608,7 +619,10 @@ class Morpheus::Cli::PricesCommand
|
|
608
619
|
end
|
609
620
|
|
610
621
|
def avail_currencies
|
611
|
-
|
622
|
+
if @avail_currencies.nil?
|
623
|
+
@avail_currencies = @prices_interface.list_currencies()['currencies'].collect {|it| it['value']}
|
624
|
+
end
|
625
|
+
@avail_currencies
|
612
626
|
end
|
613
627
|
|
614
628
|
def prompt_for_price_type(params, options, price={})
|
@@ -297,6 +297,9 @@ class Morpheus::Cli::Tasks
|
|
297
297
|
opts.on('--execute-target VALUE', String, "Execute Target" ) do |val|
|
298
298
|
options[:options]['executeTarget'] = val
|
299
299
|
end
|
300
|
+
opts.on('--credential VALUE', String, "Credential ID or \"local\"" ) do |val|
|
301
|
+
options[:options]['credential'] = val
|
302
|
+
end
|
300
303
|
opts.on('--target-host VALUE', String, "Target Host" ) do |val|
|
301
304
|
options[:options]['taskOptions'] ||= {}
|
302
305
|
options[:options]['taskOptions']['host'] = val
|
@@ -313,6 +316,10 @@ class Morpheus::Cli::Tasks
|
|
313
316
|
options[:options]['taskOptions'] ||= {}
|
314
317
|
options[:options]['taskOptions']['password'] = val
|
315
318
|
end
|
319
|
+
opts.on('--target-ssh-key VALUE', String, "Target SSH Key" ) do |val|
|
320
|
+
options[:options]['taskOptions'] ||= {}
|
321
|
+
options[:options]['taskOptions']['sshKey'] = val
|
322
|
+
end
|
316
323
|
opts.on('--git-repo VALUE', String, "Git Repo ID" ) do |val|
|
317
324
|
options[:options]['taskOptions'] ||= {}
|
318
325
|
options[:options]['taskOptions']['localScriptGitId'] = val
|
@@ -521,17 +528,38 @@ class Morpheus::Cli::Tasks
|
|
521
528
|
payload['task']['taskOptions'] ||= {}
|
522
529
|
payload['task']['taskOptions']['port'] = v_prompt['taskOptions']['port']
|
523
530
|
end
|
524
|
-
#
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
531
|
+
# Credentials
|
532
|
+
credential_code = "credential"
|
533
|
+
credential_option_type = {'code' => credential_code, 'fieldName' => credential_code, 'fieldLabel' => 'Credentials', 'type' => 'select', 'optionSource' => 'credentials', 'description' => 'Enter an existing credential ID or choose "local"', 'defaultValue' => "local", 'required' => true}
|
534
|
+
supported_credential_types = ['username-keypair', 'username-password', 'username-password-keypair'].compact.flatten.join(",").split(",").collect {|it| it.strip }
|
535
|
+
credential_params = {"new" => false, "credentialTypes" => supported_credential_types}
|
536
|
+
credential_value = Morpheus::Cli::OptionTypes.select_prompt(credential_option_type, @api_client, credential_params, options[:no_prompt], options[:options][credential_code])
|
537
|
+
if !credential_value.to_s.empty?
|
538
|
+
if credential_value == "local"
|
539
|
+
payload['task'][credential_code] = {"type" => credential_value}
|
540
|
+
elsif credential_value.to_s =~ /\A\d{1,}\Z/
|
541
|
+
payload['task'][credential_code] = {"id" => credential_value.to_i}
|
542
|
+
end
|
529
543
|
end
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
544
|
+
if credential_value == "local"
|
545
|
+
# Username
|
546
|
+
v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldContext' => 'taskOptions', 'fieldName' => 'username', 'fieldLabel' => 'Username', 'type' => 'text', 'description' => 'Username for remote execution'}], options[:options], @api_client)
|
547
|
+
if v_prompt['taskOptions'] && !v_prompt['taskOptions']['username'].to_s.empty?
|
548
|
+
payload['task']['taskOptions'] ||= {}
|
549
|
+
payload['task']['taskOptions']['username'] = v_prompt['taskOptions']['username']
|
550
|
+
end
|
551
|
+
# Password
|
552
|
+
v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldContext' => 'taskOptions', 'fieldName' => 'password', 'fieldLabel' => 'Password', 'type' => 'password', 'description' => 'Password for remote execution'}], options[:options], @api_client)
|
553
|
+
if v_prompt['taskOptions'] && !v_prompt['taskOptions']['password'].to_s.empty?
|
554
|
+
payload['task']['taskOptions'] ||= {}
|
555
|
+
payload['task']['taskOptions']['password'] = v_prompt['taskOptions']['password']
|
556
|
+
end
|
557
|
+
# SSH Key
|
558
|
+
v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldContext' => 'taskOptions', 'fieldName' => 'sshKey', 'fieldLabel' => 'Key', 'type' => 'select', 'optionSource' => 'keyPairs', 'description' => 'SSH Key for remote execution'}], options[:options], @api_client)
|
559
|
+
if v_prompt['taskOptions'] && !v_prompt['taskOptions']['sshKey'].to_s.empty?
|
560
|
+
payload['task']['taskOptions'] ||= {}
|
561
|
+
payload['task']['taskOptions']['sshKey'] = v_prompt['taskOptions']['sshKey']
|
562
|
+
end
|
535
563
|
end
|
536
564
|
end
|
537
565
|
|
@@ -639,6 +667,9 @@ class Morpheus::Cli::Tasks
|
|
639
667
|
opts.on('--execute-target VALUE', String, "Execute Target" ) do |val|
|
640
668
|
options[:options]['executeTarget'] = val
|
641
669
|
end
|
670
|
+
opts.on('--credential VALUE', String, "Credential ID or \"local\"" ) do |val|
|
671
|
+
options[:options]['credential'] = val
|
672
|
+
end
|
642
673
|
opts.on('--target-host VALUE', String, "Target Host" ) do |val|
|
643
674
|
options[:options]['taskOptions'] ||= {}
|
644
675
|
options[:options]['taskOptions']['host'] = val
|
@@ -655,6 +686,10 @@ class Morpheus::Cli::Tasks
|
|
655
686
|
options[:options]['taskOptions'] ||= {}
|
656
687
|
options[:options]['taskOptions']['password'] = val
|
657
688
|
end
|
689
|
+
opts.on('--target-ssh-key VALUE', String, "Target SSH Key" ) do |val|
|
690
|
+
options[:options]['taskOptions'] ||= {}
|
691
|
+
options[:options]['taskOptions']['sshKey'] = val
|
692
|
+
end
|
658
693
|
opts.on('--git-repo VALUE', String, "Git Repo ID" ) do |val|
|
659
694
|
options[:options]['taskOptions'] ||= {}
|
660
695
|
options[:options]['taskOptions']['localScriptGitId'] = val
|
@@ -1160,6 +1160,7 @@ module Morpheus::Cli::ProvisioningHelper
|
|
1160
1160
|
#volume['size'] = plan_size
|
1161
1161
|
#volume['sizeId'] = nil #volume.delete('sizeId')
|
1162
1162
|
end
|
1163
|
+
|
1163
1164
|
if !datastore_options.empty?
|
1164
1165
|
default_datastore = datastore_options.find {|ds| ds['value'].to_s == volume['datastoreId'].to_s}
|
1165
1166
|
v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'datastoreId', 'type' => 'select', 'fieldLabel' => 'Root Datastore', 'selectOptions' => datastore_options, 'required' => true, 'description' => 'Choose a datastore.', 'defaultValue' => default_datastore ? default_datastore['name'] : volume['datastoreId']}], options[:options])
|
@@ -62,10 +62,12 @@ module Morpheus
|
|
62
62
|
option_type['type'] = 'multiText'
|
63
63
|
end
|
64
64
|
end
|
65
|
+
credential_option_types = {}
|
65
66
|
# puts "Options Prompt #{options}"
|
66
67
|
# Sort options by default, group, advanced
|
67
68
|
cur_field_group = 'default'
|
68
69
|
self.sorted_option_types(option_types).each do |option_type|
|
70
|
+
next if option_type[:for_help_only] == true # hacky
|
69
71
|
context_map = results
|
70
72
|
value = nil
|
71
73
|
value_found = false
|
@@ -145,6 +147,29 @@ module Morpheus
|
|
145
147
|
next if !found_dep_value
|
146
148
|
end
|
147
149
|
|
150
|
+
# inject a Credentials prompt for optionTypes that have been replaced by credentials
|
151
|
+
credential_code = option_type['credentialFieldContext']
|
152
|
+
if !credential_code.to_s.empty?
|
153
|
+
if !credential_option_types[credential_code]
|
154
|
+
credential_option_type = {'code' => credential_code, 'fieldName' => credential_code, 'fieldLabel' => 'Credentials', 'type' => 'select', 'optionSource' => 'credentials', 'description' => 'Credential ID or use "local" to specify username and password', 'defaultValue' => "local", 'required' => true}
|
155
|
+
credential_option_types[credential_code] = credential_option_type
|
156
|
+
supported_credential_types = [option_type['credentialType'], option_type['credentialTypes']].compact.flatten.join(",").split(",").collect {|it| it.strip }
|
157
|
+
credential_params = {"new" => false, "credentialTypes" => supported_credential_types}
|
158
|
+
credential_value = select_prompt(credential_option_type, api_client, credential_params, no_prompt, options[credential_code])
|
159
|
+
if !credential_value.to_s.empty?
|
160
|
+
if credential_value == "local"
|
161
|
+
context_map[credential_code] = {"type" => credential_value}
|
162
|
+
elsif credential_value.to_s =~ /\A\d{1,}\Z/
|
163
|
+
context_map[credential_code] = {"id" => credential_value.to_i}
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
# skip this option unless using local credentials
|
168
|
+
if context_map[credential_code].is_a?(Hash) && context_map[credential_code]["type"] != "local"
|
169
|
+
next
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
148
173
|
cur_namespace = options
|
149
174
|
parent_context_map = context_map
|
150
175
|
parent_ns = field_name
|
data/lib/morpheus/cli/version.rb
CHANGED
data/lib/morpheus/routes.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: morpheus-cli
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 5.
|
4
|
+
version: 5.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- David Estes
|
@@ -11,7 +11,7 @@ authors:
|
|
11
11
|
autorequire:
|
12
12
|
bindir: bin
|
13
13
|
cert_chain: []
|
14
|
-
date: 2022-
|
14
|
+
date: 2022-05-13 00:00:00.000000000 Z
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
17
17
|
name: bundler
|