morpheus-cli 5.4.5 → 5.5.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.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/Dockerfile +1 -1
  3. data/lib/morpheus/api/api_client.rb +4 -0
  4. data/lib/morpheus/api/clusters_interface.rb +12 -0
  5. data/lib/morpheus/api/network_pool_servers_interface.rb +7 -0
  6. data/lib/morpheus/api/prices_interface.rb +6 -0
  7. data/lib/morpheus/api/scale_thresholds_interface.rb +9 -0
  8. data/lib/morpheus/cli/cli_command.rb +55 -23
  9. data/lib/morpheus/cli/commands/apps.rb +2 -2
  10. data/lib/morpheus/cli/commands/cloud_resource_pools_command.rb +33 -2
  11. data/lib/morpheus/cli/commands/clouds.rb +61 -31
  12. data/lib/morpheus/cli/commands/clusters.rb +66 -5
  13. data/lib/morpheus/cli/commands/cypher_command.rb +22 -23
  14. data/lib/morpheus/cli/commands/hosts.rb +5 -1
  15. data/lib/morpheus/cli/commands/instances.rb +12 -12
  16. data/lib/morpheus/cli/commands/integrations_command.rb +1 -1
  17. data/lib/morpheus/cli/commands/invoices_command.rb +8 -1
  18. data/lib/morpheus/cli/commands/jobs_command.rb +45 -225
  19. data/lib/morpheus/cli/commands/library_container_types_command.rb +52 -3
  20. data/lib/morpheus/cli/commands/library_option_lists_command.rb +18 -8
  21. data/lib/morpheus/cli/commands/library_option_types_command.rb +56 -62
  22. data/lib/morpheus/cli/commands/load_balancers.rb +11 -19
  23. data/lib/morpheus/cli/commands/network_pool_servers_command.rb +5 -2
  24. data/lib/morpheus/cli/commands/prices_command.rb +25 -11
  25. data/lib/morpheus/cli/commands/roles.rb +475 -70
  26. data/lib/morpheus/cli/commands/scale_thresholds.rb +103 -0
  27. data/lib/morpheus/cli/commands/tasks.rb +64 -22
  28. data/lib/morpheus/cli/commands/user_sources_command.rb +107 -39
  29. data/lib/morpheus/cli/commands/users.rb +10 -10
  30. data/lib/morpheus/cli/commands/view.rb +1 -0
  31. data/lib/morpheus/cli/commands/workflows.rb +21 -14
  32. data/lib/morpheus/cli/error_handler.rb +13 -4
  33. data/lib/morpheus/cli/mixins/accounts_helper.rb +1 -1
  34. data/lib/morpheus/cli/mixins/execution_request_helper.rb +1 -1
  35. data/lib/morpheus/cli/mixins/infrastructure_helper.rb +3 -3
  36. data/lib/morpheus/cli/mixins/jobs_helper.rb +173 -0
  37. data/lib/morpheus/cli/mixins/print_helper.rb +120 -38
  38. data/lib/morpheus/cli/mixins/provisioning_helper.rb +2 -3
  39. data/lib/morpheus/cli/mixins/rest_command.rb +41 -14
  40. data/lib/morpheus/cli/option_types.rb +69 -13
  41. data/lib/morpheus/cli/version.rb +1 -1
  42. data/lib/morpheus/logging.rb +6 -8
  43. data/lib/morpheus/routes.rb +3 -5
  44. metadata +6 -4
@@ -118,10 +118,10 @@ class Morpheus::Cli::LibraryContainerTypesCommand
118
118
  begin
119
119
  @library_container_types_interface.setopts(options)
120
120
  if options[:dry_run]
121
- if arg.to_s =~ /\A\d{1,}\Z/
122
- print_dry_run @library_container_types_interface.dry.get(layout_id, arg.to_i)
121
+ if id.to_s =~ /\A\d{1,}\Z/
122
+ print_dry_run @library_container_types_interface.dry.get(layout_id, id.to_i)
123
123
  else
124
- print_dry_run @library_container_types_interface.dry.list(layout_id, {name:arg})
124
+ print_dry_run @library_container_types_interface.dry.list(layout_id, {name:id})
125
125
  end
126
126
  return
127
127
  end
@@ -255,6 +255,24 @@ class Morpheus::Cli::LibraryContainerTypesCommand
255
255
  opts.on('--technology CODE', String, "Technology. This is the provision type code.") do |val|
256
256
  params['provisionTypeCode'] = val
257
257
  end
