morpheus-cli 4.2.22 → 5.0.0

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 (35) hide show
  1. checksums.yaml +4 -4
  2. data/Dockerfile +1 -1
  3. data/lib/morpheus/api/api_client.rb +14 -0
  4. data/lib/morpheus/api/billing_interface.rb +33 -0
  5. data/lib/morpheus/api/catalog_item_types_interface.rb +9 -0
  6. data/lib/morpheus/api/rest_interface.rb +0 -6
  7. data/lib/morpheus/api/roles_interface.rb +14 -0
  8. data/lib/morpheus/cli.rb +2 -2
  9. data/lib/morpheus/cli/apps.rb +3 -4
  10. data/lib/morpheus/cli/backup_jobs_command.rb +3 -0
  11. data/lib/morpheus/cli/backups_command.rb +3 -0
  12. data/lib/morpheus/cli/catalog_command.rb +507 -0
  13. data/lib/morpheus/cli/cli_command.rb +19 -11
  14. data/lib/morpheus/cli/commands/standard/source_command.rb +1 -1
  15. data/lib/morpheus/cli/commands/standard/update_command.rb +76 -0
  16. data/lib/morpheus/cli/containers_command.rb +14 -0
  17. data/lib/morpheus/cli/hosts.rb +30 -2
  18. data/lib/morpheus/cli/instances.rb +19 -1
  19. data/lib/morpheus/cli/library_option_lists_command.rb +14 -6
  20. data/lib/morpheus/cli/mixins/accounts_helper.rb +7 -6
  21. data/lib/morpheus/cli/mixins/backups_helper.rb +2 -4
  22. data/lib/morpheus/cli/mixins/catalog_helper.rb +66 -0
  23. data/lib/morpheus/cli/mixins/deployments_helper.rb +0 -1
  24. data/lib/morpheus/cli/mixins/option_source_helper.rb +1 -1
  25. data/lib/morpheus/cli/mixins/print_helper.rb +46 -0
  26. data/lib/morpheus/cli/ping.rb +0 -1
  27. data/lib/morpheus/cli/remote.rb +0 -2
  28. data/lib/morpheus/cli/roles.rb +305 -3
  29. data/lib/morpheus/cli/storage_providers_command.rb +40 -56
  30. data/lib/morpheus/cli/usage_command.rb +150 -0
  31. data/lib/morpheus/cli/user_settings_command.rb +1 -0
  32. data/lib/morpheus/cli/users.rb +12 -1
  33. data/lib/morpheus/cli/version.rb +1 -1
  34. data/lib/morpheus/formatters.rb +26 -5
  35. metadata +8 -2
@@ -10,7 +10,6 @@ module Morpheus::Cli::DeploymentsHelper
10
10
  ## Deployments
11
11
 
12
12
  def deployments_interface
13
- # @api_client.groups
14
13
  raise "#{self.class} has not defined @deployments_interface" if @deployments_interface.nil?
15
14
  @deployments_interface
16
15
  end
@@ -75,7 +75,7 @@ module Morpheus::Cli::OptionSourceHelper
75
75
 
76
76
  def get_cloud_options(refresh=false, api_params={})
77
77
  if !@available_cloud_options || refresh
78
- option_results = options_interface.options_for_source('clouds', api_params.mege({'default' => 'false'}))
78
+ option_results = options_interface.options_for_source('clouds', api_params.merge({'default' => 'false'}))
79
79
  @available_cloud_options = option_results['data'].collect {|it|
80
80
  {"name" => it["name"], "value" => it["value"], "id" => it["value"]}
81
81
  }
@@ -1285,4 +1285,50 @@ module Morpheus::Cli::PrintHelper
1285
1285
  end
1286
1286
  end
1287
1287
 
