morpheus-cli 4.2.12 → 4.2.17

Sign up to get free protection for your applications and to get access to all the features.
Files changed (78) hide show
  1. checksums.yaml +4 -4
  2. data/Dockerfile +1 -1
  3. data/README.md +8 -6
  4. data/lib/morpheus/api/api_client.rb +32 -14
  5. data/lib/morpheus/api/auth_interface.rb +4 -2
  6. data/lib/morpheus/api/backup_jobs_interface.rb +9 -0
  7. data/lib/morpheus/api/backups_interface.rb +16 -0
  8. data/lib/morpheus/api/deploy_interface.rb +25 -56
  9. data/lib/morpheus/api/deployments_interface.rb +43 -54
  10. data/lib/morpheus/api/doc_interface.rb +57 -0
  11. data/lib/morpheus/api/instances_interface.rb +5 -0
  12. data/lib/morpheus/api/rest_interface.rb +40 -0
  13. data/lib/morpheus/api/user_sources_interface.rb +0 -15
  14. data/lib/morpheus/api/users_interface.rb +2 -3
  15. data/lib/morpheus/benchmarking.rb +2 -2
  16. data/lib/morpheus/cli.rb +3 -1
  17. data/lib/morpheus/cli/access_token_command.rb +27 -10
  18. data/lib/morpheus/cli/apps.rb +23 -16
  19. data/lib/morpheus/cli/backup_jobs_command.rb +276 -0
  20. data/lib/morpheus/cli/backups_command.rb +271 -0
  21. data/lib/morpheus/cli/boot_scripts_command.rb +1 -1
  22. data/lib/morpheus/cli/cli_command.rb +176 -45
  23. data/lib/morpheus/cli/cli_registry.rb +10 -1
  24. data/lib/morpheus/cli/clusters.rb +2 -19
  25. data/lib/morpheus/cli/commands/standard/benchmark_command.rb +23 -20
  26. data/lib/morpheus/cli/commands/standard/man_command.rb +1 -1
  27. data/lib/morpheus/cli/containers_command.rb +2 -1
  28. data/lib/morpheus/cli/credentials.rb +14 -10
  29. data/lib/morpheus/cli/deploy.rb +374 -0
  30. data/lib/morpheus/cli/deployments.rb +521 -197
  31. data/lib/morpheus/cli/deploys.rb +271 -126
  32. data/lib/morpheus/cli/doc.rb +182 -0
  33. data/lib/morpheus/cli/error_handler.rb +23 -8
  34. data/lib/morpheus/cli/errors.rb +3 -2
  35. data/lib/morpheus/cli/health_command.rb +4 -3
  36. data/lib/morpheus/cli/hosts.rb +2 -1
  37. data/lib/morpheus/cli/image_builder_command.rb +2 -2
  38. data/lib/morpheus/cli/instances.rb +138 -18
  39. data/lib/morpheus/cli/invoices_command.rb +338 -223
  40. data/lib/morpheus/cli/library_layouts_command.rb +1 -1
  41. data/lib/morpheus/cli/library_option_lists_command.rb +61 -125
  42. data/lib/morpheus/cli/library_option_types_command.rb +32 -37
  43. data/lib/morpheus/cli/login.rb +9 -3
  44. data/lib/morpheus/cli/logs_command.rb +3 -2
  45. data/lib/morpheus/cli/mixins/accounts_helper.rb +158 -100
  46. data/lib/morpheus/cli/mixins/backups_helper.rb +115 -0
  47. data/lib/morpheus/cli/mixins/deployments_helper.rb +135 -0
  48. data/lib/morpheus/cli/mixins/library_helper.rb +32 -0
  49. data/lib/morpheus/cli/mixins/logs_helper.rb +18 -9
  50. data/lib/morpheus/cli/mixins/option_source_helper.rb +1 -1
  51. data/lib/morpheus/cli/mixins/print_helper.rb +149 -84
  52. data/lib/morpheus/cli/mixins/provisioning_helper.rb +2 -2
  53. data/lib/morpheus/cli/mixins/whoami_helper.rb +19 -6
  54. data/lib/morpheus/cli/network_routers_command.rb +1 -1
  55. data/lib/morpheus/cli/option_parser.rb +48 -5
  56. data/lib/morpheus/cli/option_types.rb +46 -10
  57. data/lib/morpheus/cli/price_sets_command.rb +1 -1
  58. data/lib/morpheus/cli/remote.rb +8 -10
  59. data/lib/morpheus/cli/roles.rb +49 -92
  60. data/lib/morpheus/cli/security_groups.rb +7 -1
  61. data/lib/morpheus/cli/service_plans_command.rb +10 -10
  62. data/lib/morpheus/cli/setup.rb +1 -1
  63. data/lib/morpheus/cli/shell.rb +7 -6
  64. data/lib/morpheus/cli/subnets_command.rb +1 -1
  65. data/lib/morpheus/cli/tenants_command.rb +133 -163
  66. data/lib/morpheus/cli/user_groups_command.rb +20 -65
  67. data/lib/morpheus/cli/user_settings_command.rb +115 -13
  68. data/lib/morpheus/cli/user_sources_command.rb +57 -24
  69. data/lib/morpheus/cli/users.rb +210 -186
  70. data/lib/morpheus/cli/version.rb +1 -1
  71. data/lib/morpheus/cli/whitelabel_settings_command.rb +29 -5
  72. data/lib/morpheus/cli/whoami.rb +113 -6
  73. data/lib/morpheus/cli/workflows.rb +1 -1
  74. data/lib/morpheus/ext/hash.rb +21 -0
  75. data/lib/morpheus/formatters.rb +7 -19
  76. data/lib/morpheus/terminal.rb +1 -0
  77. metadata +12 -3
  78. data/lib/morpheus/cli/auth_command.rb +0 -105
@@ -652,7 +652,7 @@ EOT
652
652
  connect(options)
653
653
  exit_code, err = 0, nil
654
654
  # if !is_master_account
655
- # print_red_alert "Permissions only available for master account"
655
+ # print_red_alert "Permissions only available for master tenant"
656
656
  # return 1
657
657
  # end
658
658
  layout = find_layout_by_name_or_id(nil, args[0])
@@ -186,49 +186,42 @@ class Morpheus::Cli::LibraryOptionListsCommand
186
186
  my_option_types = nil