258
+ opts.on('--ports NAME=PORT,NAME=PORT', String, "List of exposed port definitions in the format NAME=PORT|PROTOCOL, Example: \"WEB=80|HTTP,SECURE=443|HTTPS\"") do |val|
259
+ params['containerPorts'] ||= []
260
+ parsed_ports = val.split(",").each do |value_pair|
261
+ k,v = value_pair.strip.split("=")
262
+ value_array = v.split("|")
263
+ port_name = k.to_s.strip
264
+ port_number = value_array[0].to_s.strip.to_i
265
+ exposed_port = {'name' => port_name,'port' => port_number }
266
+ if value_array[1]
267
+ exposed_port['loadBalanceProtocol'] = value_array[1].strip
268
+ end
269
+ if exposed_port['name'].to_s.empty? || !exposed_port['port'] || exposed_port['port'].to_i <= 0
270
+ raise_command_error "Invalid exposed port definition: '#{value_pair}'. Expected format is 'NAME=PORT'", args, optparse
271
+ end
272
+ params['containerPorts'] << exposed_port
273
+ end
274
+ # options[:options]['containerPorts'] = params['containerPorts']
275
+ end
258
276
  opts.on('--scripts x,y,z', Array, "List of Script IDs") do |val|
259
277
  script_ids = val #.collect {|it| it.to_i }
260
278
  end
@@ -345,6 +363,12 @@ class Morpheus::Cli::LibraryContainerTypesCommand
345
363
 
346
364
  # payload.deep_merge!(provision_type_v_prompt)
347
365
 
366
+ # PORTS
367
+ # if !params['containerPorts']
368
+ # v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'containerPorts', 'type' => 'exposedPorts', 'fieldLabel' => 'Exposed Ports', 'description' => 'The exposed ports in the format NAME=PORT,NAME=PORT for example HTTP=80,HTTPS=443'}], prompt_params)
369
+ # params['containerPorts'] = v_prompt['containerPorts']
370
+ # end
371
+
348
372
  # ENVIRONMENT VARIABLES
349
373
  if evars
350
374
  params['environmentVariables'] = evars
@@ -367,6 +391,7 @@ class Morpheus::Cli::LibraryContainerTypesCommand
367
391
  # prompt
368
392
  end
369
393
 
394
+
370
395
  # payload = {'containerType' => params}
371
396
  payload['containerType'] ||= {}
372
397
  payload['containerType'].deep_merge!(params)
@@ -422,6 +447,24 @@ class Morpheus::Cli::LibraryContainerTypesCommand
422
447
  # opts.on('--technology CODE', String, "Technology") do |val|
423
448
  # params['provisionTypeCode'] = val
424
449
  # end
450
+ opts.on('--ports NAME=PORT,NAME=PORT', String, "List of exposed port definitions in the format NAME=PORT|PROTOCOL, Example: \"WEB=80|HTTP,SECURE=443|HTTPS\"") do |val|
451
+ params['containerPorts'] ||= []
452
+ parsed_ports = val.split(",").each do |value_pair|
453
+ k,v = value_pair.strip.split("=")
454
+ value_array = v.split("|")
455
+ port_name = k.to_s.strip
456
+ port_number = value_array[0].to_s.strip.to_i
457
+ exposed_port = {'name' => port_name,'port' => port_number }
458
+ if value_array[1]
459
+ exposed_port['loadBalanceProtocol'] = value_array[1].strip
460
+ end
461
+ if exposed_port['name'].to_s.empty? || !exposed_port['port'] || exposed_port['port'].to_i <= 0
462
+ raise_command_error "Invalid exposed port definition: '#{value_pair}'. Expected format is 'NAME=PORT'", args, optparse
463
+ end
464
+ params['containerPorts'] << exposed_port
465
+ end
466
+ # options[:options]['containerPorts'] = params['containerPorts']
467
+ end
425
468
  opts.on('--scripts x,y,z', Array, "List of Script IDs") do |val|
426
469
  script_ids = val #.collect {|it| it.to_i }
427
470
  end
@@ -455,6 +498,12 @@ class Morpheus::Cli::LibraryContainerTypesCommand
455
498
  # params = Morpheus::Cli::OptionTypes.prompt(option_types, options[:options], @api_client, options[:params])
456
499
  payload.deep_merge!({'containerType' => passed_options}) unless passed_options.empty?
457
500
 