1288
+ # convert JSON or YAML string to a map
1289
+ def parse_json_or_yaml(config, parsers = [:json, :yaml])
1290
+ rtn = {success: false, data: nil, err: nil}
1291
+ err = nil
1292
+ config = config.strip
1293
+ if config[0..2] == "---"
1294
+ parsers = [:yaml]
1295
+ end
1296
+ # ok only parse json for strings that start with {, consolidated yaml can look like json and cause issues}
1297
+ if config[0] && config[0].chr == "{" && config[-1] && config[-1].chr == "}"
1298
+ parsers = [:json]
1299
+ end
1300
+ parsers.each do |parser|
1301
+ if parser == :yaml
1302
+ begin
1303
+ # todo: one method to parse and return Hash
1304
+ # load does not raise an exception, it just returns the bad string
1305
+ #YAML.parse(config)
1306
+ config_map = YAML.load(config)
1307
+ if !config_map.is_a?(Hash)
1308
+ raise "Failed to parse config as YAML"
1309
+ end
1310
+ rtn[:data] = config_map
1311
+ rtn[:success] = true
1312
+ break
1313
+ rescue => ex
1314
+ rtn[:err] = ex if rtn[:err].nil?
1315
+ end
1316
+ elsif parser == :json
1317
+ begin
1318
+ config_map = JSON.parse(config)
1319
+ rtn[:data] = config_map
1320
+ rtn[:success] = true
1321
+ break
1322
+ rescue => ex
1323
+ rtn[:err] = ex if rtn[:err].nil?
1324
+ end
1325
+ end
1326
+ end
1327
+ return rtn
1328
+ end
1329
+
1330
+ def parse_yaml_or_json(config, parsers = [:yaml, :json])
1331
+ parse_json_or_yaml(config, parsers)
1332
+ end
1333
+
1288
1334
  end
@@ -199,7 +199,6 @@ EOT
199
199
  "Version" => lambda {|it| appliance[:build_version] },
200
200
  # "Active" => lambda {|it| it[:active] ? "Yes " + format_is_current() : "No" },
201
201
  "Response Time" => lambda {|it| format_duration_seconds(took_sec) },
202
- #"Response Time" => lambda {|it| format_sig_dig(took_sec, 3) + "s" rescue "" },
203
202
  # "Error" => lambda {|it| error_string },
204
203
  "Status" => lambda {|it| format_appliance_status(appliance, cyan) },
205
204
  }
@@ -137,7 +137,6 @@ EOT
137
137
  check_str
138
138
  },
139
139
  "Response Time" => lambda {|it| format_duration_milliseconds(it[:last_check][:took]) rescue "" },