187
187
  list_type = nil
188
188
  optparse = Morpheus::Cli::OptionParser.new do |opts|
189
- opts.banner = subcommand_usage("[type] [options]")
190
- opts.on( '-t', '--type TYPE', "Option List Type. (rest, manual)" ) do |val|
191
- list_type = val
192
- # options[:options] ||= {}
193
- # options[:options]['type'] = val
194
- end
189
+ opts.banner = subcommand_usage("[name] [options]")
195
190
  build_option_type_options(opts, options, new_option_type_list_option_types())
196
191
  build_standard_add_options(opts, options)
197
192
  opts.footer = "Create a new option list."
198
193
  end
199
194
  optparse.parse!(args)
200
-
201
-
195
+ verify_args!(args:args, optparse:optparse, max:1)
196
+ if args.count == 1
197
+ options[:options]['name'] = args[0]
198
+ end
199
+
202
200
  connect(options)
203
201
  begin
204
- passed_options = options[:options].reject {|k,v| k.is_a?(Symbol) }
205
202
  payload = nil
206
203
  if options[:payload]
207
204
  payload = options[:payload]
208
- # support -O OPTION switch on top of --payload
209
- if !passed_options.empty?
210
- payload['optionTypeList'] ||= {}
211
- payload['optionTypeList'].deep_merge!(passed_options)
212
- end
205
+ payload.deep_merge!({'optionTypeList' => parse_passed_options(options)})
213
206
  else
214
- if !list_type
215
- v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'type', 'fieldLabel' => 'Type', 'type' => 'select', 'selectOptions' => get_available_option_list_types, 'defaultValue' => 'rest', 'required' => true}], options[:options], @api_client, {})
216
- list_type = v_prompt['type']
217
- end
218
- params = passed_options
219
- v_prompt = Morpheus::Cli::OptionTypes.prompt(new_option_type_list_option_types(list_type), options[:options], @api_client, options[:params])
220
- params.deep_merge!(v_prompt)
221
- params['type'] = list_type
222
- if params['type'] == 'rest'
207
+ payload = {}
208
+ payload.deep_merge!({'optionTypeList' => parse_passed_options(options)})
209
+ list_payload = Morpheus::Cli::OptionTypes.prompt(new_option_type_list_option_types(), options[:options], @api_client, options[:params])
210
+ if list_payload['type'] == 'rest'
223
211
  # prompt for Source Headers
224
- source_headers = prompt_source_headers(options)
225
- if !source_headers.empty?
226
- params['config'] ||= {}
227
- params['config']['sourceHeaders'] = source_headers
212
+ if !(payload['optionTypeList']['config'] && payload['optionTypeList']['config']['sourceHeaders'])
213
+ source_headers = prompt_source_headers(options)
214
+ if !source_headers.empty?
215
+ list_payload['config'] ||= {}
216
+ list_payload['config']['sourceHeaders'] = source_headers
217
+ end
228
218
  end
229
219
  end
230
- list_payload = params
231
- payload = {'optionTypeList' => list_payload}
220
+ # tweak payload for API
221
+ ['ignoreSSLErrors', 'realTime'].each { |k|
222
+ list_payload[k] = ['on','true'].include?(list_payload[k].to_s) if list_payload.key?(k)
223
+ }
224
+ payload.deep_merge!({'optionTypeList' => list_payload})
232
225
  end
233
226
  @option_type_lists_interface.setopts(options)
234
227
  if options[:dry_run]
@@ -262,41 +255,31 @@ class Morpheus::Cli::LibraryOptionListsCommand
262
255
  begin
263
256
  option_type_list = find_option_type_list_by_name_or_id(args[0])
264
257
  exit 1 if option_type_list.nil?
265
- passed_options = options[:options].reject {|k,v| k.is_a?(Symbol) }
266
258
  payload = nil
267
259
  if options[:payload]
268
260
  payload = options[:payload]
269
- # support -O OPTION switch on top of --payload
270
- if !passed_options.empty?
271
- payload['optionTypeList'] ||= {}
272
- payload['optionTypeList'].deep_merge!(passed_options)
273
- end
261
+ payload.deep_merge!({'optionTypeList' => parse_passed_options(options)})
274
262
  else
275
- list_type = option_type_list['type']
276
- prompt_options = update_option_type_list_option_types(list_type)
277
- params = passed_options
278
- v_prompt = Morpheus::Cli::OptionTypes.no_prompt(prompt_options, options[:options], @api_client, options[:params])
279
- params.deep_merge!(v_prompt)
280
-
281
- if list_type == 'rest'
263
+ payload = {}
264
+ payload.deep_merge!({'optionTypeList' => parse_passed_options(options)})
265
+ list_payload = Morpheus::Cli::OptionTypes.no_prompt(update_option_type_option_types(), options[:options], @api_client)
266
+ if list_payload['type'] == 'rest'
282
267
  # parse Source Headers
283
- source_headers = prompt_source_headers(options.merge({no_prompt: true}))
284
- if !source_headers.empty?
285
- #params['config'] ||= option_type_list['config'] || {}
286
- params['config'] ||= {}
287
- params['config']['sourceHeaders'] = source_headers
268
+ if !(payload['optionTypeList']['config'] && payload['optionTypeList']['config']['sourceHeaders'])
269
+ source_headers = prompt_source_headers(options.merge({no_prompt: true}))
270
+ if !source_headers.empty?
271
+ #params['config'] ||= option_type_list['config'] || {}
272
+ params['config'] ||= {}
273
+ params['config']['sourceHeaders'] = source_headers
274
+ end
288
275
  end
289
276
  end
290
- if params.empty?
291
- print_red_alert "Specify at least one option to update"
292
- puts optparse
293
- exit 1
294
- end
295
- if params.key?('required')
296
- params['required'] = ['on','true'].include?(params['required'].to_s)
297
- end
298
- list_payload = params
299
- payload = {'optionTypeList' => list_payload}
277
+ # tweak payload for API
278
+ ['ignoreSSLErrors', 'realTime'].each { |k|
279
+ list_payload[k] = ['on','true'].include?(list_payload[k].to_s) if list_payload.key?(k)
280
+ }
281
+ payload.deep_merge!({'optionTypeList' => list_payload})
282
+ raise_command_error "Specify at least one option to update.\n#{optparse}" if payload['optionTypeList'].empty?
300
283
  end