501
+ # PORTS
502
+ # if !params['containerPorts']
503
+ # v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'containerPorts', 'type' => 'exposedPorts', 'fieldLabel' => 'Exposed Ports', 'description' => 'The exposed ports in the format NAME=PORT,NAME=PORT for example HTTP=80,HTTPS=443'}], prompt_params)
504
+ # params['containerPorts'] = v_prompt['containerPorts']
505
+ # end
506
+
458
507
  # ENVIRONMENT VARIABLES
459
508
  if evars
460
509
 
@@ -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
@@ -36,20 +36,18 @@ class Morpheus::Cli::LibraryOptionTypesCommand
36
36
  options[:phrase] = args.join(" ")
37
37
  end
38
38
  connect(options)
39
- begin
40
- params = {}
41
- params.merge!(parse_list_options(options))
42
- @option_types_interface.setopts(options)
43
- if options[:dry_run]
44
- print_dry_run @option_types_interface.dry.list(params)
45
- return
46
- end
39
+
40
+ params = {}
41
+ params.merge!(parse_list_options(options))
42
+ @option_types_interface.setopts(options)
43
+ if options[:dry_run]
44
+ print_dry_run @option_types_interface.dry.list(params)
45
+ return
46
+ end
47
47
 
48
- json_response = @option_types_interface.list(params)
49
-
50
- render_result = render_with_format(json_response, options, 'optionTypes')
51
- return 0 if render_result
48
+ json_response = @option_types_interface.list(params)
52
49
 
50
+ render_response(json_response, options, "optionTypes") do
53
51
  option_types = json_response['optionTypes']
54
52
  subtitles = []
55
53
  subtitles += parse_list_subtitles(options)
@@ -84,10 +82,8 @@ class Morpheus::Cli::LibraryOptionTypesCommand
84
82
  print_results_pagination(json_response)
85
83
  end
86
84
  print reset,"\n"
87
- rescue RestClient::Exception => e
88
- print_rest_exception(e, options)
89
- exit 1
90
85
  end
86
+ return 0, nil
91
87
  end
92
88
 
93
89
  def get(args)
@@ -110,23 +106,21 @@ class Morpheus::Cli::LibraryOptionTypesCommand
110
106
  end
111
107
 
112
108
  def _get(id, options)
113
- begin
114
- @option_types_interface.setopts(options)
115
- if options[:dry_run]
116
- if id.to_s =~ /\A\d{1,}\Z/
117
- print_dry_run @option_types_interface.dry.get(id.to_i)
118
- else
119
- print_dry_run @option_types_interface.dry.list({name: id})
120
- end
121
- return
109
+
110
+ @option_types_interface.setopts(options)
111
+ if options[:dry_run]
112
+ if id.to_s =~ /\A\d{1,}\Z/
113
+ print_dry_run @option_types_interface.dry.get(id.to_i)
114
+ else
115
+ print_dry_run @option_types_interface.dry.list({name: id})
122
116
  end
123
- option_type = find_option_type_by_name_or_id(id)
124
- return 1 if option_type.nil?
125
- json_response = {'optionType' => option_type}
126
-
127
- render_result = render_with_format(json_response, options, 'optionType')
128
- return 0 if render_result
117
+ return
118
+ end
119
+ option_type = find_option_type_by_name_or_id(id)
120
+ return 1 if option_type.nil?
121
+ json_response = {'optionType' => option_type}
129
122
 
123
+ render_response(json_response, options, "optionType") do
130
124
  print_h1 "Option Type Details"
131
125
  print cyan
132
126
  columns = {
@@ -144,15 +138,13 @@ class Morpheus::Cli::LibraryOptionTypesCommand
144
138
  "Default Value" => 'defaultValue',
145
139
  "Required" => lambda {|it| format_boolean(it['required']) },
146
140
  "Export As Tag" => lambda {|it| it['exportMeta'].nil? ? '' : format_boolean(it['exportMeta']) },
141
+ "Verify Pattern" => 'verifyPattern',
147
142
  }
148
143
  columns.delete("Option List") if option_type['optionList'].nil?
149
144
  print as_description_list(option_type, columns, options)
150
145
  print reset,"\n"
151
- return 0
152
- rescue RestClient::Exception => e
153
- print_rest_exception(e, options)
154
- return 1
155
146
  end
147
+ return 0, nil
156
148
  end
157
149
 
158
150
  def add(args)
@@ -165,37 +157,38 @@ class Morpheus::Cli::LibraryOptionTypesCommand
165
157
  end
166
158
  optparse.parse!(args)
167
159
  connect(options)