140
- #"Response Time" => lambda {|it| format_sig_dig((it[:last_check][:took]/ 1000.to_f), 3) + "s" rescue "" },
141
140
  "Error" => {display_method: lambda {|it|
142
141
  error_str = it[:last_check] ? it[:last_check][:error].to_s : ""
143
142
  error_str
@@ -1283,7 +1282,6 @@ EOT
1283
1282
  end
1284
1283
  },
1285
1284
  "Response Time" => lambda {|it| format_duration_milliseconds(it[:last_check][:took]) rescue "" },
1286
- #"Response Time" => lambda {|it| format_sig_dig((it[:last_check][:took]/ 1000.to_f), 3) + "s" rescue "" },
1287
1285
  "Status" => lambda {|it| format_appliance_status(it, cyan) },
1288
1286
  "Error" => lambda {|it|
1289
1287
  error_str = it[:last_check] ? it[:last_check][:error] : ""
@@ -13,7 +13,7 @@ class Morpheus::Cli::Roles
13
13
  include Morpheus::Cli::AccountsHelper
14
14
  include Morpheus::Cli::ProvisioningHelper
15
15
  include Morpheus::Cli::WhoamiHelper
16
- register_subcommands :list, :get, :add, :update, :remove, :'list-permissions', :'update-feature-access', :'update-global-group-access', :'update-group-access', :'update-global-cloud-access', :'update-cloud-access', :'update-global-instance-type-access', :'update-instance-type-access', :'update-global-blueprint-access', :'update-blueprint-access'
16
+ register_subcommands :list, :get, :add, :update, :remove, :'list-permissions', :'update-feature-access', :'update-global-group-access', :'update-group-access', :'update-global-cloud-access', :'update-cloud-access', :'update-global-instance-type-access', :'update-instance-type-access', :'update-global-blueprint-access', :'update-blueprint-access', :'update-global-catalog-item-type-access', :'update-catalog-item-type-access', :'update-persona-access'
17
17
  alias_subcommand :details, :get
18
18
  set_default_subcommand :list
19
19
 
@@ -101,12 +101,20 @@ class Morpheus::Cli::Roles
101
101
  opts.on('-b','--blueprint-access', "Display Blueprint Access") do
102
102
  options[:include_blueprint_access] = true
103
103
  end
104
- opts.on('-a','--all-access', "Display All Access Lists") do
104
+ opts.on(nil,'--catalog-item-type-access', "Display Catalog Item Type Access") do
105
+ options[:include_catalog_item_types_access] = true
106
+ end
107
+ opts.on(nil,'--personas', "Display Persona Access") do
108
+ options[:include_personas_access] = true
109
+ end
110
+ opts.on('-a','--all', "Display All Access Lists") do
105
111
  options[:include_feature_access] = true
106
112
  options[:include_group_access] = true
107
113
  options[:include_cloud_access] = true
108
114
  options[:include_instance_type_access] = true
109
115
  options[:include_blueprint_access] = true
116
+ options[:include_catalog_item_types_access] = true
117
+ options[:include_personas_access] = true
110
118
  end
111
119
  build_standard_get_options(opts, options)
112
120
  opts.footer = <<-EOT
@@ -198,6 +206,7 @@ EOT
198
206
  rows = rows.select {|row| row[:code].to_s =~ phrase_regexp || row[:name].to_s =~ phrase_regexp }
199
207
  end
200
208
  print as_pretty_table(rows, [:code, :name, :access], options)
209
+ print reset,"\n"
201
210
  else
202
211
  print cyan,"Use --permissions to list permissions","\n"
203
212
  end
@@ -208,6 +217,7 @@ EOT
208
217
  {"Clouds" => lambda {|it| get_access_string(it['globalZoneAccess']) } },
209
218
  {"Instance Types" => lambda {|it| get_access_string(it['globalInstanceTypeAccess']) } },
210
219
  {"Blueprints" => lambda {|it| get_access_string(it['globalAppTemplateAccess'] || it['globalBlueprintAccess']) } },
220
+ {"Catalog Item Types" => lambda {|it| get_access_string(it['globalCatalogItemTypeAccess']) } },
211
221
  ], options)
212
222
 
213
223
  #print_h2 "Group Access: #{get_access_string(json_response['globalSiteAccess'])}", options
@@ -225,6 +235,7 @@ EOT
225
235
  else
226
236
  print cyan,"Use -g, --group-access to list custom access","\n"
227
237
  end
238
+ print reset,"\n"
228
239
  else
229
240
  # print "\n"
230
241
  # print cyan,bold,"Group Access: #{get_access_string(json_response['globalSiteAccess'])}",reset,"\n"
@@ -246,6 +257,7 @@ EOT
246
257
  else
247
258
  print cyan,"Use -c, --cloud-access to list custom access","\n"
248
259
  end
260
+ print reset,"\n"
249
261
  else
250
262
  # print "\n"
251
263
  # print cyan,bold,"Cloud Access: #{get_access_string(json_response['globalZoneAccess'])}",reset,"\n"
@@ -267,6 +279,7 @@ EOT
267
279
  else
268
280
  print cyan,"Use -i, --instance-type-access to list custom access","\n"
269
281
  end
282
+ print reset,"\n"
270
283
  else
271
284
  # print "\n"
272
285
  # print cyan,bold,"Instance Type Access: #{get_access_string(json_response['globalInstanceTypeAccess'])}",reset,"\n"
@@ -290,12 +303,54 @@ EOT
290
303
  else
291
304
  print cyan,"Use -b, --blueprint-access to list custom access","\n"
292
305
  end
306
+ print reset,"\n"
293
307
  else
294
308
  # print "\n"
295
309
  # print cyan,bold,"Blueprint Access: #{get_access_string(json_response['globalAppTemplateAccess'])}",reset,"\n"
296
310
  end