301
284
  @option_type_lists_interface.setopts(options)
302
285
  if options[:dry_run]
@@ -355,42 +338,13 @@ class Morpheus::Cli::LibraryOptionListsCommand
355
338
 
356
339
  private
357
340
 
358
- def find_option_type_list_by_name_or_id(val)
359
- if val.to_s =~ /\A\d{1,}\Z/
360
- return find_option_type_list_by_id(val)
361
- else
362
- return find_option_type_list_by_name(val)
363
- end
364
- end
365
-
366
- def find_option_type_list_by_id(id)
367
- begin
368
- json_response = @option_type_lists_interface.get(id.to_i)
369
- return json_response['optionTypeList']
370
- rescue RestClient::Exception => e
371
- if e.response && e.response.code == 404
372
- print_red_alert "Option List not found by id #{id}"
373
- exit 1
374
- else
375
- raise e
376
- end
377
- end
378
- end
379
-
380
- def find_option_type_list_by_name(name)
381
- json_results = @option_type_lists_interface.list({name: name.to_s})
382
- if json_results['optionTypeLists'].empty?
383
- print_red_alert "Option List not found by name #{name}"
384
- exit 1
385
- end
386
- option_type_list = json_results['optionTypeLists'][0]
387
- return option_type_list
388
- end
341
+ # finders are in LibraryHelper
389
342
 
390
343
  def get_available_option_list_types
391
344
  [
392
345
  {'name' => 'REST', 'value' => 'rest'},
393
346
  {'name' => 'Morpheus Api', 'value' => 'api'},
347
+ {'name' => 'LDAP', 'value' => 'ldap'},
394
348
  {'name' => 'Manual', 'value' => 'manual'}
395
349
  ]
396
350
  end
@@ -399,49 +353,31 @@ class Morpheus::Cli::LibraryOptionListsCommand
399
353
  get_available_option_list_types.find {|it| code == it['value'] || code == it['name'] }
400
354
  end
401
355
 
