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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 84ddc5a04df52338a55327598f760d41e51d93d6a03fbebac557202c3a952116
4
- data.tar.gz: 3a586a705cd480573bd9da349c737a18761999096ec59bd77ce98a27a41f07d4
3
+ metadata.gz: ca02ec5cffb310afe4458568f745332e8f81f1d03a525ad1a9d9d1a3ecd08313
4
+ data.tar.gz: 323d4e3627c67fbd676bb34eb0b6f62b0eecff6180285af93599aba21f0a9781
5
5
  SHA512:
6
- metadata.gz: 43dab7d23d4e1889dfc611445dcd2da0c61dc8c7ec1245cddebea0469cede7ebfdeb453b718e0bbbc247b620be9ae4af5698f46bf1d0c689f38940894b112e7f
7
- data.tar.gz: fb08c98c2395a86b41df4fe8ce6aaa7b8d4306aa39461d849ee121a256cfcc733ab1aa58c171c6b181ca62c824a2b3e55c80a206194a5e4993d7495413472617
6
+ metadata.gz: bc72cdba318abc74aef8dec7ab8602ad7ec7196101950dfc0b29b8fe8c4298c2de5b41265a386457c38fa0ac9e46082cb86d04d7cfbc75652a1824d5a036cec1
7
+ data.tar.gz: f546fe9ed24fcc380d8aa1f2fc6d05a1cfb7186dcee48e66c340c51951b69e3b04408db5400ddcb23c9c596ccb14867571f1e21b6ceeb3e253c720d0525c21b0
data/Dockerfile CHANGED
@@ -1,5 +1,5 @@
1
1
  FROM ruby:2.5.1
2
2
 
3
- RUN gem install morpheus-cli -v 5.4.5.1
3
+ RUN gem install morpheus-cli -v 5.5.0
4
4
 
5
5
  ENTRYPOINT ["morpheus"]
@@ -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.camelcase.pluralize
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.camelcase.singularize
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
- print_clouds_table(clouds, options)
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.prompt(all_option_types, options[:options], @api_client, {zoneTypeId: cloud_type['id']})
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
- puts_error optparse.banner
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 print_clouds_table(clouds, opts={})
923
- table_color = opts[:color] || cyan
924
- rows = clouds.collect do |cloud|
925
- cloud_type = cloud_type_for_id(cloud['zoneTypeId'])
926
- {
927
- id: cloud['id'],
928
- name: cloud['name'],
929
- type: cloud_type ? cloud_type['name'] : '',
930
- location: cloud['location'],
931
- "REGION CODE": cloud['regionCode'],
932
- groups: (cloud['groups'] || []).collect {|it| it.instance_of?(Hash) ? it['name'] : it.to_s }.join(', '),
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
- puts_error "Cypher data not found in response"
189
- return 1
190
- end
191
- description_cols = {
192
- #"ID" => 'id',
193
- "Key" => lambda {|it| it["itemKey"] },
194
- "TTL" => lambda {|it|
195
- format_expiration_ttl(it["expireDate"])
196
- },
197
- # "Type" => lambda {|it|
198
- # data_type
199
- # },
200
- "Expiration" => lambda {|it|
201
- format_expiration_date(it["expireDate"])
202
- },
203
- # "Date Created" => lambda {|it| format_local_dt(it["dateCreated"]) },
204
- "Last Updated" => lambda {|it| format_local_dt(it["lastUpdated"]) },
205
- "Last Accessed" => lambda {|it| format_local_dt(it["lastAccessed"]) }
206
- }
207
- if cypher_item["expireDate"].nil?
208
- description_cols.delete("Expires")
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
- 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
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' => 9},
425
- {'dependsOnCode' => 'optionTypeList.type:ldap', 'fieldName' => 'sourceUsername', 'fieldLabel' => 'Source Username', 'type' => 'text', 'description' => "An LDAP Username for use when type is 'ldap'.", 'displayOrder' => 10},
426
- {'dependsOnCode' => 'optionTypeList.type:ldap', 'fieldName' => 'sourcePassword', 'fieldLabel' => 'Source Username', 'type' => 'text', 'description' => "An LDAP Password for use when type is 'ldap'.", 'displayOrder' => 11},
427
- {'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' => 12},
428
- {'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' => 13, 'dataType' => 'string'},
429
- {'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' => 14, 'dataType' => 'string'},
430
- {'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' => 15, 'dataType' => 'string'},
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
- if avail_currencies.include?(val.upcase)
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
- if avail_currencies.include?(val.upcase)
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
- ['CAD','EUR', 'IDR', 'XCD', 'USD', 'XOF', 'NOK', 'AUD', 'XAF', 'NZD', 'MAD', 'DKK', 'GBP', 'CHF', 'XPF', 'ILS', 'ROL', 'TRL','SEK', 'ZAR']
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
- # Host
525
- v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldContext' => 'taskOptions', 'fieldName' => 'username', 'fieldLabel' => 'Username', 'type' => 'text', 'description' => 'Username for remote execution'}], options[:options], @api_client)
526
- if v_prompt['taskOptions'] && !v_prompt['taskOptions']['username'].to_s.empty?
527
- payload['task']['taskOptions'] ||= {}
528
- payload['task']['taskOptions']['username'] = v_prompt['taskOptions']['username']
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
- # Host
531
- v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldContext' => 'taskOptions', 'fieldName' => 'password', 'fieldLabel' => 'Password', 'type' => 'password', 'description' => 'Password for remote execution'}], options[:options], @api_client)
532
- if v_prompt['taskOptions'] && !v_prompt['taskOptions']['password'].to_s.empty?
533
- payload['task']['taskOptions'] ||= {}
534
- payload['task']['taskOptions']['password'] = v_prompt['taskOptions']['password']
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
@@ -1,6 +1,6 @@
1
1
 
2
2
  module Morpheus
3
3
  module Cli
4
- VERSION = "5.4.5.1"
4
+ VERSION = "5.5.0"
5
5
  end
6
6
  end
@@ -16,11 +16,9 @@ module Morpheus::Routes
16
16
  analytics: {},
17
17
  guidance: {},
18
18
  wiki: {},
19
- costing: {
20
- budgets: {},
21
- invoices: {},
22
- usage: {},
23
- },
19
+ budgets: {},
20
+ invoices: {},
21
+ usage: {},
24
22
  approvals: {},
25
23
  activity: {},
26
24
  alarms: {},
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.5.1
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-04-15 00:00:00.000000000 Z
14
+ date: 2022-05-13 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: bundler