297
- print reset,"\n"
311
+
312
+
313
+ catalog_item_type_global_access = json_response['globalCatalogItemTypeAccess']
314
+ catalog_item_type_permissions = json_response['catalogItemTypePermissions'] || []
315
+ print cyan
316
+ # print_h2 "catalog_item_type Access: #{get_access_string(json_response['globalCatalogItemTypeAccess'])}", options
317
+ # print "\n"
318
+ if catalog_item_type_global_access == 'custom'
319
+ print_h2 "Catalog Item Type Access", options
320
+ if options[:include_catalog_item_types_access]
321
+ rows = catalog_item_type_permissions.collect do |it|
322
+ {
323
+ name: it['name'],
324
+ access: format_access_string(it['access'], ["none","read","full"]),
325
+ }
326
+ end
327
+ print as_pretty_table(rows, [:name, :access], options)
328
+ else
329
+ print cyan,"Use -b, --catalog-item-type-access to list custom access","\n"
330
+ end
331
+ else
332
+ # print "\n"
333
+ # print cyan,bold,"Catalog Item Type Access: #{get_access_string(json_response['globalCatalogItemTypeAccess'])}",reset,"\n"
334
+ end
335
+
336
+
337
+ persona_permissions = json_response['personaPermissions'] || json_response['personas'] || []
338
+ if options[:include_catalog_item_types_access]
339
+ print_h2 "Persona Access", options
340
+ rows = persona_permissions.collect do |it|
341
+ {
342
+ name: it['name'],
343
+ access: format_access_string(it['access'], ["none","read","full"]),
344
+ }
345
+ end
346
+ print as_pretty_table(rows, [:name, :access], options)
347
+ print reset,"\n"
348
+ end
349
+
350
+ # print reset,"\n"
351
+
298
352
  end
353
+
299
354
  return 0, nil
300
355
  end
301
356
 
@@ -449,6 +504,10 @@ EOT
449
504
  end
450
505
  end
451
506
 
507
+ # v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'defaultPersona', 'fieldLabel' => 'Default Persona', 'type' => 'select', 'optionSource' => 'personas', 'description' => 'Default Persona'}], options[:options], @api_client)
508
+ v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'defaultPersona', 'fieldLabel' => 'Default Persona', 'type' => 'select', 'selectOptions' => [{'name'=>'Service Catalog','value'=>'serviceCatalog'},{'name'=>'Standard','value'=>'standard'}], 'description' => 'Default Persona'}], options[:options], @api_client)
509
+ role_payload['defaultPersona'] = {'code' => v_prompt['defaultPersona']} unless v_prompt['defaultPersona'].to_s.strip.empty?
510
+
452
511
  payload = {"role" => role_payload}
453
512
  end
454
513
  @roles_interface.setopts(options)
@@ -1267,6 +1326,249 @@ EOT
1267
1326
  end
1268
1327
  end
1269
1328
 