402
- def new_option_type_list_option_types(list_type='rest')
403
- if list_type.to_s.downcase == 'rest'
404
- [
356
+ def new_option_type_list_option_types()
357
+ [
358
+ # rest
405
359
  {'fieldName' => 'name', 'fieldLabel' => 'Name', 'type' => 'text', 'required' => true, 'displayOrder' => 1},
406
360
  {'fieldName' => 'description', 'fieldLabel' => 'Description', 'type' => 'text', 'displayOrder' => 2},
407
- #{'fieldName' => 'type', 'fieldLabel' => 'Type', 'type' => 'select', 'selectOptions' => get_available_option_list_types, 'defaultValue' => 'rest', 'required' => true, 'displayOrder' => 3},
408
- {'fieldName' => 'visibility', 'fieldLabel' => 'Visibility', 'type' => 'select', 'selectOptions' => [{'name' => 'Private', 'value' => 'private'}, {'name' => 'Public', 'value' => 'public'}], 'defaultValue' => 'private', 'displayOrder' => 3},
409
- {'fieldName' => 'sourceUrl', 'fieldLabel' => 'Source Url', 'type' => 'text', 'required' => true, 'description' => "A REST URL can be used to fetch list data and is cached in the appliance database.", 'displayOrder' => 4},
410
- {'fieldName' => 'ignoreSSLErrors', 'fieldLabel' => 'Ignore SSL Errors', 'type' => 'checkbox', 'defaultValue' => 'off', 'displayOrder' => 5},
411
- {'fieldName' => 'realTime', 'fieldLabel' => 'Real Time', 'type' => 'checkbox', 'defaultValue' => 'off', 'displayOrder' => 6},
412
- {'fieldName' => 'sourceMethod', 'fieldLabel' => 'Source Method', 'type' => 'select', 'selectOptions' => [{'name' => 'GET', 'value' => 'GET'}, {'name' => 'POST', 'value' => 'POST'}], 'defaultValue' => 'GET', 'required' => true, 'displayOrder' => 7},
361
+ {'code' => 'optionTypeList.type', 'fieldName' => 'type', 'fieldLabel' => 'Type', 'type' => 'select', 'selectOptions' => get_available_option_list_types, 'defaultValue' => 'rest', 'required' => true, 'description' => 'Option List Type. eg. rest, api, ldap, manual', 'displayOrder' => 3},
362
+ {'fieldName' => 'visibility', 'fieldLabel' => 'Visibility', 'type' => 'select', 'selectOptions' => [{'name' => 'Private', 'value' => 'private'}, {'name' => 'Public', 'value' => 'public'}], 'defaultValue' => 'private', 'displayOrder' => 4},
363
+ {'dependsOnCode' => 'optionTypeList.type:rest', 'fieldName' => 'sourceUrl', 'fieldLabel' => 'Source Url', 'type' => 'text', 'required' => true, 'description' => "A REST URL can be used to fetch list data and is cached in the appliance database.", 'displayOrder' => 5},
364
+ {'dependsOnCode' => 'optionTypeList.type:rest', 'fieldName' => 'ignoreSSLErrors', 'fieldLabel' => 'Ignore SSL Errors', 'type' => 'checkbox', 'defaultValue' => false, 'displayOrder' => 6},
365
+ {'dependsOnCode' => 'optionTypeList.type:rest', 'fieldName' => 'realTime', 'fieldLabel' => 'Real Time', 'type' => 'checkbox', 'defaultValue' => false, 'displayOrder' => 7},
366
+ {'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},
413
367
  # sourceHeaders component (is done afterwards manually)
414
- {'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' => 8},
415
- {'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' => 9},
416
- {'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' => 10},
417
-
368
+ {'dependsOnCode' => 'optionTypeList.type:api', 'fieldName' => 'apiType', 'fieldLabel' => 'Option List', 'type' => 'select', 'optionSource' => 'apiOptionLists', 'required' => true, 'description' => 'The code of the api list to use, eg. clouds, servers, etc.', 'displayOrder' => 9},
369
+ {'dependsOnCode' => 'optionTypeList.type:ldap', 'fieldName' => 'sourceUsername', 'fieldLabel' => 'Source Username', 'type' => 'text', 'description' => "An LDAP Username for use when type is 'ldap'.", 'displayOrder' => 10},
370
+ {'dependsOnCode' => 'optionTypeList.type:ldap', 'fieldName' => 'sourcePassword', 'fieldLabel' => 'Source Username', 'type' => 'text', 'description' => "An LDAP Password for use when type is 'ldap'.", 'displayOrder' => 11},
371
+ {'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},
372
+ {'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},
373
+ {'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},
374
+ {'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},
418
375
  ]
419
- elsif list_type.to_s.downcase == 'api'
420
- [
421
- {'fieldName' => 'name', 'fieldLabel' => 'Name', 'type' => 'text', 'required' => true, 'displayOrder' => 1},
422
- {'fieldName' => 'description', 'fieldLabel' => 'Description', 'type' => 'text', 'displayOrder' => 2},
423
- #{'fieldName' => 'type', 'fieldLabel' => 'Type', 'type' => 'select', 'selectOptions' => [{'name' => 'Rest', 'value' => 'rest'}, {'name' => 'Manual', 'value' => 'manual'}], 'defaultValue' => 'rest', 'required' => true, 'displayOrder' => 3},
424
- {'fieldName' => 'visibility', 'fieldLabel' => 'Visibility', 'type' => 'select', 'selectOptions' => [{'name' => 'Private', 'value' => 'private'}, {'name' => 'Public', 'value' => 'public'}], 'defaultValue' => 'private', 'displayOrder' => 3},
425
- {'fieldName' => 'apiType', 'fieldLabel' => 'Option List', 'type' => 'select', 'optionSource' => 'apiOptionLists', 'required' => true, 'description' => 'The code of the api list to use, eg. clouds, servers, etc.', 'displayOrder' => 6},
426
- {'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' => 9},
427
- {'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' => 10},
428
- ]
429
- elsif list_type.to_s.downcase == 'manual'
430
- [
431
- {'fieldName' => 'name', 'fieldLabel' => 'Name', 'type' => 'text', 'required' => true, 'displayOrder' => 1},
432
- {'fieldName' => 'description', 'fieldLabel' => 'Description', 'type' => 'text', 'displayOrder' => 2},
433
- #{'fieldName' => 'type', 'fieldLabel' => 'Type', 'type' => 'select', 'selectOptions' => [{'name' => 'Rest', 'value' => 'rest'}, {'name' => 'Manual', 'value' => 'manual'}], 'defaultValue' => 'rest', 'required' => true, 'displayOrder' => 3},
434
- {'fieldName' => 'visibility', 'fieldLabel' => 'Visibility', 'type' => 'select', 'selectOptions' => [{'name' => 'Private', 'value' => 'private'}, {'name' => 'Public', 'value' => 'public'}], 'defaultValue' => 'private', 'displayOrder' => 3},
435
- {'fieldName' => 'initialDataset', 'fieldLabel' => 'Dataset', 'type' => 'code-editor', 'required' => true, 'description' => "Create an initial JSON or CSV dataset to be used as the collection for this option list. It should be a list containing objects with properties 'name', and 'value'.", 'displayOrder' => 4},
436
- ]
437
- else
438
- print_red_alert "Unknown Option List type '#{list_type}'"
439
- exit 1
440
- end
376
+
441
377
  end
442
378
 
443
- def update_option_type_list_option_types(list_type='rest')
444
- list = new_option_type_list_option_types(list_type)
379
+ def update_option_type_list_option_types()
380
+ list = new_option_type_list_option_types()
445
381
  list.each {|it|
446
382
  it.delete('required')
447
383
  it.delete('defaultValue')
@@ -65,7 +65,8 @@ class Morpheus::Cli::LibraryOptionTypesCommand
65
65
  fieldLabel: option_type['fieldLabel'],
66
66
  fieldName: option_type['fieldName'],
67
67
  default: option_type['defaultValue'],
68
- required: option_type['required'] ? 'yes' : 'no'
68
+ required: (option_type['required'].nil? ? '' : format_boolean(option_type['required'])),
69
+ export: (option_type['exportMeta'].nil? ? '' : format_boolean(option_type['exportMeta']))
69
70
  }
70
71
  end
71
72
  print cyan
@@ -76,7 +77,8 @@ class Morpheus::Cli::LibraryOptionTypesCommand
76
77
  {:fieldLabel => {:display_name => "Field Label"} },
77
78
  {:fieldName => {:display_name => "Field Name"} },
78
79
  :default,
79
- :required
80
+ :required,
81
+ :export,
80
82
  ], options)
81
83
  print reset
82
84
  print_results_pagination(json_response)
@@ -139,6 +141,7 @@ class Morpheus::Cli::LibraryOptionTypesCommand
139
141
  "Placeholder" => 'placeHolder',
140
142
  "Default Value" => 'defaultValue',
141
143
  "Required" => lambda {|it| format_boolean(it['required']) },
144
+ "Export As Tag" => lambda {|it| it['exportMeta'].nil? ? '' : format_boolean(it['exportMeta']) },
142
145
  }, option_type)
143
146
  print reset,"\n"
144
147
  return 0
@@ -162,18 +165,16 @@ class Morpheus::Cli::LibraryOptionTypesCommand
162
165
  payload = nil
163
166
  if options[:payload]
164
167
  payload = options[:payload]
165
- # support -O OPTION switch on top of --payload
166
- if options[:options]
167
- payload['optionType'] ||= {}
168
- payload['optionType'].deep_merge!(options[:options].reject {|k,v| k.is_a?(Symbol) })
169
- end
168
+ payload.deep_merge!({'optionType' => parse_passed_options(options)})
170
169
  else
171
- params = Morpheus::Cli::OptionTypes.prompt(new_option_type_option_types, options[:options], @api_client, options[:params])
172
- if params.key?('required')
173
- params['required'] = ['on','true'].include?(params['required'].to_s)
174
- end
175
- option_type_payload = params
176
- payload = {optionType: option_type_payload}
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})
177
178
  end
178
179
  @option_types_interface.setopts(options)
179
180
  if options[:dry_run]
@@ -207,27 +208,19 @@ class Morpheus::Cli::LibraryOptionTypesCommand
207
208
  begin
208
209
  option_type = find_option_type_by_name_or_id(args[0])
209
210
  exit 1 if option_type.nil?
210
-
211
211
  if options[:payload]
212
212
  payload = options[:payload]