168
- begin
169
- payload = nil
170
- if options[:payload]
171
- payload = options[:payload]
172
- payload.deep_merge!({'optionType' => parse_passed_options(options)})
173
- else
174
- payload = {}
175
- payload.deep_merge!({'optionType' => parse_passed_options(options)})
176
- option_type_payload = Morpheus::Cli::OptionTypes.prompt(new_option_type_option_types, options[:options], @api_client)
177
- # tweak payload for API
178
- option_type_payload['optionList'] = {'id' => option_type_payload['optionList'].to_i} if option_type_payload['optionList'].is_a?(String) || option_type_payload['optionList'].is_a?(Numeric)
179
- option_type_payload['required'] = ['on','true'].include?(option_type_payload['required'].to_s) if option_type_payload.key?('required')
180
- option_type_payload['exportMeta'] = ['on','true'].include?(option_type_payload['exportMeta'].to_s) if option_type_payload.key?('exportMeta')
181
- payload.deep_merge!({'optionType' => option_type_payload})
182
- end
183
- @option_types_interface.setopts(options)
184
- if options[:dry_run]
185
- print_dry_run @option_types_interface.dry.create(payload)
186
- return
187
- end
188
- json_response = @option_types_interface.create(payload)
189
- render_result = render_with_format(json_response, options)
190
- return 0 if render_result
160
+ verify_args!(args:args, optparse:optparse, max:1)
161
+ if args[0]
162
+ options[:options]['name'] = args[0]
163
+ end
164
+
165
+ payload = nil
166
+ if options[:payload]
167
+ payload = options[:payload]
168
+ payload.deep_merge!({'optionType' => parse_passed_options(options)})
169
+ else
170
+ payload = {}
171
+ payload.deep_merge!({'optionType' => parse_passed_options(options)})
172
+ option_type_payload = Morpheus::Cli::OptionTypes.prompt(new_option_type_option_types, options[:options], @api_client)
173
+ # tweak payload for API
174
+ option_type_payload['optionList'] = {'id' => option_type_payload['optionList'].to_i} if option_type_payload['optionList'].is_a?(String) || option_type_payload['optionList'].is_a?(Numeric)
175
+ option_type_payload['required'] = ['on','true'].include?(option_type_payload['required'].to_s) if option_type_payload.key?('required')
176
+ option_type_payload['exportMeta'] = ['on','true'].include?(option_type_payload['exportMeta'].to_s) if option_type_payload.key?('exportMeta')
177
+ payload.deep_merge!({'optionType' => option_type_payload})
178
+ end
179
+ @option_types_interface.setopts(options)
180
+ if options[:dry_run]
181
+ print_dry_run @option_types_interface.dry.create(payload)
182
+ return
183
+ end
184
+ json_response = @option_types_interface.create(payload)
185
+
186
+ render_response(json_response, options, "optionType") do
191
187
  option_type = json_response['optionType']
192
188
  print_green_success "Added Option Type #{option_type['name']}"
193
- #list([])
194
- get([option_type['id']] + (options[:remote] ? ["-r",options[:remote]] : []))
195
- rescue RestClient::Exception => e
196
- print_rest_exception(e, options)
197
- exit 1
189
+ _get(option_type['id'], options)
198
190
  end
191
+ return 0, nil
199
192
  end
200
193
 
201
194
  def update(args)
@@ -302,6 +295,7 @@ class Morpheus::Cli::LibraryOptionTypesCommand
302
295
  {'fieldName' => 'defaultValue', 'fieldLabel' => 'Default Value', 'type' => 'text', 'displayOrder' => 9},
303
296
  {'fieldName' => 'required', 'fieldLabel' => 'Required', 'type' => 'checkbox', 'defaultValue' => false, 'displayOrder' => 10},
304
297
  {'fieldName' => 'exportMeta', 'fieldLabel' => 'Export As Tag', 'type' => 'checkbox', 'defaultValue' => false, 'description' => 'Export as Tag.', 'displayOrder' => 11},
298
+ {'fieldName' => 'verifyPattern', 'fieldLabel' => 'Verify Pattern', 'type' => 'text', 'dependsOnCode' => 'optionType.type:text', 'description' => 'A regexp string that validates the input, use (?i) to make the matcher case insensitive', 'displayOrder' => 12},
305
299
  ]
306
300
  end
307
301
 
@@ -4,6 +4,7 @@ class Morpheus::Cli::LoadBalancers
4
4
  include Morpheus::Cli::CliCommand
5
5
  include Morpheus::Cli::RestCommand
6
6
  include Morpheus::Cli::LoadBalancersHelper
7
+ include Morpheus::Cli::ProvisioningHelper
7
8
 
8
9
  set_command_description "View and manage load balancers."