1329
+ def update_global_catalog_item_type_access(args)
1330
+ usage = "Usage: morpheus roles update-global-catalog-item-type-access [name] [full|custom|none]"
1331
+ options = {}
1332
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
1333
+ opts.banner = subcommand_usage("[name] [full|custom|none]")
1334
+ build_common_options(opts, options, [:json, :dry_run, :remote])
1335
+ end
1336
+ optparse.parse!(args)
1337
+
1338
+ if args.count < 2
1339
+ puts optparse
1340
+ exit 1
1341
+ end
1342
+ name = args[0]
1343
+ access_value = args[1].to_s.downcase
1344
+ if !['full', 'custom', 'none'].include?(access_value)
1345
+ puts optparse
1346
+ exit 1
1347
+ end
1348
+
1349
+
1350
+ connect(options)
1351
+ begin
1352
+ account = find_account_from_options(options)
1353
+ account_id = account ? account['id'] : nil
1354
+ role = find_role_by_name_or_id(account_id, name)
1355
+ exit 1 if role.nil?
1356
+
1357
+ params = {permissionCode: 'CatalogItemType', access: access_value}
1358
+ @roles_interface.setopts(options)
1359
+ if options[:dry_run]
1360
+ print_dry_run @roles_interface.dry.update_permission(account_id, role['id'], params)
1361
+ return
1362
+ end
1363
+ json_response = @roles_interface.update_permission(account_id, role['id'], params)
1364
+
1365
+ if options[:json]
1366
+ print JSON.pretty_generate(json_response)
1367
+ print "\n"
1368
+ else
1369
+ print_green_success "Role #{role['authority']} global catalog item type access updated"
1370
+ end
1371
+ rescue RestClient::Exception => e
1372
+ print_rest_exception(e, options)
1373
+ exit 1
1374
+ end
1375
+ end
1376
+
1377
+ def update_catalog_item_type_access(args)
1378
+ options = {}
1379
+ catalog_item_type_id = nil
1380
+ access_value = nil
1381
+ do_all = false
1382
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
1383
+ opts.banner = subcommand_usage("[name]")
1384
+ opts.on( '--catalog-item-type ID', String, "Catalog Item Type ID or Name" ) do |val|
1385
+ catalog_item_type_id = val
1386
+ end
1387
+ opts.on( nil, '--all', "Update all catalog item types at once." ) do
1388
+ do_all = true
1389
+ end
1390
+ opts.on( '--access VALUE', String, "Access value [full|none]" ) do |val|
1391
+ access_value = val
1392
+ end
1393
+ build_common_options(opts, options, [:json, :dry_run, :remote])
1394
+ opts.footer = "Update role access for an catalog item type or all types.\n" +
1395
+ "[name] is required. This is the name or id of a role.\n" +
1396
+ "--catalog-item-type or --all is required. This is the name or id of a catalog item type.\n" +
1397
+ "--access is required. This is the new access value."
1398
+ end
1399
+ optparse.parse!(args)
1400
+
1401
+ if args.count < 1
1402
+ puts optparse
1403
+ return 1
1404
+ end
1405
+ name = args[0]
1406
+ # support old usage: [name] [catalog_item_type] [access]
1407
+ catalog_item_type_id ||= args[1]
1408
+ access_value ||= args[2]
1409
+
1410
+ if (!catalog_item_type_id && !do_all) || !access_value
1411
+ puts_error optparse
1412
+ return 1
1413
+ end
1414
+
1415
+ access_value = access_value.to_s.downcase
1416
+
1417
+ if !['full', 'none'].include?(access_value)
1418
+ puts optparse
1419
+ return 1
1420
+ end
1421
+
1422
+ connect(options)
1423
+ begin
1424
+ account = find_account_from_options(options)
1425
+ account_id = account ? account['id'] : nil
1426
+ role = find_role_by_name_or_id(account_id, name)
1427
+ return 1 if role.nil?
1428
+
1429
+ role_json = @roles_interface.get(account_id, role['id'])
1430
+ catalog_item_type_global_access = role_json['globalCatalogItemTypeAccess']
1431
+ catalog_item_type_permissions = role_json['catalogItemTypePermissions'] || []
1432
+ if catalog_item_type_global_access != 'custom'
1433
+ print "\n", red, "Global Catalog Item Type Access is currently: #{catalog_item_type_global_access.to_s.capitalize}"
1434
+ print "\n", "You must first set it to Custom via `morpheus roles update-global-catalog-item-type-access \"#{name}\" custom`"
1435
+ print "\n\n", reset
1436
+ return 1
1437
+ end
1438
+
1439
+ # hacky, but support name or code lookup via the list returned in the show payload
1440
+ catalog_item_type = nil
1441
+ if !do_all
1442
+ if catalog_item_type_id.to_s =~ /\A\d{1,}\Z/
1443
+ catalog_item_type = catalog_item_type_permissions.find {|b| b['id'] == catalog_item_type_id.to_i }
1444
+ else
1445
+ catalog_item_type = catalog_item_type_permissions.find {|b| b['name'] == catalog_item_type_id }
1446
+ end
1447
+ if catalog_item_type.nil?
1448
+ print_red_alert "Catalog Item Type not found: '#{catalog_item_type_id}'"
1449
+ return 1
1450
+ end
1451
+ end
1452
+
1453
+ params = {}
1454
+ if do_all
1455
+ params['allCatalogItemTypes'] = true
1456
+ else
1457
+ params['catalogItemTypeId'] = catalog_item_type['id']
1458
+ end
1459
+ params['access'] = access_value
1460
+ @roles_interface.setopts(options)
1461
+ if options[:dry_run]
1462
+ print_dry_run @roles_interface.dry.update_catalog_item_type(account_id, role['id'], params)
1463
+ return
1464
+ end
1465
+ json_response = @roles_interface.update_catalog_item_type(account_id, role['id'], params)
1466
+
1467
+ if options[:json]
1468
+ print JSON.pretty_generate(json_response)
1469
+ print "\n"
1470
+ else
1471
+ if do_all
1472
+ print_green_success "Role #{role['authority']} access updated for all catalog item types"
1473
+ else
1474
+ print_green_success "Role #{role['authority']} access updated for catalog item type #{catalog_item_type['name']}"
1475
+ end
1476
+ end
1477
+ return 0
1478
+ rescue RestClient::Exception => e
1479
+ print_rest_exception(e, options)
1480
+ exit 1
1481
+ end
1482
+ end
1483
+
1484
+ def update_persona_access(args)
1485
+ options = {}
1486
+ persona_id = nil
1487
+ access_value = nil
1488
+ do_all = false
1489
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
1490
+ opts.banner = subcommand_usage("[name] [serviceCatalog|standard]")
1491
+ opts.on( '--persona CODE', String, "Persona Code" ) do |val|
1492
+ persona_id = val
1493
+ end
1494
+ opts.on( nil, '--all', "Update all personas at once." ) do
1495
+ do_all = true
1496
+ end
1497
+ opts.on( '--access VALUE', String, "Access value [full|none]" ) do |val|
1498
+ access_value = val
1499
+ end
1500
+ build_common_options(opts, options, [:json, :dry_run, :remote])
1501
+ opts.footer = "Update role access for an persona or personas.\n" +
1502
+ "[name] is required. This is the name or id of a role.\n" +
1503
+ "--persona or --all is required. This is the code of a persona.\n" +
1504
+ "--access is required. This is the new access value."
1505
+ end
1506
+ optparse.parse!(args)
1507
+
1508
+ if args.count < 1
1509
+ puts optparse
1510
+ return 1
1511
+ end
1512
+ name = args[0]
1513
+ # support old usage: [name] [persona] [access]
1514
+ persona_id ||= args[1]
1515
+ access_value ||= args[2]
1516
+
1517
+ if (!persona_id && !do_all) || !access_value
1518
+ puts_error optparse
1519
+ return 1
1520
+ end
1521
+
1522
+ access_value = access_value.to_s.downcase
1523
+
1524
+ if !['full', 'none'].include?(access_value)
1525
+ puts optparse
1526
+ return 1
1527
+ end
1528
+
1529
+ connect(options)
1530
+ begin
1531
+ account = find_account_from_options(options)
1532
+ account_id = account ? account['id'] : nil
1533
+ role = find_role_by_name_or_id(account_id, name)
1534
+ return 1 if role.nil?
1535
+
1536
+ role_json = @roles_interface.get(account_id, role['id'])
1537
+
1538
+ # no lookup right now, pass the code serviceCatalog|standard
1539
+ persona_code = persona_id
1540
+
1541
+ params = {}
1542
+ if do_all
1543
+ params['allPersonas'] = true
1544
+ else
1545
+ params['personaCode'] = persona_code
1546
+ end
1547
+ params['access'] = access_value
1548
+ @roles_interface.setopts(options)
1549
+ if options[:dry_run]
1550
+ print_dry_run @roles_interface.dry.update_persona(account_id, role['id'], params)
1551
+ return
1552
+ end
1553
+ json_response = @roles_interface.update_persona(account_id, role['id'], params)
1554
+
1555
+ if options[:json]
1556
+ print JSON.pretty_generate(json_response)
1557
+ print "\n"
1558
+ else
1559
+ if do_all
1560
+ print_green_success "Role #{role['authority']} access updated for all personas"
1561
+ else
1562
+ print_green_success "Role #{role['authority']} access updated for persona #{persona_code}"
1563
+ end
1564
+ end
1565
+ return 0
1566
+ rescue RestClient::Exception => e
1567
+ print_rest_exception(e, options)
1568
+ exit 1
1569
+ end
1570
+ end
1571
+
1270
1572
  private
1271
1573
 
1272
1574
  def add_role_option_types