213
- # support -O OPTION switch on top of --payload
214
- if options[:options]
215
- payload['optionType'] ||= {}
216
- payload['optionType'].deep_merge!(options[:options].reject {|k,v| k.is_a?(Symbol) })
217
- end
213
+ payload.deep_merge!({'optionType' => parse_passed_options(options)})
218
214
  else
219
- #params = options[:options] || {}
220
- params = Morpheus::Cli::OptionTypes.no_prompt(update_option_type_option_types, options[:options], @api_client, options[:params])
221
- if params.empty?
222
- print_red_alert "Specify at least one option to update"
223
- puts optparse
224
- exit 1
225
- end
226
- if params.key?('required')
227
- params['required'] = ['on','true'].include?(params['required'].to_s)
228
- end
229
- option_type_payload = params
230
- payload = {optionType: option_type_payload}
215
+ payload = {}
216
+ payload.deep_merge!({'optionType' => parse_passed_options(options)})
217
+ option_type_payload = Morpheus::Cli::OptionTypes.no_prompt(update_option_type_option_types, options[:options], @api_client)
218
+ # tweak payload for API
219
+ 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)
220
+ option_type_payload['required'] = ['on','true'].include?(option_type_payload['required'].to_s) if option_type_payload.key?('required')
221
+ option_type_payload['exportMeta'] = ['on','true'].include?(option_type_payload['exportMeta'].to_s) if option_type_payload.key?('exportMeta')
222
+ payload.deep_merge!({'optionType' => option_type_payload})
223
+ raise_command_error "Specify at least one option to update.\n#{optparse}" if payload['optionType'].empty?
231
224
  end
232
225
  @option_types_interface.setopts(options)
233
226
  if options[:dry_run]
@@ -296,12 +289,14 @@ class Morpheus::Cli::LibraryOptionTypesCommand
296
289
  [
297
290
  {'fieldName' => 'name', 'fieldLabel' => 'Name', 'type' => 'text', 'required' => true, 'displayOrder' => 1},
298
291
  {'fieldName' => 'description', 'fieldLabel' => 'Description', 'type' => 'text', 'displayOrder' => 2},
299
- {'fieldName' => 'fieldName', 'fieldLabel' => 'Field Name', 'type' => 'text', 'required' => true, 'description' => 'This is the input fieldName property that the value gets assigned to.', 'displayOrder' => 3},
300
- {'fieldName' => 'type', 'fieldLabel' => 'Type', 'type' => 'select', 'selectOptions' => [{'name' => 'Text', 'value' => 'text'}, {'name' => 'Password', 'value' => 'password'}, {'name' => 'Number', 'value' => 'number'}, {'name' => 'Checkbox', 'value' => 'checkbox'}, {'name' => 'Select', 'value' => 'select'}, {'name' => 'Hidden', 'value' => 'hidden'}], 'defaultValue' => 'text', 'required' => true, 'displayOrder' => 4},
301
- {'fieldName' => 'fieldLabel', 'fieldLabel' => 'Field Label', 'type' => 'text', 'required' => true, 'description' => 'This is the input label that shows typically to the left of a custom option.', 'displayOrder' => 5},
302
- {'fieldName' => 'placeHolder', 'fieldLabel' => 'Placeholder', 'type' => 'text', 'displayOrder' => 6},
303
- {'fieldName' => 'defaultValue', 'fieldLabel' => 'Default Value', 'type' => 'text', 'displayOrder' => 7},
304
- {'fieldName' => 'required', 'fieldLabel' => 'Required', 'type' => 'checkbox', 'defaultValue' => 'off', 'displayOrder' => 8},
292
+ {'fieldName' => 'fieldName', 'fieldLabel' => 'Field Name', 'type' => 'text', 'required' => true, 'description' => 'This is the input property that the value gets assigned to.', 'displayOrder' => 3},
293
+ {'code' => 'optionType.type', 'fieldName' => 'type', 'fieldLabel' => 'Type', 'type' => 'select', 'selectOptions' => [{'name' => 'Text', 'value' => 'text'}, {'name' => 'Password', 'value' => 'password'}, {'name' => 'Number', 'value' => 'number'}, {'name' => 'Checkbox', 'value' => 'checkbox'}, {'name' => 'Select', 'value' => 'select'}, {'name' => 'Hidden', 'value' => 'hidden'}], 'defaultValue' => 'text', 'required' => true, 'displayOrder' => 4},
294
+ {'fieldName' => 'optionList', 'fieldLabel' => 'Option List', 'type' => 'select', 'optionSource' => 'optionTypeLists', 'required' => true, 'dependsOnCode' => 'optionType.type:select', 'description' => "The Option List to be the source of options when type is 'select'.", 'displayOrder' => 5},
295
+ {'fieldName' => 'fieldLabel', 'fieldLabel' => 'Field Label', 'type' => 'text', 'required' => true, 'description' => 'This is the input label that shows typically to the left of a custom option.', 'displayOrder' => 6},
296
+ {'fieldName' => 'placeHolder', 'fieldLabel' => 'Placeholder', 'type' => 'text', 'displayOrder' => 7},
297
+ {'fieldName' => 'defaultValue', 'fieldLabel' => 'Default Value', 'type' => 'text', 'displayOrder' => 8},
298
+ {'fieldName' => 'required', 'fieldLabel' => 'Required', 'type' => 'checkbox', 'defaultValue' => false, 'displayOrder' => 9},
299
+ {'fieldName' => 'exportMeta', 'fieldLabel' => 'Export As Tag', 'type' => 'checkbox', 'defaultValue' => false, 'description' => 'Export as Tag.', 'displayOrder' => 10},
305
300
  ]
306
301
  end
307
302
 
@@ -30,7 +30,7 @@ class Morpheus::Cli::Login
30
30
  username, password = nil, nil
31
31
  optparse = Morpheus::Cli::OptionParser.new do |opts|
32
32
  opts.banner = usage
33
- opts.on( '-u', '--username USERNAME', "Username" ) do |val|
33
+ opts.on( '-u', '--username USERNAME', "Username. Sub-tenant users must format their username with a prefix like {subdomain}\\{username}" ) do |val|
34
34
  username = val
35
35
  end
36
36
  opts.on( '-p', '--password PASSWORD', "Password" ) do |val|
@@ -50,11 +50,15 @@ class Morpheus::Cli::Login
50
50
  opts.footer = <<-EOT