9
10
  set_command_name :'load-balancers'
@@ -17,10 +18,12 @@ class Morpheus::Cli::LoadBalancers
17
18
  register_interfaces :load_balancers, :load_balancer_types
18
19
  set_rest_has_type true
19
20
  # set_rest_type :load_balancer_types
21
+ set_rest_perms_config({enabled:true, excludes:['plans', 'visibility']})
20
22
 
21
23
  def render_response_for_get(json_response, options)
22
24
  render_response(json_response, options, rest_object_key) do
23
25
  record = json_response[rest_object_key]
26
+ options[:exclude_username] = record['username'].nil?
24
27
  print_h1 rest_label, [], options
25
28
  print cyan
26
29
  print_description_list(rest_column_definitions(options), record, options)
@@ -94,19 +97,7 @@ EOT
94
97
 
95
98
  # filtering for NSX-T only
96
99
  def rest_list_types()
97
- rest_type_interface.list({max:10000, creatable:true})[rest_type_list_key].reject {|it| it['code'] == 'nsx-t'}
98
- end
99
-
100
- def load_balancer_type_list_to_options(type_list)
101
- type_list.reject {|it| it['code'] != 'nsx-t'}.collect {|it| {'name' => it['name'], 'value' => it['code']} }
102
- end
103
-
104
- def add_load_balancer_footer_addn
105
- "#{bold}Available for NSX-T load balancers only#{reset}"
106
- end
107
-
108
- def update_load_balancer_footer_addn
109
- "#{bold}Available for NSX-T load balancers only#{reset}"
100
+ rest_type_interface.list({max:10000, creatable:true})[rest_type_list_key] # .reject {|it| it['code'] == 'nsx-t'}
110
101
  end
111
102
 
112
103
  def load_balancer_list_column_definitions(options)
@@ -120,7 +111,7 @@ EOT
120
111
  end
121
112
 
122
113
  def load_balancer_column_definitions(options)
123
- {
114
+ columns = {
124
115
  "ID" => 'id',
125
116
  "Name" => 'name',
126
117
  "Description" => 'description',
@@ -130,13 +121,14 @@ EOT
130
121
  "IP" => 'ip',
131
122
  "Host" => 'host',
132
123
  "Port" => 'port',
133
- "Username" => 'username',
134
- # "SSL Enabled" => lambda {|it| format_boolean it['sslEnabled'] },
135
- # "SSL Cert" => lambda {|it| it['sslCert'] ? it['sslCert']['name'] : '' },
136
- # "SSL" => lambda {|it| it['sslEnabled'] ? "Yes (#{it['sslCert'] ? it['sslCert']['name'] : 'none'})" : "No" },
124
+ "Price" => lambda {|it| it['price'].nil? ? 'No pricing configured' : "#{format_money(it['price']['price'], it['price']['currency'])} / #{it['price']['unit'].capitalize}"},
125
+ "Provider ID" => 'externalId'
126
+ }
127
+ columns.merge!({"Username" => 'username'}) if !options[:exclude_username]
128
+ columns.merge({
137
129
  "Created" => lambda {|it| format_local_dt(it['dateCreated']) },
138
130
  "Updated" => lambda {|it| format_local_dt(it['lastUpdated']) }
139
- }
131
+ })
140
132
  end
141
133
 
142
134
  # overridden to work with name or code
@@ -209,10 +209,13 @@ class Morpheus::Cli::NetworkPoolServersCommand
209
209
  print_red_alert "Pool Server Type not found by id '#{options['type']}'"
210
210
  return 1
211
211
  end
212
- payload['networkPoolServer']['type'] = {'id' => network_type_id.to_i }
213
212
 
214
- # ['name', 'serviceUsername', 'servicePassword', 'servicePort', 'serviceHost', 'serviceUrl', 'serviceMode', 'networkFilter', 'tenantMatch']
213
+ payload['networkPoolServer']['type'] = {'id' => network_type_id.to_i }
215
214
 
215
+ # prompt options
216
+ network_pool_server_type = @network_pool_servers_interface.get_type(network_type_id.to_i)['networkPoolServerType']
217
+ option_result = Morpheus::Cli::OptionTypes.prompt(network_pool_server_type['optionTypes'], options[:options].deep_merge({:context_map => {'networkPoolServer' => ''}}), @api_client, {}, options[:no_prompt], true)
218
+ payload['networkPoolServer'].deep_merge!(option_result)
216
219
  end
217
220
 
218
221
  @network_pool_servers_interface.setopts(options)
@@ -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={})