51
51
  Login to a remote appliance with a username and password or using an access token.
52
52
  Logging in with username and password will make an authentication api request to obtain an access token.
53
+ [username] is required, this is the username of the Morpheus User
54
+ [password] is required, this is the password of the Morpheus User
55
+ Sub-tenant users will need to pass their tenant subdomain prefix. ie. {subdomain}\\{username}
56
+ By default, the subdomain is the tenant account ID. Example: 2\\neo
53
57
  The --token option can be used to login with a valid access token instead of username and password.
54
58
  The specified token will be verified by making a whoami api request
55
59
  If successful, the access token will be saved with the active session for the remote appliance.
56
- This command will first logout any active session before attempting authorization.
57
- The --test option is available for testing credentials without updating your active session, neithing logging you out or in.
60
+ This command will first logout any active session before attempting authentication.
61
+ The --test option can be used to test credentials without updating the stored credentials for the appliance, neither logging you in or out.
58
62
  EOT
59
63
 
60
64
  end
@@ -106,6 +110,8 @@ EOT
106
110
  if options[:test_only]
107
111
  print green,"Success! Test Credentials verified for #{wallet['username']}", reset, "\n"
108
112
  else
113
+ # clear whoami cache, it will be lazily load_saved_credentials
114
+ ::Morpheus::Cli::Whoami.clear_whoami(@appliance_name, wallet['username'])
109
115
  print green,"Success! Logged in as #{wallet['username']}", reset, "\n"
110
116
  end
111
117
  end
@@ -63,9 +63,9 @@ class Morpheus::Cli::LogsCommand
63
63
  # options[:interval] = parse_time(val).utc.iso8601
64
64
  # end
65
65
  opts.on('--level VALUE', String, "Log Level. DEBUG,INFO,WARN,ERROR") do |val|
66
- params['level'] = params['level'] ? [params['level'], val].flatten : val
66
+ params['level'] = params['level'] ? [params['level'], val].flatten : [val]
67
67
  end
68
- opts.on('--table', '--table', "Format ouput as a table.") do
68
+ opts.on('-t', '--table', "Format ouput as a table.") do
69
69
  options[:table] = true
70
70
  end
71
71
  opts.on('-a', '--all', "Display all details: entire message." ) do
@@ -81,6 +81,7 @@ class Morpheus::Cli::LogsCommand
81
81
  end
82
82
  connect(options)
83
83
  begin
84
+ params['level'] = params['level'].collect {|it| it.to_s.upcase }.join('|') if params['level'] # api works with INFO|WARN
84
85
  params.merge!(parse_list_options(options))
85
86
  params['query'] = params.delete('phrase') if params['phrase']
86
87
  params['order'] = params['direction'] unless params['direction'].nil? # old api version expects order instead of direction
@@ -33,6 +33,43 @@ module Morpheus::Cli::AccountsHelper
33
33
  @roles_interface
34
34
  end
35
35
 
36
+ ## Tenants (Accounts)
37
+
38
+ def account_column_definitions()
39
+ {
40
+ "ID" => 'id',
41
+ "Name" => 'name',
42
+ # "Name" => lambda {|it| it['name'].to_s + (it['master'] ? " (Master Tenant)" : '') },
43
+ "Description" => 'description',
44
+ "Subdomain" => 'subdomain',
45
+ "# Instances" => 'stats.instanceCount',
46
+ "# Users" => 'stats.userCount',
47
+ "Role" => lambda {|it| it['role']['authority'] rescue nil },
48
+ "Master" => lambda {|it| format_boolean(it['master']) },
49
+ "Currency" => 'currency',
50
+ "Status" => lambda {|it|
51
+ status_state = nil
52
+ if it['active']
53
+ status_state = "#{green}ACTIVE#{cyan}"
54
+ else
55
+ status_state = "#{red}INACTIVE#{cyan}"
56
+ end
57
+ status_state
58
+ },
59
+ "Created" => lambda {|it| format_local_dt(it['dateCreated']) },
60
+ "Updated" => lambda {|it| format_local_dt(it['lastUpdated']) },
61
+ }
62
+ end
63
+
64
+ def list_account_column_definitions()
65
+ columns = account_column_definitions
66
+ columns.delete("Subdomain")
67
+ columns.delete("Master")
68
+ columns.delete("Currency")
69
+ return columns.upcase_keys!
70
+ end
71
+
72
+
36
73
  def find_account_by_name_or_id(val)
37
74
  if val.to_s =~ /\A\d{1,}\Z/
38
75
  return find_account_by_id(val)
@@ -47,7 +84,7 @@ module Morpheus::Cli::AccountsHelper
47
84
  return json_response['account']
48
85
  rescue RestClient::Exception => e
49
86
  if e.response && e.response.code == 404
50
- print_red_alert "Account not found by id #{id}"
87
+ print_red_alert "Tenant not found by id #{id}"
51
88
  else
52
89
  raise e
53
90
  end
@@ -57,12 +94,12 @@ module Morpheus::Cli::AccountsHelper
57
94
  def find_account_by_name(name)
58
95
  accounts = accounts_interface.list({name: name.to_s})['accounts']
59
96
  if accounts.empty?
60
- print_red_alert "Account not found by name #{name}"
97
+ print_red_alert "Tenant not found by name #{name}"
61
98
  return nil
62
99
  elsif accounts.size > 1
63
- print_red_alert "#{accounts.size} accounts found by name #{name}"
64
- print_accounts_table(accounts, {color: red})
65
- print_red_alert "Try using -A ID instead"
100
+ print_red_alert "Found #{accounts.size} tenants by name '#{name}'. Try using ID instead: #{format_list(accounts.collect {|it| it['id']}, 'or', 3)}"
101
+ print "\n"
102
+ print as_pretty_table(accounts, [:id, :name, :description], {color: red, thin: true})
66
103
  print reset,"\n"
67
104
  return nil
68
105
  else
@@ -87,6 +124,35 @@ module Morpheus::Cli::AccountsHelper
87
124
  return account
88
125
  end
89
126
 
127
+ ## Roles
128
+
129
+ def role_column_definitions(options={})
130
+ {
131
+ "ID" => 'id',
132
+ "Name" => 'authority',
133
+ "Description" => 'description',
134
+ #"Scope" => lambda {|it| it['scope'] },
135
+ "Type" => lambda {|it| format_role_type(it) },
136
+ "Multitenant" => lambda {|it|
137
+ format_boolean(it['multitenant']).to_s + (it['multitenantLocked'] ? " (LOCKED)" : "")
138
+ },
139
+ "Owner" => lambda {|it| it['owner'] ? it['owner']['name'] : '' },
140
+ #"Tenant" => lambda {|it| it['account'] ? it['account']['name'] : '' },
141
+ "Created" => lambda {|it| format_local_dt(it['dateCreated']) },
142
+ "Updated" => lambda {|it| format_local_dt(it['lastUpdated']) }
143
+ }
144
+ end
145
+
146
+ def subtenant_role_column_definitions(options={})
147
+ {
148
+ "ID" => 'id',
149
+ "Name" => 'authority',
150
+ "Description" => 'description',
151
+ "Created" => lambda {|it| format_local_dt(it['dateCreated']) },
152
+ "Updated" => lambda {|it| format_local_dt(it['lastUpdated']) }
153
+ }
154
+ end
155
+
90
156
  def find_role_by_name_or_id(account_id, val)
91
157
  if val.to_s =~ /\A\d{1,}\Z/
92
158
  return find_role_by_id(account_id, val)
@@ -114,9 +180,11 @@ module Morpheus::Cli::AccountsHelper
114
180
  print_red_alert "Role not found by name #{name}"
115
181
  return nil
116
182
  elsif roles.size > 1
117
- print_red_alert "#{roles.size} roles by name #{name}"
118
- print_roles_table(roles, {color: red, thin: true})
119
- print reset,"\n\n"
183
+ print_red_alert "Found #{roles.size} roles by name '#{name}'. Try using ID instead: #{format_list(roles.collect {|it| it['id']}, 'or', 3)}"
184
+ print "\n"
185
+ # print as_pretty_table(accounts, [:id, :name, :description], {color: red, thin: true})
186
+ print as_pretty_table(roles, {"ID" => 'id', "Name" => 'authority',"Description" => 'description'}.upcase_keys!, {color: red, thin: true})
187
+ print reset,"\n"
120
188
  return nil
121
189
  else
122
190
  return roles[0]
@@ -125,17 +193,57 @@ module Morpheus::Cli::AccountsHelper
125
193
 
126
194
  alias_method :find_role_by_authority, :find_role_by_name
127
195
 
128
- def find_user_by_username_or_id(account_id, val)
196
+
197
+ ## Users
198
+
199
+ def user_column_definitions()
200
+ {
201
+ "ID" => 'id',
202
+ "Tenant" => lambda {|it| it['account'] ? it['account']['name'] : '' },
203
+ "First Name" => 'firstName',
204
+ "Last Name" => 'lastName',
205
+ "Username" => 'username',
206
+ "Email" => 'email',
207
+ "Role" => lambda {|it| format_user_role_names(it) },
208
+ "Notifications" => lambda {|it| it['receiveNotifications'].nil? ? '' : format_boolean(it['receiveNotifications']) },
209
+ "Status" => lambda {|it| format_user_status(it) },
210
+ "Last Login" => lambda {|it| format_duration_ago(it['lastLoginDate']) },
211
+ "Created" => lambda {|it| format_local_dt(it['dateCreated']) },
212
+ "Updated" => lambda {|it| format_local_dt(it['lastUpdated']) },
213
+ }
214
+ end
215
+
216
+ def list_user_column_definitions()
217
+ columns = user_column_definitions
218
+ columns.delete("Notifications")
219
+ return columns.upcase_keys!
220
+ end
221
+
222
+ def format_user_status(user, return_color=cyan)
223
+ if user["enabled"] != true
224
+ red + "DISABLED" + return_color
225
+ elsif user["accountLocked"]
226
+ red + "ACCOUNT LOCKED" + return_color
227
+ elsif user["accountExpired"]
228
+ yellow + "ACCOUNT EXPIRED" + return_color
229
+ elsif user["passwordExpired"]
230
+ yellow + "PASSWORD EXPIRED" + return_color
231
+ else
232
+ green + "ACTIVE" + return_color
233
+ end
234
+ end
235
+
236
+ def find_user_by_username_or_id(account_id, val, params={})
129
237
  if val.to_s =~ /\A\d{1,}\Z/
130
- return find_user_by_id(account_id, val)
238
+ return find_user_by_id(account_id, val, params)
131
239
  else
132
- return find_user_by_username(account_id, val)
240
+ return find_user_by_username(account_id, val, params)
133
241
  end
134
242
  end
135
243
 
136
- def find_user_by_id(account_id, id)
244
+ def find_user_by_id(account_id, id, params={})
137
245
  begin
138
- json_response = users_interface.get(account_id, id.to_i, {includePermissions:true})
246
+ json_response = users_interface.get(account_id, id.to_i, params)
139
247
  return json_response['user']
140
248
  rescue RestClient::Exception => e
141
249
  if e.response && e.response.code == 404
@@ -146,22 +254,23 @@ module Morpheus::Cli::AccountsHelper
146
254
  end
147
255
  end
148
256
 
149
- def find_user_by_username(account_id, username)
150
- users = users_interface.list(account_id, {username: username.to_s})['users']
257
+ def find_user_by_username(account_id, username, params={})
258
+ users = users_interface.list(account_id, params.merge({username: username.to_s}))['users']
151
259
  if users.empty?
152
260
  print_red_alert "User not found by username #{username}"
153
261
  return nil
154
262
  elsif users.size > 1
155
- print_red_alert "#{users.size} users by username #{username}"
156
- print_users_table(users, {color: red, thin: true})
157
- print reset,"\n\n"
263
+ print_red_alert "Found #{users.size} users by username '#{username}'. Try using ID instead: #{format_list(users.collect {|it| it['id']}, 'or', 3)}"
264
+ print "\n"
265
+ print as_pretty_table(users, list_user_column_definitions, {color: red, thin: true})
266
+ print reset,"\n"
158
267
  return nil
159
268
  else
160
269
  return users[0]
161
270
  end
162
271
  end
163
272
 
164
- def find_all_user_ids(account_id, usernames)
273
+ def find_all_user_ids(account_id, usernames, params={})
165
274
  user_ids = []
166
275
  if usernames.is_a?(String)
167
276
  usernames = usernames.split(",").collect {|it| it.to_s.strip }.select {|it| it }.uniq
@@ -170,11 +279,11 @@ module Morpheus::Cli::AccountsHelper
170
279
  end
171
280
  usernames.each do |username|
172
281
  # save a query
173
- #user = find_user_by_username_or_id(nil, username)
282
+ #user = find_user_by_username_or_id(nil, username, params)
174
283
  if username.to_s =~ /\A\d{1,}\Z/
175
284
  user_ids << username.to_i
176
285
  else
177
- user = find_user_by_username(account_id, username)
286
+ user = find_user_by_username(account_id, username, params)
178
287
  if user.nil?
179
288
  return nil
180
289
  else
@@ -185,6 +294,32 @@ module Morpheus::Cli::AccountsHelper
185
294
  user_ids
186
295
  end
187
296
 
297
+
298
+ ## User Groups
299
+
300
+ def user_group_column_definitions()
301
+ {
302
+ "ID" => lambda {|it| it['id'] },
303
+ #"Account" => lambda {|it| it['account'] ? it['account']['name'] : '' },
304
+ "Name" => lambda {|it| it['name'] },
305
+ "Description" => lambda {|it| it['description'] },
306
+ "Server Group" => lambda {|it| it['serverGroup'] },
307
+ "Sudo Access" => lambda {|it| format_boolean it['sudoAccess'] },
308
+ # "Shared User" => lambda {|it| format_boolean it['sharedUser'] },
309
+ "# Users" => lambda {|it| it['users'].size rescue nil },
310
+ "Created" => lambda {|it| format_local_dt(it['dateCreated']) },
311
+ "Updated" => lambda {|it| format_local_dt(it['lastUpdated']) }
312
+ }
313
+ end
314
+
315
+ def list_user_group_column_definitions()
316
+ columns = user_group_column_definitions
317
+ columns.delete("Sudo Access")
318
+ columns.delete("Server Group")
319
+ columns.delete("Updated")
320
+ return columns.upcase_keys!
321
+ end
322
+
188
323
  def find_user_group_by_name_or_id(account_id, val)
189
324
  if val.to_s =~ /\A\d{1,}\Z/
190
325
  return find_user_group_by_id(account_id, val)
@@ -212,9 +347,8 @@ module Morpheus::Cli::AccountsHelper
212
347
  print_red_alert "User Group not found by name #{name}"
213
348
  return nil
214
349
  elsif user_groups.size > 1
215
- print_red_alert "#{user_groups.size} user groups found by name #{name}"
216
- print_user_groups_table(user_groups, {color: red})
217
- print_red_alert "Try using ID instead"
350
+ print_red_alert "Found #{user_groups.size} user groups by name '#{name}'. Try using ID instead: #{format_list(user_groups.collect {|it| it['id']}, 'or', 3)}"
351
+ print as_pretty_table(user_groups, [:id, :name, :description], {color: red, thin: true})
218
352
  print reset,"\n"
219
353
  return nil
220
354
  else
@@ -222,36 +356,6 @@ module Morpheus::Cli::AccountsHelper
222
356
  end
223
357
  end
224
358
 
225
- def print_accounts_table(accounts, options={})
226
- table_color = options.key?(:color) ? options[:color] : cyan
227
- rows = accounts.collect do |account|
228
- status_state = nil
229
- if account['active']
230
- status_state = "#{green}ACTIVE#{table_color}"
231
- else
232
- status_state = "#{red}INACTIVE#{table_color}"
233
- end
234
- {
235
- id: account['id'],
236
- name: account['name'],
237
- description: account['description'],
238
- role: account['role'] ? account['role']['authority'] : nil,
239
- status: status_state,
240
- dateCreated: format_local_dt(account['dateCreated'])
241
- }
242
- end
243
- print table_color if table_color
244
- print as_pretty_table(rows, [
245
- :id,
246
- :name,
247
- :description,
248
- :role,
249
- {:dateCreated => {:display_name => "Date Created"} },
250
- :status
251
- ], options.merge({color:table_color}))
252
- print reset if table_color
253
- end
254
-
255
359
  def format_role_type(role)
256
360
  str = ""
257
361
  if role['roleType'] == "account"
@@ -271,52 +375,6 @@ module Morpheus::Cli::AccountsHelper
271
375
  return str
272
376
  end
273
377
 
274
- def print_roles_table(roles, options={})
275
- table_color = options.key?(:color) ? options[:color] : cyan
276
- rows = roles.collect do |role|
277
- {
278
- id: role['id'],
279
- name: role['authority'],
280
- description: role['description'],
281
- scope: role['scope'],
282
- multitenant: role['multitenant'] ? 'Yes' : 'No',
283
- type: format_role_type(role),
284
- owner: role['owner'] ? role['owner']['name'] : "System",
285
- dateCreated: format_local_dt(role['dateCreated'])
286
- }
287
- end
288
- columns = [
289
- :id,
290
- :name,
291
- :description,
292
- # options[:is_master_account] ? :scope : nil,
293
- options[:is_master_account] ? :type : nil,
294
- options[:is_master_account] ? :multitenant : nil,
295
- options[:is_master_account] ? :owner : nil,
296
- {:dateCreated => {:display_name => "Date Created"} }
297
- ].compact
298
- if options[:include_fields]
299
- columns = options[:include_fields]
300
- end
301
- # print table_color if table_color
302
- print as_pretty_table(rows, columns, options)
303
- # print reset if table_color
304
- end
305
-
306
- def print_users_table(users, options={})
307
- table_color = options[:color] || cyan
308
- rows = users.collect do |user|
309
- {id: user['id'], username: user['username'], name: user['displayName'], first: user['firstName'], last: user['lastName'], email: user['email'], role: format_user_role_names(user), account: user['account'] ? user['account']['name'] : nil}
310
- end
311
- columns = [:id, :account, :first, :last, :username, :email, :role]
312
- if options[:include_fields]
313
- columns = options[:include_fields]
314
- end
315
- #print table_color if table_color
316
- print as_pretty_table(rows, columns, options)
317
- #print reset if table_color
318
- end
319
-
320
378
  ## These user access formatted methods should probably move up to PrintHelper to be more ubiquitous.
321
379
 
322
380
  def format_user_role_names(user)